Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
c04f198
Add sessions doc and prefer stateless mode in docs, samples, and erro…
halter73 Mar 26, 2026
e63ec21
Fix relative links and address PR review feedback
halter73 Mar 26, 2026
1a8bf45
Remove flaky stateless progress test
halter73 Mar 26, 2026
c9e8ea9
Add decision tree, backpressure warning, and normalize xrefs
halter73 Mar 26, 2026
7e703f3
Explicitly set Stateless in all WithHttpTransport calls
halter73 Mar 26, 2026
20c6816
Document DI service lifetimes and scoping behavior
halter73 Mar 26, 2026
ad14237
Reorganize sessions.md for better flow
halter73 Mar 26, 2026
2d14019
Fix idle session docs and clean up DI scope language
halter73 Mar 26, 2026
7f9aae0
Elevate backpressure warning to top-level security callout
halter73 Mar 26, 2026
b856d84
Refine client compatibility and activity tracking in sessions doc
halter73 Mar 26, 2026
040d26a
Tone down ScopeRequests=false guidance
halter73 Mar 26, 2026
26e3bb5
Soften sessions doc wording
halter73 Mar 26, 2026
d13e0f3
Fix ScopeRequests guidance for pre-scoped providers
halter73 Mar 26, 2026
083cf5b
Replace ScopeRequests sample with McpServer.Create example
halter73 Mar 26, 2026
5735987
Split DI scopes guidance for stdio vs custom transports
halter73 Mar 26, 2026
5737a99
Fix docfx xref namespaces for McpServer.Services and McpServer.Create
halter73 Mar 26, 2026
6dd7a5c
Update InMemoryTransport link to pinned lines
halter73 Mar 26, 2026
9e85bd7
Spruce up transports.md with per-mode comparison table
halter73 Mar 26, 2026
b9fe994
Add in-memory transport section and fix SSE comment wording
halter73 Mar 26, 2026
bed9aec
Fix SSE session ID mechanism in transport comparison table
halter73 Mar 26, 2026
e1625cc
Fix StreamClientTransport xref namespace
halter73 Mar 26, 2026
5f0051e
Apply suggestion from @halter73
halter73 Mar 26, 2026
841a3aa
Add legacy SSE transport coverage to sessions.md
halter73 Mar 26, 2026
04d825d
Add cancellation and disposal section to sessions.md
halter73 Mar 26, 2026
4c9c8fb
Reorder sessions.md: practical sections first, deep-dives last
halter73 Mar 26, 2026
b8f4034
Apply suggestion
halter73 Mar 26, 2026
93eecaa
Add tasks, cancellation, and observability coverage to sessions doc
halter73 Mar 26, 2026
4763a04
Apply suggestion
halter73 Mar 26, 2026
c8159bb
Refine backpressure warning with technical nuance
halter73 Mar 27, 2026
22b4dc1
Move backpressure analysis from top-level warning to detailed section
halter73 Mar 27, 2026
b5a47b5
Add SSE to request backpressure section
halter73 Mar 27, 2026
88bad8b
Disable legacy SSE endpoints by default (MCP9003)
halter73 Mar 27, 2026
62a11b9
Use generic HTTP server terminology instead of Kestrel-specific
halter73 Mar 27, 2026
4062b23
Add SSE backpressure remarks to doc comments
halter73 Mar 27, 2026
b7e68dc
Improve session docs, client behavior, and observability guidance
halter73 Mar 27, 2026
3f71fce
Clarify session intro: recommend stateless, spec-required client beha…
halter73 Mar 27, 2026
4f885d2
Reorganize sessions.md and use endpoint filter with Activity tag
halter73 Mar 27, 2026
61b7172
Add MCP003 analyzer: warn when WithHttpTransport doesn't set Stateless
halter73 Mar 27, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions docs/concepts/cancellation/cancellation.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ MCP supports [cancellation] of in-flight requests. Either side can cancel a prev
[cancellation]: https://modelcontextprotocol.io/specification/2025-11-25/basic/utilities/cancellation
[task cancellation]: https://learn.microsoft.com/dotnet/standard/parallel-programming/task-cancellation

> [!NOTE]
> The source and lifetime of the `CancellationToken` provided to server handlers depends on the transport and session mode. In [stateless mode](xref:sessions#stateless-mode-recommended), the token is tied to the HTTP request — if the client disconnects, the handler is cancelled. In [stateful mode](xref:sessions#stateful-mode-sessions), the token is tied to the session lifetime. See [Cancellation and disposal](xref:sessions#cancellation-and-disposal) for details.

### How cancellation maps to MCP notifications

When a `CancellationToken` passed to a client method (such as <xref:ModelContextProtocol.Client.McpClient.CallToolAsync*>) 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.
Expand Down
2 changes: 1 addition & 1 deletion docs/concepts/completions/completions.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ Register a completion handler when building the server. The handler receives a r

```csharp
builder.Services.AddMcpServer()
.WithHttpTransport()
.WithHttpTransport(o => o.Stateless = true)
.WithPrompts<MyPrompts>()
.WithResources<MyResources>()
.WithCompleteHandler(async (ctx, ct) =>
Expand Down
2 changes: 1 addition & 1 deletion docs/concepts/elicitation/elicitation.md
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,7 @@ Here's an example implementation of how a console application might handle elici

### 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 <xref: ModelContextProtocol.AspNetCore.HttpServerTransportOptions.Stateless> is enabled disabling server-to-client requests), the server may throw a <xref:ModelContextProtocol.UrlElicitationRequiredException>. 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:sessions) mode where server-to-client requests are disabled), the server may throw a <xref:ModelContextProtocol.UrlElicitationRequiredException>. 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

Expand Down
7 changes: 5 additions & 2 deletions docs/concepts/elicitation/samples/server/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,11 @@

builder.Services.AddMcpServer()
.WithHttpTransport(options =>
options.IdleTimeout = Timeout.InfiniteTimeSpan // Never timeout
)
{
// Elicitation requires stateful mode because it sends server-to-client requests.
// Set Stateless = false explicitly for forward compatibility in case the default changes.
options.Stateless = false;
})
.WithTools<InteractiveTools>();

builder.Logging.AddConsole(options =>
Expand Down
9 changes: 6 additions & 3 deletions docs/concepts/filters.md
Original file line number Diff line number Diff line change
Expand Up @@ -401,7 +401,7 @@ To enable authorization support, call `AddAuthorizationFilters()` when configuri

```csharp
services.AddMcpServer()
.WithHttpTransport()
.WithHttpTransport(o => o.Stateless = true)
.AddAuthorizationFilters() // Enable authorization filter support
.WithTools<WeatherTools>();
```
Expand Down Expand Up @@ -501,7 +501,7 @@ This allows you to implement logging, metrics, or other cross-cutting concerns t

```csharp
services.AddMcpServer()
.WithHttpTransport()
.WithHttpTransport(o => o.Stateless = true)
.WithRequestFilters(requestFilters =>
{
requestFilters.AddListToolsFilter(next => async (context, cancellationToken) =>
Expand Down Expand Up @@ -544,7 +544,10 @@ builder.Services.AddAuthentication("Bearer")
builder.Services.AddAuthorization();

builder.Services.AddMcpServer()
.WithHttpTransport()
.WithHttpTransport(options =>
{
options.Stateless = true;
})
.AddAuthorizationFilters() // Required for authorization support
.WithTools<WeatherTools>()
.WithRequestFilters(requestFilters =>
Expand Down
8 changes: 7 additions & 1 deletion docs/concepts/getting-started.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,13 @@ using System.ComponentModel;

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddMcpServer()
.WithHttpTransport()
.WithHttpTransport(options =>
{
// Stateless mode is recommended for servers that don't need
// server-to-client requests like sampling or elicitation.
// See the Sessions documentation for details.
options.Stateless = true;
})
.WithToolsFromAssembly();
var app = builder.Build();

Expand Down
13 changes: 13 additions & 0 deletions docs/concepts/httpcontext/httpcontext.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,16 @@ The following code snippet shows the `ContextTools` class accepting an [IHttpCon
and the `GetHttpHeaders` method accessing the current [HttpContext] to retrieve the HTTP headers from the current request.

[!code-csharp[](samples/Tools/ContextTools.cs?name=snippet_AccessHttpContext)]

### SSE transport and stale HttpContext

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.
- 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:sessions) mode, this is guaranteed since every request creates a fresh server context.

<!-- mlc-disable-next-line -->
> [!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.
5 changes: 4 additions & 1 deletion docs/concepts/httpcontext/samples/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@
// Add services to the container.

builder.Services.AddMcpServer()
.WithHttpTransport()
.WithHttpTransport(options =>
{
options.Stateless = true;
})
.WithTools<ContextTools>();

// <snippet_AddHttpContextAccessor>
Expand Down
1 change: 1 addition & 0 deletions docs/concepts/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,5 +36,6 @@ 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. |
| [Sessions](sessions/sessions.md) | Learn when to use stateless vs. stateful mode for HTTP servers and how to configure sessions. |
| [HTTP Context](httpcontext/httpcontext.md) | Learn how to access the underlying `HttpContext` 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. |
2 changes: 1 addition & 1 deletion docs/concepts/logging/logging.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ MCP servers that implement the Logging utility must declare this in the capabili
[Initialization]: https://modelcontextprotocol.io/specification/2025-11-25/basic/lifecycle#initialization

Servers built with the C# SDK always declare the logging capability. Doing so does not obligate the server
to send log messages&mdash;only allows it. Note that stateless MCP servers might not be capable of sending log
to send log messages&mdash;only allows it. Note that [stateless](xref:sessions) MCP servers might not be capable of sending log
messages as there might not be an open connection to the client on which the log messages could be sent.

The C# SDK provides an extension method <xref:Microsoft.Extensions.DependencyInjection.McpServerBuilderExtensions.WithSetLoggingLevelHandler*> on <xref:Microsoft.Extensions.DependencyInjection.IMcpServerBuilder> to allow the
Expand Down
7 changes: 5 additions & 2 deletions docs/concepts/logging/samples/server/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,11 @@

builder.Services.AddMcpServer()
.WithHttpTransport(options =>
options.IdleTimeout = Timeout.InfiniteTimeSpan // Never timeout
)
{
// Log streaming requires stateful mode because the server pushes log notifications
// to clients. Set Stateless = false explicitly for forward compatibility.
options.Stateless = false;
})
.WithTools<LoggingTools>();
// .WithSetLoggingLevelHandler(async (ctx, ct) => new EmptyResult());

Expand Down
2 changes: 1 addition & 1 deletion docs/concepts/pagination/pagination.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ When implementing custom list handlers on the server, pagination is supported by

```csharp
builder.Services.AddMcpServer()
.WithHttpTransport()
.WithHttpTransport(o => o.Stateless = true)
.WithListResourcesHandler(async (ctx, ct) =>
{
const int pageSize = 10;
Expand Down
3 changes: 3 additions & 0 deletions docs/concepts/progress/progress.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ 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.

> [!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:sessions) modes as well as stdio.
### Server Implementation

When processing a request, the server can use the <xref:ModelContextProtocol.McpSession.SendNotificationAsync*> extension method of <xref:ModelContextProtocol.Server.McpServer> to send progress updates,
Expand Down
5 changes: 4 additions & 1 deletion docs/concepts/progress/samples/server/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@
// Add services to the container.

builder.Services.AddMcpServer()
.WithHttpTransport()
.WithHttpTransport(options =>
{
options.Stateless = true;
})
.WithTools<LongRunningTools>();

builder.Logging.AddConsole(options =>
Expand Down
4 changes: 2 additions & 2 deletions docs/concepts/prompts/prompts.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ Register prompt types when building the server:

```csharp
builder.Services.AddMcpServer()
.WithHttpTransport()
.WithHttpTransport(o => o.Stateless = true)
.WithPrompts<MyPrompts>()
.WithPrompts<CodePrompts>();
```
Expand Down Expand Up @@ -197,7 +197,7 @@ foreach (var message in result.Messages)

### Prompt list change notifications

Servers can dynamically add, remove, or modify prompts at runtime and notify connected clients.
Servers can dynamically add, remove, or modify prompts at runtime and notify connected clients. These are unsolicited notifications, so they require [stateful mode or stdio](xref:sessions) — [stateless](xref:sessions#stateless-mode-recommended) servers cannot send unsolicited notifications.

#### Sending notifications from the server

Expand Down
6 changes: 4 additions & 2 deletions docs/concepts/resources/resources.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ Register resource types when building the server:

```csharp
builder.Services.AddMcpServer()
.WithHttpTransport()
.WithHttpTransport(o => o.Stateless = true)
.WithResources<MyResources>()
.WithResources<DocumentResources>();
```
Expand Down Expand Up @@ -208,7 +208,9 @@ Register subscription handlers when building the server:

```csharp
builder.Services.AddMcpServer()
.WithHttpTransport()
// Subscriptions require stateful mode because the server pushes change notifications
// to clients. Set Stateless = false explicitly for forward compatibility.
.WithHttpTransport(o => o.Stateless = false)
.WithResources<MyResources>()
.WithSubscribeToResourcesHandler(async (ctx, ct) =>
{
Expand Down
2 changes: 1 addition & 1 deletion docs/concepts/roots/roots.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ await using var client = await McpClient.CreateAsync(transport, options);

### Requesting roots from the server

Servers can request the client's root list using <xref:ModelContextProtocol.Server.McpServer.RequestRootsAsync*>:
Servers can request the client's root list using <xref:ModelContextProtocol.Server.McpServer.RequestRootsAsync*>. This is a server-to-client request, so it requires [stateful mode or stdio](xref:sessions) — it is not available in [stateless mode](xref:sessions#stateless-mode-recommended).

```csharp
[McpServerTool, Description("Lists the user's project roots")]
Expand Down
3 changes: 3 additions & 0 deletions docs/concepts/sampling/sampling.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ MCP [sampling] allows servers to request LLM completions from the client. This e

[sampling]: https://modelcontextprotocol.io/specification/2025-11-25/client/sampling

> [!NOTE]
> Sampling is a **server-to-client request** — the server sends a request back to the client over an open connection. This requires [stateful mode or stdio](xref:sessions). Sampling is not available in [stateless mode](xref:sessions#stateless-mode-recommended) because stateless servers cannot send requests to clients.

### How sampling works

1. The server calls <xref:ModelContextProtocol.Server.McpServer.SampleAsync*> (or uses the <xref:ModelContextProtocol.Server.McpServer.AsSamplingChatClient*> adapter) during tool execution.
Expand Down
Loading
Loading