Environment
- Claude Code version: 2.1.112
- OS: Windows 10 Pro (native, not WSL)
- Terminal: bash (Git Bash)
Bug Description
In .mcp.json for an HTTP-transport MCP server, ${ENV_VAR} placeholders in the headers map are not substituted with the environment variable value. The literal string ${ENV_VAR} is sent to the server. The same substitution works correctly in the env block for stdio-transport servers.
This is the same root cause as the previously reported (and bot-auto-closed) #6204. Filing a fresh report per that thread's guidance ("please file a new issue and reference this one"), adding native-Windows repro and additional diagnostic evidence.
Steps to Reproduce
-
Set env var in the shell that launches Claude Code:
export STITCH_API_KEY="AQ.Ab8..."
(Also confirmed set via "env" block in .claude/settings.local.json — same result.)
-
Configure .mcp.json:
{
"mcpServers": {
"stitch": {
"type": "http",
"url": "https://stitch.googleapis.com/mcp",
"headers": {
"X-Goog-Api-Key": "${STITCH_API_KEY}"
}
}
}
}
-
Start Claude Code. claude mcp list shows ✓ Connected because initialize is unauthenticated on this server.
-
Invoke any tool → server returns 401 because Claude Code sent no key. Claude Code then attempts OAuth DCR, which Google does not support, so the tool becomes unusable.
Expected Behavior
${STITCH_API_KEY} in headers should be substituted with the env var value, exactly as it is in env blocks.
Actual Behavior
Placeholder sent verbatim. Verified with a mitmproxy between Claude Code and the server — header value is the literal string \${STITCH_API_KEY}.
Workaround
Hardcode the secret directly in headers, then gitignore .mcp.json. Functional but defeats the whole point of env-var substitution.
"headers": { "X-Goog-Api-Key": "AQ.Ab8...actual-value..." }
Impact
Any remote MCP server requiring API-key-style header auth (Google Cloud services, internal enterprise MCP gateways, etc.) cannot use secrets hygiene with Claude Code's .mcp.json — forced to either paste secrets into source control or build a local command wrapper.
Related
Environment
Bug Description
In
.mcp.jsonfor an HTTP-transport MCP server,${ENV_VAR}placeholders in theheadersmap are not substituted with the environment variable value. The literal string${ENV_VAR}is sent to the server. The same substitution works correctly in theenvblock for stdio-transport servers.This is the same root cause as the previously reported (and bot-auto-closed) #6204. Filing a fresh report per that thread's guidance ("please file a new issue and reference this one"), adding native-Windows repro and additional diagnostic evidence.
Steps to Reproduce
Set env var in the shell that launches Claude Code:
(Also confirmed set via
"env"block in.claude/settings.local.json— same result.)Configure
.mcp.json:{ "mcpServers": { "stitch": { "type": "http", "url": "https://stitch.googleapis.com/mcp", "headers": { "X-Goog-Api-Key": "${STITCH_API_KEY}" } } } }Start Claude Code.
claude mcp listshows✓ Connectedbecauseinitializeis unauthenticated on this server.Invoke any tool → server returns 401 because Claude Code sent no key. Claude Code then attempts OAuth DCR, which Google does not support, so the tool becomes unusable.
Expected Behavior
${STITCH_API_KEY}inheadersshould be substituted with the env var value, exactly as it is inenvblocks.Actual Behavior
Placeholder sent verbatim. Verified with a mitmproxy between Claude Code and the server — header value is the literal string
\${STITCH_API_KEY}.Workaround
Hardcode the secret directly in
headers, then gitignore.mcp.json. Functional but defeats the whole point of env-var substitution.Impact
Any remote MCP server requiring API-key-style header auth (Google Cloud services, internal enterprise MCP gateways, etc.) cannot use secrets hygiene with Claude Code's
.mcp.json— forced to either paste secrets into source control or build a localcommandwrapper.Related