Skip to main content

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.

LaneWhenHow it authenticates
subscriptionClaude Code subscription on a verified macOS buildA 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-billedOrg or API mode on macOS, and all of WindowsThe 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

PlatformMechanism
macOSAutomatic 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.
WindowsWrites 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:

inferenceModels
[{ "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:

KeyWhy
chatTabEnabledThird-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.

1
Enable Developer Mode

macOS: Help → Troubleshooting → Enable Developer Mode in the menu bar. Windows: the menu at the top-left of the sign-in screen.

2
Open Configure Third-Party Inference

Developer menu → Configure Third-Party InferenceGateway. Not Settings → Developer, which is MCP servers rather than inference.

Claude Desktop's Configure third-party inference window, Connection pane with the provider set to Gateway and the Gateway base URL field

Set the provider to Gateway, then fill in the Connection fields.
3
Paste and relaunch

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.

Verify the route

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.

1
Run Connect after third-party mode is deployed
anyray-connect reports how many local items are waiting and queues the copy.
2
Sign in with the same Claude account as before
Signing in creates the session store the copy writes into.
3
Fully quit Claude Desktop
⌘Q on macOS. The copy lands while the app is down.
4
Reopen Claude Desktop
Your migrated sessions and portable local configuration are in the new profile.

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 dataWhat copies
SessionsClaude Code session records whose transcript still exists, plus complete Cowork task data
Local MCPNon-Anyray entries from claude_desktop_config.json
PluginsComplete user-scope plugin packages with a valid plugin manifest
SkillsSkills 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.

1
Export from claude.ai

Settings → Privacy → Export data. Anthropic emails a download link. Keep the .zip, because this step takes the archive itself, not the file inside it.

2
Quit Claude Desktop

A running app writes its conversation list back on exit and would undo the restore.

3
Restore
macOS / Linux
anyray-connect migrate --export ~/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
curl -fsSL https://app.anyray.ai/connect.sh | sh -s -- migrate --export ~/Downloads/claude-export.zip
4
Reopen Claude Desktop

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.

Restored chats land in Cowork, and this writes into Claude Desktop's own storage

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

1
Import the conversations file
anyray-connect history import ~/Downloads/claude-export/conversations.json
2
Ask for a past conversation

"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

ModelWhat MDM carriesHow configuration changes
First-partyDevice policy only, and no inference* keyDesktop stays on claude.ai, and Anyray covers the coding tools
BootstrapA stable HTTPS URL, the bootstrap-headers helper path, and optional OIDC parametersUpdate the bootstrap response, then relaunch Claude, with no MDM profile redeployment
StaticThe complete policy exported from Claude Desktop, including the fleet credential-helper pathEdit 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).

KeyWhat it controls
forceLoginOrgUUIDSign-in must belong to your organization. The UUID comes from your claude.ai admin console
allowedWorkspaceFoldersFolders the agent may work in
isLocalDevMcpEnabledWhether users may add their own local MCP servers
isDesktopExtensionEnabled, isDesktopExtensionDirectoryEnabledDesktop extension installs, and whether the directory is shown
secureVmFeaturesEnabledCowork
isClaudeCodeForDesktopEnabledThe embedded Code tab. Turning it off steers coding work to the standalone CLI, which Anyray does route
disableAutoUpdates, autoUpdaterEnforcementHoursThe updater
1
Author the profile

Generate two UUIDs with uuidgen. Every value is a <string>, including booleans and JSON:

claude-desktop.mobileconfig
<?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.

2
Scope it to one device and verify

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 desktop reports 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.

3
Widen the scope, then roll out Anyray for the coding tools

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.

1
Prepare an unmanaged staging Mac

Keep Claude Desktop signed out, enable Help → Troubleshooting → Enable Developer Mode, and run anyray-connect desktop.

2
Add and test organization features

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.

3
Test the complete configuration

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.

4
Export for Jamf

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 installed MDM policy makes the editor read-only

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.

1
Place the binary

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.

2
Install the helpers on every endpoint

--write verifies the binary and writes both wrappers atomically:

macOS / Linux (run as root)
sudo /usr/local/bin/anyray-connect desktop helper --write --platform posix \
--bin /usr/local/bin/anyray-connect
Windows (run elevated)
& "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.

3
Emit the policy

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-stable

Use /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:

macOS with an existing OIDC provider
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"}'
Windows with bootstrap-hosted device authorization
anyray-connect desktop fleet bootstrap windows `
https://config.example.com/claude/bootstrap claude-bootstrap.reg
Linux managed JSON
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.

MDM and bootstrap are authoritative

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.

1
Find the affected machines

Run in the signed-in user's context:

test -f "$HOME/Library/Application Support/Claude-3p/configLibrary/_meta.json" \
&& echo AFFECTED || echo clean
2
Remove only the Anyray-owned Desktop state
anyray-connect desktop disconnect

Read the output. If something changed since the original run it says so and leaves the state in place.

3
Relaunch and confirm

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.

4
Prevent a repeat

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.