Desktop reference
The mechanics behind Codex app, Claude Desktop, and Managed fleet.
Billing lanes
The lane follows the platform and the enrolled auth mode. You never pick it in the app.
| Lane | When | How it authenticates |
|---|---|---|
| subscription | Claude Code subscription on a verified macOS build | A local credential helper reads your Claude Code credential from the login Keychain on demand and adds Anyray's pass-through headers. It never persists or logs the credential, and Desktop's own first-party login stays direct. |
| API-billed | Org or API mode on macOS, and all of Windows | The helper resolves your personal gateway key (ark_…) at request time. No static key is stored in Claude's configuration. |
Keep Claude Code signed in on the subscription lane. If the credential cannot be read, Connect fails closed rather than silently moving the request to API billing. Windows has only the API-billed lane, and selecting subscription there fails closed.
How Connect configures each platform
| Platform | Mechanism |
|---|---|
| macOS | Automatic when Connect can verify the installed build's config seam: the app is 1.21459.0 or newer and its bundle still ships the configuration shape Connect writes. Before writing, Connect checks Claude is closed and no managed policy owns the setting. The subscription lane also needs a readable Claude Code credential in the login Keychain. |
| Windows | Writes the per-user policy HKCU\SOFTWARE\Policies\Claude and a key-free credential helper, with no admin rights. An existing machine-level HKLM policy is left untouched. |
On supported automatic lanes Connect configures both the inference route and the retrieval MCP server. Known conflicts are rejected before routing changes, and an interrupted write recovers by re-running the command. Key renewal never rewrites Claude policy, because the helper resolves the current key at request time. After a Desktop app update, Connect rechecks the macOS seam before any local healing and fails closed if the new build is not verifiable.
Model list and the default model
When inferenceModels is unset, Claude Desktop builds the picker from the gateway's
GET /v1/models response and selects its own default, which can be a more expensive model than the
fleet was using. The first inferenceModels entry is the default, so set the list explicitly:
[{ "name": "claude-sonnet-4-6" }, { "name": "claude-opus-5", "supports1m": true }]
Use full model ids the gateway accepts, because bare tier aliases such as sonnet rely on discovery
to resolve. Set modelDiscoveryEnabled to false to skip the discovery request and its launch delay
when the gateway is only reachable on an internal network.
Discovery follows the same route inference does: the default routing config, or the sole provider key
when exactly one is configured, falling back to Anthropic only when neither is set. The fallback
still needs an Anthropic credential, meaning a key on the console's Providers page or the seat's own
Claude subscription passed through. With neither a route nor a credential, discovery answers 424,
so a picker that errors means the deployment has no usable route yet.
Chat tab and the Cowork sandbox
Local setup and every fleet artifact set two keys:
| Key | Why |
|---|---|
chatTabEnabled | Third-party mode hides the Chat tab by default (Claude Desktop 1.13576.0 and newer). |
coworkEgressAllowedHosts = * | Third-party mode otherwise narrows the Cowork and Code sandbox network to the gateway alone (1.2581.0 and newer). * restores what first-party mode allowed. |
To restrict egress, set your own list. A managed profile outranks local setup, and static fleet artifacts carry the key. Anthropic's docs list required hosts under Egress Requirements.
Connectors under third-party inference
Connectors not labeled "Made by Anthropic" work under third-party inference. managedMcpServers
carries remote HTTP and SSE servers, local stdio commands, and the built-in microsoft365,
websearch, and github connectors.
"Made by Anthropic" connectors exist only in standard Claude Desktop. Google Workspace can be rebuilt through Google's own Workspace MCP servers, but claude.ai connector grants and OAuth identities never transfer.
Optimization boundary
Gateway-routed requests get the optimizer strategies on every lane, so a subscription seat is
optimized the same as an API-billed one. The registered anyray_retrieve and anyray_recall tools
let the optimizer externalize large or duplicated context to a recoverable handle. Without the MCP,
the optimizer still runs and saves tokens but trims context in place, so there is nothing to
retrieve.
On the manual, unverified, and fleet-policy lanes the registration is not authoritative yet, so
status and doctor report retrieval coverage as partial. Claude Desktop exposes no supported hook
that replaces tool results at the source, so retrieval through the MCP is the equivalent.
In-app setup
If automatic setup cannot run, configure the app by hand. This paste flow is the API-billed lane, and a selected subscription never falls back to it silently.
macOS: Help → Troubleshooting → Enable Developer Mode in the menu bar. Windows: the menu at the top-left of the sign-in screen.
Developer menu → Configure Third-Party Inference → Gateway. Not Settings → Developer, which is MCP servers rather than inference.

