Summary
The PreToolUse MCP health check hook (mcp-health-check.js) incorrectly marks codegraph (and potentially other
npm-installed stdio-type MCP servers) as unhealthy on Windows, because probeCommandServer() spawns the command without
shell: true. On Windows, npm packages install a #!/bin/sh wrapper script whose companion .cmd wrapper requires shell
interpretation to execute.
Environment
┌──────────┬──────────────────────────────────────────────────────────────────────────────────────────────────────┐
│ Item │ Value │
├──────────┼──────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ OS │ Windows 11 Pro (win32) │
├──────────┼──────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ Node.js │ v24.15.0 │
├──────────┼──────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ codegrap │ v0.9.3 (npm global install) │
│ h │ │
├──────────┼──────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ Hook │ ~/.claude/plugins/cache/everything-claude-code/everything-claude-code/1.9.0/scripts/hooks/mcp-health │
│ file │ -check.js │
└──────────┴──────────────────────────────────────────────────────────────────────────────────────────────────────┘
MCP Server Config
{
"type": "stdio",
"command": "codegraph",
"args": ["serve", "--mcp"]
}
Root Cause
File: mcp-health-check.js, line 299, inside probeCommandServer():
child = spawn(command, args, {
env: mergedEnv,
cwd: process.cwd(),
stdio: ['pipe', 'ignore', 'pipe']
});
shell is not set, so it defaults to false.
On Windows, npm install -g @colbymchenry/codegraph installs two entry points:
┌───────────────┬────────────────────────────────┬─────────────────────────────┐
│ File │ Type │ Size │
├───────────────┼────────────────────────────────┼─────────────────────────────┤
│ codegraph │ POSIX shell script (#!/bin/sh) │ 439 bytes │
├───────────────┼────────────────────────────────┼─────────────────────────────┤
│ codegraph.cmd │ Windows batch wrapper │ (calls node on npm-shim.js) │
└───────────────┴────────────────────────────────┴─────────────────────────────┘
When spawn('codegraph', ...) is called without shell: true on Windows:
- Node.js resolves codegraph via PATH and finds the extension-less sh script first
- CreateProcess cannot execute a #!/bin/sh script — it's not a PE executable or a recognized batch file
- Node.js reports ENOENT (even though the file exists, it is not executable by the OS)
The .cmd wrapper exists and would work, but spawn() without shell: true cannot launch .cmd files either — it throws
EINVAL on Node.js v24.
Only spawn('codegraph', args, { shell: true }) works, because cmd.exe can interpret the .cmd wrapper correctly.
Reproduction
// FAILS — same code path as the health check hook
const p = spawn('codegraph', ['status']);
// Error: spawn codegraph ENOENT
// ALSO FAILS — even with explicit .cmd extension
const p = spawn('codegraph.cmd', ['status']);
// Error: spawn EINVAL
// WORKS — shell: true lets cmd.exe handle the .cmd wrapper
const p = spawn('codegraph', ['status'], { shell: true });
// stdout: CodeGraph Status ... [OK] Index is up to date
Impact
- The health check hook marks codegraph as unhealthy in ~/.claude/mcp-health-cache.json
- All PreToolUse calls for mcp__codegraph__* tools are blocked (exit code 2)
- This persists across sessions because state is cached on disk
- The actual MCP server (spawned by the Claude Code harness, which likely uses shell: true) works fine — the health
check is a false negative
- Any npm-installed stdio MCP server on Windows is potentially affected
Affected State File
~/.claude/mcp-health-cache.json:
{
"servers": {
"codegraph": {
"status": "unhealthy",
"lastError": "spawn codegraph ENOENT",
"failureCount": 2
}
}
}
Proposed Fix
In mcp-health-check.js, line 299, add shell: true on Windows:
child = spawn(command, args, {
env: mergedEnv,
cwd: process.cwd(),
stdio: ['pipe', 'ignore', 'pipe'],
shell: process.platform === 'win32' // ← add this
});
Or more defensively, set it unconditionally — shell: true is harmless on Unix.
Workaround
Set ECC_MCP_HEALTH_FAIL_OPEN=true to bypass the health check, or delete ~/.claude/mcp-health-cache.json and restart
the session after the fix is applied.
Summary
The PreToolUse MCP health check hook (mcp-health-check.js) incorrectly marks codegraph (and potentially other
npm-installed stdio-type MCP servers) as unhealthy on Windows, because probeCommandServer() spawns the command without
shell: true. On Windows, npm packages install a #!/bin/sh wrapper script whose companion .cmd wrapper requires shell
interpretation to execute.
Environment
┌──────────┬──────────────────────────────────────────────────────────────────────────────────────────────────────┐
│ Item │ Value │
├──────────┼──────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ OS │ Windows 11 Pro (win32) │
├──────────┼──────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ Node.js │ v24.15.0 │
├──────────┼──────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ codegrap │ v0.9.3 (npm global install) │
│ h │ │
├──────────┼──────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ Hook │ ~/.claude/plugins/cache/everything-claude-code/everything-claude-code/1.9.0/scripts/hooks/mcp-health │
│ file │ -check.js │
└──────────┴──────────────────────────────────────────────────────────────────────────────────────────────────────┘
MCP Server Config
{
"type": "stdio",
"command": "codegraph",
"args": ["serve", "--mcp"]
}
Root Cause
File: mcp-health-check.js, line 299, inside probeCommandServer():
child = spawn(command, args, {
env: mergedEnv,
cwd: process.cwd(),
stdio: ['pipe', 'ignore', 'pipe']
});
shell is not set, so it defaults to false.
On Windows, npm install -g @colbymchenry/codegraph installs two entry points:
┌───────────────┬────────────────────────────────┬─────────────────────────────┐
│ File │ Type │ Size │
├───────────────┼────────────────────────────────┼─────────────────────────────┤
│ codegraph │ POSIX shell script (#!/bin/sh) │ 439 bytes │
├───────────────┼────────────────────────────────┼─────────────────────────────┤
│ codegraph.cmd │ Windows batch wrapper │ (calls node on npm-shim.js) │
└───────────────┴────────────────────────────────┴─────────────────────────────┘
When spawn('codegraph', ...) is called without shell: true on Windows:
The .cmd wrapper exists and would work, but spawn() without shell: true cannot launch .cmd files either — it throws
EINVAL on Node.js v24.
Only spawn('codegraph', args, { shell: true }) works, because cmd.exe can interpret the .cmd wrapper correctly.
Reproduction
// FAILS — same code path as the health check hook
const p = spawn('codegraph', ['status']);
// Error: spawn codegraph ENOENT
// ALSO FAILS — even with explicit .cmd extension
const p = spawn('codegraph.cmd', ['status']);
// Error: spawn EINVAL
// WORKS — shell: true lets cmd.exe handle the .cmd wrapper
const p = spawn('codegraph', ['status'], { shell: true });
// stdout: CodeGraph Status ... [OK] Index is up to date
Impact
check is a false negative
Affected State File
~/.claude/mcp-health-cache.json:
{
"servers": {
"codegraph": {
"status": "unhealthy",
"lastError": "spawn codegraph ENOENT",
"failureCount": 2
}
}
}
Proposed Fix
In mcp-health-check.js, line 299, add shell: true on Windows:
child = spawn(command, args, {
env: mergedEnv,
cwd: process.cwd(),
stdio: ['pipe', 'ignore', 'pipe'],
shell: process.platform === 'win32' // ← add this
});
Or more defensively, set it unconditionally — shell: true is harmless on Unix.
Workaround
Set ECC_MCP_HEALTH_FAIL_OPEN=true to bypass the health check, or delete ~/.claude/mcp-health-cache.json and restart
the session after the fix is applied.