Skip to content

fix: prevent server crash on JSON-RPC requests with non-scalar id#72

Open
fuleinist wants to merge 1 commit into
21st-dev:mainfrom
fuleinist:fix/jsonrpc-id-validation
Open

fix: prevent server crash on JSON-RPC requests with non-scalar id#72
fuleinist wants to merge 1 commit into
21st-dev:mainfrom
fuleinist:fix/jsonrpc-id-validation

Conversation

@fuleinist

Copy link
Copy Markdown

Summary

When the MCP SDK receives a JSON-RPC request whose id field is a non-scalar (e.g. an object like {"bad":"id"}), the SDK creates an error response that either drops the id (if it was undefined) or causes JSON.stringify to throw when it encounters a non-serialisable object id.

The crash path:

  1. Session.handleRequest() creates an error response with the invalid id
  2. JSON.stringify() throws (or omits id leaving an invalid response)
  3. StdioServerTransport emits an 'error' event
  4. The top-level .catch() handler calls cleanup()process.exit(0)
  5. Server is dead; all subsequent requests on that connection fail

The server also crashes on valid-but-unsupported id types like boolean.

Fix

Install a process.stdout.write guard before the MCP server connects the transport. Every JSON string written to stdout is intercepted, parsed, and validated. If the id field in a JSON-RPC response is not a valid JSON-RPC type (string | number | null), it is replaced with null before the string reaches the OS-level stdout buffer.

This is a belt-and-suspenders guard that is completely harmless for valid responses (no-op) and prevents the crash for the invalid-id path.

Before (crash):

{"jsonrpc":"2.0","id":{"bad":"id"},"error":{"code":-32600,"message":"Invalid Request"}}

After (safe):

{"jsonrpc":"2.0","id":null,"error":{"code":-32600,"message":"Invalid Request"}}

Changes

  • src/utils/safe-transport.ts: New module with installStdoutWriteGuard() — patches process.stdout.write to validate and sanitise JSON-RPC response id fields
  • src/index.ts: Call installStdoutWriteGuard() before server.connect(transport)
  • src/utils/safe-transport.test.ts: 21 tests covering isValidId, looksLikeJsonRpcResponse, and sanitiseJsonRpcId

Testing

npm test → 21/21 tests green (2 test suites)

Issue

Fixes #71.

When the MCP SDK receives a JSON-RPC request whose 'id' field is a
non-scalar (e.g. an object), it creates an error response that either
drops the id (if id was undefined) or crashes the server when
JSON.stringify throws on a non-serialisable object id.

The crash path: Session.handleRequest() creates an error response with
the invalid id, JSON.stringify() throws, the StdioServerTransport emits an
'error' event, the top-level error handler calls cleanup(), which calls
process.exit(0).

Fix: install a process.stdout.write guard BEFORE the MCP server connects
the transport. Every JSON string written to stdout is intercepted, parsed,
and validated. If the 'id' field in a JSON-RPC response is not a valid
JSON-RPC type (string | number | null), it is replaced with null before
the string reaches the OS-level stdout buffer.

This is a belt-and-suspenders guard: it is harmless for valid responses
(no-op) and prevents the crash for the invalid-id path.

Fixes: 21st-dev#71
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Server crashes on a JSON-RPC request with a non-scalar id

1 participant