fix(agent): launch stdio MCP servers through cmd.exe and kill their child trees on Windows#201
Open
luminary19 wants to merge 3 commits into
Open
fix(agent): launch stdio MCP servers through cmd.exe and kill their child trees on Windows#201luminary19 wants to merge 3 commits into
luminary19 wants to merge 3 commits into
Conversation
Resolve the command via PATH/PATHEXT and spawn .exe/.com directly; only real .cmd/.bat shims route through cmd.exe, now with /d /v:off /s /c, CRT argument quoting plus double caret-escaping (npm-style %* shims re-parse args a second time), and windowsVerbatimArguments so Node does not re-quote. Neutralizes & | < > ^ % ! injection from semi-trusted MCP args. POSIX spawn is unchanged.
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
stdio MCP servers never started on Windows: their launchers (
npx.cmd/uvx.cmd)are
.cmdshims that Node 24 refuses to spawn without a shell, and on close onlythe direct child was signalled so the real server process leaked.
On win32 the client now:
.exe/.comtargets directly, with no shell — only genuine
.cmd/.batshims arerouted through
cmd.exe.cmd.exe /d /v:off /s /cwith proper Windows CRT argument quoting plus double caret-escaping and
windowsVerbatimArguments(so Node does not re-quote). The double escaping isrequired because npm-style shims forward with
%*, which triggers a secondcmd.exeparse — a single-pass escaper is still injectable.taskkill /PID <pid> /T /F.Other platforms keep the direct spawn + SIGTERM path unchanged. Fixes #200
Security
The first cut launched via
cmd.exe /c <command> <args…>with unescaped args,which a background review correctly flagged as command injection
(CVE-2024-27980 class): an MCP arg such as
a&calc.exe, sourced from asemi-trusted project
.mcp.json, would break out. The hardened launcher aboveneutralizes
& | < > ^ % !across both cmd.exe parses. Verified live onNode 24.16 by resolving the real
npx.CMDand confirming benign args passthrough while
a&calc.exe,x|y,%PATH%,!v!are all caret-escaped toliterals (e.g.
a&calc.exe→a^^^&calc.exe), socalc.exenever executes asa separate command. (Command authorization — a config naming
calc.exeas thecommand itself — remains a separate boundary, unchanged here.)
Validation
npm --prefix frontend run typecheckclean (covers the agent-runtime sources);check:quality(including the standalone build) runs as the pre-push gate. TheWindows launch behavior was verified live on Windows 11: a filesystem MCP server
over
npxconnects, its tools list and execute, and closing the session removesthe whole process tree.
UI changes
None.
Risks / rollout notes
.exe/.comMCP commands now bypass the shell entirely (stricter, correct).separate concern for the MCP-config trust flow.