diff --git a/docs/concepts/cancellation/cancellation.md b/docs/concepts/cancellation/cancellation.md index 618b2f08f..9dbab09c9 100644 --- a/docs/concepts/cancellation/cancellation.md +++ b/docs/concepts/cancellation/cancellation.md @@ -47,7 +47,7 @@ The cancellation notification includes: - **RequestId**: The ID of the request to cancel, allowing the receiver to correlate the cancellation with the correct in-flight request. - **Reason**: An optional human-readable reason for the cancellation. -Cancellation notifications can be observed by registering a handler. For broader interception of notifications and other messages, delegates can be added to the collection in . +Cancellation notifications can be observed by registering a handler. For broader interception of notifications and other messages, you can add delegates to the collection in . ```csharp mcpClient.RegisterNotificationHandler( diff --git a/docs/concepts/capabilities/capabilities.md b/docs/concepts/capabilities/capabilities.md index b466736a4..76cc37512 100644 --- a/docs/concepts/capabilities/capabilities.md +++ b/docs/concepts/capabilities/capabilities.md @@ -42,7 +42,7 @@ var options = new McpClientOptions await using var client = await McpClient.CreateAsync(transport, options); ``` -Handlers for each capability (roots, sampling, elicitation) are covered in their respective documentation pages. +Handlers for each capability (roots, sampling, and elicitation) are covered in their respective documentation pages. ### Server capabilities @@ -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. -#### Checking server capabilities from the client +#### Check server capabilities from the client ```csharp await using var client = await McpClient.CreateAsync(transport); diff --git a/docs/concepts/completions/completions.md b/docs/concepts/completions/completions.md index 7570d27ed..16a16952e 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, style options) +- **Resource template argument completions**: Suggest values for URI template parameters (for example, file paths, 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. @@ -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 `System.ComponentModel.DataAnnotations.AllowedValuesAttribute` on `string` parameters of prompts or resource templates. The server automatically surfaces those values as completions without needing a custom completion handler. #### Prompt parameters @@ -115,11 +115,11 @@ public class MyResources } ``` -With these attributes in place, when a client sends a `completion/complete` request for the `language` or `section` argument, the server will automatically filter and return matching values based on what the user has typed so far. This approach can be combined with a custom completion handler registered via `WithCompleteHandler`; the handler's results are returned first, followed by any matching `AllowedValues`. +With these attributes in place, when a client sends a `completion/complete` request for the `language` or `section` argument, the server automatically filters and returns matching values based on what the user has typed so far. This approach can be combined with a custom completion handler registered via `WithCompleteHandler`; the handler's results are returned first, followed by any matching `AllowedValues`. ### Requesting completions on the client -Clients request completions using . Provide a reference to the prompt or resource template, the argument name, and the current partial value: +Clients request completions using . Provide a reference to the prompt or resource template, the argument name, and the current partial value. #### Prompt argument completions @@ -142,7 +142,7 @@ if (result.Completion.HasMore == true) } ``` -#### Resource template argument completions +#### Resource-template argument completions ```csharp // Get completions for a resource template argument diff --git a/docs/concepts/elicitation/elicitation.md b/docs/concepts/elicitation/elicitation.md index 94597fa5f..2ecb0c8bf 100644 --- a/docs/concepts/elicitation/elicitation.md +++ b/docs/concepts/elicitation/elicitation.md @@ -7,22 +7,22 @@ uid: elicitation ## Elicitation -The **elicitation** feature allows servers to request additional information from users during interactions. This enables more dynamic and interactive AI experiences, making it easier to gather necessary context before executing tasks. +The **elicitation** feature allows servers to request additional information from users during interactions. This feature enables more dynamic and interactive AI experiences, making it easier to gather necessary context before executing tasks. The protocol supports two modes of elicitation: - **Form (In-Band)**: The server requests structured data (strings, numbers, Booleans, enums) which the client collects via a form interface and returns to the server. - **URL Mode**: The server provides a URL for the user to visit (for example, for OAuth, payments, or sensitive data entry). The interaction happens outside the MCP client. -### Server Support for Elicitation +### Server support for elicitation Servers request information from users with the 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. +For form-based elicitation, the MCP Server must specify the schema of each input value it's requesting from the user. Primitive types (string, number, Boolean) and enum types are supported for elicitation requests. The schema might include a description to help the user understand what's being requested. @@ -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. @@ -158,7 +158,7 @@ var options = new McpClientOptions }; ``` -The `ElicitationHandler` is an asynchronous method that will be called when the server requests additional information. The handler should check the `Mode` of the request: +The `ElicitationHandler` is an asynchronous method that's called when the server requests additional information. The handler should check the `Mode` of the request: - **Form Mode**: Present the form defined by `RequestedSchema` to the user. Return the user's input in the `Content` of the result. - **URL Mode**: Present the `Message` and `Url` to the user. Ask for consent to open the URL. If the user consents, open the URL and return `Action="accept"`. If the user declines, return `Action="decline"`. @@ -170,11 +170,11 @@ 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 [stateless](xref:stateless) mode where server-to-client requests are disabled), 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. +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 [stateless](xref:stateless) mode where server-to-client requests are disabled), the server might 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: @@ -212,14 +212,14 @@ public async Task AccessThirdPartyResource(McpServer server, Cancellatio 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: -1. Present each URL elicitation to the user (showing the URL and message) -2. Get user consent before opening each URL -3. Optionally wait for completion notifications from the server -4. Retry the original request after the user completes the out-of-band interactions +1. Present each URL elicitation to the user (showing the URL and message). +2. Get user consent before opening each URL. +3. Optionally wait for completion notifications from the server. +4. Retry the original request after the user completes the out-of-band interactions. ```csharp try @@ -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: @@ -284,6 +284,6 @@ await using var completionHandler = client.RegisterNotificationHandler( 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 896cea6ce..8c65f4f10 100644 --- a/docs/concepts/filters.md +++ b/docs/concepts/filters.md @@ -10,27 +10,29 @@ uid: 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 - -## Message Filters +| Method | Filters for... | +|-------------------------------------|----------------------------------| +| `AddListResourceTemplatesFilter` | List resource templates handlers | +| `AddListToolsFilter` | List tools handlers | +| `AddCallToolFilter` | Call tool handlers | +| `AddListPromptsFilter` | List prompts handlers | +| `AddGetPromptFilter` | Get prompt handlers | +| `AddListResourcesFilter` | List resources handlers | +| `AddReadResourceFilter` | Read resource handlers | +| `AddCompleteFilter` | Completion handlers | +| `AddSubscribeToResourcesFilter` | Resource subscription handlers | +| `AddUnsubscribeFromResourcesFilter` | Resource unsubscription handlers | +| `AddSetLoggingLevelFilter` | Logging level handlers | + +## 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(...)`: @@ -38,7 +40,7 @@ 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) -### 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: @@ -48,7 +50,7 @@ Message filters operate at a lower level than request-specific filters and are u - 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: @@ -73,7 +75,7 @@ services.AddMcpServer() .WithTools(); ``` -#### MessageContext Properties +#### MessageContext properties Inside an incoming message filter, you have access to: @@ -82,7 +84,7 @@ Inside an incoming message filter, you have access to: - `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: @@ -108,7 +110,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: @@ -137,7 +139,7 @@ services.AddMcpServer() .WithTools(); ``` -#### Skipping Outgoing Messages +#### Skipping outgoing messages You can suppress outgoing messages by not calling `next`: @@ -158,7 +160,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`: @@ -189,7 +191,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: @@ -235,7 +237,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: @@ -291,7 +293,7 @@ services.AddMcpServer() }); ``` -## Filter Execution Order +## Filter execution order ```csharp services.AddMcpServer() @@ -306,7 +308,9 @@ services.AddMcpServer() Execution flow: `filter1 -> filter2 -> filter3 -> baseHandler -> filter3 -> filter2 -> filter1` -## Common Use Cases +## Common use cases + +Filters are commonly used for [logging](#logging), [error handling](#error-handling), [performance monitoring](#performance-monitoring), and [caching](#caching). ### Logging @@ -325,7 +329,7 @@ Execution flow: `filter1 -> filter2 -> filter3 -> baseHandler -> filter3 -> filt }); ``` -### Error Handling +### Error handling ```csharp .WithRequestFilters(requestFilters => @@ -351,7 +355,7 @@ Execution flow: `filter1 -> filter2 -> filter3 -> baseHandler -> filter3 -> filt }); ``` -### Performance Monitoring +### Performance monitoring ```csharp .WithRequestFilters(requestFilters => @@ -391,11 +395,11 @@ 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: @@ -406,9 +410,9 @@ services.AddMcpServer() .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. +**Important**: If you want to use authorization attributes like `[Authorize]` on your MCP server tools, prompts, or resources, you should always call `AddAuthorizationFilters()` when using ASP.NET Core integration. -### Authorization Attributes Support +### Authorization attributes support The MCP server automatically respects the following authorization attributes: @@ -417,7 +421,7 @@ The MCP server automatically respects the following authorization attributes: - **`[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: @@ -447,7 +451,7 @@ public class WeatherTools } ``` -### Class-Level Authorization +### Class-level authorization You can apply authorization at the class level, which affects all tools in the class: @@ -471,19 +475,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. -### 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: @@ -531,7 +535,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()`: @@ -565,7 +569,7 @@ app.MapMcp(); app.Run(); ``` -### Custom Authorization Filters +### Custom authorization filters You can also create custom authorization filters using the filter methods: diff --git a/docs/concepts/getting-started.md b/docs/concepts/getting-started.md index c6096aa60..f935c15a8 100644 --- a/docs/concepts/getting-started.md +++ b/docs/concepts/getting-started.md @@ -14,7 +14,7 @@ This guide walks you through installing the MCP C# SDK and building a minimal MC The SDK ships as three NuGet packages. Pick the one that matches your scenario: | Package | Use when... | -| - | - | +|---------|-------------| | **[ModelContextProtocol.Core](https://www.nuget.org/packages/ModelContextProtocol.Core/absoluteLatest)** | You only need the client or low-level server APIs and want the **minimum set of dependencies**. | | **[ModelContextProtocol](https://www.nuget.org/packages/ModelContextProtocol/absoluteLatest)** | You're building a client or a **stdio-based** server and want hosting, dependency injection, and attribute-based tool/prompt/resource discovery. References `ModelContextProtocol.Core`. **This is the right starting point for most projects.** | | **[ModelContextProtocol.AspNetCore](https://www.nuget.org/packages/ModelContextProtocol.AspNetCore/absoluteLatest)** | You're building an **HTTP-based** MCP server hosted in ASP.NET Core. References `ModelContextProtocol`, so you get everything above plus the HTTP transport. | @@ -29,7 +29,7 @@ The SDK ships as three NuGet packages. Pick the one that matches your scenario: > [!TIP] > You can also use the [MCP Server project template](https://learn.microsoft.com/dotnet/ai/quickstarts/build-mcp-server) to quickly scaffold a new MCP server project. -Create a new console app, add the required packages, and replace `Program.cs` with the code below to get a working MCP server that exposes a single tool over stdio: +Create a new console app and add the required packages: ``` dotnet new console @@ -37,6 +37,8 @@ dotnet add package ModelContextProtocol dotnet add package Microsoft.Extensions.Hosting ``` +Replace `Program.cs` with the following code to get a working MCP server that exposes a single tool over stdio: + ```csharp using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; @@ -66,13 +68,15 @@ public static class EchoTool The call to `WithToolsFromAssembly` discovers every class marked with `[McpServerToolType]` in the assembly and registers every `[McpServerTool]` method as a tool. Prompts and resources work the same way with `[McpServerPromptType]` / `[McpServerPrompt]` and `[McpServerResourceType]` / `[McpServerResource]`. -For HTTP-based servers using ASP.NET Core: +For HTTP-based servers using ASP.NET Core, add the following packages: ``` dotnet new web dotnet add package ModelContextProtocol.AspNetCore ``` +And add the following code: + ```csharp using ModelContextProtocol.Server; using System.ComponentModel; @@ -109,7 +113,7 @@ For production servers, configure the exact public host names for the deployment #### Browser cross-origin access -**Only** enable CORS if you intentionally want browser-based cross-origin access to this server. +**Only** enable cross-origin requests (CORS) if you intentionally want browser-based cross-origin access to this server. CORS is not a substitute for host name validation. When browser-based cross-origin access is required, limit which browser origins can call the MCP endpoint by using the most restrictive ASP.NET Core CORS policy possible. See [Enable Cross-Origin Requests (CORS) in ASP.NET Core | Microsoft Learn](https://learn.microsoft.com/aspnet/core/security/cors). @@ -117,13 +121,15 @@ For the full HTTP security examples, including `AllowedHosts` and restrictive CO ### Building an MCP client -Create a new console app, add the package, and replace `Program.cs` with the code below. This client connects to the MCP "everything" reference server, lists its tools, and calls one: +Create a new console app and add the following package: ``` dotnet new console dotnet add package ModelContextProtocol ``` +Replace `Program.cs` with the following code. This client connects to the MCP "everything" reference server, lists its tools, and calls one: + ```csharp using ModelContextProtocol.Client; using ModelContextProtocol.Protocol; @@ -157,7 +163,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`: +`McpClientTool` inherits from [`AIFunction`](https://learn.microsoft.com/dotnet/api/microsoft.extensions.ai.aifunction), so the tools returned by `ListToolsAsync` can be given directly to any `IChatClient`: ```csharp // Get available tools. diff --git a/docs/concepts/httpcontext/httpcontext.md b/docs/concepts/httpcontext/httpcontext.md index 34ee4dbda..25a42280d 100644 --- a/docs/concepts/httpcontext/httpcontext.md +++ b/docs/concepts/httpcontext/httpcontext.md @@ -34,11 +34,11 @@ and the `GetHttpHeaders` method accessing the current [HttpContext] to retrieve When using the legacy SSE transport, be aware that the `HttpContext` returned by `IHttpContextAccessor` references the long-lived SSE connection request — not the individual `POST` request that triggered the tool call. This means: -- The `HttpContext.User` may contain stale claims if the client's token was refreshed after the SSE connection was established. +- The `HttpContext.User` might contain stale claims if the client's token was refreshed after the SSE connection was established. - Request headers, query strings, and other per-request metadata will reflect the initial SSE connection, not the current operation. The Streamable HTTP transport does not have this issue because each tool call is its own HTTP request, so `IHttpContextAccessor.HttpContext` always reflects the current request. In [stateless](xref:stateless) mode, this is guaranteed since every request creates a fresh server context. > [!NOTE] -> The server validates that the user identity has not changed between the session-initiating request and subsequent requests (using the `sub`, `NameIdentifier`, or `UPN` claim). If the user identity changes, the request is rejected with `403 Forbidden`. However, other claims (roles, permissions, custom claims) are not re-validated and may become stale over the lifetime of a session. +> The server validates that the user identity has not changed between the session-initiating request and subsequent requests (using the `sub`, `NameIdentifier`, or `UPN` claim). If the user identity changes, the request is rejected with `403 Forbidden`. However, other claims (roles, permissions, custom claims) are not re-validated and might become stale over the lifetime of a session. diff --git a/docs/concepts/identity/identity.md b/docs/concepts/identity/identity.md index 3e9756b36..313523b1f 100644 --- a/docs/concepts/identity/identity.md +++ b/docs/concepts/identity/identity.md @@ -9,7 +9,7 @@ uid: identity When building production MCP servers, you often need to know _who_ is calling a tool so you can enforce permissions, filter data, or audit access. The MCP C# SDK provides built-in support for propagating the caller's identity from the transport layer into your tool, prompt, and resource handlers — no custom headers or workarounds required. -## How Identity Flows Through the SDK +## How identity flows through the SDK When a client sends a request over an authenticated HTTP transport (Streamable HTTP or SSE), the ASP.NET Core authentication middleware populates `HttpContext.User` with a `ClaimsPrincipal`. The SDK's transport layer automatically copies this `ClaimsPrincipal` into `JsonRpcMessage.Context.User`, which then flows through message filters, request filters, and finally into the handler or tool method. @@ -24,7 +24,7 @@ HTTP Request (with auth token) This means you can access the authenticated user's identity at every stage of request processing. -## Direct `ClaimsPrincipal` Parameter Injection (Recommended) +## Direct `ClaimsPrincipal` parameter injection (recommended) The simplest and recommended approach is to declare a `ClaimsPrincipal` parameter on your tool method. The SDK automatically injects the authenticated user without including it in the tool's input schema: @@ -56,7 +56,7 @@ public class UserAwarePrompts } ``` -### Why This Works +### Why this works The SDK registers `ClaimsPrincipal` as one of the built-in services available during request processing. When a tool, prompt, or resource method declares a `ClaimsPrincipal` parameter, the SDK: @@ -64,9 +64,9 @@ The SDK registers `ClaimsPrincipal` as one of the built-in services available du 2. Automatically resolves it from the current request's `User` property at invocation time. 3. Passes `null` if no authenticated user is present (when the parameter is nullable). -This behavior is transport-agnostic. For HTTP transports, the `ClaimsPrincipal` comes from ASP.NET Core authentication. For other transports (like stdio), it will be `null` unless you set it explicitly via a message filter. +This behavior is transport-agnostic. For HTTP transports, the `ClaimsPrincipal` comes from ASP.NET Core authentication. For other transports (like stdio), it's `null` unless you set it explicitly via a message filter. -## Accessing Identity in Filters +## Accessing identity in filters Both message filters and request-specific filters expose the user via `context.User`: @@ -87,7 +87,7 @@ services.AddMcpServer() .WithTools(); ``` -## Role-Based Access with `[Authorize]` Attributes +## Role-based access with `[Authorize]` attributes For declarative authorization, you can use standard ASP.NET Core `[Authorize]` attributes on your tools, prompts, and resources. This requires calling `AddAuthorizationFilters()` during server configuration: @@ -132,9 +132,9 @@ When authorization fails, the SDK automatically: - **For list operations**: Removes unauthorized items from the results so users only see what they can access. - **For individual operations**: Returns a JSON-RPC error indicating access is forbidden. -See [Filters](xref:filters) for more details on authorization filters and their execution order. +For more details on authorization filters and their execution order, see [Filters](xref:filters). -## Using `IHttpContextAccessor` (HTTP-Only Alternative) +## Using `IHttpContextAccessor` (HTTP-only alternative) If you need access to the full `HttpContext` (not just the user), you can inject `IHttpContextAccessor` into your tool class. This gives you access to HTTP headers, query strings, and other request metadata: @@ -156,9 +156,9 @@ public class HttpContextTools(IHttpContextAccessor contextAccessor) > [!IMPORTANT] > `IHttpContextAccessor` only works with HTTP transports. For transport-agnostic identity access, use `ClaimsPrincipal` parameter injection instead. -See [HTTP Context](xref:httpcontext) for more details, including important caveats about stale `HttpContext` with the legacy SSE transport. +For more details, including important caveats about stale `HttpContext` with the legacy SSE transport, see [HTTP Context](xref:httpcontext). -## Transport Considerations +## Transport considerations | Transport | Identity Source | Notes | | --- | --- | --- | @@ -166,7 +166,7 @@ See [HTTP Context](xref:httpcontext) for more details, including important cavea | SSE | Same as Streamable HTTP, but the `HttpContext` is tied to the long-lived SSE connection. | The `ClaimsPrincipal` parameter injection still works correctly, but `IHttpContextAccessor` may return stale claims if the client's token was refreshed after the SSE connection was established. | | Stdio | No built-in authentication. `ClaimsPrincipal` is `null` unless set via a message filter. | For process-level identity, you can set the user in a message filter based on environment variables or other process-level context. | -### Setting Identity for Stdio Transport +### Setting identity for stdio transport For stdio-based servers where the caller's identity comes from the process environment rather than HTTP authentication, you can set the user in a message filter: @@ -188,7 +188,7 @@ services.AddMcpServer() .WithTools(); ``` -## Full Example: Protected HTTP Server +## Full example: protected HTTP server For a complete example of an MCP server with JWT authentication, OAuth resource metadata, and protected tools, see the [ProtectedMcpServer sample](https://github.com/modelcontextprotocol/csharp-sdk/tree/main/samples/ProtectedMcpServer). diff --git a/docs/concepts/index.md b/docs/concepts/index.md index 6393d9997..05a91cfb2 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 -Install the SDK and build your first MCP client and server. +To install the SDK and build your first MCP client and server, see [Getting started](getting-started.md). -### 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. | | [Tasks](tasks/tasks.md) | Learn how to use task-based execution for long-running operations that can be polled for status and results. | -### 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 | | - | - | diff --git a/docs/concepts/logging/logging.md b/docs/concepts/logging/logging.md index aa78edab7..8d705cf88 100644 --- a/docs/concepts/logging/logging.md +++ b/docs/concepts/logging/logging.md @@ -13,23 +13,23 @@ MCP servers can expose log messages to clients through the [Logging utility]. This document 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 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 | -|-----------|------|-----------------------------------|------------------------------| -| debug | ✓ | Detailed debugging information | Function entry/exit points | -| info | ✓ | General informational messages | Operation progress updates | -| notice | | Normal but significant events | Configuration changes | -| warning | ✓ | Warning conditions | Deprecated feature usage | -| error | ✓ | Error conditions | Operation failures | -| critical | ✓ | Critical conditions | System component failures | -| alert | | Action must be taken immediately | Data corruption detected | -| emergency | | System is unusable | | +| Level | .NET | Description | Example use case | +|-------------|------|----------------------------------|----------------------------| +| `debug` | ✓ | Detailed debugging information | Function entry/exit points | +| `info` | ✓ | General informational messages | Operation progress updates | +| `notice` | | Normal but significant events | Configuration changes | +| `warning` | ✓ | Warning conditions | Deprecated feature usage | +| `error` | ✓ | Error conditions | Operation failures | +| `critical` | ✓ | Critical conditions | System component failures | +| `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. As there is no equivalent level in the MCP logging levels, Trace level logs messages are silently @@ -51,7 +51,7 @@ messages as there might not be an open connection to the client on which the log The C# SDK provides an extension method on to allow the server to perform any special logic it wants to perform when a client sets the logging level. However, the -SDK already takes care of setting the in the , so most servers will not need to +SDK already takes care of setting the in the , so most servers don't 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 , @@ -74,7 +74,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. +For the mapping between MCP and .NET logging levels, see the [Logging Levels](#logging-levels) section. [!code-csharp[](samples/client/Program.cs?name=snippet_LoggingLevel)] diff --git a/docs/concepts/pagination/pagination.md b/docs/concepts/pagination/pagination.md index 6440b7267..3276fcf3a 100644 --- a/docs/concepts/pagination/pagination.md +++ b/docs/concepts/pagination/pagination.md @@ -7,18 +7,18 @@ uid: pagination ## Pagination -MCP uses [cursor-based pagination] for all list operations that may return large result sets. +MCP uses [cursor-based pagination] for all list operations that can return large result sets. [cursor-based pagination]: https://modelcontextprotocol.io/specification/2025-11-25/server/utilities/pagination ### Overview -Instead of offset-based pagination (page 1, page 2, etc.), MCP uses opaque cursor tokens. Each paginated response may include a `NextCursor` value. If present, pass it in the next request to retrieve the next page of results. +Instead of offset-based pagination (page 1, page 2, etc.), MCP uses opaque cursor tokens. Each paginated response might include a `NextCursor` value. If present, pass it in the next request to retrieve the next page of results. 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 diff --git a/docs/concepts/progress/progress.md b/docs/concepts/progress/progress.md index fe896292a..b46bbdbd5 100644 --- a/docs/concepts/progress/progress.md +++ b/docs/concepts/progress/progress.md @@ -11,14 +11,14 @@ The Model Context Protocol (MCP) supports [progress tracking] for long-running o [progress tracking]: https://modelcontextprotocol.io/specification/2025-11-25/basic/utilities/progress -Typically progress tracking is supported by server tools that perform operations that take a significant amount of time to complete, such as image generation or complex calculations. +Typically, progress tracking is supported by server tools that perform operations that take a significant amount of time to complete, such as image generation or complex calculations. 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. > [!NOTE] > Progress notifications are sent inline as part of the response to a request — they are not unsolicited. Progress tracking works in both [stateless and stateful](xref:stateless) modes as well as stdio. -### 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. @@ -30,7 +30,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. @@ -55,7 +55,7 @@ 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. 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 automatically handles progress notifications and reports 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/prompts/prompts.md b/docs/concepts/prompts/prompts.md index 8b11e9162..062f02bdb 100644 --- a/docs/concepts/prompts/prompts.md +++ b/docs/concepts/prompts/prompts.md @@ -11,7 +11,7 @@ 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 document covers implementing prompts on the server, consuming prompts from the client, rich content types, and change notifications. ### Defining prompts on the server @@ -70,7 +70,7 @@ 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 [`ChatMessage`](https://learn.microsoft.com/dotnet/api/microsoft.extensions.ai.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. #### Image content diff --git a/docs/concepts/resources/resources.md b/docs/concepts/resources/resources.md index 959ad73f1..5784535af 100644 --- a/docs/concepts/resources/resources.md +++ b/docs/concepts/resources/resources.md @@ -11,7 +11,7 @@ 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 document covers implementing resources on the server, consuming resources from the client, resource templates, subscriptions, and change notifications. ### Defining resources on the server @@ -216,7 +216,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(); @@ -244,7 +244,7 @@ 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 diff --git a/docs/concepts/stateless/stateless.md b/docs/concepts/stateless/stateless.md index 2732c9dcd..7d32a4be8 100644 --- a/docs/concepts/stateless/stateless.md +++ b/docs/concepts/stateless/stateless.md @@ -28,11 +28,11 @@ When sessions are enabled (the current C# SDK default), the server creates and t ## Forward and backward compatibility -The `Stateless` property is the single most important setting for forward-proofing your MCP server. The current C# SDK default is `Stateless = false` (sessions enabled), but **we expect this default to change** once mechanisms like [MRTR](https://github.com/modelcontextprotocol/csharp-sdk/pull/1458) bring server-to-client interactions (sampling, elicitation, roots) to stateless mode. We recommend every server set `Stateless` explicitly rather than relying on the default: +The `Stateless` property lets you preserve your server's session behavior across SDK updates. The C# SDK default is `Stateless = false` (sessions enabled); set the property explicitly rather than relying on the default: -- **`Stateless = true`** — the best forward-compatible choice. Your server opts out of sessions entirely. No matter how the SDK default changes in the future, your behavior stays the same. If you don't need [unsolicited notifications](#how-streamable-http-delivers-messages), server-to-client requests, or session-scoped state, this is the setting to use today. +- **`Stateless = true`** — the best forward-compatible choice. Your server opts out of sessions entirely. Setting the property explicitly preserves that behavior if SDK defaults change. If you don't need [unsolicited notifications](#how-streamable-http-delivers-messages), server-to-client requests, or session-scoped state, this is the setting to use. -- **`Stateless = false`** — the right choice when your server depends on sessions for features like sampling, elicitation, roots, unsolicited notifications, or per-client isolation. Setting this explicitly protects your server from a future default change. The [MCP specification requires](https://modelcontextprotocol.io/specification/2025-11-25/basic/transports#streamable-http) that clients use sessions when a server's `initialize` response includes an `Mcp-Session-Id` header, so compliant clients will always honor your server's session. Once [MRTR](https://github.com/modelcontextprotocol/csharp-sdk/pull/1458) or a similar mechanism is available, you may be able to migrate server-to-client interactions to stateless mode and drop sessions entirely — but until then, explicit `Stateless = false` is the safe choice. See [Stateless alternatives for server-to-client interactions](#stateless-alternatives-for-server-to-client-interactions) for more on MRTR. +- **`Stateless = false`** — the right choice when your server depends on sessions for features like sampling, elicitation, roots, unsolicited notifications, or per-client isolation. Setting the property explicitly preserves stateful behavior if SDK defaults change. The [MCP specification requires](https://modelcontextprotocol.io/specification/2025-11-25/basic/transports#streamable-http) that clients use sessions when a server's `initialize` response includes an `Mcp-Session-Id` header, so compliant clients honor your server's session. > [!TIP] @@ -40,7 +40,7 @@ The `Stateless` property is the single most important setting for forward-proofi ### Migrating from legacy SSE -If your clients connect to a `/sse` endpoint (e.g., `https://my-server.example.com/sse`), they are using the [legacy SSE transport](#legacy-sse-transport) — regardless of any `Stateless` or session settings on the server. The `/sse` and `/message` endpoints are now **disabled by default** ( is `false` and marked `[Obsolete]` with diagnostic `MCP9004`). Upgrading the server SDK without updating clients will break SSE connections. +If your clients connect to a `/sse` endpoint (for example, `https://my-server.example.com/sse`), they are using the [legacy SSE transport](#legacy-sse-transport) — regardless of any `Stateless` or session settings on the server. The `/sse` and `/message` endpoints are now **disabled by default** ( is `false` and marked `[Obsolete]` with diagnostic `MCP9004`). Upgrading the server SDK without updating clients will break SSE connections. **Client-side migration.** Change the client `Endpoint` from the `/sse` path to the root MCP endpoint — the same URL your server passes to `MapMcp()`. For example: @@ -94,7 +94,7 @@ When - Ping — the server cannot ping the client to verify connectivity The proposed [MRTR mechanism](https://github.com/modelcontextprotocol/csharp-sdk/pull/1458) is designed to bring these capabilities to stateless mode, but it is not yet available. -- **[Unsolicited](#how-streamable-http-delivers-messages) server-to-client notifications** (e.g., resource update notifications, logging messages) are not supported. Every notification must be part of a direct response to a client POST request — see [How Streamable HTTP delivers messages](#how-streamable-http-delivers-messages) for why. +- **[Unsolicited](#how-streamable-http-delivers-messages) server-to-client notifications** (for example, resource update notifications, logging messages) are not supported. Every notification must be part of a direct response to a client POST request — to understand why, see [How Streamable HTTP delivers messages](#how-streamable-http-delivers-messages). - **No concurrent client isolation.** Every request is independent — the server cannot distinguish between two agents calling the same tool simultaneously, and there is no mechanism to maintain separate state per client. - **No state reset on reconnect.** Stateless servers have no concept of "the previous connection." There is no session to close and no fresh session to start. If your server holds any external state, you must manage cleanup through other means. - [Tasks](xref:tasks) **are supported** — the task store is shared across ephemeral server instances. However, task-augmented sampling and elicitation are disabled because they require server-to-client requests. @@ -130,7 +130,7 @@ When - Server-to-client requests (sampling, elicitation, roots) via an open HTTP response stream - [Unsolicited notifications](#how-streamable-http-delivers-messages) (resource updates, logging messages) via the GET stream - Resource subscriptions -- Session-scoped state (e.g., `RunSessionHandler`, state that persists across multiple requests within a session) +- Session-scoped state (for example, `RunSessionHandler`, state that persists across multiple requests within a session) ### When to use stateful mode @@ -207,7 +207,7 @@ The server tracks the last activity time for each Streamable HTTP session. Activ Streamable HTTP sessions that have no activity for the duration of (default: **2 hours**) are automatically closed. The idle timeout is checked in the background every 5 seconds. -A client can keep its session alive by maintaining any open HTTP request (e.g., a long-running POST with a streamed response or an open `GET` for unsolicited messages). Sessions with active requests are never considered idle. +A client can keep its session alive by maintaining any open HTTP request (for example, a long-running POST with a streamed response or an open `GET` for unsolicited messages). Sessions with active requests are never considered idle. When a session times out: @@ -344,7 +344,7 @@ Session resumption is useful when: When you dispose an `McpClient` (via `await using` or explicit `DisposeAsync`), the client sends an HTTP `DELETE` request to the session endpoint with the `Mcp-Session-Id` header. This tells the server to clean up the session immediately rather than waiting for the idle timeout. -The property (default: `true`) controls this behavior. Set it to `false` when you're creating a transport purely to bootstrap session information (e.g., reading capabilities) without intending to own the session's lifetime. +The property (default: `true`) controls this behavior. Set it to `false` when you're creating a transport purely to bootstrap session information (for example, reading capabilities) without intending to own the session's lifetime. ### Client transport options @@ -354,7 +354,7 @@ The following prop |----------|---------|-------------| | | `null` | Pre-existing session ID for use with . When set, the client includes this session ID immediately and starts listening for unsolicited messages. | | | `true` | Whether to send a DELETE request when the client is disposed. Set to `false` when you don't want disposal to terminate the server session. | -| | `null` | Custom headers included in all requests (e.g., for authentication). These are sent alongside the automatic `Mcp-Session-Id` header. | +| | `null` | Custom headers included in all requests (for example, for authentication). These are sent alongside the automatic `Mcp-Session-Id` header. | For transport-level options like reconnection intervals and transport mode, see [Transports](xref:transports). @@ -531,7 +531,7 @@ Every tool, prompt, and resource handler can receive a `CancellationToken`. The | **Stateless HTTP** | `HttpContext.RequestAborted` | Client disconnects, or ASP.NET Core shuts down. Identical to a standard minimal API or controller action. | | **Stateful Streamable HTTP** | Linked token: HTTP request + application shutdown + session disposal | Client disconnects, `ApplicationStopping` fires, or the session is terminated (idle timeout, DELETE, max idle count). | | **SSE (legacy)** | Linked token: GET request + application shutdown | Client disconnects the SSE stream, or `ApplicationStopping` fires. The entire session terminates with the GET stream. | -| **stdio** | Token passed to `McpServer.RunAsync()` | stdin EOF (client process exits), or the token is cancelled (e.g., host shutdown via Ctrl+C). | +| **stdio** | Token passed to `McpServer.RunAsync()` | stdin EOF (client process exits), or the token is cancelled (for example, host shutdown via Ctrl+C). | Stateless mode has the simplest cancellation story: the handler's `CancellationToken` is `HttpContext.RequestAborted` — the same token any ASP.NET Core endpoint receives. No additional tokens, linked sources, or session-level lifecycle to reason about. @@ -547,13 +547,13 @@ In stateful modes (Streamable HTTP, SSE, stdio), a client can cancel a specific When an `McpServer` is disposed — whether due to session termination, transport closure, or application shutdown — the SDK **awaits all in-flight handlers** before `DisposeAsync()` returns. This means: -- Handlers have an opportunity to complete cleanup (e.g., flushing writes, releasing locks) +- Handlers have an opportunity to complete cleanup (for example, flushing writes, releasing locks) - Scoped services created for the handler are disposed after the handler completes - The SDK logs each handler's completion at `Information` level, including elapsed time #### Graceful shutdown in ASP.NET Core -When `ApplicationStopping` fires (e.g., `SIGTERM`, `Ctrl+C`, `app.StopAsync()`), the SDK immediately cancels active SSE and GET streams so that connected clients don't block shutdown. In-flight POST request handlers continue running and are awaited before the server finishes disposing. The total shutdown time is bounded by ASP.NET Core's `HostOptions.ShutdownTimeout` (default: **30 seconds**). In practice, the SDK completes shutdown well within this limit. +When `ApplicationStopping` fires (for example, `SIGTERM`, `Ctrl+C`, `app.StopAsync()`), the SDK immediately cancels active SSE and GET streams so that connected clients don't block shutdown. In-flight POST request handlers continue running and are awaited before the server finishes disposing. The total shutdown time is bounded by ASP.NET Core's `HostOptions.ShutdownTimeout` (default: **30 seconds**). In practice, the SDK completes shutdown well within this limit. For stateless servers, shutdown is even simpler: each request is independent, so there are no long-lived sessions to drain — just standard ASP.NET Core request completion. diff --git a/docs/concepts/tasks/tasks.md b/docs/concepts/tasks/tasks.md index 91f86db1a..0753a515d 100644 --- a/docs/concepts/tasks/tasks.md +++ b/docs/concepts/tasks/tasks.md @@ -9,7 +9,7 @@ uid: tasks > [!WARNING] -> Tasks are an **experimental feature** in the MCP specification (version 2025-11-25). The API may change in future releases. See the [Experimental APIs](../../experimental.md) documentation for details on working with experimental APIs. +> Tasks are an **experimental feature** in the `2025-11-25` MCP specification revision. The APIs carry `MCPEXP001`, offer no compatibility guarantees, and can change without notice. See the [Experimental APIs](../../experimental.md) documentation for details on working with experimental APIs. The Model Context Protocol (MCP) supports [task-based execution] for long-running operations. Tasks enable a "call-now, fetch-later" pattern where clients can initiate operations that may take significant time to complete, then poll for status and retrieve results when ready. @@ -33,23 +33,23 @@ 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: @@ -70,7 +70,7 @@ 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: @@ -86,7 +86,7 @@ var taskStore = new InMemoryMcpTaskStore( ); ``` -### Tool Task Support +### Tool task support Tools automatically advertise task support when they return `Task`, `ValueTask`, `Task`, or `ValueTask`: @@ -137,7 +137,7 @@ Task support levels: - `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: @@ -217,7 +217,7 @@ When a tool returns `McpTask`, the SDK bypasses automatic task wrapping and retu > > 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: @@ -231,9 +231,9 @@ builder.Services.AddMcpServer(options => 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: @@ -267,7 +267,7 @@ if (result.Task != null) } ``` -### Polling for Task Status +### Polling for task status Use to check task status: @@ -282,7 +282,7 @@ if (task.StatusMessage != null) } ``` -### Waiting for Completion +### Waiting for completion The SDK provides helper methods for polling until a task completes: @@ -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: @@ -341,7 +341,7 @@ var cancelledTask = await client.CancelTaskAsync( 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: @@ -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,7 +446,7 @@ catch (McpProtocolException ex) when (ex.ErrorCode == McpErrorCode.InvalidParams } ``` -## Complete Example +## Complete example @@ -458,11 +458,11 @@ See the [LongRunningTasks sample](https://github.com/modelcontextprotocol/csharp - 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. -### 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,7 +470,7 @@ 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: @@ -550,7 +550,7 @@ public class JobProcessor(IMcpTaskStore taskStore) } ``` -### Simplified Example: File-Based Task Store +### Simplified example: file-based task store @@ -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 503307e66..0d4fa556c 100644 --- a/docs/concepts/tools/tools.md +++ b/docs/concepts/tools/tools.md @@ -45,7 +45,7 @@ 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 [`DataContent`](https://learn.microsoft.com/dotnet/api/microsoft.extensions.ai.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. #### Text content @@ -225,7 +225,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")] @@ -296,13 +296,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. diff --git a/docs/concepts/transports/transports.md b/docs/concepts/transports/transports.md index a3dda4ddf..eaacda709 100644 --- a/docs/concepts/transports/transports.md +++ b/docs/concepts/transports/transports.md @@ -7,7 +7,7 @@ uid: transports ## Transports -MCP uses a [transport layer] to handle the communication between clients and servers. Three transport mechanisms are supported: **stdio**, **Streamable HTTP**, and **SSE** (Server-Sent Events, legacy). +MCP uses a [transport layer] to handle the communication between clients and servers. Three transport mechanisms are supported: [**stdio**](#stdio-transport), [**Streamable HTTP**](#streamable-http-transport), and [**SSE**](#sse-transport-legacy) (Server-Sent Events, legacy). [transport layer]: https://modelcontextprotocol.io/specification/2025-11-25/basic/transports @@ -32,22 +32,22 @@ var transport = new StdioClientTransport(new StdioClientTransportOptions await using var client = await McpClient.CreateAsync(transport); ``` -Key properties: +The following table describes the key properties: -| 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 when inheriting; `null` values remove variables) | +| 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 when inheriting; `null` values remove variables) | | `InheritEnvironmentVariables` | Whether the server process inherits the current process's environment variables (default: `true`) | -| `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 | #### Environment variable inheritance -By default, the server process inherits **all** environment variables from the current process. This includes credentials, tokens, proxy settings, and internal configuration that may be sensitive or irrelevant to the server. When running third-party or untrusted MCP servers, consider disabling inheritance to prevent unintentional credential leakage: +By default, the server process inherits **all** environment variables from the current process. This includes credentials, tokens, proxy settings, and internal configuration that might be sensitive or irrelevant to the server. When running third-party or untrusted MCP servers, consider disabling inheritance to prevent unintentional credential leakage: ```csharp var transport = new StdioClientTransport(new StdioClientTransportOptions @@ -94,7 +94,7 @@ var transport = new StdioClientTransport(new StdioClientTransportOptions > [!WARNING] > **Security risk (inheriting):** Variables such as `AWS_SECRET_ACCESS_KEY`, `GITHUB_TOKEN`, `OPENAI_API_KEY`, and similar credentials present in the parent process automatically flow into the child process unless inheritance is disabled. This can unintentionally expose sensitive values to third-party or untrusted MCP servers. > -> **Compatibility risk (not inheriting):** Disabling inheritance can cause the child process to fail to start or behave incorrectly if it relies on variables provided by the OS or shell. `GetDefaultEnvironmentVariables()` covers the most common requirements — `PATH`, `HOME`, and standard system directories — so for most servers it is a safe starting point. For servers that need additional variables not in the default set (such as `DOTNET_ROOT`, `LD_LIBRARY_PATH`, `JAVA_HOME`, or proxy settings like `HTTP_PROXY`, `HTTPS_PROXY`, and `NO_PROXY`), add them on top as shown in the example above. +> **Compatibility risk (not inheriting):** Disabling inheritance can cause the child process to fail to start or behave incorrectly if it relies on variables provided by the OS or shell. `GetDefaultEnvironmentVariables()` covers the most common requirements — `PATH`, `HOME`, and standard system directories — so for most servers it's a safe starting point. For servers that need additional variables not in the default set (such as `DOTNET_ROOT`, `LD_LIBRARY_PATH`, `JAVA_HOME`, or proxy settings like `HTTP_PROXY`, `HTTPS_PROXY`, and `NO_PROXY`), add them on top as shown in the previous example. #### stdio server @@ -202,7 +202,7 @@ If you intentionally expose the server through another host name, such as a tunn #### Browser cross-origin access -**Only** enable CORS if you intentionally want browser-based cross-origin access to this server. +**Only** enable cross-origin requests (CORS) if you intentionally want browser-based cross-origin access to this server. CORS is not a substitute for host name validation. When browser-based cross-origin access is required, limit which browser origins can call the MCP endpoint by using the most restrictive ASP.NET Core CORS policy possible. See [Enable Cross-Origin Requests (CORS) in ASP.NET Core | Microsoft Learn](https://learn.microsoft.com/aspnet/core/security/cors). @@ -247,7 +247,7 @@ app.MapMcp("/mcp").RequireCors("McpBrowserClient"); In Streamable HTTP, client requests arrive as HTTP POST requests. The server holds each POST response body open as an SSE stream and writes the JSON-RPC response — plus any intermediate messages like progress notifications or server-to-client requests — back through it. This provides natural HTTP-level backpressure: each POST holds its connection until the handler completes. -In stateful mode, the client can also open a long-lived GET request to receive **unsolicited** messages — notifications or server-to-client requests that the server initiates outside any active request handler (e.g., resource-changed notifications from a background watcher). In stateless mode, the GET endpoint is not mapped, so every message must be part of a POST response. See [How Streamable HTTP delivers messages](xref:stateless#how-streamable-http-delivers-messages) for a detailed breakdown. +In stateful mode, the client can also open a long-lived GET request to receive **unsolicited** messages — notifications or server-to-client requests that the server initiates outside any active request handler (for example, resource-changed notifications from a background watcher). In stateless mode, the GET endpoint is not mapped, so every message must be part of a POST response. For a detailed breakdown, see [How Streamable HTTP delivers messages](xref:stateless#how-streamable-http-delivers-messages). A custom route can be specified. For example, the [AspNetCoreMcpPerSessionTools] sample uses a route parameter: @@ -257,7 +257,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 (when [legacy SSE is enabled](xref:stateless#legacy-sse-transport)) 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 (when [legacy SSE is enabled](xref:stateless#legacy-sse-transport)) should connect to `{route}/sse` (for example, `https://host/mcp/sse`). ### SSE transport (legacy) @@ -379,7 +379,7 @@ Console.WriteLine(await echo.InvokeAsync(new() { ["arg"] = "Hello World" })); Like [stdio](#stdio-transport), the in-memory transport is inherently single-session — there is no `Mcp-Session-Id` header, and server-to-client requests (sampling, elicitation, roots) work naturally over the bidirectional pipe. This makes it ideal for testing servers that depend on these features. See [Sessions](xref:stateless) for how session behavior varies across transports. -## Cross-Application Access +## Cross-application access The SDK provides built-in support for the [Identity Assertion Authorization Grant (ID-JAG) flow](https://github.com/modelcontextprotocol/ext-auth/blob/main/specification/draft/enterprise-managed-authorization.mdx) via `IdentityAssertionGrantProvider`. This enables non-interactive enterprise SSO scenarios where users authenticate once via their enterprise Identity Provider (IdP) and access MCP servers without per-server authorization prompts. @@ -416,4 +416,4 @@ var tokens = await provider.GetAccessTokenAsync( // Call provider.InvalidateCache() to force a fresh token exchange on the next call. ``` -The provider caches the resulting access token and reuses it until it expires. To force re-authentication (e.g. after a 401 response), call `provider.InvalidateCache()` before retrying. +The provider caches the resulting access token and reuses it until it expires. To force re-authentication (for example, after a 401 response), call `provider.InvalidateCache()` before retrying. diff --git a/docs/experimental.md b/docs/experimental.md index 1ad75a9b4..d7f337d6c 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 might change. If you want to use the API, suppress the diagnostic in one of these ways: ### Project-wide suppression diff --git a/docs/list-of-diagnostics.md b/docs/list-of-diagnostics.md index 0ea6d746f..a577ca182 100644 --- a/docs/list-of-diagnostics.md +++ b/docs/list-of-diagnostics.md @@ -2,14 +2,14 @@ This document 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###`. -| Diagnostic ID | Description | -| :------------ | :---------- | -| `MCP001` | Invalid XML documentation for MCP method | -| `MCP002` | MCP method must be partial to generate `[Description]` attributes | +| Diagnostic ID | Description | +|:--------------|:------------------------------------------------------------------| +| `MCP001` | Invalid XML documentation for MCP method | +| `MCP002` | MCP method must be partial to generate `[Description]` attributes | ## Experimental APIs @@ -19,7 +19,7 @@ As new functionality is introduced to this SDK, new in-development APIs are mark You may use experimental APIs in your application, but we advise against using these APIs in production scenarios as they may not be fully tested nor fully reliable. Additionally, we strongly recommend that library authors do not publish versions of their libraries that depend on experimental APIs as this will quite possibly lead to future breaking changes and diamond problems. -If you use experimental APIs, you will get one of the diagnostics shown below. The diagnostic is there to let you know you're using such an API so that you can avoid accidentally depending on experimental features. You may suppress these diagnostics if desired. +If you use experimental APIs, you will get one of the diagnostics shown below. The diagnostic is there to let you know you're using such an API so that you can avoid accidentally depending on experimental features. You can suppress these diagnostics if desired. | Diagnostic ID | Description | | :------------ | :---------- | @@ -34,7 +34,7 @@ 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()`. | +| `MCP9001` | In place | The `EnumSchema` and `LegacyTitledEnumSchema` APIs are deprecated as of specification version `2025-11-25`. Use the nondeprecated enum schema APIs instead. | +| `MCP9002` | Removed | The `AddXxxFilter` extension methods on `IMcpServerBuilder` (for example, `AddListToolsFilter`, `AddCallToolFilter`, `AddIncomingMessageFilter`) were superseded by `WithRequestFilters()` and `WithMessageFilters()`. | | `MCP9003` | In place | The `RequestContext(McpServer, JsonRpcRequest)` constructor is obsolete. Use the overload that accepts a `parameters` argument: `RequestContext(McpServer, JsonRpcRequest, TParams)`. | | `MCP9004` | In place | opts into the legacy SSE transport which has no built-in HTTP-level backpressure. Use Streamable HTTP instead. See [Stateless — Legacy SSE transport](xref:stateless#legacy-sse-transport) for details. | diff --git a/docs/roadmap.md b/docs/roadmap.md index 81955a710..d049bacd3 100644 --- a/docs/roadmap.md +++ b/docs/roadmap.md @@ -4,17 +4,15 @@ 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 +## MCP specification support -### Next Spec Revision +The 1.x SDK supports the `2024-11-05`, `2025-03-26`, `2025-06-18`, and `2025-11-25` MCP specification revisions. [Tasks](concepts/tasks/tasks.md) remain experimental in this SDK line. -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. diff --git a/docs/versioning.md b/docs/versioning.md index 68f147acb..be9f64b84 100644 --- a/docs/versioning.md +++ b/docs/versioning.md @@ -4,7 +4,7 @@ author: jeffhandley description: ModelContextProtocol C# SDK approach to versioning, breaking changes, and support uid: versioning --- -The ModelContextProtocol specification continues to evolve rapidly, and it's important for the C# SDK to remain current with specification additions and updates. To enable this, all NuGet packages that compose the SDK follow [Semantic Versioning 2.0.0](https://semver.org/spec/v2.0.0.html) with MAJOR.MINOR.PATCH version numbers, and optional prerelease versions. +All NuGet packages that compose the SDK follow [Semantic Versioning 2.0.0](https://semver.org/spec/v2.0.0.html) with MAJOR.MINOR.PATCH version numbers, and optional prerelease versions. Given a version number MAJOR.MINOR.PATCH, the package versions increment the: @@ -16,7 +16,7 @@ Given a version number MAJOR.MINOR.PATCH, the package versions increment the: ## Supported versions -Beginning with the 1.0.0 release, the following support policy will be applied for the official C# ModelContextProtocol SDK packages: +The following support policy applies to stable 1.x C# ModelContextProtocol SDK packages: 1. New functionality and additive APIs will be introduced in MINOR releases within the current MAJOR version only. * New functionality will not be added to an earlier MAJOR version. @@ -33,11 +33,15 @@ MAJOR or MINOR version updates might introduce or alter APIs annotated as [`[Exp Experimental APIs require suppression of diagnostic codes specific to the MCP SDK APIs, using an `MCP` prefix. -## Breaking changes +## MCP specification compatibility + +The 1.x SDK supports the `2024-11-05`, `2025-03-26`, `2025-06-18`, and [`2025-11-25`](https://modelcontextprotocol.io/specification/2025-11-25) MCP specification revisions. `2025-11-25` is used when the client or server does not configure a protocol revision. During initialization, clients and servers negotiate one of these revisions, and protocol behavior follows the negotiated revision. -Prior to the release of a stable 1.0.0 set of NuGet packages, the SDK remains in preview and breaking changes can be introduced without prior notice. Thereafter, the SDK follows Semantic Versioning and breaking changes against stable releases require increments to the MAJOR version. +The Tasks APIs in 1.x are experimental (`MCPEXP001`) and implement the Tasks feature described by the `2025-11-25` specification revision. They offer no compatibility guarantees and can change without notice. + +## Breaking changes -If feasible, the SDK will support all versions of the MCP spec. However, if breaking changes to the spec make this infeasible, preference will be given to the most recent version of the MCP spec. This would be considered a breaking change necessitating a new MAJOR version. +The 1.x SDK is stable. The SDK follows Semantic Versioning, and breaking changes against stable APIs require an increment to the MAJOR version. All releases are posted to https://github.com/modelcontextprotocol/csharp-sdk/releases with release notes. Issues and pull requests labeled with `breaking-change` are highlighted in the corresponding release notes.