[BUG] `claude mcp add` expands environment variable placeholders and writes resolved values to .mcp.json
Preflight Checklist
- [x] I have searched existing issues and this hasn't been reported yet
- [x] This is a single bug report (please file separate reports for different bugs)
- [x] I am using the latest version of Claude Code
What's Wrong?
When running claude mcp add to add a new MCP server to a project's .mcp.json, the command also expands existing environment variable placeholders (like ${GITHUB_PERSONAL_ACCESS_TOKEN}) and writes the resolved values back to the file.
This is a security risk as it can lead to accidental credential commits if the user doesn't notice the change.
What Should Happen?
Environment variable placeholders like ${VAR_NAME} should be preserved as literal strings when the CLI rewrites .mcp.json. Only the new MCP server entry should be added; existing entries should remain unchanged.
Error Messages/Logs
No error message - the operation appears to succeed, but inspecting .mcp.json reveals the expanded secrets.
Steps to Reproduce
- Create a
.mcp.jsonwith environment variable placeholders:
{
"mcpServers": {
"github": {
"type": "http",
"url": "https://api.githubcopilot.com/mcp/",
"headers": {
"Authorization": "Bearer ${GITHUB_PERSONAL_ACCESS_TOKEN}"
}
},
"context7": {
"type": "http",
"url": "https://mcp.context7.com/mcp",
"headers": {
"CONTEXT7_API_KEY": "${CONTEXT7_API_KEY}"
}
}
}
}
- Ensure the environment variables are set in your shell
- Run
claude mcp addto add a new server:
claude mcp add discourse -s project -- npx -y @discourse/mcp@latest
- Inspect
.mcp.json- the placeholders have been replaced with actual values:
{
"mcpServers": {
"github": {
"type": "http",
"url": "https://api.githubcopilot.com/mcp/",
"headers": {
"Authorization": "Bearer gho_actualTokenValueHere..."
}
},
...
}
}
Claude Model
Opus 4.5
Is this a regression?
I don't know
Last Working Version
_No response_
Claude Code Version
2.1.9
Platform
Claude Max
Operating System
macOS
Terminal/Shell
Fish shell (but likely affects all shells)
Additional Information
Security Impact: This can lead to accidental credential leaks if users commit .mcp.json without noticing the expanded secrets.
Root Cause Hypothesis: When the CLI reads .mcp.json, it appears to resolve environment variables during parsing. When it writes the file back (after adding the new MCP server), it writes the resolved values instead of preserving the original ${...} syntax.
Workaround: After running claude mcp add, use git checkout -- .mcp.json to restore the original file, then manually add the new MCP server entry.
Related Issues:
- #9427 (closed) - env variable expansion not working in plugin .mcp.json (opposite problem)
- #6204 - MCP headers with env var substitution not being sent
- #13138 - Race condition creating literal
${...}folders
This issue has 4 comments on GitHub. Read the full discussion on GitHub ↗