From 51a86156ebc5d95df28d200e806fe92686379e7b Mon Sep 17 00:00:00 2001 From: Genevieve Warren <24882762+gewarren@users.noreply.github.com> Date: Thu, 19 Mar 2026 18:15:25 -0700 Subject: [PATCH 1/4] edit pass --- docs/concepts/cancellation/cancellation.md | 8 +- docs/concepts/capabilities/capabilities.md | 40 ++--- docs/concepts/completions/completions.md | 18 +-- docs/concepts/elicitation/elicitation.md | 58 +++---- docs/concepts/filters.md | 172 ++++++++++----------- docs/concepts/getting-started.md | 8 +- docs/concepts/httpcontext/httpcontext.md | 26 ++-- docs/concepts/index.md | 10 +- docs/concepts/logging/logging.md | 17 +- docs/concepts/pagination/pagination.md | 20 +-- docs/concepts/progress/progress.md | 8 +- docs/concepts/prompts/prompts.md | 36 ++--- docs/concepts/resources/resources.md | 46 +++--- docs/concepts/roots/roots.md | 8 +- docs/concepts/sampling/sampling.md | 4 +- docs/concepts/tasks/tasks.md | 172 ++++++++++----------- docs/concepts/tools/tools.md | 74 ++++----- docs/concepts/transports/transports.md | 44 +++--- docs/experimental.md | 8 +- docs/list-of-diagnostics.md | 10 +- docs/roadmap.md | 8 +- 21 files changed, 396 insertions(+), 399 deletions(-) diff --git a/docs/concepts/cancellation/cancellation.md b/docs/concepts/cancellation/cancellation.md index 50753d259..d06980254 100644 --- a/docs/concepts/cancellation/cancellation.md +++ b/docs/concepts/cancellation/cancellation.md @@ -7,18 +7,18 @@ uid: cancellation ## Cancellation -MCP supports [cancellation] of in-flight requests. Either side can cancel a previously issued request, and `CancellationToken` parameters on MCP methods are wired to send and receive `notifications/cancelled` notifications over the protocol. +MCP supports [cancellation] of in-flight requests. Either side can cancel a previously issued request, and parameters on MCP methods are wired to send and receive `notifications/cancelled` notifications over the protocol. [cancellation]: https://modelcontextprotocol.io/specification/2025-11-25/basic/utilities/cancellation [task cancellation]: https://learn.microsoft.com/dotnet/standard/parallel-programming/task-cancellation ### How cancellation maps to MCP notifications -When a `CancellationToken` passed to a client method (such as ) is cancelled, a `notifications/cancelled` notification is sent to the server with the request ID. On the server side, the `CancellationToken` provided to the tool method is then triggered, allowing the handler to stop work gracefully. This same mechanism works in reverse for server-to-client requests. +When a passed to a client method (such as ) is cancelled, a `notifications/cancelled` notification is sent to the server with the request ID. On the server side, the provided to the tool method is then triggered, allowing the handler to stop work gracefully. This same mechanism works in reverse for server-to-client requests. ### Server-side cancellation handling -Server tool methods receive a `CancellationToken` that is triggered when the client sends a cancellation notification. Pass this token through to any async operations so they stop promptly: +Server tool methods receive a that is triggered when the client sends a cancellation notification. Pass this token through to any async operations so they stop promptly: ```csharp [McpServerTool, Description("A long-running computation")] @@ -35,7 +35,7 @@ public static async Task LongComputation( } ``` -When the client sends a cancellation notification, the `OperationCanceledException` propagates back to the client as a cancellation response. +When the client sends a cancellation notification, the propagates back to the client as a cancellation response. ### Cancellation notification details diff --git a/docs/concepts/capabilities/capabilities.md b/docs/concepts/capabilities/capabilities.md index b466736a4..75c770dc7 100644 --- a/docs/concepts/capabilities/capabilities.md +++ b/docs/concepts/capabilities/capabilities.md @@ -15,11 +15,11 @@ MCP uses a [capability negotiation] mechanism during connection setup. Clients a declares what features the client supports: -| Capability | Type | Description | -|-----------|------|-------------| -| `Roots` | | Client can provide filesystem root URIs | -| `Sampling` | | Client can handle LLM sampling requests | -| `Elicitation` | | Client can present forms or URLs to the user | +| Capability | Type | Description | +|----------------|-------------------------------|---------------------------| +| `Roots` | | Client can provide filesystem root URIs | +| `Sampling` | | Client can handle LLM sampling requests | +| `Elicitation` | | Client can present forms or URLs to the user | | `Experimental` | `IDictionary` | Experimental capabilities | Configure client capabilities when creating an MCP client: @@ -48,13 +48,13 @@ Handlers for each capability (roots, sampling, elicitation) are covered in their declares what features the server supports: -| Capability | Type | Description | -|-----------|------|-------------| -| `Tools` | | Server exposes callable tools | -| `Prompts` | | Server exposes prompt templates | -| `Resources` | | Server exposes readable resources | -| `Logging` | | Server can send log messages | -| `Completions` | | Server supports argument completions | +| Capability | Type | Description | +|----------------|-------------------------------|---------------------------| +| `Tools` | | Server exposes callable tools | +| `Prompts` | | Server exposes prompt templates | +| `Resources` | | Server exposes readable resources | +| `Logging` | | Server can send log messages | +| `Completions` | | Server supports argument completions | | `Experimental` | `IDictionary` | Experimental capabilities | Server capabilities are automatically inferred from the configured features. For example, registering tools with `.WithTools()` automatically declares the tools capability. @@ -63,24 +63,24 @@ Server capabilities are automatically inferred from the configured features. For Before using an optional feature, check whether the other side declared the corresponding capability. -#### Checking server capabilities from the client +#### Check server capabilities from the client ```csharp await using var client = await McpClient.CreateAsync(transport); -// Check if the server supports tools +// Check if the server supports tools. if (client.ServerCapabilities.Tools is not null) { var tools = await client.ListToolsAsync(); } -// Check if the server supports resources with subscriptions +// Check if the server supports resources with subscriptions. if (client.ServerCapabilities.Resources is { Subscribe: true }) { await client.SubscribeToResourceAsync("config://app/settings"); } -// Check if the server supports prompts with list-changed notifications +// Check if the server supports prompts with list-changed notifications. if (client.ServerCapabilities.Prompts is { ListChanged: true }) { mcpClient.RegisterNotificationHandler( @@ -91,13 +91,13 @@ if (client.ServerCapabilities.Prompts is { ListChanged: true }) }); } -// Check if the server supports logging +// Check if the server supports logging. if (client.ServerCapabilities.Logging is not null) { await client.SetLoggingLevelAsync(LoggingLevel.Info); } -// Check if the server supports completions +// Check if the server supports completions. if (client.ServerCapabilities.Completions is not null) { var completions = await client.CompleteAsync( @@ -112,10 +112,10 @@ if (client.ServerCapabilities.Completions is not null) During connection setup, the client and server negotiate a mutually supported MCP protocol version. After initialization, the negotiated version is available on both sides: ```csharp -// On the client +// On the client. string? version = client.NegotiatedProtocolVersion; -// On the server (within a tool or handler) +// On the server (within a tool or handler). string? version = server.NegotiatedProtocolVersion; ``` diff --git a/docs/concepts/completions/completions.md b/docs/concepts/completions/completions.md index 10996882c..4461758bd 100644 --- a/docs/concepts/completions/completions.md +++ b/docs/concepts/completions/completions.md @@ -15,8 +15,8 @@ MCP [completions] allow servers to provide argument auto-completion suggestions Completions work with two types of references: -- **Prompt argument completions**: Suggest values for prompt parameters (e.g., language names, style options) -- **Resource template argument completions**: Suggest values for URI template parameters (e.g., file paths, resource IDs) +- **Prompt argument completions**: Suggest values for prompt parameters (for example, language names and style options). +- **Resource template argument completions**: Suggest values for URI template parameters (for example, file paths and resource IDs). The server returns a object containing a list of suggested values, an optional total count, and a flag indicating if more values are available. @@ -36,7 +36,7 @@ builder.Services.AddMcpServer() var argument = @params.Argument; - // Handle prompt argument completions + // Handle prompt argument completions. if (@params.Ref is PromptReference promptRef) { var suggestions = argument.Name switch @@ -46,7 +46,7 @@ builder.Services.AddMcpServer() _ => Array.Empty() }; - // Filter suggestions based on what the user has typed so far + // Filter suggestions based on what the user has typed so far. var filtered = suggestions.Where(s => s.StartsWith(argument.Value, StringComparison.OrdinalIgnoreCase)).ToList(); return new CompleteResult @@ -60,7 +60,7 @@ builder.Services.AddMcpServer() }; } - // Handle resource template argument completions + // Handle resource template argument completions. if (@params.Ref is ResourceTemplateReference resourceRef) { var availableIds = new[] { "1", "2", "3", "4", "5" }; @@ -83,7 +83,7 @@ builder.Services.AddMcpServer() ### Automatic completions with AllowedValuesAttribute -For parameters with a known set of valid values, you can use `System.ComponentModel.DataAnnotations.AllowedValuesAttribute` on `string` parameters of prompts or resource templates. The server will automatically surface those values as completions without needing a custom completion handler. +For parameters with a known set of valid values, you can use on `string` parameters of prompts or resource templates. The server will automatically surface those values as completions without needing a custom completion handler. #### Prompt parameters @@ -124,13 +124,13 @@ Clients request completions using extension method on . The C# SDK registers an instance of with the dependency injection container, so tools can simply add a parameter of type to their method signature to access it. -#### Form Mode Elicitation (In-Band) +#### Form mode elicitation (in-band) For form-based elicitation, the MCP Server must specify the schema of each input value it is requesting from the user. Primitive types (string, number, Boolean) and enum types are supported for elicitation requests. @@ -36,8 +36,8 @@ For enum types, the SDK supports several schema formats: #### Default values -Each schema type supports a `Default` property that specifies a pre-populated value for the form field. -Clients should use defaults to pre-fill form fields, making it easier for users to accept common values or see expected input formats. +Each schema type supports a `Default` property that specifies a prepopulated value for the form field. +Clients should use defaults to prefill form fields, making it easier for users to accept common values or see expected input formats. ```csharp var result = await server.ElicitAsync(new ElicitRequestParams @@ -83,7 +83,7 @@ Enum schemas allow the server to present a set of choices to the user. - : Multi-select with display titles. ```csharp -// Titled single-select: display titles differ from values +// Titled single-select: display titles differ from values. ["priority"] = new ElicitRequestParams.TitledSingleSelectEnumSchema { Description = "Task priority", @@ -96,7 +96,7 @@ Enum schemas allow the server to present a set of choices to the user. Default = "p2" }, -// Multi-select: user can select multiple values +// Multi-select: user can select multiple values. ["tags"] = new ElicitRequestParams.UntitledMultiSelectEnumSchema { Description = "Tags to apply", @@ -115,7 +115,7 @@ The following example demonstrates how a server could request a Boolean response [!code-csharp[](samples/server/Tools/InteractiveTools.cs?name=snippet_GuessTheNumber)] -#### URL Mode Elicitation (Out-of-Band) +#### URL mode elicitation (out-of-band) For URL mode elicitation, the server provides a URL that the user must visit to complete an action. This is useful for scenarios like OAuth flows, payment processing, or collecting sensitive credentials that should not be exposed to the MCP client. @@ -134,7 +134,7 @@ var result = await server.ElicitAsync( cancellationToken); ``` -### Client Support for Elicitation +### Client support for elicitation Clients declare their support for elicitation in their capabilities as part of the `initialize` request. Clients can support `Form` (in-band), `Url` (out-of-band), or both. @@ -170,28 +170,28 @@ Here's an example implementation of how a console application might handle elici [!code-csharp[](samples/client/Program.cs?name=snippet_ElicitationHandler)] -### URL Elicitation Required Error +### URL elicitation required error When a tool cannot proceed without first completing a URL-mode elicitation (for example, when third-party OAuth authorization is needed), and calling `ElicitAsync` is not practical (for example in is enabled disabling server-to-client requests), the server may throw a . This is a specialized error (JSON-RPC error code `-32042`) that signals to the client that one or more URL-mode elicitations must be completed before the original request can be retried. -#### Throwing UrlElicitationRequiredException on the Server +#### Throwing UrlElicitationRequiredException on the server -A server tool can throw `UrlElicitationRequiredException` when it detects that authorization or other out-of-band interaction is required: +A server tool can throw when it detects that authorization or other out-of-band interaction is required: ```csharp [McpServerTool, Description("A tool that requires third-party authorization")] public async Task AccessThirdPartyResource(McpServer server, CancellationToken token) { - // Check if we already have valid credentials for this user - // (In a real app, you'd check stored tokens based on user identity) + // Check if we already have valid credentials for this user. + // (In a real app, you'd check stored tokens based on user identity.) bool hasValidCredentials = false; if (!hasValidCredentials) { - // Generate a unique elicitation ID for tracking + // Generate a unique elicitation ID for tracking. var elicitationId = Guid.NewGuid().ToString(); - // Throw the exception to signal the client needs to complete URL elicitation + // Throw the exception to signal the client needs to complete URL elicitation. throw new UrlElicitationRequiredException( "Authorization is required to access the third-party service.", [ @@ -205,16 +205,16 @@ public async Task AccessThirdPartyResource(McpServer server, Cancellatio ]); } - // Proceed with the authorized operation + // Proceed with the authorized operation. return "Successfully accessed the resource!"; } ``` The exception can include multiple elicitations if the operation requires authorization from multiple services. -#### Catching UrlElicitationRequiredException on the Client +#### Catching UrlElicitationRequiredException on the client -When the client calls a tool and receives a `UrlElicitationRequiredException`, it should: +When the client calls a tool and receives a , it should: 1. Present each URL elicitation to the user (showing the URL and message) 2. Get user consent before opening each URL @@ -231,7 +231,7 @@ catch (UrlElicitationRequiredException ex) { Console.WriteLine($"Authorization required: {ex.Message}"); - // Process each required elicitation + // Process each required elicitation. foreach (var elicitation in ex.Elicitations) { Console.WriteLine($"\nServer requests URL interaction:"); @@ -239,21 +239,21 @@ catch (UrlElicitationRequiredException ex) Console.WriteLine($" URL: {elicitation.Url}"); Console.WriteLine($" Elicitation ID: {elicitation.ElicitationId}"); - // Show security warning and get user consent + // Show security warning and get user consent. Console.Write("\nDo you want to open this URL? (y/n): "); var consent = Console.ReadLine(); if (consent?.ToLower() == "y") { - // Open the URL in the system browser + // Open the URL in the system browser. Process.Start(new ProcessStartInfo(elicitation.Url!) { UseShellExecute = true }); Console.WriteLine("Waiting for you to complete the interaction in your browser..."); - // Optionally listen for notifications/elicitation/complete notification + // Optionally listen for notifications/elicitation/complete notification. } } - // After user completes the out-of-band interaction, retry the tool call + // After user completes the out-of-band interaction, retry the tool call. Console.Write("\nPress Enter to retry the tool call..."); Console.ReadLine(); @@ -262,7 +262,7 @@ catch (UrlElicitationRequiredException ex) } ``` -#### Listening for Elicitation Completion Notifications +#### Listening for elicitation completion notifications Servers can optionally send a `notifications/elicitation/complete` notification when the out-of-band interaction is complete. Clients can register a handler to receive these notifications: @@ -277,13 +277,13 @@ await using var completionHandler = client.RegisterNotificationHandler( if (payload is not null) { Console.WriteLine($"Elicitation {payload.ElicitationId} completed!"); - // Signal that the client can now retry the original request + // Signal that the client can now retry the original request. } }); ``` This pattern is particularly useful for: -- **Third-party OAuth flows**: When the MCP server needs to obtain tokens from external services on behalf of the user -- **Payment processing**: When user confirmation is required through a secure payment interface -- **Sensitive credential collection**: When API keys or other secrets must be entered directly on a trusted server page rather than through the MCP client +- **Third-party OAuth flows**: When the MCP server needs to obtain tokens from external services on behalf of the user. +- **Payment processing**: When user confirmation is required through a secure payment interface. +- **Sensitive credential collection**: When API keys or other secrets must be entered directly on a trusted server page rather than through the MCP client. diff --git a/docs/concepts/filters.md b/docs/concepts/filters.md index 9f63dd962..640330294 100644 --- a/docs/concepts/filters.md +++ b/docs/concepts/filters.md @@ -5,50 +5,50 @@ description: MCP Server Filters uid: filters --- -# MCP Server Filters +# MCP server filters The MCP Server provides two levels of filters for intercepting and modifying request processing: 1. **Message Filters** - Low-level filters (`AddIncomingFilter`, `AddOutgoingFilter`) configured via `WithMessageFilters(...)` that intercept all JSON-RPC messages before routing. -2. **Request-Specific Filters** - Handler-level filters (e.g., `AddListToolsFilter`, `AddCallToolFilter`) configured via `WithRequestFilters(...)` that target specific MCP operations. +2. **Request-Specific Filters** - Handler-level filters (for example, `AddListToolsFilter`, `AddCallToolFilter`) configured via `WithRequestFilters(...)` that target specific MCP operations. The filters are stored in `McpServerOptions.Filters`. -## Available Request-Specific Filter Methods +## Available request-specific filter methods The following request filter methods are available on `IMcpRequestFilterBuilder` inside `WithRequestFilters(...)`: -- `AddListResourceTemplatesFilter` - Filter for list resource templates handlers -- `AddListToolsFilter` - Filter for list tools handlers -- `AddCallToolFilter` - Filter for call tool handlers -- `AddListPromptsFilter` - Filter for list prompts handlers -- `AddGetPromptFilter` - Filter for get prompt handlers -- `AddListResourcesFilter` - Filter for list resources handlers -- `AddReadResourceFilter` - Filter for read resource handlers -- `AddCompleteFilter` - Filter for completion handlers -- `AddSubscribeToResourcesFilter` - Filter for resource subscription handlers -- `AddUnsubscribeFromResourcesFilter` - Filter for resource unsubscription handlers -- `AddSetLoggingLevelFilter` - Filter for logging level handlers +- `AddListResourceTemplatesFilter` - Filter for list resource templates handlers. +- `AddListToolsFilter` - Filter for list tools handlers. +- `AddCallToolFilter` - Filter for call tool handlers. +- `AddListPromptsFilter` - Filter for list prompts handlers. +- `AddGetPromptFilter` - Filter for get prompt handlers. +- `AddListResourcesFilter` - Filter for list resources handlers. +- `AddReadResourceFilter` - Filter for read resource handlers. +- `AddCompleteFilter` - Filter for completion handlers. +- `AddSubscribeToResourcesFilter` - Filter for resource subscription handlers. +- `AddUnsubscribeFromResourcesFilter` - Filter for resource unsubscription handlers. +- `AddSetLoggingLevelFilter` - Filter for logging level handlers. -## Message Filters +## Message filters In addition to the request-specific filters above, there are low-level message filters that intercept all JSON-RPC messages before they are routed to specific handlers. Configure these on `IMcpMessageFilterBuilder` inside `WithMessageFilters(...)`: -- `AddIncomingFilter` - Filter for all incoming JSON-RPC messages (requests and notifications) -- `AddOutgoingFilter` - Filter for all outgoing JSON-RPC messages (responses and notifications) +- `AddIncomingFilter` - Filter for all incoming JSON-RPC messages (requests and notifications). +- `AddOutgoingFilter` - Filter for all outgoing JSON-RPC messages (responses and notifications). -### When to Use Message Filters +### When to use message filters Message filters operate at a lower level than request-specific filters and are useful when you need to: -- Intercept all messages regardless of type -- Implement custom protocol extensions or handle custom JSON-RPC methods -- Log or monitor all traffic between client and server -- Modify or skip messages before they reach handlers -- Send additional messages in response to specific events +- Intercept all messages regardless of type. +- Implement custom protocol extensions or handle custom JSON-RPC methods. +- Log or monitor all traffic between client and server. +- Modify or skip messages before they reach handlers. +- Send additional messages in response to specific events. -### Incoming Message Filter +### Incoming message filter `AddIncomingFilter` intercepts all incoming JSON-RPC messages before they are dispatched to request-specific handlers: @@ -60,29 +60,29 @@ services.AddMcpServer() { var logger = context.Services?.GetService>(); - // Access the raw JSON-RPC message + // Access the raw JSON-RPC message. if (context.JsonRpcMessage is JsonRpcRequest request) { logger?.LogInformation($"Incoming request: {request.Method}"); } - // Call next to continue processing + // Call next to continue processing. await next(context, cancellationToken); }); }) .WithTools(); ``` -#### MessageContext Properties +#### MessageContext properties Inside an incoming message filter, you have access to: -- `context.JsonRpcMessage` - The incoming `JsonRpcMessage` (can be `JsonRpcRequest` or `JsonRpcNotification`) -- `context.Server` - The `McpServer` instance for sending responses or notifications -- `context.Services` - The request's service provider -- `context.Items` - A dictionary for passing data between filters +- `context.JsonRpcMessage` - The incoming `JsonRpcMessage` (can be `JsonRpcRequest` or `JsonRpcNotification`). +- `context.Server` - The instance for sending responses or notifications. +- `context.Services` - The request's service provider. +- `context.Items` - A dictionary for passing data between filters. -#### Skipping Default Handlers +#### Skipping default handlers You can skip the default handler by not calling `next`. This is useful for implementing custom protocol methods: @@ -93,14 +93,14 @@ You can skip the default handler by not calling `next`. This is useful for imple { if (context.JsonRpcMessage is JsonRpcRequest request && request.Method == "custom/myMethod") { - // Handle the custom method directly + // Handle the custom method directly. var response = new JsonRpcResponse { Id = request.Id, Result = JsonSerializer.SerializeToNode(new { message = "Custom response" }) }; await context.Server.SendMessageAsync(response, cancellationToken); - return; // Don't call next - we handled it + return; // Don't call next - we handled it. } await next(context, cancellationToken); @@ -108,7 +108,7 @@ You can skip the default handler by not calling `next`. This is useful for imple }) ``` -### Outgoing Message Filter +### Outgoing message filter `AddOutgoingFilter` intercepts all outgoing JSON-RPC messages before they are sent to the client: @@ -120,7 +120,7 @@ services.AddMcpServer() { var logger = context.Services?.GetService>(); - // Inspect outgoing messages + // Inspect outgoing messages. switch (context.JsonRpcMessage) { case JsonRpcResponse response: @@ -137,7 +137,7 @@ services.AddMcpServer() .WithTools(); ``` -#### Skipping Outgoing Messages +#### Skipping outgoing messages You can suppress outgoing messages by not calling `next`: @@ -146,11 +146,11 @@ You can suppress outgoing messages by not calling `next`: { messageFilters.AddOutgoingFilter(next => async (context, cancellationToken) => { - // Suppress specific notifications + // Suppress specific notifications. if (context.JsonRpcMessage is JsonRpcNotification notification && notification.Method == "notifications/progress") { - return; // Don't send this notification + return; // Don't send this notification. } await next(context, cancellationToken); @@ -158,7 +158,7 @@ You can suppress outgoing messages by not calling `next`: }) ``` -#### Sending Additional Messages +#### Sending additional messages Outgoing message filters can send additional messages by calling `next` with a new `MessageContext`: @@ -167,7 +167,7 @@ Outgoing message filters can send additional messages by calling `next` with a n { messageFilters.AddOutgoingFilter(next => async (context, cancellationToken) => { - // Send an extra notification before certain responses + // Send an extra notification before certain responses. if (context.JsonRpcMessage is JsonRpcResponse response && response.Result is JsonObject result && result.ContainsKey("tools")) @@ -189,7 +189,7 @@ Outgoing message filters can send additional messages by calling `next` with a n }) ``` -### Message Filter Execution Order +### Message filter execution order Message filters execute in registration order, with the first registered filter being the outermost: @@ -197,14 +197,14 @@ Message filters execute in registration order, with the first registered filter services.AddMcpServer() .WithMessageFilters(messageFilters => { - messageFilters.AddIncomingFilter(incomingFilter1); // Incoming: executes first (outermost) - messageFilters.AddIncomingFilter(incomingFilter2); // Incoming: executes second - messageFilters.AddOutgoingFilter(outgoingFilter1); // Outgoing: executes first (outermost) - messageFilters.AddOutgoingFilter(outgoingFilter2); // Outgoing: executes second + messageFilters.AddIncomingFilter(incomingFilter1); // Incoming: executes first (outermost). + messageFilters.AddIncomingFilter(incomingFilter2); // Incoming: executes second. + messageFilters.AddOutgoingFilter(outgoingFilter1); // Outgoing: executes first (outermost). + messageFilters.AddOutgoingFilter(outgoingFilter2); // Outgoing: executes second. }) .WithRequestFilters(requestFilters => { - requestFilters.AddListToolsFilter(toolsFilter); // Request-specific filter + requestFilters.AddListToolsFilter(toolsFilter); // Request-specific filter. }) .WithTools(); ``` @@ -235,7 +235,7 @@ OutgoingFilter2 (after next) OutgoingFilter1 (after next) ``` -### Passing Data Between Filters +### Passing data between filters The `Items` dictionary allows you to pass data between filters processing the same message: @@ -270,7 +270,7 @@ Filters are functions that take a handler and return a new handler, allowing you services.AddMcpServer() .WithListToolsHandler(async (context, cancellationToken) => { - // Your base handler logic + // Your base handler logic. return new ListToolsResult { Tools = GetTools() }; }) .WithRequestFilters(requestFilters => @@ -279,34 +279,34 @@ services.AddMcpServer() { var logger = context.Services?.GetService>(); - // Pre-processing logic + // Pre-processing logic. logger?.LogInformation("Before handler execution"); var result = await next(context, cancellationToken); - // Post-processing logic + // Post-processing logic. logger?.LogInformation("After handler execution"); return result; }); }); ``` -## Filter Execution Order +## Filter execution order ```csharp services.AddMcpServer() .WithListToolsHandler(baseHandler) .WithRequestFilters(requestFilters => { - requestFilters.AddListToolsFilter(filter1); // Executes first (outermost) - requestFilters.AddListToolsFilter(filter2); // Executes second - requestFilters.AddListToolsFilter(filter3); // Executes third (closest to handler) + requestFilters.AddListToolsFilter(filter1); // Executes first (outermost). + requestFilters.AddListToolsFilter(filter2); // Executes second. + requestFilters.AddListToolsFilter(filter3); // Executes third (closest to handler). }); ``` Execution flow: `filter1 -> filter2 -> filter3 -> baseHandler -> filter3 -> filter2 -> filter1` -## Common Use Cases +## Common use cases ### Logging @@ -325,7 +325,7 @@ Execution flow: `filter1 -> filter2 -> filter3 -> baseHandler -> filter3 -> filt }); ``` -### Error Handling +### Error handling ```csharp .WithRequestFilters(requestFilters => @@ -351,7 +351,7 @@ Execution flow: `filter1 -> filter2 -> filter3 -> baseHandler -> filter3 -> filt }); ``` -### Performance Monitoring +### Performance monitoring ```csharp .WithRequestFilters(requestFilters => @@ -391,33 +391,33 @@ Execution flow: `filter1 -> filter2 -> filter3 -> baseHandler -> filter3 -> filt }); ``` -## Built-in Authorization Request Filters +## Built-in authorization request filters When using the ASP.NET Core integration (`ModelContextProtocol.AspNetCore`), you can add authorization filters to support `[Authorize]` and `[AllowAnonymous]` attributes on MCP server tools, prompts, and resources by calling `AddAuthorizationFilters()` on your MCP server builder. -### Enabling Authorization Request Filters +### Enabling authorization request filters To enable authorization support, call `AddAuthorizationFilters()` when configuring your MCP server: ```csharp services.AddMcpServer() .WithHttpTransport() - .AddAuthorizationFilters() // Enable authorization filter support + .AddAuthorizationFilters() // Enable authorization filter support. .WithTools(); ``` **Important**: You should always call `AddAuthorizationFilters()` when using ASP.NET Core integration if you want to use authorization attributes like `[Authorize]` on your MCP server tools, prompts, or resources. -### Authorization Attributes Support +### Authorization attributes support The MCP server automatically respects the following authorization attributes: -- **`[Authorize]`** - Requires authentication for access -- **`[Authorize(Roles = "RoleName")]`** - Requires specific roles -- **`[Authorize(Policy = "PolicyName")]`** - Requires specific authorization policies -- **`[AllowAnonymous]`** - Explicitly allows anonymous access (overrides `[Authorize]`) +- **`[Authorize]`** - Requires authentication for access. +- **`[Authorize(Roles = "RoleName")]`** - Requires specific roles. +- **`[Authorize(Policy = "PolicyName")]`** - Requires specific authorization policies. +- **`[AllowAnonymous]`** - Explicitly allows anonymous access (overrides `[Authorize]`). -### Tool Authorization +### Tool authorization Tools can be decorated with authorization attributes to control access: @@ -432,14 +432,14 @@ public class WeatherTools } [McpServerTool, Description("Gets detailed weather forecast")] - [Authorize] // Requires authentication + [Authorize] // Requires authentication. public static string GetDetailedForecast(string location) { return $"Detailed forecast for {location}: ..."; } [McpServerTool, Description("Manages weather alerts")] - [Authorize(Roles = "Admin")] // Requires Admin role + [Authorize(Roles = "Admin")] // Requires Admin role. public static string ManageWeatherAlerts(string alertType) { return $"Managing alert: {alertType}"; @@ -447,13 +447,13 @@ public class WeatherTools } ``` -### Class-Level Authorization +### Class-level authorization You can apply authorization at the class level, which affects all tools in the class: ```csharp [McpServerToolType] -[Authorize] // All tools require authentication +[Authorize] // All tools require authentication. public class RestrictedTools { [McpServerTool, Description("Restricted tool accessible to authenticated users")] @@ -463,7 +463,7 @@ public class RestrictedTools } [McpServerTool, Description("Public tool accessible to anonymous users")] - [AllowAnonymous] // Overrides class-level [Authorize] + [AllowAnonymous] // Overrides class-level [Authorize]. public static string PublicOperation() { return "Public operation completed"; @@ -471,19 +471,19 @@ public class RestrictedTools } ``` -### How Authorization Filters Work +### How authorization filters work The authorization filters work differently for list operations versus individual operations: -#### List Operations (ListTools, ListPrompts, ListResources) +#### List operations (ListTools, ListPrompts, ListResources) For list operations, the filters automatically remove unauthorized items from the results. Users only see tools, prompts, or resources they have permission to access. -#### Individual Operations (CallTool, GetPrompt, ReadResource) +#### Individual operations (CallTool, GetPrompt, ReadResource) -For individual operations, the filters throw an `McpException` with "Access forbidden" message. These get turned into JSON-RPC errors if uncaught by middleware. +For individual operations, the filters throw an with "Access forbidden" message. These get turned into JSON-RPC errors if uncaught by middleware. -### Filter Execution Order and Authorization +### Filter execution order and authorization Authorization filters are applied automatically when you call `AddAuthorizationFilters()`. These filters run at a specific point in the filter pipeline, which means: @@ -508,21 +508,21 @@ services.AddMcpServer() { var logger = context.Services?.GetService>(); - // This filter runs BEFORE authorization - sees all tools + // This filter runs BEFORE authorization - sees all tools. logger?.LogInformation("Request for tools list - will see all tools"); var result = await next(context, cancellationToken); logger?.LogInformation($"Returning {result.Tools?.Count ?? 0} tools after authorization"); return result; }); }) - .AddAuthorizationFilters() // Authorization filtering happens here + .AddAuthorizationFilters() // Authorization filtering happens here. .WithRequestFilters(requestFilters => { requestFilters.AddListToolsFilter(next => async (context, cancellationToken) => { var logger = context.Services?.GetService>(); - // This filter runs AFTER authorization - only sees authorized tools + // This filter runs AFTER authorization - only sees authorized tools. var result = await next(context, cancellationToken); logger?.LogInformation($"Post-auth filter sees {result.Tools?.Count ?? 0} authorized tools"); return result; @@ -531,7 +531,7 @@ services.AddMcpServer() .WithTools(); ``` -### Setup Requirements +### Setup requirements To use authorization features, you must configure authentication and authorization in your ASP.NET Core application and call `AddAuthorizationFilters()`: @@ -545,13 +545,13 @@ builder.Services.AddAuthorization(); builder.Services.AddMcpServer() .WithHttpTransport() - .AddAuthorizationFilters() // Required for authorization support + .AddAuthorizationFilters() // Required for authorization support. .WithTools() .WithRequestFilters(requestFilters => { requestFilters.AddCallToolFilter(next => async (context, cancellationToken) => { - // Custom call tool logic + // Custom call tool logic. return await next(context, cancellationToken); }); }); @@ -562,7 +562,7 @@ app.MapMcp(); app.Run(); ``` -### Custom Authorization Filters +### Custom authorization filters You can also create custom authorization filters using the filter methods: @@ -571,7 +571,7 @@ You can also create custom authorization filters using the filter methods: { requestFilters.AddCallToolFilter(next => async (context, cancellationToken) => { - // Custom authorization logic + // Custom authorization logic. if (context.User?.Identity?.IsAuthenticated != true) { return new CallToolResult @@ -590,6 +590,6 @@ You can also create custom authorization filters using the filter methods: Within filters, you have access to: -- `context.User` - The current user's `ClaimsPrincipal`. +- `context.User` - The current user's . - `context.Services` - The request's service provider for resolving authorization services. - `context.MatchedPrimitive` - The matched tool/prompt/resource with its metadata including authorization attributes via `context.MatchedPrimitive.Metadata`. diff --git a/docs/concepts/getting-started.md b/docs/concepts/getting-started.md index 6e096d767..b062d7b23 100644 --- a/docs/concepts/getting-started.md +++ b/docs/concepts/getting-started.md @@ -5,7 +5,7 @@ description: Install the MCP C# SDK and build your first MCP client and server. uid: getting-started --- -## Getting Started +## Getting started This guide walks you through installing the MCP C# SDK and building a minimal MCP client and server. @@ -47,7 +47,7 @@ using System.ComponentModel; var builder = Host.CreateApplicationBuilder(args); builder.Logging.AddConsole(consoleLogOptions => { - // Configure all logs to go to stderr + // Configure all logs to go to stderr. consoleLogOptions.LogToStandardErrorThreshold = LogLevel.Trace; }); builder.Services @@ -129,7 +129,7 @@ var result = await client.CallToolAsync( new Dictionary() { ["message"] = "Hello MCP!" }, cancellationToken: CancellationToken.None); -// echo always returns one and only one text content object +// echo always returns one and only one text content object. Console.WriteLine(result.Content.OfType().First().Text); ``` @@ -137,7 +137,7 @@ Clients can connect to any MCP server, not just ones created with this library. #### Using tools with an LLM -`McpClientTool` inherits from `AIFunction`, so the tools returned by `ListToolsAsync` can be handed directly to any `IChatClient`: + inherits from , so the tools returned by `ListToolsAsync` can be handed directly to any : ```csharp // Get available tools. diff --git a/docs/concepts/httpcontext/httpcontext.md b/docs/concepts/httpcontext/httpcontext.md index 7fc408835..a0f730bf3 100644 --- a/docs/concepts/httpcontext/httpcontext.md +++ b/docs/concepts/httpcontext/httpcontext.md @@ -5,27 +5,23 @@ description: How to access the HttpContext in the MCP C# SDK. uid: httpcontext --- -## HTTP Context +## HTTP context -When using the Streamable HTTP transport, an MCP server might need to access the underlying [HttpContext] for a request. -The [HttpContext] object contains request metadata such as the HTTP headers, authorization context, and the actual path and query string for the request. +When using the Streamable HTTP transport, an MCP server might need to access the underlying for a request. +The object contains request metadata such as the HTTP headers, authorization context, and the actual path and query string for the request. -To access the [HttpContext], the MCP server should add the [IHttpContextAccessor] service to the application service collection (typically in Program.cs). -Then any classes, for example, a class containing MCP tools, should accept an [IHttpContextAccessor] in their constructor and store this for use by its methods. -Methods then use the [HttpContext property][IHttpContextAccessor.HttpContext] of the accessor to get the current context. +To access the , the MCP server should add the service to the application service collection (typically in Program.cs). +Then any classes, for example, a class containing MCP tools, should accept an in their constructor and store this for use by its methods. +Methods then use the property of the accessor to get the current context. -[HttpContext]: https://learn.microsoft.com/dotnet/api/microsoft.aspnetcore.http.httpcontext -[IHttpContextAccessor]: https://learn.microsoft.com/dotnet/api/microsoft.aspnetcore.http.ihttpcontextaccessor -[IHttpContextAccessor.HttpContext]: https://learn.microsoft.com/dotnet/api/microsoft.aspnetcore.http.ihttpcontextaccessor.httpcontext - -The following code snippet illustrates how to add the [IHttpContextAccessor] service to the application service collection: +The following code snippet illustrates how to add the service to the application service collection: [!code-csharp[](samples/Program.cs?name=snippet_AddHttpContextAccessor)] -Any class that needs access to the [HttpContext] can accept an [IHttpContextAccessor] in its constructor and store it for later use. -Methods of the class can then access the current [HttpContext] using the stored accessor. +Any class that needs access to the can accept an in its constructor and store it for later use. +Methods of the class can then access the current using the stored accessor. -The following code snippet shows the `ContextTools` class accepting an [IHttpContextAccessor] in its primary constructor -and the `GetHttpHeaders` method accessing the current [HttpContext] to retrieve the HTTP headers from the current request. +The following code snippet shows the `ContextTools` class accepting an in its primary constructor +and the `GetHttpHeaders` method accessing the current to retrieve the HTTP headers from the current request. [!code-csharp[](samples/Tools/ContextTools.cs?name=snippet_AccessHttpContext)] diff --git a/docs/concepts/index.md b/docs/concepts/index.md index 85d94492f..240ff3169 100644 --- a/docs/concepts/index.md +++ b/docs/concepts/index.md @@ -4,11 +4,11 @@ Welcome to the conceptual documentation for the Model Context Protocol SDK. Here ## Contents -### [Getting Started](getting-started.md) +### [Getting started](getting-started.md) Install the SDK and build your first MCP client and server. -### Base Protocol +### Base protocol | Title | Description | | - | - | @@ -19,7 +19,7 @@ Install the SDK and build your first MCP client and server. | [Cancellation](cancellation/cancellation.md) | Learn how to cancel in-flight MCP requests using cancellation tokens and notifications. | | [Pagination](pagination/pagination.md) | Learn how to use cursor-based pagination when listing tools, prompts, and resources. | -### Client Features +### Client features | Title | Description | | - | - | @@ -27,7 +27,7 @@ Install the SDK and build your first MCP client and server. | [Roots](roots/roots.md) | Learn how clients provide filesystem roots to servers for context-aware operations. | | [Elicitation](elicitation/elicitation.md) | Learn how to request additional information from users during interactions. | -### Server Features +### Server features | Title | Description | | - | - | @@ -36,5 +36,5 @@ Install the SDK and build your first MCP client and server. | [Prompts](prompts/prompts.md) | Learn how to implement and consume reusable prompt templates with rich content types. | | [Completions](completions/completions.md) | Learn how to implement argument auto-completion for prompts and resource templates. | | [Logging](logging/logging.md) | Learn how to implement logging in MCP servers and how clients can consume log messages. | -| [HTTP Context](httpcontext/httpcontext.md) | Learn how to access the underlying `HttpContext` for a request. | +| [HTTP Context](httpcontext/httpcontext.md) | Learn how to access the underlying for a request. | | [MCP Server Handler Filters](filters.md) | Learn how to add filters to the handler pipeline. Filters let you wrap the original handler with additional functionality. | diff --git a/docs/concepts/logging/logging.md b/docs/concepts/logging/logging.md index f9a54d4aa..4c25acb75 100644 --- a/docs/concepts/logging/logging.md +++ b/docs/concepts/logging/logging.md @@ -11,13 +11,13 @@ MCP servers can expose log messages to clients through the [Logging utility]. [Logging utility]: https://modelcontextprotocol.io/specification/2025-11-25/server/utilities/logging -This document describes how to implement logging in MCP servers and how clients can consume log messages. +This article describes how to implement logging in MCP servers and how clients can consume log messages. -### Logging Levels +### Logging levels MCP uses the logging levels defined in [RFC 5424](https://datatracker.ietf.org/doc/html/rfc5424). -The MCP C# SDK uses the standard .NET [ILogger] and [ILoggerProvider] abstractions, which support a slightly +The MCP C# SDK uses the standard .NET and abstractions, which support a slightly different set of logging levels. The following table shows the levels and how they map to standard .NET logging levels. | Level | .NET | Description | Example Use Case | @@ -31,13 +31,10 @@ different set of logging levels. The following table shows the levels and how th | alert | | Action must be taken immediately | Data corruption detected | | emergency | | System is unusable | | -**Note:** .NET's [ILogger] also supports a `Trace` level (more verbose than Debug) log level. +**Note:** .NET's also supports a `Trace` level (more verbose than Debug) log level. As there is no equivalent level in the MCP logging levels, Trace level logs messages are silently dropped when sending messages to the client. -[ILogger]: https://learn.microsoft.com/dotnet/api/microsoft.extensions.logging.ilogger -[ILoggerProvider]: https://learn.microsoft.com/dotnet/api/microsoft.extensions.logging.iloggerprovider - ### Server configuration and logging MCP servers that implement the Logging utility must declare this in the capabilities sent in the @@ -54,8 +51,8 @@ server to perform any special logic it wants to perform when a client sets the l SDK already takes care of setting the in the , so most servers will not need to implement this. -MCP Servers using the MCP C# SDK can obtain an [ILoggerProvider](https://learn.microsoft.com/dotnet/api/microsoft.extensions.logging.iloggerprovider) from the method on , -and from that can create an [ILogger](https://learn.microsoft.com/dotnet/api/microsoft.extensions.logging.ilogger) instance for logging messages that should be sent to the MCP client. +MCP Servers using the MCP C# SDK can obtain an from the method on , +and from that can create an instance for logging messages that should be sent to the MCP client. [!code-csharp[](samples/server/Tools/LoggingTools.cs?name=snippet_LoggingConfiguration)] @@ -74,7 +71,7 @@ to send all log messages or none—this is not specified in the protocol. So sets a logging level to ensure it receives the desired log messages and only those messages. The `loggingLevel` set by the client is an MCP logging level. -See the [Logging Levels](#logging-levels) section above for the mapping between MCP and .NET logging levels. +See the [Logging levels](#logging-levels) section above for the mapping between MCP and .NET logging levels. [!code-csharp[](samples/client/Program.cs?name=snippet_LoggingLevel)] diff --git a/docs/concepts/pagination/pagination.md b/docs/concepts/pagination/pagination.md index 1249acbf5..aa7bfbad7 100644 --- a/docs/concepts/pagination/pagination.md +++ b/docs/concepts/pagination/pagination.md @@ -17,24 +17,24 @@ Instead of offset-based pagination (page 1, page 2, etc.), MCP uses opaque curso Two levels of API are provided for paginated operations: -1. **Convenience methods** (e.g., `ListToolsAsync()` returning `IList`) that automatically handle pagination and return all results. -2. **Raw methods** (e.g., `ListToolsAsync(ListToolsRequestParams)` returning the result type directly) that provide direct control over pagination. +1. **Convenience methods** (for example, `ListToolsAsync()` returning `IList`) that automatically handle pagination and return all results. +2. **Raw methods** (for example, `ListToolsAsync(ListToolsRequestParams)` returning the result type directly) that provide direct control over pagination. ### Automatic pagination The convenience methods on handle pagination automatically, fetching all pages and returning the complete list: ```csharp -// Fetches all tools, handling pagination automatically +// Fetches all tools, handling pagination automatically. IList allTools = await client.ListToolsAsync(); -// Fetches all resources, handling pagination automatically +// Fetches all resources, handling pagination automatically. IList allResources = await client.ListResourcesAsync(); -// Fetches all prompts, handling pagination automatically +// Fetches all prompts, handling pagination automatically. IList allPrompts = await client.ListPromptsAsync(); -// Fetches all resource templates, handling pagination automatically +// Fetches all resource templates, handling pagination automatically. IList allTemplates = await client.ListResourceTemplatesAsync(); ``` @@ -52,13 +52,13 @@ do Cursor = cursor }); - // Process this page of results + // Process this page of results. foreach (var tool in result.Tools) { Console.WriteLine($"{tool.Name}: {tool.Description}"); } - // Get the cursor for the next page (null when no more pages) + // Get the cursor for the next page (null when no more pages). cursor = result.NextCursor; } while (cursor is not null); @@ -66,7 +66,7 @@ do ### Pagination on the server -When implementing custom list handlers on the server, pagination is supported by examining the `Cursor` property of the request parameters and returning a `NextCursor` in the result: +When you implement custom list handlers on the server, to support pagination, examine the `Cursor` property of the request parameters and return a `NextCursor` in the result: ```csharp builder.Services.AddMcpServer() @@ -76,7 +76,7 @@ builder.Services.AddMcpServer() const int pageSize = 10; int startIndex = 0; - // Parse cursor to determine starting position + // Parse cursor to determine starting position. if (ctx.Params?.Cursor is { } cursor) { startIndex = int.Parse(cursor); diff --git a/docs/concepts/progress/progress.md b/docs/concepts/progress/progress.md index e259a224e..d9eaf9f50 100644 --- a/docs/concepts/progress/progress.md +++ b/docs/concepts/progress/progress.md @@ -15,7 +15,7 @@ Typically progress tracking is supported by server tools that perform operations However, progress tracking is defined in the MCP specification as a general feature that can be implemented for any request that's handled by either a server or a client. This project illustrates the common case of a server tool that performs a long-running operation and sends progress updates to the client. -### Server Implementation +### Server implementation When processing a request, the server can use the extension method of to send progress updates, specifying `"notifications/progress"` as the notification method name. @@ -27,7 +27,7 @@ The server must verify that the caller provided a `progressToken` in the request [!code-csharp[](samples/server/Tools/LongRunningTools.cs?name=snippet_SendProgress)] -### Client Implementation +### Client implementation Clients request progress updates by including a `progressToken` in the parameters of a request. Note that servers aren't required to support progress tracking, so clients should not depend on receiving progress updates. @@ -50,9 +50,9 @@ await using var handler = mcpClient.RegisterNotificationHandler(NotificationMeth }); ``` -The second way is to pass a [`Progress`](https://learn.microsoft.com/dotnet/api/system.progress-1) instance to the tool method. `Progress` is a standard .NET type that provides a way to receive progress updates. +The second way is to pass a [`Progress`](https://learn.microsoft.com/dotnet/api/system.progress-1) instance to the tool method. is a standard .NET type that provides a way to receive progress updates. For the purposes of MCP progress notifications, `T` should be . -The MCP C# SDK will automatically handle progress notifications and report them through the `Progress` instance. +The MCP C# SDK will automatically handle progress notifications and report them through the instance. This notification handler will only receive progress updates for the specific request that was made, rather than all progress notifications from the server. diff --git a/docs/concepts/prompts/prompts.md b/docs/concepts/prompts/prompts.md index 08ceaf9c0..52096a95a 100644 --- a/docs/concepts/prompts/prompts.md +++ b/docs/concepts/prompts/prompts.md @@ -11,19 +11,19 @@ MCP [prompts] allow servers to expose reusable prompt templates to clients. Prom [prompts]: https://modelcontextprotocol.io/specification/2025-11-25/server/prompts -This document covers implementing prompts on the server, consuming them from the client, rich content types, and change notifications. +This article covers implementing prompts on the server, consuming them from the client, rich content types, and change notifications. ### Defining prompts on the server Prompts can be defined in several ways: -- Using the attribute on methods within a class marked with -- Using factory methods from a delegate, `MethodInfo`, or `AIFunction` -- Deriving from or -- Implementing a custom via -- Implementing a low-level +- Using the attribute on methods within a class marked with . +- Using factory methods from a delegate, , or . +- Deriving from or . +- Implementing a custom via . +- Implementing a low-level . -The attribute-based approach is the most common and is shown throughout this document. Prompts can return `ChatMessage` instances for simple text/image content, or instances when protocol-specific content types like are needed. +The attribute-based approach is the most common and is shown throughout this article. Prompts can return instances for simple text/image content, or instances when protocol-specific content types like are needed. #### Simple prompts @@ -41,7 +41,7 @@ public class MyPrompts #### Prompts with arguments -Prompts can accept parameters to customize the generated messages. Use `[Description]` attributes to document each parameter. In addition to prompt arguments, methods can accept special parameter types that are resolved automatically: , `IProgress`, `ClaimsPrincipal`, and any service registered through dependency injection. +Prompts can accept parameters to customize the generated messages. Use `[Description]` attributes to article each parameter. In addition to prompt arguments, methods can accept special parameter types that are resolved automatically: , `IProgress`, , and any service registered through dependency injection. ```csharp [McpServerPromptType] @@ -70,11 +70,11 @@ builder.Services.AddMcpServer() ### Rich content in prompts -Prompt messages can contain more than just text. For text and image content, use `ChatMessage` from Microsoft.Extensions.AI. `DataContent` is automatically mapped to the appropriate MCP content block: image MIME types become , audio MIME types become , and all other MIME types become with binary resource contents. For text embedded resources specifically, use directly. +Prompt messages can contain more than just text. For text and image content, use from Microsoft.Extensions.AI. is automatically mapped to the appropriate MCP content block: image MIME types become , audio MIME types become , and all other MIME types become with binary resource contents. For text embedded resources specifically, use directly. #### Image content -Include images in prompts using `DataContent`: +Include images in prompts using : ```csharp [McpServerPrompt, Description("A prompt that includes an image for analysis")] @@ -95,20 +95,20 @@ public static IEnumerable AnalyzeImage( #### Embedded resources -For protocol-specific content types like , use instead of `ChatMessage`. `PromptMessage` has a `Role` property and a single `Content` property of type : +For protocol-specific content types like , use instead of . has a `Role` property and a single `Content` property of type : ```csharp -[McpServerPrompt, Description("A prompt that includes a document resource")] +[McpServerPrompt, Description("A prompt that includes a article resource")] public static IEnumerable ReviewDocument( - [Description("The document ID to review")] string documentId) + [Description("The article ID to review")] string documentId) { - string content = LoadDocument(documentId); // application logic to load by ID + string content = LoadDocument(documentId); // Application logic to load by ID. return [ new PromptMessage { Role = Role.User, - Content = new TextContentBlock { Text = "Please review the following document:" } + Content = new TextContentBlock { Text = "Please review the following article:" } }, new PromptMessage { @@ -153,7 +153,7 @@ foreach (var prompt in prompts) { Console.WriteLine($"{prompt.Name}: {prompt.Description}"); - // Show available arguments + // Show available arguments. if (prompt.ProtocolPrompt.Arguments is { Count: > 0 }) { foreach (var arg in prompt.ProtocolPrompt.Arguments) @@ -176,7 +176,7 @@ GetPromptResult result = await client.GetPromptAsync( ["code"] = "public static int Add(int a, int b) => a + b;" }); -// Process the returned messages (PromptMessage has a single Content block) +// Process the returned messages (PromptMessage has a single Content block). foreach (var message in result.Messages) { Console.WriteLine($"[{message.Role}]:"); @@ -202,7 +202,7 @@ Servers can dynamically add, remove, or modify prompts at runtime and notify con #### Sending notifications from the server ```csharp -// After adding or removing prompts dynamically +// After adding or removing prompts dynamically. await server.SendNotificationAsync( NotificationMethods.PromptListChangedNotification, new PromptListChangedNotificationParams()); diff --git a/docs/concepts/resources/resources.md b/docs/concepts/resources/resources.md index c7e713c51..3ae98c97c 100644 --- a/docs/concepts/resources/resources.md +++ b/docs/concepts/resources/resources.md @@ -11,19 +11,19 @@ MCP [resources] allow servers to expose data and content to clients. Resources r [resources]: https://modelcontextprotocol.io/specification/2025-11-25/server/resources -This document covers implementing resources on the server, consuming them from the client, resource templates, subscriptions, and change notifications. +This article covers implementing resources on the server, consuming them from the client, resource templates, subscriptions, and change notifications. ### Defining resources on the server -Resources can be defined in several ways: +You can define resources in several ways: -- Using the attribute on methods within a class marked with -- Using factory methods from a delegate, `MethodInfo`, or `AIFunction` -- Deriving from or -- Implementing a custom via -- Implementing a low-level +- Using the attribute on methods within a class marked with . +- Using factory methods from a delegate, , or . +- Deriving from or . +- Implementing a custom via . +- Implementing a low-level . -The attribute-based approach is the most common and is shown throughout this document. +The attribute-based approach is the most common and is shown throughout this article. #### Direct resources @@ -53,7 +53,7 @@ public class DocumentResources [Description("Returns an article by its ID")] public static ResourceContents GetArticle(string id) { - string? content = LoadArticle(id); // application logic to load by ID + string? content = LoadArticle(id); // Application logic to load by ID. if (content is null) { @@ -118,7 +118,7 @@ Clients can discover and read resources using resources = await client.ListResourcesAsync(); foreach (var resource in resources) @@ -132,7 +132,7 @@ foreach (var resource in resources) #### Listing resource templates ```csharp -// List resource templates (parameterized URIs) +// List resource templates (parameterized URIs). IList templates = await client.ListResourceTemplatesAsync(); foreach (var template in templates) @@ -144,7 +144,7 @@ foreach (var template in templates) #### Reading a resource ```csharp -// Read a direct resource by URI +// Read a direct resource by URI. ReadResourceResult result = await client.ReadResourceAsync("config://app/settings"); foreach (var content in result.Contents) @@ -163,7 +163,7 @@ foreach (var content in result.Contents) #### Reading a template resource ```csharp -// Read a resource using a URI template with parameter values +// Read a resource using a URI template with parameter values. ReadResourceResult result = await client.ReadResourceAsync( "file:///{path}", new Dictionary { ["path"] = "docs/readme.md" }); @@ -176,29 +176,29 @@ Clients can subscribe to resource updates to be notified when a resource's conte #### Subscribing on the client ```csharp -// Subscribe with an inline handler +// Subscribe with an inline handler. IAsyncDisposable subscription = await client.SubscribeToResourceAsync( "config://app/settings", async (notification, cancellationToken) => { Console.WriteLine($"Resource updated: {notification.Uri}"); - // Re-read the resource to get updated content + // Re-read the resource to get updated content. var updated = await client.ReadResourceAsync(notification.Uri, cancellationToken: cancellationToken); // Process updated content... }); -// Later, unsubscribe by disposing +// Later, unsubscribe by disposing. await subscription.DisposeAsync(); ``` Clients can also subscribe and unsubscribe separately: ```csharp -// Subscribe without a handler (use a global notification handler instead) +// Subscribe without a handler (use a global notification handler instead). await client.SubscribeToResourceAsync("config://app/settings"); -// Unsubscribe when no longer interested +// Unsubscribe when no longer interested. await client.UnsubscribeFromResourceAsync("config://app/settings"); ``` @@ -214,7 +214,7 @@ builder.Services.AddMcpServer() { if (ctx.Params?.Uri is { } uri) { - // Track the subscription (e.g., in a concurrent dictionary) + // Track the subscription (for example, in a concurrent dictionary). subscriptions[ctx.Server.SessionId].TryAdd(uri, 0); } return new EmptyResult(); @@ -234,7 +234,7 @@ builder.Services.AddMcpServer() When a resource's content changes, the server notifies subscribed clients: ```csharp -// Notify that a specific resource was updated +// Notify that a specific resource was updated. await server.SendNotificationAsync( NotificationMethods.ResourceUpdatedNotification, new ResourceUpdatedNotificationParams { Uri = "config://app/settings" }); @@ -242,12 +242,12 @@ await server.SendNotificationAsync( ### Resource list change notifications -When the set of available resources changes (resources added or removed), the server notifies clients: +When the set of available resources changes (resources added or removed), the server notifies clients. #### Sending notifications from the server ```csharp -// After adding or removing resources dynamically +// After adding or removing resources dynamically. await server.SendNotificationAsync( NotificationMethods.ResourceListChangedNotification, new ResourceListChangedNotificationParams()); @@ -260,7 +260,7 @@ mcpClient.RegisterNotificationHandler( NotificationMethods.ResourceListChangedNotification, async (notification, cancellationToken) => { - // Refresh the resource list + // Refresh the resource list. var updatedResources = await mcpClient.ListResourcesAsync(cancellationToken: cancellationToken); Console.WriteLine($"Resource list updated. {updatedResources.Count} resources available."); }); diff --git a/docs/concepts/roots/roots.md b/docs/concepts/roots/roots.md index 94b330871..f60e26080 100644 --- a/docs/concepts/roots/roots.md +++ b/docs/concepts/roots/roots.md @@ -15,9 +15,9 @@ MCP [roots] allow clients to inform servers about the relevant locations in the Roots provide a mechanism for the client to tell the server which directories, projects, or repositories are relevant to the current session. A server might use roots to: -- Scope file searches to the user's project directories -- Understand which repositories are being worked on -- Limit operations to specific filesystem boundaries +- Scope file searches to the user's project directories. +- Understand which repositories are being worked on. +- Limit operations to specific filesystem boundaries. Each root is represented by a with a URI and an optional human-readable name. @@ -98,7 +98,7 @@ server.RegisterNotificationHandler( NotificationMethods.RootsListChangedNotification, async (notification, cancellationToken) => { - // Re-request the roots list to get the updated set + // Re-request the roots list to get the updated set. var result = await server.RequestRootsAsync(new ListRootsRequestParams(), cancellationToken); Console.WriteLine($"Roots updated. {result.Roots.Count} roots available."); }); diff --git a/docs/concepts/sampling/sampling.md b/docs/concepts/sampling/sampling.md index 6ff7ec6fa..bc2c46b41 100644 --- a/docs/concepts/sampling/sampling.md +++ b/docs/concepts/sampling/sampling.md @@ -70,7 +70,7 @@ string response = result.Content.OfType().FirstOrDefault()?.Te Set when creating the client. This handler is called when a server sends a `sampling/createMessage` request. -#### Using an IChatClient +#### Use an IChatClient The simplest approach is to use with any implementation: @@ -116,4 +116,4 @@ McpClientOptions options = new() ### Capability negotiation -Sampling requires the client to advertise the `sampling` capability. This is handled automatically — when a is set, the client includes the sampling capability during initialization. The server can check whether the client supports sampling before calling ; if sampling is not supported, the method throws . +Sampling requires the client to advertise the `sampling` capability. This is handled automatically — when a is set, the client includes the sampling capability during initialization. The server can check whether the client supports sampling before calling ; if sampling isn't supported, the method throws . diff --git a/docs/concepts/tasks/tasks.md b/docs/concepts/tasks/tasks.md index 1947d210b..5671ec973 100644 --- a/docs/concepts/tasks/tasks.md +++ b/docs/concepts/tasks/tasks.md @@ -5,7 +5,7 @@ description: MCP Tasks for Long-Running Operations uid: tasks --- -# MCP Tasks +# MCP tasks > [!WARNING] @@ -19,11 +19,11 @@ The Model Context Protocol (MCP) supports [task-based execution] for long-runnin Tasks are useful when operations may take a long time to complete, such as: -- Large dataset processing or analysis -- Complex report generation -- Code migration or refactoring operations -- Machine learning inference or training -- Batch data transformations +- Large dataset processing or analysis. +- Complex report generation. +- Code migration or refactoring operations. +- Machine learning inference or training. +- Batch data transformations. Without tasks, clients must keep connections open for the entire duration of long-running operations. Tasks allow clients to: @@ -33,35 +33,35 @@ Without tasks, clients must keep connections open for the entire duration of lon 4. Retrieve results when complete 5. Cancel operations if needed -## Task Lifecycle +## Task lifecycle Tasks follow a defined lifecycle through these status values: | Status | Description | |--------|-------------| | `working` | Task is actively being processed | -| `input_required` | Task is waiting for additional input (e.g., elicitation) | +| `input_required` | Task is waiting for additional input (for example, elicitation) | | `completed` | Task finished successfully; results are available | | `failed` | Task encountered an error | | `cancelled` | Task was cancelled by the client | Tasks begin in the `working` status and transition to one of the terminal states (`completed`, `failed`, or `cancelled`). Once in a terminal state, the status cannot change. -## Server Implementation +## Server implementation -### Configuring Task Support +### Configuring task support To enable task support on a server, configure a task store when setting up the MCP server: ```csharp var builder = WebApplication.CreateBuilder(args); -// Create a task store for managing task state +// Create a task store for managing task state. var taskStore = new InMemoryMcpTaskStore(); builder.Services.AddMcpServer(options => { - // Enable tasks by providing a task store + // Enable tasks by providing a task store. options.TaskStore = taskStore; }) .WithHttpTransport() @@ -70,23 +70,23 @@ builder.Services.AddMcpServer(options => The is a reference implementation suitable for development and single-server deployments. For production multi-server scenarios, implement with a persistent backing store (database, Redis, etc.). -### Task Store Configuration +### Task store configuration -The `InMemoryMcpTaskStore` constructor accepts several optional parameters: +The constructor accepts several optional parameters: ```csharp var taskStore = new InMemoryMcpTaskStore( - defaultTtl: TimeSpan.FromHours(1), // Default task retention time - maxTtl: TimeSpan.FromHours(24), // Maximum allowed TTL - pollInterval: TimeSpan.FromSeconds(1), // Suggested client poll interval - cleanupInterval: TimeSpan.FromMinutes(5), // Background cleanup frequency - pageSize: 100, // Tasks per page for listing - maxTasks: 1000, // Maximum total tasks allowed - maxTasksPerSession: 100 // Maximum tasks per session + defaultTtl: TimeSpan.FromHours(1), // Default task retention time. + maxTtl: TimeSpan.FromHours(24), // Maximum allowed TTL. + pollInterval: TimeSpan.FromSeconds(1), // Suggested client poll interval. + cleanupInterval: TimeSpan.FromMinutes(5), // Background cleanup frequency. + pageSize: 100, // Tasks per page for listing. + maxTasks: 1000, // Maximum total tasks allowed. + maxTasksPerSession: 100 // Maximum tasks per session. ); ``` -### Tool Task Support +### Tool task support Tools automatically advertise task support when they return `Task`, `ValueTask`, `Task`, or `ValueTask`: @@ -95,18 +95,18 @@ Tools automatically advertise task support when they return `Task`, `ValueTask`, public class MyTools { // This tool automatically supports task-augmented calls - // because it returns Task (async method) + // because it returns Task (async method). [McpServerTool, Description("Processes a large dataset")] public static async Task ProcessDataset( int recordCount, CancellationToken cancellationToken) { - // Long-running operation + // Long-running operation. await Task.Delay(5000, cancellationToken); return $"Processed {recordCount} records"; } - // Synchronous tools don't support task augmentation by default + // Synchronous tools don't support task augmentation by default. [McpServerTool, Description("Quick operation")] public static string QuickOperation(string input) => $"Result: {input}"; } @@ -115,7 +115,7 @@ public class MyTools You can explicitly control task support using : ```csharp -// In Program.cs or configuration +// In Program.cs or configuration. builder.Services.AddMcpServer() .WithTools([ McpServerTool.Create( @@ -125,7 +125,7 @@ builder.Services.AddMcpServer() Name = "requiredTaskTool", Execution = new ToolExecution { - // Require clients to use task augmentation + // Require clients to use task augmentation. TaskSupport = ToolTaskSupport.Required } }) @@ -133,17 +133,17 @@ builder.Services.AddMcpServer() ``` Task support levels: -- `Forbidden` (default for sync methods): Tool cannot be called with task augmentation -- `Optional` (default for async methods): Tool can be called with or without task augmentation -- `Required`: Tool must be called with task augmentation +- `Forbidden` (default for sync methods): Tool cannot be called with task augmentation. +- `Optional` (default for async methods): Tool can be called with or without task augmentation. +- `Required`: Tool must be called with task augmentation. -### Explicit Task Creation with `IMcpTaskStore` +### Explicit task creation with `IMcpTaskStore` -For more control over task lifecycle, tools can directly interact with and return an `McpTask`. This approach allows you to: +For more control over task lifecycle, tools can directly interact with and return an . This approach allows you to: -- Create a task and return immediately while work continues in the background -- Control exactly when and how task status and results are updated -- Integrate with external systems for task execution +- Create a task and return immediately while work continues in the background. +- Control exactly when and how task status and results are updated. +- Integrate with external systems for task execution. Here's a simple example using `Task.Run` to schedule background work: @@ -158,7 +158,7 @@ public class MyTools(IMcpTaskStore taskStore) RequestContext context, CancellationToken cancellationToken) { - // Create a task in the store - this records the task metadata + // Create a task in the store - this records the task metadata. var task = await taskStore.CreateTaskAsync( new McpTaskMetadata { TimeToLive = TimeSpan.FromMinutes(30) }, context.JsonRpcRequest.Id!, @@ -166,16 +166,16 @@ public class MyTools(IMcpTaskStore taskStore) context.Server.SessionId, cancellationToken); - // Schedule work to run in the background (fire-and-forget) + // Schedule work to run in the background (fire-and-forget). _ = Task.Run(async () => { try { - // Simulate long-running work + // Simulate long-running work. await Task.Delay(TimeSpan.FromSeconds(10)); var result = $"Processed {itemCount} items successfully"; - // Store the completed result + // Store the completed result. await taskStore.StoreTaskResultAsync( task.TaskId, McpTaskStatus.Completed, @@ -187,7 +187,7 @@ public class MyTools(IMcpTaskStore taskStore) } catch (Exception ex) { - // Mark task as failed on error + // Mark task as failed on error. await taskStore.StoreTaskResultAsync( task.TaskId, McpTaskStatus.Failed, @@ -200,24 +200,24 @@ public class MyTools(IMcpTaskStore taskStore) } }, CancellationToken.None); - // Return immediately - client will poll for completion + // Return immediately - client will poll for completion. return task; } } ``` -When a tool returns `McpTask`, the SDK bypasses automatic task wrapping and returns the task directly to the client. +When a tool returns , the SDK bypasses automatic task wrapping and returns the task directly to the client. > [!IMPORTANT] -> **No Fault Tolerance Guarantees**: Both `InMemoryMcpTaskStore` and the automatic task support for `Task`-returning tool methods do **not** provide fault tolerance. Task state and execution are bounded by the memory of the server process. If the server crashes or restarts: +> **No Fault Tolerance Guarantees**: Both and the automatic task support for `Task`-returning tool methods do **not** provide fault tolerance. Task state and execution are bounded by the memory of the server process. If the server crashes or restarts: > - All in-memory task metadata is lost > - Any in-flight task execution is terminated > - Clients will receive errors when polling for previously created tasks > > For fault-tolerant task execution, see the [Fault-Tolerant Task Implementations](#fault-tolerant-task-implementations) section. -### Task Status Notifications +### Task status notifications When `SendTaskStatusNotifications` is enabled, the server automatically sends status updates to connected clients: @@ -225,15 +225,15 @@ When `SendTaskStatusNotifications` is enabled, the server automatically sends st builder.Services.AddMcpServer(options => { options.TaskStore = taskStore; - options.SendTaskStatusNotifications = true; // Enable notifications + options.SendTaskStatusNotifications = true; // Enable notifications. }); ``` Clients receive `notifications/tasks/status` messages when task status changes. -## Client Implementation +## Client implementation -### Calling Tools as Tasks +### Calling tools as tasks To execute a tool as a task, include the `Task` property in the request: @@ -243,7 +243,7 @@ using ModelContextProtocol.Protocol; var client = await McpClient.CreateAsync(transport); -// Call tool with task augmentation +// Call tool with task augmentation. var result = await client.CallToolAsync( new CallToolRequestParams { @@ -254,12 +254,12 @@ var result = await client.CallToolAsync( }, Task = new McpTaskMetadata { - TimeToLive = TimeSpan.FromHours(2) // Request 2-hour retention + TimeToLive = TimeSpan.FromHours(2) // Request 2-hour retention. } }, cancellationToken); -// Check if a task was created +// Check if a task was created. if (result.Task != null) { Console.WriteLine($"Task created: {result.Task.TaskId}"); @@ -267,7 +267,7 @@ if (result.Task != null) } ``` -### Polling for Task Status +### Polling for task status Use to check task status: @@ -282,24 +282,24 @@ if (task.StatusMessage != null) } ``` -### Waiting for Completion +### Waiting for completion The SDK provides helper methods for polling until a task completes: ```csharp -// Poll until task reaches terminal state +// Poll until task reaches terminal state. var completedTask = await client.PollTaskUntilCompleteAsync( taskId, cancellationToken: cancellationToken); if (completedTask.Status == McpTaskStatus.Completed) { - // Get the result as raw JSON + // Get the result as raw JSON. var resultJson = await client.GetTaskResultAsync( taskId, cancellationToken: cancellationToken); - // Deserialize to the expected type + // Deserialize to the expected type. var result = resultJson.Deserialize(McpJsonUtilities.DefaultOptions); foreach (var content in result?.Content ?? []) @@ -316,7 +316,7 @@ else if (completedTask.Status == McpTaskStatus.Failed) } ``` -### Listing Tasks +### Listing tasks List all tasks for the current session: @@ -329,7 +329,7 @@ foreach (var task in tasks) } ``` -### Cancelling Tasks +### Cancelling tasks Cancel a running task: @@ -338,10 +338,10 @@ var cancelledTask = await client.CancelTaskAsync( taskId, cancellationToken: cancellationToken); -Console.WriteLine($"Task status: {cancelledTask.Status}"); // Cancelled +Console.WriteLine($"Task status: {cancelledTask.Status}"); // Cancelled. ``` -### Handling Status Notifications +### Handling status notifications Register a handler to receive real-time status updates: @@ -365,7 +365,7 @@ var client = await McpClient.CreateAsync(transport, options); > [!NOTE] > Clients should not rely on receiving status notifications. Notifications are optional and may not be sent in all scenarios. Always use polling as the primary mechanism for tracking task status. -## Implementing a Custom Task Store +## Implementing a custom task store For production deployments, implement with a persistent backing store: @@ -392,7 +392,7 @@ public class DatabaseTaskStore : IMcpTaskStore TimeToLive = taskMetadata.TimeToLive ?? TimeSpan.FromHours(1) }; - // Store in database + // Store in database. await _db.ExecuteAsync( "INSERT INTO Tasks (TaskId, SessionId, Status, ...) VALUES (@TaskId, @SessionId, @Status, ...)", new { task.TaskId, sessionId, task.Status, ... }); @@ -405,7 +405,7 @@ public class DatabaseTaskStore : IMcpTaskStore string? sessionId, CancellationToken cancellationToken) { - // Retrieve from database with session isolation + // Retrieve from database with session isolation. return await _db.QuerySingleOrDefaultAsync( "SELECT * FROM Tasks WHERE TaskId = @TaskId AND SessionId = @SessionId", new { taskId, sessionId }); @@ -415,7 +415,7 @@ public class DatabaseTaskStore : IMcpTaskStore } ``` -### Task Store Best Practices +### Task store best practices 1. **Session Isolation**: Always filter tasks by session ID to prevent cross-session access 2. **TTL Enforcement**: Implement background cleanup of expired tasks @@ -423,7 +423,7 @@ public class DatabaseTaskStore : IMcpTaskStore 4. **Atomic Updates**: Use database transactions for status transitions 5. **Optimistic Concurrency**: Prevent lost updates with version checking or row locks -## Error Handling +## Error handling Task operations may throw with these error codes: @@ -446,23 +446,23 @@ catch (McpProtocolException ex) when (ex.ErrorCode == McpErrorCode.InvalidParams } ``` -## Complete Example +## Complete example See the [LongRunningTasks sample](https://github.com/modelcontextprotocol/csharp-sdk/tree/main/samples/LongRunningTasks) for a complete working example demonstrating: -- Server setup with a file-based `IMcpTaskStore` for durability -- Explicit task creation via `IMcpTaskStore` in tools returning `McpTask` -- Task polling and result retrieval across server restarts -- Cancellation support +- Server setup with a file-based for durability. +- Explicit task creation via in tools returning . +- Task polling and result retrieval across server restarts. +- Cancellation support. -## Fault-Tolerant Task Implementations +## Fault-tolerant task implementations -The default `InMemoryMcpTaskStore` and automatic task support for async tools are convenient for development, but they provide no durability or fault tolerance. When the server process terminates—whether due to a crash, deployment, or scaling event—all task state and in-flight computations are lost. +The default and automatic task support for async tools are convenient for development, but they provide no durability or fault tolerance. When the server process terminates—whether due to a crash, deployment, or scaling event—all task state and in-flight computations are lost. -### Why Fault Tolerance Requires External Systems +### Why fault tolerance requires external systems True fault tolerance for long-running tasks requires two key capabilities that cannot be provided by an in-process solution: @@ -470,9 +470,9 @@ True fault tolerance for long-running tasks requires two key capabilities that c 2. **Resumable Compute**: The actual work being performed must be executed by an external system that can continue running independently of the MCP server process—such as a job queue (Azure Service Bus, RabbitMQ), workflow engine (Temporal, Azure Durable Functions), or batch processing system (Azure Batch, Kubernetes Jobs). -### Explicit Task Creation with `IMcpTaskStore` +### Explicit task creation with `IMcpTaskStore` -To implement fault-tolerant tasks, tools can directly interact with `IMcpTaskStore` and return an `McpTask` instead of relying on automatic task wrapping. This approach gives you full control over task lifecycle and enables integration with external compute fabrics: +To implement fault-tolerant tasks, tools can directly interact with and return an instead of relying on automatic task wrapping. This approach gives you full control over task lifecycle and enables integration with external compute fabrics: ```csharp [McpServerToolType] @@ -485,7 +485,7 @@ public class FaultTolerantTools(IMcpTaskStore taskStore, IJobQueue jobQueue) RequestContext context, CancellationToken cancellationToken) { - // 1. Create a task in the durable store + // 1. Create a task in the durable store. var task = await taskStore.CreateTaskAsync( new McpTaskMetadata { TimeToLive = TimeSpan.FromHours(24) }, context.JsonRpcRequest.Id!, @@ -493,8 +493,8 @@ public class FaultTolerantTools(IMcpTaskStore taskStore, IJobQueue jobQueue) context.Server.SessionId, cancellationToken); - // 2. Submit work to an external compute fabric - // The job queue handles execution independently of this process + // 2. Submit work to an external compute fabric. + // The job queue handles execution independently of this process. await jobQueue.EnqueueAsync(new JobMessage { TaskId = task.TaskId, @@ -502,7 +502,7 @@ public class FaultTolerantTools(IMcpTaskStore taskStore, IJobQueue jobQueue) Input = jobInput }, cancellationToken); - // 3. Return the task immediately - client will poll for completion + // 3. Return the task immediately - client will poll for completion. return task; } } @@ -511,17 +511,17 @@ public class FaultTolerantTools(IMcpTaskStore taskStore, IJobQueue jobQueue) The external job processor updates the task store when work completes: ```csharp -// In a separate worker process or Azure Function +// In a separate worker process or Azure Function. public class JobProcessor(IMcpTaskStore taskStore) { public async Task ProcessJobAsync(JobMessage job, CancellationToken cancellationToken) { try { - // Perform the actual long-running work + // Perform the actual long-running work. var result = await DoExpensiveWorkAsync(job.Input, cancellationToken); - // Store the result in the durable task store + // Store the result in the durable task store. await taskStore.StoreTaskResultAsync( job.TaskId, McpTaskStatus.Completed, @@ -534,7 +534,7 @@ public class JobProcessor(IMcpTaskStore taskStore) } catch (Exception ex) { - // Mark task as failed + // Mark task as failed. await taskStore.StoreTaskResultAsync( job.TaskId, McpTaskStatus.Failed, @@ -550,7 +550,7 @@ public class JobProcessor(IMcpTaskStore taskStore) } ``` -### Simplified Example: File-Based Task Store +### Simplified example: file-based task store @@ -558,7 +558,7 @@ The [LongRunningTasks sample](https://github.com/modelcontextprotocol/csharp-sdk ```csharp -// Use a file-based task store for durability +// Use a file-based task store for durability. var taskStorePath = Path.Combine(Path.GetTempPath(), "mcp-tasks"); var taskStore = new FileBasedMcpTaskStore(taskStorePath); @@ -570,7 +570,7 @@ builder.Services.AddMcpServer(options => .WithTools(); ``` -The sample's tool returns an `McpTask` directly by calling `CreateTaskAsync`: +The sample's tool returns an directly by calling `CreateTaskAsync`: ```csharp [McpServerToolType] @@ -595,7 +595,7 @@ public class TaskTools(IMcpTaskStore taskStore) While this file-based approach demonstrates the pattern, production systems should use proper distributed storage and compute infrastructure for true fault tolerance and scalability. -## See Also +## See also - - diff --git a/docs/concepts/tools/tools.md b/docs/concepts/tools/tools.md index 6ac2f9a5e..a1ce4315e 100644 --- a/docs/concepts/tools/tools.md +++ b/docs/concepts/tools/tools.md @@ -11,19 +11,19 @@ MCP [tools] allow servers to expose callable functions to clients. Tools are the [tools]: https://modelcontextprotocol.io/specification/2025-11-25/server/tools -This document covers tool content types, change notifications, and schema generation. +This article covers tool content types, change notifications, and schema generation. ### Defining tools on the server -Tools can be defined in several ways: +You can define tools in several ways: -- Using the attribute on methods within a class marked with -- Using factory methods from a delegate, `MethodInfo`, or `AIFunction` -- Deriving from or -- Implementing a custom via -- Implementing a low-level +- Using the attribute on methods within a class marked with . +- Using factory methods from a delegate, , or . +- Deriving from or . +- Implementing a custom via . +- Implementing a low-level . -The attribute-based approach is the most common and is shown throughout this document. Parameters are automatically deserialized from JSON and documented using `[Description]` attributes. In addition to tool arguments, methods can accept special parameter types that are resolved automatically: , `IProgress`, `ClaimsPrincipal`, and any service registered through dependency injection. +The attribute-based approach is the most common and is shown throughout this article. Parameters are automatically deserialized from JSON and documented using `[Description]` attributes. In addition to tool arguments, methods can accept special parameter types that are resolved automatically: , `IProgress`, , and any service registered through dependency injection. ```csharp [McpServerToolType] @@ -45,7 +45,11 @@ builder.Services.AddMcpServer() ### Content types -Tools can return various content types. The simplest is a `string`, which is automatically wrapped in a . For richer content, tools can return one or more instances. Tools can also return `DataContent` from Microsoft.Extensions.AI, which is automatically mapped to the appropriate MCP content block: image MIME types become , audio MIME types become , and all other MIME types become with binary resource contents. +Tools can return various content types. The simplest is a `string`, which is automatically wrapped in a . For richer content, tools can return one or more instances. Tools can also return , which is automatically mapped to the appropriate MCP content block: + +- Image MIME types become . +- Audio MIME types become . +- All other MIME types become with binary resource contents. #### Text content @@ -65,7 +69,7 @@ Use the factor [McpServerTool, Description("Returns a generated image")] public static ImageContentBlock GenerateImage() { - byte[] pngBytes = CreateImage(); // your image generation logic + byte[] pngBytes = CreateImage(); // Your image generation logic. return ImageContentBlock.FromBytes(pngBytes, "image/png"); } ``` @@ -79,7 +83,7 @@ The factory me [McpServerTool, Description("Returns a synthesized audio clip")] public static AudioContentBlock Synthesize(string text) { - byte[] wavBytes = TextToSpeech(text); // your audio synthesis logic + byte[] wavBytes = TextToSpeech(text); // Your audio synthesis logic. return AudioContentBlock.FromBytes(wavBytes, "audio/wav"); } ``` @@ -92,7 +96,7 @@ Return an to embed a The resource can contain either text or binary data through or : ```csharp -[McpServerTool, Description("Returns a document as an embedded resource")] +[McpServerTool, Description("Returns a article as an embedded resource")] public static EmbeddedResourceBlock GetDocument() { return new EmbeddedResourceBlock @@ -101,7 +105,7 @@ public static EmbeddedResourceBlock GetDocument() { Uri = "docs://readme", MimeType = "text/plain", - Text = "This is the document content." + Text = "This is the article content." } }; } @@ -113,7 +117,7 @@ For binary resources, use : ```csharp -// List available tools +// List available tools. IList tools = await client.ListToolsAsync(); foreach (var tool in tools) @@ -168,12 +172,12 @@ foreach (var tool in tools) Console.WriteLine($"{tool.Name}: {tool.Description}"); } -// Call a tool by finding it in the list +// Call a tool by finding it in the list. McpClientTool echoTool = tools.First(t => t.Name == "echo"); CallToolResult result = await echoTool.CallAsync( new Dictionary { ["message"] = "Hello!" }); -// Process the result content blocks +// Process the result content blocks. foreach (var content in result.Content) { switch (content) @@ -201,12 +205,12 @@ Tool errors in MCP are distinct from protocol errors. When a tool encounters an #### Automatic exception handling -When a tool method throws an exception, the server catches it and returns a `CallToolResult` with `IsError = true`, with the following exceptions: +When a tool method throws an exception, the server catches it and returns a with `IsError = true`, with the following exceptions: -- is re-thrown as a JSON-RPC error response (not a tool error result). -- `OperationCanceledException` is re-thrown when the cancellation token was triggered. +- is rethrown as a JSON-RPC error response (not a tool error result). +- is rethrown when the cancellation token was triggered. -For all other exceptions, the error is returned as a tool result. If the exception derives from (excluding `McpProtocolException`, which is re-thrown above), its message is included in the error text; otherwise, a generic message is returned to avoid leaking internal details. +For all other exceptions, the error is returned as a tool result. If the exception derives from (excluding , which is rethrown above), its message is included in the error text; otherwise, a generic message is returned to avoid leaking internal details. ```csharp [McpServerTool, Description("Divides two numbers")] @@ -225,7 +229,7 @@ public static double Divide(double a, double b) #### Protocol errors -Throw to signal a protocol-level error (e.g., invalid parameters or unknown tool). These exceptions propagate as JSON-RPC error responses rather than tool error results: +Throw to signal a protocol-level error (for example, invalid parameters or unknown tool). These exceptions propagate as JSON-RPC error responses rather than tool error results: ```csharp [McpServerTool, Description("Processes the input")] @@ -234,7 +238,7 @@ public static string Process(string input) if (string.IsNullOrEmpty(input)) { // Propagates as a JSON-RPC error with code -32602 (InvalidParams) - // and message "Missing required input" + // and message "Missing required input". throw new McpProtocolException("Missing required input", McpErrorCode.InvalidParams); } @@ -269,7 +273,7 @@ Servers can dynamically add, remove, or modify tools at runtime. When the tool l Inject and call the notification method after modifying the tool list: ```csharp -// After adding or removing tools dynamically +// After adding or removing tools dynamically. await server.SendNotificationAsync( NotificationMethods.ToolListChangedNotification, new ToolListChangedNotificationParams()); @@ -284,7 +288,7 @@ mcpClient.RegisterNotificationHandler( NotificationMethods.ToolListChangedNotification, async (notification, cancellationToken) => { - // Refresh the tool list + // Refresh the tool list. var updatedTools = await mcpClient.ListToolsAsync(cancellationToken: cancellationToken); Console.WriteLine($"Tool list updated. {updatedTools.Count} tools available."); }); @@ -296,13 +300,13 @@ Tool parameters are described using [JSON Schema 2020-12]. JSON schemas are auto [JSON Schema 2020-12]: https://json-schema.org/specification -| .NET Type | JSON Schema Type | -|-----------|-----------------| -| `string` | `string` | -| `int`, `long` | `integer` | -| `float`, `double` | `number` | -| `bool` | `boolean` | -| Complex types | `object` with `properties` | +| .NET Type | JSON Schema Type | +|-------------------|----------------------------| +| `string` | `string` | +| `int`, `long` | `integer` | +| `float`, `double` | `number` | +| `bool` | `boolean` | +| Complex types | `object` with `properties` | Use `[Description]` attributes on parameters to populate the `description` field in the generated schema. This helps LLMs understand what each parameter expects. @@ -312,6 +316,6 @@ public static string Search( [Description("The search query string")] string query, [Description("Maximum results to return (1-100)")] int maxResults = 10) { - // Schema will include descriptions and default value for maxResults + // Schema will include descriptions and default value for maxResults. } ``` diff --git a/docs/concepts/transports/transports.md b/docs/concepts/transports/transports.md index 55623d51a..374501cd5 100644 --- a/docs/concepts/transports/transports.md +++ b/docs/concepts/transports/transports.md @@ -13,7 +13,7 @@ MCP uses a [transport layer] to handle the communication between clients and ser ### stdio transport -The stdio transport communicates over standard input and output streams. It is best suited for local integrations, as the MCP server runs as a child process of the client. +The stdio transport communicates over standard input and output streams. It's best suited for local integrations, as the MCP server runs as a child process of the client. #### stdio client @@ -34,15 +34,15 @@ await using var client = await McpClient.CreateAsync(transport); Key properties: -| Property | Description | -|----------|-------------| -| `Command` | The executable to launch (required) | -| `Arguments` | Command-line arguments for the process | -| `WorkingDirectory` | Working directory for the server process | +| Property | Description | +|------------------------|----------------------------------------------------| +| `Command` | The executable to launch (required) | +| `Arguments` | Command-line arguments for the process | +| `WorkingDirectory` | Working directory for the server process | | `EnvironmentVariables` | Environment variables (merged with current; `null` values remove variables) | -| `ShutdownTimeout` | Graceful shutdown timeout (default: 5 seconds) | -| `StandardErrorLines` | Callback for stderr output from the server process | -| `Name` | Optional transport identifier for logging | +| `ShutdownTimeout` | Graceful shutdown timeout (default: 5 seconds) | +| `StandardErrorLines` | Callback for stderr output from the server process | +| `Name` | Optional transport identifier for logging | #### stdio server @@ -83,13 +83,13 @@ var transport = new HttpClientTransport(new HttpClientTransportOptions await using var client = await McpClient.CreateAsync(transport); ``` -The client also supports automatic transport detection with (the default), which tries Streamable HTTP first and falls back to SSE if the server does not support it: +The client also supports automatic transport detection with (the default), which tries Streamable HTTP first and falls back to SSE if the server doesn't support it: ```csharp var transport = new HttpClientTransport(new HttpClientTransportOptions { Endpoint = new Uri("https://my-mcp-server.example.com/mcp"), - // TransportMode defaults to AutoDetect + // TransportMode defaults to AutoDetect. }); ``` @@ -135,7 +135,7 @@ A custom route can be specified. For example, the [AspNetCoreMcpPerSessionTools] app.MapMcp("/mcp"); ``` -When using a custom route, Streamable HTTP clients should connect directly to that route (e.g., `https://host/mcp`), while SSE clients should connect to `{route}/sse` (e.g., `https://host/mcp/sse`). +When using a custom route, Streamable HTTP clients should connect directly to that route (for example, `https://host/mcp`), while SSE clients should connect to `{route}/sse` (for example, `https://host/mcp/sse`). ### SSE transport (legacy) @@ -165,9 +165,9 @@ await using var client = await McpClient.CreateAsync(transport); SSE-specific configuration options: -| Property | Description | -|----------|-------------| -| `MaxReconnectionAttempts` | Maximum number of reconnection attempts on stream disconnect (default: 5) | +| Property | Description | +|-------------------------------|-------------------------------------------------------------| +| `MaxReconnectionAttempts` | Maximum number of reconnection attempts on stream disconnect (default: 5) | | `DefaultReconnectionInterval` | Wait time between reconnection attempts (default: 1 second) | #### SSE server (ASP.NET Core) @@ -193,10 +193,10 @@ No additional configuration is needed. When a client connects using the SSE prot ### Transport mode comparison -| Feature | stdio | Streamable HTTP | SSE (Legacy) | -|---------|-------|----------------|--------------| -| Process model | Child process | Remote HTTP | Remote HTTP | -| Direction | Bidirectional | Bidirectional | Server→client stream + client→server POST | -| Session resumption | N/A | ✓ | ✗ | -| Authentication | Process-level | HTTP auth (OAuth, headers) | HTTP auth (OAuth, headers) | -| Best for | Local tools | Remote servers | Legacy compatibility | +| Feature | stdio | Streamable HTTP | SSE (Legacy) | +|--------------------|---------------|----------------------------|----------------------------| +| Process model | Child process | Remote HTTP | Remote HTTP | +| Direction | Bidirectional | Bidirectional | Server→client stream + client→server POST | +| Session resumption | N/A | ✓ | ✗ | +| Authentication | Process-level | HTTP auth (OAuth, headers) | HTTP auth (OAuth, headers) | +| Best for | Local tools | Remote servers | Legacy compatibility | diff --git a/docs/experimental.md b/docs/experimental.md index 1ad75a9b4..ffefbe3a6 100644 --- a/docs/experimental.md +++ b/docs/experimental.md @@ -9,7 +9,7 @@ The Model Context Protocol C# SDK uses the [`[Experimental]`](https://learn.micr ## Suppressing experimental diagnostics -When you use an experimental API, the compiler produces a diagnostic (e.g., `MCPEXP001`) to ensure you're aware the API may change. If you want to use the API, suppress the diagnostic in one of these ways: +When you use an experimental API, the compiler produces a diagnostic (for example, `MCPEXP001`) to ensure you're aware the API may change. If you want to use the API, suppress the diagnostic in one of these ways: ### Project-wide suppression @@ -39,14 +39,14 @@ Experimental properties on protocol types are fully serialized and deserialized The behavior of experimental properties differs depending on whether you use [reflection-based or source-generated](https://learn.microsoft.com/dotnet/standard/serialization/system-text-json/source-generation) serialization: -- **Reflection-based serialization** (the default when no `JsonSerializerContext` is used): Experimental properties are included. No special configuration is needed. -- **Source-generated serialization** (using a custom `JsonSerializerContext`): Experimental properties are **not** included in your context's serialization contract. This is by design, as it protects your compiled code against binary breaking changes to experimental APIs. +- **Reflection-based serialization** (the default when no is used): Experimental properties are included. No special configuration is needed. +- **Source-generated serialization** (using a custom ): Experimental properties are **not** included in your context's serialization contract. This is by design, as it protects your compiled code against binary breaking changes to experimental APIs. This means that switching between reflection-based and source-generated serialization can silently change which properties are serialized. To avoid this, source-generation users should configure a `TypeInfoResolverChain` as described below. ### Custom `JsonSerializerContext` -If you define your own `JsonSerializerContext` that includes MCP protocol types, configure a `TypeInfoResolverChain` so the SDK's resolver handles MCP types: +If you define your own that includes MCP protocol types, configure a `TypeInfoResolverChain` so the SDK's resolver handles MCP types: ```csharp using ModelContextProtocol; diff --git a/docs/list-of-diagnostics.md b/docs/list-of-diagnostics.md index 59666d518..572a55abd 100644 --- a/docs/list-of-diagnostics.md +++ b/docs/list-of-diagnostics.md @@ -1,8 +1,8 @@ -# List of Diagnostics Produced by MCP C# SDK +# List of diagnostics produced by MCP C# SDK -This document provides information about each of the diagnostics produced by the MCP C# SDK analyzers and source generators. +This article provides information about each of the diagnostics produced by the MCP C# SDK analyzers and source generators. -## Analyzer Diagnostics +## Analyzer diagnostics Analyzer diagnostic IDs are in the format `MCP###`. @@ -24,7 +24,7 @@ If you use experimental APIs, you will get one of the diagnostics shown below. T | Diagnostic ID | Description | | :------------ | :---------- | | `MCPEXP001` | Experimental APIs for features in the MCP specification itself, including Tasks and Extensions. Tasks provide a mechanism for asynchronous long-running operations that can be polled for status and results (see [MCP Tasks specification](https://modelcontextprotocol.io/specification/draft/basic/utilities/tasks)). Extensions provide a framework for extending the Model Context Protocol while maintaining interoperability (see [SEP-2133](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2133)). | -| `MCPEXP002` | Experimental SDK APIs unrelated to the MCP specification itself, including subclassing `McpClient`/`McpServer` (see [#1363](https://github.com/modelcontextprotocol/csharp-sdk/pull/1363)) and `RunSessionHandler`, which may be removed or change signatures in a future release (consider using `ConfigureSessionOptions` instead). | +| `MCPEXP002` | Experimental SDK APIs unrelated to the MCP specification itself, including subclassing / (see [#1363](https://github.com/modelcontextprotocol/csharp-sdk/pull/1363)) and `RunSessionHandler`, which may be removed or change signatures in a future release (consider using `ConfigureSessionOptions` instead). | ## Obsolete APIs @@ -35,4 +35,4 @@ When APIs are marked as obsolete, a diagnostic is emitted to warn users that the | Diagnostic ID | Status | Description | | :------------ | :----- | :---------- | | `MCP9001` | In place | The `EnumSchema` and `LegacyTitledEnumSchema` APIs are deprecated as of specification version 2025-11-25. Use the current schema APIs instead. | -| `MCP9002` | Removed | The `AddXxxFilter` extension methods on `IMcpServerBuilder` (e.g., `AddListToolsFilter`, `AddCallToolFilter`, `AddIncomingMessageFilter`) were superseded by `WithRequestFilters()` and `WithMessageFilters()`. | +| `MCP9002` | Removed | The `AddXxxFilter` extension methods on `IMcpServerBuilder` (for example, `AddListToolsFilter`, `AddCallToolFilter`, `AddIncomingMessageFilter`) were superseded by `WithRequestFilters()` and `WithMessageFilters()`. | diff --git a/docs/roadmap.md b/docs/roadmap.md index 81955a710..b5e9a5e38 100644 --- a/docs/roadmap.md +++ b/docs/roadmap.md @@ -4,17 +4,17 @@ author: jeffhandley description: ModelContextProtocol C# SDK roadmap and spec implementation tracking uid: roadmap --- -## Spec Implementation Tracking +## Spec implementation tracking The C# SDK tracks implementation of MCP spec components using the [modelcontextprotocol project boards](https://github.com/orgs/modelcontextprotocol/projects?query=is%3Aopen), with a dedicated project board for each spec revision. For example, see the [2025-11-25 spec revision board](https://github.com/orgs/modelcontextprotocol/projects/26). -## Current Focus Areas +## Current focus areas -### Next Spec Revision +### Next spec revision The next MCP specification revision is being developed in the [protocol repository](https://github.com/modelcontextprotocol/modelcontextprotocol). The C# SDK already has experimental support for [Tasks](concepts/tasks/tasks.md) (experimental in the specification), which will be updated as the specification is revised. -### Feedback and End-to-End Scenarios +### Feedback and end-to-end scenarios The C# SDK team is actively responding to feedback and continuing to explore end-to-end scenarios for opportunities to add more APIs that implement common patterns. From 41b5facb62159370a60c332a23d27455f1336bc6 Mon Sep 17 00:00:00 2001 From: Genevieve Warren <24882762+gewarren@users.noreply.github.com> Date: Thu, 19 Mar 2026 21:24:13 -0700 Subject: [PATCH 2/4] reverts --- docs/concepts/prompts/prompts.md | 6 +++--- docs/concepts/tools/tools.md | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/concepts/prompts/prompts.md b/docs/concepts/prompts/prompts.md index 52096a95a..bcc0ee5c2 100644 --- a/docs/concepts/prompts/prompts.md +++ b/docs/concepts/prompts/prompts.md @@ -98,9 +98,9 @@ public static IEnumerable AnalyzeImage( For protocol-specific content types like , use instead of . has a `Role` property and a single `Content` property of type : ```csharp -[McpServerPrompt, Description("A prompt that includes a article resource")] +[McpServerPrompt, Description("A prompt that includes a document resource")] public static IEnumerable ReviewDocument( - [Description("The article ID to review")] string documentId) + [Description("The document ID to review")] string documentId) { string content = LoadDocument(documentId); // Application logic to load by ID. return @@ -108,7 +108,7 @@ public static IEnumerable ReviewDocument( new PromptMessage { Role = Role.User, - Content = new TextContentBlock { Text = "Please review the following article:" } + Content = new TextContentBlock { Text = "Please review the following document:" } }, new PromptMessage { diff --git a/docs/concepts/tools/tools.md b/docs/concepts/tools/tools.md index a1ce4315e..230fb810c 100644 --- a/docs/concepts/tools/tools.md +++ b/docs/concepts/tools/tools.md @@ -96,7 +96,7 @@ Return an to embed a The resource can contain either text or binary data through or : ```csharp -[McpServerTool, Description("Returns a article as an embedded resource")] +[McpServerTool, Description("Returns a document as an embedded resource")] public static EmbeddedResourceBlock GetDocument() { return new EmbeddedResourceBlock @@ -105,7 +105,7 @@ public static EmbeddedResourceBlock GetDocument() { Uri = "docs://readme", MimeType = "text/plain", - Text = "This is the article content." + Text = "This is the document content." } }; } From 4dd705b7c040a26ea59a43e33e0ac8cf99a44f86 Mon Sep 17 00:00:00 2001 From: Genevieve Warren <24882762+gewarren@users.noreply.github.com> Date: Fri, 20 Mar 2026 08:53:14 -0700 Subject: [PATCH 3/4] Apply suggestion from @gewarren --- docs/concepts/capabilities/capabilities.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/concepts/capabilities/capabilities.md b/docs/concepts/capabilities/capabilities.md index 75c770dc7..bc2e79473 100644 --- a/docs/concepts/capabilities/capabilities.md +++ b/docs/concepts/capabilities/capabilities.md @@ -63,7 +63,7 @@ Server capabilities are automatically inferred from the configured features. For Before using an optional feature, check whether the other side declared the corresponding capability. -#### Check server capabilities from the client +#### Checking server capabilities from the client ```csharp await using var client = await McpClient.CreateAsync(transport); From 2bfeeaaa93e1ef85c6e356b25ce8ad7e42c25027 Mon Sep 17 00:00:00 2001 From: Genevieve Warren <24882762+gewarren@users.noreply.github.com> Date: Fri, 20 Mar 2026 18:22:09 -0700 Subject: [PATCH 4/4] remove invalid xrefs --- docs/concepts/cancellation/cancellation.md | 2 +- docs/concepts/completions/completions.md | 2 +- docs/concepts/httpcontext/httpcontext.md | 12 ++++++------ docs/concepts/progress/progress.md | 4 ++-- docs/concepts/tools/tools.md | 2 +- docs/experimental.md | 8 ++++---- 6 files changed, 15 insertions(+), 15 deletions(-) diff --git a/docs/concepts/cancellation/cancellation.md b/docs/concepts/cancellation/cancellation.md index d06980254..d21d25e3b 100644 --- a/docs/concepts/cancellation/cancellation.md +++ b/docs/concepts/cancellation/cancellation.md @@ -35,7 +35,7 @@ public static async Task LongComputation( } ``` -When the client sends a cancellation notification, the propagates back to the client as a cancellation response. +When the client sends a cancellation notification, the `OperationCanceledException` propagates back to the client as a cancellation response. ### Cancellation notification details diff --git a/docs/concepts/completions/completions.md b/docs/concepts/completions/completions.md index 4461758bd..349a53f91 100644 --- a/docs/concepts/completions/completions.md +++ b/docs/concepts/completions/completions.md @@ -83,7 +83,7 @@ builder.Services.AddMcpServer() ### Automatic completions with AllowedValuesAttribute -For parameters with a known set of valid values, you can use on `string` parameters of prompts or resource templates. The server will automatically surface those values as completions without needing a custom completion handler. +For parameters with a known set of valid values, you can use `AllowedValuesAttribute` on `string` parameters of prompts or resource templates. The server will automatically surface those values as completions without needing a custom completion handler. #### Prompt parameters diff --git a/docs/concepts/httpcontext/httpcontext.md b/docs/concepts/httpcontext/httpcontext.md index a0f730bf3..fca52d267 100644 --- a/docs/concepts/httpcontext/httpcontext.md +++ b/docs/concepts/httpcontext/httpcontext.md @@ -10,18 +10,18 @@ uid: httpcontext When using the Streamable HTTP transport, an MCP server might need to access the underlying for a request. The object contains request metadata such as the HTTP headers, authorization context, and the actual path and query string for the request. -To access the , the MCP server should add the service to the application service collection (typically in Program.cs). -Then any classes, for example, a class containing MCP tools, should accept an in their constructor and store this for use by its methods. -Methods then use the property of the accessor to get the current context. +To access the , the MCP server should add the `IHttpContextAccessor` service to the application service collection (typically in Program.cs). +Then any classes, for example, a class containing MCP tools, should accept an `IHttpContextAccessor` in their constructor and store this for use by its methods. +Methods then use the `HttpContext` property of the accessor to get the current context. -The following code snippet illustrates how to add the service to the application service collection: +The following code snippet illustrates how to add the `IHttpContextAccessor` service to the application service collection: [!code-csharp[](samples/Program.cs?name=snippet_AddHttpContextAccessor)] -Any class that needs access to the can accept an in its constructor and store it for later use. +Any class that needs access to the can accept an `IHttpContextAccessor` in its constructor and store it for later use. Methods of the class can then access the current using the stored accessor. -The following code snippet shows the `ContextTools` class accepting an in its primary constructor +The following code snippet shows the `ContextTools` class accepting an `IHttpContextAccessor` in its primary constructor and the `GetHttpHeaders` method accessing the current to retrieve the HTTP headers from the current request. [!code-csharp[](samples/Tools/ContextTools.cs?name=snippet_AccessHttpContext)] diff --git a/docs/concepts/progress/progress.md b/docs/concepts/progress/progress.md index d9eaf9f50..8e6256856 100644 --- a/docs/concepts/progress/progress.md +++ b/docs/concepts/progress/progress.md @@ -50,9 +50,9 @@ await using var handler = mcpClient.RegisterNotificationHandler(NotificationMeth }); ``` -The second way is to pass a [`Progress`](https://learn.microsoft.com/dotnet/api/system.progress-1) instance to the tool method. is a standard .NET type that provides a way to receive progress updates. +The second way is to pass a [`Progress`](https://learn.microsoft.com/dotnet/api/system.progress-1) instance to the tool method. `Progress\` is a standard .NET type that provides a way to receive progress updates. For the purposes of MCP progress notifications, `T` should be . -The MCP C# SDK will automatically handle progress notifications and report them through the instance. +The MCP C# SDK will automatically handle progress notifications and report them through the `Progress\` instance. This notification handler will only receive progress updates for the specific request that was made, rather than all progress notifications from the server. diff --git a/docs/concepts/tools/tools.md b/docs/concepts/tools/tools.md index 230fb810c..e7de04a4e 100644 --- a/docs/concepts/tools/tools.md +++ b/docs/concepts/tools/tools.md @@ -208,7 +208,7 @@ Tool errors in MCP are distinct from protocol errors. When a tool encounters an When a tool method throws an exception, the server catches it and returns a with `IsError = true`, with the following exceptions: - is rethrown as a JSON-RPC error response (not a tool error result). -- is rethrown when the cancellation token was triggered. +- `OperationCanceledException` is rethrown when the cancellation token was triggered. For all other exceptions, the error is returned as a tool result. If the exception derives from (excluding , which is rethrown above), its message is included in the error text; otherwise, a generic message is returned to avoid leaking internal details. diff --git a/docs/experimental.md b/docs/experimental.md index ffefbe3a6..aa4352411 100644 --- a/docs/experimental.md +++ b/docs/experimental.md @@ -39,14 +39,14 @@ Experimental properties on protocol types are fully serialized and deserialized The behavior of experimental properties differs depending on whether you use [reflection-based or source-generated](https://learn.microsoft.com/dotnet/standard/serialization/system-text-json/source-generation) serialization: -- **Reflection-based serialization** (the default when no is used): Experimental properties are included. No special configuration is needed. -- **Source-generated serialization** (using a custom ): Experimental properties are **not** included in your context's serialization contract. This is by design, as it protects your compiled code against binary breaking changes to experimental APIs. +- **Reflection-based serialization** (the default when no `JsonSerializerContext` is used): Experimental properties are included. No special configuration is needed. +- **Source-generated serialization** (using a custom `JsonSerializerContext`): Experimental properties are **not** included in your context's serialization contract. This is by design, as it protects your compiled code against binary breaking changes to experimental APIs. This means that switching between reflection-based and source-generated serialization can silently change which properties are serialized. To avoid this, source-generation users should configure a `TypeInfoResolverChain` as described below. -### Custom `JsonSerializerContext` +### Custom JsonSerializerContext -If you define your own that includes MCP protocol types, configure a `TypeInfoResolverChain` so the SDK's resolver handles MCP types: +If you define your own `JsonSerializerContext` that includes MCP protocol types, configure a `TypeInfoResolverChain` so the SDK's resolver handles MCP types: ```csharp using ModelContextProtocol;