Windows: `CLAUDE_CODE_SHELL_PREFIX` is used as the executable for stdio MCP server spawns, so a bash-script prefix kills the first connection ("The system cannot find the path specified.")

Status Open
Reported on v2.1.227
Maintainer reply None cached
Activity 0 comments · opened Sep 2, 2026

Summary

On Windows, when CLAUDE_CODE_SHELL_PREFIX points at a bash script (its documented purpose is to wrap Bash-tool commands), Claude Code also substitutes that value as the command for every stdio MCP server spawn, with the real command folded into a single quoted argument. When claude.exe is launched from a native lineage (Claude desktop app, PowerShell, Task Scheduler) the child is spawned through cmd.exe, which cannot execute a .sh file, so every stdio server's first connection fails with:

Server stderr: The system cannot find the path specified.
Connection failed after 247ms (-32000): MCP error -32000: Connection closed

Interactive sessions recover because a second spawn ~0.7 s later succeeds (that respawn does not go through the prefix), but the session banner still reports the server as failed. Headless claude -p sessions never recover: the server is absent for the whole session.

Environment

  • Claude Code CLI 2.1.227 (claude.exe, build 2026-08-10), Windows 11 Pro 10.0.26200
  • CLAUDE_CODE_SHELL_PREFIX=/c/Users/<user>/.claude/shell-prefix.sh (user-scope env var; a plain bash script that logs $* and evals it — works correctly for the Bash tool)
  • Any stdio MCP server; reproduced with node.exe <path>\@tobilu\qmd\dist\cli\qmd.js mcp and with a TradingView bridge server

Reproduction (headless, four cells)

| Launch shell | CLAUDE_CODE_SHELL_PREFIX | First stdio spawn |
|---|---|---|
| PowerShell | set (/c/... path) | fails, cannot find the path specified |
| PowerShell | removed for that process | connects in 565 ms |
| Git Bash | set | connects (MSYS resolves the path; the prefix script runs) |
| Git Bash | removed | connects |

Command used in every cell (cwd is an ordinary directory):

claude -p "Reply with exactly the word ok." --max-turns 1 --output-format text

then inspect %LOCALAPPDATA%\claude-cli-nodejs\Cache\<project>\mcp-logs-<server>\<newest>.jsonl.

Where it happens

In the 2.1.227 bundle the stdio transport constructor reads (two sibling copies):

let ae = process.env.CLAUDE_CODE_SHELL_PREFIX || t.command,
    K  = process.env.CLAUDE_CODE_SHELL_PREFIX ? [gf([t.command, ...t.args])] : t.args,
...
c = { command: ae, args: K, env: {...}, stderr: "pipe" }

whereas the Bash-tool path inserts the prefix inside the bash command string (ZEo(prefix, commandString)), which is why the same value works there.

Expected

Either (a) the prefix is applied to MCP spawns through the same shell the Bash tool uses (so a bash-script prefix works on Windows), or (b) the prefix is not applied to MCP spawns, or (c) the docs state that on Windows the prefix must be a natively executable file and that it affects MCP servers.

Related

  • #66262 — same error text from the VS Code extension's serve-mode spawn while the CLI works (closed, not planned; root cause not identified there). The lineage dependence reported there matches the PowerShell-vs-Git-Bash split above.

View original on GitHub ↗