Repository navigation
Conversation
…hout an id ## Motivation and Context A message carrying `"id": null` is treated as a notification: `JsonRpcHandler` accepts a null id, the method runs, and no response is produced, so over Streamable HTTP the client gets `202 Accepted` for a `tools/call` whose write it never hears about (issue modelcontextprotocol#601). MCP narrows JSON-RPC here: a request MUST carry a string or integer id and the id MUST NOT be null. The TypeScript server refuses such a message at schema validation with 400 and the Python server parses it as a notification it has no handler for and drops it; neither runs it. A request method sent with no id member at all, a notification by JSON-RPC rules, was run the same way. A null id now fails the message with `-32600` Invalid Request, carrying a null id as the only id there is, and the method does not run; the Streamable HTTP transport treats such a message as a request, so the error reaches the client with 200 on the legacy lifecycle and 400 on the modern one, instead of a 202. A protocol request method sent without an id is not run either, and gets no response, as a notification; a custom method keeps running without an id, since `define_custom_method` serves notifications too. The invalid-id message no longer names null as accepted. Fixes modelcontextprotocol#601. ## How Has This Been Tested? `test/json_rpc_handler_test.rb` (null id refused without calling the method, the message without a pattern), `test/mcp/server_test.rb` (null id and id-less `tools/call` do not run the tool, a custom method still runs without an id) and `test/mcp/server/transports/streamable_http_transport_test.rb` (null id answered with the error on both lifecycles, an id-less `tools/call` acknowledged without running, an id-less `ping` acknowledged with no response). Against the previous library the null-id and id-less tests run the tool. ## Breaking Changes A client sending `"id": null` no longer has the method executed and gets `-32600`, as the TypeScript server already refused it; a request method sent without an id, framed as a notification, is no longer executed either, and gets no response, as the TypeScript and Python servers already dropped it.
koic
force-pushed
the
reject_requests_with_a_null_id
branch
from
October 15, 2026 08:24
9e5f6cd to
e20b7e4
Compare
atesgoral
approved these changes
Oct 15, 2026
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Motivation and Context
A message carrying
"id": nullis treated as a notification:JsonRpcHandleraccepts a null id, the method runs, and no response is produced, so over Streamable HTTP the client gets202 Acceptedfor atools/callwhose write it never hears about (issue #601). MCP narrows JSON-RPC here: a request MUST carry a string or integer id and the id MUST NOT be null. The TypeScript server refuses such a message at schema validation with 400 and the Python server parses it as a notification it has no handler for and drops it; neither runs it. A request method sent with no id member at all, a notification by JSON-RPC rules, was run the same way.A null id now fails the message with
-32600Invalid Request, carrying a null id as the only id there is, and the method does not run; the Streamable HTTP transport treats such a message as a request, so the error reaches the client with 200 on the legacy lifecycle and 400 on the modern one, instead of a 202. A protocol request method sent without an id is not run either, and gets no response, as a notification; a custom method keeps running without an id, sincedefine_custom_methodserves notifications too. The invalid-id message no longer names null as accepted.Fixes #601.
How Has This Been Tested?
test/json_rpc_handler_test.rb(null id refused without calling the method, the message without a pattern),test/mcp/server_test.rb(null id and id-lesstools/calldo not run the tool, a custom method still runs without an id) andtest/mcp/server/transports/streamable_http_transport_test.rb(null id answered with the error on both lifecycles, an id-lesstools/callacknowledged without running, an id-lesspingacknowledged with no response).Against the previous library the null-id and id-less tests run the tool.
Breaking Changes
A client sending
"id": nullno longer has the method executed and gets-32600, as the TypeScript server already refused it; a request method sent without an id, framed as a notification, is no longer executed either, and gets no response, as the TypeScript and Python servers already dropped it.Types of changes
Checklist