You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
CLI's long-lived stream path can crash with an uncaught EPIPE #2412
v2 — current (@modelcontextprotocol/inspector@latest)
Which client?
CLI
Inspector version
2.7.0 (git tag) — static code-review finding, not run locally
Node version
N/A — static code review, no live run performed
Operating system (and browser, for the web client)
N/A — static code review
Transport
stdio
MCP server under inspection
N/A — this is a static code-review finding against the 2.7.0 tag source (clients/cli/src/handlers/consume-outcome.ts), not a live reproduction against a running MCP server.
Steps to reproduce
Found via static review of the 2.7.0 tag source, not a live run.
clients/cli/src/handlers/consume-outcome.ts carries its own TODO(#1432) acknowledging the stream path doesn't yet handle EPIPE/stdout errors.
A very ordinary shell habit — piping CLI output to head, grep, or less — closes stdout early.
Against a subscribe/stream method, this can throw an uncaught EPIPE and crash the CLI process instead of exiting cleanly.
I have not run mcp-inspector --cli <server> --method <a subscribe/stream method> | head -n 5 live to observe the crash; flagging based on the code's own acknowledged TODO. I reviewed issue #1432 and this looks like a narrower, still-open gap within that broader CLI-v2 epic, not a duplicate of it.
Expected behavior
Piping CLI output into a program that exits early (head, grep, less) against a subscribe/stream method causes the CLI to exit cleanly — the same common pattern that's already handled for non-streaming output.
Actual behavior
The long-lived stream consumer path (clients/cli/src/handlers/consume-outcome.ts) does not yet handle EPIPE/stdout errors, per its own TODO(#1432), so it can throw an uncaught EPIPE and crash instead of exiting gracefully.
Suggested fix: handle EPIPE on stdout (a common pattern: swallow it and exit 0) in the long-lived stream consumer path referenced by TODO(#1432).
Logs, errors, or screenshots
No response
Already prototyped a fix?
No response
Before you submit
I searched existing issues and this is not a duplicate.
This is not a security vulnerability report (those go through the private advisory process).
I prototyped this locally from v2/main, following the repo's issues-only contribution policy (no PR opened or branch pushed).
Prompt used to drive the local prototype
Work on MCP Inspector issue #2412 on v2/main. Reproduce the long-lived CLI stdout EPIPE path, implement a minimal scoped fix that treats EPIPE as clean completion while propagating non-EPIPE stdout errors, clean up the subscription plus SIGINT/SIGTERM/stdout listeners on every exit, and add CLI tests that meet the repository's per-file 90% coverage gate. Follow the repository contribution policy and do not open a PR.
Before
The long-lived stream branch registers only signal handlers. A closed downstream stdout pipe has no error listener, so an EPIPE can become an uncaught stream error.
Prototype behavior
Register the stdout error listener before starting the subscription.
On EPIPE, stop the subscription, remove the signal/stdout listeners, and resolve normally.
On another stdout error, perform the same cleanup and reject into the CLI's existing error path.
If subscription setup throws, remove all newly registered listeners before propagating the failure.
I reproduced the failure path at unit level by emitting an EPIPE on process.stdout; the regression test verifies clean completion, one unsubscribe call, and listener cleanup. Separate tests cover non-EPIPE propagation and setup failure cleanup.
Root npm run format: pass with no unrelated source changes
I also attempted npm run local:gate. It reached the root script tests, then failed/hung in unrelated Windows scripts/gate-lease.test.mjs cases (for example, a test expected POSIX /x/y but received C:\x\y, and signal-exit assertions expected numeric POSIX exit codes). I stopped that stalled gate after capturing the failures; the CLI-scoped checks and coverage above are green.
Activity
cliffhall commentedon Sep 18, 2026
Triage: Priority Medium (total 7)
head/less); the workaround is to avoid pipingTODO(#1432), so it has been known and deferredbuglabelLabels applied at triage:
v2.Board: #28, Status
Incoming(awaiting maintainer review — no milestone yet).SeojunKim-pumisj commentedon Sep 21, 2026
I prototyped this locally from
v2/main, following the repo's issues-only contribution policy (no PR opened or branch pushed).Prompt used to drive the local prototype
Before
The long-lived
streambranch registers only signal handlers. A closed downstream stdout pipe has no error listener, so anEPIPEcan become an uncaught stream error.Prototype behavior
EPIPE, stop the subscription, remove the signal/stdout listeners, and resolve normally.I reproduced the failure path at unit level by emitting an
EPIPEonprocess.stdout; the regression test verifies clean completion, one unsubscribe call, and listener cleanup. Separate tests cover non-EPIPEpropagation and setup failure cleanup.Verification (Windows, Node 22.20.0)
consume-outcome.ts: 97.36% statements, 90% branches, 100% functions, 100% linesnpm run format: pass with no unrelated source changesI also attempted
npm run local:gate. It reached the root script tests, then failed/hung in unrelated Windowsscripts/gate-lease.test.mjscases (for example, a test expected POSIX/x/ybut receivedC:\x\y, and signal-exit assertions expected numeric POSIX exit codes). I stopped that stalled gate after capturing the failures; the CLI-scoped checks and coverage above are green.cliffhall commentedon Sep 24, 2026
Fixed by #2479 (merged to v2/main in eec0349).
Merge v2/main (64a50d6) into v2/mcpi-client
fix(cli): end the long-lived stream cleanly on EPIPE (modelcontextpro…