AI Router · CLI · MCPCheapest eligible quotes before you create
troubleshooting · retention

Fix MCP JSON-RPC parse error -32700

Keep stdout protocol-only and move banners, logs, and diagnostics to stderr.

Connect OfflineCreator with OAuth
Output contact sheet

Treat -32700 as a wire-parsing failure

JSON-RPC reserves error code `-32700` for a parse error: the receiver could not parse the input as JSON. That is narrower than `-32600 Invalid Request`, where the input can be JSON but does not form a valid JSON-RPC request. The standard parse-error response uses `id: null` because the receiver may not be able to recover the request identifier. Start at the serialized stream, before investigating tool names, arguments, authorization, or generation behavior.

For MCP over stdio, stdout is the protocol channel. The current MCP stdio specification says that the server reads JSON-RPC from stdin, writes JSON-RPC to stdout, delimits each message with a newline, forbids embedded newlines, and must not put non-MCP output on stdout. It explicitly permits UTF-8 logging on stderr. A startup banner, debug print, progress spinner, stack trace, or dependency message on stdout can therefore make an otherwise healthy server appear to have a JSON parser failure.

Preserve the transport and the exact rejected bytes before assuming stdout pollution. A July 2026 Anubis MCP Streamable HTTP report showed a server returning `-32700` for syntactically valid JSON that failed JSON-RPC shape or method checks; that misclassification was later fixed upstream. If the captured payload already parses as JSON, continue with the Invalid Request and Method not found boundary instead of rewriting logging first.

Transport switch

Classify the first invalid bytes

Capture stdout and stderr separately in a development reproduction, then inspect the first stdout line that the client rejected. Do not combine the streams with `2>&1`, because that destroys the distinction this diagnosis depends on. Also avoid pasting credentials, prompts, or private tool results into a shared trace.

Plain-text banner before JSON
Move the writer to stderrReplace readiness announcements and debug prints on stdout with the runtime's stderr logger. Confirm the corrected capture contains the announcement only on stderr.
JSON spread across several physical lines
Serialize one compact message per linePretty printing inserts embedded newlines and breaks the stdio framing contract even when the complete multi-line text would be valid JSON.
Truncated JSON or two objects joined together
Inspect the serialization boundaryNeither form is one complete JSON-RPC message on one line. Preserve the rejected line and correct the code path that produced that exact output without inferring a cause from the symptom alone.
Failure appears only with non-ASCII text
Verify the JSON encodingRFC 8259 requires UTF-8 for JSON exchanged outside a closed ecosystem and prohibits a sender-added byte-order mark. The current MCP stdio binding remains newline-framed; it does not prescribe Content-Length framing.
Valid JSON with the wrong JSON-RPC shape
Diagnose the request contract insteadA wrong method type or malformed request object belongs to Invalid Request, not Parse error. If a host still shows `-32700`, preserve the payload and treat the code as potentially mislabeled rather than rewriting stdout first.
Compatibility stamp

Reproduce the stdio boundary outside the host

Use the MCP Inspector to launch the same executable and arguments that the failing client launches. The current TypeScript SDK guide gives the pattern `npx @modelcontextprotocol/inspector node ./build/server.js`; substitute your real server command without adding a shell banner or wrapper that changes the streams. Connect, trigger the smallest supported protocol interaction, and watch the protocol transcript separately from the server console.

A minimal failing sequence is diagnostic: stdout first emits `server starting`, then emits a valid JSON-RPC response. The first line is not JSON, so the client can fail before it reaches the response. The corrected sequence sends `server starting` to stderr and leaves only the compact JSON-RPC message plus its newline on stdout. The SDK's stdio documentation demonstrates this exact `console.log` versus `console.error` distinction; the general MCP debugging guide also directs local stdio logging to stderr.

Repeat the reproduction after removing the demonstrated stdout banner. If the failure remains, preserve the exact rejected line or bytes, the client and server versions, and whether the rejection appears before or after the first valid message. Timing is an observation, not a root-cause test: compare the captured content with the newline and JSON-RPC contracts, change one identified output defect at a time, and rerun the same interaction.

Failure trace

Fix framing, logging, and encoding independently

First, reserve stdout for the transport implementation. In Node.js, change an application banner from `console.log(...)` to `console.error(...)`; in Python, configure the logger with a stderr handler rather than printing diagnostics. Do not suppress useful diagnostics: redirect them to stderr and let the host decide whether to capture or display them. Re-capture both streams and verify the changed message no longer appears on stdout.

Second, serialize each JSON-RPC message without indentation and append one line delimiter. The current stdio specification defines one newline-delimited message per line and forbids embedded newlines. Compare the rejected line with a known valid serializer result instead of assigning an unseen writer or concurrency cause.

Third, apply RFC 8259's JSON encoding boundary: JSON exchanged outside a closed ecosystem uses UTF-8, and a network sender must not add a byte-order mark. This encoding rule comes from the JSON standard, not from a Content-Length requirement in the current MCP stdio binding. Recent open reports in other implementations describe Content-Length framing bugs: one compared UTF-8 byte length with JavaScript string length and stalled on Cyrillic payloads, and another accepted two JSON-RPC values inside one frame instead of returning `-32700`. Treat those only as implementation-specific anecdotes. Do not add Content-Length handling to a newline-framed server because of them, and do not infer that byte-length comparison or multi-value frames are a general MCP stdio repair.

Related circuit

Return to the troubleshooting directory when the captured output is valid JSON and the next failure is authentication, scope, credits, upload, provider, or output related. Use the GET 405 guide when the client is probing an HTTP endpoint with the wrong method; stdout cleanup cannot change an HTTP route contract. Use the connection-closed guide when the subprocess exits or the transport closes without emitting a malformed JSON line.

Canonical plate

Keep this fix inside the parse-error boundary

This page owns JSON-RPC `-32700` diagnosis with a reproducible stdio boundary and current JSON-RPC, MCP, and UTF-8 contracts. It does not claim that every client displays the same message, that a parse error always originates in server logging, or that an HTTP parse error should be repaired by changing stdout. Preserve the transport, direction, raw boundary, and exact code before applying the runbook.

Recent community retrieval was degraded: Digg was rate-limited, Reddit returned only a partial result after HTTP 429, and X was not configured. Instagram, TikTok, YouTube, GitHub, Hacker News, and web grounding did return items, but most were promotional, off-topic, or only weakly adjacent. The cited GitHub reports are explicitly qualified implementation anecdotes, including one closed Streamable HTTP misclassification later fixed upstream, and no popularity, prevalence, benchmark, client-compatibility, or product-behavior conclusion is drawn from them.