You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Stdio client stops processing responses after rejecting a malformed response #1157
The stdio client stops processing responses after receiving either of these malformed JSON-RPC responses:
A response without jsonrpc
A response containing both result and error
The SDK logs a validation error, but the affected tool call does not fail immediately. It times out, and a subsequent ping on the same client also times out.
I reproduced both cases three times with SDK 2.0.1. Streamable HTTP rejected the same responses and continued processing requests normally.
This is separate from #1156, where a response with "jsonrpc": "1.0" is accepted.
Environment
MCP Java SDK: 2.0.1
Java: 21.0.9
Maven: 3.9.11
Protocol version: 2025-11-25
Client: Synchronous
Transport: stdio
Server: mcp-failure-lab@0.11.0
OS: Linux arm64 container
Spring AI: Not used
Vector store: Not applicable
Steps to reproduce
Initialize a synchronous Java SDK client over stdio.
Send a tool request.
Return a response with the matching request ID that either:
omits jsonrpc, or
contains both result and error.
Observe that the tool call times out.
Call ping on the same client.
Observe that ping also times out.
For the missing jsonrpc case, the client logs:
Cannot construct instance of io.modelcontextprotocol.spec.McpSchema$JSONRPCResponse:
jsonrpc must not be empty
For the response containing both fields, it logs:
Cannot construct instance of io.modelcontextprotocol.spec.McpSchema$JSONRPCResponse:
MCP responses MUST either have a result or error
Expected behavior
The malformed response should fail its matching request without stopping the stdio client from processing later messages. A subsequent ping should complete normally.
I have a fix for this and will open a PR. Here is the scope so you can redirect me before review.
The change is in StdioClientTransport only. A message that fails to deserialize no longer ends the inbound loop. If the line is a response with a usable id, the transport emits a JSON-RPC error response for that id, so the matching request fails at once and no longer waits for its timeout. A line that is not JSON is logged and skipped. Later messages are still read.
Two choices I would like your view on:
Error code. I used INTERNAL_ERROR because the fault is in the peer's response and not in a request. INVALID_REQUEST or PARSE_ERROR are arguable.
The test starts a server process that sends a response without jsonrpc, a response with both result and error, a line that is not JSON, and then a valid response. It checks that the two malformed responses fail their requests and the valid one still arrives. All mcp-core tests pass locally. I did not run the Docker and npx based suites.
Activity
hpdkhoa commentedon Oct 2, 2026
I have a fix for this and will open a PR. Here is the scope so you can redirect me before review.
The change is in StdioClientTransport only. A message that fails to deserialize no longer ends the inbound loop. If the line is a response with a usable id, the transport emits a JSON-RPC error response for that id, so the matching request fails at once and no longer waits for its timeout. A line that is not JSON is logged and skipped. Later messages are still read.
Two choices I would like your view on:
The test starts a server process that sends a response without jsonrpc, a response with both result and error, a line that is not JSON, and then a valid response. It checks that the two malformed responses fail their requests and the valid one still arrives. All mcp-core tests pass locally. I did not run the Docker and npx based suites.