Fix MCP JSON-RPC parse error -32700
Keep stdout protocol-only and move banners, logs, and diagnostics to stderr.
Connect OfflineCreator with OAuthTreat -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.
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.
- Model Context Protocol: MCP stdio transport, 2026-07-28 revision
- JSON-RPC Working Group: JSON-RPC 2.0 Specification
- RFC Editor: RFC 8259: The JavaScript Object Notation Data Interchange Format
- Model Context Protocol TypeScript SDK: Serve over stdio
- Anubis MCP contributors: Streamable HTTP: valid-JSON-but-invalid-JSON-RPC returns -32700 Parse error instead of -32600/-32601
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.
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.
- Model Context Protocol: MCP stdio transport, 2026-07-28 revision
- RFC Editor: RFC 8259: The JavaScript Object Notation Data Interchange Format
- Model Context Protocol TypeScript SDK: Serve over stdio
- Model Context Protocol: Debugging Model Context Protocol integrations
- Microsoft Agent Governance Toolkit contributors: fix: parse MCP frames as bytes
- lopper contributors: Bug(mcp): framed requests accept multiple JSON values
Choose the next guide from the observed boundary
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.
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.
- Model Context Protocol: MCP stdio transport, 2026-07-28 revision
- JSON-RPC Working Group: JSON-RPC 2.0 Specification
- Microsoft Agent Governance Toolkit contributors: fix: parse MCP frames as bytes
- Anubis MCP contributors: Streamable HTTP: valid-JSON-but-invalid-JSON-RPC returns -32700 Parse error instead of -32600/-32601
- lopper contributors: Bug(mcp): framed requests accept multiple JSON values