[BUG] Windows: npx-based MCP/plugin servers fail to connect (spawn EINVAL from .cmd shim)
Preflight Checklist
- [x] I have searched existing issues and this hasn't been reported yet
- [x] This is a single bug report (please file separate reports for different bugs)
- [x] I am using the latest version of Claude Code
What's Wrong?
On Windows, every plugin/MCP server launched via the bare command npx fails with "Failed to connect". claude mcp list shows the server as "Failed to connect". The two official-marketplace plugins that ship this config -- context7 and playwright -- are both broken out of the box.
Root cause: Claude Code spawns the MCP server command directly (no shell). On Windows, npx resolves to npx.cmd, and since Node.js 18.20.2 / 20.12.2 (the CVE-2024-27980 hardening) child_process.spawn throws EINVAL when asked to spawn a .cmd/.bat file without shell: true. So the server process never starts and the MCP handshake never begins.
This is environment-independent: it reproduces for any Windows user on Node >= 18.20.2 (all Node 22.x) with any MCP server configured as "command": "npx". Servers that launch a real .exe (e.g. serena-mcp-server) connect fine on the same machine, which isolates the cause to the .cmd-shim spawn.
What Should Happen?
npx-based MCP servers should connect successfully on Windows, the same as on macOS/Linux. Claude Code's MCP spawner should launch .cmd-shim commands (npx/npm/pnpm/yarn) via a shell on Windows -- either spawn with shell: true, or invoke as cmd.exe /c <command> <args...> -- per Node's post-CVE-2024-27980 guidance.
Error Messages/Logs
claude mcp list:
plugin:context7:context7: npx -y @upstash/context7-mcp - Failed to connect
plugin:playwright:playwright: npx @playwright/mcp@latest - Failed to connect
Minimal pure-Node reproduction of the underlying spawn failure:
> node -e "const{spawnSync}=require('child_process'); const r=spawnSync('npx.cmd',['--version'],{stdio:'pipe'}); console.log(r.error?r.error.code:'ok',(r.stdout||'').toString().trim());"
EINVAL
> node -e "const{spawnSync}=require('child_process'); const r=spawnSync('cmd',['/c','npx','--version'],{stdio:'pipe'}); console.log(r.error?r.error.code:'ok',(r.stdout||'').toString().trim());"
ok 11.4.2
Direct spawn of npx.cmd -> EINVAL. Routing through cmd /c -> works.
Steps to Reproduce
- On Windows 11 with Node.js >= 18.20.2 (e.g. Node 22.9.0), configure any MCP server with "command": "npx" -- or install the official
context7orplaywrightplugin, which ship this config:
{
"context7": {
"command": "npx",
"args": ["-y", "@upstash/context7-mcp"]
}
}
- Run: claude mcp list
- Observe: the npx-based server(s) show "Failed to connect".
Deterministic root-cause repro (no Claude Code needed):
- Run: node -e "const{spawnSync}=require('child_process'); console.log(spawnSync('npx.cmd',['--version'],{stdio:'pipe'}).error?.code)"
- Observe: EINVAL (whereas
cmd /c npx --versionsucceeds)
Workaround that fixes it: change the config to
"command": "cmd", "args": ["/c", "npx", "-y", "@upstash/context7-mcp"]
Claude Model
None
Is this a regression?
Yes, this worked in a previous version
Last Working Version
not sure
Claude Code Version
2.1.177 (Claude Code)
Platform
Anthropic API
Operating System
Windows
Terminal/Shell
PowerShell
Additional Information
Affected official-marketplace plugins out of the box: context7 and playwright (both use "command": "npx").
Why the fix belongs in Claude Code's spawner rather than per-plugin config: the cmd /c workaround must be applied to each plugin's .mcp.json (both the loaded cache copy and the marketplace source copy) and can be reverted by a future plugin update. Spawning .cmd shims via shell on Windows fixes every affected user at once.
Suggested fixes (in priority order):
A. On Windows, spawn MCP server commands with shell: true (aligns with Node's post-CVE-2024-27980 guidance).
B. Rewrite the launch to cmd.exe /c <command> <args...>.
Secondary: when a spawn throws EINVAL on Windows for a .cmd target, surface a clearer error than "Failed to connect".
Note on a misleading secondary symptom: on a cold npx cache, npx -y <pkg> must download the package, which can exceed the 30s MCP connection timeout and show "connection timed out after 30000ms". That is separate -- even with a fully warmed cache the EINVAL failure persists, so warming the cache does not fix it.
Environment:
Claude Code 2.1.177 | Windows 11 (10.0.26200.8524) | Node v22.9.0 | npm 11.4.2 | npx 11.4.2
This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