Describe the bug
When the Ruby SDK server receives a JSON-RPC message with an id and method: "notifications/cancelled", it returns a successful JSON-RPC response with result: null.
A valid MCP notification does not include an id and must not receive a response. If this id-bearing message is treated as a request, it should not be completed with a successful result: null response, because MCP result responses use an object-valued result.
- Environment
- Reproduced with stable release
v0.16.0 (e35841afe89d86c7905bbab0c9fbe1d3f075e8d0)
- Also reproduced on current
main (5518e8db032517896e0025119a9073f4aa946ba4) using a 2026-07-28-style request envelope
- Transport: stdio
To Reproduce
- Start a Ruby SDK MCP server over stdio.
- Send a
notifications/cancelled message with a JSON-RPC id:
{"jsonrpc":"2.0","id":1001,"method":"notifications/cancelled"}
// or
{"jsonrpc":"2.0","id":1001,"method":"notifications/cancelled","params":{"_meta":{"io.modelcontextprotocol/protocolVersion":"2026-07-28","io.modelcontextprotocol/clientInfo":{"name":"repro","version":"0.1.0"},"io.modelcontextprotocol/clientCapabilities":{}}}}
- Observe the response.
Expected behavior
The server should not emit a successful JSON-RPC result response for this message. A valid notifications/cancelled notification has no id and receives no response. If an id-bearing message for this notification-only method is treated as a request, it should be rejected with a JSON-RPC error rather than completed with result: null.
Logs
The server returns:
{"jsonrpc":"2.0","id":1001,"result":null}
The server remains healthy and continues to process subsequent requests.
Additional context
In lib/mcp/server.rb, notifications/cancelled is dispatched through a special-case handler:
if method == Methods::NOTIFICATIONS_CANCELLED
return ->(params) { handle_cancelled_notification(params, session: session) }
end
handle_cancelled_notification can return nil, for example when there's no session:
def handle_cancelled_notification(params, session: nil)
return unless session
# ...
end
That nil value then flows through the normal success-response path in JsonRpcHandler#process_request:
result = method.call(params)
return if result.equal?(NO_RESPONSE)
success_response(id: id, result: result) # result is nil → {"result": null}
JsonRpcHandler::NO_RESPONSE already exists for handlers that should not emit a JSON-RPC response, so this may be the appropriate sentinel for notification-style handling.
This appears related to #398 / #400, which preserved request ids for JSON-RPC envelope error responses in the same JsonRpcHandler area. This issue is different: the response id is preserved, but the message is completed as a successful response with result: null.
Describe the bug
When the Ruby SDK server receives a JSON-RPC message with an
idandmethod: "notifications/cancelled", it returns a successful JSON-RPC response withresult: null.A valid MCP notification does not include an
idand must not receive a response. If this id-bearing message is treated as a request, it should not be completed with a successfulresult: nullresponse, because MCP result responses use an object-valuedresult.v0.16.0(e35841afe89d86c7905bbab0c9fbe1d3f075e8d0)main(5518e8db032517896e0025119a9073f4aa946ba4) using a 2026-07-28-style request envelopeTo Reproduce
notifications/cancelledmessage with a JSON-RPCid:{"jsonrpc":"2.0","id":1001,"method":"notifications/cancelled"} // or {"jsonrpc":"2.0","id":1001,"method":"notifications/cancelled","params":{"_meta":{"io.modelcontextprotocol/protocolVersion":"2026-07-28","io.modelcontextprotocol/clientInfo":{"name":"repro","version":"0.1.0"},"io.modelcontextprotocol/clientCapabilities":{}}}}Expected behavior
The server should not emit a successful JSON-RPC
resultresponse for this message. A validnotifications/cancellednotification has noidand receives no response. If an id-bearing message for this notification-only method is treated as a request, it should be rejected with a JSON-RPC error rather than completed withresult: null.Logs
The server returns:
{"jsonrpc":"2.0","id":1001,"result":null}The server remains healthy and continues to process subsequent requests.
Additional context
In
lib/mcp/server.rb,notifications/cancelledis dispatched through a special-case handler:handle_cancelled_notificationcan returnnil, for example when there's no session:That
nilvalue then flows through the normal success-response path inJsonRpcHandler#process_request:JsonRpcHandler::NO_RESPONSEalready exists for handlers that should not emit a JSON-RPC response, so this may be the appropriate sentinel for notification-style handling.This appears related to #398 / #400, which preserved request ids for JSON-RPC envelope error responses in the same
JsonRpcHandlerarea. This issue is different: the response id is preserved, but the message is completed as a successful response withresult: null.