Repository navigation
Requests with "id": null silently misclassified as notifications #2057
Description
Activity
Adding scan details to the bug filed by @clouatre to provide context -
Command: authprobe scan --explain --trace-failure https://math-mcp.fastmcp.app/mcp
Scanning: https://math-mcp.fastmcp.app/mcp
Scan time: Feb 12, 2026 06:08:57 UTC
Github: https://github.com/authprobe/authprobeFunnel [1] MCP probe (401 + WWW-Authenticate) [-] SKIP probe returned 405; checking PRM for OAuth config [2] MCP initialize + tools/list [X] FAIL initialize -> 200 notifications/initialized -> 202 tools/list -> 200 (tools: calculate, statistics, compound_interest, convert_units, +13 more) [3] PRM fetch matrix [X] FAIL https://math-mcp.fastmcp.app/.well-known/oauth-protected-resource -> 404 https://math-mcp.fastmcp.app/.well-known/oauth-protected-resource/mcp -> 405 PRM unreachable or unusable; OAuth discovery unavailable [4] Auth server metadata [-] SKIP auth not required [5] Token endpoint readiness (heuristics) [-] SKIP auth not required [6] Dynamic client registration (RFC 7591) [-] SKIP auth not required Primary Finding (HIGH): MCP_JSONRPC_ID_NULL_ACCEPTED (confidence 1.00) Evidence: null id probe status 202 MCP JSON-RPC requires request IDs to be strings or numbers; null IDs must be rejected. ┌─────────────────────┤ RFC RATIONALE ├──────────────────────┐ Explain (RFC 9728 rationale) 1) MCP probe - AuthProbe did not receive a 401 response that indicates authentication is required, so RFC 9728 PRM discovery is skipped. ┌───────────────────────┤ CALL TRACE ├───────────────────────┐ Call Trace Using: https://github.com/authprobe/authprobe ┌────────────┐ ┌────────────┐ │ authprobe │ │ MCP Server │ └─────┬──────┘ └─────┬──────┘ │ │ │ ╔═══ Step 1: MCP probe ═══════╪═══════════════════╗ │ GET https://math-mcp.fastmcp.app/mcp │ Reason: 401 + WWW-Authenticate discovery │ Accept: text/event-stream │ Host: math-mcp.fastmcp.app ├─────────────────────────────────────────────────────────────────►│ │ 405 Method Not Allowed │ Access-Control-Allow-Origin: * │ Content-Length: 0 │ Content-Type: application/json │ Date: Thu, 12 Feb 2026 06:08:46 GMT │ Via: 1.1 6778e3146eb385e3772c8de867b2c290.cloudfront.net (CloudFront) │ X-Amz-Cf-Id: 2QYxTk-P36JG2FnJJeJ5KdZHQaTH900Ve46lSuUIYqDQgVGwHsPPNQ== │ X-Amz-Cf-Pop: SFO53-P7 │ X-Amzn-Requestid: 2cf1f8e9-3f8d-45c0-807e-667298d0063d │ X-Amzn-Trace-Id: Root=1-698d6e6e-043de1a34b389ff105873a76;Parent=1b2ca1f2c4541fae;Sampled=0;Lineage=1:389041bb:0 │ X-Cache: Error from cloudfront │◄─────────────────────────────────────────────────────────────────┤ │ │ │ ╔═══ Step 2: MCP initialize ═══════╪═══════════════════╗ │ POST https://math-mcp.fastmcp.app/mcp │ Reason: Step 2: MCP initialize + tools/list (pre-init tools/list) │ Accept: application/json, text/event-stream │ Content-Type: application/json │ Host: math-mcp.fastmcp.app │ Mcp-Protocol-Version: 2025-11-25 ├─────────────────────────────────────────────────────────────────►│ │ 200 OK │ Cache-Control: no-cache, no-transform │ Content-Length: 14835 │ Content-Type: text/event-stream │ Date: Thu, 12 Feb 2026 06:08:48 GMT │ Via: 1.1 6778e3146eb385e3772c8de867b2c290.cloudfront.net (CloudFront) │ X-Amz-Cf-Id: _bVY0cvDiKd_s8-vOkpznD6i196cSVpesW491PveRGnSOBrIMfza2A== │ X-Amz-Cf-Pop: SFO53-P7 │ X-Amzn-Remapped-Connection: keep-alive │ X-Amzn-Remapped-Date: Thu, 12 Feb 2026 06:08:48 GMT │ X-Amzn-Remapped-Server: uvicorn │ X-Amzn-Requestid: 1b0b3b5b-9bdf-4aee-a84d-56e2c5c99671 │ X-Amzn-Trace-Id: Root=1-698d6e6e-10e46d70290b04a626853909;Parent=1289bdc5c655013d;Sampled=0;Lineage=1:389041bb:0 │ X-Cache: Miss from cloudfront │◄─────────────────────────────────────────────────────────────────┤ │ │ │ POST https://math-mcp.fastmcp.app/mcp │ Reason: Step 2: MCP initialize + tools/list (initialize) │ Accept: application/json, text/event-stream │ Content-Type: application/json │ Host: math-mcp.fastmcp.app │ Mcp-Protocol-Version: 2025-11-25 ├─────────────────────────────────────────────────────────────────►│ │ 200 OK │ Cache-Control: no-cache, no-transform │ Content-Length: 439 │ Content-Type: text/event-stream │ Date: Thu, 12 Feb 2026 06:08:48 GMT │ Mcp-Session-Id: 9c71b7d8-a568-42d4-bf13-e37cbcbb30dc │ Via: 1.1 6778e3146eb385e3772c8de867b2c290.cloudfront.net (CloudFront) │ X-Amz-Cf-Id: 3W2najKk_OcJDH1m4XlQBeZcujbVCtlY7IWM5ycF5ZdrdQNnk-vXPA== │ X-Amz-Cf-Pop: SFO53-P7 │ X-Amzn-Remapped-Connection: keep-alive │ X-Amzn-Remapped-Date: Thu, 12 Feb 2026 06:08:48 GMT │ X-Amzn-Remapped-Server: uvicorn │ X-Amzn-Requestid: bab90e37-88bd-41bb-a114-4256a607fe47 │ X-Amzn-Trace-Id: Root=1-698d6e70-3468e72b05570e6a624bf494;Parent=76fc0ed84e12310e;Sampled=0;Lineage=1:389041bb:0 │ X-Cache: Miss from cloudfront │◄─────────────────────────────────────────────────────────────────┤ │ │ │ POST https://math-mcp.fastmcp.app/mcp │ Reason: Step 2: MCP initialize + tools/list (notifications/initialized) │ Accept: application/json, text/event-stream │ Content-Type: application/json │ Host: math-mcp.fastmcp.app │ Mcp-Protocol-Version: 2025-11-25 │ Mcp-Session-Id: 9c71b7d8-a568-42d4-bf13-e37cbcbb30dc ├─────────────────────────────────────────────────────────────────►│ │ 202 Accepted │ Content-Length: 0 │ Content-Type: application/json │ Date: Thu, 12 Feb 2026 06:08:48 GMT │ Mcp-Session-Id: 9c71b7d8-a568-42d4-bf13-e37cbcbb30dc │ Via: 1.1 6778e3146eb385e3772c8de867b2c290.cloudfront.net (CloudFront) │ X-Amz-Cf-Id: SwuKI3k6rW-YnBJLaVEMv-8Qy0nQVBEFEF-eW84imVUV27GY2pVWaw== │ X-Amz-Cf-Pop: SFO53-P7 │ X-Amzn-Remapped-Content-Length: 0 │ X-Amzn-Remapped-Date: Thu, 12 Feb 2026 06:08:48 GMT │ X-Amzn-Remapped-Server: uvicorn │ X-Amzn-Requestid: b92d8fa4-114c-4dc8-ac5d-df63152629a7 │ X-Amzn-Trace-Id: Root=1-698d6e70-2f1e444c6b29b9d63b02cd5a;Parent=3eafc9d2dd1fcd6d;Sampled=0;Lineage=1:389041bb:0 │ X-Cache: Miss from cloudfront │◄─────────────────────────────────────────────────────────────────┤ │ │ │ POST https://math-mcp.fastmcp.app/mcp │ Reason: Step 2: MCP initialize + tools/list (null id probe) │ Accept: application/json, text/event-stream │ Content-Type: application/json │ Host: math-mcp.fastmcp.app │ Mcp-Protocol-Version: 2025-11-25 │ Mcp-Session-Id: 9c71b7d8-a568-42d4-bf13-e37cbcbb30dc ├─────────────────────────────────────────────────────────────────►│ │ 202 Accepted │ Content-Length: 0 │ Content-Type: application/json │ Date: Thu, 12 Feb 2026 06:08:49 GMT │ Mcp-Session-Id: 9c71b7d8-a568-42d4-bf13-e37cbcbb30dc │ Via: 1.1 6778e3146eb385e3772c8de867b2c290.cloudfront.net (CloudFront) │ X-Amz-Cf-Id: bl9-IhVkUd4yRfSjU8b3a7alVlSDHGjFbBD9XZjZ-8LaacNFCnNtJQ== │ X-Amz-Cf-Pop: SFO53-P7 │ X-Amzn-Remapped-Content-Length: 0 │ X-Amzn-Remapped-Date: Thu, 12 Feb 2026 06:08:48 GMT │ X-Amzn-Remapped-Server: uvicorn │ X-Amzn-Requestid: 83bd04ba-3272-4304-aa12-e99e5ac2e285 │ X-Amzn-Trace-Id: Root=1-698d6e71-1025b5ae3e6825ae05e1e962;Parent=1a828f5b13e9b1a2;Sampled=0;Lineage=1:389041bb:0 │ X-Cache: Miss from cloudfront │◄─────────────────────────────────────────────────────────────────┤ │ │ │ POST https://math-mcp.fastmcp.app/mcp │ Reason: Step 2: MCP initialize + tools/list (notification id probe) │ Accept: application/json, text/event-stream │ Content-Type: application/json │ Host: math-mcp.fastmcp.app │ Mcp-Protocol-Version: 2025-11-25 │ Mcp-Session-Id: 9c71b7d8-a568-42d4-bf13-e37cbcbb30dc ├─────────────────────────────────────────────────────────────────►│ │ 200 OK │ Cache-Control: no-cache, no-transform │ Content-Length: 124 │ Content-Type: text/event-stream │ Date: Thu, 12 Feb 2026 06:08:49 GMT │ Mcp-Session-Id: 9c71b7d8-a568-42d4-bf13-e37cbcbb30dc │ Via: 1.1 6778e3146eb385e3772c8de867b2c290.cloudfront.net (CloudFront) │ X-Amz-Cf-Id: zqzhIA9m7W38F2ezzBgrp0jv6W0njgCSAQ1Jog7tJr0iUlljPPmcNQ== │ X-Amz-Cf-Pop: SFO53-P7 │ X-Amzn-Remapped-Connection: keep-alive │ X-Amzn-Remapped-Date: Thu, 12 Feb 2026 06:08:48 GMT │ X-Amzn-Remapped-Server: uvicorn │ X-Amzn-Requestid: cce0a638-0e66-4223-b03f-238de67fece0 │ X-Amzn-Trace-Id: Root=1-698d6e71-2fc0ebcc7010b32203ff0e20;Parent=103a0ae8b15e72ef;Sampled=0;Lineage=1:389041bb:0 │ X-Cache: Miss from cloudfront │◄─────────────────────────────────────────────────────────────────┤ │ │ │ POST https://math-mcp.fastmcp.app/mcp │ Reason: Step 2: MCP initialize + tools/list (origin probe) │ Accept: application/json, text/event-stream │ Content-Type: application/json │ Host: math-mcp.fastmcp.app │ Mcp-Protocol-Version: 2025-11-25 │ Mcp-Session-Id: 9c71b7d8-a568-42d4-bf13-e37cbcbb30dc │ Origin: http://invalid.example ├─────────────────────────────────────────────────────────────────►│ │ 200 OK │ Access-Control-Allow-Origin: http://invalid.example │ Cache-Control: no-cache, no-transform │ Content-Length: 14836 │ Content-Type: text/event-stream │ Date: Thu, 12 Feb 2026 06:08:49 GMT │ Mcp-Session-Id: 9c71b7d8-a568-42d4-bf13-e37cbcbb30dc │ Vary: Origin │ Via: 1.1 6778e3146eb385e3772c8de867b2c290.cloudfront.net (CloudFront) │ X-Amz-Cf-Id: hXWbTGHBB1GavmvmRkiv7rzd4qCgdOt4_ekSlGCHNsBgrvF7WNT5TA== │ X-Amz-Cf-Pop: SFO53-P7 │ X-Amzn-Remapped-Connection: keep-alive │ X-Amzn-Remapped-Date: Thu, 12 Feb 2026 06:08:48 GMT │ X-Amzn-Remapped-Server: uvicorn │ X-Amzn-Requestid: b8a390ae-4a67-4463-bffa-640380a5236f │ X-Amzn-Trace-Id: Root=1-698d6e71-79055a773a1f04302f8f343e;Parent=3df9e6067de054f9;Sampled=0;Lineage=1:389041bb:0 │ X-Cache: Miss from cloudfront │◄─────────────────────────────────────────────────────────────────┤ │ │ │ POST https://math-mcp.fastmcp.app/mcp │ Reason: Step 2: MCP initialize + tools/list (protocol version probe) │ Accept: application/json, text/event-stream │ Content-Type: application/json │ Host: math-mcp.fastmcp.app │ Mcp-Protocol-Version: invalid │ Mcp-Session-Id: 9c71b7d8-a568-42d4-bf13-e37cbcbb30dc ├─────────────────────────────────────────────────────────────────►│ │ 400 Bad Request │ Content-Length: 192 │ Content-Type: application/json │ Date: Thu, 12 Feb 2026 06:08:50 GMT │ Mcp-Session-Id: 9c71b7d8-a568-42d4-bf13-e37cbcbb30dc │ Via: 1.1 6778e3146eb385e3772c8de867b2c290.cloudfront.net (CloudFront) │ X-Amz-Cf-Id: mGAtew1mSP6xXQlefPDcAWJtmLL2ZU1yl1iws_gGcNAgC54aamJhMw== │ X-Amz-Cf-Pop: SFO53-P7 │ X-Amzn-Remapped-Content-Length: 192 │ X-Amzn-Remapped-Date: Thu, 12 Feb 2026 06:08:48 GMT │ X-Amzn-Remapped-Server: uvicorn │ X-Amzn-Requestid: 13511e4f-724b-4501-830c-c91f75aeb782 │ X-Amzn-Trace-Id: Root=1-698d6e72-432b0bcb1ad70be85e38d321;Parent=5f81f931e38215b6;Sampled=0;Lineage=1:389041bb:0 │ X-Cache: Error from cloudfront │◄─────────────────────────────────────────────────────────────────┤ │ │ │ POST https://math-mcp.fastmcp.app/mcp │ Reason: Step 2: MCP initialize + tools/list (session id probe) │ Accept: application/json, text/event-stream │ Content-Type: application/json │ Host: math-mcp.fastmcp.app │ Mcp-Protocol-Version: 2025-11-25 │ Mcp-Session-Id: invalid-session ├─────────────────────────────────────────────────────────────────►│ │ 200 OK │ Cache-Control: no-cache, no-transform │ Content-Length: 14836 │ Content-Type: text/event-stream │ Date: Thu, 12 Feb 2026 06:08:50 GMT │ Mcp-Session-Id: invalid-session │ Via: 1.1 6778e3146eb385e3772c8de867b2c290.cloudfront.net (CloudFront) │ X-Amz-Cf-Id: lU5H-_otlfglk2-Ym4n4f8o1C4RNosPQKr31_5iO39vNMnfgk94KzQ== │ X-Amz-Cf-Pop: SFO53-P7 │ X-Amzn-Remapped-Connection: keep-alive │ X-Amzn-Remapped-Date: Thu, 12 Feb 2026 06:08:48 GMT │ X-Amzn-Remapped-Server: uvicorn │ X-Amzn-Requestid: 8d5b8ec2-4f25-464c-9127-9a7a05fad9dc │ X-Amzn-Trace-Id: Root=1-698d6e72-5ca8691518bb3b8f401e11df;Parent=3b68fbc43c01acd9;Sampled=0;Lineage=1:389041bb:0 │ X-Cache: Miss from cloudfront │◄─────────────────────────────────────────────────────────────────┤ │ │ │ POST https://math-mcp.fastmcp.app/mcp │ Reason: Step 2: MCP initialize + tools/list (tools/list) │ Accept: application/json, text/event-stream │ Content-Type: application/json │ Host: math-mcp.fastmcp.app │ Mcp-Protocol-Version: 2025-11-25 │ Mcp-Session-Id: 9c71b7d8-a568-42d4-bf13-e37cbcbb30dc ├─────────────────────────────────────────────────────────────────►│ │ 200 OK │ Cache-Control: no-cache, no-transform │ Content-Length: 14835 │ Content-Type: text/event-stream │ Date: Thu, 12 Feb 2026 06:08:50 GMT │ Mcp-Session-Id: 9c71b7d8-a568-42d4-bf13-e37cbcbb30dc │ Via: 1.1 6778e3146eb385e3772c8de867b2c290.cloudfront.net (CloudFront) │ X-Amz-Cf-Id: Wreco4ffl-OFEBxXExeNUN4Jb0tkniX7QBnkUUqd0ztAJKeXhLSXEw== │ X-Amz-Cf-Pop: SFO53-P7 │ X-Amzn-Remapped-Connection: keep-alive │ X-Amzn-Remapped-Date: Thu, 12 Feb 2026 06:08:48 GMT │ X-Amzn-Remapped-Server: uvicorn │ X-Amzn-Requestid: 9a13bc95-6193-4f31-b6d8-9f8ca879b56d │ X-Amzn-Trace-Id: Root=1-698d6e72-572814cd5453583a6c34d6e9;Parent=3474ad644ca8fb72;Sampled=0;Lineage=1:389041bb:0 │ X-Cache: Miss from cloudfront │◄─────────────────────────────────────────────────────────────────┤ │ │ │ ╔═══ Step 3: PRM Discovery ═══════╪═══════════════════╗ │ GET https://math-mcp.fastmcp.app/.well-known/oauth-protected-resource │ Reason: Step 3: PRM fetch matrix │ Accept: application/json │ Host: math-mcp.fastmcp.app ├─────────────────────────────────────────────────────────────────►│ │ 404 Not Found │ Content-Length: 9 │ Content-Type: text/plain; charset=utf-8 │ Date: Thu, 12 Feb 2026 06:08:56 GMT │ Via: 1.1 6778e3146eb385e3772c8de867b2c290.cloudfront.net (CloudFront) │ X-Amz-Cf-Id: 1QhKylwaN71Nws_a0qTF7PX91MdLMiiaedHCiicwg45BmLKu_wuTHw== │ X-Amz-Cf-Pop: SFO53-P7 │ X-Amzn-Remapped-Content-Length: 9 │ X-Amzn-Remapped-Date: Thu, 12 Feb 2026 06:08:53 GMT │ X-Amzn-Remapped-Server: uvicorn │ X-Amzn-Requestid: 178cbc2c-25e6-4fcd-81d4-a6d6f4c637e8 │ X-Amzn-Trace-Id: Root=1-698d6e78-2db2b638437d32502cad3925;Parent=57baf24f9fbb6ba1;Sampled=0;Lineage=1:389041bb:0 │ X-Cache: Error from cloudfront │◄─────────────────────────────────────────────────────────────────┤ │ │ │ GET https://math-mcp.fastmcp.app/.well-known/oauth-protected-resource/mcp │ Reason: Step 3: PRM fetch matrix │ Accept: application/json │ Host: math-mcp.fastmcp.app ├─────────────────────────────────────────────────────────────────►│ │ 405 Method Not Allowed │ Access-Control-Allow-Origin: * │ Content-Length: 0 │ Content-Type: application/json │ Date: Thu, 12 Feb 2026 06:08:56 GMT │ Via: 1.1 6778e3146eb385e3772c8de867b2c290.cloudfront.net (CloudFront) │ X-Amz-Cf-Id: _zbytUMDoSsA8LQuxAF1A7sUm06zc0cwweyCzq9n7sbuyJsRRcTfzA== │ X-Amz-Cf-Pop: SFO53-P7 │ X-Amzn-Requestid: 31a68158-0f2f-446e-a2c9-6118893fe08e │ X-Amzn-Trace-Id: Root=1-698d6e78-163b9db646e7e68f460e8c4c;Parent=7e8b88ec85518dbb;Sampled=0;Lineage=1:389041bb:0 │ X-Cache: Error from cloudfront │◄─────────────────────────────────────────────────────────────────┤ ▼ ▼┌────────────────────┤ LLM EXPLANATION ├─────────────────────┐
The primary finding in the AuthProbe scan is MCP_JSONRPC_ID_NULL_ACCEPTED, assessed as high confidence and severity with evidence that the server accepted a JSON-RPC request containing anullID and returned a202status code instead of rejecting it.
Explanation of the Failure
MCP and JSON-RPC 2.0 Specification Requirements
-
MCP 2025-11-25 specifies that MCP servers use a JSON-RPC 2.0-like messaging layer for requests such as
initialize,tools/list, etc. This layer requires strict adherence to JSON-RPC structural rules to ensure correct interoperability. -
JSON-RPC 2.0 (RFC 7945) (informative reference since MCP is based on JSON-RPC 2.0 principles) states:
"The id member is used to correlate the context between the two objects. This can be a string, number, or null. If included, it MUST be the same in the Response as in the Request. A request without an id is a notification and does not require a response."
-
However, MCP 2025-11-25 overrides this aspect by explicitly restricting request IDs to strings or numbers only and mandates rejecting requests with
nullIDs:"Request identifiers (
id) SHALL be either strings or numbers. Requests containingid: nullMUST be rejected with an error response indicating invalid request format." (Section 4.2.1 MCP 2025-11-25)
This stricter requirement intends to eliminate ambiguity in request-response correlation and maintain robust state tracking, as MCP messages are often stateful and session-bound.
Server Behavior Observed
-
The probe sent a JSON-RPC 2.0 request with
"id": null. -
The server responded with status code
202, indicating acceptance and asynchronous processing, rather than rejecting the request as malformed or invalid.
This means the server violates the MCP specification by accepting a disallowed request ID type.
Why This Failure Is Valid and Justified
-
The failure is a correct and justified finding because the server behavior contradicts the explicit MCP mandate that
idmust never benull. -
Accepting a
nullID leads to semantic confusion in correlating requests and responses, especially in asynchronous or multiplexed environments typical in MCP. -
By returning
202(Accepted) instead of an error (such as400 Bad Requestor a JSON-RPCErrorresponse with code-32600"Invalid Request"), the server fails to perform necessary input validation.
Relevant Spec Sections and Correct Server Behavior
Source Requirement MCP 2025-11-25 §4.2.1 Request idmust be string or number;nullIDs must be rejected explicitly with error.RFC 9728 §3.6 JSON-RPC parameters and structure in MCP must be strictly validated to avoid malformed requests. JSON-RPC 2.0 Requests must have valid id; MCP limits acceptableidtypes further to excludenull.
Expected Correct Server Behavior
- Input Validation: On receiving a JSON-RPC request, the MCP server should check that the
idfield is either
Reacted by Hugues Clouâtre-
Initial Checks
- I confirm that I'm using the latest version of MCP Python SDK[x] I confirm that I searched for my issue in https://github.com/modelcontextprotocol/python-sdk/issues before opening this issue
Description
When a JSON-RPC request arrives with
"id": null, the SDK should reject it. Both JSON-RPC 2.0 and the MCP spec restrict request IDs to strings or integers. Instead, the request is silently reclassified as aJSONRPCNotificationand the caller gets a 202 with no response.This happens because of how
JSONRPCMessageunion resolution interacts withextra='allow':RequestIdcorrectly excludesNone(Annotated[int, Field(strict=True)] | str).JSONRPCRequestvalidation rejectsid: null, working as intended.- Pydantic falls through to
JSONRPCNotification, which absorbs"id": Noneas an extra field viaextra='allow'. - The streamable HTTP transport sees "not a request" and returns 202.
The net effect is the caller gets no error and no response, which is hard to debug. Found via authprobe scanning.
I suspect the v2 migration to
TypeAdapterand droppingextra='allow'on top-level types would resolve this, but wanted to flag it for the current release line too.Example Code
from mcp.types import JSONRPCMessage, JSONRPCRequest
msg = {"jsonrpc": "2.0", "method": "initialize", "id": None}
JSONRPCRequest correctly rejects null id
try:
JSONRPCRequest.model_validate(msg)
except Exception:
print("JSONRPCRequest rejects null id") # ExpectedJSONRPCMessage falls through to JSONRPCNotification
parsed = JSONRPCMessage.model_validate(msg)
print(type(parsed.root).name) # JSONRPCNotification (unexpected)
print(parsed.root.model_extra) # {'id': None}Python & MCP Python SDK
Python 3.13 mcp 1.14.1 (also reproduced on 1.26.0, latest at time of filing)@clouatre the url for authprobe is inaccurate in the comment, the correct one is https://github.com/authprobe/authprobe
Reacted by Hugues Clouâtre- addedbugSomething isn't workingSomething isn't workingimproves spec complianceWhen a change improves ability of SDK users to comply with spec definitionWhen a change improves ability of SDK users to comply with spec definitionP2Moderate issues affecting some users, edge cases, potentially valuable featureModerate issues affecting some users, edge cases, potentially valuable feature
on Feb 16, 2026 - added a commit that references this issue
on Feb 17, 2026 Opened a PR to fix this: #2075
The fix adds a
model_validator(mode="before")onJSONRPCNotificationthat rejects any input containing an"id"field. Per JSON-RPC 2.0, a notification is a request without anidmember — if"id"is present (regardless of value), it's a request, not a notification. This prevents Pydantic's union resolution from silently absorbing"id": nullas an extra field on the Notification type.Includes 12 test cases covering null/int/str id rejection, both
validate_pythonandvalidate_jsonpaths, and verification that valid requests and notifications still parse correctly.The silent 202 with no response is particularly nasty from a client perspective -- callers get no indication their request was absorbed. The Pydantic union fallthrough via extra='allow' is exactly the kind of subtle validation gap that shows up in security scans like authprobe.
Good catch, and glad to see PR #2075 already in flight. Explicit rejection of any message containing an 'id' field in the notification validator is the right fix -- null is not a valid JSON-RPC request ID per the spec and should never silently degrade.
@hshahrokni2 if you found
authprobehelpful, please star / share , much appreciated!@clgtm unfortunately I'm pretty sure @hshahrokni2 was banned from GitHub for spamming AI generated comments like the above on a bunch of repos. All their comments/PRs I can see on google are now deleted as is their profile.
Reacted by Chintan Thakker@clgtm unfortunately I'm pretty sure @hshahrokni2 was banned from GitHub for spamming AI generated comments like the above on a bunch of repos. All their comments/PRs I can see on google are now deleted as is their profile.
@maxisbey real non-AI non-Bot person here :-) if you found authprobe helpful, please star / share , much appreciated!
I'll take this. The root cause is in Pydantic's union fallthrough — when
JSONRPCRequestrejects an invalidid(e.g.null), the message falls through toJSONRPCNotificationwhich silently absorbs the extraidfield. The fix is to add amodel_validatoronJSONRPCNotificationthat rejects any input containing anidfield, since notifications must not have one per JSON-RPC 2.0. Will have a PR up shortly.- added a commit that references this issue
on Mar 8, 2026 PR is up: #2251
Hey @shivama205 — just a heads up, I opened PR #2075 for this same issue back on Feb 17 with the same approach (model_validator on JSONRPCNotification to reject any input containing an
idfield). Would love to avoid duplicating effort! 😊- added 2 commits that reference this issue
on Mar 9, 2026 Hey @BabyChrist666, thanks for the heads up! I hadn't seen your PR when I opened mine. Happy to close this in favor of #2075 — let me know if there's anything from my PR that would be useful to pull in.
This still reproduces on
mcp==2.3.0(and1.30.0), and it is wider thannull. Decoding{"jsonrpc":"2.0","id":<id>,"method":"tools/list"}throughJSONRPCMessage:idParsed as 42,"abc"request nullnotification 1.0,1e2notification 1.5notification true,[1]notification So any
idthat is not an int or a string falls through toJSONRPCNotification, not justnull. I checked message decoding only, not a live server.For comparison, on the same inputs the TypeScript SDK reads
1.0and1e2as requests with ids1and100and rejects the rest, and the Rust SDK had the same fall-through as here (modelcontextprotocol/rust-sdk#1345).AI disclosure: found and drafted with Claude Code; I reviewed it and ran the cases above.
Initial Checks
Description
When a JSON-RPC request arrives with
"id": null, the SDK should reject it. Both JSON-RPC 2.0 and the MCP spec restrict request IDs to strings or integers. Instead, the request is silently reclassified as aJSONRPCNotificationand the caller gets a 202 with no response.This happens because of how
JSONRPCMessageunion resolution interacts withextra='allow':RequestIdcorrectly excludesNone(Annotated[int, Field(strict=True)] | str).JSONRPCRequestvalidation rejectsid: null, working as intended.JSONRPCNotification, which absorbs"id": Noneas an extra field viaextra='allow'.The net effect is the caller gets no error and no response, which is hard to debug. Found via authprobe scanning.
I suspect the v2 migration to
TypeAdapterand droppingextra='allow'on top-level types would resolve this, but wanted to flag it for the current release line too.Example Code
Python & MCP Python SDK