You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Add decision tree, backpressure warning, and normalize xrefs
Add quick stateless-vs-stateful decision guide and explain why
stateless is recommended but not the default. Document the lack of
handler backpressure as a deployment footgun for stateful mode.
Normalize cross-doc links to use xref instead of relative paths.
Also document stale HttpContext risk with SSE transport.
Copy file name to clipboardExpand all lines: docs/concepts/elicitation/elicitation.md
+1-1Lines changed: 1 addition & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -172,7 +172,7 @@ Here's an example implementation of how a console application might handle elici
172
172
173
173
### URL Elicitation Required Error
174
174
175
-
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](../sessions/sessions.md) 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.
175
+
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.
176
176
177
177
#### Throwing UrlElicitationRequiredException on the Server
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:
36
+
37
+
- The `HttpContext.User` may contain stale claims if the client's token was refreshed after the SSE connection was established.
38
+
- Request headers, query strings, and other per-request metadata will reflect the initial SSE connection, not the current operation.
39
+
40
+
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.
41
+
42
+
<!-- mlc-disable-next-line -->
43
+
> [!NOTE]
44
+
> 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.
Servers built with the C# SDK always declare the logging capability. Doing so does not obligate the server
49
-
to send log messages—only allows it. Note that [stateless](../sessions/sessions.md) MCP servers might not be capable of sending log
49
+
to send log messages—only allows it. Note that [stateless](xref:sessions) MCP servers might not be capable of sending log
50
50
messages as there might not be an open connection to the client on which the log messages could be sent.
51
51
52
52
The C# SDK provides an extension method <xref:Microsoft.Extensions.DependencyInjection.McpServerBuilderExtensions.WithSetLoggingLevelHandler*> on <xref:Microsoft.Extensions.DependencyInjection.IMcpServerBuilder> to allow the
> **Why isn't stateless the default?** Stateful mode remains the default for backward compatibility and because it is the only HTTP mode with full feature parity with [stdio](xref:transports) (server-to-client requests, unsolicited notifications, subscriptions). Stateless is the recommended choice when you don't need those features. If your server _does_ depend on stateful behavior, consider setting `Stateless = false` explicitly so your code is resilient to a potential future default change once [MRTR](https://github.com/modelcontextprotocol/csharp-sdk/pull/1458) or similar mechanisms bring server-to-client interactions to stateless mode.
23
+
14
24
## Stateless mode (recommended)
15
25
16
26
Stateless mode is the recommended default for HTTP-based MCP servers. When enabled, the server doesn't track any state between requests, doesn't use the `Mcp-Session-Id` header, and treats each request independently. This is the simplest and most scalable deployment model.
@@ -115,6 +125,12 @@ You can mitigate this with <xref:ModelContextProtocol.AspNetCore.HttpServerTrans
115
125
116
126
Some MCP clients may not send the `Mcp-Session-Id` header on every request. When this happens, the server responds with an error: `"Bad Request: A new session can only be created by an initialize request."` This can happen after a server restart, when a client loses its session ID, or when a client simply doesn't support sessions. If you see this error, consider whether your server actually needs sessions — and if not, switch to stateless mode.
117
127
128
+
#### No built-in backpressure on request handlers
129
+
130
+
The SDK does not limit how long a handler can run or how many requests can be processed concurrently within a session. A misbehaving or compromised client can flood a stateful session with requests, and each request will spawn a handler that runs to completion. This can lead to thread starvation, GC pressure, or out-of-memory conditions that affect the entire HTTP server process — not just the offending session.
131
+
132
+
Stateless mode is significantly more resilient here because each tool call is a standard HTTP request-response. This means Kestrel and IIS connection limits, request timeouts, and rate-limiting middleware all apply naturally. The <xref:ModelContextProtocol.AspNetCore.HttpServerTransportOptions.IdleTimeout> and <xref:ModelContextProtocol.AspNetCore.HttpServerTransportOptions.MaxIdleSessionCount> settings help protect against non-malicious overuse (e.g., a buggy client creating too many sessions), but they are not a substitute for HTTP-level protections.
133
+
118
134
## stdio transport
119
135
120
136
The [stdio transport](xref:transports) is inherently single-session. The client launches the server as a child process and communicates over stdin/stdout. There is exactly one session per process, the session starts when the process starts, and it ends when the process exits.
Copy file name to clipboardExpand all lines: docs/concepts/transports/transports.md
+2-2Lines changed: 2 additions & 2 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -131,7 +131,7 @@ app.MapMcp();
131
131
app.Run();
132
132
```
133
133
134
-
By default, the HTTP transport uses **stateful sessions** — the server assigns an `Mcp-Session-Id` to each client and tracks session state in memory. For most servers, **stateless mode is recommended** instead. It simplifies deployment, enables horizontal scaling without session affinity, and avoids issues with clients that don't send the `Mcp-Session-Id` header. See [Sessions](../sessions/sessions.md) for a detailed guide on when to use stateless vs. stateful mode and how to configure session options.
134
+
By default, the HTTP transport uses **stateful sessions** — the server assigns an `Mcp-Session-Id` to each client and tracks session state in memory. For most servers, **stateless mode is recommended** instead. It simplifies deployment, enables horizontal scaling without session affinity, and avoids issues with clients that don't send the `Mcp-Session-Id` header. See [Sessions](xref:sessions) for a detailed guide on when to use stateless vs. stateful mode and how to configure session options.
135
135
136
136
A custom route can be specified. For example, the [AspNetCoreMcpPerSessionTools] sample uses a route parameter:
137
137
@@ -204,6 +204,6 @@ No additional configuration is needed. When a client connects using the SSE prot
204
204
| Process model | Child process | Remote HTTP | Remote HTTP |
205
205
| Direction | Bidirectional | Bidirectional | Server→client stream + client→server POST |
0 commit comments