Skip to main content
Pointing Claude Code or Claude Desktop at the gateway on one machine is a settings change a developer can undo. This guide makes it tamper-resistant across a fleet: the gateway URL, model mapping, MCP allowlist, and tool permissions go into system-level managed configuration that user and project settings cannot override, and a TrueFoundry binary deployed over MDM keeps the gateway credential fresh so no long-lived API key ever sits on a developer machine. It covers Claude Code (macOS · Linux · Windows) and Claude Desktop (macOS · Windows). Claude Web has no endpoint to enforce — see Claude Web for how it is governed instead.
Identity setup (SSO, domain capture) and admin-console settings are configured in Anthropic’s own console and documented in Claude’s docs. Enforce those first so access is tied to your IdP and employees can’t fall back to personal accounts; this guide covers only what is specific to a TrueFoundry deployment.

How settings are enforced

Claude Code reads settings in priority order, with the system-level managed-settings.json taking precedence and being un-overridable by developers:
System paths: macOS /Library/Application Support/ClaudeCode/managed-settings.json · Linux /etc/claude-code/managed-settings.json · Windows C:\Program Files\ClaudeCode\managed-settings.json. Claude Desktop has the equivalent in OS-native managed preferences under the com.anthropic.claudefordesktop domain — macOS /Library/Managed Preferences/<user>/com.anthropic.claudefordesktop.plist, Windows HKLM\SOFTWARE\Policies\Claude. The key reference is on Claude Desktop. One managed-settings.json enforces everything for Claude Code — gateway routing, model mapping, MCP allowlisting, tool permissions, and sandboxing. A recommended baseline:
Key controls (see Claude’s settings reference for the full list):

Deployment scripts

The scripts below deploy and lock managed-settings.json, then write the org MCP connectors for both Claude Code and Claude Desktop. On every run the script fetches a fresh gateway token for the logged-in user and writes it into ANTHROPIC_CUSTOM_HEADERS — no PAT or manual credentials needed. Schedule the script to run hourly so the token stays fresh; the browser device flow only appears on first run or after the refresh token at ~/.tf/refresh-token expires (30 days by default).
Write connectors after the binary, never before. tfy-local-ai-setup does not emit MCP keys, and on macOS it rewrites and re-locks the entire managed-preferences plist on every run — anything written before it is silently gone. On Windows the registry values are written individually, so a connector value survives, but the ordering rule is the same on both platforms.
The scripts download the tfy-local-ai-setup binary from the truefoundry/tfy-local-ai-setup GitHub repo. The README there documents all available flags, the default managed-settings.json config, and advanced usage (custom model IDs, settings templates, manual runs).
Update the placeholders in the Config section before running: <your-gateway-url>, <your-control-plane-url>, and <your-tenant-name>. The script will misconfigure Claude Code if these are left as placeholders.
Prerequisites Before running the MDM script, verify one thing: no server-managed settings active. Check the Anthropic Admin Console under Settings → Policies. If any server-side policies are enabled, they will conflict with or override the file-based managed-settings.json. Disable server-managed settings before proceeding. How the script works The script runs as root on a schedule (recommended: hourly) and does six things on every execution:
  1. Saves and loads config — On first run, the three required values (GATEWAY_URL, CONTROL_PLANE_URL, TENANT_NAME) are read from the script’s Config section and written to a root-owned config file (macOS: /Library/Preferences/com.truefoundry.tfy-local-ai-setup.conf; Linux: /etc/tfy/tfy-local-ai-setup.conf; Windows: C:\ProgramData\TrueFoundry\tfy-local-ai-setup.conf). On subsequent runs the file is loaded automatically — no changes to the script are needed. A non-empty value in the Config section overrides the saved file (useful for one-off updates).
  2. Installs or updates tfy-local-ai-setup — Downloads the binary from the tfy-local-ai-setup GitHub releases if it is not present or if the installed version does not match RELEASE_TAG. On subsequent runs where the version already matches, this step is skipped entirely.
  3. Detects the logged-in user and fetches a fresh auth token — The binary identifies who is currently logged into the machine, then silently refreshes the token from ~/.tf/refresh-token. That refresh token is valid for 30 days. Each successful run rotates it and writes a new 30-day token, so developers do not re-login as long as the script runs at least once in that window. The browser device-authorization flow only appears the very first time, or after 30 days without a refresh. Every other run completes silently in the background.
  4. Writes and locks managed-settings.json — The binary builds the JSON with the AI Gateway URL, model IDs, and the freshly fetched token, then writes it to the system-level path and locks the file so developers cannot modify it (macOS: chflags schg; Linux: chattr +i; Windows: icacls ACL).
  5. Writes the org MCP connectors — From one CONNECTORS list the script derives both payloads and writes each to its own surface: managed-mcp.json for Claude Code, and managedMcpServers in the managed preferences for Claude Desktop (macOS plist, Windows HKLM\SOFTWARE\Policies\Claude). The two schemas differ — see the table below — so keep the list as the single source of truth and let the script build the JSON.
  6. Reads everything back — Both a plutil -replace against a still-locked file and a registry write into a redirected 32-bit view can report success while changing nothing, so the script re-reads every value it wrote and exits non-zero on a mismatch. Run it with --verify (-VerifyOnly on Windows) at any time to print the machine’s current state without writing.
