Describe the bug
On the modern (2026-07-28) Streamable HTTP path, jsonrpc_http_status maps every ErrorCode::INVALID_PARAMS (-32602) response to HTTP 400. This includes ordinary application errors that the spec defines as -32602 and that are not transport validation failures:
The 2026-07-28 Streamable HTTP spec requires HTTP 400 only for HeaderMismatch, UnsupportedProtocolVersionError, and MissingRequiredClientCapabilityError, and HTTP 404 for -32601. Its backward-compatibility rules tell a client that receives HTTP 400 to inspect the body and fall back to initialize when it is not one of those recognized modern errors. A spec-compliant client could therefore treat a 400 carrying a "resource not found" -32602 as a legacy server and downgrade.
To Reproduce
- Implement a
ServerHandler whose read_resource returns Err(ErrorData::resource_not_found(...)) (or ErrorData::invalid_params(...) from get_prompt / call_tool).
- Send a valid modern Streamable HTTP request with protocol version
2026-07-28 that reaches the handler.
- Observe HTTP 400 with a JSON-RPC body containing error code
-32602.
Expected behavior
Handler-generated -32602 errors should be sent with HTTP 200 as in-band JSON-RPC errors, like other handler errors that jsonrpc_http_status does not map.
Logs
actual: HTTP 400, JSON-RPC error -32602 "Resource not found for URI: ui://widget/nope"
expected: HTTP 200, JSON-RPC error -32602
Found with rmcp 3.5.0 by MCPJam's modern-resource-not-found-invalid-params check (@mcpjam/sdk 8.20.1, profile mcp-protocol@2026-08-21.1), which expects an in-band error. The official conformance suite (0.2.0-alpha.11) does not check the status code for this case.
Additional context
The mapping is in jsonrpc_http_status:
https://github.com/modelcontextprotocol/rust-sdk/blob/8f9a28ecdb5f/crates/rmcp/src/transport/streamable_http_server/tower.rs#L646-L660
Transport-generated malformed-request errors, such as a missing protocolVersion in _meta, already build their HTTP 400 response directly in invalid_params_jsonrpc_response, so removing INVALID_PARAMS from this mapper should not change those responses.
#1303 is related. It makes malformed params for spec methods return -32602 instead of -32601, which fixes the 404, but with the current mapper those responses become HTTP 400.
Describe the bug
On the modern (
2026-07-28) Streamable HTTP path,jsonrpc_http_statusmaps everyErrorCode::INVALID_PARAMS(-32602) response to HTTP 400. This includes ordinary application errors that the spec defines as-32602and that are not transport validation failures:resources/readfor a resource that does not exist (resources: Error Handling)prompts/getwith an unknown prompt name or a missing required argument (prompts: Error Handling)tools/callfor an unknown tool (tools: Error Handling)The 2026-07-28 Streamable HTTP spec requires HTTP 400 only for
HeaderMismatch,UnsupportedProtocolVersionError, andMissingRequiredClientCapabilityError, and HTTP 404 for-32601. Its backward-compatibility rules tell a client that receives HTTP 400 to inspect the body and fall back toinitializewhen it is not one of those recognized modern errors. A spec-compliant client could therefore treat a 400 carrying a "resource not found"-32602as a legacy server and downgrade.To Reproduce
ServerHandlerwhoseread_resourcereturnsErr(ErrorData::resource_not_found(...))(orErrorData::invalid_params(...)fromget_prompt/call_tool).2026-07-28that reaches the handler.-32602.Expected behavior
Handler-generated
-32602errors should be sent with HTTP 200 as in-band JSON-RPC errors, like other handler errors thatjsonrpc_http_statusdoes not map.Logs
Found with rmcp 3.5.0 by MCPJam's
modern-resource-not-found-invalid-paramscheck (@mcpjam/sdk8.20.1, profilemcp-protocol@2026-08-21.1), which expects an in-band error. The official conformance suite (0.2.0-alpha.11) does not check the status code for this case.Additional context
The mapping is in
jsonrpc_http_status:https://github.com/modelcontextprotocol/rust-sdk/blob/8f9a28ecdb5f/crates/rmcp/src/transport/streamable_http_server/tower.rs#L646-L660
Transport-generated malformed-request errors, such as a missing
protocolVersionin_meta, already build their HTTP 400 response directly ininvalid_params_jsonrpc_response, so removingINVALID_PARAMSfrom this mapper should not change those responses.#1303 is related. It makes malformed params for spec methods return
-32602instead of-32601, which fixes the 404, but with the current mapper those responses become HTTP 400.