Skip to content

Providers

The built-in providers and how to add any Anthropic-compatible backend with a TOML table.

Updated View as Markdown

Providers are a name → config map. Anthropic-compatible and Responses-compatible upstreams can be added with configuration; provider-specific transports use native adapters:

  • kind = "anthropic" — the upstream speaks the Anthropic Messages API. shunt passes the request through, optionally injecting a different API key.
  • kind = "responses" — the upstream speaks the OpenAI Responses API. shunt translates Anthropic Messages ⇄ Responses, including streaming.
  • kind = "cursor" — the native Cursor ConnectRPC/protobuf adapter.
  • kind = "gemini" — the native Google Code Assist generateContent adapter.
  • kind = "antigravity" — the Google Antigravity backend over HTTP.
  • kind = "antigravity_cli"deprecated; invokes the local Antigravity agy CLI.

Built-in providers

Name Kind Auth Backend
anthropic anthropic passthrough api.anthropic.com — forwards the caller’s own credential
openai responses api_key (OPENAI_API_KEY) api.openai.com/v1
codex responses chatgpt_oauth chatgpt.com/backend-api — reuses ~/.codex/auth.json
xai responses api_key (XAI_API_KEY) api.x.ai/v1 — the developer API, billed per token
grok responses xai_oauth cli-chat-proxy.grok.com/v1 — the Grok CLI proxy; reuses ~/.shunt/xai-auth.json
cursor cursor cursor_oauth api2.cursor.sh — reuses ~/.shunt/cursor-auth.json (shunt login cursor)
gemini gemini google_oauth cloudcode-pa.googleapis.com — reuses ~/.gemini/oauth_creds.json
antigravity antigravity antigravity_oauth daily-cloudcode-pa.googleapis.com — reuses ~/.shunt/antigravity-auth.json (shunt login antigravity)
antigravity-cli antigravity_cli none Deprecated. Local agy CLI — same backend via subprocess

Each linked name opens its dedicated setup page. shunt add upstream <name> prints a step-by-step blueprint for built-in providers; see the CLI reference.

Gemini providers

The built-in gemini provider translates Anthropic Messages requests to Google Code Assist and reuses the Gemini CLI OAuth file at ~/.gemini/oauth_creds.json. Authenticate with the Gemini CLI first, then add explicit discovery and routing entries. shunt uses an unexpired access token directly; if shunt itself must refresh it, set SHUNT_GOOGLE_CLIENT_ID and SHUNT_GOOGLE_CLIENT_SECRET, or rerun gemini login so the CLI refreshes the shared file.

[[models]]
id = "claude-gemini-2.5-flash-via-gemini"
display_name = "[GEM ] Gemini-2.5-Flash"

[[routes]]
model = "claude-gemini-2.5-flash-via-gemini"
provider = "gemini"
upstream_model = "gemini-2.5-flash"

Tool declarations go through the same schema adaptation as the antigravity provider — both transports share one Gemini adapter — so the tuple folding and typeless-element fallbacks described under What the adapter carries on the Antigravity provider page apply to gemini routes as well.

Code Assist also recognizes preview slugs such as gemini-3.1-pro-preview and gemini-3-flash-preview, subject to account entitlement and provider capacity. It does not accept the Antigravity-only slugs such as gemini-3.6-flash-medium.

The separate antigravity provider reaches the same backend over HTTP. Authenticate it with shunt login antigravity: a Google authorization-code flow using Antigravity’s own OAuth client, which asks for two scopes (cclog, experimentsandconfigs) a Gemini CLI login never carries — so the gemini credential cannot be reused here, and vice versa. The login also resolves the Code Assist project, provisioning one through onboardUser for a first-time account, so no discovery happens in front of a request.

Map each local alias to the exact Antigravity model slug:

[[models]]
id = "claude-gemini-3.6-flash-via-antigravity"
display_name = "[AGY ] Gemini-3.6-Flash"

[[routes]]
model = "claude-gemini-3.6-flash-via-antigravity"
provider = "antigravity"
upstream_model = "gemini-3.6-flash-medium"

