# 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`.
### Claude subscription
This provider uses your claude.ai subscription through the Claude Code CLI you already have installed. It doesn't need an Anthropic API key. The separate `anthropic` provider still does.
To set it up, 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`. When magi-code sets up the provider, it runs `claude auth status --json` and expects a signed-in `claude.ai` account. It never uses API keys, stored OAuth tokens, or `--bare`. To sign out, run `claude auth logout`.
#### 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 uses a 202,000-token input ceiling instead of published model windows. Model overrides cannot raise it. When Claude subscription also handles compaction, a configurable reserve reduces conversation capacity (default 16,384 reserved, 185,616 usable). `/settings` → **Agent settings** has separate Claude thresholds and a toggle requiring another compaction provider when disabled. Automatic compaction and context checks stay enabled. See [context limits](sessions-context-cache.md#handle-context-limits) for budgeting and recovery.
#### How requests run
Each request starts a fresh, unmodified Claude CLI process and a temporary local relay. The relay lets exactly one request through to `https://api.anthropic.com/v1/messages` and refuses any further generations. It forwards the CLI's own authorization headers and keeps them in memory only. When you cancel, magi-code stops the whole CLI process group. That cleanup is why this provider requires Unix.
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. Magi-code holds back CLI output until the full response and its token usage have arrived.
Reasoning levels other than default map to the CLI's `--effort` flag. 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 generated content, then five minutes without further progress. Text, reasoning (including signatures and redacted reasoning), and tool-call output reset the progress timer; keepalives and metadata do not. An internal per-request progress timeout can override the five-minute inactivity limit. A separate 30-minute overall limit bounds each generation. Timeouts stop both the CLI process group and its relay; buffered partial output is not committed.
When a request is rejected, magi-code shows the HTTP status and a short error category. It never logs raw upstream responses or CLI stderr.
### 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.