Skip to content

fix: don't respond to unparseable messages#940

Merged
DaleSeo merged 4 commits into
modelcontextprotocol:mainfrom
tsouth89:dont-respond-to-unparseable
Jul 1, 2026
Merged

fix: don't respond to unparseable messages#940
DaleSeo merged 4 commits into
modelcontextprotocol:mainfrom
tsouth89:dont-respond-to-unparseable

Conversation

@tsouth89

Copy link
Copy Markdown
Contributor

Fixes #938.

When rmcp receives invalid JSON on the stdio / async-read-write transport, it replies with {"jsonrpc":"2.0","id":null,"error":{"code":-32700,"message":"Parse error"}}. As reported in the issue, rmcp is the only official MCP SDK that responds to unparseable input, and responding can cause an error storm: if the peer treats the response itself as invalid data and answers with another error, the two sides bounce parse errors back and forth. The reporter hit this against a stdio server that accidentally wrote a log line to stdout.

This makes the transport ignore unparseable messages instead of responding. It still logs at debug and continues reading the next message, which matches the behavior of the other official SDKs. The now-unused ErrorData import is removed, and the existing test is updated to assert that no response is sent (and that the following valid message is still delivered).

Verified locally:

  • cargo test -p rmcp --features server,transport-async-rw (the updated receive_ignores_parse_error test passes)
  • cargo clippy -p rmcp --all-features --all-targets -- -D warnings
  • cargo +nightly fmt --check

@tsouth89 tsouth89 requested a review from a team as a code owner June 28, 2026 18:08
@github-actions github-actions Bot added T-core Core library changes T-transport Transport layer changes labels Jun 28, 2026
DaleSeo
DaleSeo previously approved these changes Jun 30, 2026

@DaleSeo DaleSeo left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the fix, @tsouth89. I left one comment.

@@ -143,16 +140,12 @@ where
Ok(Some(msg)) => return Some(msg),
Ok(None) => continue,
Err(JsonRpcMessageCodecError::Serde(e)) => {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should narrow the ignore path because serde_json::Error is not limited to invalid JSON syntax. It can also represent valid JSON that fails to deserialize into the role-specific RxJsonRpcMessage<Role> shape, which should remain a protocol error rather than disappearing.

@DaleSeo DaleSeo self-requested a review June 30, 2026 23:09
@DaleSeo DaleSeo dismissed their stale review June 30, 2026 23:09

mistake

Classify the serde error in the receive loop: syntax/EOF errors are
unparsable input with no correlatable id (issue modelcontextprotocol#938) and stay silent,
while data errors (valid JSON that doesn't match the message shape) are
real protocol errors and get an error response instead of being dropped.
Add a test covering the protocol-error path.
@tsouth89

tsouth89 commented Jul 1, 2026

Copy link
Copy Markdown
Contributor Author

Good catch, thanks. You're right that serde_json::Error conflates two cases. I've narrowed it: the receive loop now classifies the error, so only Category::Syntax and Category::Eof (genuinely unparsable input, where there's no id to correlate a reply to) are ignored per #938. Category::Data (valid JSON that doesn't match the RxJsonRpcMessage<Role> shape) is treated as a real protocol error and still gets an error response instead of disappearing. Added a test covering that path.

One open question: for the data-error case I kept the existing -32700 Parse error response for a minimal change, but -32600 Invalid Request is arguably the more accurate code for well-formed JSON with the wrong shape. Happy to switch to that if you'd prefer.

DaleSeo
DaleSeo previously approved these changes Jul 1, 2026

@DaleSeo DaleSeo left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One open question: for the data-error case I kept the existing -32700 Parse error response for a minimal change, but -32600 Invalid Request is arguably the more accurate code for well-formed JSON with the wrong shape. Happy to switch to that if you'd prefer.

@tsouth89 That sounds like the right fix. Could you make that change before we merge this? Thanks!

@tsouth89

tsouth89 commented Jul 1, 2026

Copy link
Copy Markdown
Contributor Author

Done. Switched the data-error case to -32600 Invalid Request and updated the test accordingly. Thanks for the review!

@DaleSeo DaleSeo merged commit 64d22de into modelcontextprotocol:main Jul 1, 2026
19 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

T-core Core library changes T-transport Transport layer changes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

rmcp shouldn't respond to unparseable messages

2 participants