[BUG] `claude mcp add` expands environment variable placeholders and writes resolved values to .mcp.json

Resolved 💬 4 comments Opened Jan 16, 2026 by TyceHerrman Closed Apr 30, 2026

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

  1. Create a .mcp.json with 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}"
      }
    }
  }
}
  1. Ensure the environment variables are set in your shell
  1. Run claude mcp add to add a new server:
claude mcp add discourse -s project -- npx -y @discourse/mcp@latest
  1. 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

View original on GitHub ↗

This issue has 4 comments on GitHub. Read the full discussion on GitHub ↗