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.
This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