> ## Documentation Index
> Fetch the complete documentation index at: https://www.truefoundry.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Claude Code and Claude Desktop with MDM

> Set the Claude Code policy, MCP allowlist, and models, configure Claude Desktop, and verify what MDM writes on each device for the TrueFoundry AI Gateway.

This page covers the Claude-specific parts of an MDM rollout. Start with the [MDM overview](https://www.truefoundry.com/docs/ai-gateway/mdm-setup.md) to create the deployment script, then use these options in its TOML block.

Claude Code is supported on macOS, Linux, and Windows. Claude Desktop is supported on macOS and Windows. Claude Web has no device configuration to manage; see [Claude Web](https://www.truefoundry.com/docs/ai-gateway/claude-web.md).

## What gets written

| Agent | Managed settings | Who can change them |
| - | - | - |
| Claude Code | `managed-settings.json`: `/Library/Application Support/ClaudeCode/` (macOS), `/etc/claude-code/` (Linux), `C:\Program Files\ClaudeCode\` (Windows) | Locked; administrators only |
| Claude Desktop | Managed preferences: `/Library/Managed Preferences/<user>/com.anthropic.claudefordesktop.plist` (macOS), `HKLM\SOFTWARE\Policies\Claude` (Windows) | Locked; administrators only |

Claude Code applies managed settings above project and user settings, so employees cannot override the gateway, models, or policy:

```text theme={"dark"}
Managed settings (managed-settings.json)   ← written by MDM, highest priority
  └── Project settings (.claude/settings.json)
        └── User settings (~/.claude/settings.json)
```

The binary writes the gateway URL, the tier models, the employee's sign-in, and gateway MCP servers into these files on every run. Sign-in depends on the [method you chose](https://www.truefoundry.com/docs/ai-gateway/mdm-setup.md#choose-how-employees-sign-in):

| | Device login | OIDC |
| - | - | - |
| **Claude Code** | `env.ANTHROPIC_CUSTOM_HEADERS` contains `X-TFY-API-KEY: <token>` | `apiKeyHelper` runs `tfy-local-ai-setup --_print-oidc-token`. No token is stored |
| **Claude Desktop** | `inferenceGatewayApiKey` holds the token | `inferenceCredentialKind` is `external-idp` and `inferenceIdpOidc` holds the issuer and client ID. Desktop signs in itself |

> [!NOTE]
>
> Policies in Claude's Admin Console (**Settings → Policies**) do not reach sessions routed through a gateway. Put your Claude Code policy in `mdm.toml` instead.

## Set the Claude Code policy

`[agents.claude-code.managed_settings]` takes any key from [Claude Code's settings reference](https://code.claude.com/docs/en/settings#settings-files), including nested `permissions`, `env`, `sandbox`, and MCP policy keys. The starter in the overview uses this policy:

```toml theme={"dark"}
[agents.claude-code.managed_settings]
allowManagedPermissionRulesOnly = true
allowManagedHooksOnly = true
transcriptRetentionDays = 14
strictKnownMarketplaces = []

[agents.claude-code.managed_settings.permissions]
disableBypassPermissionsMode = "disable"
deny = []
ask = [
  "Bash(git push:*)",
  "Write(**)",
  "Read(**/.env)",
  "Read(**/secrets/**)",
  "Read(**/.ssh/**)",
  "Read(**/credentials/**)",
]
```

| Setting | Effect |
| - | - |
| `allowManagedPermissionRulesOnly` | Only managed permission rules apply; project and user settings cannot add rules |
| `allowManagedHooksOnly` | Only managed hooks run, which prevents injected hooks |
| `transcriptRetentionDays` | Deletes local Claude Code transcripts after this many days |
| `strictKnownMarketplaces = []` | Blocks employees from adding plugin marketplaces |
| `disableBypassPermissionsMode` | Blocks `--dangerously-skip-permissions` |
| `deny`, `ask` | Block a tool call outright, or require approval for it |

Do not set `ANTHROPIC_BASE_URL`, the model variables, or credentials here. The binary adds them after loading your policy.

> [!WARNING]
>
> **Your table replaces the built-in policy; it does not add to it.** Without a `managed_settings` table, the binary uses a built-in baseline: the `ask` rules above plus `Bash(curl:*)`, `Bash(wget:*)`, and `Read(**/.env.*)`, the other settings above, and Claude Code's [sandbox](https://code.claude.com/docs/en/sandboxing) enabled with proxy ports 8080 and 8081. Write a table whenever that baseline does not match your environment, and include every rule you want.

To keep the policy in a JSON file instead, pass `--settings-file=/path/to/policy.json` to the binary. Precedence is `--settings-file`, then the inline table, then the existing managed file, then the baseline. If the `--settings-file` path cannot be read, the binary silently uses the baseline, so check the path on a pilot device.

## Restrict MCP servers to the gateway

Gateway MCP servers from `[mcp]` are added to `managedMcpServers` in `managed-settings.json`, **alongside** servers employees add themselves. This requires Claude Code 2.1.259 or later.

To allow only servers behind your gateway, add an allowlist to the policy:

```toml theme={"dark"}
[[agents.claude-code.managed_settings.allowedMcpServers]]
serverUrl = "https://<your-gateway-host>/*"
```

> [!NOTE]
>
> Earlier MDM scripts wrote an exclusive `managed-mcp.json` that blocked every other server. This setup does not, so add `allowedMcpServers` if you relied on that behavior.

For other controls, such as blocking Claude.ai connectors or web search, see [Govern Claude's MCP traffic](https://www.truefoundry.com/docs/ai-gateway/claude-mcp-governance.md).

## Choose models

Claude Code uses three tiers. The defaults are:

```toml theme={"dark"}
[agents.claude-code]
opus_model = "claude-code/claude-opus"
sonnet_model = "claude-code/claude-sonnet"
haiku_model = "claude-code/claude-haiku"
```

Keep a recognizable Claude model name in each ID, because Claude Code uses it to decide which features to enable. See [Model configuration](https://www.truefoundry.com/docs/ai-gateway/claude-code.md#model-configuration).

* **Use Virtual Models** so you can change routing or add fallbacks in TrueFoundry without redeploying the script. See [Virtual Models](https://www.truefoundry.com/docs/ai-gateway/virtual-model.md).
* **To request 1M context** in Claude Code, add the `[1m]` suffix, for example `opus_model = "claude-code/claude-opus[1m]"`, for models that support it.

## Add Claude Desktop

Add `claude-desktop` to `tools`. Desktop uses the Claude Code tier models unless you override them:

```toml theme={"dark"}
tools = ["claude-code", "codex", "claude-desktop"]

[agents.claude-desktop]
opus_model = "claude-code/claude-opus"
sonnet_model = "claude-code/claude-sonnet"
haiku_model = "claude-code/claude-haiku"
```

The binary pins Desktop's model picker to these models and turns off model discovery.

* **MCP servers** from `[mcp]` appear under **Customize → Connectors**, where each employee selects **Connect** to authorize them. This requires Desktop 1.2581.0 or later. To allow only remote servers, pass `--claude-desktop-allow-local-mcp=false` to the binary, which blocks employees' local (stdio) servers.
* **Skills** are staged as ZIP files in `~/.tf/skills/claude-desktop`. Employees upload them through Desktop's Skills settings with code execution enabled. A newly staged ZIP does not update a Skill that was already uploaded.
* **Restrict sign-in** with binary flags: `--force-login-org=<Claude org UUID>` (repeatable) limits sign-in to your Claude organizations, and with OIDC, `--disable-deployment-mode-chooser` stops employees from leaving the gateway profile for a personal Claude.ai account.
* **Inference headers** are added with the repeatable `--desktop-header='Name: Value'` flag. A `headers` key in TOML fails validation.

With OIDC, Desktop requires version 2.7032.0 or later. See [Set up OIDC sign-in](https://www.truefoundry.com/docs/ai-gateway/mdm/oidc.md).

### Turn on tool search and 1M context

```toml theme={"dark"}
[agents.claude-desktop]
opus_model = "claude-code/claude-opus"
sonnet_model = "claude-code/claude-sonnet"
haiku_model = "claude-code/claude-haiku"
tool_search_enabled = true
prefer_1m_context = true

[agents.claude-desktop.context.opus]
supports_1m = true
prefer_1m = true

[agents.claude-desktop.context.sonnet]
supports_1m = true
prefer_1m = true
```

**Tool search** loads only tool names into each session and fetches a tool's schema when Claude needs it, so many connectors no longer crowd the context window. This setting is the only switch: Desktop removes `ENABLE_TOOL_SEARCH` from sessions when it is unset, so employees cannot turn it on themselves.

> [!WARNING]
>
> Tool search sends the `tool-search-tool-2025-10-19` beta header. Anthropic accepts it directly, but Bedrock, Vertex, and Azure-hosted Claude models drop beta headers that are not allowed for that provider, and the request then fails with HTTP 400. Test with one group first. See [Forwarding Anthropic beta features](https://www.truefoundry.com/docs/ai-gateway/claude-code.md#forwarding-anthropic-beta-features).

**1M context:** set `supports_1m` and `prefer_1m` only for tiers whose gateway models support 1M context. Desktop uses these settings rather than Claude Code's `[1m]` suffix.

### Let employees import claude.ai history

```toml theme={"dark"}
[agents.claude-desktop.history_import]
enabled = true
encoding = "json-string"
```

This adds the **Import** wizard under **Settings → Import & export**, so employees can bring claude.ai conversations and projects into Desktop. Replies in imported conversations come from your gateway models, and the transcript is replayed as context, so choose models with enough context.

* Requires Desktop 1.10628.0 or later.
* For Team or Enterprise workspaces, a workspace owner must also turn on **Allow members to export their own data** in claude.ai.
* The **Sign in to claude.ai** path needs network access to claude.ai. Importing a downloaded `.zip` file does not.
* Terminal Claude Code history is not imported.
* If import stays unavailable on macOS, try `encoding = "native-dict"`.

Optional fields: `export_enabled` (default `true`) shows **Export**, `banner_behavior` (`"detect"`, `"show"`, or `"off"`) controls the import prompt, and `automatic_3p` (beta, default `false`) imports earlier third-party sessions without asking. Turn on `automatic_3p` only with your organization's agreement, because employees are never prompted. It also requires `deploymentOrganizationUuid` in Desktop's managed preferences, which this tool does not set. See Anthropic's [import documentation](https://claude.com/docs/third-party/claude-desktop/import).

## Use a Claude Enterprise or Team subscription

To bill Claude Code usage to employees' Claude seats instead of an API key, create the Anthropic model account with an **empty API key**, as described in [Subscription passthrough](https://www.truefoundry.com/docs/ai-gateway/claude-code.md#subscription-passthrough). Then point the tiers at that account's models:

```toml theme={"dark"}
[agents.claude-code]
opus_model = "claude-enterprise/claude-opus-4-6"
sonnet_model = "claude-enterprise/claude-sonnet-4-6"
haiku_model = "claude-enterprise/claude-haiku-4-5"
```

Use device login for this setup. The binary sends the TrueFoundry credential in `X-TFY-API-KEY`, and each employee runs `/login` in Claude Code once and chooses their Claude account, so the two sign-ins are independent.

* A Virtual Model can have only **one** empty-key Anthropic target.
* If the account has an API key, the gateway uses it and bills the key instead of the seat.
* For a `401` or `403`: if the Claude session expired, the employee runs `/login` again; if the TrueFoundry sign-in expired, trigger the MDM script again.

Claude Desktop cannot use subscription passthrough.

## Verify the deployment

1. **Check the managed files are locked.** See [Check that the settings are locked](https://www.truefoundry.com/docs/ai-gateway/mdm-setup.md#deploy-to-a-pilot-device).
2. **Check sign-in in `managed-settings.json`.** With device login, look for `X-TFY-API-KEY` in `ANTHROPIC_CUSTOM_HEADERS` and no `apiKeyHelper`. With OIDC, look for `apiKeyHelper` containing `--_print-oidc-token` and no token.
3. **Check Claude Desktop's preferences.** With OIDC, look for `inferenceCredentialKind` set to `external-idp` and `inferenceIdpOidc` with `"bearerTokenType":"access_token"`. With device login, look for `inferenceGatewayApiKey`.
4. **Send a test request** from Claude Code and Claude Desktop, then find both in **AI Gateway → Request Logs**. In Claude Code, `/model` should list your gateway models.

```bash macOS theme={"dark"}
cat "/Library/Application Support/ClaudeCode/managed-settings.json"
sudo defaults read "/Library/Managed Preferences/$(stat -f%Su /dev/console)/com.anthropic.claudefordesktop.plist"
```

```bash Linux theme={"dark"}
cat /etc/claude-code/managed-settings.json
```

```powershell Windows theme={"dark"}
Get-Content "C:\Program Files\ClaudeCode\managed-settings.json"
Get-ItemProperty "HKLM:\SOFTWARE\Policies\Claude"
```

## Windows Subsystem for Linux

Claude Code inside WSL reads the distribution's own `/etc/claude-code`, not the Windows configuration. Add `--wsl` to the binary's command to also configure the employee's WSL distributions. See [the WSL caveats](https://www.truefoundry.com/docs/ai-gateway/mdm/codex.md#windows-subsystem-for-linux), which apply to both agents. Verify with `wsl.exe -d Ubuntu cat /etc/claude-code/managed-settings.json`.

## Data retention

`transcriptRetentionDays` in the [Claude Code policy](#set-the-claude-code-policy) deletes local transcripts; 7 to 14 days is a common choice. Anthropic-side retention, Zero Data Retention, and compliance are configured with Anthropic; see [Claude's data retention documentation](https://platform.claude.com/docs/en/build-with-claude/zero-data-retention).

## Troubleshooting

| Symptom | Likely cause | Fix |
| - | - | - |
| Claude Desktop's Connectors pane is empty | Desktop rejected the server entries, or is older than 1.2581.0 | Search Desktop's `main.log` (`~/Library/Logs/Claude-3p/` on macOS, `%LOCALAPPDATA%\Claude-3p\logs\` on Windows) for `mcpServerCount`. A `0` follows an error naming the rejected field |
| Desktop sign-in fails with OIDC | Desktop is older than 2.7032.0, or `openid` was removed from the scopes | Update Desktop. Keep the default `--oidc-scopes` |
| Desktop requests are rejected with OIDC | The bearer token type was changed to `id_token` | Keep the default `--oidc-bearer-token-type=access_token`; the gateway validates access tokens |
| `400` errors after turning on tool search | The model's provider drops the tool search beta header | Allow the beta for that provider, or turn tool search off |
| Employees' own MCP servers still work | Gateway servers are added alongside theirs | [Add an allowlist](#restrict-mcp-servers-to-the-gateway) |
| History import is unavailable | Desktop is too old, or the claude.ai workspace does not allow exports | Update Desktop and ask the workspace owner. On macOS, try `encoding = "native-dict"` |

## Related

* [Claude Code integration](https://www.truefoundry.com/docs/ai-gateway/claude-code.md): authentication modes, model configuration, and beta features
* [Claude Desktop integration](https://www.truefoundry.com/docs/ai-gateway/claude-desktop.md): third-party inference mode and managed preference keys
* [Govern Claude's MCP traffic](https://www.truefoundry.com/docs/ai-gateway/claude-mcp-governance.md): MCP allowlists, Claude.ai connectors, and web search
* [Configuration reference](https://www.truefoundry.com/docs/ai-gateway/mdm/configuration-reference.md): every `mdm.toml` field


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.