claude mcp add --header exposes Authorization/Bearer token value in stdout
Bug summary
claude mcp add --transport http <name> <url> --header "Authorization: Bearer <token>" prints the header value verbatim to stdout, exposing the secret in terminal history and any log/transcript that captures the session.
Reproduction
claude mcp add --transport http my-server https://example.com/mcp --header "Authorization: Bearer secret-token-123"
Output includes the raw header value on stdout:
"Authorization": "Bearer secret-token-123"
}
File modified: /path/to/.claude.json
Expected behavior
The header value should be masked (e.g. "Authorization": "Bearer ***") or omitted entirely from stdout, similar to how claude mcp add --client-secret (interactive prompt) handles secrets. The token is still correctly stored in .claude.json — the issue is only the echo to stdout.
Actual behavior
Full token value printed to stdout. Anyone who reviews the terminal buffer, tmux buffer, screen recording, session transcript, or agent conversation log can extract the raw secret.
Impact
- High in agent-driven workflows: any AI agent (Claude Code itself, or an external orchestrator running the CLI) that executes
claude mcp addcaptures the leaked token in its conversation transcript. If that transcript is shared, uploaded for debugging, or ingested by other systems, the token propagates. - Token rotation is the only mitigation once the leak happens.
Environment
- Claude Code CLI version: 2.1.128
- OS: Windows 11 (behavior is not OS-specific — the print happens in the config-write confirmation path)
- Shell: PowerShell / Git Bash
Suggested fix
Detect sensitive header names (Authorization, X-Api-Key, X-Auth-Token, Cookie, etc.) in --header values before printing config confirmation, and mask the value. Alternatively, support a --header-env VAR_NAME flag that reads from an env var and never prints the resolved value.
Related
Common pattern in other CLIs: curl -v masks auth by default in newer versions; gh auth login never echoes tokens; docker login --password-stdin avoids the same issue by reading from stdin.
3 Comments
This is worth treating as an agent-boundary leak, not just a CLI formatting bug.
In human-only use, stdout is often ephemeral. In agent-driven workflows, stdout becomes durable context: copied into transcripts, logs, reviews, task memories, and sometimes secondary model calls. Once an MCP bearer token lands there, the blast radius is larger than the terminal session that ran the command.
A clean fix would separate the stored config from the displayed confirmation:
mcp add --header "Authorization: Bearer test-secret"and asserts the secret never appears in stdout, stderr, or structured confirmation outputThat last bit matters for auditability. Operators need evidence that the MCP server was configured, but the evidence should prove the binding shape, not disclose the bearer material.
This maps closely to the AgentFolio/SATP framing we keep using: authorization can stay local to the tool/runtime, but provenance and receipts need to be safe to share. A receipt that contains the credential is not a receipt; it is another secret store.
This was fixed in v2.1.161: the
claude mcp addconfirmation now redacts sensitive header values (Authorization, API keys, cookies) instead of echoing them. You're on 2.1.128, so upgrading past 2.1.161 stops the token from being printed. Any token that already hit a transcript should be rotated. Reopen with the version and command output if you still see the raw value on 2.1.161 or later.This issue has been automatically locked since it was closed and has not had any activity for 7 days. If you're experiencing a similar issue, please file a new issue and reference this one if it's relevant.