Antigravity publishes its Gemini models with the effort in the id and does not serve a bare slug, in one of two shapes — a -low / -medium / -high suffix, or a single -tiered id that takes the effort as a request field. Which shape your account gets changes over time, so shunt reads the account’s own catalog and picks the id it actually publishes: a bare gemini-* id is resolved for you — from effort, the request, or medium — and a suffix your account no longer publishes is re-resolved rather than forwarded. Every spelling works.

For the full setup — login and scopes, project discovery, model slugs, thinking, and what the adapter carries — see the dedicated Antigravity provider page.

The deprecated antigravity-cli transport

The antigravity-cli path runs one CLI subprocess per request in agentic mode. agy is a full agent with its own tools, so it resolves its own tool calls and returns finished work — file edits included. Four consequences follow:

  • Tools are agy’s, not the caller’s, and a request asking for one is refused. This adapter can never emit a tool_use block, so a non-empty tools array, or a tool_choice of any or tool, is rejected with a 400 invalid_request_error naming the limitation. Such requests used to be dropped silently, returning a text-only 200 with stop_reason: "end_turn" — a response an agentic caller cannot act on, and a direct violation of tool_choice: {"type": "any"} while still reporting success. Two shapes stay exempt, because neither obliges a tool call: tool_choice: {"type": "none"} even alongside tools, and tool_choice: {"type": "auto"} with no tools, which is the Anthropic SDK default that many clients serialize on every request. An empty tools: [] is likewise not a tool request. Clients that drive the tool loop themselves should use the antigravity or gemini provider, both of which forward tools.
  • It runs with --dangerously-skip-permissions and, by default, --sandbox, because a non-interactive run has no channel to approve a permission prompt. See Workspace and sandboxing — this provider executes code.
  • Progress is streamed as it happens. Assistant text is forwarded incrementally and tool steps become SSE ping frames, so a long agent turn keeps producing bytes instead of going silent until it finishes. Token usage is Google’s reported count, not an estimate.
  • An undelivered send_message handoff does not fail a finished turn. Print mode advertises agy’s full interactive tool surface, including send_message, but registers no inbox. A subagent-style prompt (“report your findings back to the main agent”) is enough for the model to hand its reply to main, team-lead or user; the call fails and agy marks the run ERROR with recipient "<name>" not found — after the answer has already streamed. shunt ends that turn normally (end_turn, HTTP 200) when assistant text was streamed, and keeps failing it when nothing was, since the reply then only existed inside the undelivered message. Every other late failure still ends the turn with an SSE error. This is a compatibility shim keyed on an upstream error string; see docs/notes/agy-print-mode-tool-surface.md in the repository.
  • Gateway shutdown ends the turn on Unix. Each run has an isolated process group, so shunt terminates the agent and its tool subprocesses when shutdown starts instead of letting an unattended turn hold the graceful drain open.

shunt discovers the valid model/effort pairs from agy models on the first Antigravity turn. What happens next depends on where the value came from:

  • An explicitly configured effort the model does not offer is rejected with a 400 naming the valid levels. gemini-3.1-pro accepts only low and high, so a route pinning medium is an error rather than a silent substitution — quietly running high would change cost, latency and quota while the config claimed otherwise.
  • A default shunt chose itself is clamped to the nearest supported level, preferring the stronger. No operator intent is being overridden.
  • For a model shunt does not recognise, the configured value is passed through and agy validates it; its rejection enumerates the valid levels.

Workspace and sandboxing

agy runs unattended with permission prompts disabled, so treat this provider as arbitrary code execution as the user running shunt. Two independent settings bound it, and they do different jobs:

sandbox (default true) passes --sandbox, which keeps the agent’s reads and writes inside its workspace. This is the setting that actually contains it. Turning it off leaves an agent with unrestricted shell access.

workspace_roots (default empty) decides where the agent may start. Resolution order is SHUNT_AGY_WORKSPACE, then a Working directory: line in the request’s system prompt, then the gateway’s own directory. The system prompt is client-controlled text and routinely quotes fetched documents and tool output, so a path found there is honored only when it canonicalizes inside one of these roots — canonicalizing first so .. and symlinks cannot escape. With no roots configured, prompt-derived paths are ignored.

