From 44ab1a07af829364b60cca342e5061f2b01852fc Mon Sep 17 00:00:00 2001 From: Salih Yilboga Date: Fri, 11 Sep 2026 14:53:14 +0300 Subject: [PATCH] fix(streamable-http-server): map handler-generated HeaderMismatch to HTTP 400 `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 --- .../transport/streamable_http_server/tower.rs | 42 ++++++++++++++++++- 1 file changed, 41 insertions(+), 1 deletion(-) diff --git a/crates/rmcp/src/transport/streamable_http_server/tower.rs b/crates/rmcp/src/transport/streamable_http_server/tower.rs index 9506a2950..35b0d9e46 100644 --- a/crates/rmcp/src/transport/streamable_http_server/tower.rs +++ b/crates/rmcp/src/transport/streamable_http_server/tower.rs @@ -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,