claude mcp get/list print configured MCP secrets (headers, env vars) in cleartext with no masking
claude mcp get/list print configured MCP secrets (headers, env vars) in cleartext with no masking
Environment
- Claude Code 2.1.233 (CLI)
- macOS (Darwin 25.5.0)
Summary
claude mcp get <name> prints the full configured value of any custom HTTP header or
stdio environment variable set on an MCP server, in cleartext, with no masking. This
includes values that are self-evidently credentials (API keys, tokens) based on the
header/variable name (x-api-key, Authorization, *_KEY, *_TOKEN, etc.).
Compare to conventions in other CLI tools that manage credentials in their config/status
output (gh auth status, aws configure list, stripe config) — these mask secret
values by default (e.g. show only a trailing few characters) and require an explicit
flag to reveal the full value.
Impact
Any tool, script, screen share, terminal capture, or AI agent session that reads this
command's output gets the live credential. If that output is ever included in another
AI model's context (e.g., pasted for debugging, or read by an agent with terminal
access), the credential propagates the same way any other captured secret would —
with no indication to the user that a masked-by-default field was actually disclosed.
Steps to reproduce
export TEST_KEY="dummySecretValue999"
claude mcp add --transport http --scope local repro-http https://example.com/mcp \
-H "x-test-key: $TEST_KEY"
claude mcp get repro-http
# Headers: block shows "x-test-key: dummySecretValue999" in full
claude mcp remove repro-http --scope local
claude mcp add --transport stdio --scope local repro-stdio \
-e TEST_SECRET_ENV=dummyStdioSecret777 -- echo hello
claude mcp get repro-stdio
# Environment: block shows "TEST_SECRET_ENV=dummyStdioSecret777" in full
claude mcp remove repro-stdio --scope local
Confirmed identical behavior regardless of URL scheme (http vs https) — this is a local
config-echo issue, not a transport-security issue.
Also noted while investigating: -H header values do not support ${VAR}-style
environment variable interpolation — a header set to ${SOME_VAR} is stored and would
be sent literally as the string ${SOME_VAR}, not resolved. This means there's
currently no supported way to keep a static secret for an HTTP-transport server's
header out of the on-disk config file at all (unlike stdio's -e, which at least
keeps the value nominally "in the environment" conceptually, even though mcp get
discloses it the same way).
Suggested fix
- Mask secret-shaped values in
claude mcp get/listoutput by default (e.g., last 4
characters only), with an explicit --reveal/--unmask flag for when a user
actually needs to see the full value.
- Consider supporting
${VAR}interpolation for HTTP headers so a value can be kept out
of the on-disk config entirely, matching stdio's -e model — and if unsupported,
fail loudly rather than silently sending the literal placeholder string as the header
value.