Skip to content

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

Open

Description

@cclabadmin

Describe the bug

When the initial initialize request carries an MCP-Protocol-Version HTTP header that disagrees with initialize.params.protocolVersion in the JSON-RPC body, the server accepts the request without error. The negotiated protocol version follows the JSON-RPC body rather than the MCP-Protocol-Version request header.

The current MCP 2025-11-25 specification does not explicitly require the server to check body/header consistency on initialize, so this is filed as an implementation observation rather than a strict spec-violation claim.

  • Environment
    • Reproduced with stable releasev1.29.0 (e12cbd70)
    • Also reproduced with a main snapshot from 2026-05-16: 22595b96
    • Transport: Streamable HTTP server (stateful and stateless profiles)

To reproduce

  1. Start a TypeScript SDK Streamable HTTP server.
  2. Send an initialize request where the body protocolVersion is 2025-11-25 but the MCP-Protocol-Version header is 2025-03-26 (or vice versa).
  3. Observe that the server returns HTTP 200 with a normal initialize result.
  4. Check the negotiated protocol version in the response.

Expected behavior

Option A: the server rejects the mismatch before negotiation, for example with HTTP 400 or a JSON-RPC Invalid Request error.

Option B: the spec clarifies which field is authoritative, and the SDK documents that behavior and covers it with a regression test.

Logs

Both mismatch directions were accepted:

body=2025-11-25 header=2025-03-26 -> HTTP 200, negotiated version follows body (2025-11-25)
body=2025-03-26 header=2025-11-25 -> HTTP 200, negotiated version follows body (2025-03-26)

After initialization, subsequent responses used the body-negotiated version in the MCP-Protocol-Version header, not the original mismatched header value.

With a Streamable HTTP server running, set ENDPOINT to the server endpoint and send an initial initialize request whose HTTP header and JSON-RPC body disagree:

ENDPOINT=http://127.0.0.1:8080/mcp

curl -i -sS --http1.1 -X POST "$ENDPOINT" \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json, text/event-stream' \
  -H 'MCP-Protocol-Version: 2025-03-26' \
  --data '{"jsonrpc":"2.0","id":"init-conflict-1","method":"initialize","params":{"protocolVersion":"2025-11-25","capabilities":{},"clientInfo":{"name":"version-conflict-repro","version":"0.1.0"}}}'

Repeat with the values reversed: body 2025-03-26, header 2025-11-25.

Additional context

  • Related: SEP-2575 introduces a related future-state requirement that, for HTTP requests, the MCP-Protocol-Version header match _meta["io.modelcontextprotocol/protocolVersion"].

Activity

  1. dparikh79 commented on May 16, 2026

    @dparikh79

    I would like to take this. Quick read of the code on main:

    packages/server/src/server/streamableHttp.ts runs validateProtocolVersion() only on the non-initialize path (line 696 in the request-handling block, and similarly in the GET and DELETE handlers). The init branch (line 666 onward) accepts the request based on body parsing alone and never compares the header against params.protocolVersion. The same shape exists on v1.x (src/server/webStandardStreamableHttp.ts, validateProtocolVersion at line 874).

    Proposed fix: when the request is an initialize and the MCP-Protocol-Version header is present, reject with 400 if it does not equal initialize.params.protocolVersion. Header absent stays a soft case (the existing "header optional on initialize" behavior). This matches Option A from the issue and is consistent with the broader pattern of header-vs-body alignment that SEP-2575 is heading toward, without front-running that SEP.

    I will add a test in packages/server/test/server/streamableHttp.test.ts that exercises both directions (body newer than header, header newer than body) plus the negative case where they match. Will also mirror the change to v1.x in a separate PR if a maintainer wants it backported.

    Will hold off pushing the PR until I see a thumbs up here. Disclosure: implementation assisted by Claude; I am the human contributor reviewing.

  2. jshaofa-ui commented on May 17, 2026

    @jshaofa-ui
  3. jshaofa-ui commented on May 17, 2026

    @jshaofa-ui
  4. serejaris commented on May 27, 2026

    @serejaris

    Duplicate check update: I compared open PR #2111 against this issue.

    #2111 covers the reported Streamable HTTP initialize mismatch directly:

    • touches packages/server/src/server/streamableHttp.ts;
    • adds tests in packages/server/test/server/streamableHttp.test.ts;
    • rejects both mismatch directions with HTTP 400 / JSON-RPC -32600;
    • preserves accepted behavior when the header matches the body;
    • preserves accepted behavior when the initialize header is absent.

    I do not see extra behavior that needs a separate PR from my side. The useful next step looks like maintainer review/merge of #2111, not another duplicate branch.

  5. added
    triageQueued for automated analysis — bot will process and remove this label
    on Jul 9, 2026
  6. ghost added
    ready for workEnough information for someone to start working on
    on Jul 9, 2026
  7. ghost added
    P2Moderate issues affecting some users, edge cases, potentially valuable feature
    on Jul 9, 2026
  8. ghost added
    fix proposedBot has a verified fix diff in the comment
    on Jul 9, 2026
  9. ghost removed
    triageQueued for automated analysis — bot will process and remove this label
    on Jul 9, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    P2Moderate issues affecting some users, edge cases, potentially valuable featurebugSomething isn't workingfix proposedBot has a verified fix diff in the commentready for workEnough information for someone to start working on

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions