Authentication
Manage server authentication with token persistence, interactive login, and automatic header injection.
Commands
# Login — starts browser OAuth for HTTP servers that advertise itemail auth login
# Login with an existing bearer tokenecho "$TOKEN" | email auth login
# Login non-interactively with an existing bearer tokenecho "$TOKEN" | email auth login --non-interactive
# Login with a structured payloademail auth login --input-json '{"bearer_token": "sk-abc123", "account": "me@example.com"}'
# Check current stateemail auth status
# Clear stored credentialsemail auth logoutauth login resolves credentials in this order:
- Piped stdin —
echo "$TOKEN" | email auth login. --input-json— a JSON object with the schema below.- Browser OAuth — for streamable-HTTP configs, mcp2cli discovers the server’s OAuth metadata, dynamically registers a loopback client, starts authorization-code + PKCE, and stores the returned access token.
Pass --non-interactive to fail fast when no token is supplied via stdin or --input-json.
--input-json schema
{ "bearer_token": "<token>", "account": "<optional>"}| Field | Required | Meaning |
|---|---|---|
bearer_token | ✅ | The token sent as Authorization: Bearer <token>. |
account | — | Optional account label stored alongside the token. |
How It Works
sequenceDiagram
participant User
participant CLI as mcp2cli
participant Auth as OAuth Server
participant Store as Token Store
participant Server as MCP Server
User->>CLI: email auth login
CLI->>Server: Discover protected resource metadata
CLI->>Auth: Register loopback OAuth client
CLI->>User: Open authorization URL
User->>Auth: Approve login in browser
Auth->>CLI: Redirect to loopback callback with code
CLI->>Auth: Exchange code + PKCE verifier
Auth-->>CLI: Bearer access token
CLI->>Store: Store token for "email"
CLI-->>User: Authenticated ✓
Note over User: Later...
User->>CLI: email search --query "from:boss"
CLI->>Store: Load token for "email"
Store-->>CLI: sk-abc123
CLI->>Server: POST /mcp<br/>Authorization: Bearer sk-abc123
Server-->>CLI: Result
CLI-->>User: Output
Token Storage
Tokens are persisted per-config at:
~/.local/share/mcp2cli/instances/<name>/tokens.jsonThe file is written with 0600 permissions (owner read/write only) and contains:
{ "bearer_token": "sk-abc123"}Custom Token Path
Override the default location:
auth: token_store_file: /secure/path/tokens.jsonAuth States
| State | Meaning |
|---|---|
unauthenticated | No token stored |
active | Token stored and being sent with requests |
Check the current state:
email auth status# → Auth state: active
email --json auth status | jq '.data.auth_session.state'# → "active"Transport Behavior
| Transport | Auth Support | How |
|---|---|---|
| Streamable HTTP | ✅ | Authorization: Bearer <token> injected on every request from the stored token |
| Stdio | ❌ | Subprocess inherits environment; set env vars in config |
| Demo | ❌ | No auth needed |
The streamable-HTTP transport loads the stored token and attaches an
Authorization: Bearer <token> header to every request automatically — no
per-call flags needed. Both http and https endpoints are supported; HTTPS
connections use TLS via rustls with the webpki root certificates, so a
production endpoint like https://mcp.example.com/email works out of the box.
For stdio servers that need authentication, pass credentials via environment:
server: transport: stdio stdio: command: my-server env: API_KEY: sk-abc123Browser-Based OAuth
For streamable-HTTP servers that implement MCP OAuth discovery and dynamic client registration:
auth: browser_open_command: "xdg-open" # Linux # browser_open_command: "open" # macOSauth login discovers /.well-known/oauth-protected-resource, reads the
authorization server metadata, dynamically registers a loopback redirect URI,
opens the authorization URL, and stores the resulting bearer token. The
authorization request includes the MCP resource parameter and uses PKCE S256.
The loopback callback validates the CSRF state on every request, ignores
requests to any path other than the registered redirect URI (a stray probe
on the ephemeral port can’t derail an in-flight login), and — per the MCP
2026-07-28 authorization hardening (RFC 9207 / SEP-2468) — rejects a present
iss parameter that doesn’t match the discovered issuer.
If you already have a bearer token, pipe it or pass --input-json; this bypasses
browser OAuth and stores the token directly. auth login only reads stdin when
you actually pipe something to it — running from a script, CI runner, or IDE
terminal with nothing piped falls straight through to browser OAuth rather than
blocking.
See Also
- Configuration Reference — auth config fields
- Elicitation & Sampling — interactive auth flows