oauth: true needs no clientId and no pre-registered redirect URI: Claude registers itself against the AI Gateway’s authorization server through dynamic client registration. Leave toolPolicy out unless you are naming real tool names — its keys are individual tools, and an undocumented {"*": "allow"} risks the whole entry being rejected by the schema.
Run via Jamf, Mosyle, Kandji, or any MDM supporting script execution. Must run as root. Detects Apple Silicon vs Intel automatically.
Pass --claude-code, --codex, or --claude-desktop to target specific tools. From v1.3.6 an explicit flag configures that tool whether or not it is currently installed, so a client installed later is governed from its first launch — useful when you are rolling a tool out and the MDM job runs before the install lands. --codex covers both Codex surfaces, the CLI and the desktop app, which share the same managed config.With none of those flags the binary auto-detects what is installed and configures each one, and a machine with no AI client is a clean no-op — so the same script is safe to push fleet-wide.
Prefer a server-side option? Claude’s Admin Console (Settings → Policies) can push tool permissions, MCP allowlists, and model restrictions to all members without deploying files — useful for BYOD. Note that server-managed settings require a direct connection to api.anthropic.com and are bypassed when you route through a gateway via ANTHROPIC_BASE_URL.

Let users bring their claude.ai history across (claudeAiImport)

Teams moving onto a gateway-backed Claude Desktop usually arrive with history on claude.ai. The Import from Claude wizard copies those conversations and projects into the local session store so that work continues against your AI Gateway — but it is off by default and not user-settable. It exists only as managed config, so MDM (or the Claude Admin Console) is the only way to turn it on.
The deployment scripts above do not write this key — they cover inference and MCP connectors only. Add claudeAiImport to the same managed-preferences store yourself, after the binary runs, for the same reason connectors go last: on macOS tfy-local-ai-setup rewrites and re-locks the whole plist on every run.
claudeAiImport is an object with four subfields, all off unless set. The posture below is the one we deploy for customers rolling Desktop out fleet-wide: Like the other object-valued keys, it is stored as a JSON-encoded string:
Keep the inner booleans as real JSON booleans — "enabled":"true" is a string and will not parse as on. MDM delivery of this key needs Claude Desktop 1.25927.0 or later; earlier builds accept it only from a bootstrap server, and an older build silently ignores it.
Two switches are needed and only one of them is yours. If the source history lives in a claude.ai Team or Enterprise workspace, a workspace owner must also enable Settings → Organization → Data and privacy → Allow members to export their own data. Personal claude.ai accounts can always export. No managed key fixes that side.
Also weigh the egress: the Sign in to claude.ai path has the app authorize against claude.ai and download the export itself, so devices need egress to claude.ai for that step. On a locked-down fleet the downloaded-.zip path needs no such egress and is the one that works.
Terminal Claude Code history is not covered. “Code sessions” throughout this wizard means Claude Desktop’s Code tab. There is no Claude-Code-specific import key — Claude Code rides inside claudeAiImport — and neither half of the feature reaches the CLI: the wizard only finds sessions left by an earlier Claude Desktop install, and the export explicitly excludes terminal Claude Code sessions. A developer’s own ~/.claude history (%USERPROFILE%\.claude on Windows) stays where it is, so don’t plan a CLI migration around this flag. The similarly named isClaudeCodeForDesktopEnabled gates whether the Code tab appears at all and has nothing to do with import.
Inference still comes from your gateway: the first message in an imported conversation raises a Resume imported session? prompt, and the reply is served by your configured model. The transcript is replayed as context on that turn, so pin models with enough context for what users bring over and expect those requests in AI Gateway → Analytics. Anthropic’s Import history from claude.ai.

Stop connector schemas from crowding the context window (toolSearchEnabled)