Provider Gateway, base URL https://gateway.example.com, scheme Bearer, and your ark_…
key. Click Apply locally, then fully quit and reopen Claude.
On supported automatic setup, Connect also registers the anyray MCP server in
claude_desktop_config.json: anyray_retrieve, anyray_recall, anyray_search, anyray_read,
plus anyray_history once you have imported conversations. It loads at the same relaunch and appears
under Settings → Developer.
anyray-connect doctor --verify confirms requests reach the gateway. Lost the key?
anyray-connect print-key reprints it.
What is already on this machine, automatically
Copying needs the app fully quit, and the destination stores exist only after the new profile has
been opened and signed in to. So Connect queues the copy and completes it at the first safe
opportunity. anyray-connect doctor lists the queued migration until it finishes.
anyray-connect reports how many local items are waiting and queues the copy.To do it now instead, fully quit Claude Desktop and run anyray-connect migrate. It is safe to
re-run, because every step skips what is already there. Run it before a third-party store exists and
it says which store is missing: a Claude Code session creates the Code store, and opening the Cowork
tab creates the Cowork store.
| Local data | What copies |
|---|---|
| Sessions | Claude Code session records whose transcript still exists, plus complete Cowork task data |
| Local MCP | Non-Anyray entries from claude_desktop_config.json |
| Plugins | Complete user-scope plugin packages with a valid plugin manifest |
| Skills | Skills whose manifest explicitly marks them as user-created and not sync-managed |
The migration is selective, and it copies, never moves. The previous profile stays as it was, an existing item in the target is never overwritten, and the copy runs directly between the two profile directories for the same OS user, reporting counts only.
What never copies
Anthropic-managed bundles, cloud-managed connectors, plugins, or skills, Claude sign-in and OAuth state, connector grants, browser storage, and caches. Malformed or incomplete packages, packages containing symlinks, and Claude Code sessions whose transcript is no longer on disk are skipped. Connect reports the counts.
Claude Code transcripts live in ~/.claude/projects/, outside both profiles, and apply and revert
never touch them.
Several accounts, cancelling, and PATH
Signed into more than one account? Setup leaves the sessions where they are. Run
anyray-connect migrate to pick an account (--account <id>) or take everything
(--all-accounts).
A full anyray-connect --revert cancels an outstanding copy, and that run takes the machine off
Anyray. anyray-connect desktop disconnect keeps it queued.
If anyray-connect is not on this shell's PATH, which happens when a setup scoped with
--tools never adds the install directory, call the binary directly:
~/.anyray/bin/anyray-connect migrate.
Put them back in the app
This needs the Claude Code CLI installed on the same machine, because Connect converts the export with Claude Code's own importer. Without it, restore refuses. The searchable archive has no such requirement.
Settings → Privacy → Export data. Anthropic emails a download link. Keep the .zip, because
this step takes the archive itself, not the file inside it.
A running app writes its conversation list back on exit and would undo the restore.
- macOS / Linux
- Windows PowerShell
anyray-connect migrate --export ~/Downloads/claude-export.zip
anyray-connect migrate --export $HOME\Downloads\claude-export.zip
No anyray-connect on this shell's PATH? Go through the installer. The subcommand belongs
after sh -s --, not on the curl line.
- macOS / Linux
- Windows PowerShell
curl -fsSL https://app.anyray.ai/connect.sh | sh -s -- migrate --export ~/Downloads/claude-export.zip
& ([scriptblock]::Create((irm https://app.anyray.ai/connect.ps1))) "migrate" "--export" "$HOME\Downloads\claude-export.zip"
The conversations are listed under Cowork → Recents. Routing is unchanged.
Your claude.ai Projects come across in the same step. Each becomes a Cowork space with its custom
instructions intact and its docs written to ~/Documents/Claude/Projects/<name>. An existing folder
of that name is never written into, because Connect qualifies the name. Re-running adds only what is
missing.
The Chat tab syncs from your Claude account and has no local store to fill, and the Code tab
lists Claude Code sessions anchored to a working directory, which an imported chat does not have.
The session-storage layout is a Claude Desktop internal with no compatibility promise, so keep your
export file. If the layout changes, Connect refuses rather than half-writing, and
history import still works.
Make them searchable everywhere
anyray-connect history import ~/Downloads/claude-export/conversations.json
"What did we decide about the pricing model?" The assistant searches the archive through the
registered anyray_history MCP tool and pulls the conversation into the chat.
The archive is local: owner-only markdown under ~/.anyray/history, never uploaded, and
anyray_history reads only inside that directory. anyray-connect history status prints where it
lives and how many conversations it holds. Any Anyray-connected client with the MCP server registered
can search it.
A conversation the assistant reads becomes part of the chat, which travels through your gateway like every other request, subject to the usual content mode, encrypted at rest by default. History results are excluded from gateway-side output optimization.
Fleet policy models
| Model | What MDM carries | How configuration changes |
|---|---|---|
| First-party | Device policy only, and no inference* key | Desktop stays on claude.ai, and Anyray covers the coding tools |
| Bootstrap | A stable HTTPS URL, the bootstrap-headers helper path, and optional OIDC parameters | Update the bootstrap response, then relaunch Claude, with no MDM profile redeployment |
| Static | The complete policy exported from Claude Desktop, including the fleet credential-helper path | Edit and test in Claude Desktop, then export and redeploy the profile |
First-party is the prior decision, not a lesser one. If claude.ai currently delivers your connectors and plugins, it preserves that, and Anyray still routes every coding tool. The other two move Desktop to third-party inference, ending admin-console delivery. Prefer static over bootstrap when the bootstrap policy's unauthenticated readability is unacceptable.
Keep Claude Desktop first-party
First-party and third-party policy share one profile domain, so the same MDM channel can enforce
device policy while leaving Desktop on claude.ai. The rule: the profile carries no inference*
key. Connectors, plugins, skills, and model policy keep coming from the admin console.
Once the profile is installed, Connect detects that MDM owns Claude Desktop, refuses to change it,
and holds Desktop back from any run that did not name it (--tools claude-desktop).
| Key | What it controls |
|---|---|
forceLoginOrgUUID | Sign-in must belong to your organization. The UUID comes from your claude.ai admin console |
allowedWorkspaceFolders | Folders the agent may work in |
isLocalDevMcpEnabled | Whether users may add their own local MCP servers |
isDesktopExtensionEnabled, isDesktopExtensionDirectoryEnabled | Desktop extension installs, and whether the directory is shown |
secureVmFeaturesEnabled | Cowork |
isClaudeCodeForDesktopEnabled | The embedded Code tab. Turning it off steers coding work to the standalone CLI, which Anyray does route |
disableAutoUpdates, autoUpdaterEnforcementHours | The updater |
Generate two UUIDs with uuidgen. Every value is a <string>, including booleans and JSON:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>PayloadContent</key>
<array>
<dict>
<key>PayloadType</key>
<string>com.anthropic.claudefordesktop</string>
<key>PayloadVersion</key>
<integer>1</integer>
<key>PayloadIdentifier</key>
<string>com.example.claude-desktop.payload</string>
<key>PayloadUUID</key>
<string>FIRST-UUID</string>
<key>PayloadDisplayName</key>
<string>Claude Desktop</string>
<key>forceLoginOrgUUID</key>
<string>YOUR-CLAUDE-ORG-UUID</string>
<key>isLocalDevMcpEnabled</key>
<string>false</string>
<key>isDesktopExtensionEnabled</key>
<string>false</string>
<key>allowedWorkspaceFolders</key>
<string>["/Users/Shared/work"]</string>
</dict>
</array>
<key>PayloadType</key>
<string>Configuration</string>
<key>PayloadVersion</key>
<integer>1</integer>
<key>PayloadIdentifier</key>
<string>com.example.claude-desktop</string>
<key>PayloadUUID</key>
<string>SECOND-UUID</string>
<key>PayloadDisplayName</key>
<string>Claude Desktop</string>
</dict>
</plist>
On Windows the same names go directly under HKLM\SOFTWARE\Policies\Claude as REG_SZ or
REG_DWORD. Never a subkey, and never REG_EXPAND_SZ, which counts as machine policy the app
cannot read.
All three must pass before you widen:
- Help → Troubleshooting → Copy Managed Configuration Report lists the keys the app read.
- Developer → Configure Third-Party Inference now opens read-only and organization-managed.
anyray-connect desktopreports the centrally managed route and does not change that policy.
On a managed machine that run also writes a ready-to-deploy third-party profile to
~/.anyray/claude-desktop-anyray.mobileconfig (.reg on Windows), carrying the full Anyray
connection and no credential, so switching later is an MDM push.
Deploy the profile to the fleet, then follow
Bulk enrollment with your MDM for Claude Code and the other
surfaces. None of those artifacts touch com.anthropic.claudefordesktop.
Author the complete policy in Claude Desktop
Claude Desktop's configuration window is the supported editor for a complete third-party deployment, so there is no hand-authored connector JSON or plist. Connector definitions come from the Claude connector directory, the connector vendor, or your internal MCP service. OAuth client registration comes from that vendor or your IdP, and plugin marketplaces from approved Git repositories.
Keep Claude Desktop signed out, enable Help → Troubleshooting → Enable Developer Mode, and
run anyray-connect desktop.
Open Developer → Configure Third-Party Inference. Under Connectors & extensions, add each approved remote MCP URL and use Test this connection. Under Plugins & skills, add the approved Git marketplaces. Set the model list and default, the Chat, Cowork, and Code surfaces, workspace and tool restrictions, extension policy, egress allowlist, and telemetry policy in the same window.
Choose Apply locally, fully quit Claude Desktop, and reopen it. Verify inference, connectors, plugins, and skills. Verify the Anyray retrieval tools separately, because the local stdio registration is not automatically part of an exported managed policy.
Package the fleet-wide credential helper at one stable
absolute path on every endpoint. Change the Connection pane from the staging user's ~/.anyray
helper to that fleet path, choose Export → .mobileconfig, upload to Jamf, scope to a test
device, then widen.
Anthropic's in-app configuration, MDM deployment, and extensions references cover the exported fields. A claude.ai connector grant, OAuth token, cookie, Keychain item, or account session cannot be exported, so each user authorizes each rebuilt connector after IT deploys the policy.
An endpoint that already receives recognized Claude Desktop policy cannot edit or supplement it locally. Author and export on an unmanaged staging Mac, then replace the complete profile in Jamf.
Static helper-backed configuration
Static artifacts contain no personal key, and the credential helper holds none either. It runs as the
signed-in user and resolves that user's credentials at runtime, so each user must already be enrolled.
A subscription enrollment on macOS rides the user's own Claude seat, because the helper reads the
Claude Code credential from the login Keychain. Any other enrollment resolves the personal ark_…
key. Like the bootstrap artifacts, static ones hide the claude.ai sign-in option with
disableDeploymentModeChooser, and --apply on a developer's own machine leaves that choice alone.
Put the standalone anyray-connect binary at a durable, fleet-wide absolute path, such as
/usr/local/bin/anyray-connect or C:\Program Files\Anyray\anyray-connect.exe, updated in place
through software distribution. A system-installed fleet binary does not use Connect's per-user
self-updater.
--write verifies the binary and writes both wrappers atomically:
sudo /usr/local/bin/anyray-connect desktop helper --write --platform posix \
--bin /usr/local/bin/anyray-connect
& "C:\Program Files\Anyray\anyray-connect.exe" desktop helper --write `
--platform windows --bin "C:\Program Files\Anyray\anyray-connect.exe"
For MDM packaging instead of a local install, use desktop helper --print with the same --platform
and --bin, plus --wrapper credential or --wrapper bootstrap-headers. Stdout is only that
wrapper's bytes.
Point --helper-path at the wrapper installed in step 2:
anyray-connect desktop fleet static macos \
/usr/local/bin/anyray-credential-helper claude.mobileconfig \
--gateway https://gateway.example.com
anyray-connect desktop fleet static windows \
"C:\Program Files\Anyray\anyray-credential-helper.cmd" claude.reg \
--gateway https://gateway.example.com
anyray-connect desktop fleet static linux \
/usr/local/bin/anyray-credential-helper managed-settings.json \
--gateway https://gateway.example.com
anyray-connect desktop codex --mobileconfig codex.mobileconfig \
--gateway https://gateway.example.com \
--helper-path /usr/local/bin/anyray-credential-helper
Deploy Linux's generated file to /etc/claude-desktop/managed-settings.json. For Windows Codex, omit
--mobileconfig and pass the Windows .cmd helper path. The artifact is managed_config.toml,
deployed to %ProgramData%\OpenAI\Codex\managed_config.toml, or /etc/codex/managed_config.toml on
Linux and macOS.
Subscription-seat Codex routing cannot be a credentialless fleet artifact, so desktop codex --subscription refuses: ChatGPT OAuth still requires a personal gateway key in x-anyray-api-key.
Deploy per-user enrollment instead.
Routine key renewal needs no redeployment, because the helper resolves it at request time. Regenerate and redeploy when the gateway, helper path, model or egress policy, or another managed value changes.
--helper-path must be fleet-stableUse /usr/local/bin/anyray-credential-helper on macOS and Linux, or
C:\Program Files\Anyray\anyray-credential-helper.cmd on Windows. A path that only exists on your
machine breaks every other one.
Centrally refreshed configuration
Claude Desktop 1.10628.0 and newer can fetch its effective configuration from an organization-hosted bootstrap server. The Anyray-managed path (Managed fleet) hosts that response on the gateway. If you already run a separate bootstrap or OIDC service, Connect can emit its trust anchor for MDM instead:
anyray-connect desktop fleet bootstrap macos \
https://config.example.com/claude/bootstrap claude-bootstrap.mobileconfig \
--oidc '{"clientId":"desktop-client","issuer":"https://login.example.com/tenant/v2.0","scopes":"openid offline_access desktop-client/.default"}'
anyray-connect desktop fleet bootstrap windows `
https://config.example.com/claude/bootstrap claude-bootstrap.reg
anyray-connect desktop fleet bootstrap linux \
https://config.example.com/claude/bootstrap managed-settings.json
# Deploy the result to /etc/claude-desktop/managed-settings.json.
Each artifact carries disableDeploymentModeChooser and pins bootstrapHeadersHelper to that OS's
wrapper (/usr/local/bin/anyray-bootstrap-headers-helper, or
C:\Program Files\Anyray\anyray-bootstrap-headers-helper.cmd on Windows) with ?os= added to the
bootstrap URL. Bootstrap artifacts take no helper path on the command line. Install both wrappers on
every machine first with anyray-connect desktop helper --write
(step 2 above). Claude Desktop 1.34493.1 and later refuses a
bootstrap response that omits inferenceCredentialHelper, so your endpoint must return it too. The
Anyray endpoint names the ?os= helper path.
An external endpoint with OIDC must authenticate and authorize the bearer before returning
configuration. It can return the gateway URL, inference credential, model list, egress policy, and
other keys allowed by Anthropic's bootstrap schema. inferenceCredentialHelper is MDM-only and
cannot come from the bootstrap response. Without --oidc, Claude requires returned inference URLs to
share the bootstrap URL's origin, and the built-in Anyray endpoint is public, strictly secret-free,
and same-origin.
Claude checks the endpoint at launch and polls every 30 minutes, so a changed response takes effect on the next launch. Keep an old inference credential valid across the fleet's relaunch window. Each successful response replaces the managed configuration as a whole, meaning an omitted key is unset, and it is memory-only, so make the endpoint highly available or users who launch during an outage remain in Claude's degraded sign-in state.
When MDM sets bootstrapUrl, or any recognized Claude policy key except the three
app-behavior-only keys (disableAutoUpdates, autoUpdaterEnforcementHours, and
disableWslSessions), Claude ignores local configLibrary values. Connect reports the policy-owned
state and does not overwrite it. Copy-only migration may still populate Claude-3p, but the app
enforces the managed workspace, tool, extension, and MCP policy and may leave a copied item disabled.
On Claude Desktop 1.19367.0 and newer, Windows does not merge HKLM and HKCU: an active HKLM policy is
the entire configuration source, so put the complete desired policy in the owning tier.
Recover a fleet that already switched
The claude.ai settings still exist and apply again as soon as the app returns to first-party mode.
Run in the signed-in user's context:
test -f "$HOME/Library/Application Support/Claude-3p/configLibrary/_meta.json" \
&& echo AFFECTED || echo clean
anyray-connect desktop disconnect
Read the output. If something changed since the original run it says so and leaves the state in place.
Fully quit Claude Desktop, because configuration is read only at launch. Reopen it and choose the
Anthropic sign-in. Connectors, plugins, and the model default come back with the org session. Claude
Code is untouched throughout, and anyray-connect doctor --verify confirms it.
Deploy the first-party profile, and scope any per-user enrollment
with --tools claude-code.
One managed Mac, no MDM
A configuration profile is not an MDM-only artifact. Run anyray-connect --tools claude-desktop. If a
policy blocks the local write, Connect leaves a complete profile at
~/.anyray/claude-desktop-anyray.mobileconfig. Double-click it and approve it in System Settings →
General → Device Management, which takes an administrator password on that Mac and nothing else.