Two built-in providers route Claude Code to xAI’s Grok models. They differ only in how they authenticate and which xAI surface they hit — pick one:
| Provider | Auth | Backend | Billing |
|---|---|---|---|
grok |
xai_oauth — your SuperGrok / X Premium+ login |
cli-chat-proxy.grok.com/v1 (the Grok CLI chat proxy) |
your subscription — no per-token charge |
xai |
api_key (XAI_API_KEY) |
api.x.ai/v1 (the developer API) |
metered API credits |
Both are kind = "responses" providers speaking xAI’s Responses dialect — the same translation
path as Codex, which this page mirrors. It links out to the deeper topic pages
(Effort & Context, Model Discovery,
Providers) rather than repeating them.
Quick start
Let a coding agent wire it up for you — shunt add prints the setup blueprint for either path
(offline and read-only; the agent edits the config, the command never does):
shunt add upstream grok --print | claude # SuperGrok / X Premium+ subscription (OAuth)
shunt add upstream xai --print | claude # xAI developer API (XAI_API_KEY)Or follow the manual steps below.
How it works
shunt translates Claude Code’s Anthropic Messages request into the OpenAI Responses API, sends
it to xAI, and translates the streamed reply back. The xai Responses flavor (drops params xAI
rejects, keeps tools as function tools) is selected two ways: by an api.x.ai host, or by
auth = "xai_oauth" (the grok proxy isn’t an x.ai host, so its dialect is keyed on the auth).
| Aspect | grok (subscription) |
xai (API key) |
|---|---|---|
| Endpoint | cli-chat-proxy.grok.com/v1/responses |
api.x.ai/v1/responses |
| Auth | Grok CLI OAuth from ~/.shunt/xai-auth.json, auto-refreshed |
Bearer $XAI_API_KEY |
| Identity headers | Grok CLI headers (x-xai-token-auth, x-grok-client-identifier, x-grok-client-version) so the proxy honors the subscription |
none |
Path A — SuperGrok subscription (grok)
1. Log in
Run shunt’s own device-code login (RFC 8628). It prints a URL and a code; approve in a browser on any device — no loopback callback server:
shunt login xaiOn success shunt writes the tokens to ~/.shunt/xai-auth.json with 0600 permissions and
refreshes them automatically (a 5-minute expiry buffer; xAI rotates the refresh token on every refresh, so shunt
persists the rotated one under a single-flight lock). If the refresh token is gone or the response
omits a rotated token, shunt tells you to run shunt login xai again.
2. The provider block (optional)
grok is built in — you don’t need to declare it. This is the full default; a partial table
overrides only the keys you set (config maps deep-merge):
[providers.grok]
kind = "responses"
base_url = "https://cli-chat-proxy.grok.com/v1" # shunt appends /responses
auth = "xai_oauth" # read + auto-refresh ~/.shunt/xai-auth.json
# effort = "high" # optional — opt in to reasoning effort (§ Reasoning effort)3. Route a model to grok
[[routes]]
model = "grok-4.5"
provider = "grok"
# upstream_model = "grok-4.5" # optional: forward a different slug upstreamPath B — xAI developer API (xai)
1. Export the key
export XAI_API_KEY=xai-…2. The provider block (optional)
[providers.xai]
kind = "responses"
base_url = "https://api.x.ai/v1" # shunt appends /responses
auth = "api_key"
api_key_env = "XAI_API_KEY"3. Route a model to xai
[[routes]]
model = "grok-4.5"
provider = "xai"Model slugs
The slug catalog is xAI’s, not shunt’s — shunt forwards whatever slug you route. Current
coding/frontier slugs are grok-4.5, grok-4.3, and grok-build-0.1. Use upstream_model in a
route to map an alias onto a live slug without touching your Claude Code env. (Model
discovery only surfaces claude-named aliases you declare, so it can’t
list a raw Grok slug — reach these via ANTHROPIC_CUSTOM_MODEL_OPTION or a tier remap below.)
Select the model in Claude Code
Grok slugs don’t start with claude-, so Claude Code’s /model picker won’t list them from
discovery. The mechanics are identical to Codex — add the id to the picker directly:
export ANTHROPIC_CUSTOM_MODEL_OPTION="grok-4.5" # must resolve via [models.upstream_model], [[routes]], or [[route_prefixes]]The same Codex section covers the rest verbatim:
putting a subagent on a Grok slug via model: frontmatter, and remapping the tier aliases
(ANTHROPIC_DEFAULT_SONNET_MODEL, …) onto Grok slugs for a whole session.
Reasoning effort
Unlike Codex, effort is opt-in for Grok. Several Grok models (grok-4*, grok-3,
grok-code-fast, …) return 400 on a reasoning.effort field even though they reason natively, so
shunt sends the dial only when you configure it on the provider or route (or pass it per
request) — otherwise the model uses its native reasoning:
[providers.grok]
effort = "high" # applies to all grok traffic
# …or per route
[[routes]]
model = "grok-4.5"
provider = "grok"
effort = "high"grok-4.5 accepts reasoning.effort (verified live). Leave effort unset for any slug that
400s on it. Full precedence and the effort table: Effort & Context.
Context window
Claude Code sizes its context bar at a fixed 200k for a mapped id. If your Grok slug’s real
window is larger, raise it — the value follows a non-claude- id automatically:
export CLAUDE_CODE_MAX_CONTEXT_TOKENS=256000 # set your slug's real window, per xAI's model docsIt’s global (one value per session); set it to the smallest real window among your mapped
models, since exceeding a model’s real window causes prompt is too long overflow churn. Details
and count_tokens behavior: Effort & Context.
Web search
Claude Code’s built-in web search does not work on Grok routes. xAI’s Responses API accepts only
function tools, so shunt drops the hosted web_search tool on the xai flavor (both grok and
xai). Use a codex or openai route when you need hosted web search.
Full example (subscription path)
shunt.toml:
[server]
bind = "127.0.0.1:3001"
default_provider = "anthropic"
[providers.grok]
effort = "high" # optional: opt in to reasoning effort for all Grok traffic
[[routes]]
model = "grok-4.5"
provider = "grok"Shell (both shunt and Claude Code run with these):
shunt login xai # one-time device-code login
./target/release/shunt run # start the gateway
export ANTHROPIC_BASE_URL=http://127.0.0.1:3001
export ANTHROPIC_CUSTOM_MODEL_OPTION="grok-4.5" # add to /model pickerPick grok-4.5 from /model. Everything else in the session still flows to Anthropic unchanged;
only the mapped model’s inference is answered by your SuperGrok subscription.
Troubleshooting
| Symptom | Cause / Fix |
|---|---|
run shunt login xai on startup |
No ~/.shunt/xai-auth.json (or wrong $SHUNT_XAI_AUTH_FILE). Run shunt login xai. |
xAI refresh response missing refresh_token; run shunt login xai |
The stored refresh token was consumed/rotated away. Log in again. |
402 … personal-team-blocked:spending-limit / “need a Grok subscription” |
On the xai (developer API) path without API credits. Add credits at console.x.ai, or route to grok to use your subscription. |
403 … not authorized for API access (subscription tier gate) |
On the grok path your subscription tier doesn’t include API access — re-logging in won’t help. Set XAI_API_KEY and use the xai path, or upgrade at x.ai/grok. |
refusing to send a subscription token off-origin (from shunt check) |
An xai_oauth provider’s base_url host isn’t x.ai/grok.com, isn’t HTTPS, or isn’t kind = "responses". Fix the block. |
400 when effort is set |
That Grok slug rejects reasoning.effort. Remove effort from the provider/route for it. |
model <slug> is not enabled for this account |
Unentitled slug — confirm the slug against xAI’s catalog. |
| Web search returns nothing | Not supported on Grok routes; shunt drops the tool. Use a codex/openai route. |
See the full Troubleshooting reference for more.