Document initialize body protocolVersion is authoritative on Streamable HTTP

## Motivation and Context

When an `initialize` POST carries an `MCP-Protocol-Version` HTTP header that disagrees with
`params.protocolVersion` in the JSON-RPC body, the Streamable HTTP server accepts the request
and negotiates using the body value. The behavior was already encoded in `handle_post`
(the header check is skipped on `initialize`) but it was not stated in the code and there was
no regression test for the specific mismatch case.

MCP 2025-06-18 / 2025-11-25 only requires `MCP-Protocol-Version` on requests subsequent to
`initialize`, and the spec does not require the header and body to agree on the initial `initialize`.
All official MCP SDKs (TypeScript, Python, Rust, Java, Go, PHP) accept the mismatch and
treat the JSON-RPC body as authoritative.

## How Has This Been Tested?

Streamable HTTP tests: two new regression tests confirm that on an `initialize` POST
with a supported but mismatched `MCP-Protocol-Version` header, the server returns
HTTP 200 and the response's `result.protocolVersion` matches the JSON-RPC body value,
in both mismatch directions (header older / body newer, and header newer / body older).

## Breaking Changes

None. The behavior is unchanged: `initialize` requests with a mismatched `MCP-Protocol-Version` header
are still accepted, and the JSON-RPC body `params.protocolVersion` continues to drive negotiation.
This commit only adds an explanatory comment and regression tests.

Closes #351.
This commit is contained in:
Koichi ITO 2026-05-16 21:37:34 +09:00
commit 711f064088

View file

@ -1400,6 +1400,58 @@ module MCP
assert_equal 200, response[0]
end
test "POST initialize request negotiates body protocolVersion when header is an older supported version" do
older_version = "2025-06-18"
assert_includes Configuration::SUPPORTED_STABLE_PROTOCOL_VERSIONS, older_version
refute_equal Configuration::LATEST_STABLE_PROTOCOL_VERSION, older_version
request = create_rack_request(
"POST",
"/",
{
"CONTENT_TYPE" => "application/json",
"HTTP_MCP_PROTOCOL_VERSION" => older_version,
},
{
jsonrpc: "2.0",
method: "initialize",
id: "init",
params: { protocolVersion: Configuration::LATEST_STABLE_PROTOCOL_VERSION },
}.to_json,
)
response = @transport.handle_request(request)
assert_equal 200, response[0]
body = JSON.parse(response[2][0])
assert_equal Configuration::LATEST_STABLE_PROTOCOL_VERSION, body["result"]["protocolVersion"]
end
test "POST initialize request negotiates body protocolVersion when header is a newer supported version" do
older_version = "2025-06-18"
assert_includes Configuration::SUPPORTED_STABLE_PROTOCOL_VERSIONS, older_version
refute_equal Configuration::LATEST_STABLE_PROTOCOL_VERSION, older_version
request = create_rack_request(
"POST",
"/",
{
"CONTENT_TYPE" => "application/json",
"HTTP_MCP_PROTOCOL_VERSION" => Configuration::LATEST_STABLE_PROTOCOL_VERSION,
},
{
jsonrpc: "2.0",
method: "initialize",
id: "init",
params: { protocolVersion: older_version },
}.to_json,
)
response = @transport.handle_request(request)
assert_equal 200, response[0]
body = JSON.parse(response[2][0])
assert_equal older_version, body["result"]["protocolVersion"]
end
test "POST request with unsupported MCP-Protocol-Version returns 400" do
init_request = create_rack_request(
"POST",