Add HttpServerSessionMode for hybrid stateful/stateless HTTP serving (#1796)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Jeff Handley <jeffhandley@users.noreply.github.com>
This commit is contained in:
pardhu 2026-08-12 23:36:36 -07:00 • committed by GitHub
commit c6a97d94d6
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -168,13 +168,16 @@ await using var client = await McpClient.ResumeSessionAsync(transport, new Resum
Use the `ModelContextProtocol.AspNetCore` package to host an MCP server over HTTP. The <xref:Microsoft.AspNetCore.Builder.McpEndpointRouteBuilderExtensions.MapMcp*> method maps the Streamable HTTP endpoint at the specified route (root by default).
```csharp
using ModelContextProtocol.AspNetCore;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddMcpServer()
.WithHttpTransport(options =>
{
// Recommended for servers that don't need server-to-client requests.
options.Stateless = true;
// Stateless mode is the default and recommended for
// servers that don't need server-to-client requests.
options.SessionMode = HttpServerSessionMode.Stateless;
})
.WithTools<MyTools>();
@ -183,7 +186,7 @@ app.MapMcp();
app.Run();
```
By default, the HTTP transport runs **statelessly** — the server does not assign an `Mcp-Session-Id` or track transport session state in memory. This simplifies deployment, enables horizontal scaling without session affinity, and matches the `2026-07-28` Streamable HTTP wire format. Set `Stateless = false` explicitly when your server needs stateful sessions for unsolicited notifications, resource subscriptions, or per-client isolation. For a detailed guide on when to use stateless vs. stateful mode, configure session options, and understand [cancellation and disposal](xref:stateless#cancellation-and-disposal) behavior during shutdown, see [Stateless and Stateful](xref:stateless).
By default, the HTTP transport runs **statelessly** — the server does not assign an `Mcp-Session-Id` or track transport session state in memory. This simplifies deployment, enables horizontal scaling without session affinity, and matches the `2026-07-28` Streamable HTTP wire format. Set `SessionMode = HttpServerSessionMode.Stateful` explicitly when your server needs stateful sessions for unsolicited notifications, resource subscriptions, or per-client isolation. For a detailed guide on when to use stateless vs. stateful mode, configure session options, and understand [cancellation and disposal](xref:stateless#cancellation-and-disposal) behavior during shutdown, see [Stateless and Stateful](xref:stateless).
#### Host name validation
@ -297,20 +300,22 @@ SSE-specific configuration options:
#### SSE server (ASP.NET Core)
The ASP.NET Core integration supports SSE transport alongside Streamable HTTP. Legacy SSE endpoints (`/sse` and `/message`) are **disabled by default** and <xref:ModelContextProtocol.AspNetCore.HttpServerTransportOptions.EnableLegacySse> is marked `[Obsolete]` (diagnostic `MCP9004`). SSE always requires stateful mode; legacy SSE endpoints are never mapped when `Stateless = true`.
The ASP.NET Core integration supports SSE transport alongside Streamable HTTP. Legacy SSE endpoints (`/sse` and `/message`) are **disabled by default** and <xref:ModelContextProtocol.AspNetCore.HttpServerTransportOptions.EnableLegacySse> is marked `[Obsolete]` (diagnostic `MCP9004`). SSE always requires stateful mode; legacy SSE endpoints are never mapped when `SessionMode = HttpServerSessionMode.Stateless`.
**Why SSE is disabled by default.** The SSE transport separates request and response channels: clients `POST` JSON-RPC messages to `/message` and receive all responses through a long-lived `GET` SSE stream on `/sse`. Because the `POST` endpoint returns `202 Accepted` immediately — before the handler even runs — there is **no HTTP-level backpressure** on handler concurrency. A client (or attacker) can flood the server with tool calls without waiting for prior requests to complete. In contrast, Streamable HTTP holds each `POST` response open until the handler finishes, providing natural backpressure. For a detailed comparison and mitigations if you must use SSE, see [Request backpressure](xref:stateless#request-backpressure).
To enable legacy SSE, set `EnableLegacySse` to `true`:
```csharp
using ModelContextProtocol.AspNetCore;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddMcpServer()
.WithHttpTransport(options =>
{
// SSE requires stateful mode; opt in explicitly because stateless mode is the default.
options.Stateless = false;
options.SessionMode = HttpServerSessionMode.Stateful;
#pragma warning disable MCP9004 // EnableLegacySse is obsolete
// Enable legacy SSE endpoints for clients that don't support Streamable HTTP.