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:
parent
d6b5392831
commit
711f064088
2 changed files with 55 additions and 0 deletions
|
|
@ -342,6 +342,9 @@ module MCP
|
|||
body = parse_request_body(body_string)
|
||||
return body if parse_error_tuple?(body)
|
||||
|
||||
# The `MCP-Protocol-Version` header is only meaningful after negotiation, so on `initialize`
|
||||
# the JSON-RPC body `params.protocolVersion` is authoritative and the header (if any) is ignored.
|
||||
# This matches the TypeScript and Python SDKs.
|
||||
unless initialize_request?(body)
|
||||
return missing_session_id_response if !@stateless && !session_id
|
||||
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
Loading…
Reference in a new issue