Two malformed-request shapes are answered with -32602 INVALID_PARAMS where JSON-RPC 2.0 requires a different code. Both are still present on main (checked against the 2.2.0 wheel).
1. A body that is valid JSON but not a JSON-RPC message → -32600, not -32602
mcp/server/streamable_http.py (1.28.1: lines 501-505; 2.2.0: 591-593) catches the JSONRPCMessage.model_validate ValidationError and passes INVALID_PARAMS to _create_error_response:
except ValidationError as e: # pragma: no cover
response = self._create_error_response(
f"Validation error: {str(e)}",
HTTPStatus.BAD_REQUEST,
INVALID_PARAMS, # <- a non-Request is INVALID_REQUEST, not invalid params
)
Request: POST /mcp with {"hello": "world"} → {"code": -32602, "message": "Validation error: 11 validation errors for JSONRPCMessage…"}. There are no params to be invalid, so -32600 INVALID_REQUEST is the correct code (-32700 would suit a body that does not parse as JSON at all, which this path already handles separately).
2. An unknown method → -32601, not -32602
mcp/shared/session.py (1.28.1: line 390) catches any exception from self._receive_request_type.model_validate(...) in _receive_loop and answers:
except Exception as e:
error_response = JSONRPCError(
jsonrpc="2.0",
id=message.message.root.id,
error=ErrorData(code=INVALID_PARAMS, message="Invalid request parameters", data=""),
)
An unknown method fails the ClientRequest union exactly like a bad-params request does, so it is reported as invalid params and never reaches Server._handle_request, where the else branch already returns METHOD_NOT_FOUND (server/lowlevel/server.py, ~line 801). Request: {"jsonrpc":"2.0","id":7,"method":"no/such/method","params":{}} → -32602; JSON-RPC 2.0 requires -32601 (and the existing -32602 for a known method with bad params must be preserved).
Why it matters
A client that mis-types a method is told its params are wrong, which is not the failure it has. Discovery makes it worse: /.well-known/oauth-authorization-server advertises authorization_endpoint, and spec-conformance suites that pin the JSON-RPC codes go red against a server that is otherwise healthy.
What we did locally
INVALID_REQUEST for case 1 (matching the SDK's own "Validation error:" prefix) and a read-stream filter for case 2 that answers -32601 for a method not in types.ClientRequestType, deriving the known set from the union. Both are monkeypatches because we did not want to fork; both would be unnecessary if the two call sites used the codes above. Server._handle_request's METHOD_NOT_FOUND branch suggests case 2 is unintentional.
Two malformed-request shapes are answered with
-32602 INVALID_PARAMSwhere JSON-RPC 2.0 requires a different code. Both are still present onmain(checked against the 2.2.0 wheel).1. A body that is valid JSON but not a JSON-RPC message →
-32600, not-32602mcp/server/streamable_http.py(1.28.1: lines 501-505; 2.2.0: 591-593) catches theJSONRPCMessage.model_validateValidationErrorand passesINVALID_PARAMSto_create_error_response:Request:
POST /mcpwith{"hello": "world"}→{"code": -32602, "message": "Validation error: 11 validation errors for JSONRPCMessage…"}. There are no params to be invalid, so-32600 INVALID_REQUESTis the correct code (-32700would suit a body that does not parse as JSON at all, which this path already handles separately).2. An unknown method →
-32601, not-32602mcp/shared/session.py(1.28.1: line 390) catches any exception fromself._receive_request_type.model_validate(...)in_receive_loopand answers:An unknown
methodfails theClientRequestunion exactly like a bad-params request does, so it is reported as invalid params and never reachesServer._handle_request, where theelsebranch already returnsMETHOD_NOT_FOUND(server/lowlevel/server.py, ~line 801). Request:{"jsonrpc":"2.0","id":7,"method":"no/such/method","params":{}}→-32602; JSON-RPC 2.0 requires-32601(and the existing-32602for a known method with bad params must be preserved).Why it matters
A client that mis-types a method is told its params are wrong, which is not the failure it has. Discovery makes it worse:
/.well-known/oauth-authorization-serveradvertisesauthorization_endpoint, and spec-conformance suites that pin the JSON-RPC codes go red against a server that is otherwise healthy.What we did locally
INVALID_REQUESTfor case 1 (matching the SDK's own"Validation error:"prefix) and a read-stream filter for case 2 that answers-32601for a method not intypes.ClientRequestType, deriving the known set from the union. Both are monkeypatches because we did not want to fork; both would be unnecessary if the two call sites used the codes above.Server._handle_request'sMETHOD_NOT_FOUNDbranch suggests case 2 is unintentional.