Skip to content

Commit 522c13b

Browse files
committed
Merge branch 'main' into halter73/stateless-docs
# Conflicts: # docs/list-of-diagnostics.md # src/Common/Obsoletions.cs
2 parents f76b456 + 49bf170 commit 522c13b

45 files changed

Lines changed: 800 additions & 284 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

docs/concepts/filters.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -317,7 +317,7 @@ Execution flow: `filter1 -> filter2 -> filter3 -> baseHandler -> filter3 -> filt
317317
{
318318
var logger = context.Services?.GetService<ILogger<Program>>();
319319

320-
logger?.LogInformation($"Processing request from {context.Params?.ProgressToken}");
320+
logger?.LogInformation($"Processing request from {context.Params.ProgressToken}");
321321
var result = await next(context, cancellationToken);
322322
logger?.LogInformation($"Returning {result.Tools?.Count ?? 0} tools");
323323
return result;
@@ -339,7 +339,7 @@ Execution flow: `filter1 -> filter2 -> filter3 -> baseHandler -> filter3 -> filt
339339
catch (Exception ex)
340340
{
341341
var logger = context.Services?.GetService<ILogger<Program>>();
342-
logger?.LogError(ex, "Error while processing CallTool request for {ProgressToken}", context.Params?.ProgressToken);
342+
logger?.LogError(ex, "Error while processing CallTool request for {ProgressToken}", context.Params.ProgressToken);
343343

344344
return new CallToolResult
345345
{

docs/concepts/logging/samples/server/Tools/LoggingTools.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ public static async Task<string> LoggingTool(
1313
int duration = 10,
1414
int steps = 10)
1515
{
16-
var progressToken = context.Params?.ProgressToken;
16+
var progressToken = context.Params.ProgressToken;
1717
var stepDuration = duration / steps;
1818

1919
// <snippet_LoggingConfiguration >

docs/concepts/pagination/pagination.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ builder.Services.AddMcpServer()
7777
int startIndex = 0;
7878

7979
// Parse cursor to determine starting position
80-
if (ctx.Params?.Cursor is { } cursor)
80+
if (ctx.Params.Cursor is { } cursor)
8181
{
8282
startIndex = int.Parse(cursor);
8383
}

docs/concepts/progress/samples/server/Tools/LongRunningTools.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ public static async Task<string> LongRunningTool(
1515
int duration = 10,
1616
int steps = 5)
1717
{
18-
var progressToken = context.Params?.ProgressToken;
18+
var progressToken = context.Params.ProgressToken;
1919
var stepDuration = duration / steps;
2020

2121
for (int i = 1; i <= steps; i++)

docs/concepts/resources/resources.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -214,7 +214,7 @@ builder.Services.AddMcpServer()
214214
.WithResources<MyResources>()
215215
.WithSubscribeToResourcesHandler(async (ctx, ct) =>
216216
{
217-
if (ctx.Params?.Uri is { } uri)
217+
if (ctx.Params.Uri is { } uri)
218218
{
219219
// Track the subscription (e.g., in a concurrent dictionary)
220220
subscriptions[ctx.Server.SessionId].TryAdd(uri, 0);
@@ -223,7 +223,7 @@ builder.Services.AddMcpServer()
223223
})
224224
.WithUnsubscribeFromResourcesHandler(async (ctx, ct) =>
225225
{
226-
if (ctx.Params?.Uri is { } uri)
226+
if (ctx.Params.Uri is { } uri)
227227
{
228228
subscriptions[ctx.Server.SessionId].TryRemove(uri, out _);
229229
}

docs/concepts/roots/roots.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ Roots provide a mechanism for the client to tell the server which directories, p
1717

1818
- Scope file searches to the user's project directories
1919
- Understand which repositories are being worked on
20-
- Limit operations to specific filesystem boundaries
20+
- Focus operations on relevant filesystem locations
2121

2222
Each root is represented by a <xref:ModelContextProtocol.Protocol.Root> with a URI and an optional human-readable name.
2323

docs/concepts/stateless/stateless.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ The `Stateless` property is the single most important setting for forward-proofi
4040
4141
### Migrating from legacy SSE
4242

43-
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** (<xref:ModelContextProtocol.AspNetCore.HttpServerTransportOptions.EnableLegacySse> is `false` and marked `[Obsolete]` with diagnostic `MCP9003`). Upgrading the server SDK without updating clients will break SSE connections.
43+
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** (<xref:ModelContextProtocol.AspNetCore.HttpServerTransportOptions.EnableLegacySse> is `false` and marked `[Obsolete]` with diagnostic `MCP9004`). Upgrading the server SDK without updating clients will break SSE connections.
4444

4545
**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:
4646

@@ -54,7 +54,7 @@ Endpoint = new Uri("https://my-server.example.com/")
5454

5555
With the default <xref:ModelContextProtocol.Client.HttpTransportMode.AutoDetect> transport mode, the client automatically tries Streamable HTTP first. You can also set `TransportMode = HttpTransportMode.StreamableHttp` explicitly if you know the server supports it.
5656

57-
**Server-side migration.** If you previously relied on `/sse` being mapped automatically, you now need `EnableLegacySse = true` (suppressing the `MCP9003` warning) to keep serving those endpoints. The recommended path is to migrate all clients to Streamable HTTP and then remove `EnableLegacySse`.
57+
**Server-side migration.** If you previously relied on `/sse` being mapped automatically, you now need `EnableLegacySse = true` (suppressing the `MCP9004` warning) to keep serving those endpoints. The recommended path is to migrate all clients to Streamable HTTP and then remove `EnableLegacySse`.
5858

5959
**Transition period.** If some clients still need SSE while others have already migrated to Streamable HTTP, set `EnableLegacySse = true` with `Stateless = false`. Both transports are served simultaneously by `MapMcp()` — Streamable HTTP on the root endpoint and SSE on `/sse` and `/message`. Once all clients have migrated, remove `EnableLegacySse` and optionally switch to `Stateless = true`.
6060

@@ -720,7 +720,7 @@ In stateless mode, each HTTP request is its own "session", so `mcp.server.sessio
720720

721721
## Legacy SSE transport
722722

723-
The legacy [SSE (Server-Sent Events)](https://modelcontextprotocol.io/specification/2024-11-05/basic/transports#http-with-sse) transport is also supported by `MapMcp()` and always uses stateful mode. Legacy SSE endpoints (`/sse` and `/message`) are **disabled by default** due to [backpressure concerns](#request-backpressure). To enable them, set <xref:ModelContextProtocol.AspNetCore.HttpServerTransportOptions.EnableLegacySse> to `true` — this property is marked `[Obsolete]` with a diagnostic warning (`MCP9003`) to signal that it should only be used when you need to support legacy SSE-only clients and understand the backpressure implications. Alternatively, set the `ModelContextProtocol.AspNetCore.EnableLegacySse` [AppContext switch](https://learn.microsoft.com/dotnet/api/system.appcontext) to `true`.
723+
The legacy [SSE (Server-Sent Events)](https://modelcontextprotocol.io/specification/2024-11-05/basic/transports#http-with-sse) transport is also supported by `MapMcp()` and always uses stateful mode. Legacy SSE endpoints (`/sse` and `/message`) are **disabled by default** due to [backpressure concerns](#request-backpressure). To enable them, set <xref:ModelContextProtocol.AspNetCore.HttpServerTransportOptions.EnableLegacySse> to `true` — this property is marked `[Obsolete]` with a diagnostic warning (`MCP9004`) to signal that it should only be used when you need to support legacy SSE-only clients and understand the backpressure implications. Alternatively, set the `ModelContextProtocol.AspNetCore.EnableLegacySse` [AppContext switch](https://learn.microsoft.com/dotnet/api/system.appcontext) to `true`.
724724
725725
> [!NOTE]
726726
> Setting `EnableLegacySse = true` while `Stateless = true` throws an `InvalidOperationException` at startup, because SSE requires in-memory session state shared between the GET and POST requests.

docs/concepts/transports/transports.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -184,7 +184,7 @@ SSE-specific configuration options:
184184

185185
#### SSE server (ASP.NET Core)
186186

187-
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 `MCP9003`). SSE always requires stateful mode; legacy SSE endpoints are never mapped when `Stateless = true`.
187+
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`.
188188

189189
**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. See [Request backpressure](xref:stateless#request-backpressure) for a detailed comparison and mitigations if you must use SSE.
190190

@@ -199,11 +199,11 @@ builder.Services.AddMcpServer()
199199
// SSE requires stateful mode (the default). Set explicitly for forward compatibility.
200200
options.Stateless = false;
201201

202-
#pragma warning disable MCP9003 // EnableLegacySse is obsolete
202+
#pragma warning disable MCP9004 // EnableLegacySse is obsolete
203203
// Enable legacy SSE endpoints for clients that don't support Streamable HTTP.
204204
// See sessions doc for backpressure implications.
205205
options.EnableLegacySse = true;
206-
#pragma warning restore MCP9003
206+
#pragma warning restore MCP9004
207207
})
208208
.WithTools<MyTools>();
209209

docs/list-of-diagnostics.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,4 +36,5 @@ When APIs are marked as obsolete, a diagnostic is emitted to warn users that the
3636
| :------------ | :----- | :---------- |
3737
| `MCP9001` | In place | The `EnumSchema` and `LegacyTitledEnumSchema` APIs are deprecated as of specification version 2025-11-25. Use the current schema APIs instead. |
3838
| `MCP9002` | Removed | The `AddXxxFilter` extension methods on `IMcpServerBuilder` (e.g., `AddListToolsFilter`, `AddCallToolFilter`, `AddIncomingMessageFilter`) were superseded by `WithRequestFilters()` and `WithMessageFilters()`. |
39-
| `MCP9003` | In place | <xref:ModelContextProtocol.AspNetCore.HttpServerTransportOptions.EnableLegacySse> opts into the legacy SSE transport which has no built-in HTTP-level backpressure. Use Streamable HTTP instead. See [Sessions — Legacy SSE transport](xref:stateless#legacy-sse-transport) for details. |
39+
| `MCP9003` | In place | The `RequestContext<TParams>(McpServer, JsonRpcRequest)` constructor is obsolete. Use the overload that accepts a `parameters` argument: `RequestContext<TParams>(McpServer, JsonRpcRequest, TParams)`. |
40+
| `MCP9004` | In place | <xref:ModelContextProtocol.AspNetCore.HttpServerTransportOptions.EnableLegacySse> 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. |

samples/EverythingServer/Program.cs

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@
141141
{
142142
throw new McpException("Cannot add subscription for server with null SessionId");
143143
}
144-
if (ctx.Params?.Uri is { } uri)
144+
if (ctx.Params.Uri is { } uri)
145145
{
146146
subscriptions[ctx.Server.SessionId].TryAdd(uri, 0);
147147

@@ -165,7 +165,7 @@ await ctx.Server.SampleAsync([
165165
{
166166
throw new McpException("Cannot remove subscription for server with null SessionId");
167167
}
168-
if (ctx.Params?.Uri is { } uri)
168+
if (ctx.Params.Uri is { } uri)
169169
{
170170
subscriptions[ctx.Server.SessionId].TryRemove(uri, out _);
171171
}
@@ -223,11 +223,6 @@ await ctx.Server.SampleAsync([
223223
})
224224
.WithSetLoggingLevelHandler(async (ctx, ct) =>
225225
{
226-
if (ctx.Params?.Level is null)
227-
{
228-
throw new McpProtocolException("Missing required argument 'level'", McpErrorCode.InvalidParams);
229-
}
230-
231226
// The SDK updates the LoggingLevel field of the IMcpServer
232227

233228
await ctx.Server.SendNotificationAsync("notifications/message", new

0 commit comments

Comments
 (0)