MCP stdio reconnect spawns literal unexpanded ${HOME} in command → ENOENT, server lost for the session (still on 2.1.215; prior reports stale-closed)
Summary
${VAR} expansion in a stdio MCP server's command is applied on the initial session connect, but not on the mid-session auto-reconnect path. The reconnect spawns the literal unexpanded string, fails with ENOENT in ~2ms, and the server's tools are reported disconnected for the rest of the session.
Prior reports of this exact bug were closed without a fix: #65747 (canonical, closed as stale by github-actions on 2026-07-12 with "open a new issue if this is still relevant"), #67483 and #70146 (closed as duplicates of it). It still reproduces on the current release, so filing fresh per the stale-bot's instruction.
Environment
- Claude Code 2.1.215, macOS (darwin 24.6.0)
- Server registered in user-scope
~/.claude.json:
{
"mcpServers": {
"example": {
"type": "stdio",
"command": "${HOME}/go/bin/example-mcp",
"env": { "EXAMPLE_TOKEN": "..." }
}
}
}
Observed (from ~/Library/Caches/claude-cli-nodejs/<project>/mcp-logs-example/*.jsonl, binary name genericized)
03:40:09.533Z Starting connection with timeout of 30000ms
03:40:09.580Z Successfully connected (transport: stdio) in 48ms <-- initial connect: ${HOME} expanded, server runs fine
...
03:43:10.206Z Starting connection with timeout of 30000ms <-- mid-session reconnect attempt
03:43:10.208Z Connection failed after 2ms: ENOENT: no such file or directory, posix_spawn '${HOME}/go/bin/example-mcp'
Two stdio servers registered with ${HOME} commands failed at the same instant on the same reconnect sweep; servers registered with bare commands on PATH (npx, uvx) or absolute paths were unaffected. After the failed reconnect the session reports the server's tools as no longer available; there is no retry with expansion applied.
claude mcp list health checks the same entry successfully (it expands ${HOME}), which makes this confusing to diagnose: the server shows ✔ Connected from the CLI while the live session has lost it.
Expected
The reconnect path should apply the same environment-variable expansion to command (and args/env) as the initial spawn.
Workaround
Register the server with an absolute path instead of ${HOME}.