[providers.antigravity-cli]
kind = "antigravity_cli"
base_url = "http://localhost"
auth = "none"
# Repos an agent may be pointed at by request content.
workspace_roots = ["/Users/you/repos"]
# sandbox = false   # only where the agent needs unrestricted terminal access

Be clear about what workspace_roots is not: it chooses the starting directory, not a jail. With sandbox = false, an agent that starts in an allowed root can still act on a path named elsewhere in the prompt. Keep the sandbox on unless you have a specific reason, and do not expose this provider on a non-loopback bind to untrusted clients.

shunt refuses to start when sandbox = false is combined with a non-loopback bind: that pairing hands local shell access to anyone who can post a Messages request, and authentication alone does not make it safe. It also enforces the rule per request against the listener bound at boot. Because a hot reload cannot move that listener, reloading a loopback bind does not permit you to disable the sandbox while a public listener remains active. Either keep the sandbox on or restart shunt with a loopback bind.

The model/effort matrix is discovered in the background at startup, so no request pays for it. Until it lands, or if agy models fails, configured values are passed through and agy validates them itself — a failed discovery is retried rather than cached, after a short backoff so a persistently failing agy is not re-forked once per turn while the matrix is still empty.

A discovered matrix is not treated as a closed catalogue. agy gains models while the gateway runs, so a request naming a model the matrix does not list schedules a fresh discovery in the background, at most once a minute. That request, and any that arrive before the discovery finishes, still pass through to agy unvalidated; once a discovery that lists the model lands, later requests resolve normally. Without this, a model released after startup would stay unknown until shunt restarted.

The configured upstream slug proves which model shunt asks agy to run; it does not independently attest Google’s internal serving identity.

The codex provider (ChatGPT subscription)

Log in once with the Codex CLI; shunt reads and auto-refreshes ~/.codex/auth.json:

codex login

If the file is missing or expired, shunt returns an authentication_error telling you to run codex login.

For the full setup — auth-file handling, model selection, effort, and context sizing — see the dedicated ChatGPT / Codex guide.

The grok provider (SuperGrok / X Premium+ subscription)

Log in once with the built-in device-code flow; shunt writes and auto-refreshes ~/.shunt/xai-auth.json:

shunt login xai

xAI may gate OAuth access by subscription tier — if grok returns 403, use the xai API-key provider instead (export XAI_API_KEY=…).

The cursor provider (Cursor subscription)

The built-in cursor provider reaches your Cursor subscription through Cursor’s own ConnectRPC/protobuf AgentService — the kind = "cursor" native adapter translates it to and from Anthropic Messages, streaming, reasoning, native tool calls, and inline images included. Log in once with shunt login cursor, then route a cursor:* model id (cursor:default, cursor-plan:default, cursor-ask:default, …) — the provider is seeded by default, so no config table is required.

For the full setup — login, agent modes, wire model ids, adapter capabilities, and overrides — see the dedicated Cursor provider page.

The xai / grok providers (Grok)

Two built-in providers reach xAI’s Grok models, split by credential: grok spends your SuperGrok / X Premium+ subscription over OAuth (shunt login xai, no per-token billing), while xai uses an XAI_API_KEY against the metered developer API. A subscription bearer and an API key are not interchangeable — each works only against its own provider.

For the full setup — login, both provider blocks, model slugs, the opt-in effort dial, and the entitlement gotchas — see the dedicated xAI / Grok guide.

Adding an Anthropic-compatible backend

Most third-party “use Claude Code with X” gateways are Anthropic-Messages-compatible: kind = "anthropic" with auth = "api_key", differing only in base_url and the key env var. Ready-to-use bases (each provider links to a dedicated page with the full setup):

Provider base_url Example model IDs
Kimi (Moonshot) https://api.moonshot.ai/anthropic kimi-k3[1m], kimi-k2.7-code
Kimi Code (subscription, OAuth) https://api.kimi.com/coding use the ids your subscription exposes
DeepSeek https://api.deepseek.com/anthropic deepseek-v4-pro, deepseek-v4-flash
Z.ai (GLM) https://api.z.ai/api/anthropic glm-5.2, glm-4.7
Zhipu (GLM China) https://open.bigmodel.cn/api/anthropic glm-5.3, glm-5.3-flash
MiniMax https://api.minimax.io/anthropic see MiniMax docs
MiniMax China https://api.minimax.cn/anthropic MiniMax-M3
Mimo (Xiaomi) https://api.xiaomimimo.com/anthropic mimo-v2.5-pro — see Mimo docs
OpenRouter https://openrouter.ai/api anthropic/claude-opus-4.8
Vercel AI Gateway https://ai-gateway.vercel.sh anthropic/claude-opus-4.8 (accepts x_api_key)

