MCP server env ${VAR} left unexpanded when server is respawned mid-session (reconnect)
Summary
When an MCP server is (re)spawned mid-session (e.g. via /mcp reconnect, or an auto-reconnect after the server drops), the ${VAR} references in that server's env block are not expanded. The literal string (e.g. ${JIRA_URL}) is passed to the server process. On the initial spawn at Claude Code startup, the same config expands correctly.
Environment
- Claude Code 2.1.215 (Desktop-embedded), macOS 15 (Darwin 25.5.0)
- Env vars defined in
~/.claude/settings.jsonenv; referenced as${VAR}in~/.claude.jsonmcpServers.<name>.env
Expected
${VAR} in an MCP server's env expands to its value on every spawn, including reconnects.
Actual
- Initial spawn (at CC startup):
${VAR}→ real value ✅ - Reconnect / mid-session respawn:
${VAR}→ literal${VAR}❌
Evidence
Six live mcp-atlassian processes across sessions, identical config, correlated purely with spawn timing:
| PID | spawned vs parent CC start | JIRA_URL in process env |
|---|---|---|
| 20777 | +2s (initial) | https://jira.example.com ✅ |
| 31849 | +1s (initial) | real ✅ |
| 27046 | +55min (reconnect) | ${JIRA_URL} ❌ |
| 33057 | +63s (reconnect) | ${JIRA_URL} ❌ |
Both parent CC processes had JIRA_URL unset in their own env, confirming the value only ever arrives via ${VAR} expansion — which the reconnect path skips. Same binary (2.1.215), same CLAUDE_CONFIG_DIR (default) for working and broken cases. After a full relaunch, the initial spawn expanded correctly again.
Reproduction
- Define
FOO=barin~/.claude/settings.jsonenv. - In an MCP server config, set
env: { "FOO": "${FOO}" }. - Start Claude Code → server gets
FOO=bar. /mcp reconnect <server>→ server now getsFOO=${FOO}(literal).
Impact
Any MCP server relying on ${VAR} env indirection breaks after a reconnect — auth/URL vars resolve to literal placeholders, causing failures like Invalid URL '${JIRA_URL}/rest/...': No scheme supplied.
Workaround
Fully relaunch Claude Code (fresh initial spawn) rather than /mcp reconnect.
Note
The mid-session respawns were inferred to be reconnects from spawn timing (large gap between parent CC start and MCP start); the timing correlation itself is directly observed from the process table.
This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