fix(mcp): route tracing output to stderr to prevent JSON-RPC stdio corruption#470
Open
namesreallyblank wants to merge 1 commit into
Open
fix(mcp): route tracing output to stderr to prevent JSON-RPC stdio corruption#470namesreallyblank wants to merge 1 commit into
namesreallyblank wants to merge 1 commit into
Conversation
…rruption The ruvector-mcp binary initializes its tracing subscriber without specifying a writer, defaulting to stdout. Under the stdio MCP transport this contaminates the JSON-RPC frame stream with log lines, causing every @modelcontextprotocol/sdk client to throw a Zod parse error on the very first frame. Add .with_writer(std::io::stderr) to both the debug and release tracing subscriber builders in crates/ruvector-cli/src/mcp_server.rs. Verified by stdio smoke test: first line of stdout is now a valid JSON-RPC initialize response with serverInfo.name == "ruvector-mcp", and tracing output appears exclusively on stderr as required by the MCP stdio transport spec.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
The
ruvector-mcpbinary initializes its tracing subscriber without specifying a writer, causing it to default to stdout. Under the stdio MCP transport, this contaminates the JSON-RPC frame stream with log lines, breaking any client that parses stdout as newline-delimited JSON-RPC. This patch adds.with_writer(std::io::stderr)to both the debug and release tracing subscriber initialization branches.Root Cause
crates/ruvector-cli/src/mcp_server.rs:50-56—tracing_subscriber::fmt()without.with_writer(...)defaults tostd::io::stdout. When the binary starts with stdio transport, the first bytes on stdout are a tracing log line, not a JSON-RPC frame:Any MCP client doing
JSON.parse()on the first stdout line receives aSyntaxError.Reproduction
Before this fix, first stdout line is the tracing log. After this fix, first stdout line is a valid JSON-RPC initialize response.
MCP clients using
@modelcontextprotocol/sdkthrow a Zod parse error of this shape (observed in Claude Code's MCP logs):— the SDK is trying to validate the plain-text tracing line as a JSON-RPC frame.
Fix
Why this works
The MCP stdio transport specification (modelcontextprotocol.io/docs/concepts/transports#stdio) reserves stdout exclusively for newline-delimited JSON-RPC 2.0 frames. All diagnostic output — logs, traces, debug info — must go to stderr, which MCP clients either discard or surface as server-side diagnostics without attempting to parse as protocol frames. Routing
tracing_subscribertostd::io::stderrrestores correct protocol operation.Testing
Smoke test against the patched release binary:
Stdin:
Stdout (first and only line):
{"jsonrpc":"2.0","id":1,"result":{"capabilities":{"prompts":{},"resources":{},"tools":{}},"protocolVersion":"2024-11-05","serverInfo":{"name":"ruvector-mcp","version":"2.2.0"}}}Stderr (tracing, as expected):
Result: clean stdio. First and only stdout line is a valid JSON-RPC initialize response.
Related
fix(cli): add io-std to workspace tokio features) — same binary entrypoint areaci: add multi-platform release workflow for ruvector-mcp) — would enable the rebuilt binary to ship as a release asset with this fix included