fix(server): serve draft-version requests statelessly per SEP-2567#999
fix(server): serve draft-version requests statelessly per SEP-2567#999alexhancock wants to merge 1 commit into
Conversation
| .unwrap_or(ProtocolVersion::V_2025_03_26) | ||
| }); | ||
| let use_session = | ||
| self.config.stateful_mode && declared_version < ProtocolVersion::V_2026_07_28; |
There was a problem hiding this comment.
Since #973 has landed, could we classify the request as legacy or modern before touching the session manager, then derive use_session from that classification instead of ordering ProtocolVersion? I'm thinking of something like stateful_mode && lifecycle.is_legacy(). This would also let malformed or unsupported versions fail before any session lookup or restore.
| )) | ||
| } | ||
|
|
||
| async fn handle_post<B>(&self, request: Request<B>) -> Result<BoxResponse, BoxResponse> |
There was a problem hiding this comment.
GET and DELETE still enter legacy session handlers?
There was a problem hiding this comment.
Since 2026-07-28 requests are always stateless, should we deprecate stateful_mode in favor of a legacy-scoped option such as legacy_session_mode? After this change, the current option only affects pre-2026-07-28 traffic, but its name suggests that it controls all requests. Since v3 already allows breaking changes, this may be a good opportunity to clarify the API.
Motivation and Context
Per modelcontextprotocol/modelcontextprotocol#2567 protocol-level sessions (and the
Mcp-Session-Idheader) are removed from the Streamable HTTP transport as of the draft protocol version (2026-07-28); servers speaking that version MUST serve requests statelessly.The rust-sdk server assigned an
Mcp-Session-Idin its defaultstateful_modeeven when the negotiated protocol version was2026-07-28. This gates the session code path on the negotiated version: sessions are used only for pre-draft versions (< 2026-07-28), and draft-version requests are served statelessly even instateful_mode. Older versions retain full session behavior.How Has This Been Tested?
No dedicated SEP-2567 conformance scenario exists (the conformance PR that would add one was closed unmerged); per the maintainer, every existing draft (
2026-07-28) scenario already sends sessionless requests. Verified against the existing draft server suite:The default (stateful) server now produces results byte-for-byte identical to an explicit stateless server (48 passed / 31 failed; the 31 remaining failures are pre-existing SEP-2575
server/discovergaps, out of scope).rmcplib unit tests (232) and streamable-HTTP integration tests (session store, stale session, protocol version, JSON response — 19) pass locally.Breaking Changes
None. The session code path is unchanged for all pre-
2026-07-28versions; only the draft version changes behavior, bringing it into spec compliance.Types of changes
Checklist
Additional context
SEP-2575 (also in progress) removes the
initializehandshake for the draft version. Once that lands, the version detection here can be simplified to a lifecycle check (aninitializerequest implies the pre-draft/stateful lifecycle; draft requests never carryinitialize).