fix(mcp): repair stdio startup diagnostics - #3563
Open
joestump wants to merge 2 commits into
Open
Conversation
exec.Cmd.Args already carries argv0 as its first element, and
exec.CommandContext prepends Path as argv0 itself — so stdioCheck's
diagnostic re-run built "npx npx -y pkg" from "npx -y pkg". Rather than
the child's real startup error, the joined error carried whatever that
malformed command produced ("cannot execute binary file", as the shell
tries to exec itself as a script), which is precisely the output
stdioCheck exists to replace.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
maybeStdioErr exists so that a stdio server which dies during startup reports why — it re-runs the command and joins the child's output onto the bare io.EOF the SDK surfaces. It has not been able to do that since transports started being decorated: createSession wraps every transport in a channelTransport before Connect, so the *mcp.CommandTransport type assertion never matches and the function returns the bare EOF unchanged. A missing `npx`, or node not on PATH, reports "EOF" and throws the child's stderr away. Peel the decorators before asserting. The unwrap goes through a transportWrapper interface rather than a type switch on channelTransport so that adding another decorator cannot silently reintroduce this — a wrapper that does not implement it is the only way back to the bug. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 tasks
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.
Two independent bugs that leave
maybeStdioErrunable to do its job, so a stdio MCP server that dies during startup reportsEOFand nothing else. Split out of #3522 at a reviewer's convenience — neither has anything to do with that PR's feature.The symptom
Misconfigure a stdio server — a package name that doesn't exist, or node not on
PATH:{ "mcp": { "demo": { "type": "stdio", "command": "npx", "args": ["-y", "not-a-real-package"] } } }The server shows as errored with
EOF. The child's actual complaint is discarded, which is the exact casestdioCheckwas written for.Bug 1 — the diagnostic re-run duplicates argv0
exec.Cmd.Argsalready carries argv0 as its first element, andexec.CommandContextprependsPathas argv0 itself. PassingArgsthrough whole therefore re-runsnpx npx -y pkg:So on the occasions the diagnostic did run, it reported that malformed command's failure — typically
cannot execute binary file, the shell trying to exec itself as a script — instead of the child's real output.Bug 2 — the transport is never unwrapped
createSessionwraps every transport in achannelTransportbeforeConnect, and the error path then hands that wrapper tomaybeStdioErr:The
transport.(*mcp.CommandTransport)assertion inside can therefore never match, and the function returns the bareEOFunchanged. Every stdio startup diagnostic has been silently dead since transports started being decorated.The unwrap goes through a small
transportWrapperinterface rather than a type switch onchannelTransport, so a future decorator cannot quietly reintroduce this — failing to implement the interface is the only way back to the bug.Notes
TestStdioCheck_DoesNotDuplicateArgv0,TestMaybeStdioErr_UnwrapsChannelTransport, andTestMaybeStdioErr_UnwrapsEveryWrapper(which stacks a second, test-local decorator to pin that the unwrap peels the whole chain rather than a fixed number of layers).go test -race -failfast ./...andgolangci-lint runare green on this branch.main.🤖 Posted on behalf of
@joestumpbyclaude-opus-5using Claude Code.