Skip to content

fix: use negotiated protocol version in server-sse-multiple-streams raw POSTs - #415

Merged
pcarleton merged 1 commit into
mainfrom
fix/sse-multiple-streams-negotiated-version
Jul 27, 2026
Merged

fix: use negotiated protocol version in server-sse-multiple-streams raw POSTs#415
pcarleton merged 1 commit into
mainfrom
fix/sse-multiple-streams-negotiated-version

Conversation

@claude

@claude claude Bot commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

Before

Running the stable server-sse-multiple-streams scenario against a stateful server that enforces the protocol version negotiated during initialize (as the MCP lifecycle requires) false-failed with:

Server rejected some requests. Statuses: 400, 400, 400

The scenario initialized through the SDK's StreamableHTTPClientTransport (negotiating e.g. 2025-11-25), but its three raw concurrent follow-up POSTs hard-coded mcp-protocol-version: 2025-03-26. A version-enforcing server correctly rejected all three, so the scenario failed before ever exercising the multi-stream concurrency behavior under test.

After

The raw POSTs carry the version actually negotiated during initialize, so the scenario passes against version-enforcing servers and tests what it is meant to test. Servers that don't enforce the version are unaffected.

How

In src/scenarios/server/sse-multiple-streams.ts, after client.connect(transport) the scenario now reads the SDK transport's public protocolVersion getter into negotiatedProtocolVersion (alongside the existing session-id extraction) and uses it for the stateful mcp-protocol-version request header, falling back to the run's specVersion if the transport exposes none. The stateless (draft) branch is unchanged.

src/scenarios/server/sse-multiple-streams.test.ts adds regression coverage: a fixture HTTP server (built in the test file, following the pattern in http-standard-headers.test.ts) stores the protocolVersion it returns from initialize and 400s any later request whose MCP-Protocol-Version header doesn't match. The scenario now passes against it (verified to fail with the previous hard-coded header), and a second test pins that the fixture rejects the supported-but-non-negotiated 2025-03-26.

Fixes #412

The stateful server-sse-multiple-streams scenario hard-coded
mcp-protocol-version: 2025-03-26 on its three raw concurrent POSTs
instead of the version negotiated during initialize. A server that
enforces the negotiated session version correctly returns 400 to all
three POSTs, so the scenario false-failed before exercising the
multi-stream behavior under test.

Read transport.protocolVersion after client.connect() and use it for
the raw POST headers, falling back to the run's spec version if the
transport does not expose one. Add a regression test driving the
scenario against a fixture server that 400s any request whose
MCP-Protocol-Version header does not match what it negotiated.

Fixes #412

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0178cz8nAyFRHQyq9cg2XgZz
@pkg-pr-new

pkg-pr-new Bot commented Jul 27, 2026

Copy link
Copy Markdown

Open in StackBlitz

npx https://pkg.pr.new/@modelcontextprotocol/conformance@415

commit: 0c21832

@pcarleton
pcarleton marked this pull request as ready for review July 27, 2026 10:59
@pcarleton
pcarleton merged commit 19a97f0 into main Jul 27, 2026
8 checks passed
@pcarleton
pcarleton deleted the fix/sse-multiple-streams-negotiated-version branch July 27, 2026 10:59
pcarleton pushed a commit that referenced this pull request Jul 27, 2026
…ts (#416)

* fix: use negotiated protocol version in sse-polling raw requests

The server-sse-polling scenario hard-coded mcp-protocol-version:
2025-11-25 on its raw tools/call POST and Last-Event-ID reconnection
GET instead of the version negotiated during initialize — the same
bug class as #412. A server that enforces the negotiated session
version rejects those requests once it negotiates any other version.

Read transport.protocolVersion after client.connect() and use it for
both raw request headers, falling back to the run's spec version,
mirroring the sse-multiple-streams fix (#415). Add a regression test
whose fixture server down-negotiates to 2025-06-18 and 400s any
request whose MCP-Protocol-Version header does not match, so a fix
that hard-codes the latest version cannot pass.

Follow-up to #415.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0178cz8nAyFRHQyq9cg2XgZz

* test: harden sse-polling regression fixture per review panel

Enforce session id alongside protocol version in the fixture, record
Last-Event-ID presence per GET so the SDK's automatic standalone GET
cannot satisfy the reconnect assertion, normalize the header type
instead of casting, return a descriptive 400 body on the GET path,
add Allow to 405s, use the spec-shaped empty-data priming event, and
read sessionId via the SDK's public getter in the scenario.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0178cz8nAyFRHQyq9cg2XgZz

---------

Co-authored-by: Claude <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

server-sse-multiple-streams sends a non-negotiated protocol version

2 participants