Based on the official Deploy Claude Desktop with an LLM gateway guide — shunt is the gateway you point it at. shunt implements the Anthropic Messages API (POST /v1/messages with streaming and tool use) and the optional GET /v1/models, which is exactly the gateway contract Claude Desktop’s third-party inference expects.
1. Point Claude Desktop at shunt
In Developer → Configure Third-Party Inference…, set Inference provider to Gateway and Gateway base URL to your running shunt (default bind 127.0.0.1:3001):
| Claude Desktop key | Value |
|---|---|
inferenceProvider |
gateway |
inferenceGatewayBaseUrl |
http://127.0.0.1:3001 (or your public shunt URL) |
shunt serves plain HTTP; for anything beyond a loopback deployment, terminate TLS in front or tunnel it, exactly as for sharing the gateway.
2. Choose an authentication approach
Claude Desktop offers three approaches. shunt maps cleanly onto the static key (and its credential-helper variant); per-user SSO is a gateway-side capability shunt does not implement inbound.
| Claude Desktop approach | shunt side | Notes |
|---|---|---|
Static API key (inferenceGatewayApiKey) |
[server.auth] client token |
Recommended. |
Credential helper (inferenceCredentialHelper) |
an executable printing a [server.auth] client token |
For orgs that already mint gateway credentials. |
Interactive SSO (inferenceGatewayOidc + inferenceCredentialKind: interactive) |
not supported inbound | shunt validates static tokens, not external-IdP JWTs — see below. |
Static API key (recommended)
Enable [server.auth] on shunt and hand each user a client token:
[server.auth]
header = "x-shunt-token" # default
tokens_env = "SHUNT_CLIENT_TOKENS"Put that token in Claude Desktop’s inferenceGatewayApiKey. shunt accepts the client token in Authorization: Bearer or x-api-key, so either Gateway auth scheme works:
| Claude Desktop key | Value |
|---|---|
inferenceGatewayApiKey |
your shunt client token |
inferenceGatewayAuthScheme |
bearer (default) or x-api-key |
Without [server.auth], shunt requires no inbound credential (fine for a personal loopback gateway); Claude Desktop still wants the field populated, so enter any placeholder.
The token gates GET /v1/models and injected-credential (mapped/pooled) models; passthrough models stay open and carry the operator’s own provider credential.
3. Select models
shunt serves GET /v1/models, so Claude Desktop auto-discovers the picker at launch. Two things govern what appears.
Discovery filter. Claude Desktop’s auto-discovery shows only recognizably Claude ids — the tier-named ones (claude-sonnet-*, claude-opus-*, claude-haiku-*, claude-fable-*). shunt’s builtin catalog mirrors the reference Claude apps gateway exactly — nine tier-named ids, so Claude Desktop shows every one:
// GET /v1/models — builtin catalog (auto_include_builtin_models), all tier-named
// (each entry also carries "type": "model")
{ "data": [
{ "id": "claude-opus-4-6" }, { "id": "claude-sonnet-4-5-20250929" },
{ "id": "claude-haiku-4-5-20251001" }, { "id": "claude-fable-5" },
{ "id": "claude-opus-4-8" }, { "id": "claude-opus-4-7" },
{ "id": "claude-opus-4-1-20250805" }, { "id": "claude-sonnet-5" },
{ "id": "claude-sonnet-4-6" }
], "has_more": false, "first_id": null, "last_id": null }A curated claude-<slug>-via-<provider> alias (the pattern that works in Claude Code) is dropped by Claude Desktop — see Model Discovery → Claude Desktop recognizes only tier-named ids.
Exposing a non-Anthropic backend. Two options:
-
Map a tier-named id with a
[[routes]]upstream_model, so selecting it in Desktop resolves to your backend:[[routes]] model = "claude-sonnet-5" # a tier-named id Claude Desktop recognizes provider = "codex" upstream_model = "gpt-5.6-sol" # real backend slug -
Override discovery on the Desktop side with an explicit
inferenceModelslist of the exact ids shunt routes on. When every entry is a full id, Claude Desktop skips the/v1/modelscall.
4. Verify
Confirm shunt answers discovery and inference with the client token:
# Discovery — the token gates it when [server.auth] is set
curl -s "$SHUNT_URL/v1/models" -H "Authorization: Bearer $SHUNT_CLIENT_TOKEN" | jq '.data[].id'
# A tier-named id you mapped -> diverted to the backend
curl -s -X POST "$SHUNT_URL/v1/messages" \
-H "Authorization: Bearer $SHUNT_CLIENT_TOKEN" \
-H "anthropic-version: 2023-06-01" -H "content-type: application/json" \
-d '{"model":"claude-sonnet-5","max_tokens":16,"messages":[{"role":"user","content":"hi"}]}'Then open Claude Desktop; the model picker should list the tier-named entries. If it is empty, discovery was filtered out (non-tier ids) or /v1/models was unreachable — set inferenceModels explicitly as a fallback.