Skip to content

Requests with "id": null silently misclassified as notifications #2057

Description

@clouatre

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 a JSONRPCNotification and the caller gets a 202 with no response.

This happens because of how JSONRPCMessage union resolution interacts with extra='allow':

  1. RequestId correctly excludes None (Annotated[int, Field(strict=True)] | str).
  2. JSONRPCRequest validation rejects id: null, working as intended.
  3. Pydantic falls through to JSONRPCNotification, which absorbs "id": None as an extra field via extra='allow'.
  4. 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 TypeAdapter and dropping extra='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")  # Expected

# JSONRPCMessage 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)

Activity

  1. clgtm commented on Feb 14, 2026

    @clgtm

    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/authprobe

    Funnel
      [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 a null ID and returned a 202 status 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 null IDs:

      "Request identifiers (id) SHALL be either strings or numbers. Requests containing id: null MUST 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 id must never be null.

    • Accepting a null ID 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 as 400 Bad Request or a JSON-RPC Error response 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 id must be string or number; null IDs 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 acceptable id types further to exclude null.

    Expected Correct Server Behavior

    1. Input Validation: On receiving a JSON-RPC request, the MCP server should check that the id field is either
  2. clgtm commented on Feb 14, 2026

    @clgtm

    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 a JSONRPCNotification and the caller gets a 202 with no response.

    This happens because of how JSONRPCMessage union resolution interacts with extra='allow':

    1. RequestId correctly excludes None (Annotated[int, Field(strict=True)] | str).
    2. JSONRPCRequest validation rejects id: null, working as intended.
    3. Pydantic falls through to JSONRPCNotification, which absorbs "id": None as an extra field via extra='allow'.
    4. 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 TypeAdapter and dropping extra='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") # Expected

    JSONRPCMessage 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

  3. added
    bugSomething isn't working
    improves spec complianceWhen a change improves ability of SDK users to comply with spec definition
    P2Moderate issues affecting some users, edge cases, potentially valuable feature
    on Feb 16, 2026
  4. added a commit that references this issue on Feb 17, 2026
    27d8adb
  5. BabyChrist666 commented on Feb 17, 2026

    @BabyChrist666
    Contributor

    Opened a PR to fix this: #2075

    The fix adds a model_validator(mode="before") on JSONRPCNotification that rejects any input containing an "id" field. Per JSON-RPC 2.0, a notification is a request without an id member — if "id" is present (regardless of value), it's a request, not a notification. This prevents Pydantic's union resolution from silently absorbing "id": null as an extra field on the Notification type.

    Includes 12 test cases covering null/int/str id rejection, both validate_python and validate_json paths, and verification that valid requests and notifications still parse correctly.

  6. clgtm commented on Feb 24, 2026

    @clgtm

    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 authprobe helpful, please star / share , much appreciated!

  7. maxisbey commented on Feb 25, 2026

    @maxisbey
    Contributor

    @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.

  8. clgtm commented on Feb 25, 2026

    @clgtm

    @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!

  9. shivama205 commented on Mar 8, 2026

    @shivama205
    Contributor

    I'll take this. The root cause is in Pydantic's union fallthrough — when JSONRPCRequest rejects an invalid id (e.g. null), the message falls through to JSONRPCNotification which silently absorbs the extra id field. The fix is to add a model_validator on JSONRPCNotification that rejects any input containing an id field, since notifications must not have one per JSON-RPC 2.0. Will have a PR up shortly.

  10. added a commit that references this issue on Mar 8, 2026
    3162a4c
  11. shivama205 commented on Mar 8, 2026

    @shivama205
    Contributor

    PR is up: #2251

  12. BabyChrist666 commented on Mar 9, 2026

    @BabyChrist666
    Contributor

    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 id field). Would love to avoid duplicating effort! 😊

  13. added 2 commits that reference this issue on Mar 9, 2026
    3ed0d8b
    1e1f0be
  14. shivama205 commented on Mar 9, 2026

    @shivama205
    Contributor

    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.

  15. talhayme commented on Oct 12, 2026

    @talhayme

    This still reproduces on mcp==2.3.0 (and 1.30.0), and it is wider than null. Decoding {"jsonrpc":"2.0","id":<id>,"method":"tools/list"} through JSONRPCMessage:

    id Parsed as
    42, "abc" request
    null notification
    1.0, 1e2 notification
    1.5 notification
    true, [1] notification

    So any id that is not an int or a string falls through to JSONRPCNotification, not just null. I checked message decoding only, not a live server.

    For comparison, on the same inputs the TypeScript SDK reads 1.0 and 1e2 as requests with ids 1 and 100 and 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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    P2Moderate issues affecting some users, edge cases, potentially valuable featurebugSomething isn't workingimproves spec complianceWhen a change improves ability of SDK users to comply with spec definition

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions