`claude mcp list` prints expanded `${ENV}` values in plain text, leaking secrets to terminal/scrollback/ps
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 a plugin's .mcp.json references env vars (e.g. "-e", "TFE_TOKEN=${TFE_TOKEN}"), claude mcp list expands those ${VAR} placeholders and prints the literal secret value in the output.
Example output (token redacted for this report — the original was full clear-text):
plugin:terraform:terraform: docker run -i --rm -e TFE_TOKEN=Gas...XXXX hashicorp/terraform-mcp-server:0.4.0 - ✗ Failed to connect
This leaks the secret to:
- the terminal scrollback
- any pasted bug report, screen share, or log capture (this is exactly how I noticed the issue — while sharing diagnostic output with Claude itself)
ps auxwwonce the docker container actually runs, since the args including the literal token become part of the process command line and are visible to every user on the host
The user did the "right thing" — the token is stored in macOS Keychain and injected only at claude launch via a shell wrapper that calls security find-generic-password. Despite that, claude mcp list defeats the precaution by interpolating and printing the value.
What Should Happen
claude mcp list should display the unexpanded form (${TFE_TOKEN}) — or a redacted placeholder such as <redacted> — for any value that originates from an env-var reference in .mcp.json. Resolution should only happen at MCP-server-launch time, not at list-render time.
Steps to Reproduce
- Install a plugin whose
.mcp.jsonreferences an env var. The officialterraformplugin in theclaude-plugins-officialmarketplace is one example:
``json``
{
"terraform": {
"command": "docker",
"args": ["run", "-i", "--rm", "-e", "TFE_TOKEN=${TFE_TOKEN}",
"hashicorp/terraform-mcp-server:0.4.0"]
}
}
- Export
TFE_TOKENin the shell that launchesclaude(any source — direct export,.zshrc, or a Keychain-loading wrapper). - Run
claude mcp list. - Observe the literal token in the output where
${TFE_TOKEN}was declared.
Related Concern (worth surfacing in plugin authoring docs)
Even if claude mcp list is fixed, the docker run -e KEY=VALUE (with an explicit =VALUE) pattern still leaks the resolved value to ps. Plugin authors should be guided toward "-e", "KEY" (no =) so docker inherits the value from the parent env without putting the literal in the container's command line / process args. The terraform plugin's current .mcp.json is an example that would benefit from this change.
Claude Code Version
2.1.152
Platform
macOS (Anthropic API)
Operating System
macOS (Darwin 25.5.0)
Terminal/Shell
zsh
Additional Information
The same expansion behavior is reasonable inside a launched MCP process (it must see the resolved value), but should not be reflected in claude mcp list, /mcp dialog, or any diagnostic command that a user would naturally share when debugging.
This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