Initial Checks
Description
When the Python SDK stdio server receives an id-bearing JSON-RPC request with a deeply nested params object, the server can log a stream validation error and remain alive, but the original request is never completed with either a JSON-RPC result or a JSON-RPC error. I am filing this as a stdio response-completion/robustness issue.
In the reproduction below, a post-initialization ping request first failed receiving a response at depth 256. The server stayed responsive: follow-up shallow pings succeeded, and the process did not exit. The observable problem is that the original request id remains pending until the client-side timeout.
This also does not appear to be the same response-correlation issue where the server returns an error with id:"" or another mismatched concrete id. If the parser cannot recover the original request id, a JSON-RPC parse error with id:null would be the conventional response. MCP's typed schema models error-response id as optional string | number, so omitting the id would also be easier to reason about than leaving the request pending. On stdio, I observed an error notification and stderr validation message, but not a JSON-RPC error response frame for the original request id. From a normal JSON-RPC request/response client's perspective, the request remains incomplete.
The most relevant issue I found is #1333, but it appears to describe a different stdio responsiveness issue: slow message processing can block subsequent input. In this reproduction, the server remains responsive to later shallow pings, while the original malformed/deep request is left pending.
When processing the deeply nested stdio request, the server emitted a validation message like this:
Received exception from stream: 1 validation error for JSONRPCMessage
Invalid JSON: recursion limit exceeded
The server also emitted an MCP logging notification similar to:
{"jsonrpc":"2.0","method":"notifications/message","params":{"level":"error","logger":"mcp.server.exception_handler","data":"Internal Server Error"}}
That notification shows that the server observed the parser/validation failure, but it does not complete the pending request id.
For comparison, the Python Streamable HTTP servers did not show this pending-request behavior for the same deeply nested ping request. At depth 256 and above, both stateful and stateless HTTP returned a JSON-RPC parse error promptly, for example:
{"jsonrpc":"2.0","id":null,"error":{"code":-32700,"message":"Parse error: recursion limit exceeded ..."}}
That HTTP behavior is not what I am reporting here. I am using it only as a baseline: the stdio path leaves the request pending, while the HTTP path returns a bounded parse error.
Example Code
# Run the stdio server, complete a normal initialization phase, then send a deeply nested `ping` request like this:
import json
depth = 256
params = {"leaf": True}
for i in reversed(range(depth)):
params = {f"p{i}": params}
print(json.dumps({
"jsonrpc": "2.0",
"id": 900256,
"method": "ping",
"params": params,
}))
'''
Expected: a JSON-RPC result or JSON-RPC error response that completes request id `900256`, or some other documented bounded failure mode.
Observed:
Depth 32: response returned successfully.
Depth 64: response returned successfully.
Depth 128: response returned successfully.
Depth 256: no response for the request id within 7 seconds; later shallow ping succeeded.
Depth 512+: same pending-request behavior; later shallow pings still succeeded.
'''
Python & MCP Python SDK
- Python: 3.12.3 on Ubuntu 24.04
- MCP Python SDK stable release: `v1.27.2` (`62137874ff26dd74d2fea80ff528a7fd9ca7a5e7`)
- MCP Python SDK `main` snapshot (fetched 2026-06-01): `616476f6927a5c64213ea97bbd36a7466f410775`
Initial Checks
Description
When the Python SDK stdio server receives an id-bearing JSON-RPC request with a deeply nested
paramsobject, the server can log a stream validation error and remain alive, but the original request is never completed with either a JSON-RPC result or a JSON-RPC error. I am filing this as a stdio response-completion/robustness issue.In the reproduction below, a post-initialization
pingrequest first failed receiving a response at depth 256. The server stayed responsive: follow-up shallow pings succeeded, and the process did not exit. The observable problem is that the original request id remains pending until the client-side timeout.This also does not appear to be the same response-correlation issue where the server returns an error with
id:""or another mismatched concrete id. If the parser cannot recover the original request id, a JSON-RPC parse error withid:nullwould be the conventional response. MCP's typed schema models error-responseidas optionalstring | number, so omitting the id would also be easier to reason about than leaving the request pending. On stdio, I observed an error notification and stderr validation message, but not a JSON-RPC error response frame for the original request id. From a normal JSON-RPC request/response client's perspective, the request remains incomplete.The most relevant issue I found is #1333, but it appears to describe a different stdio responsiveness issue: slow message processing can block subsequent input. In this reproduction, the server remains responsive to later shallow pings, while the original malformed/deep request is left pending.
When processing the deeply nested stdio request, the server emitted a validation message like this:
The server also emitted an MCP logging notification similar to:
{"jsonrpc":"2.0","method":"notifications/message","params":{"level":"error","logger":"mcp.server.exception_handler","data":"Internal Server Error"}}That notification shows that the server observed the parser/validation failure, but it does not complete the pending request id.
For comparison, the Python Streamable HTTP servers did not show this pending-request behavior for the same deeply nested
pingrequest. At depth 256 and above, both stateful and stateless HTTP returned a JSON-RPC parse error promptly, for example:{"jsonrpc":"2.0","id":null,"error":{"code":-32700,"message":"Parse error: recursion limit exceeded ..."}}That HTTP behavior is not what I am reporting here. I am using it only as a baseline: the stdio path leaves the request pending, while the HTTP path returns a bounded parse error.
Example Code
Python & MCP Python SDK