Skip to content

fix(server): reject initialize with mismatched MCP-Protocol-Version header#2111

Open
dparikh79 wants to merge 1 commit into
modelcontextprotocol:mainfrom
dparikh79:fix/2108-init-protocol-version-mismatch
Open

fix(server): reject initialize with mismatched MCP-Protocol-Version header#2111
dparikh79 wants to merge 1 commit into
modelcontextprotocol:mainfrom
dparikh79:fix/2108-init-protocol-version-mismatch

Conversation

@dparikh79
Copy link
Copy Markdown

Summary

Fixes #2108.

When the initial JSON-RPC initialize request to the Streamable HTTP server carries an MCP-Protocol-Version header that disagrees with initialize.params.protocolVersion in the body, the server currently accepts the request, lets the body win, and the resulting session uses a version the client never asked for in its HTTP header.

This PR adds an equality check at the top of the initialize branch in WebStandardStreamableHTTPServerTransport.handleRequest. If the header is present and does not match the body's protocolVersion, the request is rejected with 400 Bad Request and JSON-RPC error code -32600. Header absent stays a soft case (existing behavior, since the spec allows the header to be omitted on initialize).

Why this shape

  • This is option A from the issue: defensive validation against silent mismatch.
  • The check is local to the initialize branch so it does not change behavior for any non-initialize request (those still flow through the existing validateProtocolVersion after-init path).
  • The existing validateProtocolVersion is left alone. Its JSDoc already documents that initialize defers to version negotiation; that remains true for the header-absent case. Adding the equality check next to the rest of the init-branch validation keeps the surface area minimal and the change reviewable in one block.
  • The MCP 2025-11-25 spec does not explicitly mandate this consistency check, so the change is implementation-level hardening. SEP-2575 is heading toward a related requirement for _meta["io.modelcontextprotocol/protocolVersion"], and this lays groundwork without front-running it.

Test plan

New tests in packages/server/test/server/streamableHttp.test.ts under the existing HTTPServerTransport - Protocol Version Validation block:

  • Initialize rejected (400, -32600) when header is older than body (header=2025-03-26, body=2025-11-25).
  • Initialize rejected (400, -32600) when header is newer than body (header=2025-11-25, body=2025-06-18).
  • Initialize accepted (200) when header matches body.
  • Initialize accepted (200) when header is absent (existing behavior preserved).
  • pnpm --filter @modelcontextprotocol/server typecheck clean.
  • pnpm --filter @modelcontextprotocol/server lint clean.
  • pnpm --filter @modelcontextprotocol/server test 69 / 69 passed (4 new + 65 pre-existing in streamableHttp.test.ts).
  • pnpm test:all green across all packages (integration suite 423 / 423).

Branch + changeset

Targeted at main per CONTRIBUTING ("v2 in development"). Changeset added at .changeset/fix-init-protocol-version-mismatch.md as a @modelcontextprotocol/server patch.

Happy to open a parallel PR against v1.x (where the equivalent code lives in src/server/webStandardStreamableHttp.ts, same shape) if a maintainer wants it backported.

AI Assistance Disclosure

Implementation and tests drafted with Claude assistance. I reviewed every line, ran the suites locally, and am the human contributor accountable for this PR.

…eader

When the initial JSON-RPC `initialize` request carries an
MCP-Protocol-Version header that disagrees with
params.protocolVersion in the body, the Streamable HTTP server
silently accepts the request and lets the body win, leaving the
client with a session under a version it did not ask for.

Add a header-vs-body equality check at the start of the
initialize branch in WebStandardStreamableHTTPServerTransport.
Header absent stays a soft case (existing behavior). Header
present must match body or the request gets a 400.

Tests cover both mismatch directions, the matching case, and
header-absent (existing behavior preserved).

Fixes modelcontextprotocol#2108
@dparikh79 dparikh79 requested a review from a team as a code owner May 16, 2026 17:03
@changeset-bot
Copy link
Copy Markdown

changeset-bot Bot commented May 16, 2026

🦋 Changeset detected

Latest commit: bd6677f

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 5 packages
Name Type
@modelcontextprotocol/server Patch
@modelcontextprotocol/express Patch
@modelcontextprotocol/fastify Patch
@modelcontextprotocol/hono Patch
@modelcontextprotocol/node Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new Bot commented May 16, 2026

Open in StackBlitz

@modelcontextprotocol/client

npm i https://pkg.pr.new/@modelcontextprotocol/client@2111

@modelcontextprotocol/server

npm i https://pkg.pr.new/@modelcontextprotocol/server@2111

@modelcontextprotocol/express

npm i https://pkg.pr.new/@modelcontextprotocol/express@2111

@modelcontextprotocol/fastify

npm i https://pkg.pr.new/@modelcontextprotocol/fastify@2111

@modelcontextprotocol/hono

npm i https://pkg.pr.new/@modelcontextprotocol/hono@2111

@modelcontextprotocol/node

npm i https://pkg.pr.new/@modelcontextprotocol/node@2111

commit: bd6677f

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.

Streamable HTTP server accepts mismatched MCP-Protocol-Version header and body protocolVersion on initialize

1 participant