Every connector you push through managedMcpServers costs context. By default Claude Desktop inlines the full JSON schema of every MCP tool into each session up front, so a fleet-wide connector set can spend a large slice of the window before the user types anything. Tool search puts only tool names in context and has Claude fetch a tool’s full schema the first time it needs it. Like claudeAiImport, this is managed-only and off by default, so MDM is the only way to turn it on. Unlike it, the value is a plain boolean rather than a JSON-encoded string:
If you deploy a .mobileconfig or a Group Policy template instead of writing the store directly, the key is a first-class subkey of the com.anthropic.claudefordesktop payload and appears in Anthropic’s ADMX as a normal boolean policy — no custom payload needed. MDM delivery needs Claude Desktop 1.21459.0 or later; earlier builds ignore the key. The same ordering caveat as connectors applies: the deployment scripts above don’t write it, and on macOS tfy-local-ai-setup rewrites and re-locks the whole plist on every run, so it has to go in after the binary.
Setting ENABLE_TOOL_SEARCH yourself will not work — this key is the only switch. When Desktop runs against a gateway it launches every Code session with CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS=1 and strips ENABLE_TOOL_SEARCH out of the session environment, precisely because toolSearchEnabled is unset. A developer who adds ENABLE_TOOL_SEARCH to their own ~/.claude/settings.json will therefore see no change and report tool search as broken. Setting toolSearchEnabled makes the app lift that suppression on its own — depending on the bundled Claude Code version it either stops disabling the betas or passes ENABLE_TOOL_SEARCH=force down through Claude Code managed settings. Editing the rendered config under configLibrary on a single machine is not a workaround either: that file is regenerated from managed config on the next sync.
Enabling it changes the request shape your gateway receives — sessions add the tool-search-tool-2025-10-19 anthropic-beta value and send tool_reference content blocks. Whether that survives the hop depends on which provider backs the model:
  • Anthropic direct — works as-is. TrueFoundry forwards anthropic-beta to api.anthropic.com unfiltered and does not validate /v1/messages content blocks, so the beta and the new blocks pass straight through.
  • Bedrock, Vertex, Azure Foundry, Microsoft Foundry, Bedrock Mantle — check first. These providers run anthropic-beta through a per-provider allowlist and silently drop any value not configured for them. If tool-search-tool-2025-10-19 isn’t on that provider’s list, the beta is stripped while the tool_reference blocks still go up, and the upstream rejects the request with HTTP 400.
Either way, pilot it with one group before the fleet. Anthropic’s reference: MCP tool search.

Using Claude Code MDM with a Claude Enterprise Account

If your organization has a Claude Enterprise subscription (rather than a direct Anthropic API key), the MDM setup requires one additional configuration step on TrueFoundry: you create an Anthropic provider that acts as a pass-through, and the authentication is supplied at request time by each developer’s own Claude Enterprise account — not stored as a credential on TrueFoundry. This approach also gives you per-user usage attribution on both sides — TrueFoundry logs which user made each request, and Anthropic attributes usage to the individual Claude Enterprise seat. This works because the authorization token is user-specific: each developer obtains it by logging into their own Claude Enterprise account, so every request carries their identity end-to-end through the AI Gateway.
First-time login required. The first time a developer opens Claude Code on a managed machine, they will be prompted to log in to their Claude Enterprise account through a browser. This device-authorization flow is what binds the machine to the developer’s identity and generates the initial refresh token (valid for 30 days). After that, the MDM script silently refreshes the token on every hourly run — no further interaction is needed unless 30 days pass without a refresh, or the developer switches accounts.
1

Create an Anthropic provider on TrueFoundry

In the TrueFoundry platform, navigate to Integrations → Providers and create a new provider of type Anthropic.When prompted for authentication credentials, leave the API key field empty. Do not enter any API key or secret. The AI Gateway will authenticate using the X-TFY-API-KEY token injected by the MDM script at the time of each request — no stored credential is needed or desired.
2

Copy the model IDs

After saving the provider, TrueFoundry assigns model IDs for each Claude model tier (for example, claude-enterprise/claude-opus-4-6, claude-enterprise/claude-sonnet-4-6, claude-enterprise/claude-haiku-4-5). Copy these model IDs from the provider detail page.
3

Update the MDM script config

