[BUG] Windows: plugin-shipped MCP servers using bare `npx` fail with `spawn ENOENT` (LSP fix from #17312 missed the MCP spawn path)
Summary
Plugin-shipped stdio MCP servers that use bare npx as their command (the default for most official plugins) fail with spawn npx ENOENT on Windows. The LSP spawn fix from #17312 (landed in v2.1.132) addressed the equivalent bug for LSP plugins but didn't extend to the MCP server spawn path — so MCP plugins are still broken in the same way.
This affects every official plugin I tested whose .mcp.json / plugin.json ships "command": "npx".
Affected official plugins (as of 2.1.139)
All of these Failed to connect until I manually wrapped each with cmd /c:
| Plugin | Config location | Command shipped |
|---|---|---|
| context7@claude-plugins-official | .mcp.json | npx -y @upstash/context7-mcp |
| chrome-devtools-mcp@claude-plugins-official | .claude-plugin/plugin.json (inline) | npx chrome-devtools-mcp@latest |
| azure@claude-plugins-official | .mcp.json | npx -y @azure/mcp@latest server start |
| mongodb@claude-plugins-official | mcp.json (referenced from plugin.json) | npx -y mongodb-mcp-server@1 |
| desktop-commander@claude-plugins-official | .claude-plugin/plugin.json (inline) | npx -y @wonderwhy-er/desktop-commander@latest |
| playwright@claude-plugins-official | .mcp.json | npx @playwright/mcp@latest |
Reproducer (no Claude Code needed)
const { spawn } = require('child_process');
const p = spawn('npx', ['-y', '@upstash/context7-mcp', '--version'], { stdio: 'inherit' });
p.on('error', e => console.error('SPAWN ERROR:', e.code, e.message));
On Windows 10/11, Node 20.x or 22.x:
SPAWN ERROR: ENOENT spawn npx ENOENT
With { shell: true } (or wrapping in cmd /c), it succeeds immediately:
2.2.5
Root cause
On Windows, npx is the batch shim npx.cmd. Node's child_process.spawn only resolves .cmd/.bat extensions via PATHEXT when shell: true (or shell: 'cmd.exe') is set, or when the caller passes the explicit .cmd filename. This is the same Node-on-Windows pattern that #17312 fixed for LSP server spawning in v2.1.132.
Expected behavior
The MCP server spawn path should use shell: true (or equivalent) on process.platform === 'win32' — matching whatever the LSP path now does post-#17312.
Workaround (fragile)
Edit each plugin's manifest under ~/.claude/plugins/cache/<marketplace>/<plugin>/<version>/... to wrap the command:
- "command": "npx",
- "args": ["-y", "@upstash/context7-mcp"]
+ "command": "cmd",
+ "args": ["/c", "npx", "-y", "@upstash/context7-mcp"]
This works (verified all 6 plugins above now show ✓ Connected), but gets wiped whenever the plugin updates, since the patched files live in the plugin cache rather than in any user-owned config.
Note: the location of the MCP block varies across plugins — .mcp.json at the install root, mcpServers inline in .claude-plugin/plugin.json, or a separate mcp.json referenced from plugin.json. The patch has to land in whichever file the plugin actually uses, and claude mcp list doesn't tell you which.
Why this is distinct from existing open issues
- #46360 (Playwright/Windows) is about the
claude mcp addCLI mangling/cintoC:/for user-configured servers. This issue is about plugin-shipped configs that never go throughclaude mcp add. - #32951 (local stdio MCP not loaded on native Windows) is about local servers being silently absent from
/mcp. Here the servers do appear inclaude mcp list— they just fail at the spawn step with a visible "Failed to connect". - #17312 / #19658 / #21115 are all LSP-specific. The fix that closed them addressed the LSP code path; the MCP path has the same bug and needs the same fix.
Environment
- Claude Code: 2.1.139 (latest as of filing)
- OS: Windows 10 Pro 10.0.19045
- Node: tested on 20.15.0 and 22.22.2 — both reproduce the spawn ENOENT
- Shell: PowerShell 7 (also reproduces from Git Bash and a raw
node -einvocation) - Plugin marketplace:
claude-plugins-official
Suggested fix
In whatever module spawns stdio MCP servers, mirror the LSP fix from #17312 — pass shell: true (or shell: 'cmd.exe') when process.platform === 'win32'. Same one-line change that worked for LSP should clear this entire category of plugin failures.
This issue has 8 comments on GitHub. Read the full discussion on GitHub ↗