# Provider authentication
[Feature docs index](README.md) · [Repository README](../../README.md)
## Connect a provider
Run `/login` in Mission Control. Choose OpenAI Codex for ChatGPT OAuth, Claude Subscription for CLI-managed claude.ai access, Anthropic for Messages API access billed through Anthropic API credits, OpenAI for API-key access, or Custom Provider for an OpenAI-compatible service.
Never commit credentials, copied tokens, account ids, terminal auth output, or transcripts containing secrets. Settings contain non-secret options only; credentials belong in the provider's environment variable or private `auth.json` under `~/.magi-code` (or `$MC_HOME`).
### OpenAI Codex
```text
/login openai-codex
```
Complete the ChatGPT browser OAuth flow, then check access:
```sh
magi-code --provider openai-codex --model gpt-5.5 --prompt "Reply with only: ok"
```
`openai-codex` requires provider-keyed OAuth credentials and a ChatGPT account id. It calls `https://chatgpt.com/backend-api/codex/responses`. Inference and model discovery do not use `--api-key`, `MC_API_KEY`, `OPENAI_API_KEY`, or custom-provider keys. Do not pass OAuth tokens through those options: runtime-token/API-key auth is refused for Codex.
Login displays an authorization URL and binds `127.0.0.1:1455` for the registered redirect URI `http://localhost:1455/auth/callback`. Credentials are saved in `auth.json`. The visible URL is transient and redacted from logs/debug output. Manual redirect/code input is visually redacted, never stored in settings or sessions or sent to the model, and goes to the token exchange only when explicitly submitted.
At 80×24, custom setup shows the active field; Tab and Shift-Tab move between fields. OAuth keeps browser, copy, and redirect-input controls visible. URLs that exceed the available space show a page count: Page Up and Page Down cycle through every URL page. Copy URL always copies the complete URL.
```text
/logout openai-codex
```
Logout removes the local OAuth record only. It does not revoke remote tokens or sign out browser sessions.
### OpenAI API
Export `OPENAI_API_KEY` before launching magi-code, then choose **OpenAI** in `/login` or run `/login openai`. Review the prefilled custom-provider form and press Enter to save:
- Label/id: `OpenAI` / `openai`
- API root: `https://api.openai.com/v1`
- API-key variable: `OPENAI_API_KEY`
- models.dev provider: `openai`
This uses the existing custom-provider Responses transport, not ChatGPT OAuth. Setup stores only metadata and asks before replacing an existing `openai` configuration. Refresh the catalog in `/settings` → Models, then select `/model openai/<model-name>`. `/logout openai` removes the custom-provider configuration; it does not unset the environment variable.
### Anthropic
```sh
export ANTHROPIC_API_KEY="<ANTHROPIC_API_KEY>"
magi-code --provider anthropic --model claude-sonnet-4-5-20250929 --prompt "Reply with only: ok"
```
Auth checks `ANTHROPIC_API_KEY` first, then the provider-keyed `anthropic` API-key record in `auth.json`. OAuth and no-auth configurations are rejected. Anthropic ignores `--api-key`, `MC_API_KEY`, and OpenAI keys such as `OPENAI_API_KEY`.
To save the model selection without storing a key:
```json
{
"agent": { "model": {
"provider": "anthropic",
"model": "claude-sonnet-4-5-20250929"
}
} }
```
Use `/settings` → Models → **Refresh catalog**, then `/model anthropic/<model-name>` to select a cached model. Missing Anthropic auth does not block Codex or custom-provider catalogs. `/logout anthropic` removes only the local API-key record; it cannot unset or override `ANTHROPIC_API_KEY`.
#### Prompt caching
Anthropic API prompt caching defaults to one hour. Override `providers.anthropic.cache_ttl` in `settings.json`:
```json
{
"providers": {
"anthropic": { "cache_ttl": "1h" }
}
}
```
Use `"5m"` for a shorter cache lifetime, or `null` to disable cache control. Magi-code sends top-level `cache_control`, which moves the cache breakpoint forward as the conversation grows. Cache hits require a matching prefix, enough cacheable tokens for the model, and an unexpired entry. Cache writes cost extra; enabling caching does not guarantee savings. Claude subscription leaves caching to the Claude CLI and does not use this setting.
The TUI's session input total includes uncached input, cache reads, and cache writes. Its cache percentage measures cache reads divided by that total, not tokens written to cache or money saved. A first request can write the cache while showing `0%` reads. Later output-only usage updates retain earlier input and cache counts.
### Claude subscription
This provider uses your claude.ai subscription through installed Claude Code. It needs no separate Agent SDK package or Anthropic API key. The separate `anthropic` provider still requires an API key.
Install Claude Code and run `claude auth login`. Opus 5.5 needs Claude Code 2.1.280 or newer. Then select `/model claude-subscription/claude-opus-5-5`, or launch with `--provider claude-subscription --model claude-opus-5-5`. Readiness runs `claude auth status --json` and expects a signed-in `claude.ai` account. Magi-code never installs dependencies, copies OAuth tokens, or uses `--bare`. To sign out, run `claude auth logout`.
Readiness checks and requests remove `ANTHROPIC_API_KEY`, `ANTHROPIC_AUTH_TOKEN`, `ANTHROPIC_FOUNDRY_API_KEY`, and the `CLAUDE_CODE_USE_BEDROCK`, `CLAUDE_CODE_USE_VERTEX`, and `CLAUDE_CODE_USE_FOUNDRY` backend selectors from the Claude child environment. The parent environment stays unchanged, so Anthropic API and Claude subscription models can be enabled together without sending an API key through the subscription provider.
#### Model catalog
Refreshing the catalog lists Anthropic models from models.dev, using their names and metadata. These are candidates. They aren't confirmed for your account, and the CLI and your plan decide what you can actually use. API token prices are left out. If a models.dev refresh fails and the CLI is ready, magi-code keeps showing the cached candidates. Loading from cache alone never runs the CLI or touches the network, but sending a request still requires a ready CLI.
Claude subscription normally uses a 202,000-token input ceiling. Explicit `[1m]` selections for Opus 4.6 and newer and Fable 5 and newer use 1,000,000 tokens, including `claude-opus-5-5[1m]` and `claude-fable-5[1m]`. Refresh the catalog to show extended-context variants of eligible listed models. Account access and CLI support still determine acceptance; this ceiling is not evidence of successful full-window use for every model. No model is switched automatically. Model overrides can lower, not raise, these ceilings. Compaction uses the shared `agent.compaction.auto` settings and inherits the active model; no alternative provider is required. See [context limits](sessions-context-cache.md#handle-context-limits).
#### How requests run
Each request starts one installed CLI and a temporary local relay. The relay admits one generation to `https://api.anthropic.com/v1/messages` and refuses further generations. It forwards the CLI's own authorization headers in memory only. Cancellation stops the CLI and inert MCP server in one process group; this requires Unix. See [runtime boundary and verification](claude-subscription.md).
Magi-code still runs tools, handles approvals, and owns session history and compaction. Claude sees the tool definitions and can request tool calls, but the CLI cannot execute them; magi-code does. Text, reasoning blocks and partial usage stream from the upstream response in order. Tool calls and the remaining response wait until the complete response, stop reason, token usage and CLI result have been validated. Signed thinking and redacted reasoning blocks remain intact during history replay.
An ordinary placeholder prompt starts the CLI. The relay replaces it with complete ordered host history and rejects unexpected request shapes rather than sending the placeholder. No mirrored transcript, persistent Claude session, or private assistant-input replay protocol is used. Native `modelUsage` metadata alone never raises capacity.
Magi-code sets `CLAUDE_CODE_MAX_RETRIES=0` and `CLAUDE_CODE_DISABLE_NONSTREAMING_FALLBACK=1`. The relay remains the final protection against another generation, including CLI retries that ignore those settings. There is no automatic switch to API-key billing or another model after failure.
Removing API-key overrides does not prevent subscription extra-usage charges. Existing account usage credits may be billed by Claude Code, including non-interactive requests. Magi-code does not enable credits or change your billing settings; check them before selecting an extended-context model. Subscription limits and model access still apply.
The compiled `prompts/claude-cli.md` section is appended only for Claude subscription requests and requires tool names prefixed with `mcp__magi__`. A complete call with a missing prefix or a name outside the request's tool inventory never executes. Magi-code displays and records the rejection as a failed tool result, then returns it to Claude so the model can correct the call and continue. Transport failures and incomplete or invalid streams still stop the request.
Reasoning levels other than default map to the CLI's `--effort` option. The default level leaves that choice to the CLI. The CLI also sets the output token limit, so the Anthropic API provider's 4,096-token fallback doesn't apply. The relay respects your proxy environment settings.
Requests allow five minutes for startup and the first upstream or native progress. After progress begins, the configured inactivity timeout applies: **60 seconds for primary turns and compaction**, **120 seconds for subagents**, unless changed through `providers.streaming.semantic_progress_timeout_seconds` or `providers.streaming.subagent_semantic_progress_timeout_seconds` (1–600 seconds). Internal callers without an override use five minutes.
Parsed upstream progress resets inactivity even when CLI output is delayed. This includes text, reasoning, tool arguments and usage; pings, arbitrary bytes and startup metadata do not reset it. Tool calls still wait for full validation. After upstream `message_stop`, the CLI has one fixed inactivity-timeout interval to finish; late progress cannot extend this wait. A separate 30-minute overall limit bounds each generation, and each local socket write is bounded to five minutes. Timeouts stop the process group and relay. Accepted partial text remains durable; incomplete tool calls are never released. No extra generation is admitted to recover a partial response.
Timeout and native-stream failure diagnostics capture the phase and timing before cleanup, including effective limits, ages of upstream and CLI progress, received bytes, payload bytes in fully forwarded chunks, HTTP status and completion flags. These help distinguish upstream inactivity, stalled forwarding and delayed CLI finalization. When a request is rejected, magi-code shows the HTTP status and a short error category. It never logs raw upstream responses, credential-bearing URLs or CLI stderr.
The model-specific input guard protects against oversized requests. A model's advertised window does not establish that the installed CLI, account and replay path accept it. Token estimates can differ from provider usage; keep headroom and use host compaction on size rejection.
### Custom OpenAI-compatible providers
Use `/login custom-provider` to enter a label, API-root base URL, and optional API-key environment-variable name. Setup saves non-secret metadata, never a key value. Provider ids use lowercase slugs such as `local-provider`; `openai-codex` is reserved. An id such as `openai` can use the custom-provider workflow.
If you configure an API-key variable, only that named variable is read and it must be non-empty for the provider to be ready. Without one, you can use a no-auth local provider:
```sh
# No-auth local provider:
magi-code --provider local-provider --model model-name --prompt "Reply with only: ok"
# Env-var-backed provider:
export HOSTED_PROVIDER_API_KEY="<CUSTOM_PROVIDER_API_KEY>"
magi-code --provider hosted-provider --model model-name --prompt "Reply with only: ok"
```
Custom requests use `{base_url}/responses` by default. The URL is a normalized API root, not a request endpoint. Model discovery uses `{base_url}/models`. Edit `providers.custom.<id>` in `settings.json` for options not prompted by login:
| Option | Behavior |
| --- | --- |
| `use_responses_endpoint: false` | Opts out of the default Responses endpoint and uses `{base_url}/chat/completions`; discovery stays on `/models`. There is no automatic endpoint detection. Codex is unaffected. |
| `supports_text_verbosity: true` | Declares Responses `text.verbosity` support; defaults `false`. Emission also requires Responses mode and shared `providers.openai_responses.text_verbosity`. |
| `fast_mode` | Explicit service tier and exact model ids, or sole `"*"` entry. No capability inference from names. See [Fast mode](#fast-mode). |
| `extra_models` | Adds provider-local model ids omitted by `/models`; dedupes against discovered ids and does not change the endpoint. |
| `models_dev_provider` | Exact `models.dev` namespace for matching context/reasoning/output-token metadata, not auth. Defaults to the provider id. |
For a Z.ai Coding Plan model omitted by `/models`:
```json
{
"providers": { "custom": {
"zai": {
"label": "Z.ai",
"base_url": "https://api.z.ai/api/coding/paas/v4",
"api_key_env_var": "ZAI_API_KEY",
"extra_models": ["glm-5.2"]
}
}
} }
```
Use `glm-5.2`, not `zai/glm-5.2`, in `extra_models`. Catalog enrichment requires an exact namespace and model id match, never a label, host, prefix, or base-URL guess. For example, id `openai` uses namespace `openai`; id `custom-openai` needs `models_dev_provider: "openai"` to use that metadata. An explicit namespace always wins.
`/logout <custom-provider-id>` removes custom-provider metadata only from global settings. Project provider definitions and environment credentials remain, as do sessions, unrelated settings, and other providers.
## Mission Control login controls
`/login` opens the staged **Connect Provider** modal with provider status. Use `Up`/`Down` and `Enter` to choose.
- Codex: click **Open Browser** (`Alt-O`) or **Copy URL** (`Ctrl-Y`). `Tab`/`Shift-Tab` focuses the buttons or redacted fallback field; `Enter` activates the button or submits the pasted redirect URL/code. `Esc` cancels active OAuth.
- API keys: the modal shows environment setup instructions, not a key-entry field.
- Custom providers: Details contains label, base URL, optional API-key environment-variable name, and optional `models.dev` provider override. `Tab`/`Shift-Tab` moves fields. Blank override uses the provider ID and clears any previous override. Reusing an id requires confirmation: `Enter` replaces; `Esc` returns to the form.
- Success: `Enter` or `Esc` closes. Errors: `Enter` retries, `Backspace` or `Left` goes back, and `Esc` closes.
Cancelling before Saving preserves existing credentials and settings. Once durable custom-provider saving begins, it cannot be cancelled; the modal stays open until completion is reconciled.
## Fast mode
Use `/fast on` or set global `agent.fast.enabled: true` with `agent.fast.ultrafast: false`. It defaults off, ignores project overrides, and applies to eligible primary turns, subagents, and manual or automatic blocking compaction, not session titles. `/ultrafast on` sets both flags true; either command's `off` clears both. Only one speed mode is active at a time.
Codex selects the exact catalog-advertised Fast tier id, matching Fast display/name case-insensitively. Explicit tier metadata without Fast keeps the request Standard. Legacy `additional_speed_tiers` maps `fast` to the Codex wire tier `priority`. When tier metadata or the catalog entry is missing, strictly normalized Codex GPT versions `>=5.4` use the `priority` fallback. Unsupported models/providers request no Fast tier.
Codex OAuth Responses requests include a Codex-only `x-codex-routing-hint: model=<normalized-model>`, even with Fast off, unless it is invalid as an HTTP header value. With a selected Fast tier, it adds `;tier=<service-tier-id>` and the body includes the exact `service_tier`. The hint is not sent to catalogs or custom providers.
Custom providers require explicit `providers.custom.<id>.fast_mode`, for example `{"service_tier":"priority","models":["model-name"]}`. The tier is trimmed; model ids are preserved exactly. Ids must contain 1 to 200 Unicode characters without whitespace, control characters, or secret-like values. `"*"` must be the sole entry. See [Configuration](configuration.md) for schema validation rules.
Fast is not an entitlement check or speed guarantee. The provider controls access, routing, speed, and billing/credits. In ChatGPT-authenticated Codex, Fast routing happens server-side and final response tiers do not reliably identify it: `default`, `auto`, or an absent tier leaves Fast unconfirmed, not downgraded. These observations produce no warning; `/fast status` explains the limitation without claiming Fast was confirmed. Custom-provider tier mismatches and missing confirmations still produce warnings. See [OpenAI's explanation](https://github.com/openai/codex/issues/14204#issuecomment-4033184620).
Ultrafast is a separate tier selected by `/ultrafast`, not `/fast`. For Codex, the selected model's cached catalog must advertise exact tier id `ultrafast`; there is no model-name inference, model switch, or automatic Fast fallback. Without support metadata, requests remain Standard and status reports Ultrafast unavailable. [Codex speed guidance](https://developers.openai.com/codex/speed) lists Ultrafast for GPT-6 Astra on eligible plans/workspaces; GPT-6.1 Sol supports Standard and Fast. Catalog support is not an entitlement guarantee: the backend may reject access. Ultrafast has higher usage charges; it is never enabled automatically. Custom providers can explicitly configure `service_tier: "ultrafast"` through their existing `fast_mode` metadata and `/fast`; `/ultrafast` itself is Codex-only.
In Mission Control, an unacknowledged Fast persistence-worker failure without an event blocks retry until restart; a delivered failure is cleared when its matching event is handled.
## Response detail
`providers.openai_responses.text_verbosity` accepts `low`, `medium`, or `high`. Codex uses it directly. Custom Responses providers require this value plus `use_responses_endpoint: true` and `supports_text_verbosity: true`; otherwise they omit the field.
Verbosity affects visible detail, output size, latency, and cost. It does not control reasoning effort, expose hidden/encrypted reasoning, control tool calls, or impose a hard output-token limit or exact answer length.
## Credential storage and refresh
`auth.json` is private and owner-only on Unix. Provider records may sit alongside internal non-secret `revision` and `provider_generations` metadata; do not edit that metadata. Writes preserve unrelated provider records.
Before Codex use, including applicable auxiliary work such as model discovery, near-expired or expired credentials refresh if a refresh token is available. Network exchange runs outside auth locks. Saving reacquires the in-process lock, then the cross-process file lock, and requires the same provider generation and full source record to still match. Stale/competing responses cannot overwrite newer auth; stale errors are checked against current state so newer usable credentials can win. Generations survive logout, protecting removed and recreated records from delayed refreshes.
See [Configuration](configuration.md) for file limits, paths, and credential boundaries.