In the MDM script, set the three config values to match your TrueFoundry setup:
Then confirm the model env vars in the JSON block match the model IDs from the previous step:
The Anthropic provider on TrueFoundry must have an empty API key. If a stored API key is present, TrueFoundry will use it instead of the per-request token from the MDM script, which breaks the Claude Enterprise subscription flow and may route traffic under the wrong account.
The tfy-local-ai-setup binary fetches a fresh OAuth token on every MDM run and writes it into ANTHROPIC_CUSTOM_HEADERS as X-TFY-API-KEY. TrueFoundry’s gateway forwards this header to Anthropic’s API to authenticate each request against the developer’s Claude Enterprise subscription. No long-lived credentials are stored anywhere on the managed machine.
Using a virtual model with Claude Enterprise The MDM setup works with virtual models as well. Point the model IDs in the MDM script to a virtual model instead of a direct provider model — you get two immediate benefits:
  • Fallbacks — if one target is unavailable, traffic automatically falls through to the next
  • Model changes without touching the MDM script — update the virtual model’s routing on TrueFoundry and the change takes effect immediately across all managed machines, with no script redeployment
One important constraint: only one of the virtual model’s targets can be a Claude Enterprise model account. The authorization header injected by the MDM script (X-TFY-API-KEY) is issued by Anthropic for a specific Claude Enterprise account. If multiple targets were Claude Enterprise providers, all of them would receive the same token — which is only valid for one account, causing the others to fail. Non-Claude-Enterprise targets (e.g. a fallback to a different provider) can coexist without issue. Troubleshooting: 401 / 403 errors If Claude Code returns a 401 Unauthorized or 403 Forbidden error, the session token has expired or was never initialized. The developer needs to re-authenticate manually:
  1. Open Claude Code and run /login
  2. When prompted to choose an authentication method, select Claude Enterprise (the first option)
  3. Complete the browser login flow
Once logged in, the MDM script will automatically pick up the new refresh token on its next hourly run and keep it fresh without further manual steps.

Observability

Once Claude traffic flows through the gateway, every model request and MCP tool call is traced with user, model, server, and tool attribution, and metrics for models, MCP, and guardrails are available in AI Gateway → Analytics — no extra instrumentation. Traces export to any OTEL-compatible platform for your SIEM. See Analytics overview, Request logging, and Export OpenTelemetry data. Set spending guardrails per user and team with budget limits and rate limits. For local transcripts, transcriptRetentionDays in managed-settings.json (7–14 days recommended, set to 14 in the baseline above) auto-deletes Claude Code’s on-disk history. Anthropic-side retention, Zero Data Retention, and compliance posture are configured with Anthropic — see Claude’s data retention docs.

Frequently asked questions

A direct connection gives you no central place to enforce policy. Routing through the TrueFoundry AI Gateway lets you apply access control, rate and budget limits, multi-provider routing with failover, and full tracing — across web and CLI — from a single control point, without changing client config.
The MDM deployment installs the tfy-local-ai-setup binary and runs it on a schedule (hourly) to fetch a fresh gateway token scoped to the user’s TrueFoundry identity. The token is written into ANTHROPIC_CUSTOM_HEADERS as X-TFY-API-KEY and refreshed automatically — no personal or long-lived API keys on developer machines. The refresh token cached at ~/.tf/refresh-token is valid for 30 days; hourly runs rotate it so developers are not prompted to log in again.
30 days from when it was last issued or rotated. tfy-local-ai-setup uses the TrueFoundry Auth Server refresh token rather than setting its own expiry. Each successful MDM run rotates the token and writes a new 30-day value to ~/.tf/refresh-token. If the script runs at least once within those 30 days, no re-login is required. After 30 days without a refresh, the next run opens the browser device-authorization flow. Contact TrueFoundry support if you need a different lifetime.
It centralizes MCP governance: a registry of approved servers, unified outbound authentication, role-based access, tool-level controls, guardrails, and a full audit trail of every tool call. You allowlist only the AI Gateway URL, so developers can’t connect to unvetted servers.
No. The cancelled device code expires immediately, but you can start a fresh login at any time. Since your config is already saved from the initial run, source the config file and run the binary directly — no flags to look up:
Once you complete the login, the refresh token is saved and all future MDM runs will finish silently — no browser prompt needed.
No. When managed-settings.json is deployed to the system path and locked (and allowManagedPermissionRulesOnly is set), it takes highest priority and cannot be overridden by project- or user-level settings.
No, this should only happen on the first run or when the cached refresh token at ~/.tf/refresh-token has expired (30 days without a successful refresh). Once the developer completes the login, subsequent MDM runs silently refresh the token without any browser prompt. If the window keeps appearing on every run, the refresh token is not being saved — run the setup manually (see the FAQ above) to re-authenticate and reset the token.