fix(streamable-http-server): map handler-generated HeaderMismatch to HTTP 400 (#1259)

`jsonrpc_http_status` maps several protocol errors (unsupported protocol
version, missing required client capability, invalid params) to HTTP 400
on the modern per-request Streamable HTTP path, but fell through to the
default HTTP 200 for `ErrorCode::HEADER_MISMATCH`. When a `ServerHandler`
returns `Err(ErrorData::header_mismatch(...))`, RMCP serialized the
correct JSON-RPC -32020 error but sent it with HTTP 200.

The legacy/transport-generated header validation path (SEP-2243
Mcp-Method/Mcp-Name/Mcp-Param-* mismatches) was unaffected, since
`header_mismatch_jsonrpc_response` constructs an HTTP 400 response
directly rather than going through `jsonrpc_http_status`. Only
handler-generated HeaderMismatch errors on the modern path hit the bug.

Add `ErrorCode::HEADER_MISMATCH` to the existing `BAD_REQUEST` match arm,
matching the Streamable HTTP server-validation rules in the 2026-07-28
spec. Added unit tests covering the mapper directly (header-mismatch to
400, method-not-found to 404, an unmapped code defaulting to 200).

Closes #1225

Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
voidstack 2026-09-12 03:54:40 +03:00 • committed by GitHub
commit b86995aebd
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -644,12 +644,52 @@ fn jsonrpc_http_status(message: &ServerJsonRpcMessage) -> http::StatusCode {
match error.error.code {
ErrorCode::UNSUPPORTED_PROTOCOL_VERSION
| ErrorCode::MISSING_REQUIRED_CLIENT_CAPABILITY
| ErrorCode::INVALID_PARAMS => http::StatusCode::BAD_REQUEST,
| ErrorCode::INVALID_PARAMS
| ErrorCode::HEADER_MISMATCH => http::StatusCode::BAD_REQUEST,
ErrorCode::METHOD_NOT_FOUND => http::StatusCode::NOT_FOUND,
_ => http::StatusCode::OK,
}
}
#[cfg(test)]
mod jsonrpc_http_status_tests {
use super::*;
fn error_message(code: ErrorCode) -> ServerJsonRpcMessage {
ServerJsonRpcMessage::Error(JsonRpcError::new(
Some(RequestId::Number(1)),
ErrorData::new(code, "test error", None),
))
}
/// A handler returning `ErrorData::header_mismatch(..)` on the modern per-request path
/// must map to HTTP 400, not the default HTTP 200. Regression test for
/// https://github.com/modelcontextprotocol/rust-sdk/issues/1225
#[test]
fn header_mismatch_maps_to_bad_request() {
assert_eq!(
jsonrpc_http_status(&error_message(ErrorCode::HEADER_MISMATCH)),
http::StatusCode::BAD_REQUEST
);
}
#[test]
fn method_not_found_maps_to_not_found() {
assert_eq!(
jsonrpc_http_status(&error_message(ErrorCode::METHOD_NOT_FOUND)),
http::StatusCode::NOT_FOUND
);
}
#[test]
fn unmapped_error_defaults_to_ok() {
assert_eq!(
jsonrpc_http_status(&error_message(ErrorCode::INTERNAL_ERROR)),
http::StatusCode::OK
);
}
}
fn jsonrpc_message_response(
message: ServerJsonRpcMessage,
map_protocol_status: bool,