modelcontextprotocol / modelcontextprotocol/rust-sdk

SEP-2260 follow-up: skip session tracking and the standalone GET stream at protocol >= 2026-07-28

Open
#1,108 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

2026-07-28 enhancement P2 T-transport
Dominant language
Rust
Stars
3.9k
Forks
645
Avg merge
4d 13h
Merged PRs (30d)
36

Description

Follow-up to #1033 / PR #1055.

PR #1055 makes the client reject SEP-2260-restricted requests that arrive on the standalone GET stream: the streamable HTTP transport marks inbound requests with an InboundStreamOrigin, the service layer turns that into a PeerRequestAssociation, and RoleClient::enforce_peer_request_association answers -32602 for Unassociated.

Rejecting is the right behavior for a server that sends such a request. But at protocol 2026-07-28 the client shouldn't have the stream in the first place. SEP-2567 removes sessions and the standalone GET endpoint at that version, so an Mcp-Session-Id and a GET stream are both artifacts of a pre-2026-07-28 server shape. Keeping them open at 2026-07-28 means maintaining an inbound path whose only purpose is to be refused.

The modern path already gets this right. A server/discover startup sets session_id to None unconditionally (streamable_http_client.rs, ~L892), and there's a debug_assert! that discover bootstrap creates no session state (~L1054). What's left is the legacy-shaped paths, where a session id survives regardless of the version that was ultimately negotiated. spawn_common_stream has three call sites and all three are guarded only by if let Some(session_id), never by negotiated_version:

  • Legacy startup (~L969). A server that answers legacy initialize with both a session id and protocolVersion: 2026-07-28 gets a GET stream. This is the shape the e2e test in #1055 scripts deliberately.
  • Fallback initialize after server/discover fails (~L1382, on the initialized notification). Same shape, reached through ClientLifecycleMode::Auto.
  • Session re-establishment after an expired-session 404 (~L1218). Re-spawns on the new session id without reconsulting the version.

Work: gate session tracking and spawn_common_stream on negotiated_version < ProtocolVersion::STANDARD_HEADERS, so that at 2026-07-28 and later no session id is retained and no GET stream is opened. PeerRequestAssociation::Unassociated then becomes unreachable over streamable HTTP rather than merely rejected, and the enforcement from #1055 stays as the defense for anything that still slips through.

Two open questions worth settling in the same change:

  • What to do when a server returns a session id and negotiates 2026-07-28. Dropping the id silently is the least disruptive, but the server plainly expects it echoed back, so failing startup may be more honest than half-speaking its dialect.
  • Whether session cleanup (SessionCleanupInfo, the DELETE on shutdown) should also be skipped, or kept so a session the server did create still gets torn down.

This also bears on #863 (expose the negotiated Mcp-Session-Id): if there is deliberately no session at 2026-07-28, the accessor is a legacy-only affordance, and that's probably worth saying there either way.

Not a conformance gap — the receive-side check is a SHOULD and #1055 covers the scenarios — so this is hardening rather than a fix.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start in streamable_http_client.rs at the legacy startup (~L969), session re-establishment (~L1218), fallback initialize (~L1382), and the three spawn_common_stream call sites. Review the modern server/discover path around ~L892 and ~L1054, then use the e2e scenario from #1055 as a reference. Done means modern negotiated versions retain no session id and open no GET stream, with session cleanup and startup behavior decided.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust
Domain
api, networking
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
48/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.