v2025-http: two conformance defects on the 2025 Streamable HTTP binding - #202
Merged
Conversation
Every MCP revision that defines MCP-Protocol-Version requires a server to answer 400 when the value names a version it does not speak — 2025-06-18 and 2025-11-25 as a bare 400, 2026-07-28 as 400 plus the supported list. This face never judged the value at all. The one check compared it against the version the body's _meta declares, and a request without _meta made `declared == ""` skip the comparison entirely — so the rule was unenforced for precisely the ≤ 2025-11-25 generations that demand it, since only 2026 sends that _meta. `MCP-Protocol-Version: banana` did not merely earn a 200: on initialize it minted a session. DELETE was worse off still, running no header checks at all. checkProtocolVersion refuses anything outside mcp.SupportedVersions with -32022 and the supported list, which the existing status mapping renders as 400, and DELETE now calls it too. Absence stays allowed — that is the separate carve-out for clients predating the header, and the comment justifying the old behaviour cited it as though it covered both cases. Reading the accepted set from mcp.SupportedVersions is what keeps "would we negotiate this" and "would we accept this in a header" one answer; a test drives that list directly. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
MCP 2025-11-25 makes waiting out an SSE `retry` field a MUST — a server that closes a connection without ending the stream asks for a delay, and the client "MUST respect the retry field". It is the only revision carrying the rule (2025-06-18 draws no connection-versus-stream distinction, 2026-07-28 removed resumable streams), but 2025-11-25 is what mcp.ProtocolVersion names. sseScanner parsed the field and discarded it, under a comment saying the package used its own bounded backoff instead. That backoff lives in streamLoop, which no production configuration opens. The path the shipped product does reach is the per-POST resume, and it had no delay at all: measured at 117µs against a server asking for 250ms. The hint is kept now, and the two reconnect sites spend it differently because only one has a caller waiting. The out-of-call loop honours it in full, max-ed with its own backoff. The per-call resume runs under the caller's deadline, so waitBeforeResume takes the wait when it fits and otherwise abandons the resume — sleeping out a long hint would turn an answer into a timeout, and coming back early is the one option the MUST forbids. An unparseable retry leaves the previous hint standing rather than clearing it, per the SSE standard; clearing would be a reconnect storm dressed as conformance. Both new wire-level tests fail with the guard removed, and FuzzSSEScanner ran 7.8M execs over the changed parser with no findings. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Two MCP conformance defects found reviewing the 2025 family (
2025-06-18,2025-11-25) ratherthan 2026-07-28, which rounds 1-16 covered. Each was independently verified by a subagent before any
code was written.
MCP-Protocol-Versionnaming a version this server does not speak is refused (400)retryfield is honoured on the one path that reconnects