For a provider not in this table, shunt add upstream <docs-url> prints a research blueprint that walks a coding agent through wiring it up from the provider’s own documentation.

Every row above but one takes auth = "api_key". Kimi Code is the exception: a separate, subscription-billed Kimi service from the metered Moonshot API in the row above it — different host, and OAuth instead of an API key. It has a dedicated built-in kimi-code preset (kind = "anthropic", base_url = "https://api.kimi.com/coding", auth = "kimi_oauth"), so it needs no manual [providers.*] table beyond provider = "kimi-code" under the ordered [[upstreams]] form, plus a logged-in account:

shunt login kimi --name <account-name>   # RFC 8628 device flow -> ~/.shunt/accounts/kimi/<account-name>.json
# shunt.toml — route to your Kimi Code subscription
[[upstreams]]
name = "kimi-code"
provider = "kimi-code"
auth = { mode = "kimi_oauth", account = "<account-name>" }

# Declaring [[upstreams]] replaces the built-in provider set, so keep a trailing
# anthropic passthrough — without it `shunt check` rejects the default
# server.default_provider. This is the same entry `shunt init` appends.
[[upstreams]]
name = "anthropic"
provider = "anthropic"

[[routes]]
model = "<model-id-your-subscription-exposes>"
provider = "kimi-code"

kimi_oauth is pool-capable like claude_oauth/chatgpt_oauth — use accounts = [...] in place of account to spread load across several stored Kimi accounts. See Kimi → Kimi Code (OAuth subscription) for the full walkthrough.

For example, to route Kimi’s (Moonshot) model through shunt:

[providers.kimi]
kind = "anthropic"
base_url = "https://api.moonshot.ai/anthropic"
auth = "api_key"
api_key_env = "MOONSHOT_API_KEY"

[[routes]]
model = "kimi-k3[1m]"
provider = "kimi"

[[routes]]
model = "kimi-k2.7-code"
provider = "kimi"

Then export MOONSHOT_API_KEY=…, point Claude Code at shunt, and select kimi-k3[1m] (via ANTHROPIC_CUSTOM_MODEL_OPTION or ANTHROPIC_MODEL). Run shunt check to validate — it reports an unknown provider in a route, a missing api_key_env, or a bad base_url.

Every provider key (kind, auth, api_key_header, count_tokens, …) is documented in the Configuration Reference.

Subagent plugins

The pleaseai/shunt marketplace ships ready-made Claude Code subagents pinned to each provider’s models — one agent per model. Install a plugin, then @-mention a model or set CLAUDE_CODE_SUBAGENT_MODEL. Each agent’s model: frontmatter diverts only that subagent; the main session stays on Claude.

Plugin Models (one agent each) Provider
shunt-codex gpt-5.6-sol, gpt-5.6-terra, gpt-5.6-luna codex (ChatGPT subscription)
shunt-xai grok-4.6, grok-4.5, grok-build-0.1 xai (API key) or grok (subscription)
shunt-kimi kimi-k3[1m], kimi-k2.7-code kimi
shunt-deepseek deepseek-v4-pro, deepseek-v4-flash deepseek
shunt-zai glm-5.2, glm-4.7 zai
shunt-minimax MiniMax-M3[1m] minimax
shunt-mimo mimo-v2.5-pro mimo
/plugin marketplace add pleaseai/shunt
/plugin install shunt-xai@shunt

Each plugin still needs its provider routed in shunt.toml (see the sections above) and the matching credential exported — the plugin’s own README lists the exact route and env var. The grok models can be served by either xAI provider: xai (API key, billed per token) or grok (SuperGrok / X Premium+ subscription via shunt login xai; tier-gated — fall back to xai on 403).

Navigation

Type to search…

↑↓ navigate↵ selectEsc close