Skip to content
LUNAROUTEDocs

Claude Desktop

Claude Desktop talks to LunaRoute as a third-party (“3P”) gateway. It does not read ANTHROPIC_BASE_URL, environment variables, or ~/.claude/settings.json for this — it has its own third-party inference configuration, and that is the only lever that routes it.

LunaRoute fills Desktop’s model picker by discovery: Desktop asks GET /v1/models, identifies itself with a client header, and LunaRoute answers with Claude-shaped names Desktop accepts. Nothing is hardcoded on your side.

Quit Claude Desktop first. It keeps that profile in memory and can write its own copy over your change when it exits — reopen it in step 2. Then install the CLI (CLI setup) and run:

Terminal window
npm install -g @lunaroute/cli
lunaroute setup claude-desktop

Desktop creates its own profile file the first time you configure third-party inference; the CLI merges the gateway keys into that file for you, keeping any other keys it finds. It refuses rather than guess when Desktop has not created a profile yet:

Claude Desktop has not created a third-party inference profile yet — in Desktop open Help → Troubleshooting → Enable Developer Mode, then Developer → Configure Third-Party Inference once; then re-run.

Before its first write the CLI takes a snapshot of that profile, at ~/.config/lunaroute/backups/claude-desktop/ (mode 0600 — it holds your key). That snapshot is what the undo below restores. Use lunaroute setup claude-desktop --print to preview without writing anything.

Manual configuration (the GUI way)

Prefer to do it by hand? Open Help → Troubleshooting → Enable Developer Mode, then Developer → Configure Third-Party Inference, and enter your routing URL:

https://gw.lunaroute.com

That is what makes Desktop write a profile file. Let the app create it — do not hand-create one; the file name is generated by the app and the app tracks which profile is applied.

Then open that profile file and merge in these keys:

{
"inferenceProvider": "gateway",
"inferenceGatewayBaseUrl": "https://gw.lunaroute.com",
"inferenceGatewayApiKey": "lr_your_key_here",
"inferenceGatewayAuthScheme": "bearer",
"modelDiscoveryEnabled": true,
"inferenceCustomHeaders": { "X-LunaRoute-Client": "claude-desktop" }
}

On Linux that file lives under ~/.config/Claude-3p/configLibrary/. The X-LunaRoute-Client header is what selects LunaRoute’s Claude-compatible catalog; without it you get the normal catalog, whose names Desktop filters out.

Two things about that file:

  • The key is stored literally. Desktop expands no environment variables here, so paste the key in full and keep the file private.
  • Do not add an inferenceModels list. A pinned list switches discovery off — Desktop skips the discovery call when every entry is a full model id — and then the picker shows none of LunaRoute’s models. This is the single most common way to get an empty picker.

Quit and reopen Claude Desktop (on Windows, end the Claude process in Task Manager if it will not quit). The picker now fills from discovery.

Quit Claude Desktop before this too (reopen it after). Then:

Terminal window
lunaroute setup claude-desktop --remove

That restores the profile snapshot taken before setup — byte-for-byte, including any keys your own configuration had before LunaRoute touched the file. It needs no login, and it leaves the snapshot in place, so running it twice is harmless.

If there is no snapshot (you configured the profile by hand, or setup ran on an older CLI), the command prints the keys to remove and changes nothing — it will not delete a key it cannot prove it wrote. Add --print to see what it would do without touching the file.

The picker lists Claude-shaped aliases — claude-opus-lr29, claude-haiku-lr37, and so on. The id is a wire alias that LunaRoute translates back to the catalog model before routing, so nothing about that mapping is yours to maintain.

The catalog entry also carries a human-readable label in display_name — an operator-configured value (for example GLM 5.3), falling back to the canonical LunaRoute model id when none is set. Whether Desktop renders that label in its picker is not yet verified; the alias id is what it requires.

With a gateway configured, Desktop runs local sessions only: no SSH or cloud environments, and no Remote Control.

Terminal window
curl https://gw.lunaroute.com/v1/models \
-H "Authorization: Bearer $LUNAROUTE_API_KEY" \
-H "X-LunaRoute-Client: claude-desktop"

You should get the same alias list the picker shows. The same request without the X-LunaRoute-Client header returns the normal catalog, which is what Desktop rejects.

The profile path above and the CLI’s write are verified on Linux. macOS and Windows use the same configuration shape, but their profile locations have not been verified — so the CLI refuses to guess them there, and you should check where the app wrote its own file before editing it by hand.

Symptom Cause
Picker is empty An inferenceModels list is pinned. Remove it — a pinned list disables discovery.
Picker is empty, no list pinned The X-LunaRoute-Client header is missing or misspelled in inferenceCustomHeaders.
“Configuration may need attention” A key in the profile is not one Desktop recognises, or the routing URL has a path — it takes an origin only (https://gw.lunaroute.com, no /v1).
Models list, but requests fail with 401 The key is wrong or expired. Desktop does not expand variables; re-paste the literal key.
A model appears but will not answer Its upstream is unavailable — that model’s provider credential or capacity, not your configuration.
--remove says “no snapshot exists” The profile was configured by hand. Remove the six keys it prints (or re-run setup, then --remove).
  • Authentication — key formats and header variants.
  • Models API — the catalog /v1/models serves, including the Claude Desktop variant.
  • Harness setup — Claude Code, Codex, and the other harnesses.
  • CLI — the full lunaroute setup surface.
  • Claude Code — the CLI, which routes through environment variables instead of the profile above.
  • Codex — Codex CLI, profile-based or per session.