Add stateless doc, prefer stateless mode and disable legacy SSE by default#1468
Add stateless doc, prefer stateless mode and disable legacy SSE by default#1468
Conversation
…r messages Recommend stateless mode as the default for HTTP-based MCP servers across documentation, samples, and error messages. Docs: - Add comprehensive sessions conceptual doc covering stateless (recommended), stateful, and stdio session behaviors - Update getting-started, transports, filters, and other conceptual docs to use stateless mode in examples - Add Sampling to docs table of contents - Clarify ConfigureSessionOptions runs per-request in stateless mode Samples: - Convert ProtectedMcpServer to stateless mode - Add comments to AspNetCoreMcpServer and EverythingServer explaining why they require sessions Error messages: - Improve missing Mcp-Session-Id errors to suggest stateless mode and link to session documentation Tests: - Add tests for progress notifications and ConfigureSessionOptions in stateless mode - Verify error messages reference stateless mode guidance Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
This PR updates the SDK’s guidance around HTTP server sessions by adding a dedicated “Sessions” conceptual doc, shifting most docs/samples to recommend stateless mode by default, and improving HTTP error messages to point users at stateless mode when Mcp-Session-Id is missing.
Changes:
- Added a comprehensive Sessions conceptual doc (stateless vs. stateful vs. stdio) and updated docs TOC.
- Updated multiple docs and samples to prefer
HttpServerTransportOptions.Stateless = trueby default, with notes on when stateful sessions are required. - Improved Streamable HTTP missing-session error messages and added/updated tests to validate the guidance appears.
Reviewed changes
Copilot reviewed 17 out of 17 changed files in this pull request and generated 7 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/ModelContextProtocol.AspNetCore.Tests/StreamableHttpServerConformanceTests.cs | Adds assertions that missing-session 400s include stateless guidance. |
| tests/ModelContextProtocol.AspNetCore.Tests/StatelessServerTests.cs | Adds stateless-mode tests for progress notifications and ConfigureSessionOptions behavior. |
| src/ModelContextProtocol.AspNetCore/StreamableHttpHandler.cs | Enhances 400 error messages to recommend stateless mode and link to sessions docs. |
| src/ModelContextProtocol.AspNetCore/HttpServerTransportOptions.cs | Clarifies ConfigureSessionOptions invocation semantics in stateless mode. |
| samples/ProtectedMcpServer/Program.cs | Switches sample to stateless mode with explanatory comments. |
| samples/EverythingServer/Program.cs | Adds explanation why the sample must remain stateful. |
| samples/AspNetCoreMcpServer/Program.cs | Adds explanation why the sample must remain stateful. |
| docs/concepts/transports/transports.md | Updates examples to stateless mode and adds stateless guidance in narrative/table. |
| docs/concepts/toc.yml | Adds Sessions and Sampling entries to conceptual TOC. |
| docs/concepts/sessions/sessions.md | New comprehensive sessions documentation page. |
| docs/concepts/progress/samples/server/Program.cs | Updates example to stateless mode. |
| docs/concepts/logging/logging.md | Links stateless behavior to sessions doc. |
| docs/concepts/index.md | Adds Sessions to concepts index table. |
| docs/concepts/httpcontext/samples/Program.cs | Updates example to stateless mode. |
| docs/concepts/getting-started.md | Updates getting-started HTTP server snippet to stateless mode. |
| docs/concepts/filters.md | Updates auth/filters example to stateless mode. |
| docs/concepts/elicitation/elicitation.md | Links stateless limitation note to sessions doc. |
- Fix relative links to sessions doc from subdirectories - Fix doc URLs in error messages to use .html extension - Strengthen ConfigureSessionOptions test with two requests proving per-request behavior Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
TokenProgress.Report() uses fire-and-forget (no await), so in stateless mode the SSE stream can close before notifications flush. Rewrite the test using TCS coordination: the tool reports progress then waits, giving the notification time to flush before the stream closes. A SynchronousProgress<T> helper avoids the thread pool posting race inherent to Progress<T>. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
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.
Every WithHttpTransport() call in samples and docs now explicitly sets Stateless = true or Stateless = false. This prepares for a potential future default change and makes the intent clear in code users may copy. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add 'Service lifetimes and DI scopes' section to sessions.md covering how ScopeRequests controls per-handler scoping in stateful HTTP, how stateless HTTP reuses ASP.NET Core's request scope, and how stdio defaults to per-handler scoping but is configurable. Includes summary table and cross-link from the stdio section. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Group sections by purpose: mode selection (stateless/stateful/comparison), transport details (HTTP lifecycle, deployment considerations, stdio), server configuration (options, ConfigureSessionOptions, DI scopes), security (user binding), and advanced features (migration, resumability). Move comparison table near the decision tree. Move deployment footguns under HTTP transport. Move stateless trade-offs into the stateless section. Combine 'When to use stateful' and 'When stateful shines'. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Any active HTTP request (POST or GET) prevents a session from being counted as idle, not just GET/SSE. Fix docs and API comment on MaxIdleSessionCount. Also remove redundant 'async' from 'async scope' in DI documentation since nearly all ASP.NET Core scopes are async. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Split Streamable HTTP into stateless and stateful columns, fix SSE server example that incorrectly showed Stateless = true (SSE endpoints are not mapped in stateless mode), and add cross-reference to sessions doc. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
0454970 to
b7e68dc
Compare
…vior Rewrite sessions.md intro to lead with the Stateless property recommendation, clarify that sessions enabled is the current C# SDK default (not a protocol requirement), and note the spec requires clients use sessions when servers request them. Replace middleware example with minimal API endpoint filter. Fix AllowNewSessionForNonInitializeRequests docs to call out spec non-compliance. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Restructure document flow: move client-side behavior up, fold security into server configuration, move legacy SSE to its own section near the end. Replace middleware example with minimal API endpoint filter using Activity.AddTag for the transport session ID. Migrate SSE anchors across transports.md, list-of-diagnostics.md, and filters.md. Fix endpoint filter test to avoid strict request count assertion. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
Any way we could add an analyzer to help mitigate the breaks? eg detect that Stateless = false isn't being used anywhere and then warn on any use of the problematic methods? |
|
Regarding the analyzer, I just opened #1471 to look into that. I currently have that as a PR based on this one ( |
…transports.md Add 'Forward and backward compatibility' section to sessions.md explaining why servers should set Stateless explicitly. Add 'How Streamable HTTP delivers messages' section defining solicited (POST response streams) vs unsolicited (GET stream) message delivery. Enhance transports.md with message flow overview, SSE backpressure explanation, and backpressure row in the transport comparison table. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
I added an additional section on forward and backward compatibility:
|
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Move Activity.AddTag before next() so child spans created during request processing inherit the transport session ID. Accept that the first initialize request won't have the tag (no request header yet). Update test to match the simplified pattern. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Explain that /sse endpoint clients are using the legacy transport which is now disabled by default. Cover client-side migration (change endpoint URL), server-side migration (EnableLegacySse opt-in), and transition period (both transports served simultaneously by MapMcp). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
I also added this comment about how to deal with the legacy SSE breaking change:
|
Rename docs/concepts/sessions/ to docs/concepts/stateless/ and update the uid, title, toc.yml entry, all xref links, and error message URLs. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
mikekistler
left a comment
There was a problem hiding this comment.
Looks good. 👍
There may still be some minor points of clarification but we can handle those in follow-up PRs.
# Conflicts: # docs/list-of-diagnostics.md # src/Common/Obsoletions.cs
jeffhandley
left a comment
There was a problem hiding this comment.
I haven't reviewed all of the docs changes/additions, but I sign off on the behavioral changes being made here along with the new (obsolete-out-of-the-gate) property.
This makes me realize we should be more reluctant to pick any server behavioral defaults going forward.
I was very tempted to recommend we stage this even more granularly by:
- Introduce the
EnableLegacySseproperty in 1 release - Change the behavior in the next release
But that'd be overkill, and we're giving a call-to-action anyway.
I also asked @halter73 about marking WithHttpTransport as [Obsolete] and introducing a (worse) API that forces the specification of stateful vs. stateless. We decided against it, at least for now. Presumably that would've needed to be WithStatelessHttpTransport and WithStatefulHttpTransport or something else awkward.
|
This PR has been labeled |
Recommend stateless mode as the default for HTTP-based MCP servers across documentation, samples, and error messages.
Docs:
Samples:
Error messages:
Tests: