One read of Claude Code CLI
187 pages moved out of 187 read.
accessibility First recorded · 135 lines, first recorded
# Use Claude Code with a screen reader ## Turn on screen reader mode ## Turn off screen reader mode ## What your screen reader hears ### Jump between turns ## Answer menus and prompts ## Hear when Claude Code needs you ## Accessibility settings beyond screen reader mode ## Known limitations ## Report an issue ## Related resources
The first capture of this source. The page was already there, and this is what it said.
# Use Claude Code with a screen reader > Set up Claude Code for screen readers such as VoiceOver and NVDA, plus settings for screen magnifiers, reduced motion, and colorblind-friendly themes. Claude Code has a screen reader mode that replaces its visual terminal interface with plain, linear text. Instead of boxes, progress animations, and in-place redraws, the mode prints labeled lines that a screen reader such as VoiceOver or NVDA reads in order, so you can hold a full conversation, approve tool permissions, and review output end to end. Screen reader mode is opt-in. If you use a screen magnifier, reduced motion, or a colorblind-friendly theme instead of a screen reader, see [Accessibility settings beyond screen reader mode](#accessibility-settings-beyond-screen-reader-mode). <Note> Screen reader mode requires Claude Code v2.1.181 or later. Earlier versions reject the `--ax-screen-reader` flag with `error: unknown option '--ax-screen-reader'`. </Note> ## Turn on screen reader mode Pick the method that matches how often you use a screen reader: * For one session: run `claude --ax-screen-reader`. * For sessions started from one shell: set the `CLAUDE_AX_SCREEN_READER` environment variable to `1`. In Bash or Zsh, run `export CLAUDE_AX_SCREEN_READER=1`; in PowerShell, run `$env:CLAUDE_AX_SCREEN_READER = "1"`. Add the line to your shell profile to cover every shell. * For every session on the machine: add `"axScreenReader": true` to your user [settings file](/docs/en/settings). This covers any terminal, including the VS Code integrated terminal. <Note> The methods are listed in precedence order: the [`--ax-screen-reader`](/docs/en/cli-reference#cli-flags) flag overrides the [`CLAUDE_AX_SCREEN_READER`](/docs/en/env-vars) environment variable, which overrides the [`axScreenReader`](/docs/en/settings#available-settings) setting. </Note> If you use Claude Code over SSH, set the environment variable or setting on the remote machine where Claude Code runs. When the mode is on, the first thing Claude Code prints is a confirmation line naming the method that turned it on: `[Screen Reader Mode: on via flag]`, `[Screen Reader Mode: on via env]`, or `[Screen Reader Mode: on via settings]`. The method-naming format requires Claude Code v2.1.206 or later. When Claude Code relaunches itself, for example to finish installing an update, the new process inherits the mode through the `CLAUDE_AX_SCREEN_READER` environment variable, so its confirmation line reads `[Screen Reader Mode: on via env]` regardless of which method you used. Earlier versions print `[Accessible screen reader mode: on]`. After printing the confirmation line, Claude Code holds the rest of the interface back for three seconds so your screen reader can finish speaking the line, then renders the first prompt. Press any key to end the hold early. To change the hold's length, set the `CLAUDE_AX_STARTUP_QUIET_MS` environment variable to a number of milliseconds. The default is `3000`; set it to `0` to skip the hold. Claude Code caps the hold at `600000` milliseconds, 10 minutes. Requires Claude Code v2.1.217 or later. ## Turn off screen reader mode Reverse whichever method turned the mode on: start without the flag, unset the environment variable, or set `axScreenReader` to `false`. Setting `CLAUDE_AX_SCREEN_READER=0` keeps the mode off even when the setting is `true`. ## What your screen reader hears In screen reader mode, Claude Code writes flat text: * no box-drawing characters for the interface chrome * no color-only cues * no redraws of content that hasn't changed; progress spinners render as static text * tables in Claude's replies read as `Header: value` sentences instead of a box-character grid. Requires Claude Code v2.1.198 or later; earlier versions draw tables as grids even in screen reader mode. Output accumulates in your terminal's scrollback, so you can re-read earlier turns with your screen reader's review commands or your terminal's search. Screen reader mode renders as plain scrolling text, even if you've turned on [fullscreen rendering](/docs/en/fullscreen) with the [`tui` setting](/docs/en/settings#available-settings); the setting has no effect while the mode is active. Attached background sessions still render fullscreen; see [Known limitations](#known-limitations). Each message in the transcript starts with a label your screen reader announces, naming what it is: your messages, Claude's replies, tool activity, errors, and prompts. The labels are also searchable, so you can jump between sections of the transcript by searching your terminal's scrollback: | Label | Meaning | | :--------------------- | :---------------------------------------------------------------------------------------- | | `you:` | Your messages | | `claude:` | Claude's replies | | `tool:` | Tool activity, such as a file edit or a command run | | `tool error:` | A tool that failed | | `error:` | An error in the conversation, such as a failed API request | | `Permission Required:` | A permission prompt waiting for your answer | | `Cost:` | The session cost summary when Claude Code exits, if your account [shows costs](/docs/en/costs) | The terminal cursor follows the input caret, so a screen reader's read-current-line command answers "where am I" with the prompt you're editing. As you type or press `Backspace` at the end of the input line, Claude Code writes only the characters that change, so your screen reader echoes just those characters. Deletions require Claude Code v2.1.222 or later and typing requires v2.1.219 or later; before those versions, each keystroke rewrote the line, so the screen reader re-read it. When you delete a word or a line in the input, Claude Code announces the deleted text. Requires Claude Code v2.1.218 or later. The announcement covers: * Deleting a word with `Ctrl+W`, `Option+Delete` on macOS, or `Ctrl+Backspace` on Windows * Deleting to the start of the line with `Ctrl+U` or `Cmd+Backspace` * Deleting to the end of the line with `Ctrl+K` See the [text editing shortcuts](/docs/en/interactive-mode#text-editing) for what each key does. Cycling [permission modes](/docs/en/permission-modes) with `Shift+Tab` announces the mode you land on, such as `[plan mode on]` or `[accept edits on]`. Claude Code prints the announcement once and doesn't repeat it on later redraws. Requires Claude Code v2.1.210 or later. ### Jump between turns Claude Code emits OSC 133 shell-integration markers at turn boundaries, so your terminal's jump-to-previous-prompt key moves between turns without reading through the whole transcript: * iTerm2: Cmd+Shift+Up * VS Code terminal: Ctrl+Up on Windows, Cmd+Up on macOS * Windows Terminal: no key by default; bind the `scrollToMark` action in its settings * Kitty and Ghostty: check the terminal's documentation for its jump-to-prompt key macOS Terminal doesn't act on the markers, and Claude Code doesn't emit them in WezTerm. In those terminals, search the scrollback for the `you:` label instead. ## Answer menus and prompts In screen reader mode, menus you'd normally navigate with the arrow keys, including permission prompts, become numbered lists. Each option is announced as a numbered line, followed by an `Enter selection` prompt that names the valid range. Type the number of the option you want and press Enter. * To cancel a dismissible menu: press Escape. Its prompt ends with `or Escape to cancel`. * If you type a number that isn't on the list: Claude Code announces the valid range and lets you try again. Yes-or-no prompts ask for a typed answer instead of a two-option menu. Answer `y` or `n` and press Enter. `yes` and `no` also work. ## Hear when Claude Code needs you In screen reader mode, Claude Code rings the terminal bell when it needs your attention, so you don't have to keep checking the transcript. The bell rings when: * Claude finishes a reply * a permission prompt appears * a tool that ran longer than 5 seconds finishes The bell is your terminal's standard alert. To silence it, change the bell setting in your terminal application. The bell doesn't require screen reader mode: outside the mode, set [`preferredNotifChannel`](/docs/en/settings#available-settings) to `"terminal_bell"` for similar alerts when Claude is waiting on you. See [Get a terminal bell or notification](/docs/en/terminal-config#get-a-terminal-bell-or-notification). ## Accessibility settings beyond screen reader mode These options address accessibility needs outside of screen reader mode. All of them work alongside it. * The `CLAUDE_CODE_ACCESSIBILITY` [environment variable](/docs/en/env-vars) is for screen magnifiers. Set `CLAUDE_CODE_ACCESSIBILITY=1` to keep the native terminal cursor visible so that magnifiers, such as macOS Zoom, can track the cursor position. The cursor follows keyboard focus: the input caret while you type, and the highlighted row as you move through menus and panels, such as `/config` and `/plugin`, with the arrow keys. Row tracking in menus and panels requires Claude Code v2.1.218 or later. * The `prefersReducedMotion` [setting](/docs/en/settings#available-settings) reduces or disables spinners, shimmer, and other animations without changing the rest of the interface. * The `theme` [setting](/docs/en/settings#available-settings) selects the interface colors, including the colorblind-friendly `dark-daltonized` and `light-daltonized` themes. ## Known limitations Some behaviors aren't adapted for screen reader mode: * Screen reader mode doesn't turn on automatically when a screen reader is running. * Claude Code doesn't announce a permission mode change made in any way other than cycling with `Shift+Tab`, such as entering [plan mode](/docs/en/permission-modes#analyze-before-you-edit-with-plan-mode) from a command. * Attaching to a [background session](/docs/en/agent-view) with `claude attach` or from agent view enters the terminal's alternate screen, which has no native scrollback. This is the [same behavior as other attached sessions](/docs/en/fullscreen). To get back out, press Left Arrow on an empty prompt, or Ctrl+Z if a dialog has focus. * Claude Code announces costs in the summary it prints at exit, not per turn. * Screen reader mode doesn't change [non-interactive mode](/docs/en/headless) with the `-p` flag. Non-interactive mode already writes plain text and remains an alternative for scripting. ## Report an issue If something doesn't work with your screen reader, magnifier, or terminal, open an issue on the [Claude Code issue tracker](https://github.com/anthropics/claude-code/issues) and mention your assistive technology in the title. Include your operating system, terminal application, and assistive technology name and version in the report. ## Related resources These pages hold the full reference entries and related setup for what this page covers: * [Settings](/docs/en/settings#available-settings): the `axScreenReader`, `prefersReducedMotion`, `theme`, and `preferredNotifChannel` entries * [Environment variables](/docs/en/env-vars): the `CLAUDE_AX_SCREEN_READER` and `CLAUDE_CODE_ACCESSIBILITY` entries * [CLI reference](/docs/en/cli-reference#cli-flags): the `--ax-screen-reader` flag * [Terminal configuration](/docs/en/terminal-config): bells, notifications, and themes outside screen reader mode * [Non-interactive mode](/docs/en/headless): scripted `claude -p` runs, which write plain text without screen reader mode
admin-setup First recorded · 158 lines, first recorded
# Set up Claude Code for your organization ## Choose your API provider ## Decide how settings reach devices ### WSL sessions in Claude Code Desktop ## Decide what to enforce ## Set up usage visibility ## Review data handling ## Verify and onboard ## Next steps
The first capture of this source. The page was already there, and this is what it said.
# Set up Claude Code for your organization > A decision map for administrators deploying Claude Code, covering API providers, managed settings, policy enforcement, usage monitoring, and data handling. Claude Code enforces organization policy through managed settings that take precedence over local developer configuration. You deliver those settings from the Claude admin console, your mobile device management (MDM) system, or a file on disk. The settings control which tools, commands, servers, and network destinations Claude can reach. This page walks through the deployment decisions in order. Each row links to the section below and to the reference page for that area. <Note> SSO, SCIM provisioning, and seat assignment are configured at the Claude account level. See the [Claude Enterprise Administrator Guide](https://claude.com/resources/tutorials/claude-enterprise-administrator-guide) and [seat assignment](https://support.claude.com/en/articles/11845131-use-claude-code-with-your-team-or-enterprise-plan) for those steps. </Note> | Decision | What you're choosing | Reference | | :---------------------------------------------------------------------- | :-------------------------------------------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [Choose your API provider](#choose-your-api-provider) | Where Claude Code authenticates and how it's billed | [Authentication](/docs/en/authentication), [Amazon Bedrock](/docs/en/amazon-bedrock), [Google Cloud's Agent Platform](/docs/en/google-vertex-ai), [Microsoft Foundry](/docs/en/microsoft-foundry) | | [Decide how settings reach devices](#decide-how-settings-reach-devices) | How managed policy reaches developer machines | [Server-managed settings](/docs/en/server-managed-settings), [Settings files](/docs/en/settings#settings-files) | | [Decide what to enforce](#decide-what-to-enforce) | Which tools, commands, and integrations are allowed | [Permissions](/docs/en/permissions), [Sandboxing](/docs/en/sandboxing) | | [Set up usage visibility](#set-up-usage-visibility) | How you track spend and adoption | [Analytics](/docs/en/analytics), [Monitoring](/docs/en/monitoring-usage), [Costs](/docs/en/costs) | | [Review data handling](#review-data-handling) | Data retention and compliance posture | [Data usage](/docs/en/data-usage), [Security](/docs/en/security) | ## Choose your API provider Claude Code connects to Claude through one of several API providers. Your choice affects billing, authentication, which compliance posture you inherit, and which Claude Code features your developers can use. | Provider | Choose this when | | :---------------------------- | :------------------------------------------------------------------------------------------------------------------------------------ | | Claude for Teams / Enterprise | You want Claude Code and claude.ai under one per-seat subscription with no infrastructure to run. This is the default recommendation. | | Claude Console | You're API-first or want pay-as-you-go billing | | Amazon Bedrock | You want to inherit existing AWS compliance controls and billing | | Google Cloud's Agent Platform | You want to inherit existing GCP compliance controls and billing | | Microsoft Foundry | You want to inherit existing Azure compliance controls and billing | Some Claude Code features require a claude.ai account. [Claude Code on the web](/docs/en/claude-code-on-the-web), [Routines](/docs/en/routines), [Code Review](/docs/en/code-review), [Remote Control](/docs/en/remote-control), and the [Chrome extension](/docs/en/chrome) aren't available through Console API keys or cloud-provider credentials alone. If you deploy through Amazon Bedrock, Google Cloud's Agent Platform, or Microsoft Foundry, plan whether developers also need Claude for Teams or Enterprise seats. Each feature page lists its plan requirements. For the full provider comparison covering authentication, regions, and feature parity, see the [enterprise deployment overview](/docs/en/third-party-integrations). Each provider's auth setup is in [Authentication](/docs/en/authentication). Proxy and firewall requirements in [Network configuration](/docs/en/network-config) apply regardless of provider. If you want a single endpoint in front of multiple providers or centralized request logging, see [LLM gateway](/docs/en/llm-gateway). ## Decide how settings reach devices Managed settings define organization policy. Claude Code checks the four sources in the table below in priority order and applies the first one that returns a non-empty configuration, with two exceptions: * Claude Code honors a small set of [cross-source lock keys](/docs/en/settings#precedence-within-the-managed-tier), such as the sandbox allowlist locks, when any admin-controlled source sets them. * Claude Code [merges the `env` block per key across the admin-controlled sources](/docs/en/server-managed-settings#per-key-exceptions-across-managed-sources), apart from the telemetry-unit and credential-paired routing exceptions covered there. Only admin-controlled sources contribute to the merge, developer-writable settings can't, and [`CLAUDE_CODE_DISABLE_ADMIN_ENV_UNION=1`](/docs/en/env-vars) restores the winner-only composition. Requires Claude Code v2.1.223 or later. When a [`policyHelper`](/docs/en/settings#compute-managed-settings-with-a-policy-helper) is configured, its output is the only managed configuration Claude Code reads: the lock-key checks read it alone, and no per-key `env` merge happens. | Mechanism | Delivery | Priority | Platforms | | :---------------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------- | :------------- | | Server-managed | claude.ai admin console, or a self-hosted [Claude apps gateway](/docs/en/claude-apps-gateway) for gateway sign-ins | Highest | All | | plist / registry policy | macOS: `com.anthropic.claudecode` plist<br />Windows: `HKLM\SOFTWARE\Policies\ClaudeCode` | High | macOS, Windows | | File-based managed | macOS: `/Library/Application Support/ClaudeCode/managed-settings.json`<br />Linux and WSL: `/etc/claude-code/managed-settings.json`<br />Windows: `C:\Program Files\ClaudeCode\managed-settings.json` | Medium | All | | Windows user registry | `HKCU\SOFTWARE\Policies\ClaudeCode` | Lowest | Windows only | Server-managed settings reach devices at authentication time and refresh hourly during active sessions, with no endpoint infrastructure. Delivery through the claude.ai admin console requires a Claude for Teams or Enterprise plan. Deployments on Amazon Bedrock, Google Cloud's Agent Platform, or Microsoft Foundry can get the same remote delivery by running a [Claude apps gateway](/docs/en/claude-apps-gateway), or use one of the file-based or OS-level mechanisms instead. If your organization mixes providers, configure [server-managed settings](/docs/en/server-managed-settings) for claude.ai users plus a [file-based or plist/registry fallback](/docs/en/settings#settings-files) so other users still receive managed policy. The plist and HKLM registry locations work with any provider and resist tampering because they require admin privileges to write. The Windows user registry at HKCU is writable without elevation, so treat it as a convenience default rather than an enforcement channel. By default, WSL reads only the Linux file path at `/etc/claude-code`. To extend your Windows registry and `C:\Program Files\ClaudeCode` policy to WSL on the same machine, set [`wslInheritsWindowsSettings: true`](/docs/en/settings#available-settings) in either of those admin-only Windows sources. Whichever mechanism you choose, managed values take precedence over user and project settings, apart from a few security-sensitive [exceptions](/docs/en/settings#exceptions-to-managed-settings-precedence). Array settings such as `permissions.allow` and `permissions.deny` merge entries from all sources, so developers can extend managed lists but not remove from them. For `fallbackModel` and `availableModels`, the managed value replaces lower layers rather than merging. See [Server-managed settings](/docs/en/server-managed-settings) and [Settings files and precedence](/docs/en/settings#settings-files). ### WSL sessions in Claude Code Desktop On Windows, [Claude Code Desktop can run Code sessions inside a WSL 2 distribution](/docs/en/desktop-wsl). The session's Claude Code process runs inside the distribution, so it resolves managed settings through the WSL discovery path above: Windows-only sources don't reach it unless `wslInheritsWindowsSettings: true` is deployed. On devices where managed settings are present, Desktop WSL sessions are unavailable by default. If your organization wants to enable them, contact your Anthropic account team. When they're enabled: * Deploy `wslInheritsWindowsSettings: true` through the HKLM registry or the `C:\Program Files\ClaudeCode` file so WSL sessions inherit the same policy as host sessions. * Verify by running `/status` inside a WSL session: the `Setting sources` line should show `Enterprise managed settings` with the Windows source you deployed, `(HKLM)` or `(file)`. Processes inside the WSL 2 utility VM aren't visible to Windows-side endpoint detection sensors. If you use CrowdStrike Falcon, enable the Falcon sensor for Linux on WSL 2 with the two exclusions CrowdStrike's WSL documentation requires, for the WSL virtual machine process and the VM disk image, so in-distro process and file activity is observable. Claude Code's [OpenTelemetry tool-execution telemetry](/docs/en/monitoring-usage) is emitted identically for WSL and native sessions. ## Decide what to enforce Managed settings can lock down tools, sandbox execution, restrict MCP servers and plugin sources, and control which hooks run. Each row is a control surface with the setting keys that drive it. | Control | What it does | Key settings | | :------------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------- | | [Permission rules](/docs/en/permissions) | Allow, ask, or deny specific tools and commands | `permissions.allow`, `permissions.deny` | | [Permission lockdown](/docs/en/permissions#managed-only-settings) | Only managed permission rules apply; disable `--dangerously-skip-permissions` | `allowManagedPermissionRulesOnly`, `permissions.disableBypassPermissionsMode` | | [Sandboxing](/docs/en/sandboxing) | OS-level filesystem and network isolation with domain allowlists | `sandbox.enabled`, `sandbox.network.allowedDomains` | | [Managed policy CLAUDE.md](/docs/en/memory#deploy-organization-wide-claude-md) | Org-wide instructions loaded in every session, can't be excluded | File at the managed policy path | | [MCP server control](/docs/en/managed-mcp) | Restrict which MCP servers users can add or connect to, or deploy a fixed set | `allowedMcpServers`, `deniedMcpServers`, `allowManagedMcpServersOnly`, or a deployed `managed-mcp.json` file | | [Plugin marketplace control](/docs/en/plugin-marketplaces#managed-marketplace-restrictions) | Restrict which marketplace sources users can add and install from, reject the CLI flags that sideload plugins, agents, and MCP servers for a single run, block [`command` plugin sources](/docs/en/plugin-marketplaces#command-sources), and allowlist which marketplaces' plugins can be suggested | `strictKnownMarketplaces`, `blockedMarketplaces`, `disableSideloadFlags`, `disableCommandPluginSources`, `pluginSuggestionMarketplaces` | | [Customization lockdown](/docs/en/settings#strictpluginonlycustomization) | Block skills, agents, hooks, and MCP servers from user and project sources, so they can only come from plugins or managed settings | `strictPluginOnlyCustomization` | | [Hook restrictions](/docs/en/settings#hook-configuration) | Restrict which hooks run and restrict HTTP hook URLs; see [Hook configuration](/docs/en/settings#hook-configuration) for the full effect list | `allowManagedHooksOnly`, `allowedHttpHookUrls` | | [Login enforcement](/docs/en/settings#available-settings) | Restrict login to a specific method or Anthropic organization. The method restriction applies across the terminal, VS Code extension, Agent SDK, `claude setup-token`, and `/install-github-app`; Claude Code verifies the organization for claude.ai account logins in the terminal, VS Code extension, and Agent SDK, and doesn't check it for Claude Console logins or for [gateway](/docs/en/claude-apps-gateway) sign-in. Before v2.1.212, only terminal logins applied either key. When set, sessions authenticated by `ANTHROPIC_API_KEY`, `ANTHROPIC_AUTH_TOKEN`, or `apiKeyHelper` are blocked at startup; cloud provider sessions aren't affected | `forceLoginMethod`, `forceLoginOrgUUID` | | [Disable agent view](/docs/en/agent-view#how-background-sessions-are-hosted) | Turn off `claude agents`, `--bg`, `/background`, and the on-demand supervisor | `disableAgentView` | | [Configure the corporate launcher](/docs/en/corporate-launcher) | Prefix the [background-agent supervisor](/docs/en/agent-view#how-background-sessions-are-hosted), its workers, and the [other covered background processes](/docs/en/corporate-launcher#what-the-launcher-covers) with a required corporate launcher instead of turning agent view off | `processWrapper` | | [Model restrictions](/docs/en/model-config#restrict-model-selection) | `availableModels` filters which models appear in the picker. Adding `enforceAvailableModels` also constrains the auto-selected default model. See [surface coverage](/docs/en/model-config#surface-coverage) for how this setting reaches the CLI, web, and IDE | `availableModels`, `enforceAvailableModels` | | [Version floor](/docs/en/settings) | Prevent auto-update from installing below an org-wide minimum | `minimumVersion` | | [Required version range](/docs/en/settings) | Refuse to start at all when the running version is outside an org-approved range. Stronger than `minimumVersion`, which only blocks downgrades | `requiredMinimumVersion`, `requiredMaximumVersion` | Organizations whose members authenticate through claude.ai or the Anthropic API can also govern models without deploying settings: [organization model restrictions](/docs/en/model-config#organization-model-restrictions) disable individual models, an [organization default model](/docs/en/model-config#organization-default-model) sets which model new sessions start on, and [organization effort limits](/docs/en/model-config#organization-effort-limits) cap effort levels per role. All three controls require a Claude Enterprise plan. Model restrictions and effort limits are enforced server-side; the default model is a starting point that users can change, unless the organization enforces it. Enforcement is available to a limited set of organizations; ask your Anthropic account team about availability. None of these controls reach sessions on Amazon Bedrock, Google Cloud's Agent Platform, Microsoft Foundry, or [Claude Platform on AWS](/docs/en/claude-platform-on-aws); on those providers, use `availableModels` above for restrictions and the `model` key in managed settings for a default. [Claude Code on the web](/docs/en/claude-code-on-the-web) has its own admin surface: on the Cloud environments page in admin settings, owners and admins create [organization-shared environments](/docs/en/cloud-environments#organization-shared-environments) that set the [network access level](/docs/en/cloud-environments#network-access), environment variables, and setup script for members' cloud sessions. Owners and admins choose the organization's default environment separately, at [claude.ai/admin-settings/claude-code](https://claude.ai/admin-settings/claude-code). Permission rules and sandboxing cover different layers. Denying WebFetch blocks Claude's fetch tool, but if Bash is allowed, `curl` and `wget` can still reach any URL. Sandboxing closes that gap with a network domain allowlist enforced at the OS level. For the threat model these controls defend against, see [Security](/docs/en/security). ## Set up usage visibility Choose monitoring based on what you need to report on. The dashboards, APIs, and spend controls differ between Claude for Teams or Enterprise plans and Claude Console organizations, so check the Availability column before you plan your reporting around a capability. | Capability | What you get | Availability | Where to start | | :--------------------- | :---------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :---------------------------------------------------- | | Usage monitoring | OpenTelemetry export of sessions, tools, and tokens | All providers | [Monitoring usage](/docs/en/monitoring-usage) | | Analytics dashboard | Adoption and contribution metrics with a leaderboard on Teams / Enterprise; per-user usage and spend metrics on Console | Teams / Enterprise at [claude.ai/analytics](https://claude.ai/analytics/claude-code), Console at [platform.claude.com/claude-code](https://platform.claude.com/claude-code) | [Analytics](/docs/en/analytics) | | Programmatic reporting | Per-user usage and cost data over an API | [Enterprise Analytics API](https://platform.claude.com/docs/en/api/admin/analytics) for Enterprise, [Claude Code Analytics API](https://platform.claude.com/docs/en/build-with-claude/claude-code-analytics-api) for Console | [Costs](/docs/en/costs#manage-costs-for-your-organization) | | Spend controls | Spend limits and rate limits | Admin settings for Teams / Enterprise, workspace limits for Console; on third-party clouds, cloud budget controls or a [Claude apps gateway](/docs/en/claude-apps-gateway) with per-user [spend limits](/docs/en/claude-apps-gateway-spend-limits) | [Costs](/docs/en/costs#manage-costs-for-your-organization) | On Teams and Enterprise, per-user usage and spend numbers come from the [spend report](https://support.claude.com/en/articles/12883420-view-usage-analytics-for-team-and-enterprise-plans) in your organization's analytics settings, not the analytics dashboard. Cloud providers expose spend through AWS Cost Explorer, GCP Billing, or Azure Cost Management. For planning enterprise budgets across Claude chat, Claude Code, and Cowork, see the [Claude Enterprise consumption guide](https://support.claude.com/en/articles/14782391-claude-enterprise-consumption-guide). ## Review data handling On Team, Enterprise, Claude API, and cloud provider plans, Anthropic doesn't train models on your code or prompts. Your API provider determines retention and compliance posture. | Topic | What to know | Where to start | | :------------------------ | :--------------------------------------------------------------------------------------------------- | :--------------------------------------------- | | Data usage policy | What Anthropic collects, how long it's retained, what's never used for training | [Data usage](/docs/en/data-usage) | | Zero Data Retention (ZDR) | Nothing stored after the request completes. Available to qualified accounts on Claude for Enterprise | [Zero data retention](/docs/en/zero-data-retention) | | Security architecture | Network model, encryption, authentication, audit trail | [Security](/docs/en/security) | If you need request-level audit logging or to route traffic by data sensitivity, place a gateway between developers and your provider: a self-hosted [Claude apps gateway](/docs/en/claude-apps-gateway) records a per-request audit log with IdP identity, or use another [LLM gateway](/docs/en/llm-gateway). For regulatory requirements and certifications, see [Legal and compliance](/docs/en/legal-and-compliance). ## Verify and onboard After configuring managed settings, have a developer run `/status` inside Claude Code. On the **Status** tab, the `Setting sources` line shows `Enterprise managed settings` followed by the source in parentheses, one of `(remote)`, `(plist)`, `(HKLM)`, `(HKCU)`, or `(file)`. See [Verify active settings](/docs/en/settings#verify-active-settings). Share these resources to help developers get started: * [Quickstart](/docs/en/quickstart): first-session walkthrough from install to working with a project * [Common workflows](/docs/en/common-workflows): patterns for everyday tasks like code review, refactoring, and debugging * [Claude 101](https://anthropic.skilljar.com/claude-101) and [Claude Code in Action](https://anthropic.skilljar.com/claude-code-in-action): self-paced Anthropic Academy courses For login issues, point developers to [authentication troubleshooting](/docs/en/troubleshoot-install#login-and-authentication). The most common fixes are: * Run `/logout` then `/login` to switch accounts * Run `claude update` if the enterprise auth option is missing * Restart the terminal after updating If a developer sees "You haven't been added to your organization yet," their seat doesn't include Claude Code access and needs to be updated in the admin console. ## Next steps With provider and delivery mechanism chosen, move on to detailed configuration: * [Server-managed settings](/docs/en/server-managed-settings): deliver managed policy from the Claude admin console * [Settings reference](/docs/en/settings): every setting key, file location, and precedence rule * [Monorepos and large repos](/docs/en/large-codebases): per-directory configuration patterns for organizations deploying into a monorepo * [Amazon Bedrock](/docs/en/amazon-bedrock), [Google Cloud's Agent Platform](/docs/en/google-vertex-ai), [Microsoft Foundry](/docs/en/microsoft-foundry): provider-specific deployment * [Claude Enterprise Administrator Guide](https://claude.com/resources/tutorials/claude-enterprise-administrator-guide): SSO, SCIM, seat management, and rollout playbook
advisor First recorded · 162 lines, first recorded
# Escalate hard decisions with the advisor tool ## When to use the advisor ## Enable the advisor ### Use the `/advisor` command ### Set `advisorModel` in settings ### Use the `--advisor` flag ## Choose an advisor model ### Common model pairings ## When Claude consults the advisor ## What you see during a session ## Cost ## Impact on prompt caching ## Requirements ## Turn the advisor off ## Compare with related features ## See also
The first capture of this source. The page was already there, and this is what it said.
# Escalate hard decisions with the advisor tool
> Pair your main model with a stronger advisor model that Claude consults at key moments during a task.
<Note>
The advisor tool is experimental and requires the Anthropic API. It is not available on Amazon Bedrock, Claude Platform on AWS, Google Cloud's Agent Platform, or Microsoft Foundry. Behavior, pricing, and availability may change.
</Note>
The advisor tool lets Claude consult a second, typically stronger model at key moments during a task, such as before committing to an approach, when stuck on a recurring error, or before declaring a task complete. The advisor receives the full conversation, including every tool call and result, and returns guidance that Claude applies before continuing.
The advisor runs server-side on Anthropic's infrastructure as a [server tool](https://platform.claude.com/docs/en/agents-and-tools/tool-use/advisor-tool), available to both subscription and API-billed accounts. You choose which model acts as the advisor, and Claude decides when to call it.
This page covers how to enable the advisor, which model pairings are accepted, what Claude shows during a consultation, and how advisor usage is billed.
## When to use the advisor
The advisor fits long, multi-step tasks where most turns are routine but plan quality determines the outcome. Examples include large refactors, debugging sessions where an error keeps recurring, and tasks you want independently checked before Claude declares them done.
It adds less value on short tasks where there is little to plan, or on work where every turn needs the strongest model. For those, [switch the main model](/docs/en/model-config#setting-your-model) instead, or see [how the advisor compares with opusplan and subagents](#compare-with-related-features) for other ways to get a second opinion.
## Enable the advisor
You can set the advisor model in three ways:
* **`/advisor` command**: set or change the advisor mid-session and save it as your default
* **`advisorModel` setting**: configure a persistent default in your [settings file](/docs/en/settings)
* **`--advisor` flag**: set the advisor for a single session at launch
If any of these sets an advisor model, the advisor is enabled for sessions whose main model [supports it](#choose-an-advisor-model), and an `Advisor Tool (experimental) is on and may use more tokens · /advisor` notification appears after the session starts. To stop using it, see [Turn the advisor off](#turn-the-advisor-off).
### Use the `/advisor` command
Run `/advisor` without arguments to open a picker listing the available advisor models, or pass the model directly:
```
/advisor opus
```
The command confirms with `Advisor set to` followed by the advisor model name. Your selection is saved to `advisorModel` in your user settings and persists across sessions.
If your organization's [`availableModels`](/docs/en/model-config#restrict-model-selection) allowlist excludes the saved advisor model, the advisor is not invoked until you pick an allowed model with `/advisor`. If your current main model does not support the advisor, the selection is still saved and activates when you switch to a [compatible main model](#choose-an-advisor-model) with [`/model`](/docs/en/model-config#setting-your-model).
### Set `advisorModel` in settings
To configure the advisor as a default without opening a session, set it in your settings file:
```json theme={null}
{
"advisorModel": "opus"
}
```
### Use the `--advisor` flag
To set the advisor for a single session without changing your saved setting, launch with the flag:
```bash theme={null}
claude --advisor opus
```
The flag takes precedence over the `advisorModel` setting for that session, and isn't listed in `claude --help`. It exits with an error if the session's main model does not support the advisor, or if the requested advisor model is excluded by your organization's [`availableModels`](/docs/en/model-config#restrict-model-selection) allowlist.
## Choose an advisor model
The advisor must be at least as capable as the main model. The accepted advisors for each main model are:
| Main model | Accepted advisors | Notes |
| ------------------- | ---------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Haiku 4.5 | Fable, Opus, Sonnet | Haiku can call the advisor but cannot act as one |
| Sonnet 4.6 | Fable, Opus, Sonnet | |
| Sonnet 5 | Fable, Opus, Sonnet 5 | A Sonnet 4.6 advisor is rejected |
| Opus 4.6 | Fable, Opus, Sonnet 5 | Sonnet 5 and Opus 4.6 are ranked as equally capable, so an Opus 4.6 main accepts a Sonnet 5 advisor |
| Opus 4.7 or later | Fable, and Opus 4.7 or later | Opus 4.7 and later Opus models are ranked as equally capable, so any of them accepts another as an advisor. An Opus 4.7 main with an Opus 4.6 or Sonnet 5 advisor is rejected |
| Fable 5 (v2.1.170+) | Fable | An Opus or Sonnet advisor is rejected |
Fable 5 requires Claude Code v2.1.170 or later and [Fable 5 access](/docs/en/model-config#work-with-fable-5), whether it acts as the main model or the advisor.
Set the advisor as `fable`, `opus`, or `sonnet`. These aliases resolve to Claude Code's built-in default version for each model family, which advances with new Claude Code releases. You can also pass a full model ID such as `claude-opus-5`.
Subagents inherit the configured advisor and apply the same pairing check against their own model.
Claude Code validates the pairing before sending a request:
* If the advisor is less capable than the main model, the advisor is not attached to the main model's requests. The `/advisor` command output and a notification show this. Subagents whose own model satisfies the pairing may still use the advisor.
* If the main model or the advisor is a model Claude Code does not recognize, the advisor is not attached.
### Common model pairings
Any accepted pairing works. These combinations balance cost against capability in different ways:
| Pairing | When to use |
| ---------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| Sonnet main + Opus advisor | Sonnet handles routine work and escalates planning, ambiguous failures, and completion checks to Opus |
| Sonnet main + Fable advisor | Fable 5 guidance at decision points without running Fable 5 throughout. Requires v2.1.170 or later and Fable 5 access |
| Haiku main + Opus advisor | Lowest-cost main model with strong planning. Expect higher cost than Haiku alone but lower than switching the main model to Sonnet or Opus |
| Opus main + Opus advisor | A second Opus reviews the first. Useful for high-stakes tasks where an independent check matters more than cost |
| Fable main + Fable advisor | Highest-capability pairing when Fable 5 is available (v2.1.170+). Fable is a higher tier than Opus and Sonnet, so it is the only accepted advisor for a Fable main model |
| Sonnet main + Sonnet advisor | A lower-cost second opinion for catching routine oversights |
## When Claude consults the advisor
Claude decides when to call the advisor. It tends to consult before committing to an approach, when an error keeps recurring, and before declaring a task done, but the timing is model-driven rather than rule-based.
You can ask for a consultation in your prompt the same way you would request any tool, for example `consult the advisor before you continue`. There is no setting to cap or force advisor calls; if you want Claude to consult more or less often during a task, say so in your instructions.
## What you see during a session
When Claude calls the advisor, the transcript shows an `Advising` line with the advisor model name while the call is in progress. When the result returns, the line confirms that the advisor has reviewed the conversation. Press `Ctrl+O` to expand it and read the advisor's full guidance.
Claude generally follows the advisor's guidance, but adapts when its own evidence contradicts a specific claim: if a recommended step fails when tried, or the file contents contradict the advice, Claude surfaces the conflict rather than following the guidance unconditionally.
The advisor always receives the full conversation, and Claude controls the timing. For more control or a different configuration, see [how the advisor compares with subagents and opusplan](#compare-with-related-features).
## Cost
Each advisor call sends the conversation to the advisor model, so it consumes tokens at the advisor model's rates in addition to your main model's usage. With API billing, advisor tokens are charged at the advisor model's input and output rates. On subscription plans, advisor usage counts toward your plan's usage limits.
Claude calls the advisor at decision points rather than on every turn, so pairing a faster main model with a stronger advisor typically costs less than running the stronger model throughout. Advisor usage counts toward the session totals shown by [`/usage`](/docs/en/costs#track-your-costs).
For how advisor tokens are reported in API responses, see [Usage and billing](https://platform.claude.com/docs/en/agents-and-tools/tool-use/advisor-tool#usage-and-billing) in the Claude API documentation.
## Impact on prompt caching
Enabling or disabling the advisor mid-session does not invalidate your main model's [prompt cache](/docs/en/prompt-caching). Unlike [changing model or effort level](/docs/en/prompt-caching#actions-that-invalidate-the-cache), toggling `/advisor` keeps the cached prefix intact, and the advisor's returned guidance is cached as part of the transcript on later turns.
The advisor model's own read of the conversation is not cached. Each advisor call processes the full transcript anew, with no reuse between calls.
## Requirements
The advisor tool requires all of the following:
* **Anthropic API only**: the advisor is a server-executed tool. It is not available on Amazon Bedrock, Claude Platform on AWS, Google Cloud's Agent Platform, or Microsoft Foundry. Through an [LLM gateway](/docs/en/llm-gateway) configured with `ANTHROPIC_BASE_URL`, availability depends on whether the gateway forwards the request intact to the Anthropic API.
* **Supported main model**: Opus 4.6 or later, Sonnet 4.6 or later, or Haiku 4.5. Fable 5 also qualifies on Claude Code v2.1.170 or later, and a Fable 5 main [accepts only a Fable advisor](#choose-an-advisor-model).
* **Feature-flag fetching**: Claude Code turns the advisor on through a feature flag it fetches from Anthropic. In a session where a variable that turns flag fetching off is set, such as `DISABLE_TELEMETRY`, the advisor stays off. See [Features that need feature-flag fetching](/docs/en/env-vars#features-that-need-feature-flag-fetching).
## Turn the advisor off
To stop using the advisor and clear your saved `advisorModel`, run `/advisor off` or choose **No advisor** in the `/advisor` picker:
```
/advisor off
```
To disable the advisor tool entirely, set `CLAUDE_CODE_DISABLE_ADVISOR_TOOL=1`. The `/advisor` command becomes unavailable and any configured `advisorModel` is ignored. The `--advisor` flag is accepted but has no effect. See [Environment variables](/docs/en/env-vars).
## Compare with related features
The advisor is one of several ways to combine model strengths. Pick based on when you want a second model involved.
| Approach | When the stronger model runs | How it starts |
| ----------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------- |
| Advisor tool | At decision points mid-task | Claude calls it when it needs guidance |
| [`opusplan`](/docs/en/model-config#opusplan-model-setting) | During plan mode when [allowed by `availableModels`](/docs/en/model-config#restrict-model-selection), then switches to Sonnet for execution | You enter plan mode |
| [Subagents](/docs/en/sub-agents#choose-a-model) with `model` set | For the entire delegated subtask | Claude delegates, or you invoke the subagent |
| [`/model`](/docs/en/model-config#setting-your-model) | For all subsequent turns | You switch models |
## See also
* [Model configuration](/docs/en/model-config): switch models, set effort levels, and use `opusplan`
* [Manage costs effectively](/docs/en/costs): track token usage across models
* [Advisor tool in the Claude API](https://platform.claude.com/docs/en/agents-and-tools/tool-use/advisor-tool): understand the underlying server tool, or use it directly from the Messages API
* [The advisor strategy](https://claude.com/blog/the-advisor-strategy): why pairing a fast main model with a stronger advisor works
agent-sdk/agent-loop First recorded · 459 lines, first recorded
# How the agent loop works ## The loop at a glance ## Turns and messages ## Message types ### Handle messages ## Tool execution ### Built-in tools ### Tool permissions ### Parallel tool execution ## Control how the loop runs ### Turns and budget ### Effort level ### Permission mode ### Model ## The context window ### What consumes context ### Automatic compaction ### Keep context efficient ## Sessions and continuity ## Handle the result ## Hooks ## Put it all together ## Next steps
The first capture of this source. The page was already there, and this is what it said.
# How the agent loop works
> Understand the message lifecycle, tool execution, context window, and architecture that power your SDK agents.
The Agent SDK lets you embed Claude Code's autonomous agent loop in your own applications. The SDK is a standalone package that gives you programmatic control over tools, permissions, cost limits, and output.
Both the TypeScript and Python SDKs bundle a native Claude Code binary, so most installs need no separate Claude Code install. See the [quickstart's install note](/docs/en/agent-sdk/quickstart) for the installs that do.
When you start an agent, the SDK runs the same [execution loop that powers Claude Code](/docs/en/how-claude-code-works#the-agentic-loop): Claude evaluates your prompt, calls tools to take action, receives the results, and repeats until the task is complete. This page explains what happens inside that loop so you can build, debug, and optimize your agents effectively.
## The loop at a glance
Every agent session follows the same cycle:
<img src="https://mintcdn.com/claude-code/ikqp3_70mqIahteV/images/agent-loop-diagram.svg?fit=max&auto=format&n=ikqp3_70mqIahteV&q=85&s=1c6e8f28d80dba14a7287419656f1237" className="dark:hidden" alt="Diagram of the agent loop: your prompt enters the agentic loop, where Claude evaluates and either requests tool calls, whose results feed back into another evaluation, or returns the final answer" width="720" height="212" data-path="images/agent-loop-diagram.svg" />
<img src="https://mintcdn.com/claude-code/_xqph1dUOslCOwsj/images/agent-loop-diagram-dark.svg?fit=max&auto=format&n=_xqph1dUOslCOwsj&q=85&s=afe723c52a324d3c61fa72fb02432ab6" className="hidden dark:block" alt="Diagram of the agent loop: your prompt enters the agentic loop, where Claude evaluates and either requests tool calls, whose results feed back into another evaluation, or returns the final answer" width="720" height="212" data-path="images/agent-loop-diagram-dark.svg" />
1. **Receive prompt.** Claude receives your prompt, along with the system prompt, tool definitions, and conversation history. The SDK yields a [`SystemMessage`](#message-types) with subtype `"init"` containing session metadata.
2. **Evaluate and respond.** Claude evaluates the current state and determines how to proceed. It may respond with text, request one or more tool calls, or both. The SDK yields an [`AssistantMessage`](#message-types) containing the text and any tool call requests.
3. **Execute tools.** The SDK runs each requested tool and collects the results. Each set of tool results feeds back to Claude for the next decision. You can use [hooks](/docs/en/agent-sdk/hooks) to intercept, modify, or block tool calls before they run.
4. **Repeat.** Steps 2 and 3 repeat as a cycle. Each full cycle is one turn. Claude continues calling tools and processing results until it produces a response with no tool calls.
5. **Return result.** The SDK yields a final [`AssistantMessage`](#message-types) with the text response (no tool calls), followed by a [`ResultMessage`](#message-types) with the final text, token usage, cost, and session ID.
A quick question ("what files are here?") might take one or two turns of calling `Glob` and responding with the results. A complex task ("refactor the auth module and update the tests") can chain dozens of tool calls across many turns, reading files, editing code, and running tests, with Claude adjusting its approach based on each result.
## Turns and messages
A turn is one round trip inside the loop: Claude produces output that includes tool calls, the SDK executes those tools, and the results feed back to Claude automatically. This happens without yielding control back to your code. Turns continue until Claude produces output with no tool calls, at which point the loop ends and the final result is delivered.
Consider what a full session might look like for the prompt "Fix the failing tests in auth.ts".
First, the SDK sends your prompt to Claude and yields a [`SystemMessage`](#message-types) with the session metadata. Then the loop begins:
1. **Turn 1:** Claude calls `Bash` to run `npm test`. The SDK yields an [`AssistantMessage`](#message-types) with the tool call, executes the command, then yields a [`UserMessage`](#message-types) with the output (three failures).
2. **Turn 2:** Claude calls `Read` on `auth.ts` and `auth.test.ts`. The SDK returns the file contents and yields an `AssistantMessage`.
3. **Turn 3:** Claude calls `Edit` to fix `auth.ts`, then calls `Bash` to re-run `npm test`. All three tests pass. The SDK yields an `AssistantMessage`.
4. **Final turn:** Claude produces a text-only response with no tool calls: "Fixed the auth bug, all three tests pass now." The SDK yields a final `AssistantMessage` with this text, then a [`ResultMessage`](#message-types) with the same text plus cost and usage.
That was four turns: three with tool calls, one final text-only response.
You can cap the loop with `max_turns` / `maxTurns`, which counts tool-use turns only. For example, `max_turns=2` in the loop above would have stopped before the edit step. You can also use `max_budget_usd` / `maxBudgetUsd` to cap turns based on a spend threshold.
Without limits, the loop runs until Claude finishes on its own, which is fine for well-scoped tasks but can run long on open-ended prompts ("improve this codebase"). Setting a budget is a good default for production agents. See [Turns and budget](#turns-and-budget) below for the option reference.
## Message types
As the loop runs, the SDK yields a stream of messages. Each message carries a type that tells you what stage of the loop it came from. The five core types are:
* **`SystemMessage`:** session lifecycle events. The `subtype` field distinguishes them:
* `"init"`: session metadata for the run. When a `SessionStart` or `Setup` hook runs during session startup, its [hook lifecycle messages](/docs/en/agent-sdk/typescript#sdkhookstartedmessage) arrive before the `init` message
* `"compact_boundary"`: fires after [compaction](#automatic-compaction)
* `"informational"`: plain-text status banners from the loop
* `"worker_shutting_down"`: the loop will end after the current turn because the host is exiting or Remote Control disconnected
In TypeScript, each subtype other than `"init"` is its own type in the [`SDKMessage` union](/docs/en/agent-sdk/typescript#sdkmessage) rather than a subtype of `SDKSystemMessage`.
* **`AssistantMessage`:** emitted after each Claude response, including the final text-only one. Contains text content blocks and tool call blocks from that turn.
* **`UserMessage`:** emitted after each tool execution with the tool result content sent back to Claude. Also emitted for any user inputs you stream mid-loop.
* **`StreamEvent`:** only emitted when partial messages are enabled. Contains raw API streaming events (text deltas, tool input chunks). See [Stream responses](/docs/en/agent-sdk/streaming-output).
* **`ResultMessage`:** marks the end of the agent loop. Contains the final text result, token usage, cost, and session ID. Check the `subtype` field to determine whether the task succeeded or hit a limit. A small number of trailing system events, such as `prompt_suggestion`, can arrive after it, so iterate the stream to completion rather than breaking on the result. See [Handle the result](#handle-the-result).
These five types cover the full agent loop lifecycle. Both SDKs also yield observability events such as rate-limit status and task notifications that are not required to drive the loop. See the [Python message types reference](/docs/en/agent-sdk/python#message-types) and [TypeScript message types reference](/docs/en/agent-sdk/typescript#message-types) for the complete lists.
### Handle messages
Which messages you handle depends on what you're building:
* **Final results only:** handle `ResultMessage` to get the output, cost, and whether the task succeeded or hit a limit.
* **Progress updates:** handle `AssistantMessage` to see what Claude is doing each turn, including which tools it called.
* **Live streaming:** enable partial messages (`include_partial_messages` in Python, `includePartialMessages` in TypeScript) to get `StreamEvent` messages in real time. See [Stream responses in real-time](/docs/en/agent-sdk/streaming-output).
How you check message types depends on the SDK:
* **Python:** check message types with `isinstance()` against classes imported from `claude_agent_sdk` (for example, `isinstance(message, ResultMessage)`).
* **TypeScript:** check the `type` string field (for example, `message.type === "result"`). `AssistantMessage` and `UserMessage` wrap the raw API message in a `.message` field, so content blocks are at `message.message.content`, not `message.content`.
<Accordion title="Example: Check message types and handle results">
<CodeGroup>
```python Python theme={null}
import asyncio
from claude_agent_sdk import query, AssistantMessage, ResultMessage
async def main():
try:
async for message in query(prompt="Summarize this project"):
if isinstance(message, AssistantMessage):
print(f"Turn completed: {len(message.content)} content blocks")
if isinstance(message, ResultMessage):
if message.subtype == "success":
print(message.result)
else:
print(f"Stopped: {message.subtype}")
except Exception as error:
# A single-shot query() raises after yielding an error result. If the
# failure was an error result, the error subtype branches above have
# already run; connection or process failures yield no result message.
print(f"Session ended with an error: {error}")
asyncio.run(main())
```
```typescript TypeScript theme={null}
import { query } from "@anthropic-ai/claude-agent-sdk";
try {
for await (const message of query({ prompt: "Summarize this project" })) {
if (message.type === "assistant") {
console.log(`Turn completed: ${message.message.content.length} content blocks`);
}
if (message.type === "result") {
if (message.subtype === "success") {
console.log(message.result);
} else {
console.log(`Stopped: ${message.subtype}`);
}
}
}
} catch (error) {
// A single-shot query() throws after yielding an error result. If the
// failure was an error result, the error subtype branches above have
// already run; connection or process failures yield no result message.
console.log(`Session ended with an error: ${error}`);
}
```
</CodeGroup>
</Accordion>
## Tool execution
Tools give your agent the ability to take action. Without tools, Claude can only respond with text. With tools, Claude can read files, run commands, search code, and interact with external services.
### Built-in tools
The SDK includes the same tools that power Claude Code:
| Category | Tools | What they do |
| :------------------ | :-------------------------------------------------------------- | :-------------------------------------------------------------------------- |
| **File operations** | `Read`, `Edit`, `Write` | Read, modify, and create files |
| **Search** | `Glob`, `Grep` | Find files by pattern, search content with regex |
| **Execution** | `Bash` | Run shell commands, scripts, git operations |
| **Web** | `WebSearch`, `WebFetch` | Search the web, fetch and parse pages |
| **Discovery** | `ToolSearch` | Dynamically find and load tools on-demand instead of preloading all of them |
| **Orchestration** | `Agent`, `Skill`, `AskUserQuestion`, `TaskCreate`, `TaskUpdate` | Spawn subagents, invoke skills, ask the user, track tasks |
Beyond built-in tools, you can:
* **Connect external services** with [MCP servers](/docs/en/agent-sdk/mcp) (databases, browsers, APIs)
* **Define custom tools** with [custom tool handlers](/docs/en/agent-sdk/custom-tools)
* **Load project skills** via [setting sources](/docs/en/agent-sdk/claude-code-features) for reusable workflows
### Tool permissions
Claude determines which tools to call based on the task, but you control whether those calls are allowed to execute. You can auto-approve specific tools, block others entirely, or require approval for everything. Three options work together to determine what runs:
* **`allowed_tools` / `allowedTools`** auto-approves listed tools. A read-only agent with `["Read", "Glob", "Grep"]` in its allowed tools list runs those tools without prompting. Tools not listed are still available but require permission.
* **`disallowed_tools` / `disallowedTools`** blocks listed tools, regardless of other settings. See [Permissions](/docs/en/agent-sdk/permissions) for the order that rules are checked before a tool runs.
* **`permission_mode` / `permissionMode`** controls how much human oversight you want. The SDK evaluates the active mode together with your allow and deny rules in a fixed order, described in [How permissions are evaluated](/docs/en/agent-sdk/permissions#how-permissions-are-evaluated). See [Permission mode](#permission-mode) for available modes.
You can also scope individual tools with rules like `"Bash(npm *)"` to allow only specific commands. See [Permissions](/docs/en/agent-sdk/permissions) for the full rule syntax.
When a tool is denied, Claude receives a rejection message as the tool result and typically attempts a different approach or reports that it couldn't proceed.
### Parallel tool execution
When Claude requests multiple tool calls in a single turn, both SDKs can run them concurrently or sequentially depending on the tool. Read-only tools (like `Read`, `Glob`, `Grep`, and MCP tools marked as read-only) can run concurrently. Tools that modify state (like `Edit`, `Write`, and `Bash`) run sequentially to avoid conflicts.
Custom tools default to sequential execution. To enable parallel execution for a custom tool, set `readOnlyHint` in its annotations. Both the [TypeScript](/docs/en/agent-sdk/typescript#tool) and [Python](/docs/en/agent-sdk/python#tool) SDKs use this field name from the MCP SDK.
## Control how the loop runs
You can limit how many turns the loop takes, how much it costs, how deeply Claude reasons, and whether tools require approval before running. All of these are fields on [`ClaudeAgentOptions`](/docs/en/agent-sdk/python#claudeagentoptions) (Python) / [`Options`](/docs/en/agent-sdk/typescript#options) (TypeScript).
### Turns and budget
| Option | What it controls | Default |
| :--------------------------------------------- | :--------------------------- | :------- |
| Max turns (`max_turns` / `maxTurns`) | Maximum tool-use round trips | No limit |
| Max budget (`max_budget_usd` / `maxBudgetUsd`) | Maximum cost before stopping | No limit |
When either limit is hit, the SDK returns a `ResultMessage` with a corresponding error subtype (`error_max_turns` or `error_max_budget_usd`). See [Handle the result](#handle-the-result) for how to check these subtypes and [`ClaudeAgentOptions`](/docs/en/agent-sdk/python#claudeagentoptions) / [`Options`](/docs/en/agent-sdk/typescript#options) for syntax.
The budget cap covers [subagents](/docs/en/agent-sdk/subagents): their spend counts toward the total. Once spend reaches the cap, spawning another subagent fails with `Budget limit reached`, and Claude Code stops any background subagents still running. The cap-enforcement behaviors require Claude Code v2.1.217 or later.
With [streaming input](/docs/en/agent-sdk/streaming-vs-single-mode), a message you send while a turn is still running stays queued when that turn ends at the max-turns limit, and it starts its own turn with its own max-turns limit.
### Effort level
The `effort` option controls how much reasoning Claude applies. Lower effort levels use fewer tokens per turn and reduce cost. Not all models support the effort parameter. See [Effort](https://platform.claude.com/docs/en/build-with-claude/effort) for which models support it.
| Level | Behavior | Good for |
| :--------- | :-------------------------------- | :------------------------------------------------------------------------ |
| `"low"` | Minimal reasoning, fast responses | File lookups, listing directories |
| `"medium"` | Balanced reasoning | Routine edits, standard tasks |
| `"high"` | Thorough analysis | Refactors, debugging |
| `"xhigh"` | Extended reasoning depth | Coding and agentic tasks; recommended on Fable 5, Opus 4.7+, and Sonnet 5 |
| `"max"` | Maximum reasoning depth | Multi-step problems requiring deep analysis |
If you don't set `effort`, both SDKs leave the parameter unset and defer to the model's default behavior.
<Note>
`effort` trades latency and token cost for reasoning depth within each response. [Extended thinking](https://platform.claude.com/docs/en/build-with-claude/extended-thinking) is a separate feature that produces `thinking` blocks in the output, and the `display` field on `ThinkingConfig` for [Python](/docs/en/agent-sdk/python#thinkingconfig) or [TypeScript](/docs/en/agent-sdk/typescript#thinkingconfig) controls whether you receive their text. They are independent: you can set `effort: "low"` with extended thinking enabled, or `effort: "max"` without it.
</Note>
Use lower effort for agents doing simple, well-scoped tasks (like listing files or running a single grep) to reduce cost and latency. Set `effort` in the top-level `query()` options for the whole session, or per subagent with the `effort` field on [`AgentDefinition`](/docs/en/agent-sdk/subagents#agentdefinition-configuration) to override the session level.
### Permission mode
The permission mode option (`permission_mode` in Python, `permissionMode` in TypeScript) controls whether the agent asks for approval before using tools:
| Mode | Behavior | Use case |
| :-------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | :-------------------------------------------------------------------------------------------------------------------------------------------- |
| `"default"` | Tools not covered by allow rules trigger your `canUseTool` callback; no callback means deny | Interactive applications with a custom approval callback |
| `"acceptEdits"` | Auto-approves file edits and common filesystem commands (`mkdir`, `touch`, `mv`, `cp`, etc.); other Bash commands follow default rules | You trust Claude's edits and want faster iteration, such as during prototyping or when working in an isolated directory |
| `"plan"` | Claude explores and plans without editing your source files; file edits are never auto-approved and prompt through your `canUseTool` callback | You want Claude to propose changes without executing them, such as during code review or when you need to approve changes before they're made |
| `"dontAsk"` | Never prompts. Tools pre-approved by [permission rules](/docs/en/settings#permission-settings) run; everything else is denied. `AskUserQuestion`, connector tools [your organization set to `ask`](/docs/en/mcp#organization-controls-on-connector-tools), and MCP tools marked [`requiresUserInteraction`](/docs/en/mcp#require-approval-for-a-specific-tool) are denied even if you've allowed them | You want a fixed, explicit tool surface for a headless agent and prefer a hard deny over silent reliance on `canUseTool` being absent |
| `"auto"` | Uses a model classifier to approve or deny permission prompts. See [Auto mode](/docs/en/permission-modes#eliminate-prompts-with-auto-mode) for availability and behavior | Autonomous agents that still want safety guardrails on tool use |
| `"bypassPermissions"` | Runs all allowed tools without asking, except tools matched by an explicit [`ask` rule](/docs/en/settings#permission-settings), connector tools [your organization set to `ask`](/docs/en/mcp#organization-controls-on-connector-tools), and tools that require user interaction. The [cross-session messaging safeguards](/docs/en/permission-modes#skip-all-checks-with-bypasspermissions-mode) still apply. See [How permissions are evaluated](/docs/en/agent-sdk/permissions#how-permissions-are-evaluated) for the precedence order. In the TypeScript SDK, also requires `allowDangerouslySkipPermissions: true` in `options`. Can't be used when running as root on Unix. Use only in isolated environments where the agent's actions can't affect systems you care about | CI, containers, or other isolated environments |
For interactive applications, use `"default"` with a tool approval callback to surface approval prompts. For autonomous agents on a dev machine, `"acceptEdits"` auto-approves file edits and common filesystem commands (`mkdir`, `touch`, `mv`, `cp`, etc.) while still gating other `Bash` commands behind allow rules. Reserve `"bypassPermissions"` for CI, containers, or other isolated environments. See [Permissions](/docs/en/agent-sdk/permissions) for full details.
### Model
If you don't set `model`, the SDK uses Claude Code's default, which depends on your authentication method and subscription. Set it explicitly (for example, `model="claude-sonnet-5"`) to pin a specific model or to use a smaller model for faster, cheaper agents. See [models](https://platform.claude.com/docs/en/about-claude/models) for available IDs.
## The context window
The context window is the total amount of information available to Claude during a session. It does not reset between turns within a session. Everything accumulates: the system prompt, tool definitions, conversation history, tool inputs, and tool outputs. Content that stays the same across turns (system prompt, tool definitions, CLAUDE.md) is automatically [prompt cached](https://platform.claude.com/docs/en/build-with-claude/prompt-caching), which reduces cost and latency for repeated prefixes. For how a custom system prompt or `append` text affects cache reuse across sessions, see [Modifying system prompts](/docs/en/agent-sdk/modifying-system-prompts#improve-prompt-caching-across-users-and-machines).
### What consumes context
Here's how each component affects context in the SDK:
| Source | When it loads | Impact |
| :----------------------- | :------------------------------------------------------------------------ | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **System prompt** | Every request | Small fixed cost, always present |
| **CLAUDE.md files** | Session start, via [`settingSources`](/docs/en/agent-sdk/claude-code-features) | Full content in every request (but prompt-cached, so only the first request pays full cost) |
| **Tool definitions** | Every request; MCP schemas deferred by default | Built-in tool schemas load every request. [Tool search](/docs/en/agent-sdk/mcp#mcp-tool-search) defers MCP tool schemas by default, falling back to upfront loading on unsupported models and certain platforms. See [Configure tool search](/docs/en/agent-sdk/tool-search#configure-tool-search) for the full matrix |
| **Conversation history** | Accumulates over turns | Grows with each turn: prompts, responses, tool inputs, tool outputs |
| **Skill descriptions** | Session start, via setting sources | Short summaries; full content loads only when invoked |
Large tool outputs consume significant context. Reading a big file or running a command with verbose output can use thousands of tokens in a single turn. Context accumulates across turns, so longer sessions with many tool calls build up significantly more context than short ones.
### Automatic compaction
When the context window approaches its limit, the SDK automatically compacts the conversation: it summarizes older history to free space, keeping your most recent exchanges and key decisions intact. The SDK emits a message with `type: "system"` and `subtype: "compact_boundary"` in the stream when this happens (in Python this is a `SystemMessage`; in TypeScript it is a separate `SDKCompactBoundaryMessage` type).
Compaction replaces older messages with a summary, so specific instructions from early in the conversation may not be preserved. Persistent rules belong in CLAUDE.md (loaded via [`settingSources`](/docs/en/agent-sdk/claude-code-features)) rather than in the initial prompt, because CLAUDE.md content is re-injected on every request.
You can customize compaction behavior in several ways:
* **Summarization instructions in CLAUDE.md:** The compactor reads your CLAUDE.md like any other context, so you can include a section telling it what to preserve when summarizing. The section header is free-form (not a magic string); the compactor matches on intent.
* **`PreCompact` hook:** Run custom logic before compaction occurs, for example to archive the full transcript. The hook receives a `trigger` field (`manual` or `auto`). See [hooks](/docs/en/agent-sdk/hooks).
* **Manual compaction:** Send `/compact` as a prompt string to trigger compaction on demand. Commands sent this way are ordinary SDK inputs. See [commands in the SDK](/docs/en/agent-sdk/slash-commands).
<Accordion title="Example: Summarization instructions in CLAUDE.md">
Add a section to your project's CLAUDE.md telling the compactor what to preserve. The header name isn't special; use any clear label.
```markdown CLAUDE.md theme={null}
# Summary instructions
When summarizing this conversation, always preserve:
- The current task objective and acceptance criteria
- File paths that have been read or modified
- Test results and error messages
- Decisions made and the reasoning behind them
```
</Accordion>
### Keep context efficient
A few strategies for long-running agents:
* **Use subagents for subtasks.** Each subagent starts with a fresh conversation (no prior message history, though it does load its own system prompt and project-level context like CLAUDE.md). It does not see the parent's turns, and only its final response returns to the parent as a tool result. The main agent's context grows by that summary, not by the full subtask transcript. See [What subagents inherit](/docs/en/agent-sdk/subagents#what-subagents-inherit) for details.
* **Be selective with tools.** Every tool definition takes context space. Use the `tools` field on [`AgentDefinition`](/docs/en/agent-sdk/subagents#agentdefinition-configuration) to scope subagents to the minimum set they need.
* **Watch MCP server costs.** [MCP tool search](/docs/en/agent-sdk/mcp#mcp-tool-search) defers MCP tool schemas by default and loads them on demand. When tool search is off or has fallen back to upfront loading, each MCP server adds all its tool schemas to every request, so a few servers with many tools can consume significant context before the agent does any work. See [Configure tool search](/docs/en/agent-sdk/tool-search#configure-tool-search) for the configurations where the fallback applies.
* **Use lower effort for routine tasks.** Set [effort](#effort-level) to `"low"` for agents that only need to read files or list directories. This reduces token usage and cost.
For a detailed breakdown of per-feature context costs, see [Understand context costs](/docs/en/features-overview#understand-context-costs).
## Sessions and continuity
Each interaction with the SDK creates or continues a session. Capture the session ID from `ResultMessage.session_id` (available in both SDKs) to resume later. The TypeScript SDK also exposes it as a direct field on the init `SystemMessage`; in Python it's nested in `SystemMessage.data`.
When you resume, the full context from previous turns is restored: files that were read, analysis that was performed, and actions that were taken. You can also fork a session to branch into a different approach without modifying the original.
See [Session management](/docs/en/agent-sdk/sessions) for the full guide on resume, continue, and fork patterns. To resume sessions across stateless containers or serverless hosts, pass a [`session_store` / `sessionStore` adapter](/docs/en/agent-sdk/session-storage) so the SDK mirrors transcripts to your own backend and another host can resume them. The Claude Code subprocess still writes to local disk first. See [Dual-write architecture](/docs/en/agent-sdk/session-storage#dual-write-architecture) for which copy outlives a fresh session versus a run resumed from the store, and how to keep the local copy ephemeral.
<Note>
In Python, `ClaudeSDKClient` handles session IDs automatically across multiple calls. See the [Python SDK reference](/docs/en/agent-sdk/python#choosing-between-query-and-claudesdkclient) for details.
</Note>
## Handle the result
When the loop ends, the `ResultMessage` tells you what happened and gives you the output. The `subtype` field (available in both SDKs) is the primary way to check termination state.
| Result subtype | What happened | `result` field available? |
| :------------------------------------ | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :-----------------------: |
Cut at 300 lines. The page has the rest.
agent-sdk/claude-code-features First recorded · 311 lines, first recorded
# Use Claude Code features in the SDK ## Control filesystem settings with settingSources ### What settingSources does not control ## Project instructions (CLAUDE.md and rules) ### CLAUDE.md load locations ## Skills ## Hooks ### When to use which hook type ## Choose the right feature ## Related resources
The first capture of this source. The page was already there, and this is what it said.
# Use Claude Code features in the SDK
> Load project instructions, skills, hooks, and other Claude Code features into your SDK agents.
The Agent SDK is built on the same foundation as Claude Code, which means your SDK agents have access to the same filesystem-based features: project instructions (`CLAUDE.md` and rules), skills, hooks, and more.
When you omit `settingSources`, `query()` reads the same filesystem settings as the Claude Code CLI: user, project, and local settings, CLAUDE.md files, and `.claude/` skills, agents, and commands. To run without these, pass `settingSources: []`, which limits the agent to what you configure programmatically. Managed policy settings and the global `~/.claude.json` config are read regardless of this option. See [What settingSources does not control](#what-settingsources-does-not-control).
For a conceptual overview of what each feature does and when to use it, see [Extend Claude Code](/docs/en/features-overview).
## Control filesystem settings with settingSources
The setting sources option ([`setting_sources`](/docs/en/agent-sdk/python#claudeagentoptions) in Python, [`settingSources`](/docs/en/agent-sdk/typescript#settingsource) in TypeScript) controls which filesystem-based settings the SDK loads. Pass an explicit list to opt in to specific sources, or pass an empty array to disable user, project, and local settings.
This example loads both user-level and project-level settings by setting `settingSources` to `["user", "project"]`:
<CodeGroup>
```python Python theme={null}
from claude_agent_sdk import query, ClaudeAgentOptions, AssistantMessage, ResultMessage
import asyncio
async def main():
async for message in query(
prompt="Help me refactor the auth module",
options=ClaudeAgentOptions(
# "user" loads from ~/.claude/, "project" loads from ./.claude/ in cwd.
# Together they give the agent access to CLAUDE.md, skills, hooks, and
# permissions from both locations.
setting_sources=["user", "project"],
allowed_tools=["Read", "Edit", "Bash"],
),
):
if isinstance(message, AssistantMessage):
for block in message.content:
if hasattr(block, "text"):
print(block.text)
if isinstance(message, ResultMessage) and message.subtype == "success":
print(f"\nResult: {message.result}")
asyncio.run(main())
```
```typescript TypeScript theme={null}
import { query } from "@anthropic-ai/claude-agent-sdk";
for await (const message of query({
prompt: "Help me refactor the auth module",
options: {
// "user" loads from ~/.claude/, "project" loads from ./.claude/ in cwd.
// Together they give the agent access to CLAUDE.md, skills, hooks, and
// permissions from both locations.
settingSources: ["user", "project"],
allowedTools: ["Read", "Edit", "Bash"]
}
})) {
if (message.type === "assistant") {
for (const block of message.message.content) {
if (block.type === "text") console.log(block.text);
}
}
if (message.type === "result" && message.subtype === "success") {
console.log(`\nResult: ${message.result}`);
}
}
```
</CodeGroup>
When this runs, the assistant's response prints to stdout, followed by a final result line once the run completes.
Each source loads settings from a specific location, where `<cwd>` is the working directory you pass via the `cwd` option, or the process's current directory if unset. For the full type definition, see [`SettingSource`](/docs/en/agent-sdk/typescript#settingsource) (TypeScript) or [`SettingSource`](/docs/en/agent-sdk/python#settingsource) (Python).
| Source | What it loads | Location |
| :---------- | :---------------------------------------------------------------------------------------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `"project"` | Project CLAUDE.md, `.claude/rules/*.md`, project skills, project hooks, project `settings.json` | `<cwd>/.claude/` for `settings.json` and hooks; `<cwd>` and every parent directory for CLAUDE.md and rules; `<cwd>` and every parent directory up to the repository root for skills |
| `"user"` | User CLAUDE.md, `~/.claude/rules/*.md`, user skills, user settings | `~/.claude/` |
| `"local"` | CLAUDE.local.md, `.claude/settings.local.json` | `<cwd>/.claude/` for `settings.local.json`; `<cwd>` and every parent directory for CLAUDE.local.md |
Omitting `settingSources` is equivalent to `["user", "project", "local"]`.
The `cwd` option determines where the SDK looks for project-level inputs. Project `settings.json` and hooks load only from `<cwd>/.claude/` with no parent-directory fallback.
### What settingSources does not control
`settingSources` covers user, project, and local settings. A few inputs are read regardless of its value:
| Input | Behavior | To disable |
| :----------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Managed policy settings | Endpoint-managed policy, such as an MDM plist, registry policy, or managed settings file, loads from the host. [Server-managed settings](/docs/en/server-managed-settings) are fetched on an [eligible configuration](/docs/en/server-managed-settings#platform-availability) when the session authenticates with an organization OAuth login or a directly configured API key | Endpoint policy: remove the managed settings file, plist, or registry policy from the host. Server-managed settings: controlled by your org admin; cannot be disabled from the SDK |
| `~/.claude.json` global config | Always read | Relocate with `CLAUDE_CONFIG_DIR` in `env` |
| Auto memory at `~/.claude/projects/<project>/memory/` | Loaded into the system prompt at session start. The agent writes new memories there with the standard `Write` and `Edit` tools rather than a dedicated memory tool, so those tools must be enabled for the agent to save memories | Set `autoMemoryEnabled: false` in settings, or `CLAUDE_CODE_DISABLE_AUTO_MEMORY=1` in `env` |
| [claude.ai MCP connectors](/docs/en/mcp#use-mcp-servers-from-claude-ai) | Loaded when the session authenticates with your claude.ai login. Not loaded when `CLAUDE_CODE_OAUTH_TOKEN` holds a token from [`claude setup-token`](/docs/en/authentication#generate-a-long-lived-token), which can only make model requests. Passing `mcpServers: {}` does not suppress the connectors | Set `strictMcpConfig: true`, [`disableClaudeAiConnectors: true`](/docs/en/mcp#disable-claude-ai-connectors) in settings, or `ENABLE_CLAUDEAI_MCP_SERVERS=false` in `env` |
<Warning>
Do not rely on default `query()` options for multi-tenant isolation. Because the inputs above are read regardless of `settingSources`, an SDK process can pick up host-level configuration and per-directory memory. For multi-tenant deployments, run each tenant in its own filesystem and set `settingSources: []` plus `CLAUDE_CODE_DISABLE_AUTO_MEMORY=1` in `env`. [Server-managed settings](/docs/en/server-managed-settings) are fetched when the process authenticates with an organization credential; filesystem isolation does not remove them. See [Secure deployment](/docs/en/agent-sdk/secure-deployment).
</Warning>
## Project instructions (CLAUDE.md and rules)
`CLAUDE.md` files and `.claude/rules/*.md` files give your agent persistent context about your project: coding conventions, build commands, architecture decisions, and instructions. When `settingSources` includes `"project"` (as in the example above), the SDK loads these files into context at session start. The agent then follows your project conventions without you repeating them in every prompt.
### CLAUDE.md load locations
| Level | Location | When loaded |
| :-------------------- | :---------------------------------------------------------------------------- | :-------------------------------------------------------------------------------------------------- |
| Project (root) | `<cwd>/CLAUDE.md` or `<cwd>/.claude/CLAUDE.md` | `settingSources` includes `"project"` |
| Project rules | `<cwd>/.claude/rules/*.md` and `.claude/rules/*.md` in every parent directory | `settingSources` includes `"project"` |
| Project (parent dirs) | `CLAUDE.md` files in directories above `cwd` | `settingSources` includes `"project"`, loaded at session start |
| Project (child dirs) | `CLAUDE.md` files in subdirectories of `cwd` | `settingSources` includes `"project"`, loaded on demand when the agent reads a file in that subtree |
| Local | `<cwd>/CLAUDE.local.md` and `CLAUDE.local.md` in every parent directory | `settingSources` includes `"local"` |
| User | `~/.claude/CLAUDE.md` | `settingSources` includes `"user"` |
| User rules | `~/.claude/rules/*.md` | `settingSources` includes `"user"` |
All levels are additive: if both project and user CLAUDE.md files exist, the agent sees both. There is no hard precedence rule between levels; if instructions conflict, the outcome depends on how Claude interprets them. Write non-conflicting rules, or state precedence explicitly in the more specific file ("These project instructions override any conflicting user-level defaults").
<Tip>
You can also inject context directly via `systemPrompt` without using CLAUDE.md files. See [Modify system prompts](/docs/en/agent-sdk/modifying-system-prompts). Use CLAUDE.md when you want the same context shared between interactive Claude Code sessions and your SDK agents.
</Tip>
For how to structure and organize CLAUDE.md content, see [Manage Claude's memory](/docs/en/memory).
## Skills
Skills are markdown files that give your agent specialized knowledge and invocable workflows. Unlike `CLAUDE.md` (which loads every session), skills load on demand. The agent receives skill descriptions at startup and loads the full content when relevant.
Skills are discovered from the filesystem through `settingSources`. When the `skills` option on `query()` is omitted, discovered user and project skills are enabled and the Skill tool is available, matching CLI behavior. To control which skills are enabled, pass `skills` as `"all"`, a list of skill names, or `[]` to disable all. When `skills` is set, the SDK adds the Skill tool to `allowedTools` automatically. If you also pass an explicit `tools` list, include `"Skill"` in that list so Claude can invoke skills.
<CodeGroup>
```python Python theme={null}
from claude_agent_sdk import query, ClaudeAgentOptions, ResultMessage
import asyncio
# Skills in .claude/skills/ are discovered automatically
# when settingSources includes "project"
async def main():
async for message in query(
prompt="Review this PR using our code review checklist",
options=ClaudeAgentOptions(
setting_sources=["user", "project"],
skills="all",
allowed_tools=["Read", "Grep", "Glob"],
),
):
if isinstance(message, ResultMessage) and message.subtype == "success":
print(message.result)
asyncio.run(main())
```
```typescript TypeScript theme={null}
import { query } from "@anthropic-ai/claude-agent-sdk";
// Skills in .claude/skills/ are discovered automatically
// when settingSources includes "project"
for await (const message of query({
prompt: "Review this PR using our code review checklist",
options: {
settingSources: ["user", "project"],
skills: "all",
allowedTools: ["Read", "Grep", "Glob"]
}
})) {
if (message.type === "result" && message.subtype === "success") {
console.log(message.result);
}
}
```
</CodeGroup>
<Note>
Skills must be created as filesystem artifacts (`.claude/skills/<name>/SKILL.md`). The SDK does not have a programmatic API for registering skills. See [Agent Skills in the SDK](/docs/en/agent-sdk/skills) for full details.
</Note>
## Hooks
The SDK supports two ways to define hooks, and they run side by side:
* **Filesystem hooks:** shell commands defined in `settings.json`, loaded when `settingSources` includes the relevant source. These are the same hooks you'd configure for [interactive Claude Code sessions](/docs/en/hooks-guide).
* **Programmatic hooks:** callback functions passed directly to `query()`. These run in your application process and can return structured decisions. See [Control execution with hooks](/docs/en/agent-sdk/hooks).
Both types execute during the same hook lifecycle. If you already have hooks in your project's `.claude/settings.json` and you set `settingSources: ["project"]`, those hooks run automatically in the SDK with no extra configuration.
Hook callbacks receive the tool input and return a decision dict. Returning `{}` means allow the tool to proceed. To block execution, return a `hookSpecificOutput` object with `permissionDecision: "deny"` and a `permissionDecisionReason`. The reason is sent to Claude as the tool result. See the [hooks guide](/docs/en/agent-sdk/hooks) for the full callback signature and return types.
<CodeGroup>
```python Python theme={null}
from claude_agent_sdk import query, ClaudeAgentOptions, HookMatcher, ResultMessage
import asyncio
# PreToolUse hook callback. Positional args:
# input_data: HookInput dict with tool_name, tool_input, hook_event_name
# tool_use_id: str | None, the ID of the tool call being intercepted
# context: HookContext, reserved for future abort-signal support
async def audit_bash(input_data, tool_use_id, context):
command = input_data.get("tool_input", {}).get("command", "")
if "rm -rf" in command:
return {
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"permissionDecision": "deny",
"permissionDecisionReason": "Destructive command blocked",
}
}
return {} # Empty dict: allow the tool to proceed
# Filesystem hooks from .claude/settings.json run automatically
# when settingSources loads them. You can also add programmatic hooks:
async def main():
async for message in query(
prompt="Refactor the auth module",
options=ClaudeAgentOptions(
setting_sources=["project"], # Loads hooks from .claude/settings.json
hooks={
"PreToolUse": [
HookMatcher(matcher="Bash", hooks=[audit_bash]),
]
},
),
):
if isinstance(message, ResultMessage) and message.subtype == "success":
print(message.result)
asyncio.run(main())
```
```typescript TypeScript theme={null}
import { query, type HookInput, type HookJSONOutput } from "@anthropic-ai/claude-agent-sdk";
// PreToolUse hook callback. HookInput is a discriminated union on
// hook_event_name, so narrowing on it gives TypeScript the right
// tool_input shape for this event.
const auditBash = async (input: HookInput): Promise<HookJSONOutput> => {
if (input.hook_event_name !== "PreToolUse") return {};
const toolInput = input.tool_input as { command?: string };
if (toolInput.command?.includes("rm -rf")) {
return {
hookSpecificOutput: {
hookEventName: "PreToolUse",
permissionDecision: "deny",
permissionDecisionReason: "Destructive command blocked",
},
};
}
return {}; // Empty object: allow the tool to proceed
};
// Filesystem hooks from .claude/settings.json run automatically
// when settingSources loads them. You can also add programmatic hooks:
for await (const message of query({
prompt: "Refactor the auth module",
options: {
settingSources: ["project"], // Loads hooks from .claude/settings.json
hooks: {
PreToolUse: [{ matcher: "Bash", hooks: [auditBash] }]
}
}
})) {
if (message.type === "result" && message.subtype === "success") {
console.log(message.result);
}
}
```
</CodeGroup>
### When to use which hook type
| Hook type | Best for |
| :---------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Filesystem** (`settings.json`) | Sharing hooks between CLI and SDK sessions. Supports `"command"` (shell scripts), `"http"` (POST to an endpoint), `"mcp_tool"` (call a connected MCP server's tool), `"prompt"` (LLM evaluates a prompt), and `"agent"` (spawns a verifier agent). These fire in the main agent and any subagents it spawns. |
| **Programmatic** (callbacks in `query()`) | Application-specific logic, structured decisions, and in-process integration. These also fire inside subagents. The hook input, the callback's first argument, carries `agent_id` and `agent_type` fields that identify which agent fired the hook. |
<Note>
The TypeScript SDK supports additional hook events beyond Python, including `SessionStart`, `SessionEnd`, `TeammateIdle`, and `TaskCompleted`. See the [hooks guide](/docs/en/agent-sdk/hooks) for the full event compatibility table.
</Note>
For full details on programmatic hooks, see [Control execution with hooks](/docs/en/agent-sdk/hooks). For filesystem hook syntax, see [Hooks](/docs/en/hooks).
## Choose the right feature
The Agent SDK gives you access to several ways to extend your agent's behavior. If you're unsure which to use, this table maps common goals to the right approach.
| You want to... | Use | SDK surface |
| :------------------------------------------------------------------------------------------------ | :-------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Set project conventions your agent always follows | [CLAUDE.md](/docs/en/memory) | `settingSources: ["project"]` loads it automatically |
| Give the agent reference material it loads when relevant | [Skills](/docs/en/agent-sdk/skills) | `settingSources` + `skills` option |
| Run a reusable workflow (deploy, review, release) | [User-invocable skills](/docs/en/agent-sdk/skills) | `settingSources` + `skills` option |
| Delegate an isolated subtask to a fresh context (research, review) | [Subagents](/docs/en/agent-sdk/subagents) | `agents` parameter + `allowedTools: ["Agent"]` |
| Coordinate multiple Claude Code instances with shared task lists and direct inter-agent messaging | [Agent teams](/docs/en/agent-teams) | Not directly configured via SDK options. Agent teams are a CLI feature where one session acts as the team lead, coordinating work across independent teammates |
| Run deterministic logic on tool calls (audit, block, transform) | [Hooks](/docs/en/agent-sdk/hooks) | `hooks` parameter with callbacks, or shell scripts loaded via `settingSources` |
| Give Claude structured tool access to an external service | [MCP](/docs/en/agent-sdk/mcp) | `mcpServers` parameter |
<Tip>
**Subagents versus agent teams:** Subagents are ephemeral and isolated: fresh conversation, one task, summary returned to parent. Agent teams coordinate multiple independent Claude Code instances that share a task list and message each other directly. Agent teams are a CLI feature. See [What subagents inherit](/docs/en/agent-sdk/subagents#what-subagents-inherit) and the [agent teams comparison](/docs/en/agent-teams#compare-with-subagents) for details.
</Tip>
Cut at 300 lines. The page has the rest.
agent-sdk/cost-tracking First recorded · 376 lines, first recorded
# Track cost and usage ## Understand token usage ## Track costs in streaming input mode ## Get the total cost of a query ## Track per-step and per-model usage ### Track per-step usage ### Break down usage per model ## Accumulate costs across multiple calls ## Handle errors, caching, and output token counts ### Read output tokens from the result message ### Track costs on failed conversations ### Recover totals after a session crash ### Track cache tokens ### Extend the prompt cache TTL to one hour ## Related documentation
The first capture of this source. The page was already there, and this is what it said.
# Track cost and usage
> Learn how to track token usage, estimate costs, and configure prompt caching with the Claude Agent SDK.
The Claude Agent SDK provides detailed token usage information for each interaction with Claude. This guide explains how to properly track usage and understand cost reporting, especially when dealing with parallel tool uses and multi-step conversations.
For complete API documentation, see the [TypeScript SDK reference](/docs/en/agent-sdk/typescript) and [Python SDK reference](/docs/en/agent-sdk/python).
<Warning>
The `total_cost_usd` and `costUSD` fields are client-side estimates, not authoritative billing data. The SDK computes them locally from a price table bundled at build time, so they can drift from what you are actually billed when:
* pricing changes
* the installed SDK version does not recognize a model
* billing rules apply that the client cannot model
Use these fields for development insight and approximate budgeting. For authoritative billing, use the [Usage and Cost API](https://platform.claude.com/docs/en/build-with-claude/usage-cost-api) or the Usage page in the [Claude Console](https://platform.claude.com/usage). Do not bill end users or trigger financial decisions from these fields.
</Warning>
## Understand token usage
The TypeScript and Python SDKs expose the same usage data with different field names:
* **TypeScript** provides per-step token breakdowns on each assistant message (`message.message.id`, `message.message.usage`), per-model cost via `modelUsage` on the result message, and a cumulative total on the result message.
* **Python** provides per-step token breakdowns on each assistant message as `message.usage` and `message.message_id`, per-model cost via `model_usage` on the result message, and the cumulative total on the result message as `total_cost_usd`.
Both SDKs use the same underlying cost model and expose the same granularity. The difference is in field naming and where per-step usage is nested.
Cost tracking depends on understanding how the SDK scopes usage data:
* **`query()` call:** one invocation of the SDK's `query()` function. A single call can involve multiple steps: Claude responds, uses tools, gets results, and responds again. Each call produces one [`result`](/docs/en/agent-sdk/typescript#sdkresultmessage) message at the end, except in [streaming input mode](/docs/en/agent-sdk/streaming-vs-single-mode), where one `query()` call carries multiple user turns and each turn emits its own `result` message.
* **Step:** a single request/response cycle within a `query()` call. Each step produces assistant messages with token usage.
* **Session:** a series of `query()` calls linked by a session ID (using the `resume` option). Each `query()` call within a session reports its own cost independently.
The following diagram shows the message stream from a single `query()` call, with token usage reported at each step and the cumulative estimate at the end:
<img src="https://mintcdn.com/claude-code/ikqp3_70mqIahteV/images/agent-sdk/message-usage-flow.svg?fit=max&auto=format&n=ikqp3_70mqIahteV&q=85&s=68497aee338e01cc745323af7aea378e" className="dark:hidden" alt="Diagram showing a query producing two steps of messages. Step 1 has four assistant messages sharing the same ID and usage (count once), Step 2 has one assistant message with a new ID, and the final result message shows the estimated total_cost_usd." width="760" height="520" data-path="images/agent-sdk/message-usage-flow.svg" />
<img src="https://mintcdn.com/claude-code/_xqph1dUOslCOwsj/images/agent-sdk/message-usage-flow-dark.svg?fit=max&auto=format&n=_xqph1dUOslCOwsj&q=85&s=8ea95085abc0a6b7f55ecef498bd4d14" className="hidden dark:block" alt="Diagram showing a query producing two steps of messages. Step 1 has four assistant messages sharing the same ID and usage (count once), Step 2 has one assistant message with a new ID, and the final result message shows the estimated total_cost_usd." width="760" height="520" data-path="images/agent-sdk/message-usage-flow-dark.svg" />
<Steps>
<Step title="Each step produces assistant messages">
When Claude responds, it sends one or more assistant messages. In TypeScript, each assistant message contains a nested `BetaMessage` (accessed via `message.message`) with an `id` and a [`usage`](https://platform.claude.com/docs/en/api/messages) object with token counts (`input_tokens`, `output_tokens`). In Python, the `AssistantMessage` dataclass exposes the same data directly via `message.usage` and `message.message_id`. When Claude uses multiple tools in one turn, all messages in that turn share the same ID, so deduplicate by ID to avoid double-counting.
</Step>
<Step title="The result message provides the cumulative estimate">
When the `query()` call completes, the SDK emits a result message with `total_cost_usd` and cumulative `usage`, typed as [`SDKResultMessage`](/docs/en/agent-sdk/typescript#sdkresultmessage) in TypeScript and [`ResultMessage`](/docs/en/agent-sdk/python#resultmessage) in Python. If you make multiple `query()` calls, for example in a multi-turn session, each result reflects only the cost of that individual call. If you only need the estimated total, you can ignore the per-step usage and read this single value.
In streaming input mode, each turn emits its own result message. See [Track costs in streaming input mode](#track-costs-in-streaming-input-mode) for how to read call totals in that mode.
</Step>
</Steps>
## Track costs in streaming input mode
In [streaming input mode](/docs/en/agent-sdk/streaming-vs-single-mode), one `query()` call carries multiple user turns and each turn emits its own result message. The result fields differ in scope:
* **`usage`**: covers only that turn, and within it only the main agent loop, not any subagents it ran.
* **`total_cost_usd` and `modelUsage`, or `model_usage` in Python**: carry the running total for the whole call so far.
In a call where your app never sends `/clear`, `/reset`, or `/new`, read the latest result for call totals rather than summing across results.
The running totals start over each time your app sends one of those three commands, and inside a `query()` call nothing else resets them. Three results matter for your accounting:
* **The `/clear` turn's own result**: covers only what has run since the reset, and carries a new `session_id`.
* **Every later result**: keeps counting from that reset.
* **The last result before each `/clear`**: holds the total for the turns since the previous reset.
To total the whole call, add the last result from before each `/clear` to the call's final result. Every other result, including the `/clear` turn's own, is superseded by a later one.
In TypeScript, the SDK also emits an [`SDKConversationResetMessage`](/docs/en/agent-sdk/typescript#sdkconversationresetmessage) at each reset, so you can detect resets from the stream. In Python, the SDK likewise emits a `ConversationResetMessage`. Before Python SDK v0.2.137, the Python iterator dropped that message, so on those versions count the resets yourself from the `/clear` turns your app sends.
`maxBudgetUsd`, or `max_budget_usd` in Python, is compared against the same running total, so a `/clear` also starts the budget over.
## Get the total cost of a query
The result message, typed as [`SDKResultMessage`](/docs/en/agent-sdk/typescript#sdkresultmessage) in TypeScript and [`ResultMessage`](/docs/en/agent-sdk/python#resultmessage) in Python, marks the end of the agent loop for a `query()` call. It includes `total_cost_usd`, the cumulative estimated cost across all steps in that call. In Python the field is typed as optional, so check that it isn't `None` before you read it. Success and error results both carry it, though the final result of a [session crash](#recover-totals-after-a-session-crash) may carry it zeroed.
If you use sessions to make multiple `query()` calls, each result reflects only the cost of that individual call. In streaming input mode, read call totals as described in [Track costs in streaming input mode](#track-costs-in-streaming-input-mode).
The three result-level fields differ in what they count when the agent spawns [subagents](/docs/en/agent-sdk/subagents). Use `modelUsage`, or `model_usage` in Python, for whole-tree token accounting; the `usage` field undercounts as soon as nesting occurs.
| Field | Subagent activity |
| ---------------------------- | ------------------------------------------------------------------------------------------------- |
| `usage` | Excluded. Counts only the top-level agent loop, so tokens consumed inside subagents are not added |
| `total_cost_usd` | Included. Counts subagent requests alongside the top-level loop |
| `modelUsage` / `model_usage` | Included. Counts subagent requests alongside the top-level loop, broken down by model |
The following examples iterate over the message stream from a `query()` call and print the total cost when the `result` message arrives:
<CodeGroup>
```typescript TypeScript theme={null}
import { query } from "@anthropic-ai/claude-agent-sdk";
try {
for await (const message of query({ prompt: "Summarize this project" })) {
if (message.type === "result") {
console.log(`Total cost: $${message.total_cost_usd}`);
}
}
} catch (error) {
// A single-shot query() throws after yielding an error result. If the
// failure was an error result, it still carried total_cost_usd and the
// branch above has already run; connection or process failures yield
// no result message.
console.error(`Session ended with an error: ${error}`);
}
```
```python Python theme={null}
from claude_agent_sdk import query, ResultMessage
import asyncio
async def main():
try:
async for message in query(prompt="Summarize this project"):
if isinstance(message, ResultMessage):
print(f"Total cost: ${message.total_cost_usd or 0}")
except Exception as error:
# A single-shot query() raises after yielding an error result. If the
# failure was an error result, the branch above has already run;
# connection or process failures yield no result message.
print(f"Session ended with an error: {error}")
asyncio.run(main())
```
</CodeGroup>
To bound how much subagents can add to `total_cost_usd`, set the [depth, concurrency, and spend limits](/docs/en/agent-sdk/subagents#cap-subagent-depth-concurrency-and-spend) on the query.
## Track per-step and per-model usage
The examples in this section use TypeScript field names. In Python, the equivalent fields are [`AssistantMessage.usage`](/docs/en/agent-sdk/python#assistantmessage) and `AssistantMessage.message_id` for per-step usage, and [`ResultMessage.model_usage`](/docs/en/agent-sdk/python#resultmessage) for per-model breakdowns.
### Track per-step usage
Each assistant message contains a nested `BetaMessage` (accessed via `message.message`) with an `id` and `usage` object with token counts. When Claude uses tools in parallel, multiple messages share the same `id` with identical usage data. Track which IDs you've already counted and skip duplicates to avoid inflated totals.
<Warning>
The deduplicated per-step values are accurate for input and cache tokens. Per-step `output_tokens` is a placeholder, so [read output tokens from the result message](#read-output-tokens-from-the-result-message).
</Warning>
The following example accumulates input tokens across all steps, counting each unique main-loop message ID only once and skipping subagent messages, and reads the output total from the result message, which covers the main loop:
```typescript theme={null}
import { query } from "@anthropic-ai/claude-agent-sdk";
const seenIds = new Set<string>();
let totalInputTokens = 0;
let resultOutputTokens = 0;
try {
for await (const message of query({ prompt: "Summarize this project" })) {
if (message.type === "assistant" && !message.parent_tool_use_id) {
const msgId = message.message.id;
// Parallel tool calls share the same ID, only count once
if (!seenIds.has(msgId)) {
seenIds.add(msgId);
totalInputTokens += message.message.usage.input_tokens;
}
}
if (message.type === "result") {
// Per-step output_tokens is a placeholder; the result message
// carries the accumulated output total.
resultOutputTokens = message.usage.output_tokens;
}
}
} catch (error) {
// A single-shot query() throws after yielding an error result, so the
// input total below still reflects the steps that ran before the failure.
console.error(`Session ended with an error: ${error}`);
}
console.log(`Steps: ${seenIds.size}`);
console.log(`Input tokens: ${totalInputTokens}`);
console.log(`Output tokens: ${resultOutputTokens}`);
```
### Break down usage per model
The result message includes [`modelUsage`](/docs/en/agent-sdk/typescript#modelusage), a map of model name to per-model token counts and cost. This is useful when you run multiple models (for example, Haiku for subagents and Opus for the main agent) and want to see where tokens are going.
The following example runs a query and prints the cost and token breakdown for each model used:
```typescript theme={null}
import { query } from "@anthropic-ai/claude-agent-sdk";
try {
for await (const message of query({ prompt: "Summarize this project" })) {
if (message.type !== "result") continue;
for (const [modelName, usage] of Object.entries(message.modelUsage)) {
console.log(`${modelName}: $${usage.costUSD.toFixed(4)}`);
console.log(` Input tokens: ${usage.inputTokens}`);
console.log(` Output tokens: ${usage.outputTokens}`);
console.log(` Cache read: ${usage.cacheReadInputTokens}`);
console.log(` Cache creation: ${usage.cacheCreationInputTokens}`);
}
}
} catch (error) {
// A single-shot query() throws after yielding an error result. If the
// failure was an error result, the per-model breakdown above has already
// printed; connection or process failures yield no result message.
console.error(`Session ended with an error: ${error}`);
}
```
## Accumulate costs across multiple calls
Each `query()` call returns its own `total_cost_usd`. The SDK doesn't provide a session-level total, so if your application makes multiple `query()` calls, for example in a multi-turn session or across different users, accumulate the totals yourself. In streaming input mode, read each call's total as described in [Track costs in streaming input mode](#track-costs-in-streaming-input-mode). For a call that ended in a crash, see [Recover totals after a session crash](#recover-totals-after-a-session-crash).
The following examples run two `query()` calls sequentially, add each call's `total_cost_usd` to a running total, and print both the per-call and combined cost:
<CodeGroup>
```typescript TypeScript theme={null}
import { query } from "@anthropic-ai/claude-agent-sdk";
// Track cumulative cost across multiple query() calls
let totalSpend = 0;
const prompts = [
"Read the files in src/ and summarize the architecture",
"List all exported functions in src/auth.ts"
];
for (const prompt of prompts) {
try {
for await (const message of query({ prompt })) {
if (message.type === "result") {
totalSpend += message.total_cost_usd;
console.log(`This call: $${message.total_cost_usd}`);
}
}
} catch (error) {
// A single-shot query() throws after yielding an error result. If the
// failure was an error result, this call's cost was already counted;
// connection or process failures yield no result message. Continue
// with the next prompt.
console.error(`Call failed: ${error}`);
}
}
console.log(`Total spend: $${totalSpend.toFixed(4)}`);
```
```python Python theme={null}
from claude_agent_sdk import query, ResultMessage
import asyncio
async def main():
# Track cumulative cost across multiple query() calls
total_spend = 0.0
prompts = [
"Read the files in src/ and summarize the architecture",
"List all exported functions in src/auth.ts",
]
for prompt in prompts:
try:
async for message in query(prompt=prompt):
if isinstance(message, ResultMessage):
cost = message.total_cost_usd or 0
total_spend += cost
print(f"This call: ${cost}")
except Exception as error:
# A single-shot query() raises after yielding an error result. If
# the failure was an error result, this call's cost was already
# counted; connection or process failures yield no result message.
# Continue with the next prompt.
print(f"Call failed: {error}")
print(f"Total spend: ${total_spend:.4f}")
asyncio.run(main())
```
</CodeGroup>
## Handle errors, caching, and output token counts
For accurate cost tracking, account for the placeholder output count on assistant messages, the tokens a failed conversation consumed, and cache token pricing.
### Read output tokens from the result message
Claude Code builds each assistant message from the usage the API reported when the response began, so the message's `output_tokens` is only the count the API had reported at `message_start`, before the response was generated. One API response can produce several assistant messages, and every one of them carries that same placeholder.
The API reports the real output count at the end of the response, and Claude Code adds it to the result message. Read output tokens from the result's `usage`, or from `modelUsage` for a per-model breakdown.
To watch a response's output count grow while it streams, set `includePartialMessages`, or `include_partial_messages` in Python, and read `usage` from each `message_delta` stream event, typed as [`SDKPartialAssistantMessage`](/docs/en/agent-sdk/typescript#sdkpartialassistantmessage) in TypeScript and [`StreamEvent`](/docs/en/agent-sdk/python#streamevent) in Python.
### Track costs on failed conversations
Both success and error result messages include `usage` and `total_cost_usd`; in Python both fields are typed as optional, so check that they aren't `None` before you read them.
If a conversation fails midway, you still consumed tokens up to the point of failure. Read cost data from every result message, whether its `subtype` is `success` or one of the error subtypes. On some error results, `usage` reports less than the call spent:
* **`error_during_execution` after a [session crash](#recover-totals-after-a-session-crash)**: every cost field may be zeroed.
Cut at 300 lines. The page has the rest.
agent-sdk/custom-tools First recorded · 850 lines, first recorded
# Give Claude custom tools ## Quick reference ## Create a custom tool ### Weather tool example ### Call a custom tool ### Add more tools ### Add tool annotations ## Control tool access ### Configure allowed tools ## Handle errors ## Return images and resources ### Images ### Resources ## Return structured data ## Example: unit converter ## Next steps ## Related documentation
The first capture of this source. The page was already there, and this is what it said.
# Give Claude custom tools
> Define custom tools with the Claude Agent SDK's in-process MCP server so Claude can call your functions, hit your APIs, and perform domain-specific operations.
Custom tools extend the Agent SDK by letting you define your own functions that Claude can call during a conversation. Using the SDK's in-process MCP server, you can give Claude access to databases, external APIs, domain-specific logic, or any other capability your application needs.
## Quick reference
| If you want to... | Do this |
| :------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| Define a tool | Use [`@tool`](/docs/en/agent-sdk/python#tool) (Python) or [`tool()`](/docs/en/agent-sdk/typescript#tool) (TypeScript) with a name, description, schema, and handler. See [Create a custom tool](#create-a-custom-tool). |
| Register a tool with Claude | Wrap in `create_sdk_mcp_server` / `createSdkMcpServer` and pass to `mcpServers` in `query()`. See [Call a custom tool](#call-a-custom-tool). |
| Pre-approve a tool | Add to your allowed tools. See [Configure allowed tools](#configure-allowed-tools). |
| Remove a built-in tool from Claude's context | Pass a `tools` array listing only the built-ins you want. See [Configure allowed tools](#configure-allowed-tools). |
| Let Claude call tools in parallel | Set `readOnlyHint: true` on tools with no side effects. See [Add tool annotations](#add-tool-annotations). |
| Control the error message Claude reads | Return `isError: true` to compose the message instead of surfacing the raw exception. See [Handle errors](#handle-errors). |
| Return images or files | Use `image` or `resource` blocks in the content array. See [Return images and resources](#return-images-and-resources). |
| Return a machine-readable JSON result | Set `structuredContent` on the result. See [Return structured data](#return-structured-data). |
| Scale to many tools | Use [tool search](/docs/en/agent-sdk/tool-search) to load tools on demand. |
## Create a custom tool
A tool is defined by four parts, passed as arguments to the [`tool()`](/docs/en/agent-sdk/typescript#tool) helper in TypeScript or the [`@tool`](/docs/en/agent-sdk/python#tool) decorator in Python:
* **Name:** a unique identifier Claude uses to call the tool.
* **Description:** what the tool does. Claude reads this to decide when to call it.
* **Input schema:** the arguments Claude must provide. In TypeScript this is always a [Zod schema](https://zod.dev/), and the handler's `args` are typed from it automatically. In Python this is a dict mapping names to types, like `{"latitude": float}`, which the SDK converts to JSON Schema for you. The Python decorator also accepts a full [JSON Schema](https://json-schema.org/understanding-json-schema/about) dict directly when you need enums, ranges, optional fields, or nested objects.
* **Handler:** the async function that runs when Claude calls the tool. It receives the validated arguments and must return an object with:
* `content` (required): an array of result blocks, each with a `type` of `"text"`, `"image"`, `"audio"`, `"resource"`, or `"resource_link"`. See [Return images and resources](#return-images-and-resources) for non-text blocks.
* `structuredContent` (optional): a JSON object holding the result as machine-readable data, returned alongside `content`. See [Return structured data](#return-structured-data).
* `isError` (optional): set to `true` to signal a tool failure so Claude can react to it. See [Handle errors](#handle-errors).
After defining a tool, wrap it in a server with [`createSdkMcpServer`](/docs/en/agent-sdk/typescript#createsdkmcpserver) (TypeScript) or [`create_sdk_mcp_server`](/docs/en/agent-sdk/python#create_sdk_mcp_server) (Python). The server runs in-process inside your application, not as a separate process.
### Weather tool example
This example defines a `get_temperature` tool and wraps it in an MCP server. It only sets up the tool; to pass it to `query` and run it, see [Call a custom tool](#call-a-custom-tool) below.
<CodeGroup>
```python Python theme={null}
from typing import Any
import httpx
from claude_agent_sdk import tool, create_sdk_mcp_server
# Define a tool: name, description, input schema, handler
@tool(
"get_temperature",
"Get the current temperature at a location",
{"latitude": float, "longitude": float},
)
async def get_temperature(args: dict[str, Any]) -> dict[str, Any]:
async with httpx.AsyncClient() as client:
response = await client.get(
"https://api.open-meteo.com/v1/forecast",
params={
"latitude": args["latitude"],
"longitude": args["longitude"],
"current": "temperature_2m",
"temperature_unit": "fahrenheit",
},
)
data = response.json()
# Return a content array - Claude sees this as the tool result
return {
"content": [
{
"type": "text",
"text": f"Temperature: {data['current']['temperature_2m']}°F",
}
]
}
# Wrap the tool in an in-process MCP server
weather_server = create_sdk_mcp_server(
name="weather",
version="1.0.0",
tools=[get_temperature],
)
```
```typescript TypeScript theme={null}
import { tool, createSdkMcpServer } from "@anthropic-ai/claude-agent-sdk";
import { z } from "zod";
// Define a tool: name, description, input schema, handler
const getTemperature = tool(
"get_temperature",
"Get the current temperature at a location",
{
latitude: z.number().describe("Latitude coordinate"), // .describe() adds a field description Claude sees
longitude: z.number().describe("Longitude coordinate")
},
async (args) => {
// args is typed from the schema: { latitude: number; longitude: number }
const response = await fetch(
`https://api.open-meteo.com/v1/forecast?latitude=${args.latitude}&longitude=${args.longitude}¤t=temperature_2m&temperature_unit=fahrenheit`
);
const data: any = await response.json();
// Return a content array - Claude sees this as the tool result
return {
content: [{ type: "text", text: `Temperature: ${data.current.temperature_2m}°F` }]
};
}
);
// Wrap the tool in an in-process MCP server
const weatherServer = createSdkMcpServer({
name: "weather",
version: "1.0.0",
tools: [getTemperature]
});
```
</CodeGroup>
See the [`tool()`](/docs/en/agent-sdk/typescript#tool) TypeScript reference or the [`@tool`](/docs/en/agent-sdk/python#tool) Python reference for full parameter details, including JSON Schema input formats and return value structure.
<Tip>
To make a parameter optional: in TypeScript, add `.default()` to the Zod field. In Python, the dict schema treats every key as required, so leave the parameter out of the schema, mention it in the description string, and read it with `args.get()` in the handler. The [`get_precipitation_chance` tool below](#add-more-tools) shows both patterns.
</Tip>
### Call a custom tool
Pass the MCP server you created to `query` via the `mcpServers` option. The key in `mcpServers` becomes the `{server_name}` segment in each tool's fully qualified name: `mcp__{server_name}__{tool_name}`. List that name in `allowedTools` so the tool runs without a permission prompt.
These snippets reuse the `weatherServer` from the [example above](#weather-tool-example) to ask Claude what the weather is in a specific location.
<CodeGroup>
```python Python theme={null}
import asyncio
from claude_agent_sdk import query, ClaudeAgentOptions, ResultMessage
async def main():
options = ClaudeAgentOptions(
mcp_servers={"weather": weather_server},
allowed_tools=["mcp__weather__get_temperature"],
)
async for message in query(
prompt="What's the temperature in San Francisco?",
options=options,
):
# ResultMessage is the final message after all tool calls complete
if isinstance(message, ResultMessage) and message.subtype == "success":
print(message.result)
asyncio.run(main())
```
```typescript TypeScript theme={null}
import { query } from "@anthropic-ai/claude-agent-sdk";
for await (const message of query({
prompt: "What's the temperature in San Francisco?",
options: {
mcpServers: { weather: weatherServer },
allowedTools: ["mcp__weather__get_temperature"]
}
})) {
// "result" is the final message after all tool calls complete
if (message.type === "result" && message.subtype === "success") {
console.log(message.result);
}
}
```
</CodeGroup>
Combine this snippet with the tool and server definitions from the [weather tool example](#weather-tool-example) in one file, then run it with `python weather.py` for Python or `npx tsx weather.ts` for TypeScript. Claude calls `get_temperature` and the script prints a one-line answer with the current temperature in San Francisco.
### Add more tools
A server holds as many tools as you list in its `tools` array. With more than one tool on a server, you can list each one in `allowedTools` individually or use the wildcard `mcp__weather__*` to cover every tool the server exposes.
The example below defines a second tool, `get_precipitation_chance`, and replaces the `weatherServer` definition from the [weather tool example](#weather-tool-example) with one that lists both tools in the array.
<CodeGroup>
```python Python theme={null}
# Define a second tool for the same server
@tool(
"get_precipitation_chance",
"Get the hourly precipitation probability for a location. "
"Optionally pass 'hours' (1-24) to control how many hours to return.",
{"latitude": float, "longitude": float},
)
async def get_precipitation_chance(args: dict[str, Any]) -> dict[str, Any]:
# 'hours' isn't in the schema - read it with .get() to make it optional
hours = args.get("hours", 12)
async with httpx.AsyncClient() as client:
response = await client.get(
"https://api.open-meteo.com/v1/forecast",
params={
"latitude": args["latitude"],
"longitude": args["longitude"],
"hourly": "precipitation_probability",
"forecast_days": 1,
},
)
data = response.json()
chances = data["hourly"]["precipitation_probability"][:hours]
return {
"content": [
{
"type": "text",
"text": f"Next {hours} hours: {'%, '.join(map(str, chances))}%",
}
]
}
# Rebuild the server with both tools in the array
weather_server = create_sdk_mcp_server(
name="weather",
version="1.0.0",
tools=[get_temperature, get_precipitation_chance],
)
```
```typescript TypeScript theme={null}
// Define a second tool for the same server
const getPrecipitationChance = tool(
"get_precipitation_chance",
"Get the hourly precipitation probability for a location",
{
latitude: z.number(),
longitude: z.number(),
hours: z
.number()
.int()
.min(1)
.max(24)
.default(12) // .default() makes the parameter optional
.describe("How many hours of forecast to return")
},
async (args) => {
const response = await fetch(
`https://api.open-meteo.com/v1/forecast?latitude=${args.latitude}&longitude=${args.longitude}&hourly=precipitation_probability&forecast_days=1`
);
const data: any = await response.json();
const chances = data.hourly.precipitation_probability.slice(0, args.hours);
return {
content: [{ type: "text", text: `Next ${args.hours} hours: ${chances.join("%, ")}%` }]
};
}
);
// Rebuild the server with both tools in the array
const weatherServer = createSdkMcpServer({
name: "weather",
version: "1.0.0",
tools: [getTemperature, getPrecipitationChance]
});
```
</CodeGroup>
[Tool search](/docs/en/agent-sdk/tool-search) is on by default and defers SDK MCP tools: Claude sees each tool's name in a compact list and loads its full schema on demand. With tool search disabled, every tool in this array consumes context window space on every turn. In TypeScript, pass `alwaysLoad: true` in the `extras` argument of [`tool()`](/docs/en/agent-sdk/typescript#tool) or in the options of [`createSdkMcpServer()`](/docs/en/agent-sdk/typescript#createsdkmcpserver) to keep a tool's full schema in the initial prompt.
### Add tool annotations
[Tool annotations](https://modelcontextprotocol.io/docs/concepts/tools#tool-annotations) are optional metadata describing how a tool behaves. Pass them as the fifth argument to `tool()` helper in TypeScript or via the `annotations` keyword argument for the `@tool` decorator in Python. All hint fields are Booleans.
| Field | Default | Meaning |
| :---------------- | :------ | :-------------------------------------------------------------------------------------------------------------------- |
| `readOnlyHint` | `false` | Tool does not modify its environment. Controls whether the tool can be called in parallel with other read-only tools. |
| `destructiveHint` | `true` | Tool may perform destructive updates. Informational only. |
| `idempotentHint` | `false` | Repeated calls with the same arguments have no additional effect. Informational only. |
| `openWorldHint` | `true` | Tool reaches systems outside your process. Informational only. |
Annotations are metadata, not enforcement. A tool marked `readOnlyHint: true` can still write to disk if that's what the handler does. Keep the annotation accurate to the handler.
This example adds `readOnlyHint` to the `get_temperature` tool from the [weather tool example](#weather-tool-example).
<CodeGroup>
```python Python theme={null}
from claude_agent_sdk import tool, ToolAnnotations
@tool(
"get_temperature",
"Get the current temperature at a location",
{"latitude": float, "longitude": float},
annotations=ToolAnnotations(
readOnlyHint=True
), # Lets Claude batch this with other read-only calls
)
async def get_temperature(args):
return {"content": [{"type": "text", "text": "..."}]}
```
```typescript TypeScript theme={null}
import { tool } from "@anthropic-ai/claude-agent-sdk";
import { z } from "zod";
tool(
Cut at 300 lines. The page has the rest.
agent-sdk/examples First recorded · 23 lines, first recorded
# Examples ## Run a minimal agent first ## Explore a TypeScript application ## Work through a Python recipe
The first capture of this source. The page was already there, and this is what it said.
# Examples > Find a complete, runnable Agent SDK project or a guided recipe in the Claude Cookbook that matches what you want to build. This page routes you to complete, runnable Agent SDK projects and guided Claude Cookbook recipes. TypeScript applications live in the [`claude-agent-sdk-demos`](https://github.com/anthropics/claude-agent-sdk-demos) repo, and Python recipes live in the [Claude Cookbook](https://platform.claude.com/cookbook). ## Run a minimal agent first If you haven't built anything with the SDK yet, start with one of these before a full application: * [Agent SDK quickstart](/docs/en/agent-sdk/quickstart): build your first working agent in TypeScript or Python, with setup steps included. The agent finds and fixes bugs in a sample file. * [Hello World](https://github.com/anthropics/claude-agent-sdk-demos/tree/main/hello-world): a minimal TypeScript project to clone when you want to start from repo code ## Explore a TypeScript application The TypeScript applications in [`claude-agent-sdk-demos`](https://github.com/anthropics/claude-agent-sdk-demos) are demos for local development, from an email client to a multi-agent research system. Clone the demo whose shape matches what you're building. ## Work through a Python recipe The Claude Cookbook's Agent SDK series is a sequence of recipes, each a Python notebook, that progresses from a simple research agent to sophisticated multi-agent systems. Each notebook builds on the previous one, introducing new concepts and capabilities. Start with [the one-liner research agent](https://platform.claude.com/cookbook/claude-agent-sdk-00-the-one-liner-research-agent) and work forward. For recipes across Claude products, see the full [Claude Cookbook](https://platform.claude.com/cookbook).
agent-sdk/file-checkpointing First recorded · 804 lines, first recorded
# Rewind file changes with checkpointing ## How checkpointing works ## Implement checkpointing ## Common patterns ### Checkpoint before risky operations ### Multiple restore points ## Try it out ## Limitations ## Troubleshooting ### Checkpointing options not recognized ### User messages don't have UUIDs ### "No file checkpoint found for this message" error ### "File rewinding is not enabled" error ### "ProcessTransport is not ready for writing" error ## Next steps
The first capture of this source. The page was already there, and this is what it said.
# Rewind file changes with checkpointing
> Track file changes during agent sessions and restore files to any previous state
File checkpointing tracks file modifications made through the Write, Edit, and NotebookEdit tools during an agent session, allowing you to rewind files to any previous state. Want to try it out? Jump to the [interactive example](#try-it-out).
With checkpointing, you can:
* **Undo unwanted changes** by restoring files to a known good state
* **Explore alternatives** by restoring to a checkpoint and trying a different approach
* **Recover from errors** when the agent makes incorrect modifications
<Warning>
Only changes made through the Write, Edit, and NotebookEdit tools are tracked. Changes made through Bash commands (like `echo > file.txt` or `sed -i`) are not captured by the checkpoint system, and neither are edits a [subagent](/docs/en/agent-sdk/subagents) applies, except a [skill with `context: fork`](/docs/en/skills#run-skills-in-a-subagent) that runs in the foreground.
</Warning>
## How checkpointing works
When you enable file checkpointing, the SDK creates backups of files before modifying them through the Write, Edit, or NotebookEdit tools. User messages in the response stream include a checkpoint UUID that you can use as a restore point.
<Note>
File rewinding restores files on disk to a previous state. It does not rewind the conversation itself. The conversation history and context remain intact after calling `rewindFiles()` (TypeScript) or `rewind_files()` (Python).
</Note>
The checkpoint system tracks:
* Files created during the session
* Files modified during the session
* The original content of modified files
When you rewind to a checkpoint, Claude Code deletes the files it created and restores the files it modified to their content at that point. Claude Code skips a tracked path that is a symlink, hard link, or other non-regular file. It also skips a tracked file whose parent directory no longer resolves to its checkpoint-time location, or whose backup it can't read safely. [`RewindFilesResult`](/docs/en/agent-sdk/typescript#rewindfilesresult) counts every skipped path in its `skippedLinks` field. Skipping requires Claude Code v2.1.216 or later; before v2.1.216, a rewind wrote and deleted through links at tracked paths.
## Implement checkpointing
To use file checkpointing, enable it in your options, capture checkpoint UUIDs from the response stream, then call `rewindFiles()` (TypeScript) or `rewind_files()` (Python) when you need to restore.
The following example shows the complete flow: enable checkpointing, capture the checkpoint UUID and session ID from the response stream, then resume the session later to rewind files. Each step is explained in detail below. The examples in this section use the prompt "Refactor the authentication module". Run them in a project that contains an authentication module, or change the prompt to name files that exist in your project, so you can watch files change and see the rewind restore them.
<CodeGroup>
```python Python theme={null}
import asyncio
from claude_agent_sdk import (
ClaudeSDKClient,
ClaudeAgentOptions,
UserMessage,
ResultMessage,
)
async def main():
# Step 1: Enable checkpointing
options = ClaudeAgentOptions(
enable_file_checkpointing=True,
permission_mode="acceptEdits", # Auto-accept file edits without prompting
extra_args={
"replay-user-messages": None
}, # Required to receive checkpoint UUIDs in the response stream
)
checkpoint_id = None
session_id = None
# Run the query and capture checkpoint UUID and session ID
async with ClaudeSDKClient(options) as client:
await client.query("Refactor the authentication module")
# Step 2: Capture checkpoint UUID from the first user message
async for message in client.receive_response():
if isinstance(message, UserMessage) and message.uuid and not checkpoint_id:
checkpoint_id = message.uuid
if isinstance(message, ResultMessage) and not session_id:
session_id = message.session_id
# Step 3: Later, rewind by resuming the session with an empty prompt
if checkpoint_id and session_id:
async with ClaudeSDKClient(
ClaudeAgentOptions(enable_file_checkpointing=True, resume=session_id)
) as client:
await client.query("") # Empty prompt to open the connection
async for message in client.receive_response():
await client.rewind_files(checkpoint_id)
break
print(f"Rewound to checkpoint: {checkpoint_id}")
asyncio.run(main())
```
```typescript TypeScript theme={null}
import { query } from "@anthropic-ai/claude-agent-sdk";
async function main() {
// Step 1: Enable checkpointing
const opts = {
enableFileCheckpointing: true,
permissionMode: "acceptEdits" as const, // Auto-accept file edits without prompting
extraArgs: { "replay-user-messages": null } // Required to receive checkpoint UUIDs in the response stream
};
const response = query({
prompt: "Refactor the authentication module",
options: opts
});
let checkpointId: string | undefined;
let sessionId: string | undefined;
// Step 2: Capture checkpoint UUID from the first user message
try {
for await (const message of response) {
if (message.type === "user" && message.uuid && !checkpointId) {
checkpointId = message.uuid;
}
if ("session_id" in message && !sessionId) {
sessionId = message.session_id;
}
}
} catch (error) {
// A single-shot query() throws after yielding an error result. If the
// failure was an error result, sessionId and checkpointId were already
// captured by the loop above; connection or process failures yield no
// result message.
console.error(`Session ended with an error: ${error}`);
}
// Step 3: Later, rewind by resuming the session with an empty prompt
if (checkpointId && sessionId) {
const rewindQuery = query({
prompt: "", // Empty prompt to open the connection
options: { ...opts, resume: sessionId }
});
for await (const msg of rewindQuery) {
await rewindQuery.rewindFiles(checkpointId);
break;
}
console.log(`Rewound to checkpoint: ${checkpointId}`);
}
}
main();
```
</CodeGroup>
<Steps>
<Step title="Enable checkpointing">
Configure your SDK options to enable checkpointing and receive checkpoint UUIDs:
| Option | Python | TypeScript | Description |
| ------------------------ | ------------------------------------------- | --------------------------------------------- | ------------------------------------------------ |
| Enable checkpointing | `enable_file_checkpointing=True` | `enableFileCheckpointing: true` | Tracks file changes for rewinding |
| Receive checkpoint UUIDs | `extra_args={"replay-user-messages": None}` | `extraArgs: { 'replay-user-messages': null }` | Required to get user message UUIDs in the stream |
<CodeGroup>
```python Python theme={null}
options = ClaudeAgentOptions(
enable_file_checkpointing=True,
permission_mode="acceptEdits",
extra_args={"replay-user-messages": None},
)
async with ClaudeSDKClient(options) as client:
await client.query("Refactor the authentication module")
```
```typescript TypeScript theme={null}
const response = query({
prompt: "Refactor the authentication module",
options: {
enableFileCheckpointing: true,
permissionMode: "acceptEdits" as const,
extraArgs: { "replay-user-messages": null }
}
});
```
</CodeGroup>
</Step>
<Step title="Capture checkpoint UUID and session ID">
With the `replay-user-messages` option set (shown above), each user message in the response stream has a UUID that serves as a checkpoint.
For most use cases, capture the first user message UUID (`message.uuid`); rewinding to it restores the tracked files to their original state. To store multiple checkpoints and rewind to intermediate states, see [Multiple restore points](#multiple-restore-points).
Capturing the session ID (`message.session_id`) is optional; you only need it if you want to rewind later, after the stream completes. If you're calling `rewindFiles()` immediately while still processing messages (as the example in [Checkpoint before risky operations](#checkpoint-before-risky-operations) does), you can skip capturing the session ID.
<CodeGroup>
```python Python theme={null}
checkpoint_id = None
session_id = None
async for message in client.receive_response():
# Capture the first user message UUID as the checkpoint
if isinstance(message, UserMessage) and message.uuid and checkpoint_id is None:
checkpoint_id = message.uuid
# Capture session ID from the result message
if isinstance(message, ResultMessage):
session_id = message.session_id
```
```typescript TypeScript theme={null}
let checkpointId: string | undefined;
let sessionId: string | undefined;
for await (const message of response) {
// Capture the first user message UUID as the checkpoint
if (message.type === "user" && message.uuid && !checkpointId) {
checkpointId = message.uuid;
}
// Capture session ID from any message that has it
if ("session_id" in message) {
sessionId = message.session_id;
}
}
```
</CodeGroup>
</Step>
<Step title="Rewind files">
To rewind after the stream completes, resume the session with an empty prompt and call `rewind_files()` (Python) or `rewindFiles()` (TypeScript) with your checkpoint UUID. You can also rewind during the stream; see [Checkpoint before risky operations](#checkpoint-before-risky-operations) for that pattern.
<CodeGroup>
```python Python theme={null}
async with ClaudeSDKClient(
ClaudeAgentOptions(enable_file_checkpointing=True, resume=session_id)
) as client:
await client.query("") # Empty prompt to open the connection
async for message in client.receive_response():
if checkpoint_id:
await client.rewind_files(checkpoint_id)
break
```
```typescript TypeScript theme={null}
const rewindQuery = query({
prompt: "", // Empty prompt to open the connection
options: { ...opts, resume: sessionId }
});
for await (const msg of rewindQuery) {
if (checkpointId) {
await rewindQuery.rewindFiles(checkpointId);
}
break;
}
```
</CodeGroup>
If you capture the session ID and checkpoint ID, you can also rewind from the CLI. This command requires the `claude` executable, which comes from [installing Claude Code](/docs/en/setup) and is not installed by the SDK package. The SDK enables checkpointing for you, but when you run `claude -p` directly you must set the `CLAUDE_CODE_ENABLE_SDK_FILE_CHECKPOINTING` environment variable:
```bash theme={null}
CLAUDE_CODE_ENABLE_SDK_FILE_CHECKPOINTING=true claude -p --resume <session-id> --rewind-files <checkpoint-uuid>
```
The `--rewind-files` flag does not appear in `claude --help` output, but the CLI accepts it as shown.
</Step>
</Steps>
## Common patterns
These patterns show different ways to capture and use checkpoint UUIDs depending on your use case.
### Checkpoint before risky operations
This pattern keeps only the most recent checkpoint UUID, updating it before each agent turn. If something goes wrong during processing, you can immediately rewind to the last safe state and break out of the loop.
Before running this example, replace `your_revert_condition` (Python) or `yourRevertCondition` (TypeScript) with your own check, such as error detection or a validation failure; the placeholder is not defined in the example.
<CodeGroup>
```python Python theme={null}
import asyncio
from claude_agent_sdk import ClaudeSDKClient, ClaudeAgentOptions, UserMessage
async def main():
options = ClaudeAgentOptions(
enable_file_checkpointing=True,
permission_mode="acceptEdits",
extra_args={"replay-user-messages": None},
)
safe_checkpoint = None
async with ClaudeSDKClient(options) as client:
await client.query("Refactor the authentication module")
async for message in client.receive_response():
# Update checkpoint before each agent turn starts
# This overwrites the previous checkpoint. Only keep the latest
if isinstance(message, UserMessage) and message.uuid:
safe_checkpoint = message.uuid
# Decide when to revert based on your own logic
# For example: error detection, validation failure, or user input
if your_revert_condition and safe_checkpoint:
await client.rewind_files(safe_checkpoint)
# Exit the loop after rewinding, files are restored
break
asyncio.run(main())
Cut at 300 lines. The page has the rest.
agent-sdk/hooks First recorded · 869 lines, first recorded
# Intercept and control agent behavior with hooks ## How hooks work ## Available hooks ## Configure hooks ### Matchers ### Callback functions #### Inputs #### Outputs #### Asynchronous output ## Examples ### Modify tool input ### Add context and block a tool ### Auto-approve specific tools ### Register multiple hooks ### Filter with multi-tool matchers ### Track subagent activity ### Make HTTP requests from hooks ### Forward notifications to Slack ## Fix common issues ### Hook not firing ### Matcher not filtering as expected ### Hook timeout ### Tool blocked unexpectedly ### Modified input not applied ### Session hooks not available in Python ### Subagent permission prompts multiplying ### Recursive hook loops with subagents ### systemMessage not appearing in output ## Related resources
The first capture of this source. The page was already there, and this is what it said.
# Intercept and control agent behavior with hooks
> Intercept and customize agent behavior at key execution points with hooks
Hooks are callback functions that run your code in response to agent events, like a tool being called, a session starting, or execution stopping. With hooks, you can:
* **Block dangerous operations** before they execute, like destructive shell commands or unauthorized file access
* **Log and audit** every tool call for compliance, debugging, or analytics
* **Transform inputs and outputs** to sanitize data, inject credentials, or redirect file paths
* **Require human approval** for sensitive actions like database writes or API calls
* **Track session lifecycle** to manage state, clean up resources, or send notifications
## How hooks work
<Steps>
<Step title="An event fires">
Something happens during agent execution and the SDK fires an event: a tool is about to be called (`PreToolUse`), a tool returned a result (`PostToolUse`), a subagent started or stopped, the agent is idle, or execution finished. See the [full list of events](#available-hooks).
</Step>
<Step title="The SDK collects registered hooks">
The SDK checks for hooks registered for that event type. This includes callback hooks you pass in `options.hooks` and shell command hooks from settings files when the corresponding [`settingSources`](/docs/en/agent-sdk/typescript#settingsource) or [`setting_sources`](/docs/en/agent-sdk/python#settingsource) entry is enabled, which it is for default `query()` options.
</Step>
<Step title="Matchers filter which hooks run">
If a hook has a [`matcher`](#matchers) pattern (like `"Write|Edit"`), the SDK tests it against the event's target (for example, the tool name). Hooks without a matcher run for every event of that type.
</Step>
<Step title="Callback functions execute">
Each matching hook's [callback function](#callback-functions) receives input about what's happening: the tool name, its arguments, the session ID, and other event-specific details.
</Step>
<Step title="Your callback returns a decision">
After performing any operations (logging, API calls, validation), your callback returns an [output object](#outputs) that tells the agent what to do: allow the operation, block it, modify the input, or inject context into the conversation.
</Step>
</Steps>
The following example puts these steps together. It registers a `PreToolUse` hook (step 1) with a `"Write|Edit"` matcher (step 3) so the callback only fires for file-writing tools. When triggered, the callback receives the tool's input (step 4), checks if the file path targets a `.env` file, and returns `permissionDecision: "deny"` to block the operation (step 5):
<CodeGroup>
```python Python theme={null}
import asyncio
from claude_agent_sdk import (
AssistantMessage,
ClaudeSDKClient,
ClaudeAgentOptions,
HookMatcher,
ResultMessage,
)
# Define a hook callback that receives tool call details
async def protect_env_files(input_data, tool_use_id, context):
# Extract the file path from the tool's input arguments
file_path = input_data["tool_input"].get("file_path", "")
file_name = file_path.split("/")[-1]
# Block the operation if targeting a .env file
if file_name == ".env":
return {
"hookSpecificOutput": {
"hookEventName": input_data["hook_event_name"],
"permissionDecision": "deny",
"permissionDecisionReason": "Cannot modify .env files",
}
}
# Return empty object to allow the operation
return {}
async def main():
options = ClaudeAgentOptions(
hooks={
# Register the hook for PreToolUse events
# The matcher filters to only Write and Edit tool calls
"PreToolUse": [HookMatcher(matcher="Write|Edit", hooks=[protect_env_files])]
}
)
async with ClaudeSDKClient(options=options) as client:
await client.query("Create a .env file with the standard local development database configuration")
async for message in client.receive_response():
# Filter for assistant and result messages
if isinstance(message, (AssistantMessage, ResultMessage)):
print(message)
asyncio.run(main())
```
```typescript TypeScript theme={null}
import { query, HookCallback, PreToolUseHookInput } from "@anthropic-ai/claude-agent-sdk";
// Define a hook callback with the HookCallback type
const protectEnvFiles: HookCallback = async (input, toolUseID, { signal }) => {
// Cast input to the specific hook type for type safety
const preInput = input as PreToolUseHookInput;
// Cast tool_input to access its properties (typed as unknown in the SDK)
const toolInput = preInput.tool_input as Record<string, unknown>;
const filePath = toolInput?.file_path as string;
const fileName = filePath?.split("/").pop();
// Block the operation if targeting a .env file
if (fileName === ".env") {
return {
hookSpecificOutput: {
hookEventName: preInput.hook_event_name,
permissionDecision: "deny",
permissionDecisionReason: "Cannot modify .env files"
}
};
}
// Return empty object to allow the operation
return {};
};
for await (const message of query({
prompt: "Create a .env file with the standard local development database configuration",
options: {
hooks: {
// Register the hook for PreToolUse events
// The matcher filters to only Write and Edit tool calls
PreToolUse: [{ matcher: "Write|Edit", hooks: [protectEnvFiles] }]
}
}
})) {
// Filter for assistant and result messages
if (message.type === "assistant" || message.type === "result") {
console.log(message);
}
}
```
</CodeGroup>
When you run either script, Claude attempts to create the `.env` file, the hook denies the tool call, and Claude's final response explains that it can't create `.env` files.
## Available hooks
The SDK provides hooks for different stages of agent execution. Some hooks are available in both SDKs, while others are TypeScript-only.
| Hook Event | Python SDK | TypeScript SDK | What triggers it | Example use case |
| ------------------------------------------------------ | ---------- | -------------- | --------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `PreToolUse` | Yes | Yes | Tool call request (can block or modify) | Block dangerous shell commands |
| `PostToolUse` | Yes | Yes | Tool execution result | Log all file changes to audit trail |
| `PostToolUseFailure` | Yes | Yes | Tool execution failure | Handle or log tool errors |
| `PostToolBatch` | No | Yes | A full batch of tool calls resolves, once per batch before the next model call | Inject conventions once for the whole batch |
| `UserPromptSubmit` | Yes | Yes | User prompt submission | Inject additional context into prompts |
| [`UserPromptExpansion`](/docs/en/hooks#userpromptexpansion) | No | Yes | A user-typed command, or an MCP prompt, expands into a prompt before it reaches Claude. Doesn't fire when Claude invokes a skill itself | Block a command from direct invocation or add context when a skill is typed |
| `MessageDisplay` | No | Yes | An assistant message with text completes, once per message with the full message text | Redact or reformat the displayed text without changing the transcript |
| `Stop` | Yes | Yes | Agent execution stop | Save session state before exit |
| `StopFailure` | No | Yes | The turn ends with an API error instead of a normal stop | Log failures or send alerts |
| `SubagentStart` | Yes | Yes | Subagent initialization | Track parallel task spawning |
| `SubagentStop` | Yes | Yes | Subagent completion | Aggregate results from parallel tasks |
| `PreCompact` | Yes | Yes | Conversation compaction request | Archive full transcript before summarizing |
| `PostCompact` | No | Yes | Conversation compaction completes | Log the generated summary |
| `PermissionRequest` | Yes | Yes | A tool call needs a permission decision | Custom permission handling |
| `PermissionDenied` | No | Yes | Auto mode denies a tool call, including denials without a classifier verdict | Log denials, or tell the model it may retry; Claude Code ignores `retry: true` for no-verdict denials. See [PermissionDenied](/docs/en/hooks#permissiondenied) |
| `SessionStart` | No | Yes | Session initialization | Initialize logging and telemetry |
| `SessionEnd` | No | Yes | Session termination | Clean up temporary resources |
| `Notification` | Yes | Yes | Agent status messages | Send agent status updates to Slack or PagerDuty |
| `Setup` | No | Yes | Session setup/maintenance | Run initialization tasks |
| `TeammateIdle` | No | Yes | Teammate becomes idle | Reassign work or notify |
| `TaskCreated` | No | Yes | A task is created via the `TaskCreate` tool | Enforce task naming conventions |
| `TaskCompleted` | No | Yes | Background task completes | Aggregate results from parallel tasks |
| `Elicitation` | No | Yes | An MCP server requests user input mid-task | Respond to MCP input requests programmatically |
| `ElicitationResult` | No | Yes | A user responds to an MCP elicitation | Modify or block the response before it returns to the server |
| `ConfigChange` | No | Yes | Configuration file changes | Reload settings dynamically |
| `InstructionsLoaded` | No | Yes | A `CLAUDE.md` or rules file is loaded into context | Audit which instruction files load |
| `WorktreeCreate` | No | Yes | Git worktree created | Track isolated workspaces |
| `WorktreeRemove` | No | Yes | Git worktree removed | Clean up workspace resources |
| `CwdChanged` | No | Yes | The working directory changes during a session | Reload environment variables per directory |
| `FileChanged` | No | Yes | A watched file is modified, created, or deleted | Reload configuration when project files change |
| `DirectoryAdded` | No | Yes | A working directory is added during a session | Install dependencies for a repository added mid-session |
## Configure hooks
To configure a hook, pass it in the `hooks` field of your agent options (`ClaudeAgentOptions` in Python, the `options` object in TypeScript). This snippet assumes you have already defined a hook callback, like `protect_env_files` in Python or `protectEnvFiles` in TypeScript from the example above:
<CodeGroup>
```python Python theme={null}
options = ClaudeAgentOptions(
hooks={"PreToolUse": [HookMatcher(matcher="Bash", hooks=[my_callback])]}
)
async with ClaudeSDKClient(options=options) as client:
await client.query("Your prompt")
async for message in client.receive_response():
print(message)
```
```typescript TypeScript theme={null}
for await (const message of query({
prompt: "Your prompt",
options: {
hooks: {
PreToolUse: [{ matcher: "Bash", hooks: [myCallback] }]
}
}
})) {
console.log(message);
}
```
</CodeGroup>
The `hooks` option is a dictionary in Python or an object in TypeScript, where:
* **Keys**: [hook event names](#available-hooks) such as `'PreToolUse'`, `'PostToolUse'`, and `'Stop'`
* **Values**: arrays of [matchers](#matchers), each containing an optional filter pattern and your [callback functions](#callback-functions)
### Matchers
Use matchers to filter when your callbacks fire. The `matcher` field matches against a different value depending on the hook event type. For example, tool-based hooks match against the tool name, while `Notification` hooks match against the notification type.
SDK matchers follow the same rules as [matchers in settings files](/docs/en/hooks#matcher-patterns). That section documents the exact-string and regular-expression evaluation paths, their version requirements, and the matcher values for each event type.
| Option | Type | Default | Description |
| --------- | ---------------- | ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `matcher` | `string` | `undefined` | Pattern matched against the event's filter field, following the [rules for matchers in settings files](/docs/en/hooks#matcher-patterns). For tool hooks, this is the tool name. Built-in tools include `Bash`, `Read`, `Write`, `Edit`, `Glob`, `Grep`, `WebFetch`, `Agent`, and others (see [Tool Input Types](/docs/en/agent-sdk/typescript#tool-input-types) for the full list). MCP tools use the pattern `mcp__<server>__<action>`. |
| `hooks` | `HookCallback[]` | - | Required. Array of callback functions to execute when the pattern matches |
| `timeout` | `number` | `undefined` | Timeout in seconds. When omitted, Claude Code applies the [event's default timeout](#hook-timeout). Your SDK callbacks follow the `command` hook defaults |
Use the `matcher` pattern to target specific tools whenever possible. A matcher with `'Bash'` only runs for Bash commands, while omitting the pattern runs your callbacks for every occurrence of the event.
<Tip>
**Discovering tool names:** See [Tool Input Types](/docs/en/agent-sdk/typescript#tool-input-types) for the full list of built-in tool names, or add a hook without a matcher to log all tool calls your session makes.
**MCP tool naming:** MCP tools always start with `mcp__` followed by the server name and action: `mcp__<server>__<action>`. For example, if you configure a server named `playwright`, its tools are named `mcp__playwright__browser_screenshot`, `mcp__playwright__browser_click`, and so on. The server name comes from the key you use in the `mcpServers` configuration.
</Tip>
### Callback functions
#### Inputs
Every hook callback receives three arguments:
* **Input data:** a typed object containing event details. Each hook type has its own input shape. For example, `PreToolUseHookInput` includes `tool_name` and `tool_input`, while `NotificationHookInput` includes `message`. See the full type definitions in the [TypeScript](/docs/en/agent-sdk/typescript#hookinput) and [Python](/docs/en/agent-sdk/python#hookinput) SDK references.
* All hook inputs share `session_id`, `cwd`, and `hook_event_name`.
* `agent_id` and `agent_type` are populated when the hook fires inside a subagent. In TypeScript, these are on the base hook input and available to all hook types. In Python, they are optional fields on `PreToolUse`, `PostToolUse`, `PostToolUseFailure`, and `PermissionRequest`, and required fields on `SubagentStart` and `SubagentStop`.
* **Tool use ID** (`str | None` / `string | undefined`): correlates `PreToolUse` and `PostToolUse` events for the same tool call.
* **Context:** in TypeScript, contains a `signal` property (`AbortSignal`) for cancellation. In Python, this argument is reserved for future use.
#### Outputs
Your callback returns an object with two categories of fields:
* **Top-level fields** are accepted on every event: `systemMessage` shows a message to the user, and `continue` (`continue_` in Python) determines whether the agent keeps running after this hook. Some events discard them or deliver them elsewhere. Each [event's section](/docs/en/hooks#hook-events) on the hooks page says where they land.
* **`hookSpecificOutput`** controls the current operation. The fields inside depend on the hook event type. For `PreToolUse` hooks, this is where you set `permissionDecision` (`"allow"`, `"deny"`, `"ask"`, or `"defer"`), `permissionDecisionReason`, and `updatedInput`. Returning `"defer"` ends the query so you can [resume it later](/docs/en/hooks#defer-a-tool-call-for-later). For `PostToolUse` hooks, you can set `additionalContext` to append information to the tool result. To replace the tool's output before Claude sees it, set `updatedToolOutput`, which works for any tool in both SDKs. The older `updatedMCPToolOutput` field replaces MCP tool output only and is deprecated.
Return `{}` to allow the operation without changes. SDK callback hooks use the same JSON output format as [Claude Code shell command hooks](/docs/en/hooks#json-output), which documents every field and event-specific option. For the SDK type definitions, see the [TypeScript](/docs/en/agent-sdk/typescript#synchookjsonoutput) and [Python](/docs/en/agent-sdk/python#synchookjsonoutput) SDK references.
<Note>
When multiple hooks or permission rules apply, `deny` takes priority over `defer`, which takes priority over `ask`, which takes priority over `allow`. If any hook returns `deny`, the operation is blocked regardless of other hooks.
</Note>
#### Asynchronous output
By default, the agent waits for your hook to return before proceeding. If your hook performs a side effect, such as logging or sending a webhook, and doesn't need to influence the agent's behavior, you can return an async output instead. This tells the agent to continue immediately without waiting for the hook to finish. In this snippet, `send_to_logging_service` in Python and `sendToLoggingService` in TypeScript stand in for any logging function you define:
<CodeGroup>
```python Python theme={null}
async def async_hook(input_data, tool_use_id, context):
# Start a background task, then return immediately
asyncio.create_task(send_to_logging_service(input_data))
return {"async_": True, "asyncTimeout": 30000}
```
```typescript TypeScript theme={null}
const asyncHook: HookCallback = async (input, toolUseID, { signal }) => {
// Start a background task, then return immediately
sendToLoggingService(input).catch(console.error);
return { async: true, asyncTimeout: 30000 };
};
```
</CodeGroup>
| Field | Type | Description |
| -------------- | -------- | -------------------------------------------------------------------------------------------------------------- |
| `async` | `true` | Signals async mode. The agent proceeds without waiting. In Python, use `async_` to avoid the reserved keyword. |
| `asyncTimeout` | `number` | Optional timeout in milliseconds for the background operation |
<Note>
Async outputs can't block, modify, or inject context into the operation since the agent has already moved on. Use them only for side effects like logging, metrics, or notifications.
</Note>
## Examples
Several examples in this section show only the callback function. To run one, register the callback under the matching event in the `hooks` field of your options, as shown in [Configure hooks](#configure-hooks).
### Modify tool input
This example intercepts Write tool calls and rewrites the `file_path` argument to prepend `/sandbox`, redirecting all file writes to a sandboxed directory. The callback returns `updatedInput` with the modified path and `permissionDecision: 'allow'` to auto-approve the rewritten operation:
<CodeGroup>
```python Python theme={null}
async def redirect_to_sandbox(input_data, tool_use_id, context):
if input_data["hook_event_name"] != "PreToolUse":
return {}
Cut at 300 lines. The page has the rest.
agent-sdk/hosting First recorded · 343 lines, first recorded
# Hosting the Agent SDK ## The subprocess model ### State that lives on local disk ## Choose a session pattern ### Ephemeral sessions ### Long-running sessions ### Hybrid sessions ### Multi-agent container ## Provision the container ### Container-based sandboxing ### Runtime dependencies ### Resources ### Network ## Handle production concerns ### Session and state persistence ### Observability ### Auth and secrets ### Scaling and concurrency ### Cost ### Multi-tenant isolation ## Known limitations ## Next steps
The first capture of this source. The page was already there, and this is what it said.
# Hosting the Agent SDK
> Deploy the Agent SDK in production: subprocess architecture, session persistence, scaling, observability, and multi-tenant isolation for Docker, Kubernetes, and sandbox providers.
The Agent SDK spawns and supervises a `claude` CLI subprocess that owns a shell, a working directory, and session files on disk. Hosting it is not like hosting a stateless API wrapper. Every running agent is a long-lived process tied to local state, which shapes how you allocate resources, persist sessions, and scale across tenants.
This page covers self-hosting on your own infrastructure. For deployable Dockerfiles and Kubernetes manifests, see the [hosting cookbook](https://github.com/anthropics/claude-cookbooks/tree/main/claude_agent_sdk/hosting).
If you do not need infrastructure control, custom isolation, or your own data plane, consider [Managed Agents](https://platform.claude.com/docs/en/managed-agents/overview) instead: a hosted REST API where Anthropic runs the agent and the sandbox, so your application sends events and streams back results with no hosting infrastructure to operate.
<Info>
For security hardening beyond basic sandboxing, including network controls, credential management, and isolation options, see [Secure Deployment](/docs/en/agent-sdk/secure-deployment).
</Info>
## The subprocess model
Every hosting decision on this page follows from how the SDK runs the agent. When your code calls `query()`, the SDK spawns a separate `claude` CLI process and talks to it over stdio. That subprocess owns the shell, the working directory, and the JSONL session transcripts on local disk.
<img src="https://mintcdn.com/claude-code/ikqp3_70mqIahteV/images/agent-sdk/hosting-subprocess.svg?fit=max&auto=format&n=ikqp3_70mqIahteV&q=85&s=9dac857ca9d3b1410c3734900c386004" className="dark:hidden" alt="Request flow: client to your app, which spawns a claude CLI subprocess over stdio inside the container; the subprocess writes to local disk and calls api.anthropic.com over HTTPS" width="920" height="220" data-path="images/agent-sdk/hosting-subprocess.svg" />
<img src="https://mintcdn.com/claude-code/_xqph1dUOslCOwsj/images/agent-sdk/hosting-subprocess-dark.svg?fit=max&auto=format&n=_xqph1dUOslCOwsj&q=85&s=3fdeff3d7f44b2b67762668acfbb25f5" className="hidden dark:block" alt="Request flow: client to your app, which spawns a claude CLI subprocess over stdio inside the container; the subprocess writes to local disk and calls api.anthropic.com over HTTPS" width="920" height="220" data-path="images/agent-sdk/hosting-subprocess-dark.svg" />
One agent session maps to one subprocess. Running N concurrent sessions means N subprocesses, each with its own process tree and transcript file. By default they all inherit your application's working directory, so pass `cwd` on each `query()` call when sessions need separate filesystems:
<CodeGroup>
```typescript TypeScript theme={null}
query({ prompt, options: { cwd: "/work/session-a" } })
```
```python Python theme={null}
query(prompt=prompt, options=ClaudeAgentOptions(cwd="/work/session-a"))
```
</CodeGroup>
### State that lives on local disk
Three kinds of agent state live on the container's filesystem by default. None of them survive a container restart, a scale-down, or a move to a different node.
| State | Default location |
| --------------------------- | ------------------------------------------------------------------------------------------------ |
| Session transcripts | `~/.claude/projects/`, or the `projects/` directory under `CLAUDE_CONFIG_DIR` if set |
| `CLAUDE.md` memory files | `~/.claude/CLAUDE.md` for the user tier and the session's working directory for the project tier |
| Working-directory artifacts | The session's working directory |
To persist transcripts across hosts, configure a [`SessionStore` adapter](/docs/en/agent-sdk/session-storage). Memory files and other working-directory artifacts need their own storage strategy, such as a mounted volume or an object-store sync.
For how sessions, resumption, and forking work at the API level, see [Sessions](/docs/en/agent-sdk/sessions).
## Choose a session pattern
These four patterns cover session lifecycle: how long a container lives relative to the sessions it serves. For where the container runs, the [hosting cookbook](https://github.com/anthropics/claude-cookbooks/blob/main/claude_agent_sdk/07_Hosting_the_agent.ipynb) has [deployable code](https://github.com/anthropics/claude-cookbooks/tree/main/claude_agent_sdk/hosting) for local Docker, Modal, and Kubernetes. Choose a session pattern here and a deployment target from the cookbook.
### Ephemeral sessions
Create a container for each user task and destroy it when the task completes. Best for one-off tasks. The user may still interact with the AI while the task is completing, but once completed the container is destroyed.
Example workloads include bug investigation and fix, invoice and receipt extraction, document translation, and media transformation.
The container runs a one-shot entrypoint that calls the SDK and exits. In TypeScript, save the file as `entrypoint.mts` or set `"type": "module"` in `package.json` so top-level `await` is available.
<CodeGroup>
```typescript TypeScript theme={null}
import { query } from "@anthropic-ai/claude-agent-sdk";
const prompt = process.env.TASK_PROMPT!;
for await (const message of query({ prompt, options: { maxTurns: 20 } })) {
console.log(message);
}
```
```python Python theme={null}
import asyncio
import os
from claude_agent_sdk import ClaudeAgentOptions, query
async def main():
async for message in query(
prompt=os.environ["TASK_PROMPT"],
options=ClaudeAgentOptions(max_turns=20),
):
print(message)
asyncio.run(main())
```
</CodeGroup>
### Long-running sessions
Run persistent container instances, often hosting multiple SDK processes per container, to serve ongoing work. Best for agents that take autonomous action, serve content, or handle high-volume message streams.
Example workloads include an email agent that triages and responds to incoming mail, a site builder that hosts a per-user editable site through container ports, and a chat bot that handles continuous traffic from a platform like Slack.
The container exposes an HTTP or WebSocket endpoint and maps each active session to a long-lived query and the subprocess behind it. In TypeScript, use [`streamInput()`](/docs/en/agent-sdk/typescript#query-object) to add turns to an active session and [`startup()`](/docs/en/agent-sdk/typescript#startup) to pre-warm subprocesses ahead of incoming traffic. In Python, use [`ClaudeSDKClient`](/docs/en/agent-sdk/python#claudesdkclient) to hold a session open across turns. Size the container so it can hold the maximum number of concurrent sessions in memory.
### Hybrid sessions
Ephemeral containers that hydrate from a [`SessionStore`](/docs/en/agent-sdk/session-storage) on startup and persist updates back. Best for sessions that span many interactions but sit idle between them. The container spins down during idle periods and spins back up when the user returns.
Example workloads include a personal project manager with intermittent check-ins, deep research that pauses and resumes over hours, and a customer support agent that loads ticket history across interactions.
Tune your provider's idle timeout to how frequently you expect users to return. Shutting a container down without a `SessionStore` configured loses the transcript with it, so the store is required for this pattern, not optional.
The pattern hinges on resuming a session by ID with a shared store attached:
<CodeGroup>
```typescript TypeScript theme={null}
import { query, type SessionStore } from "@anthropic-ai/claude-agent-sdk";
declare const userInput: string;
declare const sessionId: string; // looked up from your database by user
declare const sessionStore: SessionStore; // S3, Redis, Postgres, or your own adapter
for await (const message of query({
prompt: userInput,
options: { resume: sessionId, sessionStore },
})) {
// ...
}
```
```python Python theme={null}
from claude_agent_sdk import query, ClaudeAgentOptions, SessionStore
import asyncio
user_input: str = ...
session_id: str = ... # looked up from your database by user
session_store: SessionStore = ... # S3, Redis, Postgres, or your own adapter
async def main():
async for message in query(
prompt=user_input,
options=ClaudeAgentOptions(
resume=session_id,
session_store=session_store,
),
):
...
asyncio.run(main())
```
</CodeGroup>
See [Session storage](/docs/en/agent-sdk/session-storage) for the full `SessionStore` interface and reference adapters.
### Multi-agent container
Run multiple SDK subprocesses inside one container. Best for agents that must collaborate closely, for example multi-agent simulations where the agents interact with each other in a shared environment.
Give each agent its own working directory so they do not overwrite each other's files, and isolate settings loading so per-agent `CLAUDE.md` files do not leak across agents. See [Multi-tenant isolation](#multi-tenant-isolation) for the specific options.
## Provision the container
### Container-based sandboxing
Run the SDK inside a sandboxed container for process isolation, resource limits, network control, and an ephemeral filesystem. Several providers specialize in sandboxed container environments that fit the Agent SDK's model.
Questions to answer when choosing a provider:
* **Who runs the sandbox**: a sandbox-as-a-service provider operates the infrastructure for you, while self-hosted options give you software to run on your own.
* **Cold-start latency**: how long from "create a sandbox" to "ready to accept the first request." Ephemeral patterns need sub-second starts. Long-running patterns tolerate more.
* **Persistent storage**: whether the provider offers durable volumes or only ephemeral disk. The hybrid pattern needs durable storage somewhere, whether in the sandbox or alongside it.
* **Pricing model**: per-second, per-request, or flat hourly billing. Per-second pricing suits bursty ephemeral workloads. Hourly suits long-running sessions.
* **Networking**: support for custom egress rules, outbound proxies, and private VPC peering for regulated environments.
Providers to evaluate:
* [Modal Sandbox](https://modal.com/docs/guide/sandbox), with a [demo implementation](https://modal.com/docs/examples/claude-slack-gif-creator)
* [Cloudflare Sandboxes](https://github.com/cloudflare/sandbox-sdk)
* [Daytona](https://www.daytona.io/)
* [E2B](https://e2b.dev/)
* [Fly Machines](https://fly.io/docs/machines/)
* [Vercel Sandbox](https://vercel.com/docs/functions/sandbox)
For self-hosted options such as Docker, gVisor, and Firecracker, and detailed isolation configuration, see [Isolation Technologies](/docs/en/agent-sdk/secure-deployment#isolation-technologies).
### Runtime dependencies
The container needs your SDK's language runtime:
* Python 3.10+ for the Python SDK, or Node.js 18+ for the TypeScript SDK
* Both the TypeScript and Python SDKs bundle a native Claude Code binary for most installs, and the spawned CLI needs no separate Node.js install. See the [quickstart's install note](/docs/en/agent-sdk/quickstart) for the installs that need a separate native Claude Code install.
The bundled binary is pinned to the SDK package version, so updating the SDK is how you update the CLI. The SDK follows semver: take patch releases continuously and review the [TypeScript](https://github.com/anthropics/claude-agent-sdk-typescript/blob/main/CHANGELOG.md) or [Python](https://github.com/anthropics/claude-agent-sdk-python/blob/main/CHANGELOG.md) changelog before taking a minor.
### Resources
1 GiB RAM, 5 GiB disk, and 1 CPU per agent is a reasonable starting point for a freshly started instance. Memory usage grows with session length and tool activity, so size for the session lengths and concurrency you actually need rather than the idle baseline. See [Scaling and concurrency](#scaling-and-concurrency) for how to work out agents per host.
### Network
The SDK needs outbound HTTPS to `api.anthropic.com`, or to your provider's regional endpoint when running on Amazon Bedrock or Google Cloud's Agent Platform. If your agents use [MCP servers](/docs/en/agent-sdk/mcp) or external tools, they need outbound access to those endpoints as well. For production, route outbound traffic through an egress proxy that enforces domain allowlists, injects credentials, and logs requests. See [Secure Deployment](/docs/en/agent-sdk/secure-deployment) for the full pattern.
For inbound traffic, expose an HTTP or WebSocket port on the container. Your application handles client requests on that port and calls the SDK internally; the subprocess itself does not listen on the network.
## Handle production concerns
Work through these decisions before shipping a self-hosted agent.
### Session and state persistence
Default local disk is lost on restart, scale-down, or a move to a different node. For any session a user expects to resume, mirror the transcript to durable storage with a [`SessionStore` adapter](/docs/en/agent-sdk/session-storage). See [Reference implementations](/docs/en/agent-sdk/session-storage#reference-implementations) for S3, Redis, and Postgres adapters and a conformance suite for your own.
Three things to know about how `SessionStore` behaves:
* **Transcripts only**: `SessionStore` mirrors transcripts, not `CLAUDE.md` memory files or other working-directory artifacts. Mount a shared volume or sync those separately.
* **Mirror, not replacement**: the subprocess writes to local disk first, and the SDK forwards a copy of each batch to the store. A fresh session's local transcript outlives the run; a run resumed from the store deletes its local copy at the end, so the store holds the only durable copy. See [Dual-write architecture](/docs/en/agent-sdk/session-storage#dual-write-architecture).
* **`mirror_error` messages**: when the SDK can't deliver a batch to the store, it drops the batch, emits a `{ type: "system", subtype: "mirror_error" }` message, and continues the query. Alert on these if store durability matters. See [Mirror writes are best-effort](/docs/en/agent-sdk/session-storage#mirror-writes-are-best-effort) for the retry and timeout behavior.
### Observability
Agent SDK agents are long-lived processes that spawn tool calls across many API round-trips. Without telemetry you cannot see which tools ran, how long they took, or where a session stalled.
The SDK inherits OpenTelemetry configuration from the environment. Set the OTEL environment variables at the container or orchestrator level so every `query()` call exports spans, metrics, and log events to your collector. The example below enables OTLP export for all three signals. `CLAUDE_CODE_ENHANCED_TELEMETRY_BETA` is required only for traces; omit it if you export metrics and logs alone.
```bash title=".env" theme={null}
CLAUDE_CODE_ENABLE_TELEMETRY=1
CLAUDE_CODE_ENHANCED_TELEMETRY_BETA=1
OTEL_TRACES_EXPORTER=otlp
OTEL_METRICS_EXPORTER=otlp
OTEL_LOGS_EXPORTER=otlp
OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf
OTEL_EXPORTER_OTLP_ENDPOINT=http://collector.example.com:4318
```
Prompt text and tool inputs are not included in exports by default. See [Control sensitive data in exports](/docs/en/agent-sdk/observability#control-sensitive-data-in-exports) for the opt-in flags, and [Observability](/docs/en/agent-sdk/observability) for the full signal catalog.
### Auth and secrets
Three auth concerns matter at hosting time:
* **Anthropic API**: the subprocess reads `ANTHROPIC_API_KEY` from its environment. Supply it from your secret manager, or set `ANTHROPIC_BASE_URL` to route model calls through a proxy that injects the key outside the container. See [Credential management](/docs/en/agent-sdk/secure-deployment#credential-management) for the proxy pattern and [Setup in the SDK quickstart](/docs/en/agent-sdk/quickstart#setup) for supported authentication methods.
* **Inbound**: put authentication at a gateway in front of the agent container. The agent should receive pre-authenticated requests and should not be the component that validates user tokens.
* **Outbound tools**: keep tool credentials out of the agent environment. Route outbound calls through a proxy that injects API keys after the request leaves the container. The agent makes the call; the proxy adds the credential.
### Scaling and concurrency
Each session runs in its own subprocess, so concurrency on a host is bounded by how many subprocesses its RAM can hold.
Size each host with this formula:
```text theme={null}
agents per host = (host RAM - overhead) / (per-session RAM ceiling)
```
Measure the per-session ceiling by running a representative session to your target length under your expected tool load and recording peak RSS. The 1 GiB starting point in [Resources](#resources) is a floor, not the ceiling.
Horizontal-scale routing depends on your pattern. For long-running sessions, where containers hold many sessions, run a pool of containers behind a load balancer and pin each session to one container using consistent hashing on `sessionId`. A pinned session keeps hitting the same container, and therefore the same running subprocess, until it is evicted or the container restarts.
### Cost
Anthropic token cost typically dominates container infrastructure cost by an order of magnitude or more. A minimally provisioned container runs roughly \$0.05 per hour, while a single long agent session can spend dollars in tokens. See [Cost tracking](/docs/en/agent-sdk/cost-tracking) for per-session token accounting.
### Multi-tenant isolation
Default SDK behavior reads settings and `CLAUDE.md` memory files from the filesystem. In a shared container that serves multiple tenants, those files can leak one tenant's context into another tenant's session.
To isolate tenants inside a shared container:
* Pass `settingSources: []` in TypeScript or `setting_sources=[]` in Python so no filesystem settings load.
* Set `CLAUDE_CODE_DISABLE_AUTO_MEMORY=1` in `env`. [Auto memory](/docs/en/memory#auto-memory) at `~/.claude/projects/<project>/memory/` loads into the system prompt regardless of `settingSources`. See [What settingSources does not control](/docs/en/agent-sdk/claude-code-features#what-settingsources-does-not-control) for the other inputs that load unconditionally.
* Point `CLAUDE_CONFIG_DIR` at a per-tenant directory so tenants do not share the `~/.claude.json` global config.
* Use a per-tenant working directory. Pass `cwd` explicitly on every `query()` call.
* Apply per-tenant egress rules at your proxy, such as distinct outbound IPs, credentials, or domain allowlists, so a compromised tenant cannot exfiltrate data via another tenant's outbound policy.
The example below applies the four SDK-level options together. Construct `tenantDir` and `configDir` so each tenant gets a path no other tenant can read. In TypeScript, `env` replaces the subprocess environment, so spread `...process.env` to keep inherited variables like `PATH` and `ANTHROPIC_API_KEY`. In Python, `env` is merged on top of the inherited environment.
<CodeGroup>
```typescript TypeScript theme={null}
import { query } from "@anthropic-ai/claude-agent-sdk";
declare const prompt: string;
declare const tenantDir: string;
declare const configDir: string;
for await (const message of query({
prompt,
options: {
cwd: tenantDir,
settingSources: [],
env: {
...process.env,
CLAUDE_CONFIG_DIR: configDir,
CLAUDE_CODE_DISABLE_AUTO_MEMORY: "1",
},
},
})) {
// ...
}
```
```python Python theme={null}
from claude_agent_sdk import query, ClaudeAgentOptions
import asyncio
prompt: str = ...
Cut at 300 lines. The page has the rest.
agent-sdk/mcp First recorded · 882 lines, first recorded
# Connect to external tools with MCP ## Quickstart ## Add an MCP server ### In code ### From a config file ## Connection timing ## Allow MCP tools ### Tool naming convention ### Auto-approve with allowedTools ### Discover available tools ## Transport types ### stdio servers ### HTTP/SSE servers ### SDK MCP servers ## MCP tool search ## Authentication ### Pass credentials via environment variables ### HTTP headers for remote servers ### OAuth2 authentication ## Examples ### List issues from a repository ### Query a database ## Error handling ## Troubleshooting ### Server shows "failed" status ### Tools not being called ### Connection timeouts ### Tool output exceeds maximum allowed tokens ## Related resources
The first capture of this source. The page was already there, and this is what it said.
# Connect to external tools with MCP
> Configure MCP servers to extend your agent with external tools. Covers transport types, tool search for large tool sets, authentication, and error handling.
The [Model Context Protocol (MCP)](https://modelcontextprotocol.io/docs/getting-started/intro) is an open standard for connecting AI agents to external tools and data sources. With MCP, your agent can query databases, integrate with APIs like Slack and GitHub, and connect to other services without writing custom tool implementations.
MCP servers can run as local processes, connect over HTTP, or execute directly within your SDK application.
<Note>
This page covers MCP configuration for the Agent SDK. To add MCP servers to the Claude Code CLI so they load in every project, see [MCP installation scopes](/docs/en/mcp#mcp-installation-scopes).
</Note>
## Quickstart
This example connects to the [Claude Code documentation](https://code.claude.com/docs) MCP server using [HTTP transport](#http%2Fsse-servers) and uses [`allowedTools`](#allow-mcp-tools) with a wildcard to permit all tools from the server.
<CodeGroup>
```typescript TypeScript theme={null}
import { query } from "@anthropic-ai/claude-agent-sdk";
for await (const message of query({
prompt: "Use the docs MCP server to explain what hooks are in Claude Code",
options: {
mcpServers: {
"claude-code-docs": {
type: "http",
url: "https://code.claude.com/docs/mcp"
}
},
allowedTools: ["mcp__claude-code-docs__*"]
}
})) {
if (message.type === "result" && message.subtype === "success") {
console.log(message.result);
}
}
```
```python Python theme={null}
import asyncio
from claude_agent_sdk import query, ClaudeAgentOptions, ResultMessage
async def main():
options = ClaudeAgentOptions(
mcp_servers={
"claude-code-docs": {
"type": "http",
"url": "https://code.claude.com/docs/mcp",
}
},
allowed_tools=["mcp__claude-code-docs__*"],
)
async for message in query(
prompt="Use the docs MCP server to explain what hooks are in Claude Code",
options=options,
):
if isinstance(message, ResultMessage) and message.subtype == "success":
print(message.result)
asyncio.run(main())
```
</CodeGroup>
The agent connects to the documentation server, searches for information about hooks, and returns the results.
## Add an MCP server
You can configure MCP servers in code when calling `query()`, or in a `.mcp.json` file loaded via [`settingSources`](#from-a-config-file).
### In code
Pass MCP servers directly in the `mcpServers` option:
<CodeGroup>
```typescript TypeScript theme={null}
import { query } from "@anthropic-ai/claude-agent-sdk";
for await (const message of query({
prompt: "List files in my project",
options: {
mcpServers: {
filesystem: {
command: "npx",
args: ["-y", "@modelcontextprotocol/server-filesystem", "/Users/me/projects"]
}
},
allowedTools: ["mcp__filesystem__*"]
}
})) {
if (message.type === "result" && message.subtype === "success") {
console.log(message.result);
}
}
```
```python Python theme={null}
import asyncio
from claude_agent_sdk import query, ClaudeAgentOptions, ResultMessage
async def main():
options = ClaudeAgentOptions(
mcp_servers={
"filesystem": {
"command": "npx",
"args": [
"-y",
"@modelcontextprotocol/server-filesystem",
"/Users/me/projects",
],
}
},
allowed_tools=["mcp__filesystem__*"],
)
async for message in query(prompt="List files in my project", options=options):
if isinstance(message, ResultMessage) and message.subtype == "success":
print(message.result)
asyncio.run(main())
```
</CodeGroup>
### From a config file
Create a `.mcp.json` file at your project root. The file is picked up when the `project` setting source is enabled, which it is for default `query()` options. If you set `settingSources` explicitly, include `"project"` for this file to load:
```json theme={null}
{
"mcpServers": {
"filesystem": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-filesystem", "/Users/me/projects"]
}
}
}
```
## Connection timing
Claude Code registers the servers you pass in `options.mcpServers` at startup and emits the [init message](#error-handling) once the first-turn wait, if any, resolves. Servers loaded from [settings files](#from-a-config-file) such as `.mcp.json` don't get the full wait and commonly show `pending` at init. When each `options.mcpServers` server connects, and whether it delays the first turn, depends on its type:
| Server type | Delays the first turn? | First-turn wait timeout |
| :------------------------------------------------------------------------------------- | :----------------------------------------------------- | :------------------------------------------------------------------------------------------ |
| stdio server, or HTTP/SSE server without a cached tool list | Yes, until it connects | [`MCP_TIMEOUT`](/docs/en/env-vars), 30 seconds by default; the connection fails at that deadline |
| Remote server with a cached tool list, saved by Claude Code from a previous connection | No; the cached tools are available from the first turn | None; connects on its first tool call, and that deferred connect has its own timeout |
| In-process [SDK server](#sdk-mcp-servers) | No; never delays the first turn | None |
To block startup itself at a separate, earlier phase than the first-turn wait, before the init message is sent:
* Set [`MCP_CONNECTION_NONBLOCKING`](/docs/en/env-vars) to `0` to block on the whole connection batch. Claude Code caps that wait at 5 seconds by default. Adjust the cap with the [`MCP_CONNECT_TIMEOUT_MS`](/docs/en/env-vars) environment variable, in milliseconds. Servers still pending at that deadline keep connecting in the background.
* Set `alwaysLoad: true` on a server's config to make its tools available at their full schemas on the first turn, [exempt from tool search deferral](/docs/en/mcp#exempt-a-server-from-deferral). Claude Code waits at startup for that server's tools, capped at the same deadline, while other servers keep connecting in the background; a remote server with a cached tool list supplies them without connecting, per the table above.
The `system` message with subtype `init` reports each server's status at the moment it's emitted; see [Error handling](#error-handling) for reading those statuses.
## Allow MCP tools
MCP tools require explicit permission before Claude can use them. Without permission, Claude will see that tools are available but won't be able to call them.
### Tool naming convention
MCP tools follow the naming pattern `mcp__<server-name>__<tool-name>`. For example, a GitHub server named `"github"` with a `list_issues` tool becomes `mcp__github__list_issues`.
### Auto-approve with allowedTools
Use `allowedTools` to auto-approve specific MCP tools so Claude can use them without a permission prompt:
<CodeGroup>
```typescript TypeScript hidelines={1,-1} theme={null}
const _ = {
options: {
mcpServers: {
// your servers
},
allowedTools: [
"mcp__github__*", // All tools from the github server
"mcp__db__query", // Only the query tool from db server
"mcp__slack__send_message" // Only send_message from slack server
]
}
};
```
```python Python theme={null}
options = ClaudeAgentOptions(
mcp_servers={
# your servers
},
allowed_tools=[
"mcp__github__*", # All tools from the github server
"mcp__db__query", # Only the query tool from db server
"mcp__slack__send_message", # Only send_message from slack server
],
)
```
</CodeGroup>
Wildcards (`*`) let you allow all tools from a server without listing each one individually.
<Note>
**Prefer `allowedTools` over permission modes for MCP access.** `permissionMode: "acceptEdits"` does not auto-approve MCP tools (only file edits and filesystem Bash commands). `permissionMode: "bypassPermissions"` does auto-approve MCP tools but also disables most other safety prompts, which is broader than necessary; see [How permissions are evaluated](/docs/en/agent-sdk/permissions#how-permissions-are-evaluated) for the prompts that remain. A wildcard in `allowedTools` grants exactly the MCP server you want and nothing more. See [Permission modes](/docs/en/agent-sdk/permissions#permission-modes) for a full comparison.
</Note>
### Discover available tools
To see what tools an MCP server provides, check the server's documentation or inspect the `tools` array in the `system` init message. MCP tool names start with `mcp__`.
Claude Code emits the init message after the [first-turn connection wait](#connection-timing) for servers passed in `options.mcpServers`, so the `tools` array lists the `mcp__` tools of each server that has connected by then, plus those of servers with a [cached tool list](#connection-timing), which connect on first use. Tools of any other server that hasn't connected are absent; see [Error handling](#error-handling) for reading each server's status.
This filter prints the MCP tool names:
<CodeGroup>
```typescript TypeScript theme={null}
import { query } from "@anthropic-ai/claude-agent-sdk";
const options = {
mcpServers: {
// your servers
},
};
for await (const message of query({ prompt: "...", options })) {
if (message.type === "system" && message.subtype === "init") {
const mcpTools = message.tools.filter((name) => name.startsWith("mcp__"));
console.log("Available MCP tools:", mcpTools);
}
}
```
```python Python theme={null}
import asyncio
from claude_agent_sdk import query, ClaudeAgentOptions, SystemMessage
async def main():
options = ClaudeAgentOptions(
mcp_servers={
# your servers
},
)
async for message in query(prompt="...", options=options):
if isinstance(message, SystemMessage) and message.subtype == "init":
mcp_tools = [t for t in message.data.get("tools", []) if t.startswith("mcp__")]
print("Available MCP tools:", mcp_tools)
asyncio.run(main())
```
</CodeGroup>
You can also ask Claude to list the tools available from a server.
## Transport types
MCP servers communicate with your agent using different transport protocols. Check the server's documentation to see which transport it supports:
* If the docs give you a **command to run** (like `npx @modelcontextprotocol/server-filesystem`), use stdio
* If the docs give you a **URL**, use HTTP or SSE
* If you're building your own tools in code, use an SDK MCP server
### stdio servers
Local processes that communicate via stdin/stdout. Use this for MCP servers you run on the same machine. For the `.mcp.json` form, use the same fields shown at [From a config file](#from-a-config-file). In code, pass the command and its arguments:
<CodeGroup>
```typescript TypeScript hidelines={1,-1} theme={null}
const _ = {
options: {
mcpServers: {
filesystem: {
command: "npx",
args: ["-y", "@modelcontextprotocol/server-filesystem", "/Users/me/projects"]
}
},
allowedTools: ["mcp__filesystem__read_file", "mcp__filesystem__list_directory"]
}
};
```
```python Python theme={null}
options = ClaudeAgentOptions(
mcp_servers={
"filesystem": {
"command": "npx",
"args": [
"-y",
"@modelcontextprotocol/server-filesystem",
"/Users/me/projects",
],
}
},
allowed_tools=["mcp__filesystem__read_file", "mcp__filesystem__list_directory"],
)
```
</CodeGroup>
Cut at 300 lines. The page has the rest.
agent-sdk/migration-guide First recorded · 261 lines, first recorded
# Migrate to Claude Agent SDK ## Overview ## What's Changed ## Migration Steps ### For TypeScript/JavaScript Projects ### For Python Projects ## Breaking changes ### Python: ClaudeCodeOptions renamed to ClaudeAgentOptions ### System prompt no longer default ### Settings sources default ## Next Steps
The first capture of this source. The page was already there, and this is what it said.
# Migrate to Claude Agent SDK
> Guide for migrating the Claude Code TypeScript and Python SDKs to the Claude Agent SDK
## Overview
The Claude Code SDK has been renamed to the **Claude Agent SDK** and its documentation has been reorganized. This change reflects the SDK's broader capabilities for building AI agents beyond just coding tasks.
## What's Changed
| Aspect | Old | New |
| :------------------------- | :-------------------------- | :----------------------------------------------------------------------- |
| **Package Name (TS/JS)** | `@anthropic-ai/claude-code` | `@anthropic-ai/claude-agent-sdk` |
| **Python Package** | `claude-code-sdk` | `claude-agent-sdk` |
| **Documentation Location** | Claude Code docs | Claude Code docs → dedicated [Agent SDK](/docs/en/agent-sdk/overview) section |
## Migration Steps
### For TypeScript/JavaScript Projects
**1. Uninstall the old package:**
```bash theme={null}
npm uninstall @anthropic-ai/claude-code
```
**2. Install the new package:**
```bash theme={null}
npm install @anthropic-ai/claude-agent-sdk
```
**3. Update your imports:**
Change all imports from `@anthropic-ai/claude-code` to `@anthropic-ai/claude-agent-sdk`:
```typescript theme={null}
// Before
import { query, tool, createSdkMcpServer } from "@anthropic-ai/claude-code";
// After
import { query, tool, createSdkMcpServer } from "@anthropic-ai/claude-agent-sdk";
```
**4. Update package.json dependencies:**
If you have the package listed in your `package.json`, update it:
Before:
```json theme={null}
{
"dependencies": {
"@anthropic-ai/claude-code": "^0.0.42"
}
}
```
After:
```json theme={null}
{
"dependencies": {
"@anthropic-ai/claude-agent-sdk": "^0.3.0"
}
}
```
**5. Review [breaking changes](#breaking-changes)**
Make any code changes needed to complete the migration.
### For Python Projects
**1. Uninstall the old package:**
```bash theme={null}
pip uninstall -y claude-code-sdk
```
If the old package isn't installed, pip prints `WARNING: Skipping claude-code-sdk as it is not installed.` That's expected and you can continue to the next step.
**2. Install the new package:**
```bash theme={null}
pip install claude-agent-sdk
```
If `claude-code-sdk` is listed in your `requirements.txt` or `pyproject.toml`, replace it with `claude-agent-sdk`.
**3. Update your imports:**
Change all imports from `claude_code_sdk` to `claude_agent_sdk`:
```python theme={null}
# Before
from claude_code_sdk import query, ClaudeCodeOptions
# After
from claude_agent_sdk import query, ClaudeAgentOptions
```
**4. Review [breaking changes](#breaking-changes)**
Make any code changes needed to complete the migration.
## Breaking changes
<Warning>
To improve isolation and explicit configuration, Claude Agent SDK v0.1.0 introduces breaking changes for users migrating from Claude Code SDK.
</Warning>
### Python: ClaudeCodeOptions renamed to ClaudeAgentOptions
**What changed:** The Python SDK type `ClaudeCodeOptions` has been renamed to `ClaudeAgentOptions`.
**Migration:**
```python theme={null}
# BEFORE (claude-code-sdk)
from claude_code_sdk import query, ClaudeCodeOptions
options = ClaudeCodeOptions(model="claude-opus-4-7", permission_mode="acceptEdits")
# AFTER (claude-agent-sdk)
from claude_agent_sdk import query, ClaudeAgentOptions
options = ClaudeAgentOptions(model="claude-opus-4-7", permission_mode="acceptEdits")
```
### System prompt no longer default
**What changed:** The SDK no longer uses Claude Code's system prompt by default.
**Migration:**
<CodeGroup>
```typescript TypeScript theme={null}
import { query } from "@anthropic-ai/claude-agent-sdk";
// BEFORE (v0.0.x) - Used Claude Code's system prompt by default
const before = query({ prompt: "Hello" });
// AFTER (v0.1.0) - Uses minimal system prompt by default
// To get the old behavior, explicitly request Claude Code's preset:
const presetResult = query({
prompt: "Hello",
options: {
systemPrompt: { type: "preset", preset: "claude_code" }
}
});
// Or use a custom system prompt:
const customResult = query({
prompt: "Hello",
options: {
systemPrompt: "You are a helpful coding assistant"
}
});
```
```python Python theme={null}
from claude_agent_sdk import query, ClaudeAgentOptions
import asyncio
async def main():
# BEFORE (v0.0.x) - Used Claude Code's system prompt by default
async for message in query(prompt="Hello"):
print(message)
# AFTER (v0.1.0) - Uses minimal system prompt by default
# To get the old behavior, explicitly request Claude Code's preset:
async for message in query(
prompt="Hello",
options=ClaudeAgentOptions(
system_prompt={"type": "preset", "preset": "claude_code"} # Use the preset
),
):
print(message)
# Or use a custom system prompt:
async for message in query(
prompt="Hello",
options=ClaudeAgentOptions(system_prompt="You are a helpful coding assistant"),
):
print(message)
asyncio.run(main())
```
</CodeGroup>
**Why this changed:** Provides better control and isolation for SDK applications. You can now build agents with custom behavior without inheriting Claude Code's CLI-focused instructions.
### Settings sources default
This default was briefly changed in v0.1.0 to load no filesystem settings and then reverted, so no migration action is needed.
**Current behavior:** Omitting `settingSources` on `query()` loads user, project, and local filesystem settings, matching the CLI. This includes `~/.claude/settings.json`, `.claude/settings.json`, `.claude/settings.local.json`, CLAUDE.md files, and custom commands.
To run isolated from filesystem settings, pass an empty array:
<CodeGroup>
```typescript TypeScript theme={null}
import { query } from "@anthropic-ai/claude-agent-sdk";
const isolatedResult = query({
prompt: "Hello",
options: {
settingSources: [] // No filesystem settings loaded
}
});
// Or load only specific sources:
const projectOnlyResult = query({
prompt: "Hello",
options: {
settingSources: ["project"] // Only project settings
}
});
```
```python Python theme={null}
from claude_agent_sdk import query, ClaudeAgentOptions
import asyncio
async def main():
async for message in query(
prompt="Hello",
options=ClaudeAgentOptions(setting_sources=[]), # No filesystem settings loaded
):
print(message)
# Or load only specific sources:
async for message in query(
prompt="Hello",
options=ClaudeAgentOptions(
setting_sources=["project"] # Only project settings
),
):
print(message)
asyncio.run(main())
```
</CodeGroup>
Isolation is especially important for CI/CD pipelines, deployed applications, test environments, and multi-tenant systems where local customizations should not leak in.
<Note>
Python SDK 0.1.59 and earlier treated an empty list the same as omitting the option, so upgrade before relying on `setting_sources=[]`. See [What settingSources does not control](/docs/en/agent-sdk/claude-code-features#what-settingsources-does-not-control) for inputs that are read even when `settingSources` is `[]`.
</Note>
## Next Steps
* Explore the [Agent SDK Overview](/docs/en/agent-sdk/overview) to learn about available features
* Check out the [TypeScript SDK Reference](/docs/en/agent-sdk/typescript) for detailed API documentation
* Review the [Python SDK Reference](/docs/en/agent-sdk/python) for Python-specific documentation
* Learn about [Custom Tools](/docs/en/agent-sdk/custom-tools) and [MCP Integration](/docs/en/agent-sdk/mcp)
agent-sdk/modifying-system-prompts First recorded · 417 lines, first recorded
# Modifying system prompts ## How system prompts work ### Decide on a starting point ## Customize agent behavior ### CLAUDE.md files for project-level instructions #### Load CLAUDE.md with the SDK ### Output styles for persistent configurations #### Create an output style #### Activate an output style ### Append to the `claude_code` preset #### Improve prompt caching across users and machines ### Custom system prompts ## Compare the four approaches ## Combine approaches ### Combine an output style with session-specific additions ## See also
The first capture of this source. The page was already there, and this is what it said.
# Modifying system prompts
> Choose between the `claude_code` preset and a custom system prompt, and customize behavior with CLAUDE.md, output styles, append, or a fully custom prompt.
System prompts define Claude's behavior, capabilities, and response style. Start from the `claude_code` preset for CLI or IDE-like coding tools where a human watches and steers the work. Write your own prompt for agents with a different surface, identity, or permission model.
## How system prompts work
A system prompt is the initial instruction set that shapes how Claude behaves throughout a conversation. The Agent SDK has three starting points for it:
* **Minimal default**: when you don't set `systemPrompt` in TypeScript or `system_prompt` in Python, the SDK uses a minimal prompt that covers tool calling but omits Claude Code's coding guidelines, response style, and project context. This differs from `claude -p`, which uses the full Claude Code prompt by default. If you're migrating from the CLI and want matching behavior, set the `claude_code` preset.
* **`claude_code` preset**: the full system prompt that the Claude Code CLI uses, with tool usage instructions, code style and formatting guidelines, response tone and verbosity rules, security and safety instructions, and context about the working directory and environment. Set `systemPrompt: { type: "preset", preset: "claude_code" }` in TypeScript or `system_prompt={"type": "preset", "preset": "claude_code"}` in Python, optionally with `append` to add your own instructions on the end.
* **Custom string**: a prompt you write yourself. The SDK sends only what you provide.
### Decide on a starting point
The deciding factor is how closely your agent resembles Claude Code: a coding agent operating in a repository, with a human watching streaming output and steering the work. The further your product is from that, the more you'll want to write your own prompt.
| You're building | Use | What you get |
| :----------------------------------------------------------------------------------------------------------- | :--------------------------------- | :---------------------------------------------------------------------------------------------------------------------------- |
| A CLI or IDE-like coding tool where a human watches and steers, and Claude Code's defaults are what you want | `claude_code` preset | The full Claude Code prompt: tool guidance, safety rules, terminal-friendly responses, repo-convention awareness |
| The same kind of tool, plus product-specific rules like coding standards, output format, or domain context | `claude_code` preset with `append` | Everything above, with your instructions added after the preset. Nothing is removed, so this is the lowest-risk customization |
| An agent with a different surface, identity, or permission model, or a non-coding agent | Custom prompt string | Only what you write. You take responsibility for replacing the tool guidance and safety instructions your agent still needs |
| A thin tool-calling loop with no agent persona, where you supply all behavior in the user prompt | No `systemPrompt` option | The minimal default: tool-calling support and nothing else |
"Different from Claude Code" usually means one of the following:
* **Different surface**: the output isn't read in a terminal by the person who triggered it. Chat UIs, structured-output consumers, and non-coding automation each need a prompt that matches how their output is rendered and reviewed. Unattended coding automation, like a CI job that fixes lint errors or reviews diffs, still fits the preset because the work itself is what the preset is written for.
* **Different identity**: the agent shouldn't present itself as Claude Code. A support bot, a data-analysis assistant, or any domain-specific agent needs its own name, scope, and persona.
* **Different permission model**: the agent runs autonomously without a human approving each step, or operates on a narrow set of resources. Claude Code's prompt assumes a human is in the loop with access to a full toolset.
* **Non-coding tasks**: most of Claude Code's prompt is coding guidance. For research, content, or operations agents, that guidance competes with the instructions you actually need.
The [comparison table](#compare-the-four-approaches) shows what each customization method preserves.
## Customize agent behavior
Output styles, `append`, and a custom prompt string each change the system prompt directly. CLAUDE.md takes a different path: the SDK reads it and injects its content into the conversation as project context, not into the system prompt, so it shapes behavior alongside whichever system prompt you choose. [Skills](/docs/en/agent-sdk/skills), [hooks](/docs/en/agent-sdk/hooks), and [permissions](/docs/en/agent-sdk/permissions) also shape behavior outside the system prompt and are covered on their own pages.
### CLAUDE.md files for project-level instructions
CLAUDE.md files give Claude persistent project context and instructions. The SDK injects their content into the conversation and leaves the system prompt untouched, so they work with any system prompt configuration. For what to put in CLAUDE.md, where to place it, and how to write effective instructions, see [When to add to CLAUDE.md](/docs/en/memory#when-to-add-to-claude-md) and the rest of [How Claude remembers your project](/docs/en/memory). This section covers what's specific to the SDK: how CLAUDE.md loads.
The SDK reads CLAUDE.md when the matching setting source is enabled: `'project'` loads `CLAUDE.md` or `.claude/CLAUDE.md` from the working directory, and `'user'` loads `~/.claude/CLAUDE.md`. Default `query()` options enable both sources, so CLAUDE.md loads automatically. If you set `settingSources` in TypeScript or `setting_sources` in Python explicitly, include the sources you need. CLAUDE.md loading is controlled by setting sources, not by the `claude_code` preset.
#### Load CLAUDE.md with the SDK
To load CLAUDE.md, set `settingSources` to include the level your CLAUDE.md lives at. The example below loads a project-level CLAUDE.md alongside the `claude_code` preset, so Claude has both the full coding-agent prompt and your project's conventions:
<CodeGroup>
```typescript TypeScript theme={null}
import { query } from "@anthropic-ai/claude-agent-sdk";
const messages = [];
for await (const message of query({
prompt: "Add a new React component for user profiles",
options: {
systemPrompt: {
type: "preset",
preset: "claude_code" // Use Claude Code's system prompt
},
settingSources: ["project"] // Loads CLAUDE.md from project
}
})) {
messages.push(message);
}
// Now Claude has access to your project guidelines from CLAUDE.md
```
```python Python theme={null}
import asyncio
from claude_agent_sdk import query, ClaudeAgentOptions
messages = []
async def main():
async for message in query(
prompt="Add a new React component for user profiles",
options=ClaudeAgentOptions(
system_prompt={
"type": "preset",
"preset": "claude_code", # Use Claude Code's system prompt
},
setting_sources=["project"], # Loads CLAUDE.md from project
),
):
messages.append(message)
asyncio.run(main())
# Now Claude has access to your project guidelines from CLAUDE.md
```
</CodeGroup>
When you run either example, the SDK streams messages as Claude works: a system init message, assistant messages, user messages carrying tool results, and a final result message with the session outcome.
CLAUDE.md is persistent across all sessions in a project, shared with your team through git, and discovered automatically without code changes. It is not loaded if you pass an empty `settingSources` array.
### Output styles for persistent configurations
Output styles are saved configurations that modify Claude's system prompt. They're stored as markdown files and can be reused across sessions and projects.
#### Create an output style
An output style is a markdown file with [frontmatter](/docs/en/output-styles#frontmatter) for metadata, followed by the prompt content. Save it to `~/.claude/output-styles/` for a user-level style available in every project, or `.claude/output-styles/` in your repository for a project-level style you can commit and share with your team.
By default, a custom output style replaces the `claude_code` preset's software engineering instructions with your own. To keep them and layer your instructions on top, set `keep-coding-instructions: true` in the frontmatter. Keep them when your agent is still doing software engineering work. Leave them out when you're replacing the role entirely.
The example below defines a code-review persona that keeps the coding instructions, since reviewing code still benefits from Claude Code's security and code-quality guidance. Save it as `~/.claude/output-styles/code-reviewer.md` to make it available across projects:
```markdown ~/.claude/output-styles/code-reviewer.md theme={null}
---
name: Code Reviewer
description: Thorough code review assistant
keep-coding-instructions: true
---
You are an expert code reviewer.
For every code submission:
1. Check for bugs and security issues
2. Evaluate performance
3. Suggest improvements
4. Rate code quality (1-10)
```
#### Activate an output style
Once created, activate output styles via:
* **CLI**: run `/config` and select an output style
* **Settings**: set `outputStyle` in `.claude/settings.local.json`
* **TypeScript SDK**: set `outputStyle` inside the inline `settings` object passed to `query()`, or point `settings` at a settings file that sets it. `outputStyle` is not a top-level `Options` field:
```typescript theme={null}
const options = { settings: { outputStyle: "Explanatory" } };
```
The Python SDK does not have an option to select an output style programmatically. For code-only deployments where you can't write to `.claude/settings.local.json`, use `append` or a custom prompt string instead.
**Note for SDK users:** Output styles are loaded when you include `settingSources: ['user']` or `settingSources: ['project']` (TypeScript) / `setting_sources=["user"]` or `setting_sources=["project"]` (Python) in your options.
### Append to the `claude_code` preset
You can use the Claude Code preset with an `append` property to add your custom instructions while preserving all built-in functionality.
<CodeGroup>
```typescript TypeScript theme={null}
import { query } from "@anthropic-ai/claude-agent-sdk";
const messages = [];
for await (const message of query({
prompt: "Help me write a Python function to calculate fibonacci numbers",
options: {
systemPrompt: {
type: "preset",
preset: "claude_code",
append: "Always include detailed docstrings and type hints in Python code."
}
}
})) {
messages.push(message);
if (message.type === "assistant") {
console.log(message.message.content);
}
}
```
```python Python theme={null}
import asyncio
from claude_agent_sdk import query, ClaudeAgentOptions, AssistantMessage
messages = []
async def main():
async for message in query(
prompt="Help me write a Python function to calculate fibonacci numbers",
options=ClaudeAgentOptions(
system_prompt={
"type": "preset",
"preset": "claude_code",
"append": "Always include detailed docstrings and type hints in Python code.",
}
),
):
messages.append(message)
if isinstance(message, AssistantMessage):
print(message.content)
asyncio.run(main())
```
</CodeGroup>
#### Improve prompt caching across users and machines
By default, two sessions that use the same `claude_code` preset and `append` text still cannot share a prompt cache entry if they run from different working directories. This is because the preset embeds per-session context in the system prompt ahead of your `append` text: the working directory, whether it's a git repository, the platform, the active shell, the OS version, and auto memory paths. Any difference in that context produces a different system prompt and a cache miss. CLAUDE.md content doesn't affect the system prompt cache because the SDK injects it into the conversation, not the system prompt.
To make the system prompt identical across sessions, set `excludeDynamicSections: true` in TypeScript or `"exclude_dynamic_sections": True` in Python. The per-session context moves into the first user message, leaving only the static preset and your `append` text in the system prompt so identical configurations share a cache entry across users and machines.
<Note>
`excludeDynamicSections` requires `@anthropic-ai/claude-agent-sdk` v0.2.98 or later, or `claude-agent-sdk` v0.1.58 or later for Python. It applies only to the preset object form and has no effect when `systemPrompt` is a string.
</Note>
The following example pairs a shared `append` block with `excludeDynamicSections` so a fleet of agents running from different directories can reuse the same cached system prompt:
<CodeGroup>
```typescript TypeScript theme={null}
import { query } from "@anthropic-ai/claude-agent-sdk";
for await (const message of query({
prompt: "Triage the open issues in this repo",
options: {
systemPrompt: {
type: "preset",
preset: "claude_code",
append: "You operate Acme's internal triage workflow. Label issues by component and severity.",
excludeDynamicSections: true
}
}
})) {
// ...
}
```
```python Python theme={null}
import asyncio
from claude_agent_sdk import query, ClaudeAgentOptions
async def main():
async for message in query(
prompt="Triage the open issues in this repo",
options=ClaudeAgentOptions(
system_prompt={
"type": "preset",
"preset": "claude_code",
"append": "You operate Acme's internal triage workflow. Label issues by component and severity.",
"exclude_dynamic_sections": True,
},
),
):
...
asyncio.run(main())
```
</CodeGroup>
**Tradeoffs:** the working directory, the git-repo flag, the platform, the active shell, the OS version, and auto memory paths still reach Claude, but as part of the first user message rather than the system prompt. Instructions in the user message carry marginally less weight than the same text in the system prompt, so Claude may rely on them less strongly when reasoning about the current directory or auto memory paths. Enable this option when cross-session cache reuse matters more than maximally authoritative environment context.
For the equivalent flag in non-interactive CLI mode, see [`--exclude-dynamic-system-prompt-sections`](/docs/en/cli-reference).
### Custom system prompts
You can provide a custom string as `systemPrompt` to replace the default entirely with your own instructions.
<CodeGroup>
```typescript TypeScript theme={null}
import { query } from "@anthropic-ai/claude-agent-sdk";
const customPrompt = `You are a Python coding specialist.
Follow these guidelines:
- Write clean, well-documented code
- Use type hints for all functions
- Include comprehensive docstrings
- Prefer functional programming patterns when appropriate
- Always explain your code choices`;
const messages = [];
for await (const message of query({
prompt: "Create a data processing pipeline",
options: {
systemPrompt: customPrompt
}
})) {
messages.push(message);
if (message.type === "assistant") {
console.log(message.message.content);
}
}
```
```python Python theme={null}
import asyncio
from claude_agent_sdk import query, ClaudeAgentOptions, AssistantMessage
custom_prompt = """You are a Python coding specialist.
Follow these guidelines:
- Write clean, well-documented code
Cut at 300 lines. The page has the rest.
agent-sdk/observability First recorded · 249 lines, first recorded
# Observability with OpenTelemetry ## How telemetry flows from the SDK ## Enable telemetry export ### Flush telemetry from short-lived calls ## Read agent traces ## Link traces to your application ## Tag telemetry from your agent ## Attribute actions to your end users ## Control sensitive data in exports ## Related documentation
The first capture of this source. The page was already there, and this is what it said.
# Observability with OpenTelemetry
> Export traces, metrics, and events from the Agent SDK to your observability backend using OpenTelemetry.
When you run agents in production, you need visibility into what they did:
* which tools they called
* how long each model request took
* how many tokens were spent
* where failures occurred
The Agent SDK can export this data as OpenTelemetry traces, metrics, and log events to any backend that accepts the OpenTelemetry Protocol (OTLP), such as Honeycomb, Datadog, Grafana, Langfuse, or a self-hosted collector.
This guide explains how the SDK emits telemetry, how to configure the export, and how to tag and filter the data once it reaches your backend. To read token usage and cost directly from the SDK response stream instead of exporting to a backend, see [Track cost and usage](/docs/en/agent-sdk/cost-tracking).
## How telemetry flows from the SDK
The Agent SDK runs the Claude Code CLI as a child process and communicates with it over a local pipe. The CLI has OpenTelemetry instrumentation built in: it records spans around each model request and tool execution, emits metrics for token and cost counters, and emits structured log events for prompts and tool results. The SDK does not produce telemetry of its own. Instead, it passes configuration through to the CLI process, and the CLI exports directly to your collector.
Configuration is passed as environment variables. By default, the child process inherits your application's environment, so you can configure telemetry in either of two places:
* **Process environment:** set the variables in your shell, container, or orchestrator before your application starts. Every `query()` call picks them up automatically with no code change. This is the recommended approach for production deployments.
* **Per-call options:** set the variables in `ClaudeAgentOptions.env` (Python) or `options.env` (TypeScript). Use this when different agents in the same process need different telemetry settings. In Python, `env` is merged on top of the inherited environment. In TypeScript, `env` replaces the inherited environment entirely, so include `...process.env` in the object you pass.
The CLI exports three independent OpenTelemetry signals. Each has its own enable switch and its own exporter, so you can turn on only the ones you need.
| Signal | What it contains | Enable with |
| ---------- | --------------------------------------------------------------------------- | ------------------------------------------------------------------- |
| Metrics | Counters for tokens, cost, sessions, lines of code, and tool decisions | `OTEL_METRICS_EXPORTER` |
| Log events | Structured records for each prompt, API request, API error, and tool result | `OTEL_LOGS_EXPORTER` |
| Traces | Spans for each interaction, model request, tool call, and hook (beta) | `OTEL_TRACES_EXPORTER` plus `CLAUDE_CODE_ENHANCED_TELEMETRY_BETA=1` |
For the complete list of metric names, event names, and attributes, see the Claude Code [Monitoring](/docs/en/monitoring-usage) reference. The Agent SDK emits the same data because it runs the same CLI. Span names are listed in [Read agent traces](#read-agent-traces) below.
## Enable telemetry export
Telemetry is off until you set `CLAUDE_CODE_ENABLE_TELEMETRY=1` and choose at least one exporter. The most common configuration sends all three signals over OTLP HTTP to a collector.
The following example sets the variables in a dictionary and passes them through `options.env`. The agent runs a single task, and the CLI exports spans, metrics, and events to the collector at `collector.example.com` while the loop consumes the response stream:
<CodeGroup>
```python Python theme={null}
import asyncio
from claude_agent_sdk import query, ClaudeAgentOptions
OTEL_ENV = {
"CLAUDE_CODE_ENABLE_TELEMETRY": "1",
# Required for traces, which are in beta. Metrics and log events do not need this.
"CLAUDE_CODE_ENHANCED_TELEMETRY_BETA": "1",
# Choose an exporter per signal. Use otlp for the SDK; see the Note below.
"OTEL_TRACES_EXPORTER": "otlp",
"OTEL_METRICS_EXPORTER": "otlp",
"OTEL_LOGS_EXPORTER": "otlp",
# Standard OTLP transport configuration.
"OTEL_EXPORTER_OTLP_PROTOCOL": "http/protobuf",
"OTEL_EXPORTER_OTLP_ENDPOINT": "http://collector.example.com:4318",
"OTEL_EXPORTER_OTLP_HEADERS": "Authorization=Bearer your-token",
}
async def main():
options = ClaudeAgentOptions(env=OTEL_ENV)
async for message in query(
prompt="List the files in this directory", options=options
):
print(message)
asyncio.run(main())
```
```typescript TypeScript theme={null}
import { query } from "@anthropic-ai/claude-agent-sdk";
const otelEnv = {
CLAUDE_CODE_ENABLE_TELEMETRY: "1",
// Required for traces, which are in beta. Metrics and log events do not need this.
CLAUDE_CODE_ENHANCED_TELEMETRY_BETA: "1",
// Choose an exporter per signal. Use otlp for the SDK; see the Note below.
OTEL_TRACES_EXPORTER: "otlp",
OTEL_METRICS_EXPORTER: "otlp",
OTEL_LOGS_EXPORTER: "otlp",
// Standard OTLP transport configuration.
OTEL_EXPORTER_OTLP_PROTOCOL: "http/protobuf",
OTEL_EXPORTER_OTLP_ENDPOINT: "http://collector.example.com:4318",
OTEL_EXPORTER_OTLP_HEADERS: "Authorization=Bearer your-token",
};
for await (const message of query({
prompt: "List the files in this directory",
// env replaces the inherited environment in TypeScript, so spread
// process.env first to keep PATH, ANTHROPIC_API_KEY, and other variables.
options: { env: { ...process.env, ...otelEnv } },
})) {
console.log(message);
}
```
</CodeGroup>
Because the child process inherits your application's environment by default, you can achieve the same result by exporting these variables in a Dockerfile, Kubernetes manifest, or shell profile and omitting `options.env` entirely.
To confirm that export is working, check your collector's logs for incoming spans, metrics, and log events after the task completes. The CLI fails silently on export errors by default: if the endpoint is unreachable or rejects the data, the agent still runs normally and the CLI drops the telemetry without surfacing an error in your application. To surface exporter errors, set [`CLAUDE_CODE_OTEL_DIAG_STDERR=1`](/docs/en/env-vars) alongside the exporter variables and read the diagnostics through the SDK's `stderr` callback (Python) or `stderr` option (TypeScript). Requires Claude Code v2.1.179 or later.
<Note>
The `console` exporter writes telemetry to standard output, which the SDK uses
as its message channel. Do not set `console` as an exporter value when running
through the SDK. To inspect telemetry locally, point
`OTEL_EXPORTER_OTLP_ENDPOINT` at a local collector or an all-in-one Jaeger
container instead.
</Note>
### Flush telemetry from short-lived calls
The CLI batches telemetry and exports on an interval. On a clean process exit it attempts to flush pending data, but the flush is bounded by a short timeout, so spans can still be dropped if the collector is slow to respond. If your process is killed before the CLI shuts down, anything still in the batch buffer is lost. Lowering the export intervals reduces both windows.
By default, metrics export every 60 seconds and traces and logs export every 5 seconds. The following example shortens all three intervals so that data reaches the collector while a short task is still running:
<CodeGroup>
```python Python theme={null}
OTEL_ENV = {
# ... exporter configuration from the previous example ...
"OTEL_METRIC_EXPORT_INTERVAL": "1000",
"OTEL_LOGS_EXPORT_INTERVAL": "1000",
"OTEL_TRACES_EXPORT_INTERVAL": "1000",
}
```
```typescript TypeScript theme={null}
const otelEnv = {
// ... exporter configuration from the previous example ...
OTEL_METRIC_EXPORT_INTERVAL: "1000",
OTEL_LOGS_EXPORT_INTERVAL: "1000",
OTEL_TRACES_EXPORT_INTERVAL: "1000",
};
```
</CodeGroup>
## Read agent traces
Traces give you the most detailed view of an agent run. With `CLAUDE_CODE_ENHANCED_TELEMETRY_BETA=1` set, each step of the agent loop becomes a span you can inspect in your tracing backend:
* **`claude_code.interaction`:** wraps a single turn of the agent loop, from receiving a prompt to producing a response.
* **`claude_code.llm_request`:** wraps each call to the Claude API, with model name, latency, and token counts as attributes.
* **`claude_code.tool`:** wraps each tool invocation, with child spans for the permission wait (`claude_code.tool.blocked_on_user`) and the execution itself (`claude_code.tool.execution`).
* **`claude_code.hook`:** wraps each [hook](/docs/en/agent-sdk/hooks) execution. Requires detailed beta tracing (`ENABLE_BETA_TRACING_DETAILED=1` and `BETA_TRACING_ENDPOINT`) in addition to the variables above.
The `llm_request`, `tool`, and `hook` spans are children of the enclosing `claude_code.interaction` span. When the agent spawns a subagent through the Task tool, the subagent's `llm_request` and `tool` spans nest under the parent agent's `claude_code.tool` span, so the full delegation chain appears as one trace.
Spans carry a `session.id` attribute by default. When you make several `query()` calls against the same [session](/docs/en/agent-sdk/sessions), filter on `session.id` in your backend to see them as one timeline. Claude Code omits the attribute if you set `OTEL_METRICS_INCLUDE_SESSION_ID` to a falsy value.
<Note>
Tracing is in beta. Span names and attributes may change between releases. See
[Traces (beta)](/docs/en/monitoring-usage#traces-beta) in the Monitoring reference
for the trace exporter configuration variables.
</Note>
## Link traces to your application
The SDK automatically propagates W3C trace context into the CLI subprocess. When you call `query()` while an OpenTelemetry span is active in your application, the SDK injects `TRACEPARENT` and `TRACESTATE` into the child process environment, and the CLI reads them so its `claude_code.interaction` span becomes a child of your span. The agent run then appears inside your application's trace instead of as a disconnected root.
OTLP event log records emitted during the run carry the same trace context: with `TRACEPARENT` set, each record's `trace_id` and `span_id` match your application's trace, so you can join [events](/docs/en/monitoring-usage#events) to spans in your backend. Before v2.1.212, event records emitted outside an active span didn't carry `trace_id` or `span_id`.
When trace-context propagation is enabled, the CLI also forwards `TRACEPARENT` to every Bash and PowerShell command it runs. If a command launched through the Bash tool emits its own OpenTelemetry spans, those spans nest under the `claude_code.tool.execution` span that wraps the command.
Auto-injection is skipped when you set `TRACEPARENT` explicitly in `options.env`, so you can pin a specific parent context if needed. Interactive CLI sessions ignore inbound `TRACEPARENT` entirely; only Agent SDK and `claude -p` runs honor it. See [Traces (beta)](/docs/en/monitoring-usage#traces-beta) in the Monitoring reference for the full span and attribute reference.
## Tag telemetry from your agent
By default, the CLI reports `service.name` as `claude-code`. If you run several agents, or run the SDK alongside other services that export to the same collector, override the service name and add resource attributes so you can filter by agent in your backend.
The following example renames the service and attaches deployment metadata. These values are applied as OpenTelemetry resource attributes on every span, metric, and event the agent emits:
<CodeGroup>
```python Python theme={null}
options = ClaudeAgentOptions(
env={
# ... exporter configuration from the Enable telemetry export example ...
"OTEL_SERVICE_NAME": "support-triage-agent",
"OTEL_RESOURCE_ATTRIBUTES": "service.version=1.4.0,deployment.environment=production",
},
)
```
```typescript TypeScript theme={null}
const options = {
env: {
...process.env,
// ... exporter configuration from the Enable telemetry export example ...
OTEL_SERVICE_NAME: "support-triage-agent",
OTEL_RESOURCE_ATTRIBUTES:
"service.version=1.4.0,deployment.environment=production",
},
};
```
</CodeGroup>
## Attribute actions to your end users
The CLI attaches [identity attributes](/docs/en/monitoring-usage#standard-attributes) to every event based on the credential it uses to call Anthropic. When you build an application that serves many end users from one deployment, these attributes identify your service's credential, not the end user on whose behalf the agent acted.
To make tool calls and MCP activity attributable to your application's end users, inject end-user identity as resource attributes on each `query()` call. Percent-encode values before interpolating them, since `OTEL_RESOURCE_ATTRIBUTES` [reserves commas, spaces, and equals signs](/docs/en/monitoring-usage#multi-team-organization-support). The following example attaches the requesting user and tenant to every span and event from one request. It assumes a `request` object from your web framework carrying the user and tenant IDs:
<CodeGroup>
```python Python theme={null}
from urllib.parse import quote
options = ClaudeAgentOptions(
env={
# ... exporter configuration from the Enable telemetry export example ...
# request is the incoming request object from your web framework.
"OTEL_RESOURCE_ATTRIBUTES": f"enduser.id={quote(request.user_id)},tenant.id={quote(request.tenant_id)}",
},
)
```
```typescript TypeScript theme={null}
const options = {
env: {
...process.env,
// ... exporter configuration from the Enable telemetry export example ...
// request is the incoming request object from your web framework.
OTEL_RESOURCE_ATTRIBUTES: `enduser.id=${encodeURIComponent(request.userId)},tenant.id=${encodeURIComponent(request.tenantId)}`,
},
};
```
</CodeGroup>
With end-user identity attached, the `tool_decision`, `tool_result`, `mcp_server_connection`, and `permission_mode_changed` events, which export as log records named with a `claude_code.` prefix, become a per-user audit trail you can forward to a Security Information and Event Management (SIEM) platform. See [Audit security events](/docs/en/monitoring-usage#audit-security-events) in the Monitoring reference for the full list of security-relevant events and the attributes each one carries.
## Control sensitive data in exports
Telemetry is structural by default. Durations, model names, and tool names are recorded on every span; token counts are recorded when the underlying API request returns usage data, so spans for failed or aborted requests may omit them. The content your agent reads and writes is not recorded by default. These opt-in variables add content to the exported data:
| Variable | Adds |
| ------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `OTEL_LOG_USER_PROMPTS=1` | Prompt text on `claude_code.user_prompt` events and on the `claude_code.interaction` span |
| `OTEL_LOG_TOOL_DETAILS=1` | Tool input arguments (file paths, shell commands, search patterns) on `claude_code.tool_result` events |
| `OTEL_LOG_TOOL_CONTENT=1` | Full tool input and output bodies as span events on `claude_code.tool`, truncated at 60 KB by default, configurable via `CLAUDE_CODE_OTEL_CONTENT_MAX_LENGTH`, which requires Claude Code v2.1.214 or later. Requires [tracing](#read-agent-traces) to be enabled |
| `OTEL_LOG_RAW_API_BODIES` | Full Anthropic Messages API request and response JSON as `claude_code.api_request_body` and `claude_code.api_response_body` log events. Set to `1` for inline bodies truncated at 60 KB by default, or `file:<dir>` for untruncated bodies on disk with a `body_ref` path in the event. `CLAUDE_CODE_OTEL_CONTENT_MAX_LENGTH` configures the inline truncation limit, and requires Claude Code v2.1.214 or later. Bodies include the entire conversation history and have extended-thinking content redacted. Enabling this implies consent to everything the three variables above would reveal |
Leave these unset unless your observability pipeline is approved to store the data your agent handles. See [Security and privacy](/docs/en/monitoring-usage#security-and-privacy) in the Monitoring reference for the full list of attributes and redaction behavior.
## Related documentation
These guides cover adjacent topics for monitoring and deploying agents:
* [Track cost and usage](/docs/en/agent-sdk/cost-tracking): read token and cost data from the message stream without an external backend.
* [Hosting the Agent SDK](/docs/en/agent-sdk/hosting): deploy agents in containers where you can set OpenTelemetry variables at the environment level.
* [Monitoring](/docs/en/monitoring-usage): the complete reference for every environment variable, metric, and event the CLI emits.
agent-sdk/overview First recorded · 87 lines, first recorded
# Agent SDK overview ## Compare the Agent SDK to other Claude tools ## Capabilities ## Get started ## Changelog ## Report bugs ## Branding guidelines ## License and terms ## Next steps
The first capture of this source. The page was already there, and this is what it said.
# Agent SDK overview
> Build production AI agents with Claude Code as a library
An agent is an application that completes a task by planning its own steps and calling tools that read files, run commands, or edit code. The Agent SDK gives you the same tools, [agent loop](/docs/en/agent-sdk/agent-loop), and context management that power Claude Code, programmable in Python and TypeScript.
## Compare the Agent SDK to other Claude tools
The Agent SDK, the CLI, the Client SDK, and Managed Agents each fit different needs. Use the table to find the one that matches what you're building.
| If you're... | Use | Why |
| ------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------- |
| Building an agent without implementing the tool loop yourself | **Agent SDK** | A library that runs the agent loop in your own process, in Python or TypeScript. |
| Doing interactive development or running one-off tasks from a terminal | [**Claude Code CLI**](/docs/en/overview) | The terminal interface, built for daily interactive use. |
| Calling the API directly and implementing the tool loop yourself | [**Client SDK**](https://platform.claude.com/docs/en/api/client-sdks) | Direct access to the Anthropic API rather than to Claude Code. You implement the tool loop yourself. |
| Running long-running or asynchronous agents without managing your own sandbox or session infrastructure | [**Managed Agents**](https://platform.claude.com/docs/en/managed-agents/overview) | Hosted REST API, a separate product from the Agent SDK. Anthropic runs the agent and the sandbox. |
The SDK is available as a library for Python and TypeScript only. To drive the same agent loop from another language, [run the CLI as a subprocess](/docs/en/headless) with the `-p` flag and `--output-format json`.
## Capabilities
Everything that makes Claude Code powerful is available in the SDK.
| Capability | What it does | Learn more |
| ---------------------------- | -------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Built-in tools | Read, write, edit files, run commands, and search the web | [Tools reference](/docs/en/tools-reference) |
| Hooks | Run custom code at key points in the agent lifecycle | [Hooks](/docs/en/agent-sdk/hooks) |
| Subagents | Spawn specialized agents for focused subtasks | [Subagents](/docs/en/agent-sdk/subagents) |
| MCP | Connect external tools and data sources via the Model Context Protocol | [MCP](/docs/en/agent-sdk/mcp) |
| Permissions | Control which tools run automatically, which need approval | [Permissions](/docs/en/agent-sdk/permissions) |
| Sessions | Maintain context across exchanges, resume or fork later | [Sessions](/docs/en/agent-sdk/sessions) |
| Skills, commands, and memory | Load automatically from your project's `.claude/` and from `~/.claude/`, same as Claude Code | [Skills](/docs/en/agent-sdk/skills), [Commands](/docs/en/agent-sdk/slash-commands), [Memory](/docs/en/agent-sdk/modifying-system-prompts), [Configuration loading](/docs/en/agent-sdk/claude-code-features) |
| Plugins | Package skills, agents, hooks, and MCP servers, and load them by local path | [Plugins](/docs/en/agent-sdk/plugins) |
## Get started
Follow the [Quickstart](/docs/en/agent-sdk/quickstart) to install the SDK, set your API key, and build your first agent, one that finds and fixes bugs in existing code.
<Note>
Unless previously approved, Anthropic does not allow third party developers to offer claude.ai login or rate limits for their products, including agents built on the Claude Agent SDK. Use the API key authentication methods described in the [Quickstart](/docs/en/agent-sdk/quickstart) instead.
</Note>
## Changelog
View the full changelog for SDK updates, bug fixes, and new features:
* **TypeScript SDK**: [view CHANGELOG.md](https://github.com/anthropics/claude-agent-sdk-typescript/blob/main/CHANGELOG.md)
* **Python SDK**: [view CHANGELOG.md](https://github.com/anthropics/claude-agent-sdk-python/blob/main/CHANGELOG.md)
## Report bugs
If you encounter bugs or issues with the Agent SDK:
* **TypeScript SDK**: [report issues on GitHub](https://github.com/anthropics/claude-agent-sdk-typescript/issues)
* **Python SDK**: [report issues on GitHub](https://github.com/anthropics/claude-agent-sdk-python/issues)
## Branding guidelines
For partners integrating the Claude Agent SDK, use of Claude branding is optional. When referencing Claude in your product:
**Allowed:**
* "Claude Agent", preferred for dropdown menus
* "Claude", when within a menu already labeled "Agents"
* "\{YourAgentName} Powered by Claude", if you have an existing agent name
**Not permitted:**
* "Claude Code" or "Claude Code Agent"
* Claude Code-branded ASCII art or visual elements that mimic Claude Code
Your product should maintain its own branding and not appear to be Claude Code or any Anthropic product. For questions about branding compliance, contact the Anthropic [sales team](https://www.anthropic.com/contact-sales).
## License and terms
Use of the Claude Agent SDK is governed by [Anthropic's Commercial Terms of Service](https://www.anthropic.com/legal/commercial-terms), including when you use it to power products and services that you make available to your own customers and end users, except to the extent a specific component or dependency is covered by a different license as indicated in that component's LICENSE file.
## Next steps
These resources cover deeper technical detail and example projects for building with the Agent SDK.
* [Quickstart](/docs/en/agent-sdk/quickstart): build your first agent that finds and fixes bugs
* [Agent loop](/docs/en/agent-sdk/agent-loop): how Claude plans, calls tools, and decides when a task is done
* [Example agents](https://github.com/anthropics/claude-agent-sdk-demos): demo apps for local development
* [TypeScript SDK](/docs/en/agent-sdk/typescript): full TypeScript API reference and examples
* [Python SDK](/docs/en/agent-sdk/python): full Python API reference and examples
* [Agent harness design](https://claude.com/blog/a-harness-for-every-task-dynamic-workflows-in-claude-code): how the Claude Code team uses dynamic workflows to orchestrate subagents at scale
agent-sdk/permissions First recorded · 277 lines, first recorded
# Configure permissions ## How permissions are evaluated ## Allow and deny rules ## Permission modes ### Available modes ### Set permission mode ### Mode details #### Accept edits mode (`acceptEdits`) #### Don't ask mode (`dontAsk`) #### Bypass permissions mode (`bypassPermissions`) #### Plan mode (`plan`) ## Related resources
The first capture of this source. The page was already there, and this is what it said.
# Configure permissions
> Control how your agent uses tools with permission modes, hooks, and declarative allow/deny rules.
The Claude Agent SDK provides permission controls to manage how Claude uses tools. Use permission modes and rules to define what's allowed automatically, and the [`canUseTool` callback](/docs/en/agent-sdk/user-input) to handle everything else at runtime.
## How permissions are evaluated
When Claude requests a tool, the SDK checks permissions in this order:
<Steps>
<Step title="Hooks">
Run [hooks](/docs/en/agent-sdk/hooks) first. A hook can deny the call outright or pass it on. A hook that returns `allow` does not skip the deny and ask rules below; those are evaluated regardless of the hook result.
</Step>
<Step title="Deny rules">
Check `deny` rules (from `disallowed_tools` and [settings.json](/docs/en/settings#permission-settings)). If a deny rule matches, the tool is blocked, even in `bypassPermissions` mode. Bare-name deny rules like `Bash` remove the tool from Claude's context before this evaluation begins, so only scoped rules like `Bash(rm *)` are checked at this step.
</Step>
<Step title="Ask rules">
Check `ask` rules from [settings.json](/docs/en/settings#permission-settings). If an ask rule matches, the call falls through to your [`canUseTool` callback](/docs/en/agent-sdk/user-input) for confirmation, even in `bypassPermissions` mode.
Tools that require user interaction behave the same way: `AskUserQuestion` and MCP tools whose server sets [`_meta["anthropic/requiresUserInteraction"]`](/docs/en/mcp#require-approval-for-a-specific-tool) always fall through to the callback, even when an allow rule matches. In `dontAsk` mode both cases are denied instead, because that mode never prompts. The MCP annotation requires Claude Code v2.1.199 or later.
[claude.ai connector](/docs/en/mcp#organization-controls-on-connector-tools) tools your organization has set to `ask` also leave the flow at this step. Every call falls through to the callback, even in `bypassPermissions` mode and even when an allow rule matches. The callback receives the reason `Your organization requires approval for this tool`. In `dontAsk` mode the call is denied instead, because that mode never prompts.
</Step>
<Step title="Permission mode">
Apply the active [permission mode](#permission-modes). `bypassPermissions` approves everything that reaches this step. `acceptEdits` approves file operations. `plan` routes file-edit and shell-write tools to your `canUseTool` callback regardless of allow rules, so write operations cannot be auto-approved while planning. Other modes fall through.
</Step>
<Step title="Allow rules">
Check `allow` rules (from `allowed_tools` and settings.json). If a rule matches, the tool is approved.
</Step>
<Step title="canUseTool callback">
If not resolved by any of the above, call your [`canUseTool` callback](/docs/en/agent-sdk/user-input) for a decision. In `dontAsk` mode, this step is skipped and the tool is denied.
</Step>
</Steps>
<img src="https://mintcdn.com/claude-code/jYgs7qigNjO1Badj/images/agent-sdk/permissions-flow.svg?fit=max&auto=format&n=jYgs7qigNjO1Badj&q=85&s=c771ad9085b1277d3708027a49c744bc" className="dark:hidden" alt="Diagram of the six-step permission evaluation flow matching the steps above: a tool request passes through hooks, deny rules, ask rules, permission mode, allow rules, and canUseTool. Hooks, deny rules, and canUseTool can route down to Blocked; permission mode bypass, allow rules, and canUseTool can route up to Execute; ask rules route to canUseTool." width="1180" height="260" data-path="images/agent-sdk/permissions-flow.svg" />
<img src="https://mintcdn.com/claude-code/_xqph1dUOslCOwsj/images/agent-sdk/permissions-flow-dark.svg?fit=max&auto=format&n=_xqph1dUOslCOwsj&q=85&s=e53a91e9059cbf51852b7cedb4dd4251" className="hidden dark:block" alt="Diagram of the six-step permission evaluation flow matching the steps above: a tool request passes through hooks, deny rules, ask rules, permission mode, allow rules, and canUseTool. Hooks, deny rules, and canUseTool can route down to Blocked; permission mode bypass, allow rules, and canUseTool can route up to Execute; ask rules route to canUseTool." width="1180" height="260" data-path="images/agent-sdk/permissions-flow-dark.svg" />
If you pass a `canUseTool` callback that this evaluation order can never reach, the TypeScript SDK emits a Node.js process warning once when the query is constructed. The warning's code is `CLAUDE_SDK_CAN_USE_TOOL_SHADOWED`. Two configurations trigger it:
* `permissionMode: 'bypassPermissions'`, which auto-approves every call that reaches the permission mode step
* Each bare `allowedTools` entry such as `"Read"`, which auto-approves that whole tool before the callback is consulted
Entries with a specifier such as `Bash(ls *)` and the `acceptEdits` mode don't trigger it, and allow rules coming from settings files aren't visible to the check.
Listen with `process.on('warning', ...)` and match the code to log or suppress it. To gate every tool call regardless of mode and rules, use a [`PreToolUse` hook](/docs/en/agent-sdk/hooks) instead.
This page focuses on **allow and deny rules** and **permission modes**. For the other steps:
* **Hooks:** run custom code to allow, deny, or modify tool requests. See [Control execution with hooks](/docs/en/agent-sdk/hooks).
* **canUseTool callback:** prompt users for approval at runtime, when no earlier step resolves the call. See [Handle approvals and user input](/docs/en/agent-sdk/user-input).
## Allow and deny rules
`allowed_tools` and `disallowed_tools` (TypeScript: `allowedTools` / `disallowedTools`) add entries to the allow and deny rule lists in the evaluation flow above. Allow rules only affect approval: a tool not listed in `allowed_tools` is still available to Claude and falls through to the permission mode. Deny rules behave differently depending on whether they name a tool or scope a pattern within one.
| Option | Effect |
| :-------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `allowed_tools=["Read", "Grep"]` | `Read` and `Grep` are auto-approved. Tools not listed here still exist and fall through to the permission mode and `canUseTool`. |
| `disallowed_tools=["Bash"]` | The `Bash` tool definition is removed from the request. Claude does not see the tool and cannot attempt it. |
| `disallowed_tools=["Bash(rm *)"]` | `Bash` stays available. Calls matching `rm *` are denied in every permission mode, including `bypassPermissions`. Other `Bash` calls fall through to the permission mode. |
| `disallowed_tools=["*"]` | Every tool definition is removed from the request. Tool-name globs are supported in deny rules: `"*"` matches every tool and `"mcp__*"` matches every MCP tool across all servers. |
Allow rules accept tool-name globs only after a literal `mcp__<server>__` prefix. The server segment must be glob-free so the rule names a specific server you configured: `mcp__puppeteer__*` matches every tool from the `puppeteer` server, and `mcp__github__get_*` matches its `get_` tools. An unanchored entry like `allowed_tools=["*"]` or `allowed_tools=["mcp__*"]` is ignored with a startup warning and does not auto-approve anything.
Scoped rules for `Read` and `Edit` take a path pattern. `Edit(path)` rules govern all built-in tools that write files, including `Write` and `NotebookEdit`; a `Write(path)` rule is never matched by the file permission checks.
Use `//path` for an absolute filesystem path: a deny rule of `Edit(//secrets/**)` blocks writes anywhere under `/secrets` on disk. With a single leading slash, `Edit(/secrets/**)` anchors at the rule's source instead. For rules passed through `allowed_tools` or `disallowed_tools`, that means the session's working directory, so the rule doesn't block `/secrets` on disk. See [Read and Edit rules](/docs/en/permissions#read-and-edit) for the four anchor forms and how rules from settings files resolve.
<Warning>
**Auto-approved tools never reach `canUseTool`.** A tool call approved at any earlier step, by `acceptEdits` or `bypassPermissions`, or by an allow rule, skips your `canUseTool` callback, so permission checks you put there are silently bypassed for that tool. `AskUserQuestion`, MCP tools marked [`_meta["anthropic/requiresUserInteraction"]`](/docs/en/mcp#require-approval-for-a-specific-tool), and connector tools [your organization set to `ask`](/docs/en/mcp#organization-controls-on-connector-tools) still reach the callback, even when an allow rule matches.
Coverage depends on the entry's form: a bare name like `Read` or `mcp__github__get_issue` auto-approves every call to that tool, while a scoped rule like `Bash(ls *)` auto-approves only matching calls and other `Bash` calls still fall through to the callback. For checks that must run on every tool call, use a [`PreToolUse` hook](/docs/en/agent-sdk/hooks): hooks run before every other step, and a hook deny applies even in `bypassPermissions` mode.
</Warning>
For a locked-down agent, pair `allowedTools` with `permissionMode: "dontAsk"`. Listed tools are approved, apart from the always-prompt tools in the Warning above; anything else is denied outright instead of prompting:
```typescript theme={null}
const options = {
allowedTools: ["Read", "Glob", "Grep"],
permissionMode: "dontAsk"
};
```
<Warning>
**`allowed_tools` does not constrain `bypassPermissions`.** `allowed_tools` only pre-approves the tools you list. Unlisted tools are not matched by any allow rule and fall through to the permission mode, where `bypassPermissions` approves them. Setting `allowed_tools=["Read"]` alongside `permission_mode="bypassPermissions"` still approves every tool, including `Bash`, `Write`, and `Edit`. If you need `bypassPermissions` but want specific tools blocked, use `disallowed_tools`.
</Warning>
You can also configure allow, deny, and ask rules declaratively in `.claude/settings.json`. These rules are read when the `project` setting source is enabled, which it is for default `query()` options. If you set `setting_sources` (TypeScript: `settingSources`) explicitly, include `"project"` for them to apply. See [Permission settings](/docs/en/settings#permission-settings) for the rule syntax.
## Permission modes
Permission modes provide global control over how Claude uses tools. You can set the permission mode when calling `query()` or change it dynamically during streaming sessions.
### Available modes
The SDK supports these permission modes:
| Mode | Description | Tool behavior |
| :------------------ | :--------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `default` | Standard permission behavior | No auto-approvals; unmatched tools trigger your `canUseTool` callback |
| `dontAsk` | Deny instead of prompting | Anything not pre-approved by `allowed_tools` or rules is denied; connector tools [your organization set to `ask`](/docs/en/mcp#organization-controls-on-connector-tools) and tools that require user interaction are denied even if you've pre-approved them. `canUseTool` is never called |
| `acceptEdits` | Auto-accept file edits | File edits and [filesystem operations](#accept-edits-mode-acceptedits) (`mkdir`, `rm`, `mv`, etc.) are automatically approved |
| `bypassPermissions` | Bypass permission checks | Tools run without permission prompts, except tools matched by an explicit [`ask` rule](#how-permissions-are-evaluated), connector tools [your organization set to `ask`](/docs/en/mcp#organization-controls-on-connector-tools), and tools that require user interaction. The [cross-session messaging safeguards](/docs/en/permission-modes#skip-all-checks-with-bypasspermissions-mode) still apply. Use with caution |
| `plan` | Planning mode | Claude explores and plans without editing your source files; file edits are never auto-approved and prompt through your `canUseTool` callback |
| `auto` | Model-classified approvals | A model classifier approves or denies permission prompts. See [Auto mode](/docs/en/permission-modes#eliminate-prompts-with-auto-mode) for availability |
<Warning>
**Subagent inheritance:** Subagents inherit the parent session's permission mode. An [`AgentDefinition`'s `permissionMode`](/docs/en/agent-sdk/typescript#agentdefinition) can override it, except when the parent uses `bypassPermissions`, `acceptEdits`, or `auto`: those modes apply to every subagent and can't be overridden per subagent. Claude Code also ignores a definition's `permissionMode: "bypassPermissions"` when bypass mode is disabled by [`permissions.disableBypassPermissionsMode`](/docs/en/permissions#managed-settings), so that subagent runs with the parent session's mode.
Subagents may have different system prompts and less constrained behavior than your main agent, so inheriting `bypassPermissions` grants them full, autonomous system access. Explicit [`ask` rules](#how-permissions-are-evaluated), connector tools [your organization set to `ask`](/docs/en/mcp#organization-controls-on-connector-tools), and tools that require user interaction still force a prompt, as does the [`isolatePeerMachines`](/docs/en/settings#available-settings) approval for cross-machine messages.
</Warning>
### Set permission mode
You can set the permission mode once when starting a query, or change it dynamically while the session is active.
<Tabs>
<Tab title="At query time">
Pass `permission_mode` (Python) or `permissionMode` (TypeScript) when creating a query. This mode applies for the entire session unless changed dynamically.
<CodeGroup>
```python Python theme={null}
import asyncio
from claude_agent_sdk import query, ClaudeAgentOptions
async def main():
async for message in query(
prompt="Help me refactor this code",
options=ClaudeAgentOptions(
permission_mode="default", # Set the mode here
),
):
if hasattr(message, "result"):
print(message.result)
asyncio.run(main())
```
```typescript TypeScript theme={null}
import { query } from "@anthropic-ai/claude-agent-sdk";
async function main() {
for await (const message of query({
prompt: "Help me refactor this code",
options: {
permissionMode: "default" // Set the mode here
}
})) {
if ("result" in message) {
console.log(message.result);
}
}
}
main();
```
</CodeGroup>
</Tab>
<Tab title="During streaming">
Call `set_permission_mode()` (Python) or `setPermissionMode()` (TypeScript) to change the mode mid-session. The new mode takes effect immediately for all subsequent tool requests. This lets you start restrictive and loosen permissions as trust builds, for example switching to `acceptEdits` after reviewing Claude's initial approach.
<CodeGroup>
```python Python theme={null}
import asyncio
from claude_agent_sdk import ClaudeSDKClient, ClaudeAgentOptions
async def main():
async with ClaudeSDKClient(
options=ClaudeAgentOptions(
permission_mode="default", # Start in default mode
)
) as client:
await client.query("Help me refactor this code")
# Change mode dynamically mid-session
await client.set_permission_mode("acceptEdits")
# Process messages with the new permission mode
async for message in client.receive_response():
if hasattr(message, "result"):
print(message.result)
asyncio.run(main())
```
```typescript TypeScript theme={null}
import { query } from "@anthropic-ai/claude-agent-sdk";
async function main() {
const q = query({
prompt: "Help me refactor this code",
options: {
permissionMode: "default" // Start in default mode
}
});
// Change mode dynamically mid-session
await q.setPermissionMode("acceptEdits");
// Process messages with the new permission mode
for await (const message of q) {
if ("result" in message) {
console.log(message.result);
}
}
}
main();
```
</CodeGroup>
</Tab>
</Tabs>
### Mode details
#### Accept edits mode (`acceptEdits`)
Auto-approves file operations so Claude can edit code without prompting. Other tools (like Bash commands that aren't filesystem operations) still require normal permissions.
**Auto-approved operations:**
* File edits (Edit, Write tools)
* Filesystem commands: `mkdir`, `touch`, `rm`, `rmdir`, `mv`, `cp`, `sed`
Both apply only to paths inside the working directory or `additionalDirectories`. Paths outside that scope and writes to protected paths still prompt.
**Use when:** you trust Claude's edits and want faster iteration, such as during prototyping or when working in an isolated directory.
#### Don't ask mode (`dontAsk`)
Converts any permission prompt into a denial. Tools pre-approved by `allowed_tools`, `settings.json` allow rules, or a hook run as normal. Connector tools [your organization set to `ask`](/docs/en/mcp#organization-controls-on-connector-tools) and tools that require user interaction are denied even when an allow rule matches. Everything else is denied without calling `canUseTool`.
**Use when:** you want a fixed, explicit tool surface for a headless agent and prefer a hard deny over silent reliance on `canUseTool` being absent.
#### Bypass permissions mode (`bypassPermissions`)
Auto-approves tool uses without prompting, except the cases listed in the warning below. Hooks still execute and can block operations if needed.
<Warning>
Use with extreme caution. Claude has full system access in this mode. Only use in controlled environments where you trust all possible operations.
`allowed_tools` does not constrain this mode. Every tool is approved, not just the ones you listed. These controls still apply:
* Deny rules, explicit `ask` rules, and hooks are evaluated before the mode check and can still block a tool.
* Connector tools [your organization set to `ask`](/docs/en/mcp#organization-controls-on-connector-tools) and tools that require user interaction still fall through to your `canUseTool` callback.
* The [cross-session messaging safeguards](/docs/en/permission-modes#skip-all-checks-with-bypasspermissions-mode) still apply.
</Warning>
#### Plan mode (`plan`)
Claude explores the codebase and produces a plan without editing your source files. Read-only tools run as in default mode.
File edits are never auto-approved in plan mode, even when an allow rule matches. They prompt through your `canUseTool` callback instead. On Claude Code v2.1.212 or later, shell commands that modify files, such as `touch` and `rm`, reach your `canUseTool` callback the same way.
Claude may use `AskUserQuestion` to clarify requirements before finalizing the plan. See [Handle approvals and user input](/docs/en/agent-sdk/user-input#handle-clarifying-questions) for handling these prompts.
**Use when:** you want Claude to propose changes without executing them, such as during code review or when you need to approve changes before they're made.
## Related resources
For the other steps in the permission evaluation flow:
* [Handle approvals and user input](/docs/en/agent-sdk/user-input): interactive approval prompts and clarifying questions
* [Hooks guide](/docs/en/agent-sdk/hooks): run custom code at key points in the agent lifecycle
* [Permission rules](/docs/en/settings#permission-settings): declarative allow/deny rules in `settings.json`
agent-sdk/plugins First recorded · 333 lines, first recorded
# Plugins in the SDK ## Loading plugins ### Path specifications ## Verifying plugin installation ## Using plugin skills ## Complete example ## Plugin structure reference ## Multiple plugin sources ## Troubleshooting ### Plugin not loading ### Skills not appearing ## See also
The first capture of this source. The page was already there, and this is what it said.
# Plugins in the SDK
> Load custom plugins to extend Claude Code with skills, agents, hooks, and MCP servers through the Agent SDK
Plugins allow you to extend Claude Code with custom functionality that can be shared across projects. Through the Agent SDK, you can programmatically load plugins from local directories to add capabilities to your agent sessions. A plugin can include:
* **Skills**: capabilities Claude invokes autonomously when relevant. You can also invoke a plugin skill directly with `/plugin-name:skill-name`.
* **Agents**: specialized subagents for specific tasks
* **Hooks**: event handlers that respond to tool use and other events
* **MCP servers**: external tool integrations via Model Context Protocol
For complete information on plugin structure and how to create plugins, see [Plugins](/docs/en/plugins).
## Loading plugins
Load plugins by providing their local file system paths in your options configuration. The `type` field must be `"local"`, the only value the SDK accepts. The SDK supports loading multiple plugins from different locations.
To use a plugin distributed through a [marketplace](/docs/en/plugin-marketplaces) or remote repository, download it first and provide the local directory path. For the directory layout a plugin needs, see the [Plugin structure reference](#plugin-structure-reference) below.
<CodeGroup>
```typescript TypeScript theme={null}
import { query } from "@anthropic-ai/claude-agent-sdk";
for await (const message of query({
prompt: "Hello",
options: {
plugins: [
{ type: "local", path: "./my-plugin" },
{ type: "local", path: "/absolute/path/to/another-plugin" }
]
}
})) {
// Plugin commands, agents, and other features are now available
}
```
```python Python theme={null}
import asyncio
from claude_agent_sdk import query, ClaudeAgentOptions
async def main():
async for message in query(
prompt="Hello",
options=ClaudeAgentOptions(
plugins=[
{"type": "local", "path": "./my-plugin"},
{"type": "local", "path": "/absolute/path/to/another-plugin"},
]
),
):
# Plugin commands, agents, and other features are now available
pass
asyncio.run(main())
```
</CodeGroup>
### Path specifications
Plugin paths can be:
* **Relative paths**: resolved relative to your current working directory (for example, `"./plugins/my-plugin"`)
* **Absolute paths**: full file system paths (for example, `"/home/user/plugins/my-plugin"`)
<Note>
The path should point to the plugin's root directory: the parent of `skills/`, `agents/`, `hooks/`, `commands/`, or `.claude-plugin/`.
</Note>
## Verifying plugin installation
When plugins load successfully, they appear in the system initialization message. You can verify that your plugins are available:
<CodeGroup>
```typescript TypeScript theme={null}
import { query } from "@anthropic-ai/claude-agent-sdk";
for await (const message of query({
prompt: "Hello",
options: {
plugins: [{ type: "local", path: "./my-plugin" }]
}
})) {
if (message.type === "system" && message.subtype === "init") {
// Check loaded plugins
console.log("Plugins:", message.plugins);
// Example: [{ name: "my-plugin", path: "/absolute/path/to/my-plugin" }]
// Plugin skills appear with the plugin name as a prefix
console.log("Skills:", message.skills);
// Example: ["my-plugin:greet"]
// Plugin commands use the same prefix, and skills appear here too
console.log("Commands:", message.slash_commands);
// Example: ["compact", "context", "my-plugin:custom-command", "my-plugin:greet"]
}
}
```
```python Python theme={null}
import asyncio
from claude_agent_sdk import query, ClaudeAgentOptions, SystemMessage
async def main():
async for message in query(
prompt="Hello",
options=ClaudeAgentOptions(
plugins=[{"type": "local", "path": "./my-plugin"}]
),
):
if isinstance(message, SystemMessage) and message.subtype == "init":
# Check loaded plugins
print("Plugins:", message.data.get("plugins"))
# Example: [{"name": "my-plugin", "path": "/absolute/path/to/my-plugin"}]
# Plugin skills appear with the plugin name as a prefix
print("Skills:", message.data.get("skills"))
# Example: ["my-plugin:greet"]
# Plugin commands use the same prefix, and skills appear here too
print("Commands:", message.data.get("slash_commands"))
# Example: ["compact", "context", "my-plugin:custom-command", "my-plugin:greet"]
asyncio.run(main())
```
</CodeGroup>
## Using plugin skills
Skills from plugins are automatically namespaced with the plugin name to avoid conflicts. To invoke one directly, send `/plugin-name:skill-name` as the prompt.
<CodeGroup>
```typescript TypeScript theme={null}
import { query } from "@anthropic-ai/claude-agent-sdk";
// Load a plugin with a custom /greet skill
for await (const message of query({
prompt: "/my-plugin:greet", // Use plugin skill with namespace
options: {
plugins: [{ type: "local", path: "./my-plugin" }]
}
})) {
// Claude executes the custom greeting skill from the plugin
if (message.type === "assistant") {
console.log(message.message.content);
}
}
```
```python Python theme={null}
import asyncio
from claude_agent_sdk import query, ClaudeAgentOptions, AssistantMessage, TextBlock
async def main():
# Load a plugin with a custom /greet skill
async for message in query(
prompt="/my-plugin:greet", # Use plugin skill with namespace
options=ClaudeAgentOptions(
plugins=[{"type": "local", "path": "./my-plugin"}]
),
):
# Claude executes the custom greeting skill from the plugin
if isinstance(message, AssistantMessage):
for block in message.content:
if isinstance(block, TextBlock):
print(f"Claude: {block.text}")
asyncio.run(main())
```
</CodeGroup>
<Note>
If you installed a plugin via the CLI (for example, `/plugin install my-plugin@marketplace`), you can still use it in the SDK by providing its installation path. Check `~/.claude/plugins/` for CLI-installed plugins.
</Note>
## Complete example
Here's a full example demonstrating plugin loading and usage:
<CodeGroup>
```typescript TypeScript theme={null}
import { query } from "@anthropic-ai/claude-agent-sdk";
import { fileURLToPath } from "node:url";
async function runWithPlugin() {
const pluginPath = fileURLToPath(new URL("./plugins/my-plugin", import.meta.url));
console.log("Loading plugin from:", pluginPath);
for await (const message of query({
prompt: "What custom commands do you have available?",
options: {
plugins: [{ type: "local", path: pluginPath }],
maxTurns: 3
}
})) {
if (message.type === "system" && message.subtype === "init") {
console.log("Loaded plugins:", message.plugins);
console.log("Available skills:", message.skills);
console.log("Available commands:", message.slash_commands);
}
if (message.type === "assistant") {
console.log("Assistant:", message.message.content);
}
}
}
runWithPlugin().catch(console.error);
```
```python Python theme={null}
#!/usr/bin/env python3
"""Example demonstrating how to use plugins with the Agent SDK."""
import asyncio
from pathlib import Path
from claude_agent_sdk import (
AssistantMessage,
ClaudeAgentOptions,
SystemMessage,
TextBlock,
query,
)
async def run_with_plugin():
"""Example using a custom plugin."""
plugin_path = Path(__file__).parent / "plugins" / "my-plugin"
print(f"Loading plugin from: {plugin_path}")
options = ClaudeAgentOptions(
plugins=[{"type": "local", "path": str(plugin_path)}],
max_turns=3,
)
async for message in query(
prompt="What custom commands do you have available?", options=options
):
if isinstance(message, SystemMessage) and message.subtype == "init":
print(f"Loaded plugins: {message.data.get('plugins')}")
print(f"Available skills: {message.data.get('skills')}")
print(f"Available commands: {message.data.get('slash_commands')}")
if isinstance(message, AssistantMessage):
for block in message.content:
if isinstance(block, TextBlock):
print(f"Assistant: {block.text}")
if __name__ == "__main__":
asyncio.run(run_with_plugin())
```
</CodeGroup>
## Plugin structure reference
A plugin directory typically contains a `.claude-plugin/plugin.json` manifest file. The manifest is optional. When omitted, Claude Code auto-discovers components from the directory layout. The directory can include:
```text theme={null}
my-plugin/
├── .claude-plugin/
│ └── plugin.json # Plugin manifest (optional, components auto-discovered without it)
├── skills/ # Agent Skills (invoked autonomously or via /plugin-name:skill-name)
│ └── my-skill/
│ └── SKILL.md
├── commands/ # Skills as flat .md files
│ └── custom-cmd.md
├── agents/ # Custom agents
│ └── specialist.md
├── hooks/ # Event handlers
│ └── hooks.json
└── .mcp.json # MCP server definitions
```
<Note>
The `commands/` directory holds skills as flat Markdown files. Use `skills/` for new plugins. Claude Code supports both locations.
</Note>
## Multiple plugin sources
Combine plugins from different locations:
```typescript theme={null}
import * as os from "node:os";
import * as path from "node:path";
plugins: [
{ type: "local", path: "./local-plugin" },
{
type: "local",
path: path.join(os.homedir(), ".claude", "custom-plugins", "shared-plugin")
}
Cut at 300 lines. The page has the rest.
agent-sdk/python First recorded · 3633 lines, first recorded
# Agent SDK reference - Python ## Installation ## Choosing between `query()` and `ClaudeSDKClient` ## Functions ### `query()` #### Parameters #### Returns #### Example - With options ### `tool()` #### Parameters #### Input schema options #### Returns #### Example #### `ToolAnnotations` ### `create_sdk_mcp_server()` #### Parameters #### Returns #### Example ### `list_sessions()` #### Parameters #### Return type: `SDKSessionInfo` #### Example ### `get_session_messages()` #### Parameters #### Return type: `SessionMessage` #### Example ### `get_session_info()` #### Parameters #### Example ### `rename_session()` #### Parameters #### Example ### `tag_session()` #### Parameters #### Example ## Classes ### `ClaudeSDKClient` #### Methods #### Context Manager Support #### Example - Continuing a conversation #### Example - Streaming input with ClaudeSDKClient #### Example - Using interrupts #### Example - Advanced permission control ## Types ### `SdkMcpTool` ### `Transport` ### `ClaudeAgentOptions` #### Handle slow or stalled API responses ### `OutputFormat` ### `SystemPromptPreset` ### `SystemPromptFile` ### `SettingSource` #### Default behavior #### Why use setting\_sources #### Settings precedence ### `AgentDefinition` ### `PermissionMode` ### `EffortLevel` ### `CanUseTool` ### `ToolPermissionContext` ### `PermissionResult` ### `PermissionResultAllow` ### `PermissionResultDeny` ### `PermissionUpdate` ### `PermissionRuleValue` ### `ToolsPreset` ### `ThinkingConfig` ### `TaskBudget` ### `SdkBeta` ### `McpSdkServerConfig` ### `McpServerConfig` #### `McpStdioServerConfig` #### `McpSSEServerConfig` #### `McpHttpServerConfig` ### `McpServerStatusConfig` ### `McpStatusResponse` ### `McpServerStatus` ### `SdkPluginConfig` ## Message Types ### `Message` ### `UserMessage` ### `AssistantMessage` ### `AssistantMessageError` ### `SystemMessage` ### `ResultMessage` ### `StreamEvent` ### `RateLimitEvent` ### `RateLimitInfo` ### `ConversationResetMessage` ### `TaskStartedMessage` ### `TaskUsage` ### `TaskProgressMessage` ### `TaskNotificationMessage` ## Content Block Types ### `ContentBlock` ### `TextBlock` ### `ThinkingBlock` ### `ToolUseBlock` ### `ToolResultBlock` ## Error Types ### `ClaudeSDKError` ### `CLINotFoundError` ### `CLIConnectionError` ### `ProcessError` ### `CLIJSONDecodeError` ## Hook Types ### `HookEvent` ### `HookCallback` ### `HookContext` ### `HookMatcher` ### `HookInput` ### `BaseHookInput` ### `PreToolUseHookInput` ### `PostToolUseHookInput` ### `PostToolUseFailureHookInput` ### `UserPromptSubmitHookInput` ### `StopHookInput` ### `SubagentStopHookInput` ### `PreCompactHookInput` ### `NotificationHookInput` ### `SubagentStartHookInput` ### `PermissionRequestHookInput` ### `HookJSONOutput` #### `SyncHookJSONOutput` #### `HookSpecificOutput` #### `AsyncHookJSONOutput` ### Hook Usage Example ## Tool Input/Output Types ### Agent ### AskUserQuestion ### Bash ### Monitor ### Edit ### Read ### Write ### Glob ### Grep ### NotebookEdit ### WebFetch ### WebSearch ### TodoWrite ### TaskCreate ### TaskUpdate ### TaskGet ### TaskList ### TaskOutput ### TaskStop ### ExitPlanMode ### ListMcpResources ### ReadMcpResource ## Advanced Features with ClaudeSDKClient ### Building a Continuous Conversation Interface ### Using Hooks for Behavior Modification ### Real-time Progress Monitoring ## Example Usage ### Basic file operations (using query) ### Error handling ### Using custom tools with ClaudeSDKClient ## Sandbox Configuration ### `SandboxSettings` #### Example usage ### `SandboxNetworkConfig` ### `SandboxIgnoreViolations` ### Permissions Fallback for Unsandboxed Commands ## See also
The first capture of this source. The page was already there, and this is what it said.
# Agent SDK reference - Python
> Complete API reference for the Python Agent SDK, including all functions, types, and classes.
## Installation
Install the package into a virtual environment. On recent Debian, Ubuntu, and Homebrew Python installs, running `pip install` against system Python fails with `error: externally-managed-environment`.
```bash theme={null}
python3 -m venv .venv
source .venv/bin/activate
pip install claude-agent-sdk
```
For uv, Windows PowerShell, and API key setup, see [Setup in the Agent SDK quickstart](/docs/en/agent-sdk/quickstart#setup).
## Choosing between `query()` and `ClaudeSDKClient`
The Python SDK provides two ways to interact with Claude Code:
| Feature | `query()` | `ClaudeSDKClient` |
| :------------------ | :--------------------------------------------- | :--------------------------------- |
| **Session** | Creates a new session by default | Reuses same session |
| **Conversation** | Single exchange | Multiple exchanges in same context |
| **Connection** | Managed automatically | Manual control |
| **Streaming Input** | ✅ Supported | ✅ Supported |
| **Interrupts** | ❌ Not supported | ✅ Supported |
| **Hooks** | ✅ Supported | ✅ Supported |
| **Custom Tools** | ✅ Supported | ✅ Supported |
| **Continue Chat** | Manual via `continue_conversation` or `resume` | ✅ Automatic |
| **Use Case** | One-off tasks | Continuous conversations |
Use `ClaudeSDKClient` for interactive applications such as chat interfaces, or when the next action depends on Claude's response.
## Functions
<Note>Signature blocks and bare `async for` / `async with` fragments on this page are illustrative. To run them, wrap the body in `async def main(): ...` and call `asyncio.run(main())`.</Note>
### `query()`
Creates a new session for each interaction with Claude Code by default. Returns an async iterator that yields messages as they arrive. Each call to `query()` starts fresh with no memory of previous interactions unless you pass `continue_conversation=True` or `resume` in [`ClaudeAgentOptions`](#claudeagentoptions). See [Sessions](/docs/en/agent-sdk/sessions).
```python theme={null}
async def query(
*,
prompt: str | AsyncIterable[dict[str, Any]],
options: ClaudeAgentOptions | None = None,
transport: Transport | None = None
) -> AsyncIterator[Message]
```
#### Parameters
| Parameter | Type | Description |
| :---------- | :--------------------------- | :------------------------------------------------------------------------- |
| `prompt` | `str \| AsyncIterable[dict]` | The input prompt as a string or async iterable for streaming mode |
| `options` | `ClaudeAgentOptions \| None` | Optional configuration object (defaults to `ClaudeAgentOptions()` if None) |
| `transport` | `Transport \| None` | Optional custom transport for communicating with the CLI process |
#### Returns
Returns an `AsyncIterator[Message]` that yields messages from the conversation.
#### Example - With options
```python theme={null}
import asyncio
from claude_agent_sdk import query, ClaudeAgentOptions
async def main():
options = ClaudeAgentOptions(
system_prompt="You are an expert Python developer",
permission_mode="acceptEdits",
)
async for message in query(prompt="Create a Python web server", options=options):
print(message)
asyncio.run(main())
```
### `tool()`
Decorator for defining MCP tools with type safety.
```python theme={null}
def tool(
name: str,
description: str,
input_schema: type | dict[str, Any],
annotations: ToolAnnotations | None = None
) -> Callable[[Callable[[Any], Awaitable[dict[str, Any]]]], SdkMcpTool[Any]]
```
#### Parameters
| Parameter | Type | Description |
| :------------- | :---------------------------------------------- | :------------------------------------------------------------------ |
| `name` | `str` | Unique identifier for the tool |
| `description` | `str` | Human-readable description of what the tool does |
| `input_schema` | `type \| dict[str, Any]` | Schema defining the tool's input parameters (see below) |
| `annotations` | [`ToolAnnotations`](#toolannotations)` \| None` | Optional MCP tool annotations providing behavioral hints to clients |
#### Input schema options
1. **Simple type mapping** (recommended):
```python theme={null}
{"text": str, "count": int, "enabled": bool}
```
2. **JSON Schema format** (for complex validation):
```python theme={null}
{
"type": "object",
"properties": {
"text": {"type": "string"},
"count": {"type": "integer", "minimum": 0},
},
"required": ["text"],
}
```
#### Returns
A decorator function that wraps the tool implementation and returns an `SdkMcpTool` instance.
#### Example
```python theme={null}
from claude_agent_sdk import tool
from typing import Any
@tool("greet", "Greet a user", {"name": str})
async def greet(args: dict[str, Any]) -> dict[str, Any]:
return {"content": [{"type": "text", "text": f"Hello, {args['name']}!"}]}
```
#### `ToolAnnotations`
Re-exported from `mcp.types` (also available as `from claude_agent_sdk import ToolAnnotations`). All fields are optional hints; clients should not rely on them for security decisions.
| Field | Type | Default | Description |
| :---------------- | :------------- | :------ | :--------------------------------------------------------------------------------------------------------------------------------------------------- |
| `title` | `str \| None` | `None` | Human-readable title for the tool |
| `readOnlyHint` | `bool \| None` | `False` | If `True`, the tool does not modify its environment |
| `destructiveHint` | `bool \| None` | `True` | If `True`, the tool may perform destructive updates (only meaningful when `readOnlyHint` is `False`) |
| `idempotentHint` | `bool \| None` | `False` | If `True`, repeated calls with the same arguments have no additional effect (only meaningful when `readOnlyHint` is `False`) |
| `openWorldHint` | `bool \| None` | `True` | If `True`, the tool interacts with external entities (for example, web search). If `False`, the tool's domain is closed (for example, a memory tool) |
```python theme={null}
from claude_agent_sdk import tool, ToolAnnotations
from typing import Any
@tool(
"search",
"Search the web",
{"query": str},
annotations=ToolAnnotations(readOnlyHint=True, openWorldHint=True),
)
async def search(args: dict[str, Any]) -> dict[str, Any]:
return {"content": [{"type": "text", "text": f"Results for: {args['query']}"}]}
```
### `create_sdk_mcp_server()`
Create an in-process MCP server that runs within your Python application.
```python theme={null}
def create_sdk_mcp_server(
name: str,
version: str = "1.0.0",
tools: list[SdkMcpTool[Any]] | None = None
) -> McpSdkServerConfig
```
#### Parameters
| Parameter | Type | Default | Description |
| :-------- | :------------------------------ | :-------- | :---------------------------------------------------- |
| `name` | `str` | - | Unique identifier for the server |
| `version` | `str` | `"1.0.0"` | Server version string |
| `tools` | `list[SdkMcpTool[Any]] \| None` | `None` | List of tool functions created with `@tool` decorator |
#### Returns
Returns an `McpSdkServerConfig` object that can be passed to `ClaudeAgentOptions.mcp_servers`.
#### Example
```python theme={null}
from claude_agent_sdk import tool, create_sdk_mcp_server, ClaudeAgentOptions
@tool("add", "Add two numbers", {"a": float, "b": float})
async def add(args):
return {"content": [{"type": "text", "text": f"Sum: {args['a'] + args['b']}"}]}
@tool("multiply", "Multiply two numbers", {"a": float, "b": float})
async def multiply(args):
return {"content": [{"type": "text", "text": f"Product: {args['a'] * args['b']}"}]}
calculator = create_sdk_mcp_server(
name="calculator",
version="2.0.0",
tools=[add, multiply], # Pass decorated functions
)
# Use with Claude
options = ClaudeAgentOptions(
mcp_servers={"calc": calculator},
allowed_tools=["mcp__calc__add", "mcp__calc__multiply"],
)
```
### `list_sessions()`
Lists past sessions with metadata. Filter by project directory or list sessions across all projects. Synchronous; returns immediately.
```python theme={null}
def list_sessions(
directory: str | None = None,
limit: int | None = None,
offset: int = 0,
include_worktrees: bool = True
) -> list[SDKSessionInfo]
```
#### Parameters
| Parameter | Type | Default | Description |
| :------------------ | :------------ | :------ | :----------------------------------------------------------------------------------------------- |
| `directory` | `str \| None` | `None` | Directory to list sessions for. When omitted, returns sessions across all projects |
| `limit` | `int \| None` | `None` | Maximum number of sessions to return |
| `offset` | `int` | `0` | Number of sessions to skip from the start of the sorted results. Use with `limit` for pagination |
| `include_worktrees` | `bool` | `True` | When `directory` is inside a git repository, include sessions from all worktree paths |
#### Return type: `SDKSessionInfo`
| Property | Type | Description |
| :-------------- | :------------ | :------------------------------------------------------------------- |
| `session_id` | `str` | Unique session identifier |
| `summary` | `str` | Display title: custom title, auto-generated summary, or first prompt |
| `last_modified` | `int` | Last modified time in milliseconds since epoch |
| `file_size` | `int \| None` | Session file size in bytes (`None` for remote storage backends) |
| `custom_title` | `str \| None` | User-set session title |
| `first_prompt` | `str \| None` | First meaningful user prompt in the session |
| `git_branch` | `str \| None` | Git branch at the end of the session |
| `cwd` | `str \| None` | Working directory for the session |
| `tag` | `str \| None` | User-set session tag (see [`tag_session()`](#tag_session)) |
| `created_at` | `int \| None` | Session creation time in milliseconds since epoch |
#### Example
Print the 10 most recent sessions for a project. Results are sorted by `last_modified` descending, so the first item is the newest. Omit `directory` to search across all projects.
```python theme={null}
from claude_agent_sdk import list_sessions
for session in list_sessions(directory="/path/to/project", limit=10):
print(f"{session.summary} ({session.session_id})")
```
### `get_session_messages()`
Retrieves messages from a past session. Synchronous; returns immediately.
```python theme={null}
def get_session_messages(
session_id: str,
directory: str | None = None,
limit: int | None = None,
offset: int = 0
) -> list[SessionMessage]
```
#### Parameters
| Parameter | Type | Default | Description |
| :----------- | :------------ | :------- | :---------------------------------------------------------------- |
| `session_id` | `str` | required | The session ID to retrieve messages for |
| `directory` | `str \| None` | `None` | Project directory to look in. When omitted, searches all projects |
| `limit` | `int \| None` | `None` | Maximum number of messages to return |
| `offset` | `int` | `0` | Number of messages to skip from the start |
#### Return type: `SessionMessage`
| Property | Type | Description |
| :------------------- | :----------------------------- | :------------------------ |
| `type` | `Literal["user", "assistant"]` | Message role |
| `uuid` | `str` | Unique message identifier |
| `session_id` | `str` | Session identifier |
| `message` | `Any` | Raw message content |
| `parent_tool_use_id` | `None` | Reserved for future use |
Cut at 300 lines. The page has the rest.
agent-sdk/quickstart First recorded · 362 lines, first recorded
# Quickstart ## Prerequisites ## Setup ## Create a buggy file ## Build an agent that finds and fixes bugs ### Run your agent ### Try other prompts ### Customize your agent ## Key concepts ## Next steps
The first capture of this source. The page was already there, and this is what it said.
# Quickstart
> Get started with the Python or TypeScript Agent SDK to build AI agents that work autonomously
Use the Agent SDK to build an AI agent that reads your code, finds bugs, and fixes them, all without manual intervention.
**What you'll do:**
1. Set up a project with the Agent SDK
2. Create a file with some buggy code
3. Run an agent that finds and fixes the bugs automatically
## Prerequisites
* **Node.js 18+** or **Python 3.10+**
* An **Anthropic account**. If you don't have one, [sign up here](https://platform.claude.com/).
## Setup
<Steps>
<Step title="Create a project folder">
Create a new directory for this quickstart:
```bash theme={null}
mkdir my-agent
cd my-agent
```
For your own projects, you can run the SDK from any folder; it will have access to files in that directory and its subdirectories by default.
</Step>
<Step title="Install the SDK">
Install the Agent SDK package for your language:
<Tabs>
<Tab title="TypeScript (new project)">
```bash theme={null}
npm init -y
npm pkg set type=module
npm install @anthropic-ai/claude-agent-sdk
npm install --save-dev tsx
```
Setting `"type": "module"` in `package.json` lets your agent script use top-level `await`, and [tsx](https://tsx.hirok.io) runs TypeScript files directly. npm prints `added N packages` when the install succeeds.
</Tab>
<Tab title="TypeScript (existing project)">
```bash theme={null}
npm install @anthropic-ai/claude-agent-sdk
npm install --save-dev tsx
```
[tsx](https://tsx.hirok.io) runs TypeScript files directly. If your project uses CommonJS, name your agent script `agent.mts` instead of `agent.ts`. The `.mts` extension makes tsx treat the file as an ES module, so top-level `await` works without converting your whole project to ES modules. Use `agent.mts` in place of `agent.ts` in the create and run steps later in this quickstart.
</Tab>
<Tab title="Python (uv)">
[uv](https://docs.astral.sh/uv/) is a fast Python package manager that handles virtual environments automatically:
```bash theme={null}
uv init
uv add claude-agent-sdk
```
</Tab>
<Tab title="Python (pip)">
Create and activate a virtual environment, then install the package.
On macOS or Linux:
```bash theme={null}
python3 -m venv .venv
source .venv/bin/activate
pip install claude-agent-sdk
```
On Windows:
```powershell theme={null}
py -m venv .venv
.venv\Scripts\Activate.ps1
pip install claude-agent-sdk
```
If PowerShell blocks `Activate.ps1` with an execution policy error, run `Set-ExecutionPolicy -Scope Process RemoteSigned` first.
</Tab>
</Tabs>
<Note>
Both the TypeScript and Python SDKs bundle a native Claude Code binary, so most installs need no separate Claude Code install. Some installs have no bundled binary:
* If pip installs the Python SDK's source distribution instead of a platform wheel, for example on ARM64 Windows, no binary is bundled. [Install Claude Code natively](/docs/en/setup#install-claude-code). The Python SDK finds it on your `PATH`.
* The TypeScript SDK installs its binary through npm optional dependencies, so an install that skips them, for example `npm ci --omit=optional`, gets no binary even on a supported platform. Reinstall without skipping optional dependencies, or [install Claude Code natively](/docs/en/setup#install-claude-code) and set `pathToClaudeCodeExecutable` to its path.
</Note>
</Step>
<Step title="Set your API key">
Get an API key from the [Claude Console](https://platform.claude.com/), then set it as an environment variable in the shell where you'll run your agent:
<Tabs>
<Tab title="macOS / Linux">
```bash theme={null}
export ANTHROPIC_API_KEY=your-api-key
```
</Tab>
<Tab title="Windows (PowerShell)">
```powershell theme={null}
$env:ANTHROPIC_API_KEY = "your-api-key"
```
</Tab>
</Tabs>
The SDK reads the key from the environment of the process that runs your agent; it doesn't load `.env` files automatically. If you keep the key in a `.env` file, load it yourself, for example with the `dotenv` package, before calling the SDK.
The SDK also supports authentication via third-party API providers:
* **Amazon Bedrock**: set `CLAUDE_CODE_USE_BEDROCK=1` environment variable and configure AWS credentials
* **Claude Platform on AWS**: set `CLAUDE_CODE_USE_ANTHROPIC_AWS=1` and `ANTHROPIC_AWS_WORKSPACE_ID`, then configure AWS credentials
* **Google Cloud's Agent Platform**: set `CLAUDE_CODE_USE_VERTEX=1` environment variable and configure Google Cloud credentials
* **Microsoft Foundry**: set `CLAUDE_CODE_USE_FOUNDRY=1` environment variable and configure Azure credentials
See the setup guides for [Amazon Bedrock](/docs/en/amazon-bedrock), [Claude Platform on AWS](/docs/en/claude-platform-on-aws), [Google Cloud's Agent Platform](/docs/en/google-vertex-ai), or [Microsoft Foundry](/docs/en/microsoft-foundry) for details.
<Note>
Unless previously approved, Anthropic does not allow third party developers to offer claude.ai login or rate limits for their products, including agents built on the Claude Agent SDK. Please use the API key authentication methods described in this document instead.
</Note>
</Step>
</Steps>
## Create a buggy file
This quickstart walks you through building an agent that can find and fix bugs in code. First, you need a file with some intentional bugs for the agent to fix. Create `utils.py` in the `my-agent` directory and paste the following code:
```python theme={null}
def calculate_average(numbers):
total = 0
for num in numbers:
total += num
return total / len(numbers)
def get_user_name(user):
return user["name"].upper()
```
This code has two bugs:
1. `calculate_average([])` crashes with division by zero
2. `get_user_name(None)` crashes with a TypeError
## Build an agent that finds and fixes bugs
Create `agent.py` if you're using the Python SDK, or `agent.ts` for TypeScript. Use `agent.mts` instead if your existing project uses CommonJS:
<CodeGroup>
```python Python theme={null}
import asyncio
from claude_agent_sdk import query, ClaudeAgentOptions, AssistantMessage, ResultMessage
async def main():
# Agentic loop: streams messages as Claude works
async for message in query(
prompt="Review utils.py for bugs that would cause crashes. Fix any issues you find.",
options=ClaudeAgentOptions(
allowed_tools=["Read", "Edit", "Glob"], # Auto-approve these tools
permission_mode="acceptEdits", # Auto-approve file edits
),
):
# Print human-readable output
if isinstance(message, AssistantMessage):
for block in message.content:
if hasattr(block, "text"):
print(block.text) # Claude's reasoning
elif hasattr(block, "name"):
print(f"Tool: {block.name}") # Tool being called
elif isinstance(message, ResultMessage):
print(f"Done: {message.subtype}") # Final result
asyncio.run(main())
```
```typescript TypeScript theme={null}
import { query } from "@anthropic-ai/claude-agent-sdk";
// Agentic loop: streams messages as Claude works
for await (const message of query({
prompt: "Review utils.py for bugs that would cause crashes. Fix any issues you find.",
options: {
allowedTools: ["Read", "Edit", "Glob"], // Auto-approve these tools
permissionMode: "acceptEdits" // Auto-approve file edits
}
})) {
// Print human-readable output
if (message.type === "assistant" && message.message?.content) {
for (const block of message.message.content) {
if ("text" in block) {
console.log(block.text); // Claude's reasoning
} else if ("name" in block) {
console.log(`Tool: ${block.name}`); // Tool being called
}
}
} else if (message.type === "result") {
console.log(`Done: ${message.subtype}`); // Final result
}
}
```
</CodeGroup>
This code has three main parts:
1. **`query`**: the main entry point that creates the agentic loop. It returns an async iterator, so you use `async for` to stream messages as Claude works. See the full API in the [Python](/docs/en/agent-sdk/python#query) or [TypeScript](/docs/en/agent-sdk/typescript#query) SDK reference.
2. **`prompt`**: what you want Claude to do. Claude figures out which tools to use based on the task.
3. **`options`**: configuration for the agent. This example uses `allowedTools` to pre-approve `Read`, `Edit`, and `Glob`, and `permissionMode: "acceptEdits"` to auto-approve file changes. Other options include `systemPrompt`, `mcpServers`, and more. See all options for [Python](/docs/en/agent-sdk/python#claudeagentoptions) or [TypeScript](/docs/en/agent-sdk/typescript#options).
The `async for` loop keeps running as Claude thinks, calls tools, observes results, and decides what to do next. Each iteration yields a message: Claude's reasoning, a tool call, a tool result, or the final outcome. The SDK handles the orchestration, tool execution, context management, and retries, so you consume the stream. The loop ends when Claude finishes the task or hits an error.
The message handling inside the loop filters for human-readable output. Without filtering, you'd see raw message objects including system initialization and internal state, which is useful for debugging but noisy otherwise.
<Note>
This example uses streaming to show progress in real-time. If you don't need live output (e.g., for background jobs or CI pipelines), you can collect all messages at once. See [Streaming vs. single-turn mode](/docs/en/agent-sdk/streaming-vs-single-mode) for details.
</Note>
### Run your agent
Your agent is ready. Run it with the following command:
<Tabs>
<Tab title="TypeScript">
```bash theme={null}
npx tsx agent.ts
```
If you named your script `agent.mts`, run `npx tsx agent.mts` instead.
</Tab>
<Tab title="Python (uv)">
```bash theme={null}
uv run agent.py
```
</Tab>
<Tab title="Python (pip)">
With your virtual environment still activated:
```bash theme={null}
python agent.py
```
</Tab>
</Tabs>
As it works, the agent prints its reasoning and each tool it calls, ending with `Done: success`. After running, check `utils.py`. You'll see defensive code handling empty lists and null users. Your agent autonomously:
1. **Read** `utils.py` to understand the code
2. **Analyzed** the logic and identified edge cases that would crash
3. **Edited** the file to add proper error handling
This is what makes the Agent SDK different: Claude executes tools directly instead of asking you to implement them.
<Note>
If you see "API key not found", make sure you've set the `ANTHROPIC_API_KEY` environment variable in the shell where you run your agent. The SDK doesn't load `.env` files automatically. See the [full troubleshooting guide](/docs/en/troubleshooting) for more help.
</Note>
### Try other prompts
Now that your agent is set up, try some different prompts:
* `"Add docstrings to all functions in utils.py"`
* `"Add type hints to all functions in utils.py"`
* `"Create a README.md documenting the functions in utils.py"`
### Customize your agent
You can modify your agent's behavior by changing the options. Here are a few examples:
**Add web search capability:**
<CodeGroup>
```python Python theme={null}
options = ClaudeAgentOptions(
allowed_tools=["Read", "Edit", "Glob", "WebSearch"], permission_mode="acceptEdits"
)
```
```typescript TypeScript hidelines={1,-1} theme={null}
const _ = {
options: {
allowedTools: ["Read", "Edit", "Glob", "WebSearch"],
permissionMode: "acceptEdits"
}
};
```
</CodeGroup>
**Give Claude a custom system prompt:**
<CodeGroup>
Cut at 300 lines. The page has the rest.
agent-sdk/secure-deployment First recorded · 343 lines, first recorded
# Securely deploying AI agents ## Threat model ## Built-in security features ## Security principles ### Security boundaries ### Least privilege ### Defense in depth ## Isolation technologies ### Sandbox runtime ### Containers ### gVisor ### Virtual machines ### Cloud deployments ## Credential management ### The proxy pattern ### Configuring Claude Code to use a proxy ### Implementing a proxy ### Credentials for other services #### Custom tools #### Traffic forwarding ## Filesystem configuration ### Read-only code mounting ### Writable locations ## Further reading
The first capture of this source. The page was already there, and this is what it said.
# Securely deploying AI agents
> A guide to securing Claude Code and Agent SDK deployments with isolation, credential management, and network controls
Claude Code and the Agent SDK are powerful tools that can execute code, access files, and interact with external services on your behalf.
Unlike traditional software that follows predetermined code paths, these tools generate their actions dynamically based on context and goals. This flexibility is what makes them useful, but it also means their behavior can be influenced by the content they process: files, webpages, or user input. This is sometimes called prompt injection. For example, if a repository's README contains unusual instructions, Claude Code might incorporate those into its actions in ways the operator didn't anticipate. This guide covers practical ways to reduce this risk.
Not every deployment needs maximum security. A developer running Claude Code on their laptop has different requirements than a company processing customer data in a multi-tenant environment. This guide presents options ranging from Claude Code's built-in security features to hardened production architectures, so you can choose what fits your situation.
## Threat model
Agents can take unintended actions due to prompt injection (instructions embedded in content they process) or model error. Claude models are designed to resist this; see the [model overview](https://platform.claude.com/docs/en/about-claude/models/overview) and the system card for the model you deploy for evaluation details.
Defense in depth is still good practice though. For example, if an agent processes a malicious file that instructs it to send customer data to an external server, network controls can block that request entirely.
## Built-in security features
Claude Code includes several security features that address common concerns. See the [security documentation](/docs/en/security) for full details.
* **Permissions system**: Every tool and bash command can be configured to allow, block, or prompt the user for approval. Use glob patterns to create rules like "allow all npm commands" or "block any command with sudo". Organizations can set policies that apply across all users. See [permissions](/docs/en/permissions).
* **Command parsing for permissions**: Before executing bash commands, Claude Code parses them into an AST and matches the result against your permission rules. Commands that cannot be parsed cleanly, or that do not match an allow rule, require explicit approval. A small set of constructs such as `eval` always require approval regardless of allow rules. This is a permission gate, not a sandbox; it does not infer whether a command is dangerous from its target path or effects.
* **Web search summarization**: Search results are summarized rather than passing raw content directly into the context, reducing the risk of prompt injection from malicious web content.
* **Sandbox mode**: Bash commands can run in a sandboxed environment that restricts filesystem and network access. See the [sandboxing documentation](/docs/en/sandboxing) for details.
## Security principles
For deployments that require additional hardening beyond Claude Code's defaults, these principles guide the available options.
### Security boundaries
A security boundary separates components with different trust levels. For high-security deployments, you can place sensitive resources (like credentials) outside the boundary containing the agent. If something goes wrong in the agent's environment, resources outside that boundary remain protected.
For example, rather than giving an agent direct access to an API key, you could run a proxy outside the agent's environment that injects the key into requests. The agent can make API calls, but it never sees the credential itself. This pattern is useful for multi-tenant deployments or when processing untrusted content.
### Least privilege
When needed, you can restrict the agent to only the capabilities required for its specific task:
| Resource | Restriction options |
| ------------------- | ----------------------------------------------- |
| Filesystem | Mount only needed directories, prefer read-only |
| Network | Restrict to specific endpoints via proxy |
| Credentials | Inject via proxy rather than exposing directly |
| System capabilities | Drop Linux capabilities in containers |
### Defense in depth
For high-security environments, layering multiple controls provides additional protection. Options include:
* Container isolation
* Network restrictions
* Filesystem controls
* Request validation at a proxy
The right combination depends on your threat model and operational requirements.
## Isolation technologies
Different isolation technologies offer different tradeoffs between security strength, performance, and operational complexity.
<Info>
In all of these configurations, Claude Code (or your Agent SDK application) runs inside the isolation boundary (the sandbox, container, or VM). The security controls described below restrict what the agent can access from within that boundary.
</Info>
| Technology | Isolation strength | Performance overhead | Complexity |
| ----------------------- | ------------------------------ | -------------------- | ----------- |
| Sandbox runtime | Good (secure defaults) | Very low | Low |
| Containers (Docker) | Setup dependent | Low | Medium |
| gVisor | Excellent (with correct setup) | Medium/High | Medium |
| VMs (Firecracker, QEMU) | Excellent (with correct setup) | High | Medium/High |
### Sandbox runtime
For lightweight isolation without containers, [sandbox-runtime](https://github.com/anthropic-experimental/sandbox-runtime) enforces filesystem and network restrictions at the OS level.
The main advantage is simplicity: no Docker configuration, container images, or networking setup required. The proxy and filesystem restrictions are built in.
**How it works:**
* **Filesystem**: Uses OS primitives (`bubblewrap` on Linux, `sandbox-exec` on macOS) to restrict read/write access to configured paths
* **Network**: Removes network namespace (Linux) or uses Seatbelt profiles (macOS) to route network traffic through a built-in proxy
* **Configuration**: JSON-based allowlists for domains and filesystem paths
**Setup:**
```bash theme={null}
npm install @anthropic-ai/sandbox-runtime
```
Then create a configuration file specifying allowed paths and domains.
**Security considerations:**
1. **Same-host kernel**: Unlike VMs, sandboxed processes share the host kernel. A kernel vulnerability could theoretically enable escape. For some threat models this is acceptable, but if you need kernel-level isolation, use gVisor or a separate VM.
2. **No TLS inspection**: The proxy allowlists domains based on the client-supplied hostname and does not terminate or inspect encrypted traffic. Code running inside the sandbox can potentially use [domain fronting](https://en.wikipedia.org/wiki/Domain_fronting) or similar techniques to reach hosts outside the allowlist. If your threat model requires stronger guarantees, configure a [TLS-terminating proxy](#traffic-forwarding). See the [sandboxing security limitations](/docs/en/sandboxing#security-limitations) for more detail. Separately, if the agent has permissive credentials for an allowed domain, ensure it cannot use that domain to trigger other network requests or to exfiltrate data.
For many single-developer and CI/CD use cases, sandbox-runtime raises the bar significantly with minimal setup. The sections below cover containers and VMs for deployments requiring stronger isolation.
### Containers
Containers provide isolation through Linux namespaces. Each container has its own view of the filesystem, process tree, and network stack, while sharing the host kernel.
A security-hardened container configuration might look like this:
```bash theme={null}
docker run \
--cap-drop ALL \
--security-opt no-new-privileges \
--security-opt seccomp=/path/to/seccomp-profile.json \
--read-only \
--tmpfs /tmp:rw,noexec,nosuid,size=100m \
--tmpfs /home/agent:rw,noexec,nosuid,size=500m \
--network none \
--memory 2g \
--cpus 2 \
--pids-limit 100 \
--user 1000:1000 \
-v /path/to/code:/workspace:ro \
-v /var/run/proxy.sock:/var/run/proxy.sock:ro \
agent-image
```
Here's what each option does:
| Option | Purpose |
| ---------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `--cap-drop ALL` | Removes Linux capabilities like `NET_ADMIN` and `SYS_ADMIN` that could enable privilege escalation |
| `--security-opt no-new-privileges` | Prevents processes from gaining privileges through setuid binaries |
| `--security-opt seccomp=...` | Restricts available syscalls; Docker's default blocks \~44, custom profiles can block more |
| `--read-only` | Makes the container's root filesystem immutable, preventing the agent from persisting changes |
| `--tmpfs /tmp:...` | Provides a writable temporary directory that's cleared when the container stops |
| `--network none` | Removes all network interfaces; the agent communicates through the mounted Unix socket below |
| `--memory 2g` | Limits memory usage to prevent resource exhaustion |
| `--pids-limit 100` | Limits process count to prevent fork bombs |
| `--user 1000:1000` | Runs as a non-root user |
| `-v ...:/workspace:ro` | Mounts code read-only so the agent can analyze but not modify it. **Avoid mounting sensitive host directories like `~/.ssh`, `~/.aws`, or `~/.config`** |
| `-v .../proxy.sock:...` | Mounts a Unix socket connected to a proxy running outside the container (see below) |
**Unix socket architecture:**
With `--network none`, the container has no network interfaces at all. The only way for the agent to reach the outside world is through the mounted Unix socket, which connects to a proxy running on the host. This proxy can enforce domain allowlists, inject credentials, and log all traffic.
This is the same architecture used by [sandbox-runtime](https://github.com/anthropic-experimental/sandbox-runtime). Even if the agent is compromised via prompt injection, it cannot exfiltrate data to arbitrary servers. It can only communicate through the proxy, which controls what domains are reachable. For more details, see the [Claude Code sandboxing blog post](https://www.anthropic.com/engineering/claude-code-sandboxing).
**Additional hardening options:**
| Option | Purpose |
| ---------------- | -------------------------------------------------------------------------------------------------------------------- |
| `--userns-remap` | Maps container root to unprivileged host user; requires daemon configuration but limits damage from container escape |
| `--ipc private` | Isolates inter-process communication to prevent cross-container attacks |
### gVisor
Standard containers share the host kernel: when code inside a container makes a system call, it goes directly to the same kernel that runs the host. This means a kernel vulnerability could allow container escape. gVisor addresses this by intercepting system calls in userspace before they reach the host kernel, implementing its own compatibility layer that handles most syscalls without involving the real kernel.
If an agent runs malicious code (perhaps due to prompt injection), that code runs in the container and could attempt kernel exploits. With gVisor, the attack surface is much smaller: the malicious code would need to exploit gVisor's userspace implementation first and would have limited access to the real kernel.
To use gVisor with Docker, install the `runsc` runtime and configure the daemon:
```json /etc/docker/daemon.json theme={null}
{
"runtimes": {
"runsc": {
"path": "/usr/local/bin/runsc"
}
}
}
```
Then run containers with:
```bash theme={null}
docker run --runtime=runsc agent-image
```
**Performance considerations:**
| Workload | Overhead |
| --------------------- | -------------------------------------------------- |
| CPU-bound computation | \~0% (no syscall interception) |
| Simple syscalls | \~2× slower |
| File I/O intensive | Up to 10-200× slower for heavy open/close patterns |
For multi-tenant environments or when processing untrusted content, the additional isolation is often worth the overhead.
### Virtual machines
VMs provide hardware-level isolation through CPU virtualization extensions. Each VM runs its own kernel, creating a strong boundary. A vulnerability in the guest kernel doesn't directly compromise the host. However, VMs aren't automatically "more secure" than alternatives like gVisor. VM security depends heavily on the hypervisor and device emulation code.
Firecracker is designed for lightweight microVM isolation. It can boot VMs in under 125ms with less than 5 MiB memory overhead, stripping away unnecessary device emulation to reduce attack surface.
With this approach, the agent VM has no external network interface. Instead, it communicates through `vsock` (virtual sockets). All traffic routes through vsock to a proxy on the host, which enforces allowlists and injects credentials before forwarding requests.
### Cloud deployments
For cloud deployments, you can combine any of the above isolation technologies with cloud-native network controls:
1. Run agent containers in a private subnet with no internet gateway
2. Configure cloud firewall rules (AWS Security Groups, GCP VPC firewall) to block all egress except to your proxy
3. Run a proxy (such as [Envoy](https://www.envoyproxy.io/) with its `credential_injector` filter) that validates requests, enforces domain allowlists, injects credentials, and forwards to external APIs
4. Assign minimal IAM permissions to the agent's service account, routing sensitive access through the proxy where possible
5. Log all traffic at the proxy for audit purposes
## Credential management
Agents often need credentials to call APIs, access repositories, or interact with cloud services. The challenge is providing this access without exposing the credentials themselves.
### The proxy pattern
The recommended approach is to run a proxy outside the agent's security boundary that injects credentials into outgoing requests. The agent sends requests without credentials, the proxy adds them, and forwards the request to its destination.
This pattern has several benefits:
1. The agent never sees the actual credentials
2. The proxy can enforce an allowlist of permitted endpoints
3. The proxy can log all requests for auditing
4. Credentials are stored in one secure location rather than distributed to each agent
### Configuring Claude Code to use a proxy
Claude Code supports two methods for routing sampling requests through a proxy:
**Option 1: ANTHROPIC\_BASE\_URL (simple but only for sampling API requests)**
```bash theme={null}
export ANTHROPIC_BASE_URL="http://localhost:8080"
```
This tells Claude Code and the Agent SDK to send sampling requests to your proxy instead of the Claude API directly. Your proxy receives plaintext HTTP requests, can inspect and modify them (including injecting credentials), then forwards to the real API.
**Option 2: HTTP\_PROXY / HTTPS\_PROXY (system-wide)**
```bash theme={null}
export HTTP_PROXY="http://localhost:8080"
export HTTPS_PROXY="http://localhost:8080"
```
Claude Code and the Agent SDK respect these standard environment variables, routing all HTTP traffic through the proxy. For HTTPS, the proxy creates an encrypted CONNECT tunnel: it cannot see or modify request contents without TLS interception.
### Implementing a proxy
You can build your own proxy or use an existing one:
* [Envoy Proxy](https://www.envoyproxy.io/): production-grade proxy with `credential_injector` filter for adding auth headers
* [mitmproxy](https://mitmproxy.org/): TLS-terminating proxy for inspecting and modifying HTTPS traffic
* [Squid](http://www.squid-cache.org/): caching proxy with access control lists
* [LiteLLM](https://github.com/BerriAI/litellm): LLM gateway with credential injection and rate limiting
### Credentials for other services
Beyond sampling from the Claude API, agents often need authenticated access to other services, such as git repositories, databases, and internal APIs. There are two main approaches:
#### Custom tools
Provide access through an MCP server or custom tool that routes requests to a service running outside the agent's security boundary. The agent calls the tool, but the actual authenticated request happens outside. The tool calls to a proxy which injects the credentials.
For example, a git MCP server could accept commands from the agent but forward them to a git proxy running on the host, which adds authentication before contacting the remote repository. The agent never sees the credentials.
Advantages:
* **No TLS interception**: The external service makes authenticated requests directly
* **Credentials stay outside**: The agent only sees the tool interface, not the underlying credentials
#### Traffic forwarding
For Claude API calls, `ANTHROPIC_BASE_URL` lets you route requests to a proxy that can inspect and modify them in plaintext. But for other HTTPS services (GitHub, npm registries, internal APIs), the traffic is often encrypted end-to-end. Even if you route it through a proxy via `HTTP_PROXY`, the proxy only sees an opaque TLS tunnel and can't inject credentials.
To modify HTTPS traffic to arbitrary services, without using a custom tool, you need a TLS-terminating proxy that decrypts traffic, inspects or modifies it, then re-encrypts it before forwarding. This requires:
1. Running the proxy outside the agent's container
2. Installing the proxy's CA certificate in the agent's trust store (so the agent trusts the proxy's certificates)
3. Configuring `HTTP_PROXY`/`HTTPS_PROXY` to route traffic through the proxy
This approach handles any HTTP-based service without writing custom tools, but adds complexity around certificate management.
Note that not all programs respect `HTTP_PROXY`/`HTTPS_PROXY`. Most tools (curl, pip, npm, git) do, but some may bypass these variables and connect directly. For example, Node.js `fetch()` ignores these variables by default; in Node 24+ you can set `NODE_USE_ENV_PROXY=1` to enable support. For comprehensive coverage, you can use [proxychains](https://github.com/haad/proxychains) to intercept network calls, or configure iptables to redirect outbound traffic to a transparent proxy.
<Info>
A **transparent proxy** intercepts traffic at the network level, so the client doesn't need to be configured to use it. Regular proxies require clients to explicitly connect and speak HTTP CONNECT or SOCKS. Transparent proxies (like Squid or mitmproxy in transparent mode) can handle raw redirected TCP connections.
</Info>
Both approaches still require the TLS-terminating proxy and trusted CA certificate. They just ensure traffic actually reaches the proxy.
## Filesystem configuration
Filesystem controls determine what files the agent can read and write.
### Read-only code mounting
When the agent needs to analyze code but not modify it, mount the directory read-only:
```bash theme={null}
docker run -v /path/to/code:/workspace:ro agent-image
```
<Warning>
Even read-only access to a code directory can expose credentials. Common files to exclude or sanitize before mounting:
Cut at 300 lines. The page has the rest.
agent-sdk/session-storage First recorded · 338 lines, first recorded
# Persist sessions to external storage ## The `SessionStore` interface ## Quick start ## Write your own adapter ## Reference implementations ### Validate your adapter ## Behavior notes ### Dual-write architecture ### Resume from the store ### Mirror writes are best-effort ### `getSessionMessages` returns the post-compaction chain ### `forkSession` is not a byte copy ### Subagent transcripts ### Retention ## Supported on ## Related resources
The first capture of this source. The page was already there, and this is what it said.
# Persist sessions to external storage
> Mirror session transcripts to S3, Redis, or your own backend so other hosts can resume your sessions.
By default, the SDK writes session transcripts to JSONL files under `~/.claude/projects/` on the local filesystem. A `SessionStore` adapter lets you mirror those transcripts to your own backend, such as S3, Redis, or a database, so a session created on one host can be resumed on another host running from a matching working directory.
Common reasons to use a session store:
* **Multi-host deployments.** Serverless functions, autoscaled workers, and CI runners don't share a filesystem. A shared store lets replicas resume each other's sessions.
* **Durability.** Local containers are ephemeral. A store backed by S3 or a database survives restarts and redeploys.
* **Compliance and audit.** Keep transcripts in storage you already govern, with your own retention rules, encryption, and access controls.
## The `SessionStore` interface
A `SessionStore` is an object with two required methods, `append` and `load`, and four optional methods. The SDK calls `append` to write transcript entries during a query and `load` to read them back for resume.
<CodeGroup>
```typescript TypeScript theme={null}
// Exported from @anthropic-ai/claude-agent-sdk as
// SessionStore, SessionKey, SessionStoreEntry, SessionSummaryEntry.
type SessionKey = {
projectKey: string;
sessionId: string;
subpath?: string;
};
type SessionStore = {
// Required
append(key: SessionKey, entries: SessionStoreEntry[]): Promise<void>;
load(key: SessionKey): Promise<SessionStoreEntry[] | null>;
// Optional
listSessions?(
projectKey: string,
): Promise<Array<{ sessionId: string; mtime: number }>>;
listSessionSummaries?(projectKey: string): Promise<SessionSummaryEntry[]>;
delete?(key: SessionKey): Promise<void>;
listSubkeys?(key: {
projectKey: string;
sessionId: string;
}): Promise<string[]>;
};
type SessionSummaryEntry = {
sessionId: string;
mtime: number;
data: Record<string, unknown>;
};
```
```python Python theme={null}
# Exported from claude_agent_sdk as
# SessionStore, SessionKey, SessionStoreEntry, SessionSummaryEntry.
class SessionKey(TypedDict):
project_key: str
session_id: str
subpath: NotRequired[str]
class SessionStore(Protocol):
# Required
async def append(
self, key: SessionKey, entries: list[SessionStoreEntry]
) -> None: ...
async def load(self, key: SessionKey) -> list[SessionStoreEntry] | None: ...
# Optional — omit or raise NotImplementedError
async def list_sessions(
self, project_key: str
) -> list[SessionStoreListEntry]: ...
async def list_session_summaries(
self, project_key: str
) -> list[SessionSummaryEntry]: ...
async def delete(self, key: SessionKey) -> None: ...
async def list_subkeys(self, key: SessionListSubkeysKey) -> list[str]: ...
class SessionSummaryEntry(TypedDict):
session_id: str
mtime: int
data: dict[str, Any]
```
</CodeGroup>
`SessionKey` addresses one transcript. `projectKey` is a stable, filesystem-safe encoding of the working directory, `sessionId` is the session UUID, and `subpath` is set when the entry belongs to a subagent transcript or sidecar file rather than the main conversation. Because `projectKey` encodes the working directory, resume or continue from the store from a working directory matching the original run's. Treat `subpath` as an opaque key suffix; it follows the on-disk layout, for example `subagents/agent-<id>`. When `subpath` is undefined the key refers to the main transcript.
| Method | Required | Called when |
| :--------------------- | :------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `append` | Yes | After each batch of transcript entries is written locally. Entries are JSON-safe objects, one per line in the local JSONL. |
| `load` | Yes | Before the subprocess spawns when `resume` is set or `continue: true` resolves the newest store session, and once per session when listing falls back from `listSessionSummaries`. Return `null` if the session is unknown. |
| `listSessions` | No | By `listSessions({ sessionStore })` and by `query()`/`startup()` with `continue: true`. If undefined, `continue: true` throws, and `listSessions({ sessionStore })` throws unless `listSessionSummaries` is implemented. |
| `listSessionSummaries` | No | By `listSessions({ sessionStore })` to read metadata for all sessions in one call. Maintain the summaries inside `append`. If undefined, listing falls back to `listSessions` plus a per-session `load`. |
| `delete` | No | By `deleteSession({ sessionStore })`. Deleting the main key (no `subpath`) must cascade to all subkeys for that session and also remove the session's summary entry, so a deleted session stops appearing in `listSessionSummaries`. If undefined, deletion is a no-op, which suits append-only backends. |
| `listSubkeys` | No | During resume, to discover subagent transcripts. If undefined, only the main transcript is restored. |
In a `SessionSummaryEntry`, `mtime` is the sidecar's storage write time and must share a clock source with the `mtime` values `listSessions` returns. `data` is opaque SDK-owned state; persist it verbatim without interpreting it.
Build the entries by calling the exported `foldSessionSummary` helper, `fold_session_summary` in Python, on each batch inside `append`. Skip batches whose key has a `subpath`; subagent transcripts must not contribute to the main session's summary. The fold never sets `mtime`: stamp it at persist time, through the `options.mtime` argument in TypeScript or by overwriting the field on the returned entry in Python. Concurrent `append` calls for the same session can race on the sidecar, so serialize the read-fold-write with a transaction, a compare-and-swap, or a per-session lock; the fold itself is pure.
For what the SDK does with the transcript `load` returns, see [Resume from the store](#resume-from-the-store).
## Quick start
The SDK ships an `InMemorySessionStore` for development and testing. The example below runs a query with the store attached, captures the session ID from the result message, then resumes from the store in a second `query()` call. The second call passes the same store instance plus `resume`, so the SDK loads the transcript from the store instead of the local filesystem:
<CodeGroup>
```typescript TypeScript theme={null}
import { query, InMemorySessionStore } from "@anthropic-ai/claude-agent-sdk";
const store = new InMemorySessionStore();
let sessionId: string | undefined;
try {
for await (const message of query({
prompt: "List the TypeScript files under src/",
options: { sessionStore: store },
})) {
if (message.type === "result") {
sessionId = message.session_id;
}
}
} catch (error) {
// A single-shot query() throws after yielding an error result. If the
// failure was an error result, sessionId was already captured by the loop
// above; connection or process failures yield no result message.
console.error(`Session ended with an error: ${error}`);
}
// Resume from the store. The agent has full context from the first call.
for await (const message of query({
prompt: "Summarize what those files do",
options: { sessionStore: store, resume: sessionId },
})) {
if (message.type === "result" && message.subtype === "success") {
console.log(message.result);
}
}
```
```python Python theme={null}
import asyncio
from claude_agent_sdk import (
ClaudeAgentOptions,
InMemorySessionStore,
ResultMessage,
query,
)
store = InMemorySessionStore()
async def main():
session_id = None
try:
async for message in query(
prompt="List the Python files under src/",
options=ClaudeAgentOptions(session_store=store),
):
if isinstance(message, ResultMessage):
session_id = message.session_id
except Exception as error:
# A single-shot query() raises after yielding an error result. If the
# failure was an error result, session_id was already captured by the
# loop above; connection or process failures yield no result message.
print(f"Session ended with an error: {error}")
# Resume from the store. The agent has full context from the first call.
async for message in query(
prompt="Summarize what those files do",
options=ClaudeAgentOptions(session_store=store, resume=session_id),
):
if isinstance(message, ResultMessage) and message.subtype == "success":
print(message.result)
asyncio.run(main())
```
</CodeGroup>
The second query prints a summary of the files from the first query, which shows the agent resumed with full context from the store.
## Write your own adapter
Implement `append` and `load` against your backend. Add `listSessions`, `listSessionSummaries`, `delete`, and `listSubkeys` if you want `listSessions()`, one-call metadata reads, `deleteSession()`, and subagent resume to work against the store.
Entries passed to `append` are typed as `SessionStoreEntry` (a `{ type: string; ... }` object). Treat them as opaque JSON-safe values: persist them in order and return them from `load` in the same order. `load` must return entries that are deep-equal to what was appended; byte-equal serialization is not required, so backends like Postgres `jsonb` that reorder object keys are fine.
## Reference implementations
The TypeScript SDK repository includes runnable reference adapters for S3, Redis, and Postgres under [`examples/session-stores/`](https://github.com/anthropics/claude-agent-sdk-typescript/tree/main/examples/session-stores). They are not published to npm; copy the `src/` file you need into your project and install the corresponding backend client.
| Adapter | Backend client | Storage model |
| :----------------------------------------------------------------------------------------------------------------------------- | :------------------- | :--------------------------------------------------------------------------- |
| [`S3SessionStore`](https://github.com/anthropics/claude-agent-sdk-typescript/tree/main/examples/session-stores/s3) | `@aws-sdk/client-s3` | One JSONL part file per `append()`; `load()` lists, sorts, and concatenates. |
| [`RedisSessionStore`](https://github.com/anthropics/claude-agent-sdk-typescript/tree/main/examples/session-stores/redis) | `ioredis` | `RPUSH`/`LRANGE` list per transcript, plus a sorted-set session index. |
| [`PostgresSessionStore`](https://github.com/anthropics/claude-agent-sdk-typescript/tree/main/examples/session-stores/postgres) | `pg` | One row per entry in a `jsonb` table, ordered by `BIGSERIAL`. |
Each adapter takes a pre-configured client instance, so you control credentials, TLS, region, and pooling. For example, with S3:
```typescript TypeScript theme={null}
import { query } from "@anthropic-ai/claude-agent-sdk";
import { S3Client } from "@aws-sdk/client-s3";
import { S3SessionStore } from "./S3SessionStore"; // copied from examples/session-stores/s3
const store = new S3SessionStore({
bucket: "my-claude-sessions",
prefix: "transcripts",
client: new S3Client({ region: "us-east-1" }),
});
for await (const message of query({
prompt: "Hello!",
options: { sessionStore: store },
})) {
if (message.type === "result" && message.subtype === "success") {
console.log(message.result);
}
}
// Later, possibly on a different host:
for await (const message of query({
prompt: "Continue where we left off",
options: { sessionStore: store, resume: "previous-session-id" },
})) {
// ...
}
```
### Validate your adapter
Both SDKs ship a conformance suite that asserts the behavioral contract `append`, `load`, and the optional methods must satisfy. Tests for optional methods skip automatically when those methods are not implemented.
In TypeScript, copy [`shared/conformance.ts`](https://github.com/anthropics/claude-agent-sdk-typescript/blob/main/examples/session-stores/shared/conformance.ts) from the example directory into your test suite. In Python, the suite ships in the package. To run it with pytest, which isn't an SDK dependency, install pytest first:
```bash theme={null}
pip install pytest
```
Then pass your adapter to the suite in a test file as a zero-argument factory, which `run_session_store_conformance` calls once per contract to build a fresh store:
```python Python theme={null}
import pytest
from claude_agent_sdk.testing import run_session_store_conformance
@pytest.mark.anyio
async def test_my_store_conformance():
await run_session_store_conformance(MyRedisStore)
```
Passing the `MyRedisStore` class itself, as this example does, works when the constructor takes no arguments. For an adapter that takes a pre-configured client, pass a lambda that constructs the store instead. Because the contracts reuse the same session keys, each store the factory returns must start with empty storage, so have the lambda provision isolated backing storage per call, such as a fresh in-memory fake, a unique key prefix, or a new test database.
## Behavior notes
### Dual-write architecture
The Claude Code subprocess always writes each batch of transcript entries to local disk first, and the SDK then forwards the same batch to your store's `append()`, so the store is a mirror of the local transcript rather than a replacement for it. Which copy outlives the run depends on how the run started:
* **Fresh session, or a resume when the store has nothing for the session**: the local transcript under your config directory outlives the run, and the store receives a copy.
* **Run [resumed from the store](#resume-from-the-store)**: the local copy is deleted at run end, so the store holds the only durable copy.
If you don't want a fresh session to leave a transcript on local disk, set `CLAUDE_CONFIG_DIR` to a temp directory in `options.env`. A run resumed from the store already deletes its local copy, so it needs no such setting. In TypeScript, spread `process.env` into `env` as well, since the [`env` option](/docs/en/agent-sdk/typescript#options) replaces the subprocess environment.
If your app signs in through files in the config directory, such as OAuth credentials or an `apiKeyHelper` in your user `settings.json`, copy those files into the temp directory first, or set `ANTHROPIC_API_KEY` in `env` instead. Otherwise the run fails with `Not logged in`.
Two options conflict with the mirror, and the SDK throws at startup if you combine either with a store:
* **`persistSession: false`** in TypeScript: turns off the local writes the mirror is built from. The Python SDK has no equivalent option.
* **File checkpointing**, `enableFileCheckpointing` in TypeScript or `enable_file_checkpointing` in Python: writes its file backups straight to local disk, and the SDK doesn't mirror them to the store.
### Resume from the store
When you pass `resume`, or `continue: true` in TypeScript or `continue_conversation=True` in Python, together with a store, the SDK asks the store for a transcript before it spawns the subprocess:
* **`resume`**: the SDK asks for the session whose ID you passed.
* **`continue: true`** or **`continue_conversation=True`**: the SDK asks for the store's newest session.
When the store returns the transcript, the SDK writes it into a temporary config directory, runs the subprocess with `CLAUDE_CONFIG_DIR` pointing there, and deletes the directory when the run ends. The local transcript that run writes is deleted with it, which is why the store holds the only durable copy on this path.
The SDK also seeds the temporary directory with files from your real config directory. What it copies differs by language:
* **TypeScript**: credentials, `.claude.json`, and your user `settings.json`. From `settings.json` it strips the three keys that misbehave under a temporary config directory: `enabledPlugins`, `extraKnownMarketplaces`, and any `CLAUDE_CONFIG_DIR` in the file's `env` block. Auth configured in settings, such as [`apiKeyHelper`](/docs/en/settings#available-settings), works when you resume from the store. Before Agent SDK v0.3.222, the TypeScript SDK copied only credentials and `.claude.json`.
* **Python**: credentials and `.claude.json` only, so an app that authenticates through `apiKeyHelper` in your user `settings.json` fails with `Not logged in` when resuming from a store. An `apiKeyHelper` in managed or project settings still works, because Claude Code reads those files from locations that `CLAUDE_CONFIG_DIR` doesn't affect.
When the store has nothing for the session, the SDK runs under your real config directory instead, and the outcome depends on which option you passed:
* **`resume`**: both SDKs pass the ID through to the subprocess, which resumes the local transcript exactly as `resume` does without a store.
* **`continue: true`** in TypeScript: the SDK starts a fresh session.
* **`continue_conversation=True`** in Python: the SDK continues from the newest local session.
### Mirror writes are best-effort
If `append()` rejects, the SDK retries the batch up to two more times with a short backoff, for at most three attempts in total. A call that times out isn't retried, since the original call may still land. If the batch still fails, the SDK logs the error, emits a `{ type: "system", subtype: "mirror_error" }` message into the iterator, drops the batch, and continues the query. Because a retried batch can re-deliver entries that already landed, deduplicate by `entry.uuid` in your `append()` implementation.
A store outage doesn't interrupt the agent, since the subprocess writes locally first. Monitor for `mirror_error` if you need to detect store data loss. On a run [resumed from the store](#resume-from-the-store), a dropped batch has no surviving copy once the run ends.
### `getSessionMessages` returns the post-compaction chain
`getSessionMessages({ sessionStore })` returns the linked message chain the agent would see on resume. After auto-compaction, earlier turns are replaced by a summary, so a session whose store holds 503 raw entries may return 18 messages from `getSessionMessages`. For the full raw history, including pre-compaction turns and metadata entries, call `store.load(key)` directly.
Cut at 300 lines. The page has the rest.
agent-sdk/sessions First recorded · 411 lines, first recorded
# Work with sessions ## Choose an approach ### Continue, resume, and fork ## Automatic session management ### Python: `ClaudeSDKClient` ### TypeScript: `continue: true` ## Use session options with `query()` ### Capture the session ID ### Resume by ID ### Fork to explore alternatives ## Resume across hosts ## Related resources
The first capture of this source. The page was already there, and this is what it said.
# Work with sessions
> How sessions persist agent conversation history, and when to use continue, resume, and fork to return to a prior run.
A session is the conversation history the SDK accumulates while your agent works. It contains your prompt, every tool call the agent made, every tool result, and every response. The SDK writes it to disk automatically so you can return to it later.
Returning to a session means the agent has full context from before: files it already read, analysis it already performed, decisions it already made. You can ask a follow-up question, recover from an interruption, or branch off to try a different approach.
<Note>
Sessions persist the **conversation**, not the filesystem. To snapshot and revert file changes the agent made, use [file checkpointing](/docs/en/agent-sdk/file-checkpointing).
</Note>
This guide covers how to pick the right approach for your app, the SDK interfaces that track sessions automatically, how to capture session IDs and use `resume` and `fork` manually, and what to know about resuming sessions across hosts.
## Choose an approach
How much session handling you need depends on your application's shape. Session management comes into play when you send multiple prompts that should share context. Within a single `query()` call, the agent already takes as many turns as it needs, and permission prompts and `AskUserQuestion` are [handled in-loop](/docs/en/agent-sdk/user-input) (they don't end the call).
| What you're building | What to use |
| :------------------------------------------------------ | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| One-shot task: single prompt, no follow-up | Nothing extra. One `query()` call handles it. |
| Multi-turn chat in one process | [`ClaudeSDKClient` (Python) or `continue: true` (TypeScript)](#automatic-session-management). The SDK tracks the session for you with no ID handling. |
| Pick up where you left off after a process restart | `continue_conversation=True` (Python) / `continue: true` (TypeScript). Resumes the most recent session in the directory, no ID needed. |
| Resume a specific past session (not the most recent) | Capture the session ID and pass it to `resume`. |
| Try an alternative approach without losing the original | Fork the session. |
| Stateless task, don't want anything written to disk | Set [`persistSession: false`](/docs/en/agent-sdk/typescript#options) (TypeScript only). The session exists only in memory for the duration of the call. In Python, set [`CLAUDE_CODE_SKIP_PROMPT_HISTORY`](/docs/en/env-vars) in the `env` option to suppress transcript writes instead. |
### Continue, resume, and fork
Continue, resume, and fork are option fields you set on `query()` ([`ClaudeAgentOptions`](/docs/en/agent-sdk/python#claudeagentoptions) in Python, [`Options`](/docs/en/agent-sdk/typescript#options) in TypeScript).
**Continue** and **resume** both pick up an existing session and add to it. The difference is how they find that session:
* **Continue** finds the most recent session in the current directory. You don't track anything. Works well when your app runs one conversation at a time.
* **Resume** takes a specific session ID. You track the ID. Required when you have multiple sessions (for example, one per user in a multi-user app) or want to return to one that isn't the most recent.
**Fork** is different: it creates a new session that starts with a copy of the original's history. The original stays unchanged. Use fork to try a different direction while keeping the option to go back.
## Automatic session management
Both SDKs offer an interface that tracks session state for you across calls, so you don't pass IDs around manually. Use these for multi-turn conversations within a single process.
### Python: `ClaudeSDKClient`
[`ClaudeSDKClient`](/docs/en/agent-sdk/python#claudesdkclient) handles session IDs internally. Each call to `client.query()` automatically continues the same session. Call [`client.receive_response()`](/docs/en/agent-sdk/python#claudesdkclient) to iterate over the messages for the current query. Use the client as an async context manager so connection setup and teardown are handled for you, or call `connect()` and `disconnect()` manually.
This example runs two queries against the same `client`. The first asks the agent to analyze a module; the second asks it to refactor that module. Because both calls go through the same client instance, the second query has full context from the first without any explicit `resume` or session ID:
```python Python theme={null}
import asyncio
from claude_agent_sdk import (
ClaudeSDKClient,
ClaudeAgentOptions,
AssistantMessage,
ResultMessage,
TextBlock,
)
def print_response(message):
"""Print only the human-readable parts of a message."""
if isinstance(message, AssistantMessage):
for block in message.content:
if isinstance(block, TextBlock):
print(block.text)
elif isinstance(message, ResultMessage):
cost = (
f"${message.total_cost_usd:.4f}"
if message.total_cost_usd is not None
else "N/A"
)
print(f"[done: {message.subtype}, cost: {cost}]")
async def main():
options = ClaudeAgentOptions(
allowed_tools=["Read", "Edit", "Glob", "Grep"],
)
async with ClaudeSDKClient(options=options) as client:
# First query: client captures the session ID internally
await client.query("Analyze the auth module")
async for message in client.receive_response():
print_response(message)
# Second query: automatically continues the same session
await client.query("Now refactor it to use JWT")
async for message in client.receive_response():
print_response(message)
asyncio.run(main())
```
Each query prints the agent's text response followed by a status line from the result message, such as `[done: success, cost: $0.0042]`.
See the [Python SDK reference](/docs/en/agent-sdk/python#choosing-between-query-and-claudesdkclient) for details on when to use `ClaudeSDKClient` vs the standalone `query()` function.
### TypeScript: `continue: true`
The TypeScript SDK doesn't have a session-holding client object like Python's `ClaudeSDKClient`. Instead, pass `continue: true` on each subsequent `query()` call and the SDK picks up the most recent session in the current directory. No ID tracking required.
This example makes two separate `query()` calls. The first creates a fresh session; the second sets `continue: true`, which tells the SDK to find and resume the most recent session on disk. The agent has full context from the first call:
```typescript TypeScript theme={null}
import { query } from "@anthropic-ai/claude-agent-sdk";
// First query: creates a new session
try {
for await (const message of query({
prompt: "Analyze the auth module",
options: { allowedTools: ["Read", "Glob", "Grep"] }
})) {
if (message.type === "result" && message.subtype === "success") {
console.log(message.result);
}
}
} catch (error) {
// A single-shot query() throws after yielding an error result,
// so the follow-up query below still runs.
console.error(`Session ended with an error: ${error}`);
}
// Second query: continue: true resumes the most recent session
for await (const message of query({
prompt: "Now refactor it to use JWT",
options: {
continue: true,
allowedTools: ["Read", "Edit", "Write", "Glob", "Grep"]
}
})) {
if (message.type === "result" && message.subtype === "success") {
console.log(message.result);
}
}
```
<Note>
The experimental [V2 session API](/docs/en/agent-sdk/typescript-v2-preview), which provided `createSession()` with a `send` / `stream` pattern, was removed in TypeScript Agent SDK 0.3.142. Use the `query()` function and the session options described on this page instead.
</Note>
## Use session options with `query()`
### Capture the session ID
Resume and fork require a session ID. Read it from the `session_id` field on the result message ([`ResultMessage`](/docs/en/agent-sdk/python#resultmessage) in Python, [`SDKResultMessage`](/docs/en/agent-sdk/typescript#sdkresultmessage) in TypeScript), which is present on every result regardless of success or error. In TypeScript the ID is also available earlier as a direct field on the init `SystemMessage`; in Python it's nested inside `SystemMessage.data`.
<CodeGroup>
```python Python theme={null}
import asyncio
from claude_agent_sdk import query, ClaudeAgentOptions, ResultMessage
async def main():
session_id = None
try:
async for message in query(
prompt="Analyze the auth module and suggest improvements",
options=ClaudeAgentOptions(
allowed_tools=["Read", "Glob", "Grep"],
),
):
if isinstance(message, ResultMessage):
session_id = message.session_id
if message.subtype == "success":
print(message.result)
except Exception as error:
# A single-shot query() raises after yielding an error result. If the
# failure was an error result, the loop above already captured session_id;
# connection or process failures yield no result message, so session_id stays None.
print(f"Session ended with an error: {error}")
print(f"Session ID: {session_id}")
return session_id
session_id = asyncio.run(main())
```
```typescript TypeScript theme={null}
import { query } from "@anthropic-ai/claude-agent-sdk";
let sessionId: string | undefined;
try {
for await (const message of query({
prompt: "Analyze the auth module and suggest improvements",
options: { allowedTools: ["Read", "Glob", "Grep"] }
})) {
if (message.type === "result") {
sessionId = message.session_id;
if (message.subtype === "success") {
console.log(message.result);
}
}
}
} catch (error) {
// A single-shot query() throws after yielding an error result. If the
// failure was an error result, the loop above already captured sessionId;
// connection or process failures yield no result message, so sessionId stays undefined.
console.error(`Session ended with an error: ${error}`);
}
console.log(`Session ID: ${sessionId}`);
```
</CodeGroup>
When the query completes, the script prints the agent's response followed by a line such as `Session ID: 5b3f2c1a-8d4e-4f6b-9a7c-2e1d0f9b8a6c`. In the next sections, you pass this ID to `resume`.
### Resume by ID
Pass a session ID to `resume` to return to that specific session. The agent picks up with full context from wherever the session left off. Common reasons to resume:
* **Follow up on a completed task.** The agent already analyzed something; now you want it to act on that analysis without re-reading files.
* **Recover from a limit.** The first run ended with `error_max_turns` or `error_max_budget_usd` (see [Handle the result](/docs/en/agent-sdk/agent-loop#handle-the-result)); resume with a higher limit. In a single-shot `query()` call the SDK raises after yielding that error result, so catch the error before resuming.
* **Restart your process.** You captured the ID before shutdown and want to restore the conversation.
This example resumes the session from [Capture the session ID](#capture-the-session-id) with a follow-up prompt. Because you're resuming, the agent already has the prior analysis in context:
<CodeGroup>
```python Python theme={null}
import asyncio
from claude_agent_sdk import query, ClaudeAgentOptions, ResultMessage
session_id = "..." # The ID you captured in the previous example
async def main():
# Earlier session analyzed the code; now build on that analysis
async for message in query(
prompt="Now implement the refactoring you suggested",
options=ClaudeAgentOptions(
resume=session_id,
allowed_tools=["Read", "Edit", "Write", "Glob", "Grep"],
),
):
if isinstance(message, ResultMessage) and message.subtype == "success":
print(message.result)
asyncio.run(main())
```
```typescript TypeScript theme={null}
import { query } from "@anthropic-ai/claude-agent-sdk";
const sessionId = "..."; // The ID you captured in the previous example
// Earlier session analyzed the code; now build on that analysis
for await (const message of query({
prompt: "Now implement the refactoring you suggested",
options: {
resume: sessionId,
allowedTools: ["Read", "Edit", "Write", "Glob", "Grep"]
}
})) {
if (message.type === "result" && message.subtype === "success") {
console.log(message.result);
}
}
```
</CodeGroup>
You should see a response that builds on the earlier analysis instead of starting fresh. That confirms the agent resumed the session with its prior context intact.
<Tip>
Claude Code stores sessions under `~/.claude/projects/<encoded-cwd>/*.jsonl`. If you set the `CLAUDE_CONFIG_DIR` environment variable, look under `$CLAUDE_CONFIG_DIR/projects/` instead. To find your session's directory, replace every non-alphanumeric character in the absolute working directory with `-`: `/Users/me/proj` becomes `-Users-me-proj`. For a working directory whose converted name exceeds 200 characters, Claude Code [truncates the name and appends a hash](/docs/en/sessions#where-transcripts-are-stored), so match the first 200 characters of the converted name when you list `projects/`.
You can resume from any working directory:
* **Cross-directory lookup**: Claude Code searches beyond the current project directory to find the ID; see [Resume a session](/docs/en/sessions#resume-a-session) for the exact lookup order and how duplicate copies are handled.
* **Same machine only**: the session file still needs to exist on the current machine.
Before v2.1.223, the lookup was scoped to the current project directory and its git worktrees; SDK versions that bundle an older CLI still behave this way.
</Tip>
To resume sessions across machines or in serverless environments, mirror transcripts to shared storage with a [`SessionStore` adapter](/docs/en/agent-sdk/session-storage).
### Fork to explore alternatives
Forking creates a new session that starts with a copy of the original's history but diverges from that point. The fork gets its own session ID; the original's ID and history stay unchanged. You end up with two independent sessions you can resume separately.
<Note>
Forking branches the conversation history, not the filesystem. If a forked agent edits files, those changes are real and visible to any session working in the same directory. To branch and revert file changes, use [file checkpointing](/docs/en/agent-sdk/file-checkpointing).
</Note>
This example builds on [Capture the session ID](#capture-the-session-id): you've already analyzed an auth module in `session_id` and want to explore OAuth2 without losing the JWT-focused thread. The first block forks the session and captures the fork's ID (`forked_id`); the second block resumes the original `session_id` to continue down the JWT path. You now have two session IDs pointing at two separate histories:
<CodeGroup>
```python Python theme={null}
import asyncio
from claude_agent_sdk import query, ClaudeAgentOptions, ResultMessage
session_id = "..." # The ID you captured in the previous example
async def main():
# Fork: branch from session_id into a new session
forked_id = None
Cut at 300 lines. The page has the rest.
agent-sdk/skills First recorded · 329 lines, first recorded
# Agent Skills in the SDK ## Overview ## How Skills Work with the SDK ## Using Skills with the SDK ## Creating Skills ## Tool Restrictions ## Discovering Available Skills ## Testing Skills ## Troubleshooting ### Skills Not Found ### Skill Not Being Used ### Additional Troubleshooting ## Related Documentation ### Skills Guides ### SDK Resources
The first capture of this source. The page was already there, and this is what it said.
# Agent Skills in the SDK
> Extend Claude with specialized capabilities using Agent Skills in the Claude Agent SDK
## Overview
Agent Skills extend Claude with specialized capabilities that Claude autonomously invokes when relevant. Skills are packaged as `SKILL.md` files containing instructions, descriptions, and optional supporting resources.
For comprehensive information about Skills, including benefits, architecture, and authoring guidelines, see the [Agent Skills overview](https://platform.claude.com/docs/en/agents-and-tools/agent-skills/overview).
## How Skills Work with the SDK
When using the Claude Agent SDK, Skills are:
1. **Defined as filesystem artifacts**: you create each Skill as a `SKILL.md` file in its own directory, such as `.claude/skills/<name>/SKILL.md`
2. **Loaded from filesystem**: the SDK loads Skills from the filesystem locations governed by `settingSources` (TypeScript) or `setting_sources` (Python)
3. **Automatically discovered**: once filesystem settings load, the SDK discovers Skill metadata at startup from user and project directories, and loads the full content when Claude invokes the Skill
4. **Model-invoked**: Claude autonomously chooses when to use them based on context
5. **Filtered via the `skills` option**: discovered skills are enabled by default. Pass a list of skill names, `"all"`, or `[]` to control which are available in the session
Unlike subagents (which can be defined programmatically), Skills must be created as filesystem artifacts. The SDK does not provide a programmatic API for registering Skills.
<Note>
Skills are discovered through the filesystem setting sources. With default `query()` options, the SDK loads user and project sources, so skills in `~/.claude/skills/`, `<cwd>/.claude/skills/`, and `.claude/skills/` in any parent directory of `<cwd>` up to the repository root are available. If you set `settingSources` explicitly, include `'user'` or `'project'` to keep skill discovery, or use the [`plugins` option](/docs/en/agent-sdk/plugins) to load skills from a specific path.
</Note>
## Using Skills with the SDK
Set the `skills` option on `query()` to control which Skills are available to the session. When omitted, discovered Skills are enabled and the Skill tool is available, matching CLI behavior. Pass `"all"` to enable every discovered Skill, a list of Skill names to enable only those, or `[]` to disable all. When you set `skills`, the SDK adds the Skill tool to `allowedTools` automatically. If you also pass an explicit `tools` list, include `"Skill"` in that list so Claude can invoke skills.
Once configured, Claude automatically discovers Skills from the filesystem and invokes them when relevant to the user's request.
The following example sets `cwd` to the process's current working directory, so run it from inside a project that has a `.claude/skills/` directory in the current directory or any parent up to the repository root:
<CodeGroup>
```python Python theme={null}
import asyncio
import os
from claude_agent_sdk import query, ClaudeAgentOptions
async def main():
options = ClaudeAgentOptions(
cwd=os.getcwd(), # .claude/skills/ here or in a parent directory
setting_sources=["user", "project"], # Load Skills from filesystem
skills="all", # Enable every discovered Skill
allowed_tools=["Read", "Write", "Bash"],
)
async for message in query(
prompt="Help me process this PDF document", options=options
):
print(message)
asyncio.run(main())
```
```typescript TypeScript theme={null}
import { query } from "@anthropic-ai/claude-agent-sdk";
for await (const message of query({
prompt: "Help me process this PDF document",
options: {
cwd: process.cwd(), // .claude/skills/ here or in a parent directory
settingSources: ["user", "project"], // Load Skills from filesystem
skills: "all", // Enable every discovered Skill
allowedTools: ["Read", "Write", "Bash"]
}
})) {
console.log(message);
}
```
</CodeGroup>
Near the start of the stream, the SDK yields a system message with subtype `init`. Check its `skills` array to confirm your Skills loaded before Claude starts working. The array lists user-invocable Skills only. A Skill with [`user-invocable: false`](/docs/en/skills#control-who-invokes-a-skill) in its frontmatter loads and remains available to Claude but doesn't appear in the array.
To enable only specific Skills, pass their names. Names match the `name` field in `SKILL.md` or the Skill's directory name. Use `plugin:skill` for plugin-provided Skills.
<Note>
Import statements from the first example are assumed in the following code snippets.
</Note>
<CodeGroup>
```python Python theme={null}
options = ClaudeAgentOptions(skills=["pdf", "docx"])
```
```typescript TypeScript theme={null}
const options = { skills: ["pdf", "docx"] };
```
</CodeGroup>
The list takes exact Skill names only. In the TypeScript SDK, `query()` throws before starting the Claude Code process when a name can't work as an exact Skill name. In the Python SDK, `query()` raises `ValueError` in the same cases, for example:
* An empty name
* A name containing parentheses, commas, or control characters
* A name padded with whitespace
* A wildcard form such as a bare `*` or a `:*` suffix
To enable every discovered Skill, pass `skills: "all"` rather than a wildcard.
The `skills` option is a context filter, not a sandbox. Unlisted Skills are hidden from the model and rejected by the Skill tool, but their files remain on disk and are reachable through Read and Bash.
## Creating Skills
Create each Skill as a directory containing a `SKILL.md` file with YAML frontmatter and Markdown content. The `description` field determines when Claude invokes your Skill.
**Example directory structure**:
```text theme={null}
.claude/skills/processing-pdfs/
└── SKILL.md
```
For complete guidance on creating Skills, including SKILL.md structure, multi-file Skills, and examples, see:
* [Agent Skills in Claude Code](/docs/en/skills): complete guide with examples
* [Agent Skills Best Practices](https://platform.claude.com/docs/en/agents-and-tools/agent-skills/best-practices): authoring guidelines and naming conventions
## Tool Restrictions
<Note>
The `allowed-tools` frontmatter field in SKILL.md is only supported when using Claude Code CLI directly. **It does not apply when using Skills through the SDK**.
When using the SDK, control tool access through the main `allowedTools` option in your query configuration.
</Note>
To control tool access for Skills in SDK applications, use `allowedTools` to pre-approve specific tools. Without a `canUseTool` callback, anything not in the list is denied:
<CodeGroup>
```python Python theme={null}
options = ClaudeAgentOptions(
setting_sources=["user", "project"], # Load Skills from filesystem
skills="all",
allowed_tools=["Read", "Grep", "Glob"],
permission_mode="dontAsk", # Deny anything not pre-approved instead of prompting
)
async def main():
async for message in query(prompt="Analyze the codebase structure", options=options):
print(message)
asyncio.run(main())
```
```typescript TypeScript theme={null}
for await (const message of query({
prompt: "Analyze the codebase structure",
options: {
settingSources: ["user", "project"], // Load Skills from filesystem
skills: "all",
allowedTools: ["Read", "Grep", "Glob"],
permissionMode: "dontAsk" // Deny anything not pre-approved instead of prompting
}
})) {
console.log(message);
}
```
</CodeGroup>
## Discovering Available Skills
To see which Skills are available in your SDK application, ask Claude. The example below sets only the `skills` option and omits `settingSources`/`setting_sources`. When you leave `settingSources`/`setting_sources` unset, the SDK still loads Skills from the user and project sources, so the `skills` option set to `"all"` on its own makes them available to list.
<CodeGroup>
```python Python theme={null}
options = ClaudeAgentOptions(skills="all")
async def main():
async for message in query(prompt="What Skills are available?", options=options):
print(message)
asyncio.run(main())
```
```typescript TypeScript theme={null}
for await (const message of query({
prompt: "What Skills are available?",
options: {
skills: "all"
}
})) {
console.log(message);
}
```
</CodeGroup>
Claude will list the available Skills based on your current working directory and installed plugins.
## Testing Skills
Test Skills by asking questions that match their descriptions:
<CodeGroup>
```python Python theme={null}
options = ClaudeAgentOptions(
cwd=os.getcwd(),
setting_sources=["user", "project"], # Load Skills from filesystem
skills="all",
allowed_tools=["Read", "Bash"],
)
async def main():
async for message in query(prompt="Extract text from invoice.pdf", options=options):
print(message)
asyncio.run(main())
```
```typescript TypeScript theme={null}
for await (const message of query({
prompt: "Extract text from invoice.pdf",
options: {
cwd: process.cwd(),
settingSources: ["user", "project"], // Load Skills from filesystem
skills: "all",
allowedTools: ["Read", "Bash"]
}
})) {
console.log(message);
}
```
</CodeGroup>
Claude automatically invokes the relevant Skill if the description matches your request.
## Troubleshooting
### Skills Not Found
**Check settingSources configuration**: Skills are discovered through the `user` and `project` setting sources. If you set `settingSources`/`setting_sources` explicitly and omit those sources, skills are not loaded:
<CodeGroup>
```python Python theme={null}
# Skills not loaded: setting_sources excludes user and project
options = ClaudeAgentOptions(setting_sources=[], skills="all")
# Skills loaded: user and project sources included
options = ClaudeAgentOptions(
setting_sources=["user", "project"],
skills="all",
)
```
```typescript TypeScript theme={null}
// Skills not loaded: settingSources excludes user and project
const optionsWithoutSkills = {
settingSources: [],
skills: "all"
};
// Skills loaded: user and project sources included
const optionsWithSkills = {
settingSources: ["user", "project"],
skills: "all"
};
```
</CodeGroup>
For which skill directories each source loads, see the [filesystem sources table](/docs/en/agent-sdk/claude-code-features#control-filesystem-settings-with-settingsources). For more details on `settingSources`/`setting_sources`, see the [TypeScript SDK reference](/docs/en/agent-sdk/typescript#settingsource) or [Python SDK reference](/docs/en/agent-sdk/python#settingsource).
**Check working directory**: The SDK loads Skills from `.claude/skills/` in the `cwd` option and in every parent directory up to the repository root. Ensure `cwd` points at or below the directory containing `.claude/skills/`, within the same repository:
<CodeGroup>
```python Python theme={null}
# Ensure your cwd points to the directory containing .claude/skills/
options = ClaudeAgentOptions(
cwd="/path/to/project", # .claude/skills/ here or in a parent directory
setting_sources=["user", "project"], # Loads skills from these sources
skills="all",
)
```
```typescript TypeScript theme={null}
// Ensure your cwd points to the directory containing .claude/skills/
const options = {
cwd: "/path/to/project", // .claude/skills/ here or in a parent directory
settingSources: ["user", "project"], // Loads skills from these sources
skills: "all"
};
```
</CodeGroup>
See the "Using Skills with the SDK" section above for the complete pattern.
**Verify filesystem location**:
```bash theme={null}
# Check project Skills
ls .claude/skills/*/SKILL.md
# Check personal Skills
Cut at 300 lines. The page has the rest.
agent-sdk/slash-commands First recorded · 514 lines, first recorded
# Slash Commands in the SDK ## Discovering Available Slash Commands ## Sending Slash Commands ## Common Slash Commands ### `/compact` - Compact conversation history ### `/clear` - Reset conversation context ## Creating Custom Slash Commands ### File Locations ### File Format #### Basic Example #### With Frontmatter ### Using Custom Commands in the SDK ### Advanced Features #### Arguments and Placeholders #### Bash Command Execution #### File References ### Organization with Namespacing ### Practical Examples #### Pull Request Review Command #### Test Runner Command ## See Also
The first capture of this source. The page was already there, and this is what it said.
# Slash Commands in the SDK
> Learn how to use slash commands to control Claude Code sessions through the SDK
Slash commands provide a way to control Claude Code sessions with special commands that start with `/`. These commands can be sent through the SDK to perform actions like compacting context, listing context usage, or invoking custom commands. Only commands that work without an interactive terminal are dispatchable through the SDK; the `system/init` message lists the ones available in your session.
## Discovering Available Slash Commands
The Claude Agent SDK provides information about available slash commands in the system initialization message. Access this information when your session starts:
<CodeGroup>
```typescript TypeScript theme={null}
import { query } from "@anthropic-ai/claude-agent-sdk";
for await (const message of query({
prompt: "Hello Claude",
options: { maxTurns: 1 }
})) {
if (message.type === "system" && message.subtype === "init") {
console.log("Available slash commands:", message.slash_commands);
// Includes built-in commands plus bundled skills, for example:
// ["clear", "compact", "context", "usage", "code-review", "verify", ...]
}
}
```
```python Python theme={null}
import asyncio
from claude_agent_sdk import query, ClaudeAgentOptions, SystemMessage
async def main():
async for message in query(prompt="Hello Claude", options=ClaudeAgentOptions(max_turns=1)):
if isinstance(message, SystemMessage) and message.subtype == "init":
print("Available slash commands:", message.data["slash_commands"])
# Includes built-in commands plus bundled skills, for example:
# ["clear", "compact", "context", "usage", "code-review", "verify", ...]
asyncio.run(main())
```
</CodeGroup>
## Sending Slash Commands
Send slash commands by including them in your prompt string, just like regular text. Commands that act on conversation history, such as `/compact`, need prior messages to work with, so the examples below ask a question first and then send the command as a follow-up to the same conversation:
<CodeGroup>
```typescript TypeScript theme={null}
import { query } from "@anthropic-ai/claude-agent-sdk";
// Build up conversation history first
try {
for await (const message of query({
prompt: "What does the README in this directory cover?",
options: { maxTurns: 2 }
})) {
if (message.type === "result" && message.subtype === "success") {
console.log(message.result);
}
}
} catch (error) {
// A single-shot query() throws after yielding an error result,
// so the follow-up query below still runs.
console.error(`Session ended with an error: ${error}`);
}
// Send a slash command as a follow-up to the same conversation
for await (const message of query({
prompt: "/compact",
options: { continue: true, maxTurns: 1 }
})) {
if (message.type === "result") {
console.log("Command executed, result subtype:", message.subtype);
// Example output: Command executed, result subtype: success
}
}
```
```python Python theme={null}
import asyncio
from claude_agent_sdk import query, ClaudeAgentOptions, ResultMessage
async def main():
# Build up conversation history first
try:
async for message in query(
prompt="What does the README in this directory cover?",
options=ClaudeAgentOptions(max_turns=2),
):
if isinstance(message, ResultMessage) and message.subtype == "success":
print(message.result)
except Exception as error:
# A single-shot query() raises after yielding an error result,
# so the follow-up query below still runs.
print(f"Session ended with an error: {error}")
# Send a slash command as a follow-up to the same conversation
async for message in query(
prompt="/compact",
options=ClaudeAgentOptions(continue_conversation=True, max_turns=1),
):
if isinstance(message, ResultMessage):
print("Command executed, result subtype:", message.subtype)
# Example output: Command executed, result subtype: success
asyncio.run(main())
```
</CodeGroup>
<Note>
A query can end with an error result, for example when the `maxTurns` / `max_turns` limit is reached before the work completes. The final result message then has `is_error: true` and an error subtype such as `error_max_turns` instead of `success`.
After yielding that final result message, the SDK raises an error, because the CLI process exits with a non-zero code.
Wrap the loop in a `try`/`catch` in TypeScript or `try`/`except` in Python if your command might hit the limit, as shown in [Single Message Input](/docs/en/agent-sdk/streaming-vs-single-mode#single-message-input), or set `maxTurns` high enough for the work to complete. In Python, catch `Exception`: the SDK surfaces error results as a plain `Exception`.
</Note>
## Common Slash Commands
### `/compact` - Compact conversation history
The `/compact` command reduces the size of your conversation history by summarizing older messages while preserving important context. Compaction needs an existing conversation with at least two prior exchanges to summarize. This example has a conversation first, then compacts it and reads the `compact_boundary` system message that reports the result:
<CodeGroup>
```typescript TypeScript theme={null}
import { query } from "@anthropic-ai/claude-agent-sdk";
// Compaction needs existing history, so have a conversation first
try {
for await (const message of query({
prompt: "Explain what this project does",
options: { maxTurns: 2 }
})) {
if (message.type === "result" && message.subtype === "success") {
console.log(message.result);
}
}
} catch (error) {
// A single-shot query() throws after yielding an error result,
// so the follow-up query below still runs.
console.error(`Session ended with an error: ${error}`);
}
// Compact the same conversation
for await (const message of query({
prompt: "/compact",
options: { continue: true, maxTurns: 1 }
})) {
if (message.type === "system" && message.subtype === "compact_boundary") {
console.log("Compaction completed");
console.log("Pre-compaction tokens:", message.compact_metadata.pre_tokens);
console.log("Trigger:", message.compact_metadata.trigger);
// Example output:
// Compaction completed
// Pre-compaction tokens: 1842
// Trigger: manual
}
}
```
```python Python theme={null}
import asyncio
from claude_agent_sdk import query, ClaudeAgentOptions, ResultMessage, SystemMessage
async def main():
# Compaction needs existing history, so have a conversation first
try:
async for message in query(
prompt="Explain what this project does",
options=ClaudeAgentOptions(max_turns=2),
):
if isinstance(message, ResultMessage) and message.subtype == "success":
print(message.result)
except Exception as error:
# A single-shot query() raises after yielding an error result,
# so the follow-up query below still runs.
print(f"Session ended with an error: {error}")
# Compact the same conversation
async for message in query(
prompt="/compact",
options=ClaudeAgentOptions(continue_conversation=True, max_turns=1),
):
if isinstance(message, SystemMessage) and message.subtype == "compact_boundary":
print("Compaction completed")
print("Pre-compaction tokens:", message.data["compact_metadata"]["pre_tokens"])
print("Trigger:", message.data["compact_metadata"]["trigger"])
# Example output:
# Compaction completed
# Pre-compaction tokens: 1842
# Trigger: manual
asyncio.run(main())
```
</CodeGroup>
<Note>
A `compact_boundary` message only arrives when compaction ran. With nothing to summarize, `/compact` reports the reason instead of raising: the run still ends with a `success` result, no `compact_boundary` message is emitted, and the result text carries the message, for example `Not enough messages to compact.` after a single short exchange. A fresh one-shot `query()` call starts with empty context, so use this pattern in a session with prior turns, for example in [streaming input mode](/docs/en/agent-sdk/streaming-vs-single-mode) or when resuming a session.
</Note>
### `/clear` - Reset conversation context
The `/clear` command resets the conversation to an empty context, so subsequent prompts start with no prior conversation history. The previous conversation remains on disk and can be returned to by passing its session ID to the [`resume` option](/docs/en/agent-sdk/sessions#resume-by-id).
This is useful in [streaming input mode](/docs/en/agent-sdk/streaming-vs-single-mode), where you send multiple prompts over a single connection. For one-shot `query()` calls, each call already starts with empty context, so sending `/clear` has no practical effect; start a new `query()` instead.
## Creating Custom Slash Commands
In addition to using built-in slash commands, you can create your own custom commands that are available through the SDK. You define custom commands as markdown files in specific directories, the same way you configure subagents.
<Note>
Custom commands have been merged into skills. A file in `.claude/commands/` and a skill at `.claude/skills/<name>/SKILL.md` both create `/name` and work the same way. For new work, prefer skills, which add features like a directory for supporting files; see [Skills](/docs/en/agent-sdk/skills) for SDK usage. The CLI supports both locations, and the examples below remain accurate for `.claude/commands/`.
</Note>
### File Locations
Save custom slash commands in one of these directories, depending on their scope:
* **Project commands**: `.claude/commands/` - Available only in the current project. For new work, prefer `.claude/skills/`.
* **Personal commands**: `~/.claude/commands/` - Available across all your projects. For new work, prefer `~/.claude/skills/`.
### File Format
Each custom command is a markdown file where:
* The filename (without `.md` extension) becomes the command name
* The file content defines what the command does
* Optional YAML frontmatter provides configuration
#### Basic Example
Create the `.claude/commands` directory in your project if it doesn't exist, then create `.claude/commands/refactor.md`:
```markdown theme={null}
Refactor the selected code to improve readability and maintainability.
Focus on clean code principles and best practices.
```
This creates the `/refactor` command that you can use through the SDK.
#### With Frontmatter
Create `.claude/commands/security-check.md`:
```markdown theme={null}
---
allowed-tools: Read, Grep, Glob
description: Run security vulnerability scan
model: claude-opus-4-8
---
Analyze the codebase for security vulnerabilities including:
- SQL injection risks
- XSS vulnerabilities
- Exposed credentials
- Insecure configurations
```
### Using Custom Commands in the SDK
Once defined in the filesystem, custom commands are automatically available through the SDK:
<CodeGroup>
```typescript TypeScript theme={null}
import { query } from "@anthropic-ai/claude-agent-sdk";
// Use a custom command
try {
for await (const message of query({
prompt: "/refactor src/auth/login.ts",
options: { maxTurns: 3 }
})) {
if (message.type === "assistant") {
console.log("Refactoring suggestions:", message.message);
}
}
} catch (error) {
// A single-shot query() throws after yielding an error result,
// so the second query below still runs.
console.error(`Session ended with an error: ${error}`);
}
// Custom commands appear in the slash_commands list
for await (const message of query({
prompt: "Hello",
options: { maxTurns: 1 }
})) {
if (message.type === "system" && message.subtype === "init") {
console.log("Available commands:", message.slash_commands);
// Includes built-in commands plus bundled skills and your custom commands, for example:
// ["clear", "compact", "context", "usage", "code-review", "verify", "refactor", "security-check", ...]
}
}
```
Cut at 300 lines. The page has the rest.
agent-sdk/streaming-output First recorded · 344 lines, first recorded
# Stream responses in real-time ## Enable streaming output ## StreamEvent reference ## Message flow ## Stream tool calls ## Build a streaming UI ## Known limitations ## Next steps
The first capture of this source. The page was already there, and this is what it said.
# Stream responses in real-time
> Get real-time responses from the Agent SDK as text and tool calls stream in
By default, the Agent SDK yields complete `AssistantMessage` objects after Claude finishes generating each response. To receive incremental updates as text and tool calls are generated, enable partial message streaming.
<Tip>
This page covers output streaming (receiving tokens in real-time). For input modes (how you send messages), see [Send messages to agents](/docs/en/agent-sdk/streaming-vs-single-mode). You can also [stream responses using the Agent SDK via the CLI](/docs/en/headless).
</Tip>
## Enable streaming output
To enable streaming, set `include_partial_messages` (Python) or `includePartialMessages` (TypeScript) to `true` in your options. This causes the SDK to yield `StreamEvent` messages containing raw API events as they arrive, in addition to the usual `AssistantMessage` and `ResultMessage`.
Your code then needs to:
1. Check each message's type to distinguish `StreamEvent` from other message types
2. For `StreamEvent`, extract the `event` field and check its `type`
3. Look for `content_block_delta` events where `delta.type` is `text_delta`, which contain the actual text chunks
The example below enables streaming and prints text chunks as they arrive. Notice the nested type checks: first for `StreamEvent`, then for `content_block_delta`, then for `text_delta`:
<CodeGroup>
```python Python theme={null}
from claude_agent_sdk import query, ClaudeAgentOptions
from claude_agent_sdk.types import StreamEvent
import asyncio
async def stream_response():
options = ClaudeAgentOptions(
include_partial_messages=True,
allowed_tools=["Bash", "Read"],
)
async for message in query(prompt="List the files in my project", options=options):
if isinstance(message, StreamEvent):
event = message.event
if event.get("type") == "content_block_delta":
delta = event.get("delta", {})
if delta.get("type") == "text_delta":
print(delta.get("text", ""), end="", flush=True)
asyncio.run(stream_response())
```
```typescript TypeScript theme={null}
import { query } from "@anthropic-ai/claude-agent-sdk";
for await (const message of query({
prompt: "List the files in my project",
options: {
includePartialMessages: true,
allowedTools: ["Bash", "Read"]
}
})) {
if (message.type === "stream_event") {
const event = message.event;
if (event.type === "content_block_delta") {
if (event.delta.type === "text_delta") {
process.stdout.write(event.delta.text);
}
}
}
}
```
</CodeGroup>
## StreamEvent reference
When partial messages are enabled, you receive raw Claude API streaming events wrapped in an object. The type has different names in each SDK:
* **Python**: `StreamEvent` (import from `claude_agent_sdk.types`)
* **TypeScript**: `SDKPartialAssistantMessage` with `type: 'stream_event'`
Both contain raw Claude API events, not accumulated text. You need to extract and accumulate text deltas yourself. Here's the structure of each type:
<CodeGroup>
```python Python theme={null}
@dataclass
class StreamEvent:
uuid: str # Unique identifier for this event
session_id: str # Session identifier
event: dict[str, Any] # The raw Claude API stream event
parent_tool_use_id: str | None # Always None
```
```typescript TypeScript theme={null}
type SDKPartialAssistantMessage = {
type: "stream_event";
event: BetaRawMessageStreamEvent; // From Anthropic SDK
parent_tool_use_id: string | null;
uuid: UUID;
session_id: string;
ttft_ms?: number; // Time to first token in ms, present only on message_start events
};
```
</CodeGroup>
The `parent_tool_use_id` field is always `None` in Python and `null` in TypeScript. Stream events are emitted for the main session only; token-level deltas from subagents aren't forwarded. To attribute output to a subagent, use complete messages, which carry `parent_tool_use_id`. See [Detect subagent invocation](/docs/en/agent-sdk/subagents#detect-subagent-invocation).
The `event` field contains the raw streaming event from the [Claude API](https://platform.claude.com/docs/en/build-with-claude/streaming#event-types). Common event types include:
| Event Type | Description |
| :-------------------- | :---------------------------------------------- |
| `message_start` | Start of a new message |
| `content_block_start` | Start of a new content block (text or tool use) |
| `content_block_delta` | Incremental update to content |
| `content_block_stop` | End of a content block |
| `message_delta` | Message-level updates (stop reason, usage) |
| `message_stop` | End of the message |
## Message flow
With partial messages enabled, you receive messages in this order:
```text theme={null}
StreamEvent (message_start)
StreamEvent (content_block_start) - text block
StreamEvent (content_block_delta) - text chunks...
StreamEvent (content_block_stop)
StreamEvent (content_block_start) - tool_use block
StreamEvent (content_block_delta) - tool input chunks...
StreamEvent (content_block_stop)
StreamEvent (message_delta)
StreamEvent (message_stop)
AssistantMessage - complete message with all content
... tool executes ...
... more streaming events for next turn ...
ResultMessage - final result
```
Without partial messages enabled, you receive all message types except `StreamEvent`. Common types include `SystemMessage` (session initialization), `AssistantMessage` (complete responses), `ResultMessage` (final result), and a compact boundary message indicating when conversation history was compacted (`SDKCompactBoundaryMessage` in TypeScript; `SystemMessage` with subtype `"compact_boundary"` in Python).
## Stream tool calls
Tool calls also stream incrementally. You can track when tools start, receive their input as it's generated, and see when they complete. The example below tracks the current tool being called and accumulates the JSON input as it streams in. It uses three event types:
* `content_block_start`: tool begins
* `content_block_delta` with `input_json_delta`: input chunks arrive
* `content_block_stop`: tool call complete
<CodeGroup>
```python Python theme={null}
from claude_agent_sdk import query, ClaudeAgentOptions
from claude_agent_sdk.types import StreamEvent
import asyncio
async def stream_tool_calls():
options = ClaudeAgentOptions(
include_partial_messages=True,
allowed_tools=["Read", "Bash"],
)
# Track the current tool and accumulate its input JSON
current_tool = None
tool_input = ""
async for message in query(prompt="Read the README.md file", options=options):
if isinstance(message, StreamEvent):
event = message.event
event_type = event.get("type")
if event_type == "content_block_start":
# New tool call is starting
content_block = event.get("content_block", {})
if content_block.get("type") == "tool_use":
current_tool = content_block.get("name")
tool_input = ""
print(f"Starting tool: {current_tool}")
elif event_type == "content_block_delta":
delta = event.get("delta", {})
if delta.get("type") == "input_json_delta":
# Accumulate JSON input as it streams in
chunk = delta.get("partial_json", "")
tool_input += chunk
print(f" Input chunk: {chunk}")
elif event_type == "content_block_stop":
# Tool call complete - show final input
if current_tool:
print(f"Tool {current_tool} called with: {tool_input}")
current_tool = None
asyncio.run(stream_tool_calls())
```
```typescript TypeScript theme={null}
import { query } from "@anthropic-ai/claude-agent-sdk";
// Track the current tool and accumulate its input JSON
let currentTool: string | null = null;
let toolInput = "";
for await (const message of query({
prompt: "Read the README.md file",
options: {
includePartialMessages: true,
allowedTools: ["Read", "Bash"]
}
})) {
if (message.type === "stream_event") {
const event = message.event;
if (event.type === "content_block_start") {
// New tool call is starting
if (event.content_block.type === "tool_use") {
currentTool = event.content_block.name;
toolInput = "";
console.log(`Starting tool: ${currentTool}`);
}
} else if (event.type === "content_block_delta") {
if (event.delta.type === "input_json_delta") {
// Accumulate JSON input as it streams in
const chunk = event.delta.partial_json;
toolInput += chunk;
console.log(` Input chunk: ${chunk}`);
}
} else if (event.type === "content_block_stop") {
// Tool call complete - show final input
if (currentTool) {
console.log(`Tool ${currentTool} called with: ${toolInput}`);
currentTool = null;
}
}
}
}
```
</CodeGroup>
## Build a streaming UI
This example combines text and tool streaming into a cohesive UI. It tracks whether the agent is currently executing a tool (using an `in_tool` flag) to show status indicators like `[Using Read...]` while tools run. Text streams normally when not in a tool, and tool completion triggers a "done" message. This pattern is useful for chat interfaces that need to show progress during multi-step agent tasks.
<CodeGroup>
```python Python theme={null}
from claude_agent_sdk import query, ClaudeAgentOptions, ResultMessage
from claude_agent_sdk.types import StreamEvent
import asyncio
import sys
async def streaming_ui():
options = ClaudeAgentOptions(
include_partial_messages=True,
allowed_tools=["Read", "Bash", "Grep"],
)
# Track whether we're currently in a tool call
in_tool = False
async for message in query(
prompt="Find all TODO comments in the codebase", options=options
):
if isinstance(message, StreamEvent):
event = message.event
event_type = event.get("type")
if event_type == "content_block_start":
content_block = event.get("content_block", {})
if content_block.get("type") == "tool_use":
# Tool call is starting - show status indicator
tool_name = content_block.get("name")
print(f"\n[Using {tool_name}...]", end="", flush=True)
in_tool = True
elif event_type == "content_block_delta":
delta = event.get("delta", {})
# Only stream text when not executing a tool
if delta.get("type") == "text_delta" and not in_tool:
sys.stdout.write(delta.get("text", ""))
sys.stdout.flush()
elif event_type == "content_block_stop":
if in_tool:
# Tool call finished
print(" done", flush=True)
in_tool = False
elif isinstance(message, ResultMessage):
# Agent finished all work
print(f"\n\n--- Complete ---")
asyncio.run(streaming_ui())
```
```typescript TypeScript theme={null}
import { query } from "@anthropic-ai/claude-agent-sdk";
// Track whether we're currently in a tool call
let inTool = false;
for await (const message of query({
prompt: "Find all TODO comments in the codebase",
options: {
Cut at 300 lines. The page has the rest.
agent-sdk/streaming-vs-single-mode First recorded · 303 lines, first recorded
# Streaming Input ## Overview ## Streaming Input Mode (Recommended) ### How It Works ### Benefits ### Implementation Example ## Single Message Input ### When to Use Single Message Input ### Limitations ### Implementation Example
The first capture of this source. The page was already there, and this is what it said.
# Streaming Input
> Understanding the two input modes for Claude Agent SDK and when to use each
## Overview
The Claude Agent SDK supports two distinct input modes for interacting with agents:
* **Streaming Input Mode**: a persistent, interactive session
* **Single Message Input**: one-shot queries that use session state and resuming
## Streaming Input Mode (Recommended)
Streaming input mode is the **preferred** way to use the Claude Agent SDK. It provides full access to the agent's capabilities and enables rich, interactive experiences.
It allows the agent to operate as a long lived process that takes in user input, handles interruptions, surfaces permission requests, and handles session management.
### How It Works
```mermaid theme={null}
sequenceDiagram
participant App as Your Application
participant Agent as Claude Agent
participant Tools as Tools/Hooks
participant FS as Environment/<br/>File System
App->>Agent: Initialize with AsyncGenerator
activate Agent
App->>Agent: Yield Message 1
Agent->>Tools: Execute tools
Tools->>FS: Read files
FS-->>Tools: File contents
Tools->>FS: Write/Edit files
FS-->>Tools: Success/Error
Agent-->>App: Stream partial response
Agent-->>App: Stream more content...
Agent->>App: Complete Message 1
App->>Agent: Yield Message 2 + Image
Agent->>Tools: Process image & execute
Tools->>FS: Access filesystem
FS-->>Tools: Operation results
Agent-->>App: Stream response 2
App->>Agent: Queue Message 3
App->>Agent: Interrupt/Cancel
Agent->>App: Handle interruption
Note over App,Agent: Session stays alive
Note over Tools,FS: Persistent file system<br/>state maintained
deactivate Agent
```
### Benefits
In streaming input mode, you work in a persistent session with these capabilities:
* **Image uploads**: attach images directly to messages for visual analysis and understanding
* **Queued messages**: send multiple messages that process sequentially, with ability to interrupt
* **Tool integration**: full access to all tools and custom MCP servers during the session
* **Real-time feedback**: see responses as they're generated, not just final results
* **Context persistence**: maintain conversation context across multiple turns naturally
### Implementation Example
These examples read an image named `diagram.png` from the working directory. Create one there first, or change the filename to point at your own image.
<CodeGroup>
```typescript TypeScript theme={null}
import { query, type SDKUserMessage } from "@anthropic-ai/claude-agent-sdk";
import { readFile } from "fs/promises";
async function* generateMessages(): AsyncGenerator<SDKUserMessage> {
// First message
yield {
type: "user",
message: {
role: "user",
content: "Analyze this codebase for security issues"
},
parent_tool_use_id: null
};
// Wait for conditions or user input
await new Promise((resolve) => setTimeout(resolve, 2000));
// Follow-up with image
yield {
type: "user",
message: {
role: "user",
content: [
{
type: "text",
text: "Review this architecture diagram"
},
{
type: "image",
source: {
type: "base64",
media_type: "image/png",
data: await readFile("diagram.png", "base64")
}
}
]
},
parent_tool_use_id: null
};
}
// Process streaming responses
for await (const message of query({
prompt: generateMessages(),
options: {
maxTurns: 10,
allowedTools: ["Read", "Grep"]
}
})) {
if (message.type === "result" && message.subtype === "success") {
console.log(message.result);
}
}
```
```python Python theme={null}
from claude_agent_sdk import (
ClaudeSDKClient,
ClaudeAgentOptions,
AssistantMessage,
TextBlock,
)
import asyncio
import base64
async def streaming_analysis():
async def message_generator():
# First message
yield {
"type": "user",
"message": {
"role": "user",
"content": "Analyze this codebase for security issues",
},
}
# Wait for conditions
await asyncio.sleep(2)
# Follow-up with image
with open("diagram.png", "rb") as f:
image_data = base64.b64encode(f.read()).decode()
yield {
"type": "user",
"message": {
"role": "user",
"content": [
{"type": "text", "text": "Review this architecture diagram"},
{
"type": "image",
"source": {
"type": "base64",
"media_type": "image/png",
"data": image_data,
},
},
],
},
}
# Use ClaudeSDKClient for streaming input
options = ClaudeAgentOptions(max_turns=10, allowed_tools=["Read", "Grep"])
async with ClaudeSDKClient(options) as client:
# Send streaming input
await client.query(message_generator())
# Process responses
async for message in client.receive_response():
if isinstance(message, AssistantMessage):
for block in message.content:
if isinstance(block, TextBlock):
print(block.text)
asyncio.run(streaming_analysis())
```
</CodeGroup>
When you run the example, the TypeScript version prints each response as it completes. The Python version's `receive_response()` loop ends at the first result message, so it prints the security analysis; to read both responses, use one `query()` and `receive_response()` pair per message as shown in the [Python reference's example of continuing a conversation](/docs/en/agent-sdk/python#example-continuing-a-conversation).
<Note>
In the TypeScript SDK, if your message generator throws, for example when a file it reads is missing, the stream ends with an error that reads `Claude Code process aborted by user` instead of the original error, so check the code inside your generator first when you see that message. The error may also be preceded by a long minified line of bundled SDK source, so read to the end of the output for the error text.
In the Python SDK, a generator exception is logged at debug level and the session stalls without raising, so if a streaming session hangs with no output, enable debug logging and check your generator.
</Note>
## Single Message Input
Single message input is simpler but more limited.
### When to Use Single Message Input
Use single message input when:
* You need a one-shot response
* You do not need image attachments or mid-session control methods
* You need to operate in a stateless environment, such as a lambda function
### Limitations
<Warning>
Single message input mode does **not** support:
* Direct image attachments in messages
* Dynamic message queueing
* Real-time interruption
* Natural multi-turn conversations
</Warning>
If a query ends with an error result, such as `error_max_turns`, a single message `query()` call raises an error that includes the failure text after yielding the final result message, so wrap the loop in a try block if your code needs to continue. See [Handle the result](/docs/en/agent-sdk/agent-loop#handle-the-result) for the result subtypes.
### Implementation Example
<CodeGroup>
```typescript TypeScript theme={null}
import { query } from "@anthropic-ai/claude-agent-sdk";
// Simple one-shot query
// query() throws after an error result, such as error_max_turns
try {
for await (const message of query({
prompt: "Explain the authentication flow",
options: {
maxTurns: 5,
allowedTools: ["Read", "Grep"]
}
})) {
if (message.type === "result" && message.subtype === "success") {
console.log(message.result);
}
}
} catch (error) {
console.error(`Query failed: ${error}`);
}
// Continue conversation with session management
try {
for await (const message of query({
prompt: "Now explain the authorization process",
options: {
continue: true,
maxTurns: 5
}
})) {
if (message.type === "result" && message.subtype === "success") {
console.log(message.result);
}
}
} catch (error) {
console.error(`Query failed: ${error}`);
}
```
```python Python theme={null}
from claude_agent_sdk import query, ClaudeAgentOptions, ResultMessage
import asyncio
async def single_message_example():
# Simple one-shot query using query() function
# query() raises after an error result, such as error_max_turns
try:
async for message in query(
prompt="Explain the authentication flow",
options=ClaudeAgentOptions(max_turns=5, allowed_tools=["Read", "Grep"]),
):
if isinstance(message, ResultMessage) and message.subtype == "success":
print(message.result)
# The SDK raises a plain Exception for error results, so match Exception here
except Exception as e:
print(f"Query failed: {e}")
# Continue conversation with session management
try:
async for message in query(
prompt="Now explain the authorization process",
options=ClaudeAgentOptions(continue_conversation=True, max_turns=5),
):
if isinstance(message, ResultMessage) and message.subtype == "success":
print(message.result)
except Exception as e:
print(f"Query failed: {e}")
asyncio.run(single_message_example())
```
Cut at 300 lines. The page has the rest.
agent-sdk/structured-outputs First recorded · 488 lines, first recorded
# Get structured output from agents ## Why structured outputs? ## Quick start ## Type-safe schemas with Zod and Pydantic ## Output format configuration ## Example: TODO tracking agent ## Error handling ## Related resources
The first capture of this source. The page was already there, and this is what it said.
# Get structured output from agents
> Return validated JSON from agent workflows using JSON Schema, Zod, or Pydantic. Get type-safe, structured data after multi-turn tool use.
Structured outputs let you define the exact shape of data you want back from an agent. The agent can use any tools it needs to complete the task, and you still get validated JSON matching your schema at the end. Define a [JSON Schema](https://json-schema.org/understanding-json-schema/about) for the structure you need, and the SDK validates the output against it, re-prompting on mismatch. If validation does not succeed within the retry limit, the result is an error instead of structured data; see [Error handling](#error-handling).
For full type safety, use [Zod](#type-safe-schemas-with-zod-and-pydantic) (TypeScript) or [Pydantic](#type-safe-schemas-with-zod-and-pydantic) (Python) to define your schema and get strongly-typed objects back.
## Why structured outputs?
Agents return free-form text by default, which works for chat but not when you need to use the output programmatically. Structured outputs give you typed data you can pass directly to your application logic, database, or UI components.
Consider a recipe app where an agent searches the web and brings back recipes. Without structured outputs, you get free-form text that you'd need to parse yourself. With structured outputs, you define the shape you want and get typed data you can use directly in your app.
<AccordionGroup>
<Accordion title="Without structured outputs">
```text theme={null}
Here's a classic chocolate chip cookie recipe!
**Chocolate Chip Cookies**
Prep time: 15 minutes | Cook time: 10 minutes
Ingredients:
- 2 1/4 cups all-purpose flour
- 1 cup butter, softened
...
```
To use this in your app, you'd need to parse out the title, convert "15 minutes" to a number, separate ingredients from instructions, and handle inconsistent formatting across responses.
</Accordion>
<Accordion title="With structured outputs">
```json theme={null}
{
"name": "Chocolate Chip Cookies",
"prep_time_minutes": 15,
"cook_time_minutes": 10,
"ingredients": [
{ "item": "all-purpose flour", "amount": 2.25, "unit": "cups" },
{ "item": "butter, softened", "amount": 1, "unit": "cup" }
// ...
],
"steps": ["Preheat oven to 375°F", "Cream butter and sugar" /* ... */]
}
```
Typed data you can use directly in your UI.
</Accordion>
</AccordionGroup>
## Quick start
To use structured outputs, define a [JSON Schema](https://json-schema.org/understanding-json-schema/about) describing the shape of data you want, then pass it to `query()` via the `outputFormat` option (TypeScript) or `output_format` option (Python). When the agent finishes, the result message includes a `structured_output` field with validated data matching your schema.
The example below asks the agent to research Anthropic and return the company name, year founded, and headquarters as structured output.
<CodeGroup>
```typescript TypeScript theme={null}
import { query } from "@anthropic-ai/claude-agent-sdk";
// Define the shape of data you want back
const schema = {
type: "object",
properties: {
company_name: { type: "string" },
founded_year: { type: "number" },
headquarters: { type: "string" }
},
required: ["company_name"]
};
try {
for await (const message of query({
prompt: "Research Anthropic and provide key company information",
options: {
outputFormat: {
type: "json_schema",
schema: schema
}
}
})) {
// The result message contains structured_output with validated data
if (message.type === "result" && message.subtype === "success" && message.structured_output) {
console.log(message.structured_output);
// { company_name: "Anthropic", founded_year: 2021, headquarters: "San Francisco, CA" }
}
}
} catch (error) {
// A single-shot query() throws after yielding an error result, such as
// error_max_structured_output_retries; see the Error handling section.
console.error(`Session ended with an error: ${error}`);
}
```
```python Python theme={null}
import asyncio
from claude_agent_sdk import query, ClaudeAgentOptions, ResultMessage
# Define the shape of data you want back
schema = {
"type": "object",
"properties": {
"company_name": {"type": "string"},
"founded_year": {"type": "number"},
"headquarters": {"type": "string"},
},
"required": ["company_name"],
}
async def main():
try:
async for message in query(
prompt="Research Anthropic and provide key company information",
options=ClaudeAgentOptions(
output_format={"type": "json_schema", "schema": schema}
),
):
# The result message contains structured_output with validated data
if isinstance(message, ResultMessage) and message.structured_output:
print(message.structured_output)
# {'company_name': 'Anthropic', 'founded_year': 2021, 'headquarters': 'San Francisco, CA'}
except Exception as error:
# A single-shot query() raises after yielding an error result, such as
# error_max_structured_output_retries; see the Error handling section.
print(f"Session ended with an error: {error}")
asyncio.run(main())
```
</CodeGroup>
## Type-safe schemas with Zod and Pydantic
Instead of writing JSON Schema by hand, you can use [Zod](https://zod.dev/) (TypeScript) or [Pydantic](https://docs.pydantic.dev/latest/) (Python) to define your schema. These libraries generate the JSON Schema for you and let you parse the response into a fully-typed object you can use throughout your codebase with autocomplete and type checking.
The example below defines a schema for a feature implementation plan with a summary, list of steps (each with complexity level), and potential risks. The agent plans the feature and returns a typed `FeaturePlan` object. You can then access properties like `plan.summary` and iterate over `plan.steps` with full type safety.
The SDK validates schemas with JSON Schema draft-07, so schemas that declare a newer version are rejected. Zod targets draft 2020-12 by default, so pass `target: "draft-7"` when converting your schema.
<CodeGroup>
```typescript TypeScript theme={null}
import { z } from "zod";
import { query } from "@anthropic-ai/claude-agent-sdk";
// Define schema with Zod
const FeaturePlan = z.object({
feature_name: z.string(),
summary: z.string(),
steps: z.array(
z.object({
step_number: z.number(),
description: z.string(),
estimated_complexity: z.enum(["low", "medium", "high"])
})
),
risks: z.array(z.string())
});
type FeaturePlan = z.infer<typeof FeaturePlan>;
// Convert to JSON Schema using the draft-07 target the SDK expects
const schema = z.toJSONSchema(FeaturePlan, { target: "draft-7" });
// Use in query
try {
for await (const message of query({
prompt:
"Plan how to add dark mode support to a React app. Break it into implementation steps.",
options: {
outputFormat: {
type: "json_schema",
schema: schema
}
}
})) {
if (message.type === "result" && message.subtype === "success" && message.structured_output) {
// Validate and get fully typed result
const parsed = FeaturePlan.safeParse(message.structured_output);
if (parsed.success) {
const plan: FeaturePlan = parsed.data;
console.log(`Feature: ${plan.feature_name}`);
console.log(`Summary: ${plan.summary}`);
plan.steps.forEach((step) => {
console.log(`${step.step_number}. [${step.estimated_complexity}] ${step.description}`);
});
}
}
}
} catch (error) {
// A single-shot query() throws after yielding an error result, such as
// error_max_structured_output_retries; see the Error handling section.
console.error(`Session ended with an error: ${error}`);
}
```
```python Python theme={null}
import asyncio
from pydantic import BaseModel
from claude_agent_sdk import query, ClaudeAgentOptions, ResultMessage
class Step(BaseModel):
step_number: int
description: str
estimated_complexity: str # 'low', 'medium', 'high'
class FeaturePlan(BaseModel):
feature_name: str
summary: str
steps: list[Step]
risks: list[str]
async def main():
try:
async for message in query(
prompt="Plan how to add dark mode support to a React app. Break it into implementation steps.",
options=ClaudeAgentOptions(
output_format={
"type": "json_schema",
"schema": FeaturePlan.model_json_schema(),
}
),
):
if isinstance(message, ResultMessage) and message.structured_output:
# Validate and get fully typed result
plan = FeaturePlan.model_validate(message.structured_output)
print(f"Feature: {plan.feature_name}")
print(f"Summary: {plan.summary}")
for step in plan.steps:
print(
f"{step.step_number}. [{step.estimated_complexity}] {step.description}"
)
except Exception as error:
# A single-shot query() raises after yielding an error result, such as
# error_max_structured_output_retries; see the Error handling section.
print(f"Session ended with an error: {error}")
asyncio.run(main())
```
</CodeGroup>
**Benefits:**
* Full type inference (TypeScript) and type hints (Python)
* Runtime validation with `safeParse()` or `model_validate()`
* Better error messages
* Composable, reusable schemas
## Output format configuration
The `outputFormat` (TypeScript) or `output_format` (Python) option accepts an object with:
* `type`: Set to `"json_schema"` for structured outputs
* `schema`: A [JSON Schema](https://json-schema.org/understanding-json-schema/about) object defining your output structure. You can generate this from a Zod schema with `z.toJSONSchema(schema, { target: "draft-7" })` or a Pydantic model with `.model_json_schema()`
The SDK supports standard JSON Schema features including all basic types (object, array, string, number, boolean, null), `enum`, `const`, `required`, nested objects, and `$ref` definitions. For the full list of supported features and limitations, see [JSON Schema limitations](https://platform.claude.com/docs/en/build-with-claude/structured-outputs#json-schema-limitations).
A schema that isn't valid JSON Schema fails the run at startup with an error naming the problem. Before v2.1.205, an invalid schema was silently ignored and the agent returned unstructured text.
The `format` keyword, such as `"format": "email"`, is accepted as an annotation and isn't enforced by the SDK's validator. Before v2.1.205, any schema containing `format` was treated as invalid.
## Example: TODO tracking agent
This example demonstrates how structured outputs work with multi-step tool use. The agent needs to find TODO comments in the codebase, then look up git blame information for each one. It autonomously decides which tools to use (Grep to search, Bash to run git commands) and combines the results into a single structured response.
The schema includes optional fields (`author` and `date`) since git blame information might not be available for all files. The agent fills in what it can find and omits the rest.
<CodeGroup>
```typescript TypeScript theme={null}
import { query } from "@anthropic-ai/claude-agent-sdk";
// Define structure for TODO extraction
const todoSchema = {
type: "object",
properties: {
todos: {
type: "array",
items: {
type: "object",
properties: {
text: { type: "string" },
file: { type: "string" },
line: { type: "number" },
author: { type: "string" },
date: { type: "string" }
},
required: ["text", "file", "line"]
}
},
total_count: { type: "number" }
},
required: ["todos", "total_count"]
};
// Agent uses Grep to find TODOs, Bash to get git blame info
try {
Cut at 300 lines. The page has the rest.
agent-sdk/subagents First recorded · 742 lines, first recorded
# Subagents in the SDK ## Overview ## Benefits of using subagents ### Context isolation ### Parallelization ### Specialized instructions and knowledge ### Tool restrictions ## Create subagents ### Programmatic definition (recommended) ### AgentDefinition configuration ### Filesystem-based definition (alternative) ## What subagents inherit ## Invoke subagents ### Automatic invocation ### Explicit invocation ### Dynamic agent configuration ## Detect subagent invocation ## Resume subagents ## Tool restrictions ### Common tool combinations ## Cap subagent depth, concurrency, and spend ### Run Opus 5 with subagents ## Scale up with dynamic workflows ## Troubleshooting ### Claude not delegating to subagents ### Filesystem-based agents not loading ## Related documentation
The first capture of this source. The page was already there, and this is what it said.
# Subagents in the SDK
> Define and invoke subagents to isolate context, run tasks in parallel, and apply specialized instructions in your Claude Agent SDK applications.
Subagents are separate agent instances that your main agent can spawn to handle focused subtasks.
Use them to isolate context, run multiple analyses in parallel, and apply specialized instructions without adding to the main agent's prompt.
This guide explains how to define and use subagents in the SDK using the `agents` parameter.
## Overview
You can create subagents in three ways:
* **Programmatically**: use the `agents` parameter in your `query()` options. See the [TypeScript](/docs/en/agent-sdk/typescript#agentdefinition) and [Python](/docs/en/agent-sdk/python#agentdefinition) references
* **Filesystem-based**: define agents as markdown files in `.claude/agents/` directories. See [defining subagents as files](/docs/en/sub-agents)
* **Built-in general-purpose**: Claude can invoke the built-in `general-purpose` subagent at any time via the Agent tool without you defining anything
This guide focuses on the programmatic approach, which is recommended for SDK applications.
## Benefits of using subagents
### Context isolation
Each subagent runs in its own fresh conversation. Intermediate tool calls and results stay inside the subagent; only its final message returns to the parent. See [What subagents inherit](#what-subagents-inherit) for exactly what's in the subagent's context.
**Example:** a `research-assistant` subagent can explore dozens of files without any of that content accumulating in the main conversation. The parent receives a concise summary, not every file the subagent read.
### Parallelization
Multiple subagents can run concurrently, so independent subtasks finish in the time of the slowest one rather than the sum of all of them.
**Example:** during a code review, you can run `style-checker`, `security-scanner`, and `test-coverage` subagents simultaneously instead of sequentially.
### Specialized instructions and knowledge
Each subagent can have tailored system prompts with specific expertise, best practices, and constraints.
**Example:** a `database-migration` subagent can have detailed knowledge about SQL best practices, rollback strategies, and data integrity checks that would be unnecessary noise in the main agent's instructions.
### Tool restrictions
Subagents can be limited to specific tools, reducing the risk of unintended actions.
**Example:** a `doc-reviewer` subagent might only have access to Read and Grep tools, ensuring it can analyze but never accidentally modify your documentation files.
## Create subagents
### Programmatic definition (recommended)
Define subagents directly in your code using the `agents` parameter. Claude invokes subagents through the `Agent` tool, so include `Agent` in `allowedTools` to auto-approve subagent invocations without a permission prompt.
Most examples on this page print only the final result. To confirm that Claude delegated to a subagent rather than answering directly, see [Detect subagent invocation](#detect-subagent-invocation).
This example creates two subagents: a code reviewer with read-only access and a test runner that can execute commands.
<CodeGroup>
```python Python theme={null}
import asyncio
from claude_agent_sdk import query, ClaudeAgentOptions, AgentDefinition
async def main():
async for message in query(
prompt="Review the authentication module for security issues",
options=ClaudeAgentOptions(
# Auto-approve these tools, including Agent for subagent invocation
allowed_tools=["Read", "Grep", "Glob", "Agent"],
agents={
"code-reviewer": AgentDefinition(
# description tells Claude when to use this subagent
description="Expert code review specialist. Use for quality, security, and maintainability reviews.",
# prompt defines the subagent's behavior and expertise
prompt="""You are a code review specialist with expertise in security, performance, and best practices.
When reviewing code:
- Identify security vulnerabilities
- Check for performance issues
- Verify adherence to coding standards
- Suggest specific improvements
Be thorough but concise in your feedback.""",
# tools restricts what the subagent can do (read-only here)
tools=["Read", "Grep", "Glob"],
# model overrides the default model for this subagent
model="sonnet",
),
"test-runner": AgentDefinition(
description="Runs and analyzes test suites. Use for test execution and coverage analysis.",
prompt="""You are a test execution specialist. Run tests and provide clear analysis of results.
Focus on:
- Running test commands
- Analyzing test output
- Identifying failing tests
- Suggesting fixes for failures""",
# Bash access lets this subagent run test commands
tools=["Bash", "Read", "Grep"],
),
},
),
):
if hasattr(message, "result"):
print(message.result)
asyncio.run(main())
```
```typescript TypeScript theme={null}
import { query } from "@anthropic-ai/claude-agent-sdk";
for await (const message of query({
prompt: "Review the authentication module for security issues",
options: {
// Auto-approve these tools, including Agent for subagent invocation
allowedTools: ["Read", "Grep", "Glob", "Agent"],
agents: {
"code-reviewer": {
// description tells Claude when to use this subagent
description:
"Expert code review specialist. Use for quality, security, and maintainability reviews.",
// prompt defines the subagent's behavior and expertise
prompt: `You are a code review specialist with expertise in security, performance, and best practices.
When reviewing code:
- Identify security vulnerabilities
- Check for performance issues
- Verify adherence to coding standards
- Suggest specific improvements
Be thorough but concise in your feedback.`,
// tools restricts what the subagent can do (read-only here)
tools: ["Read", "Grep", "Glob"],
// model overrides the default model for this subagent
model: "sonnet"
},
"test-runner": {
description:
"Runs and analyzes test suites. Use for test execution and coverage analysis.",
prompt: `You are a test execution specialist. Run tests and provide clear analysis of results.
Focus on:
- Running test commands
- Analyzing test output
- Identifying failing tests
- Suggesting fixes for failures`,
// Bash access lets this subagent run test commands
tools: ["Bash", "Read", "Grep"]
}
}
}
})) {
if ("result" in message) console.log(message.result);
}
```
</CodeGroup>
### AgentDefinition configuration
| Field | Type | Required | Description |
| :---------------- | :---------------------------------------------------------- | :------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `description` | `string` | Yes | Natural language description of when to use this agent |
| `prompt` | `string` | Yes | The agent's system prompt defining its role and behavior |
| `tools` | `string[]` | No | Array of allowed tool names. If omitted, inherits every [tool available to subagents](/docs/en/sub-agents#available-tools) |
| `disallowedTools` | `string[]` | No | Array of tool names to remove from the agent's tool set. MCP server-level patterns are also accepted: `mcp__server` or `mcp__server__*` removes every tool from that server, and `mcp__*` removes every MCP tool from any server |
| `model` | `string` | No | Model override for this agent. Accepts an alias such as `'fable'`, `'opus'`, `'sonnet'`, `'haiku'`, `'inherit'`, or a full model ID. Defaults to main model if omitted |
| `skills` | `string[]` | No | List of skill names to preload into the agent's context at startup. Unlisted skills remain invocable through the Skill tool |
| `memory` | `'user' \| 'project' \| 'local'` | No | Memory source for this agent |
| `mcpServers` | `(string \| object)[]` | No | MCP servers available to this agent, by name or inline config |
| `initialPrompt` | `string` | No | Auto-submitted as the first user turn when this agent runs as the main thread agent. Ignored when the agent is invoked as a subagent |
| `maxTurns` | `number` | No | Maximum number of agentic turns before the agent stops |
| `background` | `boolean` | No | Run this agent as a non-blocking background task when invoked |
| `effort` | `'low' \| 'medium' \| 'high' \| 'xhigh' \| 'max' \| number` | No | Reasoning effort level for this agent |
| `permissionMode` | `PermissionMode` | No | Permission mode for tool execution within this agent |
In the Python SDK, multi-word field names such as `disallowedTools` and `mcpServers` keep their camelCase spelling to match the wire format rather than following Python's snake\_case convention. See the [`AgentDefinition` reference](/docs/en/agent-sdk/python#agentdefinition) for details.
Two subagent behaviors changed in Claude Code v2.1.198:
* Subagents run in the background by default. An Agent tool call that omits the [`run_in_background`](/docs/en/agent-sdk/typescript) input launches a background subagent, and Claude sets `run_in_background: false` when it needs the result before continuing. Before v2.1.198, omitting `run_in_background` ran the subagent synchronously. Set the `background` field to `true` to force background execution for a specific agent regardless of what Claude requests.
* A subagent inherits the main session's extended thinking configuration.
Subagents can also spawn subagents of their own. To limit how deep that nesting goes, how many subagents run at once, and how much a query spends, see [Cap subagent depth, concurrency, and spend](#cap-subagent-depth-concurrency-and-spend).
### Filesystem-based definition (alternative)
You can also define subagents as markdown files in `.claude/agents/` directories. See the [Claude Code subagents documentation](/docs/en/sub-agents) for details on this approach. Programmatically defined agents take precedence over filesystem-based agents with the same name.
<Note>
Even without defining custom subagents, Claude can spawn the built-in `general-purpose` subagent. This is useful for delegating research or exploration tasks without creating specialized agents. Include `Agent` in `allowedTools` so these invocations auto-approve without a permission prompt.
</Note>
## What subagents inherit
A subagent's context window starts fresh, with no parent conversation, but isn't empty. The only content you pass from parent to subagent is the Agent tool's prompt string, so include any file paths, error messages, or decisions the subagent needs directly in that prompt.
A subagent that has the [`SendMessage`](/docs/en/tools-reference) tool starts with a list of the other named agents running in the session, so it knows which names it can send messages to. Claude Code adds the list to the subagent's first turn automatically. A [fork](/docs/en/sub-agents#fork-the-current-conversation) doesn't get the list because it inherits the parent conversation instead. The list requires Claude Code v2.1.206 or later.
| The subagent receives | The subagent doesn't receive |
| :------------------------------------------------------------------------------------------------------------------------------------ | :----------------------------------------------------------------- |
| Its own system prompt (`AgentDefinition.prompt`) and the Agent tool's prompt | The parent's conversation history or tool results |
| Project CLAUDE.md (loaded via [`settingSources`](/docs/en/agent-sdk/claude-code-features#control-filesystem-settings-with-settingsources)) | Preloaded skill content, unless listed in `AgentDefinition.skills` |
| Tool definitions (inherited from parent or the subset in `tools`, [filtered for background runs](/docs/en/sub-agents#available-tools)) | The parent's system prompt |
<Note>
The parent receives the subagent's final message as the Agent tool result, but may summarize it in its own response. To preserve subagent output verbatim in the user-facing response, include an instruction to do so in the prompt or `systemPrompt` option you pass to the main `query()` call.
In v2.1.210 and later, Claude Code [scans the final message for instruction-shaped patterns](/docs/en/sub-agents#subagent-output-scanning) before the parent reads it. The scan treats three kinds of pattern differently:
* **Control-tag imitation**: Claude Code neutralizes a tag that only the harness emits, such as a `<system-reminder>` block, in place. It inserts a backslash after the opening angle bracket and deletes nothing.
* **Permission-configuration mentions**: Claude Code keeps references to the permission configuration, such as `.claude/settings.json`, `bypassPermissions`, or `--dangerously-skip-permissions`, as written.
* **Turn markers**: a line that starts with `Human:` or `Assistant:` gets a backslash before the colon, so the message can't imitate a conversation turn boundary.
For a control-tag or permission-configuration match, Claude Code prepends a `[harness: ...]` marker line naming the matched patterns; a turn-marker match doesn't add the marker line. Those are the only modifications the scan makes: it never removes or rewords the subagent's text.
</Note>
An API error that ends the subagent early, such as a rate limit, is never delivered as its result. See [API errors in subagents](/docs/en/sub-agents#api-errors-in-subagents) for the foreground and background behavior.
## Invoke subagents
### Automatic invocation
Claude automatically decides when to invoke subagents based on the task and each subagent's `description`. For example, if you define a `performance-optimizer` subagent with the description "Performance optimization specialist for query tuning", Claude will invoke it when your prompt mentions optimizing queries.
Write clear, specific descriptions so Claude can match tasks to the right subagent.
### Explicit invocation
To guarantee Claude uses a specific subagent, mention it by name in your prompt:
```text theme={null}
"Use the code-reviewer agent to check the authentication module"
```
This bypasses automatic matching and directly invokes the named subagent.
### Dynamic agent configuration
You can create agent definitions dynamically based on runtime conditions. This example creates a security reviewer with different strictness levels, using a more powerful model for strict reviews.
<CodeGroup>
```python Python theme={null}
import asyncio
from claude_agent_sdk import query, ClaudeAgentOptions, AgentDefinition
# Factory function that returns an AgentDefinition
# This pattern lets you customize agents based on runtime conditions
def create_security_agent(security_level: str) -> AgentDefinition:
is_strict = security_level == "strict"
return AgentDefinition(
description="Security code reviewer",
# Customize the prompt based on strictness level
prompt=f"You are a {'strict' if is_strict else 'balanced'} security reviewer...",
tools=["Read", "Grep", "Glob"],
# Key insight: use a more capable model for high-stakes reviews
model="opus" if is_strict else "sonnet",
)
async def main():
# The agent is created at query time, so each request can use different settings
async for message in query(
prompt="Review this PR for security issues",
options=ClaudeAgentOptions(
allowed_tools=["Read", "Grep", "Glob", "Agent"],
agents={
# Call the factory with your desired configuration
"security-reviewer": create_security_agent("strict")
},
),
):
if hasattr(message, "result"):
print(message.result)
asyncio.run(main())
```
```typescript TypeScript theme={null}
import { query, type AgentDefinition } from "@anthropic-ai/claude-agent-sdk";
// Factory function that returns an AgentDefinition
// This pattern lets you customize agents based on runtime conditions
function createSecurityAgent(securityLevel: "basic" | "strict"): AgentDefinition {
const isStrict = securityLevel === "strict";
return {
description: "Security code reviewer",
// Customize the prompt based on strictness level
prompt: `You are a ${isStrict ? "strict" : "balanced"} security reviewer...`,
tools: ["Read", "Grep", "Glob"],
// Key insight: use a more capable model for high-stakes reviews
model: isStrict ? "opus" : "sonnet"
};
}
// The agent is created at query time, so each request can use different settings
for await (const message of query({
prompt: "Review this PR for security issues",
options: {
Cut at 300 lines. The page has the rest.
agent-sdk/todo-tracking First recorded · 326 lines, first recorded
# Todo Lists ### Todo Lifecycle ### When Todos Are Used ## Examples ### Monitoring Todo Changes ### Real-time Progress Display ## Migrate to Task tools ## Related Documentation
The first capture of this source. The page was already there, and this is what it said.
# Todo Lists
> Track and display todos using the Claude Agent SDK for organized task management
The Claude Agent SDK includes built-in todo functionality that helps organize complex workflows and keep users informed about task progression.
<Note>
As of TypeScript Agent SDK 0.3.142 and Claude Code v2.1.142, sessions use the structured Task tools `TaskCreate`, `TaskUpdate`, `TaskGet`, and `TaskList` instead of `TodoWrite`. The Python SDK gets this change from the Claude Code CLI it launches, not from the Python package version: the switch applies once that CLI — the copy bundled inside the pip package, or one you point to with `cli_path` — is v2.1.142 or later. See [Migrate to Task tools](#migrate-to-task-tools) for how monitoring code changes. The examples on this page set `CLAUDE_CODE_ENABLE_TASKS=0` to keep showing `TodoWrite` for sessions that have not migrated yet.
</Note>
### Todo Lifecycle
Claude moves each todo through a predictable lifecycle:
1. **Created**: Claude adds the todo as `pending` when it identifies a task
2. **Activated**: Claude sets the todo to `in_progress` when it starts the work
3. **Completed**: Claude marks it completed when the task finishes successfully
4. **Removed**: Claude deletes a todo it no longer needs by setting `status: "deleted"` in a `TaskUpdate` call
### When Todos Are Used
Claude creates todos for most multi-step work, such as:
* **Complex multi-step tasks** requiring 3 or more distinct actions
* **User-provided task lists** when multiple items are mentioned
* **Non-trivial operations** that benefit from progress tracking
* **Explicit requests** when users ask for todo organization
It may skip todos for very short or single-step requests.
## Examples
Before running these examples, install the Claude Agent SDK by following the [quickstart](/docs/en/agent-sdk/quickstart).
Each example runs until the agent finishes and yields its final result message. If a session reaches its turn limit first, that result message has the `error_max_turns` subtype. Check `subtype` to detect that ending.
These examples use single-shot `query()` calls. After yielding an `error_max_turns` result, `query()` raises an error that includes `Reached maximum number of turns`. Each example wraps its loop in a try block to exit cleanly when that happens.
See [Handle the result](/docs/en/agent-sdk/agent-loop#handle-the-result) for the result subtypes.
### Monitoring Todo Changes
<CodeGroup>
```typescript TypeScript theme={null}
import { query } from "@anthropic-ai/claude-agent-sdk";
try {
for await (const message of query({
prompt: "Optimize my React app performance and track progress with todos",
// Re-enable TodoWrite, which this example monitors. Without it, the SDK uses
// Task tools instead and these tool_use blocks never appear.
options: { maxTurns: 15, env: { ...process.env, CLAUDE_CODE_ENABLE_TASKS: "0" } }
})) {
// Todo updates are reflected in the message stream
if (message.type === "assistant") {
for (const block of message.message.content) {
if (block.type === "tool_use" && block.name === "TodoWrite") {
const todos = block.input.todos;
console.log("Todo Status Update:");
todos.forEach((todo, index) => {
const status =
todo.status === "completed" ? "✅" : todo.status === "in_progress" ? "🔧" : "❌";
console.log(`${index + 1}. ${status} ${todo.content}`);
});
}
}
}
}
} catch (error) {
// A single-shot query() throws after yielding an error result,
// such as when the maxTurns limit is hit.
console.log(`Session ended with an error: ${error}`);
}
```
```python Python theme={null}
import asyncio
from claude_agent_sdk import query, ClaudeAgentOptions, AssistantMessage, ToolUseBlock
async def main():
try:
async for message in query(
prompt="Optimize my React app performance and track progress with todos",
# Re-enable TodoWrite, which this example monitors. Without it, the SDK uses
# Task tools instead and these tool_use blocks never appear.
options=ClaudeAgentOptions(max_turns=15, env={"CLAUDE_CODE_ENABLE_TASKS": "0"}),
):
# Todo updates are reflected in the message stream
if isinstance(message, AssistantMessage):
for block in message.content:
if isinstance(block, ToolUseBlock) and block.name == "TodoWrite":
todos = block.input["todos"]
print("Todo Status Update:")
for i, todo in enumerate(todos):
status = (
"✅"
if todo["status"] == "completed"
else "🔧"
if todo["status"] == "in_progress"
else "❌"
)
print(f"{i + 1}. {status} {todo['content']}")
except Exception as error:
# A single-shot query() raises after yielding an error result,
# such as when the max_turns limit is hit.
print(f"Session ended with an error: {error}")
asyncio.run(main())
```
</CodeGroup>
### Real-time Progress Display
<CodeGroup>
```typescript TypeScript theme={null}
import { query } from "@anthropic-ai/claude-agent-sdk";
class TodoTracker {
private todos: any[] = [];
displayProgress() {
if (this.todos.length === 0) return;
const completed = this.todos.filter((t) => t.status === "completed").length;
const inProgress = this.todos.filter((t) => t.status === "in_progress").length;
const total = this.todos.length;
console.log(`\nProgress: ${completed}/${total} completed`);
console.log(`Currently working on: ${inProgress} task(s)\n`);
this.todos.forEach((todo, index) => {
const icon =
todo.status === "completed" ? "✅" : todo.status === "in_progress" ? "🔧" : "❌";
const text = todo.status === "in_progress" ? todo.activeForm : todo.content;
console.log(`${index + 1}. ${icon} ${text}`);
});
}
async trackQuery(prompt: string) {
try {
for await (const message of query({
prompt,
// Re-enable TodoWrite, which this tracker watches for.
options: { maxTurns: 20, env: { ...process.env, CLAUDE_CODE_ENABLE_TASKS: "0" } }
})) {
if (message.type === "assistant") {
for (const block of message.message.content) {
if (block.type === "tool_use" && block.name === "TodoWrite") {
this.todos = block.input.todos;
this.displayProgress();
}
}
}
}
} catch (error) {
// A single-shot query() throws after yielding an error result,
// such as when the maxTurns limit is hit.
console.log(`Session ended with an error: ${error}`);
}
}
}
// Usage
const tracker = new TodoTracker();
await tracker.trackQuery("Build a complete authentication system with todos");
```
```python Python theme={null}
import asyncio
from claude_agent_sdk import query, ClaudeAgentOptions, AssistantMessage, ToolUseBlock
from typing import List, Dict
class TodoTracker:
def __init__(self):
self.todos: List[Dict] = []
def display_progress(self):
if not self.todos:
return
completed = len([t for t in self.todos if t["status"] == "completed"])
in_progress = len([t for t in self.todos if t["status"] == "in_progress"])
total = len(self.todos)
print(f"\nProgress: {completed}/{total} completed")
print(f"Currently working on: {in_progress} task(s)\n")
for i, todo in enumerate(self.todos):
icon = (
"✅"
if todo["status"] == "completed"
else "🔧"
if todo["status"] == "in_progress"
else "❌"
)
text = (
todo["activeForm"]
if todo["status"] == "in_progress"
else todo["content"]
)
print(f"{i + 1}. {icon} {text}")
async def track_query(self, prompt: str):
try:
async for message in query(
prompt=prompt,
# Re-enable TodoWrite, which this tracker watches for.
options=ClaudeAgentOptions(max_turns=20, env={"CLAUDE_CODE_ENABLE_TASKS": "0"}),
):
if isinstance(message, AssistantMessage):
for block in message.content:
if isinstance(block, ToolUseBlock) and block.name == "TodoWrite":
self.todos = block.input["todos"]
self.display_progress()
except Exception as error:
# A single-shot query() raises after yielding an error result,
# such as when the max_turns limit is hit.
print(f"Session ended with an error: {error}")
# Usage
async def main():
tracker = TodoTracker()
await tracker.track_query("Build a complete authentication system with todos")
asyncio.run(main())
```
</CodeGroup>
## Migrate to Task tools
The Task tools split the single `TodoWrite` call into `TaskCreate` for each new item and `TaskUpdate` for each status change, with `TaskList` and `TaskGet` available for the model to read back the current list. Your monitoring code still inspects `tool_use` blocks in the assistant stream, but maintains a map keyed by task ID instead of replacing the whole list on every call. The Task tools are the default as of TypeScript Agent SDK 0.3.142 and Claude Code v2.1.142, so no `options.env` change is needed.
| With `TodoWrite` | With Task tools |
| --------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| One tool call rewrites the full `todos` array | `TaskCreate` adds one item, `TaskUpdate` patches one item by `taskId` |
| Match `block.name === "TodoWrite"` | Match `block.name === "TaskCreate"` or `"TaskUpdate"` |
| Item shape: `{ content, status, activeForm }` | `TaskCreate` input: `{ subject, description, activeForm?, metadata? }`. `TaskUpdate` input: `{ taskId, status?, subject?, description?, activeForm?, addBlocks?, addBlockedBy?, owner?, metadata? }`. `status` is `"pending"`, `"in_progress"`, or `"completed"`; set `status: "deleted"` to delete |
| Render `block.input.todos` directly | Accumulate items across calls, or read a snapshot from a `TaskList` tool result |
The assigned task ID is not in the `TaskCreate` input. It comes back in the matching `tool_result` as `{ task: { id, subject } }`, so capture it from the result block to key your map. The following example shows the minimal change to the [Monitoring Todo Changes](#monitoring-todo-changes) loop. It reads only `tool_use` inputs and skips capturing IDs from `tool_result` blocks. To render a complete list, watch for a `TaskList` tool result in the stream or accumulate `TaskCreate` results and `TaskUpdate` inputs into a map.
The streamed `tool_use` input is the raw shape the model emitted. Claude Code repairs some close-but-incorrect key names before execution, mapping `id` or `task_id` to `taskId` and `active_form` to `activeForm`, but that repair is not reflected in the stream. Read `TaskUpdate` input fields defensively, as the samples below do, rather than assuming the canonical name is always present.
<CodeGroup>
```typescript TypeScript theme={null}
import { query } from "@anthropic-ai/claude-agent-sdk";
try {
for await (const message of query({
prompt: "Optimize my React app performance and track progress with todos",
options: { maxTurns: 15 },
})) {
if (message.type !== "assistant") continue;
for (const block of message.message.content) {
if (block.type !== "tool_use") continue;
if (block.name === "TaskCreate") {
const input = block.input as { subject: string };
console.log(`+ ${input.subject}`);
} else if (block.name === "TaskUpdate") {
const input = block.input as {
taskId?: string;
id?: string;
task_id?: string;
status?: string;
};
const taskId = input.taskId ?? input.id ?? input.task_id;
if (taskId && input.status) console.log(` ${taskId} -> ${input.status}`);
}
}
}
} catch (error) {
// A single-shot query() throws after yielding an error result.
console.log(`Session ended with an error: ${error}`);
}
```
```python Python theme={null}
import asyncio
from claude_agent_sdk import query, ClaudeAgentOptions, AssistantMessage, ToolUseBlock
async def main():
try:
async for message in query(
prompt="Optimize my React app performance and track progress with todos",
options=ClaudeAgentOptions(max_turns=15),
):
if not isinstance(message, AssistantMessage):
continue
for block in message.content:
if not isinstance(block, ToolUseBlock):
Cut at 300 lines. The page has the rest.
agent-sdk/tool-search First recorded · 166 lines, first recorded
# Scale to many tools with tool search ## How tool search works ## Configure tool search ## Optimize tool discovery ## Limits ## Related documentation
The first capture of this source. The page was already there, and this is what it said.
# Scale to many tools with tool search
> Scale your agent to thousands of tools by discovering and loading only what's needed, on demand.
Tool search enables your agent to work with hundreds or thousands of tools by dynamically discovering and loading them on demand. Instead of loading all tool definitions into the context window upfront, the agent searches your tool catalog and loads only the tools it needs.
This approach solves two challenges as tool libraries scale:
* **Context efficiency:** Tool definitions can consume large portions of the context window (50 tools can use 10-20K tokens), leaving less room for actual work.
* **Tool selection accuracy:** Tool selection accuracy degrades with more than 30-50 tools loaded at once.
## How tool search works
Tool search is on by default, with the exceptions listed in [Configure tool search](#configure-tool-search).
When it is active, tool definitions are withheld from the context window. The agent receives a summary of available tools and searches for relevant ones when the task requires a capability not already loaded. Up to five of the most relevant tools are loaded into context by default, where they stay available for subsequent turns. If the conversation is long enough that the SDK compacts earlier messages to free space, previously discovered tools may be removed, and the agent searches again as needed.
Tool search adds one extra round-trip the first time Claude discovers a tool (the search step), but for large tool sets this is offset by smaller context on every turn. With fewer than \~10 tools whose definitions fit comfortably in the context window, loading everything upfront is typically faster.
For details on the underlying API mechanism, see [Tool search in the API](https://platform.claude.com/docs/en/agents-and-tools/tool-use/tool-search-tool).
<Note>
Tool search isn't supported on Microsoft Foundry [deployments hosted on Azure](https://platform.claude.com/docs/en/build-with-claude/claude-in-microsoft-foundry#hosting-options), which reject it server-side: the SDK detects the rejection and loads tool definitions upfront for that deployment instead. [`ENABLE_TOOL_SEARCH`](#configure-tool-search) can't override this, since the rejection comes from the deployment itself.
</Note>
## Configure tool search
Tool search is on by default. For models on the SDK's unsupported-model list, the SDK loads tool definitions upfront instead, and no `ENABLE_TOOL_SEARCH` value overrides that. On Google Cloud's Agent Platform, the SDK decides by model generation:
* **Claude Opus 4.5, Sonnet 4.5, Haiku 4.5, and later**: tool search is on by default.
* **Earlier Agent Platform models**: the SDK loads tool definitions upfront, because their serving stacks reject the required beta header. `ENABLE_TOOL_SEARCH` can't override this.
Before Claude Code v2.1.221, the SDK disabled tool search for all models on Google Cloud's Agent Platform unless you set `ENABLE_TOOL_SEARCH`.
The SDK also disables tool search when `ANTHROPIC_BASE_URL` points to a non-first-party host, since most proxies don't forward `tool_reference` blocks. You can override that default with the `ENABLE_TOOL_SEARCH` environment variable:
| Value | Behavior |
| :------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| (unset) | Tool search is on. Tool definitions are deferred and discovered on demand. Falls back to loading upfront on Google Cloud's Agent Platform models earlier than the Claude 4.5 generation, a non-first-party `ANTHROPIC_BASE_URL`, or a Microsoft Foundry deployment hosted on Azure. |
| `true` | Tool search is always on, except on a Microsoft Foundry deployment hosted on Azure, where the server-side rejection still forces upfront loading, and on Google Cloud's Agent Platform models earlier than the Claude 4.5 generation, where the SDK keeps loading tool definitions upfront. The SDK sends the beta header through proxies, and requests fail on proxies that don't support `tool_reference` blocks. |
| `auto` | Counts the tokens in the tool definitions that tool search can defer and compares the total against the model's context window. When the total reaches 10% of the window, tool search activates. Below that, the SDK loads every tool definition into context upfront. |
| `auto:N` | Same as `auto` with a custom percentage. `auto:5` activates when those definitions reach 5% of the context window. Lower values activate sooner. |
| `false` | Tool search is off. All tool definitions are loaded into context on every turn. |
Setting [`CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS`](/docs/en/env-vars) keeps tool search off. You can't override it by setting `ENABLE_TOOL_SEARCH` yourself. Your organization can keep tool search on through [managed settings](/docs/en/settings#settings-files), on Claude Code v2.1.227 or later. [Disable pre-release capabilities](/docs/en/llm-gateway-protocol#disable-pre-release-capabilities) covers where the override applies and what the variable strips.
Tool search applies to all registered tools, whether they come from remote MCP servers or [custom SDK MCP servers](/docs/en/agent-sdk/custom-tools). When you use `auto`, the SDK counts every definition that tool search can defer toward one combined threshold: each MCP tool that isn't marked [`alwaysLoad`](/docs/en/mcp#exempt-a-server-from-deferral), from any server, plus the built-in tools that load on demand. The SDK always loads core built-in tools such as Bash, Read, and Edit upfront and doesn't count them toward the threshold.
Set the value in the `env` option on `query()`. In TypeScript, `env` replaces the subprocess environment, so spread `...process.env` to keep inherited variables. In Python, `env` is merged on top of the inherited environment. This example connects to a remote MCP server that exposes many tools, pre-approves all of them with a wildcard, and uses `auto:5` so tool search activates when the definitions it can defer reach 5% of the context window:
<CodeGroup>
```typescript TypeScript theme={null}
import { query } from "@anthropic-ai/claude-agent-sdk";
try {
for await (const message of query({
prompt: "Find and run the appropriate database query",
options: {
mcpServers: {
"enterprise-tools": {
// Connect to a remote MCP server
type: "http",
url: "https://tools.example.com/mcp"
}
},
allowedTools: ["mcp__enterprise-tools__*"], // Wildcard pre-approves all tools from this server
env: {
...process.env, // env replaces the subprocess environment, so keep inherited variables
ENABLE_TOOL_SEARCH: "auto:5" // Activate tool search when deferrable definitions reach 5% of context
}
}
})) {
if (message.type === "result" && message.subtype === "success") {
console.log(message.result);
}
}
} catch (error) {
// A single-shot query() throws after yielding an error result
console.log(`Session ended with an error: ${error}`);
}
```
```python Python theme={null}
import asyncio
from claude_agent_sdk import query, ClaudeAgentOptions, ResultMessage
async def main():
options = ClaudeAgentOptions(
mcp_servers={
"enterprise-tools": {
"type": "http",
"url": "https://tools.example.com/mcp",
}
},
allowed_tools=[
"mcp__enterprise-tools__*"
], # Wildcard pre-approves all tools from this server
env={
"ENABLE_TOOL_SEARCH": "auto:5" # Activate tool search when deferrable definitions reach 5% of context
},
)
try:
async for message in query(
prompt="Find and run the appropriate database query",
options=options,
):
if isinstance(message, ResultMessage) and message.subtype == "success":
print(message.result)
except Exception as error:
# A single-shot query() raises after yielding an error result
print(f"Session ended with an error: {error}")
asyncio.run(main())
```
</CodeGroup>
To run this example, replace `https://tools.example.com/mcp` with the URL of your own MCP server. On success the result text prints to the console.
Because this is a single-shot `query()` call, the SDK raises after yielding an error result, so the example wraps the loop in a try block. To see why a run failed, check the result message's `subtype`, such as `error_during_execution`, inside the loop. For more on result messages, see [Handle the result](/docs/en/agent-sdk/agent-loop#handle-the-result).
## Optimize tool discovery
The search mechanism matches queries against tool names and descriptions. Names like `search_slack_messages` surface for a wider range of requests than `query_slack`. Descriptions with specific keywords ("Search Slack messages by keyword, channel, or date range") match more queries than generic ones ("Query Slack").
You can also add a system prompt section listing available tool categories. This gives the agent context about what kinds of tools are available to search for. Pass the text through the `systemPrompt` option in TypeScript or `system_prompt` in Python, using the `claude_code` preset with `append`, which adds your text to the preset's prompt instead of replacing it:
<CodeGroup>
```typescript TypeScript theme={null}
options: {
systemPrompt: {
type: "preset",
preset: "claude_code",
append: "You can search for tools to interact with Slack, GitHub, and Jira."
}
}
```
```python Python theme={null}
options = ClaudeAgentOptions(
system_prompt={
"type": "preset",
"preset": "claude_code",
"append": "You can search for tools to interact with Slack, GitHub, and Jira.",
}
)
```
</CodeGroup>
For the full set of system prompt options, see [Modifying system prompts](/docs/en/agent-sdk/modifying-system-prompts).
## Limits
* **Maximum tools:** 10,000 tools in your catalog
* **Search results:** returns up to five most relevant tools per search by default
* **Model support:** Claude Sonnet 4.5, Claude Haiku 4.5, Claude Opus 4.5, and later models; see [model compatibility in the API docs](https://platform.claude.com/docs/en/agents-and-tools/tool-use/tool-search-tool#model-compatibility) for the current list. The same minimums apply on Google Cloud's Agent Platform.
## Related documentation
* [Tool search in the API](https://platform.claude.com/docs/en/agents-and-tools/tool-use/tool-search-tool): Full API documentation for tool search, including custom implementations
* [Connect MCP servers](/docs/en/agent-sdk/mcp): Connect to external tools via MCP servers
* [Custom tools](/docs/en/agent-sdk/custom-tools): Build your own tools with SDK MCP servers
* [TypeScript SDK reference](/docs/en/agent-sdk/typescript): Full API reference
* [Python SDK reference](/docs/en/agent-sdk/python): Full API reference
agent-sdk/troubleshooting First recorded · 60 lines, first recorded
# Troubleshooting ## CLI startup ### CLINotFoundError: Claude Code not found ### CLIConnectionError: Refusing to execute batch script ## Structured outputs ### structured\_output is None but the result says success ## Report a new issue
The first capture of this source. The page was already there, and this is what it said.
# Troubleshooting > Fix Agent SDK errors by the exact message you see, with the cause and fix for each error in the TypeScript and Python SDKs. Entries on this page are keyed to the error you see. Each names the cause and what to do. ## CLI startup ### CLINotFoundError: Claude Code not found The Python SDK launches the Claude Code CLI as a subprocess. When it can't find a `claude` executable, connecting fails with a `CLINotFoundError`: ``` Claude Code not found at: /your/configured/path ``` The message includes the configured path when you set `ClaudeAgentOptions(cli_path=...)` and it points at a missing file. Without `cli_path`, the SDK searches your `PATH` and common install locations, and the message includes install instructions for your platform. To fix it: * Install Claude Code if it isn't installed. See [Install Claude Code](/docs/en/setup#install-claude-code) for the command on your platform. * If you set `cli_path`, confirm the file exists and is the `claude` executable. * If you rely on `PATH` resolution, confirm `claude --version` works in the same environment your application runs in. Processes you launch outside your shell, such as from an IDE or a service manager, often run with a different `PATH`. ### CLIConnectionError: Refusing to execute batch script On Windows, connecting fails with a `CLIConnectionError` when the CLI path the Python SDK uses is a `.bat` or `.cmd` batch script, including the `claude.cmd` shim that an npm install creates: ``` Refusing to execute batch script 'C:\\Users\\you\\AppData\\Roaming\\npm\\claude.cmd': Windows runs .bat/.cmd files via cmd.exe, which can execute commands injected through CLI arguments, and no reliable escaping for cmd.exe exists. Use a native claude executable instead: install Claude Code natively (irm https://claude.ai/install.ps1 | iex), point ClaudeAgentOptions(cli_path=...) at a claude.exe, or install the claude-agent-sdk wheel for a platform that bundles claude.exe (e.g. Windows x64). ``` The refusal is deliberate security hardening, not a broken install. Windows runs batch scripts by rewriting the spawn into a `cmd.exe /c` invocation, and `cmd.exe` re-parses the whole command line at execution time, so an argument value can execute injected commands. Most Windows installs never reach this error. The Windows x64 wheel of `claude-agent-sdk` bundles a `claude.exe`, and the SDK prefers the bundled CLI, then any native `claude.exe` it can discover, before falling back to a batch shim. You see the refusal in two cases: * You set `ClaudeAgentOptions(cli_path=...)` to a `.bat` or `.cmd` file, such as npm's `claude.cmd` shim. * Your install has no bundled or native `claude.exe`, for example a source install on ARM64 Windows where the only `claude` on your `PATH` is the npm shim. To fix it, give the SDK a native executable instead of a batch script: * If you set `ClaudeAgentOptions(cli_path=...)`, point it at a `claude.exe` or remove the option. The SDK skips discovery while `cli_path` is set, so a native install alone can't take effect. * Install Claude Code natively in PowerShell: `irm https://claude.ai/install.ps1 | iex` * On x64 Windows, install the `claude-agent-sdk` wheel, which bundles `claude.exe`. Before `claude-agent-sdk` 0.2.124, the Python SDK spawned batch scripts through `cmd.exe` without this check. ## Structured outputs ### structured\_output is None but the result says success A result message can end with `subtype: "success"` while `structured_output` is `None` in Python or `undefined` in TypeScript. The run completes, but no validated output exists. One way to hit this is a schema no output can satisfy, for example conflicting length constraints. The run ends without a validation error, and the only signal is the missing `structured_output`. Treat this result as a failure in application code. Check both that `subtype` is `success` and that `structured_output` is present before using it. The [Error handling](/docs/en/agent-sdk/structured-outputs#error-handling) section shows this pattern for both SDKs. If it happens repeatedly with a schema you believe is correct, verify the schema is satisfiable, then simplify it until outputs validate, and reintroduce constraints one at a time. ## Report a new issue If your error isn't covered here, check the open issues or file a new one in the SDK repositories: [claude-agent-sdk-typescript](https://github.com/anthropics/claude-agent-sdk-typescript/issues) or [claude-agent-sdk-python](https://github.com/anthropics/claude-agent-sdk-python/issues). Include the full error text and your SDK version.
agent-sdk/typescript First recorded · 4839 lines, first recorded
# Agent SDK reference - TypeScript ## Installation ### Compile to a single executable ## Functions ### `query()` #### Parameters #### Returns ### `startup()` #### Parameters #### Returns #### Example ### `tool()` #### Parameters #### `ToolAnnotations` ### `createSdkMcpServer()` #### Parameters ### `listSessions()` #### Parameters #### Return type: `SDKSessionInfo` #### Example ### `getSessionMessages()` #### Parameters #### Return type: `SessionMessage` #### Example ### `getSessionInfo()` #### Parameters ### `renameSession()` #### Parameters ### `tagSession()` #### Parameters ### `resolveSettings()` #### Parameters #### Return type: `ResolvedSettings` #### Example ## Types ### `Options` #### Handle slow or stalled API responses ### `Query` object #### Methods #### `applyFlagSettings()` ### `WarmQuery` #### Methods ### `SDKControlInitializeResponse` ### `SDKControlInterruptResponse` ### `SDKControlGetContextUsageResponse` ### `SDKControlReadFileResponse` ### `AgentDefinition` ### `AgentMcpServerSpec` ### `SettingSource` #### Default behavior #### Why use settingSources #### Settings precedence ### `PermissionMode` ### `CanUseTool` ### `PermissionResult` ### `ToolConfig` ### `McpServerConfig` #### `McpStdioServerConfig` #### `McpSSEServerConfig` #### `McpHttpServerConfig` #### `McpSdkServerConfigWithInstance` #### `McpClaudeAIProxyServerConfig` ### `SdkPluginConfig` ## Message Types ### `SDKMessage` ### `SDKAssistantMessage` ### `SDKUserMessage` ### `SDKUserMessageReplay` ### `SDKResultMessage` ### `SDKSystemMessage` ### `SDKPartialAssistantMessage` ### `SDKCompactBoundaryMessage` ### `SDKInformationalMessage` ### `SDKWorkerShuttingDownMessage` ### `SDKPluginInstallMessage` ### `SDKPermissionDeniedMessage` ### `SDKPermissionDenial` ### `SDKMessageOrigin` ### Task-notification subkinds ### Peer origin fields ## Hook Types ### `HookEvent` ### `HookCallback` ### `HookCallbackMatcher` ### `HookInput` ### `BaseHookInput` #### `PreToolUseHookInput` #### `PostToolUseHookInput` #### `PostToolUseFailureHookInput` #### `PostToolBatchHookInput` #### `PermissionDeniedHookInput` #### `NotificationHookInput` #### `UserPromptSubmitHookInput` #### `UserPromptExpansionHookInput` #### `SessionStartHookInput` #### `SessionEndHookInput` #### `StopHookInput` #### `StopFailureHookInput` #### `SubagentStartHookInput` #### `SubagentStopHookInput` #### `PreCompactHookInput` #### `PostCompactHookInput` #### `PermissionRequestHookInput` #### `SetupHookInput` #### `TeammateIdleHookInput` #### `TaskCreatedHookInput` #### `TaskCompletedHookInput` #### `ElicitationHookInput` #### `ElicitationResultHookInput` #### `ConfigChangeHookInput` #### `InstructionsLoadedHookInput` #### `DirectoryAddedHookInput` #### `WorktreeCreateHookInput` #### `WorktreeRemoveHookInput` #### `CwdChangedHookInput` #### `FileChangedHookInput` #### `MessageDisplayHookInput` ### `HookJSONOutput` #### `AsyncHookJSONOutput` #### `SyncHookJSONOutput` ## Tool Input Types ### `ToolInputSchemas` ### Agent ### AskUserQuestion ### Bash ### Monitor ### TaskOutput ### Edit ### Read ### Write ### Glob ### Grep ### TaskStop ### NotebookEdit ### WebFetch ### WebSearch ### Workflow ### TodoWrite ### TaskCreate ### TaskUpdate ### TaskGet ### TaskList ### ExitPlanMode ### ListMcpResources ### ReadMcpResource ### EnterWorktree ### ExitWorktree ### EnterPlanMode ### CronCreate ### CronDelete ### CronList ### ScheduleWakeup ### RemoteTrigger ### PushNotification ### REPL ### ReportFindings ### Artifact ### Projects ### ReadMcpResourceDir ### RefreshMcpTools ### ShowOnboardingRolePicker ### McpInput ## Tool Output Types ### `ToolOutputSchemas` ### Agent ### AskUserQuestion ### Bash ### Monitor ### Edit ### Read ### Write ### Glob ### Grep ### TaskStop ### NotebookEdit ### WebFetch ### WebSearch ### Workflow ### TodoWrite ### TaskCreate ### TaskUpdate ### TaskGet ### TaskList ### ExitPlanMode ### ListMcpResources ### ReadMcpResource ### EnterWorktree ### ExitWorktree ### EnterPlanMode ### CronCreate ### CronDelete ### CronList ### ScheduleWakeup ### RemoteTrigger ### PushNotification ### REPL ### ReportFindings ### Artifact ### Projects ### ReadMcpResourceDir ### RefreshMcpTools ### ShowOnboardingRolePicker ### McpOutput ## Permission Types ### `PermissionUpdate` ### `PermissionBehavior` ### `PermissionUpdateDestination` ### `PermissionRuleValue` ## Other Types ### `ApiKeySource` ### `SdkBeta` ### `SlashCommand` ### `ModelInfo` ### `AgentInfo` ### `McpServerStatus` ### `McpServerStatusConfig` ### `AccountInfo` ### `ModelUsage` ### `ConfigScope` ### `NonNullableUsage` ### `Usage` ### `CallToolResult` ### `ThinkingConfig` ### `SpawnedProcess` ### `SpawnOptions` ### `McpSetServersResult` ### `RewindFilesResult` ### `SDKStatusMessage` ### `SDKTaskNotificationMessage` ### `SDKToolUseSummaryMessage` ### `SDKHookStartedMessage` ### `SDKHookProgressMessage` ### `SDKHookResponseMessage` ### `SDKToolProgressMessage` ### `SDKAuthStatusMessage` ### `SDKTaskStartedMessage` ### `SDKTaskProgressMessage` ### `SDKTaskUpdatedMessage` ### `SDKBackgroundTasksChangedMessage` ### `SDKThinkingTokensMessage` ### `SDKFilesPersistedEvent` ### `SDKRateLimitEvent` ### `SDKLocalCommandOutputMessage` ### `SDKCommandsChangedMessage` ### `SDKPromptSuggestionMessage` ### `SDKConversationResetMessage` ### `AbortError` ## Sandbox Configuration ### `SandboxSettings` #### Example usage ### `SandboxNetworkConfig` ### `SandboxFilesystemConfig` ### Permissions Fallback for Unsandboxed Commands ## See also
This page is larger than the 256 KiB this site keeps, so one side of the diff below stops where the stored text does.
The first capture of this source. The page was already there, and this is what it said.
# Agent SDK reference - TypeScript
> Complete API reference for the TypeScript Agent SDK, including all functions, types, and interfaces.
<script src="/docs/components/typescript-sdk-type-links.js" defer />
## Installation
```bash theme={null}
npm install @anthropic-ai/claude-agent-sdk
```
<Note>
The SDK bundles a native Claude Code binary for your platform as an optional dependency such as `@anthropic-ai/claude-agent-sdk-darwin-arm64`. Most installs need no separate Claude Code install. The SDK version tracks the bundled Claude Code version: SDK v0.3.191 bundles Claude Code v2.1.191, so a feature on this page that requires a Claude Code version needs the SDK release with the same patch number or later. If your package manager skips optional dependencies, the SDK throws `Native CLI binary for <platform> not found`; set [`pathToClaudeCodeExecutable`](#options) to a separately installed `claude` binary instead.
If your package manager doesn't apply npm's `libc` field, as Yarn 1.x doesn't, you get both the glibc and musl platform packages on Linux, roughly doubling the install size. The SDK still launches the correct variant. To reclaim the space in a container image, delete the platform package that doesn't match the libc where your app runs; for a glibc runtime on x64, that's `rm -rf node_modules/@anthropic-ai/claude-agent-sdk-linux-x64-musl`. On a development machine the deletion is temporary, since Yarn reinstalls the package on the next dependency change.
</Note>
### Compile to a single executable
When you compile your application into a single-file executable with `bun build --compile`, the SDK cannot resolve the bundled CLI binary at runtime. `require.resolve` does not work inside the compiled executable's `$bunfs` virtual filesystem, so the SDK throws `Native CLI binary for <platform> not found`.
To work around this, embed the platform binary as a file asset, extract it to a real path at startup with `extractFromBunfs()`, and pass that path to [`pathToClaudeCodeExecutable`](#options).
The `extractFromBunfs()` helper requires `@anthropic-ai/claude-agent-sdk` v0.3.144 or later. The example below builds for macOS on Apple Silicon:
```typescript theme={null}
import binPath from "@anthropic-ai/claude-agent-sdk-darwin-arm64/claude" with { type: "file" };
import { extractFromBunfs } from "@anthropic-ai/claude-agent-sdk/extract";
import { query } from "@anthropic-ai/claude-agent-sdk";
const cliPath = extractFromBunfs(binPath);
for await (const message of query({
prompt: "Hello",
options: { pathToClaudeCodeExecutable: cliPath },
})) {
console.log(message);
}
```
`extractFromBunfs()` copies the embedded binary out of the compiled executable's virtual filesystem to a per-user temp directory and returns the real path. Outside a compiled executable it returns the input path unchanged, so the same code runs in development without modification.
Each compiled executable embeds a single platform's binary. Match the platform package in the import to your `--target`:
* To cross-compile, install the non-matching platform package, for example `npm install @anthropic-ai/claude-agent-sdk-linux-x64 --force`.
* On Windows, the binary subpath is `claude.exe`, for example `@anthropic-ai/claude-agent-sdk-win32-x64/claude.exe`.
## Functions
### `query()`
The primary function for interacting with Claude Code. Creates an async generator that streams messages as they arrive.
```typescript theme={null}
function query({
prompt,
options
}: {
prompt: string | AsyncIterable<SDKUserMessage>;
options?: Options;
}): Query;
```
#### Parameters
| Parameter | Type | Description |
| :-------- | :--------------------------------------------------------------- | :---------------------------------------------------------------- |
| `prompt` | `string \| AsyncIterable<`[`SDKUserMessage`](#sdkusermessage)`>` | The input prompt as a string or async iterable for streaming mode |
| `options` | [`Options`](#options) | Optional configuration object (see Options type below) |
#### Returns
Returns a [`Query`](#query-object) object that extends `AsyncGenerator<`[`SDKMessage`](#sdkmessage)`, void>` with additional methods.
### `startup()`
Pre-warms the CLI subprocess by spawning it and completing the initialize handshake before a prompt is available. The returned [`WarmQuery`](#warmquery) handle accepts a prompt later and writes it to an already-ready process, so the first `query()` call resolves without paying subprocess spawn and initialization cost inline.
```typescript theme={null}
function startup(params?: {
options?: Options;
initializeTimeoutMs?: number;
}): Promise<WarmQuery>;
```
#### Parameters
| Parameter | Type | Description |
| :-------------------- | :-------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `options` | [`Options`](#options) | Optional configuration object. Same as the `options` parameter to `query()` |
| `initializeTimeoutMs` | `number` | Maximum time in milliseconds to wait for subprocess initialization. Defaults to `60000`. If initialization does not complete in time, the promise rejects with a timeout error |
#### Returns
Returns a `Promise<`[`WarmQuery`](#warmquery)`>` that resolves once the subprocess has spawned and completed its initialize handshake.
#### Example
Call `startup()` early, for example on application boot, then call `.query()` on the returned handle once a prompt is ready. This moves subprocess spawn and initialization out of the critical path.
```typescript theme={null}
import { startup } from "@anthropic-ai/claude-agent-sdk";
// Pay startup cost upfront
const warm = await startup({ options: { maxTurns: 3 } });
// Later, when a prompt is ready, this is immediate
for await (const message of warm.query("What files are here?")) {
console.log(message);
}
```
### `tool()`
Creates a type-safe MCP tool definition for use with SDK MCP servers.
```typescript theme={null}
function tool<Schema extends AnyZodRawShape>(
name: string,
description: string,
inputSchema: Schema,
handler: (args: InferShape<Schema>, extra: unknown) => Promise<CallToolResult>,
extras?: { annotations?: ToolAnnotations; searchHint?: string; alwaysLoad?: boolean }
): SdkMcpToolDefinition<Schema>;
```
#### Parameters
| Parameter | Type | Description |
| :------------ | :----------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `name` | `string` | The name of the tool |
| `description` | `string` | A description of what the tool does |
| `inputSchema` | `Schema extends AnyZodRawShape` | Zod schema defining the tool's input parameters (supports both Zod 3 and Zod 4) |
| `handler` | `(args, extra) => Promise<`[`CallToolResult`](#calltoolresult)`>` | Async function that executes the tool logic |
| `extras` | `{ annotations?: `[`ToolAnnotations`](#toolannotations)`; searchHint?: string; alwaysLoad?: boolean }` | Optional extras. `annotations` provides MCP behavioral hints to clients. `searchHint` is a one-line capability phrase shown in the deferred-tool list when [tool search](/docs/en/agent-sdk/tool-search) is active. `alwaysLoad: true` keeps this tool's full schema in the initial prompt instead of deferring it |
#### `ToolAnnotations`
Re-exported from `@modelcontextprotocol/sdk/types.js`. All fields are optional hints; clients should not rely on them for security decisions.
| Field | Type | Default | Description |
| :---------------- | :-------- | :---------- | :--------------------------------------------------------------------------------------------------------------------------------------------------- |
| `title` | `string` | `undefined` | Human-readable title for the tool |
| `readOnlyHint` | `boolean` | `false` | If `true`, the tool does not modify its environment |
| `destructiveHint` | `boolean` | `true` | If `true`, the tool may perform destructive updates (only meaningful when `readOnlyHint` is `false`) |
| `idempotentHint` | `boolean` | `false` | If `true`, repeated calls with the same arguments have no additional effect (only meaningful when `readOnlyHint` is `false`) |
| `openWorldHint` | `boolean` | `true` | If `true`, the tool interacts with external entities (for example, web search). If `false`, the tool's domain is closed (for example, a memory tool) |
```typescript theme={null}
import { tool } from "@anthropic-ai/claude-agent-sdk";
import { z } from "zod";
const searchTool = tool(
"search",
"Search the web",
{ query: z.string() },
async ({ query }) => {
return { content: [{ type: "text", text: `Results for: ${query}` }] };
},
{ annotations: { readOnlyHint: true, openWorldHint: true } }
);
```
### `createSdkMcpServer()`
Creates an MCP server instance that runs in the same process as your application.
```typescript theme={null}
function createSdkMcpServer(options: {
name: string;
version?: string;
instructions?: string;
tools?: Array<SdkMcpToolDefinition<any>>;
alwaysLoad?: boolean;
}): McpSdkServerConfigWithInstance;
```
#### Parameters
| Parameter | Type | Description |
| :--------------------- | :---------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `options.name` | `string` | The name of the MCP server |
| `options.version` | `string` | Optional version string |
| `options.instructions` | `string` | Optional server instructions, returned from `initialize` and surfaced to the model as an MCP instructions block |
| `options.tools` | `Array<SdkMcpToolDefinition>` | Array of tool definitions created with [`tool()`](#tool) |
| `options.alwaysLoad` | `boolean` | When `true`, every tool from this server stays in the initial prompt and is never deferred behind [tool search](/docs/en/agent-sdk/tool-search). Combines with per-tool `alwaysLoad` in [`tool()`](#tool) |
### `listSessions()`
Discovers and lists past sessions with light metadata. Filter by project directory or list sessions across all projects.
```typescript theme={null}
function listSessions(options?: ListSessionsOptions): Promise<SDKSessionInfo[]>;
```
#### Parameters
| Parameter | Type | Default | Description |
| :------------------------- | :-------- | :---------- | :--------------------------------------------------------------------------------- |
| `options.dir` | `string` | `undefined` | Directory to list sessions for. When omitted, returns sessions across all projects |
| `options.limit` | `number` | `undefined` | Maximum number of sessions to return |
| `options.includeWorktrees` | `boolean` | `true` | When `dir` is inside a git repository, include sessions from all worktree paths |
#### Return type: `SDKSessionInfo`
| Property | Type | Description |
| :------------- | :-------------------- | :-------------------------------------------------------------------------- |
| `sessionId` | `string` | Unique session identifier (UUID) |
| `summary` | `string` | Display title: custom title, auto-generated summary, or first prompt |
| `lastModified` | `number` | Last modified time in milliseconds since epoch |
| `fileSize` | `number \| undefined` | Session file size in bytes. Only populated for local JSONL storage |
| `customTitle` | `string \| undefined` | User-set session title (via `/rename`) |
| `firstPrompt` | `string \| undefined` | First meaningful user prompt in the session |
| `gitBranch` | `string \| undefined` | Git branch at the end of the session |
| `cwd` | `string \| undefined` | Working directory for the session |
| `tag` | `string \| undefined` | User-set session tag (see [`tagSession()`](#tagsession)) |
| `createdAt` | `number \| undefined` | Creation time in milliseconds since epoch, from the first entry's timestamp |
#### Example
Print the 10 most recent sessions for a project. Results are sorted by `lastModified` descending, so the first item is the newest. Omit `dir` to search across all projects.
```typescript theme={null}
import { listSessions } from "@anthropic-ai/claude-agent-sdk";
const sessions = await listSessions({ dir: "/path/to/project", limit: 10 });
for (const session of sessions) {
console.log(`${session.summary} (${session.sessionId})`);
}
```
### `getSessionMessages()`
Reads user and assistant messages from a past session transcript.
```typescript theme={null}
function getSessionMessages(
sessionId: string,
options?: GetSessionMessagesOptions
): Promise<SessionMessage[]>;
```
#### Parameters
| Parameter | Type | Default | Description |
| :--------------- | :------- | :---------- | :---------------------------------------------------------------------------- |
| `sessionId` | `string` | required | Session UUID to read (see `listSessions()`) |
| `options.dir` | `string` | `undefined` | Project directory to find the session in. When omitted, searches all projects |
| `options.limit` | `number` | `undefined` | Maximum number of messages to return |
| `options.offset` | `number` | `undefined` | Number of messages to skip from the start |
#### Return type: `SessionMessage`
| Property | Type | Description |
| :------------------- | :---------------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `type` | `"user" \| "assistant"` | Message role |
| `uuid` | `string` | Unique message identifier |
| `session_id` | `string` | Session this message belongs to |
| `message` | `unknown` | Raw message payload from the transcript |
| `parent_tool_use_id` | `string \| null` | For subagent messages, the `tool_use_id` of the spawning `Agent` tool call. `null` for main-session messages and older sessions |
| `parent_agent_id` | `string \| null` | For messages from a [nested subagent](/docs/en/sub-agents#let-subagents-spawn-their-own-subagents), the `agentId` of the subagent that spawned it. `null` for main-session messages, messages from top-level subagents, and older sessions. Requires Claude Code v2.1.202 or later |
#### Example
```typescript theme={null}
import { listSessions, getSessionMessages } from "@anthropic-ai/claude-agent-sdk";
const [latest] = await listSessions({ dir: "/path/to/project", limit: 1 });
if (latest) {
const messages = await getSessionMessages(latest.sessionId, {
dir: "/path/to/project",
limit: 20
});
for (const msg of messages) {
console.log(`[${msg.type}] ${msg.uuid}`);
}
}
```
### `getSessionInfo()`
Reads metadata for a single session by ID without scanning the full project directory.
```typescript theme={null}
function getSessionInfo(
sessionId: string,
options?: GetSessionInfoOptions
): Promise<SDKSessionInfo | undefined>;
```
#### Parameters
| Parameter | Type | Default | Description |
| :------------ | :------- | :---------- | :--------------------------------------------------------------------- |
| `sessionId` | `string` | required | UUID of the session to look up |
| `options.dir` | `string` | `undefined` | Project directory path. When omitted, searches all project directories |
Cut at 300 lines. The page has the rest.
agent-sdk/typescript-v2-preview First recorded · 392 lines, first recorded
# TypeScript SDK V2 session API (removed) ## Installation ## Quick start ### One-shot prompt ### Basic session ### Multi-turn conversation ### Session resume ### Cleanup ## API reference ### `unstable_v2_createSession()` ### `unstable_v2_resumeSession()` ### `unstable_v2_prompt()` ### SDKSession interface ## Feature availability ## See also
The first capture of this source. The page was already there, and this is what it said.
# TypeScript SDK V2 session API (removed)
> Reference for the removed V2 TypeScript Agent SDK session API, with session-based send/stream patterns for multi-turn conversations.
<Warning>
The V2 session API is no longer supported. TypeScript Agent SDK 0.3.142 removes `unstable_v2_createSession`, `unstable_v2_resumeSession`, `unstable_v2_prompt`, and the `SDKSession` and `SDKSessionOptions` types.
To migrate, use the [`query()` API](/docs/en/agent-sdk/typescript) and the [session options](/docs/en/agent-sdk/sessions) it accepts. Pass an `AsyncIterable<SDKUserMessage>` for multi-turn conversations, or `options.resume` to continue a saved session. This page is kept for reference if you maintain code on Agent SDK 0.2.x or earlier.
</Warning>
V2 was an experimental session API that removed the need for async generators and yield coordination. Instead of managing generator state across turns, each turn was a separate `send()`/`stream()` cycle. The API surface reduced to three concepts:
* `createSession()` / `resumeSession()`: Start or continue a conversation
* `session.send()`: Send a message
* `session.stream()`: Get the response
## Installation
Agent SDK 0.2.x is the last version that includes the V2 interface. The package version jumped from 0.2.x directly to 0.3.142, so the removal version above and the install pin below describe the same boundary. To install the last V2-compatible release, pin the major and minor version:
```bash theme={null}
npm install @anthropic-ai/[email protected]
```
<Note>
The SDK bundles a native Claude Code binary for your platform as an optional dependency, so most installs need no separate Claude Code install. See the [quickstart's install note](/docs/en/agent-sdk/quickstart) for the installs that need one.
</Note>
## Quick start
### One-shot prompt
For simple single-turn queries where you don't need to maintain a session, use `unstable_v2_prompt()`. This example sends a math question and logs the answer:
```typescript theme={null}
import { unstable_v2_prompt } from "@anthropic-ai/claude-agent-sdk";
const result = await unstable_v2_prompt("What is 2 + 2?", {
model: "claude-opus-4-7"
});
if (result.subtype === "success") {
console.log(result.result);
}
```
<details>
<summary>See the same operation in V1</summary>
```typescript theme={null}
import { query } from "@anthropic-ai/claude-agent-sdk";
const q = query({
prompt: "What is 2 + 2?",
options: { model: "claude-opus-4-7" }
});
for await (const msg of q) {
if (msg.type === "result" && msg.subtype === "success") {
console.log(msg.result);
}
}
```
</details>
### Basic session
For interactions beyond a single prompt, create a session. V2 separates sending and streaming into distinct steps:
* `send()` dispatches your message
* `stream()` streams back the response
This explicit separation makes it easier to add logic between turns (like processing responses before sending follow-ups).
The example below creates a session, sends "Hello!" to Claude, and prints the text response. It uses [`await using`](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-5-2.html#using-declarations-and-explicit-resource-management) (TypeScript 5.2+) to automatically close the session when the block exits. You can also call `session.close()` manually.
```typescript theme={null}
import { unstable_v2_createSession } from "@anthropic-ai/claude-agent-sdk";
await using session = unstable_v2_createSession({
model: "claude-opus-4-7"
});
await session.send("Hello!");
for await (const msg of session.stream()) {
// Filter for assistant messages to get human-readable output
if (msg.type === "assistant") {
const text = msg.message.content
.filter((block) => block.type === "text")
.map((block) => block.text)
.join("");
console.log(text);
}
}
```
<details>
<summary>See the same operation in V1</summary>
In V1, both input and output flow through a single async generator. For a basic prompt this looks similar, but adding multi-turn logic requires restructuring to use an input generator.
```typescript theme={null}
import { query } from "@anthropic-ai/claude-agent-sdk";
const q = query({
prompt: "Hello!",
options: { model: "claude-opus-4-7" }
});
for await (const msg of q) {
if (msg.type === "assistant") {
const text = msg.message.content
.filter((block) => block.type === "text")
.map((block) => block.text)
.join("");
console.log(text);
}
}
```
</details>
### Multi-turn conversation
Sessions persist context across multiple exchanges. To continue a conversation, call `send()` again on the same session. Claude remembers the previous turns.
This example asks a math question, then asks a follow-up that references the previous answer:
```typescript theme={null}
import { unstable_v2_createSession } from "@anthropic-ai/claude-agent-sdk";
await using session = unstable_v2_createSession({
model: "claude-opus-4-7"
});
// Turn 1
await session.send("What is 5 + 3?");
for await (const msg of session.stream()) {
// Filter for assistant messages to get human-readable output
if (msg.type === "assistant") {
const text = msg.message.content
.filter((block) => block.type === "text")
.map((block) => block.text)
.join("");
console.log(text);
}
}
// Turn 2
await session.send("Multiply that by 2");
for await (const msg of session.stream()) {
if (msg.type === "assistant") {
const text = msg.message.content
.filter((block) => block.type === "text")
.map((block) => block.text)
.join("");
console.log(text);
}
}
```
<details>
<summary>See the same operation in V1</summary>
```typescript theme={null}
import { query } from "@anthropic-ai/claude-agent-sdk";
// Must create an async iterable to feed messages
async function* createInputStream() {
yield {
type: "user",
session_id: "",
message: { role: "user", content: [{ type: "text", text: "What is 5 + 3?" }] },
parent_tool_use_id: null
};
// Must coordinate when to yield next message
yield {
type: "user",
session_id: "",
message: { role: "user", content: [{ type: "text", text: "Multiply by 2" }] },
parent_tool_use_id: null
};
}
const q = query({
prompt: createInputStream(),
options: { model: "claude-opus-4-7" }
});
for await (const msg of q) {
if (msg.type === "assistant") {
const text = msg.message.content
.filter((block) => block.type === "text")
.map((block) => block.text)
.join("");
console.log(text);
}
}
```
</details>
### Session resume
If you have a session ID from a previous interaction, you can resume it later. This is useful for long-running workflows or when you need to persist conversations across application restarts.
This example creates a session, stores its ID, closes it, then resumes the conversation:
```typescript theme={null}
import {
unstable_v2_createSession,
unstable_v2_resumeSession,
type SDKMessage
} from "@anthropic-ai/claude-agent-sdk";
// Helper to extract text from assistant messages
function getAssistantText(msg: SDKMessage): string | null {
if (msg.type !== "assistant") return null;
return msg.message.content
.filter((block) => block.type === "text")
.map((block) => block.text)
.join("");
}
// Create initial session and have a conversation
const session = unstable_v2_createSession({
model: "claude-opus-4-7"
});
await session.send("Remember this number: 42");
// Get the session ID from any received message
let sessionId: string | undefined;
for await (const msg of session.stream()) {
sessionId = msg.session_id;
const text = getAssistantText(msg);
if (text) console.log("Initial response:", text);
}
console.log("Session ID:", sessionId);
session.close();
// Later: resume the session using the stored ID
await using resumedSession = unstable_v2_resumeSession(sessionId!, {
model: "claude-opus-4-7"
});
await resumedSession.send("What number did I ask you to remember?");
for await (const msg of resumedSession.stream()) {
const text = getAssistantText(msg);
if (text) console.log("Resumed response:", text);
}
```
<details>
<summary>See the same operation in V1</summary>
```typescript theme={null}
import { query } from "@anthropic-ai/claude-agent-sdk";
// Create initial session
const initialQuery = query({
prompt: "Remember this number: 42",
options: { model: "claude-opus-4-7" }
});
// Get session ID from any message
let sessionId: string | undefined;
for await (const msg of initialQuery) {
sessionId = msg.session_id;
if (msg.type === "assistant") {
const text = msg.message.content
.filter((block) => block.type === "text")
.map((block) => block.text)
.join("");
console.log("Initial response:", text);
}
}
console.log("Session ID:", sessionId);
// Later: resume the session
const resumedQuery = query({
prompt: "What number did I ask you to remember?",
options: {
model: "claude-opus-4-7",
resume: sessionId
}
});
for await (const msg of resumedQuery) {
if (msg.type === "assistant") {
const text = msg.message.content
.filter((block) => block.type === "text")
.map((block) => block.text)
.join("");
console.log("Resumed response:", text);
}
}
```
</details>
### Cleanup
Cut at 300 lines. The page has the rest.
agent-sdk/user-input First recorded · 866 lines, first recorded
# Handle approvals and user input ## Detect when Claude needs input ## Handle tool approval requests ### Respond to tool requests ## Handle clarifying questions ### Question format #### Option previews (TypeScript) ### Response format #### Support free-text input ### Complete example ## Limitations ## Other ways to get user input ### Streaming input ### Custom tools ## Related resources
The first capture of this source. The page was already there, and this is what it said.
# Handle approvals and user input
> Surface Claude's approval requests and clarifying questions to users, then return their decisions to the SDK.
While working on a task, Claude sometimes needs to check in with users. It might need permission before deleting files, or need to ask which database to use for a new project. Your application needs to surface these requests to users so Claude can continue with their input.
Claude requests user input in two situations: when it needs **permission to use a tool** (like deleting files or running commands), and when it has **clarifying questions** (via the `AskUserQuestion` tool). Both trigger your `canUseTool` callback, which pauses execution until you return a response. This is different from normal conversation turns where Claude finishes and waits for your next message.
For clarifying questions, Claude generates the questions and options. Your role is to present them to users and return their selections. You can't add your own questions to this flow; if you need to ask users something yourself, do that separately in your application logic.
The callback can stay pending indefinitely. Execution remains paused until your callback returns, and the SDK only cancels the wait when the query itself is cancelled. If a user might take longer to respond than your process can reasonably stay running, return the [`defer` hook decision](/docs/en/hooks#defer-a-tool-call-for-later), which lets the process exit and resume later from the persisted session.
This guide shows you how to detect each type of request and respond appropriately.
## Detect when Claude needs input
Pass a `canUseTool` callback in your query options. The callback fires whenever Claude needs user input, receiving the tool name and input as arguments:
<CodeGroup>
```python Python theme={null}
from claude_agent_sdk import ClaudeAgentOptions
async def handle_tool_request(tool_name, input_data, context):
# Prompt user and return allow or deny
...
options = ClaudeAgentOptions(can_use_tool=handle_tool_request)
```
```typescript TypeScript theme={null}
async function handleToolRequest(toolName, input, options) {
// options includes { signal: AbortSignal, suggestions?: PermissionUpdate[] }
// Prompt user and return allow or deny
}
const options = { canUseTool: handleToolRequest };
```
</CodeGroup>
The callback fires in two cases:
1. **Tool needs approval**: Claude wants to use a tool that isn't auto-approved by a [permission rule](/docs/en/agent-sdk/permissions) or permission mode. Check `tool_name` for the tool (e.g., `"Bash"`, `"Write"`).
2. **Claude asks a question**: Claude calls the `AskUserQuestion` tool. Check if `tool_name == "AskUserQuestion"` to handle it differently. If you specify a `tools` array, include `AskUserQuestion` for this to work. See [Handle clarifying questions](#handle-clarifying-questions) for details.
<Warning>
**The callback never fires for auto-approved tools.** Any approval earlier in the [permission evaluation flow](/docs/en/agent-sdk/permissions#how-permissions-are-evaluated), an allow rule or a mode like `acceptEdits` or `bypassPermissions`, resolves the call before `canUseTool` is consulted. If you list a tool bare in `allowed_tools`, a `canUseTool` check for that tool never runs unless an ask rule or `plan` mode routes the call back to a prompt. For logic that must apply to every tool call, use a [`PreToolUse` hook](/docs/en/agent-sdk/hooks), which executes before the rest of the flow and can allow, deny, or modify requests.
`AskUserQuestion`, MCP tools marked [`requiresUserInteraction`](/docs/en/mcp#require-approval-for-a-specific-tool), and connector tools [your organization set to `ask`](/docs/en/mcp#organization-controls-on-connector-tools) reach the callback even when an allow rule matches. In `dontAsk` mode these calls are denied instead, without invoking the callback.
</Warning>
You can also use the [`PermissionRequest` hook](/docs/en/agent-sdk/hooks#available-hooks) to send external notifications (Slack, email, push) when Claude is waiting for approval.
## Handle tool approval requests
Once you've passed a `canUseTool` callback in your query options, it fires when Claude wants to use a tool that nothing earlier in the permission flow has approved. Your callback receives three arguments:
| Argument | Description |
| ----------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `toolName` | The name of the tool Claude wants to use (e.g., `"Bash"`, `"Write"`, `"Edit"`) |
| `input` | The parameters Claude is passing to the tool. Contents vary by tool. |
| `options` (TS) / `context` (Python) | Additional context including optional `suggestions` (proposed `PermissionUpdate` entries to avoid re-prompting) and a cancellation signal. In TypeScript, `signal` is an `AbortSignal`; in Python, the signal field is reserved for future use. See [`ToolPermissionContext`](/docs/en/agent-sdk/python#toolpermissioncontext) for Python. |
The `input` object contains tool-specific parameters. Common examples:
| Tool | Input fields |
| ------- | --------------------------------------- |
| `Bash` | `command`, `description`, `timeout` |
| `Write` | `file_path`, `content` |
| `Edit` | `file_path`, `old_string`, `new_string` |
| `Read` | `file_path`, `offset`, `limit` |
See the SDK reference for complete input schemas: [Python](/docs/en/agent-sdk/python#tool-input%2Foutput-types) | [TypeScript](/docs/en/agent-sdk/typescript#tool-input-types).
You can display this information to the user so they can decide whether to allow or reject the action, then return the appropriate response.
The following example asks Claude to create and delete a test file. When Claude attempts each operation, the callback prints the tool request to the terminal and prompts for y/n approval.
<CodeGroup>
```python Python theme={null}
import asyncio
from claude_agent_sdk import ClaudeAgentOptions, ResultMessage, query
from claude_agent_sdk.types import (
HookMatcher,
PermissionResultAllow,
PermissionResultDeny,
ToolPermissionContext,
)
async def can_use_tool(
tool_name: str, input_data: dict, context: ToolPermissionContext
) -> PermissionResultAllow | PermissionResultDeny:
# Display the tool request
print(f"\nTool: {tool_name}")
if tool_name == "Bash":
print(f"Command: {input_data.get('command')}")
if input_data.get("description"):
print(f"Description: {input_data.get('description')}")
else:
print(f"Input: {input_data}")
# Get user approval
response = input("Allow this action? (y/n): ")
# Return allow or deny based on user's response
if response.lower() == "y":
# Allow: tool executes with the original (or modified) input
return PermissionResultAllow(updated_input=input_data)
else:
# Deny: tool doesn't execute, Claude sees the message
return PermissionResultDeny(message="User denied this action")
# Required workaround: dummy hook keeps the stream open for can_use_tool
async def dummy_hook(input_data, tool_use_id, context):
return {"continue_": True}
async def prompt_stream():
yield {
"type": "user",
"message": {
"role": "user",
"content": "Create a test file in /tmp and then delete it",
},
}
async def main():
async for message in query(
prompt=prompt_stream(),
options=ClaudeAgentOptions(
can_use_tool=can_use_tool,
hooks={"PreToolUse": [HookMatcher(matcher=None, hooks=[dummy_hook])]},
),
):
if isinstance(message, ResultMessage) and message.subtype == "success":
print(message.result)
asyncio.run(main())
```
```typescript TypeScript theme={null}
import { query } from "@anthropic-ai/claude-agent-sdk";
import * as readline from "readline";
// Helper to prompt user for input in the terminal
function prompt(question: string): Promise<string> {
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
return new Promise((resolve) =>
rl.question(question, (answer) => {
rl.close();
resolve(answer);
})
);
}
for await (const message of query({
prompt: "Create a test file in /tmp and then delete it",
options: {
canUseTool: async (toolName, input) => {
// Display the tool request
console.log(`\nTool: ${toolName}`);
if (toolName === "Bash") {
console.log(`Command: ${input.command}`);
if (input.description) console.log(`Description: ${input.description}`);
} else {
console.log(`Input: ${JSON.stringify(input, null, 2)}`);
}
// Get user approval
const response = await prompt("Allow this action? (y/n): ");
// Return allow or deny based on user's response
if (response.toLowerCase() === "y") {
// Allow: tool executes with the original (or modified) input
return { behavior: "allow", updatedInput: input };
} else {
// Deny: tool doesn't execute, Claude sees the message
return { behavior: "deny", message: "User denied this action" };
}
}
}
})) {
if ("result" in message) console.log(message.result);
}
```
</CodeGroup>
<Note>
In Python, `can_use_tool` requires [streaming mode](/docs/en/agent-sdk/streaming-vs-single-mode). When you pass a finite message stream through `query(prompt=generator)` or `ClaudeSDKClient.connect(prompt=async_iterable)`, the SDK closes the input stream after the last message, before the permission callback can be invoked, unless a registered hook or in-process MCP server is keeping it open. The example above keeps it open with a `PreToolUse` hook that returns `{"continue_": True}`. Connecting with no prompt and sending messages through `ClaudeSDKClient.query()` keeps the stream open on its own and needs no hook.
</Note>
This example uses a `y/n` flow where any input other than `y` is treated as a denial. In practice, you might build a richer UI that lets users modify the request, provide feedback, or redirect Claude entirely. See [Respond to tool requests](#respond-to-tool-requests) for all the ways you can respond.
### Respond to tool requests
Your callback returns one of two response types:
| Response | Python | TypeScript |
| --------- | ------------------------------------------ | ------------------------------------- |
| **Allow** | `PermissionResultAllow(updated_input=...)` | `{ behavior: "allow", updatedInput }` |
| **Deny** | `PermissionResultDeny(message=...)` | `{ behavior: "deny", message }` |
When allowing, the tool runs with the input Claude requested unless you return a modified input, `updatedInput` in TypeScript or `updated_input` in Python. Before v2.1.207, Claude Code rejected an allow result that omitted `updatedInput` and denied the tool call with a validation error.
When denying, provide a message explaining why. Claude sees this message and may adjust its approach.
<CodeGroup>
```python Python theme={null}
from claude_agent_sdk.types import PermissionResultAllow, PermissionResultDeny
# Allow the tool to execute
return PermissionResultAllow(updated_input=input_data)
# Block the tool
return PermissionResultDeny(message="User rejected this action")
```
```typescript TypeScript theme={null}
// Allow the tool to execute
return { behavior: "allow", updatedInput: input };
// Block the tool
return { behavior: "deny", message: "User rejected this action" };
```
</CodeGroup>
Beyond allowing or denying, you can modify the tool's input or provide context that helps Claude adjust its approach:
* **Approve**: let the tool execute as Claude requested
* **Approve with changes**: modify the input before execution (e.g., sanitize paths, add constraints)
* **Approve and remember**: echo a suggested permission rule back so matching calls skip the prompt next time
* **Reject**: block the tool and tell Claude why
* **Suggest alternative**: block but guide Claude toward what the user wants instead
* **Redirect entirely**: use [streaming input](/docs/en/agent-sdk/streaming-vs-single-mode) to send Claude a completely new instruction
The `ask_user` and `askUser` helpers in the following snippets stand in for your application's own prompt UI.
<Tabs>
<Tab title="Approve">
The user approves the action as-is. Pass through the `input` from your callback unchanged and the tool executes exactly as Claude requested.
<CodeGroup>
```python Python theme={null}
async def can_use_tool(tool_name, input_data, context):
print(f"Claude wants to use {tool_name}")
approved = await ask_user("Allow this action?")
if approved:
return PermissionResultAllow(updated_input=input_data)
return PermissionResultDeny(message="User declined")
```
```typescript TypeScript theme={null}
canUseTool: async (toolName, input) => {
console.log(`Claude wants to use ${toolName}`);
const approved = await askUser("Allow this action?");
if (approved) {
return { behavior: "allow", updatedInput: input };
}
return { behavior: "deny", message: "User declined" };
};
```
</CodeGroup>
</Tab>
<Tab title="Approve with changes">
The user approves but wants to modify the request first. You can change the input before the tool executes. Claude sees the result but isn't told you changed anything. Useful for sanitizing parameters, adding constraints, or scoping access.
<CodeGroup>
```python Python theme={null}
async def can_use_tool(tool_name, input_data, context):
if tool_name == "Bash":
# User approved, but scope all commands to sandbox
sandboxed_input = {**input_data}
sandboxed_input["command"] = input_data["command"].replace(
"/tmp", "/tmp/sandbox"
)
return PermissionResultAllow(updated_input=sandboxed_input)
return PermissionResultAllow(updated_input=input_data)
```
```typescript TypeScript theme={null}
canUseTool: async (toolName, input) => {
if (toolName === "Bash") {
// User approved, but scope all commands to sandbox
const sandboxedInput = {
...input,
command: input.command.replace("/tmp", "/tmp/sandbox")
};
return { behavior: "allow", updatedInput: sandboxedInput };
Cut at 300 lines. The page has the rest.
agent-teams First recorded · 441 lines, first recorded
# Orchestrate teams of Claude Code sessions ## When to use agent teams ### Compare with subagents ## Enable agent teams ## Start your first agent team ## Control your agent team ### Choose a display mode ### Specify teammates and models ### Require plan approval for teammates ### Talk to teammates directly ### Assign and claim tasks ### Shut down teammates ### Enforce quality gates with hooks ## How agent teams work ### How Claude starts agent teams ### Architecture ### Use subagent definitions for teammates ### Permissions #### Messages between agents ### Context and communication ### Token usage ## Use case examples ### Run a parallel code review ### Investigate with competing hypotheses ## Best practices ### Give teammates enough context ### Choose an appropriate team size ### Size tasks appropriately ### Wait for teammates to finish ### Start with research and review ### Avoid file conflicts ### Monitor and steer ## Troubleshooting ### Teammates not appearing ### Too many permission prompts ### Agents stopping early ### Orphaned tmux sessions ## Limitations ## Next steps
The first capture of this source. The page was already there, and this is what it said.
# Orchestrate teams of Claude Code sessions
> Coordinate multiple Claude Code instances working together as a team, with shared tasks, inter-agent messaging, and centralized management.
<Warning>
Agent teams are experimental and disabled by default. Enable them by setting `CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1` in your [settings.json](/docs/en/settings) or environment. Without that variable, no team is set up at session start, no team directories are written, and Claude does not spawn or propose teammates. Agent teams have [known limitations](#limitations) around session resumption, task coordination, and shutdown behavior.
</Warning>
Agent teams let you coordinate multiple Claude Code instances working together. One session acts as the team lead, coordinating work, assigning tasks, and synthesizing results. Teammates work independently, each in its own context window, and communicate directly with each other.
Unlike [subagents](/docs/en/sub-agents), which run within a single session and can only report back to the main agent, you can also interact with individual teammates directly without going through the lead.
<Note>
This page describes agent teams as of v2.1.178. With `CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS` set, spawning a teammate no longer needs a setup step, and cleanup happens automatically when the session exits. Before v2.1.178, you asked Claude to create and name a team first, and Claude used the `TeamCreate` and `TeamDelete` tools to set it up and remove it. Both tools no longer exist. The `team_name` input on the Agent tool is accepted but ignored, and the `team_name` field in `TaskCreated`, `TaskCompleted`, and `TeammateIdle` [hook payloads](/docs/en/hooks#taskcreated) carries the session-derived name and is deprecated.
</Note>
## When to use agent teams
Agent teams are most effective for tasks where parallel exploration adds real value. See [use case examples](#use-case-examples) for full scenarios. The strongest use cases are:
* **Research and review**: multiple teammates can investigate different aspects of a problem simultaneously, then share and challenge each other's findings
* **New modules or features**: teammates can each own a separate piece without stepping on each other
* **Debugging with competing hypotheses**: teammates test different theories in parallel and converge on the answer faster
* **Cross-layer coordination**: changes that span frontend, backend, and tests, each owned by a different teammate
Agent teams add coordination overhead and use significantly more tokens than a single session. They work best when teammates can operate independently. For sequential tasks, same-file edits, or work with many dependencies, a single session or [subagents](/docs/en/sub-agents) are more effective.
### Compare with subagents
Both agent teams and [subagents](/docs/en/sub-agents) let you parallelize work, but they operate differently. Choose based on whether your workers need to communicate with each other. For separate sessions that pass messages to each other without a team, see [cross-session messaging](/docs/en/cross-session-messaging).
<Frame caption="Subagents only report results back to the main agent and never talk to each other. In agent teams, teammates share a task list, claim work, and communicate directly with each other.">
<img src="https://mintcdn.com/claude-code/nsvRFSDNfpSU5nT7/images/subagents-vs-agent-teams-light.png?fit=max&auto=format&n=nsvRFSDNfpSU5nT7&q=85&s=2f8db9b4f3705dd3ab931fbe2d96e42a" className="dark:hidden" alt="Diagram comparing subagent and agent team architectures. Subagents are spawned by the main agent, do work, and report results back. Agent teams coordinate through a shared task list, with teammates communicating directly with each other." width="4245" height="1615" data-path="images/subagents-vs-agent-teams-light.png" />
<img src="https://mintcdn.com/claude-code/nsvRFSDNfpSU5nT7/images/subagents-vs-agent-teams-dark.png?fit=max&auto=format&n=nsvRFSDNfpSU5nT7&q=85&s=d573a037540f2ada6a9ae7d8285b46fd" className="hidden dark:block" alt="Diagram comparing subagent and agent team architectures. Subagents are spawned by the main agent, do work, and report results back. Agent teams coordinate through a shared task list, with teammates communicating directly with each other." width="4245" height="1615" data-path="images/subagents-vs-agent-teams-dark.png" />
</Frame>
| | Subagents | Agent teams |
| :---------------- | :----------------------------------------------- | :-------------------------------------------------- |
| **Context** | Own context window; results return to the caller | Own context window; fully independent |
| **Communication** | Report results back to the main agent only | Teammates message each other directly |
| **Coordination** | Main agent manages all work | Shared task list with self-coordination |
| **Best for** | Focused tasks where only the result matters | Complex work requiring discussion and collaboration |
| **Token cost** | Lower: results summarized back to main context | Higher: each teammate is a separate Claude instance |
Use subagents when you need quick, focused workers that report back. Use agent teams when teammates need to share findings, challenge each other, and coordinate on their own.
## Enable agent teams
Agent teams are disabled by default. Enable them by setting the `CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS` environment variable to `1`, either in your shell environment or through [settings.json](/docs/en/settings):
```json settings.json theme={null}
{
"env": {
"CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS": "1"
}
}
```
## Start your first agent team
After enabling agent teams, describe the task and the teammates you want in natural language. Claude spawns them and coordinates work based on your prompt.
This example works well because the three roles are independent and can explore the problem without waiting on each other:
```text wrap theme={null}
I'm designing a CLI tool that helps developers track TODO comments across
their codebase. Spawn three teammates to explore this from different angles:
one on UX, one on technical architecture, one playing devil's advocate.
```
From there, Claude populates a [shared task list](/docs/en/interactive-mode#task-list), spawns teammates for each perspective, has them explore the problem, and synthesizes findings when finished.
Claude may sometimes use [subagents](/docs/en/sub-agents) instead of creating a team. Subagents appear in the same agent panel as teammates, so the panel alone doesn't confirm a team formed. If Claude spawned subagents instead, ask again and explicitly request an agent team.
The lead's terminal lists teammates in the agent panel below the prompt input. From the panel:
* **Up and down arrows**: select a teammate
* **Enter**: open the selected teammate's transcript and message it directly
* **Escape**: interrupt the selected teammate's current turn
As of v2.1.199, an idle teammate's row stays in the panel while any teammate or subagent is still working, so you can select it to review its transcript or send it more work. Once every agent in the panel is idle, idle rows hide after 30 seconds and reappear on the teammate's next turn; the teammate stays running and addressable while hidden. In v2.1.181 through v2.1.198, an idle row hid 30 seconds after its own turn ended, even while other teammates were still working; idle rows are not hidden on versions before v2.1.181.
When more than three teammates are idle at once, the rows beyond the first three collapse into a single row that counts the collapsed teammates, such as `2 idle agents` when five are idle. Select it and press Enter to expand the collapsed rows, or press Esc to collapse them again. Working teammates, failed teammates, and the teammate you're viewing always keep their own rows.
If you want each teammate in its own split pane, see [Choose a display mode](#choose-a-display-mode).
## Control your agent team
Tell the lead what you want in natural language. It handles team coordination, task assignment, and delegation based on your instructions.
### Choose a display mode
Agent teams support two display modes:
* **In-process**: all teammates run inside your main terminal. Use the up and down arrow keys in the agent panel to select a teammate, then press Enter to view it and type to message it directly. Works in any terminal, no extra setup required.
* **Split panes**: each teammate gets its own pane. You can see everyone's output at once and click into a pane to interact directly. Requires tmux, or iTerm2.
<Note>
`tmux` has known limitations on certain operating systems and traditionally works best on macOS. Using `tmux -CC` in iTerm2 is the suggested entrypoint into `tmux`.
</Note>
The default is `"in-process"`. Before v2.1.179 the default was `"auto"`, so upgraded sessions that previously opened split panes now stay in one terminal unless you set the mode explicitly. Set `"auto"` to enable split panes when you're already running inside a tmux session, or when your terminal is iTerm2 with the `it2` CLI installed, falling back to in-process otherwise. The `"tmux"` setting enables split-pane mode and auto-detects whether to use tmux or iTerm2 based on your terminal.
As of v2.1.186, set `"iterm2"` to use iTerm2 native split panes explicitly. This mode requires the [`it2` CLI](https://github.com/mkusaka/it2) and shows an error with the install command if `it2` is missing. The setup prompt that offers to install `it2` or switch to tmux appears under `"auto"` or `"tmux"` when your terminal is iTerm2 and tmux is available as a fallback.
To override the default, set [`teammateMode`](/docs/en/settings#available-settings) in `~/.claude/settings.json`:
```json theme={null}
{
"teammateMode": "auto"
}
```
To set the mode for a single session, pass it as a flag:
```bash theme={null}
claude --teammate-mode auto
```
The `--teammate-mode` flag is experimental and doesn't appear in `claude --help`.
Split-pane mode requires either [tmux](https://github.com/tmux/tmux/wiki) or iTerm2 with the [`it2` CLI](https://github.com/mkusaka/it2). To install manually:
* **tmux**: install through your system's package manager. See the [tmux wiki](https://github.com/tmux/tmux/wiki/Installing) for platform-specific instructions.
* **iTerm2**: install the [`it2` CLI](https://github.com/mkusaka/it2), then enable the Python API in **iTerm2 → Settings → General → Magic → Enable Python API**.
### Specify teammates and models
Claude decides the number of teammates to spawn based on your task, or you can specify exactly what you want:
```text wrap theme={null}
Spawn 4 teammates to refactor these modules in parallel. Use Sonnet for
each teammate.
```
Teammates don't inherit the lead's `/model` selection by default. To change the model used when the prompt doesn't specify one, set **Default teammate model** in `/config`. Pick **Default (leader's model)** to have teammates follow the lead's current model.
Claude Code checks each teammate's model, whether requested in your prompt or set through **Default teammate model**, against your organization's [`availableModels`](/docs/en/model-config#restrict-model-selection) allowlist. When the allowlist blocks a value, Claude Code substitutes another model:
* **Family alias such as `opus`**: On the Anthropic API and Claude Platform on AWS, Claude Code runs the teammate on the newest version of that family the allowlist permits. On providers with provider-specific model IDs, where the [substitution doesn't operate](/docs/en/model-config#restrict-model-selection), a blocked alias falls back like any other blocked value per the next bullet
* **Any other blocked value, including a family alias on providers where the substitution doesn't operate or whose family has no permitted version**: Claude Code uses the default teammate model. When the blocked value is the **Default teammate model** setting itself, Claude Code uses your provider's default Opus model, or the lead's model when the allowlist blocks that too
Teammates inherit the lead's [effort level](/docs/en/model-config#adjust-effort-level). In split-pane mode this applies from v2.1.186; earlier versions did not pass the lead's session effort to split-pane teammates.
### Require plan approval for teammates
For complex or risky tasks, you can require teammates to plan before implementing. The teammate works in read-only plan mode until the lead approves their approach:
```text wrap theme={null}
Spawn an architect teammate to refactor the authentication module.
Require plan approval before they make any changes.
```
When a teammate finishes planning, it sends a plan approval request to the lead. The lead reviews the plan and either approves it or rejects it with feedback. If rejected, the teammate stays in plan mode, revises based on the feedback, and resubmits. Once approved, the teammate exits plan mode and begins implementation.
The lead makes approval decisions autonomously. To influence the lead's judgment, give it criteria in your prompt, such as "only approve plans that include test coverage" or "reject plans that modify the database schema."
### Talk to teammates directly
Each teammate is a full, independent Claude Code session. You can message any teammate directly to give additional instructions, ask follow-up questions, or redirect their approach.
* **In-process mode**: use the up and down arrow keys in the agent panel to select a teammate, then press Enter to view its session and type to send it a message. Press `x` on a selected teammate to stop it. Press Ctrl+T to toggle the task list.
* **Split-pane mode**: click into a teammate's pane to interact with their session directly. Each teammate has a full view of their own terminal.
While you're viewing an in-process teammate, plain text and [skills](/docs/en/skills) go to that teammate, but built-in commands still run in the lead's session.
A teammate's model and fast mode are fixed when it spawns, so `/model` and `/fast` only change the lead's settings. As of v2.1.199, typing either command while viewing a teammate shows a notice that the change applies to the lead; earlier versions applied it to the lead with no indication. `/effort` still applies to the viewed teammate's later turns, because teammates follow the lead's [effort level](/docs/en/model-config#adjust-effort-level).
### Assign and claim tasks
The shared task list coordinates work across the team. The lead creates tasks and teammates work through them. Tasks have three states: pending, in progress, and completed. Tasks can also depend on other tasks: a pending task with unresolved dependencies cannot be claimed until those dependencies are completed.
The lead can assign tasks explicitly, or teammates can self-claim:
* **Lead assigns**: tell the lead which task to give to which teammate
* **Self-claim**: after finishing a task, a teammate picks up the next unassigned, unblocked task on its own
Task claiming uses file locking to prevent race conditions when multiple teammates try to claim the same task simultaneously.
### Shut down teammates
To gracefully end a teammate's session, refer to it by name. For example, with a teammate named researcher:
```text wrap theme={null}
Ask the researcher teammate to shut down
```
The lead sends a shutdown request. The teammate can approve, exiting gracefully, or reject with an explanation.
The team's shared directories are cleaned up automatically when the session ends, so there's no separate cleanup step. See [Architecture](#architecture) for which directories are removed and which persist for resumed sessions.
### Enforce quality gates with hooks
Use [hooks](/docs/en/hooks) to enforce rules when teammates finish work or tasks are created or completed:
* [`TeammateIdle`](/docs/en/hooks#teammateidle): runs when a teammate is about to go idle. Exit with code 2 to send feedback and keep the teammate working.
* [`TaskCreated`](/docs/en/hooks#taskcreated): runs when a task is being created. Exit with code 2 to prevent creation and send feedback.
* [`TaskCompleted`](/docs/en/hooks#taskcompleted): runs when a task is being marked complete. Exit with code 2 to prevent completion and send feedback.
## How agent teams work
This section covers the architecture and mechanics behind agent teams. If you want to start using them, see [Control your agent team](#control-your-agent-team) above.
### How Claude starts agent teams
An agent team forms when the first teammate is spawned, with the main session acting as the lead. There are two ways teammates get spawned:
* **You request teammates**: give Claude a task that benefits from parallel work and explicitly ask for teammates. Claude spawns them based on your instructions.
* **Claude proposes teammates**: if Claude determines your task would benefit from parallel work, it may suggest spawning teammates. You confirm before it proceeds.
In both cases, you stay in control. Claude won't spawn teammates without your approval.
### Architecture
An agent team consists of:
| Component | Role |
| :------------ | :---------------------------------------------------------------------- |
| **Team lead** | The main Claude Code session that spawns teammates and coordinates work |
| **Teammates** | Separate Claude Code instances that each work on assigned tasks |
| **Task list** | Shared list of work items that teammates claim and complete |
| **Mailbox** | Messaging system for communication between agents |
Each agent's mailbox is a JSON file at `~/.claude/teams/{team-name}/inboxes/{agent-name}.json`. Claude Code validates every entry when it reads a mailbox file. Entries that don't match the message format are reported as errors and removed from the file; the valid messages are still delivered. Before v2.1.207, a single malformed mailbox entry caused a repeated error every second and blocked delivery for that mailbox until you deleted the file manually.
Claude Code reports a message as sent only when the write to the recipient's mailbox file succeeds, whether the message is plain text or a structured protocol message such as a plan approval or shutdown request. When the write fails, for example because the disk is full or the mailbox directory isn't writable, the sending agent receives an error and nothing is sent. See [Failed to write to a teammate's inbox](/docs/en/errors#failed-to-write-to-a-teammate-inbox) for the error messages and recovery steps.
Claude Code manages task dependencies automatically: when a teammate completes a task that other tasks depend on, it unblocks the dependent tasks without any action from you.
Teams and tasks are stored locally under a session-derived name. The name is `session-` followed by the first eight characters of the session ID:
* **Team config**: `~/.claude/teams/{team-name}/config.json`
* **Task list**: `~/.claude/tasks/{team-name}/`
Claude Code generates both of these automatically at session startup and updates them as teammates join, go idle, or leave. The team config directory is removed when the session ends. The task list directory persists locally and is never uploaded, so resumed sessions keep their tasks. Retention is governed by the same [`cleanupPeriodDays`](/docs/en/settings#available-settings) you already control for session transcripts, following the [retention sweep rules](/docs/en/claude-directory#cleaned-up-automatically).
The team config holds runtime state such as session IDs and tmux pane IDs, so don't edit it by hand or pre-author it: your changes are overwritten on the next state update.
To define reusable teammate roles, use [subagent definitions](#use-subagent-definitions-for-teammates) instead.
The team config contains a `members` array with each member's name and agent ID. The lead's entry always carries the agent type `team-lead`. A teammate's entry carries whatever agent type the lead named when spawning it, whether a [built-in type](/docs/en/sub-agents#built-in-subagents) or a [subagent definition](#use-subagent-definitions-for-teammates), and omits the field when the lead named none. Teammates can read this file to discover other team members.
There is no project-level equivalent of the team config. A file like `.claude/teams/teams.json` in your project directory is not recognized as configuration; Claude treats it as an ordinary file.
### Use subagent definitions for teammates
When spawning a teammate, you can reference a [subagent](/docs/en/sub-agents) type from any [subagent scope](/docs/en/sub-agents#choose-the-subagent-scope): project, user, plugin, or CLI-defined. This lets you define a role once, such as a security-reviewer or test-runner, and reuse it both as a delegated subagent and as an agent team teammate.
To use a subagent definition, mention it by name when asking Claude to spawn the teammate:
```text wrap theme={null}
Spawn a teammate using the security-reviewer agent type to audit the auth module.
```
The teammate honors that definition's `tools` allowlist and `model`, and the definition's body is appended to the teammate's system prompt as additional instructions rather than replacing it. Team coordination tools such as `SendMessage` and the task management tools are always available to a teammate even when `tools` restricts other tools.
<Note>
The `skills` and `mcpServers` frontmatter fields in a subagent definition are not applied when that definition runs as a teammate. Teammates load skills and MCP servers from your project and user settings, the same as a regular session.
</Note>
### Permissions
Teammates start with the lead's permission settings. If the lead runs with `--dangerously-skip-permissions`, all teammates do too. After spawning, you can change individual teammate modes, but you can't set per-teammate modes at spawn time.
Teammate permission prompts appear in the lead session, so approve them there yourself. [Plan approval](#require-plan-approval-for-teammates) is the designed exception: the lead session grants teammate plan approvals without a separate prompt to you.
#### Messages between agents
When one agent sends another a message over `SendMessage`, Claude Code tells the receiving agent the message came from another Claude session, not from you. A teammate can't approve a permission prompt or supply consent on your behalf, and a teammate that was denied an action can't relay it to another teammate to bypass the check. The same rules apply to a message that arrives from [one of your other Claude Code sessions](/docs/en/cross-session-messaging#how-a-session-treats-an-incoming-message), outside the team entirely.
In [auto mode](/docs/en/permission-modes#eliminate-prompts-with-auto-mode), the classifier applies two checks to messages between agents:
* It treats an approval claim relayed from another agent as untrusted input rather than confirmation from you.
* It reviews each message before Claude Code delivers it, whether a plain message or a structured protocol message such as a shutdown request or plan approval response. A message it blocks never reaches the recipient.
### Context and communication
Each teammate has its own context window. When spawned, a teammate loads the same project context as a regular session: CLAUDE.md, MCP servers, and skills. It also receives the spawn prompt from the lead. The lead's conversation history does not carry over.
**How teammates share information:**
* **Automatic message delivery**: when teammates send messages, they're delivered automatically to recipients. The lead doesn't need to poll for updates.
* **Idle notifications**: when a teammate finishes and stops, it automatically notifies the lead. As of v2.1.198, a teammate whose turn ends on an API error notifies the lead that it failed and includes the error text, instead of appearing to finish normally.
* **Shared task list**: all agents can see task status and claim available work.
* **Teammate messaging**: send a message to one specific teammate by name. To reach everyone, send one message per recipient.
The lead assigns every teammate a name when it spawns them, and any teammate can message any other by that name. To get predictable names you can reference in later prompts, tell the lead what to call each teammate in your spawn instruction.
### Token usage
Agent teams use significantly more tokens than a single session. Each teammate has its own context window, and token usage scales with the number of active teammates. For research, review, and new feature work, the extra tokens are usually worthwhile. For routine tasks, a single session is more cost-effective. See [agent team token costs](/docs/en/costs#agent-team-token-costs) for usage guidance.
## Use case examples
These examples show how agent teams handle tasks where parallel exploration adds value.
### Run a parallel code review
A single reviewer tends to gravitate toward one type of issue at a time. Splitting review criteria into independent domains means security, performance, and test coverage all get thorough attention simultaneously. The prompt assigns each teammate a distinct lens so they don't overlap:
Cut at 300 lines. The page has the rest.
agent-view First recorded · 867 lines, first recorded
# Manage multiple agents with agent view ## Quick start ## Monitor sessions with agent view ### Read session state ### Row summaries ### Pull request status ### Peek and reply ### Attach to a session #### Switch sessions without leaving the terminal ### Organize the list ### Filter sessions ### Keyboard shortcuts ## Dispatch new agents ### From agent view #### Dispatch to a specific directory ### From inside a session #### Send the session to the background #### Copy the session with /fork #### What carries over when you background ### From your shell #### Run a shell command ### How file edits are isolated #### What deleting a session removes ### Set the model ### Permission mode, model, and effort ### Settings, plugins, and MCP servers ## Manage sessions from the shell ### List sessions as JSON ## How background sessions are hosted ### The supervisor process ### Where state is stored ### Turn off agent view ## Troubleshooting ### `claude agents` lists subagents instead of opening agent view ### Agent view opens with no sessions ### Backgrounding shows a `Background this session?` dialog ### Prompt rejected as too short ### Sessions show as failed after shutdown ### Opening a session says the conversation is already open ### Opening a session says it has no saved transcript ### A session fails before starting with a `possibly low memory` note ### Agent view says the background service did not respond ### Dispatch fails with `Could not resolve authentication method` ### Background sessions can't read Desktop, Documents, or Downloads on macOS ### Background sessions can't reach local-network hosts on macOS ### A session is slow to respond after attaching ### `.claude/worktrees/` is filling up ## Limitations ## Related resources ## Version history
The first capture of this source. The page was already there, and this is what it said.
# Manage multiple agents with agent view
> Dispatch and manage many Claude Code sessions from one screen. Agent view shows what every session is doing and which ones need your input.
Agent view, opened with `claude agents`, is one screen for all your background sessions: what's running, what needs your input, and what's done. Dispatch new sessions, watch their state at a glance instead of scrolling through transcripts, and step in only when one needs you. Each background session is a full Claude Code conversation that keeps running without a terminal attached, so you can open it, reply, and leave whenever you want.
<img src="https://mintcdn.com/claude-code/1B48Qz2Z9hac4SLG/images/agent-view-light.png?fit=max&auto=format&n=1B48Qz2Z9hac4SLG&q=85&s=7a186c96ed47d6700d084d77e786be65" className="dark:hidden" alt="Agent view in a terminal: the header shows Claude Code v2.1.140, the model, the working directory, and a summary count. Sessions are grouped under Needs input, Working, and Completed, with a dispatch input at the bottom and a footer of keyboard hints." width="1772" height="780" data-path="images/agent-view-light.png" />
<img src="https://mintcdn.com/claude-code/1B48Qz2Z9hac4SLG/images/agent-view-dark.png?fit=max&auto=format&n=1B48Qz2Z9hac4SLG&q=85&s=a5bed7434bae368faea3a8f023b52aa2" className="hidden dark:block" alt="Agent view in a terminal: the header shows Claude Code v2.1.140, the model, the working directory, and a summary count. Sessions are grouped under Needs input, Working, and Completed, with a dispatch input at the bottom and a footer of keyboard hints." width="1772" height="780" data-path="images/agent-view-dark.png" />
Use agent view when you have several independent tasks Claude can work on without you watching every step. Dispatch a bug fix, a pull request review, and a flaky-test investigation as three rows, keep working in another window, and check back when a row shows it needs you or has a result.
When you want to work more directly in any agent's session, attach to the row to enter the full conversation.
To compare agent view with subagents, agent teams, and worktrees, see [Run agents in parallel](/docs/en/agents).
<Note>
Agent view is in research preview. The interface and keyboard shortcuts may change as the feature evolves.
</Note>
## Quick start
This walkthrough covers the core agent view loop: dispatch a task, watch its row update as Claude works, peek to check on it and reply, and attach for the full conversation. The session you dispatch keeps running after you close agent view, so you can leave and come back to it.
<Steps>
<Step title="Open agent view">
From your shell, run:
```bash theme={null}
claude agents
```
If you haven't yet accepted the [workspace trust dialog](/docs/en/permissions#project-allow-rules-and-workspace-trust) for the directory, Claude Code shows it before agent view opens, the same dialog `claude` shows. Accept to save trust for the workspace and continue. If you decline, Claude Code exits without opening agent view.
Agent view opens with an input at the bottom and a table that fills in as sessions start. Press `Esc` at any time to return to your shell; if you opened agent view by backgrounding a session with `←`, `Esc` returns to that conversation instead. Your sessions keep running while you're away and reappear the next time you open agent view.
</Step>
<Step title="Dispatch a session">
Type a prompt describing a task and press `Enter`. A new background session starts on that task and appears as a row showing whether it's working, waiting on you, or done. The new session uses the model shown in the agent view header and the same [permission mode](#permission-mode-model-and-effort) you'd get running `claude` in that directory.
Every prompt you enter here starts its own new session. Typing another prompt and pressing `Enter` launches a second session alongside the first rather than sending a follow-up to it. You can run several in parallel this way.
Each session uses your subscription quota independently, so see [Limitations](#limitations) before dispatching many at once.
</Step>
<Step title="Peek and reply">
Select a row with the arrow keys and press `Space` to open the peek panel. It shows the session's most recent output, or the question it's waiting on, rather than the full transcript. Type a reply and press `Enter` to send it without leaving agent view.
</Step>
<Step title="Attach and detach">
Press `Enter` or `→` on a row to attach when you want the full conversation. The session takes over the terminal as a full interactive Claude Code session. Press `←` on an empty prompt to detach and return to the table.
</Step>
<Step title="Bring an existing session in">
This step needs a running session. If you followed the earlier steps you don't have one open in this terminal, so open a regular `claude` session in another terminal and send it a message first.
To move a session you already have open into agent view, run `/bg` inside it, or press `←` on an empty prompt to background it and open agent view in one step. In a fresh session with no messages yet, `/bg` asks you to send a message first, while `←` works right away. The session keeps running and appears as a row alongside the ones you dispatched.
</Step>
</Steps>
You can use `claude agents` as your primary entry point instead of `claude`: dispatch every task from agent view, attach when you want the full conversation, and press `←` to return to the table.
Inside a regular `claude` session, the prompt footer's `←` hint counts the background agents that are waiting on you, such as `← 2 agents`, and returns to `← for agents` when none need input. Counts above 99 show as `99+`. The count refreshes about every ten seconds while the terminal is focused and immediately when focus returns. It briefly changes color when it moves and when an agent completes, and when a background session finishes while none need your input it briefly shows the number completed, such as `← 2 done`. Both flashes are off when the [`prefersReducedMotion` setting](/docs/en/settings#available-settings) is on, and the hint is hidden in [screen reader mode](/docs/en/accessibility).
## Monitor sessions with agent view
Run `claude agents` to open agent view. It takes over the full terminal and lists every session grouped by state, with pinned sessions and the ones that need you at the top. Each row shows the session's name, current activity, and its age, counted from when the session was created; a finished session's age freezes at how long the run took.
The name is tinted with the color set by [`/color`](/docs/en/commands) in that session, including when you [background a session](#from-inside-a-session) with `←` or `/background`.
By default the list shows every background session you've started, across all your projects. A session working in one repository and another in a different worktree both appear here, regardless of which directory you opened agent view from. To narrow the list to one project, pass `--cwd`:
```bash theme={null}
claude agents --cwd ~/projects/my-app
```
This shows only sessions started under that directory. It still lists a session that has [moved into a worktree](#how-file-edits-are-isolated) under `~/projects/my-app/.claude/worktrees/`.
Interactive sessions you have open in other terminals don't appear until you [background them](#from-inside-a-session). [Subagents](/docs/en/sub-agents) and [teammates](/docs/en/agent-teams) a session spawns aren't listed as separate rows.
```text theme={null}
Pinned
✽ clawd walk cycle Drawing the walk-cycle sprite frames 3m
Ready for review
∙ jump physics Opened PR with collision fix #2048 2h
Needs input
✻ power-up design double jump or wall climb? 1m
Working
✽ collision detection Adding swept-AABB checks to CollisionSystem 2m
✢ playtest level 3 run 12 · all checkpoints cleared in 4m
Completed
✻ title screen result: menu, options, and credits done 9m
∙ sound effects result: 14 SFX exported to assets/audio 4h
… 6 more
```
### Read session state
Each row starts with an icon whose color and animation show the session's state:
| State | Icon shows as | What it means |
| :---------- | :------------ | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Working | Animated | Claude is actively running tools or generating a response |
| Needs input | Yellow | Claude is waiting on something only you can provide: an answer to a question, a permission decision, or another prompt only you can answer, such as a [sandbox](/docs/en/sandboxing) prompt to allow a network host or an MCP server's [request for input](/docs/en/mcp#respond-to-mcp-elicitation-requests). A command that needs an attached terminal, such as `/install-github-app` or the `/mcp` settings list, [holds an unattended session here too](#attach-to-a-session) |
| Idle | Dimmed | The session has nothing to do and is ready for your next prompt |
| Completed | Green | The task finished successfully |
| Failed | Red | The task ended with an error |
| Stopped | Grey | The session was stopped with `Ctrl+X` or `claude stop`, or [its process was ended from outside Claude Code](#the-supervisor-process) |
Separately, the icon's shape shows whether the underlying process is running:
| Shape | What it means |
| :------------------ | :---------------------------------------------------------------------------------------------------------------- |
| `✻` or animated `✽` | The session process is alive and replies immediately |
| `∙` | The process has exited. You can still peek, reply, or attach, and Claude restarts from where it left off |
| `✢` | A [`/loop`](/docs/en/scheduled-tasks) session sleeping between iterations. The row shows its run count and a countdown |
The `#N` label that can appear at the right edge of a row is a [pull request the session is linked to](#pull-request-status), not part of the state icon.
The terminal tab title shows the awaiting-input count while agent view is open: `2 awaiting input · claude agents` when sessions need input, or `claude agents` when none do.
While agent view is open, Claude Code also sends a notification through your configured [terminal notification channel](/docs/en/terminal-config#get-a-terminal-bell-or-notification) when a local background session starts needing your input, finishes, or fails. Sessions that run on a schedule, such as [`/loop`](/docs/en/scheduled-tasks) sessions, notify only when they need your input. Notifications use the same [`preferredNotifChannel` setting](/docs/en/settings#available-settings) as the rest of Claude Code and fire the [`Notification` hook](/docs/en/hooks#notification) with the `agent_needs_input` or `agent_completed` type.
Background sessions don't need any terminal open to keep working. A separate [supervisor process](#the-supervisor-process) runs them, so you can close agent view, close your shell, or start a new interactive session and your dispatched work keeps going.
Session state persists on disk through auto-updates and supervisor restarts. Sessions are also preserved when your machine sleeps. Their processes resume on wake and the supervisor reconnects to them instead of treating the time gap as idle. Shutting down still stops running sessions; see [Sessions show as failed after shutdown](#sessions-show-as-failed-after-shutdown) for how to recover them.
A session that was mid-response when the machine slept can come back unresponsive. When you open a session that has stopped responding, the supervisor restarts its process and the session continues the interrupted response from where it left off.
### Row summaries
The one-line summary in each row is generated by a [Haiku-class model](/docs/en/model-config) so the row can tell you what the session is doing, what it needs, or what it produced without opening the transcript. While a session is actively working, the row text updates at most once every 15 seconds from the session's own recent output without sending a model request, and the model writes a fresh summary when each turn ends.
A working row shows what the session says it's doing, and a blocked row shows the question it's asking. During a long turn, the model also rewrites the summary every few minutes so a busy row doesn't keep showing an outdated one. The summary text fills the row's remaining width; open the [peek panel](#peek-and-reply) to read a sentence the terminal edge clips.
When the list is [grouped by directory](#organize-the-list), the summary opens with the session's state as a colored word, such as `Needs input · double jump or wall climb?`. In the default state grouping, the group header already names the state, so the row shows only the summary.
The end-of-turn summary and each mid-turn rewrite are one short Haiku-class request through your normal provider, billed and handled under the same [data usage terms](/docs/en/data-usage) as the session itself. The 15-second updates between model rewrites reuse the session's own output and don't send a request. On a third-party provider or gateway with no Haiku-class model configured, the request uses the session's main model instead; set [`ANTHROPIC_DEFAULT_HAIKU_MODEL`](/docs/en/model-config#environment-variables) to choose one.
### Pull request status
When a session opens a pull request, a `#1234` label appears at the right edge of the row, linked to the pull request. Claude Code emits the link even when it can't detect hyperlink support, for example over SSH or tmux; set [`FORCE_HYPERLINK=0`](/docs/en/env-vars) to render the label as plain text. The label persists when you send a follow-up to the session, so the pull request remains visible while the row reverts to live progress. Claude opens these pull requests from the session itself when the task calls for one; [How file edits are isolated](#how-file-edits-are-isolated) covers what Claude commits and pushes without asking and when it asks first.
A session that works on an existing pull request is linked to it the same way. Claude Code finds the pull request differently depending on the command Claude runs:
* When Claude edits, comments on, closes, or marks a pull request ready with `gh`, Claude Code links the pull request that the command's own output names. A `gh` command whose captured output names no pull request doesn't create a link; `gh pr merge` is the common case, because it prints its result only to an interactive terminal.
* When Claude checks a pull request out with `gh pr checkout` or pushes to a branch, Claude Code looks the branch up with `gh pr view` and links its open pull request.
* The pull request doesn't need to exist yet when Claude pushes: Claude Code retries the branch lookup after up to five later `git`, `gh`, `glab`, or `curl` commands run in the same directory, so a pull request created after the push, including one Claude creates through the GitHub REST API, links when a retry finds it.
When a session is linked to more than one pull request, the label shows a count instead, such as `3 PRs`, colored by the open pull request that most needs attention. Open the [peek panel](#peek-and-reply) to see them all.
The pull request number is colored by its status:
| Color | Pull request status |
| :----- | :-------------------------------------------- |
| Yellow | Waiting on checks or review, or checks failed |
| Green | Checks passed and no review is blocking |
| Purple | Merged |
| Grey | Draft or closed |
For a task that ends in a pull request, check this label for the result: review and merge the pull request when its number turns green.
### Peek and reply
Press `Space` on a selected row to open the peek panel. It opens with the sentence the row truncates at the terminal edge, and which sentence that is depends on the session's state:
* A session that's waiting on you: the exact question it's asking, above the reply input
* A finished session: its result
* A working session: its full status sentence
Any pull requests linked to the session are listed next. For a session that's waiting on you, a line such as `waiting 3m` below them shows how long it has been waiting, and it's the only time shown in the panel. The age at the right edge of the row is a different number: it counts from when the session started.
Most of the time the peek panel is enough and you don't need to open the full transcript.
Type a reply in the peek panel and press `Enter` to send it to that session. When the session asks a question with predefined choices, the peek panel shows them as a numbered list and you can press a number key to pick one. A permission prompt shows as text describing what the session wants to run, without numbered options. Type a reply to answer it, or attach to answer with the standard prompt. For other blocked sessions, press `Tab` to fill the input with a suggested reply you can edit before sending. Prefix a reply with `!` to send a Bash command instead.
A reply that can't be delivered, because the background service is unreachable or the send fails, is saved and sent to the session as its next prompt when its process starts again, and the error message says the reply was saved. A reply prefixed with `!` isn't saved, because the saved text would reach the session as a plain prompt rather than run as a Bash command.
With [voice dictation](/docs/en/voice-dictation) enabled, hold or tap your push-to-talk key while the reply input is focused to dictate a reply instead of typing it. The same works in the dispatch input at the bottom of agent view.
Use `↑` and `↓` to peek at adjacent sessions without closing the panel, or `→` to attach.
### Attach to a session
Press `Enter` or `→` on a selected row to attach. Agent view is replaced by the full interactive session. When you attach, Claude posts a short recap of what happened while you were away.
While attached, the session behaves like any other Claude Code session: [commands](/docs/en/commands), keyboard shortcuts, and features all work, with the exceptions below.
While you're attached, `/install-github-app` and the [`/mcp`](/docs/en/mcp) settings list work normally, since a human at the terminal can complete their dialogs. When nobody is attached, these commands can't open their dialogs, so the session appears under `Needs input` in agent view with a row such as `open this session to manage MCP servers`, and the transcript reply says the same. Attach and run the command again to continue; the needs-input row clears when you attach. `/mcp reconnect <server>`, `/mcp enable`, and `/mcp disable` work without attaching either way.
Attached sessions always render in [fullscreen mode](/docs/en/fullscreen), regardless of your `tui` setting, because a background session has no terminal scrollback to append to. Scroll with `PgUp`, `PgDn`, or the mouse wheel, and press `Ctrl+O` for transcript mode. Your terminal's native scroll and tmux copy mode show only the current viewport, the same as when you run any fullscreen application.
Press `←` on an empty prompt, or run `/exit`, to detach and return to agent view, whether you opened the session from agent view or with `claude attach <id>` from your shell.
On Windows, if you press `←` within about half a second of attaching, Claude Code shows `Ambiguous ←, press again to detach`, because in that window the terminal can redeliver a press from before you attached. Press `←` again to detach.
`Ctrl+Z` also detaches but goes back to where you started instead: agent view if you attached from there, or your shell if you ran `claude attach`. Use `Ctrl+Z` when a dialog has focus and isn't responding to `←`.
`Ctrl+C` keeps its standard interrupt behavior while attached: it cancels a running response or `!` shell command rather than detaching. Pressing `Ctrl+C` twice on an empty prompt detaches, the same as in any session.
Detaching never stops a background session: `←`, `Ctrl+Z`, `/exit`, and double `Ctrl+C` or double `Ctrl+D` all leave it running. To end a session from inside it, run `/stop`.
#### Switch sessions without leaving the terminal
In a session running in the foreground, one you started in the terminal rather than attached to from agent view, pressing `←` on an empty prompt backgrounds it and opens agent view with that row selected, so you can switch sessions without leaving the terminal. The same single press detaches an attached session.
If you press `←` right after you delete the last of the prompt's text or move through prompt history, Claude Code asks you to confirm: the first press shows `Press ← again to open agents`, or `Press ← again to go back to agents` in an attached session, and the second press switches.
When `←` backgrounds a foreground session, agent view shows `Your conversation moved to the background` above the list, with that session's row already selected. From there:
* Press `Enter` to reopen the conversation.
* Press `Esc` to undo the switch and return to the conversation. If `Esc` shows `Still starting — try again in a moment`, the background session isn't ready yet, so press `Esc` again in a moment.
* Press `Ctrl+C` twice to exit to your shell.
When Claude Code can't reopen the conversation, it exits and prints a `claude --resume` command that resumes it.
[Claude's task list](/docs/en/interactive-mode#task-list) moves to the background session with the conversation, so the checklist is intact when you return to that row.
The row you pressed `←` from also keeps a bold, undimmed name after you move the selection with the arrow keys or the mouse, so you can tell which session you came from.
If a tool is running when you press `←`, Claude Code waits up to about ten seconds for it to finish before backgrounding, and the response continues in the background session. Press `←` again to background immediately instead of waiting. When in-flight work can't carry over to the background session, the `Background this session?` dialog appears first, the same as with [`/background`](#from-inside-a-session).
The ten-second limit doesn't apply while [subagents](/docs/en/sub-agents) are running. Claude Code keeps waiting so their work carries over, and shows a `Still backgrounding after the current tool` notice while it waits; press `←` again to background without waiting, which restarts the subagents from the beginning.
Claude Code doesn't background the session while you have unsent text in the prompt input, because the text stays in your terminal's input box and wouldn't move to the background session. If you type into the input while Claude Code waits to background the session, it cancels the switch with `Backgrounding cancelled — you have unsent text in the input. Send it or clear it, then press ← again.`
Pressing `←` creates the session's row even when the conversation has no messages yet, so `→` still returns to it.
You can turn this shortcut off with the `leftArrowOpensAgents` setting in `/config`.
### Organize the list
Agent view groups sessions so the ones that need input are at the top, with `Ready for review` and `Needs input` above `Working` and `Completed`. These group names don't map one-to-one to the [states](#read-session-state) above: a session moves to `Ready for review` when it has an open pull request, and `Completed` collects finished, failed, and stopped sessions together.
Press `Ctrl+S` to group by directory instead. Your choice persists across runs.
Within a group:
* Press `Ctrl+T` to pin a session to the top and [keep its process running](#the-supervisor-process) while idle
* Press `Shift+↑` or `Shift+↓` to reorder sessions
* Press `Ctrl+R` to rename a session
* Press `Enter` on a group header to collapse it
To remove a session from the list, press `Ctrl+X` to stop it and `Ctrl+X` again within two seconds to delete it. Pressing `Ctrl+X` on a group header deletes every session in that group after confirmation.
The second press deletes the session even when the stop attempt fails, for example because the [background service isn't responding](#agent-view-says-the-background-service-did-not-respond): the confirmation stays active for another two seconds, and the delete ends the session's process itself. Press `Esc` to dismiss the confirmation without deleting.
Except in the kept cases covered in [What deleting a session removes](#what-deleting-a-session-removes), deleting removes the session from the list, and a worktree Claude created for it is removed, kept, or left in place depending on how you delete and what the worktree holds. The conversation transcript always stays on your local machine, available through `claude --resume`.
To bring a session back on Claude Code v2.1.212 or later, type `/resume` in the dispatch input. A picker opens with past sessions of the repository you opened agent view from, newest first, including sessions you deleted from the list; sessions that already have a row aren't listed. `↑`/`↓` move the selection, `Enter` resumes the selected session as a background session so it rejoins the list as a row, and `Esc` closes the picker.
The picker opens only for a bare `/resume`. A targeted, scoped, or restricted resume can't be served by the picker, so agent view shows the `attach to a session to run it` hint instead when:
* `/resume` names an id or a search term
* the view is scoped with `--cwd`
* the view was started with [`--safe-mode`](/docs/en/cli-reference#cli-flags)
* the view was opened with a flag such as `--permission-mode` or `--settings`
Completed sessions that don't fit on screen fold into a `… N more` row. Failures and sessions with an open pull request always stay visible. The `Completed` group fills the vertical space left after the live groups, and on a short terminal the header compacts to a single summary line so sessions that are working or need input stay visible.
### Filter sessions
Type in the dispatch input to filter instead of dispatching:
| Filter | Shows |
| :---------------------- | :------------------------------------------------------------------------------------------------------- |
| `a:<name>` | Sessions running the named agent |
| `s:<state>` | Sessions in the given state, such as `s:working`. Also accepts `s:blocked` for everything waiting on you |
| `#<number>` or a PR URL | The session working on that pull request |
| Any other URL | The session whose first prompt contained that URL |
### Keyboard shortcuts
Press `?` in agent view to see every shortcut in context. The table below summarizes them.
| Shortcut | Action |
| :-------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `↑` / `↓` | Move between rows |
| `Enter` | Attach to the selected session, or dispatch if there's text in the input |
| `Space` | Open or close the peek panel for the selected session |
| `Shift+Enter` | Dispatch and attach immediately |
| `→` | Attach to the selected session |
| `Alt+1`..`Alt+9` | Attach to session 1–9 in the focused session's directory |
| `Tab` | On an empty input, browse all subagents. Otherwise apply the highlighted suggestion |
| `Ctrl+S` | Switch grouping between state and directory |
| `Ctrl+T` | Pin or unpin the selected session |
| `Ctrl+R` | Rename the selected session |
| `Ctrl+G` | Open the dispatch prompt in your `$VISUAL` or `$EDITOR` |
| `Ctrl+J` | Insert a newline in the dispatch input |
| `Ctrl+X` | Stop the session; press again within two seconds to delete it |
| `Shift+↑` / `Shift+↓` | Reorder the selected session |
| `Esc` | Close the peek panel, clear the input, or exit. When you opened agent view by backgrounding your session with `←`, the final `Esc` returns to that conversation instead of exiting |
| `Ctrl+C` | Clear the input; press twice to exit |
| `?` | Show all shortcuts |
## Dispatch new agents
Cut at 300 lines. The page has the rest.
agents First recorded · 63 lines, first recorded
# Run agents in parallel ## Choose an approach ## Check on running work ## Learn more
The first capture of this source. The page was already there, and this is what it said.
# Run agents in parallel > Compare the ways Claude Code can take on multiple tasks at once: subagents, agent view, agent teams, and dynamic workflows. [Subagents](/docs/en/sub-agents), [agent view](/docs/en/agent-view), [agent teams](/docs/en/agent-teams), and [dynamic workflows](/docs/en/workflows) each parallelize work in a different way. The right one depends on whether you want to stay in each conversation yourself, hand tasks off and check back later, or have Claude coordinate a group of workers for you. | Approach | What it gives you | Use it when | | :--------------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [Subagents](/docs/en/sub-agents) | Delegated workers inside one session that do a side task in their own context and return a summary | A side task would flood your main conversation with search results, logs, or file contents you won't reference again | | [Agent view](/docs/en/agent-view) | One screen to dispatch and monitor sessions running in the background, opened with `claude agents`. Research preview | You have several independent tasks and want to hand them off, check status at a glance, and step in only when one needs you | | [Agent teams](/docs/en/agent-teams) | Multiple coordinated sessions with a shared task list and inter-agent messaging, managed by a lead. Experimental and disabled by default | You want Claude to split a project into pieces, assign them, and keep the workers in sync | | [Dynamic workflows](/docs/en/workflows) | A script that runs many subagents and cross-checks their results, for work too big to coordinate one turn at a time or that needs more than a single pass | A job outgrows a handful of subagents, or you want findings verified against each other: a codebase-wide audit, a 500-file migration, cross-checked research, or a plan drafted from several angles | In every approach the workers are Claude sessions. To involve a different tool, expose it to Claude as an [MCP server](/docs/en/mcp). Three more tools support this work without being a way to run agents themselves: * [Worktrees](/docs/en/worktrees) give each session a separate git checkout, so parallel sessions never edit the same files. Use them for sessions you run yourself. Agent view moves each dispatched session into its own worktree automatically, and subagents you spawn can each get one too. * [Cross-session messaging](/docs/en/cross-session-messaging) lets Claude list and message your other Claude Code sessions on this machine, on another machine, or on [Claude Code on the web](/docs/en/claude-code-on-the-web), so sessions you run yourself can pass findings and status between themselves. * [`/batch`](/docs/en/commands) is a [skill](/docs/en/skills) that has Claude split one large change into 5 to 30 worktree-isolated subagents that each open a pull request. It's a packaged use of subagents and worktrees, not a separate coordination style. A few other features run Claude without you driving each step, but they solve a different problem than splitting work across agents: * A [background bash command](/docs/en/interactive-mode#background-bash-commands) runs one shell command without blocking the conversation. It doesn't spawn an agent. * A [forked subagent](/docs/en/sub-agents#fork-the-current-conversation), started with `/subtask`, is a subagent that inherits your full conversation context instead of starting fresh. It's a way to spawn a subagent, not a separate surface. To copy the whole session into a new [background session](/docs/en/agent-view#from-inside-a-session) that runs alongside it, use `/fork`. With [agent view turned off](/docs/en/agent-view#turn-off-agent-view), the forked-subagent command is `/fork` instead and `/subtask` isn't available. * A [routine](/docs/en/routines) runs a session on a schedule in the cloud, not in parallel on your machine. <Note> Running several sessions or subagents at once multiplies token usage. See [Costs](/docs/en/costs) for usage and rate-limit details. </Note> ## Choose an approach The right approach depends on who coordinates the work, whether the workers need to communicate, and whether they edit the same files: * **Who coordinates the work?** * Claude delegates and collects results inside one conversation: [subagents](/docs/en/sub-agents) * You hand off independent tasks and check back later: [agent view](/docs/en/agent-view) * Claude plans, assigns, and supervises a group of workers: [agent teams](/docs/en/agent-teams), experimental and disabled by default * A script holds the plan instead of Claude's turn-by-turn judgment: [dynamic workflows](/docs/en/workflows). See [how workflows compare to subagents and skills](/docs/en/workflows#when-to-use-a-workflow) * **Do the workers need to talk to each other?** Subagents report results back to the conversation that spawned them, and agent view sessions report only to you, though separate sessions can pass messages with [cross-session messaging](/docs/en/cross-session-messaging). Teammates in an agent team share a task list and message each other directly. * **Do the tasks touch the same files?** Isolate the work with [worktrees](/docs/en/worktrees). Subagents and sessions you run yourself can each use a separate worktree. Agent teams don't isolate teammates in worktrees, so [partition the work](/docs/en/agent-teams#avoid-file-conflicts) so each teammate owns a different set of files. ## Check on running work The command for checking on running work depends on which approach you used: * For background sessions, `claude agents` opens [agent view](/docs/en/agent-view): one screen showing every session, its state, and which ones need your input. * For subagents in the current session, named background subagents appear in the @-mention typeahead with their status. As of v2.1.198, `/agents` no longer opens a panel; it prints a notice pointing to the subagent file locations. To [create and edit custom subagents](/docs/en/sub-agents#configure-subagents), ask Claude or edit the files directly. Despite the similar name, `/agents` is separate from `claude agents`. * For anything running in the background of the current session, `/tasks` lists each item and lets you check on, attach to, or stop it. The list also includes subagents that have finished. * For dynamic workflows, `/workflows` lists running and completed runs, the phase each is in, and how many agents have finished. For a desktop view of all your sessions, see [parallel sessions in the desktop app](/docs/en/desktop#work-in-parallel-with-sessions). ## Learn more Each guide below covers setup and configuration for one approach: * [Create custom subagents](/docs/en/sub-agents): define reusable specialists and control which tools they can use. * [Manage agents with agent view](/docs/en/agent-view): dispatch sessions, watch their state, and attach when one needs you. * [Orchestrate agent teams](/docs/en/agent-teams): set up a lead and teammates, assign tasks, and review their work. * [Orchestrate dynamic workflows](/docs/en/workflows): run a bundled workflow or have Claude write one that runs many subagents and verifies their findings against each other. * [Run parallel sessions with worktrees](/docs/en/worktrees): start Claude in an isolated checkout, control what gets copied in, and clean up afterward.
amazon-bedrock First recorded · 587 lines, first recorded
# Claude Code on Amazon Bedrock ## Prerequisites ## Sign in with Bedrock ## Set up manually ### 1. Submit use case details ### 2. Configure AWS credentials #### Credential caching and resolution timeout #### Advanced credential configuration ##### Example configuration ##### Configuration settings explained ### 3. Configure Claude Code ### 4. Pin model versions #### Map each model version to an inference profile ## Startup model checks ## Cross-region inference profile prefixes ## IAM configuration ## 1M token context window ## Service tiers ## AWS Guardrails ## Use the Mantle endpoint ### Enable Mantle ### Select a Mantle model ### Run Mantle alongside the Invoke API ### Route Mantle through a gateway ### Mantle environment variables ## Troubleshooting ### Authentication loop with SSO and corporate proxies ### Region issues ### Streaming errors behind a gateway or proxy ### Zero token counts in /context ### Mantle endpoint errors ## Additional resources
The first capture of this source. The page was already there, and this is what it said.
# Claude Code on Amazon Bedrock
> Learn about configuring Claude Code through Amazon Bedrock, including setup, IAM configuration, and troubleshooting.
export const ContactSalesCard = ({surface}) => {
const utm = content => `utm_source=claude_code&utm_medium=docs&utm_content=${surface}_${content}`;
const iconArrowRight = (size = 13) => <svg width={size} height={size} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
<line x1="5" y1="12" x2="19" y2="12" />
<polyline points="12 5 19 12 12 19" />
</svg>;
const STYLES = `
.cc-cs {
--cs-slate: #141413;
--cs-clay: #d97757;
--cs-clay-deep: #c6613f;
--cs-gray-000: #ffffff;
--cs-gray-700: #3d3d3a;
--cs-border-default: rgba(31, 30, 29, 0.15);
font-family: inherit;
}
.dark .cc-cs {
--cs-slate: #f0eee6;
--cs-gray-000: #262624;
--cs-gray-700: #bfbdb4;
--cs-border-default: rgba(240, 238, 230, 0.14);
}
.cc-cs-card {
display: flex; align-items: center; justify-content: space-between;
gap: 16px; padding: 14px 16px; margin: 0;
background: var(--cs-gray-000); border: 0.5px solid var(--cs-border-default);
border-radius: 8px; flex-wrap: wrap;
}
.cc-cs-text { font-size: 13px; color: var(--cs-gray-700); line-height: 1.5; flex: 1; min-width: 240px; }
.cc-cs-text strong { font-weight: 550; color: var(--cs-slate); }
.cc-cs-actions { display: flex; align-items: center; gap: 8px; flex-shrink: 0; }
.cc-cs-btn-clay {
display: inline-flex; align-items: center; gap: 8px;
background: var(--cs-clay-deep); color: #fff; border: none;
border-radius: 8px; padding: 8px 14px;
font-size: 13px; font-weight: 500;
transition: background-color 0.15s; white-space: nowrap;
}
.cc-cs-btn-clay:hover { background: var(--cs-clay); }
.cc-cs-btn-ghost {
display: inline-flex; align-items: center; gap: 8px;
background: transparent; color: var(--cs-gray-700);
border: 0.5px solid var(--cs-border-default);
border-radius: 8px; padding: 8px 14px;
font-size: 13px; font-weight: 500;
}
.cc-cs-btn-ghost:hover { background: rgba(0, 0, 0, 0.04); }
.dark .cc-cs-btn-ghost:hover { background: rgba(255, 255, 255, 0.04); }
@media (max-width: 720px) {
.cc-cs-actions { width: 100%; }
}
`;
return <div className="cc-cs not-prose">
<style>{STYLES}</style>
<div className="cc-cs-card">
<div className="cc-cs-text">
<strong>Deploying Claude Code across your organization?</strong> Talk to sales about enterprise plans, SSO, and centralized billing.
</div>
<div className="cc-cs-actions">
<a href={`https://claude.com/pricing?${utm('view_plans')}#plans-business`} className="cc-cs-btn-ghost">
View plans
</a>
<a href={`https://claude.com/contact-sales?${utm('contact_sales')}`} className="cc-cs-btn-clay">
Contact sales {iconArrowRight()}
</a>
</div>
</div>
</div>;
};
<ContactSalesCard surface="bedrock" />
## Prerequisites
Before configuring Claude Code with Amazon Bedrock, ensure you have:
* An AWS account with Amazon Bedrock access enabled
* Access to desired Claude models (for example, Claude Sonnet 4.6) in Amazon Bedrock
* AWS CLI installed and configured (optional - only needed if you don't have another mechanism for getting credentials)
* Appropriate IAM permissions
To sign in with your own Amazon Bedrock credentials, follow [Sign in with Amazon Bedrock](#sign-in-with-bedrock) below. To deploy Claude Code across a team, use the [manual setup](#set-up-manually) steps and [pin your model versions](#4-pin-model-versions) before rolling out.
## Sign in with Bedrock
If you have AWS credentials and want to start using Claude Code through Amazon Bedrock, the login wizard walks you through it. You complete the AWS-side prerequisites once per account; the wizard handles the Claude Code side.
<Steps>
<Step title="Enable Anthropic models in your AWS account">
In the [Amazon Bedrock console](https://console.aws.amazon.com/bedrock/), open the Model catalog, select an Anthropic model, and submit the use case form. Access is granted immediately after submission. See [Submit use case details](#1-submit-use-case-details) for AWS Organizations and [IAM configuration](#iam-configuration) for the permissions your role needs.
</Step>
<Step title="Start Claude Code and choose Amazon Bedrock">
Run `claude`. At the login prompt, select **3rd-party platform**, then **Amazon Bedrock**. If you're already signed in and see the chat prompt instead, run `/setup-bedrock` to open the wizard. The command works when typed even though it isn't listed in the command menu until Bedrock is configured.
</Step>
<Step title="Follow the wizard prompts">
Choose how you authenticate to AWS: an AWS profile detected from your `~/.aws` directory, an Amazon Bedrock API key, an access key and secret, or credentials already in your environment. The wizard picks up your region, verifies which Claude models your account can invoke, and lets you pin them. It saves the result to the `env` block of your [user settings file](/docs/en/settings), so you don't need to export environment variables yourself.
</Step>
</Steps>
After you've signed in, run `/setup-bedrock` any time to reopen the wizard and change your credentials, region, or model pins. The model pin step starts from your currently pinned models. The wizard writes to `~/.claude/settings.json`, or to `$CLAUDE_CONFIG_DIR/settings.json` when [`CLAUDE_CONFIG_DIR`](/docs/en/env-vars#variables) is set.
## Set up manually
To configure Amazon Bedrock through environment variables instead of the wizard, for example in CI or a scripted enterprise rollout, follow the steps below.
### 1. Submit use case details
Before you invoke an Anthropic model for the first time, submit use case details. You do this once per AWS account.
1. Ensure you have the right IAM permissions described below
2. Navigate to the [Amazon Bedrock console](https://console.aws.amazon.com/bedrock/)
3. Select an Anthropic model from the **Model catalog**
4. Complete the use case form. Access is granted immediately after submission.
If you use AWS Organizations, you can submit the form once from the management account using the [`PutUseCaseForModelAccess` API](https://docs.aws.amazon.com/bedrock/latest/APIReference/API_PutUseCaseForModelAccess.html). This call requires the `bedrock:PutUseCaseForModelAccess` IAM permission. Approval extends to child accounts automatically.
### 2. Configure AWS credentials
Claude Code uses the default AWS SDK credential chain. Set up your credentials using one of these methods:
**Option A: AWS CLI configuration**
```bash theme={null}
aws configure
```
**Option B: Environment variables (access key)**
```bash theme={null}
export AWS_ACCESS_KEY_ID=your-access-key-id
export AWS_SECRET_ACCESS_KEY=your-secret-access-key
export AWS_SESSION_TOKEN=your-session-token
```
**Option C: Environment variables (SSO profile)**
Replace `your-profile-name` with the name of your AWS profile before running these commands.
```bash theme={null}
aws sso login --profile=your-profile-name
export AWS_PROFILE=your-profile-name
```
Claude Code requests role credentials from the IAM Identity Center region named by the profile's `sso_region`, which doesn't need to match the region you run Amazon Bedrock in. In v2.1.207, the Amazon Bedrock region overrode `sso_region`, so a profile whose IAM Identity Center instance is in a different region failed to authenticate with a `Session token not found or invalid` error.
**Option D: AWS Management Console credentials**
```bash theme={null}
aws login
```
[Learn more](https://docs.aws.amazon.com/signin/latest/userguide/command-line-sign-in.html) about `aws login`.
**Option E: Amazon Bedrock API keys**
```bash theme={null}
export AWS_BEARER_TOKEN_BEDROCK=your-bedrock-api-key
```
Amazon Bedrock API keys provide a simpler authentication method without needing full AWS credentials. [Learn more about Amazon Bedrock API keys](https://aws.amazon.com/blogs/machine-learning/accelerate-ai-development-with-amazon-bedrock-api-keys/).
#### Credential caching and resolution timeout
Claude Code resolves the AWS default credential provider chain once and keeps the resolved credentials in memory. It reuses them until five minutes before they expire, or for one hour when they carry no expiration, so an SSO-backed profile requests credentials from IAM Identity Center about once per credential lifetime. A credential error from the API clears the cache, and the retry resolves fresh credentials. Requires Claude Code v2.1.207 or later.
The cache covers every credential option above except an Amazon Bedrock API key, which doesn't use the provider chain. To resolve the chain on every request instead, set [`CLAUDE_CODE_SKIP_AWS_CRED_CACHE=1`](/docs/en/env-vars).
Each resolve of the chain times out after 60 seconds. If a step in the chain stalls, for example a `credential_process` helper that waits for input it can't receive, the request fails with [`AWS default-chain credential resolve timed out`](/docs/en/errors#aws-default-chain-credential-resolve-timed-out). If your chain runs an interactive sign-in that legitimately needs longer, such as browser-based SSO with MFA through a wrapper like `aws-vault`, raise the limit in milliseconds with [`CLAUDE_CODE_AWS_CHAIN_RESOLVE_TIMEOUT_MS`](/docs/en/env-vars). Before v2.1.207, a stalled credential resolution left the request waiting indefinitely.
#### Advanced credential configuration
Claude Code supports automatic credential refresh for AWS SSO and corporate identity providers. Add these settings to your Claude Code settings file (see [Settings](/docs/en/settings) for file locations).
These two settings have different trigger conditions:
* **`awsAuthRefresh`**: runs only when Claude Code detects that your AWS credentials are expired, either locally based on their timestamp or when the API returns a credential error, then retries the request with refreshed credentials.
* **`awsCredentialExport`**: runs at session start and on each credential reload, even when the credentials in your AWS default credential provider chain are still valid. Use this when your Amazon Bedrock account requires cross-account credentials that differ from the ones the default provider chain would resolve.
##### Example configuration
```json theme={null}
{
"awsAuthRefresh": "aws sso login --profile myprofile",
"env": {
"AWS_PROFILE": "myprofile"
}
}
```
##### Configuration settings explained
**`awsAuthRefresh`**: Use this for commands that modify the `.aws` directory, such as updating credentials, SSO cache, or config files. The command's output is displayed to the user, but interactive input isn't supported. This works well for browser-based SSO flows where the CLI displays a URL or code and you complete authentication in the browser.
**`awsCredentialExport`**: Only use this if you can't modify `.aws` and must directly return credentials. Output is captured silently and not shown to the user. The command must output JSON in this format:
```json theme={null}
{
"Credentials": {
"AccessKeyId": "value",
"SecretAccessKey": "value",
"SessionToken": "value",
"Expiration": "2026-01-01T00:00:00Z"
}
}
```
As of Claude Code v2.1.181, the flat output from `aws configure export-credentials --format process` is also accepted, with the same keys at the top level instead of nested under `Credentials`.
`Expiration` is optional. As of Claude Code v2.1.176, when the command returns a valid ISO 8601 `Expiration`, Claude Code caches the credentials until five minutes before that time. Without it, or on earlier versions, credentials are cached for one hour.
When you configure `awsCredentialExport` without `awsAuthRefresh`, Claude Code uses the exported credentials directly and doesn't re-resolve the AWS default credential provider chain at startup. Requires Claude Code v2.1.206 or later.
### 3. Configure Claude Code
Set the following environment variables to enable Amazon Bedrock:
```bash theme={null}
# Enable Bedrock integration
export CLAUDE_CODE_USE_BEDROCK=1
export AWS_REGION=us-east-1 # optional if your AWS profile already sets a region
# Optional: Override the AWS region for the small/fast model (Bedrock and Mantle).
# On Bedrock, has no effect without ANTHROPIC_DEFAULT_HAIKU_MODEL
# or the deprecated ANTHROPIC_SMALL_FAST_MODEL set.
export ANTHROPIC_SMALL_FAST_MODEL_AWS_REGION=us-west-2
# Optional: Override the Bedrock endpoint URL for custom endpoints or gateways
# export ANTHROPIC_BEDROCK_BASE_URL=https://bedrock-runtime.us-east-1.amazonaws.com
```
When enabling Amazon Bedrock for Claude Code, keep the following in mind:
* As of v2.1.172, you only need to set `AWS_REGION` to override your AWS profile's region or when your profile has no region. Claude Code resolves the region in this order:
* `AWS_REGION`
* `AWS_DEFAULT_REGION`
* the `region` set on your active AWS profile, read from the AWS shared credentials file first and then the shared config file, matching AWS SDK precedence
* `us-east-1`
The active profile is `AWS_PROFILE` if set, otherwise `default`. Set `AWS_SHARED_CREDENTIALS_FILE` or `AWS_CONFIG_FILE` to point at non-default file paths. Run `/status` to see the resolved region. When the region came from your AWS config files or the default fallback, `/status` also notes the source. On v2.1.171 and earlier, Claude Code does not read the AWS config files, so set `AWS_REGION` explicitly.
* When using Amazon Bedrock, the `/logout` command is unavailable since authentication is handled through AWS credentials.
* The WebSearch tool is not available on Amazon Bedrock. See [WebSearch tool behavior](/docs/en/tools-reference#websearch-tool-behavior).
* You can use settings files for environment variables like `AWS_PROFILE` that you don't want to leak to other processes. See [Settings](/docs/en/settings) for more information.
### 4. Pin model versions
<Warning>
Pin specific model versions when deploying to multiple users. Without pinning, model aliases such as `sonnet` and `opus` resolve to Claude Code's built-in default for Amazon Bedrock, which can lag the newest release and may not yet be available in your account. Claude Code [falls back](#startup-model-checks) to an earlier or lower-tier model at startup when the default is unavailable, but pinning lets you control when your users move to a new model.
</Warning>
Set these environment variables to specific Amazon Bedrock model IDs.
Without `ANTHROPIC_DEFAULT_OPUS_MODEL`, the `opus` alias on Amazon Bedrock resolves to Opus 5, and without `ANTHROPIC_DEFAULT_SONNET_MODEL`, the `sonnet` alias resolves to Sonnet 4.5. This example pins each alias to a specific version:
```bash theme={null}
export ANTHROPIC_DEFAULT_OPUS_MODEL='us.anthropic.claude-opus-4-8'
export ANTHROPIC_DEFAULT_SONNET_MODEL='us.anthropic.claude-sonnet-4-6'
export ANTHROPIC_DEFAULT_HAIKU_MODEL='us.anthropic.claude-haiku-4-5-20251001-v1:0'
```
These IDs use the `us.` cross-region inference profile prefix. If you use a different region prefix or application inference profiles, adjust accordingly. In AWS GovCloud regions, use the `us-gov.` prefix.
To keep the built-in default models and change only their preferred prefix, set [`ANTHROPIC_BEDROCK_REGION_PREFIX`](#cross-region-inference-profile-prefixes) instead of pinning. The difference shows in what the `opus` alias resolves to:
| You set | The `opus` alias resolves to |
| :------------------------------------------------------------ | :---------------------------------------------------------------------------- |
| `ANTHROPIC_DEFAULT_OPUS_MODEL='us.anthropic.claude-opus-4-8'` | `us.anthropic.claude-opus-4-8`, the exact ID you pinned |
| `ANTHROPIC_BEDROCK_REGION_PREFIX=eu` | `eu.anthropic.claude-opus-5`, the built-in default with your preferred prefix |
For current and legacy model IDs, see [Models overview](https://platform.claude.com/docs/en/about-claude/models/overview). For the full list of pinning environment variables, see [Model configuration](/docs/en/model-config#pin-models-for-third-party-deployments).
Claude Code uses these default models when no pinning variables are set:
| Model type | Default model |
| :--------------- | :---------------------------------------------------------------------------------------- |
| Primary model | Opus 5, for example `us.anthropic.claude-opus-5` in a `us-*` region |
| Small/fast model | Sonnet 4.5, for example `us.anthropic.claude-sonnet-4-5-20250929-v1:0` in a `us-*` region |
Background tasks such as session title generation use the small/fast model, normally a Haiku-class model. On Amazon Bedrock, Claude Code uses the default Sonnet model for background tasks because Haiku may not be enabled in every account or region. Two selections change which model carries them:
* When you select a primary model with `--model`, `ANTHROPIC_MODEL`, or the `model` setting, background tasks use that model. Setting `ANTHROPIC_DEFAULT_OPUS_MODEL` without `ANTHROPIC_DEFAULT_SONNET_MODEL` counts as a selection too, because the built-in Sonnet model may not be enabled in an account that steers its own Opus.
* To use Haiku for background tasks, set `ANTHROPIC_DEFAULT_HAIKU_MODEL` to a model ID that is available in your account.
<Warning>
Opus models have a higher per-token price than Sonnet models, so a deployment that doesn't pin a primary model is billed at the Opus rate once it updates to v2.1.207 or later. To keep Sonnet 4.5 as the primary model, set `ANTHROPIC_MODEL` to its full model ID. A deployment that steers the default with `ANTHROPIC_DEFAULT_SONNET_MODEL` and doesn't set `ANTHROPIC_DEFAULT_OPUS_MODEL` keeps its steered Sonnet model as the default.
</Warning>
On v2.1.207 through v2.1.218, the primary model on Amazon Bedrock defaulted to Opus 4.8 and the `opus` alias resolved to Opus 4.8. Before v2.1.207, the primary model defaulted to Sonnet 4.5, the `opus` alias resolved to Opus 4.6, and background tasks always used the primary model.
To customize models further, use one of these methods:
```bash theme={null}
# Using inference profile ID
Cut at 300 lines. The page has the rest.
analytics First recorded · 224 lines, first recorded
# Track team usage with analytics ## Access analytics for Team and Enterprise ### Enable contribution metrics ### Review summary metrics ### Explore the charts #### Track adoption #### Measure PRs per user #### View pull requests breakdown #### Find top contributors ### PR attribution #### Tagging criteria #### Attribution process #### Time window #### Excluded files #### Attribution notes ### Get the most from analytics #### Monitor adoption #### Measure ROI #### Identify power users #### Access data programmatically ## Access analytics for API customers ### View team insights ## Related resources
The first capture of this source. The page was already there, and this is what it said.
# Track team usage with analytics
> View Claude Code usage metrics, track adoption, and measure engineering velocity in the analytics dashboard.
Claude Code provides analytics dashboards to help organizations understand developer usage patterns, track contribution metrics, and measure how Claude Code impacts engineering velocity. Access the dashboard for your plan:
| Plan | Dashboard URL | Includes | Read more |
| ----------------------------- | -------------------------------------------------------------------------- | ------------------------------------------------------------------------------------- | ---------------------------------------------------- |
| Claude for Teams / Enterprise | [claude.ai/analytics/claude-code](https://claude.ai/analytics/claude-code) | Usage metrics, contribution metrics with GitHub integration, leaderboard, data export | [Details](#access-analytics-for-team-and-enterprise) |
| API (Claude Console) | [platform.claude.com/claude-code](https://platform.claude.com/claude-code) | Usage metrics, spend tracking, team insights | [Details](#access-analytics-for-api-customers) |
## Access analytics for Team and Enterprise
Navigate to [claude.ai/analytics/claude-code](https://claude.ai/analytics/claude-code). Admins and Owners can view the dashboard.
The Team and Enterprise dashboard includes:
* **Usage metrics**: lines of code accepted, suggestion accept rate, daily active users and sessions
* **Contribution metrics**: PRs and lines of code shipped with Claude Code assistance, with [GitHub integration](#enable-contribution-metrics)
* **Leaderboard**: top contributors ranked by Claude Code usage
* **Data export**: download contribution data as CSV for custom reporting
For per-user token counts and cost estimates, configure [OpenTelemetry export](/docs/en/monitoring-usage), or export the [spend report](https://support.claude.com/en/articles/12883420-view-usage-analytics-for-team-and-enterprise-plans) from your organization's analytics settings, which lists token usage and estimated usage-credit spend per user and per model.
### Enable contribution metrics
<Note>
Contribution metrics are in public beta and available on Claude for Teams and Claude for Enterprise plans. These metrics only cover users within your claude.ai organization. Usage through the Claude Console API or third-party integrations is not included.
</Note>
Usage and adoption data is available for all Claude for Teams and Claude for Enterprise accounts. Contribution metrics require additional setup to connect your GitHub organization.
You need the Owner role to configure analytics settings. A GitHub admin must install the GitHub app.
<Warning>
Contribution metrics are not available for organizations with [Zero Data Retention](/docs/en/zero-data-retention) enabled. The analytics dashboard will show usage metrics only.
</Warning>
<Steps>
<Step title="Install the GitHub app">
A GitHub admin installs the Claude GitHub app on your organization's GitHub account at [github.com/apps/claude](https://github.com/apps/claude).
</Step>
<Step title="Enable Claude Code analytics">
A Claude Owner navigates to [claude.ai/admin-settings/claude-code](https://claude.ai/admin-settings/claude-code) and enables the Claude Code analytics feature.
</Step>
<Step title="Enable GitHub analytics">
On the same page, enable the "GitHub analytics" toggle.
</Step>
<Step title="Authenticate with GitHub">
Complete the GitHub authentication flow and select which GitHub organizations to include in the analysis.
</Step>
</Steps>
Data typically appears within 24 hours after enabling, with daily updates. If no data appears, you may see one of these messages:
* **"GitHub app required"**: install the GitHub app to view contribution metrics
* **"Data processing in progress"**: check back in a few days and confirm the GitHub app is installed if data doesn't appear
Contribution metrics support GitHub Cloud and GitHub Enterprise Server.
### Review summary metrics
<Note>
These metrics are deliberately conservative and represent an underestimate of Claude Code's actual impact. Only lines and PRs where there is high confidence in Claude Code's involvement are counted.
</Note>
The dashboard displays these summary metrics at the top:
* **PRs with CC**: total count of merged pull requests that contain at least one line of code written with Claude Code
* **Lines of code with CC**: total lines of code across all merged PRs that were written with Claude Code assistance. Only "effective lines" are counted: lines with more than 3 characters after normalization, excluding empty lines and lines with only brackets or trivial punctuation.
* **PRs with Claude Code (%)**: percentage of all merged PRs that contain Claude Code-assisted code
* **Suggestion accept rate**: percentage of times users accept Claude Code's code editing suggestions, including Edit, Write, and NotebookEdit tool usage
* **Lines of code accepted**: total lines of code written by Claude Code that users have accepted in their sessions. This excludes rejected suggestions and does not track subsequent deletions.
### Explore the charts
The dashboard includes several charts to visualize trends over time.
#### Track adoption
The Adoption chart shows daily usage trends:
* **users**: daily active users
* **sessions**: number of active Claude Code sessions per day
#### Measure PRs per user
This chart displays individual developer activity over time:
* **PRs per user**: total number of PRs merged per day divided by daily active users
* **users**: daily active users
Use this to understand how individual productivity changes as Claude Code adoption increases.
#### View pull requests breakdown
The Pull requests chart shows a daily breakdown of merged PRs:
* **PRs with CC**: pull requests containing Claude Code-assisted code
* **PRs without CC**: pull requests without Claude Code-assisted code
Toggle to **Lines of code** view to see the same breakdown by lines of code rather than PR count.
#### Find top contributors
The Leaderboard shows the top 10 users ranked by contribution volume. Toggle between:
* **Pull requests**: shows PRs with Claude Code vs All PRs for each user
* **Lines of code**: shows lines with Claude Code vs All lines for each user
Click **Export all users** to download complete contribution data for all users as a CSV file. The export includes all users, not just the top 10 displayed.
### PR attribution
When contribution metrics are enabled, Claude Code analyzes merged pull requests to determine which code was written with Claude Code assistance. This is done by matching Claude Code session activity against the code in each PR.
#### Tagging criteria
PRs are tagged as "with Claude Code" if they contain at least one line of code written during a Claude Code session. The system uses conservative matching: only code where there is high confidence in Claude Code's involvement is counted as assisted.
#### Attribution process
When a pull request is merged:
1. Added lines are extracted from the PR diff
2. Claude Code sessions that edited matching files within a time window are identified
3. PR lines are matched against Claude Code output using multiple strategies
4. Metrics are calculated for AI-assisted lines and total lines
Before comparison, lines are normalized: whitespace is trimmed, multiple spaces are collapsed, quotes are standardized, and text is converted to lowercase.
Merged pull requests containing Claude Code-assisted lines are labeled as `claude-code-assisted` in GitHub.
#### Time window
Sessions from 21 days before to 2 days after the PR merge date are considered for attribution matching.
#### Excluded files
Certain files are automatically excluded from analysis because they are auto-generated:
* Lock files: package-lock.json, yarn.lock, Cargo.lock, and similar
* Generated code: Protobuf outputs, build artifacts, minified files
* Build directories: dist/, build/, node\_modules/, target/
* Test fixtures: snapshots, cassettes, mock data
* Lines over 1,000 characters, which are likely minified or generated
#### Attribution notes
Keep these additional details in mind when interpreting attribution data:
* Code substantially rewritten by developers, with more than 20% difference, is not attributed to Claude Code
* Sessions outside the 21-day window are not considered
* The algorithm does not consider the PR source or destination branch when performing attribution
### Get the most from analytics
Use contribution metrics to demonstrate ROI, identify adoption patterns, and find team members who can help others get started.
#### Monitor adoption
Track the Adoption chart and user counts to identify:
* Active users who can share best practices
* Overall adoption trends across your organization
* Dips in usage that may indicate friction or issues
#### Measure ROI
Contribution metrics help answer "Is this tool worth the investment?" with data from your own codebase:
* Track changes in PRs per user over time as adoption increases
* Compare PRs and lines of code shipped with vs. without Claude Code
* Use alongside [DORA metrics](https://dora.dev/), sprint velocity, or other engineering KPIs to understand changes from adopting Claude Code
#### Identify power users
The Leaderboard helps you find team members with high Claude Code adoption who can:
* Share prompting techniques and workflows with the team
* Provide feedback on what's working well
* Help onboard new users
#### Access data programmatically
On the Enterprise plan, the [Claude Enterprise Analytics API](https://platform.claude.com/docs/en/api/admin/analytics) returns per-user engagement, usage, and cost reports for your organization across Claude surfaces, including Claude Code. A Primary Owner creates a key with the `read:analytics` scope at [claude.ai/analytics/api-keys](https://claude.ai/analytics/api-keys). The API is not available on the Teams plan.
To query contribution data through GitHub instead, search for PRs labeled with `claude-code-assisted`.
## Access analytics for API customers
API customers using the Claude Console can access analytics at [platform.claude.com/claude-code](https://platform.claude.com/claude-code). You need the UsageView permission to access the dashboard, which is granted to Developer, Billing, Admin, Owner, and Primary Owner roles. To pull the same daily per-user metrics programmatically, use the [Claude Code Analytics API](https://platform.claude.com/docs/en/build-with-claude/claude-code-analytics-api) with an Admin API key.
<Note>
Contribution metrics with GitHub integration are not currently available for API customers. The Console dashboard shows usage and spend metrics only.
</Note>
The Console dashboard displays:
* **Lines of code accepted**: total lines of code written by Claude Code that users have accepted in their sessions. This excludes rejected suggestions and does not track subsequent deletions.
* **Suggestion accept rate**: percentage of times users accept code editing tool usage, including Edit, Write, and NotebookEdit tools.
* **Activity**: daily active users and sessions shown on a chart.
* **Spend**: daily API costs in dollars alongside user count.
### View team insights
The team insights table shows per-user metrics:
* **Members**: all users who have authenticated to Claude Code. API key users display by key identifier, OAuth users display by email address.
* **Spend this month**: per-user total API costs for the current month.
* **Lines this month**: per-user total of accepted code lines for the current month.
<Note>
Spend figures in the Console dashboard are estimates for analytics purposes. For actual costs, refer to your billing page.
</Note>
## Related resources
* [Monitoring with OpenTelemetry](/docs/en/monitoring-usage): export real-time metrics and events to your observability stack
* [Manage costs effectively](/docs/en/costs): set spend limits and optimize token usage
* [Permissions](/docs/en/permissions): configure roles and permissions
artifacts First recorded · 261 lines, first recorded
# Share session output as artifacts ## When to use an artifact ### What an artifact is not ## Create an artifact ## Update an artifact ## Share an artifact ### Let someone edit with you ## Pull live data with MCP connectors ### How connector calls work for viewers ### The page shows no live data for a viewer ## What you can build ### Walk through a change ### Compare alternatives ### Tune with interactive controls ### Bring the result back to your session ### Track work in progress ## Improve the visual design ## Page constraints ## Availability ## Disable artifacts ## Manage artifacts for your organization ### Enable or disable artifacts ### Control connector calls from artifacts ### Control public sharing ### Set a retention policy ### Review the audit log ### Allowlist the viewer domain ### List and delete artifacts with the Compliance API ## Related resources
The first capture of this source. The page was already there, and this is what it said.
# Share session output as artifacts
> Artifacts turn Claude Code's work into live, interactive pages on claude.ai that you can keep private, share with your organization, or publish to a public link.
<Note>
Artifacts are available on Pro, Max, Team, and Enterprise plans and require a session signed in with [`/login`](/docs/en/setup#authenticate). See [Availability](#availability) for the full set of requirements.
</Note>
An artifact is a live, interactive web page that Claude Code publishes from your session to a private URL on claude.ai. You open it in a browser, and it updates in place as the session continues. Share it from the page header when you want someone else to see it too.
<Frame>
<img src="https://mintcdn.com/claude-code/kaHIYYMIYMYPxQg9/images/artifacts-viewer.png?fit=max&auto=format&n=kaHIYYMIYMYPxQg9&q=85&s=dbfd671cdb0d15f49f808b9e89778fe1" alt="An artifact open in a browser at claude.ai/code/artifact. The viewer header shows the artifact title acme-funnel-fix, a Share button, and the author avatar. The Share menu is open with the Always share latest version toggle, a version picker reading Sharing version 2, an Everyone at Acme audience selector, and a Copy link button. Below the header, the artifact page shows two mobile mockups side by side, a funnel chart, and a row of metric cards." width="2511" height="1890" data-path="images/artifacts-viewer.png" />
</Frame>
## When to use an artifact
Use an artifact when terminal text is the wrong medium for what Claude produced: output that is easier to look at and interact with than to read line by line. Claude builds the page from anything your session can reach, including your codebase and data it pulls through your [connected tools](/docs/en/mcp), so the page can show things that would take paragraphs to describe. For example, ask Claude to:
* Walk a reviewer through a pull request with annotated diffs
* Render a dashboard from data the session already pulled
* Lay out several design or implementation options side by side
* Keep an investigation timeline that fills in while a long task runs
* Send a teammate a link instead of pasting output into Slack
* Publish a status board that [pulls fresh data through MCP connectors](#pull-live-data-with-mcp-connectors) each time someone opens it
See [What you can build](#what-you-can-build) for prompts that match these, and [Pull live data with MCP connectors](#pull-live-data-with-mcp-connectors) for the connector-backed board's prompt.
### What an artifact is not
An artifact is a capture of work, not an application. It is one self-contained page with no backend, so it can't store form input or serve multiple routes, and its only path to outside data when someone views it is [calling MCP connectors](#pull-live-data-with-mcp-connectors). For a hosted internal tool with a backend, deploy it on your own infrastructure instead. See [Page constraints](#page-constraints) for the full set of limits.
## Create an artifact
Claude may publish an artifact on its own when the output suits a page, or you can ask for one directly. To ask, name the feature or describe the visual output you want in plain language. A good candidate is anything easier to see than to read as text, such as an annotated diff, a chart, or a set of options to compare. The prompts below are two examples; see [What you can build](#what-you-can-build) for more patterns.
```text wrap theme={null}
Make an artifact that walks through this PR with the diff annotated inline.
```
```text wrap theme={null}
Build a dashboard artifact of last week's deploy failures by service and keep it updated as you investigate.
```
Claude writes the page to an HTML or Markdown file in your project, then publishes it. Before publishing a new artifact, Claude Code asks for permission; it might say something like `Claude wants to publish "Deploy failures by service" (deploy-failures.html) to a private page on claude.ai`. Republishing an artifact you have already approved does not prompt again.
Select **Yes** to publish. Claude prints the URL, and your browser opens to the new page. Press `Ctrl+]` at any time to reopen the most recent artifact from the terminal.
Claude picks the artifact's title and an emoji for its browser-tab icon. Both appear in your [gallery of artifacts](#share-an-artifact) on claude.ai and in shared links, so ask Claude to use a specific title or icon if you want one.
To stop the browser from opening automatically when a new artifact is published, set `CLAUDE_CODE_ARTIFACT_AUTO_OPEN=0` in your environment.
If Claude responds that it cannot publish, or writes a local HTML file without a link, the tool is not enabled for your session. Check the [Availability](#availability) requirements.
## Update an artifact
Ask Claude to revise the page, or let a long-running task republish as it makes progress. Claude edits the underlying file and publishes again to the same URL.
```text wrap theme={null}
Add a per-region breakdown below the summary chart and republish.
```
Anyone with the page open sees the update in place. Each publish becomes a version, and from the **Share** control in the page header you can choose which version viewers see.
To update an artifact from a different session, give Claude the artifact's URL and ask it to revise. Without the URL, a new session always creates a new artifact rather than updating an existing one.
```text wrap theme={null}
Update https://claude.ai/code/artifact/5fbea6f3-... with today's numbers.
```
## Share an artifact
A new artifact is visible only to you. To share it, open the artifact in your browser and use the **Share** control in the page header. The header names you as the artifact's author, so anyone you share it with can see who published the page. It also links to your gallery at [claude.ai/code/artifacts](https://claude.ai/code/artifacts), which lists every artifact you have created.
Who you can share with depends on your plan:
* **Within your organization**: on Team and Enterprise plans, grant access to specific people in your organization, or to everyone in it. Viewers sign in to claude.ai as members of your organization to see the page.
* **Publicly**: share a link that anyone on the internet can open, with no claude.ai sign-in required. On Pro and Max plans, a public link is the only way to share an artifact. On Team and Enterprise plans, public sharing is off until an Owner [enables it for the organization](#control-public-sharing).
### Let someone edit with you
People you share with are viewers by default: they see each version you publish but can't change the page. On Team and Enterprise plans, you can also make someone an editor. In the share dialog, add a person and switch their role from **viewer** to **editor**.
An editor publishes new versions the same way you [update the artifact from another session](#update-an-artifact): they give Claude the artifact's URL in their own session, and Claude pulls the current content and republishes with their changes. Everyone with the page open sees each update live.
## Pull live data with MCP connectors
An artifact can call [MCP connectors](/docs/en/mcp#use-mcp-servers-from-claude-ai) each time someone views it, so the page shows current data rather than a snapshot from the session that built it. Connector calls from artifacts are available on Pro, Max, Team, and Enterprise plans and require Claude Code v2.1.209 or later. On earlier versions, Claude publishes the page with whatever data the session gathered while building it.
To create a connector-backed page, name the connector and the data you want in your prompt:
```text wrap theme={null}
Build a dashboard artifact of our open pull requests that pulls the live list through my GitHub connector when the page loads.
```
Claude declares which connectors the page may call as part of publishing, and the page can't call connectors outside that declaration. Only connectors from your claude.ai account qualify: Claude names them in the declaration, and when someone views the page, each call [runs through the viewing account's own connection](#how-connector-calls-work-for-viewers) to that connector. Local MCP servers you configure in Claude Code, such as servers from `.mcp.json`, can supply data while Claude builds the page, but the published page can't call them.
The page fetches data when it loads and can refresh on an interval or when a viewer uses a refresh control on the page. Responses are cached in the viewer's browser, so a reopened page renders from the cached responses immediately, then updates with fresh results.
### How connector calls work for viewers
When a published page calls a connector, the call uses the account of the person viewing the page, not the account of the person who published it:
* **Each viewer uses their own connectors**: calls go through the viewing account's connected tools, so two people opening the same dashboard can see different data depending on what their accounts can access. The page never sees anyone's credentials; claude.ai makes the calls on the page's behalf.
* **Viewers approve access first**: claude.ai asks each viewer for permission before the page's first connector call. A viewer who declines, or who hasn't connected a connector the page uses, still sees the page without its live sections.
* **Actions use the viewer's account too**: a page can offer controls that invoke connector tools with side effects, such as posting a message or updating an issue. The action goes through the account of whoever selects the control.
When you plan to share a connector-backed page, ask Claude to include a fallback message in each live section that names the connector it needs. A viewer who's missing the connection then sees what to connect instead of an empty section.
An artifact that calls connectors can't be shared to a public link on any plan. On Team and Enterprise plans, you can keep it private or [share it within your organization](#share-an-artifact). On Pro and Max plans, where a public link is the only way to share, a connector-backed artifact stays private to you.
### The page shows no live data for a viewer
When a connector-backed page renders but its live sections stay empty for someone you shared it with, work through these causes:
* **The viewer hasn't connected the connector**: connectors are per-account, so each viewer needs their own connection to every connector the page calls. They can add one under **Settings > Connectors** on claude.ai, then reload the page.
* **The viewer declined the permission ask**: a denial lasts for the rest of that page load. Reloading the page brings the permission ask back.
* **Connector calls are turned off for the organization**: an Owner controls the [**Enable artifact connectors** toggle](#control-connector-calls-from-artifacts) in admin settings.
## What you can build
An artifact is a single HTML page, so anything you can express in HTML, CSS, and inline JavaScript is in scope. The patterns below come up most often.
### Walk through a change
Ask for a page that renders a diff or a design change with annotations beside the relevant lines, so reviewers can read your reasoning next to the code instead of reconstructing it from a description.
```text wrap theme={null}
Make an artifact that walks through this PR. Render the diff with margin annotations and color-code findings by severity.
```
### Compare alternatives
Ask for several variants on one page so you can evaluate them against each other. This works for layouts, copy, API shapes, or implementation plans.
```text wrap theme={null}
Make an artifact with four distinctly different layouts for the settings panel. Vary density and grouping, and lay them out as a grid with a one-line tradeoff under each.
```
### Tune with interactive controls
Ask for sliders, toggles, or input fields bound to whatever you are adjusting, so you can explore values directly instead of describing them.
```text wrap theme={null}
Build an artifact with sliders for the easing curve, duration, and delay so I can try values on this transition. Show the animation live as I move them.
```
### Bring the result back to your session
An artifact can act as a lightweight editor for a decision you then hand back to Claude. Ask for an export control that produces text you can paste into the terminal, so the result of interacting with the page flows back into the session instead of staying on the page.
```text wrap theme={null}
Make a triage board artifact with each open issue as a draggable card across Now, Next, Later, and Cut columns. Add a "Copy as prompt" button that gives me the final ordering to paste back here.
```
### Track work in progress
Ask Claude to keep an artifact current while a long task runs, so anyone with the link can follow along without reading the terminal.
```text wrap theme={null}
Turn this migration plan into a checklist artifact. Check items off as you complete them and add a note for anything you skip.
```
## Improve the visual design
Claude applies a built-in design skill when it builds an artifact, so pages get a deliberate palette, typography, and layout without extra prompting. Requires Claude Code v2.1.182 or later. That skill also looks for an existing design system in your project before choosing its own. Design tokens are the named color, typography, and spacing values your design system reuses. To keep artifacts consistent with your product's branding, record them where Claude can find them, such as the project's [CLAUDE.md](/docs/en/memory) or a theme file in your repository:
```markdown theme={null}
## Design system
- Colors: primary #1a4d8f, accent #f59e0b, surface #f8fafc
- Typography: Inter for body, JetBrains Mono for code
- Spacing: 8px scale, 6px border radius
```
Claude treats your design system as higher precedence than its own choices, and your prompt as higher precedence than both. The heading and format above are an example; any clear list of colors, fonts, and spacing works.
## Page constraints
Each artifact is one self-contained page. Claude Code wraps the file you publish in an HTML document shell and serves it under a strict Content Security Policy (CSP), which shapes what the page can do.
| Constraint | Effect |
| :------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| No external requests | The CSP blocks scripts, stylesheets, fonts, and images loaded from any other host, along with `fetch`, XHR, and WebSocket calls. Claude inlines CSS and JavaScript and embeds images as data URIs so the page renders without any external request. [Connector calls](#pull-live-data-with-mcp-connectors) are the exception: the page hands them to claude.ai, which makes the network call itself. |
| No backend | An artifact is a static page. It can't store data submitted through a form or authenticate viewers itself. Its only way to fetch data when someone views it is [calling MCP connectors](#pull-live-data-with-mcp-connectors), not an API of its own. |
| Single page | Relative links do not resolve, because nothing is deployed alongside the page. For multi-section content, Claude uses in-page anchors rather than separate files. |
| Source file types | The published file must be `.html`, `.htm`, or `.md`. Markdown files render as styled HTML. |
| Rendered size | The rendered page must be 16 MiB or smaller. Large embedded images are the usual cause when a publish fails for size. |
Generating an artifact uses output tokens like any other response, and a styled page is more token-intensive than the same content as terminal text. Inline CSS, JavaScript for interactive controls, and especially images embedded as data URIs are the main contributors. To reduce an artifact's token cost:
* Prefer SVG, or HTML and CSS, for diagrams over embedded raster images
* Omit interactivity you do not need
* Have the page summarize large datasets rather than inline them in full
## Availability
Artifacts require every condition below. When one is not met, Claude writes a local HTML file or says it cannot publish instead.
| Requirement | Available when |
| :------------------ | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Plan | Pro, Max, Team, or Enterprise. On Pro and Max plans, artifacts are private to you until you share them, and no admin management applies. On Team plans, artifacts are on by default. On Enterprise plans, an Owner [enables them](#manage-artifacts-for-your-organization) in claude.ai admin settings. |
| Authentication | The session is backed by a claude.ai account: sign in with `/login` in the CLI or desktop app. Claude Tag sessions are signed in through the agent's identity, so no step is needed there. Sessions using an API key, [gateway token](/docs/en/llm-gateway), or cloud-provider credential cannot publish. |
| Model provider | Anthropic API. Not available on [Amazon Bedrock](/docs/en/amazon-bedrock), [Google Cloud's Agent Platform](/docs/en/google-vertex-ai), or [Microsoft Foundry](/docs/en/microsoft-foundry). |
| Organization policy | Customer-managed encryption keys (CMEK), HIPAA, and [Zero Data Retention](/docs/en/zero-data-retention) are not enabled for the organization. |
| Surface | Claude Code CLI version 2.1.183 or later, or the Claude desktop app version 1.13576.0 or later. [Claude Tag](https://claude.com/docs/claude-tag/overview) sessions can also publish artifacts when both Claude Tag and artifacts are enabled for the organization. Off by default in [Agent SDK](/docs/en/agent-sdk/overview), GitHub Action, and MCP-server contexts, and when [`CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC`](/docs/en/env-vars) is set. |
## Disable artifacts
To turn artifacts off for your own sessions regardless of your organization's setting, use any of:
| Method | Setting |
| :----------------------------------- | :----------------------------------- |
| [Settings file](/docs/en/settings) | `"disableArtifact": true` |
| [Environment variable](/docs/en/env-vars) | `CLAUDE_CODE_DISABLE_ARTIFACT=1` |
| [Permission rule](/docs/en/permissions) | Add `Artifact` to `permissions.deny` |
## Manage artifacts for your organization
Owners on Team and Enterprise plans control artifacts from [claude.ai admin settings](https://claude.ai/admin-settings/claude-code). Artifact content is stored on Anthropic-operated infrastructure and is visible only to authenticated members of the publishing organization, unless the artifact is [shared publicly](#control-public-sharing).
### Enable or disable artifacts
To enable or disable artifacts for the whole organization, go to [**Settings > Claude Code > Capabilities**](https://claude.ai/admin-settings/claude-code) and use the **Artifacts** toggle. On Enterprise plans with role-based access control, you can additionally scope artifacts to specific roles: go to [**Settings > Roles**](https://claude.ai/admin-settings/roles), edit a role, and set the **Artifacts** permission under the **Claude Code** group.
### Control connector calls from artifacts
[Connector calls from artifacts](#pull-live-data-with-mcp-connectors) have their own toggle, separate from the **Artifacts** toggle that turns artifacts on or off. Go to [**Settings > Capabilities**](https://claude.ai/admin-settings/capabilities) and use the **Enable artifact connectors** toggle. The same toggle governs connector calls from artifacts created in claude.ai conversations, which is why it sits under **Settings > Capabilities** rather than **Settings > Claude Code**.
### Control public sharing
Public sharing is off by default on Team and Enterprise plans, so members can share artifacts only within the organization until an Owner turns it on. To let members publish artifacts to public links that anyone can view without signing in, go to **Settings > Claude Code > Capabilities** and turn on **External sharing** under the **Artifacts** toggle. Turning it back off blocks access through existing public links without changing each artifact's audience; access resumes if you re-enable it.
### Set a retention policy
To set how long artifacts are kept before automatic deletion, go to [**Settings > Data & privacy controls**](https://claude.ai/admin-settings/data-privacy-controls). You can set separate retention periods for artifacts that are still private to their author and artifacts that have been shared.
### Review the audit log
Publishing, sharing, and deleting an artifact each appear in your organization's audit log under the `claude_artifact_*` event types, the same family used for artifacts created in claude.ai conversations.
### Allowlist the viewer domain
The viewer on claude.ai loads each artifact from a sandboxed `*.claudeusercontent.com` origin. If your organization restricts outbound network access, add that domain to your allowlist alongside `claude.ai`. See [Network access requirements](/docs/en/network-config#network-access-requirements) for the full list.
### List and delete artifacts with the Compliance API
The [Compliance API](https://docs.claude.com/en/api/compliance) provides endpoints to list an organization's artifacts, retrieve a specific version's content, and delete an artifact:
| Method | Endpoint |
| :------- | :------------------------------------------------------------------ |
| `GET` | `/v1/compliance/code/artifacts` |
| `GET` | `/v1/compliance/code/artifacts/{artifact_id}/versions/{version_id}` |
| `DELETE` | `/v1/compliance/code/artifacts/{artifact_id}` |
For the request and response schemas, see the [Compliance API reference](https://docs.claude.com/en/api/compliance/code/artifacts).
## Related resources
* Browse [prompting patterns and workflows](/docs/en/prompt-library) that pair with artifacts
* Turn an artifact prompt you reuse into a [skill](/docs/en/skills) so you can invoke it as a command
* [Connect MCP servers](/docs/en/mcp) so Claude can pull data into an artifact while it builds the page
authentication First recorded · 226 lines, first recorded
# Authentication ## Log in to Claude Code ## Set up team authentication ### Claude for Teams or Enterprise ### Claude Console authentication ### Cloud provider authentication ### Restrict login to your organization ## Credential management ### Renew an expiring login ### Authentication precedence #### Anthropic profiles and federation credentials ### Generate a long-lived token
The first capture of this source. The page was already there, and this is what it said.
# Authentication
> Log in to Claude Code and configure authentication for individuals, teams, and organizations.
Claude Code supports multiple authentication methods depending on your setup. Individual users can log in with a Claude.ai account, while teams can use Claude for Teams or Enterprise, the Claude Console, or a cloud provider like Amazon Bedrock, Google Cloud's Agent Platform, or Microsoft Foundry.
## Log in to Claude Code
After [installing Claude Code](/docs/en/setup#install-claude-code), run `claude` in your terminal. On first launch, Claude Code opens a browser window for you to log in. If you've set the `ANTHROPIC_API_KEY` environment variable, Claude Code skips the login prompt and asks you to approve the key instead.
If the browser doesn't open automatically, press `c` to copy the login URL to your clipboard, then paste it into your browser.
If your browser shows a login code instead of redirecting back after you sign in, paste it into the terminal at the `Paste code here if prompted` prompt. This happens when the browser can't reach Claude Code's local callback server, which is common in WSL2, SSH sessions, and containers.
When login completes, the terminal shows `Login successful` and prompts you to press `Enter` to continue.
You can authenticate with any of these account types:
* **Claude Pro or Max subscription**: log in with your Claude.ai account. Subscribe at [claude.com/pricing](https://claude.com/pricing?utm_source=claude_code\&utm_medium=docs\&utm_content=authentication_pro_max).
* **Claude for Teams or Enterprise**: log in with the Claude.ai account your team admin invited you to.
* **Claude Console**: log in with your Console credentials. Your admin must have [invited you](#claude-console-authentication) first.
* **Cloud providers**: if your organization uses [Amazon Bedrock](/docs/en/amazon-bedrock), [Google Cloud's Agent Platform](/docs/en/google-vertex-ai), or [Microsoft Foundry](/docs/en/microsoft-foundry), set the required environment variables before running `claude`, or select **3rd-party platform** at the login prompt, which launches an interactive setup wizard for Bedrock and Vertex AI. No browser login is needed.
* **Cloud gateway**: if your organization runs a self-hosted [Claude apps gateway](/docs/en/claude-apps-gateway), sign in with corporate SSO through `/login`. The gateway-issued token is the session's only credential.
Admins can direct which login method developers use and require claude.ai logins to belong to a specific organization; see [Restrict login to your organization](#restrict-login-to-your-organization).
To log out and re-authenticate, type `/logout` at the Claude Code prompt. Logging out also resets your first-launch setup state, so the next time you run `claude` it walks you through login and setup again.
If you're having trouble logging in, see [authentication troubleshooting](/docs/en/troubleshoot-install#login-and-authentication).
## Set up team authentication
For teams and organizations, you can configure Claude Code access in one of these ways:
* [Claude for Teams or Enterprise](#claude-for-teams-or-enterprise), recommended for most teams
* [Claude Console](#claude-console-authentication)
* [Claude apps gateway](/docs/en/claude-apps-gateway), a self-hosted gateway that signs developers in with your IdP and routes inference to the cloud provider you configure
* [Amazon Bedrock](/docs/en/amazon-bedrock)
* [Google Cloud's Agent Platform](/docs/en/google-vertex-ai)
* [Microsoft Foundry](/docs/en/microsoft-foundry)
### Claude for Teams or Enterprise
[Claude for Teams](https://claude.com/pricing?utm_source=claude_code\&utm_medium=docs\&utm_content=authentication_teams#team-&-enterprise) and [Claude for Enterprise](https://anthropic.com/contact-sales?utm_source=claude_code\&utm_medium=docs\&utm_content=authentication_enterprise) provide the best experience for organizations using Claude Code. Team members get access to both Claude Code and Claude on the web with centralized billing and team management.
* **Claude for Teams**: self-service plan with collaboration features, admin tools, and billing management. Best for smaller teams.
* **Claude for Enterprise**: adds SSO, domain capture, role-based permissions, compliance API, and managed policy settings for organization-wide Claude Code configurations. Best for larger organizations with security and compliance requirements.
<Steps>
<Step title="Subscribe">
Subscribe to [Claude for Teams](https://claude.com/pricing?utm_source=claude_code\&utm_medium=docs\&utm_content=authentication_teams_step#team-&-enterprise) or contact sales for [Claude for Enterprise](https://anthropic.com/contact-sales?utm_source=claude_code\&utm_medium=docs\&utm_content=authentication_enterprise_step).
</Step>
<Step title="Invite team members">
Invite team members from the admin dashboard.
</Step>
<Step title="Install and log in">
Team members install Claude Code and log in with their Claude.ai accounts.
</Step>
</Steps>
### Claude Console authentication
For organizations that prefer API-based billing, you can set up access through the Claude Console.
<Steps>
<Step title="Create or use a Console account">
Use your existing Claude Console account or create a new one.
</Step>
<Step title="Add users">
You can add users through either method:
* Bulk invite users from within the Console: Settings -> Members -> Invite
* [Set up SSO](https://support.claude.com/en/articles/13132885-setting-up-single-sign-on-sso)
</Step>
<Step title="Assign roles">
When inviting users, assign one of:
* **Claude Code** role: users can only create Claude Code API keys
* **Developer** role: users can create any kind of API key
</Step>
<Step title="Users complete setup">
Each invited user needs to:
* Accept the Console invite
* [Check system requirements](/docs/en/setup#system-requirements)
* [Install Claude Code](/docs/en/setup#install-claude-code)
* Log in with Console account credentials
</Step>
</Steps>
### Cloud provider authentication
For teams using Amazon Bedrock, Google Cloud's Agent Platform, or Microsoft Foundry:
<Steps>
<Step title="Follow provider setup">
Follow the [Amazon Bedrock docs](/docs/en/amazon-bedrock), [Google Cloud's Agent Platform docs](/docs/en/google-vertex-ai), or [Microsoft Foundry docs](/docs/en/microsoft-foundry).
</Step>
<Step title="Distribute configuration">
Distribute the environment variables and instructions for generating cloud credentials to your users. Read more about how to [manage configuration here](/docs/en/settings).
</Step>
<Step title="Install Claude Code">
Users can [install Claude Code](/docs/en/setup#install-claude-code).
</Step>
</Steps>
### Restrict login to your organization
To require that developers' claude.ai logins belong to a specific Anthropic organization, set [`forceLoginMethod` and `forceLoginOrgUUID`](/docs/en/settings#available-settings) in [managed settings](/docs/en/settings#settings-files). Set `forceLoginOrgUUID` to your organization ID, shown in [claude.ai admin settings](https://claude.ai/admin-settings/organization) for Claude for Teams or Enterprise organizations. Claude Code reports an error for a claude.ai login to any other organization and exits at startup if the claude.ai credential in use belongs to an organization that isn't listed.
For Claude Console logins, Claude Code uses `forceLoginOrgUUID` only to pre-select the organization on the Console sign-in page when you set it to a single Console organization ID, shown at [platform.claude.com/settings/organization](https://platform.claude.com/settings/organization). It doesn't check which organization the resulting Console credential belongs to, at login or at startup, and a developer who logged in with a Console account before you deployed the keys stays logged in. To direct developers to claude.ai sign-in instead, set `forceLoginMethod` to `"claudeai"`.
Developers can log in from several paths: the terminal `/login` flow, the [VS Code extension](/docs/en/vs-code), the Agent SDK, `claude setup-token`, `/install-github-app`, and [gateway](/docs/en/claude-apps-gateway) sign-in for organizations that route through a cloud gateway. On Claude Code v2.1.212 or later, every path applies `forceLoginMethod`; before v2.1.212, only terminal logins applied either key. In the interactive `/login` flow, Claude Code pre-selects a `claudeai` or `console` method without enforcing it, so even with `forceLoginMethod` set to `"claudeai"`, a developer can still complete a Console login there. The paths differ on `forceLoginOrgUUID`:
* **Terminal, VS Code extension, and Agent SDK logins**: verify `forceLoginOrgUUID` for claude.ai account logins
* **`claude setup-token` and `/install-github-app`**: enforce only `forceLoginMethod`, so they can mint a token in a different organization
* **[Gateway](/docs/en/claude-apps-gateway) sign-in**: selected by `forceLoginMethod: "gateway"` rather than restricted by it, and doesn't authenticate against an Anthropic organization, so `forceLoginOrgUUID` doesn't apply; use your gateway identity provider to restrict access
Deploy the keys through your device management tooling. [Server-managed settings](/docs/en/server-managed-settings) reach only accounts that are already authenticated into your organization, so they can't redirect a developer's first login. If your organization distributes server-managed settings as well, set the keys in both places: managed-settings sources [don't merge](/docs/en/server-managed-settings#settings-precedence), and cached server-managed settings replace the device-managed file, apart from two kinds of keys that still fill in from a losing source:
* **The `env` block**: [merges per key](/docs/en/server-managed-settings#per-key-exceptions-across-managed-sources) in Claude Code v2.1.223 or later
* **The [cross-source lock keys](/docs/en/server-managed-settings#per-key-exceptions-across-managed-sources)**: honored from any admin source
`forceLoginMethod` and `forceLoginOrgUUID` are neither, so keep them in both places.
The keys also decide whether a session that doesn't use a login credential can start. See [`forceLoginOrgUUID`](/docs/en/settings#available-settings) in the settings reference for the full behavior.
* **`ANTHROPIC_API_KEY`, `ANTHROPIC_AUTH_TOKEN`, or `apiKeyHelper`**: blocked at startup, since organization membership can't be verified for an environment credential. Before v2.1.146, the pin applied only to the login flow and didn't block API-key credentials
* **Cloud provider sessions such as Amazon Bedrock**: not blocked, because they authenticate against your cloud provider. Restrict those through your cloud IAM policies
* **[Anthropic profile or federation credentials](#anthropic-profiles-and-federation-credentials)**: not blocked, and the keys don't check which organization the profile belongs to
## Credential management
Claude Code securely manages your authentication credentials:
* **Storage location**:
* On macOS, credentials are stored in the encrypted macOS Keychain.
* On Linux, credentials are stored in `~/.claude/.credentials.json` with file mode `0600`.
* On Windows, credentials are stored in `%USERPROFILE%\.claude\.credentials.json` and inherit the access controls of your user profile directory, which restricts the file to your user account by default.
* If you've set the `CLAUDE_CONFIG_DIR` environment variable on Linux or Windows, the `.credentials.json` file lives under that directory instead.
* Claude Code manages `.credentials.json` through `/login` and `/logout`. To route requests through a custom API endpoint, set the [`ANTHROPIC_BASE_URL`](/docs/en/env-vars) environment variable instead.
* **Supported authentication types**: Claude.ai credentials, Claude API credentials, Microsoft Foundry Auth, Bedrock Auth, Vertex Auth, Anthropic profile and [Workload Identity Federation](https://platform.claude.com/docs/en/manage-claude/workload-identity-federation) credentials, and [Claude apps gateway](/docs/en/claude-apps-gateway) session tokens.
* **Custom credential scripts**: configure the [`apiKeyHelper`](/docs/en/settings#available-settings) setting to run a shell script that returns an API key.
* **Refresh intervals**: by default, `apiKeyHelper` is called after 5 minutes or on HTTP 401 response. Set `CLAUDE_CODE_API_KEY_HELPER_TTL_MS` environment variable for custom refresh intervals.
* **Slow helper notice**: if `apiKeyHelper` takes longer than 10 seconds to return a key, Claude Code displays a warning notice in the prompt bar showing the elapsed time. If you see this notice regularly, check whether your credential script can be optimized.
* **Helper failures**: when the script exits with an error, times out, or prints nothing, requests fail with [`Your apiKeyHelper script is failing`](/docs/en/errors#your-apikeyhelper-script-is-failing) within three attempts. Before v2.1.208, helper failures surfaced as a generic 401 after about ten silent retries.
`apiKeyHelper`, `ANTHROPIC_API_KEY`, and `ANTHROPIC_AUTH_TOKEN` apply to the CLI and the surfaces that wrap it, including the VS Code extension, the Agent SDK, and GitHub Actions. Claude Desktop and cloud sessions do not call `apiKeyHelper` or read these environment variables: they use OAuth, except desktop sessions running a [third-party inference configuration](/docs/en/llm-gateway-connect#desktop-app), which authenticate with that configuration's credential.
### Renew an expiring login
When the login you created with `/login` is within three days of expiring, Claude Code shows a warning at startup: `Your login expires in 3 days · run /login to renew`. Requires Claude Code v2.1.203 or later. Before v2.1.217, the warning appeared five days out.
Run `/login` to renew. The warning is informational and never blocks a request: authentication keeps working until the login actually expires. The login lifetime itself is unchanged; the advance warning is what v2.1.203 adds.
Once the stored login expires and can't be refreshed, each request fails with [`Login expired · Please run /login`](/docs/en/errors#login-expired) until you sign in again. Before v2.1.206, an expired login surfaced as a model error instead.
You can check for this state before a request fails: [`/status`](/docs/en/commands) shows a `Login` row reading `Expired — log in again`, plus the organization and email it has saved for the expired login. The row appears only when the saved claude.ai or Claude Console login is the active credential. The row requires Claude Code v2.1.210 or later.
The warning appears only when a claude.ai or Claude Console login is the active credential, and not when a cloud provider, `ANTHROPIC_API_KEY`, `ANTHROPIC_AUTH_TOKEN`, or `apiKeyHelper` supplies the credential.
Renewing early matters most for sessions that run unattended. A [background session in agent view](/docs/en/agent-view) or a [Remote Control](/docs/en/remote-control) session that outlives the login stops making progress once the credential expires and can't recover until you sign in again.
### Authentication precedence
When multiple credentials are present, Claude Code chooses one in this order:
1. Cloud provider credentials, when `CLAUDE_CODE_USE_BEDROCK`, `CLAUDE_CODE_USE_VERTEX`, or `CLAUDE_CODE_USE_FOUNDRY` is set. See [third-party integrations](/docs/en/third-party-integrations) for setup.
2. `ANTHROPIC_AUTH_TOKEN` environment variable. Sent as the `Authorization: Bearer` header. Use this when routing through an [LLM gateway or proxy](/docs/en/llm-gateway) that authenticates with bearer tokens rather than Anthropic API keys.
3. `ANTHROPIC_API_KEY` environment variable. Sent as the `X-Api-Key` header. Use this for direct Anthropic API access with a key from the [Claude Console](https://platform.claude.com). In interactive mode, you are prompted once to approve or decline the key, and your choice is remembered. To change it later, use the "Use custom API key" toggle in `/config`. The toggle only appears while `ANTHROPIC_API_KEY` is set in your environment. In non-interactive mode (`-p`), the key is always used when present.
4. [`apiKeyHelper`](/docs/en/settings#available-settings) script output. Use this for dynamic or rotating credentials, such as short-lived tokens fetched from a vault.
5. `CLAUDE_CODE_OAUTH_TOKEN` environment variable. A long-lived OAuth token generated by [`claude setup-token`](#generate-a-long-lived-token). Use this for CI pipelines and scripts where browser login isn't available. If you run `/login` while the variable is set, Claude Code switches the current session to the new login, but reads the variable again in every new session until you remove it from your shell profile or the `env` block of a [settings file](/docs/en/settings).
6. Anthropic profile and federation credentials, the credentials that the `ant` CLI and Workload Identity Federation use. A profile that `ant auth login` wrote ranks here only when you name it in `ANTHROPIC_PROFILE`; otherwise it ranks below `/login`. See [Anthropic profiles and federation credentials](#anthropic-profiles-and-federation-credentials).
7. Subscription OAuth credentials from `/login`. This is the default for Claude Pro, Max, Team, and Enterprise users.
A signed-in [Claude apps gateway](/docs/en/claude-apps-gateway) session sits outside this list: it is a provider selection like Amazon Bedrock or Google Cloud's Agent Platform, and it outranks them. When a gateway session exists, the CLI authenticates with the gateway token even if `CLAUDE_CODE_USE_BEDROCK`, `CLAUDE_CODE_USE_VERTEX`, or `CLAUDE_CODE_USE_FOUNDRY` is set, and credential sources above such as the bearer token, API key, `apiKeyHelper`, and profiles are not used.
If you have an active Claude subscription but also have `ANTHROPIC_API_KEY` set in your environment, the API key takes precedence once approved. This can cause authentication failures if the key belongs to a disabled or expired organization. Run `unset ANTHROPIC_API_KEY` to fall back to your subscription, and check `/status` to confirm which method is active. The `Login method` row shows your subscription account, and an `API key` row appears when an API key is in use.
[Claude Code on the Web](/docs/en/claude-code-on-the-web) always uses your subscription credentials. If you set `ANTHROPIC_API_KEY` or `ANTHROPIC_AUTH_TOKEN` in the sandbox environment, it doesn't override your subscription credentials.
#### Anthropic profiles and federation credentials
A profile is a named credential configuration file in your [Anthropic configuration directory](https://platform.claude.com/docs/en/manage-claude/wif-reference#configuration-directory), by default `~/.config/anthropic` on macOS and Linux or `%APPDATA%\Anthropic` on Windows. A profile's auth mode is `oidc_federation` when you set it up for [Workload Identity Federation (WIF)](https://platform.claude.com/docs/en/manage-claude/workload-identity-federation) or `user_oauth` when [`ant auth login`](https://platform.claude.com/docs/en/cli-sdks-libraries/cli/authentication) wrote it. Claude Code doesn't read profiles or federation variables in [bare mode](/docs/en/headless#start-faster-with-bare-mode), in Claude Desktop, or in cloud sessions.
Claude Code checks three sources in this order and stops at the first one that is set. The table shows what sets each source and where it ranks against your `/login` credential.
| Source | Set by | Rank against `/login` |
| :------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :-------------------------------------------------------------------------------------------------------------------- |
| Named profile | `ANTHROPIC_PROFILE` | Above, whichever auth mode the profile has |
| Federation variables | `ANTHROPIC_FEDERATION_RULE_ID` and `ANTHROPIC_ORGANIZATION_ID`, both set | Above |
| Active profile | The [`active_config` file](https://platform.claude.com/docs/en/manage-claude/wif-reference#active-profile) in your configuration directory, or a profile named `default` | Above when its auth mode is `oidc_federation`; below a working `/login` credential when its auth mode is `user_oauth` |
The `user_oauth` rule keeps a leftover `ant auth login` profile from moving your requests off the account you signed in to with `/login`. For the federation variables, Claude Code also reads the other variables in the [WIF reference](https://platform.claude.com/docs/en/manage-claude/wif-reference#environment-variables), such as `ANTHROPIC_IDENTITY_TOKEN_FILE`, when it exchanges your identity token. For the profile file format, see the [WIF reference](https://platform.claude.com/docs/en/manage-claude/wif-reference#profile-configuration-file).
To confirm which source Claude Code chose, run `/status`: a `Profile` row names the source in place of the `Login method` row. If you start Claude Code with `--debug`, it also writes a `Using Anthropic profile auth` line with the source name to the debug log at `~/.claude/debug/<session-id>.txt`. When Claude Code passes over a `user_oauth` active profile because you have a working `/login` credential, it writes a warning to the debug log saying it's using the claude.ai login instead. When a `user_oauth` profile's login has expired and Claude Code can't renew it, requests fail with [Anthropic profile login expired](/docs/en/errors#anthropic-profile-login-expired).
Features that need your claude.ai login, such as [claude.ai connectors](/docs/en/mcp#use-mcp-servers-from-claude-ai) and [`/schedule`](/docs/en/routines), aren't available while one of these sources is selected. To stop Claude Code from selecting a source:
* **Named profile or federation variables**: unset `ANTHROPIC_PROFILE`, or unset either federation variable
* **Active profile**: run `ant auth logout` for a `user_oauth` profile, or delete the profile's file from `configs/` in your configuration directory for either auth mode
### Generate a long-lived token
For CI pipelines, scripts, or other environments where interactive browser login isn't available, generate a one-year OAuth token with `claude setup-token`:
```bash theme={null}
claude setup-token
```
The command opens the same browser authorization flow as `/login`, and the token prints to the terminal after you approve access in the browser. It does not save the token anywhere; copy it and set it as the `CLAUDE_CODE_OAUTH_TOKEN` environment variable wherever you want to authenticate:
```bash theme={null}
export CLAUDE_CODE_OAUTH_TOKEN=your-token
```
This token authenticates with your Claude subscription and requires a Pro, Max, Team, or Enterprise plan. It can only make model requests, so it can't establish [Remote Control](/docs/en/remote-control) sessions or fetch [claude.ai connectors](/docs/en/mcp#use-mcp-servers-from-claude-ai). MCP servers you configure locally still work.
[Bare mode](/docs/en/headless#start-faster-with-bare-mode) does not read `CLAUDE_CODE_OAUTH_TOKEN`. If your script passes `--bare`, authenticate with `ANTHROPIC_API_KEY` or an `apiKeyHelper` instead.
auto-mode-config First recorded · 325 lines, first recorded
# Configure auto mode ## Common boundaries ### Add a human checkpoint ## Where the classifier reads configuration ## Define trusted infrastructure ## Override the block and allow rules ## Route all shell commands through the classifier ## Inspect the defaults and your effective config ## Review denials ### Fix a denial with an allow rule, an environment entry, or a retry ### Fix repeated denials ## See also
The first capture of this source. The page was already there, and this is what it said.
# Configure auto mode
> Tell the auto mode classifier which repos, buckets, and domains your organization trusts. Set environment context, override the default block and allow rules, and inspect your effective config with the auto-mode CLI subcommands.
[Auto mode](/docs/en/permission-modes#eliminate-prompts-with-auto-mode) lets Claude Code run without routine permission prompts by routing tool calls through a classifier that blocks anything irreversible, destructive, or aimed outside your environment. Deny and explicit ask rules are evaluated before the classifier and still block or prompt. Use the `autoMode` settings block to tell that classifier which repos, buckets, and domains your organization trusts, so it stops blocking routine internal operations.
<Note>
Starting August 14, 2026, auto mode becomes the default permission mode for new sessions on Pro, Max, and Team plans. You can switch modes at any time. A default you set yourself stays in place unless you accept the one-time switch prompt, and a default your organization manages is unchanged. For details, see [the announcement](https://claude.com/blog/auto-mode-default-in-claude-code) on the blog.
Auto mode is available to all users on every provider, including the Anthropic API, [Claude Platform on AWS](/docs/en/claude-platform-on-aws), Amazon Bedrock, Google Cloud's Agent Platform, Microsoft Foundry, and signed-in [Claude apps gateway](/docs/en/claude-apps-gateway) sessions. If Claude Code reports auto mode as unavailable for your account, check the [full requirements](/docs/en/permission-modes#eliminate-prompts-with-auto-mode), which also cover the supported models and the organization-level control on Team and Enterprise plans. In v2.1.158 through v2.1.206, auto mode on Amazon Bedrock, Google Cloud's Agent Platform, Microsoft Foundry, and Claude apps gateway sessions required setting `CLAUDE_CODE_ENABLE_AUTO_MODE=1`; v2.1.207 removed the requirement.
</Note>
By default, the classifier trusts only the working directory and the current repo's configured remotes. Actions like pushing to your company's source-control org or writing to a team cloud bucket are blocked until you add them to `autoMode.environment`.
For how to enable auto mode and what it blocks by default, see [Permission modes](/docs/en/permission-modes#eliminate-prompts-with-auto-mode). This page is the configuration reference.
This page covers how to:
* [Add a human checkpoint](#add-a-human-checkpoint) for pushes and pull requests with `permissions.ask`
* [Choose where to set rules](#where-the-classifier-reads-configuration) across CLAUDE.md, user settings, and managed settings
* [Define trusted infrastructure](#define-trusted-infrastructure) with `autoMode.environment`
* [Override the block and allow rules](#override-the-block-and-allow-rules) when the defaults don't fit your pipeline
* [Route all shell commands through the classifier](#route-all-shell-commands-through-the-classifier) with `autoMode.classifyAllShell`
* [Inspect your effective config](#inspect-the-defaults-and-your-effective-config) with the `claude auto-mode` subcommands
* [Review denials](#review-denials) so you know what to add next
## Common boundaries
Auto mode allows pushes to any branch of the repository you're working in, including the default branch, and pull request creation by default. A non-default branch whose name marks it as a deploy or publication target, such as `production`, `release`, or `gh-pages`, isn't covered by that default: the classifier judges a push there on its own terms, including as a production deploy. The push's content is also still checked, so a force push, a secret entering the commit, or a change that would send secrets outside the repository when CI or a deploy pipeline runs it stays blocked.
<Info>Before v2.1.211, the classifier allowed pushes only to your working branch, branches Claude created, and routine pushes to the default branch.</Info>
If you want a human checkpoint before every push or pull request, add permission rules: the [recipes below](#add-a-human-checkpoint) keep auto mode on for everything else.
### Add a human checkpoint
The most direct mechanism is [`permissions.ask`](/docs/en/permissions#permission-rule-syntax). Content-scoped ask rules like the ones below are evaluated before the classifier and always force a permission prompt, even in auto mode, because an explicit ask rule is your stated intent to be prompted for that action. Add the rules in your [settings](/docs/en/settings#settings-files):
```json theme={null}
{
"permissions": {
"ask": [
"Bash(git push *)",
"Bash(gh pr create *)"
]
}
}
```
Pick the mechanism that matches how firm the boundary needs to be:
| Boundary | Mechanism | Behavior in auto mode |
| :-------------------------------- | :--------------------------------------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Prompt before the action | `permissions.ask` | Always prompts for content-scoped rules like the recipe above. The classifier cannot auto-approve a matching action. |
| Never run the action | `permissions.deny` | Blocks before the classifier is consulted. Neither the classifier nor user intent can override it. |
| One-off boundary for this session | State it in conversation, like "don't push until I review" | The classifier blocks matching actions, but the boundary can be lost if [context compaction](/docs/en/costs#reduce-token-usage) removes the message that stated it. Use an ask or deny rule for a durable guarantee. |
## Where the classifier reads configuration
The classifier reads the same [CLAUDE.md](/docs/en/memory) content Claude itself loads, so an instruction like "never force push" in your project's CLAUDE.md steers both Claude and the classifier at the same time. Start there for project conventions and behavioral rules.
For rules that apply across projects, such as trusted infrastructure or organization-wide deny rules, use the `autoMode` settings block. The classifier reads `autoMode` from the following scopes:
| Scope | File | Use for |
| :----------------------------- | :---------------------------------------------- | :--------------------------------------------------- |
| One developer | `~/.claude/settings.json` | Personal trusted infrastructure |
| Organization-wide | [Managed settings](/docs/en/server-managed-settings) | Trusted infrastructure distributed to all developers |
| `--settings` flag or Agent SDK | Inline JSON | Per-invocation overrides for automation |
The classifier doesn't read `autoMode` from project settings in `.claude/settings.json` or `.claude/settings.local.json`. Both files live in the repo directory, so a checked-in repo or a build step could otherwise inject its own allow rules. Before v2.1.207, the classifier also read `.claude/settings.local.json`; move any `autoMode` block in that file to `~/.claude/settings.json`. Excluding `.claude/settings.local.json` also closes the case where a repository commits the file or a local tool or build step writes it.
Entries from each scope are combined. A developer can extend `environment`, `allow`, `soft_deny`, and `hard_deny` with personal entries but can't remove entries that managed settings provide. Because allow rules act as exceptions to soft block rules inside the classifier, a developer-added `allow` entry can override an organization `soft_deny` entry: the combination is additive, not a hard policy boundary.
<Note>
The classifier is a second gate that runs after the [permissions system](/docs/en/permissions). For actions that must never run regardless of user intent or classifier configuration, use `permissions.deny` in managed settings, which blocks the action before the classifier is consulted and can't be overridden.
</Note>
## Define trusted infrastructure
For most organizations, `autoMode.environment` is the only field you need to set. It tells the classifier which repos, buckets, and domains are trusted: the classifier uses it to decide what "external" means, so any destination not listed is a potential exfiltration target.
As of Claude Code v2.1.198, `claude auto-mode defaults` prints three kinds of environment entry. Versions before v2.1.195 print only the first five trust slots.
* **Context slots**: describe your organization, stack, and security posture so the classifier reads the other rules in your context. Unlike the other two kinds, context slots have no rules of their own that target them. Each defaults to `None configured` or to the conservative assumption named next to it:
* **Organization**
* **Primary use of Claude Code**: defaults to software development
* **Cloud provider(s)**
* **Repository visibility**: a repository is assumed private unless its remote host and name indicate otherwise, or a visibility check earlier in the conversation the classifier reads shows it is public. The classifier reads your messages and the commands Claude runs, not their output, so the evidence has to be something it can read, such as your own message naming the repository as public; the output of a `gh repo view` on its own doesn't reach it. The transcript-evidence check requires Claude Code v2.1.200 or later
* **Internal sharing / snippet hosting**: public paste and gist services are treated as outside the trust boundary until you name one
* **Org-specific CLIs**
* **Secrets management**
* **CI/CD deploy targets**
* **Network posture**
* **Protected deployment namespaces / environments**: falls back to the Sensitive remote targets heuristic until you name them
* **Data retention / declassification**
* **Trust slots**: name what the classifier treats as inside your boundary. The slots are Trusted repo, Source control, Trusted internal domains, Trusted cloud buckets, Key internal services, and Internal package registry. The repo and source-control entries default to the working repository and its configured remotes. Every other trust slot defaults to `None configured`, so nothing else is trusted until you add it. A repository's visibility scopes only confidential material: a private repository is an acceptable destination for confidential material, but making a repository private never clears secrets or personal or entrusted data into it, and the classifier treats content ported, repointed, or first read from outside the working repository as not that repository's own work. This scoping requires Claude Code v2.1.203 or later.
* **Sensitivity slots**: name what the protective rules treat as high-risk. The slots are Sensitive data locations & audiences, Sensitive remote targets, and Protected IaC scopes. Each defaults to a broad heuristic, such as treating any host or namespace whose name carries `prod` or `production` as a sensitive remote target, so the protective rules are active before you configure anything. Naming concrete targets in a sensitivity slot makes those rules apply to the named targets instead of the heuristic.
<Info>Before v2.1.211, the context slots also included a Default / protected branches entry that treated `main` and `master` as protected until you named others. v2.1.211 removed it: [pushes to any branch of the repository you're working in](#common-boundaries) are allowed by default, so there is no protected-branch default to configure.</Info>
To add your own entries alongside the defaults, include the literal string `"$defaults"` in the array. The default entries are spliced in at that position, so your custom entries can go before or after them.
The following example keeps the default entries and adds an organization's repos, buckets, domains, and services.
```json theme={null}
{
"autoMode": {
"environment": [
"$defaults",
"Source control: github.example.com/acme-corp and all repos under it",
"Trusted cloud buckets: s3://acme-build-artifacts, gs://acme-ml-datasets",
"Trusted internal domains: *.corp.example.com, api.internal.example.com",
"Key internal services: Jenkins at ci.example.com, Artifactory at artifacts.example.com"
]
}
}
```
After you save your settings, run `claude auto-mode config` to [confirm the effective rules](#inspect-the-defaults-and-your-effective-config) include your entries.
Entries are prose, not regex or tool patterns. The classifier reads them as natural-language rules. Write them the way you would describe your infrastructure to a new engineer. A thorough environment section covers:
* **Organization**: your company name and what Claude Code is primarily used for, like software development, infrastructure automation, or data engineering
* **Source control**: every GitHub, GitLab, or Bitbucket org your developers push to
* **Cloud providers and trusted buckets**: bucket names or prefixes that Claude should be able to read from and write to
* **Trusted internal domains**: hostnames for APIs, dashboards, and services inside your network, like `*.internal.example.com`
* **Key internal services**: CI, artifact registries, internal package indexes, incident tooling
* **Internal package registry**: the private npm, PyPI, or other registry that installs should route through, so installs that bypass it for a public registry get blocked
* **Sensitive data locations & audiences**: the buckets, databases, or paths that hold personal data, confidential business data, credentials, regulated data, or similarly sensitive material, and the audiences that data in each location may be shared with, so the classifier protects those locations instead of guessing from content. Claude Code v2.1.195 through v2.1.197 name this entry PII / regulated-data locations and cover only locations that hold personal or regulated data, without the audience dimension
* **Sensitive remote targets**: the namespaces, hosts, or containers that count as production, so remote shells and port-forwards into them need your explicit approval
* **Protected IaC scopes**: the infrastructure resources whose apply or destroy should always require you to name the change
* **Additional context**: regulated-industry constraints, multi-tenant infrastructure, or compliance requirements that affect what the classifier should treat as risky
The Internal package registry, Sensitive data locations & audiences, Sensitive remote targets, and Protected IaC scopes entries require Claude Code v2.1.195 or later. Earlier versions still read them as plain context but don't have the built-in rules that target them.
A useful starting template: fill in the bracketed fields and remove any lines that don't apply.
```json theme={null}
{
"autoMode": {
"environment": [
"$defaults",
"Organization: {COMPANY_NAME}. Primary use: {PRIMARY_USE_CASE, e.g. software development, infrastructure automation}",
"Source control: {SOURCE_CONTROL, e.g. GitHub org github.example.com/acme-corp}",
"Cloud provider(s): {CLOUD_PROVIDERS, e.g. AWS, GCP, Azure}",
"Trusted cloud buckets: {TRUSTED_BUCKETS, e.g. s3://acme-builds, gs://acme-datasets}",
"Trusted internal domains: {TRUSTED_DOMAINS, e.g. *.internal.example.com, api.example.com}",
"Key internal services: {SERVICES, e.g. Jenkins at ci.example.com, Artifactory at artifacts.example.com}",
"Additional context: {EXTRA, e.g. regulated industry, multi-tenant infrastructure, compliance requirements}"
]
}
}
```
The more specific context you give, the better the classifier can distinguish routine internal operations from exfiltration attempts.
You don't need to fill everything in at once. A reasonable rollout: start with the defaults and add your source control org and key internal services, which resolves the most common false positives like pushing to your own repos. Add trusted domains and cloud buckets next. Fill the rest as blocks come up.
## Override the block and allow rules
Three additional fields let you replace the classifier's built-in rule lists:
* `autoMode.hard_deny`: unconditional security boundaries
* `autoMode.soft_deny`: destructive actions that user intent can clear
* `autoMode.allow`: exceptions to soft block rules
Each is an array of prose descriptions, read as natural-language rules. For tool-pattern-based hard blocks that run before the classifier, use [`permissions.deny`](/docs/en/permissions).
Inside the classifier, precedence works in four tiers:
* `hard_deny` rules block unconditionally. User intent and `allow` exceptions don't apply.
* `soft_deny` rules block next. User intent and `allow` exceptions can override these.
* `allow` rules then override matching `soft_deny` rules as exceptions.
* Explicit user intent overrides the remaining soft blocks: if the user's message directly and specifically describes the exact action Claude is about to take, the classifier allows it even when a `soft_deny` rule matches.
General requests don't count as explicit intent. Asking Claude to "clean up the repo" doesn't authorize force-pushing, but asking Claude to "force-push this branch" does.
To loosen, add to `allow` when the classifier repeatedly flags a routine pattern the default exceptions don't cover. To tighten, add to `soft_deny` for destructive risks specific to your environment that the defaults miss, or to `hard_deny` for security boundaries that must never be crossed.
To keep the built-in rules while adding your own, include the literal string `"$defaults"` in the array. The default rules are spliced in at that position, so your custom rules can go before or after them, and you continue to inherit updates as the built-in list changes across releases.
The following example keeps the defaults in all four lists and adds organization-specific rules to each.
```json theme={null}
{
"autoMode": {
"environment": [
"$defaults",
"Source control: github.example.com/acme-corp and all repos under it"
],
"allow": [
"$defaults",
"Deploying to the staging namespace is allowed: staging is isolated from production and resets nightly",
"Writing to s3://acme-scratch/ is allowed: ephemeral bucket with a 7-day lifecycle policy"
],
"soft_deny": [
"$defaults",
"Never run database migrations outside the migrations CLI, even against dev databases",
"Never modify files under infra/terraform/prod/: production infrastructure changes go through the review workflow"
],
"hard_deny": [
"$defaults",
"Never send repository contents to third-party code-review APIs"
]
}
}
```
<Danger>
Setting any of `environment`, `allow`, `soft_deny`, or `hard_deny` without `"$defaults"` replaces the entire default list for that section. If you set an array without `"$defaults"`, you discard the built-in rules for that section:
* `soft_deny`: every built-in soft block rule, including force push, `curl | bash`, production deploys, and auto-mode bypass
* `hard_deny`: the built-in data exfiltration rule
</Danger>
Each section is evaluated independently, so setting `environment` alone leaves the default `allow`, `soft_deny`, and `hard_deny` lists intact.
Only omit `"$defaults"` when you intend to take full ownership of the list. To do that safely, run `claude auto-mode defaults` to print the built-in rules, copy them into your settings file, then review each rule against your own pipeline and risk tolerance.
## Route all shell commands through the classifier
By default, narrow Bash and PowerShell allow rules such as `Bash(npm test)` carry over into auto mode and resolve before the classifier runs. Auto mode suspends only the broad rules that grant arbitrary code execution, such as `Bash(*)` or wildcarded interpreters. This means a narrow rule can still let a destructive argument through without the classifier seeing it, for example a script path or flag the rule's prefix didn't anticipate.
Set `autoMode.classifyAllShell` to `true` to suspend every Bash and PowerShell allow rule while auto mode is active, so the classifier evaluates every shell command regardless of your allow list.
```json theme={null}
{
"autoMode": {
"classifyAllShell": true
}
}
```
This trades latency for coverage: a command that an allow rule would have approved instantly now waits for a classifier decision, and each shell command counts as a classifier call.
The setting applies only while auto mode is active, and your allow rules behave normally in other permission modes.
<Note>
`autoMode.classifyAllShell` requires Claude Code v2.1.193 or later. Earlier versions ignore the key and continue to carry narrow shell allow rules into auto mode.
</Note>
## Inspect the defaults and your effective config
The `claude auto-mode` subcommands help you inspect, validate, and reset your configuration.
Print the built-in `environment`, `allow`, `soft_deny`, and `hard_deny` rules as JSON:
```bash theme={null}
claude auto-mode defaults
```
To read one rule's full wording without piping through `jq`, pass `--label` with the start of the rule's label, such as `claude auto-mode defaults --label 'Git Destructive'`. Matching is a case-insensitive prefix on each rule's label, and sections with no match print as empty lists. Requires Claude Code v2.1.208 or later.
Print what the classifier actually uses as JSON, with your settings applied where set and defaults otherwise:
```bash theme={null}
claude auto-mode config
```
Both `defaults` and `config` print the four rule lists as a single JSON object, with each rule as a prose string. This is a truncated example:
```json theme={null}
{
"allow": [
...
"Test Artifacts: Hardcoded test API keys, placeholder credentials in examples, or hardcoding test cases. Placeholder means authored as a placeholder — a file or value copied from a real secret or sensitive path is never a test artifact (see Sensitive-Source Provenance).",
...
],
"soft_deny": [
"Git Destructive [named+specifics — **must name:** the destructive operation and its target]: Force pushing (`git push --force`), deleting remote branches, tags, or releases, or rewriting remote history. Also `git commit --amend` when the commit being rewritten is not the agent's own unpushed work: either no prior `git commit` is visible (HEAD pre-dates the session), or a `git push` of the current branch is visible after the most recent commit (it has been pushed). Clears when the user asked to amend/reword/fixup, or when it is a message-only reword (`--amend -m …`, nothing newly staged) of a commit the agent visibly created this session.",
...
],
"hard_deny": [...],
"environment": [
...
"**Trusted repo**: The git repository the agent started in (its working directory) and its configured remote(s). When the repo's public/private visibility is given — by the Repository visibility entry or the user's own message — use it to scope what is OK to commit or push there: confidential material is fine in a private repo; in a public one, only that repo's own work is — and content ported, repointed, or first read from outside this session's repo is not its own work, whoever directed the port. Visibility scopes confidential material only: secrets and sensitive data (personal & entrusted) are never cleared into any repo by its visibility (see Definitions).",
...
]
}
```
Get AI feedback on your custom `allow`, `soft_deny`, and `hard_deny` rules:
```bash theme={null}
claude auto-mode critique
```
Run `claude auto-mode config` after saving your settings to confirm the effective rules are what you expect, with `"$defaults"` expanded in place. If you've written custom rules, `claude auto-mode critique` reviews them and flags entries that are ambiguous, redundant, or likely to cause false positives.
To discard your customizations and return to the built-in defaults, run the reset subcommand. It requires Claude Code v2.1.212 or later and removes the `autoMode` section from your user settings file:
```bash theme={null}
claude auto-mode reset
```
The command summarizes what it will remove and asks `Reset auto mode configuration to defaults?` before writing; pass `--yes` to skip the confirmation. Reset changes only `~/.claude/settings.json`: `autoMode` rules from [managed settings](/docs/en/server-managed-settings) or the `--settings` flag still apply.
## Review denials
To review and retry actions the auto mode classifier denied, open `/permissions` and select the **Recently denied** tab, where Claude Code records each denial. Press `r` on a denied action to mark it for retry: when you exit the dialog, Claude Code sends a message telling the model it may retry that tool call and resumes the conversation.
Cut at 300 lines. The page has the rest.
best-practices First recorded · 606 lines, first recorded
# Best practices for Claude Code ## Give Claude a way to verify its work ## Explore first, then plan, then code ## Provide specific context in your prompts ### Provide rich content ## Configure your environment ### Write an effective CLAUDE.md ### Configure permissions ### Use CLI tools ### Connect MCP servers ### Set up hooks ### Create skills ### Create custom subagents ### Install plugins ## Communicate effectively ### Ask codebase questions ### Let Claude interview you ## Manage your session ### Course-correct early and often ### Manage context aggressively ### Use subagents for investigation ### Rewind with checkpoints ### Resume conversations ## Automate and scale ### Run non-interactive mode ### Run multiple Claude sessions ### Fan out across files ### Run autonomously with auto mode ### Add an adversarial review step ## Avoid common failure patterns ## Develop your intuition ## Related resources
The first capture of this source. The page was already there, and this is what it said.
# Best practices for Claude Code
> Tips and patterns for getting the most out of Claude Code, from configuring your environment to scaling across parallel sessions.
Claude Code is an agentic coding environment. Unlike a chatbot that answers questions and waits, Claude Code can read your files, run commands, make changes, and autonomously work through problems while you watch, redirect, or step away entirely.
This changes how you work. Instead of writing code yourself and asking Claude to review it, you describe what you want and Claude figures out how to build it. Claude explores, plans, and implements.
But this autonomy still comes with a learning curve. Claude works within certain constraints you need to understand.
This guide covers patterns that have proven effective across Anthropic's internal teams and for engineers using Claude Code across various codebases, languages, and environments. For how the agentic loop works under the hood, see [How Claude Code works](/docs/en/how-claude-code-works).
***
Most best practices are based on one constraint: Claude's context window fills up fast, and performance degrades as it fills.
Claude's context window holds your entire conversation, including every message, every file Claude reads, and every command output. However, this can fill up fast. A single debugging session or codebase exploration might generate and consume tens of thousands of tokens.
This matters since LLM performance degrades as context fills. When the context window is getting full, Claude may start "forgetting" earlier instructions or making more mistakes. The context window is the most important resource to manage. To see how a session fills up in practice, [watch an interactive walkthrough](/docs/en/context-window) of what loads at startup and what each file read costs. Track context usage continuously with a [custom status line](/docs/en/statusline), and see [Reduce token usage](/docs/en/costs#reduce-token-usage) for strategies on reducing token usage.
***
## Give Claude a way to verify its work
<Tip>
Give Claude a check it can run: tests, a build, a screenshot to compare. It's the difference between a session you watch and one you walk away from.
</Tip>
Claude stops when the work looks done. Without a check it can run, "looks done" is the only signal available, and you become the verification loop: every mistake waits for you to notice it. Give Claude something that produces a pass or fail, and the loop closes on its own. Claude does the work, runs the check, reads the result, and iterates until the check passes.
The check is anything that returns a signal Claude can read in the conversation: a test suite, a build exit code, a linter, a script that diffs output against a fixture, or a [browser screenshot](/docs/en/chrome) compared against a design.
| Strategy | Before | After |
| ------------------------------------- | ------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Provide verification criteria** | *"implement a function that validates email addresses"* | *"write a validateEmail function. example test cases: [[email protected]](mailto:[email protected]) is true, invalid is false, [[email protected]](mailto:[email protected]) is false. run the tests after implementing"* |
| **Verify UI changes visually** | *"make the dashboard look better"* | *"\[paste screenshot] implement this design. take a screenshot of the result and compare it to the original. list differences and fix them"* |
| **Address root causes, not symptoms** | *"the build is failing"* | *"the build fails with this error: \[paste error]. fix it and verify the build succeeds. address the root cause, don't suppress the error"* |
Once the check exists, decide how hard it gates the stop:
* **In one prompt**: ask Claude to run the check and iterate in the same message, as in the table above.
* **Across a session**: set the check as a [`/goal` condition](/docs/en/goal). A separate evaluator re-checks it after every turn and Claude keeps working until it holds.
* **As a deterministic gate**: a [Stop hook](/docs/en/hooks#stop) runs your check as a script and blocks the turn from ending until it passes. Claude Code overrides the hook and ends the turn after 8 consecutive blocks.
* **By a second opinion**: a [verification subagent](/docs/en/sub-agents) or a [dynamic workflow](/docs/en/workflows) that checks its own findings has a fresh model try to refute the result, so the agent doing the work isn't the one grading it.
Each step trades setup for attention. The prompt version works on any task today. The `/goal` and Stop hook versions are what let an unattended run finish correctly without you.
Have Claude show evidence rather than asserting success: the test output, the command it ran and what it returned, or a screenshot of the result. Reviewing evidence is faster than re-running the verification yourself, and it works for sessions you weren't watching.
***
## Explore first, then plan, then code
<Tip>
Separate research and planning from implementation to avoid solving the wrong problem.
</Tip>
Letting Claude jump straight to coding can produce code that solves the wrong problem. Use [plan mode](/docs/en/permission-modes#analyze-before-you-edit-with-plan-mode) to separate exploration from execution.
The recommended workflow has four phases:
<Steps>
<Step title="Explore">
Enter plan mode by pressing `Shift+Tab` until the status bar shows `⏸ plan mode on`, or start the session with `claude --permission-mode plan`. Claude reads files and answers questions without making changes.
```txt title="claude (plan mode)" wrap theme={null}
read /src/auth and understand how we handle sessions and login.
also look at how we manage environment variables for secrets.
```
</Step>
<Step title="Plan">
Ask Claude to create a detailed implementation plan.
```txt title="claude (plan mode)" wrap theme={null}
I want to add Google OAuth. What files need to change?
What's the session flow? Create a plan.
```
Press `Ctrl+G` to open the plan in your text editor for direct editing before Claude proceeds.
</Step>
<Step title="Implement">
Switch out of plan mode by approving the plan or pressing `Shift+Tab`, then let Claude code, verifying against its plan.
```txt title="claude (default mode)" wrap theme={null}
implement the OAuth flow from your plan. write tests for the
callback handler, run the test suite and fix any failures.
```
</Step>
<Step title="Commit">
Ask Claude to commit with a descriptive message and create a PR.
```txt title="claude (default mode)" wrap theme={null}
commit with a descriptive message and open a PR
```
</Step>
</Steps>
<Callout>
Plan mode is useful, but also adds overhead.
For tasks where the scope is clear and the fix is small (like fixing a typo, adding a log line, or renaming a variable) ask Claude to do it directly.
Planning is most useful when you're uncertain about the approach, when the change modifies multiple files, or when you're unfamiliar with the code being modified. If you could describe the diff in one sentence, skip the plan.
</Callout>
***
## Provide specific context in your prompts
<Tip>
The more precise your instructions, the fewer corrections you'll need.
</Tip>
Claude can infer intent, but it can't read your mind. Reference specific files, mention constraints, and point to example patterns.
| Strategy | Before | After |
| ------------------------------------------------------------------------------------------------ | ---------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Scope the task.** Specify which file, what scenario, and testing preferences. | *"add tests for foo.py"* | *"write a test for foo.py covering the edge case where the user is logged out. avoid mocks."* |
| **Point to sources.** Direct Claude to the source that can answer a question. | *"why does ExecutionFactory have such a weird api?"* | *"look through ExecutionFactory's git history and summarize how its api came to be"* |
| **Reference existing patterns.** Point Claude to patterns in your codebase. | *"add a calendar widget"* | *"look at how existing widgets are implemented on the home page to understand the patterns. HotDogWidget.php is a good example. follow the pattern to implement a new calendar widget that lets the user select a month and paginate forwards/backwards to pick a year. build from scratch without libraries other than the ones already used in the codebase."* |
| **Describe the symptom.** Provide the symptom, the likely location, and what "fixed" looks like. | *"fix the login bug"* | *"users report that login fails after session timeout. check the auth flow in src/auth/, especially token refresh. write a failing test that reproduces the issue, then fix it"* |
Vague prompts can be useful when you're exploring and can afford to course-correct. A prompt like `"what would you improve in this file?"` can surface things you wouldn't have thought to ask about.
### Provide rich content
<Tip>
Use `@` to reference files, paste screenshots/images, or pipe data directly.
</Tip>
You can provide rich data to Claude in several ways:
* **Reference files with `@`** instead of describing where code lives. Claude reads the file before responding.
* **Paste images directly**. Copy/paste or drag and drop images into the prompt.
* **Give URLs** for documentation and API references. Use `/permissions` to allowlist frequently-used domains.
* **Pipe in data** by running `cat error.log | claude` to send file contents directly.
* **Let Claude fetch what it needs**. Tell Claude to pull context itself using Bash commands, MCP tools, or by reading files.
***
## Configure your environment
A few setup steps make Claude Code significantly more effective across all your sessions. For a full overview of extension features and when to use each one, see [Extend Claude Code](/docs/en/features-overview).
### Write an effective CLAUDE.md
<Tip>
Run `/init` to generate a starter CLAUDE.md file based on your current project structure, then refine over time.
</Tip>
CLAUDE.md is a special file that Claude reads at the start of every conversation. Include Bash commands, code style, and workflow rules. This gives Claude persistent context it can't infer from code alone.
The `/init` command analyzes your codebase to detect build systems, test frameworks, and code patterns, giving you a solid foundation to refine.
There's no required format for CLAUDE.md files, but keep it short and human-readable. For example:
```markdown CLAUDE.md theme={null}
# Code style
- Use ES modules (import/export) syntax, not CommonJS (require)
- Destructure imports when possible (eg. import { foo } from 'bar')
# Workflow
- Be sure to typecheck when you're done making a series of code changes
- Prefer running single tests, and not the whole test suite, for performance
```
Run `/context` to confirm Claude loaded the file. CLAUDE.md is loaded every session, so only include things that apply broadly. For domain knowledge or workflows that are only relevant sometimes, use [skills](/docs/en/skills) instead. Claude loads them on demand without bloating every conversation.
Keep it concise. For each line, ask: *"Would removing this cause Claude to make mistakes?"* If not, cut it. Bloated CLAUDE.md files cause Claude to ignore your actual instructions!
| ✅ Include | ❌ Exclude |
| ---------------------------------------------------- | -------------------------------------------------- |
| Bash commands Claude can't guess | Anything Claude can figure out by reading code |
| Code style rules that differ from defaults | Standard language conventions Claude already knows |
| Testing instructions and preferred test runners | Detailed API documentation (link to docs instead) |
| Repository etiquette (branch naming, PR conventions) | Information that changes frequently |
| Architectural decisions specific to your project | Long explanations or tutorials |
| Developer environment quirks (required env vars) | File-by-file descriptions of the codebase |
| Common gotchas or non-obvious behaviors | Self-evident practices like "write clean code" |
If Claude keeps doing something you don't want despite having a rule against it, the file is probably too long and the rule is getting lost. If Claude asks you questions that are answered in CLAUDE.md, the phrasing might be ambiguous. Treat CLAUDE.md like code: review it when things go wrong, prune it regularly, and test changes by observing whether Claude's behavior actually shifts.
You can tune instructions by adding emphasis (e.g., "IMPORTANT" or "YOU MUST") to improve adherence. Check CLAUDE.md into git so your team can contribute. The file compounds in value over time.
CLAUDE.md files can import additional files using `@path/to/import` syntax:
```markdown CLAUDE.md theme={null}
See @README.md for project overview and @package.json for available npm commands.
# Additional Instructions
- Git workflow: @docs/git-instructions.md
- Personal overrides: @~/.claude/my-project-instructions.md
```
You can place CLAUDE.md files in several locations:
* **Home folder (`~/.claude/CLAUDE.md`)**: applies to all Claude sessions
* **Project root (`./CLAUDE.md`)**: check into git to share with your team
* **Project root (`./CLAUDE.local.md`)**: personal project-specific notes; add this file to your `.gitignore` so it isn't shared with your team
* **Parent directories**: useful for monorepos where both `root/CLAUDE.md` and `root/foo/CLAUDE.md` are pulled in automatically
* **Child directories**: Claude pulls in child CLAUDE.md files on demand when it reads a file in those directories
### Configure permissions
<Tip>
Use [auto mode](/docs/en/permission-modes#eliminate-prompts-with-auto-mode) to let a classifier handle approvals, `/permissions` to allowlist specific commands, or `/sandbox` for OS-level isolation. Each reduces interruptions while keeping you in control.
</Tip>
By default, Claude Code requests permission for actions that might modify your system: file writes, Bash commands, MCP tools, etc. This is safe but tedious. After the tenth approval you're not really reviewing anymore, you're just clicking through. There are three ways to reduce these interruptions:
* **Auto mode**: a separate classifier model reviews commands and blocks only what looks risky: scope escalation, unknown infrastructure, or hostile-content-driven actions. Best when you trust the general direction of a task but don't want to click through every step
* **Permission allowlists**: permit specific tools you know are safe, like `npm run lint` or `git commit`
* **Sandboxing**: enable OS-level isolation that restricts filesystem and network access, allowing Claude to work more freely within defined boundaries
Read more about [permission modes](/docs/en/permission-modes), [permission rules](/docs/en/permissions), and [sandboxing](/docs/en/sandboxing).
### Use CLI tools
<Tip>
Tell Claude Code to use CLI tools like `gh`, `aws`, `gcloud`, and `sentry-cli` when interacting with external services.
</Tip>
CLI tools are the most context-efficient way to interact with external services. If you use GitHub, install the `gh` CLI. Claude knows how to use it for creating issues, opening pull requests, and reading comments. Without `gh`, Claude can still use the GitHub API, but unauthenticated requests often hit rate limits.
Claude is also effective at learning CLI tools it doesn't already know. Try prompts like `Use 'foo-cli-tool --help' to learn about foo tool, then use it to solve A, B, C.`
### Connect MCP servers
<Tip>
Run `claude mcp add` with a server name and URL or command to connect external tools like Notion, Figma, or your database. For example: `claude mcp add --transport http notion https://mcp.notion.com/mcp`.
</Tip>
With [MCP servers](/docs/en/mcp), you can ask Claude to implement features from issue trackers, query databases, analyze monitoring data, integrate designs from Figma, and automate workflows.
### Set up hooks
<Tip>
Use hooks for actions that must happen every time with zero exceptions.
</Tip>
[Hooks](/docs/en/hooks-guide) run scripts automatically at specific points in Claude's workflow. Unlike CLAUDE.md instructions which are advisory, hooks are deterministic and guarantee the action happens.
Claude can write hooks for you. Try prompts like *"Write a hook that runs eslint after every file edit"* or *"Write a hook that blocks writes to the migrations folder."* Edit `.claude/settings.json` directly to configure hooks by hand, and run `/hooks` to browse what's configured.
### Create skills
<Tip>
Create `SKILL.md` files in `.claude/skills/` to give Claude domain knowledge and reusable workflows.
</Tip>
[Skills](/docs/en/skills) extend Claude's knowledge with information specific to your project, team, or domain. Claude applies them automatically when relevant, or you can invoke them directly with `/skill-name`.
Create a skill by adding a directory with a `SKILL.md` to `.claude/skills/`:
```markdown .claude/skills/api-conventions/SKILL.md theme={null}
---
name: api-conventions
description: REST API design conventions for our services
---
# API Conventions
- Use kebab-case for URL paths
- Use camelCase for JSON properties
- Always include pagination for list endpoints
- Version APIs in the URL path (/v1/, /v2/)
```
Skills can also define repeatable workflows you invoke directly:
```markdown .claude/skills/fix-issue/SKILL.md theme={null}
---
name: fix-issue
description: Fix a GitHub issue
disable-model-invocation: true
---
Analyze and fix the GitHub issue: $ARGUMENTS.
1. Use `gh issue view` to get the issue details
2. Understand the problem described in the issue
3. Search the codebase for relevant files
4. Implement the necessary changes to fix the issue
5. Write and run tests to verify the fix
6. Ensure code passes linting and type checking
7. Create a descriptive commit message
8. Push and create a PR
```
Run `/fix-issue 1234` to invoke it. Use `disable-model-invocation: true` for workflows with side effects that you want to trigger manually.
### Create custom subagents
<Tip>
Define specialized assistants in `.claude/agents/` that Claude can delegate to for isolated tasks.
</Tip>
[Subagents](/docs/en/sub-agents) run in their own context with their own set of allowed tools. They're useful for tasks that read many files or need specialized focus without cluttering your main conversation.
```markdown .claude/agents/security-reviewer.md theme={null}
Cut at 300 lines. The page has the rest.
champion-kit First recorded · 189 lines, first recorded
# Champion kit ## The champion role ### What this should cost you ## Share what you discover ### What is worth sharing ### Where to share it ### The format that works ## Be the person people ask ### Answer with a prompt rather than an explanation ### Point at the feature rather than the documentation ### Questions you are likely to hear ## Grow the circle ### Patterns that tend to work ### Thirty-day playbook ### When someone wants to go deeper ## Respond to common concerns ## Quick-reference sheet
The first capture of this source. The page was already there, and this is what it said.
# Champion kit
> A playbook for engineers advocating Claude Code internally: what to share, how to answer questions, and how to grow adoption on your team.
This page is for individual engineers who are already using Claude Code and want to help their team adopt it. It covers what to share, how to answer the questions you will get, a thirty-day playbook, and responses to common concerns.
Adoption of a developer tool rarely happens because of a rollout announcement. It happens because someone on the team begins using the tool well, talks about it openly, and makes it easy for others to follow. The work you do as a champion has a disproportionate effect: every example you share shortens the learning curve for the engineers who come after you, and every question you answer in public turns one person's experience into something the whole team can build on.
## The champion role
The role consists of three behaviors that reinforce one another.
| Behavior | What it looks like in practice | Why it matters |
| ------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Share what you discover | Post the prompts, screenshots, and small wins from your own work in the places your team already reads, such as an engineering channel, a standup thread, or a pull-request description. | Examples drawn from your own codebase are more persuasive than any external documentation, because colleagues can see exactly how the tool applies to the problems they share with you. |
| Be the person people ask | When a colleague asks how you accomplished something, respond with the actual prompt you used so they can apply it directly to their own task. | A concrete, runnable example removes the gap between curiosity and a first successful use, which is where most adoption efforts stall. |
| Grow the circle | Establish a small number of lightweight, recurring habits, such as a dedicated channel or a weekly thread, so that momentum continues even when your attention is elsewhere. | Adoption that depends on a single person is fragile. Adoption that is carried by shared habits continues to compound on its own. |
### What this should cost you
Set expectations with yourself and with your lead. The activities below are intended to fit inside a normal working week, and the role should remain a multiplier on your existing work rather than an additional support responsibility.
| Activity | Time per week | Guidance |
| --------------------------------------- | ---------------- | ------------------------------------------------------------------------------------------------------------------------------ |
| Posting wins and prompts | About 15 minutes | Capture these in the moment with a screenshot and one or two sentences; avoid turning them into formal write-ups. |
| Answering questions in a shared channel | About 20 minutes | Answer publicly once, then link back to that answer when the question recurs. |
| Hosting a weekly show-and-tell thread | About 5 minutes | You post the opening prompt; the team supplies the content. |
| Optional pairing or walkthroughs | 0 to 30 minutes | Reserve this for colleagues who are genuinely blocked, and offer the [Quickstart](/docs/en/quickstart) link before scheduling time. |
## Share what you discover
Your own experience is the most persuasive material your colleagues will encounter, because it is specific to the codebase, workflows, and problems you all share. Documentation tells people what is possible; your posts show them what is actually working in your environment.
### What is worth sharing
The most useful posts describe a technique a colleague can reuse tomorrow rather than an outcome that is already complete. Techniques compound as they spread through a team; status updates do not.
Examples of reusable techniques:
* "I learned that @-mentioning a directory works. Pointing it at `@src/components/` and asking which were missing tests surfaced two I had overlooked."
* "Plan mode (`Shift+Tab`) shows exactly which files will be touched before any edit is made, which is why I am comfortable using it on shared code."
* "I configured a Stop hook so I receive a desktop notification when a long task completes. Configuration is in the thread."
* "Running `/init` generates a `CLAUDE.md` from the repository so the assistant stops re-asking about our conventions."
### Where to share it
Post wherever your team already reads. The goal is to place examples in the path of normal work rather than to create a destination.
| Location | Best suited for | Recommended format |
| ----------------------------------------------- | -------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------- |
| A `#claude-code` or general engineering channel | Discoveries, prompts, and "today I learned" moments | A screenshot accompanied by one or two sentences of context |
| Pull-request descriptions | Demonstrating the approach on real code that reviewers are already reading | A single line such as "Claude and I did this refactor; happy to walk through the approach." |
| Standups or weekly written updates | Normalizing usage with leads and skip-level managers | One sentence describing one concrete outcome |
| Team wiki or internal documentation | Durable patterns, custom skills, and `CLAUDE.md` examples | A short page, linked from the channel topic so it remains discoverable |
### The format that works
A screenshot accompanied by a single line of context, or a brief before-and-after description, is generally the right level of detail. Keep each post short enough that someone scrolling past still absorbs the point. A long write-up tends to be saved for later and forgotten, whereas a short post with a screenshot tends to be copied and tried.
The example posts below illustrate tone and length; adapt them rather than copying verbatim.
```text theme={null}
Learned today that @-mentioning a directory works. I pointed it at
@src/components/ and asked which components were missing tests, and it
surfaced two I had forgotten about.
```
```text theme={null}
I configured a Stop hook so I receive a desktop notification when a long
task completes. I started a refactor, stepped away, and was notified when
it finished. Configuration is in the thread.
```
```text theme={null}
Plan mode is the reason I am comfortable using this on code that matters.
Press Shift+Tab until you see "plan"; it lays out exactly which files it
intends to touch before changing anything.
```
## Be the person people ask
Once you have shared a few examples, questions will follow. This is where the champion role has the greatest leverage, because a good answer to one person frequently unblocks several others who are watching the same channel.
### Answer with a prompt rather than an explanation
When a colleague asks how you accomplished something, the most useful response is the prompt you actually used. They will learn more from running that prompt against their own problem than from any description you could write, and it gives them something they can act on immediately.
```text theme={null}
Colleague: How did you get it to find that race condition?
Champion: I asked, "The test in @tests/scheduler.test.ts is flaky, figure
out why," and it traced two unjoined promises in the scheduler. Try the
same phrasing on your test.
```
### Point at the feature rather than the documentation
A response such as "Try plan mode, press `Shift+Tab` until you see it" is more useful in the moment than a link to the documentation. If the person needs more depth later they will find it on their own; right now they need the single thing that unblocks them.
### Questions you are likely to hear
| Question | Suggested response | Follow-up resource |
| -------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------- |
| "What should I try it on first?" | Recommend a real but contained task, ideally a bug or chore the person has been postponing because it is tedious rather than difficult. | [Common workflows](/docs/en/common-workflows) |
| "How do I trust it with my code?" | Introduce plan mode: pressing `Shift+Tab` cycles into it, Claude proposes exactly what it intends to change, and nothing is modified until the user approves. | [Permissions](/docs/en/permissions) |
| "Is the setup worth the effort?" | Installation takes roughly two minutes, runs in the terminal, and requires no IDE extension. Running `/init` once is sufficient to begin working. | [Quickstart](/docs/en/quickstart) |
| "It produced an incorrect result." | Encourage them to provide the failure back to Claude. Pasting the error message or failing test is far more effective than rephrasing the original request. | [Common workflows](/docs/en/common-workflows) |
| "It does not understand our codebase conventions." | Suggest running `/init` to generate a `CLAUDE.md` file, then adding the team's conventions, test commands, and any directories that should be avoided. | [Memory](/docs/en/memory) |
| "Is this just autocomplete?" | Offer a brief demonstration in which Claude explains an unfamiliar file, traces a bug across services, or drafts a migration plan. These tasks require reasoning across the repository rather than completing a single line. | A two-minute live demonstration |
| "What about security and data handling?" | Refer this question to your administrator. Your organization's deployment and data-handling policy is already configured, and champions should not improvise this answer. | [Security](/docs/en/security) · [Data usage](/docs/en/data-usage) |
## Grow the circle
The objective is not to build a program or to own a rollout. It is to establish a small number of lightweight habits that allow momentum to continue after you have stopped actively driving it. When questions in the channel are being answered by people other than you, the role has done its job.
### Patterns that tend to work
| Pattern | How to run it | Effort required |
| ------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------ |
| A dedicated channel | Create a `#claude-code` channel (or a recurring thread in an existing one), pin the [Quickstart](/docs/en/quickstart) link and one strong example, and answer questions publicly so each answer benefits everyone watching. | About five minutes to set up, then ambient |
| A weekly show-and-tell thread | Each Friday, post "What did Claude help you with this week?" No preparation, slides, or meeting are required; screenshots and short descriptions are sufficient. | About two minutes per week |
| Share a custom skill | Post your most useful `.claude/skills/<name>/SKILL.md` file, for example a `/ship` skill that runs tests and lint before committing, with a one-line description. Because skills are plain Markdown, colleagues can adopt them immediately. | About five minutes per skill |
| Generate a setup guide from your own usage | Run `/team-onboarding` in a project you have spent real time in. Claude scans your recent sessions, commands, and MCP servers, then produces a guide a new teammate can paste as their first message to replay your setup. Pin it in the channel. | About two minutes |
| Pair on a first task | Offer a single fifteen-minute pairing session to anyone getting started. One successful outcome on their own code is more persuasive than any presentation. | About fifteen minutes per person |
| Identify the next champion | The colleague who asks you the most questions is usually ready to take on this role. Forward them this page and divide the channel responsibilities between you. | Negligible |
### Thirty-day playbook
If a loose plan is helpful, the sequence below reflects what tends to work across most teams. Adjust freely to fit your context.
<Steps>
<Step title="Week 1: Seed the channel">
Create the channel, pin the [Quickstart](/docs/en/quickstart), and post two or three of your own examples with the prompts included.
**Signal that it is working:** a few colleagues react or reply, and at least one question is asked in the channel.
</Step>
<Step title="Week 2: Start the rhythm">
Start the weekly show-and-tell thread, answer every question publicly, and share one custom skill or `CLAUDE.md` snippet.
**Signal that it is working:** someone other than you posts an example of their own.
</Step>
<Step title="Week 3: Pair and consolidate">
Offer two or three short pairing sessions and consolidate the most common questions and answers into a pinned FAQ message.
**Signal that it is working:** you see repeat usage, with the same colleagues returning rather than trying once and stopping.
</Step>
<Step title="Week 4: Hand off">
Identify a second champion and share a brief summary of what is working and what is not with your lead or administrator.
**Signal that it is working:** questions in the channel are being answered by people other than you.
</Step>
</Steps>
### When someone wants to go deeper
You are the warm introduction rather than the onboarding program. When a colleague moves past "should I try this" into "how do I become effective with it," point them to the [Quickstart](/docs/en/quickstart) and [Common workflows](/docs/en/common-workflows) pages. They contain short sections covering the features that are genuinely useful but difficult to discover on your own.
## Respond to common concerns
Healthy skepticism is expected; engineers should be cautious about tools that touch their code. The most effective response is rarely to argue the general case. Instead, acknowledge the concern, offer a brief reframe, and propose one concrete demonstration on the person's own code. Most concerns are resolved by a single successful experience.
| Concern | Suggested response | Evidence to offer |
| --------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------- |
| "I am faster without it." | That is likely true for code the person writes routinely. Suggest trying it on the work they tend to avoid: legacy files, unfamiliar services, or test scaffolding, where the leverage is highest. | Time one tedious task both ways and compare. |
| "I do not trust AI to touch production code." | Agree that no change should land unread. Plan mode combined with normal diff review means nothing is applied that the engineer has not inspected, the same standard as any pull request. | Demonstrate plan mode on a real file. |
| "It will make junior engineers weaker." | Used well, it is an effective explainer. Encourage junior engineers to ask Claude to explain a file and its call sites before asking it to change anything. | Run "Explain @file and where it is called from" together. |
| "I tried it once and it hallucinated." | This is usually a context problem rather than a model problem. @-mentioning the relevant files, running `/init`, and providing the actual error output typically resolves it. | Re-run their original prompt with proper `@`-context. |
| "We do not have time to learn another tool." | Claude Code is a terminal command rather than a platform. If it does not return value within the first session, it is reasonable to set it aside. | A two-minute install followed by one real bug. |
## Quick-reference sheet
The techniques below are the ones that most reliably move someone from a first trial to daily use. Pin this table in a channel or share it on its own.
| Technique | How to apply it |
| -------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Provide the right context | Use `@file` or `@directory/` references, or paste the error or log output directly. Supplying relevant context is more effective than elaborate prompting. |
| Review the plan before the edit | Press `Shift+Tab` to enter plan mode. Claude will describe the intended changes for your approval before executing them. |
| Teach it your repository | Run `/init` to generate a `CLAUDE.md` file, then add your conventions, test commands, and any directories that should not be modified. See [Memory](/docs/en/memory). |
| Reuse a workflow | Save a `SKILL.md` file in `.claude/skills/<name>/` to create a `/name` skill that the entire team can use. See [Skills](/docs/en/skills). |
| Stay informed during long tasks | Configure a Stop hook to receive a desktop notification when a long-running task completes. See [Hooks](/docs/en/hooks-guide). |
| Recover from an incorrect result | Rather than rephrasing the request, paste the failing test or stack trace back to Claude and ask it to address that specific failure. |
| Keep edits surgical | Ask for a diff, or specify "only change X." Claude respects scope when scope is stated. |
<Tip>
Claude Code is updated frequently. Verify version-specific details against the [documentation home page](/docs/en/overview) before distributing this material internally.
</Tip>
changelog First recorded · 5517 lines, first recorded
# Claude Code changelog
This page is larger than the 256 KiB this site keeps, so one side of the diff below stops where the stored text does.
The first capture of this source. The page was already there, and this is what it said.
# Claude Code changelog
> Release notes for Claude Code, including new features, improvements, and bug fixes by version.
This page is generated from the [CHANGELOG.md on GitHub](https://github.com/anthropics/claude-code/blob/main/CHANGELOG.md).
Run `claude --version` to check your installed version.
<Update label="2.1.232" description="August 13, 2026">
* Subagent forking is now on by default: a `subagent_type: "fork"` subagent inherits the full conversation and prompt cache, and non-teammate agent spawns in interactive sessions now run in the background by default
* Type `@` in the prompt to mention another Claude session by name; Claude then uses `SendMessage` to reach that session directly
* `SendMessage` now delivers to a bare name that exactly matches one live session, instead of asking to confirm with a ref first
* Interactive sessions on one machine now keep unique names: starting or renaming a session to a name another live session already uses gives it a `name-word-word` variant and tells you
* Added `/config` rows for "Dialog expiry" and "Messages from your other sessions" (cross-session inbound accept/hold/refuse)
* Added secret redaction for GitLab token families (`glrt-`, `gloas-`, `glptt-`, `glagent-`, `glimt-`, `glsoat-`, `glcbt-`, `glft-`, `glffct-`) and full redaction of routable `glpat-`/`gldt-` tokens; the `glab` CLI config store gets the same sandbox and credential-path protection as `gh`
* Added GitLab support to plugin marketplaces: bare `gitlab.com` repo URLs (including nested subgroups) now clone like `github.com` URLs, and clone auth-failure hints name your actual git host
* Settings: `additionalMarketplaces` and `allowedMarketplaces` are now accepted as friendlier aliases for `extraKnownMarketplaces` and `strictKnownMarketplaces`
* Enterprise policy: a url-typed `blockedMarketplaces` entry for a bare repo URL keeps blocking that URL when the CLI classifies it as a git clone
* Gateway: the `desktop:` overlay now accepts every released Desktop setting (was 11 hand-listed keys), validated at boot against Desktop's own schema; unknown or invalid keys fail boot
* Gateway: empty `managed.policies[].match.groups`/`admin.admin_groups` entries and malformed `email_domain` values (empty, or containing `@`, whitespace, or commas) now fail at boot instead of silently matching no one or granting admin access
* Fable 5 is offered as an advisor in `/advisor` again for organizations with Fable access, with usage-credits consent set up through `/model fable`
* Fixed a PowerShell permission bypass where variable-writing parameters could silently overwrite `$PSDefaultParameterValues` and redirect later commands' file access
* Fixed a Windows permission bypass where Git Bash followed Cygwin-style symlinks that path validation saw as regular files; writes through them now require permission approval
* Fixed nested git repositories inheriting trust from a parent directory; each repository now requires its own trust confirmation
* Fixed MCP connections hanging for the full 30-second connect timeout when a server fails to answer or sends a malformed reply to the protocol-version probe
* Fixed Remote Control sessions hosted by a bridge inside a cloud session inheriting that session's transcript or credentials
* Fixed Remote Control sessions started from Claude Desktop or an IDE appearing as a new claude.ai session each time the local session was resumed; they now reattach to the existing one
* Fixed Remote Control sessions appearing unreachable to newly attached clients while idle
* Fixed Remote Control bridge sessions not restoring conversation history when the session worker restarts
* Remote Control: resuming a conversation whose session was deleted from claude.ai or the app now starts a replacement instead of failing with a message about your login (regressed in v2.1.227)
* Fixed Cloud gateway `/login` exiting silently or leaving an unresponsive terminal after "Press Enter to continue" when managed settings failed to load; the reason is now shown
* Fixed voice mode on native builds getting stuck on "listening…" when the voice service rejected the connection; the rejection is now shown immediately
* Fixed mTLS client certificate rotation requiring a restart; Claude Code now reloads the rotated cert and key automatically on connection errors
* Fixed malformed AWS or Vertex region values being used to build request URLs; they now fall back to the default region
* Fixed stream idle timeout errors failing the request instead of recovering on Bedrock, Vertex, and gateway deployments
* Fixed content-sized overlays containing truncated text rendering one column too wide, and start-truncated text collapsing to an ellipsis
* Fixed a stray garbled character where a long shell-command or agent-description preview was cut off mid-emoji
* Fixed a startup race that could silently unregister a plugin marketplace due to concurrent writes to `known_marketplaces.json`
* Fixed `/update` and `/tui` refusing to restart while work that survives the relaunch was running
* Fixed usage-limit guidance suggesting unavailable slash commands in SDK and remote sessions
* Fixed the consent message for interactive `--advisor fable` launches, which told you to run `/model fable` in an interactive session that had just exited
* Improved fullscreen streaming: long sessions stay responsive because the whole conversation is no longer re-normalized on every update
* Improved the managed settings approval dialog: shows endpoint URLs, uses clearer wording for telemetry-only changes, skips routine OpenTelemetry options, and requires approval for server-managed sandbox binary overrides (`sandbox.bwrapPath`, `sandbox.socatPath`, `sandbox.ripgrep`)
* `/feedback` and `/bug` now open immediately when invoked while Claude is responding, instead of waiting for the turn to finish
* `/plugin install plugin@marketplace` now refreshes the marketplace first, so newly published plugins install without a manual marketplace update
* `/code-review` at high, xhigh, and max effort now runs in a background agent like the other levels
* Pasted and clipboard images are read without blocking the event loop
* Remote Control now keeps reconnecting for about 30 minutes after a network blip and no longer drops after a few blips spread across an hour
* Remote Control: resuming a conversation no longer silently takes Remote Control away from another Claude Code on the same machine that still has it; run `/remote-control` there to move it
* Updated agent panel: completed subagents hide immediately with a `/tasks` footer hint, and the "↓ N more" overflow indicator moved left for visibility
* Remote Control: the terminal now says whether a session was taken over by another device, ended from another app, or deleted, and stops suggesting a reconnect that would undo it
* Bash input redirections (`< file`) are now permission-checked like their argument spellings on all platforms
* Shortened the message shown when resuming a completed background agent
* Cowork sessions no longer inline external @-imports from user-scope memory files
* Hardened the auto-generated cross-session messaging socket directory on shared `/tmp`: a pre-planted symlink or another user's directory is now refused instead of used
* Hardened the Linux filesystem sandbox against a protected-path bypass
* Changed `sandbox.ripgrep` to be honored only from user, managed, and `--settings` settings; project settings can no longer override the sandbox's ripgrep binary
* Removed the startup tip suggesting you create custom subagents, and the matching nudge in the `/powerup` tour
</Update>
<Update label="2.1.231" description="August 13, 2026">
* Fixed MCP OAuth sign-in failing with a redirect URI mismatch for servers that use a pre-registered OAuth client, such as Slack
</Update>
<Update label="2.1.229" description="August 12, 2026">
* Documented `claude remote-control --continue` for resuming the most recent Remote Control session
* Added server-supplied Claude Code hook support for self-hosted runner sessions, matching managed-environment behavior
* Added SSE keepalive pings to gateway streaming responses during long thinking pauses, preventing idle-timeout disconnects on Vertex and Bedrock upstreams
* Added plugin marketplace `command` sources: a local command (e.g. an IDE) prints the plugin directory, which is re-resolved each session and applied without a restart; `mode: "link"` uses it in place
* `ListAgents` now marks disconnected Remote Control sessions as `offline` and labels your cloud sessions as `cloud`
* Fixed long responses partly disappearing while streaming and being printed twice in the terminal
* Fixed a crash to the error screen (including on `--resume` of the affected session) when a tool call had a non-string `glob`, `file_path`, or `command` value
* Fixed a RangeError crash when a progress bar or markdown table rendered in a very narrow terminal window (could also crash `claude --continue`/`--resume` at startup)
* Fixed a crash on Windows when a tool call or message referenced a file by an extended-length (`\\?\`) or UNC path
* Fixed auto mode failing on every tool call for users who disable the attribution header via `CLAUDE_CODE_ATTRIBUTION_HEADER` (direct Anthropic API connections)
* Fixed `/model` rejecting Sonnet/Opus 1M for claude.ai subscribers using a custom `ANTHROPIC_BASE_URL` gateway
* Fixed MCP OAuth with strict authorization servers by using `127.0.0.1` instead of `localhost` in the redirect URI
* Fixed Remote Control clients showing a stuck working spinner after a slash command typed in the laptop terminal
* Fixed the Claude Code Review workflow generated by `/install-github-app` completing without posting its review on the pull request
* Fixed multi-second UI stalls after editing a file with thousands of IDE diagnostics while the IDE extension is connected
* Fixed one-shot `claude plugin` commands leaving a stray liveness file that could prevent cleanup of outdated plugin versions
* Fixed dynamic workflows inside CPU-limited containers using the host machine's core count instead of the container's CPU limit
* Fixed a file-watcher handle leak after atomic file replacements, and an uncaught error on Windows when the scheduled-tasks watcher failed on a network or virtual filesystem
* Fixed SDK and `--input-format stream-json` sessions getting a 400 API error when a whitespace-only message was submitted
* Fixed conversations whose messages alone exceed the API's 32 MB request limit retrying compaction when no images or documents can be stripped; they now fail once with a clear message
* Fixed OpenTelemetry export from Claude Desktop sessions being rejected by the Desktop-managed gateway when that gateway is also the telemetry endpoint
* Fixed self-hosted runner and other remote sessions exiting at startup when `managed-mcp.json` is deployed and the server delivers MCP servers; those servers are now skipped with a warning
* Fixed self-hosted runner repository preparation hanging on a Git Credential Manager prompt; git now fails fast when credentials are missing
* Improved workflow fan-outs to stagger same-prefix sibling agents so subsequent agents read the cached prompt prefix instead of re-paying it (`CLAUDE_CODE_WORKFLOW_PREFIX_STAGGER_MS=0` disables)
* Improved "prompt is too long" errors to explain why automatic compaction could not recover instead of only suggesting `/compact`
* Improved sandbox: IPv6 literals in network domain lists are now bracketed (`[::1]:443`), and ambiguous spellings are enforced fail-closed and flagged by `/doctor`
* Updated `/login` to repeat the `CLAUDE_CODE_OAUTH_TOKEN` override warning after a successful login
* Changed `/commit-push-pr` so git/gh commands with dangerous flags (`--force`, `--amend`, `--no-verify`, etc.) are no longer auto-approved
* Changed self-hosted runner Windows startup to require an explicit `--base-dir`; there is no default checkout directory on Windows
* \[VSCode] "Report a problem" and `/bug` now open the built-in feedback dialog instead of a retired survey link
* \[VSCode] Made the `/btw` side-question panel resizable by dragging its boundary, in both side-docked and stacked layouts
* \[VSCode] Added session groups in the sidebar — right-click to create, rename, or delete; Cmd/Ctrl- or Shift-click to move several sessions at once
</Update>
<Update label="2.1.228" description="August 11, 2026">
* Fixed interactive sessions that could stop redrawing entirely, while the process kept running, after a rare internal layout error
* Fixed `git` / Git Bash not being found on Windows when Claude Code is launched from a parent folder of the git installation
* Fixed `/tui` reverting the session to an earlier model when `/model` had been changed since the last response
* Fixed cross-session messaging sometimes starting without an inbox in the first session after install or upgrade
* Fixed Remote Control `/resume` while connected leaking the resumed conversation's title or history into the connected session
* Fixed `claude self-hosted-runner` sessions failing on every fresh runner when the `checkout` hook fails for a repository the session doesn't push to; that repository is now skipped with a warning
* Fixed self-hosted runners ending sessions in the gap between a background task finishing and the follow-up turn starting
* Fixed session cleanup deleting contents inside a project's memory folder
* Fixed background plugin-cache cleanup deleting a plugin's cache when its only version is a symlinked development checkout
* Fixed a settings-merge issue where a marketplace entry redefined in a higher-precedence settings tier could inherit another tier's custom headers; marketplace entries now merge as whole entries
* Fixed the deferred-tools reminder occasionally being sent to the model twice after a skill invocation
* Hardened skills synced from claude.ai: they no longer shadow local commands or MCP prompts, their descriptions are sanitized and labeled, and on your machine their bodies don't run `!` commands or expand `@` files
* Improved cross-session messages: the sender and body now display inline instead of a collapsed line, and messages to Remote Control sessions on other machines show your Remote Control session name as the sender
* Improved Vertex AI credential handling: expired or missing Google Cloud credentials now fail within seconds instead of retrying for minutes
* Improved compaction progress: the retry countdown and stall hint now appear during compaction instead of only a progress bar
* Updated terminal title busy-spinner glyphs to reduce tab-bar jitter on some terminals
* Changed the Write tool so newer models can overwrite an existing file they haven't read this session, matching the Edit tool's rules; older models still require the read first
* Removed the outdated note about auto mode sessions costing slightly more from the first-use notice for Pro, Max, and Team plans
</Update>
<Update label="2.1.227" description="August 10, 2026">
* Fixed feature flags being evaluated without the user's subscription tier when a session started with an expired login token, which could wrongly prompt Max plan users to enable usage credits for Fable
* Fixed every Bash command failing under `claude-code-action` with `allowed_non_write_users` on GitHub-hosted runners
* Fixed `/tui` bringing back a conversation that had been rewound to before its first message
* Improved slash-command menu: blue now marks only the selected row, matched characters are bolded instead of recolored, and emoji or accented names keep their glyphs
* Improved performance: fewer event-loop stalls on file-not-found suggestions and at-mention size checks
</Update>
<Update label="2.1.226" description="August 8, 2026">
* Bug fixes and reliability improvements
</Update>
<Update label="2.1.225" description="August 8, 2026">
* Added gateway spend-limit support to Claude Code's usage warning; the limit-reached message now names the cap, its reset time, and the operator's message (requires the gateway on 2.1.225)
* Added a workspace trust prompt to `claude agents` for untrusted directories, matching the behavior of `claude`
* Fixed a transient 401 replacing a long-lived `CLAUDE_CODE_OAUTH_TOKEN` with a stored login's short-lived token, breaking headless sessions until restart
* Fixed MCP OAuth servers on macOS intermittently failing with a burst of 401 errors, as if never authenticated, after a keychain read timed out
* Fixed auto mode counting a safety-filter refusal of its own permission check toward the consecutive-block limit; the action is still denied, but the model is now told to move on rather than retry
* Fixed cross-session messages staying parked without a notice or expiry in headless sessions and during startup
* Fixed conversation history breaking on Remote Control session resume after very large conversations were compacted
* Fixed hovering over a session in another project in the agents list changing the directory the next agent starts in
* Fixed `claude self-hosted-runner` registering and then failing every session when `--base-dir` cannot be created or written; it now exits at startup with a clear error
* Fixed Claude Code on the web sessions being misreported as stuck, re-sending a growing event backlog on every reconnect
* Improved Remote Control: photos attached from the Claude app are now shown to Claude directly instead of being read from disk with a separate tool call
* \[VSCode] Fixed Focus view folding away the latest to-do list, a pending question's context, and settled answers; thinking-only folds show "Thought for Ns" and re-collapse when their turn completes
* SendMessage can now start a conversation with your Remote Control sessions on other machines by name (`ListAgents` shows them as `name [ref]`), instead of only replying after they message you first
* SendMessage: a Remote Control recipient you already confirmed is never swapped for a same-named session on this machine when its own list couldn't be checked
</Update>
<Update label="2.1.224" description="August 7, 2026">
* Added self-hosted environments: `claude self-hosted-runner` turns your own machines or containers into a place Claude Code web, mobile, and desktop sessions can run, on Team and Enterprise plans
* Added `archive` plugin source: install plugins from a zip over HTTPS without git or npm, with optional SHA-256 pinning
* Added a cancel-and-confirm step when removing an unavailable paste changes a command's text
* Added `ANTHROPIC_BEDROCK_REGION_PREFIX` env var for Bedrock to prefer a specific cross-region inference profile over the `AWS_REGION`-derived one
* Added `crossSessionInbound` and `dialogExpiry` settings: cross-session messages sent to a session running with bypassed permissions are held for your approval, and messages to other sessions auto-deliver
* Added sandbox credential-masking options: `extract` and `onExtractNoMatch` for structured env values, `decode: "jwt"` with `maskClaims` for JWT-aware masking, and `awsPairs`/`sigv4` for AWS SigV4 re-signing; these need `network.tlsTerminate` and are honored only from user, managed, or `--settings` settings
* Added cross-session `SendMessage`: Claude Code sessions can now message each other, on any of your machines, with `ListAgents` to discover them (macOS and Linux)
* Fixed long (>200 char) project paths resolving to another project's session directory under a shared sanitized prefix; session list, rename, fork, delete and `/resume` no longer cross projects
* Fixed `SendMessage` reporting "Message sent" when the write to a teammate's inbox had actually failed; failed deliveries are now reported as errors
* Fixed sandbox filesystem deny entries written with a trailing slash (e.g. `denyRead: "~/.aws/"`) being silently bypassable on Linux and macOS
* Fixed sandbox violation details never appearing in Bash tool results; Claude now sees which file or network access was denied and why
* Fixed MCP tools that connect mid-turn being deferred for tool search without their names announced to the model
* Fixed plugin install records being silently corrupted when the same plugin is installed in multiple projects
* Fixed recalled or restored paste content occasionally attaching wrong data or silently losing text when the paste had aged out or placeholder numbers collided
* Fixed copy-on-select on Wayland sometimes not reaching the clipboard; the two selection writes no longer race
* Fixed the feedback survey's transcript share silently failing on long sessions; a failed share now shows an error instead of a success message
* Fixed Remote Control auto-start intermittently failing with "Remote credentials fetch failed" on a cold start with a stale login token
* Fixed Remote Control and SDK clients showing a blank "(no content)" message after `/clear` and other output-less commands
* Fixed a Remote Control session recreated after its server session expired uploading prior local conversation history into the new session
* Improved fullscreen mode to keep the full pre-compaction history in scrollback across repeated compactions, instead of only the most recent interval
* Improved Remote Control: attached web and mobile clients now see compaction progress and the post-compaction boundary instead of a silent pause; `/clear` resets now propagate to attached clients
* Improved Remote Control: connection failures now show a persistent failure indicator with details and a reconnect shortcut, instead of only an 8-second toast
* Removed the 200-subagent-per-session spawn cap; long-running sessions no longer refuse new agents (concurrency and depth limits still apply)
* Changed managed settings: the approval prompt no longer re-appears after re-login or org switching when the organization's settings are unchanged
* Changed the feedback-survey transcript share: with your consent it now also uploads the last request's model settings — the system prompt (which includes your `CLAUDE.md` instructions), tool definitions, and model parameters. Secrets are redacted as before, and these fields are dropped first if the share is too large
* Changed the Bash tool description to always note that command output is displayed to the model, not reliably to the user
* Changed recalled paste placeholder numbers to renumber when accepted into the input
* Changed Remote Control to archive the stale server session instead of leaving a dead one listed when a fresh session is minted after compaction or `/resume`
* \[VSCode] Fixed the extension showing Remote Control as connected after the connection failed
* Fixed a session resume silently reconnecting Remote Control after the user turned it off (`--resume`, SDK hosts, and the VS Code extension)
* \[VSCode] Fixed sessions not honoring `remoteControlAtStartup` when explicitly enabled
</Update>
<Update label="2.1.223" description="August 6, 2026">
* Added owner wildcard entries (`"owner/*"`) to the `strictKnownMarketplaces` and `blockedMarketplaces` managed settings for allowing or blocking all marketplace repos under a GitHub org
* Added a warning when workflow agents, forked skills, slash commands, or resumed background agents' requested subagent model is restricted and the parent model runs instead
* Added a `/teleport` hint in cloud sessions showing how to continue locally with `claude --teleport <session id>`
* Fixed a Bash permission bypass where a crafted command could hide parts of itself from permission checks
* Fixed permission prompts so commands padded with tabs or invisible Unicode can no longer hide part of the command from the approval dialog
* Fixed workflow scripts being able to use dynamic `import()` to run code outside the workflow sandbox
* Fixed a permission gap where an agent definition's `bypassPermissions` mode ignored the org bypass-permissions disable policy
* Fixed resuming a session after a mid-session `/cd` coming back empty
* Fixed gateway model discovery hiding Claude models registered under provider-prefixed IDs such as `vertex_ai/claude-*` or `bedrock/anthropic.claude-*`
* Fixed `modelOverrides` keys that aren't Anthropic model IDs being treated as the session's canonical model ID; unknown keys are now ignored as documented
* Fixed managed settings: server-delivered settings no longer disable the env block of a machine-local `managed-settings.json` or MDM profile; admin env now merges per key
* Fixed sandboxed commands failing to start on Linux when `sandbox.filesystem.denyWrite` covers the working directory
* Fixed forked background agents getting stuck "already resuming" for the rest of the session when rebuilding the fork's parent prompt failed during resume
* Fixed a resumed session failing every turn, or leaving the interactive app on an unresponsive error screen, when its history held a malformed diagnostics attachment
* Fixed a rare hang when parsing unusual `git push` output
* Changed `CLAUDE_CODE_DISABLE_1M_CONTEXT` to hold every Claude model with a native 1M window to 200K via auto-compaction, not just a fixed list; a startup warning now appears when auto-compaction isn't holding the session to 200K
* Changed auto-compact to keep sessions on unrecognized model IDs within the assumed context window instead of letting them grow past it; set `CLAUDE_CODE_DISABLE_UNKNOWN_MODEL_WINDOW_ENFORCEMENT=1` to restore the previous behavior
* Changed `/review` to be an alias of `/code-review`, which reviews the current diff or a PR (`/code-review <level> <pr#>`); use `/code-review ultra` for a deep cloud review
* Changed `/code-review` with no effort level to reuse the level you typed last; type a level like `/code-review high` to change it
</Update>
<Update label="2.1.222" description="August 4, 2026">
* Fixed worktree-isolated sessions and their subagents being able to run destructive git commands against the main checkout; isolation now applies to file edits and Bash in every session type
* Fixed PreToolUse auto-allow hooks bypassing tool restrictions in background agent tasks (summaries, compaction, renames)
* Fixed `/usage-credits` on Team and Enterprise showing "you've already sent a usage credit request" for members whose earlier request was dismissed, blocking them from sending a new one
* Fixed the startup connectivity check hanging and then failing behind an HTTPS proxy; it now uses the same proxy-aware transport as API requests and times out with a clear message
* Fixed "Connection closed mid-response" errors being reported on responses that had actually completed
* Fixed `/usage` overattributing usage to MCP servers: a server's share now reflects only the requests that actually consumed its tool results, instead of every turn after any call to it
* Fixed sessions not linking to pull requests created after the branch was pushed, including through the GitHub REST API
* Fixed org-restricted `model: opus`-style subagent and teammate family aliases dropping to the parent model instead of stepping down to the newest org-allowed model in the family
* Fixed stream idle timeout firing on custom `ANTHROPIC_BASE_URL` gateways despite server keep-alive pings arriving on the wire
* Fixed claude.ai connectors being falsely marked as needing authorization when the session token is invalid — they now show a `/login` hint instead
* Fixed tool errors not being displayed for tools no longer available locally, for example after an MCP server is removed
* Fixed `SendMessage` rejecting a long summary — it now truncates instead, so sends no longer fail on a character limit
* Fixed the spinner's effort label in a subagent's transcript view showing the session's effort level instead of the subagent's own `effort:` setting
* Fixed rare crashes when a file watcher hit a filesystem error or during file-watcher teardown
* Fixed screen readers re-reading the whole input line on every backspace in `--ax-screen-reader` mode — end-of-line deletions now echo just the deleted characters
* Fixed host model-selection keys not taking precedence over a stale on-disk `managed-settings.json` when `CLAUDE_CODE_PROVIDER_MANAGED_BY_HOST` is set
* Improved auto mode safety: messages sent to other agent sessions via `SendMessage` are now evaluated by the permission classifier before dispatch
* Improved the refusal when Claude tries to invoke a skill with `disable-model-invocation`: Claude is now told to ask you to run the skill instead of replicating its workflow
* Improved the `/diff` view, the Remote Control workspace diff, and file-edit diffs in Claude Code on the web sessions to use raw git blob content, ignoring workspace-configured diff drivers and textconv
* Changed Remote Control auto-start so repo-local settings (`.claude/settings.json` or `.claude/settings.local.json`) can no longer turn it on (they can still turn it off); enable it at user scope via `/config`
* Removed ultraplan feature
</Update>
<Update label="2.1.221" description="August 4, 2026">
* \[VSCode] Added Focus view: a chat-menu toggle that hides tool activity behind an expandable per-turn summary with a live running-tool indicator, toggled with `Ctrl+Alt+F` or the "Claude Code: Toggle Focus view" command
* Added `mode: "mask"` for sandbox credential files on Linux and WSL — sandboxed commands read a sentinel copy (the whole file, or just the spans captured by an `extract` regex) while the sandbox proxy substitutes the real value on egress; on macOS file masking falls back to `deny`
* Added warnings to `claude plugin validate` when a marketplace or plugin name would be rejected by Claude Desktop's managed marketplace sync
* Added a `prompt-audit` subcommand to the `claude-api` skill for auditing prompts and tool descriptions for patterns written for older models
* Fixed a Bash tool permission-check bypass where zsh could execute hidden commands in `[[ ]]` regex conditionals; affected commands now prompt for permission
* Fixed PowerShell permission checks mishandling paths containing quote characters on Windows; such paths now prompt for approval
* Fixed the thinking toggle having no effect for the rest of a session that started with thinking off; disabling an MCP server mid-connect no longer silently reverts
* Fixed MCP servers from `--mcp-config` not being connected before the first turn in print mode (`-p`), which made the model emit tool calls as literal text
* Fixed @-mentioned files being silently dropped when pressing Esc to retract a prompt and resubmitting it
* Fixed a crash when preparing API requests for SDK MCP tools named after built-in object properties such as `constructor`
* Fixed WebSearch failing with a 400 error at effort `xhigh`/`max` when thinking is disabled
* Fixed sandboxed large uploads failing with TLS errors through the sandbox proxy
* Fixed Team and Enterprise spend-limit message incorrectly blaming the org's monthly limit instead of your individual spend limit
* Fixed Bedrock authentication with AWS SSO named profiles failing in desktop-managed sessions on Windows machines that set a stray `HOME` environment variable
* Fixed `CLAUDE_CODE_RESUME_INTERRUPTED_TURN=0` not disabling interrupted-turn auto-resume; falsy values are now honored
* Fixed a rare wake-from-sleep race where two Claude Code processes could both refresh the same MCP connector or WIF OAuth token at once, forcing re-authentication
* Fixed renaming a session from Claude Code Desktop or claude.ai not updating the CLI's session name; session names from every rename surface are now sanitized
* Fixed plugin- and org-delivered skills named after terminal-only built-ins (e.g. `/help`, `/feedback`) being un-invocable in non-interactive sessions
* Fixed the "Plugins changed" notification lingering after plugins were reloaded instead of clearing
* Fixed Vim mode: the yank register now survives dialogs, history search, and the transcript view instead of being silently emptied
* Fixed Vim mode: undoing back to an empty prompt now arms the "press ← again" confirm before returning to the agent view
* Improved tool search on Google Vertex AI: re-enabled for Claude 4.5-generation and newer models
* Improved auto mode: permission checks for parallel tool calls are now cache-efficient, and switching modes while a check is pending reliably prompts instead of applying the stale result
* Reduced prompt-cache costs for auto-mode permission checks by reusing the cached conversation prefix across decisions
* Improved Stats panel to count cache tokens in its token totals, with a breakdown by input, output, cache read, and cache write
* Improved `/ultrareview` error messages when a repo shares no history with its base: a checkout with no branches is now refused up front with advice to create one, and refusal hints no longer suggest `git fetch --unshallow` on clones that are already complete
* Improved Windows startup: process creation times are now read via a native kernel32 call instead of spawning PowerShell, so endpoint security tools that gate `powershell.exe` no longer prompt
* Changed background sessions to commit and push to preserve work, open a draft PR only when the task calls for one, follow your CLAUDE.md git instructions, and always end by reporting where the work lives
* Changed `/plugin install` to refresh a stale marketplace catalog and retry before reporting a plugin not found
* Changed plugins installed from `/plugin` to activate immediately when safe, instead of always requiring `/reload-plugins`
* Changed plugins to accept `"."` as a `skills` path, and the root-level `SKILL.md` validation error now suggests using the plugin root
* Changed `/status` to show the session kind: `interactive`, or a background job that is `attached` or `unattended`
* Changed emoji autocomplete to accept common alternate shortcodes like `:thumbsup:`, `:thumbsdown:`, and `:love:`
* Changed sessions forked with `/fork` to create a new worktree of their own instead of working in the original session's checkout
* Changed Claude in Chrome to close the browser tabs it opens once it no longer needs them
* Changed fast mode to report on the stream when usage credits run out mid-session, instead of failing silently
* Changed Monitor: a watch that exits without producing any output now says so instead of reporting "stream ended"
* Changed the Gateway `model` field validation: non-string values are rejected with a 400 instead of being forwarded
* Removed the repeated "Permission mode changed while the auto-mode classifier call was queued" notice from approval prompts
</Update>
<Update label="2.1.220" description="July 25, 2026">
* Bug fixes and reliability improvements
</Update>
<Update label="2.1.219" description="July 24, 2026">
* Added Claude Opus 5 (`claude-opus-5`), now the default Opus model — 1M context, fast mode at $10/$50 per Mtok
* Added `sandbox.network.strictAllowlist` setting to deny non-allowlisted hosts for sandboxed commands without prompting
* Added `DirectoryAdded` hook that fires after `/add-dir` or the SDK `register_repo_root` control request registers a new working directory mid-session
* Added `mcp_server_errors` to the headless stream-json init event, listing `--mcp-config` entries skipped by config validation; terminal runs print a startup warning
* Added the `workflowSizeGuideline` settings key so the advisory Dynamic workflow size guideline can be set from any settings file; the `/config` row is hidden while one does
* Added nested subagent forwarding in stream-json: subagents spawned at depth-2+ now appear when `--forward-subagent-text` is set, keyed by their spawning Agent `tool_use` id
* Fixed `claude -p` text output dropping the answer already produced when a turn dies on a mid-stream API error
* Added HTTP status and error text to `claude mcp list` and `/mcp` when a server fails to connect, and a warning for MCP config values with hidden leading or trailing whitespace
* Fixed the Fable model row showing "Requires usage credits" for plans that include it, when a stale cache had baked the label in
* Fixed the `/model` picker showing the merged Opus row as plain "Opus" instead of "Opus (1M context)"
* Fixed copy-on-select inside GNU screen printing base64 into the terminal instead of copying the selection
* Fixed Remote Control clients keeping a stale fast-mode status after a model switch, reconnect, or failed org check
* Fixed `CLAUDE_CODE_GIT_BASH_PATH` on Windows exiting or being used as bash when the path isn't a bash/sh binary; it's now ignored with a warning
* Fixed Vim mode: pressing ← on an empty prompt now returns to the agent view from NORMAL mode, not just INSERT
* Fixed screen-reader mode rewriting the entire input line on every keystroke instead of echoing only the typed character
* Improved the "Remote Control is only available via api.anthropic.com" error to name the specific setting that caused it
* Improved `claude --teleport` to show which repo your current checkout points at when it doesn't match the session's repo
* Changed dynamic workflows to default to a medium size guideline (aim for fewer than 15 agents); pick another size or unrestricted with Dynamic workflow size in `/config`
* Changed managed MCP allowlist/denylist `${VAR}` entries to resolve from the startup environment and managed-settings env instead of settings-file env
* Changed the `/model` picker to highlight only the newest model's name, so the highlight marks the new release rather than an arbitrary subset of the list
* Added the current default workflow size to the running-workflow status line, with a pointer to `/config` for changing it
* Removed Opus 4.7 from fast mode; `/fast` now applies to Opus 5 and Opus 4.8
* Updated the claude-api skill to default to Claude Opus 5, with a migration path from Opus 4.8
* Subagents can now spawn nested subagents up to depth 3 by default (was 1); set CLAUDE\_CODE\_MAX\_SUBAGENT\_SPAWN\_DEPTH=1 to disable nesting
Cut at 300 lines. The page has the rest.
channels First recorded · 374 lines, first recorded
# Push events into a running session with channels ## Supported channels ## Quickstart ## Security ## Enterprise controls ### Enable channels for your organization ### Restrict which channel plugins can run ## Research preview ## How channels compare ## Next steps
The first capture of this source. The page was already there, and this is what it said.
# Push events into a running session with channels
> Use channels to push messages, alerts, and webhooks into your Claude Code session from an MCP server. Forward CI results, chat messages, and monitoring events so Claude can react while you're away.
<Note>
Channels are in [research preview](#research-preview). They require Anthropic authentication through claude.ai or a Console API key, and are not available on Amazon Bedrock, Google Cloud's Agent Platform, or Microsoft Foundry. Team and Enterprise organizations must [explicitly enable them](#enterprise-controls).
</Note>
A channel is an MCP server that pushes events into your running Claude Code session, so Claude can react to things that happen while you're not at the terminal. Channels can be two-way: Claude reads the event and replies back through the same channel, like a chat bridge. Events only arrive while the session is open, so for an always-on setup you run Claude in a background process or persistent terminal.
Unlike integrations that spawn a fresh cloud session or wait to be polled, the event arrives in the session you already have open: see [how channels compare](#how-channels-compare).
You install a channel as a plugin and configure it with your own credentials. Telegram, Discord, and iMessage are included in the research preview.
When Claude replies through a channel, you see the inbound message in your terminal but not the reply text. The terminal shows the tool call and a confirmation (like "sent"), and the actual reply appears on the other platform.
If you manage a Team, Enterprise, or Console organization, see [Enable channels for your organization](#enterprise-controls). To build your own channel, see the [Channels reference](/docs/en/channels-reference).
## Supported channels
Each supported channel is a plugin that requires [Bun](https://bun.sh). For a hands-on demo of the plugin flow before connecting a real platform, try the [fakechat quickstart](#quickstart).
<Tabs>
<Tab title="Telegram">
View the full [Telegram plugin source](https://github.com/anthropics/claude-plugins-official/tree/main/external_plugins/telegram).
<Steps>
<Step title="Create a Telegram bot">
Open [BotFather](https://t.me/BotFather) in Telegram and send `/newbot`. Give it a display name and a unique username ending in `bot`. Copy the token BotFather returns.
</Step>
<Step title="Install the plugin">
In Claude Code, run:
```
/plugin install telegram@claude-plugins-official
```
If the install fails, match the message Claude Code reports:
* `Marketplace "claude-plugins-official" not found`: add the marketplace with `/plugin marketplace add anthropics/claude-plugins-official`, then retry the install.
* The plugin is [not found in the marketplace](/docs/en/discover-plugins#install-plugins): check the plugin name.
When the install asks for an installation scope, choose the user scope option so the plugin is available across all your projects. Check the install summary: if it reports `Run /reload-plugins to activate.`, run that command to activate the plugin's configure command.
</Step>
<Step title="Configure your token">
Run the configure command with the token from BotFather:
```
/telegram:configure <token>
```
This saves it to `~/.claude/channels/telegram/.env`. You can also set `TELEGRAM_BOT_TOKEN` in your shell environment before launching Claude Code.
</Step>
<Step title="Restart with channels enabled">
Exit Claude Code and restart with the channel flag. This starts the Telegram plugin, which begins polling for messages from your bot:
```bash theme={null}
claude --channels plugin:telegram@claude-plugins-official
```
</Step>
<Step title="Pair your account">
Open Telegram and send any message to your bot. The bot replies with a pairing code.
<Note>If your bot doesn't respond, make sure Claude Code is running with `--channels` from the previous step. The bot can only reply while the channel is active.</Note>
Back in Claude Code, run:
```
/telegram:access pair <code>
```
Then lock down access so only your account can send messages:
```
/telegram:access policy allowlist
```
</Step>
</Steps>
</Tab>
<Tab title="Discord">
View the full [Discord plugin source](https://github.com/anthropics/claude-plugins-official/tree/main/external_plugins/discord).
<Steps>
<Step title="Create a Discord bot">
Go to the [Discord Developer Portal](https://discord.com/developers/applications), click **New Application**, and name it. In the **Bot** section, create a username, then click **Reset Token** and copy the token.
</Step>
<Step title="Enable Message Content Intent">
In your bot's settings, scroll to **Privileged Gateway Intents** and enable **Message Content Intent**.
</Step>
<Step title="Invite the bot to your server">
Go to **OAuth2 > URL Generator**. Select the `bot` scope and enable these permissions:
* View Channels
* Send Messages
* Send Messages in Threads
* Read Message History
* Attach Files
* Add Reactions
Open the generated URL to add the bot to your server.
</Step>
<Step title="Install the plugin">
In Claude Code, run:
```
/plugin install discord@claude-plugins-official
```
If the install fails, match the message Claude Code reports:
* `Marketplace "claude-plugins-official" not found`: add the marketplace with `/plugin marketplace add anthropics/claude-plugins-official`, then retry the install.
* The plugin is [not found in the marketplace](/docs/en/discover-plugins#install-plugins): check the plugin name.
When the install asks for an installation scope, choose the user scope option so the plugin is available across all your projects. Check the install summary: if it reports `Run /reload-plugins to activate.`, run that command to activate the plugin's configure command.
</Step>
<Step title="Configure your token">
Run the configure command with the bot token you copied:
```
/discord:configure <token>
```
This saves it to `~/.claude/channels/discord/.env`. You can also set `DISCORD_BOT_TOKEN` in your shell environment before launching Claude Code.
</Step>
<Step title="Restart with channels enabled">
Exit Claude Code and restart with the channel flag. This connects the Discord plugin so your bot can receive and respond to messages:
```bash theme={null}
claude --channels plugin:discord@claude-plugins-official
```
</Step>
<Step title="Pair your account">
DM your bot on Discord. The bot replies with a pairing code.
<Note>If your bot doesn't respond, make sure Claude Code is running with `--channels` from the previous step. The bot can only reply while the channel is active.</Note>
Back in Claude Code, run:
```
/discord:access pair <code>
```
Then lock down access so only your account can send messages:
```
/discord:access policy allowlist
```
</Step>
</Steps>
</Tab>
<Tab title="iMessage">
View the full [iMessage plugin source](https://github.com/anthropics/claude-plugins-official/tree/main/external_plugins/imessage).
The iMessage channel reads your Messages database directly and sends replies through AppleScript. It requires macOS and needs no bot token or external service.
<Steps>
<Step title="Grant Full Disk Access">
The Messages database at `~/Library/Messages/chat.db` is protected by macOS. The first time the server reads it, macOS prompts for access: click **Allow**. The prompt names whichever app launched Bun, such as Terminal, iTerm, or your IDE.
If the prompt doesn't appear or you clicked Don't Allow, grant access manually under **System Settings > Privacy & Security > Full Disk Access** and add your terminal. Without this, the server exits immediately with `authorization denied`.
</Step>
<Step title="Install the plugin">
In Claude Code, run:
```
/plugin install imessage@claude-plugins-official
```
If the install fails, match the message Claude Code reports:
* `Marketplace "claude-plugins-official" not found`: add the marketplace with `/plugin marketplace add anthropics/claude-plugins-official`, then retry the install.
* The plugin is [not found in the marketplace](/docs/en/discover-plugins#install-plugins): check the plugin name.
When the install asks for an installation scope, choose the user scope option so the plugin is available across all your projects. If the install summary reports `Run /reload-plugins to activate.`, you can skip that here, because restarting in the next step picks up the plugin.
</Step>
<Step title="Restart with channels enabled">
Exit Claude Code and restart with the channel flag:
```bash theme={null}
claude --channels plugin:imessage@claude-plugins-official
```
</Step>
<Step title="Text yourself">
Open Messages on any device signed into your Apple ID and send a message to yourself. It reaches Claude immediately: self-chat bypasses access control with no setup.
<Note>The first reply Claude sends triggers a macOS Automation prompt asking if your terminal can control Messages. Click **OK**.</Note>
</Step>
<Step title="Allow other senders">
By default, only your own messages pass through. To let another contact reach Claude, add their handle:
```
/imessage:access allow +15551234567
```
Handles are phone numbers in `+country` format or Apple ID emails like `[email protected]`.
</Step>
</Steps>
</Tab>
</Tabs>
## Quickstart
Fakechat is an officially supported demo channel that runs a chat UI on localhost, with nothing to authenticate and no external service to configure.
Once you install and enable fakechat, you can type in the browser and the message arrives in your Claude Code session. Claude replies, and the reply shows up back in the browser. After you've tested the fakechat interface, try out [Telegram](https://github.com/anthropics/claude-plugins-official/tree/main/external_plugins/telegram), [Discord](https://github.com/anthropics/claude-plugins-official/tree/main/external_plugins/discord), or [iMessage](https://github.com/anthropics/claude-plugins-official/tree/main/external_plugins/imessage).
To try the fakechat demo, you'll need:
* Claude Code [installed and authenticated](/docs/en/quickstart#step-1-install-claude-code) with a claude.ai account or a Claude Console API key
* [Bun](https://bun.sh) installed. The pre-built channel plugins are Bun scripts. Check with `bun --version`; if that fails, [install Bun](https://bun.sh/docs/installation).
* **Team, Enterprise, or managed Console org**: your admin must [enable channels](#enterprise-controls) in managed settings
<Steps>
<Step title="Install the fakechat channel plugin">
Start a Claude Code session and run the install command:
```text theme={null}
/plugin install fakechat@claude-plugins-official
```
If the install fails, match the message Claude Code reports:
* `Marketplace "claude-plugins-official" not found`: add the marketplace with `/plugin marketplace add anthropics/claude-plugins-official`, then retry the install.
* The plugin is [not found in the marketplace](/docs/en/discover-plugins#install-plugins): check the plugin name.
When the install asks for an installation scope, choose the user scope option so the plugin is available across all your projects. If the install summary reports `Run /reload-plugins to activate.`, you can skip that here, because restarting in the next step picks up the plugin.
</Step>
<Step title="Restart with the channel enabled">
Exit Claude Code, then restart with `--channels` and pass the fakechat plugin you installed:
```bash theme={null}
claude --channels plugin:fakechat@claude-plugins-official
```
The fakechat server starts automatically. The startup screen shows a channels notice stating that messages from `plugin:fakechat@claude-plugins-official` inject directly in this session. If the plugin isn't installed or isn't on the approved allowlist, a warning line naming the problem appears below that notice.
<Tip>
You can pass several plugins to `--channels`, space-separated.
</Tip>
</Step>
<Step title="Push a message in">
Open the fakechat UI at [http://localhost:8787](http://localhost:8787) and type a message:
```text theme={null}
what's in my working directory?
```
The message arrives in your Claude Code session. The terminal shows it as an inbound channel line like `← fakechat · web: what's in my working directory?`, while the model receives it as a `<channel source="plugin:fakechat:fakechat">` event, using the plugin's scoped server name. Claude reads it, does the work, and calls fakechat's `reply` tool. The first reply triggers a permission prompt in your terminal; approve it, and the answer shows up in the chat UI.
</Step>
</Steps>
If Claude hits a permission prompt while you're away from the terminal, the session pauses until you respond. Channel servers that declare the [permission relay capability](/docs/en/channels-reference#relay-permission-prompts) can forward these prompts to you so you can approve or deny remotely. For unattended use, [`--dangerously-skip-permissions`](/docs/en/permission-modes#skip-all-checks-with-bypasspermissions-mode) bypasses most prompts, but only use it in environments you trust. Even then, these checks still prompt:
* Explicit ask rules
* Connector tools [your organization set to `ask`](/docs/en/mcp#organization-controls-on-connector-tools)
* MCP tools marked [`requiresUserInteraction`](/docs/en/mcp#require-approval-for-a-specific-tool)
* Removals targeting `/` or your home directory
* The [cross-session messaging safeguards](/docs/en/permission-modes#skip-all-checks-with-bypasspermissions-mode)
When you run channels in non-interactive mode with `-p`, tools that need terminal input, such as multiple-choice questions and plan mode approval, are disabled so the session never stalls waiting for input.
## Security
Every approved channel plugin maintains a sender allowlist: only IDs you've added can push messages, and everyone else is silently dropped.
Telegram and Discord bootstrap the list by pairing:
1. Find your bot in Telegram or Discord and send it any message
2. The bot replies with a pairing code
3. In your Claude Code session, approve the code when prompted
4. Your sender ID is added to the allowlist
iMessage works differently: texting yourself bypasses the gate automatically, and you add other contacts by handle with `/imessage:access allow`.
On top of that, you control which servers are enabled each session with `--channels`, and your organization controls availability with [`channelsEnabled`](#enterprise-controls) on claude.ai Team and Enterprise plans and on Console organizations that deploy managed settings.
Being in `.mcp.json` isn't enough to push messages: a server also has to be named in `--channels`.
The allowlist also gates [permission relay](/docs/en/channels-reference#relay-permission-prompts) if the channel declares it. Anyone who can reply through the channel can approve or deny tool use in your session, so only allowlist senders you trust with that authority.
## Enterprise controls
Cut at 300 lines. The page has the rest.
channels-reference First recorded · 764 lines, first recorded
# Channels reference ## Overview ## What you need ## Example: build a webhook receiver ## Test during the research preview ## Server options ## Notification format ## Expose a reply tool ## Gate inbound messages ## Relay permission prompts ### How relay works ### Permission request fields ### Add relay to a chat bridge ### Full example ## Package as a plugin ## See also
The first capture of this source. The page was already there, and this is what it said.
# Channels reference
> Build an MCP server that pushes webhooks, alerts, and chat messages into a Claude Code session. Reference for the channel contract: capability declaration, notification events, reply tools, sender gating, and permission relay.
<Note>
Channels are in [research preview](/docs/en/channels#research-preview). Team and Enterprise organizations must [explicitly enable them](/docs/en/channels#enterprise-controls).
</Note>
A channel is an MCP server that pushes events into a Claude Code session so Claude can react to things happening outside the terminal.
You can build a one-way or two-way channel. One-way channels forward alerts, webhooks, or monitoring events for Claude to act on. Two-way channels like chat bridges also [expose a reply tool](#expose-a-reply-tool) so Claude can send messages back. A channel with a trusted sender path can also opt in to [relay permission prompts](#relay-permission-prompts) so you can approve or deny tool use remotely.
This page covers:
* [Overview](#overview): how channels work
* [What you need](#what-you-need): requirements and general steps
* [Example: build a webhook receiver](#example-build-a-webhook-receiver): a minimal one-way walkthrough
* [Server options](#server-options): the constructor fields
* [Notification format](#notification-format): the event payload and delivery behavior
* [Expose a reply tool](#expose-a-reply-tool): let Claude send messages back
* [Gate inbound messages](#gate-inbound-messages): sender checks to prevent prompt injection
* [Relay permission prompts](#relay-permission-prompts): forward tool approval prompts to remote channels
To use an existing channel instead of building one, see [Channels](/docs/en/channels). Telegram, Discord, iMessage, and fakechat are included in the research preview.
## Overview
A channel is an [MCP](https://modelcontextprotocol.io) server that runs on the same machine as Claude Code. Claude Code spawns it as a subprocess and communicates over stdio. Your channel server is the bridge between external systems and the Claude Code session:
* **Chat platforms** (Telegram, Discord): your plugin runs locally and polls the platform's API for new messages. When someone DMs your bot, the plugin receives the message and forwards it to Claude. No URL to expose.
* **Webhooks** (CI, monitoring): your server listens on a local HTTP port. External systems POST to that port, and your server pushes the payload to Claude.
<img src="https://mintcdn.com/claude-code/9FG0ZKj9uKYiHmbi/images/channel-architecture.svg?fit=max&auto=format&n=9FG0ZKj9uKYiHmbi&q=85&s=9a037b7da80184ae49015c0256b21a1f" className="dark:hidden" alt="Architecture diagram showing external systems connecting to your local channel server, which communicates with Claude Code over stdio" width="600" height="220" data-path="images/channel-architecture.svg" />
<img src="https://mintcdn.com/claude-code/_xqph1dUOslCOwsj/images/channel-architecture-dark.svg?fit=max&auto=format&n=_xqph1dUOslCOwsj&q=85&s=ae1e494440806a6a5d74a1279e22e162" className="hidden dark:block" alt="Architecture diagram showing external systems connecting to your local channel server, which communicates with Claude Code over stdio" width="600" height="220" data-path="images/channel-architecture-dark.svg" />
## What you need
The only hard requirement is the [`@modelcontextprotocol/sdk`](https://www.npmjs.com/package/@modelcontextprotocol/sdk) package and a Node.js-compatible runtime. [Bun](https://bun.sh), [Node](https://nodejs.org), and [Deno](https://deno.com) all work. The pre-built plugins in the research preview use Bun, but your channel doesn't have to.
Your server needs to:
1. Declare the `claude/channel` capability so Claude Code registers a notification listener
2. Emit `notifications/claude/channel` events when something happens
3. Connect over [stdio transport](https://modelcontextprotocol.io/docs/concepts/transports#standard-io)
The [Server options](#server-options) and [Notification format](#notification-format) sections cover each of these in detail. See [Example: build a webhook receiver](#example-build-a-webhook-receiver) for a full walkthrough.
During the research preview, custom channels aren't on the [approved allowlist](/docs/en/channels#supported-channels). Use `--dangerously-load-development-channels` to test locally. See [Test during the research preview](#test-during-the-research-preview) for details.
## Example: build a webhook receiver
This walkthrough builds a single-file server that listens for HTTP requests and forwards them into your Claude Code session. By the end, anything that can send an HTTP POST, like a CI pipeline, a monitoring alert, or a `curl` command, can push events to Claude.
This example uses [Bun](https://bun.sh) as the runtime for its built-in HTTP server and TypeScript support. You can use [Node](https://nodejs.org) or [Deno](https://deno.com) instead; the only requirement is the [MCP SDK](https://www.npmjs.com/package/@modelcontextprotocol/sdk).
<Steps>
<Step title="Create the project">
The permission relay examples later on this page import `zod` directly, so it installs alongside the MCP SDK. Create a new directory and install both:
```bash theme={null}
mkdir webhook-channel && cd webhook-channel
bun add @modelcontextprotocol/sdk zod
```
</Step>
<Step title="Write the channel server">
Create a file called `webhook.ts`. This is your entire channel server: it connects to Claude Code over stdio, and it listens for HTTP POSTs on port 8788. When a request arrives, it pushes the body to Claude as a channel event.
```ts title="webhook.ts" theme={null}
#!/usr/bin/env bun
import { Server } from '@modelcontextprotocol/sdk/server/index.js'
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'
// Create the MCP server and declare it as a channel
const mcp = new Server(
{ name: 'webhook', version: '0.0.1' },
{
// this key is what makes it a channel — Claude Code registers a listener for it
capabilities: { experimental: { 'claude/channel': {} } },
// added to Claude's system prompt so it knows how to handle these events
instructions: 'Events from the webhook channel arrive as <channel source="webhook" ...>. They are one-way: read them and act, no reply expected.',
},
)
// Connect to Claude Code over stdio (Claude Code spawns this process)
await mcp.connect(new StdioServerTransport())
// Start an HTTP server that forwards every POST to Claude
Bun.serve({
port: 8788, // any open port works
// localhost-only: nothing outside this machine can POST
hostname: '127.0.0.1',
async fetch(req) {
const body = await req.text()
await mcp.notification({
method: 'notifications/claude/channel',
params: {
content: body, // becomes the body of the <channel> tag
// each key becomes a tag attribute, e.g. <channel path="/" method="POST">
meta: { path: new URL(req.url).pathname, method: req.method },
},
})
return new Response('ok')
},
})
```
The file does three things in order:
* **Server configuration**: creates the MCP server with `claude/channel` in its capabilities, which is what tells Claude Code this is a channel. The [`instructions`](#server-options) string goes into Claude's system prompt: tell Claude what events to expect, whether to reply, and how to route replies if it should.
* **Stdio connection**: connects to Claude Code over stdin/stdout. This is standard for any [MCP server](https://modelcontextprotocol.io/docs/concepts/transports#standard-io).
* **HTTP listener**: starts a local web server on port 8788. Every POST body gets forwarded to Claude as a channel event via `mcp.notification()`. The `content` becomes the event body, and each `meta` entry becomes an attribute on the `<channel>` tag. The listener needs access to the `mcp` instance, so it runs in the same process. You could split it into separate modules for a larger project.
</Step>
<Step title="Register your server with Claude Code">
Add the server to your MCP config so Claude Code knows how to start it. For a project-level `.mcp.json` in the same directory, use a relative path. For user-level config in `~/.claude.json`, use the full absolute path so the server can be found from any project:
```json title=".mcp.json" theme={null}
{
"mcpServers": {
"webhook": { "command": "bun", "args": ["./webhook.ts"] }
}
}
```
Claude Code reads your MCP config at startup and spawns each server as a subprocess.
</Step>
<Step title="Test it">
During the research preview, custom channels aren't on the allowlist, so start Claude Code with the development flag:
```bash theme={null}
claude --dangerously-load-development-channels server:webhook
```
Claude Code first shows a full-screen warning dialog listing the development channels you're loading. Select **I am using this for local development** to continue, or **Exit** to quit.
The first time you start a session in this project, Claude Code also asks for consent before using the new server from `.mcp.json`. The dialog reports "New MCP server found in this project: webhook". Select **Use this MCP server** to continue.
After you accept, Claude Code spawns your `webhook.ts` as a subprocess, and the HTTP listener starts automatically on the port you configured, 8788 in this example. You don't need to run the server yourself.
A dim notice below the startup banner confirms the channel is registered: `Channels (experimental) messages from server:webhook inject directly in this session · restart without --dangerously-load-development-channels to stop`.
If you see "blocked by org policy," your organization admin needs to [enable channels](/docs/en/channels#enterprise-controls) first.
In a separate terminal, simulate a webhook by sending an HTTP POST with a message to your server. This example sends a CI failure alert to port 8788 (or whichever port you configured):
```bash theme={null}
curl -X POST localhost:8788 -d "build failed on main: https://ci.example.com/run/1234"
```
The payload arrives in Claude's context as a `<channel>` tag:
```text theme={null}
<channel source="webhook" path="/" method="POST">build failed on main: https://ci.example.com/run/1234</channel>
```
Your terminal renders the event as a one-line summary, `← webhook: build failed on main: https://ci.example.com/run/1234`, rather than the raw tag. You'll then see Claude start responding: reading files, running commands, or whatever the message calls for. This is a one-way channel, so Claude acts in your session but doesn't send anything back through the webhook. To add replies, see [Expose a reply tool](#expose-a-reply-tool).
If the event doesn't arrive, the diagnosis depends on what `curl` returned:
* **`curl` succeeds but nothing reaches Claude**: run `/mcp` in your session to check the server's status. "Failed to connect" usually means a dependency or import error in your server file; check the debug log at `~/.claude/debug/<session-id>.txt` for the stderr trace.
* **`curl` fails with "connection refused"**: the port is either not bound yet or a stale process from an earlier run is holding it. `lsof -i :<port>` shows what's listening; `kill` the stale process before restarting your session.
</Step>
</Steps>
The [fakechat server](https://github.com/anthropics/claude-plugins-official/tree/main/external_plugins/fakechat) extends this pattern with a web UI, file attachments, and a reply tool for two-way chat.
## Test during the research preview
During the research preview, every channel must be on the [approved allowlist](/docs/en/channels#research-preview) to register. The development flag bypasses the allowlist for specific entries after a confirmation prompt. This example shows both entry types:
```bash theme={null}
# Testing a plugin you're developing
claude --dangerously-load-development-channels plugin:yourplugin@yourmarketplace
# Testing a bare .mcp.json server (no plugin wrapper yet)
claude --dangerously-load-development-channels server:webhook
```
The bypass is per-entry. Combining this flag with `--channels` doesn't extend the bypass to the `--channels` entries. During the research preview, the approved allowlist is Anthropic-curated, so your channel stays on the development flag while you build and test.
<Note>
This flag skips the allowlist only. The `channelsEnabled` organization policy still applies. Don't use it to run channels from untrusted sources.
</Note>
## Server options
A channel sets these options in the [`Server`](https://modelcontextprotocol.io/docs/learn/server-concepts) constructor. The `instructions` and `capabilities.tools` fields are [standard MCP](https://modelcontextprotocol.io/docs/learn/server-concepts); `capabilities.experimental['claude/channel']` and `capabilities.experimental['claude/channel/permission']` are the channel-specific additions:
| Field | Type | Description |
| :------------------------------------------------------- | :------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `capabilities.experimental['claude/channel']` | `object` | Required. Always `{}`. Presence registers the notification listener. |
| `capabilities.experimental['claude/channel/permission']` | `object` | Optional. Always `{}`. Declares that this channel can receive permission relay requests. When declared, Claude Code forwards tool approval prompts to your channel so you can approve or deny them remotely. See [Relay permission prompts](#relay-permission-prompts). |
| `capabilities.tools` | `object` | Two-way only. Always `{}`. Standard MCP tool capability. See [Expose a reply tool](#expose-a-reply-tool). |
| `instructions` | `string` | Recommended. Added to Claude's system prompt. Tell Claude what events to expect, what the `<channel>` tag attributes mean, whether to reply, and if so which tool to use and which attribute to pass back (like `chat_id`). |
To create a one-way channel, omit `capabilities.tools`. This example shows a two-way setup with the channel capability, tools, and instructions set:
```ts theme={null}
import { Server } from '@modelcontextprotocol/sdk/server/index.js'
const mcp = new Server(
{ name: 'your-channel', version: '0.0.1' },
{
capabilities: {
experimental: { 'claude/channel': {} }, // registers the channel listener
tools: {}, // omit for one-way channels
},
// added to Claude's system prompt so it knows how to handle your events
instructions: 'Messages arrive as <channel source="your-channel" ...>. Reply with the reply tool.',
},
)
```
## Notification format
Your server emits `notifications/claude/channel` with two params:
| Field | Type | Description |
| :-------- | :----------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `content` | `string` | The event body. Delivered as the body of the `<channel>` tag. |
| `meta` | `Record<string, string>` | Optional. Each entry becomes an attribute on the `<channel>` tag for routing context like chat ID, sender name, or alert severity. Keys must be identifiers: letters, digits, and underscores only. Keys containing hyphens or other characters are silently dropped. |
Your server pushes events by calling `mcp.notification()` on the `Server` instance. This example pushes a CI failure alert with two meta keys:
```ts theme={null}
await mcp.notification({
method: 'notifications/claude/channel',
params: {
content: 'build failed on main: https://ci.example.com/run/1234',
meta: { severity: 'high', run_id: '1234' },
},
})
```
The event arrives in Claude's context wrapped in a `<channel>` tag. The `source` attribute is set automatically from your server's configured name:
```text theme={null}
<channel source="your-channel" severity="high" run_id="1234">
build failed on main: https://ci.example.com/run/1234
</channel>
```
Claude Code doesn't acknowledge notifications. The `await` on `mcp.notification()` resolves when the message is written to the transport, not when Claude has processed it. If the session hasn't loaded your server as a channel, or the organization policy blocks it, Claude Code drops the events silently and returns no error to your server.
If you need delivery confirmation, track event state in your server and expose a [reply tool](#expose-a-reply-tool) that Claude can call to report status back.
Events queue into the session and are processed in order. If several notifications arrive while Claude is busy, they're delivered together on the next turn and Claude handles them as a group. To process independent event streams concurrently, run separate sessions.
## Expose a reply tool
If your channel is two-way, like a chat bridge rather than an alert forwarder, expose a standard [MCP tool](https://modelcontextprotocol.io/docs/concepts/tools) that Claude can call to send messages back. Nothing about the tool registration is channel-specific. A reply tool has three components:
1. A `tools: {}` entry in your `Server` constructor capabilities so Claude Code discovers the tool
2. Tool handlers that define the tool's schema and implement the send logic
3. An `instructions` string in your `Server` constructor that tells Claude when and how to call the tool
To add these to the [webhook receiver above](#example-build-a-webhook-receiver):
<Steps>
<Step title="Enable tool discovery">
In your `Server` constructor in `webhook.ts`, add `tools: {}` to the capabilities so Claude Code knows your server offers tools:
```ts theme={null}
capabilities: {
experimental: { 'claude/channel': {} },
tools: {}, // enables tool discovery
},
```
</Step>
<Step title="Register the reply tool">
Add the following to `webhook.ts`. The `import` goes at the top of the file with your other imports; the two handlers go between the `Server` constructor and `mcp.connect()`. This registers a `reply` tool that Claude can call with a `chat_id` and `text`:
```ts theme={null}
// Add this import at the top of webhook.ts
import { ListToolsRequestSchema, CallToolRequestSchema } from '@modelcontextprotocol/sdk/types.js'
// Claude queries this at startup to discover what tools your server offers
mcp.setRequestHandler(ListToolsRequestSchema, async () => ({
tools: [{
name: 'reply',
description: 'Send a message back over this channel',
// inputSchema tells Claude what arguments to pass
inputSchema: {
type: 'object',
properties: {
chat_id: { type: 'string', description: 'The conversation to reply in' },
text: { type: 'string', description: 'The message to send' },
},
required: ['chat_id', 'text'],
},
}],
}))
// Claude calls this when it wants to invoke a tool
mcp.setRequestHandler(CallToolRequestSchema, async req => {
if (req.params.name === 'reply') {
Cut at 300 lines. The page has the rest.
checkpointing First recorded · 103 lines, first recorded
# Checkpointing ## How checkpoints work ### Automatic tracking ### Rewind and summarize #### Rewind past a cleared conversation #### Guide a summary ## Common use cases ## Limitations ### Bash command changes not tracked ### Subagent edits not restored ### External changes not tracked ### Symlinked and hard-linked paths not restored ### Not a replacement for version control ## See also
The first capture of this source. The page was already there, and this is what it said.
# Checkpointing
> Track, rewind, and summarize Claude's edits and conversation to manage session state.
Claude Code automatically tracks Claude's file edits as you work, allowing you to quickly undo changes and rewind to previous states if anything gets off track.
## How checkpoints work
As you work with Claude, checkpointing automatically captures the state of your code before each user prompt.
### Automatic tracking
Claude Code tracks all changes made by its file editing tools:
* Every user prompt creates a new checkpoint
* Claude Code keeps file snapshots for the 100 most recent checkpoints in a session. Discarding an older checkpoint deletes the snapshot files that no remaining checkpoint references, except each file's first snapshot, which the VS Code extension uses as the baseline for its session diffs.
* Claude Code saves checkpoints with the conversation, so you can still run `/rewind` after you resume a session
* Claude Code deletes checkpoints along with sessions after 30 days, following the [retention sweep rules](/docs/en/claude-directory#cleaned-up-automatically); change the period with [`cleanupPeriodDays`](/docs/en/settings#available-settings)
### Rewind and summarize
Run `/rewind`, or press `Esc` twice when the prompt input is empty, to open the rewind menu.
<Note>
If the prompt input contains text, double `Esc` clears it instead of opening the menu. The cleared text is saved to your input history, so press `Up` to recall it after you finish in the rewind menu.
</Note>
The rewind menu lists each prompt you sent during the session. Select the point you want to act on, then choose an action:
* **Restore code and conversation**: revert both code and conversation to that point
* **Restore conversation**: rewind to that message while keeping current code
* **Restore code**: revert file changes while keeping the conversation
* **Summarize from here**: compress the conversation from this point forward into a summary, freeing context window space
* **Summarize up to here**: compress the conversation before this point into a summary, keeping later messages intact
* **Never mind**: return to the message list without making changes
The two code restore options appear only when the selected checkpoint has tracked file changes to revert. If no file edits were captured after that point, the menu offers only **Restore conversation**, the summarize options, and **Never mind**.
After restoring the conversation or choosing Summarize from here, the original prompt from the selected message is restored into the input field so you can re-send or edit it.
Choosing Summarize up to here leaves you at the end of the conversation with the input empty. With either summarize option, a **Summarized conversation** marker appears in the conversation where the compressed messages were.
#### Rewind past a cleared conversation
If you ran `/clear` earlier in the same Claude Code process, the rewind menu shows an additional entry at the top of the list labeled `/resume <session-id> (previous session)`. Select it to resume the conversation that was active before `/clear` ran. The entry is available until you exit Claude Code or resume a different session, and requires Claude Code v2.1.191 or later. On earlier versions, run `/resume` and pick the previous session from the list instead.
#### Guide a summary
Summarizing doesn't change files on disk, and the original messages stay in the session transcript, so Claude can still reference the details. To guide what the summary focuses on, highlight a **Summarize** option with the arrow keys and type instructions where the row reads **add context (optional)**, then press `Enter`. Selecting the option with its number key summarizes immediately without instructions.
<Note>
Summarize keeps you in the same session and compresses context, like a targeted `/compact`. To branch off and try a different approach while preserving the original session intact, use [`/branch`](/docs/en/sessions#branch-a-session) or `claude --continue --fork-session` instead.
</Note>
## Common use cases
Checkpoints are particularly useful when:
* **Exploring alternatives**: try different implementation approaches without losing your starting point
* **Recovering from mistakes**: quickly undo changes that introduced bugs or broke functionality
* **Iterating on features**: experiment with variations knowing you can revert to working states
* **Freeing context space**: summarize a verbose debugging session from the midpoint forward, keeping your initial instructions intact
## Limitations
### Bash command changes not tracked
Checkpointing does not track files modified by bash commands. For example, if Claude Code runs:
```bash theme={null}
rm file.txt
mv old.txt new.txt
cp source.txt dest.txt
```
These file modifications cannot be undone through rewind. Only direct file edits made through Claude's file editing tools are tracked.
### Subagent edits not restored
A [subagent](/docs/en/sub-agents) makes edits with Claude's file editing tools, but Claude Code usually doesn't capture those edits in your session's checkpoints. Whether rewinding restores them depends on how the subagent runs:
* **Foreground forked skill**: a [skill with `context: fork`](/docs/en/skills#run-skills-in-a-subagent) that runs in the foreground edits your working tree during your own turn, so rewinding restores its edits as usual. Set `background: false` to run a fork in the foreground; a few situations, [listed on the skills page](/docs/en/skills#run-skills-in-a-subagent), run it there regardless of the setting.
* **Any other subagent**: rewinding doesn't restore the edits. Use git to revert them. This includes a forked skill that runs in the background, the default, and a background [`/code-review --fix`](/docs/en/code-review) run.
### External changes not tracked
Checkpointing only tracks files that have been edited within the current session. Manual changes you make to files outside of Claude Code and edits from other concurrent sessions are normally not captured, unless they happen to modify the same files as the current session.
### Symlinked and hard-linked paths not restored
Checkpointing doesn't rewind symlinked or hard-linked files. When you pick **Restore code** or **Restore code and conversation** from the `/rewind` menu, Claude Code skips any tracked path that is a symlink or hard link and shows a `Restored the code, but skipped N files` warning. The skipped files keep their current contents. To undo the session's changes to one of them, ask Claude to reverse the edit or edit the file yourself. Config files a dotfile manager symlinks into your project and files pnpm hard-links into place both fall into this category.
To see which paths a restore skips, turn on debug logging with `/debug` before you restore: the debug log at `~/.claude/debug/<session-id>.txt` names each skipped path. For every skip reason and the recovery steps, see [the skipped-files entry in the error reference](/docs/en/errors#restored-the-code-but-skipped-files).
### Not a replacement for version control
Checkpoints are designed for quick, session-level recovery. For permanent version history and collaboration, continue using version control, such as Git, for commits, branches, and long-term history.
## See also
* [Interactive mode](/docs/en/interactive-mode) - Keyboard shortcuts and session controls
* [Commands](/docs/en/commands) - Accessing checkpoints using `/rewind`
* [CLI reference](/docs/en/cli-reference) - Command-line options
chrome First recorded · 292 lines, first recorded
# Use Claude Code with Chrome ## Capabilities ## Prerequisites ## Get started in the CLI ### Install the extension when Claude asks ### Enable Chrome by default ### Manage site permissions ### Browser tools in plan mode ## Example workflows ### Test a local web application ### Debug with console logs ### Automate form filling ### Upload files to web pages ### Draft content in Google Docs ### Extract data from web pages ### Run multi-site workflows ### Record a demo GIF ### Save screenshots to disk ## Troubleshooting ### Extension not detected ### Browser not responding ### Connection drops during long sessions ### Windows-specific issues ### Common error messages ## See also
The first capture of this source. The page was already there, and this is what it said.
# Use Claude Code with Chrome
> Connect Claude Code to your Chrome browser to test web apps, debug with console logs, automate form filling, and extract data from web pages.
Claude Code integrates with the [Claude in Chrome browser extension](https://chromewebstore.google.com/detail/claude/fcoeoabgfenejglbffodgkkbkcdhcgfn) to give you browser automation capabilities from the CLI or the [VS Code extension](/docs/en/vs-code#automate-browser-tasks-with-chrome). Build your code, then test and debug in the browser without switching contexts.
Claude opens new tabs for browser tasks and shares your browser's login state, so it can access any site you're already signed into. Browser actions run in a visible Chrome window in real time. When Claude encounters a login page or CAPTCHA, it pauses and asks you to handle it manually.
<Note>
Chrome integration works with Google Chrome and Microsoft Edge. Claude Code also detects the extension and sets up the connection in other Chromium-based browsers, including Brave, Arc, Vivaldi, and Opera. Chrome integration isn't supported in Windows Subsystem for Linux (WSL).
</Note>
## Capabilities
With Chrome connected, you can chain browser actions with coding tasks in a single workflow:
* **Live debugging**: read console errors and DOM state directly, then fix the code that caused them
* **Design verification**: build a UI from a Figma mock, then open it in the browser to verify it matches
* **Web app testing**: test form validation, check for visual regressions, or verify user flows
* **Authenticated web apps**: interact with Google Docs, Gmail, Notion, or any app you're logged into without API connectors
* **Data extraction**: pull structured information from web pages and save it locally
* **Task automation**: automate repetitive browser tasks like data entry, form filling, or multi-site workflows
* **File uploads**: attach files from your machine to upload fields on web pages
* **Session recording**: record browser interactions as GIFs to document or share what happened
## Prerequisites
Before using Claude Code with Chrome, you need:
* [Google Chrome](https://www.google.com/chrome/), [Microsoft Edge](https://www.microsoft.com/edge), or another Chromium-based browser such as Brave, Arc, Vivaldi, or Opera
* [Claude in Chrome extension](https://chromewebstore.google.com/detail/claude/fcoeoabgfenejglbffodgkkbkcdhcgfn) version 1.0.36 or higher, available in the Chrome Web Store
* [Claude Code](/docs/en/quickstart#step-1-install-claude-code)
* A direct Anthropic plan (Pro, Max, Team, or Enterprise)
Chrome integration also requires signing in with `/login`. If you authenticate with an API key or a long-lived token from [`claude setup-token`](/docs/en/authentication#generate-a-long-lived-token), Claude Code keeps Chrome integration off, even when you pass `--chrome`, because the browser extension can't authenticate with those credentials. Before v2.1.216, these sessions could enable Chrome integration, but every attempt to connect to the browser extension failed with a 403 error.
<Note>
Chrome integration is not available through third-party providers like Amazon Bedrock, Google Cloud's Agent Platform, or Microsoft Foundry. If you access Claude exclusively through a third-party provider, you need a separate claude.ai account to use this feature.
</Note>
## Get started in the CLI
<Steps>
<Step title="Launch Claude Code with Chrome">
Start Claude Code with the `--chrome` flag:
```bash theme={null}
claude --chrome
```
The first time you launch with Chrome, Claude Code shows a one-time dialog that introduces the integration and explains how site permissions work. Press Enter to continue.
To enable Chrome for future sessions without the flag, see [Enable Chrome by default](#enable-chrome-by-default).
</Step>
<Step title="Ask Claude to use the browser">
This example navigates to a page, interacts with it, and reports what it finds, all from your terminal or editor:
```text wrap theme={null}
Go to code.claude.com/docs, click on the search box,
type "hooks", and tell me what results appear
```
Before Claude's first browser action, Claude Code asks for permission to use the `claude-in-chrome` skill. Approve it and Claude opens a new tab and starts the task.
</Step>
</Steps>
Run `/chrome` at any time to check the connection status, manage permissions, reconnect the extension, or choose which connected browser to use. The integration is working when the status panel shows "Status: Enabled" and "Extension: Installed".
If more than one browser is connected, you choose which one Claude uses. When a browser action starts before you've chosen, Claude prompts you to pick one. To switch browsers later, run `/chrome` and select **Select browser…**. Claude keeps using your choice even when another browser connects. Browser selection requires Claude Code v2.1.154 or later.
For VS Code, see [browser automation in VS Code](/docs/en/vs-code#automate-browser-tasks-with-chrome).
### Install the extension when Claude asks
When Claude needs your browser in an interactive session and Claude Code doesn't detect the extension, Claude Code shows an install prompt titled "Claude wants to use your browser". Claude Code asks at most once per session.
The prompt offers three choices:
* **Install extension**: opens the extension install page in your browser and starts a guided setup. Claude Code waits for the install, connects the extension, and enables browser tools in the same session. When the connection is ready, select "Continue with browser tools" and Claude resumes the task in your browser. You can leave setup by selecting "Continue without browser tools" and finish later with `/chrome`.
* **Not now**: continues the task without browser tools. Claude Code can ask again in a later session.
* **Don't ask again**: stops the prompt in future sessions. You can still set up the integration anytime with `/chrome`.
If your organization blocks the `claude-in-chrome` MCP server with the [`deniedMcpServers` managed setting](/docs/en/managed-mcp#policy-based-control-with-allowlists-and-denylists), Claude Code doesn't show the install prompt.
### Enable Chrome by default
To avoid passing `--chrome` each session, run `/chrome` and select "Enabled by default".
Claude Code starts normally when Chrome isn't running. Before v2.1.211, startup could hang when Chrome integration was enabled but Chrome wasn't running.
In the [VS Code extension](/docs/en/vs-code#automate-browser-tasks-with-chrome), Chrome is available whenever the Chrome extension is installed. No additional flag is needed.
<Note>
Enabling Chrome by default in the CLI increases context usage since browser tools are always loaded. If you notice increased context consumption, disable this setting and use `--chrome` only when needed.
</Note>
### Manage site permissions
Site-level permissions are inherited from the Chrome extension. Manage permissions in the Chrome extension settings to control which sites Claude can browse, click, and type on.
### Browser tools in plan mode
In [plan mode](/docs/en/permission-modes#analyze-before-you-edit-with-plan-mode), browser tool calls that only read the page or browser state run without a permission prompt, and calls that change state prompt for approval.
* **Read-only calls**: `read_page`, `get_page_text`, `find`, reading console messages or network requests, and taking a screenshot
* **State-changing calls**: clicks, typing, navigation, tab and window management, and recording a GIF
An otherwise read-only call also prompts for approval when it sets a state-changing input flag, such as `createIfEmpty` on `tabs_context_mcp`, `clear` on the console and network readers, or `save_to_disk` on a screenshot. A `browser_batch` call runs without a prompt only when every action inside it is read-only.
## Example workflows
These examples show common ways to combine browser actions with coding tasks. Run `/mcp`, select `claude-in-chrome`, then select **View tools** to see the full list of available browser tools.
### Test a local web application
When developing a web app, ask Claude to verify your changes work correctly:
```text wrap theme={null}
I just updated the login form validation. Can you open localhost:3000,
try submitting the form with invalid data, and check if the error
messages appear correctly?
```
Claude navigates to your local server, interacts with the form, and reports what it observes.
### Debug with console logs
Claude can read console output to help diagnose problems. Tell Claude what patterns to look for rather than asking for all console output, since logs can be verbose:
```text wrap theme={null}
Open the dashboard page and check the console for any errors when
the page loads.
```
Claude reads the console messages and can filter for specific patterns or error types.
### Automate form filling
Speed up repetitive data entry tasks:
```text wrap theme={null}
I have a spreadsheet of customer contacts in contacts.csv. For each row,
go to the CRM at crm.example.com, click "Add Contact", and fill in the
name, email, and phone fields.
```
Claude reads your local file, navigates the web interface, and enters the data for each record.
### Upload files to web pages
Claude can attach files from your machine to upload fields on a page. Claude Code reads the file and sends its contents to the browser, so uploads work in both local and remote sessions. Requires Claude Code v2.1.211 or later.
This example attaches a log file to a form:
```text wrap theme={null}
Open the bug tracker at bugs.example.com, create a new issue,
and attach logs/session.log to it
```
Three restrictions apply to uploads:
* **Permissions**: Claude can upload a file only when the session is allowed to read it, so [permission rules](/docs/en/settings#permission-settings) that deny `Read` access to a file also block uploading it.
* **Size**: a single upload can include up to 10 MB of files in total.
* **Hard links**: Claude refuses files that have multiple hard links, which is common inside package-manager stores like `node_modules`. Copy the file and upload the copy.
### Draft content in Google Docs
Use Claude to write directly in your documents without API setup:
```text wrap theme={null}
Draft a project update based on the recent commits and add it to my
Google Doc at docs.google.com/document/d/abc123
```
Claude opens the document, clicks into the editor, and types the content. This works with any web app you're logged into: Gmail, Notion, Sheets, and more.
### Extract data from web pages
Pull structured information from websites:
```text wrap theme={null}
Go to the product listings page and extract the name, price, and
availability for each item. Save the results as a CSV file.
```
Claude navigates to the page, reads the content, and compiles the data into a structured format.
### Run multi-site workflows
Coordinate tasks across multiple websites:
```text wrap theme={null}
Check my calendar for meetings tomorrow, then for each meeting with
an external attendee, look up their company website and add a note
about what they do.
```
Claude works across tabs to gather information and complete the workflow.
### Record a demo GIF
Create shareable recordings of browser interactions:
```text wrap theme={null}
Record a GIF showing how to complete the checkout flow, from adding
an item to the cart through to the confirmation page.
```
Claude records the interaction sequence and saves it as a GIF file. The recording captures everything visible in the browser, including account details on logged-in pages, so review it before sharing it outside your team.
### Save screenshots to disk
Ask Claude to keep a screenshot as a file:
```text wrap theme={null}
Take a screenshot of the checkout page and save it to disk
```
Claude saves the image to disk and reports the file path. Before v2.1.211, the screenshot tool's `save_to_disk` option didn't write a file.
## Troubleshooting
### Extension not detected
If Claude Code can't detect the Chrome extension:
1. Verify the Chrome extension is installed and enabled in `chrome://extensions`
2. Verify Claude Code is up to date by running `claude --version`
3. Check that Chrome is running
4. Run `/chrome` and select "Reconnect extension" to re-establish the connection
5. If the issue persists, restart both Claude Code and Chrome
The first time you enable Chrome integration, Claude Code installs a native messaging host configuration file. Chrome reads this file on startup, so if the extension isn't detected on your first attempt, restart Chrome to pick up the new configuration.
Claude Code opens a browser tab prompting you to connect the extension only on that first install. Claude Code doesn't reopen it when a later session rewrites the configuration file, for example after switching builds or config directories.
If the connection still fails, verify the host configuration file exists at:
For Chrome:
* **macOS**: `~/Library/Application Support/Google/Chrome/NativeMessagingHosts/com.anthropic.claude_code_browser_extension.json`
* **Linux**: `~/.config/google-chrome/NativeMessagingHosts/com.anthropic.claude_code_browser_extension.json`
* **Windows**: check `HKCU\Software\Google\Chrome\NativeMessagingHosts\` in the Windows Registry
For Edge:
* **macOS**: `~/Library/Application Support/Microsoft Edge/NativeMessagingHosts/com.anthropic.claude_code_browser_extension.json`
* **Linux**: `~/.config/microsoft-edge/NativeMessagingHosts/com.anthropic.claude_code_browser_extension.json`
* **Windows**: check `HKCU\Software\Microsoft\Edge\NativeMessagingHosts\` in the Windows Registry
Other Chromium-based browsers read the same file from their own configuration directory, named after the browser. For example, Brave on macOS uses `~/Library/Application Support/BraveSoftware/Brave-Browser/NativeMessagingHosts/`, and on Windows each browser has its own registry key, such as `HKCU\Software\BraveSoftware\Brave-Browser\NativeMessagingHosts\`.
### Browser not responding
If Claude's browser commands stop working:
1. Check if a modal dialog (alert, confirm, prompt) is blocking the page. JavaScript dialogs block browser events and prevent Claude from receiving commands. Dismiss the dialog manually, then tell Claude to continue.
2. Ask Claude to create a new tab and try again
3. Restart the Chrome extension by disabling and re-enabling it in `chrome://extensions`
### Connection drops during long sessions
The Chrome extension's service worker can go idle during extended sessions, which breaks the connection. If browser tools stop working after a period of inactivity, run `/chrome` and select "Reconnect extension".
### Windows-specific issues
On Windows, you may encounter:
* **Named pipe conflicts (EADDRINUSE)**: if another process is using the same named pipe, restart Claude Code. Close any other Claude Code sessions that might be using Chrome.
* **Native messaging host errors**: if the native messaging host crashes on startup, try reinstalling Claude Code to regenerate the host configuration.
* **Setup pages fail to open**: update Claude Code. Before v2.1.211, the browser tab prompting you to connect the extension could fail to open on Windows.
### Common error messages
These are the most frequently encountered errors and how to resolve them:
| Error | Cause | Fix |
| ------------------------------------------- | ------------------------------------------------ | --------------------------------------------------------------- |
| "Browser extension is not connected" | Native messaging host cannot reach the extension | Restart Chrome and Claude Code, then run `/chrome` to reconnect |
| Extension shows "Not detected" in `/chrome` | Chrome extension is not installed or is disabled | Install or enable the extension in `chrome://extensions` |
| "No tab available" | Claude tried to act before a tab was ready | Ask Claude to create a new tab and retry |
| "Receiving end does not exist" | Extension service worker went idle | Run `/chrome` and select "Reconnect extension" |
## See also
* [Computer use](/docs/en/computer-use): control native macOS apps when a task can't be done in a browser
* [Use Claude Code in VS Code](/docs/en/vs-code#automate-browser-tasks-with-chrome): browser automation in the VS Code extension
* [CLI reference](/docs/en/cli-reference): command-line flags including `--chrome`
* [Common workflows](/docs/en/common-workflows): more ways to use Claude Code
* [Data and privacy](/docs/en/data-usage): how Claude Code handles your data
* [Getting started with Claude in Chrome](https://support.claude.com/en/articles/12012173-getting-started-with-claude-in-chrome): full documentation for the Chrome extension, including shortcuts, scheduling, and permissions
claude-apps-gateway First recorded · 424 lines, first recorded
# Claude apps gateway for Amazon Bedrock, Claude Platform on AWS, Google Cloud, and Microsoft Foundry ## Why Claude apps gateway ### Other gateway implementations ## Quickstart ### Prerequisites ### Steps ## Connect developers ### Set the gateway URL ### Deliver policy to Claude Desktop sessions #### Which machines need the opt-in #### Set the opt-in ### Restrict parent settings #### Deploy the locks #### Lock behavior across sources #### Settings the locks don't cover ### Connect Claude Desktop ### CI pipelines and remote machines ### What the organization can see ## Availability and limitations ## Next steps
The first capture of this source. The page was already there, and this is what it said.
# Claude apps gateway for Amazon Bedrock, Claude Platform on AWS, Google Cloud, and Microsoft Foundry
> Run Claude Code through Amazon Bedrock, Claude Platform on AWS, Google Cloud, or Microsoft Foundry behind a self-hosted gateway with SSO sign-in, per-group model access, and OTLP telemetry.
<Note>
The Claude apps gateway is designed for organizations that must, or prefer to, route inference through their own cloud provider, for example to meet [data residency](/docs/en/claude-apps-gateway-deploy#compliance-posture) requirements. If you don't have this requirement, and want access to other features such as SCIM provisioning or Claude Code on web and mobile, Claude Enterprise may be a better fit. See the [feature availability](/docs/en/feature-availability) page for a full comparison of all deployment methods.
</Note>
Claude apps gateway is a self-hosted service that sits between your developers' Claude Code clients and your model provider. Developers sign in with your corporate identity provider (IdP) instead of holding API keys or cloud credentials. The gateway holds the upstream credential, enforces model access and [managed settings](/docs/en/permissions#managed-settings) by IdP group, and relays usage telemetry to your own observability stack.
It is included in the `claude` binary, so the same executable that runs Claude Code on a laptop runs the gateway server with `claude gateway --config gateway.yaml`.
This page covers:
* [Why Claude apps gateway](#why-claude-apps-gateway), what it adds over running your own, and when something else fits better
* A [quickstart](#quickstart) with [prerequisites](#prerequisites) that takes a gateway from zero to a signed-in developer
* [Connecting developers](#connect-developers), including setting the gateway URL through managed settings
* [Availability and limitations](#availability-and-limitations) covering which Claude Code features work through the gateway and what the server supports
Companion pages go deeper. The [configuration reference](/docs/en/claude-apps-gateway-config) covers every option in the YAML file the quickstart writes, and the [deployment guide](/docs/en/claude-apps-gateway-deploy) covers per-IdP setup, Kubernetes and Cloud Run deployment, and operations.
## Why Claude apps gateway
The [gateway overview](/docs/en/gateways) covers what a gateway does and why you'd run one. Claude apps gateway is Anthropic's own gateway, built into the `claude` binary and tested alongside each Claude Code release, so it forwards the headers and request fields Claude Code sends without operators maintaining a separate allowlist. Once deployed it gives you:
* **Credentials**: the upstream API key or cloud credential lives only in your infrastructure. Developers authenticate with corporate SSO and receive short-lived bearer tokens, so offboarding happens in your IdP. Deprovision a user and their gateway access expires within the session lifetime, one hour by default.
* **Access control**: your IdP groups map to model allowlists and [managed settings](/docs/en/permissions#managed-settings) policies. The gateway enforces model access server-side, rejecting requests for non-granted models, and selects each group's managed settings policy, which the CLI applies at the [managed settings tier](/docs/en/settings#settings-precedence). Different teams get different models, tools, and permissions, and a developer can't override what their policy locks.
* **Settings delivery**: the gateway delivers managed settings to signed-in clients itself, taking the place of [server-managed settings](/docs/en/server-managed-settings) from the claude.ai admin console.
* **Telemetry**: each configured destination, such as Datadog, Splunk, or ClickHouse, receives [OpenTelemetry Protocol (OTLP) metrics](/docs/en/monitoring-usage) with token counts, model, user identity, and latency by default, with logs and traces as per-destination opt-ins.
* **Upstream routing**: clients speak the Anthropic Messages API to the gateway, and the gateway translates for each upstream, whether Amazon Bedrock, [Claude Platform on AWS](/docs/en/claude-platform-on-aws), Google Cloud's Agent Platform, Microsoft Foundry, or the Anthropic API, with failover between them. You can change regions, providers, or failover order without developers noticing or reconfiguring.
<Frame>
<img src="https://mintcdn.com/claude-code/st9_ZQOFsZa3cKFl/images/claude-gateway-architecture.svg?fit=max&auto=format&n=st9_ZQOFsZa3cKFl&q=85&s=560770d8f49bbd6f1ca7090ed1f13c03" alt="Diagram showing Claude Code clients connecting over HTTPS with bearer tokens to a self-hosted Claude apps gateway inside your infrastructure, which signs users in against your IdP, stores auth state in PostgreSQL, relays telemetry to your OTLP collector, and forwards inference to Amazon Bedrock, Claude Platform on AWS, Google Cloud, Microsoft Foundry, or the Anthropic API" width="760" height="320" data-path="images/claude-gateway-architecture.svg" />
</Frame>
<Note>
The gateway's own data plane sends nothing to Anthropic infrastructure unless the Anthropic API is a configured upstream. You control where telemetry, audit logs, managed settings, and your developers' IdP identity go, and the gateway sends none of them to Anthropic. For the remaining traffic the CLI process can send and how to close it, see [Compliance posture](/docs/en/claude-apps-gateway-deploy#compliance-posture).
</Note>
For which Claude Code features work through the gateway and what the server itself supports, see [Availability and limitations](#availability-and-limitations) below. For decisions such as cost, bypass, running multiple gateways, and serverless platforms, see the [deployment guide](/docs/en/claude-apps-gateway-deploy#deployment).
### Other gateway implementations
If you already run an LLM gateway or API gateway that meets your needs, keep using it; [Other LLM gateways](/docs/en/llm-gateway) covers configuring Claude Code against it.
The [gateway protocol reference](/docs/en/llm-gateway-protocol) documents the contract Claude Code expects from any gateway: the endpoints it calls, the headers and body fields to forward, and what stops working when they're stripped. A running Claude apps gateway serves a superset of that contract at `GET /protocol`, adding the Claude apps gateway-specific endpoints for SSO sign-in, managed settings delivery, and telemetry. Fetch it with `curl https://claude-gateway.internal.example.com/protocol` from any deployed gateway, such as the one the [quickstart](#quickstart) below produces.
Breaking changes to the protocol are announced in advance, but indefinite backwards compatibility isn't guaranteed.
## Quickstart
This quickstart walks the minimal path: register an OAuth client in your IdP, write a `gateway.yaml`, run the gateway alongside Postgres with Docker Compose, and verify sign-in end to end. It uses an Amazon Bedrock upstream; Claude Platform on AWS, Google Cloud's Agent Platform, Microsoft Foundry, and the Anthropic API are equally supported by swapping the `upstreams` block as shown in the [configuration reference](/docs/en/claude-apps-gateway-config#upstreams). At the end you have a gateway a developer can `/login` to.
<Note>
**Deploy on your private network.** Claude Code only connects to a gateway whose address is private. This is a security guard, because a trusted gateway can push settings that run commands on developer machines. Put the gateway behind an internal load balancer or VPN and give it a hostname that resolves to private IPs only.
</Note>
### Prerequisites
Have these in place before you start:
| You need | Details |
| --------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Claude Code v2.1.195 or later | The `claude gateway` subcommand and the gateway sign-in flow ship in v2.1.195. Earlier public builds don't include them. Both the machine running the gateway server and each developer's machine must be on v2.1.195 or later; run `claude update` to get the latest release. The [Claude Platform on AWS upstream](/docs/en/claude-apps-gateway-config#claude-platform-on-aws) requires Claude Code v2.1.198 or later on the gateway server. |
| OpenID Connect (OIDC) identity provider | Okta, Microsoft Entra ID, Google Workspace, Keycloak, or Dex, or any other OIDC-compliant IdP such as PingFederate. The gateway runs standard OIDC discovery and the authorization-code flow against it. SAML and LDAP aren't supported. |
| PostgreSQL 14 or later | Backs the device sign-in flow, where the browser callback writes and the polling CLI reads, plus rate-limit counters. Any managed Postgres works, including the smallest tier. Without spend limits configured, the gateway stores a few KB of short-lived auth state; with [spend limits](/docs/en/claude-apps-gateway-spend-limits), it also holds durable spend, audit, and identity tables that should be backed up. TLS via `?sslmode=require` is recommended. |
| Model upstream | Amazon Bedrock credentials, Claude Platform on AWS credentials, Google Cloud credentials, a Microsoft Foundry resource, or an Anthropic API key. Multiple upstreams are supported with failover. |
| HTTPS | The gateway must be reachable over `https://` from developer laptops and from any browser used for sign-in; the gateway serves the device-verification page on the same listener. Either provide a TLS cert via `listen.tls` or run behind a TLS-terminating ingress, and set `listen.public_url` to the external origin in both cases. A plain `http://` origin is accepted only when the gateway host is loopback: `localhost`, `127.0.0.1`, or `::1`. |
| Private-network address | At `/login`, Claude Code requires the gateway's hostname or IP address to resolve only to private addresses: RFC 1918, link-local, CGNAT `100.64.0.0/10`, IPv6 ULA `fc00::/7`, or loopback. For a gateway you host, any public address is rejected; see the [threat model](/docs/en/claude-apps-gateway-deploy#threat-model-summary) in the deployment guide. The check runs on each resolved IP, so if any address the name resolves to is public, `/login` rejects the URL. If developer machines route HTTPS through a corporate proxy, sign-in also requires the proxy host to resolve to private addresses; if it doesn't, add the gateway host to `NO_PROXY` so the CLI connects directly. |
| Linux runtime | The gateway server runs only on the native Linux binary. macOS works for local development. Windows isn't supported as a server platform. |
The gateway server requires the native `claude` binary; download a pinned release as described in [Install Claude Code](/docs/en/setup). The server uses runtime features that aren't available when Claude Code runs under Node. If you see `requires the native binary` at boot, switch to one of the standalone install methods.
### Steps
<Steps>
<Step title="Register an OAuth client in your IdP">
Decide the gateway's hostname first, because the redirect URI must match it. Create a new OIDC web application and set the redirect URI to `https://claude-gateway.<your-domain>/oauth/callback`, where the host is the same value you set as [`listen.public_url`](/docs/en/claude-apps-gateway-config#listen) in step 3. Note the `client_id` and `client_secret`. Per-IdP instructions are in [Identity provider setup](/docs/en/claude-apps-gateway-deploy#identity-provider-setup).
</Step>
<Step title="Provision a PostgreSQL database">
Any Postgres 14 or later works, including the smallest managed tier. The gateway runs its own schema migrations at boot, so the database role needs rights to create and alter tables; see [`store`](/docs/en/claude-apps-gateway-config#store).
</Step>
<Step title="Write gateway.yaml">
Secrets are read via `${ENV_VAR}` expansion so the file itself can live in version control. Use a `public_url` hostname that resolves to a private IP on your network, because `/login` rejects public addresses. The minimal config has five sections, and every other field has a default:
```yaml gateway.yaml theme={null}
listen:
host: 0.0.0.0
port: 8080
# Required unless host is a loopback address. Used for the IdP
# redirect_uri and the discovery document.
public_url: https://claude-gateway.internal.example.com
oidc:
issuer: https://login.example.com # must serve /.well-known/openid-configuration
client_id: 0oa1example2
client_secret: ${OIDC_CLIENT_SECRET}
allowed_email_domains: [example.com] # reject id_tokens outside your org
userinfo_fallback: true # for IdPs whose id_token omits email/groups; harmless otherwise
session:
jwt_secret: ${GATEWAY_JWT_SECRET} # openssl rand -base64 32
ttl_hours: 1 # also bounds revocation latency on IdP deprovision
store:
postgres_url: ${GATEWAY_POSTGRES_URL} # add ?sslmode=require for managed Postgres
upstreams:
- provider: bedrock
region: us-east-1
auth: {} # empty: AWS default credential chain
# (IRSA, EC2/ECS task role, env vars, ~/.aws)
# Models are translated per upstream automatically. The built-in catalog
# maps claude-opus-4-8 to us.anthropic.claude-opus-4-8 and so on for every
# Bedrock-supported Claude model. Set false and add a `models:` list to
# expose only specific models.
auto_include_builtin_models: true
```
This config is enough for a working sign-in loop with the default Amazon Bedrock model catalog. Once it's running, add per-group RBAC and managed settings via [`managed.policies`](/docs/en/claude-apps-gateway-config#managed), telemetry fan-out via [`telemetry`](/docs/en/claude-apps-gateway-config#telemetry), and multi-upstream failover, provisioned-throughput ARNs, or non-US regions via [`models`](/docs/en/claude-apps-gateway-config#models).
<Note>
The Amazon Bedrock upstream needs an AWS principal with `bedrock:InvokeModel` and `bedrock:InvokeModelWithResponseStream` on both the `inference-profile/us.anthropic.*` ARNs and the underlying `foundation-model/anthropic.*` ARNs, and Anthropic's one-time use case form submitted for the account from the Bedrock console's Model catalog. Supply the credential with IRSA on EKS, an ECS task role, or an EC2 instance profile rather than static keys. The [`upstreams` reference](/docs/en/claude-apps-gateway-config#upstreams) has the full IAM details, the cross-cloud credential matrix, and the `auth` blocks for the other providers.
</Note>
</Step>
<Step title="Run it">
Build a container image around the `claude` binary that meets the [image requirements](/docs/en/claude-apps-gateway-deploy#container-image), then run it alongside Postgres. The Compose file references the image as `registry.example.com/claude-gateway:2.1.198`; substitute your own registry and image tag:
```yaml docker-compose.yaml theme={null}
services:
gateway:
image: registry.example.com/claude-gateway:2.1.198
ports: ["8080:8080"]
volumes: ["./gateway.yaml:/etc/claude/gateway.yaml:ro"]
environment:
OIDC_CLIENT_SECRET: ${OIDC_CLIENT_SECRET}
GATEWAY_JWT_SECRET: ${GATEWAY_JWT_SECRET}
GATEWAY_POSTGRES_URL: postgres://gw:pw@postgres/gateway
# AWS credentials: in production, omit these and use an instance
# role. For local Compose testing, pass through your own:
AWS_ACCESS_KEY_ID: ${AWS_ACCESS_KEY_ID}
AWS_SECRET_ACCESS_KEY: ${AWS_SECRET_ACCESS_KEY}
AWS_SESSION_TOKEN: ${AWS_SESSION_TOKEN}
depends_on:
postgres:
condition: service_healthy
postgres:
image: postgres:16-alpine
environment: { POSTGRES_USER: gw, POSTGRES_PASSWORD: pw, POSTGRES_DB: gateway }
healthcheck:
test: ["CMD-SHELL", "pg_isready -U gw"]
interval: 5s
volumes: ["pgdata:/var/lib/postgresql/data"]
volumes: { pgdata: }
```
The gateway is a single Linux binary that reads the config, connects to Postgres and applies its schema migrations, runs OIDC discovery against your IdP, builds upstream clients, and starts listening. Boot is fail-closed for the config, the Postgres connection with a 5-second timeout, OIDC discovery, and upstream client construction. If any of those is unreachable or misconfigured, the gateway exits with an error rather than serving traffic in a degraded state.
A successful boot doesn't validate the inference path, because Amazon Bedrock and Google Cloud's Agent Platform instance credentials resolve on the first request, not at boot.
Watch stderr for the boot sequence. Log lines use the format `[gateway] <timestamp> <level> <message>`, audit events are single-line JSON with an `evt` field, and a startup banner, omitted below, prints between the migration and listening lines. A fresh database prints one `migration N applied` line per schema migration; an already-migrated database prints none. You should see, in order:
```text theme={null}
{"ts":"2026-06-10T17:03:21.114Z","evt":"config.load","path":"/etc/claude/gateway.yaml","sha256":"…"}
[gateway] 2026-06-10T17:03:21.395Z info waiting for migration lock (another replica may be migrating; check pg_locks for key 6775156 if this persists)
[gateway] 2026-06-10T17:03:21.408Z info migration 1 applied
…
[gateway] 2026-06-10T17:03:21.431Z info migration 6 applied
[gateway] 2026-06-10T17:03:21.512Z info claude gateway listening on http://0.0.0.0:8080
```
If boot exits before the `claude gateway listening on` line, the last line of stderr names the problem:
* an unreachable Postgres
* a Postgres role without DDL permission
* an unreachable or invalid OIDC discovery document
* a config schema violation with the offending field path
Fix it and restart.
If you already have a TLS-terminating ingress, skip Compose and run the binary directly with `claude gateway --config gateway.yaml`. Set `public_url` to the ingress origin and bind `listen` to a loopback or cluster-internal address.
</Step>
<Step title="Verify the auth surface">
Three checks confirm the gateway can authenticate a real user before you hand it to a developer.
The examples use the gateway's public URL; for the local Compose setup without an ingress, substitute `http://localhost:8080` in the first two checks. The third check opens `verification_uri_complete`, which is built from `public_url`, so for local Compose set `public_url: http://localhost:8080` in `gateway.yaml`, and add `http://localhost:8080/oauth/callback` as a second redirect URI on the OAuth client from step 1, because the gateway builds the IdP `redirect_uri` from `public_url`. The verification link then opens in your local browser.
In Windows PowerShell, run `curl.exe`; the bare `curl` is an alias for `Invoke-WebRequest` and rejects these flags.
First, fetch the discovery document, which confirms the gateway is up, the config is valid, and all boot checks passed:
```bash theme={null}
curl -s https://claude-gateway.internal.example.com/.well-known/oauth-authorization-server | jq
```
```json theme={null}
{
"issuer": "https://claude-gateway.internal.example.com",
"device_authorization_endpoint": "…/oauth/device_authorization",
"token_endpoint": "…/oauth/token",
"grant_types_supported": ["urn:ietf:params:oauth:grant-type:device_code", "refresh_token"]
}
```
The response includes additional fields, such as `response_types_supported` and `scopes_supported`.
Second, request a device authorization, which confirms the device sign-in flow works and Postgres is reachable and writable:
```bash theme={null}
curl -s -X POST https://claude-gateway.internal.example.com/oauth/device_authorization | jq
```
```json theme={null}
{
"device_code": "…",
"user_code": "WDJB-MJHT",
"verification_uri": "https://claude-gateway.internal.example.com/device",
"verification_uri_complete": "https://claude-gateway.internal.example.com/device?user_code=WDJB-MJHT",
"expires_in": 600,
"interval": 5
}
```
Third, test the browser leg by opening `verification_uri_complete` in a browser and confirming the code. You should be redirected to your IdP's sign-in page, and after signing in, land back on the gateway with a signed-in confirmation.
Use the first failing check to locate the problem:
* **First check fails**: boot didn't complete; check stderr
* **Second check fails**: Postgres isn't reachable from the gateway or the role can't write; check the connection string and grants
* **Third check doesn't reach the IdP**: check that the IdP's redirect URI matches `https://<gateway>/oauth/callback` exactly
* **Third check reaches the IdP but bounces back with an error**: read the gateway's audit log, which records every auth rejection with the reason, such as `email domain not allowed`
</Step>
<Step title="Log a developer in">
This last step happens on a developer machine, not the server. Set `forceLoginMethod` to `"gateway"` and `forceLoginGatewayUrl` to your gateway's `public_url` in that machine's [managed settings file](/docs/en/settings#settings-files), then run `/login`, press Enter on the **Cloud gateway** screen, and complete the browser sign-in. [Set the gateway URL](#set-the-gateway-url) below covers distributing both keys at scale.
</Step>
</Steps>
## Connect developers
Developers connect from their own laptops with one browser sign-in, using their corporate work account. They don't need a claude.ai account, an API key, or a subscription, because requests to the model go through the gateway using the organization's upstream credential. Connection is driven by the [client-side managed settings](/docs/en/claude-apps-gateway-config#client-side-managed-settings) you push via MDM, so there is no manual setup on the developer side; this section covers what the admin configures.
The CLI fingerprints the gateway's TLS leaf certificate on first connect and pins it per hostname. Publish the expected SHA-256 fingerprint alongside the gateway URL so developers have something to compare against. Get the fingerprint from the certificate file with `openssl x509 -noout -fingerprint -sha256 -in cert.pem`; the `/login` prompt shows the first 16 characters of the digest as lowercase hexadecimal with no separators.
When the certificate rotates, every developer sees the trust prompt again, so treat rotations as a planned event and republish the fingerprint.
Once signed in, the [model picker](/docs/en/model-config) shows the models in the developer's `availableModels` allowlist, managed settings apply at startup and refresh hourly, and telemetry routes to your collector. Sessions refresh silently before `ttl_hours` expiry, and a failed refresh after IdP deprovisioning prompts a re-login.
### Set the gateway URL
Three keys go in the per-OS [managed settings file](/docs/en/settings#settings-files) you deploy via MDM or directly on disk. `forceLoginMethod` and `forceLoginGatewayUrl` open `/login` directly on the **Cloud gateway** screen with the URL filled in, and `parentSettingsBehavior: "merge"` lets Claude Desktop deliver the gateway's egress allowlist to the Claude Code sessions it launches, explained in [Deliver policy to Claude Desktop sessions](#deliver-policy-to-claude-desktop-sessions):
```json theme={null}
{
"forceLoginMethod": "gateway",
"forceLoginGatewayUrl": "https://claude-gateway.internal.example.com",
"parentSettingsBehavior": "merge"
}
```
The developer presses Enter to connect. The [first-connect TLS fingerprint prompt](#connect-developers) still appears.
A developer can't set this up manually. The login picker has no gateway option, and `forceLoginGatewayUrl` is ignored in a developer's own settings files. `forceLoginMethod` alone, without a URL, leaves the developer at a "Contact your IT administrator" message. The login keys belong in the file you push to machines, not in the gateway's `managed.policies[].cli` block, which only reaches clients that are already connected.
### Deliver policy to Claude Desktop sessions
Claude Desktop runs embedded Claude Code sessions and passes policy to each one it launches. It builds that policy from the configuration the gateway serves it at `/user/bootstrap`: the model allowlist, disabled tools, and egress allowlist derived from the matched policy's `cli` block, plus the [`desktop` overlay](/docs/en/claude-apps-gateway-config#claude-desktop-overlay). Other `cli` keys, such as hooks, `env`, and scoped permission rules like `Bash(npm *)`, reach only clients that sign in through `/login`. Claude Desktop is pointed at the gateway through its own managed configuration and signs in with its own flow, separate from the `forceLoginMethod` and `forceLoginGatewayUrl` keys in [Set the gateway URL](#set-the-gateway-url).
Settings passed by a launching process are parent settings. Claude Code ignores parent settings on any machine that has an admin-deployed managed source, unless the highest-priority source sets `parentSettingsBehavior: "merge"`.
#### Which machines need the opt-in
Machines that only run Claude Desktop need it. Claude Desktop applies the model list and the disabled-tools list to embedded sessions itself, but the egress allowlist reaches them only as parent settings, in the form of `WebFetch` domain rules and sandbox network rules. Without the opt-in, those sessions run without the egress restriction, and nothing warns you. The gateway still rejects inference requests for models the policy doesn't grant.
Machines where developers sign in through `/login` don't need it; every Claude Code invocation fetches its policy from the gateway directly. Fleets that configure a [`policyHelper`](/docs/en/settings#compute-managed-settings-with-a-policy-helper) can't use it, because the helper's output replaces every other managed source and parent settings are never merged while a helper is configured.
#### Set the opt-in
Deploy the key, mirror it to the source that wins on each machine, then verify.
<Steps>
<Step title="Deploy the opt-in in the managed settings file">
The [snippet above](#set-the-gateway-url) already includes `parentSettingsBehavior: "merge"`, so the file you push to machines carries it.
</Step>
<Step title="Set the same key in any source that outranks the file">
Only the highest-priority admin source's value counts. A managed-preferences plist on macOS or an HKLM policy on Windows outranks the `managed-settings.json` file, and the gateway's own remote managed settings outrank both, so on machines that sign in to the gateway, also set the key in the gateway policy's [`cli` block](/docs/en/claude-apps-gateway-config#managed).
</Step>
<Step title="Check which source won">
Call the Agent SDK's [`resolveSettings()`](/docs/en/agent-sdk/typescript#resolvesettings). Its result includes a `sources` list; the managed policy entry there carries a `policyOrigin` field naming the active source. `resolveSettings()` doesn't execute a configured `policyHelper`, so on helper fleets its answer doesn't reflect the live session.
</Step>
</Steps>
### Restrict parent settings
Cut at 300 lines. The page has the rest.
claude-apps-gateway-config First recorded · 887 lines, first recorded
# Claude apps gateway configuration ## File structure ## Secret expansion ## Required sections ### `listen` ### `oidc` #### IdP requests through a forward proxy ### `session` ### `store` ### `upstreams` #### Anthropic API #### Amazon Bedrock #### Claude Platform on AWS #### Google Cloud Agent Platform #### Microsoft Foundry #### Multiple upstreams ## Optional sections ### `admin` ### `enforcement` ### `pricing` ### `models` ### `managed` #### What goes in `cli` #### Claude Desktop overlay #### Precedence with other managed sources ### `telemetry` ### HTTP tuning ## Complete example ## Client-side managed settings ## Related
The first capture of this source. The page was already there, and this is what it said.
# Claude apps gateway configuration
> Reference for every gateway.yaml option: listener and TLS, OIDC, session, Postgres store, Amazon Bedrock, Claude Platform on AWS, Google Cloud's Agent Platform, and Microsoft Foundry upstreams, model routing, managed policies, and telemetry.
A Claude apps gateway deployment is configured by one YAML file, conventionally `gateway.yaml`. The file defines everything the gateway does: where it listens, how developers sign in, where inference goes, and which policies and telemetry apply. This page is the reference for every option in that file.
To write your first one, start from the [quickstart](/docs/en/claude-apps-gateway#quickstart), which builds a minimal working config and runs it. Once you have a config you're happy with, the [deployment guide](/docs/en/claude-apps-gateway-deploy) covers containerizing and hosting it on Kubernetes, Cloud Run, or your own platform.
The gateway reads the file once, at startup, with `claude gateway --config /path/to/gateway.yaml`. Every option is validated against a schema at boot, so a malformed config fails at start with a field-level error rather than at first use.
The [complete example](#complete-example) at the end of this page exercises every section.
## File structure
Five sections are [required](#required-sections). Every other section is [optional](#optional-sections), and an omitted section takes its defaults. Unknown keys fail boot, so a typo surfaces as a named error rather than a silently ignored setting.
**Required sections:**
* [`listen`](#listen): bind address, public URL, TLS termination
* [`oidc`](#oidc): your identity provider (IdP), including issuer, client, claim mapping, and who may sign in
* [`session`](#session): the bearer tokens the gateway mints, with secret and lifetime
* [`store`](#store): PostgreSQL, for device grants and rate-limit counters
* [`upstreams`](#upstreams): where inference goes, whether Anthropic, Amazon Bedrock, Claude Platform on AWS, Google Cloud's Agent Platform, or Microsoft Foundry
**Optional sections:**
* [`admin`](#admin): Admin API auth and retention for spend limits
* [`enforcement`](#enforcement): spend-limit fail-open or fail-closed behavior
* [`pricing`](#pricing): contracted rates and a discount multiplier for the spend meter
* [`models`](#models) and `auto_include_builtin_models`: admin-curated model list and per-upstream IDs
* [`managed`](#managed): managed settings policies by IdP group
* [`telemetry`](#telemetry): OTLP forwarding to your observability stack
* [`access_control`, `limits`, `timeouts`, `rate_limits`](#http-tuning): IP allow/deny, request size caps, upstream time-to-first-byte, and per-IP sign-in limits
## Secret expansion
Don't write secrets such as `client_secret`, `jwt_secret`, or `postgres_url` directly in `gateway.yaml`. Reference them with one of the forms below, and the gateway resolves the value at boot from an environment variable or a file:
| Form | Resolves to | Use for |
| --------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------- |
| `${VAR}` | The environment variable `VAR`. Boot fails if undefined. | Container environment variables, AWS Secrets Manager via env injection |
| `${file:/path}` | Contents of the file at that absolute path, trimmed. The reference must be the field's entire value: unlike `${VAR}`, it isn't expanded inside a longer string, so for a database password set `store.password` rather than embedding it in `postgres_url`. | Kubernetes Secret volume mounts, Vault Agent, SOPS |
## Required sections
### `listen`
The `listen` block controls where the gateway serves: the bind address and port, the externally visible origin, and optional TLS termination.
| Field | Required | Description |
| ---------------------- | ------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `host` | No | Bind address. Default `0.0.0.0`. |
| `port` | No | Bind port. Default `8080`. |
| `public_url` | Unless `host` is loopback | The externally visible `https://` origin, used to build the IdP `redirect_uri` and discovery metadata. Required whenever `host` isn't a loopback address, whether TLS terminates at a proxy such as an ALB, Ingress, or Cloud Run or at the gateway itself through `tls`, because the gateway never derives its own origin from `X-Forwarded-*` headers; they are client-spoofable. Boot fails without it. `trusted_proxies` below governs client-IP resolution only. Also required to enable [telemetry](#telemetry), because the gateway builds the OTLP endpoint it pushes to clients from this URL. |
| `tls.cert` / `tls.key` | No | PEM paths if the gateway terminates TLS itself |
| `trusted_proxies` | No | CIDRs or IPs of load balancers in front of the gateway. When set, the gateway trusts `X-Forwarded-For` only from these peers and records the real client IP for per-IP rate limiting and audit. Equivalent to nginx `set_real_ip_from`. |
### `oidc`
The `oidc` block connects the gateway to your identity provider and decides who can sign in. It names the issuer and OAuth client, maps the claims that carry email and groups, and restricts sign-in by email domain or group.
OpenID Connect (OIDC) is the SSO protocol the gateway uses with your identity provider; see [Identity provider setup](/docs/en/claude-apps-gateway-deploy#identity-provider-setup) for what to register on the IdP side.
| Field | Required | Description |
| ------------------------------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `issuer` | Yes | OIDC discovery base. Must serve discovery at `/.well-known/openid-configuration`. Use HTTPS in production; the gateway accepts an `http://` issuer. A loopback issuer such as `http://localhost:8081` is rejected by the [SSRF guard](/docs/en/claude-apps-gateway-deploy#threat-model-summary) unless `CLAUDE_GATEWAY_ALLOW_LOOPBACK=1` is set in the gateway's environment. |
| `client_id` / `client_secret` | Yes | From your OAuth client registration |
| `allowed_email_domains` | No | Reject id\_tokens whose `email` claim isn't in one of these domains, case-insensitive. Defense-in-depth against multi-tenant IdP misconfiguration. Independent of this setting, an id\_token whose `email_verified` claim is explicitly `false` is always rejected. |
| `allowed_groups` | No | Restrict sign-in to members of these IdP groups, matched against `groups_claim`. A user in an allowed email domain but in none of these groups is rejected. Requires the IdP to emit the groups claim. Matching is an exact, case-sensitive string comparison against the values in that claim, and the gateway doesn't expand nested groups: to admit members of a sub-group, list the sub-group here or configure the IdP to emit flattened membership. |
| `groups_claim` | No | Which id\_token claim carries group membership. Default `groups`. Microsoft Entra emits app roles under `roles`. Accepts a flat key or an RFC 6901 JSON Pointer such as `/resource_access/gateway/roles` for nested claims. |
| `google_groups` | No | Look up the signed-in user's groups through the Google Workspace Admin SDK Directory API, because Google's id\_token carries no groups claim. Set `service_account_json_path` to a service-account key file with domain-wide delegation on the `https://www.googleapis.com/auth/admin.directory.group.readonly` scope, and `admin_email` to a Workspace administrator the service account impersonates; the Directory API requires a real admin subject. Each user's group email addresses become their groups claim, so `allowed_groups` and `managed.policies.match.groups` match on group emails. |
| `email_claim` | No | Which id\_token claim carries the user's email. Default `email`. Some IdPs, such as ADFS and Entra B2C, emit `upn` or `preferred_username` instead. Accepts a flat key, a JSON Pointer, or a list of fallback keys where the first present key is used. |
| `scopes` | No | Full override of the OIDC scopes the gateway requests. Default `[openid, profile, email, offline_access]`. Set when your IdP rejects scopes it doesn't recognize, or requires a custom scope to emit groups or email. Must include `openid`. Dropping `offline_access` disables refresh tokens, so developers re-run the browser login every `session.ttl_hours`. See [Identity provider setup](/docs/en/claude-apps-gateway-deploy#identity-provider-setup) for per-IdP scope recipes such as Google's refresh-token flow. |
| `extra_auth_params` | No | Extra query parameters appended to the IdP authorization request, verbatim. This is the override mechanism for IdP-specific behavior, such as `access_type: offline` for Google refresh tokens, `domain_hint` for some Entra tenants, or `acr_values` for step-up flows. Cannot override the gateway-managed protocol params: `state`, `nonce`, `redirect_uri`, PKCE, `scope`, `response_type`, `response_mode`, and `client_id`. |
| `userinfo_fallback` | No | When the id\_token omits email or groups, fetch them from `/userinfo`. Needed for Keycloak lightweight access tokens, the Okta org server, and ADFS minimal tokens. The id\_token stays authoritative; userinfo only fills gaps. Default `false`. |
| `use_pkce` | No | Send a PKCE (S256) challenge on the authorization request. Default `true`. Set `false` only if your IdP rejects PKCE for this confidential client. |
| `clock_skew_seconds` | No | Tolerate clock drift when validating id\_token time claims. Default `0`, which is strict. Raise if you see "token expired / not yet valid" errors right after sign-in due to host/IdP clock skew. |
| `token_endpoint_auth_method` | No | Override the token-endpoint auth method. Accepts `client_secret_basic` or `client_secret_post`. Auto-negotiated by default. |
| `id_token_signed_response_alg` | No | Expected id\_token signing algorithm. Default `RS256`. Set for IdPs that sign with ES256, PS256, or EdDSA. |
| `additional_authorized_parties` | No | Extra `azp` values to accept beyond `client_id`, for Keycloak broker and token-exchange flows |
| `discovery_url` | No | Fetch the discovery document from this URL instead of deriving it from `issuer`, for IdPs behind a proxy that rewrites the issuer host. The path must contain `/.well-known/`. |
| `use_proxy` | No | Send the gateway's own IdP requests through the forward proxy in `HTTPS_PROXY` or `HTTP_PROXY`, honoring `NO_PROXY`. Unset or `false`, those requests go direct. Requires v2.1.227 or later; see [IdP requests through a forward proxy](#idp-requests-through-a-forward-proxy) below. |
| `form_action_origins` | No | Additional origins for the `/device` page's `Content-Security-Policy: form-action` directive. The gateway already allows `'self'` and the discovered `authorization_endpoint` origin, but Chrome enforces `form-action` against the entire redirect chain. If your IdP redirects through a second host, such as Azure AD federated to ADFS, hub-spoke Okta, or a corporate SSO interceptor, list every origin the authorization request may redirect through. |
| `ca_cert_pem` | No | The PEM-encoded CA certificate itself, not a path to a file. It replaces the system trust store for IdP requests only. To load a mounted file, write `${file:/etc/gateway/idp-ca.pem}`. Use for Keycloak or Dex behind corporate PKI. |
#### IdP requests through a forward proxy
The inference upstreams honor `HTTPS_PROXY` and `HTTP_PROXY` on every version. The gateway's own requests to the IdP, discovery, JWKS, token, and userinfo, go direct unless you set `oidc.use_proxy: true`, which requires v2.1.227 or later. When a proxy variable is set, `use_proxy` is unset, and the issuer isn't covered by `NO_PROXY`, the gateway keeps those requests direct and logs a notice at boot asking you to choose; `use_proxy: false` keeps them direct and silences the notice.
With `use_proxy: true`, the pod resolves each IdP endpoint's hostname itself and asks the proxy to `CONNECT` to the resolved IP address, so the proxy must accept `CONNECT` to the IP address of every host the discovery document names, not only the issuer. Use an `http://` proxy URL. `ca_cert_pem` and the [SSRF guard](/docs/en/claude-apps-gateway-deploy#threat-model-summary) apply on the proxied path as well.
### `session`
The `session` block shapes the bearer tokens the gateway mints after sign-in: the secret that signs them and how long they live.
| Field | Required | Description |
| ------------ | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `jwt_secret` | Yes | At least 32 bytes of entropy, for example from `openssl rand -base64 32`. Signs the gateway's HS256 bearer tokens. Accepts a single string or an array for rotation: index 0 signs and all entries verify. To rotate, prepend a new secret, wait `ttl_hours`, then drop the old one. |
| `ttl_hours` | No | Gateway bearer token lifetime. Default `1`. The CLI silently refreshes before expiry when the IdP issues refresh tokens. A shorter lifetime deprovisions faster; a longer one makes fewer IdP round-trips. If your IdP can't issue refresh tokens because `offline_access` is unavailable, there is no silent refresh, so raise this to `8` or `12` to avoid sending developers back to the browser login every hour. |
### `store`
The `store` block points the gateway at its PostgreSQL database, which holds device grants and rate-limit counters.
| Field | Required | Description |
| ----------------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `postgres_url` | Yes | `postgres://` or `postgresql://` URL. Required: the device-grant rendezvous, where the browser callback writes and the polling CLI reads, needs cross-replica state. The gateway runs its own schema migrations at boot and on upgrade, so the role needs rights to create and alter tables on the target schema. See [Upgrades](/docs/en/claude-apps-gateway-deploy#upgrades) and [Postgres](/docs/en/claude-apps-gateway-deploy#postgres). |
| `username` | No | Overrides the user in `postgres_url` |
| `password` | No | Database credential. Set it here rather than in `postgres_url` so the credential stays out of the URL. Accepts any characters and takes precedence over URL credentials. |
| `max_connections` | No | Postgres connection-pool size per replica. Default `5`, which is conservative and friendly to shared databases. With [spend limits](#admin) enabled, the hot path does a few operations per inference request, so raise it for a dedicated database under load, and keep replicas × this below the database's `max_connections`. |
For local development, point `postgres_url` at a throwaway Postgres container, for example `docker run --rm -p 5432:5432 -e POSTGRES_HOST_AUTH_METHOD=trust postgres`.
### `upstreams`
`upstreams` is an ordered list. The gateway forwards inference to the first upstream that resolves the requested model. On `5xx`, `429`, `401`, `403`, `404`, or timeout it fails over to the next; other `4xx` doesn't, because those errors are attributable to the request rather than the upstream. A `401` or `403` means the gateway's own credential failed against that upstream, and a `404` means that upstream doesn't serve the requested model, so a later upstream in the list still can.
Failover on `404` requires gateway v2.1.198 or later. Earlier releases returned the first `404` to the client even when a later upstream in the list served the model.
Multiple upstreams of the same provider must set a distinct `name:`.
Amazon Bedrock, Claude Platform on AWS, Google Cloud's Agent Platform, and Microsoft Foundry clients are built once at startup, and their SDKs refresh credentials internally, so rotating cloud credentials doesn't require a restart. Static Anthropic API keys and bearers are read at startup; see [Anthropic API](#anthropic-api).
#### Anthropic API
The minimal Anthropic upstream is an API key from the [Claude Console](https://platform.claude.com):
```yaml theme={null}
upstreams:
- provider: anthropic
auth:
api_key: ${ANTHROPIC_API_KEY}
# OR an OAuth bearer (e.g. a Workload-Identity-Federation-exchanged token):
# oauth_token: ${file:/var/run/secrets/anthropic-oauth-token}
# base_url: https://api.anthropic.com # default; override for a forward proxy
```
The two credential forms differ in the header they send:
* **`api_key`**: sends `x-api-key`. Rotate it in the Claude Console and update the env var.
* **`oauth_token`**: sends `Authorization: Bearer`. Use the bearer form when your org issues short-lived tokens instead of long-lived API keys. The bearer is read once at startup, so refresh by remounting the secret and restarting.
Instead of a static key or bearer, you can use Workload Identity Federation. Create a federation rule by following the [Workload Identity Federation guide](https://platform.claude.com/docs/en/manage-claude/workload-identity-federation), then mount your workload's OIDC JWT as a file, such as a Kubernetes projected service-account token or a CI platform's id-token. The gateway exchanges the JWT for a short-lived bearer and refreshes it automatically. The token file is re-read on every exchange, so rotated projected tokens are picked up without a restart.
```yaml theme={null}
upstreams:
- provider: anthropic
auth:
federation_rule_id: ${ANTHROPIC_FEDERATION_RULE_ID}
organization_id: ${ANTHROPIC_ORGANIZATION_ID}
identity_token_file: /var/run/secrets/anthropic/id-token
# workspace_id: wrkspc_... # required if the rule covers >1 workspace
# service_account_id: svac_... # optional expected-target check
```
#### Amazon Bedrock
For the client-side Amazon Bedrock deployment that the gateway replaces or fronts, see [Claude Code on Amazon Bedrock](/docs/en/amazon-bedrock). The gateway-side upstream:
```yaml theme={null}
upstreams:
- provider: bedrock
region: us-east-1
auth: {} # preferred: AWS default credential chain
# OR explicit credentials:
# auth:
# aws_access_key_id: ${AWS_AKID}
# aws_secret_access_key: ${AWS_SK}
# aws_session_token: ${AWS_ST}
# OR a Bedrock API bearer token:
# auth:
# aws_bearer_token: ${AWS_BEARER_TOKEN}
# Override the bedrock-runtime endpoint for FIPS or VPC-endpoint deployments:
# base_url: https://bedrock-runtime-fips.us-east-1.amazonaws.com
```
An empty `auth` block uses the AWS SDK's default credential chain: env vars, `~/.aws/credentials`, ECS task role, EC2 instance metadata, or IRSA on EKS. In production, give the gateway pod an IAM role instead of embedding static keys in a container image.
Explicit credentials must be complete: the gateway fails at boot when `aws_access_key_id` and `aws_secret_access_key` aren't set together, or when `aws_session_token` is set without them. Before v2.1.207, a partial `auth:` block passed validation.
| Setup | How |
| --------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| IAM permissions | Grant the gateway's principal `bedrock:InvokeModel` and `bedrock:InvokeModelWithResponseStream` on both the inference-profile ARNs and the underlying foundation-model ARNs. For the built-in catalog in US regions: `arn:aws:bedrock:<region>:<account>:inference-profile/us.anthropic.*` and `arn:aws:bedrock:*::foundation-model/anthropic.*`. |
| Model access | Amazon Bedrock enables model access by default in commercial regions. The remaining account-level gate is Anthropic's one-time use case form: if no one in your AWS account has submitted it, open the Amazon Bedrock console, select an Anthropic model from the Model catalog, and complete the form. See [Submit use case details](/docs/en/amazon-bedrock#1-submit-use-case-details) for the AWS Organizations form and the permissions the submitter needs. |
| EKS (IRSA) | Create an IAM role with the policy above and a trust policy for your cluster's OIDC provider scoped to the gateway's service account. Annotate the service account with `eks.amazonaws.com/role-arn: arn:aws:iam::<acct>:role/claude-gateway`. `auth: {}` picks it up. |
| ECS / EC2 | Attach the IAM role to the task definition or instance profile. `auth: {}` picks it up. |
| Anywhere else | Pass credentials via the `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, and `AWS_SESSION_TOKEN` env vars, or set them explicitly in `auth:` with `${VAR}` expansion |
| Region | `region:` is the API endpoint region. Cross-region inference profiles route across the geo (US, EU, APAC) regardless of which one you pick. For non-US regions or provisioned-throughput ARNs, add a [`models:`](#models) block with the right per-upstream IDs. |
#### Claude Platform on AWS
Claude Platform on AWS serves the first-party Anthropic API on AWS infrastructure at `aws-external-anthropic.<region>.api.aws`. It uses first-party model IDs, honors `anthropic-beta` headers as sent, and serves `count_tokens`, so none of the Bedrock-specific translation applies. The `anthropicAws` provider requires Claude Code v2.1.198 or later; earlier gateway releases reject it at boot.
For the client-side deployment of the same platform, see [Claude Code on Claude Platform on AWS](/docs/en/claude-platform-on-aws). The gateway-side upstream:
```yaml theme={null}
upstreams:
- provider: anthropicAws
region: us-east-1
workspace_id: wrkspc_...
auth:
api_key: ${ANTHROPIC_AWS_API_KEY} # sent as x-api-key
# OR SigV4 via the AWS default credential chain:
# auth: {}
# OR explicit SigV4 credentials:
# auth:
# aws_access_key_id: ${AWS_ACCESS_KEY_ID}
# aws_secret_access_key: ${AWS_SECRET_ACCESS_KEY}
# Override the derived endpoint:
# base_url: https://aws-external-anthropic.us-east-1.api.aws
```
The platform runs in a separate AWS account from Amazon Bedrock and signs SigV4 requests for its own service name, `aws-external-anthropic`, so a Bedrock-scoped IAM role doesn't authorize it. An API key in `auth.api_key` takes precedence when SigV4 credentials are also set. An empty `auth` block uses the AWS SDK's default credential chain, the same chain the [Amazon Bedrock](#amazon-bedrock) upstream uses.
| Field | Required | Description |
| ------------------------------------------------------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------- |
| `region` | Yes | AWS region, lowercase letters, digits, and hyphens. The gateway derives the endpoint from it as `https://aws-external-anthropic.<region>.api.aws`. |
| `workspace_id` | Yes | Sent as a header on every request; the platform requires it |
| `auth.api_key` | No | API key for the platform, sent as `x-api-key`. Not a bearer token: the two auth modes are an API key or SigV4. |
| `auth.aws_access_key_id` / `auth.aws_secret_access_key` | No | Explicit SigV4 credentials. Setting one without the other fails at boot. `auth.aws_session_token` is accepted alongside them. |
| `base_url` | No | Override the derived endpoint |
Because the platform resolves first-party model IDs, the built-in catalog routes to it with no [`models:`](#models) block. When you curate a `models:` list, key the entry `anthropicAws:` with the first-party ID.
#### Google Cloud Agent Platform
For the equivalent client-side setup, see [Claude Code on Google Cloud](/docs/en/google-vertex-ai). The gateway-side upstream:
```yaml theme={null}
upstreams:
- provider: vertex
region: us-east5
project_id: example-prod
auth: {} # preferred: Application Default Credentials
# OR a service account key file:
# auth: { service_account_json: /secrets/sa.json }
# Override the aiplatform endpoint for Private Service Connect:
# base_url: https://us-east5-aiplatform.p.googleapis.com
```
An empty `auth` block uses Application Default Credentials: `GOOGLE_APPLICATION_CREDENTIALS`, GCE metadata, or GKE Workload Identity. Service-account JSON key files are supported but discouraged; use Workload Identity or attach a service account to the GCE or Cloud Run instance.
Set `region: global` to use the [global endpoint for Google Cloud's Agent Platform](https://cloud.google.com/vertex-ai/generative-ai/docs/learn/locations) instead of a regional one. Google then routes each request to an available region, so you don't track per-region model availability. Setting a specific region pins every request to it.
| Setup | How |
| ----------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| IAM permissions | Grant the gateway's service account `roles/aiplatform.user` on the project, or a custom role with `aiplatform.endpoints.predict`. Enable Google Cloud's Agent Platform API (`aiplatform.googleapis.com`). |
| Model access | In Model Garden, enable the Claude models for your project. They publish to specific regions; check the model card for supported regions. |
| GKE (Workload Identity) | Bind a GCP service account to the gateway's Kubernetes service account and annotate the KSA with `iam.gke.io/gcp-service-account: claude-gateway@<proj>.iam.gserviceaccount.com`. `auth: {}` picks it up. |
| Cloud Run / GCE | Set the service's service account to one with `roles/aiplatform.user`. `auth: {}` picks it up. |
| Anywhere else | `auth: { service_account_json: /secrets/sa.json }`, the path to a JSON key file mounted as a secret. The field takes a file path, not the key contents, so no `${file:…}` expansion is involved. |
#### Microsoft Foundry
For the client-side Microsoft Foundry deployment, see [Claude Code on Microsoft Foundry](/docs/en/microsoft-foundry). The gateway-side upstream:
```yaml theme={null}
upstreams:
- provider: foundry
resource: example-foundry # https://example-foundry.services.ai.azure.com
auth: { use_azure_ad: true } # preferred: DefaultAzureCredential / Managed Identity
# OR an API key:
# auth:
# api_key: ${FOUNDRY_API_KEY}
```
`use_azure_ad: true` resolves through `DefaultAzureCredential`: Managed Identity on AKS, ACI, or App Service; the Azure CLI; or environment credentials. API keys work but are project-wide and don't rotate automatically. Microsoft Foundry's endpoint is derived from `resource:`; set the optional `base_url` to override it for sovereign clouds such as Azure Government.
| Setup | How |
| ----------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| RBAC | Grant the gateway's identity `Azure AI User` or `Cognitive Services User` on the Microsoft Foundry resource |
| Deployments | Microsoft Foundry uses admin-chosen deployment names, not canonical model IDs. Add a [`models:`](#models) block mapping each canonical ID to your deployment name. |
| AKS (workload identity) | Federate a User-Assigned Managed Identity with the cluster's OIDC issuer and bind it to the gateway's service account. `use_azure_ad: true` picks it up via `WorkloadIdentityCredential`. |
| ACI / App Service | Enable system-assigned or user-assigned managed identity on the resource. `use_azure_ad: true` picks it up. |
| Anywhere else | `auth: { api_key: "${FOUNDRY_API_KEY}" }`. Quote `${…}` inside `{ }`. |
#### Multiple upstreams
The same provider can appear more than once with a distinct `name:`. This covers different regions, different accounts via different credential chains, provisioned throughput versus on-demand, and cross-provider fallback.
The gateway tries upstreams in order. `5xx`, `429`, `401`, `403`, `404`, timeouts, and missing-endpoint (`501`) fail over; other `4xx` doesn't.
`429` is per-upstream capacity, so provisioned-throughput (PT) exhaustion fails over to on-demand. `404` is per-upstream model availability, so an upstream that hasn't enabled a model doesn't block a later upstream that serves it. An upstream that can't resolve the requested model is skipped without a network round-trip.
This example routes a provisioned-throughput Amazon Bedrock allotment first, overflows to on-demand and a second account, and falls back to the Anthropic API last:
```yaml theme={null}
upstreams:
# Primary: provisioned throughput in your home region.
- name: bedrock-pt
provider: bedrock
region: us-east-1
auth: {}
# Overflow: on-demand cross-region.
- name: bedrock-od
provider: bedrock
region: us-west-2
auth: {}
# Different account: a separate Bedrock allotment via assumed-role creds.
- name: bedrock-acct2
Cut at 300 lines. The page has the rest.
claude-apps-gateway-deploy First recorded · 269 lines, first recorded
# Claude apps gateway deployment and operations ## Identity provider setup ## Deployment ### Container image ### Kubernetes ### Cloud Run ### Push the gateway URL to developer machines ## Operations ### Logs ### Health ### Outage behavior ### JWT secret rotation ### Postgres ### Upgrades ## Security ### Data flow ### Threat model summary ### User-code brute-force resistance ### Compliance posture ## Troubleshooting ## Related
The first capture of this source. The page was already there, and this is what it said.
# Claude apps gateway deployment and operations
> Register the gateway with your IdP, build the container, deploy on Kubernetes or Cloud Run, and operate it: health checks, secret rotation, upgrades, and security.
This page covers the operational side of running [Claude apps gateway](/docs/en/claude-apps-gateway): registering an OAuth client in your identity provider (IdP), deploying the gateway as a container, and running it day-to-day. For every option in the `gateway.yaml` file the gateway reads at boot, see the [Configuration reference](/docs/en/claude-apps-gateway-config).
A production deployment follows four steps in order, and the sections below match them. The first two are where you make choices; the second two are reference material to consult once it's running.
1. [Set up your identity provider](#identity-provider-setup): register the OAuth client and check the per-IdP notes for Okta, Entra, and Google
2. [Deploy the gateway](#deployment): build a pinned container image and run it on Kubernetes, Cloud Run, or your own platform. This section also covers cost, bypass, multiple-gateway, and serverless decisions
3. [Set up operations](#operations): logs, health probes, outage behavior, secret rotation, and upgrades. Reference for when you're setting up monitoring and runbooks
4. [Review the security posture](#security): what data flows where, the threat model, and compliance answers. Reference for a security review
If a sign-in or boot fails along the way, go straight to [Troubleshooting](#troubleshooting), which is keyed on the error you see.
<Note>
**Deploy on your private network.** Claude Code only connects to a gateway whose address is private. This is a security guard, because a trusted gateway can push settings that run commands on developer machines. Put the gateway you deploy behind an internal load balancer or VPN and give it a hostname that resolves to private IPs only.
</Note>
## Identity provider setup
Register a confidential OAuth/OpenID Connect (OIDC) web application with a single redirect URI, `https://<gateway>/oauth/callback`, and assign it to the users or groups who should have gateway access.
Any OIDC-compliant IdP works: Okta, Microsoft Entra ID, Google Workspace, Keycloak, Dex, PingFederate, and others. The IdP must meet three requirements:
* Serves `/.well-known/openid-configuration`, over HTTPS in production; the gateway accepts an [`http://` issuer](/docs/en/claude-apps-gateway-config#oidc), and a loopback issuer additionally requires `CLAUDE_GATEWAY_ALLOW_LOOPBACK=1`
* Supports the authorization-code flow. PKCE (Proof Key for Code Exchange) is on by default; disable it with `oidc.use_pkce: false` for IdPs that don't support it
* Returns `email` and optionally `groups` in the id\_token, or serves them from the userinfo endpoint with `oidc.userinfo_fallback: true`
For private PKI, set `oidc.ca_cert_pem`.
A few providers handle email and group claims differently:
* **Okta**: the org authorization server at `https://example.okta.com` returns a thin id\_token that omits `email` and `groups`, so set `oidc.userinfo_fallback: true` whenever you use it as `issuer`. A custom authorization server such as `https://example.okta.com/oauth2/default` that includes `email` and optionally `groups` in the id\_token emits them directly and needs no fallback. Okta emits `groups` only when the `groups` scope is requested in `oidc.scopes` and the app's groups claim filter allows it; `userinfo_fallback` can't fill a claim the IdP wasn't asked for.
* **Microsoft Entra ID**: `issuer` = `https://login.microsoftonline.com/<tenant-id>/v2.0`. Entra emits group Object IDs rather than names, so use the GUIDs in `managed.policies.match.groups`, or use App Roles for human-readable names. If your tenant emits roles under `roles` instead of `groups`, set `oidc.groups_claim: roles`.
* **Google Workspace**: `issuer` = `https://accounts.google.com`. Google's id\_token doesn't carry groups. To use group-based `allowed_groups` or `managed.policies` with Google as the IdP, configure [`oidc.google_groups`](/docs/en/claude-apps-gateway-config#oidc), which looks up each user's groups through the Admin SDK Directory API using a service account with domain-wide delegation. Without it, use `oidc.allowed_email_domains` for membership gating and `managed.policies.match.email_domain` for policy assignment. Google also ignores the standard `offline_access` scope. For refresh tokens, set `oidc.scopes: [openid, profile, email]` and `oidc.extra_auth_params: { access_type: offline, prompt: consent }`.
For support with an identity provider not covered above, see [Troubleshooting](#troubleshooting).
<Warning>
Refresh tokens let the gateway renew a developer's session silently, without sending the developer back to the browser. They also drive deprovisioning, because when the IdP disables a user, the next refresh fails and the session ends within `ttl_hours`. The gateway requests `offline_access` by default to get a refresh token. If your IdP requires explicit consent for offline access, configure the OAuth client to allow it.
If your IdP can't issue refresh tokens at all, the gateway still works, but there is no silent renewal, so developers re-run the browser login when their session expires. To keep that from happening every hour, raise [`session.ttl_hours`](/docs/en/claude-apps-gateway-config#session) to `8` or `12`. The tradeoff is deprovisioning latency, because without refresh tokens a disabled user keeps access until the longer TTL elapses.
</Warning>
## Deployment
The gateway is a single stateless Linux binary that coordinates through Postgres, so deploy it the way you deploy any other stateless service in your environment. Keep it inside your network, where your developers and IdP can reach it over HTTPS, and treat it like any service holding a production credential.
A few decisions shape the deployment beyond where it runs:
* **Cost**: no separate license or per-seat fee. The gateway is part of the `claude` binary, so you pay for inference through your existing commitment, plus the compute it runs on.
* **Bypass**: the gateway doesn't enforce that the only route to a model goes through it. A developer with their own credential can still call the provider directly, so closing that path is a network policy decision, for example blocking egress to `api.anthropic.com` except from the gateway. Blocking that egress also breaks the [WebFetch domain safety check](/docs/en/data-usage#webfetch-domain-safety-check), which calls `api.anthropic.com` from each developer's machine. Set `skipWebFetchPreflight: true` in the managed policy to disable it.
* **Multiple gateways**: each is a separate deployment with its own config, and the CLI stores trust and credentials per gateway hostname, so teams can use different gateways without conflict. To serve multiple OIDC issuers, run separate instances.
* **Serverless**: Cloud Run works if you set `min-instances: 1` to avoid cold OIDC discovery. Lambda and Cloud Functions don't work, because the gateway is a long-running HTTP server.
Every production topology here puts an L7 proxy, such as an Ingress, Cloud Run's front end, or an ALB, in front of plain-HTTP replicas. Set [`listen.trusted_proxies`](/docs/en/claude-apps-gateway-config#listen) to the proxy's source ranges so the gateway reads client IPs from `X-Forwarded-For`. The gateway honors the header only when the TCP peer is trusted. The [Google Cloud](/docs/en/claude-apps-gateway-on-gcp) and [AWS](/docs/en/claude-apps-gateway-on-aws) worked examples have concrete values per topology. Without trusted proxies, every request appears to come from the proxy's IP, which collapses per-IP rate limits into one shared bucket and records the proxy's IP in audit events.
Give the proxy any idle timeout longer than the gateway's keepalive interval, which depends on the upstream:
* On every upstream except `provider: anthropic`, the gateway writes an SSE `ping` once a stream has been silent for about 15 seconds.
* On `provider: anthropic`, the gateway passes the response through unchanged, including the Anthropic API's own pings.
A default such as the ALB's 60 seconds is enough to keep a quiet stream open. The [AWS worked example](/docs/en/claude-apps-gateway-on-aws#troubleshooting) raises it to an hour anyway, and its troubleshooting row covers gateways older than v2.1.229, which sent nothing during quiet periods on the upstreams that now get pings.
### Container image
Build your own image around the native `claude` binary from the standard Claude Code release:
1. Download the Linux build for your image architecture from a pinned release; see [Install a specific version](/docs/en/setup#install-a-specific-version) for the download URL.
2. Verify it against the release's GPG-signed `manifest.json` as described in [Binary integrity and code signing](/docs/en/setup#binary-integrity-and-code-signing).
3. Copy it into the build context.
Mirror the release into your internal registry if your builds can't reach the release host, and pin the version your fleet runs.
Beyond the binary, the image needs:
* **A glibc-based image**: the glibc build's only dynamic dependencies are glibc libraries. Musl-based images need the `linux-x64-musl` or `linux-arm64-musl` build plus additional packages; see [Alpine Linux setup](/docs/en/setup#alpine-linux-and-musl-based-distributions).
* **A writable state directory**: the gateway runs as any user, but minimal images have no writable home. Set `CLAUDE_CONFIG_DIR` to a writable path such as `/tmp/.claude`.
* **The container command**: `claude gateway --config /etc/claude/gateway.yaml`, with the config file mounted read-only and secrets supplied as environment variables; the gateway listens on `listen.port`, default `8080`.
### Kubernetes
Run the gateway as a Deployment, like any stateless service:
* Mount the config from a ConfigMap and secrets from a Secret; reference secrets in the YAML via `${file:/path/to/secret}` or as environment variables
* Terminate TLS at the Ingress and set `listen.public_url` to the Ingress hostname
* Point the readiness probe at `GET /readyz` and the liveness probe at `GET /healthz`
For a complete worked example on AWS, covering ECS Fargate or EKS, Amazon RDS, and AWS Secrets Manager, see [Deploy on AWS](/docs/en/claude-apps-gateway-on-aws).
<Note>
**Workload identity**
Prefer the platform's workload identity over static keys: IRSA on EKS for Amazon Bedrock and for Claude Platform on AWS, Workload Identity on GKE for Google Cloud's Agent Platform, and workload identity on AKS for Microsoft Foundry. Set `auth: {}` in the upstream block, or `use_azure_ad: true` for Microsoft Foundry, and the gateway picks up the pod's identity through that provider's default credential chain. For a cross-cloud pairing, such as an Amazon Bedrock upstream on GKE, set explicit credentials in the upstream's `auth` block instead. The [`upstreams` reference](/docs/en/claude-apps-gateway-config#upstreams) has per-platform setup details.
</Note>
### Cloud Run
Configure the service as follows:
* Leave `listen.port` at its default of `8080`, which matches Cloud Run's default `PORT`, or set `port: ${PORT}`
* Set `public_url` to the externally reachable origin. For production this is normally an internal load balancer's hostname, because `/login` [rejects public addresses](/docs/en/claude-apps-gateway#prerequisites) and the `*.run.app` URL resolves to one, so the Cloud Run URL alone works only for a `curl` or browser smoke test. The exception is a network where `*.run.app` resolves privately through Private Service Connect and a Cloud DNS private zone; in that topology the Cloud Run URL is a valid `public_url`. The [Google Cloud worked example](/docs/en/claude-apps-gateway-on-gcp#deploy-the-gateway) covers both.
* Mount the config as a secret volume
* Set `min-instances: 1` to avoid a cold OIDC discovery on first request
<Note>
For a complete worked example on Google Cloud, covering Cloud Run or GKE, Cloud SQL, and Secret Manager, see [Deploy on Google Cloud](/docs/en/claude-apps-gateway-on-gcp).
</Note>
### Push the gateway URL to developer machines
Once the gateway is serving, push `forceLoginMethod`, `forceLoginGatewayUrl`, and `parentSettingsBehavior: "merge"` to each developer's machine through managed settings, via MDM or by writing the per-OS `managed-settings.json` directly. Without this, `/login` shows the standard account picker with no gateway option. See [Client-side managed settings](/docs/en/claude-apps-gateway-config#client-side-managed-settings) for the file paths and the Claude Desktop `bootstrapUrl` equivalent.
## Operations
Once the gateway is serving traffic, day-to-day operation is reading its logs, probing its health, and rotating its secrets on your schedule. The subsections cover each, plus what Postgres holds and how upgrades and rollbacks behave.
### Logs
The gateway writes two streams to stderr, both JSON-friendly:
* **Audit events**: single-line JSON per security-relevant event. Pipe stderr to your log aggregator. The events emitted include `config.load`, `session.mint`, `session.refresh`, `device.authorize`, `device.verify`, `device.callback`, `auth.denied`, `access.denied`, `inference`, `managed.serve`, `desktop_bootstrap.serve`, `desktop_bootstrap.denied`, `spend.blocked`, `admin.denied`, `admin.limit.upsert`, and `admin.limit.delete`. Fields vary by event:
* Successful mint and refresh events carry `sub`, `email`, `client_ip`, and the result
* `auth.denied` and `access.denied` carry the reason and client IP, plus the request path for `auth.denied`, since no user identity exists at those denials
* `inference` records which upstream served the request and the response status
* `desktop_bootstrap.denied` records a rejected Claude Desktop bootstrap fetch with the reason (`not_configured`, `policy_not_opted_in`, or `no_policy_matched`) and the user's identity
* `admin.denied` records a rejected admin-API auth attempt with the client IP, method, path, and a reason, without the presented key material: `invalid_key` when an `x-api-key` was presented but matched no configured key, `bearer_rejected` when only an `Authorization` header was presented and it didn't verify as a gateway session in `admin.admin_groups`, or `no_credentials` when neither header was presented
* **Operational logs**: human-readable `[gateway]`-prefixed lines for boot, warnings, and upstream errors. The `CLAUDE_GATEWAY_LOG_LEVEL` environment variable controls verbosity and accepts `debug`, `info`, `warn`, or `error`, with `info` as the default. At `debug`, each sign-in and refresh also logs the names, not the values, of the claims in the id\_token, plus the names of the userinfo claims when `userinfo_fallback` supplied any, so you can diagnose `email_claim` and `groups_claim` settings without logging PII. It doesn't affect audit events, which are always emitted.
### Health
The gateway serves `GET /healthz` as a liveness probe and `GET /readyz` as a readiness probe; `/readyz` verifies the store is reachable. Both are exempt from `access_control.allow_cidrs`, so probes keep working on a locked-down listener.
The OAuth discovery document at `/.well-known/oauth-authorization-server` also returns `200` only after config load, OIDC discovery, upstream client construction, and Postgres migration all succeed, so it doubles as an end-to-end boot check.
A running gateway also serves a description of the paths and request shapes it accepts at `<public_url>/protocol`, matched to the version you're running. The contents aren't stable across releases.
### Outage behavior
If Postgres goes down, the gateway itself keeps serving signed-in developers and new sign-ins fail. Whether developers actually keep working depends on how your orchestrator handles readiness:
* **Existing sessions**: bearer tokens validate locally with the JWT secret, session refreshes don't touch the store, and the gateway process can still serve inference
* **New sign-ins**: fail until Postgres recovers, because the device flow and its rate-limit counters live in Postgres
* **[Spend-limit enforcement](/docs/en/claude-apps-gateway-spend-limits#postgres-availability)**: fails open by default during the outage, so inference still flows; flip it to fail closed if you'd rather block than run unmetered
* **Readiness**: `/readyz` reports not-ready during the outage, so orchestrators that gate traffic on readiness remove every replica from rotation at once. In that topology all traffic, including inference the gateway could still serve, fails at the load balancer until Postgres recovers. The liveness probe on `/healthz` keeps passing, so replicas aren't restarted. Point the readiness probe at `/healthz` instead if you'd rather signed-in developers keep working through a store outage; the cost is that new sign-ins fail against a replica that still reports ready.
If your IdP goes down, existing sessions work until `ttl_hours`, and new logins and refreshes fail. Set a longer `ttl_hours` if your IdP has frequent maintenance windows.
### JWT secret rotation
Rotate the signing secret in three steps so existing sessions stay valid:
1. Generate a new secret. Prepend it to the `session.jwt_secret` array.
2. Roll the deployment. New tokens sign with the new secret; old tokens still verify.
3. After `ttl_hours` plus a margin, remove the old secret and roll again.
Rotation is also the only way to force sessions out before they expire: bearer tokens validate locally against the JWT secret, so there is no per-session revocation. Replacing the secret outright, without keeping the old one in the array, invalidates every outstanding session at once. For individual offboarding, deprovision the user in your IdP; their session ends within `ttl_hours`.
### Postgres
The gateway holds five tables, all created by its boot-time migrations:
| Table | Contents | Retention |
| ------------------ | ----------------------------------------------------------------------------- | --------------------------------------------------------------- |
| `kv` | Device grants (10-minute TTL) and rate-limit counters | TTL per row |
| `spend` | Per-principal period-to-date spend counters, in cents | `admin.spend_retention_months`, default 13 |
| `spend_limits` | Configured spend caps | Until deleted via the API |
| `admin_audit` | Admin API mutation trail | `admin.audit_retention_days`, default 365 |
| `principal_emails` | Each principal's last-seen email, display name, and IdP groups. Contains PII. | `admin.identity_retention_days` since last activity, default 90 |
A 30-second loop expires `kv` rows past their TTL, and an hourly sweep enforces the retention windows on the spend tables, so nothing grows without bound. Without [spend limits](/docs/en/claude-apps-gateway-spend-limits) configured, only `kv` is written. The gateway applies its own schema migrations at boot and on every upgrade, so its database role needs rights to create and alter tables. Point it at a database or schema dedicated to the gateway to keep that grant narrow.
With spend limits in use, a lost database means lost spend tracking and caps, not only developer re-logins, so run regular backups. To erase one departed developer immediately rather than waiting on retention, run `DELETE FROM principal_emails WHERE principal = '<sub>'` directly; that removes the only table holding their email, name, and groups. `spend` and `admin_audit` rows reference the pseudonymous OIDC `sub` only.
### Upgrades
Replicas are stateless, so a rolling restart is safe at any time. The gateway runs schema migrations at boot, which means deploying the new binary self-migrates the database. Concurrent replicas serialize on a Postgres advisory lock, so only one applies each migration.
Migrations are append-only, so rolling back to a prior binary that knows fewer migrations is safe; it ignores the extra rows. Rollback also re-validates the YAML against the older binary's schema, so a config that adopted a key introduced by the newer release fails boot on the older one. Remove the new key before rolling back.
Because you pin the gateway's version in your own image, fixes in new Claude Code releases, including security fixes, reach your deployment only when you update the pin and redeploy. Include the gateway in the same patching cadence you use for other services that hold production credentials.
## Security
This section answers the questions a security review asks: what data flows through the gateway and where it goes, which attacks the design defends against, and which answers belong in a compliance questionnaire.
### Data flow
| Data | Path | Sent to Anthropic by the gateway |
| ------------------------------------------------------------------------------------------------- | ------------------------------------------------------------ | -------------------------------------------------- |
| Inference (prompts, completions) | CLI → gateway → your upstream | Only if the Anthropic API is a configured upstream |
| Telemetry (OTLP metrics, plus [opt-in logs and traces](/docs/en/claude-apps-gateway-config#telemetry)) | CLI → gateway → your collector | Never |
| Identity (email, groups, sub) | IdP → gateway → JWT → CLI; the CLI stamps it on OTLP exports | Never |
| Managed settings | Your gateway YAML → CLI | Never |
| Audit log | Gateway stderr → your aggregator | Never |
### Threat model summary
The gateway sits inside your network perimeter, but individual developer laptops aren't treated as trusted. The design accounts for this in three ways:
* Developers hold short-lived JWTs instead of raw upstream keys. The CLI-to-gateway leg uses the RFC 8628 device grant, and the gateway's authorization-code exchange with the IdP runs PKCE in the default configuration, so an intercepted IdP authorization code is useless.
* The device-verification page enforces same-origin POST and a per-IP rate limit per RFC 8628 §5.1. See [User-code brute-force resistance](#user-code-brute-force-resistance).
* Outbound requests go through a server-side request forgery (SSRF) guard that resolves DNS, blocks link-local and cloud-metadata addresses plus loopback by default, and pins the connection to the resolved IP, so operator-influenced URLs such as the IdP and OTLP destinations can't be redirected to cloud metadata endpoints. RFC 1918 private ranges are deliberately allowed, because IdPs and OTLP collectors commonly live on private IPs. Set `CLAUDE_GATEWAY_ALLOW_LOOPBACK=1` in the gateway's environment only when something the gateway must reach legitimately lives on loopback, such as a local-development IdP or a sidecar OTLP collector on `localhost`. The variable relaxes the loopback block for every operator-configured URL and also skips the boot-time warning that checks whether the pod can reach the cloud metadata endpoint, so prefer giving the collector its own internal address.
If you add your own egress controls, the gateway must reach the metadata server whenever it uses instance-metadata credentials such as workload identity.
Two threats are out of scope because they are your infrastructure to secure:
* **A compromised gateway host**: the host both holds the upstream credential and distributes [managed settings](/docs/en/claude-apps-gateway-config#managed) to every connected developer, so control over the gateway's configuration is comparable to control over your MDM. The CLI's [approval dialog](/docs/en/server-managed-settings#approval-memory) for shell-capable settings limits silent changes but doesn't replace host security.
* **A malicious OIDC provider**: the provider signs the id\_tokens the gateway trusts, so it can assert any identity. Vetting and securing your IdP is your responsibility.
### User-code brute-force resistance
The `user_code` a developer types into the `/device` verification page is 8 characters drawn from a 20-character alphabet, which yields 20⁸ or about 2.56×10¹⁰ combinations, and it expires after 10 minutes.
The gateway applies per-IP rate limits on the device-grant endpoints, configurable via [`rate_limits`](/docs/en/claude-apps-gateway-config#http-tuning). Raise the limits if many developers sign in from a single shared corporate NAT address. The limits apply only to the sign-in flow, not to inference.
### Compliance posture
* **Data residency**: the gateway's own data plane sends nothing to Anthropic unless the Anthropic API is a configured upstream; when it is, your existing data-handling agreement applies to the inference path. Telemetry, audit, identity, and settings go only to the destinations you configure.
* **Host-process traffic**: the host process is the Claude Code CLI. `claude gateway` runs under the same third-party rules as Amazon Bedrock and Google Cloud's Agent Platform deployments and sends nothing to Anthropic. Before v2.1.227, the host process sent startup telemetry such as product version and platform, which setting `CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1` in the container environment turned off. Those releases also sent one `HEAD` request at boot, with no body or credentials, to `/api/hello` on `https://api.anthropic.com`, or on `ANTHROPIC_BASE_URL` when the environment set it, unless the environment also set a proxy variable such as `HTTPS_PROXY` or an mTLS client certificate. They ignored the response, so blocking that request at the egress firewall didn't affect the gateway.
* **Client analytics**: the CLI disables its own usage analytics while signed in to a gateway, and error reporting is off by default on third-party API surfaces.
* **Client machines**: developers' CLIs still send WebFetch hostname checks and version checks to Anthropic unless `CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1` and `skipWebFetchPreflight: true` are set. See [data usage](/docs/en/data-usage).
* **Survey ratings**: the gateway credential disables the Anthropic-bound rating sink, so ratings aren't sent to Anthropic.
* **Transcript sharing**: choosing Yes on a survey's transcript-share prompt writes a local file under `~/.claude/feedback-bundles/` instead of uploading to Anthropic.
* **Client updates**: update checks are separate from gateway traffic. Pin versions through your own distribution and set `DISABLE_UPDATES` if laptops must not fetch releases. `DISABLE_AUTOUPDATER` stops only background updates while `claude update` still works.
* **TLS**: serve `public_url` over HTTPS in production, either from the gateway's own listener via `listen.tls` or from a TLS-terminating ingress in front of plain-HTTP replicas, with `listen.public_url` set in both cases. The gateway doesn't refuse plain HTTP. The IdP must serve HTTPS in production, and Postgres supports `?sslmode=require`. Set `Strict-Transport-Security` at your ingress.
* **Vulnerability disclosure**: follow [Reporting security issues](/docs/en/security#reporting-security-issues)
## Troubleshooting
For questions and feedback, use [Claude Code support](https://support.claude.com/en/collections/14445694-claude-code), or open an issue on the [Claude Code GitHub repository](https://github.com/anthropics/claude-code/issues). When reporting a problem, include:
* **Gateway issue**: the gateway's stderr for the relevant window, your `gateway.yaml` with secrets redacted, the gateway version, shown on the landing page at `/` and in the `x-cc-gateway-version` response header on `/managed/settings`, and what changed recently
* **Login issue**: the developer runs `claude --debug-file ./claude-debug.txt`, reproduces, and sends that file plus the gateway's audit log for the same window
* **Inference issue**: the model requested, the upstreams configured, and the gateway's audit log for the request, which records which upstream served it and the response status
The gateway's stderr includes the audit event stream, the audit log records developer identities, and the debug file records hook and MCP server output from the developer's machine. Review and redact these before posting to a public issue.
| Symptom | Cause | Fix |
| --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| A developer's `/login` shows the standard account picker instead of the **Cloud gateway** screen | `forceLoginMethod` or `forceLoginGatewayUrl` isn't set in managed settings on that machine | Deploy the [managed settings file](/docs/en/claude-apps-gateway#set-the-gateway-url) to the device; `/login` reads the gateway URL from there |
| Claude Desktop reports that its bootstrap configuration couldn't be fetched | `/user/bootstrap` returned 404: the policy matching the user doesn't carry a `desktop` key, or no policy matched. The gateway's audit log records each rejection as `desktop_bootstrap.denied` with the reason. | Add a `desktop` block to the policy that matches the user, or to the `match: {}` base layer; an empty `desktop: {}` suffices. See [Claude Desktop overlay](/docs/en/claude-apps-gateway-config#claude-desktop-overlay). |
| Startup shows `Gateway login is configured in managed settings, but this Claude Code build does not include Cloud gateway support.` | The installed Claude Code build predates gateway support | Have the developer update Claude Code to a release that includes Cloud gateway support |
| CLI `/login`: `Gateway hosts must be on your organization's private network; <host> resolves to the public (or unrecognized) address <ip>` | The gateway hostname resolves to at least one public IP address. Claude Code checks each resolved address and requires every one to be private. A common cause is a dual-stack name where one family resolves to a public address, including AWS internal dual-stack load balancers, which return public-range AAAA addresses. | Have the gateway name resolve only to private addresses on developer machines. For a dual-stack name, drop the public-range record or serve a separate internal-only DNS name. See the [private-network prerequisite](/docs/en/claude-apps-gateway#prerequisites). |
| CLI `/login`: `Gateway login would go through proxy <proxy>, which is not on a private network` | An `HTTPS_PROXY` or `HTTP_PROXY` applies to the gateway host and the proxy's hostname resolves to a public address. A proxy whose host resolves only to private addresses is allowed and doesn't trigger this error | Add the gateway host to `NO_PROXY` on the developer's machine so the connection is direct, or use a proxy whose hostname resolves to private addresses. The message names the exact `NO_PROXY` entry to add |
| CLI `/login`: `Could not resolve the configured HTTP proxy` | The hostname in `HTTPS_PROXY` or `HTTP_PROXY` doesn't resolve from the developer's machine, typically because it isn't connected to the corporate network | Have the developer connect to your network or VPN and retry, or fix the proxy URL |
| CLI `/login`: `Could not resolve gateway host <host>` | The machine can't resolve the gateway's internal DNS name, typically because it isn't on the corporate network | Have the developer connect to your network or VPN, then retry `/login` |
| Boot exits with a config validation error naming `store.postgres_url` | No Postgres configured; the gateway requires Postgres | Set `store.postgres_url`. For local development, use a throwaway container: `docker run --rm -p 5432:5432 -e POSTGRES_HOST_AUTH_METHOD=trust postgres`. |
| Boot exits: `requires the native binary` | Running under Node instead of the native binary | Install Claude Code with one of the [standalone install methods](/docs/en/setup) |
| Boot exits with an OIDC discovery error after `config.load` | `oidc.issuer` unreachable, or TLS chain not trusted | Check the issuer is reachable from the pod and serves `/.well-known/openid-configuration`. Set `ca_cert_pem` for private PKI. If the pod reaches the IdP only through a forward proxy, set [`oidc.use_proxy: true`](/docs/en/claude-apps-gateway-config#idp-requests-through-a-forward-proxy); on versions before v2.1.227, give the pod a direct route to each of the IdP's endpoints instead. |
| Boot exits with a Postgres permission error | The database role lacks DDL rights on its schema | Grant the role `CREATE` on the gateway's schema so it can create and alter its tables at boot |
| `/oauth/callback` shows "Sign-in could not be completed" | Email domain rejected, id\_token validation failed, or `email_verified` is explicitly `false`, which the gateway always rejects with no override | Check `allowed_email_domains` and that the IdP returns a verified `email` claim. For `email_verified: false`, fix the IdP-side verification. If your IdP emits email under a different claim name, set `oidc.email_claim`. |
| Log: `token exchange failed: id_token missing email claim` | The IdP isn't including `email` in the id\_token by default. This rejection fires only when `allowed_email_domains` is set; without it, a missing email mints a session with no email | Configure the IdP to emit `email` in the id\_token. Okta: add `email` to a custom authorization server's ID-token claims. Entra: add `email` as an optional claim on the app registration. PingFederate: enable an OpenID Connect Policy that emits `email`. If the IdP serves `email` from the userinfo endpoint but won't include it in the id\_token, such as the Okta org authorization server, set `oidc.userinfo_fallback: true`. |
| Every Amazon Bedrock request returns 502; log shows `Could not load credentials from any providers` | On EC2, IMDSv2's default hop limit of 1 blocks the instance-metadata request from inside the container. Boot and `/readyz` pass anyway because the AWS SDK resolves instance credentials on the first request, not at client construction | Raise the hop limit with `aws ec2 modify-instance-metadata-options --instance-id <id> --http-put-response-hop-limit 2`, or set it in the launch template. The change applies to every container on the instance. Prefer ECS task roles where available, which read credentials from the ECS container-credentials endpoint and avoid the change entirely, or apply the change on a dedicated gateway instance to limit the exposure. |
| IdP error: unknown or unsupported scope | The IdP rejects scopes it doesn't recognize | Set `oidc.scopes` to exactly the list your IdP accepts; it must include `openid`. The default is `openid profile email offline_access`. |
| Sessions don't silently renew after setting `oidc.scopes` | `offline_access` was dropped from the override | Add `offline_access` back if your IdP supports it. Without a refresh token, developers re-run the browser login every `session.ttl_hours`. |
| Browser shows "This request came from another site and was blocked" | Cross-site form POST, blocked as CSRF protection. Expected for embedded or proxied pages | Open the verification link directly |
| Chrome blocks the Approve button with "Refused to send form data … violates … Content Security Policy directive: form-action", but the same page works in Safari or Firefox | Chrome enforces `form-action` against the entire redirect chain. Your IdP redirects onward to a second host that isn't allowlisted. | Add each additional origin in the redirect chain to `oidc.form_action_origins`. Open Chrome DevTools → Console on the Approve page to see which origin was blocked. |
| Sign-in completes at the IdP but the callback fails, with a CSP error in Chrome or "this sign-in link has expired" in Safari | The IdP returned the code via `response_mode=form_post`, which auto-submits it cross-origin via POST to `/oauth/callback`. Chrome blocks that under a strict CSP; Safari allows the submit but the callback reads only the query string. | Make sure your IdP honors `response_mode=query`, which the gateway requests explicitly so the callback is a plain redirect |
| Login works locally but fails behind an ALB | `public_url` still names the local or inner `http://` origin, so the IdP gets the wrong `redirect_uri` | Set `listen.public_url` to the external `https://` origin and register `<public_url>/oauth/callback` with the IdP |
| Developer sees the trust prompt repeatedly | TLS cert is rotating per replica or per request | Use a stable cert at the ingress, or terminate TLS once and run replicas over plain HTTP internally |
| CLI `/login`: "Could not verify the gateway's TLS certificate" or `SELF_SIGNED_CERT_IN_CHAIN` | Gateway's TLS chain is signed by a private CA not in the CLI host's trust store | Claude Code reads the OS trust store by default on the native binary and on Node 22.15 or later; [`CLAUDE_CODE_CERT_STORE`](/docs/en/network-config#ca-certificate-store) controls this behavior. If the CA is installed in the OS trust store, ensure developers are on a current runtime. Otherwise set `NODE_EXTRA_CA_CERTS` to the CA certificate PEM before launching. The first-connect fingerprint prompt still applies. |
## Related
* [Claude apps gateway overview](/docs/en/claude-apps-gateway): quickstart and developer connection
* [Configuration reference](/docs/en/claude-apps-gateway-config): every `gateway.yaml` option
claude-apps-gateway-on-aws First recorded · 524 lines, first recorded
# Deploy Claude apps gateway on AWS ## Architecture ## Prerequisites ### Set your environment variables ## Deploy the gateway ## Terraform reference ## Troubleshooting ## Telemetry ### Client metrics, logs, and traces ### Gateway logs ### Container metrics ### Spend ## Next steps
The first capture of this source. The page was already there, and this is what it said.
# Deploy Claude apps gateway on AWS
> A worked example of running Claude apps gateway on AWS: ECS Fargate or EKS, Amazon RDS for PostgreSQL, AWS Secrets Manager, and IAM-role auth to Amazon Bedrock.
<Note>
This page walks through one way to run Claude apps gateway on AWS. The configuration is a working example for customer-managed infrastructure rather than a supported production deployment; use it to see how the pieces fit together before adapting it to your own environment. For the platform-agnostic requirements, see the [deployment guide](/docs/en/claude-apps-gateway-deploy).
</Note>
This example provisions Claude apps gateway on AWS with Amazon Bedrock as the model upstream, using either [Amazon ECS](https://aws.amazon.com/ecs/) on [AWS Fargate](https://aws.amazon.com/fargate/) or [Amazon EKS](https://aws.amazon.com/eks/) for compute. [Okta](https://www.okta.com/) is the example identity provider (IdP), but any OpenID Connect (OIDC) compliant IdP works; see [Identity provider setup](/docs/en/claude-apps-gateway-deploy#identity-provider-setup) for per-IdP details.
<Note>
Bedrock isn't the only Claude upstream on AWS. The gateway also supports Claude Platform on AWS, the Anthropic-operated Claude API with AWS authentication and AWS Marketplace billing, in place of Bedrock or alongside it. Its upstream entry, credentials, and IAM permissions differ from this page's Bedrock-scoped ones; the [Claude Platform on AWS upstream reference](/docs/en/claude-apps-gateway-config#claude-platform-on-aws) covers what changes, and the rest of this page applies unchanged.
</Note>
## Architecture
<Frame caption="The example architecture, with Amazon Bedrock as the model upstream. A Claude Platform on AWS upstream occupies the same position.">
<img src="https://mintcdn.com/claude-code/PHweeRmDUYEKff49/images/claude-gateway-aws-architecture.svg?fit=max&auto=format&n=PHweeRmDUYEKff49&q=85&s=8599cc34aa28522cde208ee831439bb4" alt="Diagram of Claude apps gateway on AWS: Claude Code clients connect over HTTPS to an internal Application Load Balancer fronting the gateway (ECS Fargate or EKS), which runs in private subnets alongside an Amazon RDS for PostgreSQL instance for session state. The gateway signs users in via OIDC against the corporate IdP, reads secrets from AWS Secrets Manager, forwards model requests to Amazon Bedrock using its IAM role, and pulls its image from Amazon ECR at deploy." width="820" height="430" data-path="images/claude-gateway-aws-architecture.svg" />
</Frame>
The gateway runs as a private HTTPS endpoint on your network that developers sign in to through your IdP. Their Claude Code sessions reach Claude models on Amazon Bedrock through the gateway's IAM role, so no model credentials land on developer machines. The reference configuration provisions:
* **Amazon ECS on AWS Fargate** service or **Amazon EKS** Deployment running the gateway container
* **Amazon ECR** repository for the gateway image
* **Amazon RDS for PostgreSQL** instance in private subnets, not publicly accessible, for the gateway's [store](/docs/en/claude-apps-gateway-config#store)
* **AWS Secrets Manager** secrets for the JWT signing key, the OIDC client secret, and the Postgres URL
* **IAM role** with `bedrock:InvokeModel` and `bedrock:InvokeModelWithResponseStream`, attached as the ECS task role or bound via IAM Roles for Service Accounts (IRSA) on EKS
* **Internal Application Load Balancer** for HTTPS
## Prerequisites
The walkthrough creates the gateway's own resources, but it builds on network and identity infrastructure you already have. Before you start, you need:
* An AWS account with permission to create the [resources above](#architecture)
* The [AWS CLI v2](https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html) installed and [authenticated](https://docs.aws.amazon.com/cli/latest/userguide/cli-chap-authentication.html), and [Docker](https://docs.docker.com/get-started/get-docker/) installed locally
* A [VPC](https://docs.aws.amazon.com/vpc/latest/userguide/what-is-amazon-vpc.html) with at least two [private subnets](https://docs.aws.amazon.com/vpc/latest/userguide/configure-subnets.html) in different Availability Zones, with outbound internet access through a [NAT gateway](https://docs.aws.amazon.com/vpc/latest/userguide/vpc-nat-gateway.html); the internal load balancer needs subnets in two AZs, and the gateway needs egress to Bedrock and your IdP
* An Okta OIDC web application with redirect URI `https://<gateway-host>/oauth/callback`; see [Identity provider setup](/docs/en/claude-apps-gateway-deploy#identity-provider-setup)
* A TLS hostname for the gateway, typically an internal DNS name in a [Route 53 private hosted zone](https://docs.aws.amazon.com/Route53/latest/DeveloperGuide/hosted-zones-private.html) pointing at the load balancer, with an [ACM certificate](https://docs.aws.amazon.com/acm/latest/userguide/gs.html) for that name, imported or issued by [AWS Private CA](https://docs.aws.amazon.com/privateca/latest/userguide/PcaWelcome.html)
### Set your environment variables
Every command on this page reads four values from your shell: `AWS_REGION`, `ACCOUNT_ID`, `VPC_ID`, and `PRIVATE_SUBNETS`.
Pick a US region where Bedrock serves the Claude models you need. The walkthrough relies on the gateway's built-in model catalog, which resolves to `us.anthropic.*` inference profiles, and the IAM policy grants those ARNs. In a non-US region, add a [`models:` block](/docs/en/claude-apps-gateway-config#models) with that geo's inference-profile IDs and change the IAM policy's ARN prefix to match.
If you don't have the VPC ID at hand, list your VPCs with `aws ec2 describe-vpcs`, then list that VPC's subnets to find two private ones in different Availability Zones:
```bash theme={null}
aws ec2 describe-subnets --filters "Name=vpc-id,Values=<your-vpc-id>" \
--query 'Subnets[].{ID:SubnetId,AZ:AvailabilityZone,CIDR:CidrBlock}' --output table
```
Export all four before continuing:
```bash theme={null}
export AWS_REGION=us-east-1 # a US region where Bedrock serves the Claude models you need
export ACCOUNT_ID="$(aws sts get-caller-identity --query Account --output text)"
export VPC_ID=<your-vpc-id>
export PRIVATE_SUBNETS="<subnet-id-a> <subnet-id-b>"
```
## Deploy the gateway
The steps below provision the full deployment with `aws` commands.
<Steps>
<Step title="Create the security groups">
Three security groups chain the traffic path: your corporate network reaches the load balancer on 443, the load balancer reaches the gateway on 8080, and the gateway reaches Postgres on 5432. Nothing else is reachable. How you attach them depends on the compute track:
* On ECS Fargate, the deploy step attaches `$ALB_SG` to the load balancer and `$GW_SG` to the service.
* On EKS, the AWS Load Balancer Controller creates its own frontend security group for the ALB, so `$ALB_SG` and `$GW_SG` go unused: the deploy step's `inbound-cidrs` annotation restricts the listener to your corporate network, and the database security group admits the cluster's security group instead.
```bash theme={null}
ALB_SG="$(aws ec2 create-security-group --group-name claude-gateway-alb \
--description "Claude gateway ALB" --vpc-id "$VPC_ID" \
--query GroupId --output text)"
GW_SG="$(aws ec2 create-security-group --group-name claude-gateway-svc \
--description "Claude gateway service" --vpc-id "$VPC_ID" \
--query GroupId --output text)"
DB_SG="$(aws ec2 create-security-group --group-name claude-gateway-db \
--description "Claude gateway Postgres" --vpc-id "$VPC_ID" \
--query GroupId --output text)"
aws ec2 authorize-security-group-ingress --group-id "$ALB_SG" \
--protocol tcp --port 443 --cidr <your-corporate-cidr>
aws ec2 authorize-security-group-ingress --group-id "$GW_SG" \
--protocol tcp --port 8080 --source-group "$ALB_SG"
aws ec2 authorize-security-group-ingress --group-id "$DB_SG" \
--protocol tcp --port 5432 --source-group "$GW_SG"
```
</Step>
<Step title="Create the IAM roles and submit the use case form">
The gateway runs with a dedicated task role whose only permission is invoking Claude models on Bedrock. Per the [Bedrock upstream reference](/docs/en/claude-apps-gateway-config#amazon-bedrock), the policy must cover both the cross-region inference-profile ARNs and the underlying foundation-model ARNs:
```bash theme={null}
cat > bedrock-invoke.json <<EOF
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Action": ["bedrock:InvokeModel", "bedrock:InvokeModelWithResponseStream"],
"Resource": [
"arn:aws:bedrock:${AWS_REGION}:${ACCOUNT_ID}:inference-profile/us.anthropic.*",
"arn:aws:bedrock:*::foundation-model/anthropic.*"
]
}]
}
EOF
cat > ecs-trust.json <<'EOF'
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Principal": { "Service": "ecs-tasks.amazonaws.com" },
"Action": "sts:AssumeRole"
}]
}
EOF
aws iam create-role --role-name claude-gateway-task \
--assume-role-policy-document file://ecs-trust.json
aws iam put-role-policy --role-name claude-gateway-task \
--policy-name bedrock-invoke --policy-document file://bedrock-invoke.json
```
ECS also needs an execution role, which the ECS agent itself uses to pull the image from ECR and inject the Secrets Manager values created later. It is separate from the task role the gateway's AWS SDK uses at runtime:
```bash theme={null}
aws iam create-role --role-name claude-gateway-execution \
--assume-role-policy-document file://ecs-trust.json
aws iam attach-role-policy --role-name claude-gateway-execution \
--policy-arn arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy
cat > secrets-read.json <<EOF
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Action": ["secretsmanager:GetSecretValue", "secretsmanager:DescribeSecret"],
"Resource": [
"arn:aws:secretsmanager:${AWS_REGION}:${ACCOUNT_ID}:secret:gateway-jwt-secret-??????",
"arn:aws:secretsmanager:${AWS_REGION}:${ACCOUNT_ID}:secret:gateway-oidc-client-secret-??????",
"arn:aws:secretsmanager:${AWS_REGION}:${ACCOUNT_ID}:secret:gateway-postgres-url-??????"
]
}]
}
EOF
aws iam put-role-policy --role-name claude-gateway-execution \
--policy-name read-gateway-secrets --policy-document file://secrets-read.json
```
The policy names one ARN per secret rather than a bare `gateway-*` wildcard, which in a shared account would also match unrelated secrets; the trailing `-??????` matches exactly the random six-character suffix Secrets Manager appends to every secret's ARN. A trailing `-*` would be a plain prefix glob and would also match longer names such as `gateway-postgres-url-prod`.
The IAM policy grants the gateway permission to call Bedrock, and Bedrock enables model access by default in commercial regions. The remaining account-level gate is Anthropic's one-time use case form: if no one in your account has submitted it, open the [Amazon Bedrock console](https://console.aws.amazon.com/bedrock/), select an Anthropic model from the Model catalog, and complete the form. Access is granted immediately after submission; see [Claude Code on Amazon Bedrock](/docs/en/amazon-bedrock#1-submit-use-case-details) for the AWS Organizations form and the IAM permissions the submitter needs.
The EKS track reuses both policy documents on an IRSA role instead of the two ECS roles; see the deploy step.
</Step>
<Step title="Provision Amazon RDS for PostgreSQL">
The instance runs in the private subnets with no public address and storage encryption on. The engine version is pinned to Postgres 16, which satisfies the gateway's supported floor of PostgreSQL 14 and guarantees the parameter-group family below matches the instance.
First, create the subnet group that places the database in the private subnets, and a parameter group with `rds.force_ssl=1` so the server rejects plaintext connections. The engine version is pinned once because the parameter group's family must match the engine major version the instance runs:
```bash theme={null}
aws rds create-db-subnet-group --db-subnet-group-name claude-gateway-db \
--db-subnet-group-description "Claude gateway" --subnet-ids $PRIVATE_SUBNETS
PG_VERSION=16
PG_FAMILY="postgres${PG_VERSION}"
aws rds create-db-parameter-group --db-parameter-group-name claude-gateway-db \
--db-parameter-group-family "$PG_FAMILY" \
--description "Claude gateway - require TLS on every connection"
aws rds modify-db-parameter-group --db-parameter-group-name claude-gateway-db \
--parameters "ParameterName=rds.force_ssl,ParameterValue=1,ApplyMethod=immediate"
```
Then create the instance with a generated master password:
```bash theme={null}
PGPASS="$(openssl rand -hex 24)"
aws rds create-db-instance --db-instance-identifier claude-gateway-db \
--engine postgres --engine-version "$PG_VERSION" \
--db-instance-class db.t4g.micro \
--allocated-storage 20 --db-name claude_gateway \
--master-username gateway --master-user-password "$PGPASS" \
--db-subnet-group-name claude-gateway-db \
--db-parameter-group-name claude-gateway-db \
--vpc-security-group-ids "$DB_SG" \
--no-publicly-accessible --storage-encrypted
```
The literal `--master-user-password` argument is visible in the process table and in audit/EDR logs while the command runs, the same exposure the secrets step's note covers. On a shared or monitored host, pass the password via `--cli-input-json` from a `0600` file instead, the way the bundle's `setup.sh` does.
Wait for the instance to come up, which can take several minutes, then read its private endpoint and assemble the connection string the gateway will use:
```bash theme={null}
aws rds wait db-instance-available --db-instance-identifier claude-gateway-db
DB_HOST="$(aws rds describe-db-instances --db-instance-identifier claude-gateway-db \
--query 'DBInstances[0].Endpoint.Address' --output text)"
GATEWAY_POSTGRES_URL="postgres://gateway:${PGPASS}@${DB_HOST}:5432/claude_gateway?sslmode=verify-full"
```
`sslmode=verify-full` makes the gateway verify the RDS server certificate's chain and hostname, not only encrypt. The trust anchor is the [AWS RDS certificate bundle](https://truststore.pki.rds.amazonaws.com/global/global-bundle.pem), which the image build step below copies to `/etc/claude/rds-global-bundle.pem` and trusts via `NODE_EXTRA_CA_CERTS`. Don't append a libpq-style `sslrootcert=` parameter to the URL: the gateway's driver reads only `sslmode` from the query string and would forward `sslrootcert` to Postgres as a startup parameter, which the server rejects.
The ECS service or EKS pods must run in this VPC so they can reach the instance's private endpoint, and the `claude-gateway-db` security group only admits the gateway's security group.
</Step>
<Step title="Write gateway.yaml">
The `upstreams` block points at Bedrock with `auth: {}`, so the gateway authenticates via the AWS default credential chain from the task role on ECS or the IRSA role on EKS. See the [configuration reference](/docs/en/claude-apps-gateway-config) for every field.
Two `listen` fields describe what fronts the gateway:
* `public_url`: the external `https://` origin, required for any non-loopback bind; see the [`listen` reference](/docs/en/claude-apps-gateway-config#listen). The gateway builds the IdP `redirect_uri` and its discovery document only from this value, never from `X-Forwarded-*` headers.
* `trusted_proxies`: the front end's source ranges. The gateway honors `X-Forwarded-For` only when the TCP peer is in this list, then walks the chain past trusted hops, so per-IP sign-in rate limits and audit events record developer IPs instead of the load balancer's.
On both tracks the front end is an internal ALB, whether created directly or by the AWS Load Balancer Controller, and an ALB's nodes take addresses from the subnets it is attached to, so set `trusted_proxies` to those subnets' CIDRs. This trusts every host in those subnets as a proxy. Keep the ALB's ingress source, your corporate CIDR, from overlapping them, and don't share the subnets with untrusted workloads that could spoof client IPs via `X-Forwarded-For`.
```yaml gateway.yaml theme={null}
listen:
host: 0.0.0.0
port: 8080
public_url: https://claude-gateway.internal.example.com
trusted_proxies: [<your-alb-subnet-cidrs>]
oidc:
issuer: https://example.okta.com
client_id: 0oa1example2
client_secret: ${OIDC_CLIENT_SECRET} # EKS: ${file:/secrets/oidc-client-secret}
allowed_email_domains: [example.com]
# The Okta org authorization server returns a thin id_token that omits
# email and groups; the gateway fills them from /userinfo.
userinfo_fallback: true
# Okta emits groups only when the `groups` scope is requested and the
# app's groups claim filter allows them.
scopes: [openid, profile, email, offline_access, groups]
session:
jwt_secret: ${GATEWAY_JWT_SECRET} # EKS: ${file:/secrets/jwt-secret}
ttl_hours: 8 # bounds deprovision latency; lower
# toward 1 for tighter revocation
store:
postgres_url: ${GATEWAY_POSTGRES_URL} # EKS: ${file:/secrets/postgres-url}
upstreams:
- provider: bedrock
region: <your-region> # match $AWS_REGION so the IAM
# policy's ARNs cover it
auth: {} # AWS default credential chain:
# ECS task role, or IRSA on EKS
```
<Note>
Only the `oidc` block is Okta-specific. To use Microsoft Entra ID instead, set `issuer` to `https://login.microsoftonline.com/<tenant-id>/v2.0`, drop `userinfo_fallback` and the `groups` scope, and note that Entra emits group Object IDs rather than names, so [`managed.policies`](/docs/en/claude-apps-gateway-config#managed) must match on the GUIDs, or on App Roles with `oidc.groups_claim: roles`. See [Identity provider setup](/docs/en/claude-apps-gateway-deploy#identity-provider-setup).
</Note>
</Step>
<Step title="Store secrets in AWS Secrets Manager">
Create three secrets; the execution role from the IAM step can already read them:
```bash theme={null}
aws secretsmanager create-secret --name gateway-jwt-secret \
--secret-string "$(openssl rand -base64 32)"
aws secretsmanager create-secret --name gateway-oidc-client-secret \
--secret-string '<your-okta-client-secret>'
aws secretsmanager create-secret --name gateway-postgres-url \
--secret-string "$GATEWAY_POSTGRES_URL"
```
Note the ARN each call prints; the ECS task definition references secrets by ARN.
<Note>
Literal `--secret-string` arguments are visible in the process table and in audit/EDR logs while each command runs. On a shared or monitored host, put the value in a `0600` file and pass `--secret-string file://<path>` instead. The bundle's `setup.sh` keeps secret values off process argv the same way, passing `0600` temporary files to `--cli-input-json`.
</Note>
Unlike the secrets, `gateway.yaml` itself contains no secret values, because every credential resolves at boot through [`${VAR}` or `${file:...}` expansion](/docs/en/claude-apps-gateway-config#secret-expansion). How everything reaches the container differs by track:
* On ECS, the next step's build copies `gateway.yaml` into the image at `/etc/claude/gateway.yaml`, and the task definition injects the three secrets as environment variables via its `secrets` field, so the YAML references `${GATEWAY_JWT_SECRET}`, `${OIDC_CLIENT_SECRET}`, and `${GATEWAY_POSTGRES_URL}`.
* On EKS, mount `gateway.yaml` from a ConfigMap and the secrets as files at `/secrets`, referenced as `${file:/secrets/...}`. Source the Kubernetes Secrets from Secrets Manager with External Secrets Operator or the Secrets Store CSI driver's AWS provider, or create them directly with `kubectl`.
</Step>
<Step title="Build and push the image to Amazon ECR">
Build the image per the [container image requirements](/docs/en/claude-apps-gateway-deploy#container-image), placing the `linux-x64` glibc binary at `./claude` in the build context. Write your own Dockerfile per those requirements or start from the bundle's [`Dockerfile`](https://github.com/anthropics/claude-code/blob/main/examples/gateway/aws/Dockerfile), which copies the filled-in `gateway.yaml` from the previous steps into the image at `/etc/claude/gateway.yaml`. On ECS that embedded copy is how the configuration reaches the container, which is why the build comes after the file is written. The EKS track instead mounts `gateway.yaml` from a ConfigMap at deploy, so the embedded copy is unused there.
The image also carries the AWS RDS certificate bundle as the trust anchor for the connection string's `sslmode=verify-full`, so download it into the build context first. AWS rotates the bundle (new regional CAs get appended), so download it per build rather than pinning a checksum or committing it:
```bash theme={null}
curl -fL --proto '=https' -o rds-global-bundle.pem \
https://truststore.pki.rds.amazonaws.com/global/global-bundle.pem
```
The container image requirements don't cover the bundle, so if you write your own Dockerfile, add the two lines that copy and trust it; the bundle's `Dockerfile` already includes both:
```dockerfile theme={null}
COPY rds-global-bundle.pem /etc/claude/rds-global-bundle.pem
ENV NODE_EXTRA_CA_CERTS=/etc/claude/rds-global-bundle.pem
```
Create the ECR repository and sign Docker in to it. Immutable tags mean the `<version>` tag the deploy step pins cannot later be silently re-pointed at a different image:
Cut at 300 lines. The page has the rest.
claude-apps-gateway-on-gcp First recorded · 315 lines, first recorded
# Deploy Claude apps gateway on Google Cloud ## What you'll build ## Prerequisites ## Deploy the gateway ## Terraform reference ## Troubleshooting ## Next steps
The first capture of this source. The page was already there, and this is what it said.
# Deploy Claude apps gateway on Google Cloud
> A worked example of running Claude apps gateway on Google Cloud: Cloud Run or GKE, Cloud SQL for PostgreSQL, Secret Manager, and service-account auth to Google Cloud's Agent Platform.
<Note>
This page walks through one way to run Claude apps gateway on Google Cloud. The configuration is a working example for customer-managed infrastructure rather than a supported production deployment; use it to see how the pieces fit together before adapting it to your own environment. For the platform-agnostic requirements, see the [deployment guide](/docs/en/claude-apps-gateway-deploy).
</Note>
This example provisions Claude apps gateway on Google Cloud with Google Cloud's Agent Platform as the model upstream, using either Cloud Run or GKE for compute. Google Workspace is the example identity provider (IdP), but any OpenID Connect (OIDC) compliant IdP works; only the `oidc` block changes. See [Identity provider setup](/docs/en/claude-apps-gateway-deploy#identity-provider-setup) for per-IdP details.
## What you'll build
<Frame>
<img src="https://mintcdn.com/claude-code/-uq-4JE0W_JO5Er5/images/claude-gateway-gcp-architecture.svg?fit=max&auto=format&n=-uq-4JE0W_JO5Er5&q=85&s=cb705151c69128ac0da235852d5600ab" alt="Diagram of Claude apps gateway on Google Cloud: Claude Code clients connect over HTTPS to the gateway (Cloud Run or GKE), which runs inside a VPC alongside a private-IP Cloud SQL database for session state. The gateway signs users in via OIDC against Google Workspace, reads config and secrets from Secret Manager, forwards model requests to Google Cloud's Agent Platform, and pulls its image from Artifact Registry at deploy." width="760" height="400" data-path="images/claude-gateway-gcp-architecture.svg" />
</Frame>
The deployment consists of:
* **Cloud Run** service or **GKE** Deployment running the gateway container
* **Artifact Registry** repository for the gateway image
* **Cloud SQL for PostgreSQL** instance, private IP only, for the gateway's [store](/docs/en/claude-apps-gateway-config#store)
* **Secret Manager** secrets for `gateway.yaml`, the JWT signing key, the OIDC client secret, and the Postgres URL
* **Service account** with `roles/aiplatform.user`, attached directly on Cloud Run or bound via Workload Identity on GKE
* **HTTPS front end** that you provide: an internal Application Load Balancer in front of Cloud Run, which this walkthrough configures the gateway for but doesn't create, or an internal **GKE Ingress** of class `gce-internal` on GKE
## Prerequisites
* A GCP project with billing enabled, and permission to create the resources above
* The `gcloud` CLI, authenticated with `gcloud auth login`, and Docker installed locally
* For the GKE track: `kubectl`, and a GKE cluster on the VPC created in the walkthrough below
* Access to the Claude models you need in Model Garden, in a region that publishes them
* A Google Workspace OAuth 2.0 web-application client with redirect URI `https://<gateway-host>/oauth/callback`; see [Identity provider setup](/docs/en/claude-apps-gateway-deploy#identity-provider-setup)
* A TLS hostname for the gateway, typically an internal DNS name pointing at the load balancer
Set the project and region once:
```bash theme={null}
export PROJECT_ID=<your-project>
export REGION=us-east5 # a region where the Claude models you need are published in Model Garden
gcloud config set project "$PROJECT_ID"
```
## Deploy the gateway
The steps below provision the full deployment with `gcloud` commands.
<Steps>
<Step title="Enable APIs">
Enable the service APIs the walkthrough uses:
```bash theme={null}
gcloud services enable \
aiplatform.googleapis.com \
artifactregistry.googleapis.com \
sqladmin.googleapis.com \
secretmanager.googleapis.com \
iamcredentials.googleapis.com \
iam.googleapis.com \
compute.googleapis.com \
servicenetworking.googleapis.com \
run.googleapis.com \
container.googleapis.com
```
The APIs you need depend on the deployment path:
* `compute` and `servicenetworking`: needed for the private-IP Cloud SQL path
* `run`: Cloud Run only
* `container`: GKE only
</Step>
<Step title="Create the service account and grant IAM">
The gateway runs as a dedicated service account with permission to call Google Cloud's Agent Platform. It reaches Cloud SQL over the VPC with a password user, so no Cloud SQL IAM role is required:
```bash theme={null}
gcloud iam service-accounts create claude-gateway --display-name="Claude apps gateway"
SA="claude-gateway@${PROJECT_ID}.iam.gserviceaccount.com"
gcloud projects add-iam-policy-binding "$PROJECT_ID" \
--member="serviceAccount:${SA}" --role="roles/aiplatform.user" --condition=None
```
Then enable the Claude models for the project in Model Garden; models publish to specific regions, so check each model card.
</Step>
<Step title="Build and push the image to Artifact Registry">
Build the image per the [container image requirements](/docs/en/claude-apps-gateway-deploy#container-image), using the `linux-x64` glibc binary, and push it:
```bash theme={null}
gcloud artifacts repositories create claude-gateway \
--repository-format=docker --location="$REGION"
gcloud auth configure-docker "${REGION}-docker.pkg.dev" --quiet
# Cloud Run requires linux/amd64. --provenance=false avoids a buildx OCI
# image index that Cloud Run rejects.
docker build --platform=linux/amd64 --provenance=false \
-t "${REGION}-docker.pkg.dev/${PROJECT_ID}/claude-gateway/gateway:<version>" .
docker push "${REGION}-docker.pkg.dev/${PROJECT_ID}/claude-gateway/gateway:<version>"
```
</Step>
<Step title="Provision Cloud SQL for PostgreSQL">
Create the instance on a VPC via Private Services Access so it has no public IP; this also satisfies projects where `constraints/sql.restrictPublicIp` is enforced:
```bash theme={null}
VPC=cc-gateway-vpc
gcloud compute networks create "$VPC" --subnet-mode=custom
gcloud compute networks subnets create cc-gateway-subnet \
--network="$VPC" --region="$REGION" --range=10.0.0.0/24
# Private Services Access: one-time per VPC
gcloud compute addresses create "google-managed-services-${VPC}" \
--global --purpose=VPC_PEERING --prefix-length=16 --network="$VPC"
gcloud services vpc-peerings connect \
--service=servicenetworking.googleapis.com \
--ranges="google-managed-services-${VPC}" --network="$VPC"
gcloud sql instances create claude-gateway-db \
--database-version=POSTGRES_16 --tier=db-g1-small --region="$REGION" \
--network="projects/${PROJECT_ID}/global/networks/${VPC}" --no-assign-ip
gcloud sql databases create claude_gateway --instance=claude-gateway-db
PGPASS="$(openssl rand -hex 24)"
gcloud sql users create gateway --instance=claude-gateway-db --password="$PGPASS"
PRIVATE_IP="$(gcloud sql instances describe claude-gateway-db \
--format='value(ipAddresses[0].ipAddress)')"
GATEWAY_POSTGRES_URL="postgres://gateway:${PGPASS}@${PRIVATE_IP}:5432/claude_gateway?sslmode=require"
```
The Cloud Run or GKE runtime must be on, or routed into, this VPC.
</Step>
<Step title="Write gateway.yaml">
The `upstreams` block points at Google Cloud's Agent Platform with `auth: {}`, so the gateway authenticates via Application Default Credentials from the runtime service account. See the [configuration reference](/docs/en/claude-apps-gateway-config) for every field.
Two `listen` fields describe what fronts the gateway:
* `public_url`: the external `https://` origin, required for any non-loopback bind; see the [`listen` reference](/docs/en/claude-apps-gateway-config#listen). The gateway builds the IdP `redirect_uri` and its discovery document only from this value, never from `X-Forwarded-*` headers.
* `trusted_proxies`: the front end's source ranges. The gateway honors `X-Forwarded-For` only when the TCP peer is in this list, then walks the chain past trusted hops, so per-IP sign-in rate limits and audit events record developer IPs instead of the load balancer's.
Set `trusted_proxies` to match your front end. An external GKE Ingress of class `gce` isn't listed: it provisions a public forwarding-rule address, which the `/login` [private-network check](/docs/en/claude-apps-gateway#prerequisites) rejects.
| Front end | `trusted_proxies` |
| -------------------------------------------------------- | --------------------------------------------------- |
| Cloud Run reached directly, no load balancer | `[169.254.0.0/16]` |
| Internal Application Load Balancer in front of Cloud Run | `169.254.0.0/16` plus your proxy-only subnet's CIDR |
| GKE internal Ingress, class `gce-internal` | Your proxy-only subnet's CIDR |
The example below uses the internal-load-balancer-in-front-of-Cloud-Run values.
```yaml gateway.yaml theme={null}
listen:
host: 0.0.0.0
port: 8080
public_url: https://claude-gateway.internal.example.com
trusted_proxies: [169.254.0.0/16, <your-proxy-only-subnet-cidr>]
oidc:
issuer: https://accounts.google.com
client_id: <your-oauth-client-id>
client_secret: ${OIDC_CLIENT_SECRET} # GKE: ${file:/secrets/oidc-client-secret}
allowed_email_domains: [example.com]
# Google ignores offline_access; these yield refresh tokens:
scopes: [openid, profile, email]
extra_auth_params: { access_type: offline, prompt: consent }
session:
jwt_secret: ${GATEWAY_JWT_SECRET} # GKE: ${file:/secrets/jwt-secret}
store:
postgres_url: ${GATEWAY_POSTGRES_URL} # GKE: ${file:/secrets/postgres-url}
upstreams:
- provider: vertex
region: <your-region> # must match $REGION
project_id: <your-project>
auth: {} # ADC via the runtime service account
```
<Note>
Google id\_tokens carry no `groups` claim. To use group-based policies in [`managed.policies`](/docs/en/claude-apps-gateway-config#managed) with Google Workspace as the IdP, configure [`oidc.google_groups`](/docs/en/claude-apps-gateway-config#oidc), which looks up each user's groups through the Admin SDK Directory API using a service account with domain-wide delegation. Without it, match on `email_domain` instead.
</Note>
</Step>
<Step title="Store secrets in Secret Manager">
Create four secrets and grant `roles/secretmanager.secretAccessor` to the `claude-gateway` service account:
| Secret | Source |
| ---------------------------- | ----------------------------------------------- |
| `gateway-jwt-secret` | `openssl rand -base64 32` |
| `gateway-oidc-client-secret` | Google Cloud Console → OAuth client |
| `gateway-postgres-url` | `$GATEWAY_POSTGRES_URL` from the Cloud SQL step |
| `gateway-config` | the full `gateway.yaml` from the previous step |
How the secrets reach the container differs by track:
* On GKE they mount as files via the Secret Manager CSI driver, and `gateway.yaml` references `${file:/secrets/...}`.
* On Cloud Run, which can't mount multiple secrets into one directory, `gateway.yaml` mounts as a file and the other three inject as environment variables, so `gateway.yaml` references `${GATEWAY_JWT_SECRET}`, `${OIDC_CLIENT_SECRET}`, and `${GATEWAY_POSTGRES_URL}` instead.
</Step>
<Step title="Deploy">
<Tabs>
<Tab title="Cloud Run">
The command below deploys for production behind an internal load balancer.
```bash theme={null}
gcloud run deploy claude-gateway \
--image="${REGION}-docker.pkg.dev/${PROJECT_ID}/claude-gateway/gateway:<version>" \
--region="$REGION" \
--service-account="claude-gateway@${PROJECT_ID}.iam.gserviceaccount.com" \
--min-instances=1 \
--max-instances=8 \
--timeout=3600 \
--ingress=internal \
--network="$VPC" --subnet=cc-gateway-subnet --vpc-egress=private-ranges-only \
--set-secrets=/etc/claude/gateway.yaml=gateway-config:latest,GATEWAY_JWT_SECRET=gateway-jwt-secret:latest,OIDC_CLIENT_SECRET=gateway-oidc-client-secret:latest,GATEWAY_POSTGRES_URL=gateway-postgres-url:latest \
--no-invoker-iam-check
```
Direct VPC egress, via `--network`, `--subnet`, and `--vpc-egress=private-ranges-only`, lets the service reach the Cloud SQL private IP directly. Each instance holds up to [`store.max_connections`](/docs/en/claude-apps-gateway-config#store) Postgres connections, five by default, so keep maximum instances × `store.max_connections` below your Cloud SQL tier's connection limit; the [reference assets](#terraform-reference) cap instances at 8 for the `db-g1-small` tier for this reason. Public egress to Google Cloud's Agent Platform endpoints and `accounts.google.com` goes directly to the internet rather than through the VPC, so no Cloud NAT is needed.
The invoker IAM check must be open or disabled. The gateway runs its own OIDC and its clients carry no GCP token, so Cloud Run's invoker check has to admit unauthenticated requests. The gateway's OIDC sign-in authenticates the request once it reaches the container, with `allowed_email_domains` gating which domains may sign in.
Two flags admit unauthenticated requests:
* `--no-invoker-iam-check`: disables the check with no `allUsers` binding to manage, and works under Domain Restricted Sharing
* `--allow-unauthenticated`: grants `allUsers` the `run.invoker` role; use it if your organization doesn't allow `--no-invoker-iam-check`
Ingress restriction via `--ingress` is a separate, independent layer from the invoker check; keep it set to limit the service to your corporate network.
By default the Cloud Run `*.run.app` URL resolves to a public address, which the `/login` [private-network check](/docs/en/claude-apps-gateway#prerequisites) rejects. Two topologies give developers a privately resolvable hostname, and Cloud Run provisions neither for you:
* **Internal Application Load Balancer**, the topology this page's `gateway.yaml` assumes: provision an internal Application Load Balancer in front of the service with an internal DNS name and certificate, and set `listen.public_url` to that hostname. The `internal` ingress setting already admits traffic from internal Application Load Balancers; `internal-and-cloud-load-balancing` additionally admits external Application Load Balancers, whose public addresses the `/login` private-network check rejects, so no topology on this page needs it.
* **Internal-only ingress with no load balancer**: keep the deploy command as is and leave `listen.public_url` as the `*.run.app` URL, the default in the [reference assets](#terraform-reference) below. For `*.run.app` to resolve privately, your network team must already operate a Private Service Connect endpoint for Google APIs, a Cloud DNS private zone resolving `*.run.app` to it, and on-premises routing to that endpoint.
Google's [private networking guide for Cloud Run](https://cloud.google.com/run/docs/securing/private-networking) covers the infrastructure both options need. Verify sign-in once the gateway is serving on a private hostname; until then, confirm the container booted from its logs in Cloud Run.
Update the OAuth client's authorized redirect URI to `<public_url>/oauth/callback` before the first sign-in. Redeploy after changing `public_url`, because the gateway builds its public origin only from that setting and ignores `X-Forwarded-Host` and `X-Forwarded-Proto`. `X-Forwarded-For` is honored for client IPs only when `listen.trusted_proxies` is set.
</Tab>
<Tab title="GKE">
The cluster must be on the `$VPC` created in the Cloud SQL step so pods can reach the database's private IP; VPC peering alone doesn't work, because Cloud SQL private IP is itself a peered network and peering is non-transitive. To create a new cluster on that VPC, pass `--network="$VPC" --subnetwork=cc-gateway-subnet` to `gcloud container clusters create`.
Enable Workload Identity on the cluster and its node pools, then bind the Google service account to the Kubernetes service account so pods inherit its credentials:
```bash theme={null}
gcloud container clusters update <cluster> --region="$REGION" \
--workload-pool="${PROJECT_ID}.svc.id.goog"
# On a Standard cluster, existing node pools also need GKE_METADATA;
# Autopilot enables this by default.
gcloud container node-pools update <pool> --cluster=<cluster> \
--region="$REGION" --workload-metadata=GKE_METADATA
kubectl create namespace claude-gateway
kubectl create serviceaccount gateway -n claude-gateway
gcloud iam service-accounts add-iam-policy-binding \
"claude-gateway@${PROJECT_ID}.iam.gserviceaccount.com" \
--role roles/iam.workloadIdentityUser \
--member "serviceAccount:${PROJECT_ID}.svc.id.goog[claude-gateway/gateway]"
kubectl annotate serviceaccount gateway -n claude-gateway \
iam.gke.io/gcp-service-account="claude-gateway@${PROJECT_ID}.iam.gserviceaccount.com"
```
Deploy the gateway as a standard Deployment plus a Service and an internal Ingress, class `gce-internal`, as described in [Kubernetes deployment](/docs/en/claude-apps-gateway-deploy#kubernetes), with:
* `serviceAccountName: gateway`
* the Secret Manager CSI driver mounting secrets at `/secrets`
* the readiness probe pointed at `GET /readyz`
Attach a BackendConfig with a raised `timeoutSec` to the gateway Service: the load balancer backend service behind GKE Ingress defaults to a 30-second timeout, which cuts off long streaming responses.
Don't apply an egress NetworkPolicy that blocks `169.254.169.254` on a Workload Identity cluster; the pod must reach the metadata server for credentials. The gateway's built-in [SSRF guard](/docs/en/claude-apps-gateway-deploy#threat-model-summary) is the defense there.
The gateway logs a boot warning that the metadata endpoint is reachable and suggests applying an egress NetworkPolicy. Under Workload Identity that warning is expected, because the pod needs the endpoint.
</Tab>
</Tabs>
</Step>
<Step title="Push the gateway URL to developer machines">
The gateway is now running, but developers can't reach it from `/login` until the gateway URL is on their machines. Deploy the full [managed settings snippet](/docs/en/claude-apps-gateway#set-the-gateway-url), with `forceLoginMethod`, `forceLoginGatewayUrl`, and the `parentSettingsBehavior: "merge"` opt-in, to each device via MDM. There is no gateway option in the login picker for a developer to select manually.
</Step>
</Steps>
## Terraform reference
The [reference deployment assets](https://github.com/anthropics/claude-code/tree/main/examples/gateway/gcp) automate the Cloud Run track on this page; the config and image assets apply to both tracks:
* `setup.sh`: an idempotent `gcloud` provisioner that walks the full Cloud Run path, from enabling APIs through the first deploy
* `terraform/`: the same deployment as infrastructure-as-code, for a greenfield deploy: a targeted apply to create the Artifact Registry repo, then build and push the image, then a full apply
* `gateway.yaml.example` and a `Dockerfile` for the distroless runtime image
The artifacts default Cloud Run ingress to `internal`, matching the deploy command on this page; that setting works with or without an internal Application Load Balancer in front of the service, and the artifacts don't create the load balancer either. The artifacts also default the invoker layer to an `allUsers` `run.invoker` grant rather than `--no-invoker-iam-check`, the inverse of this page's walkthrough; either works, and the choice depends on your organization's policy constraints.
The assets are provided as working examples, not as a supported production artifact; review and adapt them to your environment.
## Troubleshooting
For gateway boot and login errors, see the platform-agnostic [troubleshooting table](/docs/en/claude-apps-gateway-deploy#troubleshooting). The entries below are specific to Google Cloud.
Cut at 300 lines. The page has the rest.
claude-apps-gateway-spend-limits First recorded · 160 lines, first recorded
# Claude apps gateway spend limits ## Set a cap ### Authenticate to the admin API ## How enforcement works ### How requests are priced ### Postgres availability ### Usage warnings in Claude Code ## Admin API reference ### `/effective` ### `/audit` ### Pagination ## Data lifecycle ## Related
The first capture of this source. The page was already there, and this is what it said.
# Claude apps gateway spend limits
> Cap each developer's spend through the Claude apps gateway by day, week, or month. Set limits with an Admin API and the gateway enforces them live on every request.
Spend limits cap how much each developer can spend through your [Claude apps gateway](/docs/en/claude-apps-gateway) in a given day, week, or month. When a developer passes their cap, the gateway returns `429` on their next request and blocks them until the period resets or an admin raises the cap. Use spend limits to give each developer, group, or the whole organization a ceiling on a credential everyone shares.
A Claude apps gateway forwards all inference through one shared upstream credential, so your provider's bill attributes everything to that credential, not to individual developers. Without per-developer limits, one runaway agent fleet can spend the organization's entire commitment. Spend limits are the gateway's per-developer view and circuit breaker on top of that shared bill.
## Set a cap
With the [`admin:`](/docs/en/claude-apps-gateway-config#admin) block configured in `gateway.yaml`, the gateway serves an admin API at `/v1/organizations/spend_limits` and enforces caps live on every inference request. Caps themselves are set through that API, not in `gateway.yaml`; each `POST /v1/organizations/spend_limits` request creates or replaces one cap from `{scope, amount, period}`. The API mirrors the wire shapes of Anthropic's public [Admin API](https://platform.claude.com/docs/en/manage-claude/admin-api) spend-limits endpoints, so an HTTP client written against that contract can target the gateway by changing its base URL.
This request sets an org-wide default of \$500 per month for every developer:
```bash theme={null}
curl -sS https://claude-gateway.internal.example.com/v1/organizations/spend_limits \
-H "x-api-key: $GATEWAY_ADMIN_WRITE_KEY" \
-H "Content-Type: application/json" \
-d '{"scope": {"type": "organization"}, "amount": "50000", "period": "monthly"}'
```
This request layers a tighter \$100-per-day cap on each member of the `contractors` group:
```bash theme={null}
curl -sS https://claude-gateway.internal.example.com/v1/organizations/spend_limits \
-H "x-api-key: $GATEWAY_ADMIN_WRITE_KEY" \
-H "Content-Type: application/json" \
-d '{"scope": {"type": "rbac_group", "rbac_group_id": "contractors"}, "amount": "10000", "period": "daily"}'
```
| Field | Values | Description |
| ------------ | ------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `scope.type` | `user`, `rbac_group`, `organization` | `user` targets one developer by their OpenID Connect (OIDC) `sub`, the stable user ID your identity provider assigns; pass it as `scope.user_id`. `rbac_group` targets an [IdP group](/docs/en/claude-apps-gateway-config#managed) by name; pass it as `scope.rbac_group_id`. `organization` is the org-wide default. The gateway accepts all three; Anthropic's public `POST` is user-only today. |
| `amount` | Whole-number string of USD cents, or `null` | `null` is unlimited. `"0"` is a zero cap, which blocks every request. |
| `period` | `daily`, `weekly`, `monthly` | A scope can hold one cap per period, and each enforces independently: a developer is blocked if over any of them. |
A group or organization cap is a per-seat default that each member inherits, not a shared pool. Per period, a developer's effective cap resolves in this order: a per-user override, then the most restrictive of their group caps, then the org default, then unlimited. [`admin.group_limit_mode: max`](/docs/en/claude-apps-gateway-config#admin) flips the multi-group tie-break to least-restrictive instead.
### Authenticate to the admin API
Send one of:
* An `x-api-key` header matching a key in [`admin.write_keys`](/docs/en/claude-apps-gateway-config#admin) for full access, or `admin.read_keys` for `GET`-only access. Each key carries an `id` that appears in the audit log as `admin-key:<id>`, so give Terraform, CI, and each automation its own.
* A gateway bearer token whose `groups` claim includes one of [`admin.admin_groups`](/docs/en/claude-apps-gateway-config#admin). This is full access and audits as `oidc:<sub>`, so prefer it for human admins.
## How enforcement works
On each `/v1/messages` request, the gateway looks up the developer's caps and period-to-date spend in one Postgres query. A developer over any cap gets a `429` with `error.type: billing_error` and header `x-should-retry: false`.
The message names the period and reset time, such as `spend limit reached (daily; resets 2026-08-08 00:00 UTC)`, followed by your [`admin.blocked_message`](/docs/en/claude-apps-gateway-config#admin) if set. When a developer exceeds several caps at once, the message names the cap that resets last. The response also carries a `retry-after` header with the seconds remaining until that reset. Before v2.1.225 on the gateway server, the message was `spend limit reached` with no period, reset time, or `retry-after` header.
On v2.1.227 or later, the protocol reference at `<public_url>/protocol` also lists the exact usage-limit response headers and `429` body.
Caps reset on UTC calendar boundaries: daily at 00:00 UTC, weekly on Monday, and monthly on the first. The gateway never blocks `/v1/messages/count_tokens`, because token counting is free.
### How requests are priced
After each response, a usage meter reads the token counts and adds the cost to the daily, weekly, and monthly counters. It never touches the bytes sent to the client, so a metering failure can't break a response. The amounts are USD estimates, a circuit breaker rather than an invoice; for billing, reconcile against your provider's usage reporting.
The meter picks each request's rates in this order:
1. A matching [`pricing.overrides`](/docs/en/claude-apps-gateway-config#pricing) row for the upstream that served the request. Requires v2.1.227 or later.
2. List price for the upstream model ID, the string the gateway sends to the provider, when the Claude Code cost table recognizes it. The table accepts Anthropic, Amazon Bedrock, Google Cloud's Agent Platform, and Microsoft Foundry ID forms.
3. List price for the [`models[].id`](/docs/en/claude-apps-gateway-config#models) you mapped to that upstream ID, for upstream strings that carry no model name, such as an Amazon Bedrock application-inference-profile ARN or a Microsoft Foundry deployment name. Requires v2.1.218 or later.
4. The unknown-model tier of \$5/\$25 per million input/output tokens, so an ID the meter can't place is never free. The gateway warns at boot and once per ID at runtime when it uses this tier.
Whichever rate applies, the meter then multiplies the amount by [`pricing.multiplier`](/docs/en/claude-apps-gateway-config#pricing), default `1`.
Client aborts are billed too. When a stream ends without the upstream's final usage frame, the meter bills a floor estimate of about four characters per output token for the text already sent to the client, so aborting requests early doesn't evade a cap.
### Postgres availability
The pre-check queries Postgres with a two-second timeout. If the store is unreachable or times out, enforcement fails open by default: the request proceeds, the gateway logs a warning, and the response carries no `anthropic-ratelimit-unified-*` headers. Set [`enforcement.fail_closed_on_error: true`](/docs/en/claude-apps-gateway-config#enforcement) to fail closed instead, which returns the same `429 billing_error` but with the message `spend limit unavailable` and no period, reset time, or `retry-after` header. Fail-open keeps a store outage from becoming an inference outage; fail-closed guarantees no unmetered spend.
### Usage warnings in Claude Code
Claude Code warns a developer as they approach their cap: once utilization passes 75%, and again past 95% of their fullest cap. When the gateway blocks a request, Claude Code shows the gateway's `429` message as is, including your `admin.blocked_message`.
The warning works off response headers:
* With v2.1.225 or later on the gateway server, each successful `/v1/messages` response for a developer who has a cap carries their own cap utilization and reset time in the `anthropic-ratelimit-unified-*` headers.
* With v2.1.225 or later on the developer's machine as well, Claude Code reads the headers and shows the warning.
The headers always describe the developer's own cap: the gateway strips the upstream provider's rate-limit headers, which describe your shared quota, and never forwards them.
## Admin API reference
The endpoints below are served under `/v1/organizations/spend_limits`.
| Method and path | Description |
| ---------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `GET /v1/organizations/spend_limits` | List configured caps, optionally filtered to one `scope_type` of `organization`, `rbac_group`, or `user`. Query: `?limit=&after_id=&before_id=&scope_type=`. |
| `POST /v1/organizations/spend_limits` | Create or replace a cap for `{scope, period}`. |
| `GET /v1/organizations/spend_limits/{id}` | Fetch one cap by its `spl_`-prefixed ID. |
| `DELETE /v1/organizations/spend_limits/{id}` | Delete one cap. Returns `{type: "spend_limit_deleted", id}`. |
| `GET /v1/organizations/spend_limits/effective` | Resolved cap and to-date spend per principal per period. |
| `GET /v1/organizations/spend_limits/audit` | Admin mutation trail, newest-first. Query: `?limit=&after_id=`. |
Conventions mirror Anthropic's Admin API:
* A `type` on every object
* `spl_`-prefixed IDs
* Amounts as whole-number strings of USD cents; `POST` rejects any other `currency` with `400`
* The `{type: "error", error: {type, message}, request_id}` error envelope
* A `request-id` response header on every admin response, success or error; error bodies also carry it as `request_id`
Every mutation writes a before/after row to `admin_audit` in the same transaction, attributed to `admin-key:<id>` or `oidc:<sub>`.
The gateway serves the spend-limits endpoints only. Other Admin API surfaces, such as the `spend_limit_increase_requests` queue, aren't part of the gateway's admin API.
### `/effective`
`GET /v1/organizations/spend_limits/effective` returns Anthropic's `SpendSummary` schema: each row is a principal for a period, with the resolved cap, period-to-date spend, and an `actor` object. Gateway-specific differences:
* `user_id` is the OIDC `sub`.
* `actor.name` and `actor.email_address` are `null` until the principal's first inference request through the gateway. The gateway has no user directory; it records last-seen values from each user's own session JWT.
* Each row also carries a `groups` array, the principal's last-seen IdP groups. This is a gateway extension so an admin UI can show every cap tier that applies; Anthropic-shaped clients ignore it.
* Without a `user_ids[]` filter, it lists principals with recorded spend, because the gateway can't enumerate all org members.
Group-sourced caps resolve against those last-seen groups with the same `group_limit_mode` tie-break that enforcement uses, so the viewer shows the cap that actually applies.
| Query parameter | Description |
| ---------------- | ------------------------------------------------------------------------------------------------------- |
| `user_ids[]` | Repeatable. Filter to specific principals by OIDC `sub`. |
| `period[]` | Repeatable. Filter to `daily`, `weekly`, or `monthly` rows. |
| `sort` | `spend_desc` lists top spenders first. Requires exactly one `period[]`. |
| `q` | Case-insensitive substring filter over the OIDC `sub`, last-seen email, and last-seen display name. |
| `limit` / `page` | Page size, 1–1000 with a default of 20, and the opaque cursor from the previous response's `next_page`. |
<Warning>
`q=` and `user_ids[]=` ride GET query strings, so any fronting proxy or load balancer captures them in its access logs. If your PII log policy is strict, scrub these parameters there.
</Warning>
### `/audit`
Returns the spend-limit mutation trail: who changed which cap, with before/after snapshots, newest-first. `has_more` is exact. This endpoint follows the local Admin API conventions rather than a first-party wire shape.
### Pagination
The raw list pages by `after_id` and `before_id`, which are mutually exclusive `spl_…` IDs; results are ordered by creation and `has_more` reflects the traversal direction. `/effective` pages by the opaque `next_page` token passed back as `?page=`, with principals ordered ascending so pages stay stable while spend is being recorded. `limit` is 1–1000, default 20, on both. `/audit` pages by `after_id`, the numeric `id` of the last event on the previous page, and its `limit` defaults to 100.
## Data lifecycle
The gateway holds four spend-related tables; an hourly sweep enforces the retention windows:
| Table | Contents | Retention |
| ------------------ | ----------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------- |
| `spend` | Per-principal period-to-date counters in cents | [`admin.spend_retention_months`](/docs/en/claude-apps-gateway-config#admin), default 13 |
| `spend_limits` | The configured caps | Until deleted via the API |
| `admin_audit` | The mutation trail | [`admin.audit_retention_days`](/docs/en/claude-apps-gateway-config#admin), default 365 |
| `principal_emails` | Each principal's last-seen email, display name, and IdP groups. Contains PII. | [`admin.identity_retention_days`](/docs/en/claude-apps-gateway-config#admin) since last activity, default 90 |
`identity_retention_days` is deliberately shorter than `spend_retention_months`: a deprovisioned identity stops refreshing and ages out, while its anonymous spend counters remain for year-over-year reporting.
When a developer leaves, delete any per-user cap via `DELETE /v1/organizations/spend_limits/{id}`; their spend and identity rows age out on the retention windows above. To erase one person immediately, for offboarding or a data subject access request (DSAR), run `DELETE FROM principal_emails WHERE principal = '<sub>'` directly against the gateway database. That removes the only table holding their email, name, and groups. The `spend` and `admin_audit` rows reference the pseudonymous OIDC `sub` only and age out on their own windows.
## Related
* [`admin` and `enforcement` configuration](/docs/en/claude-apps-gateway-config#admin): enabling the admin API and tuning retention
* [Deployment guide](/docs/en/claude-apps-gateway-deploy#postgres): Postgres schema and backup guidance
claude-code-on-the-web First recorded · 358 lines, first recorded
# Use Claude Code on the web ## Cloud environments ## GitHub authentication options ## Move tasks between web and terminal ### From terminal to web #### Tips for cloud tasks #### Send local repositories without GitHub ### Send follow-ups from the CLI #### Output and errors ### From web to terminal #### Teleport requirements #### `--teleport` is unavailable ## Work with sessions ### Manage context ### Review changes ### Share sessions #### Share from an Enterprise or Team account #### Share from a Max or Pro account ### Archive sessions ### Delete sessions ## Auto-fix pull requests ### How Claude responds to PR activity ## Security and isolation ## Troubleshooting ### Session creation failed ### Unable to get organization UUID ### Remote Control session expired or access denied ### Environment expired ## Limitations ## Related resources
The first capture of this source. The page was already there, and this is what it said.
# Use Claude Code on the web
> Move sessions between web and terminal with `--cloud` and `--teleport`, manage and share sessions, and auto-fix pull requests from the cloud.
<Note>
Claude Code on the web is in research preview for Pro, Max, and Team users, and for Enterprise users with premium seats or Chat + Claude Code seats.
</Note>
Claude Code on the web runs tasks on Anthropic-managed cloud infrastructure at [claude.ai/code](https://claude.ai/code), or on your organization's [self-hosted environment](/docs/en/self-hosted-environments) when routed there. Sessions persist even if you close your browser, and you can monitor them from the Claude mobile app.
<Tip>
New to Claude Code on the web? Start with [Get started](/docs/en/web-quickstart) to connect your GitHub account and submit your first task.
</Tip>
This page covers the web product itself:
* [Cloud environments](#cloud-environments): where sessions run, and where to configure that
* [GitHub authentication options](#github-authentication-options): two ways to connect GitHub
* [Move tasks between web and terminal](#move-tasks-between-web-and-terminal) with `--cloud` and `--teleport`
* [Work with sessions](#work-with-sessions): reviewing, sharing, archiving, deleting
* [Auto-fix pull requests](#auto-fix-pull-requests): respond automatically to CI failures and review comments
* [Security and isolation](#security-and-isolation): how sessions are isolated
* [Limitations](#limitations): rate limits and platform restrictions
## Cloud environments
Every cloud session runs in a [cloud environment](/docs/en/cloud-environments), the saved configuration that controls network access, environment variables, and setup scripts. When you onboard, Claude Code sets up a **Default** environment with [**Trusted** network access](/docs/en/cloud-environments#access-levels). See [The Default environment](/docs/en/cloud-environments#the-default-environment) for how it's created and how sessions choose an environment when you have more than one.
The same environments apply wherever you start a cloud session: the web, the terminal, [Claude Tag](https://claude.com/docs/claude-tag/overview), [routines](/docs/en/routines), and the mobile and Desktop apps. Claude Tag channel sessions use [organization-shared environments](/docs/en/cloud-environments#organization-shared-environments) only.
See [Configure cloud environments](/docs/en/cloud-environments) to change what an environment allows, set variables, or add a setup script, and [Installed tools](/docs/en/cloud-environments#installed-tools) for what sessions include without any configuration.
## GitHub authentication options
Cloud sessions need access to your GitHub repositories to clone code and push branches. You can grant access in two ways:
| Method | How it works | Best for |
| :--------------- | :------------------------------------------------------------------------------------------ | :---------------------------------------------------------------------- |
| **GitHub App** | Authorize the Claude GitHub App during [web onboarding](/docs/en/web-quickstart). | Browser onboarding; teams that want [Auto-fix](#auto-fix-pull-requests) |
| **`/web-setup`** | Run `/web-setup` in your terminal to sync your local `gh` CLI token to your Claude account. | Individual developers who already use `gh` |
<Note>
With either method, a cloud session can access any repository the connecting GitHub account can see, not just the repositories the Claude GitHub App is installed on. App installation enables PR webhooks for [Auto-fix](#auto-fix-pull-requests); it is not a session-level access control. To restrict which repositories your team can reach from cloud sessions, restrict access on GitHub itself, for example by limiting team or repository membership for the connected GitHub accounts.
</Note>
Either method works. [`/schedule`](/docs/en/routines) checks for either form of access and prompts you to run `/web-setup` if neither is configured. See [Connect from your terminal](/docs/en/web-quickstart#connect-from-your-terminal) for the `/web-setup` walkthrough.
The GitHub App is required for [Auto-fix](#auto-fix-pull-requests), which uses the App to receive PR webhooks. If you connect with `/web-setup` and later want Auto-fix, install the App on those repositories.
Team and Enterprise Owners can disable `/web-setup` with the Quick web setup toggle at [claude.ai/admin-settings/claude-code](https://claude.ai/admin-settings/claude-code).
<Note>
Organizations with [Zero Data Retention](/docs/en/zero-data-retention) enabled can't use `/web-setup` or other cloud session features.
</Note>
## Move tasks between web and terminal
These workflows require the [Claude Code CLI](/docs/en/quickstart) signed in to the same claude.ai account. You can start new cloud sessions from your terminal, or pull cloud sessions into your terminal to continue locally. Cloud sessions persist even if you close your laptop, and you can monitor them from anywhere including the Claude mobile app.
<Note>
From the CLI, session handoff is one-way: you can pull cloud sessions into your terminal with `--teleport`, but you can't push an existing terminal session to the web. The `--cloud` flag with a task description creates a new cloud session for your current repository; with a session ID or claude.ai/code URL it instead targets that existing session, [queueing a message or attaching your terminal](/docs/en/claude-code-on-the-web#send-follow-ups-from-the-cli). The [Desktop app](/docs/en/desktop#continue-in-another-surface) provides a Continue in menu that can send a local session to the web.
</Note>
### From terminal to web
Start a cloud session from the command line with the `--cloud` flag:
```bash theme={null}
claude --cloud "Fix the authentication bug in src/auth/login.ts"
```
This creates a new cloud session on claude.ai. The cloud VM clones your current directory's GitHub remote at your current branch, not your local checkout, so push first if you have local commits. `--cloud` works with a single repository at a time. The task runs in the cloud while you continue working locally. The older `--remote` spelling still works as a deprecated alias for `--cloud`.
While the cloud container starts, the CLI shows a live checklist of setup steps, such as cloning the repository and running your [setup script](/docs/en/cloud-environments#setup-scripts). It queues messages you type during provisioning and sends them once the session is ready.
<Note>
`--cloud` creates cloud sessions. `--remote-control` is unrelated: it exposes a local CLI session for monitoring from the web. See [Remote Control](/docs/en/remote-control).
</Note>
Use `/tasks` in the Claude Code CLI to check progress, or open the session on claude.ai or the Claude mobile app to interact directly. From there you can steer Claude, provide feedback, or answer questions as in any other conversation.
If Claude asks a question and the session sits idle, you can still answer when you come back, up to [environment expiry](#environment-expired), and the session continues from your answer.
#### Tips for cloud tasks
**Plan locally, execute remotely**: for complex tasks, start Claude in plan mode to collaborate on the approach, then send work to the cloud:
```bash theme={null}
claude --permission-mode plan
```
In plan mode, Claude reads files, runs commands to explore, and proposes a plan without editing source code. Once you're satisfied, save the plan to the repo, commit, and push so the cloud VM can clone it. Then start a cloud session for autonomous execution:
```bash theme={null}
claude --cloud "Execute the migration plan in docs/migration-plan.md"
```
This pattern gives you control over the strategy while letting Claude execute autonomously in the cloud.
**Run tasks in parallel**: each `--cloud` command creates its own cloud session that runs independently. You can start multiple tasks and they'll all run simultaneously in separate sessions:
```bash theme={null}
claude --cloud "Fix the flaky test in auth.spec.ts"
claude --cloud "Update the API documentation"
claude --cloud "Refactor the logger to use structured output"
```
Monitor all sessions with `/tasks` in the Claude Code CLI. When a session completes, you can create a PR from the web interface or [teleport](#from-web-to-terminal) the session to your terminal to continue working.
#### Send local repositories without GitHub
When you run `claude --cloud` from a repository that isn't connected to GitHub, Claude Code bundles your local repository and uploads it directly to the cloud session. The bundle includes your full repository history across all branches, plus any uncommitted changes to tracked files.
This fallback activates automatically when GitHub access isn't available. To force it even when GitHub is connected, set `CCR_FORCE_BUNDLE=1`:
```bash theme={null}
CCR_FORCE_BUNDLE=1 claude --cloud "Run the test suite and fix any failures"
```
Bundled repositories must meet these limits:
* The directory must be a git repository with at least one commit
* The bundled repository must be under 100 MB. Larger repositories fall back to bundling only the current branch, then to a single squashed snapshot of the working tree, and fail only if the snapshot is still too large
* Untracked files are not included; run `git add` on files you want the cloud session to see
* Sessions created from a bundle can't push back to a remote unless you also have [GitHub authentication](#github-authentication-options) configured
### Send follow-ups from the CLI
Once a cloud session is running, wherever it executes, send it a follow-up message from the `claude` CLI on any machine where you're logged in with `claude auth login`. The CLI authenticates with your Anthropic account credentials and sends no local session state, so the command doesn't need to run from the machine that started the session, and it's the same in every shell, including PowerShell.
The primary form posts one message and exits:
```bash theme={null}
claude -p "your message" --cloud <session-id>
```
The CLI queues the message into the session and exits without waiting for a reply. Use it to steer a long-running session, queue the next step while the current one is still finishing, or send follow-ups from a [CI script](/docs/en/self-hosted-environments-testing#run-the-test-loop). You can also pipe the message on stdin instead of passing it as an argument: `echo "your message" | claude -p --cloud <session-id>`.
Without `-p`, `claude --cloud <session-id>` attaches your terminal to the session so you can converse with it directly. Interactive attach is rolling out gradually; if you see `Attaching to an existing cloud session is not enabled for your account`, contact your Anthropic account team. The `-p` queue-and-exit form isn't affected by this rollout.
For `<session-id>`, pass the bare ID, such as `session_...` or `cse_...`, or the session's `claude.ai/code/<id>` URL, with or without the scheme or query string. Find the ID in your session list at claude.ai/code.
<Note>
`--cloud` requires an Anthropic account. It's not available when Claude Code is configured for Amazon Bedrock, Google Cloud's Agent Platform, or another third-party provider. An [LLM gateway](/docs/en/llm-gateway) configured only through `ANTHROPIC_BASE_URL` doesn't count as a third-party provider for this check, but you still need to sign in with `claude auth login`. Your organization's `allow_remote_sessions` policy must also be enabled. An Owner can turn it on in the Claude Code admin settings at claude.ai/admin-settings/claude-code.
</Note>
#### Output and errors
On success, the queue-and-exit form prints the session ID and a link to view the session:
```
Sent to cloud session.
Session ID: session_01DiUkqY2kzbUbDmW1w96rfi
View: https://claude.ai/code/session_01DiUkqY2kzbUbDmW1w96rfi?from=cli&m=0
```
Pass `--output-format json` for a machine-readable result: `{ok, session_id, url}` on success, or `{ok: false, session_id, error}` when the send fails, for example when the session is missing or archived. Configuration errors, such as an unsupported provider or a disabled organization policy, print to stderr without JSON. `--output-format stream-json` isn't supported with `--cloud <session-id>`.
The CLI prefixes errors with `Error: `. A failed delivery is wrapped as `failed to send message to cloud session <id>: <reason>`.
| Message | What it means |
| --------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `Cloud sessions aren't available with <provider>. They run on Anthropic's infrastructure and require an Anthropic account.` | Claude Code is configured for a third-party provider. The message names the provider with the label your configuration uses, such as `Amazon Bedrock` or `Google Vertex AI`. Remove that provider's configuration, for example by unsetting `CLAUDE_CODE_USE_BEDROCK`, and sign in with an Anthropic account (`claude auth login`). |
| `Cloud sessions are disabled by your organization's policy. Contact your organization admin to enable them.` | The `allow_remote_sessions` organization policy is off. |
| `Attaching to an existing cloud session is not enabled for your account.` | Interactive attach, without `-p`, is rolling out gradually and isn't available for your account yet. The queue-and-exit form, `-p "..." --cloud <session-id>`, works regardless. |
| `Session not found: <id>` | The ID or URL doesn't match a session you can access. Check it against the session's claude.ai/code URL. |
| `cloud session <id> is archived and cannot accept new messages` | The session has been archived. Start a new session instead. |
### From web to terminal
Pull a cloud session into your terminal using any of these:
* **Using `--teleport`**: from the command line, run `claude --teleport` for an interactive session picker, or `claude --teleport <session-id>` to resume a specific session directly. If you have uncommitted changes, you'll be prompted to stash them first.
* **Using `/teleport`**: inside an existing CLI session, run `/teleport` or `/tp` to open the same session picker without restarting Claude Code.
* **From `/tasks`**: run `/tasks` to see your background sessions, then press `t` to teleport into one.
* **From the web interface**: select **Open in > Terminal** from the session menu to copy a command you can paste into your terminal.
* **From inside the cloud session**: type `/teleport` and Claude Code replies with the exact `claude --teleport <session-id>` command for that session, ready to run from a checkout of the repository. Requires Claude Code v2.1.223 or later in the session's environment.
When you teleport a session, Claude verifies you're in the correct repository, fetches and checks out the branch from the cloud session, and loads the full conversation history into your terminal. The terminal gets its own copy of the session: new work there stays local and doesn't appear in the cloud session on claude.ai or the Claude mobile app. To keep steering from your phone after teleporting, start [`/remote-control`](/docs/en/remote-control) in the local session.
`--teleport` is distinct from `--resume`. `--resume` reopens a conversation from this machine's local history and doesn't list cloud sessions; `--teleport` pulls a cloud session and its branch.
#### Teleport requirements
Teleport checks these requirements before resuming a session. If any requirement isn't met, you'll see an error or be prompted to resolve the issue.
| Requirement | Details |
| ------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Clean git state | Your working directory must have no uncommitted changes. Teleport prompts you to stash changes if needed. |
| Correct repository | You must run `--teleport` from a checkout of the same repository, not a fork. If you run it from a checkout of a different repository, Claude Code shows an error that names both the session's repository and your checkout's. If Claude Code can't parse your remote into a hostname, for example an SSH host alias like `git@work:owner/repo.git`, it asks you to confirm, and accepts the checkout when the remote's owner and repository name match the session's repository. |
| Branch available | The branch from the cloud session must have been pushed to the remote. Teleport automatically fetches and checks it out. |
| Same account | You must be authenticated to the same claude.ai account used in the cloud session. |
#### `--teleport` is unavailable
Teleport requires claude.ai subscription authentication. If you're authenticated via API key, run `/login` to sign in with your claude.ai account instead. If the error names your provider instead, cloud sessions aren't available through third-party providers; see the [error table](#output-and-errors). If you're already signed in via claude.ai and `--teleport` is still unavailable, your organization may have disabled cloud sessions.
## Work with sessions
Sessions appear in the sidebar at claude.ai/code. From there you can review changes, share with teammates, archive finished work, or delete sessions permanently.
### Manage context
Cloud sessions support [built-in commands](/docs/en/commands) that produce text output. Commands that only run in the terminal interface, such as `/plugin` or `/resume`, aren't available. Commands that open a picker or panel in the terminal behave differently in cloud sessions:
* **`/model`, `/effort`, `/fast`, `/color`, and `/rename`**: pass the value as an argument, for example `/model sonnet`, instead of opening the terminal picker or slider. The argument forms require Claude Code v2.1.205 or later in the session's environment and follow each command's [availability notes](/docs/en/commands#all-commands): `/effort` reports `Not applied` while a model's [launch-default effort hold](/docs/en/model-config#adjust-effort-level) is in force, and `/fast` works only in a session that started with fast mode turned on.
* **`/config`**: on the web, opens the Claude Code section of your settings instead of setting a value, and text after the command, including `key=value`, is ignored. To change settings for a cloud session, use [environment variables](/docs/en/cloud-environments#set-environment-variables) or commit [settings files](/docs/en/settings) to the repository.
For context management specifically:
| Command | Works in cloud sessions | Notes |
| :--------- | :---------------------- | :----------------------------------------------------------------------------------------------------------------------- |
| `/compact` | Yes | Summarizes the conversation to free up context. Accepts optional focus instructions like `/compact keep the test output` |
| `/context` | Yes | Shows what's currently in the context window |
| `/clear` | No | Start a new session from the sidebar instead |
Auto-compaction runs automatically when the context window approaches capacity. Claude Code on the web sets [`CLAUDE_AUTOCOMPACT_PCT_OVERRIDE`](/docs/en/env-vars) in cloud sessions itself, so compaction triggers partway through the [auto-compact window](/docs/en/model-config#set-the-auto-compact-window) rather than when the window fills. That value overrides one you add in your [environment variables](/docs/en/cloud-environments#set-environment-variables), so adding the variable there doesn't change when compaction triggers.
To change the auto-compact window instead, set [`CLAUDE_CODE_AUTO_COMPACT_WINDOW`](/docs/en/env-vars) in your environment variables, or run [`/autocompact`](/docs/en/commands#all-commands) with a token count in a session where the variable isn't set.
[Subagents](/docs/en/sub-agents) work the same way they do locally. Claude can spawn them with the Task tool to offload research or parallel work into a separate context window, keeping the main conversation lighter. Subagents defined in your repo's `.claude/agents/` are picked up automatically.
[Agent teams](/docs/en/agent-teams) are off by default but can be enabled by adding `CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1` to your [environment variables](/docs/en/cloud-environments#set-environment-variables).
### Review changes
Each session shows a diff indicator with lines added and removed, like `+42 -18`. Select it to open the diff view, leave inline comments on specific lines, and send them to Claude with your next message.
Claude Code computes these diffs, including the per-file diffs shown as Claude edits, from raw git blob content, so diff drivers and `textconv` filters configured in the repository don't apply.
See [Review and iterate](/docs/en/web-quickstart#review-and-iterate) for the full walkthrough including PR creation. To have Claude monitor the PR for CI failures and review comments automatically, see [Auto-fix pull requests](#auto-fix-pull-requests).
### Share sessions
To share a session, toggle its visibility according to the account types below. After that, share the session link as-is. Recipients see the latest state when they open the link, but their view doesn't update in real time.
#### Share from an Enterprise or Team account
For Enterprise and Team accounts, the two visibility options are **Private** and **Team**. Team visibility makes the session visible to other members of your claude.ai organization. [Claude in Slack](/docs/en/slack) sessions are automatically shared with Team visibility.
Repository access verification is enabled by default, based on the GitHub account connected to the recipient's account. Your account's display name is visible to all recipients with access.
#### Share from a Max or Pro account
For Max and Pro accounts, the two visibility options are **Private** and **Public**. Public visibility makes the session visible to any user logged into claude.ai.
Check your session for sensitive content before sharing. Sessions may contain code and credentials from private GitHub repositories. Repository access verification is not enabled by default.
To require recipients to have repository access, or to hide your name from shared sessions, go to [**Settings > Claude Code > Sharing settings**](https://claude.ai/settings/claude-code).
### Archive sessions
You can archive sessions to keep your session list organized. Archived sessions are hidden from the default session list but can be viewed by filtering for archived sessions.
To archive a session, hover over the session in the sidebar and select the archive icon.
### Delete sessions
Deleting a session permanently removes the session and its data. This action can't be undone. You can delete a session in two ways:
* **From the sidebar**: filter for archived sessions, then hover over the session you want to delete and select the delete icon
* **From the session menu**: open a session, select the dropdown next to the session title, and select **Delete**
You will be asked to confirm before a session is deleted.
## Auto-fix pull requests
Claude can watch a pull request and automatically respond to CI failures and review comments. Claude subscribes to GitHub activity on the PR, and when a check fails or a reviewer leaves a comment, Claude investigates and pushes a fix if one is clear.
<Note>
Auto-fix requires the Claude GitHub App to be installed on your repository. If you haven't already, install it from the [GitHub App page](https://github.com/apps/claude) or when prompted during [setup](/docs/en/web-quickstart#connect-github).
</Note>
There are a few ways to turn on auto-fix depending on where the PR came from and what device you're using:
* **PRs created in Claude Code on the web**: open the CI status bar and select **Auto-fix**
* **From your terminal**: run [`/autofix-pr`](/docs/en/commands) while on the PR's branch. Claude Code detects the open PR with `gh`, spawns a web session, and turns on auto-fix in one step
* **From the mobile app**: tell Claude to auto-fix the PR, for example "watch this PR and fix any CI failures or review comments"
* **Any existing PR**: paste the PR URL into a session and tell Claude to auto-fix it
Auto-fix is a per-PR toggle. To stop monitoring, open the CI status bar in the web session and clear the **Auto-fix** toggle, or tell Claude to stop watching the PR.
### How Claude responds to PR activity
When auto-fix is active, Claude receives GitHub events for the PR including new review comments and CI check failures. For each event, Claude investigates and decides how to proceed:
* **Clear fixes**: if Claude is confident in a fix and it doesn't conflict with earlier instructions, Claude makes the change, pushes it, and explains what was done in the session
* **Ambiguous requests**: if a reviewer's comment could be interpreted multiple ways or involves something architecturally significant, Claude asks you before acting
* **Duplicate or no-action events**: if an event is a duplicate or requires no change, Claude notes it in the session and moves on
GitHub does not emit a webhook when the base branch advances and creates a merge conflict, so auto-fix can't react to conflicts on its own. To resolve a conflict, open the session and ask Claude to rebase.
Claude may reply to review comment threads on GitHub as part of resolving them. These replies are posted using your GitHub account, so they appear under your username, but each reply is labeled as coming from Claude Code so reviewers know it was written by the agent and not by you directly.
<Warning>
If your repository uses comment-triggered automation such as Atlantis, Terraform Cloud, or custom GitHub Actions that run on `issue_comment` events, be aware that Claude can reply on your behalf, which can trigger those workflows. Review your repository's automation before enabling auto-fix, and consider disabling auto-fix for repositories where a PR comment can deploy infrastructure or run privileged operations.
</Warning>
## Security and isolation
Cut at 300 lines. The page has the rest.
claude-directory First recorded · 1643 lines, first recorded
# Explore the .claude directory ## Commands ## Stack ## Rules # API credentials # Testing Rules # API Design Rules ## Diff to review ## Input Validation ## Authentication ## Patterns seen ## Recurring issues ## Project ## Reference ## Auth Token Issues ## Database Connection Drops ## Explore the directory ## What's not shown ## Choose the right file ## File reference ## Troubleshoot configuration ## Application data ### Cleaned up automatically ### Kept until you delete them ### Plaintext storage ### Clear local data ## Related resources
The first capture of this source. The page was already there, and this is what it said.
# Explore the .claude directory
> Where Claude Code reads CLAUDE.md, settings.json, hooks, skills, commands, subagents, workflows, rules, and auto memory. Explore the .claude directory in your project and ~/.claude in your home directory.
export const ClaudeExplorer = () => {
const A = useMemo(() => ({href, children}) => <a href={href} style={{
color: 'var(--ce-accent)',
textDecoration: 'none',
borderBottom: '1px dotted var(--ce-accent)'
}}>{children}</a>, []);
const C = useMemo(() => ({children}) => <code style={{
fontFamily: 'var(--ce-mono)',
fontSize: '0.92em',
padding: '1px 4px',
borderRadius: '3px',
background: 'var(--ce-surface)',
border: '0.5px solid var(--ce-border-subtle)'
}}>{children}</code>, []);
const commandsNote = useMemo(() => <>Commands and skills are now the same mechanism. For new workflows, use <A href="/docs/en/skills">skills/</A> instead: same <C>/name</C> invocation, plus you can bundle supporting files.</>, []);
const FILE_TREE = useMemo(() => ({
project: {
label: 'your-project/',
children: [{
id: 'claude-md',
label: 'CLAUDE.md',
type: 'file',
icon: 'md',
color: '#6A9BCC',
badge: 'committed',
oneLiner: 'Project instructions Claude reads every session',
when: 'Loaded into context at the start of every session',
description: 'Project-specific instructions that shape how Claude works in this repository. Put your conventions, common commands, and architectural context here so Claude operates with the same assumptions your team does.',
tips: ['Target under 200 lines. Longer files still load in full but may reduce adherence', <>CLAUDE.md loads into every session. If something only matters for specific tasks, move it to a <A href="/docs/en/skills">skill</A> or a path-scoped <A href="/docs/en/memory#organize-rules-with-claude/rules/">rule</A> so it loads only when needed</>, 'List the commands you run most, like build, test, and format, so Claude knows them without you spelling them out each time', <>Run <C>/memory</C> to open and edit CLAUDE.md from within a session</>, <>Also works at <C>.claude/CLAUDE.md</C> if you prefer to keep the project root clean</>],
exampleIntro: 'This example is for a TypeScript and React project. It lists the build and test commands, the framework conventions Claude should follow, and project-specific rules like export style and file layout.',
example: `# Project conventions
## Commands
- Build: \`npm run build\`
- Test: \`npm test\`
- Lint: \`npm run lint\`
## Stack
- TypeScript with strict mode
- React 19, functional components only
## Rules
- Named exports, never default exports
- Tests live next to source: \`foo.ts\` -> \`foo.test.ts\`
- All API routes return \`{ data, error }\` shape`,
docsLink: '/en/memory'
}, {
id: 'mcp-json',
label: '.mcp.json',
type: 'file',
icon: 'json',
color: '#9B7BC4',
badge: 'committed',
oneLiner: 'Project-scoped MCP servers, shared with your team',
when: <>Servers connect when the session begins. Tool schemas are deferred by default and load on demand via <A href="/docs/en/mcp#scale-with-mcp-tool-search">tool search</A></>,
description: <>Configures Model Context Protocol (MCP) servers that give Claude access to external tools: databases, APIs, browsers, and more. This file holds the project-scoped servers your whole team uses. Personal servers you want to keep to yourself go in <C>~/.claude.json</C> instead.</>,
tips: [<>Use environment variable references for secrets: <C>{'${NOTION_TOKEN}'}</C></>, <>Lives at the project root, not inside <C>.claude/</C></>, <>For servers only you need, run <C>claude mcp add --scope user</C>. This writes to <C>~/.claude.json</C> instead of <C>.mcp.json</C></>],
exampleIntro: <>This example configures the Notion MCP server so Claude can read and update pages in your workspace. The <C>{'${NOTION_TOKEN}'}</C> reference is read from your shell environment when Claude Code starts the server, so the token never lands in the file.</>,
example: `{
"mcpServers": {
"notion": {
"command": "npx",
"args": ["-y", "@notionhq/notion-mcp-server"],
"env": {
"NOTION_TOKEN": "\${NOTION_TOKEN}"
}
}
}
}`,
docsLink: '/en/mcp'
}, {
id: 'worktreeinclude',
label: '.worktreeinclude',
type: 'file',
icon: 'md',
color: '#8FA876',
badge: 'committed',
oneLiner: 'Gitignored files to copy into new worktrees',
when: <>Read when Claude creates a git worktree via <C>--worktree</C>, the <C>EnterWorktree</C> tool, or subagent <C>isolation: worktree</C></>,
description: <>Lists gitignored files to copy from your main repository into each new worktree. Worktrees are fresh checkouts, so untracked files like <C>.env</C> are missing by default. Patterns here use <C>.gitignore</C> syntax. Only files that match a pattern and are also gitignored get copied, so tracked files are never duplicated.</>,
tips: [<>Lives at the project root, not inside <C>.claude/</C></>, <>Git-only: if you configure a <A href="/docs/en/hooks#worktreecreate">WorktreeCreate hook</A> for a different VCS, this file is not read. Copy files inside your hook script instead</>, <>Also applies to parallel sessions in the <A href="/docs/en/desktop#work-in-parallel-with-sessions">desktop app</A></>],
exampleIntro: 'This example copies your local environment files and a secrets config into every worktree Claude creates. Comments start with # and blank lines are ignored, same as .gitignore.',
example: `# Local environment
.env
.env.local
# API credentials
config/secrets.json`,
docsLink: '/en/worktrees#copy-gitignored-files-into-worktrees'
}, {
id: 'dot-claude',
label: '.claude/',
type: 'folder',
icon: 'folder',
color: 'var(--ce-accent)',
oneLiner: 'Project-level configuration, rules, and extensions',
description: 'Everything Claude Code reads that is specific to this project. If you use git, commit most files here so your team shares them; a few, like settings.local.json, are gitignored when Claude Code saves settings to them. Each file badge shows which.',
children: [{
id: 'settings-json',
label: 'settings.json',
type: 'file',
icon: 'json',
color: 'var(--ce-text-3)',
badge: 'committed',
oneLiner: 'Permissions, hooks, and configuration',
when: <>Overrides global <C>~/.claude/settings.json</C>. Local settings, CLI flags, and managed settings override this</>,
description: 'Settings that Claude Code applies directly. Permissions control which commands and tools Claude can use; hooks run your scripts at specific points in a session. Unlike CLAUDE.md, which Claude reads as guidance, these are enforced whether Claude follows them or not.',
contains: [<><A href="/docs/en/permissions">permissions</A>: allow, deny, or prompt before Claude uses specific tools or commands</>, <><A href="/docs/en/hooks">hooks</A>: run your own scripts on events like before a tool call or after a file edit</>, <><A href="/docs/en/statusline">statusLine</A>: customize the line shown at the bottom while Claude works</>, <><A href="/docs/en/settings#available-settings">model</A>: pick a default model for this project</>, <><A href="/docs/en/settings#environment-variables">env</A>: environment variables set in every session</>, <><A href="/docs/en/output-styles">outputStyle</A>: select a custom system-prompt style from output-styles/</>],
tips: [<>Bash permission patterns support wildcards: <C>Bash(npm test *)</C> matches any command starting with <C>npm test</C></>, <>Array settings like <C>permissions.allow</C> combine across all scopes; scalar settings like <C>model</C> use the most specific value</>],
exampleIntro: <>This example allows <C>npm test</C> and <C>npm run</C> commands without prompting, blocks <C>rm -rf</C>, and runs Prettier on files after Claude edits or writes them.</>,
example: `{
"permissions": {
"allow": [
"Bash(npm test *)",
"Bash(npm run *)"
],
"deny": [
"Bash(rm -rf *)"
]
},
"hooks": {
"PostToolUse": [{
"matcher": "Edit|Write",
"hooks": [{
"type": "command",
"command": "jq -r '.tool_input.file_path' | xargs npx prettier --write"
}]
}]
}
}`,
docsLink: '/en/settings'
}, {
id: 'settings-local-json',
label: 'settings.local.json',
type: 'file',
icon: 'json',
color: 'var(--ce-text-3)',
badge: 'gitignored',
oneLiner: 'Your personal settings overrides for this project',
when: 'Highest of the user-editable settings files; CLI flags and managed settings still take precedence',
description: 'Personal settings that take precedence over the project defaults. Same JSON format as settings.json, gitignored when Claude Code saves a setting to it. Use this when you need different permissions or defaults than the team config.',
tips: [<>Same schema as settings.json. Array settings like <C>permissions.allow</C> combine across scopes; scalar settings like <C>model</C> use the local value</>, <>When Claude Code saves a setting to this file in a repository that doesn't already ignore it, it adds <C>**/.claude/settings.local.json</C> to your global git excludes file: <C>core.excludesFile</C> from your global git config when it's set to an absolute or <C>~</C>-prefixed path, otherwise <C>$XDG_CONFIG_HOME/git/ignore</C>, or <C>~/.config/git/ignore</C>. To share the ignore rule with your team, also add it to the project <C>.gitignore</C></>],
exampleIntro: 'This example adds Docker permissions on top of whatever the team settings.json allows.',
example: `{
"permissions": {
"allow": [
"Bash(docker *)"
]
}
}`,
docsLink: '/en/settings'
}, {
id: 'rules',
label: 'rules/',
type: 'folder',
icon: 'folder',
color: '#9B7BC4',
oneLiner: 'Topic-scoped instructions, optionally gated by file paths',
when: <>Rules without <C>paths:</C> load at session start. Rules with <C>paths:</C> load when a matching file enters context</>,
description: [<>Project instructions split into topic files that can load conditionally based on file paths. A rule without <C>paths:</C> frontmatter loads at session start like CLAUDE.md; a rule with <C>paths:</C> loads only when Claude reads a matching file.</>, <>Like CLAUDE.md, rules are guidance Claude reads, not configuration Claude Code enforces. For guaranteed behavior use <A href="/docs/en/hooks">hooks</A> or <A href="/docs/en/permissions">permissions</A>.</>],
tips: [<>Use <C>paths:</C> frontmatter with globs to scope rules to directories or file types</>, <>Subdirectories work: <C>.claude/rules/frontend/react.md</C> is discovered automatically</>, 'When CLAUDE.md approaches 200 lines, start splitting into rules'],
docsLink: '/en/memory#organize-rules-with-claude/rules/',
children: [{
id: 'rule-testing',
label: 'testing.md',
type: 'file',
icon: 'md',
color: '#9B7BC4',
badge: 'committed',
oneLiner: 'Test conventions scoped to test files',
when: <>Loaded when Claude reads a file matching the <C>paths:</C> globs below</>,
description: <>An example rule that only loads when Claude is working on test files. The <C>paths:</C> globs in the frontmatter define which files trigger it; here, anything ending in .test.ts or .test.tsx. For other files, this rule is not loaded into context.</>,
example: `---
paths:
- "**/*.test.ts"
- "**/*.test.tsx"
---
# Testing Rules
- Use descriptive test names: "should [expected] when [condition]"
- Mock external dependencies, not internal modules
- Clean up side effects in afterEach`
}, {
id: 'rule-api',
label: 'api-design.md',
type: 'file',
icon: 'md',
color: '#9B7BC4',
badge: 'committed',
oneLiner: 'API conventions scoped to backend code',
when: <>Loaded when Claude reads a file matching the <C>paths:</C> glob below</>,
description: <>A second example showing a rule scoped to backend code. The <C>paths:</C> glob matches files under src/api/, so these conventions load only when Claude is editing API routes.</>,
example: `---
paths:
- "src/api/**/*.ts"
---
# API Design Rules
- All endpoints must validate input with Zod schemas
- Return shape: { data: T } | { error: string }
- Rate limit all public endpoints`
}]
}, {
id: 'skills',
label: 'skills/',
type: 'folder',
icon: 'folder',
color: '#D4A843',
oneLiner: 'Reusable prompts you or Claude invoke by name',
when: <>Invoked with <C>/skill-name</C> or when Claude matches the task to a skill</>,
description: <>Each skill is a folder with a SKILL.md file plus any supporting files it needs. By default, both you and Claude can invoke a skill. Use frontmatter to control that: <C>disable-model-invocation: true</C> for user-only workflows like <C>/deploy</C>, or <C>user-invocable: false</C> to hide from the <C>/</C> menu while Claude can still invoke it.</>,
tips: [<>Skills accept arguments: <C>/deploy staging</C> passes "staging" as <C>$ARGUMENTS</C>. Use <C>$0</C>, <C>$1</C>, and so on for positional access</>, <>The <C>description</C> frontmatter determines when Claude auto-invokes the skill</>, 'Bundle reference docs alongside SKILL.md. Claude knows the skill directory path and can read supporting files when you mention them'],
docsLink: '/en/skills',
children: [{
id: 'skill-review',
label: 'security-review/',
type: 'folder',
icon: 'folder',
color: '#D4A843',
oneLiner: 'A skill bundling SKILL.md with supporting files',
children: [{
id: 'skill-review-md',
label: 'SKILL.md',
type: 'file',
icon: 'md',
color: '#D4A843',
badge: 'committed',
oneLiner: 'Entrypoint: trigger, invocability, instructions',
when: <>User types <C>/security-review <target></C>; Claude cannot auto-invoke this skill</>,
description: [<>This skill uses <C>disable-model-invocation: true</C> so only you can trigger it; Claude never invokes it on its own.</>, <>The <C>!`...`</C> line runs a shell command and injects its output into the prompt. <C>$ARGUMENTS</C> substitutes whatever you typed after the skill name. Claude sees the skill directory path, so mentioning a bundled file like checklist.md lets Claude read it.</>],
example: `---
description: Reviews code changes for security vulnerabilities, authentication gaps, and injection risks
disable-model-invocation: true
argument-hint: <branch-or-path>
---
## Diff to review
!\`git diff $ARGUMENTS\`
Audit the changes above for:
1. Injection vulnerabilities (SQL, XSS, command)
2. Authentication and authorization gaps
3. Hardcoded secrets or credentials
Use checklist.md in this skill directory for the full review checklist.
Report findings with severity ratings and remediation steps.`
}, {
id: 'skill-checklist',
label: 'checklist.md',
type: 'file',
icon: 'md',
color: '#D4A843',
badge: 'committed',
oneLiner: 'Supporting file bundled with the skill',
when: 'Claude reads it on demand while running the skill',
description: <>Skills can bundle any supporting files: reference docs, templates, scripts. The skill directory path is prepended to SKILL.md, so Claude can read bundled files by name. For scripts in bash injection commands, use the <C>{'${CLAUDE_SKILL_DIR}'}</C> placeholder.</>,
example: `# Security Review Checklist
## Input Validation
- [ ] All user input sanitized before DB queries
- [ ] File upload MIME types validated
- [ ] Path traversal prevented on file operations
## Authentication
- [ ] JWT tokens expire after 24 hours
- [ ] API keys stored in environment variables
- [ ] Passwords hashed with bcrypt or argon2`
}]
}]
}, {
id: 'commands',
label: 'commands/',
type: 'folder',
icon: 'folder',
color: '#788C5D',
oneLiner: <>Single-file prompts invoked with <C>/name</C></>,
note: commandsNote,
when: <>User types <C>/command-name</C></>,
description: <>A file at <C>commands/deploy.md</C> creates <C>/deploy</C> the same way a skill at <C>skills/deploy/SKILL.md</C> does, and both can be auto-invoked by Claude. Skills use a directory with SKILL.md, letting you bundle reference docs, templates, or scripts alongside the prompt.</>,
tips: [<>Use <C>$ARGUMENTS</C> in the file to accept parameters: <C>/fix-issue 123</C></>, 'If a skill and command share a name, the skill takes precedence', 'New commands should usually be skills instead; commands remain supported'],
docsLink: '/en/skills',
children: [{
id: 'cmd-example',
label: 'fix-issue.md',
type: 'file',
icon: 'md',
color: '#788C5D',
badge: 'committed',
oneLiner: <>Invoked as <C>/fix-issue <number></C></>,
note: commandsNote,
description: [<>An example command for fixing a GitHub issue. Type <C>/fix-issue 123</C> and the <C>!`...`</C> line runs <C>gh issue view 123</C> in your shell, injecting the output into the prompt before Claude sees it.</>, <><C>$ARGUMENTS</C> substitutes whatever you typed after the command name. For positional access, use <C>$0</C> <C>$1</C> and so on.</>],
Cut at 300 lines. The page has the rest.
claude-platform-on-aws First recorded · 354 lines, first recorded
# Claude Code on Claude Platform on AWS ## Prerequisites ## Setup ### 1. Configure AWS credentials ### 2. Configure Claude Code ### 3. Pin model versions ### 4. Launch and verify ## Use the Agent SDK ## Route through a corporate proxy ## Troubleshooting ### `403 Forbidden` or `AccessDenied` on every request ### Requests fail with a missing-workspace error ### Requests still go to `api.anthropic.com` ## Additional resources
The first capture of this source. The page was already there, and this is what it said.
# Claude Code on Claude Platform on AWS
> Configure Claude Code to use the Anthropic-operated Claude API with AWS authentication, IAM access control, and AWS Marketplace billing.
export const ContactSalesCard = ({surface}) => {
const utm = content => `utm_source=claude_code&utm_medium=docs&utm_content=${surface}_${content}`;
const iconArrowRight = (size = 13) => <svg width={size} height={size} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
<line x1="5" y1="12" x2="19" y2="12" />
<polyline points="12 5 19 12 12 19" />
</svg>;
const STYLES = `
.cc-cs {
--cs-slate: #141413;
--cs-clay: #d97757;
--cs-clay-deep: #c6613f;
--cs-gray-000: #ffffff;
--cs-gray-700: #3d3d3a;
--cs-border-default: rgba(31, 30, 29, 0.15);
font-family: inherit;
}
.dark .cc-cs {
--cs-slate: #f0eee6;
--cs-gray-000: #262624;
--cs-gray-700: #bfbdb4;
--cs-border-default: rgba(240, 238, 230, 0.14);
}
.cc-cs-card {
display: flex; align-items: center; justify-content: space-between;
gap: 16px; padding: 14px 16px; margin: 0;
background: var(--cs-gray-000); border: 0.5px solid var(--cs-border-default);
border-radius: 8px; flex-wrap: wrap;
}
.cc-cs-text { font-size: 13px; color: var(--cs-gray-700); line-height: 1.5; flex: 1; min-width: 240px; }
.cc-cs-text strong { font-weight: 550; color: var(--cs-slate); }
.cc-cs-actions { display: flex; align-items: center; gap: 8px; flex-shrink: 0; }
.cc-cs-btn-clay {
display: inline-flex; align-items: center; gap: 8px;
background: var(--cs-clay-deep); color: #fff; border: none;
border-radius: 8px; padding: 8px 14px;
font-size: 13px; font-weight: 500;
transition: background-color 0.15s; white-space: nowrap;
}
.cc-cs-btn-clay:hover { background: var(--cs-clay); }
.cc-cs-btn-ghost {
display: inline-flex; align-items: center; gap: 8px;
background: transparent; color: var(--cs-gray-700);
border: 0.5px solid var(--cs-border-default);
border-radius: 8px; padding: 8px 14px;
font-size: 13px; font-weight: 500;
}
.cc-cs-btn-ghost:hover { background: rgba(0, 0, 0, 0.04); }
.dark .cc-cs-btn-ghost:hover { background: rgba(255, 255, 255, 0.04); }
@media (max-width: 720px) {
.cc-cs-actions { width: 100%; }
}
`;
return <div className="cc-cs not-prose">
<style>{STYLES}</style>
<div className="cc-cs-card">
<div className="cc-cs-text">
<strong>Deploying Claude Code across your organization?</strong> Talk to sales about enterprise plans, SSO, and centralized billing.
</div>
<div className="cc-cs-actions">
<a href={`https://claude.com/pricing?${utm('view_plans')}#plans-business`} className="cc-cs-btn-ghost">
View plans
</a>
<a href={`https://claude.com/contact-sales?${utm('contact_sales')}`} className="cc-cs-btn-clay">
Contact sales {iconArrowRight()}
</a>
</div>
</div>
</div>;
};
export const Experiment = ({flag, treatment, children}) => {
const VID_KEY = 'exp_vid';
const CONSENT_COUNTRIES = new Set(['AT', 'BE', 'BG', 'HR', 'CY', 'CZ', 'DK', 'EE', 'FI', 'FR', 'DE', 'GR', 'HU', 'IE', 'IT', 'LV', 'LT', 'LU', 'MT', 'NL', 'PL', 'PT', 'RO', 'SK', 'SI', 'ES', 'SE', 'RE', 'GP', 'MQ', 'GF', 'YT', 'BL', 'MF', 'PM', 'WF', 'PF', 'NC', 'AW', 'CW', 'SX', 'FO', 'GL', 'AX', 'GB', 'UK', 'AI', 'BM', 'IO', 'VG', 'KY', 'FK', 'GI', 'MS', 'PN', 'SH', 'TC', 'GG', 'JE', 'IM', 'CA', 'BR', 'IN']);
const fnv1a = s => {
let h = 0x811c9dc5;
for (let i = 0; i < s.length; i++) {
h ^= s.charCodeAt(i);
h += (h << 1) + (h << 4) + (h << 7) + (h << 8) + (h << 24);
}
return h >>> 0;
};
const bucket = (seed, vid) => fnv1a(fnv1a(seed + vid) + '') % 10000 < 5000 ? 'control' : 'treatment';
const [decision] = useState(() => {
const params = new URLSearchParams(location.search);
const preBucketed = document.documentElement.dataset['gb_' + flag.replace(/-/g, '_')];
const force = params.get('gb-force');
if (force) {
for (const p of force.split(',')) {
const [k, v] = p.split(':');
if (k === flag) return {
variant: v || 'treatment',
track: false
};
}
}
if (navigator.globalPrivacyControl) {
return {
variant: 'control',
track: false
};
}
const prefsMatch = document.cookie.match(/(?:^|; )anthropic-consent-preferences=([^;]+)/);
if (prefsMatch) {
try {
if (JSON.parse(decodeURIComponent(prefsMatch[1])).analytics !== true) {
return {
variant: 'control',
track: false
};
}
} catch {
return {
variant: 'control',
track: false
};
}
} else {
const country = params.get('country')?.toUpperCase() || (document.cookie.match(/(?:^|; )cf_geo=([A-Z]{2})/) || [])[1];
if (!country || CONSENT_COUNTRIES.has(country)) {
return {
variant: 'control',
track: false
};
}
}
let vid;
try {
const ajsMatch = document.cookie.match(/(?:^|; )ajs_anonymous_id=([^;]+)/);
if (ajsMatch) {
vid = decodeURIComponent(ajsMatch[1]).replace(/^"|"$/g, '');
} else {
vid = localStorage.getItem(VID_KEY);
if (!vid) {
vid = crypto.randomUUID();
}
document.cookie = `ajs_anonymous_id=${vid}; domain=.claude.com; path=/; Secure; SameSite=Lax; max-age=31536000`;
}
try {
localStorage.setItem(VID_KEY, vid);
} catch {}
} catch {
return {
variant: 'control',
track: false
};
}
const variant = preBucketed === '1' ? 'treatment' : preBucketed === '0' ? 'control' : bucket(flag, vid);
return {
variant,
track: true,
vid
};
});
useEffect(() => {
if (!decision.track) return;
fetch('https://api.anthropic.com/api/event_logging/v2/batch', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'x-service-name': 'claude_code_docs'
},
body: JSON.stringify({
events: [{
event_type: 'GrowthbookExperimentEvent',
event_data: {
device_id: decision.vid,
anonymous_id: decision.vid,
timestamp: new Date().toISOString(),
experiment_id: flag,
variation_id: decision.variant === 'treatment' ? 1 : 0,
environment: 'production'
}
}]
}),
keepalive: true
}).catch(() => {});
}, []);
return decision.variant === 'treatment' ? treatment : children;
};
<Experiment flag="docs-contact-sales-cta" treatment={<ContactSalesCard surface="claude_platform_on_aws" />} />
Claude Platform on AWS is the Anthropic-operated Claude API with AWS authentication, IAM access control, and AWS Marketplace billing. Requests reach Anthropic's API directly, so you get the same models and API features as the [Claude API](https://platform.claude.com/docs) on the same release schedule. Client-side features that Claude Code turns on through Anthropic's feature-flag service, such as [`/loop` self-pacing](/docs/en/scheduled-tasks#let-claude-choose-the-interval), are off by default, and the [advisor tool](/docs/en/advisor) is not available. See the [feature availability matrix](/docs/en/feature-availability#summary-by-provider) for the full list. You authenticate with AWS credentials or a workspace API key, and you pay through AWS Marketplace.
Use this guide to point Claude Code at a workspace you've already provisioned through Claude Platform on AWS. For the AWS subscription and workspace setup that comes before this, see the [Claude Platform on AWS documentation](https://platform.claude.com/docs/en/build-with-claude/claude-platform-on-aws).
<Note>
Subscribing through AWS Marketplace provisions a new Anthropic organization tied to your AWS account. This organization is separate from any organization you already have with Anthropic, and credentials don't transfer between them. Use the workspace ID and API keys from the AWS-linked organization, not from a pre-existing Claude Console account.
</Note>
## Prerequisites
Before configuring Claude Code, you need:
* An active Claude Platform on AWS subscription through AWS Marketplace
* A workspace in your AWS-linked Anthropic organization, with its workspace ID
* An IAM principal with permission to invoke the Anthropic service, or an API key scoped to the workspace
* AWS credentials in your environment, in `~/.aws/credentials`, or from an attached IAM role if you want SigV4 authentication. The AWS CLI is required only for the SSO login flow.
## Setup
### 1. Configure AWS credentials
Claude Code supports two authentication methods for Claude Platform on AWS. Choose the method that fits how your team manages access.
**Option A: AWS credentials with SigV4**
Claude Code signs requests with SigV4 using the standard AWS credential chain: environment variables, shared credentials in `~/.aws/credentials`, IAM roles, AWS SSO sessions, and any other sources the AWS SDK supports.
For local use, log in with the AWS CLI before starting Claude Code. The example below uses an SSO profile, but any method that produces credentials in the standard locations works.
```bash theme={null}
aws sso login --profile my-profile
export AWS_PROFILE=my-profile
```
For CI and automation, give the runner an IAM role with permission to invoke the Anthropic service and set `AWS_REGION`. The credential chain picks the role up automatically.
If your SSO credentials expire mid-session, configure [`awsAuthRefresh`](/docs/en/amazon-bedrock#advanced-credential-configuration) so Claude Code re-runs your login command and retries instead of failing. Automatic refresh on Claude Platform on AWS requires Claude Code v2.1.198 or later; earlier versions stop with a prompt to run `/login`, which can't refresh AWS credentials. Add the command to your [settings file](/docs/en/settings), such as `~/.claude/settings.json`:
```json theme={null}
{
"awsAuthRefresh": "aws sso login --profile my-profile"
}
```
Claude Code also runs this command at startup when it can't validate your existing AWS credentials, and shows the command's output in an `Authentication` panel until the login completes.
With `awsAuthRefresh` configured, `/login` shows a **Claude Platform on AWS · refresh credentials** option under **Using 3rd-party platforms**. Selecting it runs the configured command and re-reads your AWS credentials without restarting Claude Code.
**Option B: Workspace API key**
A workspace API key is a long-lived secret, useful when you don't want to manage federated AWS credentials. Generate one in the AWS Console under **Claude Platform on AWS → API keys** and set it as `ANTHROPIC_AWS_API_KEY`:
```bash theme={null}
export ANTHROPIC_AWS_API_KEY=sk-ant-xxxxx
```
The key is sent as `x-api-key` and takes precedence over SigV4, so any AWS credentials in your environment are ignored. API keys from a separate Claude Console organization won't work here.
Treat workspace API keys like any other production credential. The [user settings file](/docs/en/settings) `env` block is a convenient way to scope the key to your machine without exporting it globally.
<Note>
The `/login` and `/logout` commands don't sign you into a Claude.ai subscription for Claude Platform on AWS. Authentication runs through your AWS credentials or workspace API key.
</Note>
### 2. Configure Claude Code
Set the environment variables that route Claude Code through Claude Platform on AWS instead of the default Anthropic API.
```bash theme={null}
export CLAUDE_CODE_USE_ANTHROPIC_AWS=1
export ANTHROPIC_AWS_WORKSPACE_ID=wrkspc_01ABCDEFGHIJKLMN
export AWS_REGION=us-east-1
```
`ANTHROPIC_AWS_WORKSPACE_ID` is required and is sent on every request as the `anthropic-workspace-id` header. Replace the example `wrkspc_01ABCDEFGHIJKLMN` value with your own workspace ID from your Claude Platform on AWS setup. The base URL is computed from `AWS_REGION` as `https://aws-external-anthropic.{region}.api.aws`. To override the URL directly, set `ANTHROPIC_AWS_BASE_URL`.
Claude Platform on AWS is opt-in even when AWS credentials are present in your environment. Amazon Bedrock and Microsoft Foundry take precedence in provider routing, so unset `CLAUDE_CODE_USE_BEDROCK` and `CLAUDE_CODE_USE_FOUNDRY` if they're set.
### 3. Pin model versions
Claude Platform on AWS uses the same model IDs as the direct Claude API.
The default aliases `fable`, `opus`, `sonnet`, and `haiku` resolve to Claude Code's built-in defaults for Claude Platform on AWS, which can lag the newest release. Without `ANTHROPIC_DEFAULT_OPUS_MODEL`, the `opus` alias resolves to Opus 5. Before v2.1.219, it resolved to Opus 4.8, and before v2.1.207 to Opus 4.7.
If you deploy Claude Code to a team, pin the model IDs explicitly so a new release doesn't move everyone at once:
```bash theme={null}
export ANTHROPIC_DEFAULT_FABLE_MODEL=claude-fable-5
export ANTHROPIC_DEFAULT_OPUS_MODEL=claude-opus-4-8
export ANTHROPIC_DEFAULT_SONNET_MODEL=claude-sonnet-5
export ANTHROPIC_DEFAULT_HAIKU_MODEL=claude-haiku-4-5
```
For the full list of model IDs and aliases, see [Models overview](https://platform.claude.com/docs/en/about-claude/models/overview). For other model-related variables, see [Model configuration](/docs/en/model-config).
[Prompt caching](/docs/en/prompt-caching) is enabled automatically. To request a 1-hour cache TTL instead of the 5-minute default, set `ENABLE_PROMPT_CACHING_1H=1`. The API bills 1-hour cache writes at a higher rate. See [prompt caching pricing](https://platform.claude.com/docs/en/build-with-claude/prompt-caching#pricing) for the rates.
### 4. Launch and verify
Start Claude Code and confirm the routing:
```bash theme={null}
claude
```
The startup banner shows `Claude Platform on AWS` when the provider is active. Run `/status` to check the details: the `API provider` line reads `Claude Platform on AWS`, and the output includes your `Workspace ID`, the `AWS region`, and the `Claude Platform on AWS base URL` if you set an override.
## Use the Agent SDK
The [Agent SDK](/docs/en/agent-sdk/overview) reads the same environment variables as the CLI, so any program that spawns the Claude Code subprocess can target Claude Platform on AWS by exporting `CLAUDE_CODE_USE_ANTHROPIC_AWS`, `ANTHROPIC_AWS_WORKSPACE_ID`, and either `ANTHROPIC_AWS_API_KEY` or AWS credentials before the call.
```typescript theme={null}
import { query } from "@anthropic-ai/claude-agent-sdk";
Cut at 300 lines. The page has the rest.
claude-security First recorded · 144 lines, first recorded
# Scan your codebase for vulnerabilities ## Prerequisites ## Install the plugin ### Uninstall the plugin ## Scan and fix your codebase ### Scan only your changes ### Scope large repositories ### Read the scan results ## Fix findings ### Patches are never applied automatically ## How the plugin fits with other security tools ## Troubleshooting ## Related resources
The first capture of this source. The page was already there, and this is what it said.
# Scan your codebase for vulnerabilities
> Install the Claude Security plugin to scan your codebase for vulnerabilities in a Claude Code session and turn findings into patches you review and apply.
The Claude Security plugin runs a multi-agent vulnerability scan of your codebase inside a Claude Code session. A team of Claude agents maps your architecture, builds a threat model, hunts for vulnerabilities, and independently reviews every finding before writing the report. Use the plugin to scan a whole repository or [only a set of changes](#scan-only-your-changes), such as a branch's diff, a pull request's diff, or a single commit, then turn the findings you choose into patches that you review and apply yourself.
The plugin runs locally in your session, and each scan counts against your plan's usage limits. If you want a managed service that monitors your repositories, see the [Claude Security](https://claude.com/product/claude-security) product, available on the Enterprise plan. The plugin reaches code the managed product can't reach, such as repositories hosted on GitLab or Bitbucket, or on networks that don't allow inbound connections.
The plugin is also distinct from the review tools already in Claude Code: the [security guidance plugin](/docs/en/security-guidance) reviews code as Claude writes it, [`/security-review`](/docs/en/commands#all-commands) runs a single pass over your branch, and [Code Review](/docs/en/code-review) reviews pull requests. For how the layers stack, see [How the plugin fits with other security tools](#how-the-plugin-fits-with-other-security-tools).
## Prerequisites
To run the plugin, you need:
* Claude Code v2.1.154 or later on a paid plan, for the [dynamic workflows](/docs/en/workflows) the scan uses to orchestrate its agents. On Pro, turn them on from the Dynamic workflows row in `/config`.
* Python 3.9.6 or later available on your `PATH` as `python3`. Check with `python3 --version`. The plugin's tooling uses only the Python standard library, so nothing is installed.
* Linux, macOS, or Windows.
* Git, for change scans and for turning findings into patches; those jobs don't support other version control systems. A full scan works in any directory, with or without version control.
## Install the plugin
In a Claude Code session, install from the [official Anthropic marketplace](/docs/en/discover-plugins#official-anthropic-marketplace):
```text theme={null}
/plugin install claude-security@claude-plugins-official
```
If the install fails, the fix depends on which message Claude Code reports:
* If it reports `Marketplace "claude-plugins-official" not found`, add the marketplace with `/plugin marketplace add anthropics/claude-plugins-official`, then retry the install.
* If it reports that it [can't find the plugin in the marketplace](/docs/en/discover-plugins#install-plugins), check the plugin name for a typo.
Check the install summary. If it reports `Run /reload-plugins to activate.`, apply the pending change without a restart:
```text theme={null}
/reload-plugins
```
Once the plugin is active, you're ready to [scan and fix your codebase](#scan-and-fix-your-codebase).
### Uninstall the plugin
To remove the plugin, uninstall it from the `/plugin` menu, or run `claude plugin uninstall claude-security` in your terminal.
## Scan and fix your codebase
The plugin adds one command, `/claude-security`, which opens a menu of its three jobs: scanning the codebase, scanning a set of changes, and suggesting patches. The happy path runs a full scan, then turns its findings into patches:
<Steps>
<Step title="Open the Claude Security menu">
Run `/claude-security` and pick **Scan codebase**.
</Step>
<Step title="Choose what to scan">
The plugin reads your repository first, then offers the whole repository or a focused area, with each option's file count and relative cost stated. Pick the whole repository, or answer "I don't know" and the plugin picks a sensible default for your repository's size.
</Step>
<Step title="Confirm the run">
A scan may take a while, may use a significant number of tokens, and needs Claude Code left open while it completes. Nothing runs until you confirm.
</Step>
<Step title="Read the report">
While the scan runs, it reports each stage as it starts, with the detail available under [`/workflows`](/docs/en/workflows). Results land in a timestamped directory in your repository, described in [Read the scan results](#read-the-scan-results).
</Step>
<Step title="Turn findings into patches">
Run `/claude-security` again and pick **Suggest patches**, then choose which findings to address. Reviewed patches land in the report's `patches/` folder; [Fix findings](#fix-findings) covers how each patch is built and reviewed.
</Step>
<Step title="Apply the patches you accept">
Apply each patch from your shell with `git apply`, in its own pull request. Patches are never applied automatically.
</Step>
</Steps>
You don't have to start from the menu: ask for a job directly, as arguments to the command, such as `/claude-security scan my branch`, or in plain language, such as "scan commit abc1234". The plugin works best in [auto mode](/docs/en/permission-modes), which lets the scan's agents proceed without a permission prompt at each step; the plugin reminds you how to enable it when a job starts.
### Scan only your changes
When your branch has commits its base doesn't, the `/claude-security` menu offers to scan only that diff, so you can check a branch before merging. You can also scan one of your open pull requests, or a single commit by asking for it, such as "scan commit abc1234". Only committed changes are scanned: commit or stash in-progress edits first, or run a full scan, which reads the working tree.
Change scans need a git repository; full scans of an unversioned directory still work. Finding your open pull requests is the one step that reaches the network, and it's offered only when your session already has permission to run the GitHub CLI and `gh` is signed in.
### Scope large repositories
On a large repository, scan one area at a time instead of the whole tree. Pick one of the focused scopes the plugin offers, such as your API layer or your authentication code, and the run sizes itself to what you pick. The report's coverage section states what was and wasn't examined. Run another scan on a different area anytime.
### Read the scan results
Every scan writes its results into a timestamped `CLAUDE-SECURITY-<timestamp>/` directory in your repository:
* **`CLAUDE-SECURITY-RESULTS.md`**: the report, with each finding's ID, such as `F1`, plus its impact, exploit scenario, severity, confidence, and recommendation
* **`CLAUDE-SECURITY-RESULTS.jsonl`**: the same findings in machine-readable form, one JSON object per line
* **`CLAUDE-SECURITY-REVISION-<commit>.json`**: the revision stamp, recording which commit was scanned, at what effort, whether uncommitted changes were part of the scanned tree, and how thoroughly the run was verified, so a report is always tied to the code it describes. A scan outside version control stamps `UNVERSIONED` in place of the commit
That directory is the only change a scan makes to your checkout, and it carries its own `.gitignore`, so a stray `git add` never sweeps a report into a commit. To keep a report in history for an audit trail, delete that one `.gitignore` file and commit the directory like any other.
Findings only appear in the report after independent verifier agents analyze them, which keeps reports short and worth reading. Scans are nondeterministic: two scans of the same code can surface different findings. Run scans regularly, and use the revision stamps to attribute each report to the exact code and settings it covered.
## Fix findings
Start the fix flow by picking **Suggest patches** from the `/claude-security` menu, or ask in plain language, such as "fix finding F3", then pick which findings from the report to address. Patches are built against committed code, and the report has to still describe the code you have: findings whose code has since changed are skipped with a note, and the plugin offers a fresh scan instead of patching from a stale report. Each patch is drafted in a scratch copy of your repository, so your source files stay untouched until you apply a patch yourself.
Before delivery, each patch is reviewed by an agent independent of the one that wrote it, which runs your project's tests against the change when the code has them and reads the diff on its own terms for anything new it might introduce. A patch is written only when that review can vouch that the change addresses the one finding, introduces no new vulnerability, and leaves behavior otherwise unchanged. When it can't vouch for all three, you get a short note explaining why instead of a patch.
### Patches are never applied automatically
Applying a patch is always your decision. Patches land in the report's `patches/` folder, one `F<n>.patch` per finding with a note beside it explaining the change. Apply one from your shell, or ask Claude to apply it and open a pull request:
```bash theme={null}
git apply CLAUDE-SECURITY-<timestamp>/patches/F1.patch
```
When the patched code has no tests, the patch's note says so, so you know its review ran without a test pass. Apply each patch in its own pull request so it can be reviewed and tested on its own.
## How the plugin fits with other security tools
The Claude Security plugin is the on-demand deep-scan layer in a defense-in-depth stack, alongside the [security guidance plugin](/docs/en/security-guidance), [`/security-review`](/docs/en/commands#all-commands), [Code Review](/docs/en/code-review), the managed [Claude Security](https://claude.com/product/claude-security) product, and your existing scanners:
| Stage | Tool | What it covers |
| :--------------------- | :----------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------- |
| In session | [Security guidance plugin](/docs/en/security-guidance) | Common vulnerabilities in code Claude writes, fixed in the same session |
| On demand, single pass | [`/security-review`](/docs/en/commands#all-commands) | One-time security pass on the current branch |
| On demand, deep scan | Claude Security plugin | Multi-agent scan of a repository or diff, with independently reviewed findings and patches |
| On pull request | [Code Review](/docs/en/code-review), Team and Enterprise plans | Multi-agent correctness and security review with full codebase context |
| Managed | [Claude Security](https://claude.com/product/claude-security), Enterprise plan | Hosted scanning that monitors connected repositories |
| In CI | Your existing static analysis and dependency scanners | Language-specific rules, supply-chain checks, and policy enforcement |
The plugin doesn't replace your existing source-code security tools. Run it alongside static analysis, dependency scanning, and code review: it reasons about your code the way a human security researcher would, which complements the deterministic checks those tools provide.
## Troubleshooting
**The `/claude-security` menu opens with a Python warning.** The plugin needs `python3` 3.9.6 or later on your `PATH`. When it can't find `python3` at all, the menu warns that Claude Security won't work until one is installed; when the first `python3` on your `PATH` is older, the warning names the version it found. Install Python 3, or put a newer `python3` first on your `PATH`, then start a new session.
**You may see "Fable 5's safeguards flagged this message" when using Fable 5.** Due to Fable 5's cybersecurity safety classifiers, certain model activities will be blocked and automatically downgraded to Opus. This is expected, and the scan should still complete successfully.
## Related resources
To go deeper on the pieces this page touches:
* [Security guidance plugin](/docs/en/security-guidance): catch issues in code as Claude writes it, in the same session
* [Code Review](/docs/en/code-review): set up the PR-time multi-agent review
* [Claude Security](https://claude.com/product/claude-security): the managed service that monitors connected repositories
* [Claude Code security](/docs/en/security): how Claude Code approaches trust, permissions, and safeguards
* [Discover and install plugins](/docs/en/discover-plugins#official-anthropic-marketplace): browse other official plugins
claude-tag First recorded · 7 lines, first recorded
# Claude Tag
The first capture of this source. The page was already there, and this is what it said.
# Claude Tag > Bring Claude into your team's Slack channels with Claude Tag and find its setup and usage documentation on claude.com. [Claude Tag](https://claude.com/product/tag) is a Slack integration that runs `@Claude` in your team's channels as your organization's shared identity with admin-configured access. Anyone in a channel can tag `@Claude` into a thread and assign it a task. Read the [Claude Tag documentation](https://claude.com/docs/claude-tag/overview) on claude.com to set it up and start using it. Claude Tag is available on Team and Enterprise plans, and is distinct from the earlier [Claude Code in Slack](/docs/en/slack), which runs each session under an individual user's account. On Pro and Max plans, where Claude Tag isn't available, Claude Code in Slack remains the setup path.
cli-reference First recorded · 156 lines, first recorded
# CLI reference ## CLI commands ## CLI flags ### System prompt flags ## See also
The first capture of this source. The page was already there, and this is what it said.
# CLI reference
> Complete reference for Claude Code command-line interface, including commands and flags.
## CLI commands
You can start sessions, pipe content, resume conversations, and manage updates with these commands:
| Command | Description | Example |
| :------------------------------ | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :---------------------------------------------------------- |
| `claude` | Start interactive session | `claude` |
| `claude "query"` | Start interactive session with initial prompt | `claude "explain this project"` |
| `claude -p "query"` | Query via SDK, then exit | `claude -p "explain this function"` |
| `cat file \| claude -p "query"` | Process piped content | `cat logs.txt \| claude -p "explain"` |
| `claude -c` | Continue most recent conversation in current directory | `claude -c` |
| `claude -c -p "query"` | Continue via SDK | `claude -c -p "Check for type errors"` |
| `claude -r "<session>" "query"` | Resume session by ID or name | `claude -r "auth-refactor" "Finish this PR"` |
| `claude update` | Update to latest version | `claude update` |
| `claude gateway` | Start the self-hosted [Claude apps gateway](/docs/en/claude-apps-gateway) server, for administrators deploying SSO and policy in front of Claude Code on Amazon Bedrock, Google Cloud's Agent Platform, or Microsoft Foundry. Requires `--config` pointing at a [`gateway.yaml`](/docs/en/claude-apps-gateway-config). Available in Claude Code v2.1.195 and later. | `claude gateway --config gateway.yaml` |
| `claude install [version]` | Install or reinstall the native binary. Accepts a version like `2.1.118`, or `stable` or `latest`. See [Install a specific version](/docs/en/setup#install-a-specific-version) | `claude install stable` |
| `claude auth login` | Sign in to your Anthropic account. Use `--email` to pre-fill your email address, `--sso` to force SSO authentication, and `--console` to sign in with Anthropic Console for API usage billing instead of a Claude subscription | `claude auth login --console` |
| `claude auth logout` | Log out from your Anthropic account | `claude auth logout` |
| `claude auth status` | Show authentication status as JSON. Use `--text` for human-readable output. Exits with code 0 if logged in, 1 if not | `claude auth status` |
| `claude agents` | Open [agent view](/docs/en/agent-view) to monitor and dispatch parallel background sessions. Use `--cwd <path>` to show only sessions started under that directory, or `--json` to print active sessions as a JSON array for scripting (`--json --all` also includes completed background sessions). Pass `--permission-mode`, `--model`, `--effort`, or `--agent` to set [defaults for dispatched sessions](/docs/en/agent-view#permission-mode-model-and-effort). Accepts `--settings`, `--add-dir`, `--plugin-dir`, and `--mcp-config` like the top-level `claude` command. Opening agent view requires an interactive terminal | `claude agents --json` |
| `claude attach <id>` | Attach to a [background session](/docs/en/agent-view#manage-sessions-from-the-shell) in this terminal | `claude attach 7c5dcf5d` |
| `claude auto-mode defaults` | Print the built-in [auto mode](/docs/en/permission-modes#eliminate-prompts-with-auto-mode) classifier rules as JSON. Use `claude auto-mode config` to see your effective config with settings applied. `--label <prefix>` prints only the rules whose label starts with that prefix, matched case-insensitively. Requires Claude Code v2.1.208 or later | `claude auto-mode defaults --label 'Git Destructive'` |
| `claude auto-mode reset` | Restore the default [auto mode](/docs/en/permission-modes#eliminate-prompts-with-auto-mode) configuration by removing the `autoMode` section from your user settings file. Prompts for confirmation before writing; pass `-y`/`--yes` to skip the prompt. Rules from [managed settings](/docs/en/server-managed-settings) or the `--settings` flag still apply. Requires Claude Code v2.1.212 or later. See [Inspect the defaults and your effective config](/docs/en/auto-mode-config#inspect-the-defaults-and-your-effective-config) | `claude auto-mode reset --yes` |
| `claude daemon status` | Print the background-session [supervisor's](/docs/en/agent-view#the-supervisor-process) state, version, socket directory, and worker count for diagnostics. Exits 1 if the supervisor isn't running | `claude daemon status` |
| `claude daemon stop --any` | Stop the background-session [supervisor](/docs/en/agent-view#the-supervisor-process) and the sessions it hosts. Pass `--keep-workers` to leave background sessions running so the next supervisor reconnects to them. `--any` confirms stopping an on-demand supervisor, which is the default. Use this to recover from an [unresponsive supervisor](/docs/en/agent-view#agent-view-says-the-background-service-did-not-respond) | `claude daemon stop --any --keep-workers` |
| `claude doctor` | Print read-only installation and settings diagnostics from the terminal without starting a session, including install health, settings-file validation errors, and Remote Control eligibility. For the in-session setup checkup that can also apply fixes, run [`/doctor`](/docs/en/commands#all-commands) | `claude doctor` |
| `claude import [codex\|gemini]` | Start an interactive session that runs [`/import`](/docs/en/commands#all-commands) to bring configuration from other coding agents into Claude Code. Accepts the same `--dry-run` and `--yes` options as the command. Not available on Amazon Bedrock, Google Cloud's Agent Platform, Microsoft Foundry, or Claude Platform on AWS. Also unavailable when you turn off [feature-flag fetching](/docs/en/env-vars#features-that-need-feature-flag-fetching). Requires Claude Code v2.1.213 or later | `claude import codex --dry-run` |
| `claude logs <id>` | Print recent output from a [background session](/docs/en/agent-view#manage-sessions-from-the-shell) | `claude logs 7c5dcf5d` |
| `claude mcp` | Configure Model Context Protocol (MCP) servers | See the [Claude Code MCP documentation](/docs/en/mcp). |
| `claude mcp login <name>` | Run a configured MCP server's OAuth flow without opening the interactive `/mcp` panel. Works for HTTP, SSE, and claude.ai connector servers. Add `--no-browser` over SSH to print the authorization URL instead of opening a browser, then paste the redirect URL back at the prompt. Requires Claude Code v2.1.186 or later. See [Authenticate from the command line](/docs/en/mcp#authenticate-from-the-command-line) | `claude mcp login sentry` |
| `claude mcp logout <name>` | Clear stored OAuth credentials for an MCP server. Requires Claude Code v2.1.186 or later | `claude mcp logout sentry` |
| `claude plugin` | Manage Claude Code [plugins](/docs/en/plugins). Alias: `claude plugins`. See [plugin reference](/docs/en/plugins-reference#cli-commands-reference) for subcommands | `claude plugin install code-review@claude-plugins-official` |
| `claude project purge [path]` | Delete all local Claude Code state for a project: transcripts, task lists, debug logs, file-edit history, prompt history lines, and the project's entry in `~/.claude.json`. Omit `[path]` to pick from an interactive list. Flags: `--dry-run` to preview, `-y`/`--yes` to skip confirmation, `-i`/`--interactive` to confirm each item, `--all` for every project. See [Clear local data](/docs/en/claude-directory#clear-local-data) | `claude project purge ~/work/repo --dry-run` |
| `claude remote-control` | Start a [Remote Control](/docs/en/remote-control) server to control Claude Code from Claude.ai or the Claude app. Runs in server mode (no local interactive session). See [Server mode flags](/docs/en/remote-control#start-a-remote-control-session). After you stop the server, you can bring back the sessions it was serving. See [Resume sessions after stopping the server](/docs/en/remote-control#resume-sessions-after-stopping-the-server) | `claude remote-control --name "My Project"` |
| `claude respawn <id>` | Restart a [background session](/docs/en/agent-view#manage-sessions-from-the-shell), running or stopped, with its conversation intact. Use `--all` to restart every running session, e.g. to pick up an updated Claude Code binary | `claude respawn 7c5dcf5d` |
| `claude rm <id>` | Remove a [background session](/docs/en/agent-view#manage-sessions-from-the-shell) from the list. The conversation transcript stays on your local machine, available through `claude --resume` | `claude rm 7c5dcf5d` |
| `claude self-hosted-runner` | Start a runner process that registers this machine or container with a [self-hosted environment](/docs/en/self-hosted-environments) and hosts Claude Code cloud sessions on your infrastructure. Run `claude self-hosted-runner setup` for a guided operator walkthrough, `claude self-hosted-runner doctor` to [diagnose a deployed runner](/docs/en/self-hosted-environments-deploy#troubleshooting), and `claude self-hosted-runner orchestrator` to spawn [on-demand runners](/docs/en/self-hosted-environments-configuration#on-demand-runners). Requires Claude Code v2.1.224 or later | `claude self-hosted-runner setup` |
| `claude setup-token` | Generate a long-lived OAuth token for CI and scripts. Prints the token to the terminal without saving it. Requires a Claude subscription. See [Generate a long-lived token](/docs/en/authentication#generate-a-long-lived-token) | `claude setup-token` |
| `claude stop <id>` | Stop a [background session](/docs/en/agent-view#manage-sessions-from-the-shell). Also accepts `claude kill` | `claude stop 7c5dcf5d` |
| `claude ultrareview [target]` | Run [ultrareview](/docs/en/ultrareview#run-ultrareview-non-interactively) non-interactively. Prints findings to stdout and exits 0 on success or 1 on failure. Use `--json` for the raw payload and `--timeout <minutes>` to override the 30-minute default. Use `--post` on a `github.com` pull request target to post the finished findings to the PR as one plain comment from your GitHub account. `--no-post` is the default. Requires Claude Code v2.1.227 or later. See [Post findings to the pull request](/docs/en/ultrareview#post-findings-to-the-pull-request) | `claude ultrareview 1234 --json` |
If you mistype a subcommand, Claude Code suggests the closest match and exits without starting a session. For example, `claude udpate` prints `Did you mean claude update?`.
As of v2.1.199, `claude --dangerously-skip-permissions daemon <subcommand>` runs the `daemon` subcommand. Earlier versions treated `daemon <subcommand>` as the prompt for a new interactive session, so the subcommand never ran when the flag came first, a common setup when `claude` is aliased to include the flag. Only a leading `--dangerously-skip-permissions` or `--allow-dangerously-skip-permissions` routes to `daemon` this way; any other leading flag still starts an interactive session.
## CLI flags
Customize Claude Code's behavior with these command-line flags. `claude --help` does not list every flag, so a flag's absence from `--help` does not mean it is unavailable.
| Flag | Description | Example |
| :---------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :-------------------------------------------------------------------------------------------------- |
| `--add-dir` | Add additional working directories for Claude to read and edit files. Grants file access; most `.claude/` configuration is [not discovered](/docs/en/permissions#additional-directories-grant-file-access-not-configuration) from these directories. Validates each path exists as a directory. To persist these directories across sessions, set [`permissions.additionalDirectories`](/docs/en/settings#permission-settings) in settings | `claude --add-dir ../apps ../lib` |
| `--advisor <model>` | Enable the server-side [advisor tool](/docs/en/advisor) for this session with a model alias, `fable`, `opus`, or `sonnet`, or a full model ID. Takes precedence over the `advisorModel` setting for the session. `fable` requires [Fable 5 access](/docs/en/advisor#choose-an-advisor-model) | `claude --advisor opus` |
| `--agent` | Specify an agent for the current session (overrides the `agent` setting) | `claude --agent my-custom-agent` |
| `--agents` | Define custom subagents dynamically via JSON. Uses the same field names as subagent [frontmatter](/docs/en/sub-agents#supported-frontmatter-fields), plus a `prompt` field for the agent's instructions | `claude --agents '{"reviewer":{"description":"Reviews code","prompt":"You are a code reviewer"}}'` |
| `--allow-dangerously-skip-permissions` | Add `bypassPermissions` to the `Shift+Tab` mode cycle without starting in it. Lets you begin in a different mode like `plan` and switch to `bypassPermissions` later. See [permission modes](/docs/en/permission-modes#skip-all-checks-with-bypasspermissions-mode) | `claude --permission-mode plan --allow-dangerously-skip-permissions` |
| `--allowedTools`, `--allowed-tools` | Tools that execute without prompting for permission. See [permission rule syntax](/docs/en/settings#permission-rule-syntax) for pattern matching. To restrict which tools are available, use `--tools` instead | `"Bash(git log *)" "Bash(git diff *)" "Read"` |
| `--append-subagent-system-prompt` | Append custom text to the end of every [subagent](/docs/en/sub-agents)'s system prompt, including nested subagents. Only applies in non-interactive mode with `-p`. Requires Claude Code v2.1.205 or later | `claude -p --append-subagent-system-prompt "Cite file paths in every answer" "query"` |
| `--append-system-prompt` | Append custom text to the end of the default system prompt | `claude --append-system-prompt "Always use TypeScript"` |
| `--append-system-prompt-file` | Load additional system prompt text from a file and append to the default prompt | `claude --append-system-prompt-file ./extra-rules.txt` |
| `--autocompact <auto\|tokens>` | Set the [auto-compact window](/docs/en/model-config#set-the-auto-compact-window) for this session without changing your saved settings. Accepts the same values as `/autocompact`; that section covers the value forms and what overrides the flag. Requires Claude Code v2.1.221 or later | `claude --autocompact 500k` |
| `--ax-screen-reader` | Render screen-reader friendly output: flat text without decorative borders or animations. Forces the classic renderer, so the [`tui`](/docs/en/settings#available-settings) setting has no effect; attached [background sessions](/docs/en/agent-view) still render fullscreen. Takes precedence over [`CLAUDE_AX_SCREEN_READER`](/docs/en/env-vars) and the [`axScreenReader`](/docs/en/settings#available-settings) setting. Requires Claude Code v2.1.181 or later | `claude --ax-screen-reader` |
| `--bare` | Minimal mode: skip auto-discovery of hooks, skills, plugins, MCP servers, auto memory, and CLAUDE.md so scripted calls start faster. Claude has access to Bash, file read, and file edit tools. Sets [`CLAUDE_CODE_SIMPLE`](/docs/en/env-vars). See [bare mode](/docs/en/headless#start-faster-with-bare-mode) | `claude --bare -p "query"` |
| `--betas` | Beta headers to include in API requests (API key users only) | `claude --betas interleaved-thinking` |
| `--bg`, `--background` | Start the session as a [background agent](/docs/en/agent-view) and return immediately. Prints the session ID and management commands. Combine with `--exec` to run a shell command as a background job instead of a Claude session, or with `--agent` to run a specific subagent. Cannot be combined with `-p`/`--print`; see the [error reference](/docs/en/errors#command-line-errors) | `claude --bg "investigate the flaky test"` |
| `--channels` | (Research preview) MCP servers whose [channel](/docs/en/channels) notifications Claude should listen for in this session. Space-separated list of `plugin:<name>@<marketplace>` entries. Requires Anthropic authentication through claude.ai or a Console API key | `claude --channels plugin:my-notifier@my-marketplace` |
| `--chrome` | Enable [Chrome browser integration](/docs/en/chrome) for web automation and testing | `claude --chrome` |
| `--cloud` | With a task description, create a new [web session](/docs/en/claude-code-on-the-web) on claude.ai. With a session ID (`session_...` or `cse_...`) or a claude.ai/code URL, target that existing session instead: `-p` queues the message into it, and no `-p` attaches your terminal. See [send a follow-up message](/docs/en/claude-code-on-the-web#send-follow-ups-from-the-cli). | `claude --cloud "Fix the login bug"` |
| `--continue`, `-c` | Load the most recent conversation in the current directory. Includes sessions that added this directory with `/add-dir` | `claude --continue` |
| `--dangerously-load-development-channels` | Enable [channels](/docs/en/channels-reference#test-during-the-research-preview) that are not on the approved allowlist, for local development. Accepts `plugin:<name>@<marketplace>` and `server:<name>` entries. Prompts for confirmation | `claude --dangerously-load-development-channels server:webhook` |
| `--dangerously-skip-permissions` | Skip permission prompts. Equivalent to `--permission-mode bypassPermissions`. See [permission modes](/docs/en/permission-modes#skip-all-checks-with-bypasspermissions-mode) for what this does and does not skip. For sessions started with `--bg`, the mode [persists when the supervisor restarts the session](/docs/en/agent-view#permission-mode-model-and-effort) | `claude --dangerously-skip-permissions` |
| `--debug` | Enable debug mode with optional category filtering, such as `--debug='mcp,startup'` or `--debug='!1p'`. The filter binds only in the `=` form; a space-separated filter enables debug mode without filtering | `claude --debug='mcp,startup'` |
| `--debug-file <path>` | Write debug logs to a specific file path. Implicitly enables debug mode. Takes precedence over `CLAUDE_CODE_DEBUG_LOGS_DIR` | `claude --debug-file /tmp/claude-debug.log` |
| `--disable-slash-commands` | Disable all skills and commands for this session | `claude --disable-slash-commands` |
| `--disallowedTools`, `--disallowed-tools` | Deny rules. A bare tool name removes the matching tools from Claude's context: `"Edit"` removes Edit, `"*"` removes every tool, and `"mcp__*"` removes every MCP tool. A scoped rule such as `Bash(rm *)` leaves the tool available and denies only matching calls. A rule naming [`EndConversation`](/docs/en/tools-reference#endconversation-tool-behavior) can't remove it while any other tool remains | `"Bash(git log *)" "Bash(git diff *)" "Edit"` |
| `--effort` | Set the [effort level](/docs/en/model-config#adjust-effort-level) for the current session. Options: `low`, `medium`, `high`, `xhigh`, `max`, or `ultracode`. Available levels depend on the model. `ultracode` starts the session at `xhigh` effort with [ultracode](/docs/en/workflows#let-claude-decide-with-ultracode) turned on, and requires Claude Code v2.1.203 or later. Overrides the [`effortLevel`](/docs/en/settings#available-settings) setting for this session and does not persist | `claude --effort high` |
| `--enable-auto-mode` | Removed in v2.1.111. Auto mode is now in the `Shift+Tab` cycle by default; use `--permission-mode auto` to start in it | `claude --permission-mode auto` |
| `--environment <environment-id>` | Create a new cloud session that runs on the [self-hosted environment](/docs/en/self-hosted-environments) with the given ID. Environment IDs start with `ccpool_`. See [`--environment` dispatch behavior](/docs/en/self-hosted-environments-testing#environment-dispatch-behavior) for the interactive and non-interactive forms and the flag combinations it rejects. Requires Claude Code v2.1.224 or later | `claude -p "Fix the login bug" --environment ccpool_abc123` |
| `--exclude-dynamic-system-prompt-sections` | Move per-machine sections from the system prompt (working directory, environment info, memory paths, git-repo flag) into the first user message. Improves prompt-cache reuse across different users and machines running the same task. Only applies with the default system prompt; ignored when `--system-prompt` or `--system-prompt-file` is set. Use with `-p` for scripted, multi-user workloads | `claude -p --exclude-dynamic-system-prompt-sections "query"` |
| `--exec` | Run a shell command as a PTY-backed background job instead of starting a Claude session. Use with `--bg` to launch from the shell | `claude --bg --exec 'pytest -x'` |
| `--fallback-model` | Enable automatic fallback to the specified model(s) when the primary model is overloaded or not available, for example a retired model. Accepts a comma-separated list tried in order. See [Fallback model chains](/docs/en/model-config#fallback-model-chains). To persist a chain across sessions, use the [`fallbackModel` setting](/docs/en/settings#available-settings), which this flag overrides | `claude --fallback-model sonnet,haiku` |
| `--fork-session` | When resuming, create a new session ID instead of reusing the original (use with `--resume` or `--continue`) | `claude --resume abc123 --fork-session` |
| `--forward-subagent-text` | Emit [subagent](/docs/en/sub-agents) text and thinking blocks in the output stream as `assistant` and `user` messages with `parent_tool_use_id` set, so you can reconstruct each subagent's transcript. Without this flag, Claude Code emits only subagent `tool_use` and `tool_result` blocks. Requires `--print` and `--output-format stream-json`. Claude Code also forwards messages from [nested subagents](/docs/en/sub-agents#let-subagents-spawn-their-own-subagents), setting `parent_tool_use_id` to the ID of the Agent tool call that spawned each one; this requires Claude Code v2.1.219 or later. The [`CLAUDE_CODE_FORWARD_SUBAGENT_TEXT`](/docs/en/env-vars) environment variable enables the same behavior. Requires Claude Code v2.1.211 or later | `claude -p --output-format stream-json --verbose --forward-subagent-text "query"` |
| `--from-pr` | Open the session picker filtered to sessions linked to a specific pull request. Accepts a PR number, a GitHub or GitHub Enterprise PR URL, a GitLab merge request URL, or a Bitbucket pull request URL. Sessions are linked automatically when Claude creates the pull request | `claude --from-pr 123` |
| `--ide` | Automatically connect to IDE on startup if exactly one valid IDE is available | `claude --ide` |
| `--init` | Run [Setup hooks](/docs/en/hooks#setup) with the `init` matcher before the session (print mode only) | `claude -p --init "query"` |
| `--init-only` | Run [Setup](/docs/en/hooks#setup) and `SessionStart` hooks, then exit without starting a conversation | `claude --init-only` |
| `--include-hook-events` | Include hook lifecycle events in the output stream. `SessionStart` and `Setup` hook events are always included and don't need this flag. Some hook events, such as `Notification`, `SessionEnd`, `PreCompact`, and `PostCompact`, never produce a `hook_started` event, even with this flag. For those events, Claude Code still emits `hook_progress` while a command hook that runs for more than a second produces output, and emits `hook_response` only when a [hook that runs in the background](/docs/en/hooks#run-hooks-in-the-background) finishes. Requires `--output-format stream-json` | `claude -p --output-format stream-json --verbose --include-hook-events "query"` |
| `--include-partial-messages` | Include partial streaming events in output. Requires `--print` and `--output-format stream-json` | `claude -p --output-format stream-json --verbose --include-partial-messages "query"` |
| `--input-format` | Specify input format for print mode (options: `text`, `stream-json`) | `claude -p --output-format json --input-format stream-json` |
| `--json-schema` | Get validated JSON output matching a JSON Schema after the agent completes its workflow (print mode only). See [structured outputs](/docs/en/agent-sdk/structured-outputs). Claude Code exits with an error on an invalid schema and accepts the `format` keyword as an annotation without client-side validation | `claude -p --json-schema '{"type":"object","properties":{...}}' "query"` |
| `--maintenance` | Run [Setup hooks](/docs/en/hooks#setup) with the `maintenance` matcher before the session (print mode only) | `claude -p --maintenance "query"` |
| `--max-budget-usd` | Maximum dollar amount to spend on API calls before stopping (print mode only). Spend from [subagents](/docs/en/sub-agents) counts toward the cap. Once spend reaches the cap, spawning another subagent fails with `Budget limit reached`, and Claude Code stops background subagents that are still running; the cap-enforcement behaviors require Claude Code v2.1.217 or later | `claude -p --max-budget-usd 5.00 "query"` |
| `--max-turns` | Limit the number of agentic turns (print mode only). Exits with an error when the limit is reached. No limit by default. With `--input-format stream-json`, a message sent while Claude is working stays queued and runs as its own turn, with its own limit, when the limit ends the current one | `claude -p --max-turns 3 "query"` |
| `--mcp-config` | Load MCP servers from JSON files or strings (space-separated). When you pass this flag with `-p`, Claude Code waits for still-pending servers to connect before running the first turn, up to the [`MCP_TIMEOUT`](/docs/en/env-vars) startup timeout, 30 seconds by default; a server with a [cached tool list](/docs/en/mcp#managing-your-servers) skips the wait and connects on first use. The wait requires Claude Code v2.1.221 or later | `claude --mcp-config ./mcp.json` |
| `--model` | Sets the model for the current session with an alias for the latest model (`sonnet`, `opus`, `haiku`, or `fable`) or a model's full name. Overrides the [`model`](/docs/en/settings#available-settings) setting and [`ANTHROPIC_MODEL`](/docs/en/model-config#environment-variables) | `claude --model claude-sonnet-5` |
| `--name`, `-n` | Set a display name for the session, shown in `/resume` and the terminal title. You can resume a named session with `claude --resume <name>`. In an interactive session, if another live session on this machine already uses the name, Claude Code applies [a variant of it](/docs/en/sessions#name-your-sessions) instead. <br /><br />[`/rename`](/docs/en/commands) changes the name mid-session and also shows it on the prompt bar | `claude -n "my-feature-work"` |
| `--no-chrome` | Disable [Chrome browser integration](/docs/en/chrome) for this session | `claude --no-chrome` |
| `--no-session-persistence` | Disable session persistence so sessions are not saved to disk and cannot be resumed. Print mode only. The [`CLAUDE_CODE_SKIP_PROMPT_HISTORY`](/docs/en/env-vars) environment variable does the same in any mode | `claude -p --no-session-persistence "query"` |
| `--output-format` | Specify output format for print mode (options: `text`, `json`, `stream-json`) | `claude -p "query" --output-format json` |
| `--permission-mode` | Begin in a specified [permission mode](/docs/en/permission-modes). Accepts `default`, `acceptEdits`, `plan`, `auto`, `dontAsk`, `bypassPermissions`, or `manual` as an alias for `default`. The `manual` alias selects the mode the UI labels Manual and requires Claude Code v2.1.200 or later; `claude --help` lists it in place of `default`, and both values work. Overrides `defaultMode` from settings files | `claude --permission-mode plan` |
| `--permission-prompt-tool` | Specify an MCP tool to handle permission prompts in non-interactive mode. Claude Code waits for that tool's MCP server to connect before running the first turn, up to the [`MCP_TIMEOUT`](/docs/en/env-vars) startup timeout, 30 seconds by default. <br /><br />The prompt tool can't approve an MCP tool marked as [requiring user interaction](/docs/en/mcp#require-approval-for-a-specific-tool): Claude Code converts an `allow` result for one to a deny. This restriction requires Claude Code v2.1.199 or later | `claude -p --permission-prompt-tool mcp_auth_tool "query"` |
| `--plugin-dir` | Load a plugin from a directory or `.zip` archive for this session only. Each flag takes one path. Repeat the flag for multiple plugins: `--plugin-dir A --plugin-dir B.zip` | `claude --plugin-dir ./my-plugin` |
| `--plugin-url` | Fetch a plugin `.zip` archive from a URL for this session only. Repeat the flag for multiple plugins, or pass space-separated URLs in a single quoted value | `claude --plugin-url https://example.com/plugin.zip` |
| `--print`, `-p` | Print response without interactive mode (see [Agent SDK documentation](/docs/en/agent-sdk/overview) for programmatic usage details) | `claude -p "query"` |
| `--prompt-suggestions` | Emit a `prompt_suggestion` message with a predicted next user prompt after each turn that generates one; very short conversations can produce none. Requires `--print`, `--output-format stream-json`, and `--verbose`. See [Prompt suggestions](/docs/en/interactive-mode#prompt-suggestions) | `claude -p --prompt-suggestions --output-format stream-json --verbose "query"` |
| `--ref <branch>` | With `--environment`, base the new session's checkout on a named ref instead of local `HEAD` | `claude -p "Run the smoke test" --environment ccpool_abc123 --ref main` |
| `--remote` | Deprecated alias for `--cloud`, including the existing-session form | `claude --remote "Fix the login bug"` |
| `--remote-control`, `--rc` | Start an interactive session with [Remote Control](/docs/en/remote-control#start-a-remote-control-session) enabled so you can also control it from claude.ai or the Claude app. Optionally pass a name for the session | `claude --remote-control "My Project"` |
| `--remote-control-session-name-prefix <prefix>` | Prefix for auto-generated [Remote Control](/docs/en/remote-control) session names when no explicit name is set. Defaults to your machine's hostname, producing names like `myhost-graceful-unicorn`. Set `CLAUDE_REMOTE_CONTROL_SESSION_NAME_PREFIX` for the same effect | `claude remote-control --remote-control-session-name-prefix dev-box` |
| `--replay-user-messages` | Re-emit user messages from stdin back on stdout for acknowledgment. Requires `--input-format stream-json` and `--output-format stream-json` | `claude -p --input-format stream-json --output-format stream-json --verbose --replay-user-messages` |
| `--resume`, `-r` | Resume a specific session by ID or name, or show an interactive picker to choose a session. The picker and name search include sessions that added this directory with `/add-dir`. When you pass a session ID, Claude Code searches the current project directory and its git worktrees, then every other project on this machine. Before v2.1.223, the ID search covered only the current project directory and its git worktrees. [Background sessions](/docs/en/agent-view) appear in the picker marked with `bg` | `claude --resume auth-refactor` |
| `--safe-mode` | Start with all customizations disabled to troubleshoot a broken configuration: CLAUDE.md, skills, plugins, hooks, MCP servers, custom commands and agents, output styles, workflows, custom themes, custom keybindings, status line and file-suggestion commands, LSP servers, and auto memory do not load. Authentication, model selection, built-in tools, and permissions work normally, which differs from [`--bare`](/docs/en/headless#start-faster-with-bare-mode). Managed settings policy still applies, including policy-configured hooks, status line, and file-suggestion commands; managed plugins, managed skills, managed CLAUDE.md, and policy-configured MCP servers do not. Useful for checking whether a customization is what triggers [automatic model fallback](/docs/en/model-config#automatic-model-fallback). Sets [`CLAUDE_CODE_SAFE_MODE`](/docs/en/env-vars) | `claude --safe-mode` |
| `--session-id` | Use a specific session ID for the conversation (must be a valid UUID) | `claude --session-id "550e8400-e29b-41d4-a716-446655440000"` |
| `--setting-sources` | Comma-separated list of setting sources to load (`user`, `project`, `local`) | `claude --setting-sources user,project` |
| `--settings` | Path to a settings JSON file or an inline JSON string. Values you set here override the same keys in your `settings.json` files for this session. Keys you omit keep their file-based values. The file must be a regular file no larger than 2 MiB. See [settings precedence](/docs/en/settings#settings-precedence) | `claude --settings ./settings.json` |
| `--strict-mcp-config` | Only use MCP servers from `--mcp-config`, ignoring all other MCP configurations. See [Exclusive control with managed-mcp.json](/docs/en/managed-mcp#exclusive-control-with-managed-mcp-json) for what the flag does under a managed MCP file | `claude --strict-mcp-config --mcp-config ./mcp.json` |
| `--system-prompt` | Replace the entire system prompt with custom text | `claude --system-prompt "You are a Python expert"` |
| `--system-prompt-file` | Load system prompt from a file, replacing the default prompt | `claude --system-prompt-file ./custom-prompt.txt` |
| `--teleport` | Resume a [web session](/docs/en/claude-code-on-the-web) in your local terminal | `claude --teleport` |
| `--teammate-mode` | Set how [agent team](/docs/en/agent-teams) teammates display: `in-process` (default), `auto`, `tmux`, or `iterm2` (added in v2.1.186). Overrides the [`teammateMode`](/docs/en/settings#available-settings) setting for this session. See [Choose a display mode](/docs/en/agent-teams#choose-a-display-mode) | `claude --teammate-mode auto` |
| `--tmux` | Create a tmux session for the worktree. Requires `--worktree`. Uses iTerm2 native panes when available; pass `--tmux=classic` for traditional tmux | `claude -w feature-auth --tmux` |
| `--tools` | Restrict which built-in tools Claude can use. Use `""` to disable all, `"default"` for all, or tool names like `"Bash,Edit,Read"`. The flag doesn't affect MCP tools; to deny those too, use `--disallowedTools "mcp__*"`. A list that omits [`EndConversation`](/docs/en/tools-reference#endconversation-tool-behavior) doesn't remove it; `""` removes it only when no MCP tools remain | `claude --tools "Bash,Edit,Read"` |
| `--verbose` | Enable verbose logging, shows full turn-by-turn output. Overrides the [`viewMode`](/docs/en/settings#available-settings) setting for this session | `claude --verbose` |
| `--version`, `-v` | Output the version number | `claude -v` |
| `--worktree`, `-w` | Start Claude in an isolated [git worktree](/docs/en/worktrees) at `<repo>/.claude/worktrees/<name>`. If no name is given, one is auto-generated. Pass `#<number>` or a GitHub pull request URL to fetch that PR from `origin` and branch the worktree from it | `claude -w feature-auth` |
### System prompt flags
Claude Code provides four flags for customizing the system prompt. All four work in both interactive and non-interactive modes.
| Flag | Behavior | Example |
| :---------------------------- | :------------------------------------------ | :------------------------------------------------------ |
| `--system-prompt` | Replaces the entire default prompt | `claude --system-prompt "You are a Python expert"` |
| `--system-prompt-file` | Replaces with file contents | `claude --system-prompt-file ./prompts/review.txt` |
| `--append-system-prompt` | Appends to the default prompt | `claude --append-system-prompt "Always use TypeScript"` |
| `--append-system-prompt-file` | Appends file contents to the default prompt | `claude --append-system-prompt-file ./style-rules.txt` |
`--system-prompt` and `--system-prompt-file` are mutually exclusive. The append flags can be combined with either replacement flag.
Choose based on whether Claude Code's default identity still fits your task. Use an append flag when Claude should remain a coding assistant that also follows your extra rules: per-invocation instructions, output formatting, or domain context for a `-p` script. Appending preserves the default tool guidance, safety instructions, and coding conventions, so you only supply what differs. Use a replacement flag when the surface, identity, or permission model differs from Claude Code's, like a non-coding agent in a pipeline that no human watches. Replacing drops all of the default prompt, including tool guidance and safety instructions, so you take responsibility for whatever your task still needs.
These flags apply only to the current invocation. For persistent personas you can switch between and share across a project, use [output styles](/docs/en/output-styles). For project conventions Claude should always follow, use [CLAUDE.md](/docs/en/memory). The [Agent SDK guide on system prompts](/docs/en/agent-sdk/modifying-system-prompts#decide-on-a-starting-point) covers the same decision in more depth.
## See also
* [Chrome extension](/docs/en/chrome) - Browser automation and web testing
* [Interactive mode](/docs/en/interactive-mode) - Shortcuts, input modes, and interactive features
* [Quickstart guide](/docs/en/quickstart) - Getting started with Claude Code
* [Common workflows](/docs/en/common-workflows) - Advanced workflows and patterns
* [Settings](/docs/en/settings) - Configuration options
* [Agent SDK documentation](/docs/en/agent-sdk/overview) - Programmatic usage and integrations
cloud-environments First recorded · 671 lines, first recorded
# Configure cloud environments ## The Default environment ## Configure your environment ### Set environment variables ### Select an environment from the CLI ### Archive an environment ### Organization-shared environments ## Network access ### Access levels ### Allow specific domains ### GitHub proxy ### Security proxy ## What's available in cloud sessions ### What carries over from your setup ### Installed tools ### Work with GitHub issues and pull requests ### Link output back to the session ### Run tests, start services, and add packages #### Run tests #### Start services #### Add packages ### Resource limits ## Setup scripts ### Script requirements ### Environment caching ### Setup scripts vs. SessionStart hooks ### Install dependencies with a SessionStart hook #### Limitations in cloud sessions ## Default allowed domains ## Related resources
The first capture of this source. The page was already there, and this is what it said.
# Configure cloud environments
> Configure cloud environments for Claude Code cloud sessions: network access levels, environment variables, setup scripts, and environment caching.
<Note>
Cloud environments require [Claude Code on the web](/docs/en/claude-code-on-the-web), which is in research preview for Pro, Max, and Team users, and for Enterprise users with [premium seats or Chat + Claude Code seats](https://support.claude.com/en/articles/11845131-use-claude-code-with-your-team-or-enterprise-plan).
</Note>
Each [cloud session](/docs/en/claude-code-on-the-web) runs in a cloud environment. You can configure an environment to allow or deny [network access](#access-levels), set environment variables for the session, and run a [setup script](#setup-scripts) before Claude starts working.
The same environments apply wherever you start a cloud session: [Claude Code on the web](/docs/en/claude-code-on-the-web), the terminal with [`claude --cloud`](/docs/en/claude-code-on-the-web#from-terminal-to-web), [Claude Tag](https://claude.com/docs/claude-tag/overview), [routines](/docs/en/routines), the [Claude mobile app](/docs/en/mobile), and the [Desktop app](/docs/en/desktop). Claude Tag sessions can't run in [self-hosted environments](/docs/en/self-hosted-environments) yet; the other surfaces can route to any environment.
<Info>
[Remote Control](/docs/en/remote-control) sessions connect the web and mobile interfaces to a session on your own machine, which uses your machine's network and files, not a cloud environment. Claude Tag channel sessions use [shared environments](#organization-shared-environments) only.
</Info>
## The Default environment
Onboarding sets up the **Default** environment for you, whether you connect through [the web](/docs/en/web-quickstart#connect-github) or a CLI flow such as `/web-setup`; if web onboarding shows an environment form instead of creating the environment, keep the form's defaults to get the same **Default** environment. **Default** carries no configuration of its own:
* [**Trusted** network access](#access-levels): sessions reach package registries and other [allowlisted domains](#default-allowed-domains), and nothing else through the session's network.
* No other configuration: **Default** defines no environment variables or setup script, so sessions start with just the [pre-installed tools](#installed-tools).
With only **Default** available, every session runs in it. When you have more than one environment, sessions choose one per surface:
* On the web, the Desktop app, and the mobile app, sessions use the environment shown in the [selector](#configure-your-environment). An admin-set [organization default](#organization-shared-environments) fills the selection when you haven't picked one.
* From the CLI, sessions use your [`/remote-env` pick](#select-an-environment-from-the-cli), or fall back to your first available cloud environment. For a [self-hosted environment](/docs/en/self-hosted-environments), passing `--environment <environment-id>` with its `ccpool_` ID [when you dispatch a session](/docs/en/self-hosted-environments-testing#run-the-test-loop) overrides both for that invocation. The flag rejects Anthropic-hosted `env_` IDs, so use `/remote-env` to target those. The flag requires Claude Code v2.1.224 or later.
Configure an environment when the default isn't enough: when Claude needs to reach domains outside the [default allowlist](#default-allowed-domains), needs environment variables set for its sessions, or needs dependencies installed before it starts working.
## Configure your environment
Create, edit, and archive environments from the environment selector at [claude.ai/code](https://claude.ai/code), which you reach after [web onboarding](/docs/en/web-quickstart). Environments you create are personal to your account; [shared environments](#organization-shared-environments) created by your admins appear in the same selector. See [Installed tools](#installed-tools) for what's available without any configuration.
<Steps>
<Step title="Open the environment selector">
On [claude.ai/code](https://claude.ai/code), select the cloud icon showing the current environment's name, in the row above the message box. There's no settings page or direct URL for the selector.
<Frame>
<img src="https://mintcdn.com/claude-code/ZFId6l95856c5LSw/images/cloud-environment-selector.png?fit=max&auto=format&n=ZFId6l95856c5LSw&q=85&s=cc2813a5664519eaf5a89d793ce5af26" alt="The environment selector open above the message box at claude.ai/code. The cloud button showing the environment name Default sits in the row above the message box. The open menu lists a Local row with Download and Desktop only labels, a Cloud section where the Default environment is selected with a checkmark and shows a settings gear icon on hover, an Add cloud environment option, and a Remote Control section with setup instructions." width="1672" height="682" data-path="images/cloud-environment-selector.png" />
</Frame>
</Step>
<Step title="Add or edit an environment">
Select **Add cloud environment**, or hover over an existing environment and select the settings icon that appears on the right. The dialog includes the name, network access level, environment variables, and setup script.
<Frame>
<img src="https://mintcdn.com/claude-code/ZFId6l95856c5LSw/images/cloud-environment-dialog.png?fit=max&auto=format&n=ZFId6l95856c5LSw&q=85&s=30d4478b31d1f879f7ee287ddab32505" alt="The New cloud environment dialog. A Name field with the placeholder Default, a Network access selector set to Trusted with links to the network policy and access levels, an Environment variables box showing .env-format placeholder text with a note that values are visible to anyone using the environment, a Setup script box described as a Bash script that runs when a new session starts before Claude Code launches, and Cancel and Create environment buttons." width="874" height="1372" data-path="images/cloud-environment-dialog.png" />
</Frame>
</Step>
</Steps>
### Set environment variables
Environment variables use `.env` format, one `KEY=value` pair per line. Plain values don't need quotes, and if you quote a value with a matching pair, the quotes don't become part of the value. Quote a value that spans multiple lines or contains a `#`: in an unquoted value, `#` starts a comment and the rest of the line is dropped.
The following example defines three variables.
```text theme={null}
NODE_ENV=development
LOG_LEVEL=debug
DATABASE_URL=postgres://localhost:5432/myapp
```
Each session copies the environment's values once, at startup, into ordinary environment variables that any command Claude runs can read. Because running sessions don't re-read the configuration, editing or adding variables affects sessions you start afterward; sessions already running keep the values they started with.
Claude Code on the web also sets some variables itself when it starts a session. For [`CLAUDE_AUTOCOMPACT_PCT_OVERRIDE`](/docs/en/claude-code-on-the-web#manage-context), the value Claude Code on the web sets overrides one you add here, so adding that key here has no effect.
Anyone who uses the environment can read the values, and cloud environments have no dedicated secrets store, so don't add API keys or other credentials. If a session needs a credential anyway, see [What carries over from your setup](#what-carries-over-from-your-setup).
### Select an environment from the CLI
Run `/remote-env` in your terminal to choose the default environment for cloud sessions you create from the CLI, such as [`claude --cloud`](/docs/en/claude-code-on-the-web#from-terminal-to-web). The command opens a picker of your existing environments and saves your choice to the `remote.defaultEnvironmentId` key in your [user settings](/docs/en/settings#settings-files), so it applies in every project on your machine until you change it, unless the same key is set at a higher-precedence [settings layer](/docs/en/settings#settings-precedence), such as a repo's project settings.
A [self-hosted environment](/docs/en/self-hosted-environments) ID, which has the form `ccpool_...`, follows a stricter source rule. See [`remote.defaultEnvironmentId`](/docs/en/settings#available-settings) for the settings layers Claude Code honors it from.
`/remote-env` only sets the default: it doesn't start a session, and it can't add or edit environments. Manage them at [claude.ai/code](https://claude.ai/code).
### Archive an environment
To archive an environment, open it for editing and select **Archive**. You can't delete an environment, only archive it.
Archiving affects new sessions, not running ones:
* Sessions already running in the environment continue to work.
* The environment disappears from the selector and from `/remote-env`, so you can't pick it for new sessions.
* No new session can start in an archived environment, on any surface. If the environment was your saved [CLI default](#select-an-environment-from-the-cli), CLI cloud sessions fall back to your first available cloud environment. Anything configured with the environment explicitly, such as a [routine](/docs/en/routines#environments-and-network-access), can't start new sessions in it; point it at another environment.
### Organization-shared environments
Owners and admins on Team and Enterprise plans can create cloud environments that are shared with every member of the organization; the same roles manage everything else on the **Cloud environments** admin page, including [self-hosted environments](/docs/en/self-hosted-environments). Shared environments appear in each member's environment selector alongside their personal ones, so a team can standardize on one configuration instead of each member recreating it.
Create, edit, and archive shared environments from the **Cloud environments** page in [admin settings](https://claude.ai/admin-settings). Each shared environment has a name, a [network access level](#access-levels), [environment variables](#set-environment-variables) in `.env` format, and a [setup script](#setup-scripts). Owners and admins choose the organization's [default environment](#the-default-environment) separately, at [claude.ai/admin-settings/claude-code](https://claude.ai/admin-settings/claude-code).
Values in a shared environment reach every member's sessions in that environment. Like personal environments, shared environments have no dedicated secrets store, so don't include secrets.
In [Claude Tag](https://claude.com/docs/claude-tag/overview) channels, Claude works as your organization's shared identity, not as any member, so channel sessions use shared environments only. You can set the environment a channel uses in two ways:
* Set a shared environment as the organization's [default environment](#the-default-environment) at [claude.ai/admin-settings/claude-code](https://claude.ai/admin-settings/claude-code).
* [Pin one to a channel](https://claude.com/docs/claude-tag/admins/troubleshooting#channel-sessions-use-the-wrong-environment-or-can%E2%80%99t-find-one) in the Claude Tag admin settings.
Shared environments add to members' selectors rather than replacing them.
## Network access
Each environment sets one network access level, which controls the outbound connections its sessions can make. The default level, **Trusted**, allows package registries and other [allowlisted domains](#default-allowed-domains); **Custom** takes your own domain list.
To change an environment's network access, [open it for editing](#configure-your-environment) and use the **Network access** selector in the dialog. The cloud icon that opens the selector appears on the app surfaces listed under [The Default environment](#the-default-environment) and in the [routine editor](/docs/en/routines#environments-and-network-access); personal environments don't have a separate page in your claude.ai account settings.
<Note>
MCP connectors you enable on a session or routine work without adding their hosts to **Allowed domains**, because connector traffic travels through Anthropic's servers rather than the session's network. You configure connectors per session or per routine; remove any you don't need to limit which tools Claude can reach. This relies on the same Anthropic-bound channel noted under [Security and isolation](/docs/en/claude-code-on-the-web#security-and-isolation).
</Note>
### Access levels
The **Network access** field in the [environment dialog](#configure-your-environment) takes one of four levels:
| Level | Outbound connections |
| :---------- | :------------------------------------------------------------------------------------------- |
| **None** | No outbound network access through the session's network |
| **Trusted** | [Allowlisted domains](#default-allowed-domains) only: package registries, GitHub, cloud SDKs |
| **Full** | Any domain |
| **Custom** | Your own allowlist, optionally including the defaults |
GitHub operations use a [separate proxy](#github-proxy) that is independent of this setting, and Claude Code's connection to the Anthropic API still works at **None**, as noted under [Security and isolation](/docs/en/claude-code-on-the-web#security-and-isolation).
### Allow specific domains
To allow domains that aren't in the Trusted list, select **Custom** in the environment's network access settings, then list one domain per line in the **Allowed domains** field. This example allows three hosts an internal project might need.
```text theme={null}
api.example.com
*.internal.example.com
registry.example.com
```
Sessions in this environment can now reach `api.example.com`, any subdomain of `internal.example.com`, and `registry.example.com`, and no other domains through the session's network; [GitHub traffic](#github-proxy) and [MCP connector traffic](#network-access) don't go through this allowlist. A leading `*.` matches every subdomain. To keep the [Trusted domains](#default-allowed-domains) too, check **Also include default list of common package managers**; leave it unchecked to allow only what you list.
Each environment has its own allowed-domains list; there's no organization-level allowlist that admins can push to every member's environments. [Server-managed settings](/docs/en/server-managed-settings) still apply inside cloud sessions, but none of them adds domains to the environment's network allowlist.
### GitHub proxy
In Anthropic-hosted environments, all GitHub operations go through a dedicated proxy that keeps your real GitHub credentials outside the session's VM, independent of the environment's [access level](#access-levels). Sessions in a self-hosted environment authenticate git operations with credentials your deployment provides; [Configure git](/docs/en/self-hosted-environments-deploy#configure-git) covers the options, including per-session minted credentials and an opt-in to this same proxy. The proxy provides:
* **Git credentials**: the git client inside the VM uses a scoped credential, which the proxy verifies and swaps for your actual GitHub token.
* **API requests**: requests from the built-in GitHub tools, and from `gh` under the [`proxy-injected` placeholder](#work-with-github-issues-and-pull-requests), go out with your real credentials substituted.
* **Push protection**: `git push` works only against the session's current working branch; cloning, fetching, and PR operations work normally.
* **Repository scope**: GitHub API and release-asset requests reach only repositories attached to the session, so a setup script that downloads release assets from an unattached repository gets a 403.
* **GraphQL restrictions**: the proxy serves only a pinned set of GraphQL operations for pull-request workflows. The proxy rejects everything else on the GraphQL endpoint with a 403 that says `This GraphQL query is not enabled for this session` and names the REST fallback, `gh api repos/{owner}/{repo}/...`. The restriction applies to every request through the proxy regardless of the credentials you supply, so a `GH_TOKEN` you set gets the same 403. Claude can't reach GitHub APIs that exist only in GraphQL, such as Projects v2, through the proxy.
Committed files from public repositories arrive through `raw.githubusercontent.com`, which the [security proxy](#security-proxy) handles instead. That domain is in the default [Trusted list](#default-allowed-domains), so those files stay reachable unless the environment's [access level](#access-levels) excludes it.
### Security proxy
Cloud sessions in Anthropic-hosted environments run behind an HTTP/HTTPS network proxy for security and abuse prevention purposes; in a [self-hosted environment](/docs/en/self-hosted-environments-deploy#default-deny-egress), outbound traffic leaves through your own network boundary instead. All outbound internet traffic from an Anthropic-hosted session passes through this proxy, which provides:
* Protection against malicious requests
* Rate limiting and abuse prevention
* Content filtering for enhanced security
* A DNS-level audit trail of requested hostnames
## What's available in cloud sessions
In Anthropic-hosted environments, each session gets a fresh virtual machine (VM) running Ubuntu 24.04 on x86\_64, regardless of your own operating system and CPU architecture, with your repository cloned and common toolchains pre-installed. When a dependency provides precompiled binaries, such as Ruby gems with native extensions or prebuilt Python wheels, use its x86\_64 Linux build to match the VM. This section covers the Anthropic-hosted defaults, the built-in GitHub tools, how to [run tests and services](#run-tests-start-services-and-add-packages), and the [resource limits](#resource-limits) each VM gets.
<Note>
Sessions your organization routes to a [self-hosted environment](/docs/en/self-hosted-environments) run on your own runners instead, with the tools your runner image provides.
</Note>
### What carries over from your setup
Cloud sessions start from a fresh clone of your repository. Anything you commit to the repo is available. Anything you've installed or configured only on your own machine isn't available in the session. Your organization's policy arrives separately through [server-managed settings](/docs/en/server-managed-settings).
| | Available in cloud sessions | Why |
| :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :-------------------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Your repo's `CLAUDE.md` | Yes | Part of the clone |
| Your repo's `.claude/settings.json` hooks | Yes | Part of the clone |
| Your repo's `.mcp.json` MCP servers | Yes | Part of the clone |
| Your repo's `.claude/rules/` | Yes | Part of the clone |
| Your repo's `.claude/skills/`, `.claude/agents/`, `.claude/commands/` | Yes | Part of the clone |
| Plugins declared in `.claude/settings.json` | Yes | Installed at session start from the [marketplace](/docs/en/plugin-marketplaces) you declared. Requires network access to reach the marketplace source |
| Your organization's [server-managed settings](/docs/en/server-managed-settings) | Yes | Fetched from Anthropic's servers when the session starts. See [Surface coverage](/docs/en/model-config#surface-coverage) for how `availableModels` is enforced in cloud sessions. Settings deployed to your device through MDM or managed settings files don't apply, because the session runs on an Anthropic-managed VM; in a [self-hosted environment](/docs/en/self-hosted-environments), sessions read the managed settings file in the runner image only when server-managed settings deliver no keys, per [settings precedence](/docs/en/server-managed-settings#settings-precedence) |
| Your user `~/.claude/CLAUDE.md` | No | Lives on your machine, not in the repo |
| Your user `~/.claude/skills/`, `~/.claude/agents/`, `~/.claude/commands/` | No | Live on your machine, not in the repo. Commit them to the repo's `.claude/` directory instead. Cloud sessions automatically load skills you enable on claude.ai |
| Plugins enabled only in your user settings | No | User-scoped `enabledPlugins` lives in `~/.claude/settings.json`. Declare them in the repo's `.claude/settings.json` instead |
| MCP servers you added with `claude mcp add` at the default local scope or the user scope | No | Those write to `~/.claude.json` on your machine, not the repo. Add the server with `claude mcp add --scope project`, which writes the repo's [`.mcp.json`](/docs/en/mcp#project-scope), and commit that file |
| Transport variables in your repo's `.claude/settings.json` `env` block, such as `NODE_EXTRA_CA_CERTS` and the [mTLS client certificate variables](/docs/en/network-config#mtls-authentication) | No | The hosting environment manages the session's API connection, so Claude Code ignores these keys and notes each ignored key in the session's debug log |
| Static API tokens and credentials | No | No dedicated secrets store exists yet. See below |
| Interactive auth like AWS SSO | No | Not supported. SSO requires browser-based login that can't run in a cloud session |
To make your own configuration available in cloud sessions, commit it to the repo.
A dedicated secrets store is not yet available, and the dialog warns against adding secrets or credentials: environment variables and setup scripts live in the environment configuration, where anyone who uses the environment can read them. If a session needs a credential anyway, add it with that visibility in mind.
### Installed tools
Cloud sessions come with common language runtimes, build tools, and databases pre-installed. The table below summarizes what's included by category.
| Category | Included |
| :------------ | :------------------------------------------------------------------------- |
| **Python** | Python 3.x with pip, poetry, uv, black, mypy, pytest, ruff |
| **Node.js** | 20, 21, and 22, with npm, yarn, pnpm, bun¹, eslint, prettier, chromedriver |
| **Ruby** | 3.1, 3.2, 3.3 with gem, bundler, rbenv |
| **PHP** | 8.4 with Composer |
| **Java** | OpenJDK 21 with Maven and Gradle |
| **Go** | latest stable with module support |
| **Rust** | rustc and cargo |
| **C/C++** | GCC, Clang, cmake, ninja, conan |
| **Docker** | docker, dockerd, docker compose |
| **Databases** | PostgreSQL 16, Redis 7.0 |
| **Utilities** | git, jq, yq, ripgrep, tmux, vim, nano |
¹ Bun is installed but has known [proxy compatibility issues](#install-dependencies-with-a-sessionstart-hook) for package fetching.
For exact versions, ask Claude to run `check-tools` in a cloud session. It's a shell command installed on the session VM, not a slash command; you ask Claude because [Claude runs all VM commands for you](#run-tests-start-services-and-add-packages).
Node.js versions are installed at `/opt/node20`, `/opt/node21`, and `/opt/node22`, with 22 on `PATH` by default. To work with a different version, ask Claude to prepend that version's `bin` directory, such as `/opt/node20/bin`, to `PATH`.
Toolchains outside this list, such as the .NET SDK, aren't pre-installed even when their package registries are on the [default allowlist](#default-allowed-domains). Install them with a [setup script](#setup-scripts).
### Work with GitHub issues and pull requests
Cloud sessions include built-in GitHub tools that let Claude read issues, list pull requests, fetch diffs, and post comments without any setup. These tools authenticate through the [GitHub proxy](#github-proxy) using whichever method you configured under [GitHub authentication options](/docs/en/claude-code-on-the-web#github-authentication-options), so your token never enters the container.
You can set `GH_TOKEN` or `GITHUB_TOKEN` yourself in [environment settings](#set-environment-variables), or leave both unset and let the [GitHub proxy](#github-proxy) authenticate for you:
* If you set a token, it passes through to the container unchanged, so your scripts, and GitHub's [`gh` CLI](https://cli.github.com) if you install it, use it directly.
* If you set neither and the [GitHub proxy](#github-proxy) is handling authentication for your session, both variables read as the placeholder string `proxy-injected` in the commands Claude runs, and the proxy substitutes your real credentials on outbound GitHub requests. `gh` works without a token of your own, but a script that reads `GITHUB_TOKEN` directly gets the placeholder, not a usable token.
A token you set is an ordinary environment variable, so anyone who uses the environment can read it; the proxy path keeps the credential out of the environment configuration and the session VM.
To check which case applies to your session, ask Claude to run `echo $GH_TOKEN`.
GitHub's [`gh` CLI](https://cli.github.com) isn't pre-installed. If you need a `gh` command the built-in tools don't cover, like `gh release` or `gh workflow run`, install and authenticate it yourself:
<Steps>
<Step title="Install gh in your setup script">
Add `apt update && apt install -y gh` to your [setup script](#setup-scripts).
</Step>
<Step title="Provide a token if the proxy is not handling authentication">
If `echo $GH_TOKEN` prints `proxy-injected`, the [GitHub proxy](#github-proxy) authenticates `gh` for you and this step is unnecessary. Otherwise, add a `GH_TOKEN` environment variable to your [environment settings](#set-environment-variables) with a GitHub personal access token; like any environment variable, it's readable by anyone who uses the environment, so scope the token narrowly. `gh` reads `GH_TOKEN` automatically, so you don't need to run `gh auth login`.
</Step>
</Steps>
### Link output back to the session
Each cloud session has a transcript URL on claude.ai, and the session can read its own ID from the `CLAUDE_CODE_REMOTE_SESSION_ID` environment variable. Use this to put a traceable link in PR bodies, commit messages, Slack posts, or generated reports so a reviewer can open the run that produced them.
Commits that Claude creates in a cloud session include a `Claude-Session: <url>` git trailer, and PR bodies include the session URL on its own line. This requires v2.1.179 or later. To omit the trailer and the PR-body link, set [`attribution.sessionUrl`](/docs/en/settings#attribution-settings) to `false`. The setting requires v2.1.182 or later.
To include the session link in something other than a commit or PR, such as a Slack message Claude posts or a report file it writes, have Claude run the following command and use its output. The command converts the `cse_` prefix in the environment variable's value to the `session_` prefix that the transcript URL expects:
```bash theme={null}
echo "https://claude.ai/code/${CLAUDE_CODE_REMOTE_SESSION_ID/#cse_/session_}"
```
### Run tests, start services, and add packages
You don't get a shell into the session VM. Claude runs every command for you, so phrase the tasks in this section as requests in your prompt.
#### Run tests
Claude runs tests as part of working on a task. Ask for it in your prompt, like "fix the failing tests in `tests/`" or "run pytest after each change." Test runners that come with the [pre-installed toolchains](#installed-tools), like pytest and cargo test, work without additional setup. A runner your project declares as a dependency, like jest, installs with your dependencies.
#### Start services
PostgreSQL and Redis are pre-installed but not running by default. Ask Claude to start whichever you need; the commands it runs are:
```bash theme={null}
service postgresql start
```
```bash theme={null}
service redis-server start
```
Docker is available for running containerized services. Ask Claude to run `docker compose up` to start your project's services. Network access to pull images follows your environment's [access level](#access-levels), and the [Trusted defaults](#default-allowed-domains) include Docker Hub and other common registries.
If your images are large or slow to pull, add `docker compose pull` or `docker compose build` to your [setup script](#setup-scripts). The [environment cache](#environment-caching) keeps the pulled images, so each new session has them on disk. The cache stores files only, not running processes, so Claude still starts the containers each session.
#### Add packages
To add packages that aren't pre-installed, use a [setup script](#setup-scripts). The [environment cache](#environment-caching) keeps what the script installs, so packages you install there are available at the start of every session without reinstalling each time. You can also ask Claude to install packages mid-session, but those installs don't carry over to other sessions.
### Resource limits
Cloud sessions in Anthropic-hosted environments run with approximate resource ceilings that may change over time:
* 4 vCPUs
* 16 GB of RAM
* 30 GB of disk
The VM may stop tasks that need significantly more memory, such as large build jobs or memory-intensive tests. For workloads beyond these limits, use [Remote Control](/docs/en/remote-control) to run Claude Code on your own hardware, or run cloud sessions in a [self-hosted environment](/docs/en/self-hosted-environments) on compute your organization operates.
## Setup scripts
A setup script is a Bash script that runs when a new cloud session starts, before Claude Code launches. Use setup scripts to install dependencies, configure tools, or fetch anything the session needs that isn't pre-installed.
Scripts run as root on Ubuntu 24.04, so `apt install` and most language package managers work.
Cut at 300 lines. The page has the rest.
code-review First recorded · 381 lines, first recorded
# Code Review ## How reviews work ### Severity levels ### Rate and reply to findings ### Check run output ### What Code Review checks ## Set up Code Review ## Manually trigger reviews ## Customize reviews ### CLAUDE.md ### REVIEW\.md #### What you can tune #### Example #### Keep it focused ## View usage ## Pricing ## Troubleshooting ### Retrigger a failed or timed-out review ### Review didn't run and the PR shows a spend-cap message ### Find issues that aren't showing as inline comments ## Review a diff locally ### What the review reads and edits ### Tune effort and arguments ### Run in the foreground ### Let Claude start the review ### Escalate to ultrareview ## Related resources
The first capture of this source. The page was already there, and this is what it said.
# Code Review
> Set up automated PR reviews that catch logic errors, security vulnerabilities, and regressions using multi-agent analysis of your full codebase
<Note>
Code Review is in research preview, available for [Team and Enterprise](https://claude.ai/admin-settings/claude-code) subscriptions. It is not available for organizations with [Zero Data Retention](/docs/en/zero-data-retention) enabled. On other plans, you can still [review a diff locally](#review-a-diff-locally) with the `/code-review` command.
</Note>
Code Review analyzes your GitHub pull requests and posts findings as inline comments on the lines of code where it found issues. A fleet of specialized agents examine the code changes in the context of your full codebase, looking for logic errors, security vulnerabilities, broken edge cases, and subtle regressions.
Findings are tagged by severity and don't approve or block your PR, so existing review workflows stay intact. You can tune what Claude flags by adding a `CLAUDE.md` or `REVIEW.md` file to your repository.
To run Claude in your own CI infrastructure instead of this managed service, see [GitHub Actions](/docs/en/github-actions) or [GitLab CI/CD](/docs/en/gitlab-ci-cd). For repositories on a self-hosted GitHub instance, see [GitHub Enterprise Server](/docs/en/github-enterprise-server).
This page covers:
* [How reviews work](#how-reviews-work)
* [Setup](#set-up-code-review)
* [Triggering reviews manually](#manually-trigger-reviews) with `@claude review` and `@claude review always`
* [Customizing reviews](#customize-reviews) with `CLAUDE.md` and `REVIEW.md`
* [Pricing](#pricing)
* [Troubleshooting](#troubleshooting) failed runs and missing comments
* [Reviewing a diff locally](#review-a-diff-locally) with the `/code-review` command
## How reviews work
Once an Owner [enables Code Review](#set-up-code-review) for your organization, reviews trigger when a PR opens, on every push, or when manually requested, depending on the repository's configured behavior. Commenting `@claude review` [starts a review on a PR](#manually-trigger-reviews) in any mode.
When a review runs, multiple agents analyze the diff and surrounding code in parallel on Anthropic infrastructure. Each agent looks for a different class of issue, then a verification step checks candidates against actual code behavior to filter out false positives. The results are deduplicated, ranked by severity, and posted as inline comments on the specific lines where issues were found, with a summary in the review body. If no issues are found, Code Review updates the GitHub check run to show that no issues were detected. Claude may also post a short confirmation comment on the PR.
Reviews scale in cost with PR size and complexity, completing in 20 minutes on average. Owners can monitor review activity and spend via the [analytics dashboard](#view-usage).
### Severity levels
Each finding is tagged with a severity level:
| Marker | Severity | Meaning |
| :----- | :----------- | :------------------------------------------------------------------ |
| 🔴 | Important | A bug that should be fixed before merging |
| 🟡 | Nit | A minor issue, worth fixing but not blocking |
| 🟣 | Pre-existing | A bug that exists in the codebase but was not introduced by this PR |
Findings include a collapsible extended reasoning section you can expand to understand why Claude flagged the issue and how it verified the problem.
### Rate and reply to findings
Each review comment from Claude arrives with 👍 and 👎 already attached so both buttons appear in the GitHub UI for one-click rating. Click 👍 if the finding was useful or 👎 if it was wrong or noisy. Anthropic collects reaction counts after the PR merges and uses them to tune the reviewer. Reactions do not trigger a re-review or change anything on the PR.
Replying to an inline comment does not prompt Claude to respond or update the PR. To act on a finding, fix the code and push. If the PR is subscribed to push-triggered reviews, the next run resolves the thread when the issue is fixed. To request a fresh review without pushing, comment `@claude review` as a [top-level PR comment](#manually-trigger-reviews).
### Check run output
Beyond the inline review comments, each review populates the **Claude Code Review** check run that appears alongside your CI checks. Expand its **Details** link to see a summary of every finding in one place, sorted by severity:
| Severity | File:Line | Issue |
| ------------ | ------------------------- | -------------------------------------------------------------- |
| 🔴 Important | `src/auth/session.ts:142` | Token refresh races with logout, leaving stale sessions active |
| 🟡 Nit | `src/auth/session.ts:88` | `parseExpiry` silently returns 0 on malformed input |
Each finding also appears as an annotation in the **Files changed** tab, marked directly on the relevant diff lines. Important findings render with a red marker, nits with a yellow warning, and pre-existing bugs with a gray notice. Annotations and the severity table are written to the check run independently of inline review comments, so they remain available even if GitHub rejects an inline comment on a line that moved.
The check run always completes with a neutral conclusion so it never blocks merging through branch protection rules. If you want to gate merges on Code Review findings, read the severity breakdown from the check run output in your own CI. The last line of the Details text is a machine-readable comment your workflow can parse with `gh` and jq. To find the check run ID, list the commit's check runs with `gh api repos/OWNER/REPO/commits/<commit-sha>/check-runs --jq '.check_runs[] | {id, name}'` and take the `id` of the `Claude Code Review` run. Replace `OWNER`, `REPO`, and `CHECK_RUN_ID` with your repository owner, repository name, and that ID:
```bash theme={null}
gh api repos/OWNER/REPO/check-runs/CHECK_RUN_ID \
--jq '.output.text | split("bughunter-severity: ")[1] | split(" -->")[0] | fromjson'
```
This returns a JSON object with counts per severity, for example `{"normal": 2, "nit": 1, "pre_existing": 0}`. The `normal` key holds the count of Important findings; a non-zero value means Claude found at least one bug worth fixing before merge.
### What Code Review checks
By default, Code Review focuses on correctness: bugs that would break production, not formatting preferences or missing test coverage. You can expand what it checks by [adding guidance files](#customize-reviews) to your repository.
## Set up Code Review
An Owner enables Code Review once for the organization and selects which repositories to include.
<Steps>
<Step title="Open Claude Code admin settings">
Go to [claude.ai/admin-settings/claude-code](https://claude.ai/admin-settings/claude-code) and find the Code Review section. You need the Owner or Primary Owner role in your Claude organization and permission to install GitHub Apps in your GitHub organization.
</Step>
<Step title="Start setup">
Click **Setup**. This begins the GitHub App installation flow.
</Step>
<Step title="Install the Claude GitHub App">
Follow the prompts to install the Claude GitHub App: pick the GitHub organization that owns the repositories you want reviewed, choose which repositories the app can access, and approve the requested permissions.
To review a pull request, Claude reads your repository contents through the app's read access, and posts comments and the [check run](#check-run-output) through its write access to pull requests and checks. During installation, you grant a broader permission set shared by other Claude features, such as [GitHub Actions](/docs/en/github-actions); see [GitHub App permissions](/docs/en/github-actions#github-app-permissions) for the full list.
</Step>
<Step title="Select repositories">
Choose which repositories to enable for Code Review. If you don't see a repository, make sure you gave the Claude GitHub App access to it during installation. You can add more repositories later.
</Step>
<Step title="Set review triggers per repo">
After setup completes, the Code Review section shows your repositories in a table. For each repository, use the **Review Behavior** dropdown to choose when reviews run:
* **Once after PR creation**: review runs once when a PR is opened or marked ready for review
* **After every push**: review runs on every push to the PR branch, catching new issues as the PR evolves and auto-resolving threads when you fix flagged issues
* **Manual**: reviews start only when someone [comments `@claude review` on a PR](#manually-trigger-reviews); `@claude review always` starts a review and subscribes the PR to reviews on subsequent pushes
Reviewing on every push runs the most reviews and costs the most. Manual mode is useful for high-traffic repos where you want to opt specific PRs into review, or to only start reviewing your PRs once they're ready.
</Step>
</Steps>
The repositories table also shows the average cost per review for each repo based on recent activity. Use the row actions menu to turn Code Review on or off per repository, or to remove a repository entirely.
To verify setup, open a test PR. If you chose an automatic trigger, a check run named **Claude Code Review** appears within a few minutes. If you chose Manual, comment `@claude review` on the PR to start the first review. If no check run appears, confirm the repository is listed in your admin settings and the Claude GitHub App has access to it.
## Manually trigger reviews
Comment commands start a review on demand. They work regardless of the repository's configured trigger, so you can use them to opt specific PRs into review in Manual mode or to get an immediate re-review in other modes.
| Command | What it does |
| :---------------------- | :---------------------------------------------------------------------------- |
| `@claude review` | Starts a single review without subscribing the PR to future pushes |
| `@claude review always` | Starts a review and subscribes the PR to push-triggered reviews going forward |
| `@claude review once` | Same as `@claude review`: starts a single review without subscribing |
Use `@claude review always` when you want every subsequent push to the PR to start a fresh review, such as on a high-priority PR in a repository set to Manual mode. Because the bare command doesn't subscribe the PR, you can request a one-off second opinion without changing whether later pushes trigger reviews.
<Note>
Before a July 2026 update, `@claude review` subscribed the PR to push-triggered reviews. If you relied on that behavior, comment `@claude review always` instead. `@claude review once` still works and behaves the same as the bare command.
</Note>
For any of these commands to trigger a review:
* Post it as a top-level PR comment, not an inline comment on a diff line
* Put the command at the start of the comment, with `once` or `always` on the same line as the rest of the command
* You must have owner, member, or collaborator access to the repository
* The PR must be open
Unlike automatic triggers, manual triggers run on draft PRs, since an explicit request signals you want the review now regardless of draft status.
If a review is already running on that PR, the request is queued until the in-progress review completes. You can monitor progress via the check run on the PR.
## Customize reviews
Code Review reads two files from your repository to guide what it flags. They differ in how strongly they influence the review:
* **`CLAUDE.md`**: shared project instructions that Claude Code uses for all tasks, not just reviews. Code Review reads it as project context and flags newly introduced violations as nits.
* **`REVIEW.md`**: review-only instructions, injected directly into every agent in the review pipeline as highest priority. Use it to change what gets flagged, at what severity, and how findings are reported.
### CLAUDE.md
Code Review reads your repository's `CLAUDE.md` files and treats newly introduced violations as [nit-level](#severity-levels) findings. This works bidirectionally: if your PR changes code in a way that makes a `CLAUDE.md` statement outdated, Claude flags that the docs need updating too.
Claude reads `CLAUDE.md` files at every level of your directory hierarchy, so rules in a subdirectory's `CLAUDE.md` apply only to files under that path. See the [memory documentation](/docs/en/memory) for more on how `CLAUDE.md` works.
For review-specific guidance that you don't want applied to general Claude Code sessions, use [`REVIEW.md`](#review-md) instead.
### REVIEW\.md
`REVIEW.md` is a file at your repository root that overrides how Code Review behaves on your repo. Its contents are injected into the system prompt of every agent in the review pipeline as the highest-priority instruction block, taking precedence over the default review guidance.
Because it's pasted verbatim, `REVIEW.md` is plain instructions: [`@` import syntax](/docs/en/memory#import-additional-files) is not expanded, and referenced files are not read into the prompt. Put the rules you want enforced directly in the file.
#### What you can tune
`REVIEW.md` is freeform markdown, so anything you can express as a review instruction is in scope. The patterns below have the most impact in practice.
**Severity**: redefine what 🔴 Important means for your repo. The default calibration targets production code; a docs repo, a config repo, or a prototype might want a much narrower definition. State explicitly which classes of finding are Important and which are Nit at most. You can also escalate in the other direction, for example treating any `CLAUDE.md` violation as Important rather than the default nit.
**Nit volume**: cap how many 🟡 Nit comments a single review posts. Prose and config files can be polished forever. A cap like "report at most five nits, mention the rest as a count in the summary" keeps reviews actionable.
**Skip rules**: list paths, branch patterns, and finding categories where Claude should post no findings. Common candidates are generated code, lockfiles, vendored dependencies, and machine-authored branches, along with anything your CI already enforces like linting or spellcheck. For paths that warrant some review but not full scrutiny, set a higher bar instead of skipping entirely: "in `scripts/`, only report if near-certain and severe."
**Repo-specific checks**: add rules you want flagged on every PR, like "new API routes must have an integration test." Because `REVIEW.md` is injected as highest priority, these land more reliably than the same rules in a long `CLAUDE.md`.
**Verification bar**: require evidence before a class of finding is posted. For example, "behavior claims need a `file:line` citation in the source, not an inference from naming" cuts false positives that would otherwise cost the author a round trip.
**Re-review convergence**: tell Claude how to behave when a PR has already been reviewed. A rule like "after the first review, suppress new nits and post Important findings only" stops a one-line fix from reaching round seven on style alone.
**Summary shape**: ask for the review body to open with a one-line tally such as `2 factual, 4 style`, and to lead with "no factual issues" when that's the case. The author wants to know the shape of the work before the details.
#### Example
This `REVIEW.md` recalibrates severity for a backend service, caps nits, skips generated files, and adds repo-specific checks.
```markdown theme={null}
# Review instructions
## What Important means here
Reserve Important for findings that would break behavior, leak data,
or block a rollback: incorrect logic, unscoped database queries, PII
in logs or error messages, and migrations that aren't backward
compatible. Style, naming, and refactoring suggestions are Nit at
most.
## Cap the nits
Report at most five Nits per review. If you found more, say "plus N
similar items" in the summary instead of posting them inline. If
everything you found is a Nit, lead the summary with "No blocking
issues."
## Do not report
- Anything CI already enforces: lint, formatting, type errors
- Generated files under `src/gen/` and any `*.lock` file
- Test-only code that intentionally violates production rules
## Always check
- New API routes have an integration test
- Log lines don't include email addresses, user IDs, or request bodies
- Database queries are scoped to the caller's tenant
```
#### Keep it focused
Length has a cost: a long `REVIEW.md` dilutes the rules that matter most. Keep it to instructions that change review behavior, and leave general project context in `CLAUDE.md`.
## View usage
Go to [claude.ai/analytics/code-review](https://claude.ai/analytics/code-review) to see Code Review activity across your organization. The dashboard shows:
| Section | What it shows |
| :------------------- | :--------------------------------------------------------------------------------------- |
| PRs reviewed | Daily count of pull requests reviewed over the selected time range |
| Cost weekly | Weekly spend on Code Review |
| Feedback | Count of review comments that were auto-resolved because a developer addressed the issue |
| Repository breakdown | Per-repo counts of PRs reviewed and comments resolved |
Dashboard cost figures are estimates for monitoring activity. For invoice-accurate spend, refer to your Anthropic bill.
## Pricing
Code Review is billed based on token usage. Each review averages \$15-25 in cost, scaling with PR size, codebase complexity, and how many issues require verification. Code Review usage is billed separately through [usage credits](https://support.claude.com/en/articles/12429409-extra-usage-for-paid-claude-plans) and does not count against your plan's included usage.
The review trigger you choose affects total cost:
* **Once after PR creation**: runs once per PR
* **After every push**: runs on each push, multiplying cost by the number of pushes
* **Manual**: no reviews until someone comments `@claude review` on a PR
In Once after PR creation or Manual mode, commenting `@claude review always` [opts the PR into push-triggered reviews](#manually-trigger-reviews), so additional cost accrues per push after that comment. In After every push mode, pushes already trigger reviews, so the subscription doesn't change per-push cost. Commenting `@claude review` runs a single review without subscribing to future pushes.
Costs appear on your Anthropic bill regardless of whether your organization uses Amazon Bedrock or Google Cloud's Agent Platform for other Claude Code features. To set a monthly spend cap for Code Review, go to [claude.ai/admin-settings/usage](https://claude.ai/admin-settings/usage) and configure the limit for the Claude Code Review service.
Monitor spend via the weekly cost chart in [analytics](#view-usage) or the per-repo average cost column in admin settings.
## Troubleshooting
Review runs are best-effort. A failed run never blocks your PR, but it also doesn't retry on its own. This section covers how to recover from a failed run and where to look when the check run reports issues you can't find.
### Retrigger a failed or timed-out review
When the review infrastructure hits an internal error or exceeds its time limit, the check run completes with a title of **Code review encountered an error** or **Code review timed out**. The conclusion is still neutral, so nothing blocks your merge, but no findings are posted.
To run the review again, comment `@claude review` on the PR. This starts a fresh review without subscribing the PR to future pushes. If the PR is already subscribed to push-triggered reviews, pushing a new commit also starts a new review.
The **Re-run** button in GitHub's Checks tab does not retrigger Code Review. Use the comment command or a new push instead.
### Review didn't run and the PR shows a spend-cap message
When your organization's monthly spend cap is reached, Code Review posts a single comment on the PR explaining that the review was skipped. Reviews resume automatically at the start of the next billing period, or immediately when an admin raises the cap at [claude.ai/admin-settings/usage](https://claude.ai/admin-settings/usage).
### Find issues that aren't showing as inline comments
If the check run title says issues were found but you don't see inline review comments on the diff, look in these other locations where findings are surfaced:
* **Check run Details**: click **Details** next to the Claude Code Review check in the Checks tab. The severity table lists every finding with its file, line, and summary regardless of whether the inline comment was accepted.
* **Files changed annotations**: open the **Files changed** tab on the PR. Findings render as annotations attached directly to the diff lines, separate from review comments.
* **Review body**: if you pushed to the PR while a review was running, some findings may reference lines that no longer exist in the current diff. Those appear under an **Additional findings** heading in the review body text rather than as inline comments.
## Review a diff locally
The [`/code-review` command](/docs/en/commands) reviews a diff in your terminal without installing the GitHub App. It reports correctness bugs and reuse, simplification, and efficiency cleanups.
`/review` is an alias of `/code-review`; before v2.1.223, it was a separate command that ran a single-pass, read-only review of a GitHub pull request.
<Steps>
<Step title="Run /code-review">
From the session where you're working, run the command:
```text theme={null}
/code-review
```
It reviews your branch's commits ahead of its upstream plus any uncommitted changes, so it needs work on the branch or in the working tree to have something to report. To review something else, pass a target: a file path, a PR number, a branch name, or a ref range such as `main...my-feature`.
You can also add flags:
* `--fix`: applies the findings to your working tree after the review
* `--comment`: posts the findings as inline PR comments
* `--post`: on an `ultra` cloud review of a `github.com` pull request, preselects posting the finished findings to the PR in the launch dialog; see [Post findings to the pull request](/docs/en/ultrareview#post-findings-to-the-pull-request). Requires Claude Code v2.1.227 or later
</Step>
<Step title="Keep working">
The review runs as a background [subagent](/docs/en/sub-agents) with its own context window, so it doesn't fill your conversation. The findings arrive in your conversation when the review completes.
</Step>
<Step title="Act on the findings">
Ask Claude to fix what the review found. If you passed `--fix` or `--comment`, the review has already applied or posted its findings.
</Step>
Cut at 300 lines. The page has the rest.
commands First recorded · 163 lines, first recorded
# Commands ## Commands across a typical workflow ## All commands ## MCP prompts ## See also
The first capture of this source. The page was already there, and this is what it said.
# Commands > Complete reference for commands available in Claude Code, including built-in commands and bundled skills. Commands control Claude Code from inside a session. They provide a quick way to switch models, manage permissions, clear context, run a workflow, and more. Type `/` to see the commands available to you, or type `/` followed by letters to filter. A command is only recognized at the start of your message. Text that follows the command name becomes its arguments. As of v2.1.199, [skills](/docs/en/skills#pass-arguments-to-skills) are the exception: a skill invocation followed by more skills, such as `/skill-a /skill-b do XYZ`, loads every skill named at the start and passes the trailing text to each as arguments. Up to six skills can be chained. If you send a command while Claude is responding, it queues and runs after the current turn finishes. Some commands, such as `/status`, `/tasks`, and `/usage`, run immediately without interrupting the response. ## Commands across a typical workflow Most commands are useful at a specific point in a session, from setting up a project to shipping a change. **First session in a repo.** Run `/init` to generate a starter `CLAUDE.md`, then `/memory` to refine it. Use `/mcp` to set up any servers the project needs, ask Claude to create any [subagents](/docs/en/sub-agents) you want, and run `/permissions` to set your approval rules. **During a task.** `/plan` switches into plan mode before a large change. `/model` and `/effort` adjust which model you're using and how much reasoning it applies. When the conversation gets long, `/context` shows what's filling the window and `/compact` summarizes it to free space. Use `/btw` for a side question that shouldn't add to the conversation history. **Run work in parallel.** Claude delegates side tasks to [subagents](/docs/en/sub-agents), and `/tasks` lists the current session's background work, including subagents that have finished. `/background` detaches the whole session to keep running as a [background agent](/docs/en/agent-view) and frees your terminal. For a large change that spans the codebase, `/batch` decomposes it into independent units and runs each in its own [worktree](/docs/en/worktrees). See [Run agents in parallel](/docs/en/agents) for how these approaches relate. **Before you ship.** `/diff` shows what changed. `/code-review` checks the current diff for correctness bugs and cleanups and can apply the findings with `--fix`; pass a PR number, such as `/code-review high 1234`, to review a pull request instead. `/review` is an alias. `/code-review ultra` runs a multi-agent review in the cloud. `/security-review` checks the diff for security vulnerabilities. **Between sessions.** `/clear` starts fresh on a new task while keeping project memory. `/resume` returns to an earlier conversation, `/branch` branches the current one to try a different direction, and `/fork` copies it into a new [background session](/docs/en/agent-view). `/teleport` pulls a web session into this terminal, and `/remote-control` lets you continue this local session from another device. **When something is wrong.** `/rewind` rolls code and conversation back to a checkpoint, or summarizes part of the conversation. `/doctor` runs a setup checkup that diagnoses installation and configuration issues and can fix them, `/debug` diagnoses runtime issues, and `/feedback` reports a bug with session context attached. ## All commands The table below lists all the commands included in Claude Code. Most are built-in commands whose behavior is coded into the CLI. Two kinds of entries are marked: * **[Skill](/docs/en/skills#bundled-skills)**: a bundled skill. It works like skills you write yourself: a prompt handed to Claude, which Claude can also invoke automatically when relevant. * `/verify` runs only when you invoke it. Before v2.1.215, Claude could also run `/verify` on its own. * **[Workflow](/docs/en/workflows#bundled-workflows)**: a bundled [dynamic workflow](/docs/en/workflows) that fans work out across many subagents and runs in the background. * `/deep-research` runs only when you invoke it. Before v2.1.218, Claude could also start it on its own. To add your own commands, see [skills](/docs/en/skills). In the table below, `<arg>` indicates a required argument and `[arg]` indicates an optional one. <Note> Not every command appears for every user. Availability depends on your platform, plan, and environment. For example, `/desktop` only shows on macOS and x64 Windows when signed in with a Claude subscription, and `/upgrade` doesn't show on Enterprise plans. </Note> | Command | Purpose | | :-------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `/add-dir <path>` | Add a working directory for file access during the current session. Type a partial path to see matching directory suggestions; press `Tab` to accept one. Most `.claude/` configuration is [not discovered](/docs/en/permissions#additional-directories-grant-file-access-not-configuration) from the added directory. A successful add runs your [`DirectoryAdded` hooks](/docs/en/hooks#directoryadded) | | `/advisor [model\|off]` | Enable or disable the [advisor tool](/docs/en/advisor), which consults a second model for guidance at key moments during a task. Accepts `fable`, `opus`, `sonnet`, or a full model ID. `fable` requires [Fable 5 access](/docs/en/advisor#choose-an-advisor-model). Without an argument, opens a picker | | `/agents` | As of v2.1.198, running `/agents` prints a reminder to ask Claude to create or manage [subagents](/docs/en/sub-agents), or to edit `.claude/agents/` or `~/.claude/agents/` directly. On v2.1.197 and earlier, opens an interactive interface for creating and managing subagent configurations | | `/autocompact [auto\|<tokens>]` | Set the auto-compact window: how full the context window gets before Claude Code compacts automatically. Pass a size such as `500k`, or `auto` to return to the window tuned for your model. Claude Code saves the value to user settings and applies it to the current session. See [Set the auto-compact window](/docs/en/model-config#set-the-auto-compact-window) for accepted values and what overrides it. Without an argument, opens a dialog that shows the current window. Requires Claude Code v2.1.221 or later | | `/autofix-pr [prompt]` | Spawn a [Claude Code on the web](/docs/en/claude-code-on-the-web#auto-fix-pull-requests) session that watches the current branch's PR and pushes fixes when CI fails or reviewers leave comments. Detects the open PR from your checked-out branch with `gh pr view`; to watch a different PR, check out its branch first. By default the cloud session is told to fix every CI failure and review comment; pass a prompt to give it different instructions, for example `/autofix-pr only fix lint and type errors`. Requires the `gh` CLI and access to [Claude Code on the web](/docs/en/claude-code-on-the-web) | | `/background [prompt]` | Detach the current session to run as a [background agent](/docs/en/agent-view) and free this terminal. Pass a prompt to send one more instruction before detaching. Monitor the session with `claude agents`. To copy the conversation into a new background session while this one keeps running, use `/fork`. Alias: `/bg` | | `/batch <instruction>` | **[Skill](/docs/en/skills#bundled-skills).** Orchestrate large-scale changes across a codebase in parallel. Researches the codebase, decomposes the work into 5 to 30 independent units, and presents a plan. Once approved, spawns one [background subagent](/docs/en/sub-agents#run-subagents-in-foreground-or-background) per unit in an isolated [git worktree](/docs/en/worktrees). Each subagent implements its unit, runs tests, and opens a pull request. Requires a git repository. Example: `/batch migrate src/ from Solid to React` | | `/branch [name]` | Create a branch of the current conversation at this point, so you can try a different direction without losing the conversation as it stands. Switches you into the branch and preserves the original, which you can return to with `/resume`. To run a copy as a separate [background session](/docs/en/agent-view) instead of switching into it, use `/fork`; to hand a side task to a [subagent](/docs/en/sub-agents) that reports back into this conversation, use `/subtask` | | `/btw [question]` | Ask a [side question](/docs/en/interactive-mode#side-questions-with-%2Fbtw) about the current session without adding to the conversation. If you run `/btw` without a question, Claude Code shows your most recent side question so you can browse earlier answers; if you haven't asked one yet, Claude Code prints a usage line. Before v2.1.212, `/btw` required a question | | `/bug [report]` | Report a bug or share your conversation. You choose how much session history to include and confirm on a consent screen before anything is sent. When you're signed in to Anthropic on a first-party connection, the report goes to Anthropic; on a third-party provider, or without Anthropic credentials, Claude Code writes the report to a [local archive under `~/.claude/feedback-bundles/`](/docs/en/data-usage#telemetry-services) that you forward yourself. In the [VS Code extension](/docs/en/vs-code#use-the-prompt-box), `/bug` opens the extension's own feedback dialog instead; requires Claude Code v2.1.229 or later. Alias: `/share`. Before v2.1.212, `/bug` and `/share` were aliases of `/feedback` | | `/cd <path>` | Move this session to a new working directory, keeping the conversation and its prompt cache. Type a partial path to see matching directory suggestions; press `Tab` to accept one. Claude Code prompts you to [trust the workspace](/docs/en/permissions#project-allow-rules-and-workspace-trust) if you haven't worked in it before, and `--resume` [finds the moved session](/docs/en/sessions#resume-a-session) afterward. To grant access to an extra directory without moving the session, use `/add-dir`. Restrict or disable `/cd` targets with [`Cd` permission rules](/docs/en/permissions#cd). Requires Claude Code v2.1.169 or later | | `/chrome` | Configure [Claude in Chrome](/docs/en/chrome) settings | | `/claude-api [migrate\|managed-agents-onboard\|prompt-audit]` | **[Skill](/docs/en/skills#bundled-skills).** Load [Claude API](https://platform.claude.com/docs/en/api/overview) and Managed Agents reference material for your project's language. Also activates automatically when your code imports `anthropic` or `@anthropic-ai/sdk`. Run `migrate` to upgrade existing Claude API code to a newer model, `managed-agents-onboard` for a walkthrough that creates a new Managed Agent, or `prompt-audit` to flag instructions written for older models in your prompts, skills, and tool descriptions and propose fixes as a diff. The `prompt-audit` subcommand requires Claude Code v2.1.221 or later | | `/clear [name]` | Start a new conversation with empty context. Pass a name to label the previous conversation in the `/resume` picker. To free up context while continuing the same conversation, use `/compact` instead. Resume the previous conversation with `/resume`, or, in the same Claude Code process, restore it from [the rewind menu's previous-session entry](/docs/en/checkpointing#rewind-past-a-cleared-conversation). Aliases: `/reset`, `/new` | | `/code-review [low\|medium\|high\|xhigh\|max\|ultra] [--fix] [--comment] [pr#\|branch\|path]` | **[Skill](/docs/en/skills#bundled-skills).** Review the current diff, or a PR number, branch, or path you pass, for correctness bugs and cleanup opportunities. Pass `--fix` to apply findings, `--comment` to post them as inline GitHub PR comments, or `ultra` to run a deep [cloud review](/docs/en/ultrareview). With `ultra` on a `github.com` PR target, `--post` preselects [posting the finished findings to the PR](/docs/en/ultrareview#post-findings-to-the-pull-request) in the launch dialog. See [Review a diff locally](/docs/en/code-review#review-a-diff-locally) for the effort levels, targeting, and how it relates to `/simplify`. Alias: `/review` | | `/color [color\|default]` | Set the prompt bar color for the current session. Available colors: `red`, `blue`, `green`, `yellow`, `purple`, `orange`, `pink`, `cyan`. Use `default` to reset, or run with no argument to pick a random color. When [Remote Control](/docs/en/remote-control) is connected, the color syncs to claude.ai/code. Also available in non-interactive mode (`-p`); requires Claude Code v2.1.205 or later | | `/compact [instructions]` | Free up context by summarizing the conversation so far. Optionally pass focus instructions for the summary. See [how compaction handles rules, skills, and memory files](/docs/en/context-window#what-survives-compaction) | | `/config [key=value ...]` | Open the [Settings](/docs/en/settings) interface to adjust theme, model, [output style](/docs/en/output-styles), and other preferences. From v2.1.181, pass one or more `key=value` pairs to set a setting directly without opening the interface, for example `/config thinking=false`. From v2.1.182, named shorthand keys are also accepted, such as `/config theme=dark` or `/config model=sonnet`. The `key=value` form also works in non-interactive mode (`-p`) and from the Claude mobile app via [Remote Control](/docs/en/remote-control). Run `/config --help` to list every settable key with its options. Alias: `/settings` | | `/context [all]` | Visualize current context usage as a colored grid. Shows optimization suggestions for context-heavy tools, memory bloat, and capacity warnings. When the conversation exceeds the context window, the output includes a [warning](/docs/en/errors#context-exceeds-the-token-limit) showing how far over the limit you are and which command frees space. In [fullscreen mode](/docs/en/fullscreen), `/context` collapses the per-item breakdown to keep the grid visible. Pass `all` to expand it | | `/copy [N]` | Copy the last assistant response to clipboard. Pass a number `N` to copy the Nth-latest response: `/copy 2` copies the second-to-last. When code blocks are present, shows an interactive picker to select individual blocks or the full response. Press `w` in the picker to write the selection to a file instead of the clipboard, which is useful over SSH | | `/cost` | Alias for `/usage` | | `/dataviz [request]` | **[Skill](/docs/en/skills#bundled-skills).** Design guidance for charts, graphs, and dashboards. Claude picks the chart form for the data, assigns color by role, validates the palette for colorblind safety and contrast with a bundled script, and applies mark, interaction, and accessibility rules. Uses a brand-neutral placeholder palette that you replace with your own. Requires Claude Code v2.1.198 or later | | `/debug [description]` | **[Skill](/docs/en/skills#bundled-skills).** Enable debug logging for the current session and troubleshoot issues by reading the session debug log. Debug logging is off by default unless you started with `claude --debug`, so running `/debug` mid-session starts capturing logs from that point forward. Optionally describe the issue to focus the analysis | | `/deep-research <question>` | **[Workflow](/docs/en/workflows#bundled-workflows).** Fan out web searches on a question, fetch and cross-check sources, and synthesize a cited report | | `/design-login` | Authorize design-system access for `/design-sync` with your claude.ai account | | `/design-sync [hint]` | **[Skill](/docs/en/skills#bundled-skills).** Convert your repo's React design system and upload it to [Claude Design](https://claude.ai/design), so designs it produces use your real components. Optionally name the design system, for example `/design-sync Acme DS`. A first-time sync verifies every component and can take a few hours on a large repo. Available on the Anthropic API; on Amazon Bedrock, Google Cloud's Agent Platform, Microsoft Foundry, and Claude Platform on AWS the underlying tool can't reach claude.ai, so the command is unavailable | | `/desktop` | Continue the current session in the Claude Code Desktop app. Requires macOS or x64 Windows and a Claude subscription. Alias: `/app` | | `/diff` | Open an interactive diff viewer showing uncommitted changes and per-turn diffs. Use left/right arrows to switch between the current git diff and individual Claude turns, and up/down to browse files. Press Enter to open the selected file's diff, scroll it with up/down or PageUp/PageDown, and press Esc to return to the file list. Claude Code computes these diffs from raw git blob content, so diff drivers and `textconv` filters configured in `.gitattributes` or git config don't apply. Before v2.1.222, workspace-configured drivers and filters could rewrite the viewer's output. The open viewer also refreshes automatically when the repository's git state changes outside the session, such as a branch switch or commit in another terminal; the auto-refresh requires Claude Code v2.1.198 or later | | `/doctor` | **[Skill](/docs/en/skills#bundled-skills).** Run a setup checkup that diagnoses issues and can fix them. Checks installation health, including duplicate or leftover installs, `PATH` problems, and unparseable settings files. Finds unused skills, MCP servers, and plugins versus their context cost, flags slow [hooks](/docs/en/hooks), and checks for a newer version on your [release channel](/docs/en/setup#configure-release-channel). Deduplicates local `CLAUDE.md` files against checked-in ones, trims checked-in [`CLAUDE.md`](/docs/en/memory#my-claude-md-is-too-large) files by cutting content Claude could derive from the codebase, and migrates the always-loaded guidance that remains into [skills](/docs/en/skills) and nested `CLAUDE.md` files that load on demand. Also offers to make [auto mode](/docs/en/permissions#permission-modes) your default and to [pre-approve](/docs/en/permissions) frequently denied read-only commands. Reports findings first and asks for confirmation before changing anything. From the terminal, `claude doctor` prints read-only installation diagnostics without starting a session. Alias: `/checkup`. The `CLAUDE.md` trim check requires Claude Code v2.1.206 or later. Before v2.1.205, `/doctor` opened a read-only diagnostics screen and pressing `f` sent the report to Claude | | `/effort [level\|auto]` | Set the model [effort level](/docs/en/model-config#adjust-effort-level). Accepts `low`, `medium`, `high`, `xhigh`, `max`, or [`ultracode`](/docs/en/workflows#let-claude-decide-with-ultracode); available levels depend on the model, and `max` and `ultracode` are session-only. `auto` resets to the model default. Without an argument, opens an interactive slider. Takes effect immediately without waiting for the current response to finish. Also available in non-interactive mode (`-p`) with a level argument, where it applies to the current session only; requires Claude Code v2.1.205 or later. While the [model-default effort hold](/docs/en/model-config#adjust-effort-level) is in force, a non-interactive `/effort` reports `Not applied`, so pass `--effort` at launch instead | | `/exit` | Exit the CLI. In an attached [background session](/docs/en/agent-view#attach-to-a-session), this detaches and the session keeps running. Alias: `/quit` | | `/export [filename]` | Export the current conversation as plain text. With a filename, writes directly to that file. Without, opens a dialog to copy to clipboard or save to a file | | `/fast [on\|off]` | Toggle [fast mode](/docs/en/fast-mode) on or off. Availability in non-interactive mode with `-p` is limited; see [Toggle fast mode](/docs/en/fast-mode#toggle-fast-mode). Requires Claude Code v2.1.205 or later | | `/feedback [report]` | Send product feedback about Claude Code. Opens the same dialog as [`/bug`](#all-commands) with the same consent step and sending rules | | `/fewer-permission-prompts` | **[Skill](/docs/en/skills#bundled-skills).** Scan your transcripts for common read-only Bash and MCP tool calls, then add a prioritized allowlist to project `.claude/settings.json` to reduce permission prompts | | `/focus` | Toggle the focus view, which shows only your last prompt, a one-line tool-call summary with edit diffstats, and the final response. The tool-call summary also counts the subagents launched in the turn and collapses completed background-task notifications into a single count. The selection persists across sessions; set [`viewMode`](/docs/en/settings#available-settings) in settings to override it. Only available in [fullscreen rendering](/docs/en/fullscreen). The [VS Code extension](/docs/en/vs-code#use-the-prompt-box) offers its own Focus view as a command-menu toggle, stored as an extension setting, independent of `viewMode` | | `/fork [prompt]` | [Copy the current conversation](/docs/en/agent-view#copy-the-session-with-%2Ffork) into a new background session and keep working here. Pass a prompt and the copy starts working on it immediately; without one it waits in agent view for its first prompt. Except when the copy [edits in place](/docs/en/agent-view#how-file-edits-are-isolated), Claude Code instructs it to create a worktree of its own before making code changes; the isolation instruction requires Claude Code v2.1.221 or later. To hand a side task to a subagent whose result comes back into this conversation, use `/subtask`; to switch into a copy yourself, use `/branch`. Requires Claude Code v2.1.212 or later; on v2.1.161 through v2.1.211, and whenever [agent view is turned off](/docs/en/agent-view#turn-off-agent-view), `/fork` starts a [forked subagent](/docs/en/sub-agents#fork-the-current-conversation) instead | | `/goal [condition\|clear]` | Set a [goal](/docs/en/goal): Claude keeps working across turns until the condition is met. With no argument, shows the current or most recently achieved goal. `clear`, `stop`, `off`, `reset`, `none`, or `cancel` removes an active goal early | | `/heapdump` | Write a JavaScript heap snapshot and a memory breakdown to `~/Desktop`, or your home directory on Linux without a Desktop folder, for diagnosing high memory usage. Attach only the `-diagnostics.json` file when reporting a memory issue; the `.heapsnapshot` contains your full conversation and credentials, so don't share it. Doesn't appear in the command menu; type it in full. See [what to do with the output](/docs/en/troubleshooting#high-cpu-or-memory-usage) | | `/help` | Show help and available commands | | `/hooks` | View [hook](/docs/en/hooks) configurations for tool events | | `/ide` | Manage IDE integrations and show status | | `/import [codex\|gemini] [--dry-run] [--yes]` | Bring configuration from other coding agents on your machine, currently OpenAI Codex and Google Gemini CLI, into Claude Code, including instruction files, MCP servers, commands, subagents, and skills. In [non-interactive mode](/docs/en/headless) with `-p`, `/import` lists what it found and gives you the command that confirms the import. Add `--dry-run` to preview without writing anything, or `--yes` to skip the interactive picker. Not available on Amazon Bedrock, Google Cloud's Agent Platform, Microsoft Foundry, or Claude Platform on AWS. Also unavailable when you turn off [feature-flag fetching](/docs/en/env-vars#features-that-need-feature-flag-fetching). Requires Claude Code v2.1.213 or later | | `/init` | Initialize project with a `CLAUDE.md` guide. Set `CLAUDE_CODE_NEW_INIT=1` for an interactive flow that also walks through skills, hooks, and personal memory files. If `/init` finds configuration from a coding agent that `/import` supports, it offers to carry it over with `/import` | | `/insights` | Generate an HTML report analyzing your recent sessions on this machine: which projects you work in, how you use Claude Code, where things go wrong, and features to try. Not available in [cloud sessions](/docs/en/claude-code-on-the-web). See [Analyze your usage patterns](/docs/en/costs#analyze-your-usage-patterns) for the report location, retention, and cost | | `/install-github-app` | Install the Claude GitHub App for a repository, with an optional step to set up [GitHub Actions](/docs/en/github-actions) workflows and secrets. Walks you through selecting a repo and configuring the integration | | `/install-slack-app` | Install the Claude Slack app. Opens a browser to complete the OAuth flow | | `/keybindings` | Open your [keyboard shortcuts](/docs/en/keybindings) file | | `/list-agents` | List the subagents and other Claude Code sessions Claude can message, with the name to use for each; [agent team](/docs/en/agent-teams) teammates aren't listed, since Claude reaches them through the team's roster. See [cross-session messaging](/docs/en/cross-session-messaging). Also available as `/peers`. Requires Claude Code v2.1.224 or later; earlier versions report `Unknown command: /list-agents`. Available only in sessions where [cross-session messaging is enabled](/docs/en/cross-session-messaging#availability) | | `/login` | Sign in to your Anthropic account | | `/logout` | Sign out from your Anthropic account | | `/loop [interval] [prompt]` | **[Skill](/docs/en/skills#bundled-skills).** Run a prompt repeatedly while the session stays open. Omit the interval and, [where available](/docs/en/scheduled-tasks#let-claude-choose-the-interval), Claude self-paces between iterations. Omit the prompt and, [where available](/docs/en/scheduled-tasks#let-claude-choose-the-interval), Claude runs an autonomous maintenance check or the prompt in `.claude/loop.md`. Example: `/loop 5m check if the deploy finished`. See [Run prompts on a schedule](/docs/en/scheduled-tasks). Alias: `/proactive` | | `/mcp [reconnect <server>\|enable\|disable [<server>\|all]]` | Manage MCP server connections and OAuth authentication. Run with no argument to open the interactive list, pass `reconnect <server>` to reconnect one disconnected server, or pass `enable`/`disable` with a server name or `all` to change connection state without opening the dialog. Also available in non-interactive mode (`-p`), where running it with no argument prints a text summary of server status instead of opening the list; requires Claude Code v2.1.205 or later | | `/memory` | Edit `CLAUDE.md` files, enable or disable [auto memory](/docs/en/memory#auto-memory), and view auto memory entries | | `/mobile` | Show QR code to download the Claude mobile app. Aliases: `/ios`, `/android` | | `/model [model]` | Switch the AI model and save it as your default for new sessions. For models that support it, use left/right arrows to [adjust effort level](/docs/en/model-config#adjust-effort-level). With no argument, opens a picker; press `s` on a row to switch for the current session only. The picker asks for confirmation when the conversation has prior output, since the next response re-reads the full history without cached context. Once confirmed, the change applies without waiting for the current response to finish. Also available in non-interactive mode (`-p`) with a model argument instead of the picker, where it applies to the current session only and isn't saved as your default; requires Claude Code v2.1.205 or later | | `/passes` | Share a free week of Claude Code with friends. Only visible if your account is eligible | | `/permissions` | Manage allow, ask, and deny rules for tool permissions. Opens an interactive dialog where you can view rules by scope, add or remove rules, manage working directories, and review [recent auto mode denials](/docs/en/auto-mode-config#review-denials). Alias: `/allowed-tools` | | `/plan [description]` | Enter plan mode directly from the prompt. Pass an optional description to enter plan mode and immediately start with that task, for example `/plan fix the auth bug` | | `/plugin [subcommand]` | Manage Claude Code [plugins](/docs/en/plugins). Run with no argument to open the plugin menu, or pass a subcommand such as `list`, `install`, `enable`, or `disable` to act directly. Claude Code can activate a plugin during the install; the [install summary](/docs/en/discover-plugins#install-plugins) tells you whether it did or whether to run `/reload-plugins` | | `/powerup` | Discover Claude Code features through quick interactive lessons with animated demos | | `/pr-comments [PR]` | Removed in v2.1.91. Ask Claude directly to view pull request comments instead. On earlier versions, fetches and displays comments from a GitHub pull request; automatically detects the PR for the current branch, or pass a PR URL or number. Requires the `gh` CLI | | `/privacy-settings` | View and update your privacy settings. Only available for Pro and Max plan subscribers | | `/radio` | Open Claude FM lo-fi radio in your browser. Prints the stream URL when no browser is available. Not available on Amazon Bedrock, Google Cloud's Agent Platform, Microsoft Foundry, or Claude Platform on AWS | | `/recap` | Generate a one-line summary of the current session on demand. See [Session recap](/docs/en/interactive-mode#session-recap) for the automatic recap that appears after you've been away | | `/release-notes` | View the changelog in an interactive version picker. Select a specific version to see its release notes, or choose to show all versions. The notes appear in your transcript without entering the conversation Claude sees | | `/reload-plugins [--force]` | Reload all active [plugins](/docs/en/plugins) to apply pending changes without restarting. Reports counts for each reloaded component and flags any load errors. When the reload would change which MCP tools are loaded and invalidate the prompt cache, the command warns and skips unless you pass `--force` | | `/reload-skills` | Re-scan [skill](/docs/en/skills) and command directories so skills added or changed on disk during the session become available without restarting. Reports how many skills are available and how many were added or removed. Added in v2.1.152 | | `/remote-control` | Make this session available for [Remote Control](/docs/en/remote-control) from claude.ai. Running it while signed out prints that Remote Control requires a claude.ai subscription and tells you how to sign in; before v2.1.206 it reported `Unknown command: /remote-control`. Alias: `/rc` | | `/remote-env` | Choose the default environment for [cloud agents](/docs/en/cloud-environments#select-an-environment-from-the-cli) | | `/rename [name]` | Rename the current session and show the name on the prompt bar. Without a name, auto-generates one from conversation history. Also available in non-interactive mode (`-p`); requires Claude Code v2.1.205 or later. From every rename surface, including claude.ai and the desktop app, Claude Code replaces control and invisible characters in the new name with spaces and caps the name at 200 characters. If the name is empty once invisible characters are removed, Claude Code rejects it and shows `That name is empty once invisible characters are removed. Usage: /rename <name>`. The character replacement and length cap require Claude Code v2.1.221 or later. If another live session on this machine already uses a name you pass, Claude Code applies [a variant of it](/docs/en/sessions#name-your-sessions) instead | | `/resume [session]` | Resume a conversation by ID or name, or open the session picker. [Background sessions](/docs/en/agent-view) appear in the picker marked with `bg`; one that is still running can't be resumed here, so attach to it from `claude agents` or stop it there first. Alias: `/continue` | | `/review [low\|medium\|high\|xhigh\|max\|ultra] [--fix] [--comment] [pr#\|branch\|path]` | Alias of [`/code-review`](/docs/en/code-review#review-a-diff-locally): reviews the current diff, or a PR number, branch, or path you pass, such as `/review 1234`, and takes the same effort levels and flags. With no level given, the review reuses the last `low` through `max` level you typed; see [Review a diff locally](/docs/en/code-review#review-a-diff-locally) for the exact rules. For a deep cloud review, use [`/code-review ultra`](/docs/en/ultrareview). Before v2.1.223, `/review` was a separate command that ran a single-pass, read-only review of a GitHub pull request by number, listing open PRs to pick from when run with no argument; from v2.1.186 through v2.1.201, it ran the same multi-agent engine as `/code-review medium` | | `/rewind` | Rewind the conversation and/or code to a previous point, or summarize from a selected message. See [checkpointing](/docs/en/checkpointing). Aliases: `/checkpoint`, `/undo` | | `/run` | **[Skill](/docs/en/skills#bundled-skills).** Launch and drive your project's app to see a change working, not only passing tests. See [Run and verify your app](/docs/en/skills#run-and-verify-your-app). Requires Claude Code v2.1.145 or later | | `/run-skill-generator` | **[Skill](/docs/en/skills#bundled-skills).** Teach `/run` and `/verify` how to build, launch, and drive your project's app from a clean environment by writing a per-project [skill](/docs/en/skills#run-and-verify-your-app). Requires Claude Code v2.1.145 or later | | `/sandbox` | Toggle [sandbox mode](/docs/en/sandboxing). Available on supported platforms only | | `/schedule [description]` | Create, update, list, or run [routines](/docs/en/routines), which execute in the cloud. Claude walks you through the setup conversationally. You can also ask about a [routine's recent runs](/docs/en/routines#manage-routines-from-the-cli). Alias: `/routines` | | `/scroll-speed` | Adjust mouse wheel [scroll speed](/docs/en/fullscreen#mouse-wheel-scrolling) interactively, with a ruler you can scroll while the dialog is open to preview the change. Available in [fullscreen rendering](/docs/en/fullscreen) only and not in the JetBrains IDE terminal | | `/security-review` | Analyze the changes on your current branch for security vulnerabilities. Reviews the diff between your branch and origin's default branch, identifying risks like injection, auth issues, and data exposure. Needs an `origin` remote; if the review fails with an `ambiguous argument` error, see the [error reference](/docs/en/errors#security-review-fails-without-origin-head) | | `/setup-bedrock` | Configure [Amazon Bedrock](/docs/en/amazon-bedrock) authentication, region, and model pins through an interactive wizard. Only visible when `CLAUDE_CODE_USE_BEDROCK=1` is set. First-time Amazon Bedrock users can also access this wizard from the login screen | | `/setup-vertex` | Configure [Google Cloud's Agent Platform](/docs/en/google-vertex-ai) authentication, project, region, and model pins through an interactive wizard. Only visible when `CLAUDE_CODE_USE_VERTEX=1` is set. First-time Google Cloud's Agent Platform users can also access this wizard from the login screen | | `/simplify [target]` | **[Skill](/docs/en/skills#bundled-skills).** Review the changed code for cleanup opportunities and apply the fixes. Four review [agents](/docs/en/sub-agents) run in parallel, covering reuse of existing helpers, simplification, efficiency, and whether the change is at the right level of abstraction. From v2.1.154, the review doesn't look for correctness bugs. Use `/code-review` to find bugs. On earlier versions, `/simplify` is equivalent to `/code-review --fix`. Pass a path or PR reference to review a specific target | | `/skills` | List available [skills](/docs/en/skills). Type to filter the list by name. Press `t` to sort by token count. Press `Space` to [cycle a skill's visibility to Claude and the `/` menu](/docs/en/skills#override-skill-visibility-from-settings), then `Enter` to save | | `/stats` | Alias for `/usage`. Opens on the Stats tab | | `/status` | Open the Settings interface on the Status tab, showing version, model, account, and connectivity. A `Session kind` row reads `background job · attached` or `background job · unattended` in a [background session](/docs/en/agent-view), depending on whether a terminal is attached, and `interactive` in any other session. Before v2.1.221, `/status` didn't show this row. Works while Claude is responding | | `/statusline` | Configure Claude Code's [status line](/docs/en/statusline). Describe what you want, or run without arguments to auto-configure from your shell prompt | | `/stickers` | Order Claude Code stickers | | `/stop` | Stop the current [background session](/docs/en/agent-view). Only available while attached to a background session; the transcript and any worktree are kept. To detach without stopping, use `/exit` or press `←` | | `/subtask <task>` | Spawn a [forked subagent](/docs/en/sub-agents#fork-the-current-conversation): a background subagent that inherits the full conversation and works on the task while you keep working. Its result returns to this conversation when it finishes. To copy the conversation into a separate background session instead, use `/fork`. Requires Claude Code v2.1.212 or later; on v2.1.161 through v2.1.211 this command is `/fork`. When [agent view is turned off](/docs/en/agent-view#turn-off-agent-view), `/subtask` isn't available and `/fork` keeps the forked-subagent behavior | | `/tasks` | View and manage background work in the current session, including subagents that have finished. Also available as `/bashes` | | `/team-onboarding` | Generate a team onboarding guide from your Claude Code usage history. Claude analyzes your sessions, commands, and MCP server usage from the past 30 days and produces a markdown guide a teammate can paste as a first message to get set up quickly. For claude.ai subscribers on Pro, Max, Team, and Enterprise plans, also returns a share link teammates can open directly in Claude Code | | `/teleport` | Pull a [Claude Code on the web](/docs/en/claude-code-on-the-web#from-web-to-terminal) session into this terminal. Opens a picker, then fetches the branch and conversation. Also available as `/tp`. Requires a claude.ai subscription | | `/terminal-setup` | Configure terminal keybindings for Shift+Enter and other shortcuts. Only visible in terminals that need it, like VS Code, Cursor, Devin Desktop, Alacritty, or Zed | | `/theme` | Change the color theme. Includes an `auto` option that matches your terminal's light or dark background, light and dark variants, colorblind-accessible (daltonized) themes, ANSI themes that use your terminal's color palette, and any [custom themes](/docs/en/terminal-config#create-a-custom-theme) from `~/.claude/themes/` or plugins. Select **New custom theme…** to create one | | `/tui [default\|fullscreen]` | Set the terminal UI renderer and relaunch into it with your conversation intact. `fullscreen` enables the [flicker-free alt-screen renderer](/docs/en/fullscreen). With no argument, prints the active renderer | | `/ultraplan <prompt>` | Removed. Use [plan mode](/docs/en/permission-modes#analyze-before-you-edit-with-plan-mode) instead. Previously sent a planning task to a [Claude Code on the web](/docs/en/claude-code-on-the-web) session for review in your browser | | `/ultrareview [PR or branch]` | Run a deep, multi-agent code review in a cloud sandbox with [ultrareview](/docs/en/ultrareview). Pass a PR reference to review that pull request, or a branch name to change the comparison base. The preferred invocation is now `/code-review ultra`, and `/ultrareview` remains as an alias. Includes 3 free runs on Pro and Max, then requires [usage credits](https://support.claude.com/en/articles/12429409-extra-usage-for-paid-claude-plans) | | `/upgrade` | Open the upgrade page in your browser to switch to a higher plan tier. When the browser fails to open, the command shows a sign-in prompt without printing the URL | | `/usage` | Show session cost, plan usage limits, and activity stats. On a Pro, Max, Team, or Enterprise plan, includes a breakdown of usage by skill, subagent, plugin, and MCP server. See the [cost tracking guide](/docs/en/costs#using-the-%2Fusage-command) for details. `/cost` and `/stats` are aliases | | `/usage-credits` | Configure usage credits, or request them from your admin, when you hit a limit. Opens your [usage-credits billing settings](/docs/en/costs#add-usage-credits-to-your-subscription) in the browser, except that Team and Enterprise members without billing access instead send a usage-credits request to their admin from the CLI, after confirming in a dialog that the request notifies their admins. When no browser can open the billing page, for example over SSH, the command prints the URL to visit instead; this requires Claude Code v2.1.205 or later, and earlier versions showed nothing in that case. Previously `/extra-usage` | | `/verify` | **[Skill](/docs/en/skills#bundled-skills).** Confirm a code change does what it should by building your project's app, running it, and observing the result, rather than relying on tests or type checks. See [Run and verify your app](/docs/en/skills#run-and-verify-your-app). Requires Claude Code v2.1.145 or later | | `/vim` | Removed in v2.1.92. To toggle between Vim and Normal editing modes, use `/config` → Editor mode | | `/voice [hold\|tap\|off]` | Toggle [voice dictation](/docs/en/voice-dictation), or enable it in a specific mode. Requires a Claude.ai account | | `/web-setup` | Connect your GitHub account to [Claude Code on the web](/docs/en/web-quickstart#connect-from-your-terminal) using your local `gh` CLI credentials. `/schedule` prompts for this automatically if GitHub isn't connected | | `/workflows` | Open the [workflow](/docs/en/workflows#watch-the-run) progress view to watch, pause, resume, or save running and completed workflows | ## MCP prompts MCP servers can expose prompts that appear as commands. These use the format `/mcp__<server>__<prompt>` and are dynamically discovered from connected servers. See [MCP prompts](/docs/en/mcp#use-mcp-prompts-as-commands) for details. ## See also * [Skills](/docs/en/skills): create your own commands * [Interactive mode](/docs/en/interactive-mode): keyboard shortcuts, Vim mode, and command history * [CLI reference](/docs/en/cli-reference): launch-time flags
common-workflows First recorded · 529 lines, first recorded
# Common workflows ## Prompt recipes ### Understand new codebases #### Get a quick codebase overview #### Find relevant code ### Fix bugs efficiently ### Refactor code ### Work with tests ### Create pull requests ### Handle documentation ### Work in notes and non-code folders ### Work with images ### Reference files and directories ### Run Claude on a schedule ### Ask Claude about its capabilities #### Example questions ## Resume previous conversations ## Run parallel sessions with worktrees ## Plan before editing ## Delegate research to subagents ## Pipe Claude into scripts ## Next steps
The first capture of this source. The page was already there, and this is what it said.
# Common workflows
> Step-by-step guides for exploring codebases, fixing bugs, refactoring, testing, and other everyday tasks with Claude Code.
This page collects short recipes for everyday development. For higher-level guidance on prompting and context management, see [Best practices](/docs/en/best-practices).
This page covers:
* [Prompt recipes](#prompt-recipes) for exploring code, fixing bugs, refactoring, testing, PRs, and documentation
* [Resume previous conversations](#resume-previous-conversations) so a task can span multiple sittings
* [Run parallel sessions with worktrees](#run-parallel-sessions-with-worktrees) so concurrent edits don't collide
* [Plan before editing](#plan-before-editing) to review changes before they touch disk
* [Delegate research to subagents](#delegate-research-to-subagents) to keep your main context clean
* [Pipe Claude into scripts](#pipe-claude-into-scripts) for CI and batch processing
## Prompt recipes
These are prompt patterns for everyday tasks like exploring unfamiliar code, debugging, refactoring, writing tests, and creating PRs. Each works in any Claude Code surface; adapt the wording to your project.
### Understand new codebases
For configuring Claude Code in a monorepo or large codebase, see [Monorepos and large repos](/docs/en/large-codebases).
#### Get a quick codebase overview
Suppose you've just joined a new project and need to understand its structure quickly.
<Steps>
<Step title="Navigate to the project root directory">
```bash theme={null}
cd /path/to/project
```
Replace `/path/to/project` with the path to your project.
</Step>
<Step title="Start Claude Code">
```bash theme={null}
claude
```
</Step>
<Step title="Ask for a high-level overview">
```text wrap theme={null}
give me an overview of this codebase
```
</Step>
<Step title="Dive deeper into specific components">
```text wrap theme={null}
explain the main architecture patterns used here
```
```text wrap theme={null}
what are the key data models?
```
```text wrap theme={null}
how is authentication handled?
```
</Step>
</Steps>
<Tip>
Tips:
* Start with broad questions, then narrow down to specific areas
* Ask about coding conventions and patterns used in the project
* Request a glossary of project-specific terms
</Tip>
#### Find relevant code
Suppose you need to locate code related to a specific feature or functionality.
<Steps>
<Step title="Ask Claude to find relevant files">
```text wrap theme={null}
find the files that handle user authentication
```
</Step>
<Step title="Get context on how components interact">
```text wrap theme={null}
how do these authentication files work together?
```
</Step>
<Step title="Understand the execution flow">
```text wrap theme={null}
trace the login process from front-end to database
```
</Step>
</Steps>
<Tip>
Tips:
* Be specific about what you're looking for
* Use domain language from the project
* Install a [code intelligence plugin](/docs/en/discover-plugins#code-intelligence) for your language to give Claude precise "go to definition" and "find references" navigation
</Tip>
***
### Fix bugs efficiently
Suppose you've encountered an error message and need to find and fix its source.
<Steps>
<Step title="Share the error with Claude">
```text wrap theme={null}
I'm seeing an error when I run npm test
```
</Step>
<Step title="Ask for fix recommendations">
```text wrap theme={null}
suggest a few ways to fix the @ts-ignore in user.ts
```
</Step>
<Step title="Apply the fix">
```text wrap theme={null}
update user.ts to add the null check you suggested
```
</Step>
</Steps>
<Tip>
Tips:
* Tell Claude the command to reproduce the issue and get a stack trace
* Mention any steps to reproduce the error
* Let Claude know if the error is intermittent or consistent
</Tip>
***
### Refactor code
Suppose you need to update old code to use modern patterns and practices.
For porting an entire codebase to a new language, see [how Anthropic runs large-scale code migrations with Claude Code](https://claude.com/blog/ai-code-migration) on the blog.
<Steps>
<Step title="Identify legacy code for refactoring">
```text wrap theme={null}
find deprecated API usage in our codebase
```
</Step>
<Step title="Get refactoring recommendations">
```text wrap theme={null}
suggest how to refactor utils.js to use modern JavaScript features
```
</Step>
<Step title="Apply the changes safely">
```text wrap theme={null}
refactor utils.js to use ES2024 features while maintaining the same behavior
```
</Step>
<Step title="Verify the refactoring">
```text wrap theme={null}
run tests for the refactored code
```
</Step>
</Steps>
<Tip>
Tips:
* Ask Claude to explain the benefits of the modern approach
* Request that changes maintain backward compatibility when needed
* Do refactoring in small, testable increments
</Tip>
***
### Work with tests
Suppose you need to add tests for uncovered code.
<Steps>
<Step title="Identify untested code">
```text wrap theme={null}
find functions in NotificationsService.swift that are not covered by tests
```
</Step>
<Step title="Generate test scaffolding">
```text wrap theme={null}
add tests for the notification service
```
</Step>
<Step title="Add meaningful test cases">
```text wrap theme={null}
add test cases for edge conditions in the notification service
```
</Step>
<Step title="Run and verify tests">
```text wrap theme={null}
run the new tests and fix any failures
```
</Step>
</Steps>
Claude can generate tests that follow your project's existing patterns and conventions. When asking for tests, be specific about what behavior you want to verify. Claude examines your existing test files to match the style, frameworks, and assertion patterns already in use.
For comprehensive coverage, ask Claude to identify edge cases you might have missed. Claude can analyze your code paths and suggest tests for error conditions, boundary values, and unexpected inputs that are easy to overlook.
***
### Create pull requests
You can create pull requests by asking Claude directly ("create a pr for my changes"), or guide Claude through it step-by-step:
<Steps>
<Step title="Summarize your changes">
```text wrap theme={null}
summarize the changes I've made to the authentication module
```
</Step>
<Step title="Generate a pull request">
```text wrap theme={null}
create a pr
```
</Step>
<Step title="Review and refine">
```text wrap theme={null}
enhance the PR description with more context about the security improvements
```
</Step>
</Steps>
When you create a PR using `gh pr create`, the session is automatically linked to that PR. To find it later, run `claude --from-pr 1234` with your own PR number, which opens the session picker filtered to sessions linked to that PR, or paste the PR URL into the [`/resume` picker](/docs/en/sessions#use-the-session-picker) search.
<Tip>
Review Claude's generated PR before submitting and ask Claude to highlight potential risks or considerations.
</Tip>
### Handle documentation
Suppose you need to add or update documentation for your code.
<Steps>
<Step title="Identify undocumented code">
```text wrap theme={null}
find functions without proper JSDoc comments in the auth module
```
</Step>
<Step title="Generate documentation">
```text wrap theme={null}
add JSDoc comments to the undocumented functions in auth.js
```
</Step>
<Step title="Review and enhance">
```text wrap theme={null}
improve the generated documentation with more context and examples
```
</Step>
<Step title="Verify documentation">
```text wrap theme={null}
check if the documentation follows our project standards
```
</Step>
</Steps>
<Tip>
Tips:
* Specify the documentation style you want (JSDoc, docstrings, etc.)
* Ask for examples in the documentation
* Request documentation for public APIs, interfaces, and complex logic
</Tip>
***
### Work in notes and non-code folders
Claude Code works in any directory. Run it inside a notes vault, a documentation folder, or any collection of markdown files to search, edit, and reorganize content the same way you would code.
The `.claude/` directory and `CLAUDE.md` sit alongside other tools' config directories without conflict. Claude reads files fresh on each tool call, so it sees edits you make in another application the next time it reads that file.
***
### Work with images
Suppose you need to work with images in your codebase, and you want Claude's help analyzing image content.
<Steps>
Cut at 300 lines. The page has the rest.
communications-kit First recorded · 526 lines, first recorded
# Communications kit ## Launch communications ### Before you send ### The announcement ### Executive sponsor variant ### Pilot group variant ### Champion recruitment DM ## Tips and tricks campaign ### Get started ### Project memory ### Control and safety ### Connect your tools ### Automate your workflows ### Day-to-day development ### Share and scale ### Security and admin ## Quick reference ### FAQ responses ### Prompt templates
The first capture of this source. The page was already there, and this is what it said.
# Communications kit
> Launch announcements, drip-campaign messages, and FAQ responses for rolling Claude Code out to your engineering organization.
This page is for administrators and engineering leads rolling Claude Code out to a team. It provides copy-ready launch announcements, a tips-and-tricks drip campaign, and one-line FAQ responses for the questions you will be asked most.
<Note>
Treat everything here as draft copy, not finished copy. Rewrite each message in your organization's voice, swap the example tasks for real bugs and modules from your own codebase, and replace the `[bracketed placeholders]` before sending. The announcements that drive adoption are the ones that read like someone at your company wrote them.
</Note>
## Launch communications
One announcement in two formats, plus two optional variants. Pick whichever fits your rollout and rewrite it from there.
### Before you send
Work through this checklist before the announcement goes out. Each item closes a gap that otherwise turns into a launch-day support thread.
| Item | Why it matters |
| ------------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------- |
| `#claude-code` channel created and linked in the message | Gives questions one place to land |
| Install command tested on at least one machine in your environment | Catches proxy or firewall issues before everyone hits them at once |
| Security and data-handling link ready ([Data usage](/docs/en/data-usage) or your internal equivalent) | "Where does my code go?" will be the first reply |
| One concrete first task chosen, a real bug or file in your codebase | Generic examples don't convert; "fix the flaky test in `auth_test.go`" does |
| A named owner for the channel for the first 48 hours | Unanswered launch-day questions kill momentum |
| A C-suite sponsor lined up to send or co-sign the announcement | Exec-sent launches consistently see higher first-week adoption than admin-sent ones |
### The announcement
Use this as your standard org-wide rollout message. It covers what Claude Code is, gives a two-minute install path, hands readers one concrete task to try, and answers "where does my code go?" before anyone has to ask.
<Tabs>
<Tab title="Email">
```text theme={null}
Subject: Claude Code is live for [Engineering / your team]
Team,
As of today you have access to Claude Code, an AI coding agent that runs in
your terminal, reads your actual codebase, and works through real tasks end
to end: debugging, refactors, tests, PRs. It is not autocomplete and it is
not a chat window. It edits files, runs your commands, and asks permission
before anything risky.
Get running in two minutes:
curl -fsSL https://claude.ai/install.sh | bash
cd <your-repo>
claude
Then run /init once. Claude reads your project and writes a CLAUDE.md with
your build commands and conventions, so you stop re-explaining the basics.
Then try one of these on the repo you are already in:
- "The test in [file] is flaky. Figure out why and fix it"
- "Walk me through how [module] handles [X]"
- "Look at my working diff and tell me what's risky before I push"
Where your code goes: Claude Code runs in your terminal and talks directly
to Anthropic's API, with no third-party servers in the loop. It asks before
editing files or running commands. Under our Enterprise agreement, Anthropic
does not use your code or prompts to train its models.
Details: https://code.claude.com/docs/en/data-usage
https://code.claude.com/docs/en/security
Where to go with questions: #claude-code. [Owner name] is watching it
this week.
- [Name]
P.S. Prefer your editor? There is a VS Code extension and a JetBrains
plugin. Same agent, no terminal required.
```
</Tab>
<Tab title="Slack or Teams">
```markdown theme={null}
🚀 *Claude Code is live for [team]*
AI coding agent, runs in your terminal, reads your repo, does real work:
bugs, refactors, tests, PRs. Asks before it touches anything.
`curl -fsSL https://claude.ai/install.sh | bash` → `cd your-repo` → `claude`
*First thing to try* → run `/init`, then: "the test in [file] is flaky,
figure out why and fix it."
🔒 Runs in your terminal, talks only to Anthropic's API. Under our
Enterprise plan your code and prompts are not used to train models.
Data usage → https://code.claude.com/docs/en/data-usage
📚 Quickstart · VS Code · Free 1-hr course
https://code.claude.com/docs/en/quickstart
https://code.claude.com/docs/en/vs-code
https://anthropic.skilljar.com/claude-code-in-action
Questions → this thread. [Owner] is on point.
```
</Tab>
</Tabs>
### Executive sponsor variant
Send this from your sponsoring executive, such as the CTO, CIO, or SVP Engineering, under their name and from their account. Launches that go out under an exec's name consistently see higher open rates and faster first-week activation than the same message from an admin or tooling team. It signals a company priority rather than an optional experiment.
This version is deliberately stripped to one ask: install it and run it on one real task. The exec's job is to make the ask land; the standard announcement and `#claude-code` handle the how.
<Tabs>
<Tab title="Email">
```text theme={null}
Subject: One thing I'd like every engineer to try this week
Team,
We have turned on Claude Code for all of engineering. It is an AI agent
that works directly in your terminal, on your actual codebase, and the
early results from teams already using it are strong enough that I want
everyone on it this week.
I am asking for ten minutes:
curl -fsSL https://claude.ai/install.sh | bash
cd <your-repo>
claude
Then hand it one real task: the bug you have been putting off, or "walk me
through how [module] works."
That is the whole ask. [Owner name] and team are in #claude-code for
anything you hit along the way.
- [Exec Name]
[Title]
```
</Tab>
<Tab title="Slack or Teams">
```markdown theme={null}
📣 *From [Exec Name]: one thing to try this week*
We have turned on *Claude Code* for all of engineering. Early results are
strong enough that I am asking everyone to give it ten minutes on real
work this week.
`curl -fsSL https://claude.ai/install.sh | bash` → `cd your-repo` →
`claude` → hand it one real task.
That's it. Questions → #claude-code.
```
</Tab>
</Tabs>
### Pilot group variant
Use for a phased rollout. Send to the pilot cohort only.
```text theme={null}
Subject: You're in the Claude Code pilot
[Name / team],
You are in the first wave of Claude Code at [company]. We picked this group
because you will put it on real problems and tell us the truth about it.
The ask: use it on at least one real task this week, then drop a note in
#claude-code-pilot covering what worked, what was annoying, and what
surprised you. That feedback decides how we roll it out to everyone else.
[Continue with "Get running in two minutes" from the standard announcement]
One extra thing for pilots: on your first multi-file change, press Shift+Tab
until you see "plan". Claude will lay out exactly what it intends to do
before it touches a file. It is the fastest way to calibrate how much to
trust it.
```
### Champion recruitment DM
After launch, DM the two or three people who are most active in `#claude-code`.
```text theme={null}
Hey [name], your #claude-code posts are doing more for adoption than my
announcement did. A couple of people told me your [thread / screenshot]
was why they actually tried it.
Want to make that semi-official? Low lift: mostly keep posting what you
are posting, plus first crack at new features and a direct line to the
Anthropic team. I can share a short playbook if you're in.
```
## Tips and tricks campaign
Ready-to-paste Slack or Teams messages designed to drive feature activation after launch. Each follows the same pattern: a hook, the payoff, a "try it now" prompt, and a docs link. Drip them one or two a week in `#claude-code`, or pick the handful that match your team's gaps. They stand alone with no required order.
### Get started
**Choosing the right model**
```markdown theme={null}
🎯 *Tip: Match the model to the moment*
Using Opus to fix a typo burns compute. Using Haiku for a 12-file refactor
is asking for a re-do.
Claude Code runs on the same models as the Claude app, and you can switch
mid-session. *Sonnet* is the workhorse default for everyday feature work,
bugs, tests, and reviews. Reach for *Opus* on large refactors, gnarly
debugging, or anything high-stakes. Drop to *Haiku* for quick questions,
formatting, and mechanical edits where speed wins. *Fable 5* is the most
capable model for your hardest, longest-running tasks; it is not the
default, so select it with `/model fable`, and note that cybersecurity and
biology content falls back to Opus automatically. Opus 5 runs its own
checks, so flagged cybersecurity content switches models and flagged
biology content is refused.
*Try it now:* type `/model` and pick Sonnet if you haven't already. It is
the right default for most tasks.
📖 Model configuration → https://code.claude.com/docs/en/model-config
```
| Model | Best for |
| ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| Fable 5 | The hardest, longest-running tasks. Opt-in only: select it with `/model fable`. Cybersecurity or biology content [falls back to Opus](/docs/en/model-config#automatic-model-fallback) |
| Opus | Large-scale refactors, complex debugging, architecture decisions, high-stakes changes. On Opus 5, cybersecurity or biology content triggers [automatic model fallback or a refusal](/docs/en/model-config#automatic-model-fallback) |
| Sonnet | Everyday feature work, bug fixes, tests, documentation, code review. Recommended default. |
| Haiku | Quick questions, formatting, mechanical edits, rapid iteration |
**Quick wins to try first**
```markdown theme={null}
🚀 *Tip: Three things to try in your first 10 minutes*
Installed Claude Code but not sure what to actually ask it? Start with the
stuff that has been bugging you all week.
- Fix something annoying: "the test in [file] is flaky, figure out why"
- Get oriented in code you didn't write: "walk me through how [module] works"
- Sanity-check before you push: "look at my working diff and tell me what
looks risky"
None of these need setup. Just `cd` into your repo and run `claude`.
*Try it now:* pick the bug you have been avoiding and paste the error
message in.
📖 Quickstart → https://code.claude.com/docs/en/quickstart
```
### Project memory
**`/init` and CLAUDE.md**
```markdown theme={null}
📁 *Tip: Stop re-explaining your repo every session*
Telling Claude "we use pnpm, not npm" for the fifth time? There is a
one-time fix.
Run `/init` once per repo. Claude reads your project structure and writes a
CLAUDE.md file with your build commands, architecture, and conventions.
Every future session in that repo starts from this file automatically. Keep
it under two screens. It is a cheat sheet, not documentation.
*Try it now:* open your main repo, run `claude`, type `/init`. Thirty
seconds, pays off every session after.
📖 CLAUDE.md and project memory → https://code.claude.com/docs/en/memory
```
**@-references**
```markdown theme={null}
📎 *Tip: Stop pasting file contents into the chat*
Copying 200 lines of a component into your prompt so Claude can "see" it?
You don't have to.
Type `@` then a file path. Claude pulls the file directly into context.
Works for whole directories too.
> the styles in @src/components/Button.tsx look off, check against
> @docs/design-system.md
*Try it now:* type `@` then Tab. Autocomplete shows you every file in reach.
📖 Referencing files → https://code.claude.com/docs/en/common-workflows
```
### Control and safety
**Permission modes**
```markdown theme={null}
🛡️ *Tip: One keystroke between "look but don't touch" and "just do it"*
Sometimes you want Claude to ask before every edit. Sometimes you just want
it to ship. You shouldn't have to pick one forever.
Cut at 300 lines. The page has the rest.
computer-use First recorded · 207 lines, first recorded
# Let Claude use your computer from the CLI ## What you can do with computer use ## When computer use applies ## Enable computer use ## Approve apps per session ## How Claude works on your screen ### One session at a time ### Apps are hidden while Claude works ### Screenshots are downscaled automatically ### Stop at any time ## Safety and the trust boundary ## Example workflows ### Validate a native build ### Reproduce a layout bug ### Test a simulator flow ## Differences from the Desktop app ## Troubleshooting ### "Computer use is in use by another Claude session" ### macOS permissions prompt keeps reappearing ### `computer-use` doesn't appear in `/mcp` ## See also
The first capture of this source. The page was already there, and this is what it said.
# Let Claude use your computer from the CLI
> Enable computer use in the Claude Code CLI so Claude can open apps, click, type, and see your screen on macOS. Test native apps, debug visual issues, and automate GUI-only tools without leaving your terminal.
<Note>
Computer use is a research preview on macOS that requires a Pro or Max plan. It is not available on Team or Enterprise plans. It requires an interactive session, so it is not available in non-interactive mode with the `-p` flag.
</Note>
Computer use lets Claude open apps, control your screen, and work on your machine the way you would. From the CLI, Claude can compile a Swift app, launch it, click through every button, and screenshot the result, all in the same conversation where it wrote the code.
This page covers how computer use works in the CLI. For the Desktop app on macOS or Windows, see [computer use in Desktop](/docs/en/desktop#let-claude-use-your-computer).
## What you can do with computer use
Computer use handles tasks that require a GUI: anything you'd normally have to leave the terminal and do by hand.
* **Build and validate native apps**: ask Claude to build a macOS menu bar app. Claude writes the Swift, compiles it, launches it, and clicks through every control to verify it works before you ever open it.
* **End-to-end UI testing**: point Claude at a local Electron app and say "test the onboarding flow." Claude opens the app, clicks through signup, and screenshots each step. No Playwright config, no test harness.
* **Debug visual and layout issues**: tell Claude "the modal is clipping on small windows." Claude resizes the window, reproduces the bug, screenshots it, patches the CSS, and verifies the fix. Claude sees what you see.
* **Drive GUI-only tools**: interact with design tools, hardware control panels, the iOS Simulator, or proprietary apps that have no CLI or API.
## When computer use applies
Claude has several ways to interact with an app or service. Computer use is the broadest and slowest, so Claude tries the most precise tool first:
* If you have an [MCP server](/docs/en/mcp) for the service, Claude uses that.
* If the task is a shell command, Claude uses Bash.
* If the task is browser work and you have [Claude in Chrome](/docs/en/chrome) set up, Claude uses that.
* If none of those apply, Claude uses computer use.
Screen control is reserved for things nothing else can reach: native apps, simulators such as the iOS Simulator, and tools without an API.
## Enable computer use
Computer use is available as a built-in MCP server called `computer-use`. It's off by default until you enable it.
<Steps>
<Step title="Open the MCP menu">
In an interactive Claude Code session, run:
```text theme={null}
/mcp
```
Find `computer-use` in the server list. It shows as disabled.
</Step>
<Step title="Enable the server">
Select `computer-use` and choose **Enable**. The setting persists per project, so you only do this once for each project where you want computer use.
</Step>
<Step title="Grant macOS permissions">
The first time Claude tries to use your computer, you'll see a prompt to grant two macOS permissions:
* **Accessibility**: lets Claude click, type, and scroll
* **Screen Recording**: lets Claude see what's on your screen
The prompt includes links to open the relevant System Settings pane. Grant both, then select **Try again** in the prompt. macOS may require you to restart Claude Code after granting Screen Recording.
</Step>
</Steps>
After setup, ask Claude to do something that needs the GUI:
```text theme={null}
Build the app target, launch it, and click through each tab to make
sure nothing crashes. Screenshot any error states you find.
```
## Approve apps per session
Enabling the `computer-use` server doesn't grant Claude access to every app on your machine. The first time Claude needs a specific app in a session, a prompt appears in your terminal showing:
* Which apps Claude wants to control
* Any extra permissions requested, such as clipboard access
* How many other apps will be hidden while Claude works
Choose **Allow for this session** or **Deny**. Approvals last for the current session. You can approve multiple apps at once when Claude requests them together.
Apps with broad reach show an extra warning in the prompt so you know what approving them grants:
| Warning | Applies to |
| :------------------------- | :----------------------------------------------------------- |
| Equivalent to shell access | Terminal, iTerm, VS Code, Warp, and other terminals and IDEs |
| Can read or write any file | Finder |
| Can change system settings | System Settings |
These apps aren't blocked. The warning lets you decide whether the task warrants that level of access.
Claude's level of control also varies by app category: browsers and trading platforms are view-only, terminals and IDEs are click-only, and everything else gets full control. See [app permissions in Desktop](/docs/en/desktop#app-permissions) for the complete tier breakdown.
## How Claude works on your screen
Understanding the flow helps you anticipate what Claude will do and how to intervene.
### One session at a time
Only one session at a time can use your computer. Claude Code takes a machine-wide lock when a session performs its first computer use action. Claude Code releases it when that session exits, not when the task finishes. If another session is already using your computer, Claude Code rejects new attempts and tells you which session holds the lock. Exit that session first.
### Apps are hidden while Claude works
When Claude starts controlling your screen, other visible apps are hidden so Claude interacts with only the approved apps. Your terminal window stays visible and is excluded from screenshots, so you can watch the session and Claude never sees its own output.
When Claude finishes the turn, hidden apps are restored automatically.
### Screenshots are downscaled automatically
Claude Code downscales every screenshot before sending it to the model. You don't need to lower your display resolution or resize windows on Retina or other high-resolution displays. A 16-inch MacBook Pro at native Retina resolution captures at 3456×2234 and downscales to roughly 1372×887, preserving aspect ratio.
There is no setting to change the target size. If on-screen text or controls are too small for Claude to read after downscaling, increase their size in the app rather than changing your display resolution.
### Stop at any time
When Claude acquires the lock, a macOS notification appears: "Claude is using your computer · press Esc to stop." Press `Esc` anywhere to abort the current action immediately, or press `Ctrl+C` in the terminal. Either way, Claude stops, unhides your apps, and returns control to you. The session keeps the [computer use lock](#one-session-at-a-time) until it exits.
A second notification appears when Claude is done.
## Safety and the trust boundary
<Warning>
Unlike the [sandboxed Bash tool](/docs/en/sandboxing), computer use runs on your actual desktop with access to the apps you approve. Claude checks each action and flags potential prompt injection from on-screen content, but the trust boundary is different. See the [computer use safety guide](https://support.claude.com/en/articles/14128542) for best practices.
</Warning>
The built-in guardrails reduce risk without requiring configuration:
* **Per-app approval**: Claude can only control apps you've approved in the current session.
* **Sentinel warnings**: apps that grant shell, filesystem, or system settings access are flagged before you approve.
* **Terminal excluded from screenshots**: Claude never sees your terminal window, so on-screen prompts in your session can't feed back into the model.
* **Global escape**: the `Esc` key aborts computer use from anywhere, and the key press is consumed so prompt injection can't use it to dismiss dialogs.
* **Lock file**: only one session can control your machine at a time.
## Example workflows
These examples show common ways to combine computer use with coding tasks.
### Validate a native build
After making changes to a macOS or iOS app, have Claude compile and verify in one pass:
```text theme={null}
Build the MenuBarStats target, launch it, open the preferences window,
and verify the interval slider updates the label. Screenshot the
preferences window when you're done.
```
Claude runs `xcodebuild`, launches the app, interacts with the UI, and reports what it finds.
### Reproduce a layout bug
When a visual bug only appears at certain window sizes, let Claude find it:
```text theme={null}
The settings modal clips its footer on narrow windows. Resize the app
window down until you can reproduce it, screenshot the clipped state,
then check the CSS for the modal container.
```
Claude resizes the window, captures the broken state, and reads the relevant stylesheets.
### Test a simulator flow
Drive the iOS Simulator without writing XCTest:
```text theme={null}
Open the iOS Simulator, launch the app, tap through the onboarding
screens, and tell me if any screen takes more than a second to load.
```
Claude controls the simulator the same way you would with a mouse. This flow applies to the CLI; in the Desktop app, the same request opens the [iOS Simulator pane](/docs/en/desktop-ios-simulator) instead of screen control.
## Differences from the Desktop app
The CLI and Desktop surfaces share the same computer use engine, with a few differences:
| Feature | Desktop | CLI |
| :------------------- | :------------------------------------------------------- | :------------------------------ |
| Platforms | macOS and Windows | macOS only |
| Enable | Toggle in **Settings > General** (under **Desktop app**) | Enable `computer-use` in `/mcp` |
| Denied apps list | Configurable in Settings | Not yet available |
| Auto-unhide toggle | Optional | Always on |
| Dispatch integration | Dispatch-spawned sessions can use computer use | Not applicable |
## Troubleshooting
### "Computer use is in use by another Claude session"
Another Claude Code session holds the lock, which it keeps until it exits. Exit that session. If the other session crashed, the lock is released automatically when Claude detects the process is no longer running.
### macOS permissions prompt keeps reappearing
macOS sometimes requires a restart of the requesting process after you grant Screen Recording. Quit Claude Code completely and start a new session. If the prompt persists, open **System Settings > Privacy & Security > Screen Recording** and confirm your terminal app is listed and enabled.
### `computer-use` doesn't appear in `/mcp`
The server only appears on eligible setups. Check that:
* You're on macOS. Computer use in the CLI is not available on Linux or Windows. On Windows, use [computer use in Desktop](/docs/en/desktop#let-claude-use-your-computer) instead.
* You're on a Pro or Max plan. Run `/status` to confirm your subscription.
* You're authenticated through claude.ai. Computer use is not available with third-party providers like Amazon Bedrock, Google Cloud's Agent Platform, or Microsoft Foundry. If you access Claude exclusively through a third-party provider, you need a separate claude.ai account to use this feature.
* You're in an interactive session. Computer use is not available in non-interactive mode with the `-p` flag.
## See also
* [Computer use in Desktop](/docs/en/desktop#let-claude-use-your-computer): the same capability with a graphical settings page
* [Claude in Chrome](/docs/en/chrome): browser automation for web-based tasks
* [MCP](/docs/en/mcp): connect Claude to structured tools and APIs
* [Sandboxing](/docs/en/sandboxing): how Claude's Bash tool isolates filesystem and network access
* [Computer use safety guide](https://support.claude.com/en/articles/14128542): best practices for safe computer use
context-window First recorded · 1626 lines, first recorded
# Explore the context window ## What the timeline shows ## What survives compaction ## When your context fills up ## Check your own session ## Related resources
The first capture of this source. The page was already there, and this is what it said.
# Explore the context window
> An interactive simulation of how Claude Code's context window fills during a session. See what loads automatically, what each file read costs, and when rules and hooks fire.
export const ContextWindow = () => {
const MAX = 200000;
const STARTUP_END = 0.2;
{}
const EVENTS = useMemo(() => [{}, {
t: 0.015,
kind: 'auto',
label: 'System prompt',
tokens: 4200,
color: '#6B6964',
vis: 'hidden',
desc: 'Core instructions for behavior, tool use, and response formatting. Always loaded first. You never see it.',
link: null
}, {
t: 0.035,
kind: 'auto',
label: 'Auto memory (MEMORY.md)',
tokens: 680,
color: '#E8A45C',
vis: 'hidden',
desc: "Claude's notes to itself from previous sessions: build commands it learned, patterns it noticed, mistakes to avoid. The first 200 lines or 25KB, whichever comes first, are loaded into the conversation context.",
link: '/en/memory#auto-memory'
}, {
t: 0.06,
kind: 'auto',
label: 'Environment info',
tokens: 280,
color: '#6B6964',
vis: 'hidden',
desc: 'Working directory, platform, shell, OS version, and whether this is a git repo. Git branch, status, and recent commits load as a separate block at the very end of the system prompt.',
link: null
}, {
t: 0.08,
kind: 'auto',
label: 'MCP tools (deferred)',
tokens: 120,
color: '#9B7BC4',
vis: 'hidden',
desc: 'MCP tool names listed so Claude knows what is available. By default, full schemas stay deferred and Claude loads specific ones on demand via tool search when a task needs them. Set `ENABLE_TOOL_SEARCH=auto` to load schemas upfront when they fit within 10% of the context window, or `ENABLE_TOOL_SEARCH=false` to load everything.',
link: '/en/mcp#scale-with-mcp-tool-search'
}, {
t: 0.1,
kind: 'auto',
label: 'Skill descriptions',
tokens: 450,
color: '#D4A843',
vis: 'hidden',
noSurviveCompact: true,
desc: 'One-line descriptions of available skills so Claude knows what it can invoke. Full skill content loads only when Claude actually uses one. Skills with `disable-model-invocation: true` are not in this list. They stay completely out of context until you invoke them with `/name`. Unlike the rest of the startup content, this listing is not re-injected after `/compact`. Only skills you actually invoked get preserved.',
link: '/en/skills'
}, {
t: 0.12,
kind: 'auto',
label: '~/.claude/CLAUDE.md',
tokens: 320,
color: '#6A9BCC',
vis: 'hidden',
desc: 'Your global preferences. Applies to every project. Loaded alongside project instructions at the start of every conversation.',
link: '/en/memory#choose-where-to-put-claude-md-files'
}, {
t: 0.14,
kind: 'auto',
label: 'Project CLAUDE.md',
tokens: 1800,
color: '#6A9BCC',
vis: 'hidden',
desc: 'Project conventions, build commands, architecture notes. The most important file you can create. Lives in your project root, so your whole team gets the same instructions.',
tip: 'Keep it under 200 lines. Move reference content to skills or path-scoped rules so it only loads when needed.',
link: '/en/memory'
}, {}, {
t: 0.22,
kind: 'user',
label: 'Your prompt',
tokens: 45,
color: '#558A42',
vis: 'full',
desc: '"Fix the auth bug where users get 401 after token refresh"',
link: null
}, {}, {
t: 0.28,
kind: 'claude',
label: 'Read src/api/auth.ts',
tokens: 2400,
color: '#8A8880',
vis: 'brief',
desc: 'Main auth file. You see "Read auth.ts" in your terminal, but the 2,400 tokens of file content only Claude sees.',
tip: 'File reads dominate context usage. Be specific in prompts ("fix the bug in auth.ts") so Claude reads fewer files. For research-heavy tasks, use a subagent.',
link: null
}, {
t: 0.32,
kind: 'claude',
label: 'Read src/lib/tokens.ts',
tokens: 1100,
color: '#8A8880',
vis: 'brief',
desc: 'Following imports to the token module. Shown as a one-liner in your terminal.',
link: null
}, {
t: 0.35,
kind: 'auto',
label: 'Rule: api-conventions.md',
tokens: 380,
color: '#4A9B8E',
vis: 'brief',
desc: 'This rule in `.claude/rules/` has a `paths:` pattern matching `src/api/**`. It loaded automatically when Claude read a file in that directory. You see "Loaded .claude/rules/api-conventions.md" in your terminal, but not the rule content.',
link: '/en/memory#path-specific-rules'
}, {
t: 0.38,
kind: 'claude',
label: 'Read middleware.ts',
tokens: 1800,
color: '#8A8880',
vis: 'brief',
desc: 'Tracing the auth flow deeper.',
link: null
}, {
t: 0.41,
kind: 'claude',
label: 'Read auth.test.ts',
tokens: 1600,
color: '#8A8880',
vis: 'brief',
desc: 'Checking existing tests for expected behavior.',
link: null
}, {
t: 0.44,
kind: 'auto',
label: 'Rule: testing.md',
tokens: 290,
color: '#4A9B8E',
vis: 'brief',
desc: 'Another path-scoped rule, this one matching `*.test.ts` files. Triggered when Claude read auth.test.ts. Shown as a one-line "Loaded" notice.',
link: '/en/memory#path-specific-rules'
}, {
t: 0.47,
kind: 'claude',
label: 'grep "refreshToken"',
tokens: 600,
color: '#A09E96',
vis: 'brief',
desc: 'Search results across the codebase. You see the command ran, not the full output.',
link: null
}, {}, {
t: 0.53,
kind: 'claude',
label: "Claude's analysis",
tokens: 800,
color: '#D97757',
vis: 'full',
desc: 'Explains the bug: token invalidated too early in the rotation. This text appears in your terminal.',
link: null
}, {
t: 0.57,
kind: 'claude',
label: 'Edit auth.ts',
tokens: 400,
color: '#D97757',
vis: 'full',
desc: 'Fixes the token rotation order. The diff appears in your terminal.',
link: null
}, {
t: 0.59,
kind: 'hook',
label: 'Hook: prettier',
tokens: 120,
color: '#B8860B',
vis: 'hidden',
desc: 'A PostToolUse hook in `settings.json` runs prettier after every file edit and reports back via `hookSpecificOutput.additionalContext`. That field enters Claude\'s context. Plain stdout on exit 0 does not. It is written to the debug log only.',
tip: 'Output JSON with `additionalContext` to send info to Claude. For PostToolUse hooks, exit code 2 surfaces stderr as an error but cannot block since the tool already ran. Keep output concise since it enters context without truncation.',
link: '/en/hooks-guide'
}, {
t: 0.62,
kind: 'claude',
label: 'Edit auth.test.ts',
tokens: 600,
color: '#D97757',
vis: 'full',
desc: 'Adds a regression test for the fix. The diff appears in your terminal.',
link: null
}, {
t: 0.64,
kind: 'hook',
label: 'Hook: prettier',
tokens: 100,
color: '#B8860B',
vis: 'hidden',
desc: 'The same hook fires again for the test file. Every matching tool event triggers it.',
link: '/en/hooks-guide'
}, {
t: 0.67,
kind: 'claude',
label: 'npm test output',
tokens: 1200,
color: '#A09E96',
vis: 'brief',
desc: 'Runs the test suite. You see "Running npm test..." and the pass count, not the full 1,200 tokens of output.',
link: null
}, {
t: 0.70,
kind: 'claude',
label: 'Summary',
tokens: 400,
color: '#D97757',
vis: 'full',
desc: '"Fixed token rotation. Added regression test. All tests pass."',
link: null
}, {}, {
t: 0.72,
kind: 'user',
label: 'Your follow-up',
tokens: 40,
color: '#558A42',
vis: 'full',
desc: '"Use a subagent to research session timeout handling, then fix it"',
tip: 'Follow-ups add to the same context. Delegating research to a subagent keeps large file reads out of your main window.',
link: null
}, {
t: 0.79,
kind: 'claude',
label: 'Spawn research subagent',
tokens: 80,
color: '#D97757',
vis: 'brief',
desc: "Claude delegates the research to a subagent with a fresh, separate context window. It loads CLAUDE.md and the same MCP and skill setup, but starts without your conversation history or the main session's auto memory.",
link: '/en/sub-agents'
}, {
t: 0.795,
kind: 'sub',
label: 'System prompt',
tokens: 0,
subTokens: 900,
color: '#6B6964',
vis: 'hidden',
desc: "The subagent gets its own system prompt, shorter than the main session's. For the general-purpose agent, it's a brief prompt plus environment details. The main session's auto memory is not included. If a custom agent has memory: in its frontmatter, it loads its own separate MEMORY.md here instead.",
link: '/en/sub-agents#enable-persistent-memory'
}, {
t: 0.80,
kind: 'sub',
label: 'Project CLAUDE.md (own copy)',
tokens: 0,
subTokens: 1800,
color: '#6A9BCC',
vis: 'hidden',
desc: "The subagent loads CLAUDE.md too. Same file, same content, but it counts against the subagent's context, not yours. The built-in Explore and Plan agents skip this for a smaller context.",
link: '/en/sub-agents'
}, {
t: 0.805,
kind: 'sub',
label: 'MCP tools + skills',
tokens: 0,
subTokens: 970,
color: '#9B7BC4',
vis: 'hidden',
desc: "The subagent has access to the same MCP servers and skills. It gets most of the parent's tools, minus several that don't apply in a nested context, including plan-mode controls, background-task tools, and by default the Agent tool itself to prevent recursion.",
link: '/en/sub-agents'
}, {
t: 0.81,
kind: 'sub',
label: 'Task prompt from main',
tokens: 0,
subTokens: 120,
color: '#558A42',
vis: 'hidden',
desc: "Instead of a user prompt, the subagent receives the task Claude wrote for it: 'Research session timeout handling in this codebase.'",
link: '/en/sub-agents'
}, {
t: 0.82,
kind: 'sub',
label: 'Read session.ts',
tokens: 0,
subTokens: 2200,
color: '#8A8880',
vis: 'hidden',
desc: "Now the subagent does its work. This file read fills the subagent's context, not yours.",
link: '/en/sub-agents'
}, {
t: 0.825,
kind: 'sub',
label: 'Read timeouts.ts',
tokens: 0,
subTokens: 800,
color: '#8A8880',
vis: 'hidden',
desc: "Another file read in the subagent's separate context.",
link: '/en/sub-agents'
}, {
t: 0.83,
kind: 'sub',
label: 'Read config/*.ts',
tokens: 0,
subTokens: 3100,
color: '#8A8880',
vis: 'hidden',
desc: "The subagent can read as many files as it needs. None of this touches your main context.",
link: '/en/sub-agents'
}, {
Cut at 300 lines. The page has the rest.
corporate-launcher First recorded · 140 lines, first recorded
# Run Claude Code behind a corporate launcher ## What the launcher covers ### Processes that start outside the launcher ### Helper process names in process monitors ## Set up the launcher ## The launcher contract ### Format of the launcher value ## Relationship to `CLAUDE_CODE_SHELL_PREFIX` ## Related resources
The first capture of this source. The page was already there, and this is what it said.
# Run Claude Code behind a corporate launcher
> Route the processes Claude Code starts from its own binary, including the background service and every agent view session, through a required launcher with CLAUDE_CODE_PROCESS_WRAPPER or the processWrapper setting.
Some organizations require every process on a workstation to start through a mandatory launcher. The launcher applies the sandbox, network controls, or credential injection that the company's security posture depends on, and a binary that starts without it is a policy violation.
`CLAUDE_CODE_PROCESS_WRAPPER` starts every process Claude Code launches from its own binary through your launcher: the background service, every session it hosts in [agent view](/docs/en/agent-view), and Claude Code's relaunches after an update. Set it to your launcher's absolute path, and Claude Code runs the launcher with the Claude Code command as its arguments.
A launcher that wraps the `claude` command on your `PATH` can't reach these processes, because they start from the binary's direct path without looking up `claude`.
<Note>
`CLAUDE_CODE_PROCESS_WRAPPER` requires Claude Code v2.1.208 or later. Earlier versions ignore the variable and start every process unwrapped. The equivalent [`processWrapper` setting](/docs/en/settings#available-settings) requires v2.1.210 or later. Earlier versions ignore it as an unknown key, apply no launcher, and report no error.
After deploying either form, use the [Verify step](#set-up-the-launcher) to confirm the running version applies it.
</Note>
## What the launcher covers
With `CLAUDE_CODE_PROCESS_WRAPPER` set, Claude Code starts each of the following processes through your launcher:
* The background service that `claude agents` and background sessions start on demand.
* The terminal host and the Claude Code session inside every agent view row, including the warm standby sessions the service keeps ready.
* Sessions the service respawns after an update or a crash.
* The relaunch Claude Code performs of itself to finish installing an update, including agent view's restart-for-update action.
* The [Remote Control](/docs/en/remote-control) worker processes the background service manages. Requires Claude Code v2.1.210 or later.
* The split-pane teammate sessions that [agent teams](/docs/en/agent-teams) start in tmux or iTerm2. Teammate panes are interactive rather than background processes, but Claude Code starts them from its own binary, so the launcher covers them. Requires Claude Code v2.1.210 or later.
On Windows, the variable is ignored: the launcher contract depends on `exec`, which Windows doesn't support. A Windows machine with the variable set runs every process unwrapped and keeps working, and the only signal is a warning in the [debug log](/docs/en/troubleshooting). If your launcher policy covers Windows, the variable doesn't satisfy it there: count Windows machines as unwrapped when you plan the rollout.
### Processes that start outside the launcher
The following processes don't start through the launcher:
* An [installed background service](/docs/en/agent-view#the-supervisor-process) whose unit was written before the launcher was configured: `launchd` or `systemd` starts that process from its unit file. `/status` and `claude daemon status` warn while the running service and the configured launcher don't match, and the sessions the service spawns still start through the launcher once the service restarts with the variable in its settings.
* A session you start yourself in a terminal, which runs however you invoked it. To cover these sessions, put a script named `claude` in a directory earlier on `PATH` that runs your launcher with the real binary; don't replace the managed symlink. Self-spawns don't consult `PATH`, so the two launchers never stack.
* The first process of a `claude-cli://` deep link, which the operating system's protocol handler starts directly. Everything that session starts in the background afterward runs through the launcher. To close this path entirely, [prevent handler registration](/docs/en/deep-links#registration-and-supported-platforms) with the `disableDeepLinkRegistration` setting.
* The relaunch that `--worktree` combined with `--tmux` performs: the terminal multiplexer starts that pane, not Claude Code's binary.
* The native-messaging host that [Claude in Chrome](/docs/en/chrome) registers: the browser starts it, not Claude Code's binary.
### Helper process names in process monitors
With a launcher configured, `ps` and Activity Monitor show the versioned binary name for the background helper processes instead of Claude Code's `claude bg-pty-host` and `claude bg-spare` labels, because the launcher's `exec` rebuilds the argument list. The renaming is a side effect, not concealment: the processes are otherwise unchanged, and Claude Code identifies its own processes by binary path, never by display name.
## Set up the launcher
<Steps>
<Step title="Write the launcher script">
Create an executable script at an absolute path, such as `/opt/corp/launcher`. Claude Code runs it with the full Claude Code command as its arguments, and the script must end by calling `exec "$@"` so it replaces itself with Claude Code:
```bash theme={null}
#!/bin/sh
# Your organization's setup: enter the sandbox, apply
# network controls, or inject credentials.
exec "$@"
```
Make it executable with `chmod +x`. The setup portion is whatever your launcher must do before Claude Code runs; [the launcher contract](#the-launcher-contract) below lists the rules the script has to follow.
<Note>
If you previously replaced the `~/.local/bin/claude` symlink with your launcher, restore the original symlink in the same change. A replaced symlink makes the first wrapped session start the background service through both launchers at once, and it puts the installation into an externally managed state: `/doctor` reports it, auto-update leaves the file in place, and cleanup of old versions stays disabled until the installer manages that path again.
</Note>
</Step>
<Step title="Set CLAUDE_CODE_PROCESS_WRAPPER in settings">
Set the variable in the `env` block of a settings file so the detached background service inherits it. A shell `export` isn't enough: the background service starts on demand, outlives your shell, and never re-reads shell profiles.
For one machine, add it to `~/.claude/settings.json`. To deploy it to every machine in your organization, put the same block in [managed settings](/docs/en/permissions#managed-settings):
```json theme={null}
{
"env": {
"CLAUDE_CODE_PROCESS_WRAPPER": "/opt/corp/launcher"
}
}
```
When more than one source sets the variable, the managed settings value overrides both `~/.claude/settings.json` and a value exported in the shell, so users can't point self-spawns at a different launcher.
The [`processWrapper` setting](/docs/en/settings#available-settings) carries the same value as a named, top-level settings key. Set it when your organization pushes settings as individual keys rather than an `env` block. The `processWrapper` setting requires Claude Code v2.1.210 or later. The following settings file sets the same launcher through the key:
```json theme={null}
{
"processWrapper": "/opt/corp/launcher"
}
```
`CLAUDE_CODE_PROCESS_WRAPPER` takes precedence when both are set.
Because `processWrapper` is a named setting, an organization that delivers it through [remote managed settings](/docs/en/settings#settings-files) sees it listed on the [security approval dialog](/docs/en/server-managed-settings#security-approval-dialogs) alongside the other settings that run administrator-supplied executables.
Project and local settings can't configure the launcher. A file committed to a repository must not be able to put a binary in front of every Claude Code process on the machine, so Claude Code ignores `CLAUDE_CODE_PROCESS_WRAPPER` in `.claude/settings.json` or `.claude/settings.local.json` with a warning in the [debug log](/docs/en/troubleshooting), and never reads the `processWrapper` key from those files.
</Step>
<Step title="Restart the background service and your sessions">
A running background service and any open `claude` sessions read the variable once at startup, so they keep launching unwrapped processes until restarted. Run `claude daemon stop --any` to stop the on-demand service; the next command that needs it, such as `claude agents`, starts a wrapped one. An [installed service](/docs/en/agent-view#the-supervisor-process) takes `claude daemon stop` without `--any`. Then restart your open `claude` sessions.
On machines you can't restart by hand, the first session started after the settings push retires a leftover unwrapped on-demand service automatically. A machine where no new session starts keeps its unwrapped service until one does, and an installed service always needs the restart in this step.
</Step>
<Step title="Verify">
Run `/status` in a session: the Self-exec entry shows the resolved launch command and warns when the running background service doesn't match it. `claude daemon status` prints the same information from the shell, including after you unset the variable, when `/status` no longer shows the entry.
</Step>
</Steps>
## The launcher contract
When the launcher can't run, Claude Code refuses to start the process instead of starting it unwrapped. On Windows, [the variable is ignored](#what-the-launcher-covers) and processes start unwrapped. Claude Code holds the script to these rules:
* **End with `exec "$@"`.** A launcher that forks a child and exits leaves an orphaned Claude Code process the background service can't track. Agent view marks such a session failed with a message naming the launcher, and the service reaps what the launcher left behind.
* **Don't reorder, absorb, or prepend arguments.** The first argument is the Claude Code binary and everything after it is its argv.
* **Pass every inherited environment variable through to `exec`.** Adding variables, such as injected credentials, is fine; dropping inherited ones is not.
* The per-session authentication tokens, the model and provider selection, and `CLAUDE_CODE_PROCESS_WRAPPER` itself all travel on the inherited environment, so a launcher that rebuilds it from an allow list breaks the sessions it starts, and `/status` reports a launcher mismatch.
* If the launcher must enter a namespace or sandbox that resets the environment, re-export the inherited environment inside it verbatim.
* **Reach `exec` within about three seconds each time the launcher runs.** A cold background dispatch runs the launcher twice in series before the first byte of output, so do slow work such as a single sign-on exchange lazily or from a cache.
* A launcher that runs far past the budget is treated as a stalled start and restarted.
* **Tolerate being invoked from inside itself.** Claude Code applies the launcher to every nested self-spawn, so a launcher that acquires an exclusive resource must detect that it already holds it.
* **Don't write to the terminal before Claude Code starts.** Anything printed before the `exec` is reported as the crash cause if the session dies before initializing.
### Format of the launcher value
`CLAUDE_CODE_PROCESS_WRAPPER` and the `processWrapper` setting take the same format. For most launchers, the value is the script's absolute path, like `/opt/corp/launcher`.
To pass your launcher arguments of its own, write them after the path. Claude Code parses the value as an argument list, not a shell command:
* Whitespace separates tokens, and double quotes group a token that contains spaces.
* A value that starts with `[` is read as a JSON string array, such as `["/opt/corp/launcher", "--profile", "cc"]`.
* Shell syntax doesn't work: there is no variable expansion or globbing, and an unquoted operator such as `;`, `|`, `&`, or `$(` is rejected as a configuration error rather than reinterpreted.
When the value can't be used, Claude Code refuses to start the affected process and [reports the reason](/docs/en/errors#claude_code_process_wrapper-launcher-errors).
## Relationship to `CLAUDE_CODE_SHELL_PREFIX`
`CLAUDE_CODE_PROCESS_WRAPPER` wraps Claude Code's own processes and passes the command through as separate argv tokens for the launcher to `exec`. [`CLAUDE_CODE_SHELL_PREFIX`](/docs/en/env-vars) wraps the shell commands Claude runs on your behalf, such as Bash tool calls, hooks, and the commands that start stdio MCP servers, and passes each one as a single shell-quoted string in `$1` for the wrapper to re-evaluate. A launcher written for one doesn't work as the other.
## Related resources
* [Agent view](/docs/en/agent-view): the background sessions and supervisor process the launcher covers
* [Environment variables](/docs/en/env-vars): the `CLAUDE_CODE_PROCESS_WRAPPER` reference entry
* [Managed settings](/docs/en/permissions#managed-settings): deliver the `env` block across a fleet
* [Launcher error reference](/docs/en/errors#claude_code_process_wrapper-launcher-errors): the refusal messages and how to recover
costs First recorded · 308 lines, first recorded
# Manage costs effectively ## Track your costs ### Using the `/usage` command #### Plan usage breakdown #### When the usage request fails ### Analyze your usage patterns ### Add usage credits to your subscription ## Manage costs for your organization ### Claude for Teams and Enterprise ### Claude Console #### Rate limit recommendations ### Cloud providers ### When a developer asks about a limit ### Agent team token costs ## Reduce token usage ### Manage context proactively ### Choose the right model ### Reduce MCP server overhead ### Install code intelligence plugins for typed languages ### Offload processing to hooks and skills ### Move instructions from CLAUDE.md to skills ### Adjust extended thinking ### Delegate verbose operations to subagents ### Manage agent team costs ### Write specific prompts ### Work efficiently on complex tasks ## Background token usage ## Why usage climbs in a long session ## Understanding changes in Claude Code behavior
The first capture of this source. The page was already there, and this is what it said.
# Manage costs effectively
> Track token usage, set team spend limits, and reduce Claude Code costs with context management, model selection, extended thinking settings, and preprocessing hooks.
Claude Code charges by API token consumption. For subscription plan pricing (Pro, Max, Team, Enterprise), see [claude.com/pricing](https://claude.com/pricing). Per-developer costs vary widely based on model selection, codebase size, and usage patterns such as running multiple instances or automation.
Across enterprise deployments, the average cost is around \$13 per developer per active day and \$150-250 per developer per month, with costs remaining below \$30 per active day for 90% of users. To estimate spend for your own team, start with a small pilot group and use the tracking tools below to establish a baseline before wider rollout.
This page covers how to [track your costs](#track-your-costs), [manage costs for your organization](#manage-costs-for-your-organization), and [reduce token usage](#reduce-token-usage).
## Track your costs
### Using the `/usage` command
<Note>
The Session block in `/usage` shows API token usage and is intended for API users. Claude Max and Pro subscribers have usage included in their subscription, so the session cost figure isn't relevant for billing purposes. Subscribers see plan usage bars, activity stats, and a usage breakdown on the same screen.
</Note>
The Session block at the top of `/usage` shows detailed token usage statistics for your current session. Claude Code computes the dollar figure locally from token counts priced at standard list rates, so it doesn't reflect promotional pricing or contracted discounts and may differ from your actual bill. For authoritative billing, see the Usage page in the [Claude Console](https://platform.claude.com/usage).
```text theme={null}
Total cost: $0.55
Total duration (API): 6m 20s
Total duration (wall): 6h 33m 10s
Total code changes: 0 lines added, 0 lines removed
Usage by model:
claude-sonnet-4-6: 1.2k input, 5.3k output, 940.0k cache read, 50.0k cache write ($0.55)
```
These totals reset when `/clear` starts a new session, so the next session's total cost starts at \$0. Before v2.1.211, they kept accumulating across `/clear` for the lifetime of the Claude Code process.
#### Plan usage breakdown
On a Pro, Max, Team, or Enterprise plan, `/usage` also shows a breakdown of what counts against your plan limits:
* **Attribution**: recent usage attributed to skills, subagents, plugins, and individual MCP servers, each shown as a percentage of the total. An MCP server's share counts only the requests that consumed one of its tool results. Before v2.1.222, after one call to an MCP server, Claude Code attributed every subsequent request to that server, overstating its share.
* **Behavior flags**: behaviors such as long context or cache misses, flagged when one accounts for 10% or more of recent usage.
Press `d` or `w` to switch between the last 24 hours and the last 7 days. The figures are approximate and computed from local session history on this machine, so usage from other devices or claude.ai is not included.
In the [VS Code extension](/docs/en/vs-code#check-account-and-usage), the same breakdown appears in the Account & usage dialog with a Day and Week toggle. Requires Claude Code v2.1.174 or later.
#### When the usage request fails
When the request for your plan limits fails, most often because the usage endpoint is rate limited, `/usage` shows the last usage bars it loaded on this machine within the past 60 minutes, along with a `Showing last-known usage` note stating how long ago that data was fetched. Press `r` to retry; a successful retry replaces the last-known bars with fresh data. Without a snapshot from the past 60 minutes, `/usage` reports that the usage endpoint is rate limited and offers the same retry shortcut. Before v2.1.208, a rate-limited request in a session that hadn't loaded usage yet always showed the error with no bars.
### Analyze your usage patterns
Run [`/insights`](/docs/en/commands#all-commands) for a report on how you work rather than how many tokens you've used. It analyzes your recent sessions on this machine and writes an HTML report covering what you work on, friction points such as misunderstood requests or buggy code, and suggestions for using Claude Code more effectively. A single run analyzes up to 200 sessions it hasn't seen before and skips very short ones. When sessions are left out, the report header shows the analyzed count with the total in parentheses, for example `200 sessions (412 total)`.
Claude Code writes the latest report to `~/.claude/usage-data/report.html` and saves a timestamped copy of each run in the same directory, so earlier reports aren't overwritten. Claude Code deletes reports on the same schedule as the rest of your session data: at startup, it removes files older than [`cleanupPeriodDays`](/docs/en/claude-directory#cleaned-up-automatically), 30 days by default.
You can run `/insights` on any plan and with any provider. The analysis runs through the same provider and account as your regular sessions, and the tokens count against your plan or API usage. Sessions from other devices and claude.ai aren't included.
### Add usage credits to your subscription
[Usage credits](https://support.claude.com/en/articles/12429409-extra-usage-for-paid-claude-plans) let you keep working past your plan's usage limit. To manage them, run `/usage-credits` after signing in with your claude.ai subscription through `/login`; the command isn't available with API key authentication. What it opens depends on your role:
| Your role | What `/usage-credits` does |
| :----------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| Pro or Max subscriber | Opens [**Settings > Usage**](https://claude.ai/settings/usage) on claude.ai in the browser. In its **Usage credits** section you can turn usage credits on or off and check your credit balance, this month's spend, and your monthly spend limit |
| Team or Enterprise member with billing access | Opens your organization's usage settings, [**Admin settings > Usage**](https://claude.ai/admin-settings/usage), in the browser |
| Team or Enterprise member without billing access | Asks you to confirm, then sends a request to your organization's admins. Before v2.1.211, Claude Code sent the request without a confirmation step |
For Team and Enterprise members without billing access, the confirmation appears only in interactive sessions: in non-interactive mode with the `-p` flag and from [Remote Control](/docs/en/remote-control), the command sends no request and tells you to run it in an interactive session instead.
If you run `/usage-credits` again while your earlier request is waiting on an admin, Claude Code tells you a request has already been sent rather than sending a duplicate. After an admin dismisses your request, running the command again sends a new one. Before v2.1.222, a dismissed request also blocked new requests.
On Pro and Max plans, when you reach your spend limit with usage credits still available, Claude Code prompts you to raise or remove the limit without leaving the CLI. If the server rejects the change, see [Could not update your spend limit](/docs/en/errors#could-not-update-your-spend-limit).
## Manage costs for your organization
Which controls you have depends on how your organization accesses Claude Code: a Claude for Teams or Enterprise plan, the Claude Console, or a cloud provider. On Teams and Enterprise plans, usage draws from each member's seat allowance. On the Console and on cloud providers, usage is billed per token to your organization. If your organization mixes sign-in methods, each developer is metered according to the one they authenticated with.
The table maps each setup to where you see spend, where you cap it, and how you pull per-user numbers. On an individual Pro or Max plan you have no organization to manage, so track your own usage-credit spend, including [fast mode](/docs/en/fast-mode#see-where-fast-mode-spend-appears), under [Add usage credits to your subscription](#add-usage-credits-to-your-subscription).
| Your setup | See spend | Cap spend | Per-user reporting |
| :-------------------------------------------------------------------------------------- | :---------------------------------------------------------------------------------------------------------------------------------- | :----------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| [Claude for Teams or Enterprise](#claude-for-teams-and-enterprise) | [Spend report in org analytics](https://support.claude.com/en/articles/12883420-view-usage-analytics-for-team-and-enterprise-plans) | Spend limits in admin settings | [Spend report CSV](https://support.claude.com/en/articles/12883420-view-usage-analytics-for-team-and-enterprise-plans); [Enterprise Analytics API](https://platform.claude.com/docs/en/api/admin/analytics) on Enterprise |
| [Claude Console (API)](#claude-console) | [Console usage page](https://platform.claude.com/usage) | Workspace spend limits | [Console dashboard](https://platform.claude.com/claude-code), [Claude Code Analytics API](https://platform.claude.com/docs/en/build-with-claude/claude-code-analytics-api) |
| [Amazon Bedrock, Google Cloud's Agent Platform, or Microsoft Foundry](#cloud-providers) | Your cloud billing console | Your cloud's budget controls | [OpenTelemetry](/docs/en/monitoring-usage) or an [LLM gateway](/docs/en/llm-gateway) |
[OpenTelemetry export](/docs/en/monitoring-usage) works on every setup and is the only option that streams per-user token and cost metrics into your own observability stack in near real time.
### Claude for Teams and Enterprise
On Claude for Teams and Enterprise plans, each member's Claude Code usage draws from a per-seat allowance that resets on a rolling five-hour window and a weekly window. The allowance is shared with Claude chat and Cowork, and its size depends on the member's [seat tier](https://support.claude.com/en/articles/11845131-use-claude-code-with-your-team-or-enterprise-plan) (Standard or Premium). Your controls live in the claude.ai admin console, not the Claude Console.
* **See spend**: the [spend report in org analytics](https://support.claude.com/en/articles/12883420-view-usage-analytics-for-team-and-enterprise-plans) shows estimated spend per user and per model, with CSV export, updated daily. The report covers usage-credit spend and appears once usage credits are turned on. Usage inside the seat allowance isn't metered in dollars.
* **See adoption**: the [analytics dashboard](https://claude.ai/analytics/claude-code) shows daily active users, sessions, and contribution metrics, with CSV export of contribution data. See [track team usage with analytics](/docs/en/analytics).
* **Cap spend**: the seat allowance is the default ceiling. To let members continue past it, turn on [usage credits](https://support.claude.com/en/articles/12429409-extra-usage-for-paid-claude-plans) and set spend limits at the organization, group, or individual member level.
* **Pull per-user numbers**: on the Enterprise plan, the [Enterprise Analytics API](https://platform.claude.com/docs/en/api/admin/analytics) returns per-user usage and cost reports across Claude surfaces, including Claude Code. A Primary Owner creates a key with the `read:analytics` scope at [claude.ai/analytics/api-keys](https://claude.ai/analytics/api-keys). On the Teams plan, export the [spend report CSV](https://support.claude.com/en/articles/12883420-view-usage-analytics-for-team-and-enterprise-plans), which lists token usage and estimated spend per user and per model.
The [Claude Enterprise consumption guide](https://support.claude.com/en/articles/14782391-claude-enterprise-consumption-guide) is the planning reference for admins. It explains how consumption differs across Claude chat, Claude Code, and Cowork, and gives per-user dollar starting points for budgeting. Budget more for a coding seat than a chat seat: each Claude Code turn carries file contents, tool calls, and multi-step reasoning, so one debugging session can consume more than a day of chat.
### Claude Console
API organizations manage Claude Code spend through [workspaces](https://platform.claude.com/docs/en/build-with-claude/workspaces). You can [set workspace spend limits](https://platform.claude.com/docs/en/build-with-claude/workspaces#workspace-limits) on total Claude Code spend and [view cost and usage reporting](https://platform.claude.com/docs/en/build-with-claude/workspaces#usage-and-cost-tracking) in the Console.
<Note>
When you first authenticate Claude Code with your Claude Console account, a workspace called "Claude Code" is automatically created for you. This workspace provides centralized cost tracking and management for all Claude Code usage in your organization. You cannot create API keys for this workspace; it is exclusively for Claude Code authentication and usage.
For organizations with custom rate limits, Claude Code traffic in this workspace counts toward your organization's overall API rate limits. You can set a [workspace rate limit](https://platform.claude.com/docs/en/api/rate-limits#setting-lower-limits-for-workspaces) on this workspace's Limits page in the Claude Console to cap Claude Code's share and protect other production workloads.
</Note>
For per-user reporting, the [Console dashboard](https://platform.claude.com/claude-code) shows spend and accepted lines per member, and the [Claude Code Analytics API](https://platform.claude.com/docs/en/build-with-claude/claude-code-analytics-api) returns the same daily per-user metrics programmatically with an [Admin API key](https://platform.claude.com/settings/admin-keys). See [analytics for API customers](/docs/en/analytics#access-analytics-for-api-customers).
#### Rate limit recommendations
When setting up Claude Code for teams, consider these Token Per Minute (TPM) and Request Per Minute (RPM) per-user recommendations based on your organization size:
| Team size | TPM per user | RPM per user |
| ------------- | ------------ | ------------ |
| 1-5 users | 200k-300k | 5-7 |
| 5-20 users | 100k-150k | 2.5-3.5 |
| 20-50 users | 50k-75k | 1.25-1.75 |
| 50-100 users | 25k-35k | 0.62-0.87 |
| 100-500 users | 15k-20k | 0.37-0.47 |
| 500+ users | 10k-15k | 0.25-0.35 |
For example, if you have 200 users, you might request 20k TPM for each user, or 4 million total TPM (200\*20,000 = 4 million).
The TPM per user decreases as team size grows because fewer users tend to use Claude Code concurrently in larger organizations. These rate limits apply at the organization level, not per individual user, which means individual users can temporarily consume more than their calculated share when others aren't actively using the service.
<Note>
If you anticipate scenarios with unusually high concurrent usage (such as live training sessions with large groups), you may need higher TPM allocations per user.
</Note>
### Cloud providers
On Amazon Bedrock, Google Cloud's Agent Platform, and Microsoft Foundry, Claude Code is billed per token to your cloud account, and spend controls live in your cloud provider's billing console. Claude Code does not send metrics from your cloud back to Anthropic, so the [analytics dashboards](/docs/en/analytics) and the Claude Code Analytics API do not cover this usage.
For per-user cost attribution, you have three options:
* **OpenTelemetry**: [export metrics](/docs/en/monitoring-usage) from each developer's machine to your own observability stack. This gives you per-user token counts, costs, and tool activity regardless of provider.
* **A Claude apps gateway**: a self-hosted [Claude apps gateway](/docs/en/claude-apps-gateway) provides per-user usage attribution, OTLP metrics with token counts, and [per-user spend limits](/docs/en/claude-apps-gateway-spend-limits) on these providers.
* **An LLM gateway**: route all Claude Code traffic through a proxy that tracks spend per key. Several large enterprises reported using [LiteLLM](/docs/en/llm-gateway), an open-source tool that [tracks spend by key](https://docs.litellm.ai/docs/proxy/virtual_keys#tracking-spend). This project is unaffiliated with Anthropic and has not been audited for security.
### When a developer asks about a limit
Developers usually bring limit questions to their admin, so it helps to know which ceiling they hit. The four situations mean different things:
* **"You've hit your session limit" or "You've hit your weekly limit"**: a seat-based usage window on a subscription plan. These windows are shared across all models, so switching models with `/model` doesn't restore access, though it does keep the developer working after the model-specific "You've hit your Opus limit" message. The message shows when the window resets, and the developer can run `/usage-credits` to request usage beyond the allowance if you have [usage credits](https://support.claude.com/en/articles/12429409-extra-usage-for-paid-claude-plans) turned on. See [usage limit errors](/docs/en/errors#youve-hit-your-session-limit).
* **A spend limit message from a [Claude apps gateway](/docs/en/claude-apps-gateway)**: the developer passed a spend cap you set on your self-hosted gateway, and the gateway blocks their requests until the period resets or you raise the cap. See [gateway spend limits](/docs/en/claude-apps-gateway-spend-limits) for caps, reset schedules, and the message the developer sees.
* **A context or auto-compact warning**: not a usage limit. The conversation has grown close to the session's [auto-compact window](/docs/en/model-config#set-the-auto-compact-window), the threshold where Claude Code summarizes older history to free space. Point the developer at [reduce token usage](#reduce-token-usage).
* **Unexpectedly high spend on an API or cloud-provider plan**: usually traces back to long sessions that were never cleared or to Opus left as the default model. The highest-impact habits to share are clearing between unrelated tasks and matching the model to the job, both covered in [reduce token usage](#reduce-token-usage).
### Agent team token costs
[Agent teams](/docs/en/agent-teams) spawn multiple Claude Code instances, each with its own context window. Token usage scales with the number of active teammates and how long each one runs.
To keep agent team costs manageable:
* Use Sonnet for teammates. It balances capability and cost for coordination tasks.
* Keep teams small. Each teammate runs its own context window, so token usage is roughly proportional to team size.
* Keep spawn prompts focused. Teammates load CLAUDE.md, MCP servers, and skills automatically, but everything in the spawn prompt adds to their context from the start.
* Shut down teammates when their work is done. Each active teammate continues consuming tokens until it exits or the session ends.
* Agent teams are disabled by default. Set `CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1` in your [settings.json](/docs/en/settings) or environment to enable them. See [enable agent teams](/docs/en/agent-teams#enable-agent-teams).
## Reduce token usage
Token costs scale with context size: the more context Claude processes, the more tokens you use. Claude Code automatically optimizes costs through [prompt caching](/docs/en/prompt-caching), which reduces costs for repeated content like system prompts, and auto-compaction, which summarizes conversation history when approaching context limits.
The following strategies help you keep context small and reduce per-message costs.
### Manage context proactively
Use `/usage` to check your current token usage, or [configure your status line](/docs/en/statusline#context-window-usage) to display it continuously.
* **Clear between tasks**: Use `/clear` to start fresh when switching to unrelated work. Stale context wastes tokens on every subsequent message. Use `/rename` before clearing so you can easily find the session later, then `/resume` to return to it.
* **Add custom compaction instructions**: `/compact Focus on code samples and API usage` tells Claude what to preserve during summarization. In a fresh session, `/compact` prints `Not enough messages to compact.` because there's no conversation history to summarize yet.
You can also customize compaction behavior in your CLAUDE.md file at the root of your project:
```markdown theme={null}
# Compact instructions
When you are using compact, please focus on test output and code changes
```
### Choose the right model
Sonnet handles most coding tasks well and costs less than Opus. Reserve Opus for complex architectural decisions or multi-step reasoning. Use `/model` to switch models mid-session, or set a default in `/config`. For simple subagent tasks, specify `model: haiku` in your [subagent configuration](/docs/en/sub-agents#choose-a-model).
### Reduce MCP server overhead
MCP tool definitions are [deferred by default](/docs/en/mcp#scale-with-mcp-tool-search), so only tool names enter context until Claude uses a specific tool. Run `/context` to see what's consuming space.
* **Prefer CLI tools when available**: Tools like `gh`, `aws`, `gcloud`, and `sentry-cli` are still more context-efficient than MCP servers because they don't add any per-tool listing. Claude can run CLI commands directly.
* **Disable unused servers**: Run `/mcp` to see configured servers and disable any you're not actively using.
### Install code intelligence plugins for typed languages
[Code intelligence plugins](/docs/en/discover-plugins#code-intelligence) give Claude precise symbol navigation instead of text-based search, reducing unnecessary file reads when exploring unfamiliar code. A single "go to definition" call replaces what might otherwise be a grep followed by reading multiple candidate files. Installed language servers also report type errors automatically after edits, so Claude catches mistakes without running a compiler.
### Offload processing to hooks and skills
Custom [hooks](/docs/en/hooks) can preprocess data before Claude sees it. Instead of Claude reading a 10,000-line log file to find errors, a hook can grep for `ERROR` and return only matching lines, reducing context from tens of thousands of tokens to hundreds.
A [skill](/docs/en/skills) can give Claude domain knowledge so it doesn't have to explore. For example, a "codebase-overview" skill could describe your project's architecture, key directories, and naming conventions. When Claude invokes the skill, it gets this context immediately instead of spending tokens reading multiple files to understand the structure.
For example, this PreToolUse hook filters test output to show only failures:
<Tabs>
<Tab title="settings.json">
Add this to your [settings.json](/docs/en/settings#settings-files) to run the hook before every Bash command:
```json theme={null}
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "~/.claude/hooks/filter-test-output.sh"
}
]
}
]
}
}
```
</Tab>
<Tab title="filter-test-output.sh">
The hook calls this script. Create the folder with `mkdir -p ~/.claude/hooks`, save the script below as `~/.claude/hooks/filter-test-output.sh`, and make it executable with `chmod +x ~/.claude/hooks/filter-test-output.sh`. It checks if the command is a test runner and modifies it to show only failures:
```bash theme={null}
#!/bin/bash
input=$(cat)
cmd=$(echo "$input" | jq -r '.tool_input.command')
# If running tests, filter to show only failures
if [[ "$cmd" =~ ^(npm test|pytest|go test) ]]; then
filtered_cmd="$cmd 2>&1 | grep -A 5 -E '(FAIL|ERROR|error:)' | head -100"
echo "{\"hookSpecificOutput\":{\"hookEventName\":\"PreToolUse\",\"permissionDecision\":\"allow\",\"updatedInput\":{\"command\":\"$filtered_cmd\"}}}"
else
echo "{}"
fi
```
</Tab>
</Tabs>
To verify the setup, run `/hooks` and check that the hook appears under PreToolUse. You can also start Claude Code with `claude --debug` and run a test command such as `npm test`. The debug log shows `modified tool input keys: [command]` when the hook rewrites the command.
### Move instructions from CLAUDE.md to skills
Your [CLAUDE.md](/docs/en/memory) file is loaded into context at session start. If it contains detailed instructions for specific workflows (like PR reviews or database migrations), those tokens are present even when you're doing unrelated work. [Skills](/docs/en/skills) load on-demand only when invoked, so moving specialized instructions into skills keeps your base context smaller. Aim to keep CLAUDE.md under 200 lines by including only essentials.
### Adjust extended thinking
Extended thinking is enabled by default because it significantly improves performance on complex planning and reasoning tasks. Thinking tokens are billed as output tokens, and the default budget can be tens of thousands of tokens per request depending on the model. For simpler tasks where deep reasoning isn't needed, you can reduce costs by lowering the [effort level](/docs/en/model-config#adjust-effort-level) with `/effort` or in `/model`, disabling thinking in `/config`, or, on models with a [fixed thinking budget](/docs/en/model-config#adaptive-reasoning-and-fixed-thinking-budgets), lowering the budget by setting the `MAX_THINKING_TOKENS` [environment variable](/docs/en/env-vars), for example `MAX_THINKING_TOKENS=8000`. Adaptive-reasoning models ignore nonzero budgets, so use effort levels there instead. Disabling thinking is not available on Fable 5, which always uses extended thinking.
### Delegate verbose operations to subagents
Running tests, fetching documentation, or processing log files can consume significant context. Delegate these to [subagents](/docs/en/sub-agents#isolate-high-volume-operations) so the verbose output stays in the subagent's context while only a summary returns to your main conversation.
### Manage agent team costs
Agent teams use approximately 7x more tokens than standard sessions when teammates run in plan mode, because each teammate maintains its own context window and runs as a separate Claude instance. Keep team tasks small and self-contained to limit per-teammate token usage. See [agent teams](/docs/en/agent-teams) for details.
### Write specific prompts
Vague requests like "improve this codebase" trigger broad scanning. Specific requests like "add input validation to the login function in auth.ts" let Claude work efficiently with minimal file reads.
### Work efficiently on complex tasks
For longer or more complex work, these habits help avoid wasted tokens from going down the wrong path:
* **Use plan mode for complex tasks**: Press Shift+Tab to cycle to [plan mode](/docs/en/permission-modes#analyze-before-you-edit-with-plan-mode) before implementation. Claude explores the codebase and proposes an approach for your approval, preventing expensive re-work when the initial direction is wrong.
* **Course-correct early**: If Claude starts heading the wrong direction, press Escape to stop immediately. Use `/rewind` or double-tap Escape to restore conversation and code to a previous checkpoint.
* **Give verification targets**: Include test cases, paste screenshots, or define expected output in your prompt. When Claude can verify its own work, it catches issues before you need to request fixes.
* **Test incrementally**: Write one file, test it, then continue. This catches issues early when they're cheap to fix.
## Background token usage
Claude Code uses tokens for some background functionality even when idle:
* **Conversation summarization**: Background jobs that summarize previous conversations for the `claude --resume` feature
* **Command processing**: Some commands like `/usage` may generate requests to check status
These background processes consume a small amount of tokens (typically under \$0.04 per session) even without active interaction.
## Why usage climbs in a long session
A session that has been open for hours can use far more of your plan limits than your activity suggests, usually for one of these reasons:
* **Long context**: Claude Code sends your full conversation with every request, and each time Claude uses tools it sends another request carrying that batch of tool results. With [prompt caching](/docs/en/prompt-caching), Claude Code re-reads that history at the [cached token rate](https://platform.claude.com/docs/en/about-claude/pricing), so a one-line question in a session that has been open all day still draws usage for the whole conversation. See [Manage context proactively](#manage-context-proactively) for ways to keep your context small
* **Cache misses**: your first message after a break longer than the [cache lifetime](/docs/en/prompt-caching#cache-lifetime) misses the cache and reprocesses your full context. The lifetime is an hour on a subscription and drops to five minutes once you're drawing on [usage credits](https://support.claude.com/en/articles/12429409-extra-usage-for-paid-claude-plans); on an API key or cloud provider, it's five minutes by default. You can keep the one-hour lifetime while drawing on usage credits by setting [`ENABLE_PROMPT_CACHING_1H=1`](/docs/en/env-vars). On Pro and Max plans, when you resume a large session after a long break, Claude Code [offers to resume from a summary](/docs/en/sessions#resume-from-a-summary) so later requests don't carry the full history
* **Scheduled tasks**: a [scheduled task](/docs/en/scheduled-tasks) fires on its interval even while the session is idle, sending your full context each time
* **Cross-session messages**: Claude Code delivers a [message from another of your sessions](/docs/en/cross-session-messaging) as a new turn when this session sits idle, sending your full context each time. To hold inbound messages instead of delivering them, set [`crossSessionInbound`](/docs/en/settings#available-settings) to `hold`
* **Agent teammates**: each active [teammate](#agent-team-token-costs) keeps consuming tokens until it exits
* **Compaction**: `/compact` reads the conversation it summarizes, so [compacting a large context](/docs/en/prompt-caching#compacting-the-conversation) is itself a large request. When you want a fresh start instead of continuity, `/clear` costs nothing
On a Pro, Max, Team, or Enterprise plan, the `/usage` breakdown flags behaviors that account for 10% or more of your recent usage, such as long context or cache misses, each with a tip to reduce it.
## Understanding changes in Claude Code behavior
Cut at 300 lines. The page has the rest.
cross-session-messaging First recorded · 274 lines, first recorded
# Message your other Claude Code sessions ## When to use cross-session messaging ## Message another session ### Message delivery ### See which sessions Claude can reach ### Message sessions on other machines ## How a session treats an incoming message ### Control inbound messages ### Non-interactive sessions ## Restrict cross-session messaging ### Require approval for cross-machine messages ### Turn off cross-session messaging ## Availability ## Limitations ## Related resources
The first capture of this source. The page was already there, and this is what it said.
# Message your other Claude Code sessions
> Let Claude list and message your other Claude Code sessions on this machine, and reach your sessions on other machines or on the web.
<Note>
Cross-session messaging requires Claude Code v2.1.224 or later and runs on macOS and Linux. When a session meets the requirements, messaging is on with nothing to enable. See [Availability](#availability) for provider requirements and how to confirm a session has it.
</Note>
Cross-session messaging lets Claude deliver a message from one of your Claude Code sessions to another. When a change in one session breaks what another is building on, Claude can warn that session before you notice. When one session settles a question another is blocked on, Claude can send the answer across.
A message is a piece of text one Claude writes to another, never conversation history or files. To move a whole conversation or its context, [resume the session](/docs/en/sessions#resume-a-session) instead.
Claude uses two tools for this: `ListAgents` to discover which agents it can reach, and `SendMessage` to deliver a message to one of them by name. With the same `SendMessage` tool, Claude can also message [subagents](/docs/en/sub-agents#resume-subagents) and [agent team](/docs/en/agent-teams) teammates within a single session or team. This page covers messages between your independent sessions.
## When to use cross-session messaging
Use messaging when one of your sessions has something another session needs mid-task. Claude can send a message on its own when it sees the need, for example after making a change that affects work another session is doing, or you can ask it to send one. The common cases:
* **Hand over a finding**: when one session discovers a breaking change or makes a decision, Claude summarizes it for the session working on the affected area, instead of you re-explaining it there.
* **Coordinate parallel worktrees**: when sessions work the same repository in separate [worktrees](/docs/en/worktrees), Claude can tell the other sessions what landed.
* **Get status from long-running work**: have a migration or test run report back to the session you're watching, or ask it yourself from there.
* **Message across machines**: reach one of your sessions on another machine or on the web.
Use messaging between independent sessions that you start and steer yourself. Claude Code has a dedicated feature for each of the other ways to run or reach multiple sessions, so use the one built for what you're doing instead:
* To continue one conversation in another terminal, or share its context with a new session, [resume the session](/docs/en/sessions#resume-a-session)
* For a coordinated team of sessions Claude spawns and supervises, use [agent teams](/docs/en/agent-teams)
* To watch and steer many sessions from one place, use [agent view](/docs/en/agent-view)
* To steer a session yourself from your phone or another device, rather than have sessions message each other, use [Remote Control](/docs/en/remote-control)
* To push external events, such as CI results or chat messages, into a session, use [channels](/docs/en/channels)
## Message another session
When one of your sessions learns something another session needs, such as a finding, a status, or a decision, Claude passes it along instead of you copy-pasting between terminals. Claude discovers the target with `ListAgents` and sends with `SendMessage`, so you never call either tool yourself. Claude can decide to send a message without being asked, and you can also prompt for one.
To prompt one yourself, tell Claude what you want the other session to know or do. This example is a prompt you type, not a message Claude sends:
```text wrap theme={null}
Ask the session running in my other terminal whether the migration finished
```
Claude writes the actual message itself, so your prompt can leave the content to Claude. This prompt asks for a summary without dictating its wording, and what Claude sends varies:
```text wrap theme={null}
Explain what we just did to the session working on the payments API
```
To name the target yourself, mention the session in your prompt: type `@` followed by the first letters of the session's name and pick the session from the typeahead, the same way you [@-mention a subagent](/docs/en/sub-agents#invoke-subagents-explicitly). Requires Claude Code v2.1.232 or later. Claude Code inserts the mention, such as `@api-worker`, and tells Claude which session it names, so Claude can message that session without listing your sessions first. This prompt names the target with a mention:
```text wrap theme={null}
Let @api-worker know the schema migration finished
```
Once you type at least one letter after the `@`, Claude Code suggests your other live sessions on this machine; after a bare `@`, session rows don't appear. A cloud or Remote Control session appears in the suggestions only after Claude has already listed or messaged your sessions beyond this machine. You can also type the mention without the picker. When more than one live session answers to the mentioned name, Claude asks you which one you mean before sending.
For what the message Claude writes looks like when it arrives, including an example of one, see [what a message looks like](#what-a-message-looks-like).
### Message delivery
The receiving Claude reads the message between tool calls during an active turn, so a running tool is never interrupted. When the receiving session is idle, Claude Code starts a new turn with the message.
Between two ordinary interactive sessions with default settings, Claude Code delivers the message. Delivery isn't guaranteed in every configuration, though. The receiving session checks each arriving message against its own [inbound controls](#control-inbound-messages), and the check ends in one of three outcomes:
* **Delivered**: Claude Code passes the message to the receiving Claude.
* **Held**: Claude Code sets the message aside undelivered. A held message reaches Claude only when you approve it or a later mode or settings change allows it.
* **Refused**: Claude Code drops the message without delivering it.
Once delivered, the message counts toward [usage](/docs/en/costs) like a prompt you type, and the receiving Claude can reply to the sender the same way, except in the [one-way cross-machine case](#message-sessions-on-other-machines).
Permission boundaries stay per-session. Claude is instructed never to ask another session for an action that was denied or blocked in its own session, or that its own permission settings would block, and to route that work back to you instead. On the receiving side, the [receiving session's own permission prompts and rules still apply](#how-a-session-treats-an-incoming-message) to anything the message asks for.
### See which sessions Claude can reach
Claude finds a message's target on its own, so you don't need to run anything before asking it to send. To see for yourself which sessions Claude can reach, run the `/list-agents` command. It lists each session with the name it answers to, and that name is where Claude addresses a message. The listing covers:
* **Subagents**: agents running inside the current session. [Agent team](/docs/en/agent-teams) teammates aren't listed; Claude messages them through the team's own roster.
* **Your other local sessions**: Claude Code sessions running on the same machine, including [background sessions](/docs/en/agent-view). A session appears only when it binds an [inbox socket](#the-sessions-inbox-socket).
* **Your cloud sessions**: your [Claude Code on the web](/docs/en/claude-code-on-the-web) sessions, shown while this session is connected to [Remote Control](/docs/en/remote-control). Claude Code labels them `cloud` in the listing.
* **Your Remote Control sessions on other machines**: shown while this session is connected to [Remote Control](/docs/en/remote-control), and labeled `Remote Control`. Claude Code shows `offline` as the status of a session whose Remote Control connection has dropped.
Claude addresses a session beyond this machine by name, the same as a local session. See [Message sessions on other machines](#message-sessions-on-other-machines) for how those messages travel.
A session answers to the name you set with the [`/rename`](/docs/en/commands) command or the [`--name`](/docs/en/cli-reference#cli-flags) flag. When you don't set one, Claude Code names the session itself. For an interactive session, Claude Code derives the name from the working directory's folder name, such as `my-app-3f` in a `my-app` directory.
When you rename a session, or start or resume an interactive one, with a name another live session on this machine already uses, Claude Code leaves the name with the session that already has it and [renames yours to a variant](/docs/en/sessions#name-your-sessions). Sessions can still share a name, for example when one of them runs an earlier version of Claude Code or the shared name is one Claude Code generated. Claude Code shows each local session's working directory in the `/list-agents` output, so you can tell same-named sessions apart when they run in different directories. Claude addresses the message in one of two ways, depending on how many live sessions answer to the name:
* **One session answers to the name**: Claude Code delivers the message on the name alone.
* **Several sessions share the name, or Claude Code couldn't check everywhere your sessions run**: Claude adds a short identifier to each row of its listing and uses the identifier in the address.
### Message sessions on other machines
How a message travels, and whether it passes through Anthropic servers, depends on where the target session runs:
| Where the other session runs | How the message travels |
| :------------------------------------------------------ | :------------------------------------------------------------------------------------------------------ |
| On this machine | Over a per-session socket, never through Anthropic servers |
| On another of your machines | Through Anthropic servers, arriving over that machine's [Remote Control](/docs/en/remote-control) connection |
| On [Claude Code on the web](/docs/en/claude-code-on-the-web) | Through Anthropic servers, straight to the cloud session |
Starting a conversation with a session on another of your machines requires Claude Code v2.1.225 or later and a target that [appears in the listing](#see-which-sessions-claude-can-reach). Before v2.1.225, Claude could only reply to a message that arrived from one.
Same-machine delivery works wherever the feature is enabled. Each session registers itself in files on disk and binds its inbox socket there. When Claude lists or messages your local sessions, Claude Code reads those files to find them, so two sessions can reach each other only when they can see the same files. A container has its own filesystem, so a session inside it and a session on the host can't reach each other. Two sessions inside the same container can still message each other, including on a [self-hosted runner](/docs/en/self-hosted-environments).
While this session is connected to Remote Control, when you message a session on another of your machines, Claude Code shows the message in that session's conversation under this session's Remote Control name. The Claude on that machine can reply to that name. For example, when this session is connected to Remote Control as `laptop-graceful-unicorn` and you message your desktop, you see the message in the desktop session under `laptop-graceful-unicorn`.
If this session isn't connected to Remote Control when Claude sends to a session beyond this machine, the message still goes through, but without a [reply address](#what-a-message-looks-like), so the receiving Claude can't answer it. Claude is told as much when it sends.
To require your approval before any message goes beyond this machine, set [`isolatePeerMachines`](#require-approval-for-cross-machine-messages).
## How a session treats an incoming message
When session A messages session B, Claude Code tells B's Claude that the message came from another session, not from you, and limits what the message can do:
* **It can't approve anything**: a message from another session never counts as your consent, so it can't answer a pending permission prompt on your behalf.
* **It can't change configuration**: Claude Code instructs the receiving Claude never to change permission settings, `CLAUDE.md`, or other configuration because another session asked.
* **Commands don't run**: a command in the message's text, such as `/compact`, arrives as plain text. Claude Code never executes it.
* **Permission prompts still fire**: if acting on the message requires a permission the receiving session doesn't have, you see the same prompt you'd see for any other work.
<h3 id="what-a-message-looks-like">
What a message looks like
</h3>
When the message arrives, it appears in the conversation under the sender's session name and stays there. Claude Code queues it while Claude is mid-turn, or starts a new turn with it right away when the session is idle.
A message is a piece of text one Claude writes to another. Claude receives it with the sender's name and a reply address, except for a [one-way cross-machine message](#message-sessions-on-other-machines), which carries no reply address. You see the name and the text, and the receiving session gets only that text, never the sender's conversation history or files.
This example is a message one Claude wrote to another, as the receiving session sees it:
```text wrap theme={null}
Schema migration finished: the new column is tenant_id, and rebasing on main is safe now.
```
### Control inbound messages
Set [`crossSessionInbound`](/docs/en/settings#available-settings) to choose what a session does with messages arriving from your other sessions:
| Value | Behavior |
| :------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `accept` | Claude Code delivers each message to Claude |
| `hold` | Claude Code shows a notice for each message and doesn't deliver it. If an `accept` later applies, per the [precedence rules](/docs/en/settings#available-settings), Claude Code releases the held messages |
| `refuse` | Claude Code drops each message without delivering it |
Beyond editing a settings file, you can select the value in the `/config` row **Messages from your other sessions**. Claude Code writes the value you select to your user settings. The row requires Claude Code v2.1.232 or later and doesn't appear while managed settings or the `--settings` flag sets the key, since a user-settings value wouldn't apply then. Claude Code rejects the `/config crossSessionInbound=value` shorthand for this key.
To see which value applies, follow the `crossSessionInbound` precedence rules in the [settings reference](/docs/en/settings#available-settings). When no value applies, Claude Code decides per message from the two sessions' permission modes. It groups sessions that [bypass permission prompts](/docs/en/permission-modes#skip-all-checks-with-bypasspermissions-mode) into one class, and every other session into the other. Plan mode counts as bypassing in sessions with bypass permissions available, and [auto](/docs/en/permission-modes#eliminate-prompts-with-auto-mode), `acceptEdits`, and `dontAsk` count as prompting:
* **The receiving session prompts for permissions**: Claude Code delivers each message. It holds one for your approval only when the sending session identifies itself as bypassing permission prompts.
* **The receiving session bypasses permission prompts**: Claude Code holds each message for your approval. It delivers one only when the sending session identifies itself as also bypassing.
When the default holds a message, Claude Code opens an approval dialog in the receiving session. The dialog shows the sender and a preview:
* **Approve** delivers that one message to Claude.
* **Deny**, or dismissing the dialog, drops it.
* When the dialog stays unanswered past the [`dialogExpiry`](/docs/en/settings#available-settings) deadline, Claude Code closes it and drops the message. The deadline defaults to five minutes. While no terminal is attached to a [background session](/docs/en/agent-view), Claude Code leaves the dialog open past the deadline. After you attach, Claude Code closes the dialog and drops the message only if it stays unanswered for a full deadline period.
* If this session's permission-mode class changes while messages are held, Claude Code re-applies the inbound rules, delivers the messages they now accept, and shows a notice.
* If a change makes `refuse` apply while messages are held, Claude Code drops every held message and reports a denial to each sender it can reach.
When the sender runs on the same machine, Claude Code tells the sending session what happened. A notice appears there when the message is held, and a follow-up reports the outcome when the receiver later delivers, denies, or expires it. A message refused on arrival produces no sender-side notice.
Claude Code holds at most 100 messages, separately from the delivery queue, and past that drops the oldest.
### Non-interactive sessions
Claude Code binds an inbox socket for a [`claude -p`](/docs/en/headless) session like an interactive one, so a long-running `-p` worker can receive messages and appears in the listing. When you start a session in [bare mode](/docs/en/headless#start-faster-with-bare-mode), Claude Code doesn't bind the socket, so that session can't receive messages and doesn't appear in the agent list.
A `-p` session can't show the approval dialog. When the [inbound default](#control-inbound-messages) holds a message there, Claude Code keeps it for the same [`dialogExpiry`](/docs/en/settings#available-settings) deadline the dialog uses, five minutes by default:
* **Before the deadline**: if a mode or settings change allows the message, Claude Code delivers it.
* **Past the deadline**: Claude Code drops the message and reports it as expired to a sender it can reach.
Set `dialogExpiry` to `"never"` to keep default-held messages until the session ends. A message held by an explicit `hold` setting doesn't expire; Claude Code delivers it only when an `accept` later applies.
When the session ends with messages still held, Claude Code reports them as expired to each sender it can reach. Before v2.1.225, no deadline applied in a `-p` session: a held message stayed held unless a permission-mode change during the run delivered it, and a session that ended with held messages reported nothing to their senders.
To let a `-p` worker take messages unattended, start it with `crossSessionInbound` set to `accept` in its `--settings` value. An `accept` in your user settings also works but applies to every session you run.
<h3 id="the-sessions-inbox-socket">
The session's inbox socket
</h3>
Read this section when a session you expect isn't in the agent list, when you want a script or hook to post into a session, or when a sandboxed command can't reach the socket.
Claude Code binds an inbox socket for each session with cross-session messaging enabled, where other sessions on the machine deliver messages. It restricts the socket to your operating-system user, so on a shared machine another user's sessions can't reach it. For which session kinds bind one, see [Non-interactive sessions](#non-interactive-sessions).
You can find the path in two places:
* `/status` shows it in the `Peer address` row. The path is prefixed with `uds:`.
* Claude Code exports it to [hooks](/docs/en/hooks) and Bash commands as the [`CLAUDE_CODE_MESSAGING_SOCKET`](/docs/en/env-vars#variables) environment variable:
* In a session that starts with messaging on, Claude Code exports the variable before any hook runs, including `SessionStart`.
* When a session starts before Claude Code has fetched the feature flag that turns messaging on, such as the first session after you install Claude Code or upgrade from a version without messaging, Claude Code binds the inbox and exports the variable as soon as the fetch completes. Hooks and processes that started before that point keep the variable unset, while later hooks and Bash commands see it.
* Each session exports its own socket, never one inherited from a parent session.
Alongside the path, Claude Code exports a per-session token as [`CLAUDE_CODE_MESSAGING_TOKEN`](/docs/en/env-vars#variables). A script posting to its own session's socket can send `{"type":"auth","token":"<token>"}` as the first line of its connection. The [own-child rules](#own-child-messages) below say when Claude Code consults the token and how it treats a message it can't verify.
<span id="own-child-messages" />Claude Code runs messages arriving on the socket through the same [inbound controls](#control-inbound-messages) as any other peer message, with one exception and one prerequisite:
* **Own-child messages**: when no `crossSessionInbound` value applies, Claude Code delivers a message it verifies came from the session's own child processes, such as a hook or Bash command posting back to its own session's socket.
* On Linux, including inside WSL 2, Claude Code can verify by process evidence even for a child that has already exited. On macOS it can verify that way only while the posting process is still running, and in a container where Claude Code runs as process ID 1 it has no process evidence at all.
* Where that process evidence is missing, on macOS after the posting process has exited and in containers where Claude Code runs as process ID 1, Claude Code verifies a child that sent the session's exported [`CLAUDE_CODE_MESSAGING_TOKEN`](/docs/en/env-vars#variables) as its first-line auth frame.
* When Claude Code can verify neither way, it treats the message like any other that asserts no permission class, so a session that bypasses permission prompts holds it for your approval.
* **Sandboxed sessions**: control whether a Bash command can reach the socket from inside the [sandbox](/docs/en/sandboxing) with the sandbox's Unix-socket settings, [`sandbox.network.allowAllUnixSockets` and `sandbox.network.allowUnixSockets`](/docs/en/settings#sandbox-settings).
## Restrict cross-session messaging
Beyond the per-message defaults, you can narrow messaging in two ways. Require your approval before any message leaves the machine, or turn messaging off for a session or an organization.
### Require approval for cross-machine messages
Set [`isolatePeerMachines`](/docs/en/settings#available-settings) to `true` to require your explicit approval before any `SendMessage` reaches a session beyond this machine:
```json theme={null}
{
"isolatePeerMachines": true
}
```
With this set, Claude Code asks for your approval before Claude's message to a session beyond this machine leaves, even in `bypassPermissions` mode, which skips ordinary permission prompts. A `true` from any settings scope applies, so a checked-in project file can turn the requirement on but not off. Claude Code doesn't prompt for messages between sessions on the same machine.
### Turn off cross-session messaging
Receiving and sending are separate controls, so turn off whichever direction you need, or both. Use `crossSessionInbound` for messages that arrive, and permission rules for what Claude here can send or list:
* **Stop receiving**: set `crossSessionInbound` to `refuse`, and Claude Code drops inbound peer messages without delivering them. From project or local settings, `refuse` applies over every other source, and from your user settings it applies unless managed settings or the `--settings` flag set a value.
* **Stop sending and listing**: add [permission deny rules](/docs/en/permissions#tool-specific-permission-rules) naming `SendMessage` and `ListAgents`. Both take the bare tool name with no specifier.
Administrators can turn both sides off for an organization in [managed settings](/docs/en/permissions#managed-settings), combining the deny rules with the `refuse`:
```json theme={null}
{
"permissions": {
"deny": ["SendMessage", "ListAgents"]
},
"crossSessionInbound": "refuse"
}
```
With this in place, Claude Code still binds each session's inbox socket, but drops every message that arrives on it without delivering anything to Claude. Denying `SendMessage` also removes messaging to subagents and agent-team teammates, since the same tool serves both. A refusing session shows no visible change, in its own `/status` or in other sessions' listings, so confirm the setting from the session's configuration.
## Availability
Cross-session messaging requires Claude Code v2.1.224 or later. Availability also depends on your platform, provider, and configuration:
* **Operating system**: available on macOS and Linux, including Linux inside WSL 2. Claude Code doesn't offer cross-session messaging on native Windows.
* **Provider**: not available on Amazon Bedrock, Claude Platform on AWS, Google Cloud's Agent Platform, or Microsoft Foundry.
* **Feature-flag evaluation**: when any of [`CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC`, `DISABLE_TELEMETRY`, `DO_NOT_TRACK`, or `DISABLE_GROWTHBOOK`](/docs/en/env-vars#variables) turns off the feature-flag evaluation the feature depends on, cross-session messaging stays off. Each variable's row says which values do that. Unset whichever applies. These variables can come from your shell, from a settings file's `env` map, or from managed settings.
To check a session, type `/list-agents`, also available as `/peers`. The result separates a session that doesn't have the feature from a session where something narrower blocked a message, such as a missing `SendMessage` tool or a refused send:
* **`/list-agents` isn't recognized**: the session doesn't have cross-session messaging. Work through the requirements above, starting with `claude --version` for the version requirement.
* **`/list-agents` works but a send didn't arrive**: messaging is on, and something narrower applies:
* **Deny rules**: a [permission deny rule](#turn-off-cross-session-messaging) removes the `SendMessage` and `ListAgents` tools.
* **Inbound controls**: the [receiving session's inbound controls](#control-inbound-messages) can hold or drop what you send it.
* **Cloud session missing**: a cloud session appears only while this session is connected to [Remote Control](/docs/en/remote-control).
* **Other-machine session missing**: a session on another of your machines appears only when it runs with [Remote Control](/docs/en/remote-control) and this session is connected as well.
* **Starting a conversation**: [Message sessions on other machines](#message-sessions-on-other-machines) covers starting a conversation with a session beyond this machine.
In a session with messaging, `/status` also shows a `Peer address` row with the session's own inbox address.
## Limitations
The limits here are properties of the messaging channel itself and apply wherever the feature runs. For platform and provider gaps, see [Availability](#availability) instead.
* **Plain text only**: Claude sends only plain text across sessions. Structured [agent team](/docs/en/agent-teams) protocol messages stay within a team.
* **Message loops are throttled**: Claude Code rate-limits repeated messages per sender, drops identical repeats arriving within a short window, and caps accepted messages waiting for Claude to read them at 50 per session. A message loop between two sessions therefore stops on its own.
## Related resources
* [Subagents](/docs/en/sub-agents#resume-subagents) and [agent teams](/docs/en/agent-teams#messages-between-agents): messaging within a single session or team
* [Background agents](/docs/en/agent-view): dispatch and monitor the parallel sessions you might message
* [Remote Control](/docs/en/remote-control): connect this session to reach your sessions on other machines
* [Settings](/docs/en/settings#available-settings): `crossSessionInbound`, `isolatePeerMachines`, and `dialogExpiry`
* [Permission modes](/docs/en/permission-modes): the modes behind the inbound default's two classes
* [Tools reference](/docs/en/tools-reference): the `ListAgents` and `SendMessage` rows in the tools table
* [Run agents in parallel](/docs/en/agents): compare the ways Claude Code runs multiple agents
data-usage First recorded · 133 lines, first recorded
# Data usage ## Data policies ### Data training policy ### Development Partner Program ### Feedback using the `/feedback` command ### Session quality surveys ### Data retention ## Data access ## Local Claude Code: Data flow and dependencies ### Cloud execution: Data flow and dependencies ## Telemetry services ## Default behaviors by API provider ### WebFetch domain safety check
The first capture of this source. The page was already there, and this is what it said.
# Data usage > Learn about Anthropic's data usage policies for Claude ## Data policies ### Data training policy **Consumer users (Free, Pro, and Max plans)**: We give you the choice to allow your data to be used to improve future Claude models. We will train new models using data from Free, Pro, and Max accounts when this setting is on (including when you use Claude Code from these accounts). **Commercial users**: (Team and Enterprise plans, API, 3rd-party platforms, and Claude Gov) maintain existing policies: Anthropic does not train generative models using code or prompts sent to Claude Code under commercial terms, unless the customer has chosen to provide their data to us for model improvement (for example, the [Developer Partner Program](https://support.claude.com/en/articles/11174108-about-the-development-partner-program)). ### Development Partner Program If you explicitly opt in to methods to provide us with materials to train on, such as via the [Development Partner Program](https://support.claude.com/en/articles/11174108-about-the-development-partner-program), we may use those materials provided to train our models. An organization admin can expressly opt-in to the Development Partner Program for their organization. Note that this program is available only for Anthropic first-party API, and not for Amazon Bedrock or Google Cloud's Agent Platform users. ### Feedback using the `/feedback` command If you choose to send us feedback about Claude Code using the `/feedback` command, we may use your feedback to improve our products and services. Transcripts shared via `/feedback`, or via `/bug` and `/share`, which report through the same path, are retained for 5 years. ### Session quality surveys When you see the "How is Claude doing this session?" prompt in Claude Code, responding to this survey, including selecting "Dismiss", records only your rating. We do not collect or store any conversation transcripts, inputs, outputs, or other session data as part of the rating prompt itself. Unlike thumbs up/down feedback or `/feedback` reports, this session quality survey is a simple product satisfaction metric. After the rating prompt, you may see a separate follow-up asking "Can Anthropic look at your session transcript to help us improve Claude Code?". This is an optional second step distinct from the rating: * **Yes**: uploads your conversation transcript, any subagent transcripts, and the raw session log file from disk to Anthropic. Known API key and token patterns are redacted before upload. Source code, file contents, and other conversation content are uploaded as-is. Shared transcripts are retained for up to 6 months. On Amazon Bedrock, Google Cloud's Agent Platform, Microsoft Foundry, and signed-in [Claude apps gateway](/docs/en/claude-apps-gateway) sessions, Yes writes the same payload to a local archive under `~/.claude/feedback-bundles/` instead of uploading; nothing leaves your machine until you forward that file. * **No**: declines without sending anything * **Don't ask again**: declines and stops this follow-up from appearing in future sessions Nothing is uploaded unless you explicitly select **Yes**. Organizations with [zero data retention](/docs/en/zero-data-retention), or where product feedback is disabled by organization policy, or where `CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC` is set, never see this follow-up. Your responses to this survey, including session transcripts submitted after the rating prompt, do not impact your data training preferences and cannot be used to train our AI models. To disable these surveys, set `CLAUDE_CODE_DISABLE_FEEDBACK_SURVEY=1`. The survey is also disabled when `DISABLE_TELEMETRY`, `DO_NOT_TRACK`, or `CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC` is set. Organizations that block nonessential traffic but capture survey responses through their own [OpenTelemetry collector](/docs/en/monitoring-usage) can opt the survey back in by setting `CLAUDE_CODE_ENABLE_FEEDBACK_SURVEY_FOR_OTEL=1`. The survey then logs ratings to the configured collector only. The transcript-share follow-up and all other Anthropic-bound feedback traffic stay disabled. To control frequency instead of disabling, set [`feedbackSurveyRate`](/docs/en/settings#available-settings) in your settings file to a probability between `0` and `1`. ### Data retention Anthropic retains Claude Code data based on your account type and preferences. **Consumer users (Free, Pro, and Max plans)**: * Users who allow data use for model improvement: 5-year retention period to support model development and safety improvements * Users who don't allow data use for model improvement: 30-day retention period * Privacy settings can be changed at any time at [claude.ai/settings/data-privacy-controls](https://claude.ai/settings/data-privacy-controls). **Commercial users (Team, Enterprise, and API)**: * Standard: 30-day retention period * [Zero data retention](/docs/en/zero-data-retention): available to qualified accounts for Claude Code on Claude for Enterprise. ZDR is not included in the standard Enterprise plan; it is enabled on a per-organization basis by your account team after confirming eligibility * Local caching: Claude Code clients store session transcripts locally in plaintext under `~/.claude/projects/` for 30 days by default to enable session resumption. Adjust the period with `cleanupPeriodDays`. See [application data](/docs/en/claude-directory#application-data) for what's stored and how to clear it. You can delete individual Claude Code on the web sessions at any time. Deleting a session permanently removes the session's event data. For instructions on how to delete sessions, see [Delete sessions](/docs/en/claude-code-on-the-web#delete-sessions). Learn more about data retention practices in our [Privacy Center](https://privacy.anthropic.com/). For full details, please review our [Commercial Terms of Service](https://www.anthropic.com/legal/commercial-terms) (for Team, Enterprise, and API users) or [Consumer Terms](https://www.anthropic.com/legal/consumer-terms) (for Free, Pro, and Max users) and [Privacy Policy](https://www.anthropic.com/legal/privacy). ## Data access For all first party users, you can learn more about what data is logged for [local Claude Code](#local-claude-code-data-flow-and-dependencies) and [remote Claude Code](#cloud-execution-data-flow-and-dependencies). [Remote Control](/docs/en/remote-control) sessions follow the local data flow since all execution happens on your machine; while connected, the session transcript is also stored on Anthropic servers to sync the conversation across devices, as described in [Connection and security](/docs/en/remote-control#connection-and-security). Note for remote Claude Code, Claude accesses the repository where you initiate your Claude Code session. Claude does not access repositories that you have connected but have not started a session in. ## Local Claude Code: Data flow and dependencies The diagram below shows how Claude Code connects to external services during installation and normal operation. Solid lines indicate required connections, while dashed lines represent optional or user-initiated data flows. <img src="https://mintcdn.com/claude-code/YR4DRZyI3CdsXkiT/images/claude-code-data-flow.svg?fit=max&auto=format&n=YR4DRZyI3CdsXkiT&q=85&s=2846ea92cfc2297b8620c31c82b482ad" className="dark:hidden" alt="Diagram showing Claude Code's external connections: install/update connects to the distribution server, and user requests connect to Anthropic's Console auth and public-api, with optional telemetry flows carrying metrics and error reports to Anthropic and third-party services. Feedback sent with /feedback goes to Google Cloud Storage and optionally creates a GitHub issue" width="720" height="520" data-path="images/claude-code-data-flow.svg" /> <img src="https://mintcdn.com/claude-code/_xqph1dUOslCOwsj/images/claude-code-data-flow-dark.svg?fit=max&auto=format&n=_xqph1dUOslCOwsj&q=85&s=4fb6e8c88a9740845217bf2a2b040877" className="hidden dark:block" alt="Diagram showing Claude Code's external connections: install/update connects to the distribution server, and user requests connect to Anthropic's Console auth and public-api, with optional telemetry flows carrying metrics and error reports to Anthropic and third-party services. Feedback sent with /feedback goes to Google Cloud Storage and optionally creates a GitHub issue" width="720" height="520" data-path="images/claude-code-data-flow-dark.svg" /> Claude Code runs locally. To interact with the LLM, Claude Code sends data over the network. This data includes all user prompts and model outputs, encrypted in transit via TLS 1.2+. Claude Code is compatible with most popular VPNs and LLM proxies. Encryption at rest depends on your model provider: | Provider | Encryption at rest | | ----------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Anthropic API | Infrastructure-level disk encryption (AES-256). Enable [Zero Data Retention](/docs/en/zero-data-retention) for no server-side persistence. | | Amazon Bedrock | AES-256 with AWS-managed keys. Customer-managed keys available via AWS KMS. | | Google Cloud's Agent Platform | Google-managed encryption keys. CMEK available. | | Microsoft Foundry | Depends on the deployment's [hosting option](https://platform.claude.com/docs/en/build-with-claude/claude-in-microsoft-foundry#hosting-options). For Hosted on Azure deployments, prompts and completions remain within Azure; only usage metadata and content flagged by Anthropic's safety systems egress to Anthropic. For Hosted on Anthropic deployments, requests route to Anthropic infrastructure with AES-256 disk encryption. | Claude Code is built on Anthropic's APIs. For details on API security controls, including API logging procedures, see the compliance artifacts in the [Anthropic Trust Center](https://trust.anthropic.com). ### Cloud execution: Data flow and dependencies When using [Claude Code on the web](/docs/en/claude-code-on-the-web), sessions run in Anthropic-managed virtual machines by default instead of locally. Sessions your organization routes to a [self-hosted environment](/docs/en/self-hosted-environments) run on infrastructure you control; for what stays on your machines and what still goes to Anthropic, see [What stays on your infrastructure](/docs/en/self-hosted-environments#what-stays-on-your-infrastructure). In Anthropic-hosted cloud sessions: * **Code and data storage:** Your repository is cloned to an isolated VM. Code and session data are subject to the retention and usage policies for your account type (see Data retention section above) * **Credentials:** GitHub authentication is handled through a secure proxy; your GitHub credentials never enter the sandbox * **Network traffic:** All outbound traffic goes through a security proxy for audit logging and abuse prevention * **Session data:** Prompts, code changes, and outputs follow the same data policies as local Claude Code usage For security details about cloud execution, see [Security](/docs/en/security#cloud-execution-security). ## Telemetry services Claude Code sends two kinds of operational telemetry: usage metrics and error reports. You can turn each off individually with the environment variables below, or disable all non-essential traffic at once by setting `CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC`. Setting `DISABLE_TELEMETRY` or `CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC` also disables the feature-flag evaluation that [Remote Control](/docs/en/remote-control#requirements) depends on; `DISABLE_ERROR_REPORTING` doesn't. **Metrics**: latency, reliability, and usage patterns, sent to Anthropic and to third-party logging infrastructure over TLS. Metrics never include your code, prompts, or file paths. Set `DISABLE_TELEMETRY=1` to opt out. **Error reports**: error messages and stack traces from Claude Code's own internals, sent to a third-party error tracking service over TLS. Claude Code redacts known patterns of secrets, file paths, email addresses, and other personal information before anything leaves your machine. Set `DISABLE_ERROR_REPORTING=1` to opt out. Error reporting is on only when all of these apply: * you sign in with a Claude Pro or Max subscription * you're running Claude Code v2.1.198 or later * you're connecting directly to the Claude API * your organization doesn't have a zero data retention or HIPAA agreement When you run the `/feedback` command, a copy of your conversation history including code is sent to Anthropic. The `/bug` and `/share` commands submit through the same path. Before submitting, you choose how much history to include: the current session only, which is the default, or also other sessions from the same project over the last 24 hours or 7 days. The data is encrypted in transit via TLS and stored in Google Cloud Storage, which encrypts stored data at rest by default. Optionally, a GitHub issue is created in the public repository. To opt out, set the `DISABLE_FEEDBACK_COMMAND` environment variable to `1`. When you use a third-party provider such as Amazon Bedrock or Google Cloud's Agent Platform, or have no Anthropic credentials configured, `/feedback` writes the report to a local archive under `~/.claude/feedback-bundles/` instead of sending it to Anthropic. Known API key and token patterns are redacted before the archive is written. Nothing leaves your machine until you send that file to your Anthropic account representative or attach it to a support request. ## Default behaviors by API provider By default, error reporting, telemetry, and bug reporting are disabled when using Amazon Bedrock, Google Cloud's Agent Platform, Microsoft Foundry, or Claude Platform on AWS. Session quality surveys and the WebFetch domain safety check are exceptions and run regardless of provider. On a signed-in [Claude apps gateway](/docs/en/claude-apps-gateway) session, usage analytics, error reporting, and survey ratings to Anthropic are disabled by the gateway credential itself, with no setting to re-enable them. You can opt out of all non-essential traffic, including surveys, at once by setting `CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC`. This variable doesn't affect the WebFetch check or official plugin marketplace auto-install; each has its own opt-out: `skipWebFetchPreflight` in [settings](/docs/en/settings) for WebFetch, and `CLAUDE_CODE_DISABLE_OFFICIAL_MARKETPLACE_AUTOINSTALL` for the marketplace. Here are the full default behaviors: | Service | Claude API | Google Cloud's Agent Platform API | Amazon Bedrock API | Microsoft Foundry API | Claude Platform on AWS | | ------------------------------------ | ----------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------- | | **Metrics** | Default on.<br />`DISABLE_TELEMETRY=1` to disable. | Default off.<br />`CLAUDE_CODE_USE_VERTEX` must be 1. | Default off.<br />`CLAUDE_CODE_USE_BEDROCK` must be 1. | Default off.<br />`CLAUDE_CODE_USE_FOUNDRY` must be 1. | Default off.<br />`CLAUDE_CODE_USE_ANTHROPIC_AWS` must be 1. | | **Error reports** | On for Pro and Max sign-ins on v2.1.198+, otherwise off.<br />`DISABLE_ERROR_REPORTING=1` to disable. | Default off.<br />`CLAUDE_CODE_USE_VERTEX` must be 1. | Default off.<br />`CLAUDE_CODE_USE_BEDROCK` must be 1. | Default off.<br />`CLAUDE_CODE_USE_FOUNDRY` must be 1. | Default off.<br />`CLAUDE_CODE_USE_ANTHROPIC_AWS` must be 1. | | **Claude API (`/feedback` reports)** | Default on.<br />`DISABLE_FEEDBACK_COMMAND=1` to disable. | Default off.<br />`CLAUDE_CODE_USE_VERTEX` must be 1. | Default off.<br />`CLAUDE_CODE_USE_BEDROCK` must be 1. | Default off.<br />`CLAUDE_CODE_USE_FOUNDRY` must be 1. | Default off.<br />`CLAUDE_CODE_USE_ANTHROPIC_AWS` must be 1. | | **Session quality surveys** | Default on.<br />`CLAUDE_CODE_DISABLE_FEEDBACK_SURVEY=1` to disable. | Default on.<br />`CLAUDE_CODE_DISABLE_FEEDBACK_SURVEY=1` to disable. | Default on.<br />`CLAUDE_CODE_DISABLE_FEEDBACK_SURVEY=1` to disable. | Default on.<br />`CLAUDE_CODE_DISABLE_FEEDBACK_SURVEY=1` to disable. | Default on.<br />`CLAUDE_CODE_DISABLE_FEEDBACK_SURVEY=1` to disable. | | **WebFetch domain safety check** | Default on.<br />`skipWebFetchPreflight: true` in [settings](/docs/en/settings) to disable. | Default on.<br />`skipWebFetchPreflight: true` in [settings](/docs/en/settings) to disable. | Default on.<br />`skipWebFetchPreflight: true` in [settings](/docs/en/settings) to disable. | Default on.<br />`skipWebFetchPreflight: true` in [settings](/docs/en/settings) to disable. | Default on.<br />`skipWebFetchPreflight: true` in [settings](/docs/en/settings) to disable. | All environment variables can be checked into `settings.json` (see [settings reference](/docs/en/settings)). When a host platform sets `CLAUDE_CODE_PROVIDER_MANAGED_BY_HOST`, metrics default to on for Google Cloud's Agent Platform, Amazon Bedrock, Microsoft Foundry, and Claude Platform on AWS, and follow the standard `DISABLE_TELEMETRY` opt-out. Error reporting and `/feedback` reports remain off by default on those providers. ### WebFetch domain safety check Before fetching a URL, the WebFetch tool sends the requested hostname to `api.anthropic.com` to check it against a safety blocklist maintained by Anthropic. Only the hostname is sent, not the full URL, path, or page contents. Claude Code caches a hostname that passes the check for five minutes, and re-checks a blocked or failed hostname on the next request. This check runs regardless of which model provider you use and is not affected by `CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC`. If your network blocks `api.anthropic.com`, WebFetch requests fail until you either allowlist the domain or set `skipWebFetchPreflight: true` in [settings](/docs/en/settings). Disabling the check means WebFetch attempts to retrieve any URL without consulting the blocklist, so combine it with [`WebFetch` permission rules](/docs/en/permissions#webfetch) if you need to restrict which domains Claude can reach.
debug-your-config First recorded · 121 lines, first recorded
# Debug your configuration ## See what loaded into context ## Check resolved settings ## Check MCP servers ## Check hooks ## Test against a clean configuration ## Check common causes ## Related resources
The first capture of this source. The page was already there, and this is what it said.
# Debug your configuration
> Diagnose why CLAUDE.md, settings, hooks, MCP servers, or skills aren't taking effect. Use /context, /doctor, /hooks, and /mcp to see what actually loaded.
When Claude ignores an instruction or a feature you configured doesn't appear, the cause is usually that the file didn't load, it loaded from a different location than you expected, or another file overrode it. This guide shows how to inspect what Claude Code actually loaded so you can narrow down which applies.
For installation, authentication, and connectivity problems, see [Troubleshoot installation and login](/docs/en/troubleshoot-install) instead.
## See what loaded into context
The `/context` command shows everything occupying the context window for the current session, broken down by category: system prompt, system tools, MCP tools, custom subagents with the source each loaded from, memory files, skills, and conversation messages. Run it first to confirm whether your `CLAUDE.md`, rules, or skill descriptions are present at all.
For detail on a specific category, follow up with the dedicated command:
| Command | Shows |
| :--------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `/memory` | Memory file locations across user and project scopes with the option to open each in your editor, plus access to the auto memory folder and the auto memory toggle |
| `/skills` | Available skills from project, user, and plugin sources |
| `/hooks` | Active hook configurations |
| `/mcp` | Connected MCP servers and their status |
| `/permissions` | Resolved allow and deny rules currently in effect |
| `/doctor` | Setup checkup: installation health, invalid settings files, unused extensions, duplicate [subagent](/docs/en/sub-agents) names in the same directory, and checked-in `CLAUDE.md` content Claude can derive from the codebase, with proposed fixes |
| `/debug [issue]` | Enables debug logging for the session and prompts Claude to diagnose using the log output and settings paths |
| `/status` | Active settings sources, including whether managed settings are in effect |
If a memory file is missing from the `/context` breakdown, check its location against [how CLAUDE.md files load](/docs/en/memory#how-claude-md-files-load). Subdirectory `CLAUDE.md` files load on demand when Claude reads a file in that directory with the Read tool, not at session start.
If `/context` confirms the file loaded but Claude still isn't following a particular instruction, the issue is likely how the instruction is written rather than whether it loaded. CLAUDE.md works well for the kinds of guidance you'd give a new teammate, such as project conventions, build commands, and where files belong.
Adherence drops when an instruction is vague enough to interpret multiple ways, when two files give conflicting direction, or when the file has grown long enough that individual rules get less attention. [Write effective instructions](/docs/en/memory#write-effective-instructions) covers the specificity, size, and structure patterns that keep adherence high.
<Note>
CLAUDE.md and permissions solve different problems. CLAUDE.md tells Claude how your project works so it makes good decisions. [Permissions](/docs/en/permissions) and [hooks](/docs/en/hooks) enforce limits regardless of what Claude decides. Use CLAUDE.md for "we do it this way here." Use permissions or hooks for security boundaries and anything that must never happen, where you need a guarantee instead of guidance.
</Note>
## Check resolved settings
Settings merge across managed, user, project, and local scopes. Managed settings apply first when present. Among the rest, the closer scope overrides the broader one in the order local, then project, then user. Some settings can also be set by command-line flags or [environment variables](/docs/en/env-vars), which act as another override layer. When a setting doesn't seem to apply, the value you set is usually being overridden by another scope or an environment variable.
Run `/doctor` to check your configuration and installation. It reports what it finds, including invalid settings files, duplicate installations, unused extensions, and checked-in `CLAUDE.md` content Claude can derive from the codebase, then proposes fixes it applies only after you confirm. The `CLAUDE.md` trim check requires Claude Code v2.1.206 or later. Before v2.1.205, `/doctor` opened a read-only diagnostics screen and pressing `f` sent the report to Claude to fix.
From the terminal, `claude doctor` prints read-only installation and settings diagnostics without starting a session.
Run `/status` to see which settings sources are active, including whether managed settings are in effect. To understand which scope wins for a given key, see [How scopes interact](/docs/en/settings#how-scopes-interact).
## Check MCP servers
Run `/mcp` to see every configured server, its connection status, and whether you have approved it for the current project. A server can be defined correctly but still not provide tools for a few common reasons:
* Project-scoped servers in `.mcp.json` require a one-time approval. If the prompt was dismissed, the server stays disabled until you approve it from `/mcp`.
* A server that fails to start shows as failed in `/mcp`. Relative file paths in `command` or `args` are a frequent cause, since they resolve against the directory you launched Claude Code from rather than the location of `.mcp.json`.
* A server that shows as connected but lists zero tools has started successfully but isn't returning a tool list. Select **Reconnect** from `/mcp`. If the count stays at zero, run `claude --debug=mcp` and read the server's stderr in the debug log at `~/.claude/debug/<session-id>.txt`.
For configuration locations and scope rules, see [MCP](/docs/en/mcp).
## Check hooks
Run `/hooks` to list every hook registered for the current session, grouped by event. If a hook you defined doesn't appear, it isn't being read: hooks go under the `"hooks"` key in a settings file, not in a standalone file.
If the hook appears but doesn't fire, the matcher is the usual cause. Check it for these mistakes:
* The `matcher` field is a single string that uses `|` to match multiple tool names, for example `"Edit|Write"`. A `,` separator is equivalent, so `"Edit,Write"` matches the same tools. Before v2.1.191, a comma fell through to regex evaluation and the matcher never matched, so use `|` if you aren't on v2.1.191 yet.
* A misspelled tool name produces a matcher that matches nothing, so the hook fails silently.
* An array value is a schema error: Claude Code shows a settings error notice and rejects the whole user, project, or local settings file, `claude doctor` reports the validation failure, and no hook from that file appears in `/hooks`. In [managed settings](/docs/en/settings#settings-files), only the invalid entry is stripped and the file's other hooks still apply.
Edits to `settings.json` take effect in the running session after a brief file-stability delay. You don't need to restart. If `/hooks` still shows the old definition a few seconds after saving, run `/hooks` again to refresh the view.
If `/hooks` shows the hook but it still does not fire, the next step is to watch hook evaluation live. Start a session with `claude --debug` and trigger the tool call. The debug log records each event, which matchers were checked, and the hook's exit code and output. See [Debug hooks](/docs/en/hooks#debug-hooks) for the log format and [hooks troubleshooting](/docs/en/hooks-guide#limitations-and-troubleshooting) for common failure patterns.
## Test against a clean configuration
Start with [`claude --safe-mode`](/docs/en/cli-reference#cli-flags), which launches a session with all customizations disabled, including `CLAUDE.md`, skills, plugins, hooks, MCP servers, and custom commands and agents. Authentication, model selection, built-in tools, and permissions work normally. If the problem disappears in safe mode, one of those surfaces is the cause; use the targeted checks above to find which. Safe mode still applies managed hooks and settings policy from your organization. Managed plugins, skills, CLAUDE.md, and MCP servers are turned off.
If the problem persists in safe mode, or your settings themselves are suspect, compare against a session that loads nothing from your usual setup. Point [`CLAUDE_CONFIG_DIR`](/docs/en/env-vars) at an empty directory to bypass everything under `~/.claude`, and launch from a directory that has no `.claude` folder, `.mcp.json`, or `CLAUDE.md` so project configuration is also skipped.
```bash theme={null}
cd /tmp && CLAUDE_CONFIG_DIR=/tmp/claude-clean claude
```
The clean session has no user or project settings, hooks, MCP servers, plugins, or memory. On the first launch, expect the first-run setup screens, starting with theme selection. If you see them, the clean configuration directory is in effect. Later launches with the same directory skip these screens because Claude Code saves onboarding state there.
* Managed settings still apply if your organization deploys them, since they live at a system path outside `~/.claude`
* On Linux and Windows, you'll be prompted to log in again because credentials are stored under the configuration directory
* On macOS, credentials are in the Keychain and carry over to the clean session
If the problem disappears here, the cause is somewhere in your real `~/.claude` or project `.claude` files. Reintroduce them one at a time, by copying files into the temporary directory or by launching from your project, to find which one. If it persists in the clean session, the cause is outside your user and project configuration. Run `/status` to check whether managed settings are in effect, look for [environment variables](/docs/en/env-vars) that affect Claude Code, then see [Troubleshooting](/docs/en/troubleshooting).
## Check common causes
Most configuration surprises trace back to a small set of location and syntax rules. Check these before assuming a bug:
| Symptom | Cause | Fix |
| :------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Hook never fires | `matcher` is a JSON array instead of a string | Use a single string with `\|` to match multiple tools, for example `"Edit\|Write"`. See [matcher patterns](/docs/en/hooks#matcher-patterns). |
| Hook never fires | `matcher` uses `,` as a separator on a version before v2.1.191 | Claude Code v2.1.191 or later treats `,` as a list separator like `\|`. Earlier versions evaluate a comma as a literal character, so `"Edit,Write"` matches nothing. Use `\|` instead, or upgrade Claude Code. |
| Hook never fires | `matcher` value is lowercase, for example `"bash"` | Matching is case-sensitive. Tool names are capitalized: `Bash`, `Edit`, `Write`, `Read`. |
| Hook never fires | Hooks are defined in a standalone file instead of `settings.json` | There is no standalone hooks file for project or user config. Define hooks under the `"hooks"` key in `settings.json`. Only [plugins](/docs/en/plugins-reference#hooks) load a separate `hooks/hooks.json`. See [hook configuration](/docs/en/hooks). |
| Permissions, hooks, or env set globally are ignored | Configuration was added to `~/.claude.json` | `~/.claude.json` holds app state and UI toggles. `permissions`, `hooks`, and `env` belong in `~/.claude/settings.json`. These are two different files. |
| A `settings.json` value seems ignored | The same key is set in `settings.local.json` | `settings.local.json` overrides `settings.json`, and both override `~/.claude/settings.json`. See [settings precedence](/docs/en/settings#how-scopes-interact). |
| Skill doesn't appear in `/skills` | Skill file is at `.claude/skills/name.md` instead of in a folder | Use a folder with `SKILL.md` inside: `.claude/skills/name/SKILL.md`. |
| Skill appears in `/skills` but Claude never invokes it | Skill has `disable-model-invocation: true` in its frontmatter, or its description doesn't match how you phrase the request | Check the badge in `/skills`: a "user-only" label means Claude won't trigger it on its own. See [skill invocation](/docs/en/skills). |
| Subdirectory `CLAUDE.md` instructions seem ignored | Subdirectory files load on demand, not at session start | They load when Claude reads a file in that directory with the Read tool, not at launch and not when writing or creating files there. See [how CLAUDE.md files load](/docs/en/memory#how-claude-md-files-load). |
| Subagent ignores `CLAUDE.md` instructions | The built-in Explore and Plan agents skip `CLAUDE.md`. Custom subagents load it the same way the main conversation does | For Explore or Plan, restate the instruction in your delegating prompt. For a custom subagent, put critical instructions in the agent file body, which becomes the agent's system prompt. See [what loads at startup](/docs/en/sub-agents#what-loads-at-startup). |
| Cleanup logic never runs at session end | No `SessionEnd` hook configured | Add a `SessionEnd` hook in `settings.json`. See the [hook events list](/docs/en/hooks#hook-events). |
| MCP servers in `.mcp.json` never load | File is under `.claude/` or uses Claude Desktop's config format | Project MCP config goes at the repository root as `.mcp.json`, not inside `.claude/`. See [MCP configuration](/docs/en/mcp). |
| MCP servers added under `mcpServers` in `settings.json` never appear | `settings.json` does not read an `mcpServers` key | Define project servers in `.mcp.json` at the repository root, or run `claude mcp add --scope user` for user-scoped servers. See [MCP configuration](/docs/en/mcp). |
| Project MCP server added but doesn't appear | The one-time approval prompt was dismissed | Project-scoped servers require approval. Run `/mcp` to see status and approve. |
| MCP server fails to start from some directories | `command` or `args` uses a relative file path | Use absolute paths for local scripts. Executables on your `PATH` like `npx` or `uvx` work as-is. |
| MCP server starts without expected environment variables | The server's config entry doesn't set them, and they aren't in the environment Claude Code passes to stdio servers: its own environment, minus the [variables it strips from subprocesses](/docs/en/monitoring-usage#administrator-configuration) | Set per-server `env` inside the server's `.mcp.json` entry, which doesn't depend on the launch environment or workspace trust. |
| `Bash(rm *)` deny rule doesn't block `/bin/rm` or `find -delete` | Prefix rules match the literal command string, not the underlying executable | Add explicit patterns for each variant, or use a [PreToolUse hook](/docs/en/hooks-guide) or the [sandbox](/docs/en/sandboxing) for a hard guarantee. |
## Related resources
For full reference on each configuration surface, see the dedicated page:
* **[`.claude` directory reference](/docs/en/claude-directory)**: every config file location and what reads it
* **[Settings](/docs/en/settings)**: precedence order and the full key list
* **[Hooks reference](/docs/en/hooks)**: event names, payloads, and `--debug` output format
* **[MCP](/docs/en/mcp)**: server configuration, approval, and `/mcp` output
* **[Troubleshoot installation and login](/docs/en/troubleshoot-install)**: `command not found`, PATH, and authentication problems
* **[Troubleshooting](/docs/en/troubleshooting)**: performance, hangs, and search issues
deep-links First recorded · 191 lines, first recorded
# Launch sessions from links ## How it works ### What a launched session shows ## Build a link ### Choose between `cwd` and `repo` ## Examples ### Embed a link in a runbook ### Open a link from the shell ## Registration and supported platforms ## Open a VS Code tab instead of a terminal ## Troubleshooting ### Clicking the link does nothing ### xdg-open is not found on Linux ### The link renders as plain text instead of being clickable ### The session opens in my home directory instead of the repo ### The link opens the wrong terminal ## Learn more
The first capture of this source. The page was already there, and this is what it said.
# Launch sessions from links
> Open a Claude Code terminal session from a URL. Embed `claude-cli://` links in runbooks, alerts, and dashboards so a click opens Claude Code in the right repo with the right prompt.
A deep link is a `claude-cli://` URL that opens Claude Code in a new terminal window. The URL can carry a working directory and a prompt to pre-fill.
This lets you share a one-click starting point for a task: anyone with Claude Code installed who clicks the link sees a session open with the prompt already typed. The prompt is populated but not sent until you press Enter.
Because a deep link is a URL, you can put one anywhere a link can go:
* An incident runbook step that opens the affected service's repo with a diagnostic prompt
* A monitoring alert or dashboard that links to an investigation prompt for a specific metric
* A README or wiki page that opens the project with an onboarding prompt
* A CI failure notification that pre-fills the failing job's name
This page covers how to [build a link](#build-a-link), [embed one in a runbook or trigger it from the shell](#examples), and [manage or disable handler registration](#registration-and-supported-platforms) on each platform.
## How it works
The `claude-cli://` prefix is a custom URL scheme that Claude Code registers with your operating system, similar to how `mailto:` links open your email client. When you click a deep link:
1. The browser or app hands the URL to your operating system.
2. The operating system recognizes the `claude-cli://` prefix and starts Claude Code on your machine.
3. A new terminal window opens with Claude Code running in the directory the link specified, and the link's prompt text already in the input box.
4. You read the prompt, edit it if you want, and press Enter to send it.
The link itself can be hosted anywhere, but the session always opens locally on the computer where you clicked. See [Registration and supported platforms](#registration-and-supported-platforms) for which terminal emulator opens on each operating system.
<Note>
The platform that displays the link must allow custom URL schemes. GitHub-rendered Markdown allows `http` and `https` but strips schemes like `claude-cli://` in READMEs, issues, pull requests, and wikis. Only the link text shows, with no link behind it and the URL hidden. See [Troubleshooting](#the-link-renders-as-plain-text-instead-of-being-clickable) for a workaround.
</Note>
### What a launched session shows
A deep link never executes anything on its own. The link only chooses a directory and fills the prompt box. If you click a link from a page you do not trust, the prompt is still inert: nothing reaches the model until you read what was filled in and press Enter.
When the session opens, a warning line below the input box reads `Prompt from an external link` and stays visible until you send or clear the prompt. For prompts over 1,000 characters, the warning includes the character count and tells you to scroll and review the full text before pressing Enter, since long prompts can push instructions off screen. Permission rules, `CLAUDE.md`, and trust prompts for the selected directory apply the same way as for any other session.
## Build a link
Every deep link starts with `claude-cli://open`, which is the only path the handler accepts, followed by optional query parameters. The minimal form opens Claude Code in your home directory with an empty prompt:
```text theme={null}
claude-cli://open
```
To try a link without putting it on a page, paste it into your browser's address bar or [open it from the shell](#open-a-link-from-the-shell).
Add parameters to control where the session starts and what the prompt box contains:
| Parameter | Description |
| --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `q` | Text to pre-fill in the prompt box. [URL-encode](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/encodeURIComponent) the value. Use `%0A` for line breaks in multi-line prompts. Maximum 5,000 characters. |
| `cwd` | Absolute path to use as the working directory. Network and UNC paths are rejected, and so are paths that contain invisible or bidirectional control characters. |
| `repo` | A GitHub `owner/name` slug. Claude Code resolves it to a local clone it has seen before and starts there. If you have no matching clone, the session opens in your home directory instead. |
`cwd` and `repo` are [two ways to set the working directory](#choose-between-cwd-and-repo). If you pass both, `cwd` takes precedence and `repo` is ignored, even if the `cwd` path does not exist.
The following link points at a repository called `acme/payments` with a two-line diagnostic prompt. Replace `acme/payments` with your repository's `owner/name` slug when you build your own:
```text theme={null}
claude-cli://open?repo=acme/payments&q=Investigate%20the%20failed%20deploy%20of%20payments-api.%0ACheck%20recent%20commits%20to%20main%20and%20the%20last%20successful%20build.
```
Clicking it opens a new terminal window, starts Claude Code in your local clone of `acme/payments`, and fills the prompt box with the decoded text:
```text theme={null}
Investigate the failed deploy of payments-api.
Check recent commits to main and the last successful build.
```
You can edit the prompt before pressing Enter to send it. See [Choose between `cwd` and `repo`](#choose-between-cwd-and-repo) for how the local path is selected when you have multiple clones or worktrees.
### Choose between `cwd` and `repo`
Use `cwd` when everyone who clicks the link has the project at the same absolute path, such as a standardized devcontainer or VM image.
Use `repo` when the link is shared and each person clones to a different location. Claude Code resolves the slug to a local path as follows:
* Each time you run `claude` in a Git repository, Claude Code records that directory's path against the repository's GitHub `owner/name` slug.
* When a deep link arrives, `repo` opens whichever matching path you used most recently. Claude Code tracks multiple clones and worktrees separately, so it picks the one you worked in last.
* The link does not change which branch is checked out. The session opens in whatever state that directory is currently in.
The welcome header shows which path it picked so you can confirm the right clone opened.
## Examples
The sections below show two common ways to use a deep link: as a Markdown link in a document and as a command in a script or shell alias.
### Embed a link in a runbook
A deep link in a runbook gives whoever is triaging a one-click way to start investigating in the right repository with a prepared prompt. The platform that renders the runbook must allow custom URL schemes. GitHub-rendered Markdown does not allow `claude-cli://`, so a deep link in a GitHub README, issue, or wiki shows only its label with no clickable link. See [the troubleshooting note](#the-link-renders-as-plain-text-instead-of-being-clickable) for a workaround.
The prompt is part of the URL and must be URL-encoded. To produce the encoded value, pass your prompt text through `encodeURIComponent` in a browser console or any URL encoder.
The example below adds an investigation entry point to an incident runbook for a service called `web-gateway`:
```markdown theme={null}
## High 5xx rate on web-gateway
1. Acknowledge the page in PagerDuty.
2. [Open Claude Code in the gateway repo](claude-cli://open?repo=acme/web-gateway&q=5xx%20rate%20is%20elevated%20on%20web-gateway.%20Check%20recent%20deploys%2C%20error%20logs%20from%20the%20last%2030%20minutes%2C%20and%20open%20incidents%20in%20Linear.)
3. Post initial findings in #incident.
```
To use this in your own runbook, replace `acme/web-gateway` with your service's repository slug. This allows engineers with Claude Code installed and a local clone of that repository to click step 2 and start investigating with the prompt ready to send.
### Open a link from the shell
You can also open a deep link from a shell script, alias, or automation rather than by clicking it. Call your operating system's URL-opening command with the link as the argument. These commands rely on the handler that Claude Code [registers when you send your first prompt of an interactive session](#registration-and-supported-platforms) on the machine.
<Tabs>
<Tab title="macOS">
The built-in `open` command passes the URL to the registered `claude-cli://` handler:
```bash theme={null}
open "claude-cli://open?repo=acme/payments&q=review%20open%20PRs"
```
</Tab>
<Tab title="Linux">
Most desktop environments provide `xdg-open`, which passes the URL to the registered handler:
```bash theme={null}
xdg-open "claude-cli://open?repo=acme/payments&q=review%20open%20PRs"
```
On success, a new terminal window opens with Claude Code running and the prompt pre-filled. If the shell reports that `xdg-open` isn't found, see [Troubleshooting](#xdg-open-is-not-found-on-linux).
</Tab>
<Tab title="Windows">
In PowerShell, `Start-Process` passes the URL to the registered handler:
```powershell theme={null}
Start-Process "claude-cli://open?repo=acme/payments&q=review%20open%20PRs"
```
In `cmd.exe`, `start` treats its first quoted argument as a window title, so pass an empty title before the URL:
```cmd theme={null}
start "" "claude-cli://open?repo=acme/payments&q=review%20open%20PRs"
```
</Tab>
</Tabs>
## Registration and supported platforms
Claude Code registers the `claude-cli://` handler with your operating system on macOS, Linux, and Windows when you send your first prompt of an interactive session. Starting `claude` and exiting without sending a prompt doesn't register the handler. You don't run a separate install command. Registration writes to user-level locations only:
| Platform | Handler location |
| -------- | ------------------------------------------------------------------------------------------------------------------ |
| macOS | `~/Applications/Claude Code URL Handler.app` |
| Linux | `claude-code-url-handler.desktop` under `$XDG_DATA_HOME/applications`, defaulting to `~/.local/share/applications` |
| Windows | `HKEY_CURRENT_USER\Software\Classes\claude-cli` |
The handler launches Claude Code in a detected terminal emulator. On macOS, Claude Code remembers the terminal from your most recent interactive session and reuses it, supporting iTerm2, Ghostty, kitty, Alacritty, WezTerm, and Terminal.app. On Linux it honors the `$TERMINAL` environment variable, then `x-terminal-emulator`, then a list of common emulators. On Windows it prefers Windows Terminal, then PowerShell, then `cmd.exe`.
To prevent registration entirely, set [`disableDeepLinkRegistration`](/docs/en/settings) to `"disable"` in `settings.json`. To enforce this across an organization so users cannot re-enable it, set it in [managed settings](/docs/en/server-managed-settings) instead.
## Open a VS Code tab instead of a terminal
The VS Code extension registers its own handler at `vscode://anthropic.claude-code/open`, which opens a Claude Code editor tab rather than a terminal window. See [Launch a VS Code tab from other tools](/docs/en/vs-code#launch-a-vs-code-tab-from-other-tools) for that URL's parameters.
## Troubleshooting
### Clicking the link does nothing
The handler likely isn't registered yet. Registration happens when you send your first prompt of an interactive session, not when the session starts. Start an interactive `claude` session on that machine, send any prompt, exit, and try the link again. If you are on Linux without a desktop environment, `xdg-open` may have nothing to dispatch to.
### xdg-open is not found on Linux
The `xdg-open` command is part of the `xdg-utils` package, which minimal server images, containers, and WSL distributions often leave out. Install `xdg-utils` with your distribution's package manager, for example `sudo apt install xdg-utils`, then run the command again. If the command then runs but nothing opens, `xdg-open` may have no desktop environment to dispatch to; see [Clicking the link does nothing](#clicking-the-link-does-nothing).
### The link renders as plain text instead of being clickable
Some Markdown renderers only allow `http` and `https` links and strip other URL schemes. GitHub does this in READMEs, issues, pull requests, and wikis: `[label](claude-cli://...)` renders as just `label`, with no link and the URL removed. On these platforms, put the deep link in a code block so readers can see the URL and paste it into their browser's address bar.
### The session opens in my home directory instead of the repo
The `repo` parameter only resolves to clones Claude Code has already seen. Run `claude` inside the clone once so Claude Code records its path, or switch the link to use `cwd` with an absolute path.
### The link opens the wrong terminal
On macOS, start `claude` in your preferred terminal once and the next deep link will use it. On Linux, set the `$TERMINAL` environment variable to your preferred emulator's command name. On Windows, the order is fixed: install Windows Terminal if you want links to open there instead of a PowerShell or `cmd.exe` window.
## Learn more
These pages cover related ways to launch or extend Claude Code sessions:
* [Skills](/docs/en/skills): store a long runbook prompt as a `/skill` in the repo so the deep link's `q` parameter only has to name it
* [Non-interactive mode](/docs/en/headless): run Claude from a script and capture the output without opening a terminal
desktop First recorded · 960 lines, first recorded
# Desktop application ## Start a session ## Work with code ### Use the prompt box ### Add files and context to prompts ### Choose a permission mode ### Preview your app ### Browse external sites #### Approve Claude's actions on a site #### Choose between the Browser and the Chrome extension #### Restrict external browsing for your organization ### Review changes with diff view ### Review your code ### Monitor pull request status ## Arrange your workspace ### Run commands in the terminal ### Open and edit files ### Open files in other apps ### Switch view modes ### Keyboard shortcuts ### Check usage ## Let Claude use your computer ### When computer use applies ### Enable computer use ### App permissions ## Manage sessions ### Work in parallel with sessions ### Ask a side question without derailing the session ### Watch background tasks ### Work across sessions ### Run long-running tasks remotely ### Continue in another surface ### Sessions from Dispatch ## Extend Claude Code ### Connect external tools ### Use skills ### Install plugins ### Configure preview servers #### Auto-verify changes #### Configuration fields ##### When to use `program` vs `runtimeExecutable` ##### Open the preview at a specific URL #### Port conflicts #### Examples ## Environment configuration ### Local sessions ### Cloud sessions ### SSH sessions #### Pre-configure SSH connections for your team #### Restrict which SSH hosts users can connect to ## Enterprise configuration ### Admin console controls ### Managed settings ### Device management policies ### Network access requirements ### Authentication and SSO ### Data handling ### Deployment ## Coming from the CLI? ### CLI flag equivalents ### Shared configuration #### MCP servers from the Claude Desktop chat app ### Feature comparison ### What's not available in Desktop ## Troubleshooting ### Check your version ### 403 or authentication errors in the Code tab ### Blank or stuck screen on launch ### "Failed to load session" ### Session not finding installed tools ### Git and Git LFS errors ### MCP servers not working on Windows ### App won't quit ### Windows-specific issues ### "Branch doesn't exist yet" when opening in CLI ### Still stuck?
The first capture of this source. The page was already there, and this is what it said.
# Desktop application
> Get more out of Claude Code Desktop: parallel sessions with Git isolation, drag-and-drop pane layout, integrated terminal and file editor, side chats, computer use, Dispatch sessions from your phone, visual diff review, app previews, PR monitoring, connectors, and enterprise configuration.
The Claude Desktop app has three tabs: **Chat** for conversations, **Cowork** for [Dispatch and longer agentic work](https://claude.com/product/cowork), and **Code** for software development. This page is the reference for the Code tab.
<CardGroup cols={3}>
<Card title="Download for macOS" icon="apple" href="https://claude.ai/api/desktop/darwin/universal/dmg/latest/redirect?utm_source=claude_code&utm_medium=docs">
Universal build for Intel and Apple Silicon
</Card>
<Card title="Download for Windows" icon="windows" href="https://claude.ai/api/desktop/win32/x64/setup/latest/redirect?utm_source=claude_code&utm_medium=docs">
For x64 processors
</Card>
<Card title="Get Claude for Linux (beta)" icon="linux" href="/docs/en/desktop-linux">
apt or .deb for Ubuntu and Debian
</Card>
</CardGroup>
For Windows ARM64, download the [ARM64 installer](https://claude.ai/api/desktop/win32/arm64/setup/latest/redirect?utm_source=claude_code\&utm_medium=docs). On Linux, install with apt; see [Claude Desktop on Linux](/docs/en/desktop-linux).
After installing, launch Claude, sign in, and click the **Code** tab. The first time you open it on Windows, you need [Git for Windows](https://git-scm.com/downloads/win) installed; restart the app after installing it. For a walkthrough of your first session, see the [Get started guide](/docs/en/desktop-quickstart).
In the Code tab, each conversation is a **session**: it has its own chat history, project folder, and code changes, independent of any other session. The sidebar lists your sessions and lets you run several in parallel. Within a session you can:
* [Review and comment on diffs](#review-changes-with-diff-view), then [watch the resulting PR through CI](#monitor-pull-request-status)
* [Preview your running app](#preview-your-app) in the Browser pane while Claude verifies its own changes, and [open external sites](#browse-external-sites) alongside it
* Watch Claude [run and test your iOS app](/docs/en/desktop-ios-simulator) in the iOS Simulator pane
* [Arrange panes](#arrange-your-workspace) for the chat, diff, browser, terminal, and file editor side by side
* Ask a [side question](#ask-a-side-question-without-derailing-the-session) that uses the session's context without derailing it
* Let Claude [check on, message, or archive your other sessions](#work-across-sessions)
* [Connect external tools](#connect-external-tools) like GitHub, Slack, and Linear
* Let Claude [open apps and control your screen](#let-claude-use-your-computer)
* Run on your machine, in the [cloud](#run-long-running-tasks-remotely), or over [SSH](#ssh-sessions)
For [scheduled recurring work](/docs/en/desktop-scheduled-tasks), [keyboard shortcuts](#keyboard-shortcuts), or [sending tasks from your phone](#sessions-from-dispatch), see the linked pages and sections. If you already use the terminal-based CLI, see the [CLI comparison](#coming-from-the-cli) for what carries over.
## Start a session
Before you send your first message, configure four things in the prompt area:
* **Environment**: choose where Claude runs. Select **Local** for your machine, **Cloud** for a [cloud session](#cloud-sessions) that continues after you close the app, an [**SSH connection**](#ssh-sessions) for a remote machine you manage, or on Windows a [**WSL distribution**](/docs/en/desktop-wsl). See [environment configuration](#environment-configuration).
* **Project folder**: select the folder or repository Claude works in. For cloud sessions, you can add [multiple repositories](#run-long-running-tasks-remotely).
* **Model**: pick a [model](/docs/en/model-config#available-models) from the dropdown next to the send button. You can change this during the session.
* **Permission mode**: choose how much autonomy Claude has from the [mode selector](#choose-a-permission-mode). You can change this during the session.
Type your task and press **Enter** to start. Each session tracks its own context and changes independently.
## Work with code
Give Claude the right context, control how much it does on its own, and review what it changed.
### Use the prompt box
Type what you want Claude to do and press **Enter** to send. Claude reads your project files, makes changes, and runs commands based on your [permission mode](#choose-a-permission-mode). You can redirect Claude at any point: click the stop button to interrupt immediately, or type a correction and press **Enter** to send it without stopping the running action. Claude reads the correction as soon as the current action completes and adjusts before its next step.
The **+** button next to the prompt box gives you access to file attachments, [skills](#use-skills), [connectors](#connect-external-tools), and [plugins](#install-plugins).
### Add files and context to prompts
The prompt box supports two ways to bring in external context:
* **@mention files**: type `@` followed by a filename to add a file to the conversation context. Claude can then read and reference that file. @mention is not available in cloud or WSL sessions.
* **Attach files**: attach images, PDFs, and other files to your prompt using the attachment button, or drag and drop files directly into the prompt. This is useful for sharing screenshots of bugs, design mockups, or reference documents.
### Choose a permission mode
Permission modes control how much autonomy Claude has during a session: whether it asks before editing files, running commands, or both. You can switch modes at any time using the mode selector next to the send button. Start with Manual to see exactly what Claude does, then move to Accept edits or Plan as you get comfortable.
To set a default mode for new local sessions, add `permissions.defaultMode` to your [settings file](/docs/en/settings#settings-files). The desktop app reads the same settings files as the CLI. A mode you pick in the selector is remembered per folder and takes precedence over `defaultMode` for that folder, except Plan, which applies to the current session only.
| Mode | Settings key | Behavior |
| ---------------------- | ------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Manual** | `default` | Claude asks before editing files or running commands. You see a diff and can accept or reject each change. Recommended for new users. |
| **Accept edits** | `acceptEdits` | Claude auto-accepts file edits and common filesystem commands like `mkdir`, `touch`, and `mv`, but still asks before running other terminal commands. Use this when you trust file changes and want faster iteration. |
| **Plan** | `plan` | Claude reads files and runs commands to explore, then proposes a plan without editing your source code. Good for complex tasks where you want to review the approach first. |
| **Auto** | `auto` | Claude executes all actions with background safety checks that verify alignment with your request. Reduces permission prompts while maintaining oversight. Appears when your account meets the [availability requirements](#auto-mode-availability) below; there is no separate Settings toggle for it. |
| **Bypass permissions** | `bypassPermissions` | Claude runs without permission prompts, except those forced by explicit [ask rules](/docs/en/permissions#manage-permissions), connector tools [your organization set to `ask`](/docs/en/mcp#organization-controls-on-connector-tools), MCP tools marked [`requiresUserInteraction`](/docs/en/mcp#require-approval-for-a-specific-tool), removals targeting `/` or your home directory, safety classifiers when Claude [acts on external sites](#browse-external-sites), or desktop actions where Claude always asks first, such as [archiving a session](#work-across-sessions); the CLI's [cross-session messaging safeguards](/docs/en/permission-modes#skip-all-checks-with-bypasspermissions-mode) also still apply. Equivalent to `--dangerously-skip-permissions` in the CLI. On Pro and Max plans, enable it in your Settings → Claude Code under "Allow bypass permissions mode"; on Team and Enterprise plans there is no Settings toggle, and organization policy controls it instead. Only use this in sandboxed containers or VMs. |
Earlier versions of the Code tab labeled these modes Ask permissions, Auto accept edits, and Plan mode.
The `dontAsk` permission mode is available only in the [CLI](/docs/en/permission-modes#allow-only-pre-approved-tools-with-dontask-mode).
<span id="auto-mode-availability" />
Auto mode is available to all users on the Anthropic API and requires Claude Opus 4.6 or later, Sonnet 4.6 or later, or Fable 5. Organization administrators can turn auto mode off with the `disableAutoMode` key in [managed settings](#managed-settings).
In Enterprise deployments that route Desktop to Google Cloud's Agent Platform, auto mode is also available by default; see [Auto mode on Bedrock, Agent Platform, or Foundry](/docs/en/permission-modes#enable-auto-mode-on-bedrock-agent-platform-or-foundry) for the supported models.
<Tip title="Best practice">
Start complex tasks in Plan so Claude maps out an approach before making changes. Once you approve the plan, switch to Accept edits or Manual to execute it. See [explore first, then plan, then code](/docs/en/best-practices#explore-first-then-plan-then-code) for more on this workflow.
</Tip>
Cloud sessions support Accept edits, Plan, and Auto. Accept edits corresponds to `default` mode: cloud sessions pre-approve file edits, so the selector shows Accept edits instead of Manual. Bypass permissions isn't available in cloud sessions, including sessions in a [self-hosted environment](/docs/en/self-hosted-environments).
Enterprise admins can restrict which permission modes are available. See [enterprise configuration](#enterprise-configuration) for details.
### Preview your app
Claude can start a dev server and open it in the Browser pane to verify its changes. This works for frontend web apps as well as backend servers: Claude can test API endpoints, view server logs, and iterate on issues it finds. In most cases, Claude starts the server automatically after editing project files. You can also ask Claude to preview at any time. By default, Claude [auto-verifies](#auto-verify-changes) changes after every edit.
The Browser pane can also open static HTML files, PDFs, images, and videos from your project. Click an HTML, PDF, image, or video path in the chat to open it there.
From the Browser pane, you can:
* Interact with your running app directly in the Browser pane
* Watch Claude verify its own changes automatically: it takes screenshots, inspects the DOM, clicks elements, fills forms, and fixes issues it finds
* Start or stop servers from the server dropdown in the session toolbar
* Persist cookies and local storage across server restarts by selecting **Persist sessions** in the dropdown, so you don't have to re-login during development
* Edit the server configuration or stop all servers at once
Claude creates the initial server configuration based on your project. If your app uses a custom dev command, edit `.claude/launch.json` to match your setup. See [Configure preview servers](#configure-preview-servers) for the full reference.
To clear saved session data, or to turn the Browser off entirely, use the toggles in Settings → Claude Code.
### Browse external sites
The Browser pane is a tabbed browser, so you can open documentation, issue trackers, or any other site next to your running app. To open the Browser, press **Cmd+Shift+B** on macOS or **Ctrl+Shift+B** on Windows, or select it from the **Views** menu. When you click an external link in the chat, a chooser offers **Open in app** to use the Browser pane or **Default browser** to use your own; **Cmd**-click on macOS or **Ctrl**-click on Windows opens a link in your system browser directly. You can sign in to sites in the pane, including popup sign-in flows such as Google OAuth.
Claude can read and interact with external pages using the same tools it uses to [verify your app](#preview-your-app), with two additional safety checks:
* Safety classifiers review Claude's write actions on external pages, such as clicking and typing, in every permission mode. These are the same classifiers [auto mode](#choose-a-permission-mode) uses, and when they flag an action, you get a permission prompt regardless of mode.
* In permission modes other than Auto and Bypass permissions, a domain allowlist check also applies before Claude navigates to a new site.
#### Approve Claude's actions on a site
The first time Claude acts on an external site, a permission card appears and Claude waits for your choice: **Allow once**, **Always allow**, or **Deny**. **Allow once** approves the action without saving anything. **Always allow** saves the approval for that site on your device, and you can revoke it in Settings. Each site needs its own approval, including subdomains. Your local dev servers and project files don't need approval, so [auto-verify](#auto-verify-changes) keeps working without prompts.
Even on an approved site, Claude won't purchase items, create accounts, or bypass CAPTCHAs without your input. Browsing in the Browser pane uses the same safety model as the [Claude in Chrome extension](/docs/en/chrome). See [Using Claude in Chrome safely](https://support.claude.com/en/articles/12902428-using-claude-in-chrome-safely) for how Claude handles sensitive sites and risky actions.
#### Choose between the Browser and the Chrome extension
The Browser pane uses a clean browser profile, separate from your personal browser, with none of your saved logins or history. Use it for building and testing your app and for sites that don't need your identity. When you want Claude to act as you in your logged-in sessions, use the [Claude in Chrome extension](/docs/en/chrome) instead, which shares your browser's login state.
#### Restrict external browsing for your organization
The Browser follows the same [site allowlist and blocklist controls](https://support.claude.com/en/articles/13065128-claude-in-chrome-admin-controls) as the Claude in Chrome extension. If your organization already configured those lists for the extension, the Browser respects them automatically. Administrators can also turn off Claude's tools on external pages with the [`browserExternalPageTools` managed setting](#managed-settings). With tools disabled, users can still navigate to external sites; Claude's tools can't read or act on them.
To turn off external browsing entirely, set the [`disableBrowserExternalNavigation` managed setting](#managed-settings) to `true`. This blocks all external navigation in the Browser, including sites on your organization's allowlist; localhost dev servers and file previews keep working. Use `browserExternalPageTools` to let users keep browsing external sites without Claude's tools, and `disableBrowserExternalNavigation` to block external sites for both users and Claude.
### Review changes with diff view
After Claude makes changes to your code, the diff view lets you review modifications file by file before creating a pull request.
When Claude changes files, a diff stats indicator appears showing the number of lines added and removed, such as `+12 -1`. Click this indicator to open the diff viewer, which displays a file list on the left and the changes for each file on the right.
To comment on specific lines, click any line in the diff to open a comment box. Type your feedback and press **Enter** to add the comment. After adding comments to multiple lines, submit all comments at once:
* **macOS**: press **Cmd+Enter**
* **Windows**: press **Ctrl+Enter**
Claude reads your comments and makes the requested changes, which appear as a new diff you can review.
### Review your code
In the diff view, click **Review code** in the top-right toolbar to ask Claude to evaluate the changes before you commit. Claude examines the current diffs and leaves comments directly in the diff view. You can respond to any comment or ask Claude to revise.
The review focuses on high-signal issues: compile errors, definite logic errors, security vulnerabilities, and obvious bugs. It does not flag style, formatting, pre-existing issues, or anything a linter would catch.
### Monitor pull request status
After you open a pull request, a CI status bar appears in the session. Claude Code uses the GitHub CLI to poll check results and surface failures.
* **Auto-fix**: when enabled, Claude automatically attempts to fix failing CI checks by reading the failure output and iterating.
* **Auto-merge**: when enabled, Claude merges the PR once all checks pass. The merge method is squash. Enable auto-merge in your [GitHub repository settings](https://docs.github.com/en/repositories/configuring-branches-and-merges-in-your-repository/configuring-pull-request-merges/managing-auto-merge-for-pull-requests-in-your-repository) first; without it, Claude can't merge the PR.
Use the **Auto-fix** and **Auto-merge** toggles in the CI status bar to enable either option. Claude Code also sends a desktop notification when CI finishes. To archive the session automatically once the PR merges or closes, turn on [auto-archive](#work-in-parallel-with-sessions) in Settings → Claude Code.
<Note>
PR monitoring requires the [GitHub CLI (`gh`)](https://cli.github.com/) to be installed and authenticated on your machine. If `gh` is not installed, Desktop prompts you to install it the first time you try to create a PR.
</Note>
## Arrange your workspace
The Code tab is built around panes you can arrange in any layout: chat, diff, browser, terminal, file, plan, tasks, and subagent, along with the [iOS Simulator](/docs/en/desktop-ios-simulator) on macOS. Drag a pane by its header to reposition it, or drag a pane edge to resize it. Press **Cmd+\\** on macOS or **Ctrl+\\** on Windows to close the focused pane. Open additional panes from the **Views** menu in the session toolbar.
<Note>
The pane layout, terminal, file editor, and view modes in this section require Claude Desktop v1.2581.0 or later. Open **Claude → Check for Updates** on macOS or **Help → Check for Updates** on Windows to update.
</Note>
### Run commands in the terminal
The integrated terminal lets you run commands alongside your session without switching to another app. Open it from the **Views** menu or press **Ctrl+\`** on macOS or Windows. The terminal opens in your session's working directory and shares the same environment as Claude, so commands like `npm test` or `git status` see the same files Claude is editing. To open a second terminal tab, click **+** in the terminal pane header or right-click a folder in the chat to choose **Open in terminal**. The terminal is available in local sessions only.
### Open and edit files
Click a file path in the chat or diff viewer to open it in the file pane. HTML, PDF, image, and video paths open in the [Browser pane](#preview-your-app) instead. Make spot edits and click **Save** to write them back. If the file changed on disk since you opened it, the pane warns you and lets you override or discard. Click **Discard** to revert your edits, or click the path in the pane header to copy the absolute path.
The file pane is available in local and SSH sessions. For cloud sessions, ask Claude to make the change.
### Open files in other apps
Right-click any file path in the chat, diff viewer, or file pane to open a context menu:
* **Attach as context**: add the file to your next prompt
* **Open in**: open the file in an installed editor such as VS Code, Cursor, or Zed
* **Show in Finder** on macOS, **Show in Explorer** on Windows: open the containing folder
* **Copy path**: copy the absolute path to your clipboard
### Switch view modes
View modes control how much detail appears in the chat transcript. Switch modes from the **Transcript view** dropdown next to the send button, or press **Ctrl+O** on macOS or Windows to cycle through them.
| Mode | What it shows |
| ----------- | -------------------------------------------------------------- |
| **Normal** | Tool calls collapsed into summaries, with full text responses |
| **Verbose** | Every tool call, file read, and intermediate step Claude takes |
| **Summary** | Only Claude's final responses and the changes it made |
Use Verbose when debugging why Claude took a particular action. Use Summary when you're running multiple sessions and want to scan results quickly.
### Keyboard shortcuts
Press **Cmd+/** on macOS or **Ctrl+/** on Windows to see all shortcuts available in the Code tab. On Windows, use **Ctrl** in place of **Cmd** for the shortcuts below. Session cycling, the terminal toggle, and the view-mode toggle use **Ctrl** on every platform.
| Shortcut | Action |
| ------------------------------------- | -------------------------------- |
| `Cmd` `/` | Show keyboard shortcuts |
| `Cmd` `N` | New session |
| `Cmd` `W` | Close session |
| `Ctrl` `Tab` / `Ctrl` `Shift` `Tab` | Next or previous session |
| `Cmd` `Shift` `]` / `Cmd` `Shift` `[` | Next or previous session |
| `Esc` | Stop Claude's response |
| `Cmd` `Shift` `D` | Toggle diff pane |
| `Cmd` `Shift` `B` | Toggle Browser pane |
| `Cmd` `Shift` `S` | Select an element in the Browser |
| `Ctrl` `` ` `` | Toggle terminal pane |
| `Cmd` `\` | Close focused pane |
| `Cmd` `;` | Open side chat |
| `Ctrl` `O` | Cycle view modes |
| `Cmd` `Shift` `M` | Open permission mode menu |
| `Cmd` `Shift` `I` | Open model menu |
| `Cmd` `Shift` `E` | Open effort menu |
| `1`–`9` | Select item in an open menu |
These shortcuts apply only to the Code tab. The terminal-based [interactive mode shortcuts](/docs/en/interactive-mode#keyboard-shortcuts), such as `Shift+Tab` to cycle modes, do not apply in Desktop.
### Check usage
Click the usage ring next to the model picker to see your current context window usage and your plan usage for the period. Context usage is per session; plan usage is shared across all your Claude Code surfaces.
## Let Claude use your computer
Computer use lets Claude open your apps, control your screen, and work directly on your machine the way you would. Ask Claude to interact with a desktop tool that has no CLI, or automate something that only works through a GUI. For running and testing iOS apps, Desktop opens the dedicated [iOS Simulator pane](/docs/en/desktop-ios-simulator) instead of controlling your screen; the pane works without enabling computer use.
<Note>
Computer use is a research preview on macOS and Windows that requires a Pro or Max plan. It is not available on Team or Enterprise plans. The Claude Desktop app must be running.
</Note>
Computer use is off by default. [Enable it in Settings](#enable-computer-use) before Claude can control your screen. On macOS, you also need to grant Accessibility and Screen Recording permissions.
<Warning>
Unlike the [sandboxed Bash tool](/docs/en/sandboxing), computer use runs on your actual desktop with access to whatever you approve. Claude checks each action and flags potential prompt injection from on-screen content, but the trust boundary is different. See the [computer use safety guide](https://support.claude.com/en/articles/14128542) for best practices.
</Warning>
### When computer use applies
Claude has several ways to interact with an app or service, and computer use is the broadest and slowest. It tries the most precise tool first:
* If you have a [connector](#connect-external-tools) for a service, Claude uses the connector.
* If the task is a shell command, Claude uses Bash.
* If the task is browser work and you have [Claude in Chrome](/docs/en/chrome) set up, Claude uses that.
* If the task is running or testing an iOS app, Claude uses the [iOS Simulator pane](/docs/en/desktop-ios-simulator), which doesn't use screen control.
* If none of those apply, Claude uses computer use.
The [per-app access tiers](#app-permissions) reinforce this: browsers are capped at view-only, and terminals and IDEs at click-only, steering Claude toward the dedicated tool even when computer use is active. Screen control is reserved for things nothing else can reach, like native apps, hardware control panels, or proprietary tools without an API.
### Enable computer use
Computer use is off by default. If you ask Claude to do something that needs it while it's off, Claude tells you it could do the task if you enable computer use in Settings.
<Steps>
<Step title="Update the desktop app">
Make sure you have the latest version of Claude Desktop. On macOS and Windows, download or update at [claude.com/download](https://claude.com/download); on Linux, update through your package manager ([instructions](/docs/en/desktop-linux)). Then restart the app.
</Step>
<Step title="Turn on the toggle">
In the desktop app, go to **Settings > General** (under **Desktop app**). Find the **Computer use** toggle and turn it on. On Windows, the toggle takes effect immediately and setup is complete. On macOS, continue to the next step.
If you don't see the toggle, confirm you're on macOS or Windows with a Pro or Max plan, then update and restart the app.
</Step>
<Step title="Grant macOS permissions">
On macOS, grant two system permissions before the toggle takes effect:
* **Accessibility**: lets Claude click, type, and scroll
* **Screen Recording**: lets Claude see what's on your screen
The Settings page shows the current status of each permission. If either is denied, click the badge to open the relevant System Settings pane.
</Step>
</Steps>
### App permissions
The first time Claude needs to use an app, a prompt appears in your session. Click **Allow for this session** or **Deny**. Approvals last for the current session, or 30 minutes in [Dispatch-spawned sessions](#sessions-from-dispatch).
The prompt also shows what level of control Claude gets for that app. These tiers are fixed by app category and can't be changed:
| Tier | What Claude can do | Applies to |
Cut at 300 lines. The page has the rest.
desktop-ios-simulator First recorded · 144 lines, first recorded
# Test iOS apps in the simulator ## Requirements ## Run your app in the simulator ## Control the simulator yourself ## How sessions manage devices ## Grant Claude access to a device ### Allow a device the first time ### Actions that follow your permission mode ### Turn off simulator access ## Limitations ## Troubleshooting ### The simulator pane doesn't open when Claude runs the app ### The simulator pane says no simulators were found ### The simulator pane fails with Xcode 27 ## See also
The first capture of this source. The page was already there, and this is what it said.
# Test iOS apps in the simulator
> Claude Code Desktop opens your app in the iOS Simulator pane when Claude builds, runs, or checks it, with a separate simulator for each session.
<Note>
The iOS Simulator pane is in public beta in Claude Code Desktop on macOS. It's available on Pro, Max, Team, and Enterprise plans, except in Enterprise organizations that have a HIPAA configuration or [Zero Data Retention (ZDR)](/docs/en/zero-data-retention) enabled.
</Note>
The iOS Simulator pane shows your app running in Apple's iOS Simulator next to your conversation in Claude Code Desktop. When Claude builds, installs, launches, or checks your app in a simulator, the pane opens automatically and streams the device screen live. Use it to watch Claude run and test your app, or to tap through the app yourself while Claude keeps working.
The simulator pane drives the simulator directly, so it doesn't need [computer use](/docs/en/desktop#let-claude-use-your-computer) and never takes over your screen or hides your other windows. From the CLI, Claude reaches the iOS Simulator through [computer use](/docs/en/computer-use#test-a-simulator-flow) instead, which controls the simulator on your screen the same way you would with a mouse.
## Requirements
The simulator pane uses Apple's simulator tooling, which the desktop app doesn't include. Before starting a session, make sure you have:
* Claude Desktop v1.24012.0 or later
* A Mac, since Apple's iOS Simulator runs only on macOS
* [Xcode](https://developer.apple.com/xcode/) with the iOS platform installed, which provides the simulator devices. If Xcode lists no simulators yet, see [The simulator pane says no simulators were found](#the-simulator-pane-says-no-simulators-were-found)
* Use Xcode 26.x. The pane doesn't yet work with Xcode 27, which replaces the Simulator app with Device Hub. If `xcode-select` points at Xcode 27 on your Mac, see [The simulator pane fails with Xcode 27](#the-simulator-pane-fails-with-xcode-27)
<Note>
On this page, "device" refers to a simulated iPhone or iPad, one of the same simulator devices you manage in Xcode under **Window → Devices and Simulators**, not physical hardware.
</Note>
The simulator pane is available in local sessions only. In [cloud](/docs/en/desktop#run-long-running-tasks-remotely) and [SSH](/docs/en/desktop#ssh-sessions) sessions, Claude runs on a machine that can't reach the simulators on your Mac.
## Run your app in the simulator
You don't need a command or setting to open the simulator pane. Claude opens it when it runs your app in a simulator.
<Steps>
<Step title="Open your iOS project">
In Claude Code Desktop, open the **Code** tab and start a session with your app's project as the [project folder](/docs/en/desktop#start-a-session). Any project that builds an app for the iOS Simulator works.
</Step>
<Step title="Ask Claude to run or test the app">
Phrase the task around running or verifying the app. For example:
```text theme={null}
Build the app and run it in the simulator to check the onboarding flow.
```
</Step>
<Step title="Watch the app in the simulator pane">
When the app launches in a simulator, the iOS Simulator pane opens next to the conversation. The first time Claude uses a device, the desktop app asks you to allow it; see [Grant Claude access to a device](#grant-claude-access-to-a-device). Claude installs the app, taps through it, and reads the screen to verify its own changes while you watch.
</Step>
</Steps>
The simulator pane opens whenever Claude launches the app in a simulator, at any point in the session. When your request is about seeing the app, for example "does the new screen look right?", Claude starts a simulator before it begins the work. After Claude fixes a bug or changes a screen, ask it to verify the change: relaunching the app reopens the pane if it isn't open.
The simulator pane shows whichever device the app actually launched in. To test on a specific device, name it in your request, for example "run it on the iPhone SE simulator", and Claude targets that device when it builds and launches.
A device Claude boots also appears in Apple's Simulator app, and Claude can install the app on a device you already have booted.
You can also open the simulator pane yourself. Once the session has a simulator attached or has edited Swift files, the **Views** menu in the session toolbar shows an **iOS Simulator** entry. If the pane isn't showing a device yet, click **Attach simulator**, or pick a specific device from the device menu next to it; picking a shut-down device boots it. If Xcode or its simulators are missing, the pane shows the setup steps instead and checks them off as you complete them.
## Control the simulator yourself
The simulator pane is interactive, not only a viewer. While Claude works, or between tasks, you can:
* Tap and swipe by clicking and dragging on the device screen
* Press hardware buttons with the same shortcuts as Apple's Simulator app: **Cmd+Shift+H** for Home, **Cmd+L** to lock, **Cmd+Up Arrow** and **Cmd+Down Arrow** for volume
* Rotate the device a quarter turn clockwise with the rotate button or **Cmd+Right Arrow**
* Switch which device the pane shows from the device menu, which lists each simulator's OS version and whether it's booted
* Save a screenshot with **Cmd+S** or a screen recording with **Cmd+R**, using the pane's capture buttons or the shortcuts; the files are saved to your Desktop
* Stop streaming a device without shutting it down by clicking **Detach simulator**, which returns the pane to its **Attach simulator** state
The row under the device name tunes the video stream from the simulator. Lower **Frame rate** or **Resolution** if the pane strains your Mac, switch **Encoding** between H.264 and JPEG, or check **FPS** to display the frame rate the pane is receiving. These settings change how the pane displays the device, not how the app runs.
You and Claude drive the same device, so your taps change the app state Claude sees. To have Claude check a specific screen, navigate to it by tapping, then ask. While Claude is driving the device, the pane shows a **Claude is using this device** badge above the screen; hold off tapping until the badge clears, so the result reflects the app rather than your input.
## How sessions manage devices
Each device belongs to the session that launched it, so [parallel sessions](/docs/en/desktop#work-in-parallel-with-sessions) don't share a device: what you see in one session's pane reflects that session's work, not another's. Switching sessions in the sidebar switches the simulator view along with the conversation, and switching back resumes the same device where it left off. If Claude works with more than one device, each opens its own pane, up to 4 per session.
Claude Code Desktop shuts down the simulators it booted once they're no longer in use: when you quit the app, when you archive the session, or 10 minutes after you detach a device from its pane. Devices you boot yourself, whether from the pane or in Apple's Simulator app, are never shut down automatically. To shut down the attached device right away, use the shutdown button in the pane.
## Grant Claude access to a device
Claude asks for your consent before it controls a device, while building the app or opening a URL on it follows your session's permission mode. You or your organization can also turn Claude's access off entirely.
### Allow a device the first time
The first time Claude uses a simulator, the desktop app asks you to allow it. The consent covers controlling that device and taking screenshots of it, and you give it once per device rather than once per session. Claude's screenshots of the device are sent to Anthropic and kept under your normal conversation retention settings, so don't sign in to real accounts on a device Claude uses.
After you allow a device, Claude's actions on it, such as tapping, typing, launching the app, and taking screenshots, run without further prompts. They carry the same trust as you clicking in the pane, and they only touch the simulated device, so the pane doesn't need the macOS Accessibility and Screen Recording permissions that computer use requires.
If you decline, the device still boots and the pane still works for your own taps; only Claude's access stays off. To change your mind later, click **Let Claude use it** in the pane.
### Actions that follow your permission mode
Two actions follow your session's [permission mode](/docs/en/permissions#permission-modes) instead of the one-time consent:
* Opening a URL on the device, for example to test a deep link or load a page in the device's Safari, because a URL can carry data off the device.
* Building the app, because `xcodebuild` runs your project's build scripts on your Mac. Checking on a build already in progress doesn't prompt.
### Turn off simulator access
You can turn Claude's simulator access off in the desktop app's settings. Organizations have two ways to turn it off for everyone:
* The `disableMobileSimulatorTools` [managed setting](/docs/en/desktop#managed-settings) blocks Claude's simulator tools. The simulator pane stays usable for your own taps, and the setting can't be overridden from within the app.
* The `requireCoworkFullVmSandbox` policy key, which runs Claude's tools inside an isolated virtual machine instead of on your Mac, disables the simulator pane and Claude's simulator tools entirely, so the pane can't attach a device while it's set.
Claude tells you when either applies.
## Limitations
Claude drives simulated devices only and can't control a physical iPhone or iPad. To test on one, run the app on it from Xcode yourself, then describe what you see or attach a screenshot to the conversation for Claude to work from.
## Troubleshooting
### The simulator pane doesn't open when Claude runs the app
Claude may not have recognized that you wanted to run or test the app, or the simulator tooling may be missing. Check the following:
* State the goal explicitly, for example "run the app in the iOS Simulator and tap through the signup flow".
* Confirm Xcode and the iOS simulators are installed and that your Xcode version meets the [requirements](#requirements).
* If your organization manages Claude Code, the [simulator tools may be disabled by policy](#turn-off-simulator-access).
* If you're in an Enterprise organization that has a HIPAA configuration or ZDR enabled, the simulator pane isn't available to you.
* The simulator pane requires Claude Desktop v1.24012.0 or later. Open **Claude → Check for Updates**, then restart the app.
### The simulator pane says no simulators were found
If `xcode-select` points at Xcode 27, the pane can report no simulators even though devices exist; see [The simulator pane fails with Xcode 27](#the-simulator-pane-fails-with-xcode-27). Otherwise, Xcode is installed but has no iOS simulators to list. The simulator pane shows the setup steps to follow and checks them off as each one completes. To install the missing piece manually, download the iOS simulator runtime from Xcode's settings, or run `xcodebuild -downloadPlatform iOS`.
### The simulator pane fails with Xcode 27
The pane doesn't yet work with Xcode 27, which replaces the Simulator app with Device Hub. With Xcode 27 selected, attaching a device fails, or the pane reports that no simulators were found even though devices exist.
The pane uses whichever Xcode `xcode-select` points at. If Xcode 27 is your only install, install Xcode 26.x alongside it first. Then select the 26.x install by its path. For example, if it's installed as `/Applications/Xcode-26.4.app`:
```bash theme={null}
sudo xcode-select -s /Applications/Xcode-26.4.app
```
Run `xcode-select -p` to check which install is selected.
## See also
* [Computer use in Desktop](/docs/en/desktop#let-claude-use-your-computer): screen control for apps without a dedicated pane
* [Computer use from the CLI](/docs/en/computer-use): how the CLI reaches the iOS Simulator
* [Work in parallel with sessions](/docs/en/desktop#work-in-parallel-with-sessions): how sessions isolate changes
* [Get started with Claude Code Desktop](/docs/en/desktop-quickstart)
desktop-linux First recorded · 127 lines, first recorded
# Claude Desktop on Linux (beta) ## Requirements ## Install ### Install from a downloaded file ## Update ## Uninstall ## Troubleshoot ### Unable to locate package claude-desktop ## What's not in the Linux beta yet
The first capture of this source. The page was already there, and this is what it said.
# Claude Desktop on Linux (beta)
> Install and update the Claude desktop app on Ubuntu and Debian
<Note>
Linux support for the Claude desktop app is in beta.
</Note>
The desktop app on Linux gives you the same Chat, Cowork, and Claude Code experience as on macOS and Windows: parallel sessions, visual diff review, an integrated terminal and editor, and live app preview. See [Use Claude Code Desktop](/docs/en/desktop) for the feature reference.
## Requirements
* Ubuntu 22.04 or later, or Debian 12 or later
* x86\_64 or arm64
Other Debian-based distributions that meet these requirements may work but aren't officially tested.
## Install
Install from Anthropic's apt repository so that updates arrive through your system's regular package updates. Open a terminal and run the commands in each step.
<Steps>
<Step title="Add Anthropic's apt repository">
This step downloads the signing key with `curl` and verifies it with `gpg`, which fresh Debian and Ubuntu installations may not include. If either command reports `command not found`, install both first:
```bash theme={null}
sudo apt install curl gnupg
```
Download Anthropic's signing key:
```bash theme={null}
sudo curl -fsSLo /usr/share/keyrings/claude-desktop-archive-keyring.asc https://downloads.claude.ai/claude-desktop/key.asc
```
If this download fails, `apt update` later fails with `NO_PUBKEY BAA929FF1A7ECACE`. Confirm the key downloaded and belongs to Anthropic before continuing:
```bash theme={null}
gpg --show-keys /usr/share/keyrings/claude-desktop-archive-keyring.asc
```
The fingerprint gpg prints should be `31DDDE24DDFAB679F42D7BD2BAA929FF1A7ECACE`. If gpg reports that the file can't be opened or contains no valid OpenPGP data, the download failed or returned the wrong content: confirm your network can reach `downloads.claude.ai`, then rerun the download command.
Register the repository:
```bash theme={null}
echo "deb [arch=amd64,arm64 signed-by=/usr/share/keyrings/claude-desktop-archive-keyring.asc] https://downloads.claude.ai/claude-desktop/apt/stable stable main" | sudo tee /etc/apt/sources.list.d/claude-desktop.list
```
</Step>
<Step title="Install the package">
```bash theme={null}
sudo apt update && sudo apt install claude-desktop
```
</Step>
<Step title="Launch and sign in">
Launch **Claude** from your application launcher, or run `claude-desktop` from a terminal, and sign in with your Anthropic account.
The Linux app signs in the same way as on macOS and Windows: with a claude.ai subscription, or through your organization's SSO. Desktop doesn't accept a Claude Console API key directly; use the [CLI](/docs/en/quickstart) for API-key authentication. For enterprise deployments that route Desktop to Google Cloud's Agent Platform or an LLM gateway, see [Claude Desktop on 3P](https://claude.com/docs/third-party/claude-desktop/overview) and [network configuration](/docs/en/network-config).
</Step>
</Steps>
### Install from a downloaded file
If you can't install through the apt repository, download the `.deb` package directly from the repository's package pool. This command looks up the newest package for your architecture in the repository index, then downloads it to the current directory:
```bash theme={null}
curl -fLO "https://downloads.claude.ai/claude-desktop/apt/stable/$(curl -s "https://downloads.claude.ai/claude-desktop/apt/stable/dists/stable/main/binary-$(dpkg --print-architecture)/Packages" | grep '^Filename: pool/main/c/claude-desktop/claude-desktop_' | sort -V | tail -n 1 | cut -d' ' -f2)"
```
If the command fails with `Remote file name has no length`, the lookup returned no package path. This can mean the repository index couldn't be fetched, for example when your network blocks `downloads.claude.ai`, or that no package exists for your architecture. Confirm that your network can reach `downloads.claude.ai` and that `dpkg --print-architecture` prints `amd64` or `arm64`; the repository doesn't publish packages for other architectures.
To install without registering Anthropic's apt repository, first create `/etc/default/claude-desktop` with the line `CLAUDE_DESKTOP_ADD_REPO="false"`. Without the repository, apt doesn't deliver new versions; to update, re-run the download command and reinstall, or [register the repository](#install) later.
Then open the downloaded file with your software installer, such as GNOME Software, or install it with apt from the directory that contains the downloaded file:
```bash theme={null}
sudo apt install ./claude-desktop_*.deb
```
If apt reports `E: Unsupported file ./claude-desktop_*.deb given on commandline`, the pattern didn't match a `.deb` file in the current directory. Confirm the download completed, then run the command again from the directory that contains the file.
Installing the `.deb` also registers Anthropic's apt repository at `/etc/apt/sources.list.d/claude-desktop.list`, so future updates arrive with your system's [regular package updates](#update).
## Update
The desktop app doesn't update itself on Linux. Updates arrive with your system's regular package updates:
```bash theme={null}
sudo apt update && sudo apt upgrade
```
Your distribution's graphical software updater will also pick up new versions.
## Uninstall
```bash theme={null}
sudo apt remove claude-desktop
```
Uninstalling the package also removes the repository entry and signing key it registered. If you added the repository entry yourself with the [Add Anthropic's apt repository](#install) step, remove it too:
```bash theme={null}
sudo rm /etc/apt/sources.list.d/claude-desktop.list
```
## Troubleshoot
### Unable to locate package claude-desktop
If `sudo apt install claude-desktop` fails with `E: Unable to locate package claude-desktop`, apt didn't find the repository you added. Check the following:
* Confirm the repository entry was written. `cat /etc/apt/sources.list.d/claude-desktop.list` should show the `deb` line from the [Add Anthropic's apt repository](#install) step. If the file is empty or missing, run that step again.
* Confirm your architecture is supported. `dpkg --print-architecture` should print `amd64` or `arm64`. The repository doesn't publish packages for other architectures.
* Run `sudo apt update` again and check its output for errors related to `downloads.claude.ai`. A network or key error there means the repository was added but couldn't be reached or verified.
If the repository is in place and reachable and the package is still not found, [install from a downloaded file](#install-from-a-downloaded-file) instead.
## What's not in the Linux beta yet
* **Computer Use**: [app and screen control](/docs/en/desktop#let-claude-use-your-computer) isn't available on Linux.
* **Dictation**: voice input isn't available in the Linux desktop app. Use [voice dictation](/docs/en/voice-dictation) in the CLI instead.
* **Quick Entry global hotkey**: works on X11. On native Wayland it requires your desktop environment's GlobalShortcuts portal.
* **Fedora and RHEL**: only Debian-based distributions are supported today. Support for additional distributions is coming in the future.
For anything not yet available in the desktop app, the [CLI](/docs/en/quickstart) runs the same Claude Code engine and supports a wider range of Linux distributions; see the [system requirements](/docs/en/setup#system-requirements).
desktop-quickstart First recorded · 134 lines, first recorded
# Get started with the desktop app ## Install ## Start your first session ## Now what? ## Coming from the CLI? ## What's next
The first capture of this source. The page was already there, and this is what it said.
# Get started with the desktop app
> Install Claude Code on desktop and start your first coding session
The desktop app gives you Claude Code with a graphical interface built for running multiple sessions side by side: a sidebar for managing parallel work, a drag-and-drop layout with an integrated terminal and file editor, visual diff review, live app preview, GitHub PR monitoring with auto-merge, and scheduled tasks. No terminal required.
<CardGroup cols={3}>
<Card title="Download for macOS" icon="apple" href="https://claude.ai/api/desktop/darwin/universal/dmg/latest/redirect?utm_source=claude_code&utm_medium=docs">
Universal build for Intel and Apple Silicon
</Card>
<Card title="Download for Windows" icon="windows" href="https://claude.ai/api/desktop/win32/x64/setup/latest/redirect?utm_source=claude_code&utm_medium=docs">
For x64 processors
</Card>
<Card title="Get Claude for Linux (beta)" icon="linux" href="/docs/en/desktop-linux">
apt or .deb for Ubuntu and Debian
</Card>
</CardGroup>
For Windows ARM64, download the [ARM64 installer](https://claude.ai/api/desktop/win32/arm64/setup/latest/redirect?utm_source=claude_code\&utm_medium=docs). On Linux, install with apt; see [Claude Desktop on Linux](/docs/en/desktop-linux).
<Note>
Claude Code requires a [Pro, Max, Team, or Enterprise subscription](https://claude.com/pricing?utm_source=claude_code\&utm_medium=docs\&utm_content=desktop_quickstart_pricing).
</Note>
This page walks through installing the app and starting your first session. If you're already set up, see [Use Claude Code Desktop](/docs/en/desktop) for the full reference.
The desktop app has three tabs:
* **Chat**: General conversation with no file access, similar to claude.ai.
* **Cowork**: An autonomous background agent that works on tasks in a sandboxed virtual machine with its own environment, running independently while you do other work. On-device Cowork sessions run the VM on your computer; remote Cowork sessions run on an Anthropic-managed VM instead.
* **Code**: An interactive coding assistant with direct access to your local files. You review and approve each change in real time.
Chat and Cowork are covered in the [Claude Help Center](https://support.claude.com/); installing and deploying the desktop app is covered in the [Claude Desktop support articles](https://support.claude.com/en/collections/16163169-claude-desktop). This page focuses on the **Code** tab.
## Install
<Steps>
<Step title="Install and sign in">
On macOS and Windows, download the installer from the links above and run it. On Linux, follow the install steps in [Claude Desktop on Linux](/docs/en/desktop-linux). Launch Claude from your Applications folder on macOS, the Start menu on Windows, or your application launcher on Linux, then sign in with your Anthropic account.
</Step>
<Step title="Open the Code tab">
Click the **Code** tab at the top center. If clicking Code prompts you to upgrade, you need to [subscribe to a paid plan](https://claude.com/pricing?utm_source=claude_code\&utm_medium=docs\&utm_content=desktop_quickstart_upgrade) first. If it prompts you to sign in online, complete the sign-in and restart the app. If you see a 403 error, see [authentication troubleshooting](/docs/en/desktop#403-or-authentication-errors-in-the-code-tab).
</Step>
</Steps>
The desktop app includes Claude Code. You don't need to install Node.js or the CLI separately. To use `claude` from the terminal, install the CLI separately. See [Get started with the CLI](/docs/en/quickstart).
## Start your first session
With the Code tab open, choose a project and give Claude something to do.
<Steps>
<Step title="Choose an environment and folder">
Select **Local** to run Claude on your machine using your files directly. Click **Select folder** and choose your project directory.
<Tip>
Start with a small project you know well. It's the fastest way to see what Claude Code can do. On Windows, [Git](https://git-scm.com/downloads/win) must be installed for local sessions to work. Most Macs include Git by default.
</Tip>
You can also select:
* **Cloud**: Run sessions in the cloud that continue even if you close the app. Cloud sessions use the same infrastructure as [Claude Code on the web](/docs/en/claude-code-on-the-web).
* **SSH**: Connect to a remote machine over SSH, such as your own servers, cloud VMs, or dev containers. Desktop installs Claude Code on the remote machine automatically the first time you connect.
* **WSL** (Windows): Run the session inside a [WSL 2 distribution](/docs/en/desktop-wsl); Claude Code, tools, and git execute on the Linux side with native paths.
</Step>
<Step title="Choose a model">
Select a model from the dropdown next to the send button. See [models](/docs/en/model-config#available-models) for a comparison of the available models. You can change the model later from the same dropdown.
</Step>
<Step title="Tell Claude what to do">
Type what you want Claude to do:
* `Find a TODO comment and fix it`
* `Add tests for the main function`
* `Create a CLAUDE.md with instructions for this codebase`
A [session](/docs/en/desktop#work-in-parallel-with-sessions) is a conversation with Claude about your code. Each session tracks its own context and changes, so you can work on multiple tasks without them interfering with each other.
</Step>
<Step title="Review and accept changes">
By default, the Code tab starts in [Manual mode](/docs/en/desktop#choose-a-permission-mode), where Claude proposes changes and waits for your approval before applying them. You'll see:
1. A [diff view](/docs/en/desktop#review-changes-with-diff-view) showing exactly what will change in each file
2. Accept/Reject buttons to approve or decline each change
3. Real-time updates as Claude works through your request
If you reject a change, Claude will ask how you'd like to proceed differently. Your files aren't modified until you accept.
</Step>
</Steps>
## Now what?
You've made your first edit. For the full reference on everything Desktop can do, see [Use Claude Code Desktop](/docs/en/desktop). Here are some things to try next.
**Interrupt and steer.** You can redirect Claude at any point. Click the stop button to interrupt immediately, or type a correction and press **Enter** to send it without stopping the running action. Either way, you don't have to wait for it to finish or start over.
**Give Claude more context.** Type `@filename` in the prompt box to pull a specific file into the conversation, attach images and PDFs using the attachment button, or drag and drop files directly into the prompt. The more context Claude has, the better the results. See [Add files and context](/docs/en/desktop#add-files-and-context-to-prompts).
**Use skills for repeatable tasks.** Type `/` or click **+** → **Slash commands** to browse [built-in commands](/docs/en/commands), [custom skills](/docs/en/skills), and plugin skills. Skills are reusable prompts you can invoke whenever you need them, like code review checklists or deployment steps.
**Review changes before committing.** After Claude edits files, a `+12 -1` indicator appears. Click it to open the [diff view](/docs/en/desktop#review-changes-with-diff-view), review modifications file by file, and comment on specific lines. Claude reads your comments and revises. Click **Review code** to have Claude evaluate the diffs itself and leave inline suggestions.
**Adjust how much control you have.** Your [permission mode](/docs/en/desktop#choose-a-permission-mode) sets how much Claude can do without asking for approval:
* **Manual**: the default. Claude asks before editing files or running commands.
* **Accept edits**: Claude auto-accepts file edits for faster iteration.
* **Plan**: Claude proposes an approach without editing any files, which is useful before a large refactor.
**Add plugins for more capabilities.** Click the **+** button next to the prompt box and select **Plugins** to browse and install [plugins](/docs/en/desktop#install-plugins) that add skills, agents, MCP servers, and more.
**Arrange your workspace.** Drag the chat, diff, terminal, file, and browser panes into whatever layout you want. Open the terminal with **Ctrl+\`** to run commands alongside your session, or click a file path to open it in the file pane. See [Arrange your workspace](/docs/en/desktop#arrange-your-workspace).
**Preview your app.** When you run your dev server in the desktop, your app opens in the Browser pane, which can also [open external sites](/docs/en/desktop#browse-external-sites). Claude can view the running app, test endpoints, inspect logs, and iterate on what it sees. See [Preview your app](/docs/en/desktop#preview-your-app).
**Track your pull request.** After opening a PR, Claude Code monitors CI check results and can automatically fix failures or merge the PR once all checks pass. See [Monitor pull request status](/docs/en/desktop#monitor-pull-request-status).
**Put Claude on a schedule.** Set up [scheduled tasks](/docs/en/desktop-scheduled-tasks) to run Claude automatically on a recurring basis: a daily code review every morning, a weekly dependency audit, or a briefing that pulls from your connected tools.
**Scale up when you're ready.** Open [parallel sessions](/docs/en/desktop#work-in-parallel-with-sessions) from the sidebar to work on multiple tasks at once, each in its own Git worktree, and open the [tasks pane](/docs/en/desktop#watch-background-tasks) to watch the subagents and background commands a session has running. Open a [side chat](/docs/en/desktop#ask-a-side-question-without-derailing-the-session) to ask a question without derailing the main thread. Send [long-running work to the cloud](/docs/en/desktop#run-long-running-tasks-remotely) so it continues even if you close the app, or [continue a session on the web or in your IDE](/docs/en/desktop#continue-in-another-surface) if a task takes longer than expected. [Connect external tools](/docs/en/desktop#extend-claude-code) like GitHub, Slack, and Linear to bring your workflow together.
## Coming from the CLI?
Desktop runs the same engine as the CLI with a graphical interface. You can run both simultaneously on the same project, and they share configuration (CLAUDE.md files, MCP servers, hooks, skills, and settings). For a full comparison of features, flag equivalents, and what's not available in Desktop, see [CLI comparison](/docs/en/desktop#coming-from-the-cli).
## What's next
* [Use Claude Code Desktop](/docs/en/desktop): permission modes, parallel sessions, diff view, connectors, and enterprise configuration
* [Troubleshooting](/docs/en/desktop#troubleshooting): solutions to common errors and setup issues
* [Best practices](/docs/en/best-practices): tips for writing effective prompts and getting the most out of Claude Code
* [Common workflows](/docs/en/common-workflows): tutorials for debugging, refactoring, testing, and more
desktop-scheduled-tasks First recorded · 104 lines, first recorded
# Schedule recurring tasks in Claude Code Desktop ## Compare scheduling options ## Create a scheduled task ## Schedule options ## How scheduled tasks run ## Missed runs ## Permissions for scheduled tasks ## Manage scheduled tasks ## Related resources
The first capture of this source. The page was already there, and this is what it said.
# Schedule recurring tasks in Claude Code Desktop > Set up scheduled tasks in Claude Code Desktop to run Claude automatically on a recurring basis for daily code reviews, dependency audits, or morning briefings. Scheduled tasks start a new session automatically at a time and frequency you choose. Use them for recurring work like daily code reviews, dependency update checks, or morning briefings that pull from your calendar and inbox. The Desktop app's **Routines** page lets you create both local scheduled tasks and remote [routines](/docs/en/routines). A local task runs on your machine with direct access to your files and tools, but only fires while the app is open and your computer is awake. A remote routine runs in the cloud even when your computer is off, and can also fire on API calls or GitHub events. This page covers local scheduled tasks; for remote routines and their trigger options, see [Routines](/docs/en/routines). ## Compare scheduling options Claude Code offers three ways to schedule recurring or one-off work: | | [Cloud](/docs/en/routines) | [Desktop](/docs/en/desktop-scheduled-tasks) | [`/loop`](/docs/en/scheduled-tasks) | | :------------------------- | :---------------------------------- | :------------------------------------- | :---------------------------------- | | Runs on | Cloud, Anthropic-managed by default | Your machine | Your machine | | Requires machine on | No | Yes | Yes | | Requires open session | No | No | Yes | | Persistent across restarts | Yes | Yes | Restored on `--resume` if unexpired | | Access to local files | No (fresh clone) | Yes | Yes | | MCP servers | Connectors configured per task | [Config files](/docs/en/mcp) and connectors | Inherits from session | | Permission prompts | No (runs autonomously) | Configurable per task | Inherits from session | | Customizable schedule | Via `/schedule` in the CLI | Yes | Yes | | Minimum interval | 1 hour | 1 minute | 1 minute | <Tip> Use **cloud tasks** for work that should run reliably without your machine. Use **Desktop tasks** when you need access to local files and tools. Use **`/loop`** for quick polling during a session. </Tip> <Note> By default, scheduled tasks run against whatever state your working directory is in, including uncommitted changes. Enable the worktree toggle when creating the task to give each run its own isolated Git worktree, the same way [parallel sessions](/docs/en/desktop#work-in-parallel-with-sessions) work. </Note> ## Create a scheduled task Click **Routines** in the sidebar, then click **New routine** and choose **Local**. Configure these fields: | Field | Description | | ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | Name | Identifier for the task. Converted to lowercase kebab-case and used as the folder name on disk. Must be unique across your tasks. | | Description | Short summary shown in the task list. | | Instructions | What Claude should do when the task runs. Write this the same way you'd write any message in the prompt box. The instructions input includes pickers for the permission mode and model, and below it you select the working folder and whether to run in an isolated worktree. | | Schedule | How often the task runs. See [schedule options](#schedule-options) below. | A folder is required before you can save the task. If you haven't trusted that folder yet, Desktop prompts you to trust it before saving. You can also create a task by describing what you want in any session. For example, "set up a daily code review that runs every morning at 9am" creates a recurring task, and "remind me at 3pm tomorrow to check the deploy" creates a one-time task that disables itself after it fires. ## Schedule options Pick a preset from the Schedule control: * **Manual**: no schedule, only runs when you click **Run now**. Useful for saving a prompt you trigger on demand * **Hourly**: runs every hour * **Daily**: shows a time picker, defaults to 9:00 AM local time * **Weekdays**: same as Daily but skips Saturday and Sunday * **Weekly**: shows a time picker and a day picker For intervals the picker doesn't offer, such as every 15 minutes, the first of each month, or a single run at a specific future time, ask Claude in any Desktop session to set the schedule. Use plain language; for example, "schedule a task to run all the tests every 6 hours." ## How scheduled tasks run Scheduled tasks run on your machine. Desktop checks the schedule every minute while the app is open and starts a fresh session when a task is due, independent of any manual sessions you have open. Each task gets a small delay of a few minutes after the scheduled time to stagger API traffic. The delay is deterministic: the same task always starts at the same offset. When a task fires, you get a desktop notification and a new session appears under a **Scheduled** section in the sidebar. Open it to see what Claude did, review changes, or respond to permission prompts. Claude can edit files, run commands, create commits, and open pull requests, the same as in a session you start yourself, but can't send or receive [messages between your desktop sessions](/docs/en/desktop#work-across-sessions) through the desktop app's session surface. Tasks only run while the desktop app is running and your computer is awake. If your computer sleeps through a scheduled time, the run is skipped. To prevent idle-sleep, enable **Keep computer awake** in Settings under **Desktop app → General**. Closing the laptop lid still puts it to sleep. For tasks that need to run even when your computer is off, or that should trigger on an API call or GitHub event, create a remote [routine](/docs/en/routines) instead. ## Missed runs When the app starts or your computer wakes, Desktop checks whether each task missed any runs in the last seven days. If it did, Desktop starts exactly one catch-up run for the most recently missed time and discards anything older. A daily task that missed six days runs once on wake. Desktop shows a notification when a catch-up run starts. Keep this in mind when writing prompts. A task scheduled for 9am might run at 11pm if your computer was asleep all day. If timing matters, add guardrails to the prompt itself, for example: "Only review today's commits. If it's after 5pm, skip the review and just post a summary of what was missed." ## Permissions for scheduled tasks Each task has its own permission mode, which you set when creating or editing the task. Allow rules from `~/.claude/settings.json` also apply to scheduled task sessions. If a task runs in [Manual mode](/docs/en/desktop#choose-a-permission-mode) and needs to run a tool it doesn't have permission for, the run stalls until you approve it. The session stays open in the sidebar so you can answer later. To avoid stalls, click **Run now** after creating a task, watch for permission prompts, and select "always allow" for each one. Future runs of that task auto-approve the same tools without prompting. You can review and revoke these approvals from the task's detail page. Connector tools [your organization set to `ask`](/docs/en/mcp#organization-controls-on-connector-tools) and MCP tools marked [`requiresUserInteraction`](/docs/en/mcp#require-approval-for-a-specific-tool) prompt on every call and don't offer an always-allow option. Runs that call these tools stall each time. ## Manage scheduled tasks Click a task in the **Routines** list to open its detail page. From here you can: * **Run now**: start the task immediately without waiting for the next scheduled time * **Status**: toggle between Active and Paused to pause or resume scheduled runs without deleting the task * **Edit**: change the instructions, schedule, folder, or other settings * **Review history**: see every past run, including skipped runs. Hover a skipped entry to see why: your computer was asleep, the previous run was still in progress, or other scheduled tasks were already running. Click **Show more** to load older entries. * **Review allowed permissions**: see and revoke saved tool approvals for this task from the **Always allowed** panel * **Delete**: remove the task and archive all sessions it created. An **Also delete files on disk** checkbox appears in the confirmation dialog; check it to also remove the task's `SKILL.md` file and associated data from `~/.claude/scheduled-tasks/`. You can also list, create, edit, and pause tasks by asking Claude in any Desktop session. For example, "pause my dependency-audit task" or "show me my scheduled tasks." To delete a task, use the **Delete** button on its detail page. A scheduled task can also modify its own schedule or prompt from within a running session using the `update_scheduled_task` MCP tool. This lets a task reschedule itself based on what it finds, for example, rescheduling a code review to run earlier when it detects a release branch has been created. To edit a task's prompt on disk, open `~/.claude/scheduled-tasks/<task-name>/SKILL.md` (or under [`CLAUDE_CONFIG_DIR`](/docs/en/env-vars) if set). The file uses YAML frontmatter for `name` and `description`, with the prompt as the body. Changes take effect on the next run. Schedule, folder, model, and enabled state are not in this file: change them through the Edit form or ask Claude. ## Related resources * [Routines](/docs/en/routines): run tasks in the cloud on a schedule, via API call, or in response to GitHub events, even when your computer is off * [Run prompts on a schedule](/docs/en/scheduled-tasks): session-scoped scheduling with `/loop` in the CLI * [Claude Code GitHub Actions](/docs/en/github-actions): run Claude on a schedule in CI instead of on your machine * [Use Claude Code Desktop](/docs/en/desktop): the full Desktop app guide
desktop-wsl First recorded · 43 lines, first recorded
# Claude Code Desktop in WSL ## Requirements ## Start a WSL session ## What works in a WSL session ## Managed devices
The first capture of this source. The page was already there, and this is what it said.
# Claude Code Desktop in WSL
> Run Code sessions inside a WSL 2 distribution on Windows
On Windows, the Code tab can run a session inside a WSL 2 distribution instead of on Windows itself. The session's Claude Code process, its tools, and git all execute inside the distribution, using its Linux toolchain and native Linux paths, the same environment your project targets.
Use a WSL session when your repository lives inside the distribution's filesystem. Working on those files from Windows goes through a network filesystem, which is slow and breaks file watching; running the session inside the distribution avoids both.
## Requirements
* Windows 10 or 11 with [WSL 2](https://learn.microsoft.com/windows/wsl/install). WSL 1 isn't supported.
* At least one installed distribution (for example, Ubuntu).
* `git` installed inside the distribution.
## Start a WSL session
<Steps>
<Step title="Pick a distribution">
Start a new session in the Code tab and open the environment picker. Your installed WSL 2 distributions appear in a **WSL** section. Pick one.
</Step>
<Step title="Choose a folder">
The session starts in the distribution's home directory. Use the folder picker to choose a project folder. Browsing happens inside the distribution, with Linux paths like `/home/you/project`.
</Step>
<Step title="Trust the folder">
The first session in a folder shows the workspace trust dialog. You grant trust per distribution and folder. A folder you trust in one distribution isn't trusted in another distribution or at the same path on Windows.
</Step>
</Steps>
The first session in a distribution takes a little longer while Claude sets up inside it. You can also open a `\\wsl.localhost\...` folder from the normal folder picker, and it reopens inside that distribution.
Folders you've used recently appear in the picker per distribution, so reconnecting to a project is one click.
## What works in a WSL session
Parallel sessions, side chats, visual diff review, branch and pull request status, and worktrees all work, backed by the git and toolchain inside the distribution. "Open in editor" opens VS Code connected to the distribution through [Remote - WSL](https://code.visualstudio.com/docs/remote/wsl).
A few features aren't available in WSL sessions yet: the integrated terminal, connectors and plugins, session forking, the file browser pane, and file suggestions when you type `@` in the composer.
## Managed devices
On devices managed by an organization, WSL sessions may be unavailable. If session start fails with a message that the device is managed, that's controlled by your administrator. Administrators: see [how settings reach devices](/docs/en/admin-setup#decide-how-settings-reach-devices) in the deployment guide.
devcontainer First recorded · 195 lines, first recorded
# Development containers ## Add Claude Code to your dev container ## Persist authentication and settings across rebuilds ## Enforce organization policy ## Restrict network egress ## Run without permission prompts ## Try the reference container ## Next steps
The first capture of this source. The page was already there, and this is what it said.
# Development containers
> Run Claude Code inside a dev container for consistent, isolated environments across your team.
A [development container](https://containers.dev/), or dev container, lets you define an identical, isolated environment that every engineer on your team can run. With Claude Code installed in that container, commands Claude runs execute inside it rather than on the host machine, while edits to your project files appear in your local repository as you work.
This page covers [installing Claude Code in a dev container](#add-claude-code-to-your-dev-container), then a set of self-contained configuration topics: persisting authentication across rebuilds, enforcing organization policy, restricting network egress, and running without permission prompts. Read the ones that match your setup.
<Warning>
While the dev container provides substantial protections, no system is completely immune to all attacks.
When executed with `--dangerously-skip-permissions`, dev containers do not prevent a malicious project from exfiltrating anything accessible inside the container, including the Claude Code credentials stored in [`~/.claude`](/docs/en/claude-directory).
Only use dev containers when developing with trusted repositories, and monitor Claude's activities.
Avoid mounting host secrets such as `~/.ssh` or cloud credential files into the container; prefer repository-scoped or short-lived tokens.
</Warning>
<Accordion title="How dev containers work with your editor">
<img src="https://mintcdn.com/claude-code/YvJyjZfd9yMihr0i/images/devcontainer-architecture.svg?fit=max&auto=format&n=YvJyjZfd9yMihr0i&q=85&s=9017b1d16a446c6cc37ba562f35b9aae" className="dark:hidden" alt="Diagram showing an editor on the host connecting to a Docker dev container. Claude Code, the terminal, and build tools run inside the container. The host repository is bind-mounted into the container as the workspace." width="640" height="300" data-path="images/devcontainer-architecture.svg" />
<img src="https://mintcdn.com/claude-code/_xqph1dUOslCOwsj/images/devcontainer-architecture-dark.svg?fit=max&auto=format&n=_xqph1dUOslCOwsj&q=85&s=a0a340b1f2afc6a590696102c8acaaca" className="hidden dark:block" alt="Diagram showing an editor on the host connecting to a Docker dev container. Claude Code, the terminal, and build tools run inside the container. The host repository is bind-mounted into the container as the workspace." width="640" height="300" data-path="images/devcontainer-architecture-dark.svg" />
A dev container runs as a Docker container, either on your machine or on a cloud host such as GitHub Codespaces. An editor that supports the Dev Containers spec, such as VS Code, GitHub Codespaces, a JetBrains IDE, or Cursor, connects to that container: you browse and edit files in the editor as usual, but the integrated terminal, language servers, and build tools all run inside the container rather than on your host. Editors without dev container support, such as plain Vim, are not part of this workflow.
Claude Code runs inside the container, so it sees the same files, dependencies, and tools as the rest of your project's toolchain. In VS Code you can use either the [Claude Code extension panel](/docs/en/vs-code) or run `claude` in the integrated terminal; both run inside the container and share the same `~/.claude` configuration.
</Accordion>
## Add Claude Code to your dev container
Claude Code installs into any dev container through the [Claude Code Dev Container Feature](https://github.com/anthropics/devcontainer-features/tree/main/src/claude-code).
The settings work with any tool that supports the Dev Containers spec, such as VS Code, GitHub Codespaces, or JetBrains IDEs. The steps below use VS Code as an example.
When you open the container in VS Code or Codespaces, the feature also adds the Claude Code VS Code extension; other editors ignore that part.
<Tip>
New to dev containers? The [VS Code Dev Containers tutorial](https://code.visualstudio.com/docs/devcontainers/tutorial) walks through installing Docker, the extension, and opening your first container. For a fuller hardened example with a firewall and persistent volumes, see [Try the reference container](#try-the-reference-container).
</Tip>
<Steps>
<Step title="Create or update devcontainer.json">
Save the following as `.devcontainer/devcontainer.json` in your repository, or add the `features` block to your existing file.
The version tag at the end, such as `:1.0`, pins the feature's install script, not the Claude Code release. The feature installs the latest Claude Code, and Claude Code auto-updates itself inside the container by default.
To pin the CLI version or disable auto-update, see [Enforce organization policy](#enforce-organization-policy).
```json .devcontainer/devcontainer.json theme={null}
{
"image": "mcr.microsoft.com/devcontainers/base:ubuntu",
"features": {
"ghcr.io/anthropics/devcontainer-features/claude-code:1.0": {}
}
}
```
Replace the `image` line with your project's base image or remove it if your existing file uses a Dockerfile.
The Claude Code feature installs Node.js itself when the base image doesn't provide it. If that install fails and the build stops with `Failed to install Node.js and npm`, add `"ghcr.io/devcontainers/features/node:1": {}` to the `features` block above the Claude Code feature and rebuild.
</Step>
<Step title="Rebuild the container">
Open the VS Code Command Palette with `Cmd+Shift+P` on Mac or `Ctrl+Shift+P` on Windows and Linux, and run **Dev Containers: Rebuild Container**.
For other tools, follow that tool's rebuild action: see [rebuilding in GitHub Codespaces](https://docs.github.com/en/codespaces/developing-in-a-codespace/rebuilding-the-container-in-a-codespace), the [Dev Containers CLI](https://github.com/devcontainers/cli), or your IDE's dev container documentation.
</Step>
<Step title="Sign in to Claude Code">
Open a terminal in the rebuilt container and run `claude`, then follow the authentication prompt.
</Step>
</Steps>
What you see at the authentication prompt depends on your provider:
* **Anthropic**: sign in through a browser with your Claude or Anthropic Console account
* **[Amazon Bedrock, Google Cloud's Agent Platform, or Microsoft Foundry](/docs/en/third-party-integrations)**: Claude Code uses your cloud provider credentials, with no browser prompt
For cloud providers, pass credentials into the container as environment variables through `containerEnv`, a Codespaces secret, or your cloud's workload identity rather than mounting credential files from the host. See [Amazon Bedrock](/docs/en/amazon-bedrock), [Google Cloud's Agent Platform](/docs/en/google-vertex-ai), or [Microsoft Foundry](/docs/en/microsoft-foundry) for the credential chain Claude Code reads.
See [Choose your API provider](/docs/en/admin-setup#choose-your-api-provider) to decide which path fits your organization.
<Note>
If the browser sign-in completes but the callback never reaches the container, copy the code shown in the browser and paste it at the `Paste code here if prompted` prompt in the terminal. This can happen when the editor's port forwarding doesn't route the localhost callback.
</Note>
## Persist authentication and settings across rebuilds
By default, the container's home directory is discarded on rebuild, so engineers must sign in again each time. Claude Code stores its authentication token, user settings, and session history under the [`~/.claude`](/docs/en/claude-directory) directory. It stores your OAuth account, personal MCP servers, and per-project trust in [`~/.claude.json`](/docs/en/settings#global-config-settings), a separate file outside that directory, so mounting a volume at `~/.claude` alone doesn't keep you signed in. Mount a named volume at `~/.claude` and set [`CLAUDE_CONFIG_DIR`](/docs/en/env-vars) to the same path so Claude Code writes `.claude.json` inside the volume.
The following example mounts the volume and sets `CLAUDE_CONFIG_DIR` for a container whose `remoteUser` is `node`:
```json devcontainer.json theme={null}
"mounts": [
"source=claude-code-config,target=/home/node/.claude,type=volume"
],
"containerEnv": {
"CLAUDE_CONFIG_DIR": "/home/node/.claude"
}
```
Replace `/home/node` with the home directory of your container's `remoteUser`. If you already set `containerEnv`, for example in [Enforce organization policy](#enforce-organization-policy), add `CLAUDE_CONFIG_DIR` to that object rather than adding a second one.
To isolate state per project rather than sharing one volume across all repositories, include the `${devcontainerId}` variable in the source name. The [reference configuration](https://github.com/anthropics/claude-code/blob/main/.devcontainer/devcontainer.json) uses `source=claude-code-config-${devcontainerId}` for this purpose.
In GitHub Codespaces, `~/.claude` persists when you stop and start a codespace but is cleared when you rebuild the container, so the configuration above applies there too.
To carry authentication across codespaces, store `ANTHROPIC_API_KEY` or a `CLAUDE_CODE_OAUTH_TOKEN` from [`claude setup-token`](/docs/en/authentication#generate-a-long-lived-token) as a [Codespaces secret](https://docs.github.com/en/codespaces/managing-your-codespaces/managing-your-account-specific-secrets-for-github-codespaces). Codespaces exposes secrets as environment variables inside the container automatically.
## Enforce organization policy
A dev container is a convenient place to apply organization policy, because the same image and configuration run on every engineer's machine.
Claude Code reads `/etc/claude-code/managed-settings.json` on Linux and applies it at the highest precedence in the [settings hierarchy](/docs/en/settings#how-scopes-interact), so values there override anything an engineer sets in `~/.claude` or the project's `.claude/` directory. Copy the file into place from your Dockerfile:
```dockerfile Dockerfile theme={null}
RUN mkdir -p /etc/claude-code
COPY managed-settings.json /etc/claude-code/managed-settings.json
```
Because the Dockerfile lives in the repository, anyone with write access can change or remove this step. For policy that engineers cannot bypass by editing repository files, deliver managed settings through [server-managed settings](/docs/en/server-managed-settings) or your MDM instead. See [managed settings files](/docs/en/settings#settings-files) for the available keys and the other delivery paths.
To set [environment variables](/docs/en/env-vars) that apply to every Claude Code session in the container, add them to `containerEnv` in your `devcontainer.json`. The following example opts out of telemetry and error reporting and prevents Claude Code from auto-updating after install:
```json devcontainer.json theme={null}
"containerEnv": {
"CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC": "1",
"DISABLE_AUTOUPDATER": "1"
}
```
`CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC` also disables the feature-flag evaluation that [Remote Control](/docs/en/remote-control#requirements) and the other [features that need feature-flag fetching](/docs/en/env-vars#features-that-need-feature-flag-fetching) depend on, so sessions in the container can't use them.
The Dev Container Feature always installs the latest Claude Code release. To pin a specific Claude Code version for reproducible builds, install it from your Dockerfile with `npm install -g @anthropic-ai/[email protected]` instead of using the feature, and set `DISABLE_AUTOUPDATER` as shown above.
For the full list of policy controls including permission rules, tool restrictions, and MCP server allowlists, see [Set up Claude Code for your organization](/docs/en/admin-setup).
To make [MCP servers](/docs/en/mcp) available inside the container, define them at [project scope](/docs/en/mcp#mcp-installation-scopes) in a `.mcp.json` file at the repository root so they are checked in alongside your dev container configuration. Install any binaries that local stdio servers depend on in your Dockerfile, and add remote server domains to your network allowlist.
## Restrict network egress
You can limit the container's outbound traffic to only the domains Claude Code needs. See [Network access requirements](/docs/en/network-config#network-access-requirements) for the inference and authentication domains, and [Telemetry services](/docs/en/data-usage#telemetry-services) for the optional telemetry and error reporting connections and how to disable them.
The reference container includes an [`init-firewall.sh`](https://github.com/anthropics/claude-code/blob/main/.devcontainer/init-firewall.sh) script that blocks all outbound traffic except the domains Claude Code and your development tools need. Running a firewall inside a container requires extra permissions, so the reference adds the `NET_ADMIN` and `NET_RAW` capabilities through `runArgs`. The firewall script and these capabilities are not required for Claude Code itself: you can leave them out and rely on your own network controls instead.
## Run without permission prompts
Because the container runs Claude Code as a non-root user and confines command execution to the container, you can pass `--dangerously-skip-permissions` for unattended operation. The CLI rejects this flag when launched as root, so confirm `remoteUser` is set to a non-root account.
Skipping permission prompts removes your opportunity to review tool calls before they run. Claude can still modify any file in the bind-mounted workspace, which appears directly on your host, and reach anything the container's network policy allows. Pair this flag with the [network egress restrictions](#restrict-network-egress) above to limit what a bypassed session can reach.
If you want fewer prompts without disabling safety checks, consider [auto mode](/docs/en/permission-modes#eliminate-prompts-with-auto-mode) instead, which has a classifier review actions before they run. To prevent engineers from using `--dangerously-skip-permissions` at all, set `permissions.disableBypassPermissionsMode` to `"disable"` in [managed settings](/docs/en/settings#permission-settings).
## Try the reference container
The [`anthropics/claude-code`](https://github.com/anthropics/claude-code/tree/main/.devcontainer) repository includes an example dev container that combines the CLI, the egress firewall, persistent volumes, and a Zsh-based shell. It is provided as a working example rather than a maintained base image; use it to see how the pieces fit together before applying them to your own configuration.
<Steps>
<Step title="Install prerequisites">
Install VS Code and the [Dev Containers extension](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers).
</Step>
<Step title="Clone the reference">
Clone the [Claude Code repository](https://github.com/anthropics/claude-code) and open it in VS Code.
</Step>
<Step title="Reopen in container">
When prompted, click **Reopen in Container**, or run **Dev Containers: Reopen in Container** from the Command Palette.
</Step>
<Step title="Start Claude Code">
Once the container finishes building, open a terminal with `` Ctrl+` `` and run `claude` to sign in and start your first session.
</Step>
</Steps>
To use this configuration with your own project, copy the `.devcontainer/` directory into your repository and adjust the Dockerfile for your toolchain, or return to [Add Claude Code to your dev container](#add-claude-code-to-your-dev-container) to add only the feature to a setup you already have.
The reference configuration consists of three files. None of them are required when you add Claude Code to your own dev container through the feature, but they show one way to combine the pieces.
| File | Purpose |
| ---------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------- |
| [`devcontainer.json`](https://github.com/anthropics/claude-code/blob/main/.devcontainer/devcontainer.json) | Volume mounts, `runArgs` capabilities, VS Code extensions, and `containerEnv` |
| [`Dockerfile`](https://github.com/anthropics/claude-code/blob/main/.devcontainer/Dockerfile) | Base image, development tools, and the Claude Code install |
| [`init-firewall.sh`](https://github.com/anthropics/claude-code/blob/main/.devcontainer/init-firewall.sh) | Blocks all outbound network traffic except the allowed domains |
## Next steps
Once Claude Code is running in your dev container, the pages below cover the rest of an organization rollout: choosing an authentication path, delivering managed policy outside the repository, monitoring usage, and understanding what Claude Code stores and sends.
* [Set up Claude Code for your organization](/docs/en/admin-setup): choose an authentication provider, decide how policy reaches devices, and plan the rollout
* [Server-managed settings](/docs/en/server-managed-settings): deliver managed policy from the Claude.ai admin console so engineers cannot bypass it by editing repository files
* [Monitor usage and audit activity](/docs/en/monitoring-usage): export OpenTelemetry metrics and review what your team is running
* [Network access requirements](/docs/en/network-config#network-access-requirements): the full domain allowlist for proxies and firewalls
* [Telemetry services and opt-out](/docs/en/data-usage#telemetry-services): what Claude Code sends by default and the environment variables that disable it
* [Explore the `.claude` directory](/docs/en/claude-directory): what the volume mount holds, including credentials, settings, and session history
* [Sandbox environments](/docs/en/sandbox-environments): compare dev containers with the built-in Bash sandbox, custom containers, and VMs
* [Security model](/docs/en/security): how Claude Code's permission system, sandboxing, and prompt-injection protections fit together
* [Permission modes](/docs/en/permission-modes): the full range from plan mode to auto mode to bypass, and when to use each
discover-plugins First recorded · 525 lines, first recorded
# Discover and install prebuilt plugins through marketplaces ## How marketplaces work ## Official Anthropic marketplace ### Code intelligence #### What Claude gains from code intelligence plugins ### External integrations ### Automatic security review ### Development workflows ### Output styles ## Community marketplace ## Try it: add the demo marketplace ## Add marketplaces ### Add from GitHub ### Add from other Git hosts ### Add from local paths ### Add from remote URLs ## Install plugins ## Manage installed plugins ### Apply plugin changes without restarting ## Manage marketplaces ### Use the interactive interface ### Use CLI commands ### Configure auto-updates ## Configure team marketplaces ## Security ## Troubleshooting ### /plugin command not recognized ### Common issues ### Code intelligence issues ## Next steps
The first capture of this source. The page was already there, and this is what it said.
# Discover and install prebuilt plugins through marketplaces
> Find and install plugins from marketplaces to extend Claude Code with new skills, agents, and capabilities.
Plugins extend Claude Code with skills, agents, hooks, and MCP servers. Plugin marketplaces are catalogs that help you discover and install these extensions without building them yourself.
Looking to create and distribute your own marketplace? See [Create and distribute a plugin marketplace](/docs/en/plugin-marketplaces).
## How marketplaces work
A marketplace is a catalog of plugins that someone else has created and shared. Using a marketplace is a two-step process:
<Steps>
<Step title="Add the marketplace">
This registers the catalog with Claude Code so you can browse what's available. No plugins are installed yet.
</Step>
<Step title="Install individual plugins">
Browse the catalog and install the plugins you want.
</Step>
</Steps>
## Official Anthropic marketplace
Claude Code adds the official Anthropic marketplace (`claude-plugins-official`) automatically the first time you start it interactively. If Claude Code can't add it, for example because your network blocks the download or a [marketplace policy](/docs/en/plugin-marketplaces#managed-marketplace-restrictions) blocked an earlier attempt, add it yourself with `/plugin marketplace add anthropics/claude-plugins-official`.
To browse what's available, run `/plugin` and go to the **Discover** tab, or view the catalog at [claude.com/plugins](https://claude.com/plugins).
To install a plugin from the official marketplace, use `/plugin install <name>@claude-plugins-official`. For example, to install the GitHub integration:
```shell theme={null}
/plugin install github@claude-plugins-official
```
`/plugin` opens an interactive panel in the terminal CLI. If Claude replies that `/plugin` isn't available in this environment, use the [plugin browser](/docs/en/desktop#install-plugins) in the Claude desktop app, or declare the plugin under [`enabledPlugins`](/docs/en/settings#enabledplugins) in `.claude/settings.json` for cloud sessions.
If the install fails, match the message Claude Code reports:
* `Marketplace "claude-plugins-official" not found`: add the marketplace with `/plugin marketplace add anthropics/claude-plugins-official`, then retry the install.
* The plugin is [not found in the marketplace](#install-plugins): check the plugin name.
<Note>
The official marketplace is curated by Anthropic, and inclusion is at Anthropic's discretion. The in-app submission forms add plugins to the [community marketplace](#community-marketplace), not the official one. To distribute plugins independently, [create your own marketplace](/docs/en/plugin-marketplaces) and share it with users.
</Note>
The official marketplace includes several categories of plugins:
### Code intelligence
Code intelligence plugins enable Claude Code's built-in LSP tool, giving Claude the ability to jump to definitions, find references, and see type errors immediately after edits. These plugins configure [Language Server Protocol](https://microsoft.github.io/language-server-protocol/) connections, the same technology that powers VS Code's code intelligence.
Install the language server binary from the table below before using these plugins; the plugin doesn't install it for you. If you already have a language server installed, Claude may prompt you to install the corresponding plugin when you open a project.
| Language | Plugin | Binary required |
| :--------- | :------------------ | :--------------------------- |
| C/C++ | `clangd-lsp` | `clangd` |
| C# | `csharp-lsp` | `csharp-ls` |
| Go | `gopls-lsp` | `gopls` |
| Java | `jdtls-lsp` | `jdtls` |
| Kotlin | `kotlin-lsp` | `kotlin-language-server` |
| Lua | `lua-lsp` | `lua-language-server` |
| PHP | `php-lsp` | `intelephense` |
| Python | `pyright-lsp` | `pyright-langserver` |
| Rust | `rust-analyzer-lsp` | `rust-analyzer` |
| Swift | `swift-lsp` | `sourcekit-lsp` |
| TypeScript | `typescript-lsp` | `typescript-language-server` |
You can also [create your own LSP plugin](/docs/en/plugins-reference#lsp-servers) for other languages.
<Note>
If you see `Executable not found in $PATH` in the `/plugin` Errors tab after installing a plugin, install the required binary from the table above.
</Note>
#### What Claude gains from code intelligence plugins
Once a code intelligence plugin is installed and its language server binary is available, Claude gains two capabilities:
* **Automatic diagnostics**: after every file edit Claude makes, the language server reports errors and warnings back, so Claude sees type errors, missing imports, and syntax issues without running a compiler or linter. If Claude introduces an error, it notices and fixes it in the same turn.
* **Code navigation**: Claude can use the language server to jump to definitions, find references, get type info on hover, list symbols, find implementations, and trace call hierarchies. These operations give Claude more precise navigation than grep-based search, though availability may vary by language and environment.
You don't need to configure diagnostics beyond installing the plugin. To read them yourself, press **Ctrl+O** when Claude Code shows an indicator such as **Found 3 new diagnostic issues in 2 files**.
If you run into issues, see [Code intelligence troubleshooting](#code-intelligence-issues).
### External integrations
These plugins bundle pre-configured [MCP servers](/docs/en/mcp) so you can connect Claude to external services without manual setup:
* **Source control**: `github`, `gitlab`
* **Project management**: `atlassian` (Jira/Confluence), `asana`, `linear`, `notion`
* **Design**: `figma`
* **Infrastructure**: `vercel`, `firebase`, `supabase`
* **Communication**: `slack`
* **Monitoring**: `sentry`
### Automatic security review
The `security-guidance` plugin reviews each change Claude makes for common vulnerabilities and instructs Claude to fix what it finds in the same session. See [Catch security issues as Claude writes code](/docs/en/security-guidance) for what it checks and how to add project-specific rules.
### Development workflows
Plugins that add skills and agents for common development tasks:
* **commit-commands**: Git commit workflows including commit, push, and PR creation
* **pr-review-toolkit**: specialized agents for reviewing pull requests
* **agent-sdk-dev**: tools for building with the Claude Agent SDK
* **plugin-dev**: toolkit for creating your own plugins
### Output styles
Customize how Claude responds:
* **explanatory-output-style**: educational insights about implementation choices
* **learning-output-style**: interactive learning mode for skill building
## Community marketplace
The community marketplace at [`anthropics/claude-plugins-community`](https://github.com/anthropics/claude-plugins-community) hosts third-party plugins that have passed Anthropic's automated validation and safety screening. Each plugin is pinned to a specific commit SHA in the catalog. Unlike the official marketplace, you add it manually:
```shell theme={null}
/plugin marketplace add anthropics/claude-plugins-community
```
Then install plugins from it using the `claude-community` marketplace name:
```shell theme={null}
/plugin install <plugin-name>@claude-community
```
To submit your own plugin to the community marketplace, see [Submit your plugin to the community marketplace](/docs/en/plugins#submit-your-plugin-to-the-community-marketplace) in the create-plugins guide.
## Try it: add the demo marketplace
Anthropic also maintains a [demo plugins marketplace](https://github.com/anthropics/claude-code/tree/main/plugins) (`claude-code-plugins`) with example plugins that show what's possible with the plugin system. Unlike the official marketplace, you need to add this one manually.
<Steps>
<Step title="Add the marketplace">
From within Claude Code, run the `plugin marketplace add` command for the `anthropics/claude-code` marketplace:
```shell theme={null}
/plugin marketplace add anthropics/claude-code
```
This downloads the marketplace catalog and makes its plugins available to you.
</Step>
<Step title="Browse available plugins">
Run `/plugin` to open the plugin manager. This opens a tabbed interface with four tabs you can cycle through using **Tab**, or **Shift+Tab** to go backward:
* **Discover**: browse available plugins from all your marketplaces
* **Installed**: view and manage your installed plugins
* **Marketplaces**: add, remove, or update your added marketplaces
* **Errors**: view any plugin loading errors
Go to the **Discover** tab to see plugins from the marketplace you just added. When your administrator has allowlisted the marketplace via the [`pluginSuggestionMarketplaces`](/docs/en/settings#available-settings) managed setting, plugins marked as relevant to your current working directory are pinned at the top with a **suggested for this directory** label.
</Step>
<Step title="Install a plugin">
Select a plugin to view its details. The details pane shows what the plugin contains and what it costs:
* A **Context cost** estimate so you can see how many tokens the plugin will add to your [context window](/docs/en/features-overview#understand-context-costs) every turn
* The plugin's **Last updated** date
* A **Will install** section listing the plugin's commands, agents, skills, hooks, and MCP and LSP servers, so you can review exactly what it adds before installing
Not every plugin provides the data behind these fields. For plugins from local or custom marketplaces, you may not see the **Context cost** and **Last updated** rows, and the **Will install** section may show **Components will be discovered at installation** instead.
Choose an installation scope:
* **User scope**: install for yourself across all projects
* **Project scope**: install for all collaborators on this repository
* **Local scope**: install for yourself in this repository only
For example, select **commit-commands**, a plugin that adds git workflow skills, and install it to your user scope.
You can also start the install from the command line:
```shell theme={null}
/plugin install commit-commands@claude-code-plugins
```
See [Configuration scopes](/docs/en/settings#configuration-scopes) to learn more about scopes.
</Step>
<Step title="Use your new plugin">
Check the install summary: if it reports `Run /reload-plugins to activate.`, run `/reload-plugins`, and if that warns that the reload will re-read the conversation, rerun it as `/reload-plugins --force`.
Plugin skills are namespaced by the plugin name, so **commit-commands** provides skills like `/commit-commands:commit`.
Try it out by making a change to a file and running:
```shell theme={null}
/commit-commands:commit
```
This stages your changes, generates a commit message, and creates the commit.
Each plugin works differently. Check the plugin's details in the **Discover** tab to see the commands and skills it provides, or visit its homepage for usage guidance.
</Step>
</Steps>
## Add marketplaces
Use the `/plugin marketplace add` command to add marketplaces from different sources.
<Tip>
**Shortcuts**: You can use `/plugin market` instead of `/plugin marketplace`, and `rm` instead of `remove`.
</Tip>
* **GitHub repositories**: `owner/repo` format, for example `anthropics/claude-code`
* **Git URLs**: any git repository URL, including GitLab, Bitbucket, and self-hosted servers
* **Local paths**: directories or direct paths to `marketplace.json` files
* **Remote URLs**: direct URLs to hosted `marketplace.json` files
### Add from GitHub
Add a GitHub repository that contains a `.claude-plugin/marketplace.json` file using the `owner/repo` format, where `owner` is the GitHub username or organization and `repo` is the repository name.
For example, `anthropics/claude-code` refers to the `claude-code` repository owned by `anthropics`:
```shell theme={null}
/plugin marketplace add anthropics/claude-code
```
### Add from other Git hosts
Add any git repository by providing the full URL. This works with any Git host, including GitLab, Bitbucket, and self-hosted servers. Include the `.git` suffix so Claude Code clones the repository rather than treating the URL as a direct link to a hosted `marketplace.json` file.
Include the `https://` prefix as well. Claude Code v2.1.196 and later reject a host typed without it, such as `gitlab.com/company/plugins.git`, as an invalid GitHub `owner/repo` shorthand, and the error tells you to add the prefix. Earlier versions misread it as a GitHub repository path and fail at clone time.
Using HTTPS:
```shell theme={null}
/plugin marketplace add https://gitlab.com/company/plugins.git
```
Using SSH:
```shell theme={null}
/plugin marketplace add [email protected]:company/plugins.git
```
To add a specific branch or tag, append `#` followed by the ref:
```shell theme={null}
/plugin marketplace add https://gitlab.com/company/plugins.git#v1.0.0
```
### Add from local paths
Add a local directory that contains a `.claude-plugin/marketplace.json` file:
```shell theme={null}
/plugin marketplace add ./my-marketplace
```
You can also add a direct path to a `marketplace.json` file:
```shell theme={null}
/plugin marketplace add ./path/to/marketplace.json
```
### Add from remote URLs
Add a remote `marketplace.json` file via URL:
```shell theme={null}
/plugin marketplace add https://example.com/marketplace.json
```
<Note>
URL-based marketplaces have some limitations compared to Git-based marketplaces. If you encounter "path not found" errors when installing plugins, see [Troubleshooting](/docs/en/plugin-marketplaces#plugins-with-relative-paths-fail-in-url-based-marketplaces).
</Note>
## Install plugins
Once you've added marketplaces, you can install a plugin by name:
```shell theme={null}
/plugin install plugin-name@marketplace-name
```
The command opens that plugin's details, where you choose an [installation scope](/docs/en/settings#configuration-scopes). You see the same choices when you run `/plugin`, go to the **Discover** tab, and press **Enter** on a plugin:
* **User scope**: install for yourself across all projects
* **Project scope**: install for all collaborators on this repository, which adds the plugin to `.claude/settings.json`
* **Local scope**: install for yourself in this repository only, not shared with collaborators
To install without an interactive step, use the [`claude plugin install`](/docs/en/plugins-reference#plugin-install) shell command, which installs to user scope unless you pass `--scope`. For a plugin with a [`command` source](/docs/en/plugin-marketplaces#how-users-accept-the-command), pass `--yes` to accept the command it displays.
You may also see plugins with **managed** scope. These are installed by administrators via [managed settings](/docs/en/settings#settings-files) and can't be modified.
Claude Code looks the plugin up in its local copy of the marketplace catalog. How you name the plugin controls whether Claude Code refreshes that copy first:
* **With a marketplace name**: when you install `plugin-name@marketplace-name`, in a session or with `claude plugin install`, Claude Code refreshes that marketplace before the lookup, whether or not you turned on [auto-update](#configure-auto-updates) for it and even if you set `DISABLE_AUTOUPDATER`. Before v2.1.232, Claude Code didn't refresh the marketplace before the lookup. Claude Code skips this refresh in four cases:
* The marketplace wasn't [added from GitHub, another Git host, or a remote URL](#add-marketplaces).
* A [seed directory](/docs/en/plugin-marketplaces#pre-populate-plugins-for-containers) supplies the marketplace.
* Claude Code refreshed the marketplace within the last 30 seconds.
* You set [`CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC`](/docs/en/env-vars).
* **Plugin name only**: when you run `/plugin install plugin-name` in a session, Claude Code refreshes only the marketplaces it also [updates in the background](#configure-auto-updates), and only after the lookup misses. `claude plugin install plugin-name` reads the cached catalogs without refreshing. To install a plugin that was published after your last refresh, run `/plugin marketplace update <marketplace-name>` in a session or [`claude plugin marketplace update <marketplace-name>`](/docs/en/plugin-marketplaces#plugin-marketplace-update) in your shell, then retry the install.
Cut at 300 lines. The page has the rest.
env-vars First recorded · 484 lines, first recorded
# Environment variables ## Set environment variables ### In your shell ### In settings files ## Precedence ## Variables ## Features that need feature-flag fetching ### First session after an install or upgrade ## See also
This page is larger than the 256 KiB this site keeps, so one side of the diff below stops where the stored text does.
The first capture of this source. The page was already there, and this is what it said.
# Environment variables
> Reference for environment variables that control Claude Code behavior.
Environment variables can control Claude Code behavior such as model selection, authentication, request routing, and feature toggles. Many of the same behaviors can also be configured through a [settings file](/docs/en/settings) field, a [CLI flag](/docs/en/cli-reference), or an in-session command like `/model`.
This page covers how to:
* [Set environment variables](#set-environment-variables) in your shell or in a settings file
* [Check which value applies](#precedence) when a behavior can be set more than one way
* [Look up the variables Claude Code reads](#variables)
* [See which features stop working](#features-that-need-feature-flag-fetching) when a variable turns feature-flag fetching off
## Set environment variables
A variable you set in your shell lasts for that terminal session, while a variable in a settings file applies every time `claude` runs.
### In your shell
Set the variable before launching `claude`:
<Tabs>
<Tab title="macOS, Linux, WSL">
```bash theme={null}
export API_TIMEOUT_MS="1200000"
claude
```
To set it for every session, add the `export` line to `~/.bashrc`, `~/.zshrc`, or your shell's profile file.
</Tab>
<Tab title="Windows PowerShell">
```powershell theme={null}
$env:API_TIMEOUT_MS = "1200000"
claude
```
To set it for every session, run `[Environment]::SetEnvironmentVariable("API_TIMEOUT_MS", "1200000", "User")` and open a new terminal.
</Tab>
<Tab title="Windows CMD">
```batch theme={null}
set API_TIMEOUT_MS=1200000
claude
```
To set it for every session, run `setx API_TIMEOUT_MS "1200000"` and open a new terminal.
</Tab>
</Tabs>
The assignment line prints nothing on success, so confirm the variable is set by printing it in the same shell before you run `claude`:
<Tabs>
<Tab title="macOS, Linux, WSL">
```bash theme={null}
echo $API_TIMEOUT_MS
```
</Tab>
<Tab title="Windows PowerShell">
```powershell theme={null}
echo $env:API_TIMEOUT_MS
```
</Tab>
<Tab title="Windows CMD">
```batch theme={null}
echo %API_TIMEOUT_MS%
```
</Tab>
</Tabs>
### In settings files
Add variables under the `env` key in a `settings.json` file, creating the file if it doesn't exist. Claude Code reads them directly from the file, so they take effect no matter how `claude` was launched. A running session applies new and changed values to its environment when you save the file, but a feature that reads its variables once at startup, such as [OpenTelemetry monitoring](/docs/en/monitoring-usage), keeps its startup values until you relaunch. Removing a variable from the file doesn't unset it in a running session; the removal takes effect the next time you launch `claude`.
```json ~/.claude/settings.json theme={null}
{
"env": {
"API_TIMEOUT_MS": "1200000",
"BASH_DEFAULT_TIMEOUT_MS": "300000"
}
}
```
The file you choose controls who the variables apply to:
| File | Applies to |
| :---------------------------- | :------------------------------------------------------------------------------------------------------------------------------ |
| `~/.claude/settings.json` | You, in every project |
| `.claude/settings.json` | Everyone working in the project, checked into source control |
| `.claude/settings.local.json` | You, in this project only, gitignored when Claude Code saves a setting to it; add it to your gitignore if you create it by hand |
| Managed settings | Everyone in your organization, deployed by an admin |
See [Settings files](/docs/en/settings#settings-files) for where each file lives and [Settings precedence](/docs/en/settings#settings-precedence) for how they combine when more than one sets the same variable.
## Precedence
Some behaviors have both an environment variable and a dedicated settings key. If you set both, the environment variable takes precedence. For example, `ANTHROPIC_MODEL` overrides the `model` setting, and `CLAUDE_CODE_AUTO_CONNECT_IDE` overrides `autoConnectIde`. Claude Code uses the settings key only when the environment variable is unset.
When the same variable is set in both your shell and a settings file `env` block, the settings file value applies. Claude Code writes each `env` entry into the process environment at startup and again when the file changes, replacing the value inherited from the shell. A few variables are special-cased; the [`env` setting](/docs/en/settings#available-settings) lists the exceptions.
In a settings file you can set a variable but you can't remove one. To override a variable you can't unset, such as a stale `CLAUDE_CODE_USE_VERTEX` exported by a shell profile you don't control, set it to an empty string in the `env` block: `"CLAUDE_CODE_USE_VERTEX": ""`. Claude Code treats the empty value as unset for provider selection. Subprocesses still inherit the empty value.
Between settings files, `env` values follow [settings precedence](/docs/en/settings#settings-precedence), so a managed settings entry overrides the same variable in user or project settings.
How an environment variable interacts with CLI flags and in-session commands varies per feature: `--model` and `/model` override `ANTHROPIC_MODEL`, while `CLAUDE_CODE_EFFORT_LEVEL` overrides `/effort`. When a variable interacts with another configuration source, its row in the [Variables](#variables) list states the precedence or links to the page that documents it.
Claude Code reads shell environment variables at startup, so changes to them take effect the next time you launch `claude`. Variables set under the `env` key in settings files are reapplied to a running session when the file changes, with the startup-only exception described in [In settings files](#in-settings-files).
## Variables
Numeric variables such as timeouts, token budgets, and retry counts accept scientific notation and digit-separator spellings in addition to plain digits, except where a variable's row notes it takes plain digits only. For example, Claude Code reads `2e3` as 2000 and `64_000` as 64000. Before v2.1.211, these spellings could silently set a much smaller value, such as `1e6` setting a timeout to 1.
<Note>
For variables that turn a behavior on or off, set `1` or `true` to turn it on and `0` or `false` to turn it off, in any casing.
Some variables read only whether you set them at all, so any non-empty value including `0` turns the behavior on, and you turn the behavior off by unsetting the variable or setting it to an empty value. These variables work that way:
* `CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC`
* `DISABLE_TELEMETRY`
* `DISABLE_ERROR_REPORTING`
* `CLAUDE_CODE_TMUX_TRUECOLOR`
* `FALLBACK_FOR_ALL_PRIMARY_MODELS`
* `IS_DEMO`
One other variable has its own rule: `FORCE_HYPERLINK` reads a number, so only `0` turns it off. Each variable's row also states its own rule.
</Note>
| Variable | Purpose |
| :------------------------------------------------------ | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `ANTHROPIC_API_KEY` | API key sent as `X-Api-Key` header. When set, this key is used instead of your Claude Pro, Max, Team, or Enterprise subscription even if you are logged in. In non-interactive mode (`-p`), the key is always used when present. In interactive mode, you are prompted to approve the key once before it overrides your subscription. To use your subscription instead, run `unset ANTHROPIC_API_KEY` |
| `ANTHROPIC_AUTH_TOKEN` | Custom value for the `Authorization` header (the value you set here will be prefixed with `Bearer `) |
| `ANTHROPIC_AWS_API_KEY` | Workspace API key for [Claude Platform on AWS](/docs/en/claude-platform-on-aws), generated in the AWS Console. Sent as `x-api-key` and takes precedence over AWS SigV4 |
| `ANTHROPIC_AWS_BASE_URL` | Override the [Claude Platform on AWS](/docs/en/claude-platform-on-aws) endpoint URL. Use for custom regions or when routing through an [LLM gateway](/docs/en/llm-gateway). Defaults to `https://aws-external-anthropic.{AWS_REGION}.api.aws` |
| `ANTHROPIC_AWS_WORKSPACE_ID` | Required for [Claude Platform on AWS](/docs/en/claude-platform-on-aws). Sent on every request as the `anthropic-workspace-id` header |
| `ANTHROPIC_BASE_URL` | Override the API endpoint to route requests through a proxy or gateway. When set to a non-first-party host, [MCP tool search](/docs/en/mcp#scale-with-mcp-tool-search) is disabled by default. Set `ENABLE_TOOL_SEARCH=true` if your proxy forwards `tool_reference` blocks. As of v2.1.196, [Remote Control](/docs/en/remote-control#requirements) is disabled when this points at a host other than `api.anthropic.com`, matching its behavior on Amazon Bedrock, Google Cloud's Agent Platform, and Microsoft Foundry |
| `ANTHROPIC_BEDROCK_BASE_URL` | Override the Amazon Bedrock endpoint URL. Use for custom Amazon Bedrock endpoints or when routing through an [LLM gateway](/docs/en/llm-gateway). See [Amazon Bedrock](/docs/en/amazon-bedrock) |
| `ANTHROPIC_BEDROCK_MANTLE_BASE_URL` | Override the Amazon Bedrock Mantle endpoint URL. See [Mantle endpoint](/docs/en/amazon-bedrock#use-the-mantle-endpoint) |
| `ANTHROPIC_BEDROCK_REGION_PREFIX` | Cross-region inference profile prefix (`us`, `eu`, `apac`, `jp`, `au`, or `global`) Claude Code tries first instead of the one derived from the AWS region. Ignored in AWS GovCloud regions. Requires Claude Code v2.1.224 or later. See [Amazon Bedrock](/docs/en/amazon-bedrock#cross-region-inference-profile-prefixes) |
| `ANTHROPIC_BEDROCK_SERVICE_TIER` | Amazon Bedrock [service tier](https://docs.aws.amazon.com/bedrock/latest/userguide/service-tiers-inference.html) (`default`, `flex`, or `priority`). Sent as the `X-Amzn-Bedrock-Service-Tier` header. See [Amazon Bedrock](/docs/en/amazon-bedrock#service-tiers) |
| `ANTHROPIC_BETAS` | Comma-separated list of additional `anthropic-beta` header values to include in API requests. Claude Code already sends the beta headers it needs; use this to opt into an [Anthropic API beta](https://platform.claude.com/docs/en/api/beta-headers) before Claude Code adds native support. Unlike the [`--betas` flag](/docs/en/cli-reference#cli-flags), which requires API key authentication, this variable works with all auth methods including Claude.ai subscription |
| `ANTHROPIC_CUSTOM_HEADERS` | Custom headers to add to requests (`Name: Value` format, newline-separated for multiple headers). If a name or value contains a character an HTTP header can't carry, such as a curly quote or a zero-width space, Claude Code fails the request with an error that identifies the pair by position. Requires Claude Code v2.1.227 or later. [Invalid request header value](/docs/en/errors#invalid-request-header-value) lists the exact character set and where the check runs |
| `ANTHROPIC_CUSTOM_MODEL_OPTION` | Model ID to add as a custom entry in the `/model` picker. Use this to make a non-standard or gateway-specific model selectable without replacing built-in aliases. See [Model configuration](/docs/en/model-config#add-a-custom-model-option) |
| `ANTHROPIC_CUSTOM_MODEL_OPTION_DESCRIPTION` | Display description for the custom model entry in the `/model` picker. Defaults to `Custom model (<model-id>)` when not set |
| `ANTHROPIC_CUSTOM_MODEL_OPTION_NAME` | Display name for the custom model entry in the `/model` picker. Defaults to the model ID when not set |
| `ANTHROPIC_CUSTOM_MODEL_OPTION_SUPPORTED_CAPABILITIES` | Comma-separated list of [capabilities](/docs/en/model-config#customize-pinned-model-display-and-capabilities) the custom model supports, for example `effort,thinking`. See [Model configuration](/docs/en/model-config#customize-pinned-model-display-and-capabilities) |
| `ANTHROPIC_DEFAULT_FABLE_MODEL` | Model ID that the `fable` alias resolves to, and the ID Claude Code recognizes as Fable 5 for [automatic model fallback](/docs/en/model-config#automatic-model-fallback) on third-party providers. See [Model configuration](/docs/en/model-config#environment-variables) |
| `ANTHROPIC_DEFAULT_FABLE_MODEL_DESCRIPTION` | Display description for the pinned Fable model in the `/model` picker. Defaults to `Custom Fable model` when not set. See [Model configuration](/docs/en/model-config#customize-pinned-model-display-and-capabilities) |
| `ANTHROPIC_DEFAULT_FABLE_MODEL_NAME` | Display name for the pinned Fable model in the `/model` picker. Defaults to the model ID when not set. See [Model configuration](/docs/en/model-config#customize-pinned-model-display-and-capabilities) |
| `ANTHROPIC_DEFAULT_FABLE_MODEL_SUPPORTED_CAPABILITIES` | Comma-separated list of [capabilities](/docs/en/model-config#customize-pinned-model-display-and-capabilities) the pinned Fable model supports, for example `effort,thinking`. See [Model configuration](/docs/en/model-config#customize-pinned-model-display-and-capabilities) |
| `ANTHROPIC_DEFAULT_HAIKU_MODEL` | Model ID that the `haiku` alias resolves to, also used for [background functionality](/docs/en/costs#background-token-usage). See [Model configuration](/docs/en/model-config#environment-variables) |
| `ANTHROPIC_DEFAULT_HAIKU_MODEL_DESCRIPTION` | Display description for the pinned Haiku model in the `/model` picker. Defaults to `Custom Haiku model` when not set. See [Model configuration](/docs/en/model-config#customize-pinned-model-display-and-capabilities) |
| `ANTHROPIC_DEFAULT_HAIKU_MODEL_NAME` | Display name for the pinned Haiku model in the `/model` picker. Defaults to the model ID when not set. See [Model configuration](/docs/en/model-config#customize-pinned-model-display-and-capabilities) |
| `ANTHROPIC_DEFAULT_HAIKU_MODEL_SUPPORTED_CAPABILITIES` | Comma-separated list of [capabilities](/docs/en/model-config#customize-pinned-model-display-and-capabilities) the pinned Haiku model supports, for example `effort,thinking`. See [Model configuration](/docs/en/model-config#customize-pinned-model-display-and-capabilities) |
| `ANTHROPIC_DEFAULT_OPUS_MODEL` | Model ID that the `opus` alias resolves to, and that `opusplan` uses while Plan Mode is active. See [Model configuration](/docs/en/model-config#environment-variables) |
| `ANTHROPIC_DEFAULT_OPUS_MODEL_DESCRIPTION` | Display description for the pinned Opus model in the `/model` picker. When not set, defaults to `Custom Opus model`, or `Custom Opus model (1M context)` if the pinned model ID has the `[1m]` suffix and `CLAUDE_CODE_DISABLE_1M_CONTEXT` isn't turned on. See [Model configuration](/docs/en/model-config#customize-pinned-model-display-and-capabilities) |
| `ANTHROPIC_DEFAULT_OPUS_MODEL_NAME` | Display name for the pinned Opus model in the `/model` picker. Defaults to the model ID when not set. See [Model configuration](/docs/en/model-config#customize-pinned-model-display-and-capabilities) |
| `ANTHROPIC_DEFAULT_OPUS_MODEL_SUPPORTED_CAPABILITIES` | Comma-separated list of [capabilities](/docs/en/model-config#customize-pinned-model-display-and-capabilities) the pinned Opus model supports, for example `effort,thinking`. See [Model configuration](/docs/en/model-config#customize-pinned-model-display-and-capabilities) |
| `ANTHROPIC_DEFAULT_SONNET_MODEL` | Model ID that the `sonnet` alias resolves to, and that `opusplan` uses when Plan Mode is not active. See [Model configuration](/docs/en/model-config#environment-variables) |
| `ANTHROPIC_DEFAULT_SONNET_MODEL_DESCRIPTION` | Display description for the pinned Sonnet model in the `/model` picker. When not set, defaults to `Custom Sonnet model`, or `Custom Sonnet model (1M context)` if the pinned model ID has the `[1m]` suffix and `CLAUDE_CODE_DISABLE_1M_CONTEXT` isn't turned on. See [Model configuration](/docs/en/model-config#customize-pinned-model-display-and-capabilities) |
| `ANTHROPIC_DEFAULT_SONNET_MODEL_NAME` | Display name for the pinned Sonnet model in the `/model` picker. Defaults to the model ID when not set. See [Model configuration](/docs/en/model-config#customize-pinned-model-display-and-capabilities) |
| `ANTHROPIC_DEFAULT_SONNET_MODEL_SUPPORTED_CAPABILITIES` | Comma-separated list of [capabilities](/docs/en/model-config#customize-pinned-model-display-and-capabilities) the pinned Sonnet model supports, for example `effort,thinking`. See [Model configuration](/docs/en/model-config#customize-pinned-model-display-and-capabilities) |
| `ANTHROPIC_FEDERATION_RULE_ID` | Federation rule ID for [Workload Identity Federation](https://platform.claude.com/docs/en/manage-claude/workload-identity-federation). When you set it together with `ANTHROPIC_ORGANIZATION_ID`, Claude Code selects federation credentials, which rank above your `/login` credential. See [authentication precedence](/docs/en/authentication#authentication-precedence) |
| `ANTHROPIC_FOUNDRY_API_KEY` | API key for Microsoft Foundry authentication (see [Microsoft Foundry](/docs/en/microsoft-foundry)) |
| `ANTHROPIC_FOUNDRY_AUTH_TOKEN` | Bearer token for Microsoft Foundry authentication, such as a Microsoft Entra access token. Claude Code sends it as the `Authorization: Bearer` header. Takes precedence over `ANTHROPIC_FOUNDRY_API_KEY` and over the Azure default credential chain. See [Microsoft Foundry](/docs/en/microsoft-foundry). Requires Claude Code v2.1.203 or later |
| `ANTHROPIC_FOUNDRY_BASE_URL` | Full base URL for the Microsoft Foundry resource (for example, `https://my-resource.services.ai.azure.com/anthropic`). Alternative to `ANTHROPIC_FOUNDRY_RESOURCE` (see [Microsoft Foundry](/docs/en/microsoft-foundry)) |
| `ANTHROPIC_FOUNDRY_RESOURCE` | Microsoft Foundry resource name (for example, `my-resource`). Required if `ANTHROPIC_FOUNDRY_BASE_URL` is not set (see [Microsoft Foundry](/docs/en/microsoft-foundry)) |
| `ANTHROPIC_MODEL` | Name of the model setting to use (see [Model Configuration](/docs/en/model-config#environment-variables)) |
| `ANTHROPIC_ORGANIZATION_ID` | Organization ID for [Workload Identity Federation](https://platform.claude.com/docs/en/manage-claude/workload-identity-federation). Set it together with `ANTHROPIC_FEDERATION_RULE_ID`. See [authentication precedence](/docs/en/authentication#authentication-precedence) |
| `ANTHROPIC_PROFILE` | Name of the Anthropic profile to authenticate with, such as one created by [`ant auth login`](https://platform.claude.com/docs/en/cli-sdks-libraries/cli/authentication). The named profile ranks above your `/login` credential. See [authentication precedence](/docs/en/authentication#authentication-precedence) |
| `ANTHROPIC_SMALL_FAST_MODEL` | \[DEPRECATED] Name of [Haiku-class model for background tasks](/docs/en/costs) |
| `ANTHROPIC_SMALL_FAST_MODEL_AWS_REGION` | Override AWS region for the Haiku-class model when using Amazon Bedrock or Amazon Bedrock Mantle. On Amazon Bedrock, this only takes effect when `ANTHROPIC_DEFAULT_HAIKU_MODEL` or the deprecated `ANTHROPIC_SMALL_FAST_MODEL` is also set, since Amazon Bedrock otherwise runs background tasks on the [default Sonnet model or the primary model](/docs/en/amazon-bedrock#4-pin-model-versions) in the session region |
| `ANTHROPIC_VERTEX_BASE_URL` | Override Google Cloud's Agent Platform endpoint URL. Use for custom Google Cloud's Agent Platform endpoints or when routing through an [LLM gateway](/docs/en/llm-gateway). See [Google Cloud's Agent Platform](/docs/en/google-vertex-ai) |
| `ANTHROPIC_VERTEX_PROJECT_ID` | GCP project ID for Google Cloud's Agent Platform requests. Overridden by `GCLOUD_PROJECT`, `GOOGLE_CLOUD_PROJECT`, or the project in your `GOOGLE_APPLICATION_CREDENTIALS` credential file. See [Google Cloud's Agent Platform](/docs/en/google-vertex-ai) |
| `ANTHROPIC_WORKSPACE_ID` | Workspace ID for [workload identity federation](https://platform.claude.com/docs/en/manage-claude/workload-identity-federation). Set this when your federation rule is scoped to more than one workspace so the token exchange knows which workspace to target |
| `API_FORCE_IDLE_TIMEOUT` | Override the 5-minute body idle timeout that aborts a streaming model response when no bytes arrive. Set to `0` to turn the timeout off, for example when a slow [gateway](/docs/en/llm-gateway) or local model pauses longer than 5 minutes between chunks, or `1` to keep it on for every provider. When unset, the timeout is active on providers other than the direct Anthropic API and [Claude Platform on AWS](/docs/en/claude-platform-on-aws). The [stream watchdogs](/docs/en/network-config#streaming-idle-watchdogs) run independently of it and abort a long silent pause even when you set `0` here. Requires Claude Code v2.1.169 or later |
| `API_TIMEOUT_MS` | Timeout for API requests in milliseconds (default: 600000, or 10 minutes; maximum: 2147483647). Increase this when requests time out on slow networks or when routing through a proxy. Values above the maximum overflow the underlying timer and cause requests to fail immediately |
| `AWS_BEARER_TOKEN_BEDROCK` | Amazon Bedrock API key for authentication (see [Amazon Bedrock API keys](https://aws.amazon.com/blogs/machine-learning/accelerate-ai-development-with-amazon-bedrock-api-keys/)) |
| `BASH_DEFAULT_TIMEOUT_MS` | Default timeout for long-running bash commands (default: 120000, or 2 minutes) |
| `BASH_MAX_OUTPUT_LENGTH` | Maximum number of characters of bash output that Claude Code reads back into a command's result (default: 30000; maximum: 150000). See [Output limits](/docs/en/tools-reference#output-limits) |
| `BASH_MAX_TIMEOUT_MS` | Maximum timeout the model can set for long-running bash commands (default: 600000, or 10 minutes). The effective ceiling is the larger of this and `BASH_DEFAULT_TIMEOUT_MS` |
| `CCR_FORCE_BUNDLE` | Set to `1` to force [`claude --cloud`](/docs/en/claude-code-on-the-web#send-local-repositories-without-github) to bundle and upload your local repository even when GitHub access is available |
| `CLAUDECODE` | Set to `1` in subprocesses Claude Code spawns (Bash and PowerShell tools, tmux sessions, [hook](/docs/en/hooks) commands, [status line](/docs/en/statusline) commands, stdio [MCP server](/docs/en/mcp) subprocesses). IDE extensions also set this in their integrated terminals. Use to detect when a script is running inside a subprocess spawned by Claude Code. To check whether the current process was spawned directly by a tool call or hook, rather than inside a stdio MCP server that Claude Code started, use `CLAUDE_CODE_CHILD_SESSION` instead |
| `CLAUDE_AFK_COUNTDOWN_MS` | How many milliseconds before auto-continue the on-screen countdown appears on an unanswered [`AskUserQuestion`](/docs/en/tools-reference) dialog. Default `20000` (20 seconds), capped at the auto-continue timeout. Has no effect unless auto-continue is on; see the [`askUserQuestionTimeout`](/docs/en/settings#available-settings) setting and `CLAUDE_AFK_TIMEOUT_MS`. Requires Claude Code v2.1.198 or later |
| `CLAUDE_AFK_TIMEOUT_MS` | How many milliseconds of idle time before an unanswered [`AskUserQuestion`](/docs/en/tools-reference) dialog auto-continues without you. Auto-continue is off by default; opt in with the [`askUserQuestionTimeout`](/docs/en/settings#available-settings) setting. This variable is an override for demos and automated tests: when set, it takes precedence over that setting and turns auto-continue on even when the setting is unset or `never`. Setting `0` doesn't turn the timeout off; it closes the dialog immediately. In v2.1.198 and v2.1.199, auto-continue was on by default with a `60000` (60 seconds) timeout. Requires Claude Code v2.1.198 or later |
| `CLAUDE_AGENT_SDK_DISABLE_BUILTIN_AGENTS` | Set to `1` to disable all built-in [subagent](/docs/en/sub-agents) types such as Explore and Plan. Only applies in non-interactive mode (the `-p` flag). Useful for SDK users who want a blank slate |
| `CLAUDE_AGENT_SDK_MCP_NO_PREFIX` | Set to `1` to skip the `mcp__<server>__` prefix on tool names from SDK-created MCP servers. Tools use their original names. SDK usage only |
| `CLAUDE_ASYNC_AGENT_STALL_TIMEOUT_MS` | Stall timeout in milliseconds for background subagents. Default `600000` (10 minutes). The timer resets on each streaming progress event; if no progress arrives within the window, the subagent is aborted and the task is marked failed, surfacing any partial result to the parent |
| `CLAUDE_AUTOCOMPACT_PCT_OVERRIDE` | Set the percentage (1-100) of the auto-compact window at which auto-compaction triggers. Use lower values like `50` to compact earlier; the variable can't raise the threshold, so values above the default percentage are ignored. It applies only in sessions that [compact before the model's context limit](/docs/en/model-config#context-window-and-auto-compaction). Applies to both main conversations and subagents |
| `CLAUDE_AUTO_BACKGROUND_TASKS` | Set to `1` to force-enable automatic backgrounding of long-running agent tasks. When enabled, subagents are moved to the background after running for approximately two minutes. Also enables [automatic backgrounding of long MCP tool calls](/docs/en/mcp#automatic-backgrounding-of-long-tool-calls) in non-interactive mode on Claude Code v2.1.212 or later |
| `CLAUDE_AX_SCREEN_READER` | Set to `1` to render screen-reader friendly output: flat text without decorative borders or animations. Set to `0` to force screen-reader mode off even when [`axScreenReader`](/docs/en/settings#available-settings) is `true`. The [`--ax-screen-reader`](/docs/en/cli-reference#cli-flags) flag takes precedence. Requires Claude Code v2.1.181 or later |
| `CLAUDE_AX_STARTUP_QUIET_MS` | In [screen reader mode](/docs/en/accessibility), how many milliseconds Claude Code holds the first interface render after the startup confirmation line, so your screen reader can speak the line in full before new output interrupts it. Default `3000`. Set `0` to render immediately. Claude Code caps the hold at `600000` (10 minutes). Your first keystroke ends the hold early. Requires Claude Code v2.1.217 or later |
| `CLAUDE_BASH_MAINTAIN_PROJECT_WORKING_DIR` | Return to the original working directory after each Bash or PowerShell command in the main session |
| `CLAUDE_BYTE_STREAM_IDLE_TIMEOUT_MS` | Timeout in milliseconds for the byte-level streaming idle watchdog only; when set, it takes precedence over `CLAUDE_STREAM_IDLE_TIMEOUT_MS` for that watchdog and leaves the event-level watchdog unchanged. Claude Code clamps this variable to between 10 seconds and 30 minutes. Requires Claude Code v2.1.210 or later |
| `CLAUDE_CLIENT_PRESENCE_FILE` | Path to a file that an external tool, such as a screen-lock listener, creates when you unlock your screen and deletes when you lock it. While the file exists, Claude Code skips [Remote Control mobile push notifications](/docs/en/remote-control#mobile-push-notifications), so you stop getting pushes while you are actively using the computer. When the file is absent or unreadable, notifications are sent as normal. Claude Code checks the file once per push-triggering event rather than polling it. Requires Claude Code v2.1.181 or later |
| `CLAUDE_CODE_ACCESSIBILITY` | Set to `1` to keep the native terminal cursor visible and disable the inverted-text cursor indicator. Allows screen magnifiers like macOS Zoom to track cursor position |
| `CLAUDE_CODE_ADDITIONAL_DIRECTORIES_CLAUDE_MD` | Set to `1` to load memory files from directories specified with `--add-dir`. Loads `CLAUDE.md`, `.claude/CLAUDE.md`, `.claude/rules/*.md`, and `CLAUDE.local.md`. By default, additional directories do not load memory files |
| `CLAUDE_CODE_ALT_SCREEN_FULL_REPAINT` | Set to `1` to repaint the entire screen on every frame in [fullscreen rendering](/docs/en/fullscreen) instead of sending incremental updates. Use this if fullscreen mode shows stale or misplaced text fragments. Claude Code enables this automatically for background sessions and [agent view](/docs/en/agent-view) on Windows |
| `CLAUDE_CODE_ALWAYS_ENABLE_EFFORT` | Set to `1` to send the [effort](/docs/en/model-config#adjust-effort-level) parameter with every request, even when Claude Code does not recognize the model ID as effort-capable. Use this when routing through an [LLM gateway](/docs/en/llm-gateway) or third-party provider that serves models under custom identifiers. Models that reject the effort parameter at the API, including Claude 3 models, Sonnet 4.0 and 4.5, Opus 4.0 and 4.1, and Haiku 4.5, are still excluded so requests do not fail |
| `CLAUDE_CODE_API_KEY_HELPER_TTL_MS` | Interval in milliseconds at which credentials should be refreshed (when using [`apiKeyHelper`](/docs/en/settings#available-settings)) |
| `CLAUDE_CODE_ARTIFACT_AUTO_OPEN` | Set to `0` to stop Claude Code from opening the browser automatically when a new [artifact](/docs/en/artifacts) is published. Republishing an existing artifact does not open the browser regardless of this setting |
| `CLAUDE_CODE_ATTRIBUTION_HEADER` | Set to `0` to omit the [attribution block](/docs/en/llm-gateway-protocol#system-prompt-attribution-block), which carries the client version and a prompt fingerprint, from the start of the system prompt. Caching on a direct connection to the Anthropic API is unaffected either way. In some direct-connection setups, Claude Code keeps the block on [auto mode](/docs/en/permission-modes#eliminate-prompts-with-auto-mode) classifier requests even when you set `0`. In [System prompt attribution block](/docs/en/llm-gateway-protocol#system-prompt-attribution-block), check which connections and credentials this covers. Before v2.1.181 the block included a per-request token on custom base URLs and Microsoft Foundry connections, so on those versions set it to `0` when your LLM gateway caches on the request body or forwards requests to a third-party provider, or when you connect to Microsoft Foundry directly |
| `CLAUDE_CODE_AUTO_COMPACT_WINDOW` | Set the [auto-compact window](/docs/en/model-config#set-the-auto-compact-window) in tokens, from `100000` to `1000000`. Accepts a plain integer such as `500000` only: a value like `500k` reads as `500` and clamps to the 100K minimum. Takes precedence over the `/autocompact` command, the `--autocompact` flag, and the `autoCompactWindow` setting. The status line's `used_percentage` always measures against the model's full context window, so once this variable is set, that percentage no longer indicates when compaction will run |
| `CLAUDE_CODE_AUTO_CONNECT_IDE` | Override automatic [IDE connection](/docs/en/vs-code). By default, Claude Code connects automatically when launched inside a supported IDE's integrated terminal. Set to `false` to prevent this. Set to `true` to force a connection attempt when auto-detection fails, such as when tmux obscures the parent terminal. Takes precedence over the [`autoConnectIde`](/docs/en/settings#global-config-settings) global config setting |
| `CLAUDE_CODE_AWS_CHAIN_RESOLVE_TIMEOUT_MS` | Time in milliseconds Claude Code waits for the AWS default credential provider chain to produce credentials before the request fails with [`AWS default-chain credential resolve timed out`](/docs/en/errors#aws-default-chain-credential-resolve-timed-out) (default: `60000`). Raise it when a step in your chain legitimately needs longer, such as a browser-based SSO sign-in with MFA through a wrapper like `aws-vault`. Applies wherever Claude Code signs with the default chain: [Amazon Bedrock](/docs/en/amazon-bedrock#credential-caching-and-resolution-timeout), [Claude Platform on AWS](/docs/en/claude-platform-on-aws), and the [Mantle endpoint](/docs/en/amazon-bedrock#use-the-mantle-endpoint). Requires Claude Code v2.1.207 or later |
| `CLAUDE_CODE_BRIDGE_SESSION_ID` | Set automatically in Bash tool and [hook command](/docs/en/hooks) subprocesses while the session has an active [Remote Control](/docs/en/remote-control) connection, and removed when the connection ends. The value is the session's ID in `session_` form, the same identifier that appears in the session's `claude.ai/code` URL, so a script can link back to the session that ran it. Requires Claude Code v2.1.199 or later. In [cloud sessions](/docs/en/claude-code-on-the-web), read `CLAUDE_CODE_REMOTE_SESSION_ID` instead |
| `CLAUDE_CODE_CERT_STORE` | Comma-separated list of CA certificate sources for TLS connections. `bundled` is the Mozilla CA set shipped with Claude Code. `system` is the operating system trust store, read only on runtimes with `tls.getCACertificates`: the native binary, or Node 22.15 or later for npm installs. See [CA certificate store](/docs/en/network-config#ca-certificate-store). Default is `bundled,system` |
| `CLAUDE_CODE_CHILD_SESSION` | Set to `1` in subprocesses Claude Code spawns via the Bash, PowerShell, and Monitor tools, [hook](/docs/en/hooks) commands, and [status line](/docs/en/statusline) commands. Not set for stdio [MCP server](/docs/en/mcp) subprocesses, which are long-lived and outlive the session that spawned them. Unlike `CLAUDECODE`, this is only set by Claude Code itself when it launches a subprocess and not by IDE extensions, so it reliably distinguishes a nested session from a top-level `claude` launched in an IDE-integrated terminal. A nested interactive `claude` TUI started this way is automatically excluded from `--resume`, `--continue`, up-arrow history, and the `claude agents` list. Non-interactive `claude -p` sessions still persist. Set `CLAUDE_CODE_FORCE_SESSION_PERSISTENCE=1` to override this exclusion. Requires Claude Code v2.1.172 or later |
| `CLAUDE_CODE_CLIENT_CERT` | Path to client certificate file for mTLS authentication |
| `CLAUDE_CODE_CLIENT_KEY` | Path to client private key file for mTLS authentication |
| `CLAUDE_CODE_CLIENT_KEY_PASSPHRASE` | Passphrase for encrypted CLAUDE\_CODE\_CLIENT\_KEY (optional) |
| `CLAUDE_CODE_CONNECT_TIMEOUT_MS` | Removed in v2.1.186 and now a no-op. Previously set a separate timeout for the connect, TLS, and response-header phase of a streaming API request. Use `API_TIMEOUT_MS` for the per-request timeout |
| `CLAUDE_CODE_DEBUG_LOGS_DIR` | Override the debug log file path. Despite the name, this is a file path, not a directory. Requires debug mode to be enabled separately via `--debug`, `/debug`, or the `DEBUG` environment variable: setting this variable alone does not enable logging. The [`--debug-file`](/docs/en/cli-reference#cli-flags) flag does both at once. Defaults to `~/.claude/debug/<session-id>.txt` |
| `CLAUDE_CODE_DEBUG_LOG_LEVEL` | Minimum log level written to the debug log file. Values: `verbose`, `debug` (default), `info`, `warn`, `error`. Set to `verbose` to include high-volume diagnostics like full status line command output, or raise to `error` to reduce noise |
| `CLAUDE_CODE_DISABLE_1M_CONTEXT` | Set to `1` to disable [1M context window](/docs/en/model-config#extended-context) support. When set, 1M model variants are unavailable in the model picker, and Claude Code holds sessions on models with a native 1M window, such as [Sonnet 5](/docs/en/model-config#sonnet-5-context-window) and Fable 5, to a 200K window; see [Extended context](/docs/en/model-config#extended-context) for how the hold is enforced. Useful for enterprise environments with compliance requirements. For its role in correcting the window for an unrecognized `[1m]` model ID, see [Correct the window for a gateway or custom model ID](/docs/en/model-config#correct-the-window-for-a-gateway-or-custom-model-id) |
| `CLAUDE_CODE_DISABLE_ADAPTIVE_THINKING` | Set to `1` to disable [adaptive reasoning](/docs/en/model-config#adjust-effort-level) on Opus 4.6 and Sonnet 4.6 and fall back to the fixed thinking budget controlled by `MAX_THINKING_TOKENS`. Has no effect on Fable 5, Sonnet 5, or Opus 4.7 and later, which always use adaptive reasoning |
| `CLAUDE_CODE_DISABLE_ADMIN_ENV_UNION` | Set to `1` to stop Claude Code from merging [managed settings](/docs/en/settings#precedence-within-the-managed-tier) `env` blocks per key across admin sources, so only the highest-priority source's whole `env` block applies, as before v2.1.223. Set it in the environment that launches Claude Code, since Claude Code ignores a copy delivered through a settings `env` block. Requires Claude Code v2.1.223 or later |
| `CLAUDE_CODE_DISABLE_ADVISOR_TOOL` | Set to `1` to disable the [advisor tool](/docs/en/advisor). The `/advisor` command becomes unavailable, any configured `advisorModel` is ignored, and the `--advisor` flag is accepted but has no effect, so existing scripts that pass it continue to work without errors |
| `CLAUDE_CODE_DISABLE_AGENT_VIEW` | Set to `1` to turn off [background agents and agent view](/docs/en/agent-view): `claude agents`, `--bg`, `/background`, and the on-demand supervisor. Equivalent to the [`disableAgentView`](/docs/en/settings#available-settings) setting |
| `CLAUDE_CODE_DISABLE_ALTERNATE_SCREEN` | Set to `1` to disable [fullscreen rendering](/docs/en/fullscreen) and use the classic main-screen renderer. The conversation stays in your terminal's native scrollback so `Cmd+f` and tmux copy mode work as usual. Takes precedence over `CLAUDE_CODE_NO_FLICKER` and the [`tui`](/docs/en/settings#available-settings) setting. You can also switch with `/tui default`. Does not apply to background sessions opened from [agent view](/docs/en/agent-view), which always use fullscreen rendering |
| `CLAUDE_CODE_DISABLE_ARTIFACT` | Set to `1` to disable the [Artifact](/docs/en/artifacts) tool, which publishes session output as a private web page on claude.ai. Equivalent to the [`disableArtifact`](/docs/en/settings#available-settings) setting |
| `CLAUDE_CODE_DISABLE_ATTACHMENTS` | Set to `1` to disable attachment processing. File mentions with `@` syntax are sent as plain text instead of being expanded into file content |
| `CLAUDE_CODE_DISABLE_AUTO_MEMORY` | Set to `1` to disable [auto memory](/docs/en/memory#auto-memory). Set to `0` to force auto memory on even when `--bare` mode or [`autoMemoryEnabled: false`](/docs/en/settings#available-settings) would otherwise disable it. When disabled, Claude does not create or load auto memory files |
| `CLAUDE_CODE_DISABLE_BACKGROUND_TASKS` | Set to `1` to disable all background task functionality, including the `run_in_background` parameter on Bash and subagent tools, auto-backgrounding, and the Ctrl+B shortcut |
| `CLAUDE_CODE_DISABLE_BEDROCK_CONTENT_TYPE_GUARD` | Set to `1` to skip the check that an [Amazon Bedrock](/docs/en/amazon-bedrock) streaming response carries the `application/vnd.amazon.eventstream` content-type. Without this variable, a response with a different content-type fails with an error naming that content-type, which means a [gateway or proxy is transforming the response](/docs/en/amazon-bedrock#streaming-errors-behind-a-gateway-or-proxy). Set it only when the gateway rewrites the `Content-Type` header but passes the binary event-stream body through unmodified; if the body itself was transformed, requests fail with `Truncated event message received` instead. Requires Claude Code v2.1.208 or later |
| `CLAUDE_CODE_DISABLE_BG_EXIT_HANDOFF` | Set to `1` to stop a [background session's](/docs/en/agent-view) running background shell commands, dynamic workflows, and, as of v2.1.198, background subagents when the [supervisor](/docs/en/agent-view#the-supervisor-process) stops, restarts, or updates that session's process, instead of handing them to the session's next process. Affects only that handoff: backgrounding a session with `←` or [`/background`](/docs/en/agent-view#from-inside-a-session) still carries in-flight work over, and `CLAUDE_DISABLE_ADOPT` turns off both. Requires Claude Code v2.1.196 or later |
| `CLAUDE_CODE_DISABLE_BG_SHELL_PRESSURE_REAP` | Set to `1` to stop Claude Code from terminating [background shell commands](/docs/en/interactive-mode#background-bash-commands) when the operating system reports memory pressure. By default, on macOS and Linux, Claude Code terminates a background shell started in the main session on a memory-pressure signal once the session has been idle for 30 minutes and no turn or subagent is running. Windows has no memory-pressure signal, so this variable has no effect there. Requires Claude Code v2.1.193 or later |
| `CLAUDE_CODE_DISABLE_BUNDLED_SKILLS` | Set to `1` to disable the [skills](/docs/en/skills) and workflows included with Claude Code: bundled skills and workflows are removed entirely, while built-in commands like `/init` stay typable but are hidden from the model. `/doctor` stays typable like the built-in commands; hide it with `DISABLE_DOCTOR_COMMAND` instead. Skills from plugins, `.claude/skills/`, and `.claude/commands/` are unaffected. Equivalent to the [`disableBundledSkills`](/docs/en/settings#available-settings) setting |
| `CLAUDE_CODE_DISABLE_CLAUDE_MDS` | Set to `1` to prevent loading any CLAUDE.md memory files into context, including user, project, and auto memory files |
| `CLAUDE_CODE_DISABLE_CRON` | Set to `1` to disable [scheduled tasks](/docs/en/scheduled-tasks). The `/loop` skill and cron tools become unavailable and any already-scheduled tasks stop firing, including tasks that are already running mid-session |
| `CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS` | Set to `1` to strip Anthropic-specific `anthropic-beta` request headers and beta tool-schema fields (such as `defer_loading` and `eager_input_streaming`) from API requests. Use this when a proxy gateway rejects requests with errors like "Unexpected value(s) for the `anthropic-beta` header" or "Extra inputs are not permitted". Standard fields (`name`, `description`, `input_schema`, `cache_control`) are preserved. [MCP tool search](/docs/en/mcp#scale-with-mcp-tool-search) is disabled and all MCP tools load upfront, even when you set `ENABLE_TOOL_SEARCH`. On Claude Code v2.1.227 or later, [managed settings](/docs/en/settings#settings-files) can keep tool search on. [Disable pre-release capabilities](/docs/en/llm-gateway-protocol#disable-pre-release-capabilities) covers where the override applies |
| `CLAUDE_CODE_DISABLE_EXPLORE_PLAN_AGENTS` | Set to `1` to disable the built-in [Explore and Plan subagents](/docs/en/sub-agents#built-in-subagents). Claude explores with its search tools or the general-purpose subagent instead, and [plan mode](/docs/en/permission-modes#analyze-before-you-edit-with-plan-mode) reads files directly rather than launching Explore and Plan agents. Custom subagents named `Explore` or `Plan` are unaffected. To remove every built-in subagent type in the Agent SDK or non-interactive mode, use `CLAUDE_AGENT_SDK_DISABLE_BUILTIN_AGENTS` instead. Requires Claude Code v2.1.198 or later |
| `CLAUDE_CODE_DISABLE_FAST_MODE` | Set to `1` to disable [fast mode](/docs/en/fast-mode) |
| `CLAUDE_CODE_DISABLE_FEEDBACK_SURVEY` | Set to `1` to disable the "How is Claude doing?" session quality surveys. Surveys are also disabled when `DISABLE_TELEMETRY`, `DO_NOT_TRACK`, or `CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC` is set, unless `CLAUDE_CODE_ENABLE_FEEDBACK_SURVEY_FOR_OTEL` opts back in. To set a sample rate instead of disabling outright, use the [`feedbackSurveyRate`](/docs/en/settings#available-settings) setting. See [Session quality surveys](/docs/en/data-usage#session-quality-surveys) |
| `CLAUDE_CODE_DISABLE_FILE_CHECKPOINTING` | Set to `1` to disable file [checkpointing](/docs/en/checkpointing). The `/rewind` command will not be able to restore code changes. Overrides the [`fileCheckpointingEnabled`](/docs/en/settings#available-settings) setting |
| `CLAUDE_CODE_DISABLE_GIT_INSTRUCTIONS` | Set to `1` to remove built-in commit and PR workflow instructions and the git status snapshot from Claude's system prompt. Useful when using your own git workflow skills. Takes precedence over the [`includeGitInstructions`](/docs/en/settings#available-settings) setting when set |
| `CLAUDE_CODE_DISABLE_LEGACY_MODEL_REMAP` | Set to `1` to prevent automatic remapping of Opus 4.0 and 4.1 to the current Opus version on the Anthropic API. Use when you intentionally want to pin an older model. The remap does not run on Amazon Bedrock, Google Cloud's Agent Platform, or Microsoft Foundry |
| `CLAUDE_CODE_DISABLE_MOUSE` | Set to `1` to disable mouse tracking in [fullscreen rendering](/docs/en/fullscreen). Keyboard scrolling with `PgUp` and `PgDn` still works. Use this to keep your terminal's native copy-on-select behavior |
| `CLAUDE_CODE_DISABLE_MOUSE_CLICKS` | Set to `1` to disable click, drag, and hover handling in [fullscreen rendering](/docs/en/fullscreen) while keeping mouse-wheel scrolling. Use this when you want wheel scroll to work inside Claude Code but don't want clicks to position the cursor, expand tool output, or open links. `CLAUDE_CODE_DISABLE_MOUSE` takes precedence when both are set. Requires Claude Code v2.1.195 or later |
| `CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC` | Set to any non-empty value, such as `1`, to disable nonessential network traffic: auto-updates, telemetry, error reporting, the `/feedback` command, release notes, [gateway model discovery](/docs/en/llm-gateway-connect#add-gateway-models-to-the-model-picker) refreshes, and availability checks such as the [fast mode](/docs/en/fast-mode#use-fast-mode-behind-proxies-and-llm-gateways) check. It also stops the [background runs of plugin `command` sources](/docs/en/plugin-marketplaces#when-claude-code-re-runs-the-command), which are local commands rather than network traffic, because they can trigger dependency installs. **Setting it to `0` or `false` still disables this traffic**, unlike most on/off variables; unset the variable to allow it again. Also disables feature-flag fetching, which makes [Remote Control](/docs/en/remote-control#requirements) and the other [features that need feature-flag fetching](#features-that-need-feature-flag-fetching) unavailable. Official plugin marketplace auto-install isn't covered; disable it with `CLAUDE_CODE_DISABLE_OFFICIAL_MARKETPLACE_AUTOINSTALL` |
| `CLAUDE_CODE_DISABLE_NONSTREAMING_FALLBACK` | Set to `1` to disable the non-streaming fallback when a streaming request fails mid-stream. Streaming errors propagate to the retry layer instead. Useful when a proxy or gateway causes the fallback to produce duplicate tool execution |
| `CLAUDE_CODE_DISABLE_NOTIFICATION_PRESENCE_CHECK` | Set to `1` to send the `PushNotification` tool's desktop notification even while you are typing in or focused on the terminal. By default the tool skips both the desktop notification and the [mobile push](/docs/en/remote-control#mobile-push-notifications) when it detects recent keyboard activity or terminal focus. This variable disables only that local check, so the server can still suppress the mobile push when it detects that you are active. Requires Claude Code v2.1.193 or later |
| `CLAUDE_CODE_DISABLE_OFFICIAL_MARKETPLACE_AUTOINSTALL` | Set to `1` to disable automatic registration of the official plugin marketplace. Claude Code reads the variable when it is about to register the marketplace, usually during a machine's first interactive launch. If the variable is set at that point, Claude Code skips the registration permanently. Unsetting the variable later doesn't undo the skip. Run `claude plugin marketplace add anthropics/claude-plugins-official` to register the marketplace at any time |
| `CLAUDE_CODE_DISABLE_POLICY_SKILLS` | Set to `1` to skip loading skills from the system-wide managed skills directory. Useful for container or CI sessions that should not load operator-provisioned skills |
| `CLAUDE_CODE_DISABLE_TERMINAL_TITLE` | Set to `1` to disable automatic terminal title updates based on conversation context. In Agent SDK and `claude -p` sessions, this also skips the background small/fast-model request that generates the session title |
| `CLAUDE_CODE_DISABLE_THINKING` | Set to `1` to omit the `thinking` parameter from API requests entirely. This is a compatibility option for proxies and gateways that reject the parameter. The variable's behavior is unchanged from earlier versions; on models that think by default, omitting the parameter means the model may still think. To explicitly disable [extended thinking](https://platform.claude.com/docs/en/build-with-claude/extended-thinking) on the Anthropic API, use `MAX_THINKING_TOKENS=0` instead, which is also ineffective on Fable 5 since it cannot have thinking turned off. On [third-party providers](/docs/en/third-party-integrations), `0` likewise omits the parameter, so the two variables behave the same there |
| `CLAUDE_CODE_DISABLE_UNKNOWN_MODEL_WINDOW_ENFORCEMENT` | Set to `1` to skip proactive [auto-compaction](/docs/en/costs#reduce-token-usage) when Claude Code doesn't recognize the model ID, such as an [LLM gateway](/docs/en/llm-gateway) alias. Without this variable, Claude Code compacts at the context window it assumes for the ID. `CLAUDE_CODE_MAX_CONTEXT_TOKENS` can correct the assumed window instead; see [Correct the window for a gateway or custom model ID](/docs/en/model-config#correct-the-window-for-a-gateway-or-custom-model-id) for when each variable applies. Requires Claude Code v2.1.223 or later |
| `CLAUDE_CODE_DISABLE_VIRTUAL_SCROLL` | Set to `1` to disable virtual scrolling in [fullscreen rendering](/docs/en/fullscreen) and render every message in the transcript. Use this if scrolling in fullscreen mode shows blank regions where messages should appear |
| `CLAUDE_CODE_DISABLE_WORKFLOWS` | Set to `1` to disable [workflows](/docs/en/workflows#turn-workflows-off). Equivalent to the [`disableWorkflows`](/docs/en/settings#available-settings) setting |
| `CLAUDE_CODE_EFFORT_LEVEL` | Set the effort level for supported models. Values: `low`, `medium`, `high`, `xhigh`, `max`, or `auto` to use the model default. Available levels depend on the model. Takes precedence over `/effort` and the `effortLevel` setting. See [Adjust effort level](/docs/en/model-config#adjust-effort-level) |
| `CLAUDE_CODE_ENABLE_APPEND_SUBAGENT_PROMPT` | Set to `1` to enable appending extra text to the end of every [subagent](/docs/en/sub-agents)'s system prompt. The [`--append-subagent-system-prompt`](/docs/en/cli-reference#cli-flags) flag supplies the appended text and sets this variable automatically, so you don't need to set it yourself. Requires Claude Code v2.1.205 or later |
| `CLAUDE_CODE_ENABLE_AUTO_MODE` | Accepted for compatibility with older releases and has no effect. Auto mode is available by default on every provider, including Amazon Bedrock, Google Cloud's Agent Platform, Microsoft Foundry, and signed-in [Claude apps gateway](/docs/en/claude-apps-gateway) sessions. In v2.1.158 through v2.1.206, setting this to `1` was required to make [auto mode](/docs/en/permission-modes#eliminate-prompts-with-auto-mode) available on those providers |
| `CLAUDE_CODE_ENABLE_AWAY_SUMMARY` | Override [session recap](/docs/en/interactive-mode#session-recap) availability. Set to `0` to force recaps off regardless of the `/config` toggle. Set to `1` to force recaps on when [`awaySummaryEnabled`](/docs/en/settings#available-settings) is `false`. Takes precedence over the setting and `/config` toggle |
| `CLAUDE_CODE_ENABLE_BACKGROUND_PLUGIN_REFRESH` | Set to `1` to refresh plugin state at turn boundaries in [non-interactive mode](/docs/en/headless) after a background install completes. Off by default because the refresh changes the system prompt mid-session, which invalidates [prompt caching](/docs/en/prompt-caching) for that turn |
| `CLAUDE_CODE_ENABLE_FEEDBACK_SURVEY_FOR_OTEL` | Set to `1` to route the "How is Claude doing?" session quality survey to your own [OpenTelemetry collector](/docs/en/monitoring-usage) when Anthropic-bound nonessential traffic is blocked. Survey ratings are emitted only as OTEL events to your configured collector. No survey data is sent to Anthropic in this mode. Applies when `CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC`, `DISABLE_TELEMETRY`, or `DO_NOT_TRACK` is set, and has no effect otherwise. `CLAUDE_CODE_DISABLE_FEEDBACK_SURVEY` and the organization product feedback policy take precedence |
| `CLAUDE_CODE_ENABLE_FINE_GRAINED_TOOL_STREAMING` | Controls whether tool call inputs stream from the API as Claude generates them. With this off, a large tool input such as a long file write arrives only after Claude finishes generating it, which can look like it's hanging. Enabled by default on the Anthropic API. On Amazon Bedrock and Google Cloud's Agent Platform, enabled per model where the deployed container supports it. Set to `0` to opt out. Set to `1` to force on when routing through a proxy via `ANTHROPIC_BASE_URL`, `ANTHROPIC_VERTEX_BASE_URL`, or `ANTHROPIC_BEDROCK_BASE_URL`. Off by default on Microsoft Foundry and [gateway](/docs/en/llm-gateway) connections |
| `CLAUDE_CODE_ENABLE_GATEWAY_MODEL_DISCOVERY` | Set to `1` to populate the `/model` picker from your gateway's `/v1/models` endpoint when `ANTHROPIC_BASE_URL` points at an Anthropic-compatible gateway such as LiteLLM, Kong, or an internal proxy. Off by default because gateways backed by a shared API key would otherwise show every user every model the key can access. Discovered models are still filtered by an [`availableModels`](/docs/en/settings#available-settings) allowlist the session receives; deliver the list through [MDM or a managed settings file](/docs/en/settings#settings-files), since [server-managed delivery is not available on gateway configurations](/docs/en/server-managed-settings#platform-availability) |
| `CLAUDE_CODE_ENABLE_OPUS_4_7_FAST_MODE` | Removed in v2.1.142, when the [fast mode](/docs/en/fast-mode) default moved from Opus 4.6 to Opus 4.7 |
| `CLAUDE_CODE_ENABLE_PROMPT_SUGGESTION` | Set to `false` to disable prompt suggestions (the "Prompt suggestions" toggle in `/config`). These are the grayed-out predictions that appear in your prompt input. Takes precedence over the [`promptSuggestionEnabled`](/docs/en/settings#available-settings) setting. See [Prompt suggestions](/docs/en/interactive-mode#prompt-suggestions) |
| `CLAUDE_CODE_ENABLE_TASKS` | Controls whether sessions use the structured Task tools (`TaskCreate`, `TaskUpdate`, `TaskGet`, `TaskList`) or the legacy `TodoWrite` tool. As of Claude Code v2.1.142, Task tools are the default in all modes. Set to `0` to revert to `TodoWrite`. See [Task list](/docs/en/interactive-mode#task-list) and [Migrate to Task tools](/docs/en/agent-sdk/todo-tracking#migrate-to-task-tools) |
| `CLAUDE_CODE_ENABLE_TELEMETRY` | Set to `1` to enable OpenTelemetry data collection for metrics and logging. Required before configuring OTel exporters. See [Monitoring](/docs/en/monitoring-usage) |
| `CLAUDE_CODE_EXIT_AFTER_STOP_DELAY` | Time in milliseconds to wait after the query loop becomes idle before automatically exiting. Useful for automated workflows and scripts using SDK mode |
| `CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS` | Set to `1` to enable [agent teams](/docs/en/agent-teams). Agent teams are experimental and disabled by default |
| `CLAUDE_CODE_EXTRA_BODY` | JSON object to merge into the top level of every API request body. Useful for passing provider-specific parameters that Claude Code doesn't expose directly. A value exported in your shell also applies to the [background sessions](/docs/en/agent-view) you dispatch with `claude agents` or `--bg`. Before v2.1.206, background sessions ignored a shell-exported value and used whatever copy the background supervisor process inherited |
| `CLAUDE_CODE_FILE_READ_MAX_OUTPUT_TOKENS` | Override the default token limit for file reads. Useful when you need to read larger files in full |
| `CLAUDE_CODE_FORCE_SESSION_PERSISTENCE` | Set to `1` to force transcript persistence, prompt history, and `claude agents` registration even when this `claude` was launched from inside another Claude Code session. Use when an inherited `CLAUDE_CODE_CHILD_SESSION` value, for example from a `screen` session or a background launcher first started by Claude Code's Bash tool, causes a genuine top-level session to be misclassified as nested. As of v2.1.178, Claude Code detects the tmux case automatically and ignores the inherited marker, so tmux no longer needs this variable. Also honored on v2.1.169 and earlier; has no effect on v2.1.170 and v2.1.171, where the nested-session detection it overrides was removed |
| `CLAUDE_CODE_FORCE_STRIKETHROUGH` | Set to `1` to force strikethrough rendering for `~~text~~` in Claude's responses when your terminal supports it but is not auto-detected, such as over SSH without `TERM_PROGRAM` forwarded. Without this, undetected terminals show the literal `~~` markers instead of rendering the text as strikethrough. Requires Claude Code v2.1.186 or later |
| `CLAUDE_CODE_FORCE_SYNC_OUTPUT` | Set to `1` to force-enable DEC private mode 2026 [synchronized output](https://gist.github.com/christianparpart/d8a62cc1ab659194337d73e399004036) when your terminal supports it but is not auto-detected. Useful for emulators such as Emacs `eat` that implement BSU/ESU but do not reply to the capability probe. Has no effect under tmux. Unlike `CLAUDE_CODE_NO_FLICKER`, which switches to [fullscreen rendering](/docs/en/fullscreen), this doesn't change the renderer |
| `CLAUDE_CODE_FORK_SUBAGENT` | Controls [fork mode](/docs/en/sub-agents#turn-fork-mode-on-or-off), which lets Claude spawn [forked subagents](/docs/en/sub-agents#fork-the-current-conversation) itself and is on by default in interactive sessions only. Set to `1` to turn it on in `claude -p` and the Agent SDK as well, or `0` to turn it off in every kind of session. The `/subtask` command works whether or not fork mode is on. The interactive default requires Claude Code v2.1.232 or later |
| `CLAUDE_CODE_FORWARD_SUBAGENT_TEXT` | Set to `1` to emit [subagent](/docs/en/sub-agents) text and thinking blocks in `claude -p --output-format stream-json` output, the same behavior as the [`--forward-subagent-text`](/docs/en/cli-reference#cli-flags) flag. Use the variable when a harness invokes `claude` and can't pass the flag itself. Unlike the flag, which exits with an error outside non-interactive mode with stream-json output, the variable is ignored there so that nested invocations keep working when it's set process-wide. Requires Claude Code v2.1.211 or later |
| `CLAUDE_CODE_GIT_BASH_PATH` | Windows only: path to the Git Bash executable (`bash.exe`). Use when Git Bash is installed but not in your PATH. If the path doesn't exist or the file isn't named `bash.exe`, `sh.exe`, `bash`, or `sh`, Claude Code ignores the variable and auto-detects Git Bash as if it were unset, logging a warning visible with `--debug`. Before v2.1.219, Claude Code exited at startup when the path didn't exist, and used any existing file as the shell without checking that it was bash or sh. See [Windows setup](/docs/en/setup#set-up-on-windows) |
| `CLAUDE_CODE_GLOB_HIDDEN` | Set to `false` to exclude dotfiles from results when Claude invokes the [Glob tool](/docs/en/tools-reference#glob-tool-behavior). Included by default. Does not affect `@` file autocomplete, `ls`, Grep, or Read |
| `CLAUDE_CODE_GLOB_NO_IGNORE` | Set to `false` to make the [Glob tool](/docs/en/tools-reference#glob-tool-behavior) respect `.gitignore` patterns. By default, Glob returns all matching files including gitignored ones. Does not affect `@` file autocomplete, which has its own [`respectGitignore` setting](/docs/en/settings#available-settings) |
| `CLAUDE_CODE_GLOB_TIMEOUT_SECONDS` | Timeout in seconds for Glob tool file discovery. Defaults to 20 seconds on most platforms and 60 seconds on WSL |
| `CLAUDE_CODE_HIDE_CWD` | Set to `1` to hide the working directory in the startup logo. Useful for screenshares or recordings where the path exposes your OS username |
| `CLAUDE_CODE_IDE_HOST_OVERRIDE` | Override the host address used to connect to the IDE extension. By default Claude Code auto-detects the correct address, including WSL-to-Windows routing |
| `CLAUDE_CODE_IDE_SKIP_AUTO_INSTALL` | Set to `1` to skip auto-installation of IDE extensions. Equivalent to setting [`autoInstallIdeExtension`](/docs/en/settings#global-config-settings) to `false` |
| `CLAUDE_CODE_IDE_SKIP_VALID_CHECK` | Set to `1` to skip validation of IDE lockfile entries during connection. Use when auto-connect fails to find your IDE despite it running |
| `CLAUDE_CODE_MAX_CONCURRENT_SUBAGENTS` | How many [subagents](/docs/en/sub-agents#concurrent-subagent-limit) can be running in one session before the Agent tool refuses to spawn another (default: 20). Accepts a positive whole number in plain digits; anything else is ignored, so the variable can adjust the cap but can't disable it. Requires Claude Code v2.1.217 or later |
| `CLAUDE_CODE_MAX_CONTEXT_TOKENS` | Override the context window size Claude Code assumes for the active model. As of v2.1.193, how it applies depends on how Claude Code resolves the model ID; see [Correct the window for a gateway or custom model ID](/docs/en/model-config#correct-the-window-for-a-gateway-or-custom-model-id). Use this when routing to a model through `ANTHROPIC_BASE_URL` whose context window does not match the built-in size for its name |
| `CLAUDE_CODE_MAX_OUTPUT_TOKENS` | Set the maximum number of output tokens for most requests. Defaults and caps vary by model; see [max output tokens](https://platform.claude.com/docs/en/about-claude/models/overview#latest-models-comparison). Claude Code defaults to 32000 for model IDs it doesn't recognize, such as gateway-specific names, and lowers values above a model's cap to the cap. Increasing this value reduces the effective context window available before [auto-compaction](/docs/en/costs#reduce-token-usage) triggers |
| `CLAUDE_CODE_MAX_RETRIES` | Override the number of times to retry failed API requests (default: 10). Capped at 15 as of v2.1.186; as of v2.1.199, `CLAUDE_CODE_RETRY_WATCHDOG` raises the default and removes the cap. For unattended sessions that need to wait through longer outages, set `CLAUDE_CODE_RETRY_WATCHDOG` instead |
| `CLAUDE_CODE_MAX_SUBAGENTS_PER_SESSION` | Removed in v2.1.224 and now a no-op. Previously capped the total number of [subagents](/docs/en/sub-agents) Claude could spawn with the Agent tool in one session (default: 200); spawning past the cap failed with `Subagent spawn limit reached`. The [concurrent subagent limit](/docs/en/sub-agents#concurrent-subagent-limit) and the [depth limit](/docs/en/sub-agents#let-subagents-spawn-their-own-subagents) still apply |
| `CLAUDE_CODE_MAX_SUBAGENT_SPAWN_DEPTH` | Number of [subagent layers](/docs/en/sub-agents#let-subagents-spawn-their-own-subagents) allowed below the main conversation (default: 3). At the default, subagents can spawn their own subagents, and a subagent at the third layer can't spawn further; set `1` to turn nesting off. In v2.1.217 through v2.1.218, the default was 1, so a subagent couldn't spawn its own unless you raised the limit; v2.1.219 raised the default to 3. Accepts a positive whole number in plain digits; anything else is ignored, so the limit can be adjusted but not removed. Requires Claude Code v2.1.217 or later |
| `CLAUDE_CODE_MAX_TOOL_USE_CONCURRENCY` | Maximum number of read-only tools and subagents that can execute in parallel (default: 10). Higher values increase parallelism but consume more resources |
| `CLAUDE_CODE_MAX_TURNS` | Cap the number of agentic turns when no explicit limit is passed. Equivalent to passing [`--max-turns`](/docs/en/cli-reference#cli-flags), which takes precedence when both are set. A value that is not a positive integer is rejected at startup with an error rather than treated as no cap |
| `CLAUDE_CODE_MAX_WEB_SEARCHES_PER_SESSION` | Cap on the total number of [WebSearch](/docs/en/tools-reference#websearch-tool-behavior) calls one session can make (default: 200). When Claude reaches the cap, further WebSearch calls return a notice telling it to continue with the information it already gathered. Accepts a positive whole number with no upper bound. Anything else is ignored and the default applies, so the cap can be raised but not turned off. Requires Claude Code v2.1.212 or later |
| `CLAUDE_CODE_MCP_ALLOWLIST_ENV` | Set to `1` to spawn stdio MCP servers with only a safe baseline environment plus the server's configured `env`, instead of inheriting your shell environment |
| `CLAUDE_CODE_MCP_AUTO_BACKGROUND_MS` | Elapsed time in milliseconds before a still-running MCP tool call [moves to a background task](/docs/en/mcp#automatic-backgrounding-of-long-tool-calls) (default: 120000, or 2 minutes). Set to `0` to turn automatic backgrounding off. Requires Claude Code v2.1.212 or later |
| `CLAUDE_CODE_MCP_TOOL_IDLE_TIMEOUT` | Idle timeout in milliseconds for MCP tool calls. When a stdio, HTTP, SSE, WebSocket, or [claude.ai connector](/docs/en/mcp#use-mcp-servers-from-claude-ai) MCP server sends no response and no progress notification for this long, the tool call aborts with an error instead of waiting for the overall `MCP_TOOL_TIMEOUT`. Overrides the per-transport defaults of 300000 (5 minutes) for network servers and 1800000 (30 minutes) for stdio servers. Set to `0` to disable the idle check. Values below 1000 are raised to one second, and the value is capped at the effective `MCP_TOOL_TIMEOUT`. A per-server `timeout` in `.mcp.json` of at least 1000 raises that server's idle window to at least the `timeout` value. Doesn't apply to IDE servers or SDK in-process servers. Requires Claude Code v2.1.187 or later. Before v2.1.203, stdio servers were exempt from the idle timeout |
| `CLAUDE_CODE_MESSAGING_SOCKET` | Set by Claude Code, not by you: in sessions that bind an [inbox socket](/docs/en/cross-session-messaging#the-sessions-inbox-socket), Claude Code exports that socket's path to hooks and Bash commands when it binds the socket. In a session that starts with messaging on, Claude Code binds the socket before any hook runs. Other sessions on the machine deliver messages to this path. Each session exports its own socket rather than one inherited from a parent, and messages arriving on it go through the session's [inbound controls](/docs/en/cross-session-messaging#control-inbound-messages). Settings `env` blocks can't set it. Requires Claude Code v2.1.224 or later |
| `CLAUDE_CODE_MESSAGING_TOKEN` | Set by Claude Code, not by you: in sessions that bind an [inbox socket](/docs/en/cross-session-messaging#the-sessions-inbox-socket), Claude Code exports this per-session token to hooks and Bash commands alongside `CLAUDE_CODE_MESSAGING_SOCKET`. A script posting to the socket can send `{"type":"auth","token":"<token>"}` as its first line to prove it belongs to the session. The [own-child rules](/docs/en/cross-session-messaging#the-sessions-inbox-socket) say when Claude Code consults it. Each session exports its own token, never one inherited from a parent session. Settings `env` blocks can't set it. Requires Claude Code v2.1.228 or later |
| `CLAUDE_CODE_NATIVE_CURSOR` | Set to `1` to show the terminal's own cursor at the input caret instead of a drawn block. The cursor respects the terminal's blink, shape, and focus settings |
| `CLAUDE_CODE_NEW_INIT` | Set to `1` to make `/init` run an interactive setup flow. The flow asks which files to generate, including CLAUDE.md, skills, and hooks, before exploring the codebase and writing them. Without this variable, `/init` generates a CLAUDE.md automatically without prompting |
| `CLAUDE_CODE_NO_FLICKER` | Set to `1` to enable [fullscreen rendering](/docs/en/fullscreen), a research preview that reduces flicker and keeps memory flat in long conversations. Equivalent to the [`tui`](/docs/en/settings#available-settings) setting; you can also switch with `/tui fullscreen` |
| `CLAUDE_CODE_OAUTH_REFRESH_TOKEN` | OAuth refresh token for Claude.ai authentication. When set, `claude auth login` exchanges this token directly instead of opening a browser. Requires `CLAUDE_CODE_OAUTH_SCOPES`. Useful for provisioning authentication in automated environments |
| `CLAUDE_CODE_OAUTH_SCOPES` | Space-separated OAuth scopes the refresh token was issued with, such as `"user:profile user:inference user:sessions:claude_code"`. Required when `CLAUDE_CODE_OAUTH_REFRESH_TOKEN` is set |
| `CLAUDE_CODE_OAUTH_TOKEN` | OAuth access token for claude.ai authentication. Alternative to `/login` for SDK and automated environments. Takes precedence over keychain-stored credentials. Generate one with [`claude setup-token`](/docs/en/authentication#generate-a-long-lived-token). Unless you run [`/login`](/docs/en/authentication#authentication-precedence), Claude Code uses the token you set for the whole session. To replace an expired token, generate a new one and restart |
| `CLAUDE_CODE_OPUS_4_6_FAST_MODE_OVERRIDE` | Removed in v2.1.160 and now a no-op. Previously pinned [fast mode](/docs/en/fast-mode) to Claude Opus 4.6 instead of the current default. Opus 4.6 no longer supports fast mode |
Cut at 300 lines. The page has the rest.
errors First recorded · 2226 lines, first recorded
# Error reference ## Find your error ## Automatic retries ### What you see while Claude Code retries or waits ### Tune retry behavior ## Server errors ### API Error: 500 Internal server error ### API Error: Repeated 529 Overloaded errors ### Request timed out ### The response above may be incomplete ### Auto mode cannot determine the safety of an action ### Agent terminated early due to an API error ## Usage limits ### Usage credits required for 1M context ### Server is temporarily limiting requests ### Request rejected (429) ### Credit balance is too low ### Could not update your spend limit ## Authentication errors ### Not logged in ### Could not resolve authentication method ### Invalid API key ### Your apiKeyHelper script is failing ### Invalid request header value ### This organization has been disabled ### Your organization has disabled API key authentication ### Your organization has disabled Claude subscription access ### Remote Control requires the Anthropic API ### OAuth token revoked or expired ### API Error: 401 Invalid authentication credentials ### Login expired ### Anthropic profile login expired ### OAuth scope requirement ### claude.ai rejected the session token ### AWS credentials expired or invalid ### AWS authentication failed ### AWS default-chain credential resolve timed out ## Network and connection errors ### Unable to connect to API ### Unable to connect to Anthropic services ### Socket is closed ### Bedrock streaming response has an unexpected content-type ### SSL certificate errors ### Host not allowed in a cloud session ## Request errors ### Prompt is too long ### Context exceeds the token limit ### Error during compaction: Conversation too long ### Request too large ### Image was too large ### Unable to resize image ### PDF errors ### Extra inputs are not permitted ### Model is not a recognized model id ### Claude Opus is not available with the Claude Pro plan ### thinking.type.enabled is not supported for this model ### Thinking budget exceeds output limit ### Tool use or thinking block mismatch ### Usage Policy refusal ### Safety measures flagged a cybersecurity topic ## Installation errors ### Installation was killed before it could finish ### The connection dropped while downloading the update ## Command-line errors ### Conflict between --bg and --print ### The --json-schema value is not a valid JSON Schema ### Settings file exceeds the 2MiB limit ### Workspace not trusted when starting Remote Control ### claude import is not yet available in this build ### Could not read Claude Code config ### Could not import a server from Claude Desktop ### MCP permission prompt tool not found ### Input contained only whitespace ### Diff is too large for ultrareview ### Could not find merge-base with the base branch ### Your checkout has no branches ### Failed to resume the conversation ### No conversation found with the session ID ## Plugin errors ### Marketplace is registered from an untrusted source ### Plugin archive integrity check failed ## Tool errors ### Agent would be spawned with zero tools ### File is covered by a Read deny rule ### Memory index is over its read limit ### pkill pattern matches the Claude Code process ## Background session errors ### Commands refused in a background session ### Write or command blocked because the path cannot be safely resolved ### Write or command blocked because the path names a network location ### This session has no saved transcript ### CLAUDE\_CODE\_PROCESS\_WRAPPER launcher errors ### EUNKNOWN when starting a background session ## Wrapper and IDE errors ### Claude Code process exited with code N ## Rewind warnings ## Session saving warnings ### Transcript writes are failing ## Configuration warnings ### Workspace has not been trusted ### Is not matched by file permission checks ## Responses seem lower quality than usual ## Report an error
The first capture of this source. The page was already there, and this is what it said.
# Error reference
> Look up Claude Code runtime error messages with what each one means and how to fix it.
This page lists runtime errors Claude Code displays and how to recover from each one, plus what to check when responses seem off without an error. For installation errors such as `command not found` or TLS failures during setup, see [Troubleshoot installation and login](/docs/en/troubleshoot-install).
Except for [Wrapper and IDE errors](#wrapper-and-ide-errors), which the launching program prints rather than Claude Code itself, these errors and recovery commands apply across the CLI, the [Desktop app](/docs/en/desktop), and [Claude Code on the web](/docs/en/claude-code-on-the-web), since all three wrap the same Claude Code CLI. For other surface-specific issues, see the troubleshooting section on that surface's page.
<Note>
Claude Code calls the Claude API for model responses, so most runtime errors map to an underlying API error code. This page covers what each error means inside Claude Code and how to recover. For the raw HTTP status code definitions, see the [Claude Platform error reference](https://platform.claude.com/docs/en/api/errors).
</Note>
## Find your error
Match the message you see in your terminal to a section below.
| Message | Section |
| :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :---------------------------------------------------------------------------------------------------------------------------- |
| `API Error: 500 Internal server error` | [Server errors](#api-error-500-internal-server-error) |
| `API Error: Repeated 529 Overloaded errors` | [Server errors](#api-error-repeated-529-overloaded-errors) |
| `Request timed out` | [Server errors](#request-timed-out), or [Network](#unable-to-connect-to-api) if the message mentions your internet connection |
| `Server error mid-response. The response above may be incomplete.` | [Server errors](#the-response-above-may-be-incomplete) |
| `Connection lost mid-response` / `Your computer went to sleep mid-response` / `The response stopped arriving` | [Server errors](#the-response-above-may-be-incomplete) |
| `Connection closed mid-response` / `Response stalled mid-stream` | [Server errors](#the-response-above-may-be-incomplete) |
| `Connection lost before a response was produced` / `Your computer went to sleep before a response was produced` / `The response stalled before a response was produced` | [Automatic retries](#automatic-retries) |
| `Connection closed while thinking` / `Response stalled while thinking` | [Automatic retries](#automatic-retries) |
| `Connection lost while your computer was asleep` | [Automatic retries](#automatic-retries) |
| `<model> is temporarily unavailable, so auto mode cannot determine the safety of...` | [Server errors](#auto-mode-cannot-determine-the-safety-of-an-action) |
| `Auto mode could not evaluate this action and is blocking it for safety` | [Server errors](#auto-mode-cannot-determine-the-safety-of-an-action) |
| `Auto mode classifier transcript exceeded context window` | [Server errors](#auto-mode-cannot-determine-the-safety-of-an-action) |
| `Agent aborted: auto mode classifier request refused by the safety safeguard` | [Server errors](#auto-mode-cannot-determine-the-safety-of-an-action) |
| `Agent terminated early due to an API error` | [Server errors](#agent-terminated-early-due-to-an-api-error) |
| `You've hit your session limit` / `You've hit your weekly limit` | [Usage limits](#youve-hit-your-session-limit) |
| `Usage credits required for 1M context` | [Usage limits](#usage-credits-required-for-1m-context) |
| `Server is temporarily limiting requests` | [Usage limits](#server-is-temporarily-limiting-requests) |
| `Request rejected (429)` | [Usage limits](#request-rejected-429) |
| `Credit balance is too low` | [Usage limits](#credit-balance-is-too-low) |
| `Could not update your spend limit` | [Usage limits](#could-not-update-your-spend-limit) |
| `spend limit reached` / `spend limit unavailable` | [Usage limits](#spend-limit-reached) |
| `Not logged in · Please run /login` | [Authentication](#not-logged-in) |
| `Could not resolve authentication method` | [Authentication](#could-not-resolve-authentication-method) |
| `Invalid API key` | [Authentication](#invalid-api-key) |
| `Your apiKeyHelper script is failing` | [Authentication](#your-apikeyhelper-script-is-failing) |
| `Invalid auth token · Fix external auth token` | [Authentication](#invalid-request-header-value) |
| `Invalid ANTHROPIC_CUSTOM_HEADERS · Fix the environment variable` | [Authentication](#invalid-request-header-value) |
| `Invalid request header from the environment · Fix the environment variable` | [Authentication](#invalid-request-header-value) |
| `This organization has been disabled` | [Authentication](#this-organization-has-been-disabled) |
| `Your organization has disabled API key authentication` | [Authentication](#your-organization-has-disabled-api-key-authentication) |
| `Your organization has disabled Claude subscription access` | [Authentication](#your-organization-has-disabled-claude-subscription-access) |
| `Routines are disabled by your organization's policy` | [Authentication](#routines-are-disabled-by-your-organizations-policy) |
| `Remote Control is only available when using Claude via api.anthropic.com` | [Authentication](#remote-control-requires-the-anthropic-api) |
| `OAuth token refresh failed — run /login to re-authenticate` | [Authentication](#remote-control-couldnt-refresh-your-login) |
| `JWT refresh failed: no OAuth token — run /login` | [Authentication](#remote-control-couldnt-refresh-your-login) |
| `Claude.ai login expired` | [Authentication](#remote-control-couldnt-refresh-your-login) |
| `Claude.ai login was rejected — run /login, then /remote-control` | [Authentication](#remote-control-couldnt-refresh-your-login) |
| `OAuth token unavailable — run /login to restore Remote Control` | [Authentication](#remote-control-couldnt-refresh-your-login) |
| `OAuth token revoked` / `OAuth token has expired` | [Authentication](#oauth-token-revoked-or-expired) |
| `API Error: 401 Invalid authentication credentials` | [Authentication](#api-error-401-invalid-authentication-credentials) |
| `Login expired · Please run /login` | [Authentication](#login-expired) |
| `Failed to authenticate: OAuth session expired and could not be refreshed` | [Authentication](#login-expired) |
| `Anthropic profile login expired · Re-authenticate your Anthropic profile` | [Authentication](#anthropic-profile-login-expired) |
| `does not meet scope requirement user:profile` | [Authentication](#oauth-scope-requirement) |
| `claude.ai rejected the session token` / `session token rejected` | [Authentication](#claude-ai-rejected-the-session-token) |
| `AWS credentials expired or invalid` | [Authentication](#aws-credentials-expired-or-invalid) |
| `AWS authentication failed` | [Authentication](#aws-authentication-failed) |
| `AWS default-chain credential resolve timed out` | [Authentication](#aws-default-chain-credential-resolve-timed-out) |
| `Could not load the default credentials` on Google Cloud's Agent Platform | [Automatic retries](#automatic-retries) |
| `Unable to connect to API` | [Network](#unable-to-connect-to-api) |
| `Connection refused —` / `Can't reach the API server —` / `No internet route —` / `Couldn't connect through your proxy` / `Connection dropped`, each ending with an error code in parentheses | [Network](#unable-to-connect-to-api) |
| `Unable to connect to Anthropic services` during setup | [Network](#unable-to-connect-to-anthropic-services) |
| `Socket is closed` | [Network](#socket-is-closed) |
| `Waiting for API response · will retry in` | [Automatic retries](#automatic-retries), or [Network](#unable-to-connect-to-api) if it persists |
| `Bedrock streaming response has content-type "..."; expected "application/vnd.amazon.eventstream"` | [Network](#bedrock-streaming-response-has-an-unexpected-content-type) |
| `SSL certificate verification failed` | [Network](#ssl-certificate-errors) |
| `SSL certificate error (...)` during login or startup | [Network](#ssl-certificate-errors) |
| `403` with `x-deny-reason: host_not_allowed` in a cloud or routine session | [Network](#host-not-allowed-in-a-cloud-session) |
| `403` with `This GraphQL query is not enabled for this session` in a cloud session | [GitHub proxy](/docs/en/cloud-environments#github-proxy) |
| `Couldn't reconnect to your Remote Control session` | [Network](#couldnt-reconnect-to-your-remote-control-session) |
| `Couldn't share the transcript.` | [Network](#couldnt-share-the-transcript) |
| `Prompt is too long` / `Input is too long for requested model` | [Request errors](#prompt-is-too-long) |
| `Prompt is too long · automatic compaction failed:` | [Request errors](#prompt-is-too-long) |
| `Context exceeds the ...-token limit by ... tokens` in `/context` output | [Request errors](#context-exceeds-the-token-limit) |
| `Error during compaction: Conversation too long` | [Request errors](#error-during-compaction-conversation-too-long) |
| `Request too large` | [Request errors](#request-too-large) |
| `Request too large for the API's 32MB request limit` | [Request errors](#request-too-large) |
| `Image was too large` | [Request errors](#image-was-too-large) |
| `Unable to resize image` | [Request errors](#unable-to-resize-image) |
| `PDF too large` / `PDF is password protected` | [Request errors](#pdf-errors) |
| `Extra inputs are not permitted` | [Request errors](#extra-inputs-are-not-permitted) |
| `There's an issue with the selected model` | [Request errors](#theres-an-issue-with-the-selected-model) |
| `Model ... is not a recognized model id` | [Request errors](#model-is-not-a-recognized-model-id) |
| `Claude Opus is not available with the Claude Pro plan` | [Request errors](#claude-opus-is-not-available-with-the-claude-pro-plan) |
| `Model ... is restricted by your organization's settings` | [Request errors](#model-is-restricted-by-your-organizations-settings) |
| `thinking.type.enabled is not supported for this model` | [Request errors](#thinking-type-enabled-is-not-supported-for-this-model) |
| `max_tokens must be greater than thinking.budget_tokens` | [Request errors](#thinking-budget-exceeds-output-limit) |
| `API Error: 400 due to tool use concurrency issues` | [Request errors](#tool-use-or-thinking-block-mismatch) |
| `<model> can't help with this. Start a new session to continue` | [Request errors](#usage-policy-refusal) |
| `Claude Code is unable to respond to this request, which appears to violate our Usage Policy` | [Request errors](#usage-policy-refusal) |
| `<model>'s safeguards flagged this message` | [Request errors](#safety-measures-flagged-a-cybersecurity-topic) |
| `<model> has safety measures that flagged this message for a cybersecurity topic` | [Request errors](#safety-measures-flagged-a-cybersecurity-topic) |
| `Installation was killed before it could finish (exit code 137)` | [Installation errors](#installation-was-killed-before-it-could-finish) |
| `The connection dropped while downloading the update` | [Installation errors](#the-connection-dropped-while-downloading-the-update) |
| `Download timed out: exceeded the total deadline` | [Installation errors](#the-connection-dropped-while-downloading-the-update) |
| `--bg and --print conflict` | [Command-line errors](#command-line-errors) |
| `Error: --json-schema is not a valid JSON Schema` | [Command-line errors](#command-line-errors) |
| `Error: Settings file exceeds the 2MiB limit` | [Command-line errors](#settings-file-exceeds-the-2mib-limit) |
| `Error: Workspace not trusted` when starting Remote Control | [Command-line errors](#workspace-not-trusted-when-starting-remote-control) |
| `` `claude import` is not yet available in this build `` | [Command-line errors](#claude-import-is-not-yet-available-in-this-build) |
| `Could not read Claude Code config` | [Command-line errors](#could-not-read-claude-code-config) |
| `Could not import <server>: <reason>` | [Command-line errors](#could-not-import-a-server-from-claude-desktop) |
| `Error: MCP tool <name> (passed via --permission-prompt-tool) not found` | [Command-line errors](#mcp-permission-prompt-tool-not-found) |
| `Shell command failed for pattern "..."`, from `/security-review` or any skill that injects dynamic context | [Command-line errors](#security-review-fails-without-origin-head) |
| `Shell command permission check failed for pattern "..."`, from a skill that injects dynamic context | [Command-line errors](#security-review-fails-without-origin-head) |
| ``Skill <name> requires bash (`shell: bash` in frontmatter) but Git Bash was not found`` | [Command-line errors](#security-review-fails-without-origin-head) |
| `Input must be provided either through stdin or as a prompt argument when using --print` | [Command-line errors](#input-must-be-provided-when-using-print) |
| `Error: Input contained only whitespace` | [Command-line errors](#input-contained-only-whitespace) |
| `Blank prompt — the message was only whitespace, so nothing was sent to the model.` | [Command-line errors](#input-contained-only-whitespace) |
| `Diff is too large for ultrareview` / `PR #<N> is too large for ultrareview` | [Command-line errors](#diff-is-too-large-for-ultrareview) |
| `Could not find merge-base with <branch>` | [Command-line errors](#could-not-find-merge-base-with-the-base-branch) |
| `Your checkout has no branches (detached HEAD only)` | [Command-line errors](#your-checkout-has-no-branches) |
| `Failed to resume the conversation` | [Command-line errors](#failed-to-resume-the-conversation) |
| `No conversation found with session ID: <session-id>` | [Command-line errors](#no-conversation-found-with-the-session-id) |
| `Marketplace "<name>" is registered from an untrusted source` | [Plugin errors](#marketplace-is-registered-from-an-untrusted-source) |
| `references ${user_config.*} in a shell-form command` | [Plugin errors](#plugin-command-references-user-config) |
| `Monitor "<name>" from plugin <plugin> references ${user_config.*} in its command` | [Plugin errors](#plugin-command-references-user-config) |
| `headersHelper for MCP server '<name>' references ${user_config.*}` | [Plugin errors](#plugin-command-references-user-config) |
| `Plugin archive integrity check failed` | [Plugin errors](#plugin-archive-integrity-check-failed) |
| `would be spawned with zero tools — refusing` | [Tool errors](#agent-would-be-spawned-with-zero-tools) |
| `File is covered by a Read deny rule in your permission settings` | [Tool errors](#file-is-covered-by-a-read-deny-rule) |
| `Error: this write left the memory index at MEMORY.md at ..., over its ... read limit` | [Tool errors](#memory-index-is-over-its-read-limit) |
| `pkill: refusing to run` | [Tool errors](#pkill-pattern-matches-the-claude-code-process) |
| `Failed to write to <name>'s inbox — nothing was sent` | [Tool errors](#failed-to-write-to-a-teammate-inbox) |
| `Failed to write the plan approval request to the lead's inbox — plan not submitted` | [Tool errors](#failed-to-write-to-a-teammate-inbox) |
| `Can't open MCP settings while no terminal is attached to this background session` | [Background session errors](#commands-refused-in-a-background-session) |
| `Can't open MCP settings in a background session` | [Background session errors](#commands-refused-in-a-background-session) |
| `blocked because the path is spelled in a form that cannot be safely resolved` | [Background session errors](#write-or-command-blocked-because-the-path-cannot-be-safely-resolved) |
| `blocked because the path is network-shaped` | [Background session errors](#write-or-command-blocked-because-the-path-names-a-network-location) |
| `This session has no saved transcript` | [Background session errors](#this-session-has-no-saved-transcript) |
| `This session was running agent '<name>', which is no longer available` | [Background session errors](#session-agent-no-longer-available) |
| `CLAUDE_CODE_PROCESS_WRAPPER: launcher ...` | [Background session errors](#claude_code_process_wrapper-launcher-errors) |
| `EUNKNOWN: unknown error, uv_spawn` | [Background session errors](#eunknown-when-starting-a-background-session) |
| `Claude Code process exited with code N` | [Wrapper and IDE errors](#claude-code-process-exited-with-code-n) |
| `Could not locate the Claude CLI on PATH` | [Wrapper and IDE errors](#could-not-locate-the-claude-cli-on-path) |
| `Restored the code, but skipped N files` | [Rewind warnings](#restored-the-code-but-skipped-files) |
| `Transcript writes are failing (...)` | [Session saving warnings](#transcript-writes-are-failing) |
| `Transcript saving is off — CLAUDE_CODE_SKIP_PROMPT_HISTORY is set` | [Session saving warnings](#transcript-saving-is-off-skip-prompt-history) |
| `Transcript saving is off — inherited CLAUDE_CODE_CHILD_SESSION marker` | [Session saving warnings](#transcript-saving-is-off-child-session-marker) |
| `Ignoring N permissions.allow entries from ... this workspace has not been trusted` | [Configuration warnings](#workspace-has-not-been-trusted) |
| `... is not matched by file permission checks` | [Configuration warnings](#is-not-matched-by-file-permission-checks) |
| `CLAUDE_CODE_DISABLE_1M_CONTEXT is set, but the 200K limit isn't enforced` | [Configuration warnings](#the-200k-limit-isnt-enforced) |
| Responses seem lower quality than usual | [Response quality](#responses-seem-lower-quality-than-usual) |
## Automatic retries
Claude Code retries transient failures up to 10 times with exponential backoff before showing you an error. It doesn't always retry a failure that arrives partway through Claude's response. When you see one of the errors on this page, Claude Code has already made whatever retries apply to that failure; the lists below say which failures get the full budget, which get a smaller one, and which get none.
Claude Code retries these failures:
* Server errors, overloaded responses, and request timeouts that arrive before any of Claude's response has streamed.
* Dropped connections. When a connection drops partway through a request before Claude has completed any part of its response, including its thinking, Claude Code re-issues the request with the same backoff and the turn continues, even if some text had already started streaming. When it drops after Claude has finished thinking but before it has started any text or tool call, Claude Code instead re-issues the request up to two times in quick succession, and ends the turn with `Connection lost before a response was produced` if the connection keeps dropping at that point.
* A connection that Claude Code detects was broken by your computer going to sleep partway through a request. Claude Code counts it as a dropped connection under the rules above; once the retry label names the specific reason, it reads `Connection lost while your computer was asleep`, and if the turn ends after Claude has finished thinking but before any text or tool call, the message reads `Your computer went to sleep before a response was produced`.
* A stalled response stream, when none of the response has arrived yet or when Claude has finished thinking but hasn't started any text or tool call: Claude Code aborts the stalled connection and re-issues the request at most once, outside the 10-attempt budget above. If the response stalls a second time after Claude has finished thinking but before any text or tool call, Claude Code ends the turn with `The response stalled before a response was produced`.
* Temporary 429 throttles, but not a gateway's spend-limit `429`, which isn't a throttle; see [Spend limit reached](#spend-limit-reached).
* When you're signed in with a claude.ai subscription, this includes 429 throttles that don't carry your plan's quota headers. Before v2.1.199, Claude Code retried those throttles only for API key and Enterprise sign-ins.
* A request rejected because the input plus `max_tokens` exceeds the context limit. Re-sending it unchanged would fail the same way, so Claude Code retries with a reduced `max_tokens`, and stops retrying and compacts instead in two cases:
* When no reduction can fit, for example when the conversation itself nearly fills the context window.
* When a retry can't shrink `max_tokens` any further. Before v2.1.218, Claude Code could re-send a reduced request that still didn't fit, such as when the extended thinking budget exceeded the remaining context, until the retry budget ran out.
* An expired or missing Google Cloud credential on [Google Cloud's Agent Platform](/docs/en/google-vertex-ai), which surfaces as an error such as `Could not load the default credentials`. Claude Code discards its cached credentials and retries up to two times, running your [`gcpAuthRefresh`](/docs/en/google-vertex-ai#advanced-credential-configuration) command if you configured one, then reports the error so you can re-authenticate right away. [Google Cloud's Agent Platform troubleshooting](/docs/en/google-vertex-ai#troubleshooting) covers re-authenticating. Before v2.1.228, Claude Code retried a failing credential through the full retry budget before showing the error.
Before v2.1.227, `Connection lost before a response was produced` read `Connection closed while thinking, before producing a response` and `The response stalled before a response was produced` read `Response stalled while thinking, before producing a response`.
Claude Code doesn't retry these failures:
* A TLS certificate validation failure, such as a TLS-inspecting proxy, a missing `NODE_EXTRA_CA_CERTS` bundle, or an expired certificate. Claude Code reports the error on the first attempt, so you can fix the certificate setup right away; see [SSL certificate errors](#ssl-certificate-errors). Claude Code still retries transient TLS conditions such as a handshake timeout. Before v2.1.199, Claude Code retried certificate failures through the full retry budget before showing the error.
* A server error, dropped connection, or stalled stream that arrives after Claude has completed a block of text or a tool call, or has started one after finishing its thinking, but before it finishes the response. Claude Code could execute the same tool calls twice if it re-ran the request, so it keeps what Claude completed and shows an [incomplete-response notice](#the-response-above-may-be-incomplete). Claude Code still runs any tool calls Claude completed and continues the turn from their results. Before v2.1.199, Claude Code discarded the partial output and reported the whole turn as an error when a server error arrived mid-stream.
* A failure that arrives after Claude has finished the response: nothing needs retrying, so Claude Code keeps the complete response and ends the turn normally.
* An [Amazon Bedrock streaming response with an unexpected content-type](#bedrock-streaming-response-has-an-unexpected-content-type), because the gateway or proxy rewriting the response would rewrite the retry the same way. Requires Claude Code v2.1.208 or later.
### What you see while Claude Code retries or waits
While retrying, the spinner shows a `Retrying in Ns · attempt x/y` countdown after an error label. The label names the specific reason from the first attempt for failures you can act on right away: the network is down, a TLS handshake failed, or you hit a rate limit. For other errors it reads `API error` at first. As of v2.1.198 it switches to the specific reason from the third attempt, or on the final attempt when `CLAUDE_CODE_MAX_RETRIES` allows fewer than three; earlier versions switch only on the final attempt.
As of v2.1.198, the usual spinner tip is suppressed during retries. Once the error reason is revealed, if the failure is a 529 overload the line below the countdown also names where to check service status: `status.claude.com` on the Anthropic API, or the provider or gateway host named in the message on other configurations.
If no data arrives on the response stream for 20 seconds while a request is still pending, the spinner shows `Waiting for API response · will retry in … · check your network` before any retry has started. The request hasn't failed yet: the countdown runs to the point where Claude Code aborts the stalled connection. After the abort, Claude Code retries the request, ends the turn with an error when the failure isn't retryable or [retries run out](#automatic-retries), shows [The response above may be incomplete](#the-response-above-may-be-incomplete) and continues the turn from the results of any tool calls Claude completed, or ends the turn normally when Claude had already finished the response before the stall. The banner clears on its own once data resumes or a retry succeeds; if it reappears on every attempt, treat it as a [network issue](#unable-to-connect-to-api). Before v2.1.185, the banner appeared after 10 seconds with different wording.
While Claude is consulting the [advisor](/docs/en/advisor), the banner appears after 90 seconds without data instead of 20, because a long advisor review can send nothing for well over 20 seconds. Before v2.1.214, the 20-second threshold applied during advisor calls too, so the banner appeared during advisor reviews even when nothing was wrong.
### Tune retry behavior
You can tune retry behavior with these environment variables:
| Variable | Default | Effect |
| :------------------------------------------- | :------ | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| [`CLAUDE_CODE_MAX_RETRIES`](/docs/en/env-vars) | 10 | Number of retry attempts. Capped at 15 as of v2.1.186; as of v2.1.199 `CLAUDE_CODE_RETRY_WATCHDOG` raises the default and removes the cap. Lower it to surface failures faster in scripts. |
| [`CLAUDE_CODE_RETRY_WATCHDOG`](/docs/en/env-vars) | unset | Set to `1` in unattended sessions such as CI jobs to retry `429` and `529` capacity errors indefinitely instead of failing after `CLAUDE_CODE_MAX_RETRIES` attempts. As of v2.1.199 it also raises the default retry count for other transient errors, such as server errors, timeouts, and dropped connections, to 300, roughly three hours of backoff, and removes the cap of 15 on `CLAUDE_CODE_MAX_RETRIES` if you set that variable explicitly. |
| [`API_TIMEOUT_MS`](/docs/en/env-vars) | 600000 | Per-request timeout in milliseconds. Raise it for slow networks or proxies. |
## Server errors
Most of these errors come from the inference provider: Anthropic's service on the Anthropic API, and the service behind that provider's endpoint on Amazon Bedrock, Google Cloud's Agent Platform, Microsoft Foundry, or a custom gateway. [Auto mode cannot determine the safety of an action](#auto-mode-cannot-determine-the-safety-of-an-action) and [Agent terminated early due to an API error](#agent-terminated-early-due-to-an-api-error) also cover causes on your side, such as an Amazon Bedrock account that can't invoke the classifier model or a subagent that hit a usage limit.
### API Error: 500 Internal server error
Claude Code shows the status code and the API's error message for any 5xx response. The example below shows a 500 response on the Anthropic API:
```text theme={null}
API Error: 500 Internal server error. This is a server-side issue, usually temporary — try again in a moment. If it persists, check https://status.claude.com.
```
The trailing sentence names where to check service health and varies by provider. Amazon Bedrock, Google Cloud's Agent Platform, and Microsoft Foundry configurations name that provider's service status. A custom `ANTHROPIC_BASE_URL` names the gateway host.
This indicates an unexpected failure inside the API. It is not caused by your prompt, settings, or account.
**What to do:**
* Check [status.claude.com](https://status.claude.com), or the provider status page named in the message, for active incidents
* Wait a minute, then send your message again. Your original message is still in the conversation, so for a long prompt you can type `try again` instead of pasting the whole thing.
* If the error persists with no posted incident, run `/feedback` so Anthropic can investigate with your request details. See [Report an error](#report-an-error) if `/feedback` is unavailable in your environment.
### API Error: Repeated 529 Overloaded errors
The API is temporarily at capacity across all users. Claude Code has already retried several times before showing this message:
```text theme={null}
API Error: Repeated 529 Overloaded errors. The API is at capacity — this is usually temporary. Try again in a moment. If it persists, check https://status.claude.com.
```
The trailing sentence varies by provider in the same way as the 500 error above.
A 529 is not your usage limit and doesn't count against your quota.
**What to do:**
* Check [status.claude.com](https://status.claude.com), or the provider status page named in the message, for capacity notices
* Try again in a few minutes
* Run `/model` and switch to a different model to keep working, since capacity is tracked per model. Claude Code prompts you to do this when one model is under particularly high load, for example `Opus is experiencing high load, please use /model to switch to Sonnet`.
### Request timed out
The API didn't respond before the connection deadline.
```text theme={null}
Request timed out
```
This can happen during periods of high load or when the model is generating a very large response. The default request timeout is 10 minutes.
**What to do:**
* Retry the request
* For long-running tasks, break the work into smaller prompts
* If a slow network or proxy is the cause, raise `API_TIMEOUT_MS` as described in [Automatic retries](#automatic-retries)
* If timeouts are frequent and your network is otherwise healthy, see [Network and connection errors](#network-and-connection-errors) below
### The response above may be incomplete
A streaming request failed while the response was still in progress, after Claude had completed a block of text or a tool call, or had started one after finishing its thinking. Re-sending the request could run the same tool calls twice, so Claude Code keeps the output Claude completed and appends this notice instead of discarding the turn. Which variant you see names the cause:
```text theme={null}
API Error: Server error mid-response. The response above may be incomplete.
API Error: Connection lost mid-response. The response above may be incomplete.
API Error: Your computer went to sleep mid-response. The response above may be incomplete.
API Error: The response stopped arriving. The response above may be incomplete.
```
* `Server error mid-response`: a mid-stream overloaded or 5xx server error. This variant requires Claude Code v2.1.199 or later; before then that case discarded the partial output and reported the whole turn as an error.
* `Connection lost mid-response`: the connection dropped.
* `Your computer went to sleep mid-response`: Claude Code detected that your computer went to sleep while the response was streaming. Once your computer wakes, Claude Code treats the connection as broken and stops reading from it.
* `The response stopped arriving`: the connection stayed open but stopped delivering data, so the streaming idle watchdog aborted it. Before v2.1.222, Claude Code could also report this failure on [gateway](/docs/en/gateways) connections reached through `ANTHROPIC_BASE_URL` or `ANTHROPIC_AWS_BASE_URL` while the server's keep-alive pings were still arriving, because it counted only parsed response events there; upgrading stops those spurious timeouts on those routes. Gateways reached through a provider base URL such as `ANTHROPIC_BEDROCK_BASE_URL` aren't wrapped by the byte watchdog; see [Streaming idle watchdogs](/docs/en/network-config#streaming-idle-watchdogs).
Before v2.1.227, `Connection lost mid-response` read `Connection closed mid-response` and `The response stopped arriving` read `Response stalled mid-stream`.
When one of these failures lands at another point in the turn, Claude Code handles it without this notice:
* Earlier in the response, Claude Code either retries the failure or ends the turn with a different error. See [Automatic retries](#automatic-retries).
* When one of these failures arrives after Claude has finished the response, Claude Code keeps the complete response and ends the turn normally, without this notice. Before v2.1.222, Claude Code showed this notice when the connection dropped or stalled after the response finished, and reported the turn as an error even though the response was complete.
**What to do:**
* In an interactive session, read the response that remains on screen: Claude Code keeps every block Claude completed before the error, but discards an interrupted final block when the turn ends, so the final sentences or tool calls may be missing. Reply with `continue` to have Claude pick up from its last completed block.
* In [non-interactive mode](/docs/en/headless) (`-p`):
* With the default text output, Claude Code prints the last completed block of text it still holds from earlier in the turn, followed by this message. When it holds none, Claude Code prints this message alone, for example because Claude Code compacted the conversation mid-turn and cleared that text. Before v2.1.219, Claude Code printed only this message in `-p` text output and dropped the response it had already produced.
* With `--output-format json` or `stream-json`, Claude Code reports this message in the `result` field.
* To continue the turn, resume the session and send `continue` as described in [Continue conversations](/docs/en/headless#continue-conversations).
### Auto mode cannot determine the safety of an action
The model that [auto mode](/docs/en/permission-modes#eliminate-prompts-with-auto-mode) uses to classify actions couldn't produce a decision, so auto mode didn't approve the action automatically. The message you see depends on how the classifier failed.
Reads, searches, and edits inside your working directory skip the classifier, so they keep working in all of these cases.
When the classifier model is unavailable:
```text theme={null}
<model> is temporarily unavailable, so auto mode cannot determine the safety of <tool> right now. Wait a moment and then try this action again.
```
When Claude Code can determine the failure category, it names the category in parentheses after `temporarily unavailable`, for example `<model> is temporarily unavailable (rate-limited), so auto mode cannot determine the safety of <tool> right now`. The categories are `(rate-limited)`, `(overloaded)`, `(server error)`, `(timed out)`, and `(connection failed)`. Rate-limited, overloaded, and server errors are transient, and retrying works. If `(timed out)` or `(connection failed)` repeats, check your connection; see [Unable to connect to API](#unable-to-connect-to-api). Before v2.1.229, the message never named a category and read `Wait briefly and then try this action again`.
Cut at 300 lines. The page has the rest.
fast-mode First recorded · 196 lines, first recorded
# Speed up responses with fast mode ## Toggle fast mode ### Switch models while fast mode is on ## Understand the cost tradeoff ### See where fast mode spend appears ## Decide when to use fast mode ### Fast mode vs effort level ## Requirements ### Enable fast mode for your organization ### Use fast mode behind proxies and LLM gateways ### Require per-session opt-in ## Handle rate limits ## Research preview ## See also
The first capture of this source. The page was already there, and this is what it said.
# Speed up responses with fast mode
> Get faster Opus responses in Claude Code by toggling fast mode.
<Note>
Fast mode is in [research preview](#research-preview). The feature, pricing, and availability may change based on feedback.
</Note>
Fast mode is a high-speed configuration for Claude Opus, making the model up to 2.5x faster at a higher cost per token. Toggle it on with `/fast` when you need speed for interactive work like rapid iteration or live debugging, and toggle it off when cost matters more than latency.
Fast mode is not a different model. It uses Claude Opus with a different API configuration that prioritizes speed over cost efficiency. You get identical quality and capabilities with faster responses. Fast mode is supported on Opus 5 and Opus 4.8. It is not available on Sonnet, Haiku, or other models.
Claude Code treats Opus 4.7 like any other model without fast mode support: switching to it turns fast mode off. Fast mode for Opus 4.7 was deprecated on June 25, 2026, and removed on July 24, 2026.
What to know:
* Use `/fast` to toggle on fast mode in the Claude Code CLI. Fast mode is not supported in the VS Code extension.
* Fast mode pricing per MTok input/output is \$10/\$50 on Opus 5 and Opus 4.8.
* Available to Claude Code users on subscription plans (Pro/Max/Team/Enterprise) and on Claude Console. Team and Enterprise organizations need an Owner to enable it first, and Console organizations need access provisioned first, both described under [Requirements](#requirements).
* For Claude Code users on subscription plans (Pro/Max/Team/Enterprise), fast mode is available via usage credits only and not included in the subscription rate limits.
## Toggle fast mode
Toggle fast mode in either of these ways:
* Type `/fast` and press Tab to toggle on or off
* Set `"fastMode": true` in your [user settings file](/docs/en/settings)
By default, fast mode you turn on in an interactive session persists across sessions. In [non-interactive mode](/docs/en/headless), with the `-p` flag, `/fast` works only in a session launched with fast mode in its [`--settings`](/docs/en/cli-reference#cli-flags) value, for example `claude -p --settings '{"fastMode": true}'`; the toggle then applies to that session only and isn't saved as your default, and in any other non-interactive session the command reports that fast mode isn't available. You can configure fast mode to reset each session. See [require per-session opt-in](#require-per-session-opt-in) for details.
For the best cost efficiency, enable fast mode at the start of a session rather than switching mid-conversation. See [understand the cost tradeoff](#understand-the-cost-tradeoff) for details.
When you enable fast mode:
* If you're on a different model, Claude Code automatically switches to Opus
* You'll see a confirmation message: "Fast mode ON"
* A small `↯` icon appears next to the prompt while fast mode is active
* Run `/fast` again at any time to check whether fast mode is on or off
Opus 5 is the fast mode default in Claude Code v2.1.219 and later. Before v2.1.219, fast mode defaulted to Opus 4.8 on v2.1.154 through v2.1.218, and to Opus 4.7 on v2.1.142 through v2.1.153.
When you disable fast mode with `/fast` again, you remain on Opus. To switch to a different model, use `/model`.
### Switch models while fast mode is on
Fast mode follows your model switches in both directions:
* **Switch away**: when you switch to a model that doesn't support fast mode, Claude Code turns fast mode off. This includes Opus 4.7; before v2.1.221, fast mode stayed on after a switch to Opus 4.7 and the API rejected the requests.
* **Switch back**: switching back to a supported Opus model turns fast mode on again when your saved fast mode preference is on, the same preference a new session starts from by default. A model switch never turns fast mode on for a session whose saved preference is off, and with [per-session opt-in](#require-per-session-opt-in) configured, switching back doesn't turn it on either; run `/fast` to re-enable it.
Whenever a model switch turns fast mode on or off, Claude Code shows a `Fast mode ON` or `Fast mode OFF` confirmation, and the `↯` icon appears while fast mode is on. This holds whether you switch with `/model`, with [`/config model=<model>`](/docs/en/settings), or from a device connected through [Remote Control](/docs/en/remote-control).
Claude Code resends the session's fast mode status to devices connected through Remote Control after a model switch, a reconnection, or a failed [availability check](#use-fast-mode-behind-proxies-and-llm-gateways).
## Understand the cost tradeoff
Fast mode has higher per-token pricing than standard Opus:
| Model | Input (MTok) | Output (MTok) |
| -------- | ------------ | ------------- |
| Opus 5 | \$10 | \$50 |
| Opus 4.8 | \$10 | \$50 |
Fast mode pricing is flat across the full 1M token context window. For the standard Opus rate to compare against, see the [Claude pricing reference](https://platform.claude.com/docs/en/about-claude/pricing).
The first time you enable fast mode in a conversation, you pay the full fast mode uncached input token price for the entire conversation context. The deeper into a conversation you are, the more this costs, so enabling fast mode from the start is cheaper. The cost applies once per conversation, so toggling fast mode off and on again later does not repeat it. For the mechanism, see [how fast mode interacts with the prompt cache](/docs/en/prompt-caching#turning-on-fast-mode).
### See where fast mode spend appears
You see fast mode spend in a different place depending on how you signed in, so first run [`/status`](/docs/en/commands) to check. If it shows a `Login method` row such as `Claude Max account`, you signed in with a Claude subscription. If it shows an `API key` row instead, your requests bill to a Claude Console organization.
* **Pro and Max**: you pay for fast mode from your usage credits. Go to [**Settings > Usage**](https://claude.ai/settings/usage) on claude.ai, where the **Usage credits** section shows how much you've spent in usage credits this month. That figure includes fast mode but doesn't break it out separately.
* **Team and Enterprise**: your organization pays for your fast mode usage from its usage credits. For where your organization sees that spend, see [Claude for Teams and Enterprise](/docs/en/costs#claude-for-teams-and-enterprise).
* **Claude Console**: your organization pays for fast mode with the rest of its API usage. On the Console [Usage](https://platform.claude.com/usage) and [Cost](https://platform.claude.com/cost) pages, select **Speed (Research Preview)** in the **Group by** menu to separate fast mode from standard-speed usage. You see that option only when the selected date range includes fast mode usage.
## Decide when to use fast mode
Fast mode is best for interactive work where response latency matters more than cost:
* Rapid iteration on code changes
* Live debugging sessions
* Time-sensitive work with tight deadlines
Standard mode is better for:
* Long autonomous tasks where speed matters less
* Batch processing or CI/CD pipelines
* Cost-sensitive workloads
### Fast mode vs effort level
Fast mode and effort level both affect response speed, but differently:
| Setting | Effect |
| ---------------------- | -------------------------------------------------------------------------------- |
| **Fast mode** | Same model quality, lower latency, higher cost |
| **Lower effort level** | Less thinking time, faster responses, potentially lower quality on complex tasks |
You can combine both: use fast mode with a lower [effort level](/docs/en/model-config#adjust-effort-level) for maximum speed on straightforward tasks.
## Requirements
Fast mode requires all of the following:
* **Anthropic API or subscription only**: fast mode is available through the Anthropic Console API and for Claude subscription plans using usage credits. It is not available on Amazon Bedrock, Google Cloud's Agent Platform, Microsoft Foundry, or Claude Platform on AWS. Console organizations must also have [fast mode access provisioned](#enable-fast-mode-for-your-organization).
* **Usage credits turned on for subscription plans**: on a Pro, Max, Team, or Enterprise plan, your account must have [usage credits](/docs/en/costs#add-usage-credits-to-your-subscription) turned on, which allows billing beyond your plan's included usage. Until they're on, `/fast` shows "Fast mode requires usage credits · /usage-credits to turn them on". On Pro and Max, turn them on in the **Usage credits** section of [**Settings > Usage**](https://claude.ai/settings/usage) on claude.ai, or run `/usage-credits` to open that page. On Team and Enterprise, a member with billing access turns them on for the organization at [**Admin settings > Usage**](https://claude.ai/admin-settings/usage), and a member without it runs `/usage-credits` to send the organization's admins a request.
<Note>
Fast mode usage draws directly from usage credits, even if you have remaining usage on your plan.
</Note>
* **Paid Console organization**: Claude Console accounts don't use usage credits, and your organization pays for fast mode per token with the rest of its API usage. On the Console's free Evaluation plan, `/fast` shows "Fast mode unavailable during evaluation. Please purchase credits." To clear it, purchase credits in your [Console billing settings](https://platform.claude.com/settings/billing).
* **Owner enablement for Team and Enterprise**: fast mode is disabled by default for Team and Enterprise organizations. An Owner must explicitly [enable fast mode](#enable-fast-mode-for-your-organization) before users can access it.
<Note>
If fast mode has not been enabled for your organization, the `/fast` command will show "Fast mode has been disabled by your organization." If your organization's [`availableModels`](/docs/en/model-config#restrict-model-selection) allowlist excludes the fast-mode Opus model, `/fast` is refused with "is not in your organization's allowed models". The exception is a session already running on an allowed Opus model that supports fast mode: `/fast` enables fast mode on your current model instead of switching models.
</Note>
### Enable fast mode for your organization
Where you enable fast mode depends on which product your organization uses:
* **Console** (API customers): an admin enables it in [Claude Code preferences](https://platform.claude.com/claude-code/preferences). Fast mode is in [research preview](#research-preview), so your organization must also have fast mode access provisioned before fast mode requests succeed. To get access, contact your account manager or join the waitlist, as described in [fast mode on the Claude API](https://platform.claude.com/docs/en/build-with-claude/fast-mode).
Without provisioned access, the API rejects each fast mode request with a 429, and Claude Code treats each rejection as a [fast mode rate limit](#handle-rate-limits). Unlike a rate limit's cooldown, the rejections continue until access is provisioned.
* **Claude AI** (Team and Enterprise): an Owner enables it at [Admin Settings > Claude Code](https://claude.ai/admin-settings/claude-code)
Another option to disable fast mode entirely is to set `CLAUDE_CODE_DISABLE_FAST_MODE=1`. See [Environment variables](/docs/en/env-vars).
### Use fast mode behind proxies and LLM gateways
Before offering fast mode, Claude Code checks your organization's fast mode availability with a request directly to `api.anthropic.com`. The check doesn't follow [`ANTHROPIC_BASE_URL`](/docs/en/llm-gateway-connect#set-the-base-url-and-credential), so on a network that routes Claude traffic through an [LLM gateway](/docs/en/llm-gateway) and blocks direct egress to `api.anthropic.com`, the check fails even though inference requests work. The check does use a configured [HTTP proxy](/docs/en/network-config#proxy-configuration), so a network block fails the check only where `api.anthropic.com` is unreachable even through the proxy.
When the check fails, `/fast` reports "Fast mode unavailable due to network connectivity issues", and requests run at standard speed, even when your organization has fast mode enabled. A check that succeeded in the past keeps working from its cached result, so a blocked check mostly affects new installations.
The same connectivity message appears on an open network when the check reaches `api.anthropic.com` but presents a credential Anthropic rejects. A session whose resolved key is a gateway-issued credential, held in [`ANTHROPIC_API_KEY`](/docs/en/llm-gateway-connect#set-the-base-url-and-credential) or produced by an [`apiKeyHelper`](/docs/en/settings#available-settings), sends the check with that key, and the rejected request is reported as a connectivity failure.
To restore fast mode, allowlist direct egress to `api.anthropic.com` where a network block is the cause, or set whichever variable matches how the check fails:
* `CLAUDE_CODE_SKIP_FAST_MODE_NETWORK_ERRORS=1` treats a failed check as available and still honors a "disabled by your organization" response. Use it when your network refuses the connection, or when Anthropic rejects a gateway credential; allowlisting doesn't help the credential case, since nothing is blocked.
* `CLAUDE_CODE_SKIP_FAST_MODE_ORG_CHECK=1` skips the check entirely. Use it when your network intercepts the request rather than refusing it.
Two gateway configurations report "Fast mode has been disabled by your organization" rather than the connectivity message, even when your organization has fast mode enabled:
* A session that authenticates with [`ANTHROPIC_AUTH_TOKEN`](/docs/en/llm-gateway-connect#set-the-base-url-and-credential) alone skips the check: without a claude.ai login or an Anthropic API key, and without a cached successful check, Claude Code treats fast mode as disabled by your organization without sending the request.
* A proxy that intercepts the check and answers with its own page, for example a TLS-inspecting proxy returning an HTTP 200 block page, is read as a response saying your organization has fast mode disabled.
In both cases, set `CLAUDE_CODE_SKIP_FAST_MODE_ORG_CHECK=1` to restore fast mode. `CLAUDE_CODE_SKIP_FAST_MODE_NETWORK_ERRORS` doesn't apply to either case, since it only bypasses failed checks and both of these produce a disabled response instead. Allowlisting direct egress doesn't help the bearer-token case, which never sends the request.
The variables affect only the client-side check. When your organization has fast mode disabled, the API rejects fast mode requests whether or not they're set.
Setting `CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC` also suppresses the availability check. Without a previously cached successful check, `/fast` reports "Fast mode is currently unavailable"; both skip variables restore fast mode in that configuration too.
### Require per-session opt-in
By default, fast mode a user turns on in an interactive session persists across sessions. To change this, set `fastModePerSessionOptIn` to `true` in any [settings file](/docs/en/settings#settings-files), which causes each session to start with fast mode off and requires users to explicitly enable it with `/fast`. Owners on [Team](https://claude.com/pricing?utm_source=claude_code\&utm_medium=docs\&utm_content=fast_mode_teams#team-&-enterprise) or [Enterprise](https://anthropic.com/contact-sales?utm_source=claude_code\&utm_medium=docs\&utm_content=fast_mode_enterprise) plans can deploy it organization-wide through [server-managed settings](/docs/en/server-managed-settings).
```json theme={null}
{
"fastModePerSessionOptIn": true
}
```
This is useful for controlling costs in organizations where users run multiple concurrent sessions. The user's fast mode preference is still saved, so removing this setting restores the default persistent behavior.
## Handle rate limits
Fast mode has separate rate limits from standard Opus. All supported Opus models share one fast mode rate limit pool: usage on any of them draws from the same limits. When you hit the fast mode rate limit:
1. Fast mode automatically falls back to standard speed
2. The `↯` icon turns gray to indicate cooldown
3. You continue working at standard speed and pricing
4. When the cooldown expires, fast mode automatically re-enables
To disable fast mode manually instead of waiting for cooldown, run `/fast` again.
If you run out of usage credits mid-session, Claude Code retries each rejected fast mode request at standard speed and pricing, so you keep working, and there is no cooldown. How you see the rejection depends on the session type:
* In an interactive session, Claude Code shows a "Fast mode disabled · usage credits exhausted" notification and turns fast mode off for the rest of the session. Your saved fast mode preference doesn't change; run `/fast` to turn fast mode back on.
* In [non-interactive mode](/docs/en/headless) with `--output-format stream-json`, and through the Agent SDK, Claude Code emits the same text on the message stream as a `system` message with subtype `notification`, once per turn while you're out of usage credits. Fast mode stays on.
## Research preview
Fast mode is a research preview feature. This means:
* The feature may change based on feedback
* Availability and pricing are subject to change
* The underlying API configuration may evolve
Report issues or feedback through your usual Anthropic support channels.
## See also
* [Model configuration](/docs/en/model-config): switch models and adjust effort levels
* [Manage costs effectively](/docs/en/costs): track token usage and reduce costs
* [Status line configuration](/docs/en/statusline): display model and context information
feature-availability First recorded · 329 lines, first recorded
# Feature availability ## Availability by model provider ### Features available on every provider ### Features that require a Claude subscription ### CLI capabilities that vary by provider ### Admin and analytics ### Summary by provider ## Availability by subscription plan ## Model availability ## Related resources
The first capture of this source. The page was already there, and this is what it said.
# Feature availability
> Compare which Claude Code features are available across Anthropic subscription plans, the Anthropic Console, Amazon Bedrock, Claude Platform on AWS, Google Cloud's Agent Platform, and Microsoft Foundry.
The Claude Code CLI and everything that runs locally work on every provider. For setup instructions per provider, see the [Enterprise deployment overview](/docs/en/third-party-integrations). To skip straight to what is missing on your provider, see the [summary by provider](#summary-by-provider) tabs.
In the tables below, ✓ means available, ✗ means not available, and "See note" links to a footnote for partial support. A qualifier after ✓ narrows availability to that subset, and "Admin-enabled" means the feature is off until an organization admin turns it on.
## Availability by model provider
How you authenticate determines which features Claude Code can reach. For a single list of what is missing on your provider, see the [summary by provider](#summary-by-provider) tabs. To find your column in the tables:
* **Claude subscription**: you sign in with a claude.ai account on the Pro, Max, Team, or Enterprise plan
* **Anthropic Console**: you authenticate with an Anthropic API key
* **Amazon Bedrock**: you use Claude models from the Amazon Bedrock model catalog and set `CLAUDE_CODE_USE_BEDROCK`. The [Mantle endpoint](/docs/en/amazon-bedrock#use-the-mantle-endpoint) (`CLAUDE_CODE_USE_MANTLE`) is covered by this column
* **Claude Platform on AWS**: you bought Claude through AWS Marketplace but call the Anthropic API, and set `CLAUDE_CODE_USE_ANTHROPIC_AWS`
* **Google Cloud's Agent Platform**: Google-operated; you set `CLAUDE_CODE_USE_VERTEX`
* **Microsoft Foundry**: Anthropic-operated; you set `CLAUDE_CODE_USE_FOUNDRY`
### Features available on every provider
These work on every provider:
* [CLI](/docs/en/quickstart) and [Agent SDK](/docs/en/agent-sdk/overview)
* [VS Code](/docs/en/vs-code) and [JetBrains](/docs/en/jetbrains) extensions
* [Subagents](/docs/en/sub-agents), [hooks](/docs/en/hooks-guide), [commands](/docs/en/commands), and [skills](/docs/en/skills)
* [CLAUDE.md memory](/docs/en/memory), [plugins](/docs/en/plugins), and [MCP servers](/docs/en/mcp)
* [Checkpoints](/docs/en/checkpointing), [sandboxing](/docs/en/sandboxing), and [Workflows](/docs/en/workflows)
* [OpenTelemetry metrics](/docs/en/monitoring-usage) and the [managed settings file](/docs/en/settings#settings-files)
Three of these have provider-specific differences:
* **MCP servers**: [connectors from claude.ai](/docs/en/mcp#use-mcp-servers-from-claude-ai) load only when your claude.ai subscription is the active authentication method. [Tool search](/docs/en/mcp#configure-tool-search) is off by default when `ANTHROPIC_BASE_URL` points to a non-first-party host, and isn't supported on Google Cloud's Agent Platform models earlier than the Claude 4.5 generation or on Microsoft Foundry [deployments hosted on Azure](https://platform.claude.com/docs/en/build-with-claude/claude-in-microsoft-foundry#hosting-options)
* **Subagents**: the built-in [Explore subagent](/docs/en/sub-agents#built-in-subagents) caps its inherited model at Opus on the Claude API, and inherits the main conversation's model directly on any other provider, including Claude Platform on AWS
* **[Commands](/docs/en/commands#all-commands)**: `/design-sync`, `/import` with its `claude import` subcommand form, and `/radio` are unavailable on Amazon Bedrock, Google Cloud's Agent Platform, Microsoft Foundry, and Claude Platform on AWS, `/voice` requires a claude.ai account, and `/list-agents` and its alias `/peers` are available only in sessions where [cross-session messaging is enabled](/docs/en/cross-session-messaging#availability)
### Features that require a Claude subscription
These require signing in with a claude.ai account and are not reachable with an Anthropic Console API key or from a third-party provider:
* [Claude Code on the web](/docs/en/claude-code-on-the-web), Claude Code on mobile, and [Claude Code in Slack](/docs/en/slack)
* [Claude Code Desktop](/docs/en/desktop)
* [Routines](/docs/en/routines) (`/schedule`)
* [Ultrareview](/docs/en/ultrareview)
* [Code Review](/docs/en/code-review): Team and Enterprise plans
* [Remote Control](/docs/en/remote-control)
* [Chrome extension](/docs/en/chrome)
* [Computer use](/docs/en/computer-use): Pro and Max plans
* [Artifacts](/docs/en/artifacts): Pro, Max, Team, and Enterprise plans
* [Voice dictation](/docs/en/voice-dictation)
Desktop is the partial exception: [gateway routing can be configured in the app or by an administrator](/docs/en/llm-gateway-connect#desktop-app), Enterprise deployments can route Desktop to Google Cloud's Agent Platform or a gateway provider via [managed settings](https://claude.com/docs/third-party/claude-desktop/configuration), and [Claude Desktop on 3P](https://claude.com/docs/third-party/claude-desktop/overview) runs the Code tab on Amazon Bedrock, Google Cloud's Agent Platform, Microsoft Foundry, or a self-hosted LLM gateway. For per-plan availability of these features, see [Availability by subscription plan](#availability-by-subscription-plan).
### CLI capabilities that vary by provider
These features work in the local CLI but depend on a server-side capability that not every provider exposes.
<table>
<thead>
<tr>
<th>Feature</th>
<th>Claude subscription</th>
<th>Anthropic Console</th>
<th>Amazon Bedrock</th>
<th>Claude Platform on AWS</th>
<th>Google Cloud's Agent Platform</th>
<th>Microsoft Foundry</th>
</tr>
</thead>
<tbody>
<tr>
<td>[Web search](/docs/en/tools-reference#websearch-tool-behavior)</td>
<td>✓</td>
<td>✓</td>
<td>✗</td>
<td>✓</td>
<td>See note <sup><a href="#fn1">1</a></sup></td>
<td>✓ ([deployments hosted on Anthropic](https://platform.claude.com/docs/en/build-with-claude/claude-in-microsoft-foundry#hosting-options))</td>
</tr>
<tr>
<td>[Fast mode](/docs/en/fast-mode)</td>
<td>✓ ([Owner-enabled](/docs/en/fast-mode#enable-fast-mode-for-your-organization) on Team and Enterprise)</td>
<td>✓ (provisioned organizations)</td>
<td>✗</td>
<td>✗</td>
<td>✗</td>
<td>✗</td>
</tr>
<tr>
<td>[Auto mode](/docs/en/auto-mode-config)</td>
<td>✓</td>
<td>✓</td>
<td>See note <sup><a href="#fn2">2</a></sup></td>
<td>✓</td>
<td>See note <sup><a href="#fn2">2</a></sup></td>
<td>See note <sup><a href="#fn2">2</a></sup></td>
</tr>
<tr>
<td>[Advisor](/docs/en/advisor)</td>
<td>✓</td>
<td>✓</td>
<td>✗</td>
<td>✗</td>
<td>✗</td>
<td>✗</td>
</tr>
<tr>
<td>[Cross-session messaging](/docs/en/cross-session-messaging)</td>
<td>✓ (macOS and Linux) <sup><a href="#fn6">6</a></sup></td>
<td>✓ (macOS and Linux) <sup><a href="#fn6">6</a></sup></td>
<td>✗</td>
<td>✗</td>
<td>✗</td>
<td>✗</td>
</tr>
<tr>
<td>[Channels](/docs/en/channels)</td>
<td>✓</td>
<td>✓</td>
<td>✗</td>
<td>✗</td>
<td>✗</td>
<td>✗</td>
</tr>
<tr>
<td>[`/loop` scheduled tasks](/docs/en/scheduled-tasks)</td>
<td>✓</td>
<td>✓</td>
<td>See note <sup><a href="#fn3">3</a></sup></td>
<td>See note <sup><a href="#fn3">3</a></sup></td>
<td>See note <sup><a href="#fn3">3</a></sup></td>
<td>See note <sup><a href="#fn3">3</a></sup></td>
</tr>
<tr>
<td>[GitHub Actions](/docs/en/github-actions)</td>
<td>✓</td>
<td>✓</td>
<td>✓</td>
<td>✗</td>
<td>✓</td>
<td>✓</td>
</tr>
<tr>
<td>[GitLab CI/CD](/docs/en/gitlab-ci-cd)</td>
<td>✓</td>
<td>✓</td>
<td>✓</td>
<td>✓</td>
<td>✓</td>
<td>✗</td>
</tr>
</tbody>
</table>
### Admin and analytics
Organization-level controls and usage visibility.
<table>
<thead>
<tr>
<th>Feature</th>
<th>Claude subscription</th>
<th>Anthropic Console</th>
<th>Amazon Bedrock</th>
<th>Claude Platform on AWS</th>
<th>Google Cloud's Agent Platform</th>
<th>Microsoft Foundry</th>
</tr>
</thead>
<tbody>
<tr>
<td>[Analytics dashboard and API](/docs/en/analytics)</td>
<td>✓ (dashboard: Team and Enterprise; API: Enterprise)</td>
<td>✓ <sup><a href="#fn5">5</a></sup></td>
<td>✗</td>
<td>✗</td>
<td>✗</td>
<td>✗</td>
</tr>
<tr>
<td>[Server-managed settings](/docs/en/server-managed-settings)</td>
<td>✓ (Team and Enterprise)</td>
<td>✓ (Team and Enterprise)</td>
<td>✗</td>
<td>✗</td>
<td>✗</td>
<td>✗</td>
</tr>
<tr>
<td>[Zero Data Retention](/docs/en/zero-data-retention)</td>
<td>✓ (qualified Enterprise accounts)</td>
<td>✓ (qualified accounts)</td>
<td>See note <sup><a href="#fn4">4</a></sup></td>
<td>✓ (qualified accounts)</td>
<td>See note <sup><a href="#fn4">4</a></sup></td>
<td>See note <sup><a href="#fn4">4</a></sup></td>
</tr>
</tbody>
</table>
<span id="fn1" style={{display: 'block', position: 'relative', top: '-120px'}} /><sup>1</sup> On Google Cloud's Agent Platform, web search is available for Claude 4 models and later.<br />
<span id="fn2" style={{display: 'block', position: 'relative', top: '-120px'}} /><sup>2</sup> On these providers, auto mode supports only Claude Sonnet 5, Opus 4.7 or later, and Fable 5. See [Auto mode configuration](/docs/en/auto-mode-config). In v2.1.158 through v2.1.206, auto mode on these providers also required setting `CLAUDE_CODE_ENABLE_AUTO_MODE=1`; v2.1.207 removed the requirement.<br />
<span id="fn3" style={{display: 'block', position: 'relative', top: '-120px'}} /><sup>3</sup> Explicit intervals such as `/loop every 2 hours` work on every provider. On Amazon Bedrock, Claude Platform on AWS, Google Cloud's Agent Platform, and Microsoft Foundry, `/loop` cannot pick its own interval or supply the default maintenance prompt, so a prompt with no interval runs every 10 minutes, and `/loop` with no arguments shows the usage message. See [Scheduled tasks](/docs/en/scheduled-tasks).<br />
<span id="fn4" style={{display: 'block', position: 'relative', top: '-120px'}} /><sup>4</sup> Subject to your agreement with the cloud provider.<br />
<span id="fn5" style={{display: 'block', position: 'relative', top: '-120px'}} /><sup>5</sup> Dashboard and API only. [Contribution metrics](/docs/en/analytics#enable-contribution-metrics) requires a claude.ai Team or Enterprise organization.<br />
<span id="fn6" style={{display: 'block', position: 'relative', top: '-120px'}} /><sup>6</sup> Requires Claude Code v2.1.224 or later. WSL 2 counts as Linux; native Windows isn't supported. With API key authentication, same-machine messaging only. Claude can find your [Claude Code on the web](/docs/en/claude-code-on-the-web) sessions and your sessions on other machines only from a session that is connected to [Remote Control](/docs/en/remote-control). Connecting needs a claude.ai sign-in and the other [Remote Control requirements](/docs/en/remote-control#requirements). See [Message sessions on other machines](/docs/en/cross-session-messaging#message-sessions-on-other-machines).
<Note>
If you authenticate through an [LLM gateway](/docs/en/llm-gateway), feature availability matches the underlying provider the gateway forwards to. Some Anthropic-only features such as the [Advisor](/docs/en/advisor) work only if the gateway forwards requests intact to the Anthropic API.
</Note>
### Summary by provider
Each tab lists what is unavailable or partially supported on that provider, with alternatives where one exists. Everything not listed works the same as on a Claude subscription, apart from the [provider-specific differences](#features-available-on-every-provider) noted above. On Amazon Bedrock, Google Cloud's Agent Platform, Microsoft Foundry, and Claude Platform on AWS, error reporting and telemetry to Anthropic are off by default. See [default behaviors by API provider](/docs/en/data-usage#default-behaviors-by-api-provider) for what traffic still reaches Anthropic and how to opt out.
<Tabs>
<Tab title="Amazon Bedrock">
**Not available:** all [features that require a Claude subscription](#features-that-require-a-claude-subscription), plus [web search](/docs/en/tools-reference#websearch-tool-behavior), [fast mode](/docs/en/fast-mode), [Advisor](/docs/en/advisor), [Channels](/docs/en/channels), [cross-session messaging](/docs/en/cross-session-messaging), the [analytics dashboard](/docs/en/analytics), [server-managed settings](/docs/en/server-managed-settings), and the [`/design-sync`, `/import`, and `/radio` commands](/docs/en/commands#all-commands).
**Partial support:**
* [Desktop](/docs/en/desktop): only via [Claude Desktop on 3P](https://claude.com/docs/third-party/claude-desktop/overview)
* [Auto mode](/docs/en/auto-mode-config): Sonnet 5, Opus 4.7 or later, and Fable 5 only
* [`/loop`](/docs/en/scheduled-tasks): explicit intervals only
* [`/code-review`](/docs/en/code-review#let-claude-start-the-review): runs when you type it; Claude doesn't start it on its own
* [Zero Data Retention](/docs/en/zero-data-retention): subject to your AWS agreement
**Alternatives:** for scheduling, use [`/loop`](/docs/en/scheduled-tasks) with an explicit interval instead of `/schedule`. For cloud sessions, use [GitHub Actions](/docs/en/github-actions) or [GitLab CI/CD](/docs/en/gitlab-ci-cd). For web lookups, use the [WebFetch tool](/docs/en/tools-reference#webfetch-tool-behavior) with a specific URL.
</Tab>
<Tab title="Claude Platform on AWS">
**Not available:** all [features that require a Claude subscription](#features-that-require-a-claude-subscription), plus [fast mode](/docs/en/fast-mode), [Advisor](/docs/en/advisor), [Channels](/docs/en/channels), [cross-session messaging](/docs/en/cross-session-messaging), [GitHub Actions](/docs/en/github-actions), the [analytics dashboard](/docs/en/analytics), [server-managed settings](/docs/en/server-managed-settings), and the [`/design-sync`, `/import`, and `/radio` commands](/docs/en/commands#all-commands).
**Available where Amazon Bedrock is not:** [web search](/docs/en/tools-reference#websearch-tool-behavior).
**Partial support:**
* [`/loop`](/docs/en/scheduled-tasks): explicit intervals only
* [`/code-review`](/docs/en/code-review#let-claude-start-the-review): runs when you type it; Claude doesn't start it on its own
**Alternatives:** for scheduling, use [`/loop`](/docs/en/scheduled-tasks) with an explicit interval instead of `/schedule`. For cloud sessions, use [GitLab CI/CD](/docs/en/gitlab-ci-cd).
</Tab>
<Tab title="Google Cloud's Agent Platform">
**Not available:** all [features that require a Claude subscription](#features-that-require-a-claude-subscription), plus [fast mode](/docs/en/fast-mode), [Advisor](/docs/en/advisor), [Channels](/docs/en/channels), [cross-session messaging](/docs/en/cross-session-messaging), the [analytics dashboard](/docs/en/analytics), [server-managed settings](/docs/en/server-managed-settings), and the [`/design-sync`, `/import`, and `/radio` commands](/docs/en/commands#all-commands).
**Partial support:**
* [Desktop](/docs/en/desktop): via [managed settings](https://claude.com/docs/third-party/claude-desktop/configuration) or [Claude Desktop on 3P](https://claude.com/docs/third-party/claude-desktop/overview)
* [Web search](/docs/en/tools-reference#websearch-tool-behavior): Claude 4 models and later
* [Auto mode](/docs/en/auto-mode-config): Sonnet 5, Opus 4.7 or later, and Fable 5 only
* [`/loop`](/docs/en/scheduled-tasks): explicit intervals only
* [`/code-review`](/docs/en/code-review#let-claude-start-the-review): runs when you type it; Claude doesn't start it on its own
* [Zero Data Retention](/docs/en/zero-data-retention): subject to your Google Cloud agreement
**Alternatives:** for scheduling, use [`/loop`](/docs/en/scheduled-tasks) with an explicit interval instead of `/schedule`. For cloud sessions, use [GitHub Actions](/docs/en/github-actions) or [GitLab CI/CD](/docs/en/gitlab-ci-cd).
</Tab>
<Tab title="Microsoft Foundry">
**Not available:** all [features that require a Claude subscription](#features-that-require-a-claude-subscription), plus [fast mode](/docs/en/fast-mode), [Advisor](/docs/en/advisor), [Channels](/docs/en/channels), [cross-session messaging](/docs/en/cross-session-messaging), [GitLab CI/CD](/docs/en/gitlab-ci-cd), the [analytics dashboard](/docs/en/analytics), [server-managed settings](/docs/en/server-managed-settings), and the [`/design-sync`, `/import`, and `/radio` commands](/docs/en/commands#all-commands).
**Partial support:**
* [Desktop](/docs/en/desktop): only via [Claude Desktop on 3P](https://claude.com/docs/third-party/claude-desktop/overview)
* [Web search](/docs/en/tools-reference#websearch-tool-behavior): [deployments hosted on Anthropic](https://platform.claude.com/docs/en/build-with-claude/claude-in-microsoft-foundry#hosting-options) only
* [Auto mode](/docs/en/auto-mode-config): Sonnet 5, Opus 4.7 or later, and Fable 5 only
* [`/loop`](/docs/en/scheduled-tasks): explicit intervals only
* [`/code-review`](/docs/en/code-review#let-claude-start-the-review): runs when you type it; Claude doesn't start it on its own
* [Zero Data Retention](/docs/en/zero-data-retention): subject to your Azure agreement
**Alternatives:** for scheduling, use [`/loop`](/docs/en/scheduled-tasks) with an explicit interval instead of `/schedule`. For cloud sessions, use [GitHub Actions](/docs/en/github-actions).
</Tab>
<Tab title="Anthropic Console">
**Not available:** all [features that require a Claude subscription](#features-that-require-a-claude-subscription).
Everything in [CLI capabilities that vary by provider](#cli-capabilities-that-vary-by-provider) is available, except that [fast mode](/docs/en/fast-mode) requires [provisioned access](/docs/en/fast-mode#enable-fast-mode-for-your-organization). [Server-managed settings](/docs/en/server-managed-settings) are also available when your API key belongs to a Team or Enterprise organization.
</Tab>
</Tabs>
## Availability by subscription plan
If you authenticate through Amazon Bedrock, Google Cloud's Agent Platform, Microsoft Foundry, or an Anthropic Console API key, this section does not apply to you. When you sign in with a claude.ai account, your plan determines which of the features below are available.
| Feature | Pro | Max | Team | Enterprise |
| :-------------------------------------------------------------------------- | :-- | :-- | :------------ | :-------------------------------- |
| [Claude Code on the web](/docs/en/claude-code-on-the-web) | ✓ | ✓ | ✓ | ✓ <sup><a href="#fn7">7</a></sup> |
Cut at 300 lines. The page has the rest.
features-overview First recorded · 342 lines, first recorded
# Extend Claude Code ## Overview ## Match features to your goal ### Build your setup over time ### Compare similar features ### Understand how features layer ### Combine features ## Understand context costs ### Context cost by feature ### Understand how features load ## Learn more
The first capture of this source. The page was already there, and this is what it said.
# Extend Claude Code
> Understand when to use CLAUDE.md, Skills, subagents, hooks, MCP, and plugins.
Claude Code combines a model that reasons about your code with [built-in tools](/docs/en/how-claude-code-works#tools) for file operations, search, execution, and web access. The built-in tools cover most coding tasks. This guide covers the extension layer: features you add to customize what Claude knows, connect it to external services, and automate workflows.
<Note>
For how the core agentic loop works, see [How Claude Code works](/docs/en/how-claude-code-works).
</Note>
**New to Claude Code?** Start with [CLAUDE.md](/docs/en/memory) for project conventions, then add other extensions [as specific triggers come up](#build-your-setup-over-time).
## Overview
Extensions plug into different parts of the agentic loop:
* **[CLAUDE.md](/docs/en/memory)** adds persistent context Claude sees every session
* **[Skills](/docs/en/skills)** add reusable knowledge and invocable workflows
* **[Code intelligence](/docs/en/tools-reference#lsp-tool-behavior)** connects Claude to a language server for symbol-level navigation and live type errors
* **[MCP](/docs/en/mcp)** connects Claude to external services and tools
* **[Subagents](/docs/en/sub-agents)** run their own loops in isolated context, returning summaries
* **[Agent teams](/docs/en/agent-teams)** coordinate multiple independent sessions with shared tasks and peer-to-peer messaging
* **[Hooks](/docs/en/hooks-guide)** run your script, HTTP request, prompt, or subagent when Claude Code reaches a lifecycle event
* **[Plugins](/docs/en/plugins)** and **[marketplaces](/docs/en/plugin-marketplaces)** package and distribute these features
[Skills](/docs/en/skills) are the most flexible extension. A skill is a markdown file containing knowledge, workflows, or instructions. You can invoke skills with a command like `/deploy`, or Claude can load them automatically when relevant. Skills can run in your current conversation or in an isolated context via subagents.
## Match features to your goal
Features range from always-on context that Claude sees every session, to on-demand capabilities you or Claude can invoke, to background automation that runs on specific events. The table below shows what's available and when each one makes sense.
| Feature | What it does | When to use it | Example |
| -------------------------------------------------------------- | ------------------------------------------------------------- | ------------------------------------------------------------------------------- | ------------------------------------------------------------------------------- |
| **CLAUDE.md** | Persistent context loaded every conversation | Project conventions, "always do X" rules | "Use pnpm, not npm. Run tests before committing." |
| **Skill** | Instructions, knowledge, and workflows Claude can use | Reusable content, reference docs, repeatable tasks | `/deploy` runs your deployment checklist; API docs skill with endpoint patterns |
| **Subagent** | Isolated execution context that returns summarized results | Context isolation, parallel tasks, specialized workers | Research task that reads many files but returns only key findings |
| **[Agent teams](/docs/en/agent-teams)** | Coordinate multiple independent Claude Code sessions | Parallel research, new feature development, debugging with competing hypotheses | Spawn reviewers to check security, performance, and tests simultaneously |
| **[Code intelligence](/docs/en/tools-reference#lsp-tool-behavior)** | Language-server navigation and diagnostics | Typed languages, large codebases where grep is slow or imprecise | Jump to a symbol's definition instead of reading the whole file |
| **MCP** | Connect to external services | External data or actions | Query your database, post to Slack, control a browser |
| **Hook** | Script, HTTP request, prompt, or subagent triggered by events | Automation that must run on every matching event | Run ESLint after every file edit |
| **[Artifact](/docs/en/artifacts)** | Publish session output as a private, interactive web page | Output you want to see or share visually rather than as terminal text | An incident timeline that updates as Claude investigates |
**[Plugins](/docs/en/plugins)** are the packaging layer. A plugin bundles skills, hooks, subagents, and MCP servers into a single installable unit. Plugin skills are namespaced (like `/my-plugin:review`) so multiple plugins can coexist. Use plugins when you want to reuse the same setup across multiple repositories or distribute to others via a **[marketplace](/docs/en/plugin-marketplaces)**.
### Build your setup over time
You don't need to configure everything up front. Each feature has a recognizable trigger, and most teams add them in roughly this order:
| Trigger | Add |
| :------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------- |
| Claude gets a convention or command wrong twice | Add it to [CLAUDE.md](/docs/en/memory) |
| You keep typing the same prompt to start a task | Save it as a user-invocable [skill](/docs/en/skills) |
| You paste the same playbook or multi-step procedure into chat for the third time | Capture it as a [skill](/docs/en/skills) |
| You keep copying data from a browser tab Claude can't see | Connect that system as an [MCP server](/docs/en/mcp) |
| Claude reads many files to find where a symbol is defined or used | Install a [code intelligence plugin](/docs/en/discover-plugins#code-intelligence) for your language |
| A side task floods your conversation with output you won't reference again | Route it through a [subagent](/docs/en/sub-agents) |
| You want something to happen every time without asking | Write a [hook](/docs/en/hooks-guide) |
| A second repository needs the same setup | Package it as a [plugin](/docs/en/plugins) |
The same triggers tell you when to update what you already have. A repeated mistake or a recurring review comment is a CLAUDE.md edit, not a one-off correction in chat. A workflow you keep tweaking by hand is a skill that needs another revision.
### Compare similar features
Some features can seem similar. For a deeper walkthrough of choosing between them, see [Steering Claude Code: when to use CLAUDE.md, skills, hooks, and subagents](https://claude.com/blog/steering-claude-code-skills-hooks-rules-subagents-and-more) on the blog. Here's how to tell them apart.
<Tabs>
<Tab title="Skill vs Subagent">
Skills and subagents solve different problems:
* **Skills** are reusable content you can load into any context
* **Subagents** are isolated workers that run separately from your main conversation
| Aspect | Skill | Subagent |
| ----------------------------------------------- | ---------------------------------------------- | ---------------------------------------------------------------- |
| **What it is** | Reusable instructions, knowledge, or workflows | Isolated worker with its own context |
| **Key benefit** | Share content across contexts | Context isolation. Work happens separately, only summary returns |
| **[Context window](/docs/en/context-window) impact** | Adds to your main window | Uses a separate window with its own input and output tokens |
| **Best for** | Reference material, invocable workflows | Tasks that read many files, parallel work, specialized workers |
**Skills can be reference or action.** Reference skills provide knowledge Claude uses throughout your session (like your API style guide). Action skills tell Claude to do something specific (like `/deploy` that runs your deployment workflow).
**Use a subagent** when you need context isolation or when your context window is getting full. The subagent might read dozens of files or run extensive searches, but your main conversation only receives a summary. Since subagent work doesn't consume your main context, this is also useful when you don't need the intermediate work to remain visible. Custom subagents can have their own instructions and can preload skills.
**They can combine.** A subagent can preload specific skills (`skills:` field). A skill can run in isolated context using `context: fork`. See [Skills](/docs/en/skills) for details.
</Tab>
<Tab title="CLAUDE.md vs Skill">
Both store instructions, but they load differently and serve different purposes.
| Aspect | CLAUDE.md | Skill |
| ------------------------- | ---------------------------- | --------------------------------------- |
| **Loads** | Every session, automatically | On demand |
| **Can include files** | Yes, with `@path` imports | Yes, with `@path` imports |
| **Can trigger workflows** | No | Yes, with `/<name>` |
| **Best for** | "Always do X" rules | Reference material, invocable workflows |
**Put it in CLAUDE.md** if Claude should always know it: coding conventions, build commands, project structure, "never do X" rules.
**Put it in a skill** if it's reference material Claude needs sometimes (API docs, style guides) or a workflow you trigger with `/<name>` (deploy, review, release).
**Rule of thumb:** Keep CLAUDE.md under 200 lines. If it's growing, move reference content to skills or split into [`.claude/rules/`](/docs/en/memory#organize-rules-with-claude/rules/) files.
</Tab>
<Tab title="CLAUDE.md vs Rules vs Skills">
All three store instructions, but they load differently:
| Aspect | CLAUDE.md | `.claude/rules/` | Skill |
| ------------ | ----------------------------------- | -------------------------------------------------- | ---------------------------------------- |
| **Loads** | Every session | Every session, or when matching files are opened | On demand, when invoked or relevant |
| **Scope** | Whole project | Can be scoped to file paths | Task-specific |
| **Best for** | Core conventions and build commands | Language-specific or directory-specific guidelines | Reference material, repeatable workflows |
**Use CLAUDE.md** for instructions every session needs: build commands, test conventions, project architecture.
**Use rules** to keep CLAUDE.md focused. Rules with [`paths` frontmatter](/docs/en/memory#path-specific-rules) only load when Claude works with matching files, saving context.
**Use skills** for content Claude only needs sometimes, like API documentation or a deployment checklist you trigger with `/<name>`.
</Tab>
<Tab title="Subagent vs Agent team">
Both parallelize work, but they're architecturally different:
* **Subagents** run inside your session and report results back to your main context
* **Agent teams** are independent Claude Code sessions that communicate with each other
| Aspect | Subagent | Agent team |
| ----------------- | ------------------------------------------------ | --------------------------------------------------- |
| **Context** | Own context window; results return to the caller | Own context window; fully independent |
| **Communication** | Reports results back to the main agent only | Teammates message each other directly |
| **Coordination** | Main agent manages all work | Shared task list with self-coordination |
| **Best for** | Focused tasks where only the result matters | Complex work requiring discussion and collaboration |
| **Token cost** | Lower: results summarized back to main context | Higher: each teammate is a separate Claude instance |
**Use a subagent** when you need a quick, focused worker: research a question, verify a claim, review a file. The subagent does the work and returns a summary. Your main conversation stays clean.
**Use an agent team** when teammates need to share findings, challenge each other, and coordinate independently. Agent teams are best for research with competing hypotheses, parallel code review, and new feature development where each teammate owns a separate piece.
**Transition point:** If you're running parallel subagents but hitting context limits, or if your subagents need to communicate with each other, agent teams are the natural next step. For separate sessions that pass messages to each other without a team, see [cross-session messaging](/docs/en/cross-session-messaging).
<Note>
Agent teams are experimental and disabled by default. See [agent teams](/docs/en/agent-teams) for setup and current limitations.
</Note>
</Tab>
<Tab title="MCP vs Skill">
MCP connects Claude to external services. Skills extend what Claude knows, including how to use those services effectively.
| Aspect | MCP | Skill |
| -------------- | ---------------------------------------------------- | ------------------------------------------------------- |
| **What it is** | Protocol for connecting to external services | Knowledge, workflows, and reference material |
| **Provides** | Tools and data access | Knowledge, workflows, reference material |
| **Examples** | Slack integration, database queries, browser control | Code review checklist, deploy workflow, API style guide |
These solve different problems and work well together:
**MCP** gives Claude purpose-built tools for an external system, with the connection and authentication handled by the server.
**Skills** give Claude knowledge about how to use those tools effectively, plus workflows you can trigger with `/<name>`. A skill might include your team's database schema and query patterns, or a `/post-to-slack` workflow with your team's message formatting rules.
Example: An MCP server connects Claude to your database. A skill teaches Claude your data model, common query patterns, and which tables to use for different tasks.
</Tab>
<Tab title="Hook vs Skill">
Claude Code runs a hook at a lifecycle event; it loads a skill into context for Claude to apply.
| Aspect | Hook | Skill |
| ---------------- | --------------------------------------------------------------------------------- | --------------------------------------------------------------------- |
| **Runs** | A shell command, HTTP request, LLM prompt, or subagent | Instructions Claude reads and follows |
| **Triggered by** | [Lifecycle events](/docs/en/hooks#hook-events) such as `PostToolUse` or `SessionStart` | You typing `/<name>`, or Claude matching the description to your task |
| **Determinism** | Always fires on its event; the trigger is guaranteed | Claude interprets the instructions; outcome can vary |
| **Context cost** | Zero unless the hook returns output | Description loads each session; full content loads when used |
| **Best for** | Linting after edits, blocking unsafe commands, logging, notifications | Workflows that need reasoning, reference material, multi-step tasks |
**Use a hook** when the action must happen the same way every time and doesn't need Claude to think. For example: format on save, reject `rm -rf /`, post a Slack message when a session ends.
**Use a skill** when Claude should decide how to apply the steps, or when the content is knowledge rather than a script. For example: a `/release` checklist, your API style guide, a debugging playbook.
**Put guardrails in hooks.** An instruction like "never edit `.env`" in CLAUDE.md or a skill is a request, not a guarantee. A `PreToolUse` hook that blocks the edit is enforcement. If a rule must hold every time, make it a hook rather than a prompt instruction.
**Hook output lands in context.** A `PostToolUse` hook that runs your linter feeds results back as text Claude reads; a `/fix-lint` skill tells Claude how to resolve them.
</Tab>
</Tabs>
### Understand how features layer
Features can be defined at multiple levels: user-wide, per-project, via plugins, or through managed policies. You can also nest CLAUDE.md files in subdirectories or place skills in specific packages of a monorepo. When the same feature exists at multiple levels, here's how they layer:
* **CLAUDE.md files** are additive: all levels contribute content to Claude's context simultaneously. Files from your working directory and above load at launch; subdirectories load as you work in them. When instructions conflict, Claude uses judgment to reconcile them, with more specific instructions typically taking precedence. See [how CLAUDE.md files load](/docs/en/memory#how-claude-md-files-load).
* **Skills and subagents** override by name: when the same name exists at multiple levels, one definition wins based on priority (managed > user > project for skills; managed > CLI flag > project > user > plugin for subagents). Plugin skills are [namespaced](/docs/en/plugins#add-skills-to-your-plugin) to avoid conflicts. See [skill discovery](/docs/en/skills#where-skills-live) and [subagent scope](/docs/en/sub-agents#choose-the-subagent-scope).
* **MCP servers** override by name: local > project > user. See [MCP scope](/docs/en/mcp#scope-hierarchy-and-precedence).
* **Hooks** merge: all registered hooks fire for their matching events regardless of source. See [hooks](/docs/en/hooks).
### Combine features
Each extension solves a different problem: CLAUDE.md handles always-on context, skills handle on-demand knowledge and workflows, MCP handles external connections, subagents handle isolation, and hooks handle automation. Real setups combine them based on your workflow.
For example, you might use CLAUDE.md for project conventions, a skill for your deployment workflow, MCP to connect to your database, and a hook to run linting after every edit. Each feature handles what it's best at.
| Pattern | How it works | Example |
| ---------------------- | -------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------- |
| **Skill + MCP** | MCP provides the connection; a skill teaches Claude how to use it well | MCP connects to your database, a skill documents your schema and query patterns |
| **Skill + Subagent** | A skill spawns subagents for parallel work | `/audit` skill kicks off security, performance, and style subagents that work in isolated context |
| **CLAUDE.md + Skills** | CLAUDE.md holds always-on rules; skills hold reference material loaded on demand | CLAUDE.md says "follow our API conventions," a skill contains the full API style guide |
| **Hook + MCP** | A hook triggers external actions through MCP | Post-edit hook sends a Slack notification when Claude modifies critical files |
## Understand context costs
Every feature you add consumes some of Claude's context. Too much can fill up your context window, but it can also add noise that makes Claude less effective; skills may not trigger correctly, or Claude may lose track of your conventions. Understanding these trade-offs helps you build an effective setup. For an interactive view of how these features combine in a running session, see [Explore the context window](/docs/en/context-window).
### Context cost by feature
Each feature has a different loading strategy and context cost:
| Feature | When it loads | What loads | Context cost |
| --------------------- | ------------------------------ | --------------------------------------------------- | -------------------------------------------- |
| **CLAUDE.md** | Session start | Full content | Every request |
| **Skills** | Session start + when used | Descriptions at start, full content when used | Low (descriptions every request)\* |
| **MCP servers** | Session start | Tool names; full schemas on demand | Low until a tool is used |
| **Code intelligence** | After file edits and on demand | Diagnostics after edits; symbol locations on lookup | Low; reduces file reads elsewhere |
| **Subagents** | When spawned | Fresh context with specified skills | Isolated from main session |
| **Hooks** | On trigger | Nothing (runs externally) | Zero, unless hook returns additional context |
\*By default, skill descriptions load at session start so Claude can decide when to use them. Set `disable-model-invocation: true` in a skill's frontmatter to hide it from Claude entirely until you invoke it manually. This reduces context cost to zero for skills you only trigger yourself. For a skill you didn't write, set [`skillOverrides`](/docs/en/skills#override-skill-visibility-from-settings) in settings to do the same without editing its file.
### Understand how features load
Each feature loads at different points in your session. The tabs below explain when each one loads and what goes into context.
<img src="https://mintcdn.com/claude-code/ikqp3_70mqIahteV/images/context-loading.svg?fit=max&auto=format&n=ikqp3_70mqIahteV&q=85&s=aab139e750494a237ae2e0c8f9139b0a" className="dark:hidden" alt="Context loading: CLAUDE.md loads at session start and stays in every request. MCP tool names load at start with full schemas deferred until use. Skills load descriptions at start, full content on invocation. Subagents get isolated context. Hooks run externally." width="720" height="382" data-path="images/context-loading.svg" />
<img src="https://mintcdn.com/claude-code/_xqph1dUOslCOwsj/images/context-loading-dark.svg?fit=max&auto=format&n=_xqph1dUOslCOwsj&q=85&s=b274089ef9612d9c760bca9838557626" className="hidden dark:block" alt="Context loading: CLAUDE.md loads at session start and stays in every request. MCP tool names load at start with full schemas deferred until use. Skills load descriptions at start, full content on invocation. Subagents get isolated context. Hooks run externally." width="720" height="382" data-path="images/context-loading-dark.svg" />
<Tabs>
<Tab title="CLAUDE.md">
**When:** Session start
**What loads:** Full content of all CLAUDE.md files (managed, user, and project levels).
**Inheritance:** Claude reads CLAUDE.md files from your working directory up to the root, and discovers nested ones in subdirectories as it accesses those files. See [How CLAUDE.md files load](/docs/en/memory#how-claude-md-files-load) for details.
<Tip>Keep CLAUDE.md under 200 lines. Move reference material to skills, which load on-demand.</Tip>
</Tab>
<Tab title="Skills">
Skills are extra capabilities in Claude's toolkit. They can be reference material (like an API style guide) or invocable workflows you trigger with `/<name>` (like `/deploy`). Claude Code includes [bundled skills](/docs/en/commands) like `/code-review`, `/batch`, and `/debug` that work out of the box. You can also create your own. Claude uses skills when appropriate, or you can invoke one directly.
**When:** Depends on the skill's configuration. By default, descriptions load at session start and full content loads when used. For user-only skills (`disable-model-invocation: true`), nothing loads until you invoke them.
**What loads:** For model-invocable skills, Claude sees names and descriptions in every request. When you invoke a skill with `/<name>` or Claude loads it automatically, the full content loads into your conversation.
**How Claude chooses skills:** Claude matches your task against skill descriptions to decide which are relevant. If descriptions are vague or overlap, Claude may load the wrong skill or miss one that would help. To tell Claude to use a specific skill, invoke it with `/<name>`. Skills with `disable-model-invocation: true` are invisible to Claude until you invoke them.
**Context cost:** Low until used. User-only skills have zero cost until invoked.
**In subagents:** Skills work differently in subagents. Instead of on-demand loading, skills listed in the subagent's `skills` field are fully preloaded into its context at launch. Subagents can still discover and invoke unlisted project, user, and plugin skills through the Skill tool.
<Tip>Use `disable-model-invocation: true` for skills with side effects. This saves context and ensures only you trigger them.</Tip>
</Tab>
<Tab title="MCP servers">
**When:** Session start.
**What loads:** Tool names from connected servers. Full JSON schemas stay deferred until Claude needs a specific tool.
**Context cost:** [Tool search](/docs/en/mcp#scale-with-mcp-tool-search) is on by default, so idle MCP tools consume minimal context.
<Tip>Run `/mcp` to see connection status and token costs per server. Claude Code [reconnects to remote servers automatically](/docs/en/mcp#automatic-reconnection) if they drop, and you can disconnect servers you're not actively using.</Tip>
</Tab>
<Tab title="Code intelligence">
**When:** After file edits, and on demand when Claude navigates code.
**What loads:** Type errors and warnings after each file edit. Definition, reference, and type information when Claude looks up a symbol.
**Context cost:** Low. Symbol lookups often replace broad file reads, so net context use can go down.
<Tip>The LSP tool is inactive until you install a [code intelligence plugin](/docs/en/discover-plugins#code-intelligence) for your language.</Tip>
</Tab>
<Tab title="Subagents">
**When:** On demand, when you or Claude spawns one for a task.
**What loads:** Fresh, isolated context containing:
* The agent's own system prompt, not the full Claude Code system prompt
* Full content of skills listed in the agent's `skills:` field
* CLAUDE.md and git status, except the built-in Explore and Plan agents [omit both](/docs/en/sub-agents#what-loads-at-startup)
* Whatever context the lead agent passes in the prompt
**Context cost:** Isolated from main session. Subagents don't inherit your conversation history or invoked skills.
<Tip>Use subagents for work that doesn't need your full conversation context. Their isolation prevents bloating your main session.</Tip>
</Tab>
<Tab title="Hooks">
**When:** On trigger. Claude Code runs hooks at specific lifecycle events like tool execution, session boundaries, prompt submission, permission requests, and compaction. See [Hooks](/docs/en/hooks) for the full list.
**What loads:** Nothing by default. Hooks execute outside the main conversation.
**Context cost:** Zero, unless the hook returns output that gets added as messages to your conversation.
Cut at 300 lines. The page has the rest.
fullscreen First recorded · 240 lines, first recorded
# Fullscreen rendering ## Enable fullscreen rendering ## What changes ## Use the mouse ## Scroll the conversation ### Auto-follow ### Mouse wheel scrolling ### Scroll in the JetBrains IDE terminal ## Search and review the conversation ## Clear the conversation ## Use with tmux ## Keep native text selection ## Troubleshooting ### Stale or misplaced text on screen ## Research preview
The first capture of this source. The page was already there, and this is what it said.
# Fullscreen rendering
> Enable a smoother, flicker-free rendering mode with mouse support and stable memory usage in long conversations.
<Note>
Fullscreen rendering is a [research preview](#research-preview). If you first used Claude Code on or after May 6, 2026, Claude Code renders fullscreen by default; run `/tui default` to switch back. If you started earlier, you keep the classic renderer; run `/tui fullscreen` to switch in your current conversation. Behavior may change based on feedback.
</Note>
Fullscreen rendering is an alternative rendering path for the Claude Code CLI that eliminates flicker, keeps memory usage flat in long conversations, and adds mouse support. It draws the interface on the terminal's alternate screen buffer, like `vim` or `htop`, and only renders messages that are currently visible. This reduces the amount of data sent to your terminal on each update.
The difference is most noticeable in terminal emulators where rendering throughput is the bottleneck, such as the VS Code integrated terminal, tmux, and iTerm2. If your terminal scroll position jumps to the top while Claude is working, or the screen flashes as tool output streams in, this mode addresses those.
<Note>
The term fullscreen describes how Claude Code takes over the terminal's drawing surface, the way `vim` does. It has nothing to do with maximizing your terminal window, and works at any window size.
</Note>
## Enable fullscreen rendering
Run `/tui fullscreen` inside any Claude Code conversation. The CLI saves the [`tui` setting](/docs/en/settings#available-settings) and relaunches into fullscreen with your conversation intact, so you can switch mid-session without losing context. Run `/tui default` to switch back to the classic renderer, or `/tui` with no argument to print which renderer is active.
In [screen reader mode](/docs/en/accessibility), Claude Code always uses the classic renderer except in attached [background sessions](/docs/en/agent-view), which still render fullscreen. If you run `/tui fullscreen` in any other session, Claude Code prints an explanation instead of switching and doesn't change the saved `tui` setting.
The relaunched session keeps the conversation as it appears on screen. Claude Code also carries over the model you last picked with [`/model`](/docs/en/model-config#setting-your-model). If you ran [`/rewind`](/docs/en/checkpointing#rewind-and-summarize) earlier in the session, the relaunch resumes from the rewound point rather than the longer transcript saved on disk. If you rewound to before your first message, the relaunched session starts empty.
You can also set the `CLAUDE_CODE_NO_FLICKER` environment variable before starting Claude Code:
```bash theme={null}
CLAUDE_CODE_NO_FLICKER=1 claude
```
The `tui` setting and the environment variable are equivalent. The `/tui` command clears `CLAUDE_CODE_NO_FLICKER` from the relaunched process so the setting it writes takes effect.
## What changes
Fullscreen rendering changes how the CLI draws to your terminal. The input box stays fixed at the bottom of the screen instead of moving as output streams in. If the input doesn't move while Claude is working, fullscreen rendering is active. Only visible messages are kept in the render tree, so memory stays constant regardless of conversation length.
Because the conversation lives in the alternate screen buffer instead of your terminal's scrollback, a few things work differently:
| Before | Now | Details |
| :-------------------------------------------------- | :----------------------------------------------------------------------------- | :------------------------------------------------------------------------ |
| `Cmd+f` or tmux search to find text | `Ctrl+o` for transcript mode, then `/` to search or `[` to write to scrollback | [Search and review the conversation](#search-and-review-the-conversation) |
| Terminal's native click-and-drag to select and copy | In-app selection, copies automatically on mouse release | [Use the mouse](#use-the-mouse) |
| `Cmd`-click to open a URL | `Cmd`-click on macOS, `Ctrl`-click elsewhere | [Use the mouse](#use-the-mouse) |
If mouse capture interferes with your workflow, you can [turn it off](#keep-native-text-selection) while keeping the flicker-free rendering.
## Use the mouse
Fullscreen rendering captures mouse events and handles them inside Claude Code:
* **Click in the prompt input** to position your cursor anywhere in the text you're typing.
* **Click a suggestion in the `/` command or `@` file list** to accept it. Hovering highlights the row under your cursor.
* **Click an option in a select menu** to choose it. This covers permission prompts, `/model`, `/config`, and other dialogs that show a list of options. Hovering shows a pointer on the row under your cursor. Requires Claude Code v2.1.187 or later.
* **Click an option in a multi-select menu** to toggle it, and click the submit button to confirm your choices. Clicking a free-text row, such as the `Other` row in a multiple-choice question, focuses its input field so you can type an answer. Requires Claude Code v2.1.208 or later.
* **Click a collapsed tool result** to expand it and see the full output. Click again to collapse. The tool call and its result expand together. Only messages that have more to show are clickable.
* **Hold `Cmd` on macOS, or `Ctrl` on Linux and Windows, and click a URL or file path** to open it. Plain `http://` and `https://` URLs open in your browser, and file paths in tool output, like the ones printed after an Edit or Write, open in your default application. A plain click without the modifier doesn't open links, matching native terminal behavior.
* Claude Code renders a network (UNC) path, such as `\\server\share\file.ts`, as plain text with no link, because opening a network path can send your Windows credentials to the host it names.
* Some macOS terminals forward `Cmd`+click to the running app instead of opening the link themselves, and the terminal mouse protocol has no way to encode the `Cmd` key, so Claude Code receives a plain click. In Ghostty, and in Warp on macOS, Claude Code detects this and lets a plain click on a link open it, and holding `Cmd` still works.
* In the VS Code integrated terminal and similar xterm.js-based terminals, Claude Code defers to the terminal's own link handler, which uses the same gesture.
* **Click and drag** to select text anywhere in the conversation. Double-click selects a word, matching iTerm2's word boundaries so a file path selects as one unit. Double-clicking a URL selects the whole URL, including the scheme. Triple-click selects the line.
* **Scroll with the mouse wheel** to move through the conversation.
Selected text copies to your clipboard automatically on mouse release. To turn this off, toggle Copy on select in `/config`.
With Copy on select off, press `Ctrl+Shift+c` to copy manually. On terminals that support the kitty keyboard protocol, such as kitty, WezTerm, Ghostty, and iTerm2, `Cmd+c` also works. If you have a selection active, `Ctrl+c` copies instead of cancelling.
With a selection active, hold `Shift` and press the arrow keys to extend it from the keyboard. `Shift+↑` and `Shift+↓` scroll the viewport when the selection reaches the top or bottom edge. `Shift+Home` and `Shift+End` extend to the start or end of the current line.
## Scroll the conversation
Fullscreen rendering handles scrolling inside the app. Use these shortcuts to navigate:
| Shortcut | Action |
| :-------------- | :--------------------------------------------------- |
| `PgUp` / `PgDn` | Scroll up or down by half a screen |
| `Ctrl+Home` | Jump to the start of the conversation |
| `Ctrl+End` | Jump to the latest message and re-enable auto-follow |
| Mouse wheel | Scroll a few lines at a time |
You can scroll back to the start of the session even after [compaction](/docs/en/context-window#what-survives-compaction). Claude continues working from the compaction summary, but Claude Code keeps every earlier message in the fullscreen scrollback across repeated compactions.
On keyboards without dedicated `PgUp`, `PgDn`, `Home`, or `End` keys, like MacBook keyboards, hold `Fn` with the arrow keys: `Fn+↑` sends `PgUp`, `Fn+↓` sends `PgDn`, `Fn+←` sends `Home`, and `Fn+→` sends `End`. `Ctrl+Fn+→` doesn't reach Claude Code on macOS, so a MacBook keyboard has no working jump-to-bottom chord by default. Instead, use one of these options:
* Click the [jump-to-bottom button](#auto-follow).
* Scroll to the bottom with the mouse wheel to resume following.
* Rebind `scroll:bottom` to a chord your keyboard can send.
These actions are rebindable. See [Scroll actions](/docs/en/keybindings#scroll-actions) for the full list of action names, including half-page and full-page variants that have no default binding.
### Auto-follow
Scrolling up pauses auto-follow so new output doesn't pull you back to the bottom. A `Jump to bottom` button floats over the bottom edge of the transcript while you're scrolled up, and shows a count such as `3 new messages` when new output arrives. Click it, press `Ctrl+End`, or scroll to the bottom to resume following.
While auto-follow is paused, the view also stays where you scrolled it when a response finishes streaming.
The button's keyboard hint reflects what your keyboard can send. On macOS it suggests clicking, or `Fn+↓` to scroll, because `Ctrl+End` doesn't reach Claude Code from a Mac keyboard. Rebind [`scroll:bottom`](/docs/en/keybindings#scroll-actions) and the button shows your chord on every platform.
On a terminal too narrow for the full label, the button shortens the hint instead of wrapping onto the transcript row underneath.
To turn auto-follow off entirely so the view stays where you leave it, open `/config` and set Auto-scroll to off. With auto-scroll disabled, the view never jumps to the bottom on its own. Permission prompts and other dialogs that need a response still scroll into view regardless of this setting.
### Mouse wheel scrolling
Mouse wheel scrolling requires your terminal to forward mouse events to Claude Code. Most terminals do this whenever an application requests it. iTerm2 makes it a per-profile setting: if the wheel does nothing but `PgUp` and `PgDn` work, open Settings → Profiles → Terminal and turn on Enable mouse reporting. The same setting is also required for click-to-expand and text selection to work.
If mouse wheel scrolling feels slow, your terminal may be sending one scroll event per physical notch with no multiplier. Some terminals, like Ghostty and iTerm2 with faster scrolling enabled, already amplify wheel events. Others, including the VS Code integrated terminal, send exactly one event per notch. Claude Code can't detect which.
Set `CLAUDE_CODE_SCROLL_SPEED` to multiply the base scroll distance:
```bash theme={null}
export CLAUDE_CODE_SCROLL_SPEED=3
```
A value of `3` matches the default in `vim` and similar applications. The setting accepts any positive value up to 20, including fractional values below 1 such as `0.25` to slow accelerated trackpad and wheel scrolling in terminals that already amplify wheel events.
To adjust scroll speed interactively, run `/scroll-speed`. The dialog shows a ruler you can scroll while it is open so you can feel the change immediately. Press `←` and `→` to adjust the speed, `r` to reset to the auto-detected default, and `Enter` to save. The dialog steps in whole numbers up to 10, and on terminals that support finer control it also offers quarter steps down to 0.25. Quarter steps require Claude Code v2.1.172 or later.
The command writes the same value the `CLAUDE_CODE_SCROLL_SPEED` environment variable sets, persisted to `~/.claude/settings.json`. The dialog's maximum is 10: if you set a higher value through the environment variable, the dialog shows 10, and saving from the dialog persists 10. The command isn't available in the JetBrains IDE terminal.
Separately from the base speed, Claude Code accelerates the scroll rate when you spin the wheel quickly, so a fast spin covers more distance than the same number of slow notches. To turn acceleration off and keep a constant rate per notch, set `wheelScrollAccelerationEnabled` to `false` in [`settings.json`](/docs/en/settings#available-settings). This setting requires Claude Code v2.1.174 or later.
### Scroll in the JetBrains IDE terminal
In the JetBrains IDE terminal, Claude Code applies its own scroll handling and ignores `CLAUDE_CODE_SCROLL_SPEED`. The terminal sends scroll events at a much higher rate than other emulators, so a multiplier tuned elsewhere overshoots here.
In 2025.2, the terminal also has scroll-wheel bugs that produce spurious arrow keys and wrong-direction events. Claude Code detects these at runtime and mitigates them automatically, so trackpad and mouse wheel scrolling work without configuration. For the best scroll experience, upgrade to 2025.3 or later. Claude Code shows a hint the first time you scroll if it detects the bug.
## Search and review the conversation
`Ctrl+o` toggles between the normal prompt and transcript mode.
For a quieter view that shows only your last prompt, a one-line summary of tool calls with edit diffstats, and the final response, run `/focus`. The setting persists across sessions. Run `/focus` again to turn it off.
Transcript mode gains `less`-style navigation and search:
| Key | Action |
| :----------------------------------- | :----------------------------------------------------------------------------------------------------- |
| `/` | Open search. Type to find matches, `Enter` to accept, `Esc` to cancel and restore your scroll position |
| `n` / `N` | Jump to next or previous match. Works after you've closed the search bar |
| `j` / `k` or `↑` / `↓` | Scroll one line |
| `g` / `G` or `Home` / `End` | Jump to top or bottom |
| `{` / `}` | Jump to the previous or next prompt |
| `Ctrl+u` / `Ctrl+d` | Scroll half a page |
| `Ctrl+b` / `Ctrl+f` or `Space` / `b` | Scroll a full page |
| `Ctrl+o`, `Esc`, or `q` | Exit transcript mode and return to the prompt |
Your terminal's `Cmd+f` and tmux search don't see the conversation because it lives in the alternate screen buffer, not the native scrollback. To hand the content back to your terminal, press `Ctrl+o` to enter transcript mode first, then:
* **`[`**: writes the full conversation into your terminal's native scrollback buffer, with all tool output expanded. The conversation is now ordinary text in your terminal, so `Cmd+f`, tmux copy mode, and any other native tool can search or select it. Long sessions may pause for a moment while this happens. This lasts until you exit transcript mode with `Esc` or `q`, which returns you to fullscreen rendering. The next `Ctrl+o` starts fresh.
* **`v`**: writes the conversation to a temporary file and opens it in `$VISUAL` or `$EDITOR`.
## Clear the conversation
Press `Ctrl+L` twice within two seconds to run `/clear` and start a new conversation. The first press redraws the screen and shows a hint; the second press clears the conversation. On macOS, double-pressing `Cmd+K` also runs `/clear`.
## Use with tmux
Fullscreen rendering works inside tmux, with three caveats.
Mouse wheel scrolling requires tmux's mouse mode. If your `~/.tmux.conf` doesn't already enable it, add this line and reload your config:
```bash theme={null}
set -g mouse on
```
Without mouse mode, wheel events go to tmux instead of Claude Code. Keyboard scrolling with `PgUp` and `PgDn` works either way. Claude Code prints a one-time hint at startup if it detects tmux with mouse mode off.
Fullscreen rendering is incompatible with iTerm2's tmux integration mode, which is the mode you enter with `tmux -CC`. In integration mode, iTerm2 renders each tmux pane as a native split rather than letting tmux draw to the terminal. The alternate screen buffer and mouse tracking don't work correctly there: the mouse wheel does nothing, and double-click can corrupt the terminal state. Don't enable fullscreen rendering in `tmux -CC` sessions. Regular tmux inside iTerm2, without `-CC`, works fine.
tmux releases through the 3.6 series don't implement synchronized output, so under those versions you may see more flicker during redraws than when running Claude Code directly in your terminal. Claude Code probes the terminal for synchronized-output support at startup and uses it when the terminal reports it. If you see flicker under tmux, upgrade to the newest tmux or run Claude Code in its own terminal tab outside tmux.
## Keep native text selection
Mouse capture is the most common friction point, especially over SSH or inside tmux. When Claude Code captures mouse events, your terminal's native copy-on-select stops working. The selection you make with click-and-drag exists inside Claude Code, not in your terminal's selection buffer, so tmux copy mode, Kitty hints, and similar tools don't see it.
Claude Code writes the selection to your system clipboard, and the path it uses depends on your setup. On a local session it runs a native clipboard tool:
* **macOS**: `pbcopy`
* **Linux**: `wl-copy` on Wayland, or `xclip` or `xsel` on X11, whichever is installed. Claude Code writes both the clipboard and the PRIMARY selection, so middle-click paste works.
* **Windows and WSL**: PowerShell `Set-Clipboard`
Inside tmux it also writes to the tmux paste buffer. Over SSH it falls back to OSC 52 escape sequences. Inside GNU screen, Claude Code copies long selections to the clipboard too. Before v2.1.219, if you copied a selection longer than roughly 570 characters, GNU screen printed base64 text into the window instead. Claude Code prints a toast after each copy telling you which path it used.
Some terminals block OSC 52 by default. iTerm2 blocks it until you turn on Settings → General → Selection → Applications in terminal may access clipboard; running [`/terminal-setup`](/docs/en/terminal-config) in iTerm2 enables this for you.
For a one-off native selection, the key to use depends on your terminal:
* **Terminal.app**: `Fn`
* **iTerm2**: `Option`
* **VS Code, Cursor, and Devin Desktop**: `Shift`, or `Option` on macOS with the `terminal.integrated.macOptionClickForcesSelection` setting enabled
* **Most other terminals**: `Shift`
Hold that key while you click and drag. Your terminal handles the selection itself instead of passing it to Claude Code, so copy shortcuts like `Cmd+C` work on what you select. Claude Code also shows the correct key in its on-screen hint.
Over SSH or inside tmux, Claude Code can't always detect the terminal you're connecting from, so the hint lists the candidate keys instead.
If you rely on native selection all the time, set `CLAUDE_CODE_DISABLE_MOUSE=1` to opt out of mouse capture while keeping the flicker-free rendering and flat memory:
```bash theme={null}
CLAUDE_CODE_NO_FLICKER=1 CLAUDE_CODE_DISABLE_MOUSE=1 claude
```
With mouse capture disabled, keyboard scrolling with `PgUp`, `PgDn`, `Ctrl+Home`, and `Ctrl+End` still works, and your terminal handles selection natively. You lose click-to-position-cursor, click-to-expand tool output, URL clicking, and wheel scrolling inside Claude Code.
To keep wheel scrolling but turn off click, drag, and hover handling, set `CLAUDE_CODE_DISABLE_MOUSE_CLICKS=1` instead. Requires Claude Code v2.1.195 or later. `CLAUDE_CODE_DISABLE_MOUSE` takes precedence when both variables are set.
With clicks disabled, Claude Code still captures the mouse, so the wheel and touchpad scroll the conversation but left clicks do nothing inside Claude Code. You still need to hold your terminal's key for native click-and-drag selection. Right-click and middle-click paste continue to work on terminals that support them.
## Troubleshooting
### Stale or misplaced text on screen
Fullscreen rendering sends only the cells that changed between frames. Some terminals, most commonly Windows Terminal and other ConPTY-backed hosts, coalesce these positioned writes incorrectly and leave fragments of earlier output on screen until you resize the window.
Set [`CLAUDE_CODE_ALT_SCREEN_FULL_REPAINT=1`](/docs/en/env-vars) to repaint every cell on every frame instead of sending incremental updates.
On Windows PowerShell:
```powershell theme={null}
$env:CLAUDE_CODE_ALT_SCREEN_FULL_REPAINT = "1"
claude
```
On macOS or Linux:
```bash theme={null}
CLAUDE_CODE_ALT_SCREEN_FULL_REPAINT=1 claude
```
On Windows, Claude Code already enables full repaint automatically for background sessions and [agent view](/docs/en/agent-view), so you only need to set the variable for an interactive fullscreen session you launched directly.
## Research preview
Fullscreen rendering is a research preview feature. It has been tested on common terminal emulators, but you may encounter rendering issues on less common terminals or unusual configurations.
If you encounter a problem, run `/feedback` inside Claude Code to report it, or open an issue on the [claude-code GitHub repo](https://github.com/anthropics/claude-code/issues). Include your terminal emulator name and version.
To turn fullscreen rendering off, run `/tui default`, or unset `CLAUDE_CODE_NO_FLICKER` if you enabled it that way. When you switch back with `/tui default`, Claude Code may first show an optional feedback prompt asking what made you switch. Type a reason and press `Enter` to send it, or press `Esc` to skip. The CLI relaunches into the classic renderer either way. To force the classic renderer regardless of the saved `tui` setting, set `CLAUDE_CODE_DISABLE_ALTERNATE_SCREEN=1`. The classic renderer keeps the conversation in your terminal's native scrollback so `Cmd+f` and tmux copy mode work as usual.
Background sessions opened from [agent view](/docs/en/agent-view) or `claude attach` always use fullscreen rendering. The attaching terminal enters the alternate screen buffer to show the session, and the classic renderer has no scrollback or mouse handling there, so the `tui` setting and `CLAUDE_CODE_DISABLE_ALTERNATE_SCREEN` don't apply to them.
gateways First recorded · 69 lines, first recorded
# Run Claude Code through a gateway ## How a gateway works ## Choose a gateway ### Claude apps gateway ### Other gateways ## Subscriptions and gateways ## Configure separately from the gateway ## Next steps
The first capture of this source. The page was already there, and this is what it said.
# Run Claude Code through a gateway > Route Claude Code through a self-hosted gateway for centralized credentials, usage tracking, and cost controls. Covers the architecture, Anthropic's Claude apps gateway, and using other gateway products. A gateway is a proxy your organization runs between Claude Code and a model provider. Claude Code sends API traffic to the gateway instead of directly to the provider, and the gateway forwards it using a credential your organization holds. Developers authenticate to the gateway rather than holding provider credentials, so authentication, usage tracking, budgets, and audit logging happen in one place you control. Claude Code includes a self-hosted gateway, [Claude apps gateway](/docs/en/claude-apps-gateway), in the `claude` binary, so you don't have to adopt a separate gateway product to run one. If your organization already runs an [LLM gateway](/docs/en/llm-gateway), Claude Code works with that too. This page covers: * [How a gateway sits between Claude Code and your provider](#how-a-gateway-works) * [Choosing between Claude apps gateway and a gateway you already run](#choose-a-gateway) * [How gateways interact with claude.ai subscriptions](#subscriptions-and-gateways) * [What's configured separately from the gateway](#configure-separately-from-the-gateway) ## How a gateway works Each developer's Claude Code sends its requests to the gateway's address and authenticates with a gateway-issued credential. The gateway authenticates the developer, applies whatever access and budget rules you configure, and forwards the request to your provider with the organization's credential. The provider can be Anthropic's API or a [cloud provider](/docs/en/third-party-integrations) such as Amazon Bedrock, Google Cloud's Agent Platform, or Microsoft Foundry; the gateway's configuration decides. With Claude apps gateway, or another gateway that exposes a single Anthropic-format endpoint, changing provider doesn't require touching developer machines. <Frame> <img src="https://mintcdn.com/claude-code/-uq-4JE0W_JO5Er5/images/llm-gateway-flow.svg?fit=max&auto=format&n=-uq-4JE0W_JO5Er5&q=85&s=1c1a8dcc0cfcc3a58652cc8e28cd3e20" alt="Diagram showing Claude Code routing through a gateway. In a developer machines zone, the Claude Code CLI and VS Code extension send requests to the gateway address with a per-developer credential. In a zone labeled your infrastructure, the gateway handles authentication, usage tracking, budgets, and routing, and forwards requests with your organization's credential. In a model providers zone, a solid arrow leads to the provider you configure, shown as the Anthropic API, and dashed arrows lead to other provider options, illustrated with Amazon Bedrock, Google Cloud, and Microsoft Foundry as examples." width="780" height="322" data-path="images/llm-gateway-flow.svg" /> </Frame> Two kinds of credential are involved: * **Developer credential**: each developer holds their own, issued by the gateway. It authenticates them to the gateway and identifies them in usage tracking * **Provider credential**: the gateway holds one credential for your provider account, shared by all forwarded traffic ## Choose a gateway Claude Code works with Anthropic's own gateway or with a gateway your organization already runs. ### Claude apps gateway Claude apps gateway is Anthropic's self-hosted gateway, included in the `claude` binary. It routes to Amazon Bedrock, Claude Platform on AWS, Google Cloud, Microsoft Foundry, or the Anthropic API as the upstream. Developers sign in with your corporate identity provider through `/login`, the gateway enforces model access and [managed settings](/docs/en/permissions#managed-settings) by IdP group, and it emits [OpenTelemetry Protocol (OTLP)](/docs/en/monitoring-usage) usage metrics to your own observability stack. Because it is built and tested alongside each Claude Code release, it forwards the headers and request fields Claude Code sends. A gateway maintained separately needs its [forwarding rules updated](/docs/en/llm-gateway-protocol#forward-as-open-lists) as those headers and fields change with each release; Claude apps gateway releases with the CLI, so there is no list to keep current. See [Availability and limitations](/docs/en/claude-apps-gateway#availability-and-limitations) for the small set of features that behave differently on a gateway session. The gateway sign-in is a browser SSO step, and there is no service-token flow, so a CI pipeline with no developer to approve the sign-in can't authenticate through it; configure those against your provider directly. Agent SDK sessions and `claude -p` runs on a machine where a developer has signed in use that machine's gateway session and are governed by its policies. See [CI pipelines and remote machines](/docs/en/claude-apps-gateway#ci-pipelines-and-remote-machines). See [Claude apps gateway](/docs/en/claude-apps-gateway) to deploy it. ### Other gateways If your organization already runs an LLM gateway or API gateway, you can use it instead. Anthropic doesn't endorse, maintain, or audit other gateway products, and doesn't support routing Claude Code to non-Claude models through any gateway. See [Other LLM gateways](/docs/en/llm-gateway) for the admin rollout checklist, what a gateway must implement, and how to point Claude Code at it. ## Subscriptions and gateways When developers connect through a gateway with a gateway credential, usage is billed to your organization's provider account at API rates, and their claude.ai subscriptions aren't used or charged. Setting [`ANTHROPIC_AUTH_TOKEN`](/docs/en/env-vars) for a gateway you run, or signing in to a Claude apps gateway with `/login`, turns off subscription login for that session. Every request forwarded under that credential is charged to the account behind the gateway's provider credential. The exception is setting only `ANTHROPIC_BASE_URL`, with no gateway credential. Requests still route through the gateway, but a saved claude.ai login stays the active credential, so the subscription's usage limits and billing apply. [Other LLM gateways](/docs/en/llm-gateway#subscriptions-and-gateways) covers that configuration and what the gateway has to forward for it to work. ## Configure separately from the gateway A gateway routes model API requests. A few things you might expect it to handle are configured elsewhere: * **Which model answers**: pick the model with the `/model` command or [model environment variables](/docs/en/model-config#setting-your-model). The gateway decides where requests go, not which model the developer selects. Claude apps gateway can bound the choice with a per-group `availableModels` allowlist, but the developer still picks within it. * **Other network traffic**: Claude Code itself sends version checks and downloads directly to Anthropic, separate from the gateway path. Whether the optional client telemetry stream is also on depends on your provider; the [telemetry defaults table](/docs/en/data-usage#telemetry-services) covers each case. On a signed-in Claude apps gateway session, the gateway credential disables the Anthropic-bound analytics, and the [telemetry destination](/docs/en/claude-apps-gateway#whats-enforced-on-developers) depends on how the session signed in. Your network still needs egress to the [required domains](/docs/en/network-config), or set [`CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC`](/docs/en/env-vars) to turn off the optional streams. * **Corporate HTTP proxies**: an `HTTPS_PROXY` sits between Claude Code and every server it talks to, including the gateway. If your network requires one, [configure the proxy](/docs/en/network-config) in addition to the gateway. For a Claude apps gateway you host, [sign-in checks that the proxy host is also on a private network](/docs/en/claude-apps-gateway#prerequisites); if it isn't, add the gateway host to `NO_PROXY` so the CLI connects to it directly. ## Next steps The next page depends on who runs the gateway. Anthropic's gateway runs from the `claude` binary and has its own setup guide; a gateway your organization already runs has a protocol to implement and an admin rollout checklist. * [Claude apps gateway](/docs/en/claude-apps-gateway) to deploy Anthropic's self-hosted gateway with SSO sign-in and OTLP telemetry * [Other LLM gateways](/docs/en/llm-gateway) for what a gateway your organization already runs must implement, and how to point Claude Code at it * [Set up Claude Code for your organization](/docs/en/admin-setup) for the wider rollout decisions a gateway is one part of
github-actions First recorded · 398 lines, first recorded
# Claude Code GitHub Actions ## Setup ### Quick setup ### Manual setup ### Set up for an organization ### Uninstall ### GitHub App permissions ## Interactive and automation modes ### Who can trigger runs ## Example use cases ### Respond to @claude mentions ### Run a skill ### Run on a schedule ## Best practices ### Define project standards in CLAUDE.md ### Protect your credentials ### Manage costs ## Use a cloud provider ## Troubleshooting ### Claude not responding to @claude commands ### CI not running on Claude's commits ### Authentication errors ## Advanced configuration ### Action parameters ### Pass CLI arguments ## Upgrade from beta ## What's next
The first capture of this source. The page was already there, and this is what it said.
# Claude Code GitHub Actions
> Run Claude Code in GitHub Actions workflows to respond to @claude mentions, automate tasks, and turn issues into pull requests
[Claude Code GitHub Actions](https://github.com/anthropics/claude-code-action) is a GitHub Action that runs Claude Code inside your repository's workflows. Mention `@claude` in a pull request or issue comment to have Claude analyze code, implement changes, and push commits. You can also give the Claude Code GitHub Action a prompt to run automatically on any GitHub event. Use it to turn issues into pull requests, fix bugs from a comment, or automate recurring tasks.
Several products share the Claude Code name. This page covers the `claude-code-action` workflow integration, which you configure with workflow files in your repository. For the related products, see:
* [Code Review](/docs/en/code-review): automatic review on every pull request, without writing a workflow
* [Claude Code on the web](/docs/en/claude-code-on-the-web): Claude Code sessions from your browser or phone
* [Claude Agent SDK](/docs/en/agent-sdk/overview): custom automation outside GitHub Actions. The Claude Code GitHub Action is built on the SDK
* [GitHub Enterprise Server](/docs/en/github-enterprise-server): Claude Code with self-hosted GitHub
## Setup
You can set up the Claude Code GitHub Action in one of two ways:
* **Quick setup**: run `/install-github-app` from Claude Code. Claude Code installs the GitHub App, adds your authentication secret, and prepares the workflow pull request for you
* **Manual setup**: install the app, add the secret, and copy the workflow file into your repository yourself. Use this path when you don't run Claude Code locally, when the command fails, or when you want full control of the workflow files
For either path, you need admin access to the repository.
### Quick setup
Before you start, install the [GitHub CLI](https://cli.github.com) and authenticate it with `gh auth login`. Claude Code checks for it and warns you if it's missing.
Open `claude` in the repository you want to connect, run `/install-github-app`, and follow the prompts. Claude Code installs the Claude GitHub App, then sets up an authentication secret for the workflows:
* If Claude Code already has an API key, it reuses that key, and offers to keep the repository's existing `ANTHROPIC_API_KEY` secret if one is already set
* Otherwise, choose between creating a long-lived token with your Claude subscription and pasting in an API key
Claude Code saves the credential as a repository secret, named `ANTHROPIC_API_KEY` for an API key or `CLAUDE_CODE_OAUTH_TOKEN` for a subscription token.
Claude Code then pushes a branch with the workflow files you select, already set to use that secret, and opens GitHub in your browser with a pull request ready to create. Create and merge that pull request, and `@claude` works in the repository.
If you select the review workflow, Claude posts each review on the pull request itself, as an inline comment on each issue it finds or as one summary comment when it finds none. Claude skips some pull requests, such as drafts. The [review workflow example](#run-a-skill) uses the same skill and lists them. Before v2.1.229, Claude wrote its review only to the workflow run log.
To update a review workflow that an earlier version generated, do one of the following:
* Run `/install-github-app` again. When the repository already has a `claude.yml`, select **Update workflow file with latest version**. Claude Code pushes fresh copies of the workflow files to a new branch and opens the pull request, the same as a first install.
* Add the `--comment` argument and the `claude_args` line from the [review workflow example](#run-a-skill) to the checked-in file yourself, which keeps any other edits you made to it.
After installing the GitHub App, Claude Code asks whether to continue with GitHub Actions setup. Choose **Skip for now** to stop with only the GitHub App installed. Run `/install-github-app` again later to finish the workflow and secret steps. Before v2.1.187, Claude Code proceeded straight to workflow selection.
<Note>
* When you install the GitHub App, you grant it several permissions. See [GitHub App permissions](#github-app-permissions) for the full set
* Quick setup works with the Claude API and Claude subscriptions. If you use Amazon Bedrock, Google Cloud's Agent Platform, or Microsoft Foundry, see [Use Claude Code GitHub Actions with cloud providers](/docs/en/github-actions-cloud-providers)
</Note>
### Manual setup
To configure the Claude Code GitHub Action without running `/install-github-app`, install the app, add a secret, and copy a workflow file yourself:
<Steps>
<Step title="Install the Claude GitHub App">
Install the [Claude GitHub App](https://github.com/apps/claude) to your repository. The Claude Code GitHub Action relies on three of the app's permissions:
* **Contents**: read and write, so Claude can modify repository files
* **Issues**: read and write, so Claude can respond to issues
* **Pull requests**: read and write, so Claude can create PRs and push changes
During installation, you also grant permissions that other Claude features use. See [GitHub App permissions](#github-app-permissions) for the full set.
</Step>
<Step title="Add an authentication secret">
Add one of the following secrets to your repository, depending on how you authenticate. See GitHub's guide to [using secrets in GitHub Actions](https://docs.github.com/en/actions/security-guides/using-secrets-in-github-actions).
* `ANTHROPIC_API_KEY`: a Claude API key from the [Claude Console](https://console.anthropic.com)
* `CLAUDE_CODE_OAUTH_TOKEN`: an OAuth token that authenticates with your Claude subscription, available on Pro, Max, Team, and Enterprise plans. Generate one by running `claude setup-token` locally. See [Generate a long-lived token](/docs/en/authentication#generate-a-long-lived-token)
In workflow files, pass the secret to the matching input: `anthropic_api_key` for an API key, or `claude_code_oauth_token` for an OAuth token.
</Step>
<Step title="Copy the workflow file">
Copy [examples/claude.yml](https://github.com/anthropics/claude-code-action/blob/main/examples/claude.yml) into your repository's `.github/workflows/` directory. The file is a working workflow, not just an example. As committed, Claude responds whenever someone mentions `@claude` in an issue or pull request, authenticating with the `ANTHROPIC_API_KEY` secret. If you added `CLAUDE_CODE_OAUTH_TOKEN` instead, change the workflow's `anthropic_api_key` line to `claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}`.
</Step>
</Steps>
<Tip>
After setup, test the Claude Code GitHub Action by tagging `@claude` in an issue or PR comment.
</Tip>
### Set up for an organization
With quick setup or manual setup, you configure one repository at a time. To roll the Claude Code GitHub Action out across an organization:
* Install the [Claude GitHub App](https://github.com/apps/claude) once at the organization level, choosing all repositories or a selected list
* Store the authentication secret as an organization-level Actions secret so each repository doesn't need its own copy
* Add the workflow file to each repository that should run the Claude Code GitHub Action, or define the job once as a [reusable workflow](https://docs.github.com/en/actions/using-workflows/reusing-workflows) that each repository calls
For a secret shared across repositories, authenticate with an API key from the [Claude Console](https://console.anthropic.com) rather than an OAuth token, since an OAuth token is tied to the subscription of the person who ran `claude setup-token`.
To avoid storing a long-lived secret entirely, authenticate through workload identity federation, where the Claude Code GitHub Action exchanges the workflow's GitHub OpenID Connect (OIDC) token for Claude API access through a Claude Console service account. Set these inputs:
* `anthropic_federation_rule_id`: the federation rule ID, `fdrl_...`
* `anthropic_organization_id`: your Anthropic organization ID
* `anthropic_service_account_id`: the service account ID, `svac_...`. Optional, since the federation rule you create in the Console already targets a service account
* `anthropic_workspace_id`: the workspace ID, `wrkspc_...`. Optional when the federation rule targets a single workspace
Grant the workflow the `id-token: write` permission, which the Claude Code GitHub Action needs for the federation exchange even when you pass your own `github_token`. See the [Claude Code GitHub Action's setup guide](https://github.com/anthropics/claude-code-action/blob/main/docs/setup.md) for the Console-side configuration.
For data handling and retention questions in a security review, see [data usage](/docs/en/data-usage) and [security](/docs/en/security).
### Uninstall
To remove the Claude Code GitHub Action, undo each piece of the setup that applies to your installation:
* **Workflow files**: delete the workflows that use `anthropics/claude-code-action` from `.github/workflows/`. If you used quick setup, look for `claude.yml` and, if you selected the review workflow, `claude-code-review.yml`. With the workflows deleted, the Claude Code GitHub Action no longer runs
* **Secrets**: delete the `ANTHROPIC_API_KEY` or `CLAUDE_CODE_OAUTH_TOKEN` secret from the repository, and from organization-level Actions secrets if you [shared it across repositories](#set-up-for-an-organization). If you delete a secret, the credential it held stays valid. To retire an API key entirely, also delete the key in the [Claude Console](https://console.anthropic.com)
* **GitHub App**: uninstall the Claude GitHub App in your repository or organization settings under GitHub Apps, but only if you don't use it for another Claude feature, such as Code Review or web auto-fix
If you configured a [cloud provider](/docs/en/github-actions-cloud-providers), also delete the provider secrets, such as `AWS_ROLE_TO_ASSUME`, the `GCP_*` secrets, or the `AZURE_*` secrets, and uninstall the custom GitHub App along with its `APP_ID` and `APP_PRIVATE_KEY` secrets.
### GitHub App permissions
The [Claude GitHub App](https://github.com/apps/claude) is shared by every Claude feature that integrates with GitHub, including the Claude Code GitHub Action, [Code Review](/docs/en/code-review), and [auto-fix for pull requests](/docs/en/claude-code-on-the-web#auto-fix-pull-requests) on Claude Code on the web. A GitHub App has a single permission set covering all of its features, so the set includes some permissions that the Claude Code GitHub Action doesn't use.
When you install the app, you grant the following permissions:
| Permission | Access |
| ---------------- | -------------- |
| Actions | Read and write |
| Checks | Read and write |
| Contents | Read and write |
| Discussions | Read and write |
| Issues | Read and write |
| Members | Read |
| Metadata | Read |
| Pull requests | Read and write |
| Repository hooks | Read and write |
| Statuses | Read |
| Workflows | Read and write |
The permission set can also change ahead of the features that use it. When the app requests a permission it didn't have before, GitHub prompts the account owner to approve it, an organization owner for an organization install, and the installation keeps its old permissions until they do. For example, when Actions access changes from read to write, the app can re-run workflows rather than only view runs and logs, so GitHub asks the owner to approve the change.
When you install the app, you accept its full permission set. GitHub doesn't let you accept a subset. If your organization requires only the permissions the Claude Code GitHub Action uses, create a custom GitHub App with Contents, Issues, and Pull requests instead, following the [Claude Code GitHub Action's setup guide](https://github.com/anthropics/claude-code-action/blob/main/docs/setup.md). A custom app covers only the Claude Code GitHub Action. Code Review and web auto-fix still require the official app.
For details on how the Claude Code GitHub Action limits what Claude can do with these permissions, see the [security documentation](https://github.com/anthropics/claude-code-action/blob/main/docs/security.md).
## Interactive and automation modes
The Claude Code GitHub Action detects how to run from your workflow configuration:
* **Interactive mode**: when the workflow provides no `prompt` input, Claude waits for the trigger phrase, `@claude` by default, in an issue or pull request comment, in a pull request review, or in the body or title of a newly opened issue, then responds to that request. Progress and results appear as a comment on the triggering issue or PR.
* **Automation mode**: when the workflow provides a `prompt` input, Claude runs without waiting for a mention, subject only to the [access checks](#who-can-trigger-runs). By default, results appear in the workflow run log rather than a comment. Claude can post to the issue or pull request when the prompt directs it to and it has a tool that can post, as in the [code-review example](#run-a-skill).
### Who can trigger runs
In both modes, the Claude Code GitHub Action runs two checks on the triggering actor before Claude starts, and the run fails when either check rejects it:
* **Write access**: on issue and pull request events, the triggering user must have write access to the repository. To allow specific users without write access, set `allowed_non_write_users` and pass your own `github_token` input. Events that no user authors, such as a `schedule` trigger, skip this check.
* **Human actor**: on every event, the Claude Code GitHub Action rejects a bot actor unless you list it in `allowed_bots`, which keeps bots from triggering Claude in a loop. This check also applies to scheduled runs, which GitHub attributes to a repository user, usually the one who last changed the workflow's `cron` schedule. If that user is a bot, list it in `allowed_bots`.
## Example use cases
The [examples directory](https://github.com/anthropics/claude-code-action/tree/main/examples) contains ready-to-use workflows for different scenarios.
The examples on this page show API key authentication. If you authenticate with a Claude subscription, replace the `anthropic_api_key` line in any example with `claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}`.
### Respond to @claude mentions
This workflow runs the Claude Code GitHub Action in interactive mode, so Claude responds whenever someone mentions `@claude` in an issue or PR comment.
```yaml theme={null}
name: Claude Code
on:
issue_comment:
types: [created]
pull_request_review_comment:
types: [created]
jobs:
claude:
if: contains(github.event.comment.body, '@claude')
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
issues: write
id-token: write
actions: read
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 1
- uses: anthropics/claude-code-action@v1
with:
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
```
The parts of this workflow that aren't boilerplate:
* `id-token: write`: required for the Claude Code GitHub Action's default GitHub App authentication
* `actions: read`: lets Claude read CI results on PRs
* `actions/checkout`: gives Claude a local copy of the repository to work in
* `if`: keeps runners from starting on comments that don't mention `@claude`. The Claude Code GitHub Action also checks the trigger phrase itself before responding
Once the workflow is in place, mention `@claude` in any issue or PR comment with a request:
```text wrap theme={null}
@claude implement this feature based on the issue description
@claude how should I implement user authentication for this endpoint?
@claude fix the TypeError in the user dashboard component
```
Claude replies in a comment on the same issue or PR and updates it as it works.
### Run a skill
The `prompt` input accepts a [skill](/docs/en/skills) invocation as well as plain text:
* For a skill in your repository's `.claude/skills/` directory, run `actions/checkout` before the `anthropics/claude-code-action` step so the skill files are available on the runner, then pass `/skill-name` as the `prompt`.
* For a skill packaged in a [plugin](/docs/en/plugins), install the plugin with the `plugin_marketplaces` and `plugins` inputs, then pass the namespaced `/plugin-name:skill-name` as the `prompt`. The `plugins` input takes `plugin-name@marketplace-name`, where the marketplace name comes from the marketplace's own manifest rather than its repository URL.
The following workflow installs the `code-review` plugin and runs its skill when a pull request is opened, updated, reopened, or marked ready for review. It runs the same plugin as the review workflow from quick setup. Use a workflow like this when you want to control the prompt, model, and triggers yourself. For automatic reviews without maintaining a workflow file, see [Code Review](/docs/en/code-review). On public repositories, GitHub withholds secrets from runs triggered by fork pull requests, so the review runs only on pull requests from branches in the same repository.
```yaml theme={null}
name: Code Review
on:
pull_request:
types: [opened, synchronize, ready_for_review, reopened]
jobs:
review:
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: read
issues: read
id-token: write
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 1
- uses: anthropics/claude-code-action@v1
with:
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
plugin_marketplaces: "https://github.com/anthropics/claude-code.git"
plugins: "code-review@claude-code-plugins"
prompt: "/code-review:code-review --comment ${{ github.repository }}/pull/${{ github.event.pull_request.number }}"
claude_args: '--allowedTools "mcp__github_inline_comment__create_inline_comment"'
```
Two lines in this workflow control where the review goes:
* **`--comment`**: Claude posts its review on the pull request, as an inline comment on each issue it finds or as one summary comment when it finds none. Without it, Claude posts nothing, and you read the findings in the workflow run log.
* **`claude_args`**: keep this line even though the skill's own `allowed-tools` frontmatter names the same tool, because the Claude Code GitHub Action starts the MCP server that posts inline comments only when `--allowedTools` in `claude_args` names it.
Claude skips draft and closed pull requests, pull requests it judges not to need a review, such as automated or trivial ones, and pull requests that already have a comment from Claude.
### Run on a schedule
With a `prompt` input, the Claude Code GitHub Action runs in automation mode on any GitHub event, including a cron schedule. For a plain-text prompt, Claude has no shell or GitHub API access until you grant the tools the prompt needs, with `--allowedTools` in `claude_args` or a [`permissions.allow` rule](/docs/en/permissions#permission-rule-syntax) in the `settings` input. If you invoke a skill instead, Claude can use the tools its [`allowed-tools` frontmatter](/docs/en/skills#pre-approve-tools-for-a-skill) grants. GitHub runs scheduled workflows only from the default branch and, in public repositories, disables the schedule after 60 days without repository activity.
This workflow generates a report in the workflow run log at 09:00 UTC each day. Its `claude_args` line [passes CLI arguments](#pass-cli-arguments) that select the model and allow two GitHub MCP tools. Claude reads commits and issues through the GitHub API with those tools, so you can omit the checkout step:
```yaml theme={null}
name: Daily Report
on:
schedule:
- cron: "0 9 * * *"
jobs:
report:
runs-on: ubuntu-latest
permissions:
contents: read
issues: read
id-token: write
steps:
- uses: anthropics/claude-code-action@v1
with:
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
prompt: "Generate a summary of yesterday's commits and open issues"
claude_args: |
--model claude-opus-4-8
--allowedTools "mcp__github__list_commits,mcp__github__list_issues"
```
## Best practices
### Define project standards in CLAUDE.md
Create a `CLAUDE.md` file in your repository root to define code style guidelines, review criteria, project-specific rules, and preferred patterns. Claude follows these guidelines when creating PRs and responding to requests. See the [memory documentation](/docs/en/memory) for details.
### Protect your credentials
<Warning>
Never commit API keys or OAuth tokens directly to your repository. Always store them as GitHub Secrets and reference them in workflows, for example `anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}`.
</Warning>
Grant the workflow only the permissions it needs, and review Claude's changes before merging.
For comprehensive security guidance including permissions and authentication, see the [Claude Code Action security documentation](https://github.com/anthropics/claude-code-action/blob/main/docs/security.md).
### Manage costs
Each run consumes two kinds of resources:
* **GitHub Actions minutes**: the Claude Code GitHub Action runs on GitHub-hosted runners, which consume your GitHub Actions minutes. See [GitHub's billing documentation](https://docs.github.com/en/billing/managing-billing-for-your-products/managing-billing-for-github-actions/about-billing-for-github-actions) for pricing and minute limits.
* **API tokens**: each interaction consumes tokens based on the length of prompts and responses, task complexity, and codebase size. See [Claude's pricing page](https://claude.com/platform/api) for current token rates. If you authenticate with an OAuth token, runs use your Claude subscription instead of API billing.
You can lower both kinds of cost by giving Claude clearer context and by capping how much work each run can do:
Cut at 300 lines. The page has the rest.
github-actions-cloud-providers First recorded · 316 lines, first recorded
# Use Claude Code GitHub Actions with cloud providers ## Choose your provider ## Prerequisites ## Set up the integration ## Troubleshooting ## What's next
The first capture of this source. The page was already there, and this is what it said.
# Use Claude Code GitHub Actions with cloud providers
> Run Claude Code GitHub Actions through Amazon Bedrock, Google Cloud's Agent Platform, or Microsoft Foundry instead of the Claude API
[Claude Code GitHub Actions](/docs/en/github-actions) calls the Claude API by default. To route inference through your own cloud account instead, set the Claude Code GitHub Action's provider input and configure your cloud to trust the workflow's OpenID Connect (OIDC) token. The workflow authenticates with that token, so you store no long-lived cloud credential in your repository.
<Info>
This page builds on the [GitHub Actions setup](/docs/en/github-actions#setup). It assumes you already know the workflow file and the `anthropics/claude-code-action` step, and covers only what a cloud provider changes.
</Info>
## Choose your provider
The Claude Code GitHub Action supports three providers, and the setup steps below differ only in the cloud-side configuration. Use the one where your organization already has Claude model access. You tell the Claude Code GitHub Action which provider to use with one input in the `anthropics/claude-code-action` step's `with:` block:
* **Amazon Bedrock**: `use_bedrock: "true"`
* **Google Cloud's Agent Platform**: `use_vertex: "true"`
* **Microsoft Foundry**: `use_foundry: "true"`
The following snippet shows the input in place for Amazon Bedrock. You don't need to edit a workflow yet, because the complete workflow examples later on this page already include the input for each provider.
```yaml theme={null}
- uses: anthropics/claude-code-action@v1
with:
use_bedrock: "true"
```
## Prerequisites
Before you start, you need:
* Admin access to the repository where the Claude Code GitHub Action runs, to install a GitHub App and add secrets
* Permission to create identity resources in your cloud account: IAM roles and OIDC identity providers on AWS, Workload Identity Federation resources and service accounts on Google Cloud, or Microsoft Entra applications on Azure
* Claude model access on your provider:
* **Amazon Bedrock**: access granted to Claude models. Cross-region inference profiles, such as the `us.` model IDs in this page's examples, need access granted in every region of their region group. See [Claude Code on Amazon Bedrock](/docs/en/amazon-bedrock)
* **Google Cloud's Agent Platform**: a project with the Agent Platform API enabled and access to Claude models. See [Claude Code on Google Cloud's Agent Platform](/docs/en/google-vertex-ai)
* **Microsoft Foundry**: a Foundry resource with a Claude model deployment. See [Claude Code on Microsoft Foundry](/docs/en/microsoft-foundry)
## Set up the integration
Beyond the prerequisites, you create four things: a GitHub identity for the Claude Code GitHub Action, the cloud-side trust configuration, the repository secrets, and the workflow file. The steps below walk through each.
<Steps>
<Step title="Choose a GitHub identity">
The Claude Code GitHub Action pushes commits and posts comments through a GitHub identity. The [quick setup](/docs/en/github-actions#quick-setup) installs the official Claude GitHub App for this. With a cloud provider, you choose the identity yourself:
* **Official [Claude GitHub App](https://github.com/apps/claude)**: install it on the repository, or skip to the next step if it's already installed
* **Custom GitHub App**: create your own app, described below, when you want only the three permissions the Claude Code GitHub Action uses rather than the [official app's full set](/docs/en/github-actions#github-app-permissions)
* **GitHub's automatic `GITHUB_TOKEN`**: no app to create or install, but GitHub doesn't trigger your CI workflows on commits made with it
The workflow examples in the fourth step authenticate with a custom app. That step also says what to change for the other two options.
To create a custom app, [register a new GitHub App](https://docs.github.com/en/apps/creating-github-apps/registering-a-github-app/registering-a-github-app) with webhooks disabled, since this integration doesn't use them. Grant it three repository permissions:
* **Contents**: read and write
* **Issues**: read and write
* **Pull requests**: read and write
After registering the app, generate a private key and keep the downloaded `.pem` file, note the App ID from the app's settings page, and [install the app](https://docs.github.com/en/apps/using-github-apps/installing-your-own-github-app) on the repository where the Claude Code GitHub Action runs. You add the key and the ID as secrets in the third step.
</Step>
<Step title="Configure cloud authentication">
Configure your cloud to trust the OIDC token that GitHub issues to the workflow, so each workflow run gets short-lived cloud credentials. The bullets in each tab summarize what to create, and each tab links the cloud vendor's own guide for the console-level steps.
<Tabs>
<Tab title="Amazon Bedrock">
Create the trust configuration in your AWS account, following the [AWS guide to creating OIDC identity providers](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_providers_create_oidc.html):
* Add a GitHub OIDC identity provider with provider URL `https://token.actions.githubusercontent.com` and audience `sts.amazonaws.com`
* Create an IAM role trusted by that provider as a web identity, and attach the scoped invocation policy from [IAM configuration](/docs/en/amazon-bedrock#iam-configuration), which grants `bedrock:InvokeModel`, `bedrock:InvokeModelWithResponseStream`, `bedrock:ListInferenceProfiles`, and `bedrock:GetInferenceProfile`, along with two `aws-marketplace` subscription actions
* Limit the role's trust policy to your repository with a subject condition such as `repo:your-org/your-repo:*`. See [GitHub's OIDC hardening guide](https://docs.github.com/en/actions/deployment/security-hardening-your-deployments/about-security-hardening-with-openid-connect) for the claim format
Note the role's ARN. You add it as a secret in the next step.
</Tab>
<Tab title="Google Cloud's Agent Platform">
Create the federation resources in your Google Cloud project, following the [Workload Identity Federation documentation](https://cloud.google.com/iam/docs/workload-identity-federation):
* Enable three APIs: IAM Credentials, Security Token Service (STS), and the Agent Platform API, whose service name is `aiplatform.googleapis.com`
* Create a Workload Identity Pool with a GitHub OIDC provider whose issuer is `https://token.actions.githubusercontent.com`, and add an attribute condition that limits the pool to your repository
* Create a dedicated service account with only the `Vertex AI User` role, which is `roles/aiplatform.user`, and allow the pool to impersonate it
Note the provider's full resource name and the service account's email address. You add them as secrets in the next step.
</Tab>
<Tab title="Microsoft Foundry">
Create a Microsoft Entra application with a federated credential for your repository, following [Microsoft's guide to authenticating from GitHub Actions](https://learn.microsoft.com/en-us/azure/developer/github/connect-from-azure-openid-connect):
* Register a Microsoft Entra application and add a federated identity credential that trusts tokens GitHub issues to your repository. A user-assigned managed identity works in place of an application. Both have the client ID you note below
* Assign the application the `Azure AI User` role on your Foundry resource. See [Azure RBAC configuration](/docs/en/microsoft-foundry#azure-rbac-configuration) for a narrower custom role
Note the application's client ID, your tenant ID, and your subscription ID. You add them as secrets in the next step.
</Tab>
</Tabs>
</Step>
<Step title="Add repository secrets">
In the repository where the Claude Code GitHub Action runs, add the secrets for your provider, plus the two app secrets if you created a custom GitHub App in the first step. See GitHub's guide to [using secrets in GitHub Actions](https://docs.github.com/en/actions/security-guides/using-secrets-in-github-actions).
| Secret | Needed for | Value |
| -------------------------------- | ----------------------------- | ------------------------------------------- |
| `AWS_ROLE_TO_ASSUME` | Amazon Bedrock | The ARN of the IAM role |
| `GCP_WORKLOAD_IDENTITY_PROVIDER` | Google Cloud's Agent Platform | The provider's full resource name |
| `GCP_SERVICE_ACCOUNT` | Google Cloud's Agent Platform | The service account's email address |
| `AZURE_CLIENT_ID` | Microsoft Foundry | The Entra application's client ID |
| `AZURE_TENANT_ID` | Microsoft Foundry | Your Microsoft Entra tenant ID |
| `AZURE_SUBSCRIPTION_ID` | Microsoft Foundry | Your Azure subscription ID |
| `APP_ID` | Custom GitHub App | The GitHub App's ID |
| `APP_PRIVATE_KEY` | Custom GitHub App | The contents of the `.pem` private key file |
</Step>
<Step title="Create the workflow file">
Create a workflow file for your provider, such as `.github/workflows/claude.yml`. Each example responds to `@claude` mentions, authenticates to GitHub with a custom app, and includes the `id-token: write` permission, which GitHub requires to issue the OIDC token that your cloud provider exchanges for credentials.
If you chose a different GitHub identity in the first step, adjust the example:
* **Official Claude GitHub App**: delete the Generate GitHub App token step and the `github_token` line
* **GitHub's automatic token**: delete the token-generation step and change the `github_token` line to `github_token: ${{ secrets.GITHUB_TOKEN }}`
<Warning>
On public repositories, a comment containing the trigger phrase from any user starts this workflow. The credential steps run before the Claude Code GitHub Action checks the commenter's write access, so the action rejects unauthorized users only after the workflow has generated an App token and signed in to your cloud provider, which leaves audit-log entries and consumes Actions minutes. To avoid those runs, add a step that verifies the commenter's write access before the credential steps.
</Warning>
<Tabs>
<Tab title="Amazon Bedrock">
Replace the `aws-region` value with your own. The credentials step exports it as `AWS_REGION` for the rest of the job.
```yaml theme={null}
name: Claude PR Action
permissions:
contents: write
pull-requests: write
issues: write
id-token: write
on:
issue_comment:
types: [created]
pull_request_review_comment:
types: [created]
issues:
types: [opened]
jobs:
claude-pr:
if: |
(github.event_name == 'issue_comment' && contains(github.event.comment.body, '@claude')) ||
(github.event_name == 'pull_request_review_comment' && contains(github.event.comment.body, '@claude')) ||
(github.event_name == 'issues' && (contains(github.event.issue.body, '@claude') || contains(github.event.issue.title, '@claude')))
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v6
- name: Generate GitHub App token
id: app-token
uses: actions/create-github-app-token@v2
with:
app-id: ${{ secrets.APP_ID }}
private-key: ${{ secrets.APP_PRIVATE_KEY }}
- name: Configure AWS Credentials (OIDC)
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ secrets.AWS_ROLE_TO_ASSUME }}
aws-region: us-west-2
- uses: anthropics/claude-code-action@v1
with:
github_token: ${{ steps.app-token.outputs.token }}
use_bedrock: "true"
claude_args: '--model us.anthropic.claude-sonnet-4-6'
```
<Tip>
Bedrock model IDs include a cross-region inference profile prefix such as `us.`. Use the prefix for the region group where you granted model access.
</Tip>
</Tab>
<Tab title="Google Cloud's Agent Platform">
Replace the `CLOUD_ML_REGION` value with your own. You don't need to hardcode the project ID, because the workflow reads it from the `auth` step's output.
```yaml theme={null}
name: Claude PR Action
permissions:
contents: write
pull-requests: write
issues: write
id-token: write
on:
issue_comment:
types: [created]
pull_request_review_comment:
types: [created]
issues:
types: [opened]
jobs:
claude-pr:
if: |
(github.event_name == 'issue_comment' && contains(github.event.comment.body, '@claude')) ||
(github.event_name == 'pull_request_review_comment' && contains(github.event.comment.body, '@claude')) ||
(github.event_name == 'issues' && (contains(github.event.issue.body, '@claude') || contains(github.event.issue.title, '@claude')))
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v6
- name: Generate GitHub App token
id: app-token
uses: actions/create-github-app-token@v2
with:
app-id: ${{ secrets.APP_ID }}
private-key: ${{ secrets.APP_PRIVATE_KEY }}
- name: Authenticate to Google Cloud
id: auth
uses: google-github-actions/auth@v2
with:
workload_identity_provider: ${{ secrets.GCP_WORKLOAD_IDENTITY_PROVIDER }}
service_account: ${{ secrets.GCP_SERVICE_ACCOUNT }}
- uses: anthropics/claude-code-action@v1
with:
github_token: ${{ steps.app-token.outputs.token }}
use_vertex: "true"
claude_args: '--model claude-sonnet-5'
env:
ANTHROPIC_VERTEX_PROJECT_ID: ${{ steps.auth.outputs.project_id }}
CLOUD_ML_REGION: us-east5
```
</Tab>
<Tab title="Microsoft Foundry">
Replace `your-resource-name` with your Foundry resource name. Claude Code builds the endpoint URL from it. The `azure/login` step signs in with the workflow's OIDC token, and Claude Code picks up the credentials through the Azure [default credential chain](https://learn.microsoft.com/en-us/azure/developer/javascript/sdk/authentication/credential-chains#defaultazurecredential-overview).
```yaml theme={null}
name: Claude PR Action
permissions:
contents: write
pull-requests: write
issues: write
id-token: write
on:
issue_comment:
types: [created]
pull_request_review_comment:
types: [created]
issues:
types: [opened]
jobs:
claude-pr:
if: |
(github.event_name == 'issue_comment' && contains(github.event.comment.body, '@claude')) ||
(github.event_name == 'pull_request_review_comment' && contains(github.event.comment.body, '@claude')) ||
(github.event_name == 'issues' && (contains(github.event.issue.body, '@claude') || contains(github.event.issue.title, '@claude')))
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v6
- name: Generate GitHub App token
id: app-token
uses: actions/create-github-app-token@v2
with:
app-id: ${{ secrets.APP_ID }}
private-key: ${{ secrets.APP_PRIVATE_KEY }}
- name: Authenticate to Azure
uses: azure/login@v2
with:
client-id: ${{ secrets.AZURE_CLIENT_ID }}
tenant-id: ${{ secrets.AZURE_TENANT_ID }}
subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
- uses: anthropics/claude-code-action@v1
with:
github_token: ${{ steps.app-token.outputs.token }}
use_foundry: "true"
claude_args: '--model claude-sonnet-5'
env:
ANTHROPIC_FOUNDRY_RESOURCE: your-resource-name
```
<Tip>
Use a model ID that matches a Claude deployment in your Foundry resource. See [Claude Code on Microsoft Foundry](/docs/en/microsoft-foundry) for model configuration and version pinning.
</Tip>
</Tab>
</Tabs>
With any provider, you can bound run length and cost by adding `--max-turns` to `claude_args`. See [Manage costs](/docs/en/github-actions#manage-costs).
</Step>
<Step title="Test the setup">
Mention `@claude` in an issue or PR comment, then watch the run in the repository's Actions tab. Claude replies in a comment on the same issue or PR.
Cut at 300 lines. The page has the rest.
github-enterprise-server First recorded · 226 lines, first recorded
# Claude Code with GitHub Enterprise Server ## What works with GitHub Enterprise Server ## Admin setup ### GitHub App permissions ### Manual setup ### Network requirements ## Developer workflow ### Teleport sessions to your terminal ## Plugin marketplaces on GHES ### Add a GHES marketplace ### Pre-register GHES marketplaces with managed settings ### Allowlist GHES marketplaces in managed settings ## Limitations ## Troubleshooting ### Web session fails to clone repository ### Marketplace add fails with a policy error ### Marketplace add on claude.ai fails with a GitHub access error ### GHES instance not reachable ### Session start fails with `Unable to get organization UUID` ## Related resources
The first capture of this source. The page was already there, and this is what it said.
# Claude Code with GitHub Enterprise Server
> Connect Claude Code to your self-hosted GitHub Enterprise Server instance for web sessions, code review, and plugin marketplaces.
<Note>
GitHub Enterprise Server support is available for Team and Enterprise plans.
</Note>
GitHub Enterprise Server (GHES) support lets your organization use Claude Code with repositories hosted on your self-managed GitHub instance instead of github.com. Once an Owner connects your GHES instance, developers can run web sessions and get automated code reviews without any per-repository configuration. Plugin marketplaces hosted on your instance are also supported; credential requirements vary by surface, as described in [Plugin marketplaces on GHES](#plugin-marketplaces-on-ghes).
For repositories on github.com, see [Claude Code on the web](/docs/en/claude-code-on-the-web) and [Code Review](/docs/en/code-review). To run Claude in your own CI infrastructure, see [GitHub Actions](/docs/en/github-actions).
## What works with GitHub Enterprise Server
The table below shows which Claude Code features support GHES and any differences from github.com behavior.
| Feature | GHES support | Notes |
| :--------------------- | :-------------- | :----------------------------------------------------------------------------------------------------------------------------- |
| Claude Code on the web | ✅ Supported | An Owner connects the GHES instance once; developers use `claude --cloud` or [claude.ai/code](https://claude.ai/code) as usual |
| Code Review | ✅ Supported | Same automated PR reviews as github.com |
| Claude Security | ✅ Supported | Available in public beta for Enterprise plans at [claude.ai/security](https://claude.ai/security) |
| Teleport sessions | ✅ Supported | Move sessions between web and terminal with `--teleport` |
| Plugin marketplaces | ✅ Supported | Credential requirements differ by surface. See [Plugin marketplaces on GHES](#plugin-marketplaces-on-ghes) |
| Contribution metrics | ✅ Supported | Delivered via webhooks to the [analytics dashboard](/docs/en/analytics) |
| GitHub Actions | ✅ Supported | Requires manual workflow setup; `/install-github-app` is github.com only |
| GitHub MCP server | ❌ Not supported | The GitHub MCP server does not work with GHES instances |
## Admin setup
An Owner connects your GHES instance to Claude Code once. After that, developers in your organization can use GHES repositories without any additional configuration. You need the Owner or Primary Owner role in your Claude organization and permission to create GitHub Apps on your GHES instance.
The guided setup generates a GitHub App manifest and redirects you to your GHES instance to create the app in one click. If your environment blocks the redirect flow, an [alternative manual setup](#manual-setup) is available.
<Steps>
<Step title="Open Claude Code admin settings">
Go to [claude.ai/admin-settings/claude-code](https://claude.ai/admin-settings/claude-code) and find the GitHub Enterprise Server section.
</Step>
<Step title="Start the guided setup">
Click **Connect**. Enter a display name of up to 20 characters for the connection and your GHES hostname, for example `github.example.com`. If your GHES instance uses a self-signed or private certificate authority, paste the CA certificate in the optional field.
</Step>
<Step title="Create the GitHub App">
Click **Continue to GitHub Enterprise**. Your browser redirects to your GHES instance with a pre-filled app manifest. Review the configuration and click **Create GitHub App**. GHES redirects you back to Claude with the app credentials stored automatically.
</Step>
<Step title="Install the app on your repositories">
From the GitHub App page on your GHES instance, install the app on the repositories or organizations you want Claude to access. You can start with a subset and add more later.
</Step>
<Step title="Enable features">
Return to [claude.ai/admin-settings/claude-code](https://claude.ai/admin-settings/claude-code) and enable [Code Review](/docs/en/code-review#set-up-code-review), Claude Security, and [contribution metrics](/docs/en/analytics#enable-contribution-metrics) for your GHES repositories using the same configuration as github.com.
</Step>
</Steps>
### GitHub App permissions
The manifest configures the GitHub App with the permissions and webhook events below, which together cover web sessions, Code Review, Claude Security, plugin marketplaces, and contribution metrics:
| Permission | Access | Used for |
| :------------------- | :------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Contents | Read and write | Cloning repositories and pushing branches |
| Pull requests | Read and write | Creating PRs and posting review comments |
| Issues | Read and write | Responding to issue mentions |
| Checks | Read and write | Posting Code Review check runs |
| Actions | Read | Reading CI status for auto-fix |
| Commit statuses | Read | Reading CI status from providers that report commit statuses instead of check runs |
| Repository hooks | Read and write | Creating a webhook on a plugin marketplace repository when **Sync automatically** is turned on for a marketplace in [Organization settings > Plugins](https://claude.ai/admin-settings/plugins) |
| Metadata | Read | Required by GitHub for all apps |
| Organization members | Read | Matching the Claude GitHub App on github.com, which uses it to check a connecting user's organization role when linking an installation |
The app subscribes to `pull_request`, `issue_comment`, `pull_request_review_comment`, `pull_request_review`, `check_run`, and `status` events.
GitHub applies a manifest only when the app is created, so an app created from an earlier version of the manifest keeps the permissions and events it was created with. If your app is missing any of the permissions or events above, add them in the app's settings on your GHES instance. GitHub then asks an owner of each installation to approve the new permissions, and the installation keeps its old permissions until they do.
### Manual setup
If the guided redirect flow is blocked by your network configuration, click **Add manually** instead of Connect. Create a GitHub App on your GHES instance with the [permissions and events above](#github-app-permissions), then enter the connection details in the form: a display name, your GHES hostname and optional port, and the app's ID, client ID, client secret, webhook secret, and private key. The form also accepts an optional custom CA certificate and read replica hostnames.
Claude generates the app's webhook URL when you save the connection. After you click **Add configuration**, open the connection's **More options** menu, select **Copy webhook URL**, and paste the URL into the app's webhook settings on your GHES instance. Use the same webhook secret you entered in the form.
### Network requirements
For Anthropic-hosted sessions, your GHES instance must be reachable from Anthropic infrastructure so Claude can clone repositories and post review comments. If your GHES instance is behind a firewall, allowlist the [Anthropic API IP addresses](https://platform.claude.com/docs/en/api/ip-addresses). Sessions in a [self-hosted environment](/docs/en/self-hosted-environments-deploy#configure-git) clone from inside your network instead, unless the runner opts into the [Anthropic git proxy](/docs/en/self-hosted-environments-deploy#use-the-anthropic-git-proxy), which fetches from Anthropic's side and needs the same reachability; the [SCM connector](/docs/en/self-hosted-environments-reference#scm-connector-flags) covers the hosted pre-session flows, such as the repository picker, for a GHES host that's only routable internally.
## Developer workflow
Once an Owner has connected the GHES instance, no developer-side configuration is needed. Claude Code detects your GHES hostname automatically from the git remote in your working directory.
Clone a repository from your GHES instance as you normally would, replacing `github.example.com` and the repository path with your GHES hostname and repository:
```bash theme={null}
git clone [email protected]:platform/api-service.git
cd api-service
```
Then start a web session. Claude detects the GHES host from your git remote and routes the session through your organization's configured instance:
```bash theme={null}
claude --cloud "Add retry logic to the payment webhook handler"
```
The session clones your repository from GHES and pushes changes back to a branch. Monitor progress with `/tasks` or at [claude.ai/code](https://claude.ai/code). See [Claude Code on the web](/docs/en/claude-code-on-the-web) for the full cloud session workflow including diff review, auto-fix, and routines.
### Teleport sessions to your terminal
Pull a web session into your local terminal with `claude --teleport`. Teleport verifies you're in a checkout of the same GHES repository before fetching the branch and loading the session history. See [teleport requirements](/docs/en/claude-code-on-the-web#teleport-requirements) for details.
## Plugin marketplaces on GHES
Host plugin marketplaces on your GHES instance to distribute internal tooling across your organization. The marketplace structure is identical to github.com-hosted marketplaces, but installation works differently depending on where you add the marketplace, and credentials differ across surfaces:
| Surface | How installation works | What each user needs |
| :------------------------------------------ | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Claude Code CLI and desktop | Claude Code clones the marketplace repository using the machine's existing git credentials | Git access to your GHES host from their machine |
| Managed settings (`extraKnownMarketplaces`) | Claude Code registers the entry and clones the repository using the machine's existing git credentials | Git access to your GHES host from their machine |
| claude.ai organization plugin settings | An Owner selects the GHES instance as the source; Anthropic's backend fetches and syncs the repository using the GitHub App from [admin setup](#admin-setup) | Nothing per user once added. The Owner adding it needs their own GitHub Enterprise account connected as an access check, and the GitHub App must be installed on the marketplace repository |
| claude.ai user settings | Anthropic's backend fetches the repository using the submitting user's GitHub Enterprise connection | Their own GitHub Enterprise account connected to Claude |
| Claude Code on the web | Cloud sessions clone marketplaces inside the session sandbox. The sandbox can reach your GHES instance only when the session's repository is on that same instance, and its git credentials are scoped to the session's repositories | Not reliable for GHES-hosted marketplaces: a different host than the session's repository is not reachable, and even same-instance installs can fail. Use the CLI, managed settings, or claude.ai instead |
<Warning>
GitHub Enterprise connections on claude.ai are per user when a marketplace is added from user settings. The [admin setup](#admin-setup) connects your GHES instance to your organization, but it does not connect individual user accounts: each user who adds a GHES marketplace from their own settings must first connect their own GitHub Enterprise account, and one user's connection, including the Owner's, does not cover anyone else. Marketplaces added by an Owner in the organization plugin settings do not put this requirement on users, because ongoing fetches use the organization's GitHub App. The Owner adding the marketplace still needs their own GitHub Enterprise account connected at add time.
</Warning>
### Add a GHES marketplace
The `owner/repo` shorthand always resolves to github.com. For GHES-hosted marketplaces, use the full git URL, replacing `github.example.com` and the repository path with your own. HTTPS URLs are recommended:
```bash theme={null}
/plugin marketplace add https://github.example.com/platform/claude-plugins.git
```
SSH URLs work if the machine already trusts your GHES host:
```bash theme={null}
/plugin marketplace add [email protected]:platform/claude-plugins.git
```
Claude Code runs git non-interactively and rejects SSH connections to hosts that are not in the machine's `known_hosts` file. An HTTPS URL with a git credential helper avoids the `known_hosts` requirement.
See [Create and distribute a plugin marketplace](/docs/en/plugin-marketplaces) for the full guide to building marketplaces.
### Pre-register GHES marketplaces with managed settings
The `extraKnownMarketplaces` setting pre-registers a marketplace so developers get it without manual setup. It works from [any settings file](/docs/en/settings#extraknownmarketplaces), including a repository's `.claude/settings.json`; managed settings deliver it organization-wide:
```json theme={null}
{
"extraKnownMarketplaces": {
"internal-tools": {
"source": {
"source": "git",
"url": "https://github.example.com/platform/claude-plugins.git"
}
}
}
}
```
Claude Code installs these marketplaces locally: it registers each entry and clones the repository with the machine's existing git credentials. This path does not go through claude.ai, so the per-user GitHub Enterprise connection is not required. For a successful rollout:
* **Use a full git URL.** The `owner/repo` shorthand always resolves to github.com and cannot reference a GHES host.
* **Prefer HTTPS URLs.** SSH clones fail on machines that do not already trust your GHES host key. An HTTPS URL with your organization's standard git credential helper works on any machine with credentials configured.
* **Confirm each machine can clone from your GHES host.** If a machine lacks credentials, the marketplace is registered but never installed, and its plugins report as not found instead of prompting for credentials.
* **Confirm the setting reaches each machine.** A managed settings file only takes effect on machines it's deployed to, for example through your device management system. See [managed settings](/docs/en/settings#settings-files) for file locations.
### Allowlist GHES marketplaces in managed settings
If your organization uses [managed settings](/docs/en/settings) to restrict which marketplaces developers can add, use the `hostPattern` source type to allow all marketplaces from your GHES instance without enumerating each repository. See [settings files](/docs/en/settings#settings-files) for file locations on each platform. Add the JSON to your `managed-settings.json` file or equivalent MDM policy:
```json theme={null}
{
"strictKnownMarketplaces": [
{
"source": "hostPattern",
"hostPattern": "^github\\.example\\.com$"
}
]
}
```
See the [strictKnownMarketplaces](/docs/en/settings#strictknownmarketplaces) and [extraKnownMarketplaces](/docs/en/settings#extraknownmarketplaces) settings reference for the complete schema.
## Limitations
A few features behave differently on GHES than on github.com. The [feature table](#what-works-with-github-enterprise-server) summarizes support; this section covers the workarounds.
* **`/install-github-app` command**: follow the [admin setup](#admin-setup) flow on claude.ai instead. If you also want GitHub Actions workflows on GHES, adapt the [example workflow](https://github.com/anthropics/claude-code-action/blob/main/examples/claude.yml) manually.
* **GitHub MCP server**: use the `gh` CLI configured for your GHES host instead. Run `gh auth login --hostname github.example.com` to authenticate, then Claude can use `gh` commands in sessions.
## Troubleshooting
### Web session fails to clone repository
If `claude --cloud` fails with a clone error, verify that an Owner has completed setup for your GHES instance and that the GitHub App is installed on the repository you're working in. Ask the Owner who connected the instance to confirm that the hostname registered in Claude settings matches the hostname in your git remote.
### Marketplace add fails with a policy error
If `/plugin marketplace add` is blocked for your GHES URL, your organization has restricted marketplace sources. Ask your admin to add a `hostPattern` entry for your GHES hostname in [managed settings](#allowlist-ghes-marketplaces-in-managed-settings).
### Marketplace add on claude.ai fails with a GitHub access error
If adding a GHES marketplace from your user settings fails with a generic error like "Marketplace couldn't be added", check your GitHub Enterprise connection first. This is what appears when your own GitHub Enterprise account is not connected to Claude, even if your organization's GHES instance is configured and other users are connected. The dialog does not point to the GitHub Enterprise connect flow, and the "Connect to GitHub" option on the Browse tab signs in to github.com, which does not grant access to GHES repositories.
To connect your GitHub Enterprise account: the repository picker on [claude.ai/code](https://claude.ai/code) offers a connect option for each configured GHES instance, and Owners can also connect from the GitHub Enterprise section of the [Claude Code admin settings](https://claude.ai/admin-settings/claude-code). Then add the marketplace again. Alternatively, ask an Owner to add the marketplace in the organization plugin settings, which removes the per-user connection requirement.
On other claude.ai surfaces, a "Repository not found. If it's private, GitHub access is required" error on a GHES marketplace usually indicates the same missing connection. Connect your GitHub Enterprise account through one of the paths above, then try again.
### GHES instance not reachable
If reviews or Anthropic-hosted web sessions time out, your GHES instance may not be reachable from Anthropic infrastructure. Confirm your firewall allows inbound connections from the [Anthropic API IP addresses](https://platform.claude.com/docs/en/api/ip-addresses). Sessions in a [self-hosted environment](/docs/en/self-hosted-environments) reach GHES from inside your network, so for them check the runner's own network path and the [SCM connector](/docs/en/self-hosted-environments-reference#scm-connector-flags) instead.
### Session start fails with `Unable to get organization UUID`
Web sessions require a Team or Enterprise organization. Sign in with `/login` using your organization account. If you authenticate with an API key instead, web sessions fail earlier with a message asking you to run `/login`.
## Related resources
These pages cover the features referenced throughout this guide in more depth:
* [Claude Code on the web](/docs/en/claude-code-on-the-web): run Claude Code sessions on cloud infrastructure
* [Code Review](/docs/en/code-review): automated PR reviews
* [Plugin marketplaces](/docs/en/plugin-marketplaces): build and distribute plugin catalogs
* [Analytics](/docs/en/analytics): track usage and contribution metrics
* [Managed settings](/docs/en/settings): organization-wide policy configuration
* [Network configuration](/docs/en/network-config): firewall and IP allowlist requirements
gitlab-ci-cd First recorded · 431 lines, first recorded
# Claude Code GitLab CI/CD ## Why use Claude Code with GitLab? ## How it works ## What can Claude do? ## Setup ### Quick setup ### Manual setup (recommended for production) ## Example use cases ### Turn issues into MRs ### Get implementation help ### Fix bugs quickly ## Using with Amazon Bedrock and Google Cloud ## Configuration examples ### Amazon Bedrock job example (OIDC) ### Agent Platform job example (Workload Identity Federation) ## Best practices ### CLAUDE.md configuration ### Security considerations ### Optimizing performance ### CI costs ## Troubleshooting ### Claude not responding to @claude commands ### Job can't write comments or open MRs ### Authentication errors ## Advanced configuration ### Common parameters and variables ### Customizing Claude's behavior
The first capture of this source. The page was already there, and this is what it said.
# Claude Code GitLab CI/CD
> Learn about integrating Claude Code into your development workflow with GitLab CI/CD
<Info>
Claude Code for GitLab CI/CD is currently in beta. Features and functionality may evolve as we refine the experience.
This integration is maintained by GitLab. For support, see the following [GitLab issue](https://gitlab.com/gitlab-org/gitlab/-/issues/573776).
</Info>
<Note>
This integration is built on top of the [Claude Code CLI and Agent SDK](/docs/en/agent-sdk/overview), enabling programmatic use of Claude in your CI/CD jobs and custom automation workflows.
</Note>
## Why use Claude Code with GitLab?
* **Instant MR creation**: Describe what you need, and Claude proposes a complete MR with changes and explanation
* **Automated implementation**: Turn issues into working code with a single command or mention
* **Project-aware**: Claude follows your `CLAUDE.md` guidelines and existing code patterns
* **Simple setup**: Add one job to `.gitlab-ci.yml` and a masked CI/CD variable
* **Enterprise-ready**: Choose Claude API, Amazon Bedrock, or Google Cloud's Agent Platform to meet data residency and procurement needs
* **Secure by default**: Runs in your GitLab runners with your branch protection and approvals
## How it works
Claude Code uses GitLab CI/CD to run AI tasks in isolated jobs and commit results back via MRs:
1. **Event-driven orchestration**: GitLab listens for your chosen triggers (for example, a comment that mentions `@claude` in an issue, MR, or review thread). The job collects context from the thread and repository, builds prompts from that input, and runs Claude Code.
2. **Provider abstraction**: Use the provider that fits your environment:
* Claude API (SaaS)
* Amazon Bedrock (IAM-based access, cross-region options)
* Google Cloud's Agent Platform (GCP-native, Workload Identity Federation)
3. **Sandboxed execution**: Each interaction runs in a container with strict network and filesystem rules. Claude Code enforces workspace-scoped permissions to constrain writes. Every change flows through an MR so reviewers see the diff and approvals still apply.
Pick regional endpoints to reduce latency and meet data-sovereignty requirements while using existing cloud agreements.
## What can Claude do?
Claude Code enables powerful CI/CD workflows that transform how you work with code:
* Create and update MRs from issue descriptions or comments
* Analyze performance regressions and propose optimizations
* Implement features directly in a branch, then open an MR
* Fix bugs and regressions identified by tests or comments
* Respond to follow-up comments to iterate on requested changes
## Setup
### Quick setup
The fastest way to get started is to add a minimal job to your `.gitlab-ci.yml` and set your API key as a masked variable.
1. **Add a masked CI/CD variable**
* Go to **Settings** → **CI/CD** → **Variables**
* Add `ANTHROPIC_API_KEY` (masked, protected as needed)
2. **Add a Claude job to `.gitlab-ci.yml`**
```yaml theme={null}
stages:
- ai
claude:
stage: ai
image: node:24-alpine3.21
# Adjust rules to fit how you want to trigger the job:
# - manual runs
# - merge request events
# - web/API triggers when a comment contains '@claude'
rules:
- if: '$CI_PIPELINE_SOURCE == "web"'
- if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
variables:
GIT_STRATEGY: fetch
before_script:
- apk update
- apk add --no-cache git curl bash
- curl -fsSL https://claude.ai/install.sh | bash
# The installer places claude in ~/.local/bin, which isn't on PATH in this image
- export PATH="$HOME/.local/bin:$PATH"
script:
# Optional: start a GitLab MCP server if your setup provides one
- /bin/gitlab-mcp-server || true
# Use AI_FLOW_* variables when invoking via web/API triggers with context payloads
- echo "$AI_FLOW_INPUT for $AI_FLOW_CONTEXT on $AI_FLOW_EVENT"
- >
claude
-p "${AI_FLOW_INPUT:-'Review this MR and implement the requested changes'}"
--permission-mode acceptEdits
--allowedTools "Bash Read Edit Write mcp__gitlab"
--debug
```
After adding the job and your `ANTHROPIC_API_KEY` variable, test by running the job manually from **CI/CD** → **Pipelines**, or trigger it from an MR to let Claude propose updates in a branch and open an MR if needed.
<Note>
To run on Amazon Bedrock or Google Cloud's Agent Platform instead of the Claude API, see the [Using with Amazon Bedrock and Google Cloud](#using-with-amazon-bedrock-and-google-cloud) section below for authentication and environment setup.
</Note>
### Manual setup (recommended for production)
If you prefer a more controlled setup or need enterprise providers:
1. **Configure provider access**:
* **Claude API**: Create and store `ANTHROPIC_API_KEY` as a masked CI/CD variable
* **Amazon Bedrock**: **Configure GitLab** → **AWS OIDC** and create an IAM role for Amazon Bedrock
* **Google Cloud's Agent Platform**: **Configure Workload Identity Federation for GitLab** → **GCP**
2. **Add project credentials for GitLab API operations**:
* Use `CI_JOB_TOKEN` by default, or create a Project Access Token with `api` scope
* Store as `GITLAB_ACCESS_TOKEN` (masked) if using a PAT
3. **Add the Claude job to `.gitlab-ci.yml`**: use the [Quick setup](#quick-setup) job for the Claude API, or a provider job from [Configuration examples](#configuration-examples)
4. **(Optional) Enable mention-driven triggers**:
* Add a project webhook for "Comments (notes)" to your event listener (if you use one)
* Have the listener call the pipeline trigger API with variables like `AI_FLOW_INPUT` and `AI_FLOW_CONTEXT` when a comment contains `@claude`
## Example use cases
### Turn issues into MRs
In an issue comment:
```text wrap theme={null}
@claude implement this feature based on the issue description
```
Claude analyzes the issue and codebase, writes changes in a branch, and opens an MR for review.
### Get implementation help
In an MR discussion:
```text wrap theme={null}
@claude suggest a concrete approach to cache the results of this API call
```
Claude proposes changes, adds code with appropriate caching, and updates the MR.
### Fix bugs quickly
In an issue or MR comment:
```text wrap theme={null}
@claude fix the TypeError in the user dashboard component
```
Claude locates the bug, implements a fix, and updates the branch or opens a new MR.
## Using with Amazon Bedrock and Google Cloud
For enterprise environments, you can run Claude Code entirely on your cloud infrastructure with the same developer experience.
<Tabs>
<Tab title="Amazon Bedrock">
### Prerequisites
Before setting up Claude Code with Amazon Bedrock, you need:
1. An AWS account with Amazon Bedrock access to the desired Claude models
2. GitLab configured as an OIDC identity provider in AWS IAM
3. An IAM role with Amazon Bedrock permissions and a trust policy restricted to your GitLab project/refs
4. GitLab CI/CD variables for role assumption:
* `AWS_ROLE_TO_ASSUME` (role ARN)
* `AWS_REGION` (Amazon Bedrock region)
### Setup instructions
Configure AWS to allow GitLab CI jobs to assume an IAM role via OIDC (no static keys).
**Required setup:**
1. Enable Amazon Bedrock and request access to your target Claude models
2. Create an IAM OIDC provider for GitLab if not already present
3. Create an IAM role trusted by the GitLab OIDC provider, restricted to your project and protected refs
4. Attach least-privilege permissions for Amazon Bedrock invoke APIs
Use the [Amazon Bedrock job example](#configuration-examples) to exchange the job's OIDC token for temporary AWS credentials at runtime.
</Tab>
<Tab title="Google Cloud's Agent Platform">
### Prerequisites
Before setting up Claude Code with Google Cloud's Agent Platform, you need:
1. A Google Cloud project with:
* Google Cloud's Agent Platform API enabled
* Workload Identity Federation configured to trust GitLab OIDC
2. A dedicated service account with only the required Google Cloud's Agent Platform roles
3. GitLab CI/CD variables:
* `GCP_WORKLOAD_IDENTITY_PROVIDER` (provider resource name without the `//iam.googleapis.com/` prefix, such as `projects/123456789/locations/global/workloadIdentityPools/my-pool/providers/my-provider`)
* `GCP_SERVICE_ACCOUNT` (service account email)
* `GCP_PROJECT_ID` (Google Cloud project ID)
### Setup instructions
Configure Google Cloud to allow GitLab CI jobs to impersonate a service account via Workload Identity Federation.
**Required setup:**
1. Enable IAM Credentials API, STS API, and Google Cloud's Agent Platform API
2. Create a Workload Identity Pool and provider for GitLab OIDC
3. Create a dedicated service account with Google Cloud's Agent Platform roles
4. Grant the WIF principal permission to impersonate the service account
Use the [Agent Platform job example](#configuration-examples) to authenticate without storing keys.
</Tab>
</Tabs>
## Configuration examples
Below are ready-to-use snippets you can adapt to your pipeline.
### Amazon Bedrock job example (OIDC)
**Prerequisites:**
* Amazon Bedrock enabled with access to your chosen Claude model(s)
* GitLab OIDC configured in AWS with a role that trusts your GitLab project and refs
* IAM role with Amazon Bedrock permissions (least privilege recommended)
**Required CI/CD variables:**
* `AWS_ROLE_TO_ASSUME`: ARN of the IAM role for Amazon Bedrock access
* `AWS_REGION`: Amazon Bedrock region (for example, `us-west-2`)
GitLab mints the job's OIDC token from the `id_tokens:` block and exposes it as `GITLAB_OIDC_TOKEN`. Set `aud` to the audience value you configured on the IAM OIDC identity provider in AWS, for example your GitLab instance URL.
```yaml theme={null}
stages:
- ai
claude-bedrock:
stage: ai
image: node:24-alpine3.21
rules:
- if: '$CI_PIPELINE_SOURCE == "web"'
id_tokens:
GITLAB_OIDC_TOKEN:
aud: https://gitlab.example.com
before_script:
- apk add --no-cache bash curl jq git aws-cli
- curl -fsSL https://claude.ai/install.sh | bash
# The installer places claude in ~/.local/bin, which isn't on PATH in this image
- export PATH="$HOME/.local/bin:$PATH"
# Exchange the job's OIDC token for AWS credentials
- export AWS_WEB_IDENTITY_TOKEN_FILE="/tmp/oidc_token"
- printf "%s" "$GITLAB_OIDC_TOKEN" > "$AWS_WEB_IDENTITY_TOKEN_FILE"
- >
aws sts assume-role-with-web-identity
--role-arn "$AWS_ROLE_TO_ASSUME"
--role-session-name "gitlab-claude-$(date +%s)"
--web-identity-token "file://$AWS_WEB_IDENTITY_TOKEN_FILE"
--duration-seconds 3600 > /tmp/aws_creds.json
- export AWS_ACCESS_KEY_ID="$(jq -r .Credentials.AccessKeyId /tmp/aws_creds.json)"
- export AWS_SECRET_ACCESS_KEY="$(jq -r .Credentials.SecretAccessKey /tmp/aws_creds.json)"
- export AWS_SESSION_TOKEN="$(jq -r .Credentials.SessionToken /tmp/aws_creds.json)"
script:
- /bin/gitlab-mcp-server || true
- >
claude
-p "${AI_FLOW_INPUT:-'Implement the requested changes and open an MR'}"
--permission-mode acceptEdits
--allowedTools "Bash Read Edit Write mcp__gitlab"
--debug
variables:
AWS_REGION: "us-west-2"
CLAUDE_CODE_USE_BEDROCK: "1"
```
<Note>
Model IDs for Amazon Bedrock include region-specific prefixes (for example, `us.anthropic.claude-sonnet-4-6`). Pass the desired model via your job configuration or prompt if your workflow supports it.
</Note>
### Agent Platform job example (Workload Identity Federation)
**Prerequisites:**
* Google Cloud's Agent Platform API enabled in your GCP project
* Workload Identity Federation configured to trust GitLab OIDC
* A service account with Google Cloud's Agent Platform permissions
**Required CI/CD variables:**
* `GCP_WORKLOAD_IDENTITY_PROVIDER`: provider resource name without the `//iam.googleapis.com/` prefix, such as `projects/123456789/locations/global/workloadIdentityPools/my-pool/providers/my-provider`
* `GCP_SERVICE_ACCOUNT`: service account email
* `GCP_PROJECT_ID`: Google Cloud project ID
* `CLOUD_ML_REGION`: Google Cloud's Agent Platform region (for example, `us-east5`)
GitLab mints the job's OIDC token from the `id_tokens:` block and exposes it as `GITLAB_OIDC_TOKEN`. Set `aud` to the audience value you configured on the Workload Identity Pool provider, for example your GitLab instance URL. The job writes the token to a file, and the credential configuration's `credential_source` entry tells Google's auth libraries to read it from there. Setting `GOOGLE_APPLICATION_CREDENTIALS` to the credential configuration file makes it available to Claude Code through [Application Default Credentials](/docs/en/google-vertex-ai#3-configure-gcp-credentials).
```yaml theme={null}
stages:
- ai
claude-vertex:
stage: ai
Cut at 300 lines. The page has the rest.
glossary First recorded · 339 lines, first recorded
# Glossary ## A ### Agent teams ### Agentic coding ### Agentic harness ### Agentic loop ### Artifact ### Auto memory ### Auto mode ## B ### Bare mode ### Bundled skills ## C ### Channel ### Checkpoint ### `.claude` directory ### CLAUDE.md ### Command ### Compaction ### Connector ### Context window ## D ### Dispatch ## E ### Effort level ### Extended thinking ## H ### Hook ## M ### Managed settings ### MCP (Model Context Protocol) ### MCP server ### MCP Tool Search ## N ### Non-interactive mode ## O ### Output style ## P ### Permission mode ### Permission rule ### Plan mode ### Plugin ### Project trust ### Prompt injection ## R ### Remote Control ### Rules ## S ### Sandboxing ### Session ### Settings layers ### Skill ### Subagent ### Surface ## T ### Teleport ### Tool ### Turn ## V ### Verification loop ## W ### Worktree isolation ## Deprecated and renamed terms
The first capture of this source. The page was already there, and this is what it said.
# Glossary > Definitions for Claude Code terminology. Learn what agentic loop, compaction, CLAUDE.md, hooks, subagents, MCP, and other core concepts mean. This glossary defines Claude Code terminology. Each entry links to the page where the concept is covered in depth. For model-level concepts like tokens, temperature, and RAG, see the [platform glossary](https://platform.claude.com/docs/en/about-claude/glossary). For Claude Desktop terms such as desktop extension, MCPB, and DXT, see the [Claude Help Center](https://support.claude.com/). ## A ### Agent teams Multiple independent Claude Code sessions coordinated by a team lead, with a shared task list and peer-to-peer messaging. Unlike [subagents](#subagent), which run within a single session and report only to the parent, teammates each have their own context window and you can interact with any of them directly. Agent teams are experimental and must be enabled by setting `CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1`. Learn more: [Run agent teams](/docs/en/agent-teams) ### Agentic coding A workflow where the AI can read files, run commands, and make changes autonomously while you watch, redirect, or step away, as opposed to chat-based assistants that only respond with text you must apply yourself. Claude Code is agentic because it has [tools](#tool) that let it act, not just advise. Learn more: [How Claude Code works](/docs/en/how-claude-code-works) ### Agentic harness The tools, context management, and execution environment that turn a language model into a capable coding agent. Claude Code is the harness; Claude is the model inside it. The harness supplies file access, shell execution, permission gating, memory loading, and the loop that chains actions together. Learn more: [How Claude Code works](/docs/en/how-claude-code-works) ### Agentic loop The cycle Claude works through for every task: gather context, take action, verify results, and repeat until done. Each tool use returns information that informs the next step. You can interrupt the loop at any point to redirect. Most extension points, including [hooks](#hook), [skills](#skill), and [MCP](#mcp-model-context-protocol), plug into specific phases of this loop. Learn more: [How Claude Code works](/docs/en/how-claude-code-works#the-agentic-loop) ### Artifact A live, interactive web page Claude Code publishes from your session to a private URL on claude.ai, so you can see output visually or share it instead of reading terminal text. The page updates in place when the session republishes. Artifacts you create from Claude Code appear in the same gallery as artifacts created in claude.ai conversations. Sharing depends on your plan: on Pro and Max, a public link that anyone can open; on Team and Enterprise, sharing within your organization, plus public links once an Owner enables them. Learn more: [Share session output as artifacts](/docs/en/artifacts) ### Auto memory Notes Claude writes for itself based on your corrections and preferences, stored per git repository under `~/.claude/projects/`. All worktrees of the same repository share one auto memory directory. The first 200 lines or 25 KB of the `MEMORY.md` index loads at the start of every session. Auto memory is the Claude-written counterpart to [CLAUDE.md](#claude-md), which you write. Learn more: [Auto memory](/docs/en/memory#auto-memory) ### Auto mode A [permission mode](#permission-mode) where a separate classifier model reviews actions in the background, so most run without approval prompts; explicit ask rules still prompt. The classifier blocks scope escalation, untrusted infrastructure, and [prompt injection](#prompt-injection). It never sees tool results, so injected instructions cannot influence its decisions. Learn more: [Eliminate prompts with auto mode](/docs/en/permission-modes#eliminate-prompts-with-auto-mode) ## B ### Bare mode With `--bare`, Claude Code starts without loading hooks, skills, plugins, MCP servers, auto memory, or CLAUDE.md. Recommended for CI and scripted calls where you need the same result on every machine. Learn more: [Start faster with bare mode](/docs/en/headless#start-faster-with-bare-mode) ### Bundled skills Prompt-based playbooks included with Claude Code, such as `/batch`, `/code-review`, `/debug`, and `/loop`. Unlike built-in commands, which execute fixed logic, bundled skills give Claude a detailed prompt and let it orchestrate the work, so they can spawn agents, read files, and adapt to your codebase. Learn more: [Bundled skills](/docs/en/skills#bundled-skills) ## C ### Channel An [MCP server](#mcp-model-context-protocol) that pushes events into your running session so Claude can react to things that happen while you're away from the terminal. Channels can be two-way: Claude reads an inbound event and replies back through the same channel. Telegram, Discord, and iMessage are included in the research preview. Learn more: [Channels](/docs/en/channels) ### Checkpoint A restore point created at each prompt you send. Claude Code snapshots files before every edit so a checkpoint can revert them. Press `Esc` twice or run `/rewind` to restore code, conversation, or both to an earlier point, or to summarize part of the conversation from a selected message. Checkpoints are saved with the conversation, so a resumed session can still `/rewind` to them. They're separate from git and don't track changes made through the Bash tool. Learn more: [Checkpointing](/docs/en/checkpointing) ### `.claude` directory The directory where Claude Code reads project-scoped configuration: settings, hooks, skills, subagents, rules, and auto memory. A project has `.claude/` at its root; your user-level defaults are at `~/.claude/`. Learn more: [The `.claude` directory](/docs/en/claude-directory) ### CLAUDE.md A markdown file of persistent instructions you write for Claude, loaded at the start of every session as a user message after the system prompt. Put project conventions, architecture notes, and "always do X" rules here. Project-root CLAUDE.md survives [compaction](#compaction) and is re-read fresh from disk afterward. You can place CLAUDE.md at project scope in `./CLAUDE.md` or `./.claude/CLAUDE.md`, at user scope in `~/.claude/CLAUDE.md`, or as [managed policy](#managed-settings) for your organization. All discovered files are concatenated into context rather than overriding each other, ordered from broadest scope to most specific. Learn more: [CLAUDE.md files](/docs/en/memory#claude-md-files) ### Command A reusable instruction you invoke by typing `/name` in the prompt. Built-in commands such as `/clear`, `/model`, and `/compact` control the session. You can define your own commands as files in `.claude/commands/`, or install them from a [plugin](#plugin). [Skills](#skill) are the recommended way to package multi-step commands. Two other uses of the word are unrelated: `claude` CLI subcommands such as `claude mcp add`, listed in the [CLI reference](/docs/en/cli-reference#cli-commands), and the `command` field of a stdio [MCP server](#mcp-server) entry, which specifies the executable Claude Code launches to start the server. Learn more: [Commands](/docs/en/commands) · [Skills](/docs/en/skills) ### Compaction Automatic summarization of your conversation when the [context window](#context-window) approaches its limit. Older tool outputs are cleared first, then the conversation is summarized. Project-root CLAUDE.md and auto memory survive compaction and reload from disk; instructions given only in conversation may be lost. Run `/compact` to trigger manually, optionally with a focus like `/compact focus on the API changes`. Learn more: [What survives compaction](/docs/en/context-window#what-survives-compaction) · [When context fills up](/docs/en/how-claude-code-works#when-context-fills-up) ### Connector An [MCP server](#mcp-server) added to your claude.ai account rather than configured in Claude Code. When you sign in to Claude Code with that account, your connectors appear in `/mcp` alongside the servers you added locally. Organizations can also provision connectors and set per-tool controls on them. Learn more: [Use MCP servers from claude.ai](/docs/en/mcp#use-mcp-servers-from-claude-ai) ### Context window The working memory for a session, holding conversation history, file contents, command outputs, CLAUDE.md, auto memory, loaded skills, and system instructions. As you work, context fills up until [compaction](#compaction) summarizes it. Run `/context` to see what's using space. For the underlying model concept, see the [platform glossary](https://platform.claude.com/docs/en/about-claude/glossary#context-window). Learn more: [Explore the context window](/docs/en/context-window) ## D ### Dispatch A phone-initiated task router that spawns a Claude Code session in the Desktop app when you send a coding task from the Claude mobile app. Your prompt routes to the right tool automatically. Available on Pro and Max plans. Learn more: [Sessions from Dispatch](/docs/en/desktop#sessions-from-dispatch) ## E ### Effort level A setting that controls how much of the adaptive-reasoning thinking budget Claude uses on each turn. Higher effort means more thinking tokens and deeper reasoning; lower effort is faster and cheaper. Effort is supported on Fable 5, on Opus 4.6 and later, and on Sonnet 4.6 and later. Learn more: [Adjust effort level](/docs/en/model-config#adjust-effort-level) ### Extended thinking Visible step-by-step reasoning the model performs before responding. You can adjust it with the [effort level](#effort-level), or cap thinking tokens with `MAX_THINKING_TOKENS` on models with a fixed thinking budget. Thinking appears in gray italic text in the terminal. Learn more: [Use extended thinking](/docs/en/model-config#extended-thinking) ## H ### Hook A user-defined handler that executes automatically at a specific point in Claude Code's lifecycle, such as before a tool runs, after a file edit, or at session start. Handlers can be a shell command, HTTP endpoint, MCP tool, LLM prompt, or subagent. Hooks are deterministic: they fire at fixed lifecycle points rather than at the model's discretion. A hook configuration has three levels: * **Hook event**: the lifecycle point * **Matcher**: filters which events fire it * **Hook handler**: what runs Learn more: [Get started with hooks](/docs/en/hooks-guide) · [Hooks reference](/docs/en/hooks) ## M ### Managed settings Settings enforced org-wide by IT or DevOps, delivered from Anthropic's servers through the admin console or deployed to devices at an OS-level path outside `~/.claude`. User and project settings cannot override managed settings. Server-managed delivery applies on [eligible configurations](/docs/en/server-managed-settings#platform-availability); see [Security considerations](/docs/en/server-managed-settings#security-considerations). Use this for security policies, compliance requirements, or standardized tooling across a fleet. Learn more: [Server-managed settings](/docs/en/server-managed-settings) · [Settings files](/docs/en/settings#settings-files) ### MCP (Model Context Protocol) An open standard for connecting AI tools to external data sources and services. MCP servers give Claude new tools for Slack, Jira, databases, browsers, and hundreds of other integrations. You connect servers via `/mcp` or by adding them to `.mcp.json`. For the protocol itself, see the [platform glossary](https://platform.claude.com/docs/en/about-claude/glossary#mcp-model-context-protocol). Learn more: [Model Context Protocol](/docs/en/mcp) ### MCP server A program that gives Claude tools, prompts, or resources over [MCP](#mcp-model-context-protocol). You add servers with `claude mcp add`, in `.mcp.json`, through a [plugin](#plugin), or as a claude.ai [connector](#connector). A local stdio server runs as a process Claude Code starts from the `command` and `args` fields of its configuration, which have nothing to do with the [commands](#command) you type at the prompt. Learn more: [Model Context Protocol](/docs/en/mcp) ### MCP Tool Search A context-saving mechanism that defers MCP tool schemas until needed. Only tool names load at startup; Claude fetches the full schema on demand when it decides to use a specific tool. This keeps idle MCP servers from consuming much context. Learn more: [Scale with MCP Tool Search](/docs/en/mcp#scale-with-mcp-tool-search) ## N ### Non-interactive mode A mode that executes a single prompt and exits without an interactive prompt, invoked with `-p` or `--print`. Used for CI, scripts, and piping. The run is still saved as a resumable session unless you pass `--no-session-persistence`. The [Agent SDK](/docs/en/agent-sdk/overview) is the Python and TypeScript equivalent. Formerly called headless mode. Learn more: [Run Claude Code programmatically](/docs/en/headless) ## O ### Output style A configuration that modifies Claude's system prompt to change response behavior, tone, or format. Output styles turn off the software-engineering-specific parts of the default system prompt, unlike [CLAUDE.md](#claude-md) which is delivered as a user message following the system prompt. Built-in styles include Default, Proactive, Explanatory, and Learning. Learn more: [Output styles](/docs/en/output-styles) ## P ### Permission mode The baseline approval behavior for the session. Cycle with `Shift+Tab` in the CLI or use the mode selector in VS Code, Desktop, and claude.ai. Available modes are `default`, `acceptEdits`, `plan`, `auto`, `dontAsk`, and `bypassPermissions`. The `default` mode is labeled Manual in the CLI, in the VS Code and JetBrains extensions, and in the desktop app, and Claude Code accepts `manual` as an alias for the value. Learn more: [Choose a permission mode](/docs/en/permission-modes) ### Permission rule A settings entry that allows, asks about, or denies a tool invocation based on the tool name and argument pattern. Rules are evaluated deny→ask→allow, first match wins. Permission rules are fine-grained controls layered on top of the broader [permission mode](#permission-mode). Learn more: [Configure permissions](/docs/en/permissions) ### Plan mode A [permission mode](#permission-mode) where Claude researches and proposes changes without editing your source files. It can read, search, and run exploration commands, then presents a plan for approval before touching anything. Enter plan mode with `/plan` or by pressing `Shift+Tab`. Learn more: [Analyze before you edit with plan mode](/docs/en/permission-modes#analyze-before-you-edit-with-plan-mode) ### Plugin A bundle of skills, hooks, subagents, and MCP servers packaged as a single installable unit. Plugin skills are namespaced as `plugin-name:skill-name` so multiple plugins coexist. Distribute plugins across teams via a [marketplace](/docs/en/plugin-marketplaces). Learn more: [Plugins](/docs/en/plugins) ### Project trust A dialog accepting a directory before Claude Code loads its configuration. Acceptance is saved per project directory, except your home directory, where trust is held for the current session only and the prompt reappears on each launch. Until you trust a directory, Claude Code holds back some of the content its repository supplies, such as project allow rules and marketplaces from `.claude/settings.json`. [What runs before you trust a folder](/docs/en/permissions#what-runs-before-you-trust-a-folder) lists each kind of content, including what a `-p` session runs without a dialog. Learn more: [The `.claude` directory](/docs/en/claude-directory) ### Prompt injection Hostile instructions embedded in a file, web page, or tool result that attempt to redirect Claude toward actions you never asked for. Claude Code's defenses include the permission system, command injection detection, and trust verification. [Auto mode](#auto-mode) adds a server-side probe that scans tool results for suspicious content and a classifier that never sees tool results, so injected text cannot influence its approval decisions. Learn more: [Protect against prompt injection](/docs/en/security#protect-against-prompt-injection) ## R ### Remote Control A way to continue a local Claude Code session from your phone or browser via claude.ai. Your code execution and files stay on your machine; the interface is remote. Different from Claude Code on the web, which runs in a cloud sandbox. Learn more: [Remote Control](/docs/en/remote-control) ### Rules Modular instruction files in `.claude/rules/` that load alongside CLAUDE.md. A rule can be path-scoped with YAML `paths:` frontmatter so it only loads when Claude reads a matching file, keeping context lean until it's relevant. Learn more: [Organize rules with `.claude/rules/`](/docs/en/memory#organize-rules-with-claude/rules/) ## S ### Sandboxing OS-level filesystem and network isolation for the Bash tool. Commands run inside a boundary you define upfront, so Claude can work freely within it without per-command approval prompts. Sandboxing is a separate layer from [permission rules](#permission-rule). Learn more: [Sandboxing](/docs/en/sandboxing) ### Session A conversation tied to your current directory, with its own independent [context window](#context-window). Sessions can be resumed with `claude -c`, forked with `--fork-session` to preserve history under a new session ID, or run in parallel across terminals. Running `/clear` starts a new session; the previous one stays stored and is available via `/resume`. Each session's transcript is stored under `~/.claude/projects/`. Learn more: [Work with sessions](/docs/en/how-claude-code-works#work-with-sessions) ### Settings layers The hierarchy Claude Code reads configuration from, in precedence order from highest to lowest: [managed policy](#managed-settings), command-line arguments, local settings at `.claude/settings.local.json`, project settings at `.claude/settings.json`, then user settings at `~/.claude/settings.json`. Arrays merge across layers; scalars at a higher layer override lower ones. See [Settings precedence](/docs/en/settings#settings-precedence). Learn more: [Settings files](/docs/en/settings#settings-files) ### Skill A `SKILL.md` file containing instructions, knowledge, or a workflow that Claude adds to its toolkit. Claude loads a skill automatically when relevant, or you invoke it directly with `/skill-name`. Skills follow the Agent Skills open standard; Claude Code extends it with invocation control and subagent execution. Skills are the recommended successor to custom commands. A file at `.claude/commands/deploy.md` and one at `.claude/skills/deploy/SKILL.md` both create `/deploy` and work the same way; existing command files continue to work. Learn more: [Extend Claude with skills](/docs/en/skills) ### Subagent A specialized AI assistant that runs in its own context window with a custom system prompt, specific tool access, and independent permissions. It works on a delegated task and returns a summary to the main conversation. Use subagents to keep large explorations out of your primary context or to run parallel research. Different from [agent teams](#agent-teams), where each agent is a full independent session you can talk to directly. Built-in subagents include Explore, Plan, and general-purpose. Learn more: [Create custom subagents](/docs/en/sub-agents) ### Surface Any place you access Claude Code: the CLI, VS Code, JetBrains, Desktop, or claude.ai. All surfaces share the same engine. Sessions on your machine read your local CLAUDE.md, settings, and skills; [cloud sessions](/docs/en/cloud-environments#what-carries-over-from-your-setup) start from a fresh clone of your repository and don't read `~/.claude/` on your machine. Slack and the Chrome extension are integrations that connect to a surface rather than surfaces themselves. Learn more: [Platforms and integrations](/docs/en/platforms) ## T ### Teleport A command, `/teleport`, that pulls a cloud Claude Code session into your local terminal. Claude fetches the branch, loads the conversation history, and resumes from the web session's last state. The reverse direction is `--cloud`, which sends a local task to run on the web. Learn more: [From web to terminal](/docs/en/claude-code-on-the-web#from-web-to-terminal)
Cut at 300 lines. The page has the rest.
goal First recorded · 140 lines, first recorded
# Keep Claude working toward a goal ## Compare ways to keep a session running ## Use `/goal` ### Set a goal ### Write an effective condition ### Check status ### Clear a goal ### Resume with an active goal ### Run non-interactively ## How evaluation works ## Requirements ## See also
The first capture of this source. The page was already there, and this is what it said.
# Keep Claude working toward a goal
> Set a completion condition with /goal and Claude keeps working across turns until the condition is met.
The `/goal` command sets a completion condition and Claude keeps working toward it without you prompting each step. After each turn, a small fast model checks whether the condition holds. If not, Claude starts another turn instead of returning control to you. The goal clears automatically once the condition is met.
Use a goal for substantial work with a verifiable end state:
* Migrating a module to a new API until every call site compiles and tests pass
* Implementing a design doc until all acceptance criteria hold
* Splitting a large file into focused modules until each is under a size budget
* Working through a labeled issue backlog until the queue is empty
## Compare ways to keep a session running
Three approaches keep the current session running between prompts. Pick based on what should start the next turn:
| Approach | Next turn starts when | Stops when |
| :------------------------------------------------------------------ | :------------------------- | :---------------------------------------------- |
| `/goal` | The previous turn finishes | A model confirms the condition is met |
| [`/loop`](/docs/en/scheduled-tasks#run-a-prompt-repeatedly-with-%2Floop) | A time interval elapses | You stop it, or Claude decides the work is done |
| [Stop hook](/docs/en/hooks-guide#prompt-based-hooks) | The previous turn finishes | Your own script or prompt decides |
`/goal` and a Stop hook both fire after every turn. `/goal` is a session-scoped shortcut: you type a condition and it's active for the current session only. A Stop hook lives in your settings file, applies to every session in its scope, and can run a script for deterministic checks or a prompt for model-evaluated ones.
[Auto mode](/docs/en/auto-mode-config) on its own approves tool calls within a single turn but doesn't start a new one. Claude stops when it judges the work done. `/goal` adds a separate evaluator that checks your condition after every turn, so completion is decided by a fresh model rather than the one doing the work. The two are complementary: auto mode removes per-tool prompts, and `/goal` removes per-turn prompts.
<Tip>
The approaches above keep the current session running. You can also schedule work that runs independent of any open session, such as nightly tests or morning triage. See [scheduling options](/docs/en/scheduled-tasks#compare-scheduling-options) for cloud routines and desktop scheduled tasks.
</Tip>
## Use `/goal`
One goal can be active per session. The same command sets, checks, and clears it depending on the argument.
### Set a goal
Run `/goal` followed by the condition you want satisfied. If a goal is already active, the new one replaces it.
```text theme={null}
/goal all tests in test/auth pass and the lint step is clean
```
Setting a goal starts a turn immediately, with the condition itself as the directive. You don't need to send a separate prompt. While the goal is active, a `◎ /goal active` indicator shows how long the goal has been running.
A goal doesn't change permissions. In the default permission mode, Claude still asks before tool calls that your settings don't already allow, such as the test command above. To let goal turns run unattended, pair `/goal` with [auto mode](/docs/en/auto-mode-config).
After each turn, the evaluator returns a short reason explaining why the condition is or isn't met. The most recent reason appears in the status view and in the transcript so you can see what Claude is working toward next.
### Write an effective condition
The [evaluator](#how-evaluation-works) judges your condition against what Claude has surfaced in the conversation. It doesn't run commands or read files independently, so write the condition as something Claude's own output can demonstrate. "All tests in `test/auth` pass" works because Claude runs the tests and the result lands in the transcript for the evaluator to read.
A condition that holds up across many turns usually has:
* **One measurable end state**: a test result, a build exit code, a file count, an empty queue
* **A stated check**: how Claude should prove it, such as "`npm test` exits 0" or "`git status` is clean"
* **Constraints that matter**: anything that must not change on the way there, such as "no other test file is modified"
The condition can be up to 4,000 characters.
To bound how long a goal runs, include a turn or time clause in the condition, such as `or stop after 20 turns`. Claude reports progress against that clause each turn and the evaluator judges it from the conversation.
### Check status
Run `/goal` with no arguments to see the current state.
```text theme={null}
/goal
```
If a goal is active, the status shows:
* The condition
* How long it has been running
* How many turns have been evaluated
* The current token spend
* The evaluator's most recent reason
The turn count and the most recent reason appear after the first evaluation has run.
If no goal is active but one was achieved earlier in the session, the status shows the achieved condition along with its duration, turn count, and token spend.
### Clear a goal
Run `/goal clear` to remove an active goal before its condition is met.
```text theme={null}
/goal clear
```
Claude prints `Goal cleared:` followed by the condition to confirm, or `No goal set` if nothing was active.
`stop`, `off`, `reset`, `none`, and `cancel` are accepted as aliases for `clear`. Running `/clear` to start a new conversation also removes any active goal.
### Resume with an active goal
A goal that was still active when a session ended is restored when you resume that session with `--resume` or `--continue`. The condition carries over, but the turn count, timer, and token-spend baseline all reset on resume. A goal that was already achieved or cleared is not restored.
### Run non-interactively
`/goal` works in [non-interactive mode](/docs/en/headless), in the [desktop app](/docs/en/desktop), and through [Remote Control](/docs/en/remote-control). Setting a goal with `-p` runs the loop to completion in a single invocation:
```bash theme={null}
claude -p "/goal CHANGELOG.md has an entry for every PR merged this week"
```
With the default text output, nothing prints until the condition is met, so a goal that runs many turns can look stuck. Add `--output-format stream-json --verbose` to emit each message as the loop runs.
Interrupt the process with Ctrl+C to stop a non-interactive goal before the condition is met.
## How evaluation works
`/goal` is a wrapper around a session-scoped [prompt-based Stop hook](/docs/en/hooks#prompt-based-hooks). Each time Claude finishes a turn, Claude Code sends the condition and the conversation so far to your configured [small fast model](/docs/en/model-config), which defaults to Haiku on the Claude API; on a third-party provider, check your [provider page](/docs/en/third-party-integrations) for the platform's default. The model answers yes or no and gives a short reason.
* **No**: Claude keeps working and takes the reason as guidance for the next turn.
* **Yes**: Claude Code clears the goal and records an achieved entry in the transcript.
To evaluate on a different model, set [`ANTHROPIC_DEFAULT_HAIKU_MODEL`](/docs/en/model-config#environment-variables).
<Warning>
Claude Code reads `ANTHROPIC_DEFAULT_HAIKU_MODEL` everywhere it uses the small fast model, not only for `/goal` evaluation. When you set it, Claude Code also resolves the [`haiku` alias](/docs/en/model-config#model-aliases) to that model and runs [background functionality](/docs/en/costs#background-token-usage), such as conversation summarization, on it.
</Warning>
The evaluator runs on whichever provider your session is configured for. It does not call tools, so it can only judge what Claude has already surfaced in the conversation.
<Note>
Evaluation tokens are billed on the small fast model configured for your provider and are typically negligible compared to main-turn spend.
</Note>
## Requirements
Claude Code makes `/goal` available under the same [workspace trust rule as hooks in settings files](/docs/en/permissions#what-runs-before-you-trust-a-folder), because the evaluator is part of the hooks system. `/goal` is also unavailable when [`disableAllHooks`](/docs/en/hooks#disable-or-remove-hooks) is `true` after settings precedence applies, or when [`allowManagedHooksOnly`](/docs/en/settings#hook-configuration) is set in managed settings. In each case, the command tells you why instead of silently doing nothing.
## See also
* [Run a prompt repeatedly with `/loop`](/docs/en/scheduled-tasks#run-a-prompt-repeatedly-with-%2Floop): re-run on a time interval instead of until a condition holds
* [Prompt-based hooks](/docs/en/hooks-guide#prompt-based-hooks): write your own Stop hook when you need custom evaluation logic
* [Auto mode](/docs/en/auto-mode-config): approve tool calls automatically so each goal turn runs unattended
* [Scheduling comparison](/docs/en/scheduled-tasks#compare-scheduling-options): run work on a schedule independent of any open session
google-vertex-ai First recorded · 306 lines, first recorded
# Claude Code on Google Cloud's Agent Platform ## Prerequisites ## Sign in with Agent Platform ## Region configuration ## Set up manually ### 1. Enable Agent Platform API ### 2. Request model access ### 3. Configure GCP credentials #### Advanced credential configuration ### 4. Configure Claude Code ### 5. Pin model versions ### 6. Verify your configuration ## Startup model checks ## IAM configuration ## 1M token context window ## Troubleshooting ## Additional resources
The first capture of this source. The page was already there, and this is what it said.
# Claude Code on Google Cloud's Agent Platform
> Learn about configuring Claude Code through Google Cloud's Agent Platform, formerly Vertex AI, including setup, IAM configuration, and troubleshooting.
export const ContactSalesCard = ({surface}) => {
const utm = content => `utm_source=claude_code&utm_medium=docs&utm_content=${surface}_${content}`;
const iconArrowRight = (size = 13) => <svg width={size} height={size} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
<line x1="5" y1="12" x2="19" y2="12" />
<polyline points="12 5 19 12 12 19" />
</svg>;
const STYLES = `
.cc-cs {
--cs-slate: #141413;
--cs-clay: #d97757;
--cs-clay-deep: #c6613f;
--cs-gray-000: #ffffff;
--cs-gray-700: #3d3d3a;
--cs-border-default: rgba(31, 30, 29, 0.15);
font-family: inherit;
}
.dark .cc-cs {
--cs-slate: #f0eee6;
--cs-gray-000: #262624;
--cs-gray-700: #bfbdb4;
--cs-border-default: rgba(240, 238, 230, 0.14);
}
.cc-cs-card {
display: flex; align-items: center; justify-content: space-between;
gap: 16px; padding: 14px 16px; margin: 0;
background: var(--cs-gray-000); border: 0.5px solid var(--cs-border-default);
border-radius: 8px; flex-wrap: wrap;
}
.cc-cs-text { font-size: 13px; color: var(--cs-gray-700); line-height: 1.5; flex: 1; min-width: 240px; }
.cc-cs-text strong { font-weight: 550; color: var(--cs-slate); }
.cc-cs-actions { display: flex; align-items: center; gap: 8px; flex-shrink: 0; }
.cc-cs-btn-clay {
display: inline-flex; align-items: center; gap: 8px;
background: var(--cs-clay-deep); color: #fff; border: none;
border-radius: 8px; padding: 8px 14px;
font-size: 13px; font-weight: 500;
transition: background-color 0.15s; white-space: nowrap;
}
.cc-cs-btn-clay:hover { background: var(--cs-clay); }
.cc-cs-btn-ghost {
display: inline-flex; align-items: center; gap: 8px;
background: transparent; color: var(--cs-gray-700);
border: 0.5px solid var(--cs-border-default);
border-radius: 8px; padding: 8px 14px;
font-size: 13px; font-weight: 500;
}
.cc-cs-btn-ghost:hover { background: rgba(0, 0, 0, 0.04); }
.dark .cc-cs-btn-ghost:hover { background: rgba(255, 255, 255, 0.04); }
@media (max-width: 720px) {
.cc-cs-actions { width: 100%; }
}
`;
return <div className="cc-cs not-prose">
<style>{STYLES}</style>
<div className="cc-cs-card">
<div className="cc-cs-text">
<strong>Deploying Claude Code across your organization?</strong> Talk to sales about enterprise plans, SSO, and centralized billing.
</div>
<div className="cc-cs-actions">
<a href={`https://claude.com/pricing?${utm('view_plans')}#plans-business`} className="cc-cs-btn-ghost">
View plans
</a>
<a href={`https://claude.com/contact-sales?${utm('contact_sales')}`} className="cc-cs-btn-clay">
Contact sales {iconArrowRight()}
</a>
</div>
</div>
</div>;
};
<ContactSalesCard surface="vertex" />
## Prerequisites
Before configuring Claude Code with Google Cloud's Agent Platform, formerly Vertex AI, ensure you have:
* A Google Cloud Platform (GCP) account with billing enabled
* A GCP project with Google Cloud's Agent Platform API enabled
* Access to desired Claude models (for example, Claude Sonnet 4.6)
* Google Cloud SDK (`gcloud`) installed and configured
* Quota allocated in desired GCP region
To sign in with your own Google Cloud's Agent Platform credentials, follow [Sign in with Google Cloud's Agent Platform](#sign-in-with-agent-platform) below. To deploy Claude Code across a team, use the [manual setup](#set-up-manually) steps and [pin your model versions](#5-pin-model-versions) before rolling out.
## Sign in with Agent Platform
If you have Google Cloud credentials and want to start using Claude Code through Google Cloud's Agent Platform, the login wizard walks you through it. You complete the GCP-side prerequisites once per project; the wizard handles the Claude Code side.
<Steps>
<Step title="Enable Claude models in your GCP project">
[Enable Google Cloud's Agent Platform API](#1-enable-agent-platform-api) for your project, then request access to the Claude models you want in the [Google Cloud's Agent Platform Model Garden](https://console.cloud.google.com/vertex-ai/model-garden). See [IAM configuration](#iam-configuration) for the permissions your account needs.
</Step>
<Step title="Start Claude Code and choose Google Cloud's Agent Platform">
Run `claude`. At the login prompt, select **3rd-party platform**, then **Google Vertex AI**, the label the login prompt still uses for Google Cloud's Agent Platform. If you're already signed in, run `/login` to open the same menu.
</Step>
<Step title="Follow the wizard prompts">
Choose how you authenticate to Google Cloud: Application Default Credentials from `gcloud`, a service account key file, or credentials already in your environment. The wizard detects your project and region, verifies which Claude models your project can invoke, and lets you pin them. It saves the result to the `env` block of your [user settings file](/docs/en/settings), so you don't need to export environment variables yourself.
</Step>
</Steps>
After you've signed in, run `/setup-vertex` any time to reopen the wizard and change your credentials, project, region, or model pins. The model pin step starts from your currently pinned models. The wizard writes to `~/.claude/settings.json`, or to `$CLAUDE_CONFIG_DIR/settings.json` when [`CLAUDE_CONFIG_DIR`](/docs/en/env-vars#variables) is set.
## Region configuration
Claude Code supports Google Cloud's Agent Platform [global](https://cloud.google.com/blog/products/ai-machine-learning/global-endpoint-for-claude-models-generally-available-on-vertex-ai), multi-region, and regional endpoints. Set `CLOUD_ML_REGION` to `global`, a multi-region location such as `eu` or `us`, or a specific region such as `us-east5`. Claude Code selects the correct Google Cloud's Agent Platform hostname for each form, including the `aiplatform.eu.rep.googleapis.com` and `aiplatform.us.rep.googleapis.com` hosts for multi-region locations.
<Note>
Google Cloud's Agent Platform may not support the Claude Code default models on every endpoint type. Model availability varies across [specific regions](https://cloud.google.com/vertex-ai/generative-ai/docs/learn/locations#genai-partner-models), multi-region locations, and [global endpoints](https://cloud.google.com/vertex-ai/generative-ai/docs/partner-models/use-partner-models#supported_models). You may need to switch to a supported location or specify a supported model.
</Note>
## Set up manually
To configure Google Cloud's Agent Platform through environment variables instead of the wizard, for example in CI or a scripted enterprise rollout, follow the steps below.
### 1. Enable Agent Platform API
Enable Google Cloud's Agent Platform API in your GCP project. Replace `YOUR-PROJECT-ID` with your GCP project ID here and in the configuration step below:
```bash theme={null}
# Set your project ID
gcloud config set project YOUR-PROJECT-ID
# Enable Agent Platform API
gcloud services enable aiplatform.googleapis.com
```
### 2. Request model access
Request access to Claude models in Google Cloud's Agent Platform:
1. Navigate to the [Google Cloud's Agent Platform Model Garden](https://console.cloud.google.com/vertex-ai/model-garden)
2. Search for "Claude" models
3. Request access to desired Claude models (for example, Claude Sonnet 4.6)
4. Wait for approval (may take 24-48 hours)
### 3. Configure GCP credentials
Claude Code uses standard Google Cloud authentication.
For more information, see [Google Cloud authentication documentation](https://cloud.google.com/docs/authentication).
Claude Code supports [X.509 certificate-based Workload Identity Federation](https://cloud.google.com/iam/docs/workload-identity-federation-with-x509-certificates) through the same Application Default Credentials chain. Set `GOOGLE_APPLICATION_CREDENTIALS` to the path of your credential configuration file.
<Note>
Claude Code uses `ANTHROPIC_VERTEX_PROJECT_ID` as the project ID for Google Cloud's Agent Platform requests. The `GCLOUD_PROJECT` and `GOOGLE_CLOUD_PROJECT` environment variables and the credential file referenced by `GOOGLE_APPLICATION_CREDENTIALS` take precedence over it. If none of these are set, the project ID is resolved from your `gcloud` configuration or the attached service account.
</Note>
#### Advanced credential configuration
Claude Code supports automatic credential refresh for GCP through the `gcpAuthRefresh` setting. Add it to your Claude Code [settings file](/docs/en/settings), for example `~/.claude/settings.json`. When Claude Code detects that your GCP credentials are expired or cannot be loaded, it runs the configured command to obtain new credentials before retrying the request.
```json theme={null}
{
"gcpAuthRefresh": "gcloud auth application-default login",
"env": {
"ANTHROPIC_VERTEX_PROJECT_ID": "your-project-id"
}
}
```
Claude Code shows you the command's output, but can't send the command interactive input. This works well for browser-based authentication flows where the CLI shows a URL and you complete authentication in the browser. The refresh command times out after three minutes if authentication does not complete. If you set `gcpAuthRefresh` in project settings such as `.claude/settings.json`, Claude Code runs it under the same [workspace trust rule as hooks in settings files](/docs/en/permissions#what-runs-before-you-trust-a-folder), which includes `-p` sessions in folders you've never trusted.
### 4. Configure Claude Code
Set the following environment variables:
```bash theme={null}
# Enable Agent Platform integration
export CLAUDE_CODE_USE_VERTEX=1
export CLOUD_ML_REGION=global
export ANTHROPIC_VERTEX_PROJECT_ID=YOUR-PROJECT-ID
# Optional: Override the Agent Platform endpoint URL for custom endpoints or gateways
# export ANTHROPIC_VERTEX_BASE_URL=https://aiplatform.googleapis.com
# When CLOUD_ML_REGION=global, override region for models that don't support global endpoints
export VERTEX_REGION_CLAUDE_HAIKU_4_5=us-east5
export VERTEX_REGION_CLAUDE_4_6_SONNET=europe-west1
```
Most model versions have a corresponding `VERTEX_REGION_CLAUDE_*` variable. See the [Environment variables reference](/docs/en/env-vars) for the full list. Check [Google Cloud's Agent Platform Model Garden](https://console.cloud.google.com/vertex-ai/model-garden) to determine which models support global endpoints versus regional only.
[Prompt caching](/docs/en/prompt-caching) is enabled automatically. To disable it, set `DISABLE_PROMPT_CACHING=1`. To request a 1-hour cache TTL instead of the 5-minute default, set `ENABLE_PROMPT_CACHING_1H=1`; cache writes with a 1-hour TTL are billed at a higher rate. For heightened rate limits, contact Google Cloud support. When using Google Cloud's Agent Platform, the `/logout` command is unavailable since authentication is handled through Google Cloud credentials.
Claude Code decides between [MCP tool search](/docs/en/mcp#scale-with-mcp-tool-search) and upfront loading by model generation:
* **Claude Opus 4.5, Sonnet 4.5, Haiku 4.5, and later**: Claude Code enables tool search by default.
* **Earlier models, including all Claude 3.x models**: Claude Code loads MCP tool definitions upfront, because their Agent Platform serving stacks reject the required beta header. Setting `ENABLE_TOOL_SEARCH=true` doesn't override this.
Set `ENABLE_TOOL_SEARCH=false` to disable tool search on every model. Before v2.1.221, Claude Code disabled tool search for all models on Google Cloud's Agent Platform unless you set `ENABLE_TOOL_SEARCH=true`.
### 5. Pin model versions
<Warning>
Pin specific model versions when deploying to multiple users. Without pinning, model aliases such as `sonnet` and `opus` resolve to Claude Code's built-in default for Google Cloud's Agent Platform, which can lag the newest release and may not yet be enabled in your project. Claude Code [falls back](#startup-model-checks) to an earlier or lower-tier model at startup when the default is unavailable, but pinning lets you control when your users move to a new model.
</Warning>
Set these environment variables to specific Google Cloud's Agent Platform model IDs.
Without `ANTHROPIC_DEFAULT_OPUS_MODEL`, the `opus` alias on Google Cloud's Agent Platform resolves to Opus 5, and without `ANTHROPIC_DEFAULT_SONNET_MODEL`, the `sonnet` alias resolves to Sonnet 4.5. This example pins each alias to a specific version:
```bash theme={null}
export ANTHROPIC_DEFAULT_OPUS_MODEL='claude-opus-4-8'
export ANTHROPIC_DEFAULT_SONNET_MODEL='claude-sonnet-5'
export ANTHROPIC_DEFAULT_HAIKU_MODEL='claude-haiku-4-5@20251001'
```
For current and legacy model IDs, see [Models overview](https://platform.claude.com/docs/en/about-claude/models/overview). See [Model configuration](/docs/en/model-config#pin-models-for-third-party-deployments) for the full list of environment variables.
Claude Code uses these default models when no pinning variables are set:
| Model type | Default value |
| :--------------- | :--------------------------- |
| Primary model | `claude-opus-5` |
| Small/fast model | `claude-sonnet-4-5@20250929` |
Background tasks such as session title generation use the small/fast model, normally a Haiku-class model. On Google Cloud's Agent Platform, Claude Code uses the default Sonnet model for background tasks because Haiku may not be enabled in every project or region. Two selections change which model carries them:
* When you select a primary model with `--model`, `ANTHROPIC_MODEL`, or the `model` setting, background tasks use that model. Setting `ANTHROPIC_DEFAULT_OPUS_MODEL` without `ANTHROPIC_DEFAULT_SONNET_MODEL` counts as a selection too, because the built-in Sonnet model may not be enabled in a project that steers its own Opus.
* To use Haiku for background tasks, set `ANTHROPIC_DEFAULT_HAIKU_MODEL` to a model ID that is available in your project.
<Warning>
Opus models have a higher per-token price than Sonnet models, so a deployment that doesn't pin a primary model is billed at the Opus rate once it updates to v2.1.207 or later. To keep Sonnet 4.5 as the primary model, set `ANTHROPIC_MODEL` to its full model ID. A deployment that steers the default with `ANTHROPIC_DEFAULT_SONNET_MODEL` and doesn't set `ANTHROPIC_DEFAULT_OPUS_MODEL` keeps its steered Sonnet model as the default.
</Warning>
On v2.1.207 through v2.1.218, the primary model on Google Cloud's Agent Platform defaulted to Opus 4.8 and the `opus` alias resolved to Opus 4.8. Before v2.1.207, the primary model defaulted to Sonnet 4.5, the `opus` alias resolved to Opus 4.6, and background tasks always used the primary model.
To customize models further:
```bash theme={null}
export ANTHROPIC_MODEL='claude-opus-4-8'
export ANTHROPIC_DEFAULT_HAIKU_MODEL='claude-haiku-4-5@20251001'
```
### 6. Verify your configuration
Start Claude Code and run `/status` to confirm the setup. The `API provider` line shows `Google Vertex AI`, and the `GCP project`, `Default region`, and `Model` lines show your project ID, region, and resolved model. If the provider line is missing, the environment variables aren't reaching the process. Confirm they are exported in the shell where you launched `claude`, or set them in the `env` block of your [settings file](/docs/en/settings).
## Startup model checks
When Claude Code starts with Google Cloud's Agent Platform configured, it verifies that the models it intends to use are accessible in your project.
If you have pinned a model version that is older than the current Claude Code default, and your project can invoke the newer version, Claude Code prompts you to update the pin. Accepting writes the new model ID to your [user settings file](/docs/en/settings) and restarts Claude Code. Declining is remembered until the next default version change.
If you have not pinned a model and the current default is unavailable in your project, Claude Code falls back for the current session and shows a notice. It tries earlier versions of the default model first and, when the default is an Opus model and no Opus version is available, falls back to the default Sonnet model. The fallback is not persisted. Enable the newer model in [Model Garden](https://console.cloud.google.com/vertex-ai/model-garden) or [pin a version](#5-pin-model-versions) to make the choice permanent.
When you start the session on a specific Sonnet or Opus version, with `--model`, `ANTHROPIC_MODEL`, or the [`model` setting](/docs/en/settings), that version acts as the session's pinned default for the matching `sonnet` or `opus` alias. Claude Code skips the availability check for the built-in default your model replaces and starts on the model you configured, with no fallback notice.
Model aliases such as `opus` don't act as pins, and neither does a model ID Claude Code doesn't recognize.
## IAM configuration
Assign the `roles/aiplatform.user` role, which includes the required permissions:
* `aiplatform.endpoints.predict` - Required for model invocation and token counting
For more restrictive permissions, create a custom role with only the permissions above.
For details, see [Google Cloud's Agent Platform IAM documentation](https://cloud.google.com/vertex-ai/docs/general/access-control).
<Note>
Create a dedicated GCP project for Claude Code to simplify cost tracking and access control.
</Note>
## 1M token context window
Claude Sonnet 5, Opus 4.6 and later, and Sonnet 4.6 support the [1M token context window](https://platform.claude.com/docs/en/build-with-claude/context-windows#context-window-sizes-by-model) on Google Cloud's Agent Platform. Sonnet 5 always runs with the 1M window, with no `[1m]` variant to select. For the other models, Claude Code automatically enables the extended context window when you select a 1M model variant.
The [setup wizard](#sign-in-with-agent-platform) offers a 1M context option when it pins models. To enable it for a manually pinned model instead, append `[1m]` to the model ID. See [Pin models for third-party deployments](/docs/en/model-config#pin-models-for-third-party-deployments) for details.
## Troubleshooting
If you encounter "Could not load the default credentials" errors:
* Run `gcloud auth application-default login` to set up Application Default Credentials
* Set `GOOGLE_APPLICATION_CREDENTIALS` to a service account key file path
* See [Configure GCP credentials](#3-configure-gcp-credentials) for all options
If you encounter quota issues:
* Check current quotas or request quota increase through [Cloud Console](https://cloud.google.com/docs/quotas/view-manage)
If you encounter "model not found" 404 errors:
* Confirm model is Enabled in [Model Garden](https://console.cloud.google.com/vertex-ai/model-garden)
* Verify the model is available in the location you specified. Some models are offered only on `global` or multi-region locations such as `eu` and `us`, not in specific regions
* If using `CLOUD_ML_REGION=global`, check that your models support global endpoints in [Model Garden](https://console.cloud.google.com/vertex-ai/model-garden) under "Supported features". For models that don't support global endpoints, either:
* Specify a supported model via `ANTHROPIC_MODEL` or `ANTHROPIC_DEFAULT_HAIKU_MODEL`, or
* Set a region or multi-region location using `VERTEX_REGION_<MODEL_NAME>` environment variables
If you encounter 429 errors:
* For regional endpoints, ensure the primary model and small/fast model are supported in your selected region
* Consider switching to `CLOUD_ML_REGION=global` for better availability
Cut at 300 lines. The page has the rest.
headless First recorded · 311 lines, first recorded
# Run Claude Code programmatically ## Basic usage ### Start faster with bare mode ### Background tasks at exit ## Examples ### Pipe data through Claude ### Add Claude to a build script ### Get structured output ### Stream responses #### Follow subagent messages #### Handle API retries #### Read session metadata #### Fail CI when a plugin or MCP server doesn't load #### Track plugin installs ### Auto-approve tools ### Create a commit ### Customize the system prompt ### Continue conversations ## Next steps
The first capture of this source. The page was already there, and this is what it said.
# Run Claude Code programmatically
> Use the Agent SDK to run Claude Code programmatically from the CLI, Python, or TypeScript.
The [Agent SDK](/docs/en/agent-sdk/overview) gives you the same tools, agent loop, and context management that power Claude Code. It's available as a CLI for scripts and CI/CD, or as [Python](/docs/en/agent-sdk/python) and [TypeScript](/docs/en/agent-sdk/typescript) packages for full programmatic control.
To run Claude Code in non-interactive mode, pass `-p` with your prompt and the [CLI options](/docs/en/cli-reference) you need:
```bash theme={null}
claude -p "Find and fix the bug in auth.py" --allowedTools "Read,Edit,Bash"
```
This page covers using the Agent SDK via the CLI (`claude -p`). For the Python and TypeScript SDK packages with structured outputs, tool approval callbacks, and native message objects, see the [full Agent SDK documentation](/docs/en/agent-sdk/overview).
## Basic usage
Add the `-p` (or `--print`) flag to any `claude` command to run it non-interactively. Not every [CLI option](/docs/en/cli-reference) combines with `-p`. Claude Code rejects `--bg`, and rejects `--cloud` with a task description, with an error naming the conflict; `--cloud` with a session ID and `-p` instead [queues a message into that cloud session](/docs/en/claude-code-on-the-web#send-follow-ups-from-the-cli) and exits. Options you'll combine with `-p` often include:
* `--continue` for [continuing conversations](#continue-conversations)
* `--allowedTools` for [auto-approving tools](#auto-approve-tools)
* `--output-format` for [structured output](#get-structured-output)
This example asks Claude a question about your codebase and prints the response:
```bash theme={null}
claude -p "What does the auth module do?"
```
Claude Code exits with code 0 on success and a non-zero code when the run fails, so your scripts can branch on the exit status. If you pass an invalid flag, Claude Code reports the error to stderr before the run starts. When a failure happens inside the run, such as missing authentication, Claude Code prints the failure as the result on stdout.
### Start faster with bare mode
Add `--bare` to reduce startup time by skipping auto-discovery of hooks, skills, plugins, MCP servers, auto memory, and CLAUDE.md. Without it, `claude -p` loads the same [context](/docs/en/how-claude-code-works#the-context-window) an interactive session would, including anything configured in the working directory or `~/.claude`.
Bare mode is useful for CI and scripts where you need the same result on every machine. A hook in a teammate's `~/.claude` or an MCP server in the project's `.mcp.json` won't run, because bare mode never reads them.
Without `--bare`, Claude Code runs the hooks in a project's `.claude/settings.json` even in a folder you've never trusted, because a `-p` session shows no workspace trust dialog. It also connects the servers in the project's `.mcp.json`, because a `-p` session can't show the per-server approval prompt either. [What runs before you trust a folder](/docs/en/permissions#what-runs-before-you-trust-a-folder) covers each kind of repository content under `-p` and how to keep it out.
This example runs a one-off summarize task in bare mode and pre-approves the Read tool so the call completes without a permission prompt. Set `ANTHROPIC_API_KEY` before running it, because bare mode doesn't use your subscription login:
```bash theme={null}
claude --bare -p "Summarize README.md" --allowedTools "Read"
```
In bare mode, Claude Code never reads OAuth credentials or the system keychain. For the Anthropic API, set `ANTHROPIC_API_KEY` in the environment, with a key created in the [Claude Console](https://platform.claude.com), or supply an `apiKeyHelper` in the `--settings` JSON. Amazon Bedrock, Google Cloud's Agent Platform, and Microsoft Foundry continue to read their own provider credentials as usual.
In bare mode Claude has access to the Bash, file read, and file edit tools. Pass any context you need with a flag:
| To load | Use |
| ----------------------- | ------------------------------------------------------- |
| System prompt additions | `--append-system-prompt`, `--append-system-prompt-file` |
| Settings | `--settings <file-or-json>` |
| MCP servers | `--mcp-config <file-or-json>` |
| Custom agents | `--agents <json>` |
| A plugin | `--plugin-dir <path>`, `--plugin-url <url>` |
<Note>
`--bare` is the recommended mode for scripted and SDK calls, and will become the default for `-p` in a future release.
</Note>
### Background tasks at exit
If Claude starts a [background Bash task](/docs/en/tools-reference#bash-tool-behavior) during a `claude -p` run, for example a dev server or a watch build, that shell is terminated about five seconds after Claude has returned its final result and stdin has closed. The grace period lets a task that finishes right after the result still deliver its output. Before v2.1.163, a never-exiting background process would hold the `claude -p` invocation open indefinitely.
Background [subagents](/docs/en/sub-agents) and workflows are exempt from the five-second grace because their result is part of the final output, so `claude -p` waits for them to complete. From v2.1.182, that wait is capped at ten minutes by default so a stuck background agent cannot hold the process open indefinitely. Adjust the cap with [`CLAUDE_CODE_PRINT_BG_WAIT_CEILING_MS`](/docs/en/env-vars), or set it to `0` to wait without a limit.
If you stop a `claude -p` run with SIGTERM, for example from `kill`, a process supervisor, or an SDK host closing the session, Claude Code aborts the in-progress turn, terminates the process tree of any running Bash command, runs [`SessionEnd` hooks](/docs/en/hooks#sessionend), and exits with code 143.
## Examples
These examples highlight common CLI patterns. Where a command names a file such as `auth.py` or `build-error.txt`, substitute a file from your own project. In CI or other scripted environments, add [`--bare`](#start-faster-with-bare-mode) so Claude Code starts without loading the host's hooks, plugins, auto memory, or `CLAUDE.md`.
### Pipe data through Claude
Non-interactive mode reads stdin, so you can pipe data in and redirect the response out like any other command-line tool.
This example pipes a build log into Claude and writes the explanation to a file:
```bash theme={null}
cat build-error.txt | claude -p 'concisely explain the root cause of this build error' > output.txt
```
With `--output-format json`, the response payload includes `total_cost_usd` and a per-model cost breakdown, so scripted callers can track spend per invocation without consulting the [usage dashboard](/docs/en/costs). Both figures are [client-side estimates](/docs/en/agent-sdk/cost-tracking) and can differ from your actual bill.
<Note>
Piped stdin is capped at 10MB. If you exceed the cap, Claude Code exits with a clear error and a non-zero status. To work with larger inputs, write the content to a file and reference the file path in your prompt instead of piping it.
</Note>
If Claude Code can't read stdin, for example because the process that started it disconnected its end, Claude Code prints a warning to stderr and continues with the prompt from the command line. Before v2.1.211, an unreadable stdin on Windows crashed the session or made it exit silently with no output.
### Add Claude to a build script
You can wrap a non-interactive call in a script to use Claude as a project-specific linter or reviewer.
This `package.json` script pipes the diff against `main` into Claude and asks it to report typos. Piping the diff means Claude doesn't need Bash permission to read it, and the escaped double quotes keep the script portable to Windows:
```json theme={null}
{
"scripts": {
"lint:claude": "git diff main | claude -p \"you are a typo linter. for each typo in this diff, report filename:line on one line and the issue on the next. return nothing else.\""
}
}
```
Run it with `npm run lint:claude`.
### Get structured output
Use `--output-format` to control how responses are returned:
* `text` (default): plain text output
* `json`: structured JSON with result, session ID, and metadata
* `stream-json`: newline-delimited JSON for real-time streaming
This example returns a project summary as JSON with session metadata, with the text result in the `result` field:
```bash theme={null}
claude -p "Summarize this project" --output-format json
```
To get output conforming to a specific schema, use `--output-format json` with `--json-schema` and a [JSON Schema](https://json-schema.org/) definition. The response includes metadata about the request (session ID, usage, etc.) with the structured output in the `structured_output` field.
This example extracts function names and returns them as an array of strings:
```bash theme={null}
claude -p "Extract the main function names from auth.py" \
--output-format json \
--json-schema '{"type":"object","properties":{"functions":{"type":"array","items":{"type":"string"}}},"required":["functions"]}'
```
If the value isn't a valid JSON Schema, `claude` exits with `Error: --json-schema is not a valid JSON Schema` followed by the validator's diagnostic. Claude Code accepts schemas that use the `format` keyword, such as `"format": "email"`, but treats `format` as an annotation and doesn't enforce it. Before v2.1.205, Claude Code silently ignored an invalid schema and returned unstructured text, and treated any schema containing `format` as invalid.
<Tip>
Use a tool like [jq](https://jqlang.org/) to parse the response and extract specific fields:
```bash theme={null}
# Extract the text result
claude -p "Summarize this project" --output-format json | jq -r '.result'
# Extract structured output
claude -p "Extract function names from auth.py" \
--output-format json \
--json-schema '{"type":"object","properties":{"functions":{"type":"array","items":{"type":"string"}}},"required":["functions"]}' \
| jq '.structured_output'
```
</Tip>
### Stream responses
Use `--output-format stream-json` with `--verbose` and `--include-partial-messages` to receive tokens as they're generated. Each line is a JSON object representing an event:
```bash theme={null}
claude -p "Explain recursion" --output-format stream-json --verbose --include-partial-messages
```
The last line of the stream is a `result` message with the final response text, cost, and session metadata.
If your consumer reads the stream slowly, Claude Code waits for the queued output to drain before exiting, scaling the wait with how much is still queued, capped at 30 seconds. Before v2.1.214 the exit wait was capped at about two seconds, which could cut off the end of a large response.
The following example uses [jq](https://jqlang.org/) to filter for text deltas and display just the streaming text. The `-r` flag outputs raw strings (no quotes) and `-j` joins without newlines so tokens stream continuously:
```bash theme={null}
claude -p "Write a poem" --output-format stream-json --verbose --include-partial-messages | \
jq -rj 'select(.type == "stream_event" and .event.delta.type? == "text_delta") | .event.delta.text'
```
For programmatic streaming with callbacks and message objects, see [Stream responses in real-time](/docs/en/agent-sdk/streaming-output) in the Agent SDK documentation.
#### Follow subagent messages
Messages from [subagents](/docs/en/sub-agents) appear in the stream as `assistant` and `user` messages whose `parent_tool_use_id` field is the ID of the tool call that spawned the subagent. Messages from the main conversation carry `null` in that field.
By default, Claude Code emits only subagent `tool_use` and `tool_result` blocks. Pass [`--forward-subagent-text`](/docs/en/cli-reference#cli-flags) or set [`CLAUDE_CODE_FORWARD_SUBAGENT_TEXT`](/docs/en/env-vars) to also emit subagent text and thinking blocks, so you can reconstruct each subagent's transcript. This requires Claude Code v2.1.211 or later.
When you enable either option, Claude Code forwards messages from [subagents at every nesting depth](/docs/en/sub-agents#let-subagents-spawn-their-own-subagents): when a subagent spawns its own subagent, the nested subagent's messages carry the ID of the Agent tool call that spawned it in `parent_tool_use_id`, so you can rebuild the full nesting tree by following those IDs. Before v2.1.219, messages from nested subagents didn't appear in the stream.
#### Handle API retries
When an API request fails with a retryable error, Claude Code emits a `system/api_retry` event before retrying. You can use this to surface retry progress or implement custom backoff logic.
| Field | Type | Description |
| ---------------- | --------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `type` | `"system"` | message type |
| `subtype` | `"api_retry"` | identifies this as a retry event |
| `attempt` | integer | current attempt number, starting at 1 |
| `max_retries` | integer | total retries permitted |
| `retry_delay_ms` | integer | milliseconds until the next attempt |
| `error_status` | integer or null | HTTP status code, or `null` for connection errors with no HTTP response |
| `error` | string | error category: `authentication_failed`, `oauth_org_not_allowed`, `billing_error`, `rate_limit`, `overloaded`, `invalid_request`, `model_not_found`, `server_error`, `max_output_tokens`, or `unknown` |
| `uuid` | string | unique event identifier |
| `session_id` | string | session the event belongs to |
#### Read session metadata
The `system/init` event reports session metadata including the model, tools, MCP servers, and loaded plugins. It is the first event in the stream unless startup events precede it:
* `plugin_install` events, when [`CLAUDE_CODE_SYNC_PLUGIN_INSTALL`](/docs/en/env-vars) is set.
* [`hook_started`, `hook_progress`, and `hook_response` events](/docs/en/agent-sdk/typescript#sdkhookstartedmessage), while a configured [`SessionStart`](/docs/en/hooks#sessionstart) or [`Setup`](/docs/en/hooks#setup) hook runs. These stream as the hook produces them. Claude Code v2.1.169 through v2.1.203 delivered them in one batch after the hook completed, still ahead of `system/init`; v2.1.204 restored live delivery.
The event also carries an optional `capabilities` array of strings naming the protocol behaviors this Claude Code version implements, such as `interrupt_receipt_v1` or `interrupt_cancel_queued_v1`. Check it to feature-detect instead of comparing version strings, and ignore values you don't recognize. The field requires Claude Code v2.1.205 or later and is absent from earlier versions. See [`SDKSystemMessage`](/docs/en/agent-sdk/typescript#sdksystemmessage) for the capability list.
#### Fail CI when a plugin or MCP server doesn't load
Use the plugin fields in the `system/init` event to catch a plugin that didn't load:
| Field | Type | Description |
| --------------- | ----- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `plugins` | array | plugins that loaded successfully, each with `name` and `path` |
| `plugin_errors` | array | plugin load-time errors, each with `plugin`, `type`, and `message`. Includes unsatisfied dependency versions and `--plugin-dir` load failures such as a missing path or invalid archive. Affected plugins are demoted and absent from `plugins`. The key is omitted when there are no errors |
Use the MCP server fields the same way. When you pass [`--mcp-config`](/docs/en/cli-reference#cli-flags) with `-p`, Claude Code waits for still-pending servers before running the first turn, up to the [`MCP_TIMEOUT`](/docs/en/env-vars) startup timeout, 30 seconds by default. A remote server with a [cached tool list](/docs/en/agent-sdk/mcp#connection-timing) skips the wait, shows `pending` in `system/init`, and connects on its first tool call. The wait requires Claude Code v2.1.221 or later.
Claude Code validates each `--mcp-config` entry at startup and skips entries that fail validation, for example a `url` entry with no `type`. The run continues and exits cleanly, so check these fields to catch a server that never loaded:
| Field | Type | Description |
| ------------------- | ----- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `mcp_servers` | array | MCP servers in the session, each with `name` and `status` |
| `mcp_server_errors` | array | `--mcp-config` entries skipped by config validation, each with `name`, `type`, and `message`. `type` is a skip category such as `unknown_type`, `url_missing_type`, `invalid_config`, or `reserved_name`; treat values you don't recognize as a generic skip. Affected servers are absent from `mcp_servers`. The key is omitted when there are no errors, so a CI gate can fail on a non-empty array. Requires Claude Code v2.1.219 or later |
When you run the command by hand in a terminal, Claude Code also prints a startup warning to stderr, such as `Warning: 1 MCP server skipped due to invalid config:`, followed by the reason for each skipped entry. When you redirect stderr, or when a program such as a CI runner or an SDK host captures it, Claude Code prints no warning and reports the skipped entries only in the `mcp_server_errors` field. The warning requires Claude Code v2.1.219 or later.
#### Track plugin installs
When [`CLAUDE_CODE_SYNC_PLUGIN_INSTALL`](/docs/en/env-vars) is set, Claude Code emits `system/plugin_install` events while marketplace plugins install before the first turn. Use these to surface install progress in your own UI.
| Field | Type | Description |
| ------------ | -------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------- |
| `type` | `"system"` | message type |
| `subtype` | `"plugin_install"` | identifies this as a plugin install event |
| `status` | `"started"`, `"installed"`, `"failed"`, or `"completed"` | `started` and `completed` bracket the overall install; `installed` and `failed` report individual marketplaces |
| `name` | string, optional | marketplace name, present on `installed` and `failed` |
| `error` | string, optional | failure message, present on `failed` |
| `uuid` | string | unique event identifier |
| `session_id` | string | session the event belongs to |
### Auto-approve tools
Use `--allowedTools` to let Claude use certain tools without prompting. This example runs a test suite and fixes failures, allowing Claude to execute Bash commands and read/edit files without asking for permission:
```bash theme={null}
claude -p "Run the test suite and fix any failures" \
--allowedTools "Bash,Read,Edit"
```
To set a baseline for the whole session instead of listing individual tools, pass a [permission mode](/docs/en/permission-modes). `dontAsk` denies anything not in your `permissions.allow` rules or the [read-only command set](/docs/en/permissions#read-only-commands), which is useful for locked-down CI runs. `AskUserQuestion`, connector tools [your organization set to `ask`](/docs/en/mcp#organization-controls-on-connector-tools), and MCP tools marked [`requiresUserInteraction`](/docs/en/mcp#require-approval-for-a-specific-tool) are denied even when an allow rule matches.
`acceptEdits` lets Claude write files without prompting and also auto-approves common filesystem commands such as `mkdir`, `touch`, `mv`, and `cp`. Apart from the [read-only command set](/docs/en/permissions#read-only-commands), other shell commands and network requests still need an `--allowedTools` entry or a `permissions.allow` rule. See [what `acceptEdits` auto-approves](/docs/en/permission-modes#auto-approve-file-edits-with-acceptedits-mode) for the full list.
This example applies lint fixes with `acceptEdits` as the baseline:
```bash theme={null}
claude -p "Apply the lint fixes" --permission-mode acceptEdits
```
### Create a commit
This example reviews staged changes and creates a commit with an appropriate message:
```bash theme={null}
claude -p "Look at my staged changes and create an appropriate commit" \
--allowedTools "Bash(git diff *),Bash(git log *),Bash(git status *),Bash(git commit *)"
```
The `--allowedTools` flag uses [permission rule syntax](/docs/en/settings#permission-rule-syntax). The trailing ` *` enables prefix matching, so `Bash(git diff *)` allows any command starting with `git diff`. The space before `*` is important: without it, `Bash(git diff*)` would also match `git diff-index`.
<Note>
User-invoked [skills](/docs/en/skills) and custom commands work in `-p` mode: include `/skill-name` in the prompt string and Claude Code expands it before running. Built-in commands that only run in the terminal interface, such as `/login`, aren't available in `-p` mode. `/model`, `/effort`, `/fast`, `/color`, and `/rename` accept the value as an argument, for example `/model sonnet`, and `/mcp` with no argument prints a text summary of server status; these forms require Claude Code v2.1.205 or later and follow each command's [availability notes](/docs/en/commands#all-commands). To change a setting from a `-p` invocation, pass `key=value` to `/config`, for example `/config thinking=false`.
</Note>
### Customize the system prompt
Use `--append-system-prompt` to add instructions while keeping Claude Code's default behavior. This example pipes a PR diff to Claude and instructs it to review for security vulnerabilities. Save it as a shell script, for example `review.sh`:
```bash theme={null}
gh pr diff "$1" | claude -p \
--append-system-prompt "You are a security engineer. Review for vulnerabilities." \
--output-format json
```
In the script, `"$1"` stands for the first argument you pass on the command line. Run `bash review.sh 123` and the shell replaces `"$1"` with `123`, so the script fetches the diff for PR 123. Claude Code prints the review as JSON, with the text in the `result` field.
See [system prompt flags](/docs/en/cli-reference#system-prompt-flags) for more options including `--system-prompt` to fully replace the default prompt.
### Continue conversations
Use `--continue` to continue the most recent conversation, or `--resume` with a session ID to continue a specific conversation. This example runs a review, then sends follow-up prompts:
```bash theme={null}
# First request
claude -p "Review this codebase for performance issues"
# Continue the most recent conversation
claude -p "Now focus on the database queries" --continue
claude -p "Generate a summary of all issues found" --continue
```
If you're running multiple conversations, capture the session ID to resume a specific one:
```bash theme={null}
session_id=$(claude -p "Start a review" --output-format json | jq -r '.session_id')
Cut at 300 lines. The page has the rest.
hooks First recorded · 3421 lines, first recorded
# Hooks reference ## Hook lifecycle ### How a hook resolves ## Configuration ### Hook locations ### Matcher patterns #### Match MCP tools ### Hook handler fields #### Common fields #### Command hook fields ##### Exec form and shell form #### HTTP hook fields #### MCP tool hook fields #### Prompt and agent hook fields ### Reference scripts by path ### Hooks in skills and agents ### The `/hooks` menu ### Disable or remove hooks ## Hook input and output ### Common input fields ### Exit code output #### Exit code 0 #### Exit code 2 #### Other exit codes #### Timeouts #### Exit code 2 behavior per event ### HTTP response handling ### JSON output #### Emit terminal notifications #### Add context for Claude #### Decision control ## Hook events ### SessionStart #### SessionStart input #### SessionStart decision control #### Persist environment variables ### Setup #### Setup input #### Setup decision control ### InstructionsLoaded #### InstructionsLoaded input #### InstructionsLoaded decision control ### UserPromptSubmit #### UserPromptSubmit input #### UserPromptSubmit decision control ### UserPromptExpansion #### UserPromptExpansion input #### UserPromptExpansion decision control ### MessageDisplay #### MessageDisplay input #### MessageDisplay output ### PreToolUse #### PreToolUse input ##### Bash ##### PowerShell ##### Write ##### Edit ##### Read ##### Glob ##### Grep ##### WebFetch ##### WebSearch ##### Agent ##### AskUserQuestion ##### ExitPlanMode #### PreToolUse decision control #### Defer a tool call for later ### PermissionRequest #### PermissionRequest input #### PermissionRequest decision control #### Permission update entries ### PostToolUse #### PostToolUse input #### PostToolUse decision control ### PostToolUseFailure #### PostToolUseFailure input #### PostToolUseFailure decision control ### PostToolBatch #### PostToolBatch input #### PostToolBatch decision control ### PermissionDenied #### PermissionDenied input #### PermissionDenied decision control ### Notification #### Notification input ### SubagentStart #### SubagentStart input ### SubagentStop #### SubagentStop input ### TaskCreated #### TaskCreated input #### TaskCreated decision control ### TaskCompleted #### TaskCompleted input #### TaskCompleted decision control ### Stop #### Stop input #### Stop decision control ### StopFailure #### StopFailure input ### TeammateIdle #### TeammateIdle input #### TeammateIdle decision control ### ConfigChange #### ConfigChange input #### ConfigChange decision control ### CwdChanged #### CwdChanged input #### CwdChanged output ### DirectoryAdded #### DirectoryAdded input ### FileChanged #### FileChanged input #### FileChanged output ### WorktreeCreate #### WorktreeCreate input #### WorktreeCreate output ### WorktreeRemove #### WorktreeRemove input ### PreCompact #### PreCompact input ### PostCompact #### PostCompact input ### SessionEnd #### SessionEnd input ### Elicitation #### Elicitation input #### Elicitation output ### ElicitationResult #### ElicitationResult input #### ElicitationResult output ## Prompt-based hooks ### How prompt-based hooks work ### Prompt hook configuration ### Response schema ### Check multiple conditions before stopping ## Agent-based hooks ### How agent hooks work ### Agent hook configuration ## Run hooks in the background ### Configure an async hook ### How async hooks execute ### Run tests after file changes ### Limitations ## Security considerations ### Disclaimer ### Workspace trust ### Security best practices ## Windows PowerShell tool ## Debug hooks
This page is larger than the 256 KiB this site keeps, so one side of the diff below stops where the stored text does.
The first capture of this source. The page was already there, and this is what it said.
# Hooks reference
> Reference for Claude Code hook events, configuration schema, JSON input/output formats, exit codes, async hooks, HTTP hooks, prompt hooks, and MCP tool hooks.
<Tip>
For a quickstart guide with examples, see [Automate actions with hooks](/docs/en/hooks-guide).
</Tip>
Hooks are user-defined shell commands, HTTP endpoints, or LLM prompts that execute automatically at specific points in Claude Code's lifecycle. Hooks run wherever Claude Code runs: sessions in the terminal, IDE extensions, the [Desktop app](/docs/en/desktop-quickstart), and [Claude Code on the web](/docs/en/claude-code-on-the-web) all fire the same hook events. Use this reference to look up event schemas, configuration options, JSON input/output formats, and advanced features like async hooks, HTTP hooks, and MCP tool hooks.
## Hook lifecycle
Claude Code runs hooks at specific points during a session. When an event fires and a matcher matches, Claude Code passes JSON context about the event to your hook handler. For command hooks, input arrives on stdin. For HTTP hooks, it arrives as the POST request body. Your handler can then inspect the input, take action, and optionally return a decision.
Events fall into three cadences:
* once per session: `SessionStart` and `SessionEnd`
* once per turn: `UserPromptSubmit`, `Stop`, and `StopFailure`
* on every tool call inside the agentic loop: `PreToolUse` and `PostToolUse`, except [`EndConversation`](/docs/en/tools-reference#endconversation-tool-behavior) calls, which skip both
<div style={{maxWidth: "500px", margin: "0 auto"}}>
<Frame>
<img src="https://mintcdn.com/claude-code/jhXrDR5TrSZ5hgXM/images/hooks-lifecycle.svg?fit=max&auto=format&n=jhXrDR5TrSZ5hgXM&q=85&s=3ca47113d5956460e6e4611b8dbc63b7" className="dark:hidden" alt="Hook lifecycle diagram showing optional Setup feeding into SessionStart, then a per-turn loop containing UserPromptSubmit, UserPromptExpansion for slash commands, the nested agentic loop (PreToolUse, PermissionRequest, PostToolUse, PostToolUseFailure, PostToolBatch, SubagentStart/Stop, TaskCreated, TaskCompleted), and Stop or StopFailure, followed by TeammateIdle, PreCompact, PostCompact, and SessionEnd, with Elicitation and ElicitationResult nested inside MCP tool execution, PermissionDenied as a side branch from PermissionRequest for auto-mode denials, WorktreeCreate, WorktreeRemove, Notification, ConfigChange, InstructionsLoaded, CwdChanged, FileChanged, and DirectoryAdded as standalone async events, and MessageDisplay as a display-only event that runs while assistant message text streams" width="520" height="1228" data-path="images/hooks-lifecycle.svg" />
<img src="https://mintcdn.com/claude-code/jhXrDR5TrSZ5hgXM/images/hooks-lifecycle-dark.svg?fit=max&auto=format&n=jhXrDR5TrSZ5hgXM&q=85&s=0ffe95014d33411538778a66e4173973" className="hidden dark:block" alt="Hook lifecycle diagram showing optional Setup feeding into SessionStart, then a per-turn loop containing UserPromptSubmit, UserPromptExpansion for slash commands, the nested agentic loop (PreToolUse, PermissionRequest, PostToolUse, PostToolUseFailure, PostToolBatch, SubagentStart/Stop, TaskCreated, TaskCompleted), and Stop or StopFailure, followed by TeammateIdle, PreCompact, PostCompact, and SessionEnd, with Elicitation and ElicitationResult nested inside MCP tool execution, PermissionDenied as a side branch from PermissionRequest for auto-mode denials, WorktreeCreate, WorktreeRemove, Notification, ConfigChange, InstructionsLoaded, CwdChanged, FileChanged, and DirectoryAdded as standalone async events, and MessageDisplay as a display-only event that runs while assistant message text streams" width="520" height="1228" data-path="images/hooks-lifecycle-dark.svg" />
</Frame>
</div>
The table below summarizes when each event fires. The [Hook events](#hook-events) section documents the full input schema and decision control options for each one.
| Event | When it fires |
| :-------------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `SessionStart` | When a session begins or resumes |
| `Setup` | When you start Claude Code with `--init-only`, or with `--init` or `--maintenance` in `-p` mode. For one-time preparation in CI or scripts |
| `UserPromptSubmit` | When you submit a prompt, before Claude processes it |
| `UserPromptExpansion` | When a user-typed command expands into a prompt, before it reaches Claude. Can block the expansion |
| `PreToolUse` | Before a tool call executes. Can block it |
| `PermissionRequest` | When a tool call needs a permission decision |
| `PermissionDenied` | When auto mode denies a tool call, including denials without a classifier verdict. Use JSON `hookSpecificOutput.retry: true` to tell the model it may retry the denied tool call. Claude Code ignores `retry` when the classifier produced no verdict |
| `PostToolUse` | After a tool call succeeds |
| `PostToolUseFailure` | After a tool call fails |
| `PostToolBatch` | After a full batch of parallel tool calls resolves, before the next model call |
| `Notification` | When Claude Code sends a notification |
| `MessageDisplay` | While assistant message text is displayed |
| `SubagentStart` | When a subagent is spawned |
| `SubagentStop` | When a subagent finishes |
| `TaskCreated` | When a task is being created via `TaskCreate` |
| `TaskCompleted` | When a task is being marked as completed |
| `Stop` | When Claude finishes responding |
| `StopFailure` | When the turn ends due to an API error |
| `TeammateIdle` | When an [agent team](/docs/en/agent-teams) teammate is about to go idle |
| `InstructionsLoaded` | When a CLAUDE.md or `.claude/rules/*.md` file is loaded into context. Fires at session start and when files are lazily loaded during a session |
| `ConfigChange` | When a configuration file changes during a session |
| `CwdChanged` | When the working directory changes, for example when Claude executes a `cd` command. Useful for reactive environment management with tools like direnv |
| `DirectoryAdded` | When a working directory is added mid-session via `/add-dir` or the SDK `register_repo_root` control request |
| `FileChanged` | When a watched file changes on disk. The `matcher` field specifies which filenames to watch |
| `WorktreeCreate` | When a worktree is being created via `--worktree`, `isolation: "worktree"`, or for a background session. Replaces default git behavior |
| `WorktreeRemove` | When a worktree is being removed at session exit, when a subagent finishes, or when you delete a background session |
| `PreCompact` | Before context compaction |
| `PostCompact` | After context compaction completes |
| `Elicitation` | When an MCP server requests user input during a tool call |
| `ElicitationResult` | After a user responds to an MCP elicitation, before the response is sent back to the server |
| `SessionEnd` | When a session terminates |
### How a hook resolves
To see how these pieces fit together, consider this `PreToolUse` hook that blocks destructive shell commands.
<Tabs>
<Tab title="macOS/Linux">
The `matcher` narrows to Bash tool calls and the `if` condition narrows further to Bash subcommands matching `rm *`, so `block-rm.sh` only spawns when both filters match:
```json theme={null}
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"if": "Bash(rm *)",
"command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/block-rm.sh",
"args": []
}
]
}
]
}
}
```
The script reads the JSON input from stdin, extracts the command, and returns a `permissionDecision` of `"deny"` if it contains `rm -rf`. Save it to `.claude/hooks/block-rm.sh` in your project and make it executable with `chmod +x .claude/hooks/block-rm.sh` so Claude Code can run it:
```bash theme={null}
#!/bin/bash
# .claude/hooks/block-rm.sh
COMMAND=$(jq -r '.tool_input.command')
if echo "$COMMAND" | grep -q 'rm -rf'; then
jq -n '{
hookSpecificOutput: {
hookEventName: "PreToolUse",
permissionDecision: "deny",
permissionDecisionReason: "Destructive command blocked by hook"
}
}'
else
exit 0 # no decision; normal permission flow applies
fi
```
This script, like the other Bash examples on this page that parse JSON input, uses `jq`, so install `jq` and make sure it is on your `PATH` before trying them.
</Tab>
<Tab title="Windows (PowerShell)">
The matcher `Bash|PowerShell` covers the [PowerShell tool](#powershell) as well as Bash. A single `if` rule matches only one tool's calls, so each tool gets its own handler: the first narrows to Bash subcommands matching `rm *`, the second to PowerShell commands matching `Remove-Item *`. Both run the same script through `powershell.exe`:
```json theme={null}
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash|PowerShell",
"hooks": [
{
"type": "command",
"if": "Bash(rm *)",
"command": "powershell.exe",
"args": [
"-NoProfile",
"-ExecutionPolicy",
"Bypass",
"-File",
"${CLAUDE_PROJECT_DIR}/.claude/hooks/block-rm.ps1"
]
},
{
"type": "command",
"if": "PowerShell(Remove-Item *)",
"command": "powershell.exe",
"args": [
"-NoProfile",
"-ExecutionPolicy",
"Bypass",
"-File",
"${CLAUDE_PROJECT_DIR}/.claude/hooks/block-rm.ps1"
]
}
]
}
]
}
}
```
The `-NoProfile` flag skips loading your PowerShell profile so the hook starts fast, and `-ExecutionPolicy Bypass` lets PowerShell run the local script file.
The script reads the JSON input from stdin, extracts the command, and returns a `permissionDecision` of `"deny"` if it contains `rm -rf` or `Remove-Item` followed by `-Recurse`. Save it to `.claude/hooks/block-rm.ps1` in your project:
```powershell theme={null}
# .claude/hooks/block-rm.ps1
$callInput = [Console]::In.ReadToEnd() | ConvertFrom-Json
$command = $callInput.tool_input.command
if ($command -match 'rm -rf|Remove-Item.*-Recurse') {
@{
hookSpecificOutput = @{
hookEventName = "PreToolUse"
permissionDecision = "deny"
permissionDecisionReason = "Destructive command blocked by hook"
}
} | ConvertTo-Json
} else {
exit 0 # no decision; normal permission flow applies
}
```
</Tab>
</Tabs>
Now suppose Claude Code decides to run `Bash "rm -rf /tmp/build"` against the macOS/Linux config. Here's what happens:
<Frame>
<img src="https://mintcdn.com/claude-code/ikqp3_70mqIahteV/images/hook-resolution.svg?fit=max&auto=format&n=ikqp3_70mqIahteV&q=85&s=be0bf3053550c26de5f54cd64674c197" className="dark:hidden" alt="Diagram of hook resolution: PreToolUse fires, the matcher checks for a Bash match, then the if condition checks for a Bash(rm *) match. If both match, the hook command runs and returns permissionDecision deny, so the tool call is blocked and Claude Code continues. If either check fails to match, the hook is skipped and the tool call is allowed to proceed." width="930" height="270" data-path="images/hook-resolution.svg" />
<img src="https://mintcdn.com/claude-code/_xqph1dUOslCOwsj/images/hook-resolution-dark.svg?fit=max&auto=format&n=_xqph1dUOslCOwsj&q=85&s=e80af91f8507cee6bd51ac3c2dd92f63" className="hidden dark:block" alt="Diagram of hook resolution: PreToolUse fires, the matcher checks for a Bash match, then the if condition checks for a Bash(rm *) match. If both match, the hook command runs and returns permissionDecision deny, so the tool call is blocked and Claude Code continues. If either check fails to match, the hook is skipped and the tool call is allowed to proceed." width="930" height="270" data-path="images/hook-resolution-dark.svg" />
</Frame>
<Steps>
<Step title="Event fires">
The `PreToolUse` event fires. Claude Code sends the tool input as JSON on stdin to the hook:
```json theme={null}
{ "tool_name": "Bash", "tool_input": { "command": "rm -rf /tmp/build" }, ... }
```
</Step>
<Step title="Matcher checks">
The matcher `"Bash"` matches the tool name, so this hook group activates. If you omit the matcher or use `"*"`, the group activates on every occurrence of the event.
</Step>
<Step title="If condition checks">
The `if` condition `"Bash(rm *)"` matches because `rm -rf /tmp/build` is a subcommand matching `rm *`, so this handler spawns. If the command had been `npm test`, the `if` check would fail and `block-rm.sh` would never run, avoiding the process spawn overhead. The `if` field is optional; without it, every handler in the matched group runs.
</Step>
<Step title="Hook handler runs">
The script inspects the full command and finds `rm -rf`, so it prints a decision to stdout:
```json theme={null}
{
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"permissionDecision": "deny",
"permissionDecisionReason": "Destructive command blocked by hook"
}
}
```
If the command had been a safer `rm` variant like `rm file.txt`, the script would hit `exit 0` instead. Exit code 0 with no output means the hook has no decision to report, so the tool call continues through the normal [permission flow](/docs/en/permissions). The hook can deny the call, but staying silent doesn't approve it.
</Step>
<Step title="Claude Code acts on the result">
Claude Code reads the JSON decision, blocks the tool call, and shows Claude the reason.
</Step>
</Steps>
The [Configuration](#configuration) section below documents the full schema, and each [hook event](#hook-events) section documents what input your command receives and what output it can return.
## Configuration
Hooks are defined in JSON settings files. The configuration has three levels of nesting:
1. Choose a [hook event](#hook-events) to respond to, like `PreToolUse` or `Stop`
2. Add a [matcher group](#matcher-patterns) to filter when it fires, like "only for the Bash tool"
3. Define one or more [hook handlers](#hook-handler-fields) to run when matched
See [How a hook resolves](#how-a-hook-resolves) above for a complete walkthrough with an annotated example.
<Note>
This page uses specific terms for each level: **hook event** for the lifecycle point, **matcher group** for the filter, and **hook handler** for the shell command, HTTP endpoint, MCP tool, prompt, or agent that runs. "Hook" on its own refers to the general feature.
</Note>
### Hook locations
Where you define a hook determines its scope:
| Location | Scope | Shareable |
| :--------------------------------------------------------- | :---------------------------- | :---------------------------------------------------- |
| `~/.claude/settings.json` | All your projects | No, local to your machine |
| `.claude/settings.json` | Single project | Yes, can be committed to the repo |
| `.claude/settings.local.json` | Single project | No, gitignored when Claude Code saves a setting to it |
| Managed policy settings | Organization-wide | Yes, admin-controlled |
| [Plugin](/docs/en/plugins) `hooks/hooks.json` | When plugin is enabled | Yes, bundled with the plugin |
| [Skill](/docs/en/skills) or [agent](/docs/en/sub-agents) frontmatter | While the component is active | Yes, defined in the component file |
Cloud sessions on [Claude Code on the web](/docs/en/claude-code-on-the-web) don't read your local `~/.claude/settings.json`; hooks there come from the repo and from your organization's server-managed settings. See [what carries over from your setup](/docs/en/cloud-environments#what-carries-over-from-your-setup) for which files reach a cloud session.
For details on settings file resolution, see [settings](/docs/en/settings).
Hooks from settings files, managed policy settings, and plugins also run inside [subagents](/docs/en/sub-agents). When a subagent calls a tool, tool events such as `PreToolUse` and `PostToolUse` fire the same configured hooks as in the main conversation, and the input carries the `agent_id` and `agent_type` [common input fields](#common-input-fields) that identify the subagent.
Enterprise administrators can use `allowManagedHooksOnly` to restrict which hooks run:
* Your user, project, local, and plugin hooks are blocked. Hooks from plugins force-enabled in managed settings `enabledPlugins` are exempt
* Claude Code also narrows your [`statusLine`](/docs/en/statusline), [`fileSuggestion`](/docs/en/settings#file-suggestion-settings), and [`subagentStatusLine`](/docs/en/statusline#subagent-status-lines) settings to managed settings
* Claude Code also disables plugins with a [`command` source](/docs/en/plugin-marketplaces#command-sources), including plugins force-enabled in managed settings `enabledPlugins`, unless [`disableCommandPluginSources`](/docs/en/settings#available-settings) is explicitly set to `false`
See [Hook configuration](/docs/en/settings#hook-configuration).
Hook entries merge across settings levels rather than replacing each other: user, project, and local settings add their own hooks without removing managed ones, and the [`disableAllHooks`](#disable-or-remove-hooks) setting can't disable managed hooks from outside managed settings.
The [HTTP hook allowlists](/docs/en/settings#hook-configuration) apply to hooks from every source, including managed policy settings:
* `allowedHttpHookUrls`: when defined at any settings level, Claude Code runs an HTTP hook handler only if its URL matches the merged allowlist
* `httpHookAllowedEnvVars`: when defined, Claude Code interpolates only the environment variables on that list into hook headers
### Matcher patterns
The `matcher` field filters when hooks fire. How a matcher is evaluated depends on the characters it contains:
| Matcher value | Evaluated as | Example |
| :---------------------------------------------------- | :--------------------------------------------------------------------------------------------------- | :---------------------------------------------------------------------------------------------------------------------------------------------- |
| `"*"`, `""`, or omitted | Match all | fires on every occurrence of the event |
| Only letters, digits, `_`, `-`, spaces, `,`, and `\|` | Exact string, or list of exact strings separated by `\|` or `,` with optional surrounding whitespace | `Bash` matches only the Bash tool; `Edit\|Write` and `Edit, Write` each match either tool exactly; `code-reviewer` matches only that agent type |
| Contains any other character | JavaScript regular expression, unanchored | `^Notebook` matches any tool whose name starts with `Notebook`; `mcp__memory__.*` matches every tool from the `memory` server |
A matcher on the regular-expression path is tested with JavaScript's `RegExp.prototype.test`, which succeeds on a match anywhere in the value. `Edit.*` matches both `Edit` and `NotebookEdit`; wrap the pattern in `^` and `$`, as in `^Edit$`, when you need a whole-string match.
Comma separators and the surrounding whitespace tolerance require Claude Code v2.1.191 or later.
Hyphens in the exact-match set require Claude Code v2.1.195 or later. On earlier versions a hyphenated name like `code-reviewer` is evaluated as an unanchored regular expression, so it also fires for `senior-code-reviewer`; anchor it as `^code-reviewer$` on those versions to match only that name.
`FileChanged` and `StopFailure` use a narrower exact-match set of letters, digits, `_`, and `|` only. A hyphen, space, or comma in a matcher for those two events keeps it on the regular-expression path, and only `|` separates alternatives. Every other event with matcher support in the table that follows accepts `|` or `,`.
The `FileChanged` event doesn't follow these rules when building its watch list. See [FileChanged](#filechanged).
Each event type matches on a different field:
| Event | What the matcher filters | Example matcher values |
| :------------------------------------------------------------------------------------------------------------------------------------------------ | :----------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
Cut at 300 lines. The page has the rest.
hooks-guide First recorded · 1028 lines, first recorded
# Automate actions with hooks ## Set up your first hook ## What you can automate ### Get notified when Claude needs input ### Auto-format code after edits ### Block edits to protected files ### Re-inject context after compaction ### Audit configuration changes ### Reload environment when directory or files change ### Auto-approve specific permission prompts ## How hooks work ### Combine results from multiple hooks ### Read input and return output #### Hook input #### Hook output #### Structured JSON output ### Filter hooks with matchers #### Filter by tool name and arguments with the `if` field ### Configure hook location ## Prompt-based hooks ## Agent-based hooks ## HTTP hooks ## Limitations and troubleshooting ### Limitations ### Hooks and permission modes ### Hook not firing ### Hook error in output ### `/hooks` shows no hooks configured ### Stop hook hits the block cap ### Hook JSON has no effect ### Debug techniques ## Learn more
The first capture of this source. The page was already there, and this is what it said.
# Automate actions with hooks
> Run shell commands automatically when Claude Code edits files, finishes tasks, or needs input. Format code, send notifications, validate commands, and enforce project rules.
Hooks are user-defined shell commands. Claude Code runs them at specific points in its lifecycle, which gives you deterministic control: certain actions always happen rather than relying on the LLM to choose to run them. Use hooks to enforce project rules, automate repetitive tasks, and integrate Claude Code with your existing tools.
For decisions that require judgment rather than deterministic rules, you can also use [prompt-based hooks](#prompt-based-hooks) or [agent-based hooks](#agent-based-hooks) that use a Claude model to evaluate conditions.
For other ways to extend Claude Code, see [skills](/docs/en/skills) for giving Claude additional instructions and executable commands, [subagents](/docs/en/sub-agents) for running tasks in isolated contexts, and [plugins](/docs/en/plugins) for packaging extensions to share across projects.
<Tip>
This guide covers common use cases and how to get started. For full event schemas, JSON input/output formats, and advanced features like async hooks and MCP tool hooks, see the [Hooks reference](/docs/en/hooks).
</Tip>
## Set up your first hook
To create a hook, add a `hooks` block to a [settings file](#configure-hook-location). This walkthrough creates a desktop notification hook, so you get alerted whenever Claude is waiting for your input instead of watching the terminal.
<Steps>
<Step title="Add the hook to your settings">
Open `~/.claude/settings.json` and add a `Notification` hook. If the file doesn't exist, create it. The example below uses `osascript` for macOS; see [Get notified when Claude needs input](#get-notified-when-claude-needs-input) for Linux and Windows commands.
```json theme={null}
{
"hooks": {
"Notification": [
{
"matcher": "",
"hooks": [
{
"type": "command",
"command": "osascript -e 'display notification \"Claude Code needs your attention\" with title \"Claude Code\"'"
}
]
}
]
}
}
```
If your settings file already has a `hooks` key, add `Notification` as a sibling of the existing event keys rather than replacing the whole object. Each event name is a key inside the single `hooks` object:
```json theme={null}
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [{ "type": "command", "command": "jq -r '.tool_input.file_path' | xargs npx prettier --write" }]
}
],
"Notification": [
{
"matcher": "",
"hooks": [{ "type": "command", "command": "osascript -e 'display notification \"Claude Code needs your attention\" with title \"Claude Code\"'" }]
}
]
}
}
```
You can also ask Claude to write the hook for you by describing what you want in the CLI.
</Step>
<Step title="Verify the configuration">
Type `/hooks` to open the hooks browser. You'll see a list of all available hook events, with a count next to each event that has hooks configured. Select `Notification` to confirm your new hook appears in the list. Selecting the hook shows its details: the event, matcher, type, source file, and command.
</Step>
<Step title="Test the hook">
Press `Esc` to return to the CLI. Ask Claude to do something that requires permission, then switch away from the terminal. You should receive a desktop notification.
</Step>
</Steps>
<Tip>
The `/hooks` menu is read-only. To add, modify, or remove hooks, edit your settings JSON directly or ask Claude to make the change.
</Tip>
## What you can automate
Hooks let you run code at key points in Claude Code's lifecycle: format files after edits, block commands before they execute, send notifications when Claude needs input, inject context at session start, and more. For the full list of hook events, see the [Hooks reference](/docs/en/hooks#hook-lifecycle).
Each example includes a ready-to-use configuration block that you add to a [settings file](#configure-hook-location).
For a production example of hooks that run a separate model review and feed findings back into the session, see [how the `security-guidance` plugin integrates with Claude Code](/docs/en/security-guidance#how-the-plugin-integrates-with-claude-code).
### Get notified when Claude needs input
Get a desktop notification whenever Claude finishes working and needs your input, so you can switch to other tasks without checking the terminal.
This hook uses the `Notification` event, which Claude Code fires when Claude is waiting for input or permission. See [when each notification type fires](/docs/en/hooks#notification) for the exact timing. Each tab below uses the platform's native notification command. Add this to `~/.claude/settings.json`:
<Tabs>
<Tab title="macOS">
```json theme={null}
{
"hooks": {
"Notification": [
{
"matcher": "",
"hooks": [
{
"type": "command",
"command": "osascript -e 'display notification \"Claude Code needs your attention\" with title \"Claude Code\"'"
}
]
}
]
}
}
```
<Accordion title="If no notification appears">
`osascript` routes notifications through the built-in Script Editor app. If Script Editor doesn't have notification permission, the command fails silently, and macOS won't prompt you to grant it. Run this in Terminal once to make Script Editor appear in your notification settings:
```bash theme={null}
osascript -e 'display notification "test"'
```
Nothing will appear yet. Open **System Settings > Notifications**, find **Script Editor** in the list, and turn on **Allow Notifications**. Run the command again to confirm the test notification appears.
</Accordion>
</Tab>
<Tab title="Linux">
```json theme={null}
{
"hooks": {
"Notification": [
{
"matcher": "",
"hooks": [
{
"type": "command",
"command": "notify-send 'Claude Code' 'Claude Code needs your attention'"
}
]
}
]
}
}
```
<Accordion title="If no notification appears">
`notify-send` needs a desktop notification daemon, which headless servers, SSH sessions, and most containers don't have. Test the command directly first:
```bash theme={null}
notify-send 'Claude Code' 'test'
```
If the command isn't found, install the `libnotify-bin` package on Debian and Ubuntu, or your distribution's equivalent.
</Accordion>
</Tab>
<Tab title="Windows (PowerShell)">
```json theme={null}
{
"hooks": {
"Notification": [
{
"matcher": "",
"hooks": [
{
"type": "command",
"command": "powershell.exe -Command \"[System.Reflection.Assembly]::LoadWithPartialName('System.Windows.Forms'); [System.Windows.Forms.MessageBox]::Show('Claude Code needs your attention', 'Claude Code')\""
}
]
}
]
}
}
```
<Accordion title="If no dialog appears">
This command opens a dialog box rather than a notification in the corner of your screen, so the dialog can open behind your terminal window. Test the command directly in PowerShell first. If you run Claude Code inside WSL, `powershell.exe` must be available on your `PATH` through Windows interop.
</Accordion>
</Tab>
</Tabs>
The empty `matcher` fires on all notification types. To fire only on specific events, set it to one of these values:
| Matcher | Fires when |
| :----------------------- | :------------------------------------------------------------------------------------------------------- |
| `permission_prompt` | Claude needs you to approve a tool use and you haven't typed for about 6 seconds |
| `idle_prompt` | Claude finished responding about 60 seconds ago and you haven't typed since |
| `auth_success` | Authentication completes |
| `elicitation_dialog` | An MCP server opens an elicitation form and you haven't typed for about 6 seconds |
| `elicitation_url_dialog` | An MCP server asks you to open a browser URL and you haven't typed for about 6 seconds |
| `elicitation_complete` | An MCP elicitation form is submitted or dismissed |
| `elicitation_response` | An MCP elicitation response is sent back to the server |
| `agent_needs_input` | A background session starts waiting on your input. Fires only while [agent view](/docs/en/agent-view) is open |
| `agent_completed` | A background session finishes or fails. Fires only while [agent view](/docs/en/agent-view) is open |
The `agent_needs_input` and `agent_completed` matchers require Claude Code v2.1.198 or later.
Type `/hooks` and select `Notification` to confirm the hook is registered. For the full event schema, see the [Notification reference](/docs/en/hooks#notification).
### Auto-format code after edits
Automatically run [Prettier](https://prettier.io/) on every file Claude edits, so formatting stays consistent without manual intervention.
This hook uses the `PostToolUse` event with an `Edit|Write` matcher, so it runs only after file-editing tools. The command extracts the edited file path with [`jq`](https://jqlang.org/) and passes it to Prettier. Add this to `.claude/settings.json` in your project root:
```json theme={null}
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "jq -r '.tool_input.file_path' | xargs npx prettier --write"
}
]
}
]
}
}
```
To test the hook, ask Claude to add a line with single-quoted strings to a JavaScript file, then open the file: with Prettier's default settings, the hook rewrites them to double quotes.
When the hook succeeds, Claude Code shows nothing in the conversation. To confirm the hook ran, check that the edited file is reformatted, or see [Debug techniques](#debug-techniques).
<Note>
The Bash examples on this page use `jq` for JSON parsing. Install it with `brew install jq` on macOS, `apt-get install jq` on Debian and Ubuntu, or see [`jq` downloads](https://jqlang.org/download/).
</Note>
### Block edits to protected files
Prevent Claude from modifying sensitive files like `.env`, `package-lock.json`, or anything in `.git/`. Claude receives feedback explaining why the edit was blocked, so it can adjust its approach.
This example uses a separate script file that the hook calls. The script checks the target file path against a list of protected patterns and exits with code 2 to block the edit.
<Steps>
<Step title="Create the hook script">
Save this to `.claude/hooks/protect-files.sh`:
```bash theme={null}
#!/bin/bash
# protect-files.sh
INPUT=$(cat)
FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty')
# Normalize Windows backslash separators so the patterns below match
FILE_PATH="${FILE_PATH//\\//}"
PROTECTED_PATTERNS=(".env" "package-lock.json" ".git/")
for pattern in "${PROTECTED_PATTERNS[@]}"; do
if [[ "$FILE_PATH" == *"$pattern"* ]]; then
echo "Blocked: $FILE_PATH matches protected pattern '$pattern'" >&2
exit 2
fi
done
exit 0
```
</Step>
<Step title="Make the script executable on macOS and Linux">
Hook scripts must be executable for Claude Code to run them:
```bash theme={null}
chmod +x .claude/hooks/protect-files.sh
```
</Step>
<Step title="Register the hook">
Add a `PreToolUse` hook to `.claude/settings.json` that runs the script before any `Edit` or `Write` tool call:
```json theme={null}
{
"hooks": {
"PreToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/protect-files.sh"
}
]
}
]
}
}
```
</Step>
<Step title="Test the hook">
Ask Claude to add a comment to your `.env` file. Claude Code blocks the edit before it runs and passes the script's `Blocked:` message to Claude as feedback.
</Step>
</Steps>
### Re-inject context after compaction
When Claude's context window fills up, compaction summarizes the conversation to free space. This can lose important details. Use a `SessionStart` hook with a `compact` matcher to re-inject critical context after every compaction.
Any text your command writes to stdout is added to Claude's context. This example reminds Claude of project conventions and recent work. Add this to `.claude/settings.json` in your project root:
Cut at 300 lines. The page has the rest.
how-claude-code-works First recorded · 259 lines, first recorded
# How Claude Code works ## The agentic loop ### Models ### Tools ## What Claude can access ## Environments and interfaces ### Execution environments ### Interfaces ## Work with sessions ### Work across branches ### Resume or fork sessions ### The context window #### When context fills up #### Manage context with skills and subagents ## Stay safe with checkpoints and permissions ### Undo changes with checkpoints ### Control what Claude can do ## Work effectively with Claude Code ### Ask Claude Code for help ### It's a conversation #### Interrupt and steer ### Be specific upfront ### Give Claude something to verify against ### Explore before implementing ### Delegate, don't dictate ## What's next
The first capture of this source. The page was already there, and this is what it said.
# How Claude Code works
> Understand the agentic loop, built-in tools, and how Claude Code interacts with your project.
Claude Code is an agentic assistant that runs in your terminal. While it excels at coding, it can help with anything you can do from the command line: writing docs, running builds, searching files, researching topics, and more.
This guide covers the core architecture, built-in capabilities, and [tips for working effectively](#work-effectively-with-claude-code). For step-by-step walkthroughs, see [Common workflows](/docs/en/common-workflows). For extensibility features like skills, MCP, and hooks, see [Extend Claude Code](/docs/en/features-overview).
## The agentic loop
When you give Claude a task, it works through three phases: **gather context**, **take action**, and **verify results**. These phases blend together. Claude uses tools throughout, whether searching files to understand your code, editing to make changes, or running tests to check its work.
<img src="https://mintcdn.com/claude-code/ikqp3_70mqIahteV/images/agentic-loop.svg?fit=max&auto=format&n=ikqp3_70mqIahteV&q=85&s=4a30fb7ce2815012a9f27c955e2c6bb0" className="dark:hidden" alt="Diagram of the agentic loop: Your prompt leads to Claude gathering context, taking action, verifying results, and repeating until task complete. You can interrupt at any point." width="720" height="280" data-path="images/agentic-loop.svg" />
<img src="https://mintcdn.com/claude-code/_xqph1dUOslCOwsj/images/agentic-loop-dark.svg?fit=max&auto=format&n=_xqph1dUOslCOwsj&q=85&s=75e1d55ed76857a952f9a2dffbab02df" className="hidden dark:block" alt="Diagram of the agentic loop: Your prompt leads to Claude gathering context, taking action, verifying results, and repeating until task complete. You can interrupt at any point." width="720" height="280" data-path="images/agentic-loop-dark.svg" />
The loop adapts to what you ask. A question about your codebase might only need context gathering. A bug fix cycles through all three phases repeatedly. A refactor might involve extensive verification. Claude decides what each step requires based on what it learned from the previous step, chaining dozens of actions together and course-correcting along the way.
You're part of this loop too. You can interrupt at any point to steer Claude in a different direction, provide additional context, or ask it to try a different approach. Claude works autonomously but stays responsive to your input.
The agentic loop is powered by two components: [models](#models) that reason and [tools](#tools) that act. Claude Code serves as the **agentic harness** around Claude: it provides the tools, context management, and execution environment that turn a language model into a capable coding agent.
### Models
Claude Code uses Claude models to understand your code and reason about tasks. Claude can read code in any language, understand how components connect, and figure out what needs to change to accomplish your goal. For complex tasks, it breaks work into steps, executes them, and adjusts based on what it learns.
[Multiple models](/docs/en/model-config) are available with different tradeoffs. Sonnet handles most coding tasks well. Opus provides stronger reasoning for complex architectural decisions. Switch with `/model` during a session or start with `claude --model <name>`.
When this guide says "Claude chooses" or "Claude decides," it's the model doing the reasoning.
### Tools
Tools are what make Claude Code agentic. Without tools, Claude can only respond with text. With tools, Claude can act: read your code, edit files, run commands, search the web, and interact with external services. Each tool use returns information that feeds back into the loop, informing Claude's next decision.
The built-in tools generally fall into five categories, each representing a different kind of agency.
| Category | What Claude can do |
| --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **File operations** | Read files, edit code, create new files, rename and reorganize |
| **Search** | Find files by pattern, search content with regex, explore codebases |
| **Execution** | Run shell commands, start servers, run tests, use git |
| **Web** | Search the web, fetch documentation, look up error messages |
| **Code intelligence** | See type errors and warnings after edits, jump to definitions, find references (requires [code intelligence plugins](/docs/en/discover-plugins#code-intelligence)) |
These are the primary capabilities. Claude also has tools for spawning subagents, asking you questions, and other orchestration tasks. See [Tools available to Claude](/docs/en/tools-reference) for the complete list.
Claude chooses which tools to use based on your prompt and what it learns along the way. When you say "fix the failing tests," Claude might:
1. Run the test suite to see what's failing
2. Read the error output
3. Search for the relevant source files
4. Read those files to understand the code
5. Edit the files to fix the issue
6. Run the tests again to verify
Each tool use gives Claude new information that informs the next step. This is the agentic loop in action.
**Extending the base capabilities:** The built-in tools are the foundation. You can extend what Claude knows with [skills](/docs/en/skills), connect to external services with [MCP](/docs/en/mcp), automate workflows with [hooks](/docs/en/hooks), and offload tasks to [subagents](/docs/en/sub-agents). These extensions form a layer on top of the core agentic loop. See [Extend Claude Code](/docs/en/features-overview) for guidance on choosing the right extension for your needs.
## What Claude can access
This guide focuses on the terminal. Claude Code also runs in [VS Code](/docs/en/vs-code), [JetBrains IDEs](/docs/en/jetbrains), and other environments.
When you run `claude` in a directory, Claude Code gains access to:
* **Your project.** Files in your directory and subdirectories, plus files elsewhere with your permission.
* **Your terminal.** Any command you could run: build tools, git, package managers, system utilities, scripts. If you can do it from the command line, Claude can too.
* **Your git state.** Current branch, uncommitted changes, and recent commit history.
* **Your [CLAUDE.md](/docs/en/memory).** A markdown file where you store project-specific instructions, conventions, and context that Claude should know every session.
* **[Auto memory](/docs/en/memory#auto-memory).** Learnings Claude saves automatically as you work, like project patterns and your preferences. The first 200 lines or 25KB of MEMORY.md, whichever comes first, load at the start of each session.
* **Extensions you configure.** [MCP servers](/docs/en/mcp) for external services, [skills](/docs/en/skills) for workflows, [subagents](/docs/en/sub-agents) for delegated work, and [Claude in Chrome](/docs/en/chrome) for browser interaction.
Because Claude sees your whole project, it can work across it. When you ask Claude to "fix the authentication bug," it searches for relevant files, reads multiple files to understand context, makes coordinated edits across them, runs tests to verify the fix, and commits the changes if you ask. This is different from inline code assistants that only see the current file.
## Environments and interfaces
The agentic loop, tools, and capabilities described above are the same everywhere you use Claude Code. What changes is where the code executes and how you interact with it.
### Execution environments
Claude Code runs in three environments, each with different tradeoffs for where your code executes.
| Environment | Where code runs | Use case |
| ------------------ | ------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------- |
| **Local** | Your machine | Default. Full access to your files, tools, and environment |
| **Cloud** | Anthropic-managed VMs, or [self-hosted environments](/docs/en/self-hosted-environments) your organization operates | Offload tasks, work on repos you don't have locally |
| **Remote Control** | Your machine, controlled from a browser | Use the web UI while execution and your files stay local |
### Interfaces
You can access Claude Code through the terminal, the [desktop app](/docs/en/desktop), [IDE extensions](/docs/en/vs-code), [claude.ai/code](https://claude.ai/code), [Remote Control](/docs/en/remote-control), [Slack](/docs/en/slack), and [CI/CD pipelines](/docs/en/github-actions). The interface determines how you see and interact with Claude, but the underlying agentic loop is identical. See [Use Claude Code everywhere](/docs/en/overview#use-claude-code-everywhere) for the full list.
## Work with sessions
Claude Code saves your conversation locally as you work. Each message, tool use, and result is written to a plaintext JSONL file under `~/.claude/projects/`, which enables [rewinding](#undo-changes-with-checkpoints), [resuming, and forking](#resume-or-fork-sessions) sessions. Before Claude makes code changes, it also snapshots the affected files so you can revert if needed. For paths, retention, and how to clear this data, see [application data in `~/.claude`](/docs/en/claude-directory#application-data).
**Sessions are independent.** Each new session starts with a fresh context window, without the conversation history from previous sessions. Claude can persist learnings across sessions using [auto memory](/docs/en/memory#auto-memory), and you can add your own persistent instructions in [CLAUDE.md](/docs/en/memory).
### Work across branches
Each Claude Code conversation is a session tied to your current directory. The `/resume` picker shows sessions from the current worktree by default, with keyboard shortcuts to widen the list to other worktrees or projects. See [Manage sessions](/docs/en/sessions#use-the-session-picker) for the full list of picker shortcuts and how name resolution works.
Claude sees your current branch's files. When you switch branches, Claude sees the new branch's files, but your conversation history stays the same. Claude remembers what you discussed even after switching.
Since sessions are tied to directories, you can run parallel Claude sessions by using [git worktrees](/docs/en/worktrees), which create separate directories for individual branches.
### Resume or fork sessions
Resuming a session with `claude --continue` or `claude --resume` reopens it under the same session ID and appends new messages to the existing conversation. Forking with `--fork-session` or `/branch` copies the history into a new session ID, leaving the original unchanged.
<img src="https://mintcdn.com/claude-code/ikqp3_70mqIahteV/images/session-continuity.svg?fit=max&auto=format&n=ikqp3_70mqIahteV&q=85&s=04ed0984a58e4127e05b3640265241a3" className="dark:hidden" alt="Diagram of session continuity: resume continues the same session, fork creates a new branch with a new ID." width="560" height="280" data-path="images/session-continuity.svg" />
<img src="https://mintcdn.com/claude-code/_xqph1dUOslCOwsj/images/session-continuity-dark.svg?fit=max&auto=format&n=_xqph1dUOslCOwsj&q=85&s=886a384bce8298594e43f124617ea665" className="hidden dark:block" alt="Diagram of session continuity: resume continues the same session, fork creates a new branch with a new ID." width="560" height="280" data-path="images/session-continuity-dark.svg" />
For the resume flags, the `/resume` picker, naming, and what happens when the same session is open in two terminals, see [Manage sessions](/docs/en/sessions).
### The context window
Claude's context window holds your conversation history, file contents, command outputs, [CLAUDE.md](/docs/en/memory), [auto memory](/docs/en/memory#auto-memory), loaded skills, and system instructions. As you work, context fills up. Claude compacts automatically, but instructions from early in the conversation can get lost. Put persistent rules in CLAUDE.md, and run `/context` to see what's using space.
For an interactive walkthrough of what loads and when, see [Explore the context window](/docs/en/context-window).
#### When context fills up
Claude Code manages context automatically as you approach the limit. It clears older tool outputs first, then summarizes the conversation if needed. Your requests and key code snippets are preserved; detailed instructions from early in the conversation may be lost. Put persistent rules in CLAUDE.md rather than relying on conversation history.
To control what's preserved during compaction, add a "Compact Instructions" section to CLAUDE.md or run `/compact` with a focus (like `/compact focus on the API changes`).
If a single file or tool output is so large that context refills immediately after each summary, Claude Code stops auto-compacting after a few attempts and shows an error instead of looping. See [Auto-compaction stops with a thrashing error](/docs/en/troubleshooting#auto-compaction-stops-with-a-thrashing-error) for recovery steps.
Run `/context` to see what's using space. MCP tool definitions are deferred by default and loaded on demand via [tool search](/docs/en/mcp#scale-with-mcp-tool-search), so only tool names consume context until Claude uses a specific tool. Run `/mcp` to check per-server costs.
#### Manage context with skills and subagents
Beyond compaction, you can use other features to control what loads into context.
[Skills](/docs/en/skills) load on demand. Claude sees skill descriptions at session start, but the full content only loads when a skill is used. For skills you invoke manually, set `disable-model-invocation: true` to keep descriptions out of context until you need them. For skills you didn't write, use [`skillOverrides`](/docs/en/skills#override-skill-visibility-from-settings) to do the same from settings.
[Subagents](/docs/en/sub-agents) get their own fresh context, completely separate from your main conversation. Their work doesn't bloat your context. When done, they return a summary. This isolation is why subagents help with long sessions.
See [context costs](/docs/en/features-overview#understand-context-costs) for what each feature costs, and [reduce token usage](/docs/en/costs#reduce-token-usage) for tips on managing context.
## Stay safe with checkpoints and permissions
Claude has two safety mechanisms: checkpoints let you undo file changes, and permissions control what Claude can do without asking.
### Undo changes with checkpoints
**File edits are reversible.** Before Claude edits a file, it snapshots the current contents. If something goes wrong, press `Esc` twice to rewind to a previous state, or ask Claude to undo.
Checkpoints are separate from git and remain available when you resume a conversation. They only cover file changes, and a restore [skips symlinked and hard-linked files](/docs/en/checkpointing#symlinked-and-hard-linked-paths-not-restored). Actions that affect remote systems (databases, APIs, deployments) can't be checkpointed, which is why Claude asks before running commands with external side effects.
### Control what Claude can do
Press `Shift+Tab` to cycle through permission modes:
* **Manual**: Claude asks before file edits and shell commands
* **Accept edits**: Claude edits files and runs common filesystem commands like `mkdir` and `mv` without asking, still asks for other commands
* **Plan**: Claude explores and proposes a plan without editing your source files
* **Auto**: Claude evaluates all actions with background safety checks
You can also allow specific commands in `.claude/settings.json` so Claude doesn't ask each time. This is useful for trusted commands like `npm test` or `git status`. Settings can be scoped from organization-wide policies down to personal preferences. See [Permissions](/docs/en/permissions) for details.
***
## Work effectively with Claude Code
These tips help you get better results from Claude Code.
### Ask Claude Code for help
Claude Code can teach you how to use it. Ask questions like "how do I set up hooks?" or "what's the best way to structure my CLAUDE.md?" and Claude will explain.
Built-in commands also guide you through setup:
* `/init` walks you through creating a CLAUDE.md for your project
* `/doctor` runs a setup checkup that diagnoses installation and configuration issues and can fix them
### It's a conversation
Claude Code is conversational. You don't need perfect prompts. Start with what you want, then refine:
```text theme={null}
Fix the login bug
```
\[Claude investigates, tries something]
```text theme={null}
That's not quite right. The issue is in the session handling.
```
\[Claude adjusts approach]
When the first attempt isn't right, you don't start over. You iterate.
#### Interrupt and steer
You can redirect Claude at any point without waiting for the turn to finish or starting over:
* **Press `Esc`** to stop Claude immediately. The running tool call is canceled and Claude waits for your next instruction.
* **Type a correction and press `Enter`** to send it without stopping the running tool. Claude reads it as soon as the current action completes and adjusts before deciding its next step.
### Be specific upfront
The more precise your initial prompt, the fewer corrections you'll need. Reference specific files, mention constraints, and point to example patterns.
```text theme={null}
The checkout flow is broken for users with expired cards.
Check src/payments/ for the issue, especially token refresh.
Write a failing test first, then fix it.
```
Vague prompts work, but you'll spend more time steering. Specific prompts like the one above often succeed on the first attempt.
### Give Claude something to verify against
Claude performs better when it can check its own work. Include test cases, paste screenshots of expected UI, or define the output you want.
```text theme={null}
Implement validateEmail. Test cases: '[email protected]' → true,
'invalid' → false, '[email protected]' → false. Run the tests after.
```
For visual work, paste a screenshot of the design and ask Claude to compare its implementation against it.
### Explore before implementing
For complex problems, separate research from coding. Use plan mode (`Shift+Tab` twice) to analyze the codebase first:
```text theme={null}
Read src/auth/ and understand how we handle sessions.
Then create a plan for adding OAuth support.
```
Review the plan, refine it through conversation, then let Claude implement. This two-phase approach produces better results than jumping straight to code.
### Delegate, don't dictate
Think of delegating to a capable colleague. Give context and direction, then trust Claude to figure out the details:
```text theme={null}
The checkout flow is broken for users with expired cards.
The relevant code is in src/payments/. Can you investigate and fix it?
```
You don't need to specify which files to read or what commands to run. Claude figures that out.
## What's next
<CardGroup cols={2}>
<Card title="Extend with features" icon="puzzle-piece" href="/docs/en/features-overview">
Add Skills, MCP connections, and custom commands
</Card>
<Card title="Common workflows" icon="graduation-cap" href="/docs/en/common-workflows">
Step-by-step guides for typical tasks
</Card>
</CardGroup>
interactive-mode First recorded · 438 lines, first recorded
# Interactive mode ## Keyboard shortcuts ### General controls ### Text editing ### Theme and display ### Multiline input ### Quick commands ### Transcript viewer ### Voice input ## Commands ## Vim editor mode ### Mode switching ### Remap INSERT-mode key sequences ### Navigation (NORMAL mode) ### Editing (NORMAL mode) ### Text objects (NORMAL mode) ### Visual mode ## Command history ### Reverse search with Ctrl+R ## Background Bash commands ### How backgrounding works ### Shell mode with `!` prefix ## Prompt suggestions ### When Claude Code skips suggestions ### Turn prompt suggestions off ## Emoji shortcodes ## Side questions with /btw ## Task list ## Session recap ## PR review status ## See also
The first capture of this source. The page was already there, and this is what it said.
# Interactive mode
> Complete reference for keyboard shortcuts, input modes, and interactive features in Claude Code sessions.
## Keyboard shortcuts
<Note>
Keyboard shortcuts may vary by platform and terminal. In [fullscreen rendering](/docs/en/fullscreen), press `?` in the transcript viewer to see available shortcuts there.
**macOS users**: Option/Alt key shortcuts (`Alt+B`, `Alt+F`, `Alt+Y`, `Alt+P`) require configuring Option as Meta in your terminal. See [Enable Option key shortcuts on macOS](/docs/en/terminal-config#enable-option-key-shortcuts-on-macos) for the setting in each terminal.
</Note>
### General controls
| Shortcut | Description | Context |
| :------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `Ctrl+C` | Interrupt, or clear input | Interrupts a running operation. If nothing is running, the first press clears the prompt input and a second press exits Claude Code |
| `Ctrl+X Ctrl+K` | Stop all running [background subagents](/docs/en/sub-agents#run-subagents-in-foreground-or-background) in this session. Press twice within 3 seconds to confirm | Subagent control |
| `Ctrl+D` | Exit Claude Code session | The first press shows a confirmation hint and a second press within 800ms exits. When the prompt has text, `Ctrl+D` deletes the character after the cursor instead |
| `Ctrl+G` or `Ctrl+X Ctrl+E` | Open in default text editor | Edit your prompt or custom response in your default text editor. `Ctrl+X Ctrl+E` is the readline-native binding. Turn on **Show last response in external editor** in `/config` to prepend Claude's previous reply as `#`-commented context above your prompt; Claude Code strips the comment block when you save |
| `Ctrl+L` | Redraw screen | Forces a full terminal redraw, keeping input and conversation history. Use this to recover if the display becomes garbled or partially blank. In [fullscreen rendering](/docs/en/fullscreen#clear-the-conversation), if you press `Ctrl+L` once, Claude Code redraws the screen and also shows a hint that pressing it again runs `/clear`. If you press it twice within two seconds, Claude Code runs `/clear` and starts a new conversation |
| `Ctrl+O` | Toggle transcript viewer | Shows detailed tool usage and execution, with a timestamp and the model used on each assistant message. Also expands MCP calls, which collapse to a single line like "Called slack 3 times" by default |
| `Ctrl+R` | Reverse search command history | Search through previous commands interactively |
| `Ctrl+V` or `Cmd+V` (iTerm2) or `Alt+V` (Windows and WSL) | Paste image from clipboard | Inserts an `[Image #N]` chip at the cursor so you can reference it positionally in your prompt. On WSL, both `Ctrl+V` and `Alt+V` are bound; use `Alt+V` if your terminal intercepts `Ctrl+V` |
| `Ctrl+B` | Background running tasks | Backgrounds Bash commands and agents. Tmux users press twice |
| `Ctrl+T` | Toggle Claude's task checklist | Show or hide [Claude's to-do checklist](#task-list) in the status area. This is not the background-task view; use [`/tasks`](/docs/en/commands) to see running shells and subagents |
| `Ctrl+S` | Stash or restore prompt | With text in the input, stashes it and clears the prompt. Pressed again on an empty prompt, restores the stashed text, cursor position, and pasted content |
| `Ctrl+Z` | Suspend Claude Code | Unix only. Suspends the process to your shell; run `fg` to resume |
| `Left/Right arrows` | Cycle through dialog tabs | Navigate between tabs in permission dialogs and menus |
| `Up/Down arrows` or `Ctrl+P`/`Ctrl+N` | Move cursor or navigate command history | When the input spans more than one visual row, whether wrapped or multiline, first moves the cursor within the prompt. Once the cursor is on the first or last visual row, pressing again navigates command history |
| `Esc` | Interrupt Claude, or close a dialog | Stop the current response or tool call mid-turn so you can redirect. Claude keeps the work done so far. When a dialog such as a permission prompt is open, `Esc` closes the dialog rather than interrupting Claude |
| `Esc` + `Esc` | Clear input draft, or rewind | When the prompt input contains text, double `Esc` clears it and saves the draft to history so `Up` recalls it. When the input is empty, double `Esc` opens the [rewind menu](/docs/en/checkpointing) to restore or summarize code and conversation from a previous point |
| `Shift+Tab`, or `Alt+M` on Windows when the Node or Bun runtime doesn't enable VT input mode | Cycle permission modes | Cycle through `default` (labeled Manual in the mode indicator), `acceptEdits`, `plan`, and any modes you have enabled, such as `auto` or `bypassPermissions`. See [permission modes](/docs/en/permission-modes). |
| `Option+P` (macOS) or `Alt+P` (Windows/Linux) | Switch model | Switch models without clearing your prompt |
| `Option+T` (macOS) or `Alt+T` (Windows/Linux) | Toggle extended thinking | Enable or disable extended thinking mode. Has no effect on Fable 5, which always uses extended thinking. Works on macOS without configuring Option as Meta |
| `Option+O` (macOS) or `Alt+O` (Windows/Linux) | Toggle fast mode | Enable or disable [fast mode](/docs/en/fast-mode) |
### Text editing
| Shortcut | Description | Context |
| :------------------------- | :----------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `Ctrl+A` | Move cursor to start of current line | In multiline input, moves to the start of the current logical line |
| `Ctrl+E` | Move cursor to end of current line | In multiline input, moves to the end of the current logical line |
| `Ctrl+K` | Delete to end of line | Stores deleted text for pasting |
| `Ctrl+U` | Delete from cursor to line start | Stores deleted text for pasting. Repeat to clear across lines in multiline input. On macOS, terminal emulators including iTerm2 and Terminal.app map `Cmd+Backspace` to this shortcut |
| `Ctrl+W` | Delete previous word | Stores deleted text for pasting. On macOS, `Option+Delete` also deletes the previous word, and on Windows, `Ctrl+Backspace` does |
| `Ctrl+Y` | Paste deleted text | Paste text deleted with `Ctrl+K`, `Ctrl+U`, or `Ctrl+W` |
| `Alt+Y` (after `Ctrl+Y`) | Cycle paste history | After pasting, cycle through previously deleted text. Requires [Option as Meta](#keyboard-shortcuts) on macOS |
| `Alt+B` | Move cursor back one word | Word navigation. Requires [Option as Meta](#keyboard-shortcuts) on macOS |
| `Alt+F` | Move cursor forward one word | Word navigation. Requires [Option as Meta](#keyboard-shortcuts) on macOS |
| `Ctrl+_` or `Ctrl+Shift+-` | Undo last input edit | Restores the previous input text and cursor position |
### Theme and display
| Shortcut | Description | Context |
| :------- | :----------------------------------------- | :----------------------------------------------------------------------------------------------------------- |
| `Ctrl+T` | Toggle syntax highlighting for code blocks | Only works inside the `/theme` picker menu. Controls whether code in Claude's responses uses syntax coloring |
### Multiline input
| Method | Shortcut | Context |
| :--------------- | :------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Quick escape | `\` + `Enter` | Works in all terminals |
| Option key | `Option+Enter` | After enabling [Option as Meta](/docs/en/terminal-config#enable-option-key-shortcuts-on-macos) on macOS |
| Shift+Enter | `Shift+Enter` | Native in iTerm2, WezTerm, Ghostty, Kitty, Warp, Apple Terminal, Windows Terminal. For other terminals, see [Enter multiline prompts](/docs/en/terminal-config#enter-multiline-prompts) |
| Control sequence | `Ctrl+J` | Works in any terminal without configuration |
| Paste mode | Paste directly | For code blocks, logs |
### Quick commands
| Shortcut | Description | Notes |
| :----------------- | :----------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `/` at start | Command or skill | See [commands](#commands) and [skills](/docs/en/skills) |
| `!` at start | Shell mode | Run a command directly, add its output to the session, and have Claude respond to it |
| `@` | File path mention | Trigger file path autocomplete. In sessions with [cross-session messaging](/docs/en/cross-session-messaging#message-another-session), when you type at least one letter after the `@`, Claude Code also suggests your other live sessions on this machine, so you can tell Claude to message the one you pick. Requires Claude Code v2.1.232 or later |
| `:` | Emoji shortcode | Type a full `:name:` to insert the emoji, or two or more characters for suggestions. See [Emoji shortcodes](#emoji-shortcodes). Requires Claude Code v2.1.217 or later |
| `?` on empty input | Toggle the shortcut help panel | Typing `?` when the input already contains text inserts the character |
### Transcript viewer
When the transcript viewer is open (toggled with `Ctrl+O`), these shortcuts are available. Run `/tui` with no argument to check which renderer is active. `Ctrl+E` can be rebound via [`transcript:toggleShowAll`](/docs/en/keybindings).
| Shortcut | Description |
| :------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `?` | Toggle the keyboard shortcut help panel. Requires [fullscreen rendering](/docs/en/fullscreen) |
| `{` / `}` | Jump to the previous or next user prompt, like vim paragraph motion. Requires [fullscreen rendering](/docs/en/fullscreen) |
| `Ctrl+E` | Toggle show all content. Available in the classic renderer only, not in [fullscreen rendering](/docs/en/fullscreen) |
| `[` | Write the full conversation to your terminal's native scrollback so `Cmd+F`, tmux copy mode, and other native tools can search it. Requires [fullscreen rendering](/docs/en/fullscreen#search-and-review-the-conversation) |
| `v` | Write the conversation to a temporary file and open it in `$VISUAL` or `$EDITOR`. Requires [fullscreen rendering](/docs/en/fullscreen) |
| `q`, `Ctrl+C`, `Esc` | Exit transcript view. All three can be rebound via [`transcript:exit`](/docs/en/keybindings) |
### Voice input
| Shortcut | Description | Notes |
| :------------------ | :-------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Hold or tap `Space` | Voice dictation | Requires [voice dictation](/docs/en/voice-dictation) to be enabled. Hold to record, or run `/voice tap` for tap-to-toggle. [Rebindable](/docs/en/voice-dictation#rebind-the-dictation-key) |
## Commands
Type `/` in Claude Code to see all available commands, or type `/` followed by any letters to filter. The `/` menu shows everything you can invoke: built-in commands, bundled and user-authored [skills](/docs/en/skills), and commands contributed by [plugins](/docs/en/plugins) and [MCP servers](/docs/en/mcp#use-mcp-prompts-as-commands). Not all built-in commands are visible to every user since some depend on your platform or plan.
In [fullscreen rendering](/docs/en/fullscreen#use-the-mouse), the `/` command and `@` file suggestion lists also respond to the mouse: hovering highlights a row and clicking accepts it.
See the [commands reference](/docs/en/commands) for the full list of commands included in Claude Code.
## Vim editor mode
Enable vim-style editing via `/config` → Editor mode.
### Mode switching
| Command | Action | From mode |
| :------ | :------------------------------------ | :------------- |
| `Esc` | Enter NORMAL mode | INSERT, VISUAL |
| `i` | Insert before cursor | NORMAL |
| `I` | Insert at beginning of line | NORMAL |
| `a` | Insert after cursor | NORMAL |
| `A` | Insert at end of line | NORMAL |
| `o` | Open line below | NORMAL |
| `O` | Open line above | NORMAL |
| `v` | Start character-wise visual selection | NORMAL |
| `V` | Start line-wise visual selection | NORMAL |
### Remap INSERT-mode key sequences
The [`vimInsertModeRemaps`](/docs/en/settings#available-settings) setting maps a two-key INSERT-mode sequence to Escape, so a mapping like `jj` returns you to NORMAL mode. Requires Claude Code v2.1.208 or later.
The following `~/.claude/settings.json` example turns on vim mode and maps `jj` to Escape:
```json theme={null}
{
"editorMode": "vim",
"vimInsertModeRemaps": { "jj": "<Esc>" }
}
```
Each key is exactly two printable characters typed in sequence, and `"<Esc>"` is the only supported target. Entries with a different length or target are ignored.
Typing the first character of a sequence inserts it normally. Pressing the second character within one second removes that pending character and switches to NORMAL mode, leaving neither character in your input. After the one-second window, or if a different key follows, both characters stay as literal text, so you can still type a word containing the sequence by pausing between the two keys.
Claude Code reads this setting from your user settings file, the `--settings` flag, and [managed settings](/docs/en/permissions#managed-settings) only. Entries in a project's `.claude/settings.json` or `.claude/settings.local.json` are ignored, so a checked-out repository can't remap your keystrokes.
### Navigation (NORMAL mode)
| Command | Action |
| :-------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------- |
| `h`/`j`/`k`/`l` | Move left/down/up/right |
| `Space` | Move right |
| `w` | Next word |
| `e` | End of word |
| `b` | Previous word |
| `0` | Beginning of line |
| `$` | End of line |
| `^` | First non-blank character |
| `gg` | Beginning of input |
| `G` | End of input |
| `f{char}` | Jump to next occurrence of character |
| `F{char}` | Jump to previous occurrence of character |
| `t{char}` | Jump to just before next occurrence of character |
| `T{char}` | Jump to just after previous occurrence of character |
| `;` | Repeat last f/F/t/T motion |
| `,` | Repeat last f/F/t/T motion in reverse |
| `/` | Open reverse history search, same as `Ctrl+R`. The empty search prompt shows a hint: press `Esc` then `i` then `/` to open the command menu instead |
<Note>
In vim NORMAL mode, if the cursor is at the beginning or end of input and can't move further, `j`/`k` and `↑`/`↓` navigate command history instead. `←` on an empty prompt opens [agent view](/docs/en/agent-view) from NORMAL mode as well as INSERT; before v2.1.219, `←` on an empty prompt did nothing in NORMAL mode.
</Note>
### Editing (NORMAL mode)
| Command | Action |
| :------------- | :------------------------------------------------------------------------------------------------------------------------ |
| `x` | Delete character |
| `dd` | Delete line |
| `D` | Delete to end of line |
| `dw`/`de`/`db` | Delete word/to end/back |
| `cc` | Change line |
| `C` | Change to end of line |
| `cw`/`ce`/`cb` | Change word/to end/back |
| `s` | Substitute character: delete the character under the cursor and enter INSERT mode. Requires Claude Code v2.1.211 or later |
| `S` | Substitute line: clear the line and enter INSERT mode. Requires Claude Code v2.1.211 or later |
| `yy`/`Y` | Yank (copy) line |
| `yw`/`ye`/`yb` | Yank word/to end/back |
| `p` | Paste after cursor |
| `P` | Paste before cursor |
| `>>` | Indent line |
| `<<` | Dedent line |
| `J` | Join lines |
| `u` | Undo |
| `.` | Repeat last change |
### Text objects (NORMAL mode)
Text objects work with operators like `d`, `c`, and `y`:
| Command | Action |
| :-------- | :--------------------------------------- |
| `iw`/`aw` | Inner/around word |
| `iW`/`aW` | Inner/around WORD (whitespace-delimited) |
| `i"`/`a"` | Inner/around double quotes |
| `i'`/`a'` | Inner/around single quotes |
| `i(`/`a(` | Inner/around parentheses |
| `i[`/`a[` | Inner/around brackets |
| `i{`/`a{` | Inner/around braces |
### Visual mode
Press `v` for character-wise selection or `V` for line-wise selection. Motions extend the selection, and operators act on it directly.
| Command | Action |
| :--------------- | :--------------------------------------------------- |
| `d`/`x` | Delete selection |
| `y` | Yank selection |
| `c`/`s` | Change selection |
| `p` | Replace selection with register contents |
| `r{char}` | Replace every selected character with `{char}` |
| `~`/`u`/`U` | Toggle, lowercase, or uppercase selection |
| `>`/`<` | Indent or dedent selected lines |
| `J` | Join selected lines |
| `o` | Swap cursor and anchor |
| `iw`/`aw`/`i"`/… | Select a text object |
| `v`/`V` | Toggle between character-wise and line-wise, or exit |
Block-wise visual mode with `Ctrl+V` is not supported.
## Command history
Claude Code keeps a history of the prompts you type, and Up-arrow recall reaches prompts from past sessions of the same project:
* Input history is stored per working directory
* Running `/clear` starts a new session: recall then lists the new session's prompts first, with earlier sessions' prompts after them. The previous session's conversation is preserved and can be resumed.
* Submitting the same prompt twice in a row records one history entry, so pressing Up steps to the previous distinct prompt
* When you recall a prompt that included pasted text, Claude Code sends the full pasted content again when you resubmit. If the content has since been [cleaned up](/docs/en/claude-directory#cleaned-up-automatically), Claude Code doesn't send the literal `[Pasted text #N]` string; see [Paste large content](/docs/en/terminal-config#paste-large-content) for what happens to the prompt
* History expansion with `!` is disabled by default
### Reverse search with Ctrl+R
Press `Ctrl+R` to interactively search through your command history. In [fullscreen rendering](/docs/en/fullscreen), `Ctrl+R` opens a search dialog instead: type to filter, press `Up` and `Down` to move through matches, and press `Ctrl+S` to cycle the scope through this session, this project, and all projects. Press `Enter` or `Tab` to place a match in the prompt input, or `Esc` to cancel. The steps below describe the classic renderer's inline search:
1. **Start search**: press `Ctrl+R` to activate reverse history search
2. **Type query**: enter text to search for in previous commands. The search term is highlighted in matching results
3. **Navigate matches**: press `Ctrl+R` again to cycle through older matches
4. **Search scope**: the inline search always searches prompts from all projects
5. **Accept match**:
* Press `Tab` or `Esc` to accept the current match and continue editing
* Press `Enter` to accept and execute the command immediately
6. **Cancel search**:
* Press `Ctrl+C` to cancel and restore your original input
* Press `Backspace` on empty search to cancel
The inline search scans your full prompt history, newest first, with duplicates collapsed to the newest occurrence. The fullscreen dialog searches your whole prompt history in the selected scope, newest first, with duplicates collapsed to the newest occurrence: the most recent prompts appear immediately, and matches from older prompts fill in as Claude Code loads the rest. Matching prompts display with the search term highlighted, so you can find and reuse previous inputs.
Accepting a match or canceling the search takes effect immediately, even while Claude Code is still loading the history.
## Background Bash commands
Claude Code supports running Bash commands in the background, allowing you to continue working while long-running processes execute.
### How backgrounding works
When Claude Code runs a command in the background, it runs the command asynchronously and immediately returns a background task ID. Claude Code can respond to new prompts while the command continues executing in the background.
To run commands in the background, you can either:
* Prompt Claude Code to run a command in the background
* Press `Ctrl+B` to move a regular Bash tool invocation to the background. Tmux users must press `Ctrl+B` twice due to tmux's prefix key.
**Key features:**
* Output is written to a file and Claude can retrieve it using the Read tool
* Background tasks have unique IDs for tracking and output retrieval
* Background tasks are automatically cleaned up when Claude Code exits. If you background the session instead of exiting it, Claude Code hands them to the background session, where they keep running. See [background a running session](/docs/en/agent-view#from-inside-a-session)
* Background tasks are automatically terminated if output exceeds 5GB, with a note in stderr explaining why
* On macOS and Linux, Claude Code terminates running background tasks when the operating system signals memory pressure, provided the session has been idle for at least 30 minutes and no turn or subagent is running. Set [`CLAUDE_CODE_DISABLE_BG_SHELL_PRESSURE_REAP`](/docs/en/env-vars) to `1` to turn this off. Requires Claude Code v2.1.193 or later. Background commands owned by a [subagent](/docs/en/sub-agents) are instead terminated after 60 minutes, configurable in milliseconds with [`CLAUDE_SUBAGENT_BG_SHELL_MAX_MS`](/docs/en/env-vars). A command owned by a subagent running in the foreground also ends when that subagent gives its final response; see [Background commands](/docs/en/tools-reference#background-commands) in the tools reference. Before v2.1.218, neither the memory-pressure reap nor the 60-minute limit covered commands moved to the background with `Ctrl+B`
To disable all background task functionality, set the `CLAUDE_CODE_DISABLE_BACKGROUND_TASKS` environment variable to `1`. See [Environment variables](/docs/en/env-vars) for details.
**Common backgrounded commands:**
* Build tools (webpack, vite, make)
* Package managers (npm, yarn, pnpm)
* Test runners (jest, pytest)
* Development servers
* Long-running processes (docker, terraform)
### Shell mode with `!` prefix
Run shell commands directly without going through Claude by prefixing your input with `!`:
```bash theme={null}
! npm test
! git status
! ls -la
```
Shell mode:
* Adds the command and its output to the conversation context
* Shows real-time progress and output
* Supports the same `Ctrl+B` backgrounding for long-running commands
Cut at 300 lines. The page has the rest.
jetbrains First recorded · 224 lines, first recorded
# JetBrains IDEs ## Supported IDEs ## Features ## Installation ## Usage ### From your IDE ### From external terminals ## Configuration ### Claude Code settings ### Plugin settings #### General settings #### ESC key configuration ## Special configurations ### Remote development ### WSL configuration #### Allow WSL2 traffic through Windows Firewall #### Switch WSL2 to mirrored networking ## Troubleshooting ### Plugin not working ### IDE not detected ### Command not found ## Security considerations ### The built-in IDE MCP server
The first capture of this source. The page was already there, and this is what it said.
# JetBrains IDEs
> Use Claude Code with JetBrains IDEs including IntelliJ, PyCharm, WebStorm, and more
Claude Code integrates with JetBrains IDEs through a dedicated plugin, providing features like interactive diff viewing, selection context sharing, and more.
## Supported IDEs
The Claude Code plugin works with most JetBrains IDEs, including:
* IntelliJ IDEA
* PyCharm
* Android Studio
* WebStorm
* PhpStorm
* GoLand
## Features
* **Quick launch**: use `Cmd+Esc` (Mac) or `Ctrl+Esc` (Windows/Linux) to open Claude Code directly from your editor, or click the Claude Code button in the UI
* **Diff viewing**: Claude Code opens code changes in the IDE diff viewer instead of the terminal; change this with the **Diff tool** setting in `/config`
* **Selection context**: the current selection or tab in the IDE is automatically shared with Claude Code. [`Read` deny rules](/docs/en/permissions#read-and-edit) block this sharing for matching files
* **File reference shortcuts**: use `Cmd+Option+K` (Mac) or `Alt+Ctrl+K` (Linux/Windows) to insert file references such as `@src/auth.ts#L1-99`
* **Diagnostic sharing**: after Claude edits a file, Claude Code pulls the IDE's new diagnostics for that file, such as lint and syntax errors, into the conversation, so Claude notices errors its edits introduce
## Installation
The plugin runs the `claude` command in your IDE's integrated terminal and connects to it. It does not bundle its own copy of the CLI, so install both pieces:
<Steps>
<Step title="Install the Claude Code CLI">
Follow the [quickstart](/docs/en/quickstart) to install the CLI if you haven't already. The plugin shows a "Cannot launch Claude Code" notification when `claude` isn't on your PATH.
</Step>
<Step title="Install the JetBrains plugin">
Install the [Claude Code plugin](https://plugins.jetbrains.com/plugin/27310-claude-code-beta-) from the JetBrains Marketplace and restart your IDE.
</Step>
</Steps>
If `claude` is installed somewhere your IDE can't find, set the full path in the plugin's [Claude command setting](#general-settings).
Claude Code works with any paid Claude subscription (Pro, Max, Team, or Enterprise) or a Claude Console account, and no API key is required. You'll be prompted to [log in](/docs/en/authentication#log-in-to-claude-code) the first time you run `claude`.
<Note>
After installing the plugin, you may need to restart your IDE completely for it to take effect.
</Note>
## Usage
### From your IDE
Run `claude` from your IDE's integrated terminal, and all integration features will be active.
### From external terminals
Use the `/ide` command in any external terminal to connect Claude Code to your JetBrains IDE and activate all features:
```bash theme={null}
claude
```
```text theme={null}
/ide
```
When the connection succeeds, Claude Code confirms with a message like `Connected to IntelliJ IDEA.` If Claude Code detects a running IDE that doesn't have the plugin, `/ide` installs the plugin for you and asks you to restart the IDE.
If you want Claude to have access to the same files as your IDE, start Claude Code from the same directory as your IDE project root.
## Configuration
### Claude Code settings
Configure IDE integration through Claude Code's settings:
1. Run `claude`
2. Enter the `/config` command
3. Set **Diff tool** to `auto` to show diffs in the IDE, or `terminal` to keep them in the terminal
The **Diff tool** entry appears in `/config` only when Claude Code is connected to the IDE, so run `claude` from the JetBrains terminal or run [`/ide`](/docs/en/commands) first from an external terminal. See [`diffTool`](/docs/en/settings#global-config-settings) for the underlying setting.
### Plugin settings
Configure the Claude Code plugin by going to **Settings → Tools → Claude Code \[Beta]**:
#### General settings
* **Claude command**: specify a custom command to run Claude, for example `claude`, `/usr/local/bin/claude`, or `npx @anthropic-ai/claude-code`
* **Suppress notification for Claude command not found**: skip notifications about not finding the Claude command
* **Enable using Option+Enter for multi-line prompts**: on macOS only. When enabled, Option+Enter inserts new lines in Claude Code prompts. Disable if the Option key is being captured unexpectedly. Requires a terminal restart.
* **Enable automatic updates**: automatically check for and install plugin updates, applied on restart
<Tip>
For WSL users: Set `wsl -d Ubuntu -- bash -lic "claude"` as your Claude command (replace `Ubuntu` with your WSL distribution name)
</Tip>
#### ESC key configuration
If the ESC key doesn't interrupt Claude Code operations in JetBrains terminals:
1. Go to **Settings → Tools → Terminal**
2. Either:
* Uncheck "Move focus to the editor with Escape", or
* Click "Configure terminal keybindings" and delete the "Switch focus to Editor" shortcut
3. Apply the changes
This allows the ESC key to properly interrupt Claude Code operations.
## Special configurations
### Remote development
<Warning>
When using JetBrains Remote Development, you must install the plugin on the remote host via **Settings → Plugin (Host)**, not on your local client machine.
</Warning>
### WSL configuration
If you're using Claude Code on WSL2 with a JetBrains IDE and see "No available IDEs detected", the cause is usually WSL2's NAT networking or Windows Firewall blocking the connection between WSL2 and the IDE running on the Windows host. WSL1 uses the host's network directly and isn't affected.
#### Allow WSL2 traffic through Windows Firewall
This is the recommended fix because it keeps your existing WSL2 networking mode.
<Steps>
<Step title="Find your WSL2 IP address">
From inside your WSL shell, run:
```bash theme={null}
hostname -I
```
Note your subnet: take the first two segments of the address and follow them with `.0.0/16`. For example, if the address is `172.21.123.45`, your subnet is `172.21.0.0/16`.
</Step>
<Step title="Create a firewall rule">
Open PowerShell as Administrator and run the following, adjusting the IP range to match your subnet:
```powershell theme={null}
New-NetFirewallRule -DisplayName "Allow WSL2 Internal Traffic" -Direction Inbound -Protocol TCP -Action Allow -RemoteAddress 172.21.0.0/16 -LocalAddress 172.21.0.0/16
```
</Step>
<Step title="Restart your IDE and Claude Code">
Close and reopen both so the new rule takes effect.
</Step>
</Steps>
#### Switch WSL2 to mirrored networking
Mirrored networking requires Windows 11 22H2 or later. If you're on Windows 10, use the firewall rule above instead.
Add this to `.wslconfig` in your Windows user directory:
```ini theme={null}
[wsl2]
networkingMode=mirrored
```
Then restart WSL with `wsl --shutdown` from PowerShell.
## Troubleshooting
### Plugin not working
If the plugin is installed but Claude Code features don't appear in your IDE:
* Ensure you're running Claude Code from the project root directory
* Check that the JetBrains plugin is enabled in the IDE settings
* Completely restart the IDE (you may need to do this multiple times)
* For Remote Development, ensure the plugin is installed in the remote host
### IDE not detected
If the `/ide` command shows "No available IDEs detected":
* Verify the plugin is installed and enabled
* Restart the IDE completely
* If you expected an automatic connection without running `/ide`, check that you launched `claude` from the IDE's integrated terminal
* For WSL users, see [WSL configuration](#wsl-configuration) above
### Command not found
If clicking the Claude icon shows "command not found":
1. Verify Claude Code is installed by running `claude --version` in a terminal
2. Configure the Claude command path in plugin settings
3. For WSL users, use the WSL command format mentioned in the configuration section
## Security considerations
When Claude Code runs in a JetBrains IDE in [`acceptEdits` permission mode](/docs/en/permission-modes#auto-approve-file-edits-with-acceptedits-mode), it may be able to modify IDE configuration files that can be automatically executed by your IDE. This may increase the risk of running Claude Code in `acceptEdits` mode and allow bypassing Claude Code's permission prompts for bash execution.
When running in JetBrains IDEs, consider:
* Using manual approval mode for edits
* Taking extra care to ensure Claude is only used with trusted prompts
* Being aware of which files Claude Code has access to modify
For Claude Code installation or login problems outside the IDE, see [Troubleshoot installation and login](/docs/en/troubleshoot-install).
### The built-in IDE MCP server
When the plugin is active, it runs a local MCP server that the CLI connects to automatically. This is how the CLI opens diffs in the IDE's native diff viewer, reads your current selection for `@`-mentions, and pulls inspection diagnostics into the conversation.
The server is named `ide` and is hidden from `/mcp` because there's nothing to configure. If your organization uses a [`PreToolUse` hook](/docs/en/hooks#pretooluse) to allowlist MCP tools, though, you'll need to know it exists.
**Selection and open-file context.** While connected, the CLI includes your current editor selection and the path of the active file as context on each prompt you send. The transcript shows a `⧉ Selected N lines from <file>` line when this happens. To exclude a sensitive file such as `.env`, add a [`Read` deny rule](/docs/en/permissions#read-and-edit) for its path. A matching deny rule prevents both the selected text and the open-file notice for that file from reaching Claude.
**Transport and authentication.** The server listens on an OS-assigned ephemeral port, and the port is not configurable. The transport is unencrypted `ws://`; on loopback, any process that could capture the traffic can also read the token from the lock file, so TLS would not add protection against a local attacker. Each IDE start generates a fresh random auth token, writes it to a lock file at `~/.claude/ide/<port>.lock`, and the CLI must present it as the `X-Claude-Code-Ide-Authorization` header to connect. If `CLAUDE_CONFIG_DIR` is set, the lock file is written to `$CLAUDE_CONFIG_DIR/ide/` instead.
**Tools exposed to the model.** The server hosts several tools, but only one is visible to the model. The rest are internal RPC the CLI uses for its own UI, such as opening diffs and reading selections, and are filtered out before the tool list reaches Claude.
| Tool name (as seen by hooks) | What it does | Read-only |
| ---------------------------- | --------------------------------------------------------------------------------------------------------------------- | --------- |
| `mcp__ide__getDiagnostics` | Returns the IDE's inspection diagnostics, the errors and warnings shown in the editor. Optionally scoped to one file. | Yes |
The JetBrains plugin does not expose a code-execution tool to the model.
**Listening interface.** Which network interface the server binds to is controlled by **Accept connections from all network interfaces** under **Settings → Tools → Claude Code \[Beta] → Networking (Advanced)**. With the setting disabled, the server listens on `127.0.0.1` only and is not reachable from other hosts. With it enabled, the port is reachable from your local network. The setting exists for cases where the CLI cannot reach the IDE over loopback, such as WSL2 with default NAT networking or a remote-IDE setup; see [WSL configuration](#wsl-configuration) for that scenario.
<Warning>
Enabling **Accept connections from all network interfaces** makes the IDE MCP port reachable from your local network. Connections still require the auth token from the lock file, but because the transport is unencrypted `ws://`, both the session traffic and that token cross the network in cleartext when the setting is on. Only turn it on when loopback genuinely cannot work. For WSL2, prefer [mirrored networking](#switch-wsl2-to-mirrored-networking) so the Windows loopback interface is shared with the Linux VM and the socket can stay on loopback.
</Warning>
keybindings First recorded · 476 lines, first recorded
# Customize keyboard shortcuts ## Configuration file ## Contexts ## Available actions ### App actions ### History actions ### Chat actions ### Autocomplete actions ### Confirmation actions ### Permission actions ### Transcript actions ### History search actions ### Task actions ### Theme actions ### Help actions ### Tabs actions ### Attachments actions ### Footer actions ### Message selector actions ### Diff actions ### Model picker actions ### Select actions ### Plugin actions ### Settings actions ### Voice actions ### Scroll actions ## Keystroke syntax ### Modifiers ### Uppercase letters ### Chords ### Special keys ## Unbind default shortcuts ## Reserved shortcuts ## Terminal conflicts ## Vim mode interaction ## Validation
The first capture of this source. The page was already there, and this is what it said.
# Customize keyboard shortcuts
> Customize keyboard shortcuts in Claude Code with a keybindings configuration file.
Claude Code supports customizable keyboard shortcuts. Run `/keybindings` to create or open your configuration file at `~/.claude/keybindings.json`.
## Configuration file
The keybindings configuration file is an object with a `bindings` array. Each block specifies a context and a map of keystrokes to actions.
<Note>Changes to the keybindings file are automatically detected and applied without restarting Claude Code.</Note>
| Field | Description |
| :--------- | :------------------------------------------------- |
| `$schema` | Optional JSON Schema URL for editor autocompletion |
| `$docs` | Optional documentation URL |
| `bindings` | Array of binding blocks by context |
This example binds `Ctrl+E` to open an external editor in the chat context, and unbinds `Ctrl+U`:
```json theme={null}
{
"$schema": "https://www.schemastore.org/claude-code-keybindings.json",
"$docs": "https://code.claude.com/docs/en/keybindings",
"bindings": [
{
"context": "Chat",
"bindings": {
"ctrl+e": "chat:externalEditor",
"ctrl+u": null
}
}
]
}
```
## Contexts
Each binding block specifies a **context** where the bindings apply:
| Context | Description |
| :---------------- | :----------------------------------------------------------- |
| `Global` | Applies everywhere in the app |
| `Chat` | Main chat input area |
| `Autocomplete` | Autocomplete menu is open |
| `Settings` | Settings menu |
| `Confirmation` | Permission and confirmation dialogs |
| `Tabs` | Tab navigation components |
| `Help` | Help menu is visible |
| `Transcript` | Transcript viewer |
| `HistorySearch` | History search mode (Ctrl+R) |
| `Task` | Background task is running |
| `ThemePicker` | Theme picker dialog |
| `Attachments` | Image attachment navigation in select dialogs |
| `Footer` | Footer indicator navigation (tasks, teams, diff, artifacts) |
| `MessageSelector` | Rewind and summarize dialog message selection |
| `DiffDialog` | Diff viewer navigation |
| `ModelPicker` | Model picker effort level |
| `Select` | Generic select/list components |
| `Plugin` | Plugin dialog (browse, discover, manage) |
| `Scroll` | Conversation scrolling and text selection in fullscreen mode |
Before v2.1.205, a `Doctor` context and a `doctor:fix` action existed for the `/doctor` diagnostics screen.
## Available actions
Actions follow a `namespace:action` format, such as `chat:submit` to send a message or `app:toggleTodos` to show the task list. Each context has specific actions available.
### App actions
Actions available in the `Global` context:
| Action | Default | Description |
| :--------------------- | :-------- | :----------------------------------------------------------------------------------------------------------- |
| `app:interrupt` | Ctrl+C | Cancel current operation |
| `app:exit` | Ctrl+D | Exit Claude Code. Press twice within 800ms to confirm |
| `app:redraw` | (unbound) | Force terminal redraw |
| `app:toggleTodos` | Ctrl+T | Toggle visibility of Claude's to-do checklist. This is not the [`/tasks`](/docs/en/commands) background-task view |
| `app:toggleTranscript` | Ctrl+O | Toggle verbose transcript |
### History actions
Actions for navigating command history:
| Action | Default | Description |
| :----------------- | :------ | :-------------------- |
| `history:search` | Ctrl+R | Open history search |
| `history:previous` | Up | Previous history item |
| `history:next` | Down | Next history item |
### Chat actions
Actions available in the `Chat` context:
| Action | Default | Description |
| :-------------------- | :-------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `chat:cancel` | Escape | Cancel current input |
| `chat:clearInput` | Ctrl+L | Force a full screen redraw, preserving input. In [fullscreen rendering](/docs/en/fullscreen#clear-the-conversation), press twice within two seconds to run `/clear` |
| `chat:clearScreen` | Cmd+K | In [fullscreen rendering](/docs/en/fullscreen#clear-the-conversation), press twice within two seconds to run `/clear` |
| `chat:killAgents` | Ctrl+X Ctrl+K | Stop all running [background subagents](/docs/en/sub-agents#run-subagents-in-foreground-or-background) in this session |
| `chat:cycleMode` | Shift+Tab\* | Cycle permission modes |
| `chat:modelPicker` | Meta+P | Open model picker |
| `chat:fastMode` | Meta+O | Toggle fast mode |
| `chat:thinkingToggle` | Meta+T | Toggle extended thinking |
| `chat:submit` | Enter | Submit message |
| `chat:newline` | Ctrl+J | Insert a newline without submitting |
| `chat:undo` | Ctrl+\_, Ctrl+Shift+- | Undo last action |
| `chat:externalEditor` | Ctrl+G, Ctrl+X Ctrl+E | Open in external editor |
| `chat:stash` | Ctrl+S | Stash current prompt |
| `chat:imagePaste` | Ctrl+V (Alt+V on Windows and WSL) | Paste image from clipboard. On WSL, both shortcuts are bound by default |
\*On Windows without VT mode (Node \<24.2.0/\<22.17.0, Bun \<1.2.23), defaults to Meta+M.
### Autocomplete actions
Actions available in the `Autocomplete` context:
| Action | Default | Description |
| :---------------------- | :------ | :------------------ |
| `autocomplete:accept` | Tab | Accept suggestion |
| `autocomplete:dismiss` | Escape | Dismiss menu |
| `autocomplete:previous` | Up | Previous suggestion |
| `autocomplete:next` | Down | Next suggestion |
### Confirmation actions
Actions available in the `Confirmation` context:
| Action | Default | Description |
| :-------------------------- | :-------- | :--------------------------------------------------------------------------------------------------------------------------------- |
| `confirm:yes` | Y, Enter | Confirm action |
| `confirm:no` | N, Escape | Decline action |
| `confirm:previous` | Up | Previous option |
| `confirm:next` | Down | Next option |
| `confirm:nextField` | Tab | Next field |
| `confirm:previousField` | (unbound) | Previous field |
| `confirm:toggle` | Space | Toggle selection |
| `confirm:cycleMode` | Shift+Tab | Cycle permission modes |
| `confirm:toggleExplanation` | Ctrl+E | Toggle a model-generated [explanation of the command](/docs/en/permissions#permission-system) on Bash and PowerShell permission prompts |
### Permission actions
Actions available in the `Confirmation` context for permission dialogs:
| Action | Default | Description |
| :----------------------- | :-------- | :------------------------------------------------------------------------------------------------------------------ |
| `permission:toggleDebug` | (unbound) | Toggle permission debug info. The previous default of Ctrl+D was removed in v2.1.146 because it shadowed `app:exit` |
### Transcript actions
Actions available in the `Transcript` context:
| Action | Default | Description |
| :------------------------- | :---------------- | :---------------------- |
| `transcript:toggleShowAll` | Ctrl+E | Toggle show all content |
| `transcript:exit` | q, Ctrl+C, Escape | Exit transcript view |
`transcript:toggleShowAll` applies in the classic renderer only; in [fullscreen rendering](/docs/en/fullscreen), the transcript viewer doesn't offer a show-all toggle.
### History search actions
Actions available in the `HistorySearch` context:
| Action | Default | Description |
| :------------------------- | :---------- | :---------------------------------------- |
| `historySearch:next` | Ctrl+R | Next match |
| `historySearch:accept` | Escape, Tab | Accept selection |
| `historySearch:cancel` | Ctrl+C | Cancel search |
| `historySearch:execute` | Enter | Execute selected command |
| `historySearch:cycleScope` | Ctrl+S | Cycle scope: session, project, everywhere |
The `historySearch:next`, `historySearch:accept`, `historySearch:cancel`, and `historySearch:execute` defaults apply to the inline history search in the classic renderer, which always searches prompts from all projects. `historySearch:cycleScope` takes effect only in [fullscreen rendering](/docs/en/fullscreen), where `Ctrl+R` opens a search dialog instead and `Ctrl+S` cycles its scope. The dialog's other keys are fixed and can't be rebound: `Enter` or `Tab` places the highlighted match in the prompt input and `Esc` cancels.
### Task actions
Actions available in the `Task` context:
| Action | Default | Description |
| :---------------- | :-------------------- | :-------------------------------------------------------------------------------------------------------------- |
| `task:background` | Ctrl+B, Ctrl+X Ctrl+B | Background current task. The Ctrl+X Ctrl+B chord requires v2.1.169 or later and avoids the tmux prefix conflict |
### Theme actions
Actions available in the `ThemePicker` context:
| Action | Default | Description |
| :------------------------------- | :------ | :------------------------- |
| `theme:toggleSyntaxHighlighting` | Ctrl+T | Toggle syntax highlighting |
### Help actions
Actions available in the `Help` context:
| Action | Default | Description |
| :------------- | :------ | :-------------- |
| `help:dismiss` | Escape | Close help menu |
### Tabs actions
Actions available in the `Tabs` context:
| Action | Default | Description |
| :-------------- | :-------------- | :----------- |
| `tabs:next` | Tab, Right | Next tab |
| `tabs:previous` | Shift+Tab, Left | Previous tab |
### Attachments actions
Actions available in the `Attachments` context:
| Action | Default | Description |
| :--------------------- | :---------------- | :------------------------- |
| `attachments:next` | Right | Next attachment |
| `attachments:previous` | Left | Previous attachment |
| `attachments:remove` | Backspace, Delete | Remove selected attachment |
| `attachments:exit` | Down, Escape | Exit attachment navigation |
### Footer actions
Actions available in the `Footer` context:
| Action | Default | Description |
| :---------------------- | :---------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `footer:next` | Right | Next footer item |
| `footer:previous` | Left | Previous footer item |
| `footer:up` | Up | Navigate up in footer (deselects at top) |
| `footer:down` | Down | Navigate down in footer |
| `footer:openSelected` | Enter | Open selected footer item |
| `footer:clearSelection` | Escape | Clear footer selection |
| `footer:dismiss` | Backspace, Delete | Dismiss the selected [artifact](/docs/en/artifacts) link from the footer; the published artifact itself is unaffected. On other footer rows, these keys have no effect. Requires v2.1.217 or later |
### Message selector actions
Actions available in the `MessageSelector` context:
| Action | Default | Description |
| :----------------------- | :---------------------------------------- | :---------------- |
| `messageSelector:up` | Up, K, Ctrl+P | Move up in list |
| `messageSelector:down` | Down, J, Ctrl+N | Move down in list |
| `messageSelector:top` | Ctrl+Up, Shift+Up, Meta+Up, Shift+K | Jump to top |
| `messageSelector:bottom` | Ctrl+Down, Shift+Down, Meta+Down, Shift+J | Jump to bottom |
| `messageSelector:select` | Enter | Select message |
### Diff actions
Actions available in the `DiffDialog` context:
| Action | Default | Description |
| :-------------------- | :-------- | :-------------------------------------------------------------------------------------------------------------------------------------------------- |
| `diff:dismiss` | Escape | Close diff viewer; from the detail view, returns to the file list instead |
| `diff:previousSource` | Left | Previous diff source |
| `diff:nextSource` | Right | Next diff source |
| `diff:previousFile` | Up, K | Previous file in the file list; scroll up one line in the detail view |
| `diff:nextFile` | Down, J | Next file in the file list; scroll down one line in the detail view |
| `diff:viewDetails` | Enter | View diff details |
| `diff:back` | (unbound) | Go back in diff viewer. Escape performs the back action via `diff:dismiss`. The previous default of Left in the detail view was removed in v2.1.203 |
The diff detail view also binds pager-style keys to the standard [scroll actions](#scroll-actions). These bindings are part of the `DiffDialog` context and apply only in the detail view; the `Scroll` context defaults listed under [Scroll actions](#scroll-actions) are unchanged.
| Action | Default | Description |
| :-------------------- | :------------- | :-------------------------- |
| `scroll:pageUp` | PageUp | Scroll up half a viewport |
| `scroll:pageDown` | PageDown | Scroll down half a viewport |
| `scroll:fullPageUp` | Shift+Space, B | Scroll up a full viewport |
| `scroll:fullPageDown` | Space | Scroll down a full viewport |
| `scroll:top` | G, Home | Jump to the top |
| `scroll:bottom` | Shift+G, End | Jump to the bottom |
### Model picker actions
Actions available in the `ModelPicker` context:
| Action | Default | Description |
| :---------------------------- | :------ | :------------------------------------------- |
| `modelPicker:decreaseEffort` | Left | Decrease effort level |
| `modelPicker:increaseEffort` | Right | Increase effort level |
| `modelPicker:thisSessionOnly` | s | Apply highlighted model to this session only |
### Select actions
Actions available in the `Select` context:
| Action | Default | Description |
| :---------------- | :-------------- | :--------------- |
| `select:next` | Down, J, Ctrl+N | Next option |
| `select:previous` | Up, K, Ctrl+P | Previous option |
| `select:accept` | Enter | Accept selection |
| `select:cancel` | Escape | Cancel selection |
### Plugin actions
Actions available in the `Plugin` context:
| Action | Default | Description |
| :---------------- | :------ | :------------------------------------------------------------------------- |
| `plugin:toggle` | Space | Toggle plugin selection |
| `plugin:install` | I | Install selected plugins |
| `plugin:favorite` | F | Favorite the selected plugin so it sorts near the top of the Installed tab |
### Settings actions
Cut at 300 lines. The page has the rest.
large-codebases First recorded · 481 lines, first recorded
# Set up Claude Code in a monorepo or large codebase ## What this guide covers ### Settings on this page ### The example monorepo ## Choose where to start Claude ## Layer CLAUDE.md files by directory ### Choose between per-directory CLAUDE.md and path-scoped rules ### Exclude irrelevant CLAUDE.md files ## Reduce what Claude reads ### Block reads of generated and vendored code ### Reduce file reads with code intelligence ## Scope worktrees and file access ### Check out only the directories you need ### Grant access across packages or repositories ## Add per-directory skills ### Keep skills discoverable ## Centralize conventions when layering stops scaling ### Recommend the right plugin at session start ## Put it together ## Scope and plan changes that span packages ## Next steps
The first capture of this source. The page was already there, and this is what it said.
# Set up Claude Code in a monorepo or large codebase
> Configure Claude Code for monorepos and large single-tree codebases with nested CLAUDE.md files, sparse worktrees, code intelligence, and per-package skills so Claude stays focused on the code you're working in.
A large codebase can be one repository with millions of lines or a monorepo with many packages. Claude Code works at any size, but as the codebase grows, the defaults tuned for smaller projects can fill the context window with instructions and file reads unrelated to the task, costing tokens and degrading Claude's performance.
This guide shows individual developers and engineering teams how to scope Claude to the part of the codebase a task touches. Each section notes whether a setting is personal to your machine or committed to the repository.
## What this guide covers
The [table below](#settings-on-this-page) lists each setting and what it accomplishes. The [file tree after it](#the-example-monorepo) is the example monorepo every code sample on this page refers to.
### Settings on this page
Each setting below is independent. They layer rather than replace each other, so apply whichever fit your repository. [Choose where to start Claude](#choose-where-to-start-claude) determines where your settings files live, so read it first. [Put it together](#put-it-together) shows all of them combined.
| I want to | Use |
| :-------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------- |
| Load only the conventions for the code you touch, instead of one root file covering every subsystem | Per-directory [CLAUDE.md files](#layer-claude-md-files-by-directory) |
| Exclude CLAUDE.md files for packages you never work in | [`claudeMdExcludes`](#exclude-irrelevant-claude-md-files) |
| Block Claude from opening build output, generated code, and vendored dependencies | [`Read` deny rules](#block-reads-of-generated-and-vendored-code) in `permissions.deny` |
| Find a symbol's definition or callers through the language server instead of scanning files | A [code intelligence plugin](#reduce-file-reads-with-code-intelligence) |
| Check out only the directories a task needs when Claude creates a worktree | [`worktree.sparsePaths`](#check-out-only-the-directories-you-need) |
| Read and edit a sibling package or another repository from the same session | [`--add-dir`](#grant-access-across-packages-or-repositories) or `additionalDirectories` |
| Give Claude procedures specific to one area that load only when relevant | Per-directory [skills](#add-per-directory-skills) |
| Replace many per-directory CLAUDE.md files with one set of conventions everyone installs | A [plugin](#centralize-conventions-when-layering-stops-scaling) in an internal marketplace |
<Tip>
For workflow techniques that keep context small in any repository, such as [running exploration in a subagent](/docs/en/best-practices#use-subagents-for-investigation) so file reads stay out of the main conversation, see [Best practices for Claude Code](/docs/en/best-practices). To roll out a baseline configuration to every developer in your organization, see [Set up Claude Code for your organization](/docs/en/admin-setup).
</Tip>
### The example monorepo
The examples throughout this page refer to a monorepo with three packages. The same patterns work in a large single-tree codebase: where an example uses `packages/api/`, substitute your own subsystem directory such as `src/backend/` or `lib/core/`.
```text theme={null}
monorepo/
CLAUDE.md # root instructions
packages/
api/
CLAUDE.md # API-specific instructions
.claude/skills/
src/
web/
CLAUDE.md # frontend-specific instructions
.claude/skills/
src/
shared/
CLAUDE.md # shared library instructions
src/
```
## Choose where to start Claude
Where you launch `claude` determines which files Claude can read and edit without an additional permission grant, which CLAUDE.md files load into context at startup, and which project settings apply.
| Start from | File access | CLAUDE.md loaded at launch | Use when |
| :-------------- | :-------------------------------------- | :------------------------------------------------------------------- | :----------------------------------------- |
| Repository root | Every file | Root only; subdirectory files load on demand when Claude reads there | Tasks span multiple packages or subsystems |
| A subdirectory | That subtree only, until you grant more | That directory's plus every ancestor's | Work is scoped to one package or subsystem |
Project settings in `.claude/settings.json` load only from your starting directory and are not inherited from parent directories the way CLAUDE.md files are: a `.claude/settings.json` at the repository root applies only when you start from the root.
Each section below states whether its settings file belongs at the repository root or in the subdirectory you start from, and whether it is committed or kept local.
## Layer CLAUDE.md files by directory
In a large codebase, a single CLAUDE.md at the repository root tends to either grow to cover every subsystem's conventions, costing context on instructions unrelated to the current task, or stay too generic to be useful. Splitting instructions across per-directory files means Claude loads repository-wide rules plus only the conventions for the code you're working in.
Claude Code loads every [CLAUDE.md](/docs/en/memory) file from your working directory and every parent directory at launch, then loads each subdirectory's file on demand when it reads files there. A root file sets repository-wide rules and each subdirectory adds its own.
A common split is two levels:
* **Root `CLAUDE.md`**: instructions that apply everywhere, such as coding standards, commit conventions, and repository layout
* **Per-subdirectory `CLAUDE.md`**: conventions specific to that area's stack. In a monorepo that's one per package. In a large single tree it's one per subsystem such as `src/db/` or `src/api/`
Commit these files to the repository so teammates inherit them. Each directory's owner typically maintains its file.
The root `CLAUDE.md` orients Claude to the repository structure:
```markdown CLAUDE.md theme={null}
This is a monorepo with three packages under packages/:
- packages/api: Node.js REST API with Express, TypeScript, and PostgreSQL
- packages/web: React frontend with Vite, TypeScript, and TailwindCSS
- packages/shared: shared TypeScript utilities used by both api and web
Run commands from the package directory, not the monorepo root.
Each package has its own tsconfig.json, package.json, and test suite.
```
Each subdirectory's `CLAUDE.md`, here `packages/api/CLAUDE.md`, adds context specific to that area's stack:
```markdown packages/api/CLAUDE.md theme={null}
This package is the REST API server.
- Run tests: `npm test` (uses Vitest)
- Run dev server: `npm run dev` (port 3001)
- Database migrations: `npm run migrate`
- Environment variables: copy `.env.example` to `.env`
API routes are in src/routes/. Each route file exports an Express router.
Database queries use Knex in src/db/. Never write raw SQL strings in route handlers.
```
When you start Claude from `packages/api/`, it loads both `packages/api/CLAUDE.md` and the root `CLAUDE.md`. Claude sees the local instructions alongside the repository-wide rules, with no instructions from `packages/web/` in context. The same holds for any subdirectory in a non-monorepo tree. To confirm which files loaded, run `/context` and check the list under **Memory files**.
A few ways to keep the files current as the codebase and models change:
* **Review in pull requests**: treat CLAUDE.md edits like any other documentation change so conventions track the code
* **Revisit after major model releases**: instructions that worked around an older model's limitation may become overhead once a newer model handles the case on its own. For example, a rule that forces single-file refactors can be deleted once the limitation is gone
* **Add a Stop hook that proposes updates**: a [`Stop` hook](/docs/en/hooks#stop) receives the path to the session transcript when Claude finishes responding, so a script can review the session and propose CLAUDE.md updates while the gap it exposed is fresh
For more on how CLAUDE.md files load and interact, see [Memory and project instructions](/docs/en/memory).
### Choose between per-directory CLAUDE.md and path-scoped rules
Per-directory `CLAUDE.md` files and [path-scoped rules](/docs/en/memory#path-specific-rules) under `.claude/rules/` both let you target instructions to part of the tree. They differ in where the file lives and when it loads.
| Approach | File location | Loads when | Use when |
| :----------------------------------- | :--------------------------------------- | :-------------------------------------------------------------------------------------- | :---------------------------------------------------------------------------------------- |
| Per-directory `CLAUDE.md` | Inside the directory, alongside its code | At launch when started from that directory, or on demand when Claude reads a file there | Directory owners maintain their own conventions; instructions are versioned with the code |
| Path-scoped rule in `.claude/rules/` | Central `.claude/` at the repo root | When Claude works with a file matching the rule's `paths:` glob | You want all conventions in one place, or the same rule applies to many scattered paths |
For a comparison that also covers skills, see [Compare similar features](/docs/en/features-overview#compare-similar-features).
### Exclude irrelevant CLAUDE.md files
When you start Claude from the repository root, each subdirectory's CLAUDE.md loads as soon as Claude reads a file in that directory. The `claudeMdExcludes` setting skips specific files by path or glob pattern so they never load.
Use this for directories you never work in, such as other teams' packages, legacy code, or vendored subtrees. The exclusion list is static, not a per-task switch. To focus on one package today and another tomorrow, [start Claude from that package's directory](#choose-where-to-start-claude) instead of editing exclusions.
If you only want these exclusions for yourself, put the setting in `.claude/settings.local.json`. Claude Code adds that file to your global gitignore when it saves a setting there. Since you are creating it by hand here, add it to your gitignore yourself. Patterns use glob syntax matched against absolute file paths, so start relative-style patterns with `**/` to match anywhere in the tree. The example below excludes a package owned by another team:
```json .claude/settings.local.json theme={null}
{
"claudeMdExcludes": [
"**/packages/web/**"
]
}
```
This skips every CLAUDE.md and rules file under that package. The root CLAUDE.md and the packages you do work in still load normally.
These patterns cover other common cases:
* `"**/packages/*/CLAUDE.md"`: excludes every package's CLAUDE.md while keeping the root
* `"**/packages/legacy-*/**"`: excludes every package whose name matches the glob, including rules
* `"/home/user/monorepo/legacy/CLAUDE.md"`: excludes one specific file by absolute path
Managed policy CLAUDE.md files cannot be excluded, so organization-wide instructions always apply. You can set `claudeMdExcludes` at any [settings scope](/docs/en/settings#configuration-scopes): user, project, local, or managed. Arrays merge across scopes, so a team can set project-level defaults while individuals add local overrides.
For the full exclusion documentation, see [Exclude specific CLAUDE.md files](/docs/en/memory#exclude-specific-claude-md-files).
## Reduce what Claude reads
Instructions are only part of what ends up in Claude's context. File reads are another cost that grows with the codebase. The settings below block reads of irrelevant paths and replace exhaustive file scans with language-server lookups.
### Block reads of generated and vendored code
Claude's content searches respect `.gitignore` by default, so paths already listed there, such as `node_modules/`, `dist/`, and `build/`, stay out of search results without additional configuration.
For paths that are checked in, such as a vendored SDK or committed generated code, add `Read` deny rules in `permissions.deny` to block Claude from opening those files even when a search lists them.
The deny rules can cover everyone working in the repository, only you, or every session on the machine, depending on which settings file you put them in:
* **Everyone working in the repository**: commit the rules to `.claude/settings.json`. Like other project settings on this page, that file loads only from your starting directory, so place it at the repository root if you start Claude there, or in each package's `.claude/` if you start from subdirectories.
* **Yourself only**: use `.claude/settings.local.json` at the repository root, which loads in every CLI session inside the repository regardless of starting directory. Relative patterns like the example's `Read(./vendor/**)` still [anchor at the directory you start Claude Code from](/docs/en/permissions#read-and-edit), so if you start sessions from subdirectories, write the rules in this file as `//`-absolute paths, such as `Read(//absolute/path/to/repo/vendor/**)`. Before v2.1.211, `.claude/settings.local.json` also loaded only from the starting directory.
* **Everyone, enforced in every session**: set the rules in [managed settings](/docs/en/settings#settings-files), which user and project settings cannot override.
The example below blocks build artifacts and a vendored SDK:
```json .claude/settings.json theme={null}
{
"permissions": {
"deny": [
"Read(./**/dist/**)",
"Read(./**/build/**)",
"Read(./**/*.generated.*)",
"Read(./vendor/**)"
]
}
}
```
Deny rules cover Claude's built-in file tools and recognized Bash file commands, including `cat`, `head`, `grep`, and `find`, when a denied path is passed as an argument. They do not filter denied paths out of a recursive search's output, and they do not cover arbitrary subprocesses that open files themselves. For the full pattern syntax, see [Read and Edit permission rules](/docs/en/permissions#read-and-edit).
### Reduce file reads with code intelligence
In a large codebase, finding where a symbol is defined or used can cost many file reads and grep calls. [Code intelligence plugins](/docs/en/discover-plugins#code-intelligence) connect Claude to a language server so it can jump to definitions, find references, and surface type errors directly instead of scanning the tree.
The official marketplace has plugins for TypeScript, Python, Go, Rust, and other common languages. Run the command below inside a Claude Code session to install the TypeScript plugin:
```shell theme={null}
/plugin install typescript-lsp@claude-plugins-official
```
If the install fails, match the message Claude Code reports:
* `Marketplace "claude-plugins-official" not found`: add the marketplace with `/plugin marketplace add anthropics/claude-plugins-official`, then retry the install.
* The plugin is [not found in the marketplace](/docs/en/discover-plugins#install-plugins): check the plugin name.
To enable a plugin for everyone in the repository rather than installing it yourself, add it to the [`enabledPlugins` project setting](/docs/en/settings#plugin-settings).
Code intelligence plugins require the language's language server binary on each developer's machine. See [which binary each language requires](/docs/en/discover-plugins#code-intelligence). Installing from the official marketplace requires network access to GitHub, where the marketplace is hosted. On a restricted network, [add the marketplace from an internal Git host or local path](/docs/en/discover-plugins#add-from-other-git-hosts) instead.
This pairs well with `claudeMdExcludes` and the `Read` deny rules above. Those keep irrelevant content out of context, and code intelligence keeps Claude from reading through what remains to locate a definition.
## Scope worktrees and file access
These settings control what's on disk in worktrees and which directories Claude can read and write beyond your starting point.
### Check out only the directories you need
The `--worktree` flag starts a session in a new git worktree so changes stay isolated from your main checkout. By default it checks out the entire repository. In a large repository, the `worktree.sparsePaths` setting uses git sparse-checkout to write only the listed directories plus root-level files to disk, so worktrees start faster and use less space.
If everyone working in this directory needs the same paths, commit the setting to `.claude/settings.json`. To add paths for yourself, use `.claude/settings.local.json`: the lists merge across scopes, so a local file can add paths to the committed list but not remove them.
The JSON examples on this page show one setting at a time. If your `.claude/settings.json` already contains other keys, such as the `permissions.deny` rules above, add the `worktree` key alongside them rather than replacing the file. [Put it together](#put-it-together) shows the combined result.
The example below shows the committed file:
```json .claude/settings.json theme={null}
{
"worktree": {
"sparsePaths": [
".claude",
"packages/api",
"packages/shared"
]
}
}
```
When Claude creates a worktree, it checks out only `.claude/`, `packages/api/`, and `packages/shared/` instead of the full tree. Paths in `sparsePaths` are relative to the repository root, regardless of which subdirectory you start Claude from. Any directory paths work here, not only package roots.
This is particularly useful for [subagent worktree isolation](/docs/en/worktrees#isolate-subagents-with-worktrees). Subagents are parallel Claude instances spawned for subtasks, and each one that runs in a worktree gets a lightweight checkout instead of the full tree. All worktrees in a session share the same `sparsePaths`, so if one subagent needs `packages/api/` and another needs `packages/web/`, list both.
List directories in `sparsePaths`, not individual files. Root-level files like `package.json`, `tsconfig.base.json`, and lock files are always checked out alongside the directories you list. Root-level directories are not, so include `.claude` in the list if you want the repository root's `.claude/settings.json`, `.claude/rules/`, or `.claude/skills/` available inside the worktree.
Sparse checkout requires git to enable `extensions.worktreeConfig` in the repository's shared `.git/config` while a sparse worktree exists. Claude Code removes that entry after the last worktree is removed, but only if Claude Code added it. It never removes a value you set yourself. Before v2.1.207, the entry remained after the last worktree was removed, and go-git-based tools such as `tea` failed to open the repository until you ran `git config --unset extensions.worktreeConfig`.
To avoid duplicating large directories like `node_modules` across worktrees, pair `sparsePaths` with `symlinkDirectories` in the same `.claude/settings.json`:
```json .claude/settings.json theme={null}
{
"worktree": {
"sparsePaths": [
".claude",
"packages/api",
"packages/shared"
],
"symlinkDirectories": [
"node_modules"
]
}
}
```
This creates a symlink from each worktree's `node_modules/` back to the main repository's copy rather than duplicating it on disk.
<Note>
The `sparsePaths` and `symlinkDirectories` settings are read from your starting directory before the worktree is created. After creation, the session's working directory is the worktree root, not the subdirectory you launched from. Project settings inside the worktree therefore load from the worktree root's `.claude/settings.json`, the checked-out copy of the repository root's file. Put any other settings you need inside worktrees, such as permission rules or hooks, in the repository root's `.claude/settings.json`.
</Note>
For the full worktree settings reference, see [Worktree settings](/docs/en/settings#worktree-settings).
### Grant access across packages or repositories
This section applies when you start Claude from a subdirectory, or when a task spans multiple checkouts. If you start from the repository root in a single large tree, Claude already has access to every file and you can skip this.
When you start Claude from `packages/api/`, it can read and write files within that directory. If a task requires changes across packages, such as updating a shared type that both `api` and `web` import, you need to grant access to the sibling directory. The same mechanism grants access to a separately-checked-out repository.
The `additionalDirectories` setting in `.claude/settings.json` gives Claude access to directories outside the working directory. The example below grants access to two sibling packages:
```json packages/api/.claude/settings.json theme={null}
{
"permissions": {
"additionalDirectories": [
"../shared",
"../web"
]
}
}
```
Relative paths resolve against the directory you start Claude from. With this configuration, Claude can read and edit files in `packages/shared/` and `packages/web/` while working from `packages/api/`.
You can also grant access at runtime without editing settings by passing `--add-dir` when you start Claude:
```bash theme={null}
claude --add-dir ../shared
```
However you add a directory, Claude can read and edit files in it. Whether the directory's CLAUDE.md, `.claude/rules/` files, and skills also load depends on how you added it:
| Added with | Loads CLAUDE.md and rules | Loads skills |
| :------------------------------------- | :--------------------------------------- | :----------- |
| `additionalDirectories` setting | Never | Never |
| `--add-dir` flag or `/add-dir` command | Only with the environment variable below | Yes |
Cut at 300 lines. The page has the rest.
legal-and-compliance First recorded · 53 lines, first recorded
# Legal and compliance ## Legal agreements ### License ### Commercial agreements ## Compliance ### Healthcare compliance (BAA) ## Usage policy ### Acceptable use ### Authentication and credential use ## Security and trust ### Trust and safety ### Security vulnerability reporting
The first capture of this source. The page was already there, and this is what it said.
# Legal and compliance > Legal agreements, compliance certifications, and security information for Claude Code. ## Legal agreements ### License Your use of Claude Code is subject to: * [Commercial Terms](https://www.anthropic.com/legal/commercial-terms) - for Team, Enterprise, and Claude API users * [Consumer Terms of Service](https://www.anthropic.com/legal/consumer-terms) - for Free, Pro, and Max users ### Commercial agreements Whether you're using the Claude API directly (1P) or accessing it through Amazon Bedrock or Google Cloud's Agent Platform (3P), your existing commercial agreement will apply to Claude Code usage, unless we've mutually agreed otherwise. ## Compliance ### Healthcare compliance (BAA) If a customer has a Business Associate Agreement (BAA) with us, and wants to use Claude Code, the BAA will automatically extend to cover Claude Code if the customer has executed a BAA and has [Zero Data Retention (ZDR)](/docs/en/zero-data-retention) activated. The BAA will be applicable to that customer's API traffic flowing through Claude Code. ZDR is enabled on a per-organization basis, so each organization must have ZDR enabled separately to be covered under the BAA. ## Usage policy ### Acceptable use Claude Code usage is subject to the [Anthropic Usage Policy](https://www.anthropic.com/legal/aup). Advertised usage limits for Pro and Max plans assume ordinary, individual usage of Claude Code and the Agent SDK. ### Authentication and credential use Claude Code authenticates with Anthropic's servers using OAuth tokens or API keys. These authentication methods serve different purposes: * **OAuth authentication** is intended exclusively for purchasers of Claude Free, Pro, Max, Team, and Enterprise subscription plans and is designed to support ordinary use of Claude Code and other native Anthropic applications. For the sign-in steps, see [Logging in to your Claude account](https://support.claude.com/en/articles/13189465-logging-in-to-your-claude-account); for how Claude Code performs OAuth authentication, see [Authentication](/docs/en/authentication). * **Developers** building products or services that interact with Claude's capabilities, including those using the [Agent SDK](/docs/en/agent-sdk/overview), should use API key authentication through [Claude Console](https://platform.claude.com/) or a supported cloud provider. Anthropic does not permit third-party developers to offer Claude.ai login or to route requests through Free, Pro, or Max plan credentials on behalf of their users. Anthropic reserves the right to take measures to enforce these restrictions and may do so without prior notice. For questions about permitted authentication methods for your use case, please [contact sales](https://www.anthropic.com/contact-sales?utm_source=claude_code\&utm_medium=docs\&utm_content=legal_compliance_contact_sales). ## Security and trust ### Trust and safety You can find more information in the [Anthropic Trust Center](https://trust.anthropic.com) and [Transparency Hub](https://www.anthropic.com/transparency). ### Security vulnerability reporting Anthropic manages our security program through HackerOne. [Use this form to report vulnerabilities](https://hackerone.com/4f1f16ba-10d3-4d09-9ecc-c721aad90f24/embedded_submissions/new). *** © Anthropic PBC. All rights reserved. Use is subject to applicable Anthropic Terms of Service.
llm-gateway First recorded · 52 lines, first recorded
# Other LLM gateways ## What a gateway provides ## Roll out a gateway ## Subscriptions and gateways ## Related pages
The first capture of this source. The page was already there, and this is what it said.
# Other LLM gateways > Route Claude Code through an LLM gateway your organization already runs. Covers connecting Claude Code to a gateway, rolling one out for your organization, and what Claude Code sends to a gateway. This section covers using a gateway product your organization already runs, rather than [Claude apps gateway](/docs/en/claude-apps-gateway). For what a gateway is, how it sits between Claude Code and your provider, and how to choose between Claude apps gateway and another product, see the [gateway overview](/docs/en/gateways). <Note> * If you're a developer connecting to an existing gateway: [connect Claude Code to your gateway](/docs/en/llm-gateway-connect) * If you're an admin rolling out a gateway for your organization: [deploy and distribute a gateway](/docs/en/llm-gateway-rollout) * If you're configuring a gateway product: the [gateway protocol reference](/docs/en/llm-gateway-protocol) </Note> Any gateway that exposes a [supported API format](/docs/en/llm-gateway-protocol#api-formats) works. Anthropic doesn't endorse, maintain, or audit third-party gateway products, and doesn't support routing Claude Code to non-Claude models through any gateway. Deploy the gateway following its own documentation, then complete the Claude Code side with the [rollout steps below](#roll-out-a-gateway). ## What a gateway provides A gateway gives your organization one place to manage: * **Credentials**: the provider key stays server-side; developers hold gateway credentials instead * **Usage tracking**: attribute usage by developer or team, regardless of which provider serves the request * **Cost controls**: enforce budgets and rate limits in one place * **Audit logging**: log every model request for compliance * **Provider switching**: change the provider in gateway configuration, without touching developer machines All of these except provider switching apply whether the upstream is Anthropic's API or a [cloud provider](/docs/en/third-party-integrations). Provider switching without reconfiguring developer machines also depends on the gateway exposing a single [Anthropic-format endpoint](/docs/en/llm-gateway-protocol#api-formats) regardless of upstream; a gateway that exposes a provider's own format ties the client configuration to that provider. The tradeoff is that the gateway becomes infrastructure your organization operates. Claude Code adds capabilities with each release, and a gateway that doesn't forward them breaks the corresponding features, so the gateway product needs to be kept updated as Claude Code evolves. The [gateway protocol reference](/docs/en/llm-gateway-protocol) covers what to forward. ## Roll out a gateway When you're ready to roll out an LLM gateway to your organization, the sequence is the same whichever gateway product you choose: 1. Deploy the gateway and give it your provider credential, so it can authenticate the requests it forwards. 2. Issue each developer a gateway credential, so usage is attributed to the developer and offboarding revokes one credential. 3. Distribute the configuration through a [managed settings file](/docs/en/settings#settings-files) and your secrets tooling, so every machine receives the base URL and a credential. When both are distributed, developers don't configure anything. If you don't have settings distribution in place, developers follow the [connect page](/docs/en/llm-gateway-connect) to set the variables themselves. 4. Have each developer [check for the configuration in Claude Code](/docs/en/llm-gateway-connect#check-for-an-existing-configuration), so distribution problems surface before they depend on the gateway. [Roll out an LLM gateway for your organization](/docs/en/llm-gateway-rollout) walks each step and shows the configuration files to distribute at each one. The gateway is one part of organization setup; for policy enforcement, usage visibility, and data handling decisions, see [Set up Claude Code for your organization](/docs/en/admin-setup). ## Subscriptions and gateways While a [gateway credential variable](/docs/en/llm-gateway-connect#set-the-credential-variable) or `apiKeyHelper` is active, a developer's claude.ai subscription isn't used: the credential replaces the subscription login for that session, and the subscription's usage limits don't apply. That traffic is billed per token to whoever owns the credential the gateway forwards, such as your organization's Anthropic Console account, or your Amazon Bedrock, Google Cloud's Agent Platform, or Microsoft Foundry account when the gateway routes there. [`ANTHROPIC_BASE_URL`](/docs/en/llm-gateway-connect#set-the-base-url-and-credential) is the variable that points Claude Code at the gateway. Setting only that variable, without a gateway credential, doesn't replace the subscription. Requests still route through the gateway, but a saved claude.ai login remains the active credential, so its usage limits and billing apply. Gateways that pass this traffic on to Anthropic must forward the OAuth capability in `anthropic-beta`; see the [request headers reference](/docs/en/llm-gateway-protocol#request-headers). ## Related pages * [Gateway overview](/docs/en/gateways): how a gateway works and how to choose between Claude apps gateway and another product * [Claude apps gateway](/docs/en/claude-apps-gateway): Anthropic's self-hosted gateway with SSO sign-in and OTLP telemetry * [Connect Claude Code to an LLM gateway](/docs/en/llm-gateway-connect): set the base URL and credential on your own machine, with per-surface configuration and a troubleshooting table * [Roll out an LLM gateway for your organization](/docs/en/llm-gateway-rollout): the admin checklist for deploying a gateway, issuing developer credentials, and distributing managed settings * [Gateway protocol reference](/docs/en/llm-gateway-protocol): what Claude Code sends to a gateway, for operators configuring one, covering endpoints, headers to forward, and feature pass-through
llm-gateway-connect First recorded · 526 lines, first recorded
# Connect Claude Code to an LLM gateway ## Check for an existing configuration ## Configure Claude Code yourself ### Set the credential variable ### Set the base URL and credential #### Set as shell environment variables #### Set in a settings file ### Verify the connection #### Confirm in Claude Code ### How the credential variable maps to a header ### Conflicts with an existing login ## Configure each surface ### VS Code extension ### Desktop app ### GitHub Actions ### Agent SDK ### Slack, web, and Remote Control ## Additional configuration ### Send additional headers ### Add gateway models to the model picker ### Rotate credentials with apiKeyHelper ### Turn off traffic outside the gateway path ### Route to a cloud provider through a gateway #### Amazon Bedrock #### Google Cloud's Agent Platform #### Microsoft Foundry #### Claude Platform on AWS #### Confirm the provider route ## Troubleshoot gateway errors ## Related resources
The first capture of this source. The page was already there, and this is what it said.
# Connect Claude Code to an LLM gateway
> Point Claude Code at your organization's LLM gateway. Check whether your admin already configured it, or set the base URL and credential yourself, then verify the connection and fix gateway errors.
An [LLM gateway](/docs/en/llm-gateway) is a proxy your organization runs between Claude Code and the model provider. When your organization uses one, Claude Code authenticates to the gateway with a credential your organization issues instead of your personal claude.ai login.
This page is for developers running Claude Code through a gateway their organization operates. It covers two paths: [checking whether your administrator already configured it for you](#check-for-an-existing-configuration), and [configuring it yourself](#configure-claude-code-yourself) when they haven't.
<Note>
* To deploy a gateway for your organization, see [Roll out an LLM gateway](/docs/en/llm-gateway-rollout)
* For what Claude Code sends to a gateway, see the [gateway protocol reference](/docs/en/llm-gateway-protocol)
</Note>
## Check for an existing configuration
Administrators can distribute the gateway address and credential through [managed settings](/docs/en/settings#settings-files), device management, or an [`apiKeyHelper`](#rotate-credentials-with-apikeyhelper), so Claude Code picks them up at startup with nothing for you to set. To check whether your organization already did this:
<Steps>
<Step title="Start Claude Code">
Run `claude`. If it opens to the login screen instead of a session, no gateway credential was distributed; [configure it yourself](#configure-claude-code-yourself) below.
</Step>
<Step title="Check the Status tab">
If Claude Code started a session without showing the login screen, run `/status`, which opens on the **Status** tab, and check two lines:
* `Anthropic base URL`: this line only appears when a gateway address is set. If it isn't there, Claude Code isn't pointed at the gateway; [configure it yourself](#configure-claude-code-yourself) below.
* `Auth token` or `API key`: a line naming `ANTHROPIC_AUTH_TOKEN`, `ANTHROPIC_API_KEY`, or an `apiKeyHelper` confirms a gateway credential is active. A `Login method` line naming a claude.ai account instead means the credential wasn't distributed; [set it yourself](#set-the-credential-variable).
</Step>
<Step title="Send a test message">
Close the `/status` menu and send any prompt in Claude Code. A normal response from Claude, with no error, confirms the gateway connection works.
</Step>
</Steps>
If both lines in the `/status` menu look right but the message to Claude fails, see the [troubleshooting table](#troubleshoot-gateway-errors).
## Configure Claude Code yourself
To configure Claude Code for the gateway yourself, you need from your gateway team:
* The gateway's base URL
* A credential: a key or token string, or a command that fetches one
* If your gateway team didn't say which kind of credential it is, the [credential variable section](#set-the-credential-variable) below covers what to try
The sections below cover the configuration in order:
* [Set the credential variable](#set-the-credential-variable) and [set the base URL](#set-the-base-url-and-credential): the two variables every gateway connection needs
* [Verify the connection](#verify-the-connection): confirm it works before persisting anything
* [Configure each surface](#configure-each-surface): if you are using a surface besides the Claude Code CLI, such as VS Code, see how to configure it with your gateway credentials
* [Additional configuration](#additional-configuration): variables some gateways need beyond the base URL and credential, such as a custom header, a credential helper, model discovery, a provider-format base URL, or turning off traffic outside the gateway path. Set these only if your administrator named them or your network restricts egress
### Set the credential variable
To authenticate Claude Code to the gateway, set your credential in an environment variable. Which variable depends on what your gateway team told you:
| Set the credential in | Use when |
| :------------------------------------------------------ | :-------------------------------------------------------------- |
| `ANTHROPIC_AUTH_TOKEN` | Your gateway team said "bearer token" or "Authorization header" |
| `ANTHROPIC_API_KEY` | Your gateway team said "API key" or "x-api-key" |
| [`apiKeyHelper`](#rotate-credentials-with-apikeyhelper) | The credential rotates or comes from a vault |
If you weren't told which kind, use `ANTHROPIC_AUTH_TOKEN`; the [verification request](#verify-the-connection) below shows how to tell if you need to switch.
### Set the base URL and credential
Set the gateway's base URL and the credential variable you picked above as environment variables. The examples use `ANTHROPIC_AUTH_TOKEN`; swap it for `ANTHROPIC_API_KEY` if that's [the variable you picked](#set-the-credential-variable). You can set them [in your shell](#set-as-shell-environment-variables), which lasts for one terminal session, or [in a Claude Code settings file](#set-in-a-settings-file), which persists everywhere Claude Code runs.
For your first connection, start with shell exports and run the [verification request](#verify-the-connection) before moving the values to a settings file.
#### Set as shell environment variables
Replace the values with the ones your gateway team gave you:
<Tabs>
<Tab title="Bash or Zsh">
```bash theme={null}
export ANTHROPIC_BASE_URL=https://llm-gateway.example.com
export ANTHROPIC_AUTH_TOKEN=sk-gateway-key
```
</Tab>
<Tab title="PowerShell">
```powershell theme={null}
$env:ANTHROPIC_BASE_URL = "https://llm-gateway.example.com"
$env:ANTHROPIC_AUTH_TOKEN = "sk-gateway-key"
```
</Tab>
</Tabs>
Shell exports apply only to that terminal session and programs started from it. An editor launched from the dock or Start menu won't see them. To make the values persist across new terminals, add the same lines to your shell profile, such as `~/.zshrc`, `~/.bashrc`, or your PowerShell `$PROFILE`.
If you export the gateway only in your shell, it doesn't reliably reach background agents hosted by the [supervisor](/docs/en/agent-view#how-background-sessions-are-hosted); see [how each background session sources its gateway](/docs/en/agent-view#the-supervisor-process). Use a settings file for any gateway that background agents must always route through.
#### Set in a settings file
To make the configuration apply everywhere Claude Code runs, including [background agents](/docs/en/agent-view#how-background-sessions-are-hosted), set the variables in the `env` block of a [settings file](/docs/en/settings) instead of relying on your shell. Settings files have different scopes:
* `~/.claude/settings.json` applies to all your projects. On Windows the path is `%USERPROFILE%\.claude\settings.json`
* `.claude/settings.local.json` applies to one project. Claude Code adds it to your global gitignore when it saves a setting there; if you create it by hand or have Claude write it, add it to your gitignore yourself first so you don't accidentally commit your credential
<Warning>
Don't put the credential in a project's `.claude/settings.json`. That file is committed and shared with everyone who clones the repository.
</Warning>
The `env` block looks the same in either file:
```json theme={null}
{
"env": {
"ANTHROPIC_BASE_URL": "https://llm-gateway.example.com",
"ANTHROPIC_AUTH_TOKEN": "sk-gateway-key"
}
}
```
When both a shell export and a settings-file `env` block set the same variable, the settings-file value applies. Run `/status` to see which base URL and credential source Claude Code is using.
### Verify the connection
With the variables exported in your shell, send a one-token request to the gateway directly. This confirms the URL and credential work before you open Claude Code, so a failure points at the gateway rather than your configuration. The commands below read the shell variables, so they need the [shell exports](#set-as-shell-environment-variables) even if you also put the values in a settings file.
<Tabs>
<Tab title="Bash or Zsh">
```bash theme={null}
curl -X POST "$ANTHROPIC_BASE_URL/v1/messages" \
-H "Authorization: Bearer $ANTHROPIC_AUTH_TOKEN" \
-H "anthropic-version: 2023-06-01" \
-H "content-type: application/json" \
-d '{"model": "claude-sonnet-4-6", "max_tokens": 1, "messages": [{"role": "user", "content": "."}]}'
```
</Tab>
<Tab title="PowerShell">
```powershell theme={null}
Invoke-RestMethod -Method Post -Uri "$env:ANTHROPIC_BASE_URL/v1/messages" `
-Headers @{ "Authorization" = "Bearer $env:ANTHROPIC_AUTH_TOKEN"; "anthropic-version" = "2023-06-01" } `
-ContentType "application/json" `
-Body '{"model": "claude-sonnet-4-6", "max_tokens": 1, "messages": [{"role": "user", "content": "."}]}'
```
</Tab>
</Tabs>
If your gateway expects keys in the `x-api-key` header, replace the `Authorization` header with `x-api-key: $ANTHROPIC_API_KEY` in the Bash command, or the `"Authorization"` hashtable entry with `"x-api-key" = "$env:ANTHROPIC_API_KEY"` in the PowerShell command.
A JSON response that starts with `{"id":"msg_` and includes a `"content":[...]` field means the gateway is reachable and the credential works. An error naming an unknown model still proves the URL and credential work, since the gateway authenticated the request before rejecting the model name; you don't need to find a model your gateway serves for this test. A `401` means the credential was rejected: if you guessed the variable, switch to the other one and re-export.
#### Confirm in Claude Code
Start `claude` from the same shell so it inherits the exports, send a message, and run `/status`.
On the **Status** tab, the `Anthropic base URL` line should show your gateway address, which confirms requests are routing there; if the line isn't there, the variable didn't reach the session. An `Auth token` or `API key` line naming the variable you set confirms the gateway credential is active rather than a saved claude.ai login.
If the message fails, or `/status` doesn't show the gateway URL, see the [troubleshooting table](#troubleshoot-gateway-errors) below.
### How the credential variable maps to a header
Each variable sends the credential in a different HTTP header: `ANTHROPIC_AUTH_TOKEN` in `Authorization: Bearer`, `ANTHROPIC_API_KEY` in `x-api-key`, and `apiKeyHelper` in both. A credential in the wrong variable reaches the gateway in a header it doesn't read, and the request fails with `401`. If the verification request returned `401`, switch to the other variable and try again.
### Conflicts with an existing login
A gateway credential variable takes precedence over a saved claude.ai login or Console key. Your claude.ai login stays saved and unused while the variable is set; unset the variable and Claude Code goes back to it. With `ANTHROPIC_AUTH_TOKEN`, the variable takes precedence immediately. With `ANTHROPIC_API_KEY`, you are prompted once in interactive mode to approve the key before it takes over.
Run `/status` to confirm which credential source is active. If startup shows an auth-conflict warning naming two sources, see the first row of the [troubleshooting table](#troubleshoot-gateway-errors) for which one to drop. To clear a saved login so only the gateway credential remains, run `/logout`.
## Configure each surface
The CLI reads the environment variables and settings files above. The other surfaces are the VS Code extension, the desktop app, GitHub Actions, the Agent SDK, and the cloud surfaces such as Slack and the web; the sections below cover whether those settings reach each one.
### VS Code extension
Set the gateway variables for the [VS Code extension](/docs/en/vs-code) in `claudeCode.environmentVariables`, in VS Code's own user settings opened with the **Preferences: Open User Settings (JSON)** command. The extension checks credentials from this setting before launching, so it's the reliable place for the gateway credential; values in `~/.claude/settings.json` reach the spawned process but not the extension's own login check.
```json theme={null}
{
"claudeCode.environmentVariables": [
{ "name": "ANTHROPIC_BASE_URL", "value": "https://llm-gateway.example.com" },
{ "name": "ANTHROPIC_AUTH_TOKEN", "value": "sk-gateway-key" }
]
}
```
### Desktop app
The desktop app reads gateway routing from its [third-party inference configuration](https://claude.com/docs/third-party/claude-desktop/gateway), not from `ANTHROPIC_BASE_URL` or `settings.json`. That configuration can come from your organization or from a form in the app itself:
* **Distributed by an administrator**: if your organization has [deployed the configuration](/docs/en/llm-gateway-rollout#distribute-through-managed-settings), the desktop app routes through the gateway with no setup on your part
* **Configured locally**: for devices without an administrator-distributed configuration, open Help → Troubleshooting → Enable Developer Mode, which restarts the app with a Developer menu. Then open Developer → Configure Third-Party Inference and enter your gateway base URL. An administrator-distributed configuration takes precedence and makes this form read-only
With the gateway configuration active, the desktop app runs sessions on your local machine only: the environment picker doesn't offer SSH sessions or Anthropic-hosted cloud environments, and [Remote Control](/docs/en/remote-control) is unavailable. To use Claude Code on a remote host through the gateway, run the CLI on that host with [`ANTHROPIC_BASE_URL` and the gateway credential](#set-the-base-url-and-credential) set there.
If the desktop app shows `Gateway was unreachable`, the app couldn't reach the configured base URL at startup; check the URL and network path with the [curl test above](#verify-the-connection).
### GitHub Actions
[Claude Code GitHub Actions](/docs/en/github-actions) reads `ANTHROPIC_BASE_URL` and `ANTHROPIC_CUSTOM_HEADERS` from the workflow's `env` block. Pass the credential as the action's `anthropic_api_key` input; the action sets it as `ANTHROPIC_API_KEY`, so it reaches the gateway in the `x-api-key` header.
For an `x-api-key` gateway, set the base URL in `env` and pass the gateway key as the input:
```yaml theme={null}
env:
ANTHROPIC_BASE_URL: https://llm-gateway.example.com
steps:
- uses: anthropics/claude-code-action@v1
with:
anthropic_api_key: ${{ secrets.GATEWAY_API_KEY }}
```
For a bearer-token gateway, pass the same secret twice: as the `anthropic_api_key` input and as `ANTHROPIC_AUTH_TOKEN` in the workflow `env` block. The action requires `anthropic_api_key`, `CLAUDE_CODE_OAUTH_TOKEN`, or workload identity federation before it launches Claude Code, and it doesn't read `ANTHROPIC_AUTH_TOKEN`, so the input is there only to satisfy that launch check. The env variable is what puts the key in the `Authorization` header the gateway reads; the copy in `x-api-key` is ignored:
```yaml theme={null}
env:
ANTHROPIC_BASE_URL: https://llm-gateway.example.com
ANTHROPIC_AUTH_TOKEN: ${{ secrets.GATEWAY_API_KEY }}
steps:
- uses: anthropics/claude-code-action@v1
with:
anthropic_api_key: ${{ secrets.GATEWAY_API_KEY }}
```
For the action's other authentication options, including `CLAUDE_CODE_OAUTH_TOKEN` and workload identity federation, see [Claude Code GitHub Actions](/docs/en/github-actions) and the action's [README](https://github.com/anthropics/claude-code-action#readme).
### Agent SDK
The [Agent SDK](/docs/en/agent-sdk/overview) has no gateway-specific options; it passes environment variables to the Claude Code process it spawns. Each SDK accepts an `env` option that sets the spawned process's environment, and the TypeScript and Python SDKs treat it differently:
* TypeScript: the spawned process inherits the parent environment by default, but setting `options.env` replaces the environment entirely. Spread `process.env` into it to keep your gateway variables.
* Python: `ClaudeAgentOptions(env=...)` merges on top of the inherited environment, so gateway variables set in the parent process carry through without spreading.
<CodeGroup>
```ts TypeScript theme={null}
const result = query({
prompt: "...",
options: {
env: {
...process.env,
ANTHROPIC_BASE_URL: "https://llm-gateway.example.com",
ANTHROPIC_AUTH_TOKEN: process.env.GATEWAY_KEY,
},
},
})
```
```python Python theme={null}
options = ClaudeAgentOptions(
env={
"ANTHROPIC_BASE_URL": "https://llm-gateway.example.com",
"ANTHROPIC_AUTH_TOKEN": os.environ["GATEWAY_KEY"],
}
)
```
</CodeGroup>
### Slack, web, and Remote Control
[Claude Code in Slack](/docs/en/slack) and [Claude Code on the web](/docs/en/claude-code-on-the-web) are Anthropic-hosted products that always use Anthropic's API; they aren't part of a gateway deployment. Gateway variables set in a cloud session's environment configuration are not applied. If your traffic must stay on the gateway, don't enable these surfaces for those users.
[Remote Control](/docs/en/remote-control) and [voice dictation](/docs/en/voice-dictation) both rely on a claude.ai identity: Remote Control to pair a live session with your account, and voice dictation to reach the claude.ai transcription endpoint. They are unavailable while `ANTHROPIC_API_KEY`, `ANTHROPIC_AUTH_TOKEN`, or an `apiKeyHelper` is active. Remote Control is also disabled while `ANTHROPIC_BASE_URL` points at a non-Anthropic host, so signing in with claude.ai isn't enough on its own.
To restore either feature, log in with claude.ai and unset the gateway variables that feature checks. The Remote Control section of `claude doctor` names the credential variable to unset.
* Voice dictation: unset the gateway credential
* Remote Control: unset the gateway credential and `ANTHROPIC_BASE_URL`
## Additional configuration
These settings cover cases beyond the base URL and credential. Set them only if your administrator's instructions, your network's egress rules, or the [troubleshooting table](#troubleshoot-gateway-errors) call for one.
### Send additional headers
Some gateways route or tag requests using a custom header in addition to the credential, for example a tenant identifier or a routing key. To send one, set [`ANTHROPIC_CUSTOM_HEADERS`](/docs/en/env-vars) with one `Name: Value` pair per line. The example below adds a routing header named `X-Org-Route`:
<Tabs>
<Tab title="Bash or Zsh">
```bash theme={null}
export ANTHROPIC_CUSTOM_HEADERS="X-Org-Route: prod"
```
</Tab>
<Tab title="PowerShell">
```powershell theme={null}
$env:ANTHROPIC_CUSTOM_HEADERS = "X-Org-Route: prod"
```
</Tab>
</Tabs>
You can also set `ANTHROPIC_CUSTOM_HEADERS` in the `env` block of a settings file. Use `\n` between pairs there, since JSON strings can't span multiple lines:
```json theme={null}
{
"env": {
"ANTHROPIC_CUSTOM_HEADERS": "X-Org-Route: prod\nX-Tenant: example"
}
}
```
### Add gateway models to the model picker
With model discovery enabled, Claude Code queries the gateway for its model list at startup and adds those names to the `/model` picker alongside the built-in entries.
Cut at 300 lines. The page has the rest.
llm-gateway-protocol First recorded · 206 lines, first recorded
# Gateway protocol reference ## API formats ### Foundry and Claude Platform on AWS ### Optional endpoints and startup traffic ### Streaming ### Format mismatch with the upstream ## Request headers ### Forward as open lists ## System prompt attribution block ## Feature pass-through ### Automatic retry and error forwarding ### Disable pre-release capabilities ## Model discovery ### When discovery runs ### Request and response ### Picker entries and caching ## Related resources
The first capture of this source. The page was already there, and this is what it said.
# Gateway protocol reference
> The API contract between Claude Code and an LLM gateway: endpoints, headers and body fields to forward, feature degradation when fields are stripped, attribution headers for cost tracking, and model discovery.
This page documents the requests Claude Code sends to a gateway, including the endpoints it calls, the headers and body fields the gateway must forward, and which features stop working when it doesn't. It is written for operators configuring a gateway product to work with Claude Code.
A running [Claude apps gateway](/docs/en/claude-apps-gateway) serves a machine-readable version of this contract at `GET /protocol`, covering the same forwarding requirements plus the Claude apps gateway-specific endpoints for SSO sign-in, managed-settings delivery, and telemetry. Claude apps gateway runs from the same `claude` binary as the CLI, so the [Claude apps gateway quickstart](/docs/en/claude-apps-gateway#quickstart) is the shortest path to a running instance you can fetch the spec from.
<Note>
* To roll out an existing or third-party gateway for your organization, see [Roll out an LLM gateway](/docs/en/llm-gateway-rollout)
* If you're an individual developer authenticating Claude Code to a gateway with a credential you were given, see [Connect Claude Code to an LLM gateway](/docs/en/llm-gateway-connect)
</Note>
This page covers:
* [API formats](#api-formats) and the endpoints to serve for each
* [Request headers](#request-headers): which must reach the upstream and which your gateway can consume
* The [system prompt attribution block](#system-prompt-attribution-block) and how it interacts with prompt caching
* [Feature pass-through](#feature-pass-through): what breaks when headers or body fields are stripped
* [Model discovery](#model-discovery)
This page uses two terms for what your gateway does with each header and body field:
* **Forward unchanged**: pass it to the upstream byte-for-byte
* **Consume**: the gateway may read it for routing, attribution, or tracing and need not forward it
Anything not marked forward unchanged is yours to consume or ignore.
## API formats
A gateway must expose at least one of the following API formats to Claude Code clients. Which format Claude Code speaks is determined by the client's configuration: the variable in the Selected by column of the table below points Claude Code at your gateway in that format. Google Cloud's Agent Platform is Google Cloud's Claude endpoint, formerly Vertex AI; its variable names keep the `VERTEX` spelling.
| Format | Selected by | Endpoints | Forward unchanged |
| :--------------------------------------- | :------------------------------------------------------------ | :-------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------- |
| Anthropic Messages | `ANTHROPIC_BASE_URL` | `/v1/messages`, `/v1/messages/count_tokens` (optional) | `anthropic-beta` and `anthropic-version` request headers |
| Amazon Bedrock InvokeModel | `ANTHROPIC_BEDROCK_BASE_URL` with `CLAUDE_CODE_USE_BEDROCK=1` | `/model/{model}/invoke`, `/model/{model}/invoke-with-response-stream`, `/model/{model}/count-tokens` (optional) | `anthropic_beta` and `anthropic_version` request body fields |
| Google Cloud's Agent Platform rawPredict | `ANTHROPIC_VERTEX_BASE_URL` with `CLAUDE_CODE_USE_VERTEX=1` | `:rawPredict`, `:streamRawPredict`, `count-tokens:rawPredict` (optional) | `anthropic-beta` and `anthropic-version` request headers, and the `anthropic_version` request body field |
### Foundry and Claude Platform on AWS
Microsoft Foundry and the [Claude Platform on AWS](/docs/en/claude-platform-on-aws) implement the Anthropic Messages format. Claude Code routes to them through their own variables, `ANTHROPIC_FOUNDRY_BASE_URL` and `ANTHROPIC_AWS_BASE_URL`, but a gateway fronting either implements the Anthropic Messages row above. A gateway fronting the Claude Platform on AWS must also forward the `anthropic-workspace-id` header, which [that platform requires on every request](/docs/en/claude-platform-on-aws).
### Optional endpoints and startup traffic
Token-counting endpoints are the only optional ones: when they're absent, Claude Code falls back to counting context usage through the inference endpoint instead. Inference requests post to `/v1/messages?beta=true`, so match on the path, not the full URL. The Google Cloud's Agent Platform method suffixes attach to the publisher model path, as in `/projects/{project}/locations/{location}/publishers/anthropic/models/{model}:streamRawPredict`.
A gateway also sees best-effort startup traffic it can reject without breaking anything. An Anthropic Messages-format gateway receives a `HEAD /api/hello` connection-warming probe, which Claude Code skips when an HTTP proxy or client certificate is configured. An Amazon Bedrock-format gateway receives a `GET /inference-profiles?type=SYSTEM_DEFINED` request and, when the configured model is an inference profile, `GET /inference-profiles/{profile}` lookups.
The [fast mode](/docs/en/fast-mode) availability check never appears in gateway logs: it calls `api.anthropic.com` directly rather than following `ANTHROPIC_BASE_URL`, so on a network that blocks direct egress to `api.anthropic.com`, fast mode can report a connectivity error while inference through the gateway keeps working. The [WebFetch domain safety check](/docs/en/data-usage#webfetch-domain-safety-check) also calls `api.anthropic.com` directly. [Use fast mode behind proxies and LLM gateways](/docs/en/fast-mode#use-fast-mode-behind-proxies-and-llm-gateways) covers the variables that restore it.
### Streaming
Inference responses must stream. Claude Code consumes server-sent events as they arrive, so a gateway that buffers complete responses before relaying them stalls the client.
Forward keep-alive pings as well. On connections through `ANTHROPIC_BASE_URL` or `ANTHROPIC_AWS_BASE_URL`, Claude Code counts every byte your gateway relays, including SSE `ping` events and comment lines, and aborts a stream that goes silent for 300 seconds by default. The upstream's pings are the only traffic during long thinking pauses, so if your gateway strips or buffers them, Claude Code aborts the stream during those pauses; [Automatic retries](/docs/en/errors#automatic-retries) covers what an aborted stream reports based on how far the response had progressed. An upstream that sends no pings at all, such as Amazon Bedrock's binary event-stream, leaves those pauses with nothing to forward. When translating from such an upstream, emit your own `ping` events during silent gaps. Gateways reached through `ANTHROPIC_BEDROCK_BASE_URL`, `ANTHROPIC_VERTEX_BASE_URL`, or `ANTHROPIC_FOUNDRY_BASE_URL` aren't wrapped by this byte-level watchdog, even when they relay the Anthropic Messages format; there, a [5-minute idle timeout](/docs/en/env-vars) aborts a silent stream instead, and on `ANTHROPIC_BEDROCK_BASE_URL` connections you can add the byte watchdog with [`CLAUDE_ENABLE_BYTE_WATCHDOG_BEDROCK`](/docs/en/env-vars).
### Format mismatch with the upstream
Which format the client speaks determines what your gateway receives. The common failure mode is a mismatch between the format the client sends to your gateway and the format the upstream provider behind it accepts.
* When the client speaks the Amazon Bedrock or Google Cloud's Agent Platform format, Claude Code sends only the subset of its full capability set that those providers accept
* When the client speaks the Anthropic Messages format, Claude Code sends the full set, even if your gateway forwards to an Amazon Bedrock or Google Cloud's Agent Platform upstream
Bridging that difference is your gateway's job. [Feature pass-through](#feature-pass-through) describes what breaks when it doesn't.
## Request headers
Claude Code includes these headers on API requests. Header names are case-insensitive on the wire. Forward `anthropic-version` and `anthropic-beta` unchanged, plus `anthropic-workspace-id` when the upstream is the [Claude Platform on AWS](/docs/en/claude-platform-on-aws); the rest the gateway may consume for routing, attribution, and tracing, and need not forward.
| Header | Description |
| :------------------------------ | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `Authorization`, `x-api-key` | The developer's gateway credential, in one or both headers depending on which [credential variable](/docs/en/llm-gateway-connect#set-the-credential-variable) they set |
| `anthropic-version` | API version, currently `2023-06-01`. Amazon Bedrock- and Google Cloud's Agent Platform-format requests also carry the `anthropic_version` body field, whose value is the provider dialect string, not this header's value |
| `anthropic-beta` | Comma-separated capability values for the request. Forward the header verbatim; don't allowlist individual values, because the set changes with Claude Code releases. When the developer authenticates with a claude.ai login, which is possible when `ANTHROPIC_BASE_URL` is set without a gateway credential variable, this header also carries an OAuth capability that the upstream requires, and stripping it fails those requests with `401` |
| `x-claude-code-session-id` | A unique identifier for the current Claude Code session. Use it to aggregate all requests from one session without parsing request bodies |
| `x-claude-code-agent-id` | Identifier of the [subagent](/docs/en/sub-agents) that issued the request, present only on requests from an agent Claude Code spawned inside the session. Use it with the session ID to attribute cost to parallel agents |
| `x-claude-code-parent-agent-id` | Identifier of the agent that spawned the requesting agent, present only for nested agents |
Subagent IDs are generated fresh for each spawn. Teammate agents, the named members of an [agent team](/docs/en/agent-teams), reuse a stable name-based ID across reconnections. In both cases the ID identifies an agent, not a person or a device, so don't treat the agent ID header as a user identifier.
If your developers set `ANTHROPIC_CUSTOM_HEADERS`, those headers appear on requests as well.
### Forward as open lists
Treat the headers and body fields as open lists, not closed ones. Claude Code gains capabilities over releases, and they arrive as new `anthropic-beta` values, new request body fields, and occasionally new `anthropic-*` or `x-claude-code-*` headers.
When forwarding to an Anthropic-format upstream, pass `anthropic-*` request headers and request body fields through unchanged rather than allowlisting the ones you see today. A gateway pinned to an observed list strips the next capability's header or field and breaks it on the release that introduces it.
The exception is a non-Anthropic upstream such as Amazon Bedrock or Google Cloud's Agent Platform, where bridging the schema difference is the gateway's job; see [feature pass-through](#feature-pass-through).
## System prompt attribution block
Claude Code prepends a short attribution block to the system prompt containing the client version and a fingerprint derived from the conversation. The `api.anthropic.com` endpoint strips the block before processing when it arrives unchanged as the first system block, so it doesn't affect first-party prompt caching. Any other upstream receives it as part of the prompt.
The strip is positional, so it only works when the gateway forwards the `system` array unchanged. To keep the block out of the prompt without losing other system content:
* Forward the `system` array exactly as received, keeping the block first: prepending another system block, reordering the array, or converting it to a single string defeats the strip, and the block then reaches the model and the prompt cache key.
* Keep the block in its own array entry: the endpoint treats a merged block that starts with the attribution header as attribution in its entirety and drops everything merged into it, including the rest of the system prompt.
* If your gateway must reshape system content, set [`CLAUDE_CODE_ATTRIBUTION_HEADER=0`](/docs/en/env-vars) so Claude Code omits the block. Anthropic and the cloud providers' Claude endpoints read the block for attribution, so omit it at the client rather than stripping or moving it in the gateway.
Requests that reach the endpoint unmodified are unaffected.
The variable exists for gateway and third-party caching compatibility, not as a privacy control: on a direct connection the full request already goes to the Anthropic API either way. When both of these hold, Claude Code keeps the block on [auto mode](/docs/en/permission-modes#eliminate-prompts-with-auto-mode) classifier requests even when you set the variable to `0`:
* The requests go to `api.anthropic.com`, with `ANTHROPIC_BASE_URL` unset or naming that host and no third-party provider selected.
* The active credential isn't an [Anthropic profile or federation credential](/docs/en/authentication#anthropic-profiles-and-federation-credentials).
Classifier requests skip the rest of Claude Code's system prompt, so on those requests the block is the only marker in the request body that identifies them as Claude Code traffic. When either condition fails, through an LLM gateway, on a third-party provider, or with a profile or federation credential active, setting `0` removes the block from classifier requests too. Before v2.1.229, this exception didn't exist: setting `0` removed the block from those classifier requests, and when the API declined the unidentified requests, auto mode failed on every action it sent to the classifier.
From Claude Code v2.1.181, the block is stable for the lifetime of a conversation when requests route through a custom base URL, so a gateway-side prompt cache keyed on the full request body works without disabling it, and any provider your gateway forwards to receives a stable prompt prefix. Before v2.1.181 the block included a per-request token that changed the start of the system prompt on every request. On those versions, set `CLAUDE_CODE_ATTRIBUTION_HEADER=0` when your gateway does either of these:
* Implements a prompt cache keyed on the request body.
* Forwards requests to a third-party provider such as Amazon Bedrock, Microsoft Foundry, or Google Cloud's Agent Platform, in the Anthropic Messages format or the provider's own, where the changing prefix reduces prompt-cache reuse on that provider.
## Feature pass-through
Claude Code treats an `ANTHROPIC_BASE_URL` gateway as an Anthropic-format endpoint and sends it the beta headers and request body fields it sends to `api.anthropic.com`, except a small set of diagnostics and defaults reserved for direct connections, such as the fine-grained tool streaming default covered below. That set varies by release, so don't depend on its contents.
Capabilities that add body fields pair them with a beta header, and the pair travels together. A gateway that strips the header while passing the body, or forwards an Anthropic-format body to an upstream with a different schema, produces hard `400` errors; only when both halves are absent together does the feature turn off quietly. A gateway that rewrites or redacts request bodies for content inspection breaks the pairing the same way stripping does, so inspect without modifying. The table notes where a feature deviates from the pairing.
Fine-grained tool streaming is one of the direct-connection defaults: it is off by default whenever requests route through a custom base URL, and a gateway receives it when developers set [`CLAUDE_CODE_ENABLE_FINE_GRAINED_TOOL_STREAMING=1`](/docs/en/env-vars).
| Feature | Header and body pair | Symptom when broken | Remediation |
| :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------- |
| [Adaptive reasoning](/docs/en/model-config#adjust-effort-level) | No beta header. Claude Code sends `thinking: {"type": "adaptive"}` for Claude 4.6 and later, and treats model names it doesn't recognize, such as gateway aliases, as current models that receive the field | `400` naming the `thinking` field or the `adaptive` tag when the upstream model build doesn't accept it | Upgrade the upstream. On Opus 4.6 and Sonnet 4.6, developers can set `CLAUDE_CODE_DISABLE_ADAPTIVE_THINKING=1` instead |
| [Context management](https://platform.claude.com/docs/en/build-with-claude/context-editing) | Context management beta header pairs with the `context_management` body field | `400` with `Extra inputs are not permitted`. Common when a gateway accepts Anthropic-format requests but forwards them to Amazon Bedrock | Forward both, or [`CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS=1`](/docs/en/env-vars) |
| [Extended context](https://platform.claude.com/docs/en/build-with-claude/context-windows#context-window-sizes-by-model) and [interleaved thinking](https://platform.claude.com/docs/en/build-with-claude/extended-thinking#interleaved-thinking) | Beta headers only, no body field | Silently unavailable when the header is stripped; the upstream never sees the capability request | Forward `anthropic-beta` verbatim |
| Beta [tool fields](https://platform.claude.com/docs/en/agents-and-tools/tool-use/overview) | Tool-related beta headers pair with tool schema fields such as `strict` and `defer_loading` | `400` naming the unrecognized tool schema field when the body passes through without its header | Forward both, or [`CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS=1`](#disable-pre-release-capabilities) |
| [Effort](https://platform.claude.com/docs/en/build-with-claude/effort) and [structured outputs](https://platform.claude.com/docs/en/build-with-claude/structured-outputs) | The `output_config` body field carries effort, structured-output format, and task budget settings; each pairs with its own beta header | `400` naming `output_config`, often `Extra inputs are not permitted`, on Amazon Bedrock and Google Cloud's Agent Platform upstreams | Forward the field and its headers together |
| [Token counting](https://platform.claude.com/docs/en/build-with-claude/token-counting) | No beta pairing; uses the `count_tokens` endpoint | Claude Code falls back to counting context usage through the messages endpoint | Expose the endpoint so token counts don't consume inference requests |
The `ANTHROPIC_DEFAULT_*_MODEL_SUPPORTED_CAPABILITIES` [variables](/docs/en/model-config) declare model capabilities only in the provider configurations: `CLAUDE_CODE_USE_BEDROCK`, `CLAUDE_CODE_USE_VERTEX`, `CLAUDE_CODE_USE_FOUNDRY`, and [`CLAUDE_CODE_USE_MANTLE`](/docs/en/amazon-bedrock#use-the-mantle-endpoint). They have no effect behind an `ANTHROPIC_BASE_URL` gateway.
### Automatic retry and error forwarding
Claude Code retries automatically after some upstream rejections and disables the rejected capability for the rest of the conversation. Rejections of the `thinking` field, of [thinking signatures](https://platform.claude.com/docs/en/build-with-claude/extended-thinking), and of mid-conversation system messages all recover this way. Context management and tool schema field rejections don't retry; those `400` errors reach the developer.
The retry logic matches on the upstream's error wording, so forward error response bodies unmodified. A gateway that wraps upstream errors in its own envelope breaks the recovery path even when it preserves the status code.
### Disable pre-release capabilities
`CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS=1` stops Claude Code from sending pre-release capabilities and their body fields on every provider, including context management and the beta tool fields. The variable doesn't affect adaptive reasoning, which is selected by model rather than by beta. It never suppresses the OAuth capability that subscription authentication requires.
On Claude Code v2.1.227 or later, your organization can keep [MCP tool search](/docs/en/mcp#scale-with-mcp-tool-search) on under this variable through [managed settings](/docs/en/settings#settings-files). What Claude Code sends with that override in place depends on how you connect:
* On a direct connection, or through a gateway set with `ANTHROPIC_BASE_URL`, Claude Code keeps sending the tool-search beta header, `defer_loading` tool fields, and `tool_reference` blocks, and strips the rest
* On a cloud provider, or signed in through a [Claude apps gateway](/docs/en/claude-apps-gateway), the override has no effect
The set of capabilities Claude Code sends grows over releases. For current beta header strings, see the [beta headers reference](https://platform.claude.com/docs/en/api/beta-headers); test your gateway against new Claude Code releases rather than pinning to an observed list.
## Model discovery
When `ANTHROPIC_BASE_URL` points at a gateway that exposes the Anthropic Messages format, Claude Code can query the gateway's `/v1/models` endpoint at startup and add the returned models to the `/model` picker.
Developers enable it by setting [`CLAUDE_CODE_ENABLE_GATEWAY_MODEL_DISCOVERY=1`](/docs/en/env-vars), in their own environment or through managed settings. Discovery is off by default so that gateways backed by a shared API key don't surface every model the key can access to every user.
### When discovery runs
Discovery applies only to the Anthropic Messages format. It doesn't run when:
* Any `CLAUDE_CODE_USE_*` provider variable is set, even if `ANTHROPIC_BASE_URL` is also set
* `ANTHROPIC_BASE_URL` is unset or points at `api.anthropic.com`
* Nonessential traffic is disabled, through [`CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC`](/docs/en/env-vars) or organization policy
### Request and response
The request is `GET /v1/models?limit=1000` with a 3-second timeout, and any redirect is treated as failure so the credential can't leak to a redirect target. A gateway that responds slowly or redirects `/v1/models`, even `http` to `https`, fails discovery silently; serve the endpoint directly at the configured base URL.
The discovery request sends exactly one credential header:
* `ANTHROPIC_AUTH_TOKEN` as a bearer token, when set
* Otherwise the resolved API key, including an [`apiKeyHelper`](/docs/en/llm-gateway-connect#rotate-credentials-with-apikeyhelper) value, in the `x-api-key` header
This differs from inference requests, which send a helper value in both headers. A gateway that authenticates `/v1/models` must accept `x-api-key` for helper deployments. Any headers from `ANTHROPIC_CUSTOM_HEADERS` are included as well.
Claude Code reads `id` and the optional `display_name` from each entry in the response's `data` array:
```json theme={null}
{
"data": [
{ "id": "claude-sonnet-4-6", "display_name": "Claude Sonnet 4.6" },
{ "id": "claude-opus-4-8" }
]
}
```
Claude Code keeps an entry when its `id` contains `claude` or `anthropic` anywhere in the string, matched case-insensitively, and ignores the rest. Provider-prefixed IDs such as `vertex_ai/claude-sonnet-4-6` or `bedrock/anthropic.claude-sonnet-4-5` pass the filter; an ID that contains neither substring doesn't. Before v2.1.223, Claude Code kept an entry only when its `id` began with `claude` or `anthropic`, which hid provider-prefixed IDs.
### Picker entries and caching
The picker is the interactive model list that opens when a developer runs `/model` in Claude Code. Each discovered entry is labeled "From gateway" and uses `display_name` when provided. The [`availableModels` managed setting](/docs/en/settings#available-settings) bounds what discovery can add.
A discovered ID is skipped when it exactly matches a row already in the picker, or when both the discovered and existing IDs resolve to [Fable](/docs/en/model-config#work-with-fable-5). A discovered explicit ID is also folded into a built-in entry when both resolve to the same model. Built-in rows are keyed on aliases such as `sonnet`, so a discovered explicit ID of the model the alias currently resolves to, such as `claude-sonnet-5`, collapses into the `sonnet` row, while an ID the alias doesn't resolve to, such as `claude-sonnet-4-6`, still adds its own "From gateway" row alongside the built-in entry. Before v2.1.197, Claude Code didn't fold explicit IDs into built-in entries, so a discovered ID such as `claude-sonnet-5` added its own "From gateway" row alongside the `sonnet` row.
Results are cached to `~/.claude/cache/gateway-models.json`, or `%USERPROFILE%\.claude\cache\gateway-models.json` on Windows, and refreshed on each startup. If you set [`CLAUDE_CONFIG_DIR`](/docs/en/env-vars), the cache lives under that directory instead. If the request fails or the gateway doesn't implement `/v1/models`, the picker falls back to the cached list from the previous startup or to the built-in model list. If your gateway serves Claude models under aliases that don't match the discovery filter, developers can add those aliases manually with the [model configuration](/docs/en/model-config) variables.
## Related resources
For the rest of the gateway documentation set and the underlying API references:
* [Gateway overview](/docs/en/gateways): what a gateway is and how to choose between Claude apps gateway and another product
* [Other LLM gateways](/docs/en/llm-gateway): how to roll out a gateway your organization runs and how it interacts with claude.ai subscriptions
* [Roll out an LLM gateway for your organization](/docs/en/llm-gateway-rollout): the admin checklist that uses this contract
* [Connect Claude Code to an LLM gateway](/docs/en/llm-gateway-connect): per-developer configuration and the troubleshooting table
* [Beta headers reference](https://platform.claude.com/docs/en/api/beta-headers): the current set of `anthropic-beta` values
* [Messages API](https://platform.claude.com/docs/en/api/messages): the API format an Anthropic-format gateway implements
llm-gateway-rollout First recorded · 269 lines, first recorded
# Roll out an LLM gateway for your organization ## Prerequisites ### Gateway requirements ## Rollout steps ### Confirm the gateway routes your models ### Issue developer credentials ### Test Claude Code against the gateway ### Distribute the configuration #### What to distribute #### Distribute through managed settings #### Hand developers the values to set themselves ### Verify the rollout ## Maintain the gateway ## Related resources
The first capture of this source. The page was already there, and this is what it said.
# Roll out an LLM gateway for your organization
> Deploy a gateway product for Claude Code: configure it to forward what Claude Code sends, issue developer credentials, distribute the configuration through managed settings, and verify the rollout.
This page walks an administrator through rolling out an LLM gateway for Claude Code. It assumes you have a gateway product deployed that meets the [gateway requirements](#gateway-requirements). Deploying or operating any specific product isn't covered here; deploy yours following its vendor's documentation.
<Note>
* To connect Claude Code on your own machine to an existing gateway, see [Connect Claude Code to an LLM gateway](/docs/en/llm-gateway-connect)
* For what Claude Code sends to a gateway and what to forward, see the [gateway protocol reference](/docs/en/llm-gateway-protocol)
</Note>
## Prerequisites
To complete the rollout, you'll need:
* A gateway deployed on your infrastructure, serving HTTPS at the exact address you'll distribute to developers, not an address that redirects to it, and configured to route Claude model names to your provider
* A provider credential for the gateway to forward with:
* For the Anthropic API: an API key from the [Claude Console](https://platform.claude.com/settings/keys)
* For a cloud provider: cloud credentials with model access. See the prerequisites on the [Amazon Bedrock](/docs/en/amazon-bedrock#prerequisites), [Google Cloud's Agent Platform](/docs/en/google-vertex-ai#prerequisites), or [Microsoft Foundry](/docs/en/microsoft-foundry#prerequisites) page
* A way to deliver settings files to developer machines, such as MDM or configuration management
* If you don't have one yet, [how settings reach devices](/docs/en/admin-setup#decide-how-settings-reach-devices) compares the options
### Gateway requirements
Whichever product provides the gateway, it must:
* **Accept a supported API format**: one of the formats in the [API formats table](/docs/en/llm-gateway-protocol#api-formats). The rollout steps below assume the Anthropic Messages API at `POST /v1/messages`, which most gateways serve
* **Stream responses**: pass server-sent events through as they arrive, including keep-alive pings, instead of buffering the whole response; [streaming](/docs/en/llm-gateway-protocol#streaming) covers what buffering or stripped pings break
* **Route Claude model names**: map each name developers use to an upstream model. Claude Code sends a model name such as `claude-sonnet-4-6` in each request; in most gateway products the mapping is a model list or routing table in the gateway's own configuration
* **Forward headers and body unchanged**: pass `anthropic-beta`, `anthropic-version`, and the request body through in both directions; the [feature pass-through table](/docs/en/llm-gateway-protocol#feature-pass-through) maps each to the feature that breaks without it
* **Return upstream errors unmodified**: Claude Code's automatic recovery matches on error wording, so wrapping errors in the gateway's own envelope breaks it
* **Exempt the path from request-body WAF inspection**: Claude Code prompts carry source code and XML-style tags that match cross-site-scripting body rules; a WAF in front of the gateway returns `403` on real sessions while short test requests pass
Optionally, serve `GET /v1/models` so Claude Code can populate the model picker from your gateway with [model discovery](/docs/en/llm-gateway-protocol#model-discovery).
## Rollout steps
The rollout takes five steps, each with a checkpoint:
1. [Confirm the gateway routes your models](#confirm-the-gateway-routes-your-models)
2. [Issue each developer a credential](#issue-developer-credentials)
3. [Test Claude Code against the gateway](#test-claude-code-against-the-gateway)
4. [Distribute the base URL and credentials](#distribute-the-configuration)
5. [Verify from a developer machine](#verify-the-rollout)
The steps involve three different credentials, and the checkpoints name them by placeholder so you can tell which one is at fault when something fails:
| Credential | Who holds it | Placeholder in checkpoints |
| :-------------------------------- | :--------------------------------------------------------------------------------------------------- | :---------------------------------------------------------- |
| Provider credential | The gateway, which forwards it to the upstream provider | Configured on the gateway; never appears in client commands |
| Gateway administrative credential | You, if your gateway product issues one for its admin or test interface | `<gateway-key>` |
| Developer key | Each developer, issued by the gateway in [Issue developer credentials](#issue-developer-credentials) | `<developer-key>` |
### Confirm the gateway routes your models
Your gateway should already be configured with your provider credential, listening at its base URL, and forwarding requests to your provider's API. Test that the path works end to end with a minimal request, substituting two values from your deployment:
* `<gateway-key>` is whatever credential lets you call the gateway right now: an administrative key, a test key, or your own developer key if you've already issued one. Not every gateway product has a separate admin credential; if yours doesn't, issue yourself a developer key in [Issue developer credentials](#issue-developer-credentials) first
* `model` is a Claude model name your gateway is configured to route. The example uses `claude-sonnet-4-6`; substitute a name you've configured
<Tabs>
<Tab title="Bash or Zsh">
```bash theme={null}
curl -X POST "https://llm-gateway.example.com/v1/messages" \
-H "Authorization: Bearer <gateway-key>" \
-H "anthropic-version: 2023-06-01" \
-H "content-type: application/json" \
-d '{"model": "claude-sonnet-4-6", "max_tokens": 1, "messages": [{"role": "user", "content": "."}]}'
```
</Tab>
<Tab title="PowerShell">
```powershell theme={null}
Invoke-RestMethod -Method Post -Uri "https://llm-gateway.example.com/v1/messages" `
-Headers @{ "Authorization" = "Bearer <gateway-key>"; "anthropic-version" = "2023-06-01" } `
-ContentType "application/json" `
-Body '{"model": "claude-sonnet-4-6", "max_tokens": 1, "messages": [{"role": "user", "content": "."}]}'
```
</Tab>
</Tabs>
**Checkpoint**: a `200` with a `content` field means the gateway reached the provider with that model name. A `404` means that name isn't routed at the gateway; a `401` from the provider means the gateway's provider credential is wrong.
Repeat the request once per Claude model name in your gateway's routing configuration. A name the gateway doesn't route returns `404` to any developer who selects it, so test every name before rollout.
<Note>
Avoid serving the gateway behind a redirect. A redirect can drop the request body or strip the credential header on inference requests, and [model discovery](/docs/en/llm-gateway-protocol#model-discovery) treats any redirect as a failure so the credential cannot leak to a redirect target.
</Note>
### Issue developer credentials
Each developer needs their own gateway key to authenticate. Create a credential per developer at the gateway, following your product's credential management documentation.
Confirm a freshly issued key works against the gateway with the same request as [Confirm the gateway routes your models](#confirm-the-gateway-routes-your-models), replacing `<gateway-key>` with the new `<developer-key>`:
<Tabs>
<Tab title="Bash or Zsh">
```bash theme={null}
curl -X POST "https://llm-gateway.example.com/v1/messages" \
-H "Authorization: Bearer <developer-key>" \
-H "anthropic-version: 2023-06-01" \
-H "content-type: application/json" \
-d '{"model": "claude-sonnet-4-6", "max_tokens": 1, "messages": [{"role": "user", "content": "."}]}'
```
</Tab>
<Tab title="PowerShell">
```powershell theme={null}
Invoke-RestMethod -Method Post -Uri "https://llm-gateway.example.com/v1/messages" `
-Headers @{ "Authorization" = "Bearer <developer-key>"; "anthropic-version" = "2023-06-01" } `
-ContentType "application/json" `
-Body '{"model": "claude-sonnet-4-6", "max_tokens": 1, "messages": [{"role": "user", "content": "."}]}'
```
</Tab>
</Tabs>
**Checkpoint**: a `200` with a `content` field means the developer key reaches the gateway and the gateway forwards it. A `401` here, when [the previous step](#confirm-the-gateway-routes-your-models) succeeded, means the developer key is wrong or hasn't taken effect at the gateway yet.
Issuing one key per developer rather than a shared key is what makes per-developer usage attribution and individual offboarding work. The environment variable that holds the key depends on which header the gateway reads. For a gateway that checks credentials in the `Authorization: Bearer` header, developers set their key in `ANTHROPIC_AUTH_TOKEN`. For a gateway that reads keys from the `x-api-key` header, developers set `ANTHROPIC_API_KEY` instead; the [credential table](/docs/en/llm-gateway-connect#set-the-credential-variable) covers the mapping.
### Test Claude Code against the gateway
Run Claude Code through the gateway yourself before distributing anything, using the same configuration the rollout will deliver fleet-wide. Type these directly in a terminal, not in a `.env` or settings file; they last only for this terminal session, so closing it returns your machine to its normal configuration. Use `ANTHROPIC_API_KEY` instead of `ANTHROPIC_AUTH_TOKEN` if your gateway reads the `x-api-key` header:
<Tabs>
<Tab title="Bash or Zsh">
```bash theme={null}
export ANTHROPIC_BASE_URL=https://llm-gateway.example.com
export ANTHROPIC_AUTH_TOKEN="<developer-key>"
```
</Tab>
<Tab title="PowerShell">
```powershell theme={null}
$env:ANTHROPIC_BASE_URL = "https://llm-gateway.example.com"
$env:ANTHROPIC_AUTH_TOKEN = "<developer-key>"
```
</Tab>
</Tabs>
Then send a one-shot prompt through the gateway:
```bash theme={null}
claude -p "Reply with one word: connected"
```
**Checkpoint**: the prompt returns a response, and the request appears in the gateway's log as a `POST` to the `/v1/messages` path with status `200`. Claude Code appends a query string such as `?beta=true`, so match on the path, not the full URL. Two failure messages point in different directions:
* `Not logged in`: check the gateway log to tell the two causes apart. If it's empty, no credential reached the session and no request left the machine; re-run the exports in the shell you're testing from. If it shows a rejected request with `x-api-key` in the `401` body, the gateway expects keys in that header instead; switch to `ANTHROPIC_API_KEY`
* `Failed to authenticate. API Error: 401` means a credential was sent and rejected, and the gateway log says where: a `401` naming `api.anthropic.com` or your provider's endpoint means the gateway reached the upstream but its provider credential was rejected, so the developer key worked and the provider credential the gateway holds is wrong or a placeholder
A wrong or unreachable base URL produces a different symptom: Claude Code [retries the connection with backoff](/docs/en/errors#automatic-retries) and can sit with no output for several minutes before reporting an error. If the command appears to hang, check the gateway log instead of waiting; no arriving request means `ANTHROPIC_BASE_URL` doesn't point at the gateway.
### Distribute the configuration
Every developer machine needs the gateway address and a credential. You can distribute them centrally through [managed settings](/docs/en/settings#settings-files), so developers configure nothing, or hand developers the values to set themselves.
#### What to distribute
The same set of variables applies whichever path you choose. Most rollouts only need `ANTHROPIC_BASE_URL` and a credential; include the conditional rows when your gateway setup calls for them.
| Variable or setting | What it does | Include when |
| :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `ANTHROPIC_BASE_URL` | Sends Claude Code's API requests to the gateway instead of `api.anthropic.com` | Always |
| `apiKeyHelper`, or a credential in `ANTHROPIC_AUTH_TOKEN` or `ANTHROPIC_API_KEY` | Authenticates each request to the gateway. The helper runs a command to fetch the key; the variables hold a static key, sent as `Authorization: Bearer` and `x-api-key` respectively | Always; one of the three |
| `ANTHROPIC_CUSTOM_HEADERS` | Adds extra HTTP headers to every API request | Your gateway requires a tenant or routing header on every request |
| `CLAUDE_CODE_ENABLE_GATEWAY_MODEL_DISCOVERY` | Queries the gateway's `/v1/models` at startup and adds the returned names to the `/model` picker | Your gateway serves `/v1/models` and you want developers' pickers populated from it |
| `CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS` | Stops Claude Code sending pre-release capability headers and body fields. [Disable pre-release capabilities](/docs/en/llm-gateway-protocol#disable-pre-release-capabilities) covers the exact scope | Your gateway forwards to an Amazon Bedrock or Google Cloud's Agent Platform upstream that rejects beta fields. See [Gateway requirements](#gateway-requirements) |
| `CLAUDE_CODE_SKIP_FAST_MODE_NETWORK_ERRORS` or `CLAUDE_CODE_SKIP_FAST_MODE_ORG_CHECK` | Restores [fast mode](/docs/en/fast-mode) when its availability check, which calls `api.anthropic.com` directly rather than following `ANTHROPIC_BASE_URL`, fails, is intercepted, or is skipped for lack of an Anthropic credential | Your organization uses fast mode, and developers authenticate with `ANTHROPIC_AUTH_TOKEN` alone, with a gateway-issued key in `ANTHROPIC_API_KEY` or from an `apiKeyHelper`, or your network blocks or intercepts direct requests to `api.anthropic.com`; [use fast mode behind proxies and LLM gateways](/docs/en/fast-mode#use-fast-mode-behind-proxies-and-llm-gateways) covers which of the two variables matches your configuration |
| `ANTHROPIC_MODEL` or [`ANTHROPIC_DEFAULT_HAIKU_MODEL`](/docs/en/model-config) | Set which model name Claude Code requests for the main session and for background traffic | Your gateway routes model names that don't match Claude Code's defaults, or you route [background functionality](/docs/en/costs#background-token-usage) to a different model. Route both the override names and the built-in model IDs Claude Code requests when no override is set, since some background sub-calls request a built-in ID regardless of the override; [model configuration](/docs/en/model-config) covers which model each part of a session uses |
| `ANTHROPIC_BEDROCK_BASE_URL`, `ANTHROPIC_VERTEX_BASE_URL`, `ANTHROPIC_FOUNDRY_BASE_URL`, or `ANTHROPIC_AWS_BASE_URL` with the [variables for that provider](/docs/en/llm-gateway-connect#route-to-a-cloud-provider-through-a-gateway) | Point Claude Code at the gateway through a provider-specific base URL. Amazon Bedrock and Google Cloud's Agent Platform also switch to those providers' native request format | Your gateway fronts Amazon Bedrock, Google Cloud's Agent Platform, Microsoft Foundry, or the Claude Platform on AWS; see [API formats](/docs/en/llm-gateway-protocol#api-formats) |
#### Distribute through managed settings
Deliver the variables through the `env` block of a [managed settings file](/docs/en/settings#settings-files), pushed by MDM, registry policy, or configuration management:
```json theme={null}
{
"env": {
"ANTHROPIC_BASE_URL": "https://llm-gateway.example.com"
},
"apiKeyHelper": "/usr/local/bin/get-gateway-key"
}
```
Add the conditional variables from the table to the same `env` block. A managed `ANTHROPIC_BASE_URL` is enforced and cannot be overridden by a developer's shell export, since Claude Code applies it over the process environment and lower-precedence settings.
Do not include `forceLoginMethod` or `forceLoginOrgUUID` in managed settings alongside a gateway credential. On Claude Code v2.1.146 and later, either key, with any value, blocks `ANTHROPIC_API_KEY`, `ANTHROPIC_AUTH_TOKEN`, and `apiKeyHelper` at startup, so developers see `This machine's managed settings require a first-party login` and cannot proceed. 
[Server-managed settings](/docs/en/server-managed-settings#platform-availability) delivery requires a direct connection to `api.anthropic.com`, so it does not reach gateway-routed sessions. Gateway deployments use this file-based managed settings path, which enforces the same keys.
For the credential, distribute one [`apiKeyHelper`](/docs/en/llm-gateway-connect#rotate-credentials-with-apikeyhelper) command in the managed settings file as shown above; the command authenticates to your secrets store as the local developer, so each machine receives its own key. Alternatively, deliver each developer their key through your existing secrets process and have them set `ANTHROPIC_AUTH_TOKEN` themselves.
Some environments need separate delivery:
* The desktop app reads gateway routing from its third-party inference configuration, not from managed settings; deploy that file through MDM alongside managed settings so desktop sessions route through the gateway too. See the [desktop third-party configuration docs](https://claude.com/docs/third-party/claude-desktop/configuration) and the [desktop gateway docs](https://claude.com/docs/third-party/claude-desktop/gateway)
* CI runners need `ANTHROPIC_BASE_URL` and the credential set in the [runner's environment](/docs/en/llm-gateway-connect#configure-each-surface)
* WSL on managed Windows machines reads the Windows managed settings only when [`wslInheritsWindowsSettings`](/docs/en/settings#available-settings) is `true`
#### Hand developers the values to set themselves
If you don't have managed-settings distribution in place, send each developer what they need to follow the [connect page](/docs/en/llm-gateway-connect#configure-claude-code-yourself):
* The gateway URL
* Their personal credential
* **Which variable to put the credential in**: `ANTHROPIC_AUTH_TOKEN` for a bearer-token gateway, or `ANTHROPIC_API_KEY` for an `x-api-key` gateway. Telling developers which one saves them the trial-and-error described on the [connect page](/docs/en/llm-gateway-connect#set-the-credential-variable)
* Any conditional variables from the [What to distribute table](#what-to-distribute), with their values
The [connect page](/docs/en/llm-gateway-connect#configure-claude-code-yourself) walks developers through setting each one.
**Checkpoint**: on a developer machine, `claude` starts a session without showing the login screen, since the distributed credential satisfies authentication. Then run `/status` and open the **Status** tab: the `Anthropic base URL` line shows the gateway address, and for managed distribution the `Setting sources` line includes managed settings. A login screen, or a missing `Anthropic base URL` line, means the configuration didn't reach the machine.
### Verify the rollout
Confirm everything works from a developer machine, not the gateway host, so the test covers the network path developers use. Send a streaming request, which checks the endpoint, streaming pass-through, and model routing at once:
<Tabs>
<Tab title="Bash or Zsh">
```bash theme={null}
curl -N -X POST "https://llm-gateway.example.com/v1/messages" \
-H "Authorization: Bearer <developer-key>" \
-H "anthropic-version: 2023-06-01" \
-H "content-type: application/json" \
-d '{"model": "claude-sonnet-4-6", "max_tokens": 16, "stream": true, "messages": [{"role": "user", "content": "count to 3"}]}'
```
</Tab>
<Tab title="PowerShell">
```powershell theme={null}
$body = '{"model": "claude-sonnet-4-6", "max_tokens": 16, "stream": true, "messages": [{"role": "user", "content": "count to 3"}]}'
$body | curl.exe -N -X POST "https://llm-gateway.example.com/v1/messages" `
-H "Authorization: Bearer <developer-key>" `
-H "anthropic-version: 2023-06-01" `
-H "content-type: application/json" `
--data-binary '@-'
```
</Tab>
</Tabs>
You should see `data:` lines arrive incrementally. The whole response arriving at once after a pause means the gateway is buffering, which stalls Claude Code; a `404` means the model name isn't routed. Repeat per model name.
Then start `claude` and send a message. Each symptom at this step has one cause:
* A login prompt means a credential gap. Run `/status` and open the **Status** tab: when the `Setting sources` line doesn't include managed settings, the distribution didn't reach the machine; when it does, the developer credential wasn't delivered, so set `ANTHROPIC_AUTH_TOKEN` or the `apiKeyHelper`
* `Failed to authenticate` errors mean the gateway is rejecting requests; its log says which credential failed. A rejection the gateway logs itself names the developer key, while a `401` from `api.anthropic.com` or your provider's endpoint means the provider credential the gateway holds was rejected
* A one-time approval prompt for the key is expected on first use when the gateway expects keys in the `x-api-key` header, set as `ANTHROPIC_API_KEY`. With `ANTHROPIC_AUTH_TOKEN`, no prompt appears and the variable takes over silently; a previously saved claude.ai login is inactive for that session
If your organization uses [fast mode](/docs/en/fast-mode), run `/fast` here too: the availability check calls `api.anthropic.com` directly rather than following the gateway base URL, so a gateway-routed session can report fast mode as unavailable or disabled even though inference works. [Use fast mode behind proxies and LLM gateways](/docs/en/fast-mode#use-fast-mode-behind-proxies-and-llm-gateways) maps each message to the variable that restores it, distributed with [the rest of the configuration](#distribute-the-configuration).
Finally, check the gateway's logs for the message you sent: the credential identifies the developer, and the [`x-claude-code-session-id` header](/docs/en/llm-gateway-protocol#request-headers) groups requests by session. If features fail with the [troubleshooting symptoms](/docs/en/llm-gateway-connect#troubleshoot-gateway-errors), the gateway is stripping headers or rewriting errors; see the [gateway requirements](#gateway-requirements) above.
## Maintain the gateway
After rollout, three kinds of change reach the gateway over time. Each has a symptom to watch for and an action to take.
| Change | Symptom when the gateway hasn't kept up | Action |
| :--------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| New Claude Code releases add `anthropic-beta` values and request body fields | Developers report `400` errors naming a new field after they update Claude Code; see [feature pass-through](/docs/en/llm-gateway-protocol#feature-pass-through) | Forward `anthropic-*` headers and request bodies verbatim rather than allowlisting; test new Claude Code releases against the gateway before they reach developers |
| New Claude models become available | Developers selecting a new model name get `404`; the `/model` picker doesn't list it | Add the model name to the gateway's routing configuration, then re-run the [routing check](#confirm-the-gateway-routes-your-models). If you distribute `ANTHROPIC_MODEL` or the default-model variables, update the managed settings |
| Credentials expire or need rotation | All developer requests start failing with `401` from the upstream | Rotate the gateway's provider credential on its own schedule; developer keys rotate at the gateway, and an [`apiKeyHelper`](/docs/en/llm-gateway-connect#rotate-credentials-with-apikeyhelper) handles per-developer rotation without redistributing settings |
When sizing per-key rate limits, account for the client [retrying transient failures](/docs/en/errors#automatic-retries), including `429` responses, up to 10 times with backoff, honoring `Retry-After`. Keep the [protocol reference](/docs/en/llm-gateway-protocol) as the contract for what each Claude Code release sends.
## Related resources
* [Connect Claude Code to an LLM gateway](/docs/en/llm-gateway-connect): the developer-facing setup steps, with per-surface configuration and a troubleshooting table you can hand to developers
* [Gateway protocol reference](/docs/en/llm-gateway-protocol): the wire contract for gateway operators, covering endpoints, headers to forward, and the feature pass-through table
* [Settings files and precedence](/docs/en/settings#settings-files): how managed, project, and user settings combine, and where the managed file goes on each platform
* [Set up Claude Code for your organization](/docs/en/admin-setup): the wider rollout this gateway is one part of, including policy enforcement, usage visibility, and data handling
managed-mcp First recorded · 375 lines, first recorded
# Control MCP server access for your organization ## Choose a pattern ## Exclusive control with managed-mcp.json ### Authenticate with per-user credentials ### Validate the configuration ### Disable MCP entirely ### Allow claude.ai connectors alongside the managed set ## Policy-based control with allowlists and denylists ### Match servers by URL, command, or name ### How a server is evaluated #### How policy entries expand ### Example configuration ### Restrict the allowlist to managed settings only ## How restrictions appear to users ## Monitor MCP usage ## Configuration summary ## Related resources
The first capture of this source. The page was already there, and this is what it said.
# Control MCP server access for your organization
> Restrict which MCP servers users can add or connect to with managed configuration files, allowlists, and denylists.
By default, anyone running Claude Code can connect any [MCP server](/docs/en/mcp) they choose. Anthropic reviews connectors against its [listing criteria](https://claude.com/docs/connectors/building/review-criteria) before adding them to the [Anthropic Directory](https://claude.ai/directory), but doesn't security-audit or manage any MCP server. As an administrator, you can restrict which servers run in your organization, from deploying a fixed approved set to disabling MCP entirely.
This page covers how to:
* [Choose a pattern](#choose-a-pattern) that matches how much control you need
* [Deploy a fixed server set with `managed-mcp.json`](#exclusive-control-with-managed-mcp-json), including how to [disable MCP entirely](#disable-mcp-entirely)
* [Control servers with allowlists and denylists](#policy-based-control-with-allowlists-and-denylists)
* [Tell users what to expect](#how-restrictions-appear-to-users) when a restriction blocks a server
* [Monitor which servers your organization actually uses](#monitor-mcp-usage)
<Note>
The [Security](/docs/en/security) page covers the MCP threat model and how to evaluate a server before approving it. [Decide what to enforce](/docs/en/admin-setup#decide-what-to-enforce) covers MCP restrictions alongside the other administrative controls.
</Note>
## Choose a pattern
Claude Code supports a range of restriction levels. Each pattern uses one or both of the mechanisms covered below: `managed-mcp.json` for deploying a fixed set, and `allowedMcpServers`/`deniedMcpServers` for filtering what users configure.
| Pattern | What it does | Configure |
| :---------------------- | :-------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------- |
| **Disable MCP** | No servers load, apart from the [VS Code extension's own in-process server](#exclusive-control-with-managed-mcp-json) | `managed-mcp.json` with an empty server map |
| **Fixed deployment** | Every user gets the same servers and can't add others | `managed-mcp.json` with the servers you want |
| **Approved catalog** | Publish a list of approved servers; users add the ones they want, anything else is blocked | `allowedMcpServers` + `allowManagedMcpServersOnly: true` |
| **Plugin servers only** | Servers can only come from plugins; users can't add their own | [`strictPluginOnlyCustomization`](/docs/en/settings#strictpluginonlycustomization) with `mcp` in the list |
| **Soft allowlist** | Enforce an allowlist that users can broaden in their own settings | `allowedMcpServers` without `allowManagedMcpServersOnly` |
| **Denylist only** | Block known-bad servers, allow everything else | `deniedMcpServers` |
| **No restrictions** | Users add anything | Don't deploy any managed MCP configuration |
<Note>
Claude Code doesn't have a built-in MCP server registry that users can browse and install from. For the approved-catalog pattern, share the approved list and its `claude mcp add` commands somewhere your users will find them, such as an internal wiki, or distribute the servers as plugins through a [managed plugin marketplace](/docs/en/plugin-marketplaces#managed-marketplace-restrictions) so users can browse and install them from `/plugin`.
</Note>
## Exclusive control with managed-mcp.json
If you deploy a `managed-mcp.json` file, Claude Code loads only the servers that file defines, plus the VS Code extension's own in-process server in sessions the extension starts. Users cannot add, modify, or use any other MCP servers, including plugin-provided servers and servers passed with the [`--mcp-config` CLI flag](/docs/en/cli-reference#cli-flags). The file also suppresses claude.ai connectors unless you [allow them alongside the managed set](#allow-claude-ai-connectors-alongside-the-managed-set).
What a user sees when a session does receive `--mcp-config` servers depends on where the session runs:
* On a workstation, Claude Code exits at startup with `You cannot dynamically configure MCP servers when an enterprise MCP config is present`.
* In [cloud sessions](/docs/en/claude-code-on-the-web), which receive claude.ai connectors and other server-delivered servers through `--mcp-config`, Claude Code starts with the managed servers only. Nothing in the session tells the user which servers were left out. Claude Code names them in a warning on its stderr, which a [self-hosted runner](/docs/en/self-hosted-environments-configuration#mcp-servers) records at the `debug` log level. Before v2.1.229, these sessions exited with the same error a workstation shows.
If a user passes `--strict-mcp-config`, Claude Code exits at startup on a workstation and in a cloud session alike, because that flag asks to replace the managed set.
Two other settings can further filter the managed set:
* `allowedMcpServers` and `deniedMcpServers` apply to managed servers too, so a managed server that doesn't pass them won't load.
* A user's own `deniedMcpServers` merges in from their settings, so users can block a managed server for themselves.
See [How a server is evaluated](#how-a-server-is-evaluated) for the full order of checks.
`managed-mcp.json` is a standalone file, so it cannot be delivered through [server-managed settings](/docs/en/server-managed-settings). Any process that can write to a system path with administrator privileges can deploy it. At scale, that's usually through device management tooling, such as Jamf or a configuration profile on macOS, Group Policy or Intune on Windows, or your fleet management of choice on Linux. Claude Code looks for the file at one of these paths:
| Platform | Path |
| :------------ | :--------------------------------------------------------- |
| macOS | `/Library/Application Support/ClaudeCode/managed-mcp.json` |
| Linux and WSL | `/etc/claude-code/managed-mcp.json` |
| Windows | `C:\Program Files\ClaudeCode\managed-mcp.json` |
The file uses the same format as a project [`.mcp.json`](/docs/en/mcp#project-scope) file:
```json theme={null}
{
"mcpServers": {
"github": {
"type": "http",
"url": "https://api.githubcopilot.com/mcp/"
},
"sentry": {
"type": "http",
"url": "https://mcp.sentry.dev/mcp"
},
"company-internal": {
"type": "stdio",
"command": "/usr/local/bin/company-mcp-server",
"args": ["--config", "/etc/company/mcp-config.json"],
"env": {
"COMPANY_API_URL": "https://internal.example.com"
}
}
}
}
```
### Authenticate with per-user credentials
Any user on the machine can read this file, so don't store API keys or other credentials in `env` blocks. Pass per-user credentials with one of these instead:
* [`${VAR}` expansion](/docs/en/mcp#environment-variable-expansion-in-mcp-json) to read secrets from each user's environment.
* [OAuth or per-user headers](/docs/en/mcp#authenticate-with-remote-mcp-servers) so each user authenticates as themselves.
* [`headersHelper`](/docs/en/mcp#use-dynamic-headers-for-custom-authentication) to generate credentials at connection time.
### Validate the configuration
To confirm the file is in effect, run two checks on a managed machine:
1. `claude mcp list` shows only the servers in `managed-mcp.json`. If a user's own servers still appear, the file isn't being read; check the path and permissions.
2. `claude mcp add --transport http test https://example.com/mcp` fails with `Cannot add MCP server: enterprise MCP configuration is active and has exclusive control over MCP servers`. The URL doesn't need to be a real server, since the policy check rejects the command before anything is contacted.
### Disable MCP entirely
Deploy a `managed-mcp.json` containing an empty server map to block every MCP server apart from the [VS Code extension's own in-process server](#exclusive-control-with-managed-mcp-json):
```json theme={null}
{
"mcpServers": {}
}
```
`claude mcp add` fails with the enterprise-policy error above. Servers users had previously configured stop loading the next time they start a session, with no warning that policy is the reason.
### Allow claude.ai connectors alongside the managed set
Deploying `managed-mcp.json` suppresses [claude.ai connectors](/docs/en/mcp#use-mcp-servers-from-claude-ai) by default, including connectors an administrator configured for the organization in the claude.ai admin console. To load those connectors alongside the servers in `managed-mcp.json`, set `"allowAllClaudeAiMcps": true` in a [managed settings source](/docs/en/admin-setup#decide-how-settings-reach-devices). Requires Claude Code v2.1.149 or later.
With the setting enabled, Claude Code loads the same claude.ai connectors it would load if `managed-mcp.json` weren't deployed. [Allowlists and denylists](#policy-based-control-with-allowlists-and-denylists) still apply to those connectors, so you can block specific ones with `deniedMcpServers`. The setting affects only claude.ai connectors; plugin-provided servers stay suppressed.
The setting also affects only the connectors Claude Code fetches itself. [Cloud sessions](/docs/en/claude-code-on-the-web) receive connectors as server-delivered `--mcp-config` entries, and Claude Code suppresses those whenever `managed-mcp.json` is deployed, whether or not you set `allowAllClaudeAiMcps`.
Claude Code reads this setting only from admin-controlled policy tiers: server-managed settings, an MDM-deployed plist or HKLM registry key, or a system `managed-settings.json` file. Placing it in user or project settings has no effect, so users cannot re-enable connectors that exclusive control suppressed.
## Policy-based control with allowlists and denylists
Allowlists and denylists filter which configured servers are allowed to load. They aren't a registry: a server still has to be added by a user, a plugin, or `managed-mcp.json` before the allowlist or denylist applies to it. To deploy servers to users, use [`managed-mcp.json`](#exclusive-control-with-managed-mcp-json). Both lists also filter servers passed with the [`--mcp-config` CLI flag](/docs/en/cli-reference#cli-flags); `--strict-mcp-config` limits which configuration files load and doesn't bypass either list.
To make the allowlist authoritative, set `allowedMcpServers` and `allowManagedMcpServersOnly: true` together in a [managed settings source](/docs/en/admin-setup#decide-how-settings-reach-devices), such as server-managed settings or a deployed `managed-settings.json` file. [Restrict the allowlist to managed settings only](#restrict-the-allowlist-to-managed-settings-only) shows the configuration. Without `allowManagedMcpServersOnly`, allowlists from every settings source merge, including a user's own `~/.claude/settings.json`, so a user can broaden what your allowlist permits. Denylists merge from every source regardless.
<Note>
`allowManagedMcpServersOnly` is separate from `allowManagedPermissionRulesOnly`, which locks down [permission rules](/docs/en/permissions#managed-settings) only. Setting that flag does not enforce the MCP allowlist.
</Note>
### Match servers by URL, command, or name
`allowedMcpServers` and `deniedMcpServers` are lists of entries. Each entry is an object with a single key that identifies servers by their URL, their command, or their name:
| Key | Matches | Use for |
| :-------------- | :-------------------------------------------------------------------- | :------------------------------------- |
| `serverUrl` | A remote server URL, exact or with `*` wildcards | HTTP and SSE servers |
| `serverCommand` | The exact command and arguments that start a stdio server | Stdio servers |
| `serverName` | The user-assigned label. Exact match only; wildcards are not expanded | Either type, but see the Warning below |
Leaving `allowedMcpServers` unset is different from setting it to an empty array:
| Setting | Unset (default) | Empty array `[]` | Populated |
| :------------------ | :------------------ | :----------------- | :---------------------------- |
| `allowedMcpServers` | All servers allowed | No servers allowed | Only matching servers allowed |
| `deniedMcpServers` | No servers blocked | No servers blocked | Matching servers blocked |
See [Invalid entries in managed settings](/docs/en/settings#invalid-entries-in-managed-settings) for what happens when an entry fails schema validation.
<Warning>
A `serverName` entry, in either list, is not a security control. The name is the label a user assigns when running `claude mcp add` or editing a config file, not the underlying server, so a user can call any server `github`. For claude.ai connectors the name is the display name returned by claude.ai, which can change. To enforce which servers actually run, add `serverCommand` or `serverUrl` entries.
</Warning>
The `serverName` validation differs between the two lists:
* In `deniedMcpServers`, `serverName` accepts any non-empty string, so you can block [claude.ai connectors](/docs/en/mcp#use-mcp-servers-from-claude-ai) by their display name. For example, `{ "serverName": "claude.ai Slack" }` blocks the Slack connector. Prefer a `serverUrl` entry when you need the deny to be robust to renames, or when a connector name collides and gains a ` (N)` suffix.
* In `allowedMcpServers`, `serverName` is limited to letters, numbers, hyphens, and underscores. Use `serverUrl` to allowlist a claude.ai connector.
To turn off all claude.ai connectors, see [`disableClaudeAiConnectors`](/docs/en/mcp#disable-claude-ai-connectors).
### How a server is evaluated
Before loading a server, including one from `managed-mcp.json`, Claude Code runs three checks in order:
1. **Merge the lists.** Allowlist and denylist entries from every settings source combine into one allowlist and one denylist. When `allowManagedMcpServersOnly` is `true`, only the managed allowlist is kept; the denylist always merges from every source.
2. **Check the denylist.** A server that matches any denylist entry, by URL, command, or name, is blocked. Nothing overrides a denylist match.
3. **Check the allowlist.** If `allowedMcpServers` isn't set anywhere, every server that passed the denylist loads. If it is set, what the server must match depends on its type, shown in the table below.
| Server type | Allowed when it matches |
| :------------------- | :--------------------------------------------------------------------------------------------------------------- |
| Remote (HTTP or SSE) | A `serverUrl` entry. A `serverName` match counts only when the allowlist contains no `serverUrl` entries |
| Stdio | A `serverCommand` entry. A `serverName` match counts only when the allowlist contains no `serverCommand` entries |
Three matching rules apply inside those checks:
* **Commands match exactly.** Every argument, in order. `["npx", "-y", "server"]` does not match `["npx", "server"]` or `["npx", "-y", "server", "--flag"]`.
* **`serverCommand` and `serverUrl` values expand before matching.** Both the policy entry and the server's configured value go through [`${VAR}` and `${VAR:-default}` expansion](/docs/en/mcp#environment-variable-expansion-in-mcp-json), so an entry written as `["${HOME}/bin/server"]` matches a server config that uses either the same reference or the expanded path. On Windows, reference an environment variable that is set there, such as `${USERPROFILE}` instead of `${HOME}`. `serverName` values match literally and never expand. The two sides read different environments; [How policy entries expand](#how-policy-entries-expand) covers which, and how allowlist and denylist entries differ.
* **URLs support `*` wildcards** anywhere in the pattern, including the scheme. Hostname matching is case-insensitive and ignores a trailing FQDN dot, so `https://Mcp.Example.com/*` matches `https://mcp.example.com/api`. Paths stay case-sensitive.
| Pattern | Allows |
| :-------------------------- | :--------------------------------------------------------------------- |
| `https://mcp.example.com/*` | All paths on a specific domain |
| `https://mcp.example.com` | Also all paths on that domain. A pattern with no path matches any path |
| `https://*.example.com/*` | Any subdomain of `example.com` |
| `http://localhost:*/*` | Any port on localhost |
| `*://mcp.example.com/*` | Any scheme to a specific domain |
#### How policy entries expand
The server's configured value expands from the live process environment, like the rest of `.mcp.json`. A policy entry expands from a pinned environment instead, so a variable set by a project or user settings file can't change what an allowlist entry means. Because a policy entry still depends on the launching shell's value for any variable it references, use literal URLs and commands for entries you rely on for enforcement.
| Entry list | Expands from | Expansion that would change a URL entry's scheme, host, or path scope |
| ------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------- |
| `allowedMcpServers` | The environment Claude Code started with, plus `env` values from managed settings | Claude Code ignores the entry |
| `deniedMcpServers` | The same, and a variable with no startup value and no `:-default` fills from settings files outside the repository, such as user or managed settings, which only ever widens what the entry matches | The entry still matches |
Requires Claude Code v2.1.219 or later.
### Example configuration
The configuration below sets up a hard allowlist with a denylist. The highlighted lines change how the rest of the list is evaluated, and the callouts after the block explain each one:
```json {3,5,11} theme={null}
{
"allowedMcpServers": [
{ "serverUrl": "https://api.githubcopilot.com/*" },
{ "serverUrl": "https://mcp.sentry.dev/*" },
{ "serverCommand": ["npx", "-y", "@modelcontextprotocol/server-filesystem", "."] },
{ "serverCommand": ["python", "/usr/local/bin/approved-server.py"] },
{ "serverUrl": "https://mcp.example.com/*" },
{ "serverUrl": "https://*.internal.example.com/*" }
],
"deniedMcpServers": [
{ "serverName": "dangerous-server" },
{ "serverCommand": ["npx", "-y", "unapproved-package"] },
{ "serverUrl": "https://*.untrusted.example.com/*" }
]
}
```
* **Line 3**: the first `serverUrl` entry. Once one exists, every remote server must match a URL pattern, so a user can't get an unlisted remote server through by giving it an allowed name.
* **Line 5**: the first `serverCommand` entry. Same effect for stdio servers, so every local server must match a listed command exactly.
* **Line 11**: a `serverName` entry in the denylist. Denylist entries always apply, so any server named `dangerous-server` is blocked regardless of its URL or command.
A `serverName` entry in this allowlist would never match anything, since both transport types already have stricter entries.
The accordions below walk through how a server is evaluated against other allowlist and denylist combinations.
<Accordion title="URL-only allowlist">
```json theme={null}
{
"allowedMcpServers": [
{ "serverUrl": "https://mcp.example.com/*" },
{ "serverUrl": "https://*.internal.example.com/*" }
]
}
```
| Server | Result |
| :---------------------------------------------------- | :------------------------------------------- |
| HTTP server at `https://mcp.example.com/api` | Allowed: matches URL pattern |
| HTTP server at `https://api.internal.example.com/mcp` | Allowed: matches wildcard subdomain |
| HTTP server at `https://external.example.com/mcp` | Blocked: doesn't match any URL pattern |
| Stdio server with any command | Blocked: no name or command entries to match |
</Accordion>
<Accordion title="Command-only allowlist">
```json theme={null}
{
"allowedMcpServers": [
{ "serverCommand": ["npx", "-y", "approved-package"] }
]
}
```
| Server | Result |
| :---------------------------------------------------- | :-------------------------------- |
| Stdio server with `["npx", "-y", "approved-package"]` | Allowed: matches command |
| Stdio server with `["node", "server.js"]` | Blocked: doesn't match command |
| HTTP server named `my-api` | Blocked: no name entries to match |
</Accordion>
<Accordion title="Mixed name and command allowlist">
```json theme={null}
{
"allowedMcpServers": [
{ "serverName": "github" },
{ "serverCommand": ["npx", "-y", "approved-package"] }
]
}
```
| Server | Result |
| :----------------------------------------------------------------------- | :-------------------------------------------------------------------- |
| Stdio server named `local-tool` with `["npx", "-y", "approved-package"]` | Allowed: matches command |
| Stdio server named `local-tool` with `["node", "server.js"]` | Blocked: command entries exist but doesn't match |
| Stdio server named `github` with `["node", "server.js"]` | Blocked: stdio servers must match commands when command entries exist |
| HTTP server named `github` | Allowed: matches name |
| HTTP server named `other-api` | Blocked: name doesn't match |
</Accordion>
<Accordion title="Name-only allowlist">
```json theme={null}
{
"allowedMcpServers": [
{ "serverName": "github" },
{ "serverName": "internal-tool" }
]
}
```
| Server | Result |
| :-------------------------------------------------- | :------------------------------- |
| Stdio server named `github` with any command | Allowed: no command restrictions |
| Stdio server named `internal-tool` with any command | Allowed: no command restrictions |
| HTTP server named `github` | Allowed: matches name |
Cut at 300 lines. The page has the rest.
mcp First recorded · 1278 lines, first recorded
# Connect Claude Code to tools via MCP ## What you can do with MCP ## Find and build MCP servers ## Installing MCP servers ### Option 1: Add a remote HTTP server ### Option 2: Add a remote SSE server ### Option 3: Add a local stdio server ### Option 4: Add a remote WebSocket server ### Managing your servers #### Server status #### Project server approvals and workspace trust #### Server status detail #### Configuration warnings #### Tool availability ### Disable a server without removing it ### Dynamic tool updates ### Automatic reconnection ### Push messages with channels ### Automatic backgrounding of long tool calls ### Plugin-provided MCP servers ## MCP installation scopes ### Local scope ### Project scope ### User scope ### Scope hierarchy and precedence ### Environment variable expansion in `.mcp.json` ## Practical examples ### Example: Connect to GitHub for code reviews ### Example: Query your PostgreSQL database ## Authenticate with remote MCP servers ### Authenticate from the command line ### Use a fixed OAuth callback port ### Use pre-configured OAuth credentials ### Override OAuth metadata discovery ### Restrict OAuth scopes ### Use dynamic headers for custom authentication ## Add MCP servers from JSON configuration ## Import MCP servers from Claude Desktop ## Use MCP servers from claude.ai ### Organization controls on connector tools ### Disable claude.ai connectors ## Use Claude Code as an MCP server ## MCP output limits and warnings ### Raise the limit for a specific tool ## Tool input schemas with a root-level combinator ## Require approval for a specific tool ## Respond to MCP elicitation requests ## Use MCP resources ### Reference MCP resources ## Scale with MCP tool search ### How it works ### For MCP server authors ### Configure tool search ### Exempt a server from deferral ## Use MCP prompts as commands ### Execute MCP prompts ## Managed MCP configuration
The first capture of this source. The page was already there, and this is what it said.
# Connect Claude Code to tools via MCP
> Learn how to connect Claude Code to your tools with the Model Context Protocol.
Claude Code can connect to hundreds of external tools and data sources through the [Model Context Protocol (MCP)](https://modelcontextprotocol.io/introduction), an open source standard for AI-tool integrations. MCP servers give Claude Code access to your tools, databases, and APIs.
Connect a server when you find yourself copying data into chat from another tool, like an issue tracker or a monitoring dashboard. Once connected, Claude can read and act on that system directly instead of working from what you paste.
If you're connecting your first server, start with the [MCP quickstart](/docs/en/mcp-quickstart) for a step-by-step walkthrough. This page is the full reference.
## What you can do with MCP
With MCP servers connected, you can ask Claude Code to:
* **Implement features from issue trackers**: "Add the feature described in JIRA issue ENG-4521 and create a PR on GitHub."
* **Analyze monitoring data**: "Check Sentry and Statsig to check the usage of the feature described in ENG-4521."
* **Query databases**: "Find emails of 10 random users who used feature ENG-4521, based on our PostgreSQL database."
* **Integrate designs**: "Update our standard email template based on the new Figma designs that were posted in Slack"
* **Automate workflows**: "Create Gmail drafts inviting these 10 users to a feedback session about the new feature."
* **React to external events**: an MCP server can also act as a [channel](/docs/en/channels) that pushes messages into your session, so Claude reacts to Telegram messages, Discord chats, or webhook events while you're away.
## Find and build MCP servers
Browse reviewed connectors in the [Anthropic Directory](https://claude.ai/directory). Directory connectors use the same MCP infrastructure as Claude Code, so you can add any remote server listed there with `claude mcp add`.
<Warning>
Verify you trust each server before connecting it. Servers that fetch external content can expose you to [prompt injection risk](/docs/en/security#protect-against-prompt-injection).
</Warning>
To build your own server, see the [MCP server guide](https://modelcontextprotocol.io/docs/develop/build-server) for protocol fundamentals and the [Claude connector building docs](https://claude.com/docs/connectors/building) for authentication, testing, and Directory submission.
You can also have Claude scaffold a server for you with the official [`mcp-server-dev` plugin](https://github.com/anthropics/claude-plugins-official/tree/main/plugins/mcp-server-dev).
<Steps>
<Step title="Install the plugin">
In a Claude Code session, run:
```
/plugin install mcp-server-dev@claude-plugins-official
```
If the install fails, match the message Claude Code reports:
* `Marketplace "claude-plugins-official" not found`: add the marketplace with `/plugin marketplace add anthropics/claude-plugins-official`, then retry the install.
* The plugin is [not found in the marketplace](/docs/en/discover-plugins#install-plugins): check the plugin name.
Check the install summary: if it reports `Run /reload-plugins to activate.`, run that command.
</Step>
<Step title="Run the build skill">
```
/mcp-server-dev:build-mcp-server
```
Claude asks about your use case and scaffolds a remote HTTP or local stdio server.
</Step>
</Steps>
## Installing MCP servers
MCP servers can be configured in several ways depending on your needs:
### Option 1: Add a remote HTTP server
HTTP servers are the recommended option for connecting to remote MCP servers. This is the most widely supported transport for cloud-based services.
```bash theme={null}
# Basic syntax
claude mcp add --transport http <name> <url>
# Real example: Connect to Notion
claude mcp add --transport http notion https://mcp.notion.com/mcp
# Example with Bearer token
claude mcp add --transport http secure-api https://api.example.com/mcp \
--header "Authorization: Bearer your-token"
```
When configuring MCP servers via JSON in `.mcp.json`, `~/.claude.json`, or `claude mcp add-json`, the `type` field accepts `streamable-http` as an alias for `http`. The MCP specification uses the name `streamable-http` for this transport, so configurations copied from server documentation work without modification.
A JSON entry that has a `url` but no `type` is a configuration error, because Claude Code reads an entry with no `type` as a stdio server. Claude Code skips that server and reports `MCP server "<name>" has a "url" but no "type"; add "type": "http" (or "sse" / "ws") to this entry`. Before v2.1.202, Claude Code reported this misconfiguration as `command: expected string, received undefined`.
In `--output-format stream-json` runs, Claude Code also reports a skipped `--mcp-config` entry in the `system/init` event's [`mcp_server_errors` field](/docs/en/headless#stream-responses), so scripts can detect that the server never loaded. This requires Claude Code v2.1.219 or later.
### Option 2: Add a remote SSE server
<Warning>
The SSE (Server-Sent Events) transport is deprecated. Use HTTP servers instead, where available.
</Warning>
Some services still expose only an SSE endpoint. Use the same command as the HTTP transport, with `--transport sse`:
```bash theme={null}
# Basic syntax
claude mcp add --transport sse <name> <url>
# Real example: Connect to Asana
claude mcp add --transport sse asana https://mcp.asana.com/sse
# Example with authentication header
claude mcp add --transport sse private-api https://api.company.com/sse \
--header "X-API-Key: your-key-here"
```
### Option 3: Add a local stdio server
Stdio servers run as local processes on your machine. They're ideal for tools that need direct system access or custom scripts.
Claude Code sets `CLAUDE_PROJECT_DIR` in the spawned server's environment to the project root, so your server can resolve project-relative paths without depending on the working directory. This is the same directory hooks receive in their `CLAUDE_PROJECT_DIR` variable. Read it from inside your server process, for example `process.env.CLAUDE_PROJECT_DIR` in Node or `os.environ["CLAUDE_PROJECT_DIR"]` in Python.
`CLAUDE_PROJECT_DIR` is the stable project root and doesn't change when you add or remove working directories mid-session. A server that limits its own filesystem access to a set of allowed directories should implement the MCP `roots/list` request instead. Claude Code answers `roots/list` with the session's launch directory plus every [additional working directory](/docs/en/permissions#working-directories) you've granted with `--add-dir`, `/add-dir`, or the `additionalDirectories` setting. Claude Code sends `notifications/roots/list_changed` when that set changes. Before v2.1.203, `roots/list` returned only the launch directory and Claude Code didn't send `notifications/roots/list_changed`.
This variable is set in the server's environment, not in Claude Code's own environment, so referencing it via `${VAR}` expansion in the `command` or `args` of a project-scoped `.mcp.json` entry or a local- or user-scoped server entry in `~/.claude.json` requires a default such as `${CLAUDE_PROJECT_DIR:-.}`. Plugin-provided MCP configurations substitute `${CLAUDE_PROJECT_DIR}` directly and don't need the default.
```bash theme={null}
# Basic syntax
claude mcp add [options] <name> -- <command> [args...]
# Real example: Add Airtable server
claude mcp add --env AIRTABLE_API_KEY=YOUR_KEY --transport stdio airtable \
-- npx -y airtable-mcp-server
```
<Note>
**Important: Separate server arguments with `--`**
For stdio servers, the `--` (double dash) separates Claude's own options, such as `--transport`, `--env`, and `--scope`, from the command and arguments that run the server. Everything after `--` is passed to the server untouched.
For example:
* `claude mcp add --transport stdio myserver -- npx server` → runs `npx server`
* `claude mcp add --env KEY=value --transport stdio myserver -- python server.py --port 8080` → runs `python server.py --port 8080` with `KEY=value` in environment
Without `--`, Claude Code would try to parse the server's flags, like `--port` above, as its own options.
`--env` accepts multiple `KEY=value` pairs. If the server name comes directly after `--env`, the CLI reads the name as another pair and rejects it, so place at least one other option between `--env` and the server name, as in the examples above.
</Note>
### Option 4: Add a remote WebSocket server
WebSocket servers hold a persistent bidirectional connection, which suits remote MCP servers that push events to Claude unprompted. Use HTTP instead when your server only responds to requests, since HTTP supports OAuth and the `claude mcp add --transport` flag, while WebSocket supports neither.
Configure WebSocket servers in `.mcp.json` or with `claude mcp add-json`:
```bash theme={null}
claude mcp add-json events-server \
'{"type":"ws","url":"wss://mcp.example.com/socket","headers":{"Authorization":"Bearer YOUR_TOKEN"}}'
```
The `type: "ws"` entry accepts the same `url`, `headers`, `headersHelper`, `timeout`, and `alwaysLoad` fields as `http`. Authentication is header-only, so pass a static token in `headers` or generate one at connect time with [`headersHelper`](#use-dynamic-headers-for-custom-authentication). The `claude mcp add --transport` flag doesn't accept `ws`.
### Managing your servers
Once configured, you can manage your MCP servers with these commands:
```bash theme={null}
# List all configured servers
claude mcp list
# Get details for a specific server
claude mcp get notion
# Remove a server
claude mcp remove notion
# (within Claude Code) Check server status
/mcp
```
#### Server status
`claude mcp add` confirms a successful add by printing an `Added ...` line, which means the configuration was written. `claude mcp list` then shows a health status next to each server it lists, such as `✔ Connected`, `! Needs authentication`, or `✘ Failed to connect`. A failure status means Claude Code couldn't connect to that server, not that the list command failed.
Project-scoped servers from `.mcp.json` that are awaiting your approval appear in `claude mcp list` and `claude mcp get <name>` as ``⏸ Pending approval (run `claude` to approve)``. Run `claude` interactively to review and approve them. `claude mcp get <name>` shows rejected servers as `✘ Rejected (see disabledMcpjsonServers in settings)`.
WebSocket servers don't appear in `claude mcp list` output. Use `claude mcp get <name>` or the `/mcp` panel to check them.
#### Project server approvals and workspace trust
As of v2.1.196, `claude mcp list` and `claude mcp get` read `.mcp.json` approvals only from settings files that aren't checked into the repository until you trust the workspace by running `claude` in it and accepting the workspace trust dialog. A cloned repository can't approve its own servers: [`enableAllProjectMcpServers` or `enabledMcpjsonServers`](/docs/en/settings#available-settings) committed to the project's `.claude/settings.json` is ignored in an untrusted folder, and the server stays at `⏸ Pending approval` instead of being connected and health-checked.
Approvals from these sources still apply in an untrusted folder:
* your user `~/.claude/settings.json`
* managed settings
* settings passed with `--settings`
Approvals in an untracked `.claude/settings.local.json` also apply, but only after you accept a trust dialog for that folder or one of its parent directories: Claude Code runs git to check whether the file is tracked, and it runs that check only in a trusted folder. In a folder you've never trusted, the file's approvals wait for the trust dialog unless the folder is your own configuration home: your home directory, or a directory whose `.claude` you've set as [`CLAUDE_CONFIG_DIR`](/docs/en/env-vars). Before v2.1.207, an untracked `.claude/settings.local.json` approved servers in a folder you'd never trusted.
A `disabledMcpjsonServers` entry in any settings file still rejects the server.
#### Server status detail
In `/mcp`, a server's menu, and the [`/plugin`](/docs/en/plugins) manager, a remote (HTTP or SSE) server you've used before can show a `cached` status such as `cached 2h ago · connects on first use · 5 tools`. Claude Code loaded the server's tool list from a previous session instead of connecting at startup, and it connects the server the first time Claude calls one of its tools. The tools are available from your first message, so you don't need to do anything. To make every server connect at startup instead, set [`MCP_DISCOVERY_CACHE=0`](/docs/en/env-vars). The discovery cache and its `cached` status require Claude Code v2.1.221 or later.
When a server's status is `✘ Failed to connect`, `claude mcp list` appends the failure detail to that status line, and `claude mcp get <name>` shows it on an `Issue:` line: the HTTP status or error code, plus any error text the server returned. The server's detail view in `/mcp` includes the same server-reported text in its `Issue:` row. Claude Code redacts credential-like text from this detail and never includes the expanded server URL, which can carry secrets. Claude Code appends no detail to a `✘ Connection error` status, because the exception text it would print there can embed that URL. Before v2.1.219, both commands showed only the bare failure status, without the status code or the server's error text.
A remote server whose configuration has an empty `url` shows as `not configured` in `/mcp`, in `claude mcp list`, and in the [`/plugin`](/docs/en/plugins) manager, and Claude Code doesn't attempt to connect to it. A plugin can include a placeholder entry like this for a connector you configure later, so Claude Code doesn't report it as an error or a setup issue. The server's detail view in `/mcp` reads `No URL configured for this server`; set the entry's `url` to connect it. Before v2.1.208, Claude Code reported an empty `url` as a configuration issue with a prompt to reconnect.
#### Configuration warnings
Claude Code also warns when an MCP config value carries hidden leading or trailing whitespace, which often comes from pasting a token with a trailing newline. Claude Code checks `command`, `url`, each `args` entry, and the values and key names under `env` and `headers`. Claude Code shows the warning in `claude mcp list` output and in `/mcp`, naming the affected fields without echoing their values, for example `Leading or trailing whitespace in: headers.Authorization`. Claude Code doesn't trim the whitespace and uses the values exactly as written, so edit the configuration to remove it.
Some server names are reserved for Claude Code's built-in servers: `workspace`, `claude-in-chrome`, `computer-use`, `Claude Preview`, and `Claude Browser`. If your configuration defines a server with a reserved name, Claude Code skips it at load time and shows a warning asking you to rename it. `claude mcp add` rejects a reserved name with an error.
`Claude Preview` and `Claude Browser` both name the built-in server that the [Claude Code desktop app's preview pane](/docs/en/desktop#preview-your-app) uses. Before v2.1.205, `Claude Browser` wasn't reserved, so a user-configured server could register under that name.
#### Tool availability
The `/mcp` panel shows the tool count next to each connected server and flags servers that advertise the tools capability but expose no tools.
If your request needs tools from a server that is still connecting in the background, Claude waits for that server before continuing. How the wait happens depends on your configuration:
* **With [tool search](#scale-with-mcp-tool-search), the default**: the wait happens inside the `ToolSearch` call.
* **Without tool search**: Claude uses the `WaitForMcpServers` tool instead. Configurations without tool search include a custom `ANTHROPIC_BASE_URL`, `ENABLE_TOOL_SEARCH=false`, and a model earlier than the Claude 4.5 generation on Google Cloud's Agent Platform.
* **On a Microsoft Foundry [deployment hosted on Azure](https://platform.claude.com/docs/en/build-with-claude/claude-in-microsoft-foundry#hosting-options)**: Claude starts on the tool-search path rather than with `WaitForMcpServers`, since Claude Code discovers the deployment's server-side rejection only from the API. After Claude Code switches that deployment to [upfront loading](#scale-with-mcp-tool-search), tools from a server that finishes connecting become available on Claude's next request.
With tool search enabled, when a server finishes connecting while Claude is working, Claude Code lists the server's tool names to Claude on its next request in the same turn. Claude can then search for and call those tools without waiting for your next message.
### Disable a server without removing it
Toggle a server off in the `/mcp` panel to stop Claude Code from connecting to it without losing its configuration. Claude Code still lists the server in `/mcp`, marked as disabled.
When you toggle a server, Claude Code records your choice per project in `~/.claude.json`, in one of two lists that cover disjoint sets of servers:
* `disabledMcpServers`: an opt-out list for user-configured servers, plugin servers, claude.ai connectors, and built-in servers that default to on. Claude Code doesn't connect to a server you list here. When you disable a claude.ai connector with the per-project `/mcp` toggle described in [Disable claude.ai connectors](#disable-claude-ai-connectors), Claude Code writes it to this list under its display name, for example `claude.ai Slack`.
* `enabledMcpServers`: an opt-in list for built-in servers that default to off, such as `computer-use`. Claude Code connects to a default-off server only when you list it here.
Claude Code consults exactly one of the two lists for each server, so neither list overrides the other. If you add a regular server to `enabledMcpServers`, or a default-off built-in server to `disabledMcpServers`, Claude Code ignores the entry.
`disabledMcpServers` and `enabledMcpServers` are unrelated to [`enabledMcpjsonServers` and `disabledMcpjsonServers`](/docs/en/settings#available-settings), which control approval of servers defined in a project's `.mcp.json` file.
### Dynamic tool updates
Claude Code supports MCP `list_changed` notifications, allowing MCP servers to dynamically update their available tools, prompts, and resources without requiring you to disconnect and reconnect. When an MCP server sends a `list_changed` notification, Claude Code automatically refreshes the available capabilities from that server.
If a refresh request fails, Claude Code keeps the server's previously discovered tools, prompts, and resources until a later refresh succeeds. Before v2.1.214, a transient error during the refresh replaced the server's tools, prompts, and resources with an empty list.
### Automatic reconnection
If an HTTP or SSE server disconnects mid-session, Claude Code automatically reconnects with exponential backoff: up to five attempts, starting at a one-second delay and doubling each time. The server appears as pending in `/mcp` while reconnection is in progress. After five failed attempts the server is marked as failed and you can retry manually from `/mcp`. Stdio servers are local processes and are not reconnected automatically.
The same backoff applies when an HTTP or SSE server fails its initial connection at startup. Claude Code retries the initial connection up to three times on transient errors such as a 5xx response, a connection refused, or a timeout, then marks the server as failed if it still can't connect. Authentication and not-found errors are not retried because they require a configuration change to resolve.
When a configured server fails to connect, Claude Code tells Claude which server failed and its connection error, including in `ToolSearch` results that find no matching tool, so Claude reports the connection failure in its response. Requires [tool search](#scale-with-mcp-tool-search), which is enabled by default. In configurations without tool search, such as a custom `ANTHROPIC_BASE_URL`, `ENABLE_TOOL_SEARCH=false`, or a model that doesn't support tool search, and on Amazon Bedrock, Google Cloud's Agent Platform, and Microsoft Foundry, Claude Code doesn't report failed server connections to Claude.
The capability discovery requests that run after a successful connection, such as `tools/list`, `prompts/list`, and `resources/list`, also retry transient network and server errors up to three times with short backoff. Authentication errors, 4xx responses, and request timeouts are not retried.
### Push messages with channels
An MCP server can also push messages directly into your session so Claude can react to external events like CI results, monitoring alerts, or chat messages. To enable this, your server declares the `claude/channel` capability and you opt it in with the `--channels` flag at startup. See [Channels](/docs/en/channels) to use an officially supported channel, or [Channels reference](/docs/en/channels-reference) to build your own.
<Tip>
Tips:
* Use the `-s` or `--scope` flag to specify where the configuration is stored:
* `local` (default): available only to you in the current project
* `project`: shared with everyone in the project via the `.mcp.json` file
* `user`: available to you across all projects
* Set environment variables with `-e` or `--env` flags (for example, `-e KEY=value`)
* The `--transport` and `--header` flags also accept `-t` and `-H` short forms
* Configure MCP server startup timeout using the `MCP_TIMEOUT` environment variable (for example, `MCP_TIMEOUT=10000 claude` sets a 10-second timeout)
* Set a per-server tool execution timeout by adding a `timeout` field in milliseconds to that server's `.mcp.json` entry, for example `"timeout": 600000` for ten minutes. This overrides the `MCP_TOOL_TIMEOUT` environment variable for that server only
* Claude Code displays a warning when MCP tool output exceeds 10,000 tokens and limits output to 25,000 tokens by default. To raise the limit, set the `MAX_MCP_OUTPUT_TOKENS` environment variable (for example, `MAX_MCP_OUTPUT_TOKENS=50000`); the warning threshold is fixed. See [MCP output limits and warnings](#mcp-output-limits-and-warnings)
* Use `/mcp` to authenticate with remote servers that require OAuth 2.0 authentication
</Tip>
The per-server `timeout` is a hard wall-clock limit per tool call, and progress notifications from the server don't extend it. Values below 1000 are ignored and fall through to `MCP_TOOL_TIMEOUT`, or to its default of about 28 hours when that variable is unset. For an HTTP, SSE, or [claude.ai connector](/docs/en/mcp#use-mcp-servers-from-claude-ai) server there is also a second, per-request timer that covers each request through to the server's first response byte. That timer is 60 seconds unless you set the per-server `timeout` or `MCP_TOOL_TIMEOUT`; setting either to 60 seconds or higher raises the per-request timer to that value, a lower value doesn't shorten it, and the 28-hour default of an unset `MCP_TOOL_TIMEOUT` never feeds it. Stdio and WebSocket servers have no per-request timer. Before v2.1.162, values below 1000 were floored to one second instead.
A per-server `timeout` of at least 1000 also acts as a floor on the idle timeout described below: Claude Code never aborts that server's tool calls for idleness sooner than the per-server `timeout`. Requires Claude Code v2.1.203 or later.
A tool call to an MCP server that sends no response and no progress notification for the idle window aborts with an error instead of waiting for the wall-clock limit. The idle timeout requires Claude Code v2.1.187 or later. It applies to every server type except IDE servers and SDK in-process servers. The idle window defaults to five minutes for HTTP, SSE, WebSocket, and [claude.ai connector](#use-mcp-servers-from-claude-ai) servers, and to 30 minutes for stdio servers. Before v2.1.203, stdio servers were exempt from the idle timeout.
Set the [`CLAUDE_CODE_MCP_TOOL_IDLE_TIMEOUT`](/docs/en/env-vars) environment variable in milliseconds to change the idle window, or set it to `0` to disable the check.
These timeouts bound how long a call can run, not always how long it blocks the session: a main-conversation call that runs past two minutes moves to a background task first. See [Automatic backgrounding of long tool calls](#automatic-backgrounding-of-long-tool-calls).
### Automatic backgrounding of long tool calls
An MCP tool call in the main conversation that is still running after two minutes moves to a background task instead of blocking the session. Claude receives the task ID immediately and keeps working, and the result arrives as a task notification when the call settles. Automatic backgrounding requires Claude Code v2.1.212 or later.
The task appears in [`/tasks`](/docs/en/commands#all-commands), where you can also stop it, and it doesn't survive exiting the session. The per-call limits still apply while the call runs in the background: the wall-clock limit set by the per-server `timeout` or [`MCP_TOOL_TIMEOUT`](/docs/en/env-vars), and the idle timeout set by [`CLAUDE_CODE_MCP_TOOL_IDLE_TIMEOUT`](/docs/en/env-vars).
Set the [`CLAUDE_CODE_MCP_AUTO_BACKGROUND_MS`](/docs/en/env-vars) environment variable in milliseconds to change the threshold, or set it to `0` to turn automatic backgrounding off. Setting `CLAUDE_CODE_DISABLE_BACKGROUND_TASKS` to `1` also turns it off, along with all other background task features.
Some calls never move to the background:
* Calls from [subagents](/docs/en/sub-agents); Claude Code backgrounds only main-conversation calls
* Calls to IDE servers
* Calls in [non-interactive mode](/docs/en/headless), unless `CLAUDE_AUTO_BACKGROUND_TASKS` is set to `1`, since a one-shot run can end before the result arrives
A call waiting on an open [elicitation dialog](#respond-to-mcp-elicitation-requests) isn't backgrounded while the dialog is open; the server is blocked on your input, not slow, so Claude Code defers the move until the dialog closes.
### Plugin-provided MCP servers
[Plugins](/docs/en/plugins) can bundle MCP servers that provide tools and integrations when you enable the plugin. Plugin MCP servers work identically to user-configured servers.
**How plugin MCP servers work**:
* Plugins define MCP servers in `.mcp.json` at the plugin root or inline in `plugin.json`
Cut at 300 lines. The page has the rest.
mcp-quickstart First recorded · 391 lines, first recorded
# Connect to MCP servers ## Before you begin ## Add and verify a server ## Where servers are saved ### Find your configuration on disk ## Change server scope ### Use a server in all your projects ### Share a server with your team ## Additional MCP server examples ### Add a local server ### Connect a server that requires sign-in ## Edit .mcp.json directly ## Connect from other surfaces ## Troubleshooting ## Next steps
The first capture of this source. The page was already there, and this is what it said.
# Connect to MCP servers
> Add an MCP server to Claude Code, verify the connection, and find the configuration on disk.
The [Model Context Protocol (MCP)](https://modelcontextprotocol.io/introduction) lets Claude Code use tools beyond its built-in set, such as searching an issue tracker, querying a database, or controlling a web browser. These tools come from MCP servers, which run on your machine or as hosted services.
This guide walks you through connecting one MCP server end to end with the Claude Code CLI. By the end, you'll have a server connected and responding, know where its configuration lives on disk, and know how to fix the most common connection errors.
<Note>
You can also add MCP servers from other surfaces, including the desktop app, VS Code, and the web. See [Connect from other surfaces](#connect-from-other-surfaces).
</Note>
For every way to connect and configure MCP servers in Claude Code, see the [MCP reference](/docs/en/mcp).
## Before you begin
Make sure you have:
* [Claude Code installed](/docs/en/quickstart) and authenticated
* A terminal open in a project directory. Any directory works, including an empty one.
## Add and verify a server
The example below connects to the [Claude Code documentation MCP server](https://code.claude.com/docs/mcp), a hosted server with full-text search over the Claude Code docs. It doesn't require authentication or any special configuration, so it works well as a first server to test the setup flow with.
The steps are the same for any server: add it, check the connection status, then use it in a session, with an optional cleanup step at the end. Some servers add a step, like a browser sign-in, shown in [Additional MCP server examples](#additional-mcp-server-examples). For more servers to connect, browse the [Anthropic Directory](/docs/en/mcp#find-and-build-mcp-servers).
<Steps>
<Step title="Add the MCP server">
Register the server with Claude Code. Run this in your terminal, not inside a `claude` session: you're configuring the server before starting a conversation.
```bash theme={null}
claude mcp add --transport http claude-code-docs https://code.claude.com/docs/mcp
```
The parts of the command:
* `claude mcp add`: registers a server with Claude Code.
* `--transport http`: the server is hosted at a URL rather than run as a local process.
* `claude-code-docs`: a name you make up. Calling the same server `docs` would work identically. Claude Code uses whatever name you pick to label the server's tools in Claude's output and to refer to the server in commands like `claude mcp remove`.
* `https://code.claude.com/docs/mcp`: the URL where the server is hosted.
The command prints a confirmation like `Added HTTP MCP server claude-code-docs with URL: https://code.claude.com/docs/mcp to local config`, followed by a `File modified:` line showing the configuration file it wrote. The `local config` part means the server is registered to you, in this project: if you start Claude Code in a different project, this server isn't active there. To register a server once for all your projects, add it at user scope, covered in [Change server scope](#change-server-scope).
</Step>
<Step title="Check the connection status">
Confirm the server appears in your server list and check its status:
```bash theme={null}
claude mcp list
```
The server appears with a status indicator:
| Status | Meaning |
| :----------------------------------------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `✔ Connected` | Ready to use. This is what you should see for `claude-code-docs` |
| `! Connected · tools fetch failed` | The server connected but couldn't list its tools. Run `claude mcp get <name>` for the error detail |
| `! Needs authentication` | The server is reachable but needs a browser sign-in, or a token passed with `--header`. See [Connect a server that requires sign-in](#connect-a-server-that-requires-sign-in) |
| `✘ Failed to connect` | Server didn't respond. See [Troubleshooting](#troubleshooting) |
| `✘ Connection error` | The connection attempt threw an error. See [Troubleshooting](#troubleshooting) |
| ``⏸ Pending approval (run `claude` to approve)`` | A project-scoped server you haven't approved yet. See [Edit .mcp.json directly](#edit-mcp-json-directly) |
Some legacy Windows consoles, such as the default console on Windows 10, don't support these Unicode glyphs and show `√` and `×` in place of `✔` and `✘`.
</Step>
<Step title="Use the server">
Start a session and ask Claude to use the new server by name:
```bash theme={null}
claude
```
```text wrap theme={null}
Use the claude-code-docs server to look up what MCP_TIMEOUT does
```
<Info>
You don't normally need to name a server in your prompt, since Claude chooses relevant tools on its own. Naming it here guarantees the demonstration goes through the new server rather than another tool, such as web fetch, that could answer the same question.
</Info>
The first time Claude calls the server, it asks for permission to use the new tool. Approve it to continue. The tool call in Claude's output is labeled with the server name, which is how you confirm the answer came from the MCP server rather than Claude's built-in knowledge.
</Step>
<Step title="Remove the server">
This step is optional. When you're done experimenting, you can remove the server:
```bash theme={null}
claude mcp remove claude-code-docs
```
The command confirms with `Removed MCP server "claude-code-docs" from local config` and a `File modified:` line showing the file it updated.
<Note>
Each connected server takes some space in [Claude's context window](/docs/en/how-claude-code-works#the-context-window) because its tool names and server instructions load into every session. Removing servers you no longer use keeps that space free.
</Note>
</Step>
</Steps>
## Where servers are saved
The `claude mcp add` command writes the server's details to a configuration file. By default it registers the server at `local` scope: private to you, active only in the current project. Pass `--scope user` to register it once for all your projects, or `--scope project` to share it with teammates. [Change server scope](#change-server-scope) walks through both.
<Note>
`claude mcp add` works the same in every shell, including PowerShell and Command Prompt. Inside a `claude` session, use the `/mcp` command to check and manage servers you've already added.
</Note>
There are other ways to add a server, each covered later on this page:
* [Add a local server](#add-a-local-server): run a program on your machine instead of connecting to a URL.
* [Edit `.mcp.json` directly](#edit-mcp-json-directly): write the JSON entry yourself instead of using the command.
* [Connect a server that requires sign-in](#connect-a-server-that-requires-sign-in): add a hosted server that needs a browser sign-in before its tools work.
### Find your configuration on disk
The `claude mcp add` command writes the server to one of three scopes, stored across two files, depending on the `--scope` flag. You don't need to edit these files directly, but knowing where they are helps with debugging and version control.
| Scope | File | Available to |
| :-------- | :----------------------------------------------------- | :--------------------------------------- |
| `local` | `~/.claude.json`, under the entry for this project | Only you, only this project. The default |
| `project` | `.mcp.json` in your project root | Everyone who clones the project |
| `user` | `~/.claude.json`, under the top-level `mcpServers` key | Only you, all projects |
On Windows, `~/.claude.json` resolves to `%USERPROFILE%\.claude.json`, typically `C:\Users\YourName\.claude.json`. If you've set [`CLAUDE_CONFIG_DIR`](/docs/en/env-vars), Claude Code reads `.claude.json` from inside that directory instead.
Run `claude mcp get claude-code-docs` to see which scope holds a server's definition. For how the scopes interact when the same server is defined in more than one, see [MCP installation scopes](/docs/en/mcp#mcp-installation-scopes).
## Change server scope
A server's scope is fixed when you add it, so changing scope means removing the entry and re-adding it at the new one. Both cases below start by removing the local entry from the first walkthrough, so the server has only one definition. If you already removed it at the end of that walkthrough, skip this command:
```bash theme={null}
claude mcp remove claude-code-docs --scope local
```
### Use a server in all your projects
Re-add the server at `user` scope to make it active in every project you open, still private to you:
```bash theme={null}
claude mcp add --scope user --transport http claude-code-docs https://code.claude.com/docs/mcp
```
### Share a server with your team
Re-add the server at `project` scope, which writes to `.mcp.json` in the project root:
```bash theme={null}
claude mcp add --scope project --transport http claude-code-docs https://code.claude.com/docs/mcp
```
Commit `.mcp.json` to version control. Teammates who clone the repository and start Claude Code see a prompt to approve the server, then it connects for them too.
## Additional MCP server examples
The first walkthrough used a hosted server that connects without any sign-in. The examples below cover the other two common shapes, with the same add, check, use flow.
### Add a local server
A local stdio server is a program Claude Code starts as a subprocess on your machine, rather than a service it reaches over a URL. Use one for tools that need access to local resources like a browser, your filesystem, or a database socket.
The [Playwright MCP server](https://github.com/microsoft/playwright-mcp) is a good one to try: it gives Claude a browser it can navigate, click, and read, and it needs no account. It runs through `npx`, so it requires [Node.js](https://nodejs.org/en/download) 18 or later.
<Steps>
<Step title="Add the Playwright server">
Register the server with the command Claude Code should run to start it:
```bash theme={null}
claude mcp add playwright -- npx -y @playwright/mcp@latest
```
This command differs from the hosted example in three ways:
* There's no `--transport` flag, because local servers use the default `stdio` transport.
* Everything after the `--` separator is the command Claude Code runs to start the server.
* `-y` tells `npx` to install the package without prompting.
The command prints a confirmation like `Added stdio MCP server playwright with command: npx -y @playwright/mcp@latest to local config`, followed by a `File modified:` line showing the configuration file it wrote.
Playwright drives whichever Chrome is already installed on your machine. To use a different browser, append `--browser` with the browser name, for example `--browser firefox`, after `@playwright/mcp@latest`.
</Step>
<Step title="Check the connection">
The `Added` confirmation means the entry was saved, not that the command runs. Check the connection:
```bash theme={null}
claude mcp list
```
The first check can show `✘ Failed to connect` while `npx` downloads the package, so wait a moment and run it again. Once the download finishes, the status changes to `✔ Connected`. If it still shows `✘ Failed to connect` after a couple of retries, see [Troubleshooting](#troubleshooting).
</Step>
<Step title="Use the browser">
Give Claude a task that needs the browser:
```text wrap theme={null}
Use playwright to open https://example.com and tell me the page title
```
A browser window opens so you can watch it work, and the tool calls in Claude's output are labeled with the `playwright` server name and the action, like `browser_navigate`.
Try pointing it at your local dev server to check that a page still renders after a change, or have it walk through a bug report step by step.
</Step>
</Steps>
### Connect a server that requires sign-in
Hosted services like Sentry, Linear, and Notion run their MCP servers behind OAuth: you add the server's URL, then sign in through your browser.
The steps below use Sentry as the example. To connect a different service, substitute its URL, which you can find in the [Anthropic Directory](/docs/en/mcp#find-and-build-mcp-servers) or the service's documentation.
<Steps>
<Step title="Add the server">
The `add` command is the same as for the docs server, with Sentry's URL:
```bash theme={null}
claude mcp add --transport http sentry https://mcp.sentry.dev/mcp
```
After adding, `claude mcp list` shows the server with `! Needs authentication`. That's expected: the next step completes the sign-in.
</Step>
<Step title="Authenticate in your browser">
Start a Claude Code session and open the MCP panel:
```text wrap theme={null}
/mcp
```
Select `sentry` from the list, press Enter, and choose `Authenticate`. Your browser opens to Sentry's sign-in page. Approve the connection there.
Back in Claude Code, the server's status changes to connected. If sign-in fails or the browser doesn't open, see [Troubleshooting](#troubleshooting).
</Step>
<Step title="Use the server">
Ask Claude something that needs the service, like `What Sentry projects do I have access to?`, and look for tool calls labeled with the `sentry` server name in its output.
</Step>
</Steps>
Servers that authenticate with a static token instead of OAuth take the token at add time with `--header "Authorization: Bearer <token>"`. See the [GitHub example](/docs/en/mcp#example-connect-to-github-for-code-reviews) for a worked version.
## Edit .mcp.json directly
Every file in the [scope table](#find-your-configuration-on-disk) uses the same JSON format for server entries. This section edits `.mcp.json`, the project-scope file. It's the one most worth writing by hand because it's checked into the repository, where it doubles as configuration-as-code for your team.
Create `.mcp.json` in your project root. The example below defines both servers from this guide, the hosted docs server reached over HTTP and the Playwright server as a local `stdio` process:
```json theme={null}
{
"mcpServers": {
"claude-code-docs": {
"type": "http",
"url": "https://code.claude.com/docs/mcp"
},
"playwright": {
"type": "stdio",
"command": "npx",
"args": ["-y", "@playwright/mcp@latest"]
}
}
}
```
The fields differ by server type:
* For HTTP servers, `url` is the endpoint Claude Code connects to.
* For stdio servers, `command` and `args` are the program it runs.
After saving the file, start a new Claude Code session in the project. Claude Code reads `.mcp.json` at startup.
The first time Claude Code sees a project-scoped server, it asks you to approve it. The prompt exists so a repository you clone can't launch processes on your machine without your consent. Approve the prompt, or run `/mcp` to approve later if you missed it.
Once you've approved, run `/mcp` and check that the servers show as connected. If one shows an error instead, see [Troubleshooting](#troubleshooting).
## Connect from other surfaces
This guide uses the `claude mcp` CLI commands, but every Claude Code surface can connect to MCP servers:
* **Claude Code desktop app**: add servers through the [Connectors UI](/docs/en/desktop#connect-external-tools).
* **Claude Desktop chat app**: a separate app from Claude Code. To copy servers from its `claude_desktop_config.json` into the CLI, run `claude mcp add-from-claude-desktop` on macOS or WSL.
* **VS Code**: see [Connect to external tools with MCP](/docs/en/vs-code#connect-to-external-tools-with-mcp).
* **Claude Code on the web**: reads `.mcp.json` from your repository. See [Edit .mcp.json directly](#edit-mcp-json-directly).
* **Claude.ai**: connectors you add at [claude.ai/customize/connectors](https://claude.ai/customize/connectors) load automatically in the CLI when you sign in with that account. See [Use MCP servers from Claude.ai](/docs/en/mcp#use-mcp-servers-from-claude-ai).
## Troubleshooting
If a server doesn't connect, check its status with `/mcp` inside a session or `claude mcp list` from your shell, then match the symptom below. The `/mcp` panel also lets you reconnect or authenticate without leaving the session.
<AccordionGroup>
<Accordion title="/mcp shows No MCP servers configured">
Claude Code didn't find any servers for the current directory. The most common causes:
* You ran `claude mcp add` from a different project. Local-scoped servers are tied to the project where you added them: the repository root, or the exact directory if you weren't in a git repository. Re-add the server from the project you're in now, or add it with `--scope user` so it isn't tied to a project.
* You edited a configuration file at the wrong path. The correct files are `~/.claude.json` and `<project>/.mcp.json`. Claude Code doesn't read paths such as `~/.claude/.mcp.json`, `~/.claude/config/mcp.json`, `~/.claude/mcp.json`, or `%APPDATA%\Claude\mcp.json`. For user-scoped servers, run `claude mcp add --scope user`, which writes to the `mcpServers` key in `~/.claude.json`; for project-scoped servers, edit `.mcp.json` at the project root.
</Accordion>
<Accordion title="Status shows Failed to connect or Connection error">
Both statuses mean the server didn't start or the URL didn't respond. They can also appear for HTTP servers that reject the token you configured in `headers.Authorization`; a server that wants a token you haven't configured shows `! Needs authentication` instead, covered in [Connect a server that requires sign-in](#connect-a-server-that-requires-sign-in).
Your first step depends on which status you see:
Cut at 300 lines. The page has the rest.
memory First recorded · 458 lines, first recorded
# How Claude remembers your project ## CLAUDE.md vs auto memory ## CLAUDE.md files ### When to add to CLAUDE.md ### Choose where to put CLAUDE.md files ### Set up a project CLAUDE.md ### Write effective instructions ### Import additional files ### AGENTS.md ### How CLAUDE.md files load #### Load from additional directories ### Organize rules with `.claude/rules/` #### Set up rules #### Path-specific rules #### Share rules across projects with symlinks #### User-level rules ### Manage CLAUDE.md for large teams #### Deploy organization-wide CLAUDE.md #### Exclude specific CLAUDE.md files ## Auto memory ### Enable or disable auto memory ### Storage location ### How it works ### Audit and edit your memory ## View and edit with `/memory` ## Troubleshoot memory issues ### Claude isn't following my CLAUDE.md ### I don't know what auto memory saved ### My CLAUDE.md is too large ### Instructions seem lost after `/compact` ## Related resources
The first capture of this source. The page was already there, and this is what it said.
# How Claude remembers your project
> Give Claude persistent instructions with CLAUDE.md files, and let Claude accumulate learnings automatically with auto memory.
Each Claude Code session begins with a fresh context window. Two mechanisms carry knowledge across sessions:
* **CLAUDE.md files**: instructions you write to give Claude persistent context
* **Auto memory**: notes Claude writes itself based on your corrections and preferences
This page covers how to:
* [Write and organize CLAUDE.md files](#claude-md-files)
* [Scope rules to specific file types](#organize-rules-with-claude/rules/) with `.claude/rules/`
* [Configure auto memory](#auto-memory) so Claude takes notes automatically
* [Troubleshoot](#troubleshoot-memory-issues) when instructions aren't being followed
## CLAUDE.md vs auto memory
Claude Code has two complementary memory systems. Both are loaded at the start of every conversation. Claude treats them as context, not enforced configuration. To block an action regardless of what Claude decides, use a [PreToolUse hook](/docs/en/hooks-guide) instead. The more specific and concise your instructions, the more consistently Claude follows them.
| | CLAUDE.md files | Auto memory |
| :------------------- | :------------------------------------------------ | :--------------------------------------------------------------- |
| **Who writes it** | You | Claude |
| **What it contains** | Instructions and rules | Learnings and patterns |
| **Scope** | Project, user, or org | Per repository, shared across worktrees |
| **Loaded into** | Every session | Every session (first 200 lines or 25KB) |
| **Use for** | Coding standards, workflows, project architecture | Build commands, debugging insights, preferences Claude discovers |
Use CLAUDE.md files when you want to guide Claude's behavior. Auto memory lets Claude learn from your corrections without manual effort.
Subagents can also maintain their own auto memory. See [subagent configuration](/docs/en/sub-agents#enable-persistent-memory) for details.
## CLAUDE.md files
CLAUDE.md files are markdown files that give Claude persistent instructions for a project, your personal workflow, or your entire organization. You write these files in plain text; Claude reads them at the start of every session.
### When to add to CLAUDE.md
Treat CLAUDE.md as the place you write down what you'd otherwise re-explain. Add to it when:
* Claude makes the same mistake a second time
* A code review catches something Claude should have known about this codebase
* You type the same correction or clarification into chat that you typed last session
* A new teammate would need the same context to be productive
Keep it to facts Claude should hold in every session: build commands, conventions, project layout, "always do X" rules. If an entry is a multi-step procedure or only matters for one part of the codebase, move it to a [skill](/docs/en/skills) or a [path-scoped rule](#organize-rules-with-claude/rules/) instead. The [extension overview](/docs/en/features-overview#build-your-setup-over-time) covers when to use each mechanism.
### Choose where to put CLAUDE.md files
CLAUDE.md files can live in several locations, each with a different scope. The table below lists them in load order, from broadest scope to most specific, so a project instruction appears in context after a user instruction.
| Scope | Location | Purpose | Use case examples | Shared with |
| ------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------- | -------------------------------------------------------------------- | ------------------------------- |
| **Managed policy** | • macOS: `/Library/Application Support/ClaudeCode/CLAUDE.md`<br />• Linux and WSL: `/etc/claude-code/CLAUDE.md`<br />• Windows: `C:\Program Files\ClaudeCode\CLAUDE.md` | Organization-wide instructions managed by IT/DevOps | Company coding standards, security policies, compliance requirements | All users in organization |
| **User instructions** | `~/.claude/CLAUDE.md` | Personal preferences for all projects | Code styling preferences, personal tooling shortcuts | Just you (all projects) |
| **Project instructions** | `./CLAUDE.md` or `./.claude/CLAUDE.md` | Team-shared instructions for the project | Project architecture, coding standards, common workflows | Team members via source control |
| **Local instructions** | `./CLAUDE.local.md` | Personal project-specific preferences; add to `.gitignore` | Your sandbox URLs, preferred test data | Just you (current project) |
CLAUDE.md and CLAUDE.local.md files in the directory hierarchy above the working directory are loaded in full at launch. Files in subdirectories load on demand when Claude reads files in those directories. See [How CLAUDE.md files load](#how-claude-md-files-load) for the full resolution order.
For large projects, you can break instructions into topic-specific files using [project rules](#organize-rules-with-claude/rules/). Rules let you scope instructions to specific file types or subdirectories.
### Set up a project CLAUDE.md
A project CLAUDE.md can be stored in either `./CLAUDE.md` or `./.claude/CLAUDE.md`. Create this file and add instructions that apply to anyone working on the project: build and test commands, coding standards, architectural decisions, naming conventions, and common workflows. These instructions are shared with your team through version control, so focus on project-level standards rather than personal preferences. To confirm the file loaded, run `/context` in a session and check the list under **Memory files**.
<Tip>
Run `/init` to generate a starting CLAUDE.md automatically. Claude analyzes your codebase and creates a file with build commands, test instructions, and project conventions it discovers. If a CLAUDE.md already exists, `/init` suggests improvements rather than overwriting it. Refine from there with instructions Claude wouldn't discover on its own.
Set `CLAUDE_CODE_NEW_INIT=1` to enable an interactive multi-phase flow. `/init` asks which artifacts to set up: CLAUDE.md files, skills, and hooks. It then explores your codebase with a subagent, fills in gaps via follow-up questions, and presents a reviewable proposal before writing any files.
</Tip>
### Write effective instructions
CLAUDE.md files are loaded into the context window at the start of every session, consuming tokens alongside your conversation. The [context window visualization](/docs/en/context-window) shows where CLAUDE.md loads relative to the rest of the startup context. Because they're context rather than enforced configuration, how you write instructions affects how reliably Claude follows them. Specific, concise, well-structured instructions work best.
**Size**: target under 200 lines per CLAUDE.md file. Longer files consume more context and reduce adherence. If your instructions are growing large, use [path-scoped rules](#path-specific-rules) so instructions load only when Claude works with matching files. You can also split content into [imports](#import-additional-files) for organization, though imported files still load and enter the context window at launch.
**Structure**: use markdown headers and bullets to group related instructions. Claude scans structure the same way readers do: organized sections are easier to follow than dense paragraphs.
**Specificity**: write instructions that are concrete enough to verify. For example:
* "Use 2-space indentation" instead of "Format code properly"
* "Run `npm test` before committing" instead of "Test your changes"
* "API handlers live in `src/api/handlers/`" instead of "Keep files organized"
**Consistency**: if two rules contradict each other, Claude may pick one arbitrarily. Review your CLAUDE.md files, nested CLAUDE.md files in subdirectories, and [`.claude/rules/`](#organize-rules-with-claude/rules/) periodically to remove outdated or conflicting instructions. In monorepos, use [`claudeMdExcludes`](#exclude-specific-claude-md-files) to skip CLAUDE.md files from other teams that aren't relevant to your work.
### Import additional files
CLAUDE.md files can import additional files using `@path/to/import` syntax. Imported files are expanded and loaded into context at launch alongside the CLAUDE.md that references them.
Both relative and absolute paths are allowed. Relative paths resolve relative to the file containing the import, not the working directory. Imported files can recursively import other files, with a maximum depth of four hops.
Import parsing skips Markdown code spans and fenced code blocks. To mention a path in your CLAUDE.md without importing it, wrap it in backticks: writing `` `@README` `` keeps the text literal, while `@README` outside backticks imports the file.
To pull in a README, package.json, and a workflow guide, reference them with `@` syntax anywhere in your CLAUDE.md:
```text theme={null}
See @README for project overview and @package.json for available npm commands for this project.
# Additional Instructions
- git workflow @docs/git-instructions.md
```
For private per-project preferences that shouldn't be checked into version control, create a `CLAUDE.local.md` at the project root. It loads alongside `CLAUDE.md` and is treated the same way. Add `CLAUDE.local.md` to your `.gitignore` so it isn't committed. With `CLAUDE_CODE_NEW_INIT=1` set, running `/init` and choosing the personal option does this for you.
If you work across multiple git worktrees of the same repository, a gitignored `CLAUDE.local.md` only exists in the worktree where you created it. To share personal instructions across worktrees, import a file from your home directory instead:
```text theme={null}
# Individual Preferences
- @~/.claude/my-project-instructions.md
```
<Warning>
An import in a project-level memory file is external when its path resolves outside your working directory, like the home directory import above. The first time Claude Code encounters external imports in a project, it shows an approval dialog listing the files. If you decline, the imports stay disabled and the dialog doesn't appear again.
The dialog protects you from files other people commit to a shared project. Imports in user-scope memory files, such as `~/.claude/CLAUDE.md` and `~/.claude/rules/`, are files you wrote yourself, so they load without the dialog and carry the same trust as the rest of your personal configuration.
</Warning>
For a more structured approach to organizing instructions, see [`.claude/rules/`](#organize-rules-with-claude/rules/).
### AGENTS.md
Claude Code reads `CLAUDE.md`, not `AGENTS.md`. If your repository already uses `AGENTS.md` for other coding agents, create a `CLAUDE.md` that imports it so both tools read the same instructions without duplicating them. You can also add Claude-specific instructions below the import. Claude loads the imported file at session start, then appends the rest:
```markdown CLAUDE.md theme={null}
@AGENTS.md
## Claude Code
Use plan mode for changes under `src/billing/`.
```
A symlink also works if you don't need to add Claude-specific content:
```bash theme={null}
ln -s AGENTS.md CLAUDE.md
```
The command prints no output on success. In your next session, run `/context` and confirm `CLAUDE.md` appears under **Memory files**.
On Windows, creating a symlink requires Administrator privileges or Developer Mode, so use the `@AGENTS.md` import instead.
Running [`/init`](/docs/en/commands) reads Cursor rules, in `.cursor/rules/` or `.cursorrules`, and Copilot rules, in `.github/copilot-instructions.md`, and incorporates the relevant parts into the generated `CLAUDE.md`. With `CLAUDE_CODE_NEW_INIT=1` set, `/init` also reads `AGENTS.md`, `.devin/rules/`, `.windsurf/rules/` or `.windsurfrules`, and `.clinerules`.
You can also run [`/import`](/docs/en/commands) to bring a supported coding agent's configuration into Claude Code, which appends a one-time copy of instruction files such as `AGENTS.md` to the matching `CLAUDE.md` and carries over MCP servers, commands, subagents, and skills. Requires Claude Code v2.1.213 or later.
### How CLAUDE.md files load
Claude Code reads CLAUDE.md files by walking up the directory tree from your current working directory, checking each directory along the way for `CLAUDE.md` and `CLAUDE.local.md` files. This means if you run Claude Code in `foo/bar/`, it loads instructions from `foo/bar/CLAUDE.md`, `foo/CLAUDE.md`, and any `CLAUDE.local.md` files alongside them.
All discovered files are concatenated into context rather than overriding each other. Across the directory tree, content is ordered from the filesystem root down to your working directory. For the `foo/bar/` example, `foo/CLAUDE.md` appears in context before `foo/bar/CLAUDE.md`, so instructions closer to where you launched Claude are read last. Within each directory, `CLAUDE.local.md` is appended after `CLAUDE.md`, so your personal notes are the last thing Claude reads at that level.
Claude also discovers `CLAUDE.md` and `CLAUDE.local.md` files in subdirectories under your current working directory. Instead of loading them at launch, they are included when Claude reads files in those subdirectories.
If you work in a large monorepo where other teams' CLAUDE.md files get picked up, use [`claudeMdExcludes`](#exclude-specific-claude-md-files) to skip them. For the full layout of root and per-directory CLAUDE.md files and rules, see [Monorepos and large repos](/docs/en/large-codebases).
Block-level HTML comments (`<!-- maintainer notes -->`) in CLAUDE.md files are stripped before the content is injected into Claude's context. Use them to leave notes for human maintainers without spending context tokens on them. Comments inside code blocks are preserved. When you open a CLAUDE.md file directly with the Read tool, comments remain visible.
#### Load from additional directories
The `--add-dir` flag gives Claude access to additional directories outside your main working directory. By default, CLAUDE.md files from these directories are not loaded.
To also load memory files from additional directories, set the `CLAUDE_CODE_ADDITIONAL_DIRECTORIES_CLAUDE_MD` environment variable:
```bash theme={null}
CLAUDE_CODE_ADDITIONAL_DIRECTORIES_CLAUDE_MD=1 claude --add-dir ../shared-config
```
This loads `CLAUDE.md`, `.claude/CLAUDE.md`, `.claude/rules/*.md`, and `CLAUDE.local.md` from the additional directory. `CLAUDE.local.md` is skipped if you exclude `local` from [`--setting-sources`](/docs/en/cli-reference).
### Organize rules with `.claude/rules/`
For larger projects, you can organize instructions into multiple files using the `.claude/rules/` directory. This keeps instructions modular and easier for teams to maintain. Rules can also be [scoped to specific file paths](#path-specific-rules), so they only load into context when Claude works with matching files, reducing noise and saving context space.
<Note>
Rules load into context every session or when matching files are opened. For task-specific instructions that don't need to be in context all the time, use [skills](/docs/en/skills) instead, which only load when you invoke them or when Claude determines they're relevant to your prompt.
</Note>
#### Set up rules
Place markdown files in your project's `.claude/rules/` directory. Each file should cover one topic, with a descriptive filename like `testing.md` or `api-design.md`. All `.md` files are discovered recursively, so you can organize rules into subdirectories like `frontend/` or `backend/`:
```text theme={null}
your-project/
├── .claude/
│ ├── CLAUDE.md # Main project instructions
│ └── rules/
│ ├── code-style.md # Code style guidelines
│ ├── testing.md # Testing conventions
│ └── security.md # Security requirements
```
Rules without [`paths` frontmatter](#path-specific-rules) are loaded at launch with the same priority as `.claude/CLAUDE.md`.
Project rules are skipped if you exclude `project` from [`--setting-sources`](/docs/en/cli-reference). Before v2.1.211, rules that load on demand, including path-scoped rules and rules in nested `.claude/rules/` directories, loaded even when `project` was excluded.
#### Path-specific rules
Rules can be scoped to specific files using YAML frontmatter with the `paths` field. These conditional rules only apply when Claude is working with files matching the specified patterns.
```markdown theme={null}
---
paths:
- "src/api/**/*.ts"
---
# API Development Rules
- All API endpoints must include input validation
- Use the standard error response format
- Include OpenAPI documentation comments
```
Rules without a `paths` field are loaded unconditionally and apply to all files. Path-scoped rules trigger when Claude reads files matching the pattern, not on every tool use. As of v2.1.198, matching also works when Claude reaches a file through a symlinked path to the project directory, for example in a symlinked checkout.
Use glob patterns in the `paths` field to match files by extension, directory, or any combination:
| Pattern | Matches |
| ---------------------- | ---------------------------------------- |
| `**/*.ts` | All TypeScript files in any directory |
| `src/**/*` | All files under `src/` directory |
| `*.md` | Markdown files in the project root |
| `src/components/*.tsx` | React components in a specific directory |
You can specify multiple patterns and use brace expansion to match multiple extensions in one pattern:
```markdown theme={null}
---
paths:
- "src/**/*.{ts,tsx}"
- "lib/**/*.ts"
- "tests/**/*.test.ts"
---
```
Each brace group multiplies the number of expanded patterns: `src/*.{ts,tsx}` expands to two patterns, and `{a,b}/{c,d}/*.{ts,tsx}` to eight. To keep expansion bounded, a rule's whole `paths` list shares one budget of 1,000 expanded patterns and 4 MiB, and patterns without braces don't count against it.
Claude Code uses any pattern that would exceed the budget unexpanded, and its literal braces match no files. Before v2.1.217, a `paths` value with many brace groups stalled or crashed the CLI at startup.
Glob syntax treats `[` as the start of a bracket expression such as `[abc]`. A pattern with a `[` that can't be read as a bracket expression, such as `photos [2024/**`, is invalid: it matches nothing, and the rule's other patterns keep working. To match a literal `[` in a file name, escape it as `photos \[2024/**`. Before v2.1.207, one invalid pattern made the Read tool fail for every file the rule was evaluated against, instead of matching nothing.
#### Share rules across projects with symlinks
The `.claude/rules/` directory supports symlinks, so you can maintain a shared set of rules and link them into multiple projects. Symlinks are resolved and loaded normally, and circular symlinks are detected and handled gracefully.
This example links both a shared directory and an individual file:
```bash theme={null}
ln -s ~/shared-claude-rules .claude/rules/shared
ln -s ~/company-standards/security.md .claude/rules/security.md
```
#### User-level rules
Personal rules in `~/.claude/rules/` apply to every project on your machine. Use them for preferences that aren't project-specific:
```text theme={null}
~/.claude/rules/
├── preferences.md # Your personal coding preferences
└── workflows.md # Your preferred workflows
```
User-level rules are loaded before project rules, giving project rules higher priority.
### Manage CLAUDE.md for large teams
For organizations deploying Claude Code across teams, you can centralize instructions and control which CLAUDE.md files are loaded.
#### Deploy organization-wide CLAUDE.md
Organizations can deploy a centrally managed CLAUDE.md that applies to all users on a machine. This file cannot be excluded by individual settings.
<Steps>
<Step title="Create the file at the managed policy location">
* macOS: `/Library/Application Support/ClaudeCode/CLAUDE.md`
* Linux and WSL: `/etc/claude-code/CLAUDE.md`
* Windows: `C:\Program Files\ClaudeCode\CLAUDE.md`
</Step>
<Step title="Deploy with your configuration management system">
Use MDM, Group Policy, Ansible, or similar tools to distribute the file across developer machines. See [managed settings](/docs/en/permissions#managed-settings) for other organization-wide configuration options.
</Step>
</Steps>
The `claudeMd` key lets you put managed CLAUDE.md content directly inside `managed-settings.json` instead of deploying a separate file.
**Scope**: every Claude Code session on the machine, in every repository. For repository-specific guidance, commit a project CLAUDE.md instead.
**Precedence**: same as a managed CLAUDE.md file. Loads before user and project CLAUDE.md.
**Where it's honored**: managed and policy settings only. Setting `claudeMd` in user, project, or local settings has no effect.
The example below adds behavioral instructions directly in a managed settings file:
```json theme={null}
{
"claudeMd": "Always run `make lint` before committing.\nNever push directly to main."
}
Cut at 300 lines. The page has the rest.
microsoft-foundry First recorded · 233 lines, first recorded
# Claude Code on Microsoft Foundry ## Prerequisites ## Setup ### 1. Provision Microsoft Foundry resource ### 2. Configure Azure credentials ### 3. Configure Claude Code ### 4. Pin model versions ### 5. Run Claude Code ## Azure RBAC configuration ## Troubleshooting ## Additional resources
The first capture of this source. The page was already there, and this is what it said.
# Claude Code on Microsoft Foundry
> Learn about configuring Claude Code through Microsoft Foundry, including setup, configuration, and troubleshooting.
export const ContactSalesCard = ({surface}) => {
const utm = content => `utm_source=claude_code&utm_medium=docs&utm_content=${surface}_${content}`;
const iconArrowRight = (size = 13) => <svg width={size} height={size} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
<line x1="5" y1="12" x2="19" y2="12" />
<polyline points="12 5 19 12 12 19" />
</svg>;
const STYLES = `
.cc-cs {
--cs-slate: #141413;
--cs-clay: #d97757;
--cs-clay-deep: #c6613f;
--cs-gray-000: #ffffff;
--cs-gray-700: #3d3d3a;
--cs-border-default: rgba(31, 30, 29, 0.15);
font-family: inherit;
}
.dark .cc-cs {
--cs-slate: #f0eee6;
--cs-gray-000: #262624;
--cs-gray-700: #bfbdb4;
--cs-border-default: rgba(240, 238, 230, 0.14);
}
.cc-cs-card {
display: flex; align-items: center; justify-content: space-between;
gap: 16px; padding: 14px 16px; margin: 0;
background: var(--cs-gray-000); border: 0.5px solid var(--cs-border-default);
border-radius: 8px; flex-wrap: wrap;
}
.cc-cs-text { font-size: 13px; color: var(--cs-gray-700); line-height: 1.5; flex: 1; min-width: 240px; }
.cc-cs-text strong { font-weight: 550; color: var(--cs-slate); }
.cc-cs-actions { display: flex; align-items: center; gap: 8px; flex-shrink: 0; }
.cc-cs-btn-clay {
display: inline-flex; align-items: center; gap: 8px;
background: var(--cs-clay-deep); color: #fff; border: none;
border-radius: 8px; padding: 8px 14px;
font-size: 13px; font-weight: 500;
transition: background-color 0.15s; white-space: nowrap;
}
.cc-cs-btn-clay:hover { background: var(--cs-clay); }
.cc-cs-btn-ghost {
display: inline-flex; align-items: center; gap: 8px;
background: transparent; color: var(--cs-gray-700);
border: 0.5px solid var(--cs-border-default);
border-radius: 8px; padding: 8px 14px;
font-size: 13px; font-weight: 500;
}
.cc-cs-btn-ghost:hover { background: rgba(0, 0, 0, 0.04); }
.dark .cc-cs-btn-ghost:hover { background: rgba(255, 255, 255, 0.04); }
@media (max-width: 720px) {
.cc-cs-actions { width: 100%; }
}
`;
return <div className="cc-cs not-prose">
<style>{STYLES}</style>
<div className="cc-cs-card">
<div className="cc-cs-text">
<strong>Deploying Claude Code across your organization?</strong> Talk to sales about enterprise plans, SSO, and centralized billing.
</div>
<div className="cc-cs-actions">
<a href={`https://claude.com/pricing?${utm('view_plans')}#plans-business`} className="cc-cs-btn-ghost">
View plans
</a>
<a href={`https://claude.com/contact-sales?${utm('contact_sales')}`} className="cc-cs-btn-clay">
Contact sales {iconArrowRight()}
</a>
</div>
</div>
</div>;
};
<ContactSalesCard surface="foundry" />
## Prerequisites
Before configuring Claude Code with Microsoft Foundry, ensure you have:
* An Azure subscription with access to Microsoft Foundry
* RBAC permissions to create Microsoft Foundry resources and deployments
* Azure CLI installed and configured (optional - only needed if you don't have another mechanism for getting credentials)
<Note>
If you are deploying Claude Code to multiple users, [pin your model versions](#4-pin-model-versions) before rolling out.
</Note>
## Setup
### 1. Provision Microsoft Foundry resource
First, create a Claude resource in Azure:
1. Navigate to the [Microsoft Foundry portal](https://ai.azure.com/)
2. Create a new resource, noting your resource name
3. Create deployments for the Claude models, noting the deployment name you give each; you'll set these names as the model variables in step 4:
* Claude Opus
* Claude Sonnet
* Claude Haiku
When you configure a deployment, you also choose its [hosting option](https://platform.claude.com/docs/en/build-with-claude/claude-in-microsoft-foundry#hosting-options), which determines whether inference runs on Azure or on Anthropic infrastructure.
### 2. Configure Azure credentials
Claude Code supports three authentication methods for Microsoft Foundry. Choose the method that best fits your security requirements.
**Option A: API key authentication**
1. Navigate to your resource in the Microsoft Foundry portal
2. Go to the **Endpoints and keys** section
3. Copy **API Key**
4. Set the environment variable, replacing `your-azure-api-key` with the key you copied:
```bash theme={null}
export ANTHROPIC_FOUNDRY_API_KEY=your-azure-api-key
```
**Option B: Microsoft Entra ID authentication**
When neither `ANTHROPIC_FOUNDRY_API_KEY` nor `ANTHROPIC_FOUNDRY_AUTH_TOKEN` is set, Claude Code automatically uses the Azure SDK [default credential chain](https://learn.microsoft.com/en-us/azure/developer/javascript/sdk/authentication/credential-chains#defaultazurecredential-overview).
This supports a variety of methods for authenticating local and remote workloads.
On local environments, you commonly may use the Azure CLI:
```bash theme={null}
az login
```
**Option C: Bearer token authentication**
Claude Code sends the value of `ANTHROPIC_FOUNDRY_AUTH_TOKEN` on every request as the `Authorization: Bearer` header. Use this option when another process, such as a host application or a sign-in script, has already obtained an access token for you. Requires Claude Code v2.1.203 or later.
Set the variable to a bearer token that Microsoft Entra ID issued for your resource:
```bash theme={null}
export ANTHROPIC_FOUNDRY_AUTH_TOKEN=your-entra-access-token
```
`ANTHROPIC_FOUNDRY_AUTH_TOKEN` takes precedence over `ANTHROPIC_FOUNDRY_API_KEY` and over the default credential chain.
<Note>
When using Microsoft Foundry, the `/logout` command is unavailable since authentication is handled through Azure credentials.
</Note>
### 3. Configure Claude Code
Set the following environment variables to enable Microsoft Foundry:
```bash theme={null}
# Enable Microsoft Foundry integration
export CLAUDE_CODE_USE_FOUNDRY=1
# Azure resource name (replace {resource} with your resource name)
export ANTHROPIC_FOUNDRY_RESOURCE={resource}
# Or provide the full base URL:
# export ANTHROPIC_FOUNDRY_BASE_URL=https://{resource}.services.ai.azure.com/anthropic
```
### 4. Pin model versions
<Warning>
Pin specific model versions for every deployment. Without pinning, model aliases such as `sonnet` and `opus` resolve to Claude Code's built-in default for Microsoft Foundry, which can lag the newest release and may not yet be available in your account. Microsoft Foundry has no startup model check, so requests fail when the default is unavailable. When you create Azure deployments, select a specific model version rather than "auto-update to latest."
</Warning>
Set the model variables to match the deployment names you created in step 1.
Without `ANTHROPIC_DEFAULT_OPUS_MODEL`, the `opus` alias on Microsoft Foundry resolves to Opus 4.6. Set it to the ID of a newer Opus model, such as Opus 4.8:
```bash theme={null}
export ANTHROPIC_DEFAULT_OPUS_MODEL='claude-opus-4-8'
export ANTHROPIC_DEFAULT_SONNET_MODEL='claude-sonnet-5'
export ANTHROPIC_DEFAULT_HAIKU_MODEL='claude-haiku-4-5'
```
Background tasks such as session title generation use the small/fast model, normally a Haiku-class model. On Microsoft Foundry, Claude Code defaults this to the primary model because not every account has a Haiku deployment. To use Haiku for background tasks, set `ANTHROPIC_DEFAULT_HAIKU_MODEL` to a Haiku deployment that is available in your account, as shown above.
For current and legacy model IDs, see [Models overview](https://platform.claude.com/docs/en/about-claude/models/overview). See [Model configuration](/docs/en/model-config#pin-models-for-third-party-deployments) for the full list of environment variables.
[Prompt caching](/docs/en/prompt-caching) is enabled automatically. To request a 1-hour cache TTL instead of the 5-minute default, set the following variable; cache writes with a 1-hour TTL are billed at a higher rate:
```bash theme={null}
export ENABLE_PROMPT_CACHING_1H=1
```
### 5. Run Claude Code
With the environment variables set, start Claude Code from your project directory:
```bash theme={null}
claude
```
Claude Code reads `CLAUDE_CODE_USE_FOUNDRY` and the other Microsoft Foundry variables from the environment and connects to your Azure resource on the first prompt. Unlike Amazon Bedrock and Google Cloud's Agent Platform, Microsoft Foundry has no interactive setup wizard, so the environment variables in steps 3 and 4 are the only configuration path.
To verify your setup, run `/status` inside Claude Code. The API provider line shows `Microsoft Foundry`, along with the resource name or base URL you configured.
## Azure RBAC configuration
The `Azure AI User` and `Cognitive Services User` default roles include all required permissions for invoking Claude models.
For more restrictive permissions, create a custom role with the following:
```json theme={null}
{
"permissions": [
{
"dataActions": [
"Microsoft.CognitiveServices/accounts/providers/*"
]
}
]
}
```
For details, see [Microsoft Foundry RBAC documentation](https://learn.microsoft.com/en-us/azure/ai-foundry/concepts/rbac-azure-ai-foundry).
## Troubleshooting
If you receive an error "Failed to get token from azureADTokenProvider: ChainedTokenCredential authentication failed":
* Configure Entra ID on the environment, or set `ANTHROPIC_FOUNDRY_API_KEY`.
If requests fail with repeated connection errors on the first prompt:
* Check that `ANTHROPIC_FOUNDRY_RESOURCE` is set to your actual resource name rather than a placeholder. Claude Code builds the endpoint URL from this value, so an incorrect name points at a host that doesn't exist.
## Additional resources
* [Microsoft Foundry documentation](https://learn.microsoft.com/en-us/azure/ai-foundry/what-is-azure-ai-foundry)
* [Microsoft Foundry models](https://ai.azure.com/explore/models)
* [Microsoft Foundry pricing](https://azure.microsoft.com/en-us/pricing/details/ai-foundry/)
mobile First recorded · 81 lines, first recorded
# Claude Code on mobile ## Get the app ## Work from your phone ### Start and monitor cloud sessions ### Continue a local session with Remote Control ### Get push notifications ## Limitations ## Related resources
The first capture of this source. The page was already there, and this is what it said.
# Claude Code on mobile
> Start, monitor, and steer Claude Code tasks from your phone with the Claude app for iOS and Android.
The Claude app for [iOS](https://apps.apple.com/us/app/claude-by-anthropic/id6473753684) and [Android](https://play.google.com/store/apps/details?id=com.anthropic.claude) is a client for Claude Code sessions rather than a place where code runs. From your phone you reach [cloud sessions](#start-and-monitor-cloud-sessions) in the cloud, a session running on your own machine through [Remote Control](#continue-a-local-session-with-remote-control), or the Desktop app through [Dispatch](/docs/en/desktop#sessions-from-dispatch).
<Note>
Claude Code doesn't have a separate mobile app: cloud sessions and Remote Control both live in the **Code** tab in the Claude app, and Dispatch is a task you message in the app.
</Note>
## Get the app
<Steps>
<Step title="Download the Claude app">
Install the Claude app for [iOS](https://apps.apple.com/us/app/claude-by-anthropic/id6473753684) or [Android](https://play.google.com/store/apps/details?id=com.anthropic.claude). On an iPad, install the same iOS app.
<Tip>
Run `/mobile` in a Claude Code session to display a download QR code you can scan. `/ios` and `/android` do the same thing.
</Tip>
</Step>
<Step title="Sign in">
Sign in with the same claude.ai account and organization you use for Claude Code. Cloud sessions and Remote Control require a claude.ai account, so they aren't reachable with an Anthropic Console API key or from a third-party provider such as Amazon Bedrock.
</Step>
<Step title="Open the Code tab">
Tap **Code** in the app's navigation to reach your sessions, or open [claude.ai/code/new](https://claude.ai/code/new) on your phone to start a new Code session in the app. If you don't see the Code tab, your plan or organization may not include these features; see [availability by subscription plan](/docs/en/feature-availability#availability-by-subscription-plan).
</Step>
</Steps>
## Work from your phone
From the app you can start cloud sessions, drive a Claude Code session running on your computer, or message Dispatch a task. The app is the same for all three; they differ in where the work happens.
| Feature | What you connect to | When to use |
| :--------------------------------------------------- | :-------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------- |
| [Claude Code on the web](/docs/en/claude-code-on-the-web) | A cloud session on cloud infrastructure, Anthropic-managed by default | Your repository is on GitHub and the task should keep running after you put your phone away. See the [web quickstart](/docs/en/web-quickstart) to set up. |
| [Remote Control](/docs/en/remote-control) | A Claude Code session running on your computer | The work needs your local filesystem, tools, or MCP servers. |
| [Dispatch](/docs/en/desktop#sessions-from-dispatch) | The Desktop app on your computer | You want to message a task and let Dispatch decide how to run it. Requires a Pro or Max plan. |
If your computer will be off, use cloud sessions, which run in the cloud and continue with your laptop closed. Remote Control and Dispatch drive your own machine, so it needs to stay on with Claude Code or the Desktop app running. If your machine sleeps during a Remote Control session, Claude Code reconnects when the machine comes back online.
For a fuller comparison, see [work when you are away from your terminal](/docs/en/platforms#work-when-you-are-away-from-your-terminal).
Cloud sessions and Remote Control run from the **Code** tab. For Dispatch, which you message as a task in the app, see [sessions from Dispatch](/docs/en/desktop#sessions-from-dispatch).
### Start and monitor cloud sessions
Claude Code on the web runs tasks on cloud infrastructure, Anthropic-managed by default, so a session continues after you put your phone away. From the Code tab, select a repository and branch, describe the task, and submit it. Sessions persist across devices: a task you start on your laptop is ready to review from your phone, and one you start from your phone is waiting when you're back at your desk.
Open a session in the app to check progress, answer Claude's questions, or steer it in a new direction. You can also tell Claude to [watch a pull request](/docs/en/claude-code-on-the-web#auto-fix-pull-requests) and fix CI failures or review comments as they arrive. To connect GitHub and set up your environment, follow the [web quickstart](/docs/en/web-quickstart), and see [Claude Code on the web](/docs/en/claude-code-on-the-web) for everything cloud sessions can do.
### Continue a local session with Remote Control
Remote Control connects the Claude app to a Claude Code session running on your machine, so code execution and filesystem access stay local while you drive the session from your phone. Start the session on your computer with `claude remote-control`, or run `/remote-control` in a session that's already open. Then scan the session QR code the terminal can display, or open the Claude app, tap **Code**, and pick the session from the list. See [connect from another device](/docs/en/remote-control#connect-from-another-device) for each option.
Attachments you add in the Claude app reach the local session too. Claude sees attached photos directly as part of your message. Claude Code downloads other files to your machine and passes them to Claude as `@` file references. For requirements, invocation modes, and troubleshooting, see the [Remote Control overview](/docs/en/remote-control).
### Get push notifications
When Remote Control is active, Claude can send push notifications to your phone, typically when a long-running task finishes or when it needs a decision from you. You can also ask for one in your prompt, such as `notify me when the tests finish`. See [mobile push notifications](/docs/en/remote-control#mobile-push-notifications) for the two `/config` toggles and delivery troubleshooting.
Dispatch sends its own notification when a Code session it spawned finishes or needs your approval, described in [sessions from Dispatch](/docs/en/desktop#sessions-from-dispatch).
## Limitations
The mobile client covers most of what a session needs, with a few limitations:
* **Local-only commands**: commands that only run in the terminal interface, such as `/plugin` and `/resume`, don't work from the app. The [Remote Control limitations](/docs/en/remote-control#limitations) list the commands that do work from mobile and how their behavior differs.
* **Permission modes**: cloud sessions offer Accept edits, Plan, and Auto in the mode dropdown, and Remote Control sessions offer Manual, Accept edits, and Plan. You can't select Bypass permissions from the app in either case, and you can't select Auto for a Remote Control session. See [switch permission modes](/docs/en/permission-modes#switch-permission-modes).
* **Dispatch plans**: Dispatch requires a Pro or Max plan and isn't available on Team or Enterprise.
## Related resources
* [Platforms and integrations](/docs/en/platforms): compare every surface Claude Code runs on
* [Claude Code on the web](/docs/en/claude-code-on-the-web): how cloud sessions run and how to move work to and from your terminal
* [Configure cloud environments](/docs/en/cloud-environments): network access levels, environment variables, and setup scripts for cloud sessions
* [Remote Control](/docs/en/remote-control): continue a local session from any device
* [Sessions from Dispatch](/docs/en/desktop#sessions-from-dispatch): how Dispatch tasks become Code sessions in the Desktop app
* [Channels](/docs/en/channels): ask Claude something from your phone via Telegram, Discord, or iMessage while the work runs on your machine
* [Claude Code in Slack](/docs/en/slack): delegate coding tasks from your Slack workspace by mentioning `@Claude`
model-config First recorded · 788 lines, first recorded
# Model configuration ## Available models ### Model aliases ### Work with Fable 5 #### Fable 5 and usage credits ### Setting your model ## Restrict model selection ### Surface coverage ### Default model behavior ### Enforce the allowlist for the Default model ### Control the model users run on ### Merge behavior ### Mantle model IDs ### Organization model restrictions ## Organization default model ## Organization effort limits ## Special model behavior ### `default` model setting ### `opusplan` model setting ### Fallback model chains ### Automatic model fallback #### Check what triggered fallback #### Ask before switching #### Enable fallback on Bedrock, Agent Platform, and Foundry #### Security research and biology workloads ### Adjust effort level #### Choose an effort level #### Use ultrathink for one-off deep reasoning #### Set the effort level #### Adaptive reasoning and fixed thinking budgets ### Extended thinking ### Extended context #### Sonnet 5 context window ## Context window and auto-compaction ### Set the auto-compact window ### Default auto-compact thresholds ### Correct the window for a gateway or custom model ID ## Checking your current model ## Add a custom model option ## Environment variables ### Pin models for third-party deployments ### Customize pinned model display and capabilities ### Override model IDs per version ### Prompt caching configuration
The first capture of this source. The page was already there, and this is what it said.
# Model configuration
> Configure which model Claude Code uses, effort levels, extended context, and the auto-compact window
## Available models
For the `model` setting in Claude Code, you can configure either:
* A **model alias**
* A **model name**
* Anthropic API: a full **[model name](https://platform.claude.com/docs/en/about-claude/models/overview)**
* Amazon Bedrock: an inference profile ARN
* Microsoft Foundry: a deployment name
* Google Cloud's Agent Platform: a version name
For guidance on which model and effort level fit different kinds of work, see [Choosing a Claude model and effort level in Claude Code](https://claude.com/blog/claude-model-and-effort-level-in-claude-code) on the blog.
<Note>
`ANTHROPIC_BASE_URL` changes where requests are sent, not which model answers them. To route Claude through an LLM gateway, see [LLM gateways](/docs/en/llm-gateway).
</Note>
### Model aliases
Use a model alias to select model settings without remembering exact version numbers:
| Model alias | Behavior |
| ---------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **`default`** | Special value that clears any model override and reverts to the recommended model for your account type, or to the [organization default model](#organization-default-model) when an admin has set one. Not itself a model alias |
| **`best`** | Uses Fable 5 where your organization has access to it, otherwise the latest Opus model |
| **`fable`** | Uses Claude Fable 5 for your hardest and longest-running tasks |
| **`sonnet`** | Uses the latest Sonnet model for daily coding tasks |
| **`opus`** | Uses the latest Opus model for complex reasoning tasks |
| **`haiku`** | Uses the fast and efficient Haiku model for simple tasks |
| **`sonnet[1m]`** | Uses Sonnet with a [1 million token context window](https://platform.claude.com/docs/en/build-with-claude/context-windows#context-window-sizes-by-model) for long sessions. No effect when `sonnet` already resolves to Sonnet 5 with its native 1M window; behind an [LLM gateway](/docs/en/llm-gateway), selects the 1M window for Sonnet 5 |
| **`opus[1m]`** | Uses Opus with a [1 million token context window](https://platform.claude.com/docs/en/build-with-claude/context-windows#context-window-sizes-by-model) for long sessions |
| **`opusplan`** | Special mode that uses `opus` during plan mode, then switches to `sonnet` for execution |
The version that the `opus` and `sonnet` aliases resolve to depends on the provider:
| Provider | `opus` | `sonnet` |
| :--------------------------------------------------- | :------- | :--------- |
| Anthropic API | Opus 5 | Sonnet 5 |
| [Claude Platform on AWS](/docs/en/claude-platform-on-aws) | Opus 5 | Sonnet 4.6 |
| Amazon Bedrock, Google Cloud's Agent Platform | Opus 5 | Sonnet 4.5 |
| Microsoft Foundry | Opus 4.6 | Sonnet 4.5 |
Where an alias resolves to an older model, newer models are available by selecting the full model name explicitly or setting `ANTHROPIC_DEFAULT_OPUS_MODEL` or `ANTHROPIC_DEFAULT_SONNET_MODEL`.
Before v2.1.219, `opus` resolved to Opus 4.8 on the Anthropic API from v2.1.154, and on Claude Platform on AWS, Amazon Bedrock, and Google Cloud's Agent Platform from v2.1.207. Before v2.1.207, `opus` resolved to Opus 4.7 on Claude Platform on AWS and to Opus 4.6 on Amazon Bedrock and Google Cloud's Agent Platform.
Aliases point to the recommended version for your provider and update over time. To pin to a specific version, use the full model name, for example `claude-opus-5`, or set the corresponding environment variable like `ANTHROPIC_DEFAULT_OPUS_MODEL`.
<Note>
Opus 5 requires Claude Code v2.1.219 or later. Sonnet 5 requires v2.1.197 or later. Opus 4.8 requires v2.1.154 or later. Run `claude update` to upgrade.
</Note>
### Work with Fable 5
[Claude Fable 5](https://platform.claude.com/docs/en/about-claude/models/introducing-claude-fable-5-and-claude-mythos-5) is the most capable model in Claude Code, suited to tasks larger than a single sitting. It sustains long autonomous sessions, investigates before acting, and verifies its work more often than smaller models.
Fable 5 is not the default model. Select it with `/model fable`. Requests that its safety classifiers flag, most often in cybersecurity and biology domains, trigger [automatic model fallback](#automatic-model-fallback).
To get the most from Fable 5:
* **Describe the outcome, not the steps**: hand it the result you want and let it plan the path. To keep it working until that outcome holds, [set a goal](/docs/en/goal).
* **Hand it ambiguous problems**: root-cause investigations, outage debugging, and architecture decisions are where the extra investigation and verification pay off.
* **Skip the verification reminders**: it verifies its own work with less prompting, so reminders to test or check are usually unnecessary.
* **Size up larger tasks**: give it work you would normally break into pieces. It holds long sessions without losing the thread.
<Note>
Fable 5 requires Claude Code v2.1.170 or later. Older versions do not show Fable 5 in the model picker and cannot select it. Run `claude update` to upgrade. Fable 5 is not available under [zero data retention](/docs/en/zero-data-retention), where the `/model` picker either omits it or shows it disabled.
</Note>
On the Anthropic API, the `/model` picker lists Fable 5 only after the server reports it available for your organization. When you type `/model fable`, Claude Code checks availability with the server directly, so the selection can succeed before the picker lists the entry.
#### Fable 5 and usage credits
Depending on your plan and seat tier, Fable 5 usage can bill to [usage credits](https://support.claude.com/en/articles/12429409-extra-usage-for-paid-claude-plans) instead of drawing on your plan's included limits. When it does, the `/model` picker shows "Requires usage credits" on the Fable 5 row. To manage usage credits, see [Add usage credits to your subscription](/docs/en/costs#add-usage-credits-to-your-subscription).
In interactive sessions, Claude Code shows a consent prompt before a Fable 5 request bills usage credits. Members of Enterprise plans with organization billing don't see the prompt. You can continue on Fable 5 using usage credits or switch to your default model. You can also dismiss the prompt:
* In the `/model` picker, you keep your current model.
* Mid-session, Claude Code continues the turn on your default model.
After you choose to continue on Fable 5 using usage credits, Claude Code doesn't show the prompt again.
In [non-interactive mode](/docs/en/headless) with the `-p` flag and through the Agent SDK, Claude Code never shows the consent prompt. When a Fable 5 request there would bill to usage credits, Claude Code bills it without asking.
### Setting your model
You can configure your model in several ways, listed in order of priority:
1. **During session**: use `/model <alias|name>` to switch immediately, or run `/model` with no argument to open the picker. The picker asks for confirmation when the conversation has prior output, since the next response re-reads the full history without cached context
2. **At startup**: launch with `claude --model <alias|name>`
3. **Environment variable**: set `ANTHROPIC_MODEL=<alias|name>`
4. **Settings**: configure permanently in your settings file using the `model` field
As of v2.1.153, `/model` saves your choice as the default for new sessions by writing the `model` field in your user settings. In the picker:
* `Enter`: switch model and save as your default
* `s`: switch model for this session only
Typing `/model <name>` directly behaves like `Enter`. A model set with `/model` in [non-interactive mode](/docs/en/headless), with the `-p` flag, applies to the current session only and isn't saved as your default. Project and managed settings still take precedence and reapply on the next launch. An [organization default model](#organization-default-model) that your admin has configured to override user selection also reapplies on the next launch.
In v2.1.144 through v2.1.152, `/model` applied to the current session only and `d` in the picker saved a default.
The `--model` flag and `ANTHROPIC_MODEL` environment variable apply only to the session you launch with them. To run different models in different terminals at the same time, launch each one with its own `--model` flag rather than switching with `/model`.
Prices in the `/model` picker appear when Claude Code talks to the Anthropic API, directly or through an [LLM gateway](/docs/en/llm-gateway) that proxies it, and the price on a row is the price of the model that row selects. On [third-party providers](/docs/en/third-party-integrations) such as Amazon Bedrock and on the [Claude apps gateway](/docs/en/claude-apps-gateway), your provider or gateway determines what you pay, so picker rows show no price. The price is a display label only; it doesn't affect which model a row selects or what your provider bills. Before v2.1.206, [Claude Platform on AWS](/docs/en/claude-platform-on-aws) and gateway sessions showed Anthropic list prices, and a row could show the price of a different model than the one it selected.
Resumed sessions started with `claude --resume`, `--continue`, or the `/resume` picker keep the model they were using when the transcript was saved, regardless of the current `model` setting. If the restored model has been retired or is excluded by [`availableModels`](#restrict-model-selection), the session falls through to the normal precedence order. This prevents another session's `/model` choice from changing the model on resume. On providers that use provider-specific deployment IDs rather than Anthropic model IDs, such as Amazon Bedrock, Google Cloud's Agent Platform, and Microsoft Foundry, the transcript model isn't restored at all and the session resolves its model through the normal precedence order.
A model you pick for the new launch with `--model` or `ANTHROPIC_MODEL` still takes precedence over the restored model. As of v2.1.195, so does an [`ANTHROPIC_DEFAULT_OPUS_MODEL`](#environment-variables) family variable.
When the active model at startup comes from project or managed settings rather than your own selection, the startup header shows which settings file set it. Run `/model` to override; the project or managed setting reapplies on the next launch. On platforms that embed Claude Code and set [`CLAUDE_CODE_PROVIDER_MANAGED_BY_HOST`](/docs/en/env-vars), the host's model configuration takes precedence over managed model settings, while a managed `availableModels` allowlist stays in force unless the host supplies its own; the key-level enumeration is under [Settings precedence](/docs/en/settings#settings-precedence).
When a model switch is requested through the [Agent SDK](/docs/en/agent-sdk/overview) `setModel()` method or by an app such as the [Desktop app](/docs/en/desktop) that runs the Claude Code CLI for you, Claude Code checks that the string is one it recognizes before saving it. This check requires Claude Code v2.1.200 or later. On the Anthropic API, Claude Code recognizes:
* a model alias
* an entry from the `/model` picker
* any name that starts with `claude-`
* a value you configured yourself as a [custom model option](#add-a-custom-model-option) or in [`modelOverrides`](#override-model-ids-per-version)
Claude Code rejects an unrecognized string with `Model "<name>" is not a recognized model id.` and the session keeps its current model, instead of saving the string and failing on the next request. See [the error reference](/docs/en/errors#model-is-not-a-recognized-model-id) for recovery steps.
The check runs only on the Anthropic API. On Amazon Bedrock, Google Cloud's Agent Platform, Microsoft Foundry, [Claude Platform on AWS](/docs/en/claude-platform-on-aws), and behind an [LLM gateway](/docs/en/llm-gateway) or a custom `ANTHROPIC_BASE_URL`, your provider or gateway defines the model names, so Claude Code passes any string through without checking it. The check also doesn't cover the `--model` flag, the `ANTHROPIC_MODEL` environment variable, or the `model` setting; a mistyped value there produces [There's an issue with the selected model](/docs/en/errors#theres-an-issue-with-the-selected-model) on the first request instead.
When the requested model has a scheduled retirement date or is automatically remapped to a newer version, Claude Code shows a warning that names the requested model. Interactive sessions show it as a startup notice. From v2.1.182, the same warning is written to stderr in [non-interactive mode](/docs/en/headless) when using the default text output format. The check also covers a `model` set in [subagent frontmatter](/docs/en/sub-agents). The stderr warning is suppressed for `--output-format json` and `stream-json`; read the actual model from the `modelUsage` field of the [result message](/docs/en/headless#get-structured-output) instead.
For example, start a session on Opus:
```bash theme={null}
claude --model opus
```
Then switch models from within the session:
```text theme={null}
/model sonnet
```
Example settings file:
```json theme={null}
{
"permissions": {
"allow": ["Bash(npm run lint)"]
},
"model": "opus"
}
```
## Restrict model selection
Enterprise administrators can use `availableModels` in [managed or policy settings](/docs/en/settings#settings-files) to restrict which models users can select. Entries match a model family such as `sonnet`, a version prefix such as `claude-sonnet-4-5`, or a full model ID such as `claude-sonnet-4-5-20250929`.
On platforms that embed Claude Code and set [`CLAUDE_CODE_PROVIDER_MANAGED_BY_HOST`](/docs/en/env-vars), the host's model configuration takes precedence over managed model settings, while a managed `availableModels` allowlist stays in force unless the host supplies its own; the key-level enumeration is under [Settings precedence](/docs/en/settings#settings-precedence).
When `availableModels` is set, the allowlist applies everywhere a user can specify a model:
* **Main session model**: `/model`, the `--model` flag, the `ANTHROPIC_MODEL` environment variable, the `model` setting, and the model restored when [resuming a session](#setting-your-model)
* **Alias resolution**: the `ANTHROPIC_DEFAULT_OPUS_MODEL`, `ANTHROPIC_DEFAULT_SONNET_MODEL`, `ANTHROPIC_DEFAULT_HAIKU_MODEL`, and `ANTHROPIC_DEFAULT_FABLE_MODEL` environment variables cannot redirect an allowed alias to a model outside the list
* **Fast mode**: `/fast` refuses to toggle when it would implicitly switch to an Opus model outside the list, with the message "is not in your organization's allowed models"
* **Subagent and teammate models**: the `model` field in [subagent](/docs/en/sub-agents#choose-a-model) frontmatter, the Agent tool's `model` parameter, [agent team](/docs/en/agent-teams#specify-teammates-and-models) teammate models including the `teammateDefaultModel` setting, `CLAUDE_CODE_SUBAGENT_MODEL`, and, on v2.1.197 and earlier, the model picker in the `/agents` wizard 
* **Skill and command models**: the `model` frontmatter in [skills and commands](/docs/en/skills)
* **Advisor model**: the configured [`advisorModel`](/docs/en/advisor) setting and the `--advisor` flag
* **Background agent model**: the model selected in the [dispatch picker](/docs/en/agent-view)
On the Anthropic API and [Claude Platform on AWS](/docs/en/claude-platform-on-aws), a model family alias, `opus`, `sonnet`, `haiku`, or `fable`, resolves to the newest version of its family that the allowlist permits. When the allowlist pins specific versions, for example `["sonnet", "claude-opus-4-6"]`, both `/model opus` and `--model opus` select Claude Opus 4.6, the newest permitted Opus, and show a notice naming both the requested and substituted models. Before v2.1.205, an alias whose newest released version was outside the list was rejected or replaced like any other blocked selection, even when the list permitted an older version.
The substitution needs a permitted version to land on: when the allowlist permits no version of the alias's family, the alias follows the rejection and replacement behavior below like any other blocked value.
Claude Code handles any other blocked selection according to where the model was set:
* **`/model`**: Claude Code rejects the switch with an error
* **`--model` flag, `ANTHROPIC_MODEL`, or the `model` setting**: Claude Code replaces the value at startup with a warning naming both the requested and substituted models, and the session starts on the default model
* **Subagent or teammate override**: Claude Code falls back to the [subagent's inherited model](/docs/en/sub-agents#choose-a-model) or the [default teammate model](/docs/en/agent-teams#specify-teammates-and-models) rather than failing the request. In interactive sessions, Claude Code warns you when it substitutes a subagent's model, by this fallback or by the newest-permitted-version substitution above, naming the requested and substituted models; it doesn't report a teammate's fallback. When the blocked value is the **Default teammate model** setting itself, Claude Code runs the teammate on your provider's default Opus model, or on the lead's model when the allowlist blocks that too. Where the newest-permitted-version substitution above operates, a blocked family alias follows it instead; before v2.1.222, an alias fell back like any other blocked value on every provider
* **Skill or command override**: Claude Code ignores the override, including a blocked family alias, and the skill or command runs on the session model. A skill or command that [runs in a subagent](/docs/en/skills#run-skills-in-a-subagent) follows the subagent behavior above instead
* **`advisorModel` setting**: the advisor is disabled for the session
* **`--advisor` flag**: Claude Code exits with an error at launch
Claude Code hides excluded models from the `/model` picker. A full model ID in the list that has no built-in picker row, such as an older version that the list pins, appears in the `/model` picker as its own labeled row. Before v2.1.199, such an ID was selectable only by typing `/model <id>`.
Model changes that Claude Code makes on your behalf are checked the same way:
* **[Fallback model chains](#fallback-model-chains)**: entries outside the allowlist are dropped
* **Plan-mode upgrades**: on the Anthropic API and Claude Platform on AWS, an upgrade such as [`opusplan`](#opusplan-model-setting) to an excluded model uses the newest permitted version of the upgrade family. On providers with provider-specific model IDs, and when no version is permitted, the upgrade is skipped and planning continues on the session's model
* **[Automatic model fallback](#automatic-model-fallback)**: a fallback whose target is excluded does not run, so the flagged request ends with a refusal instead
* **[Auto mode classifier](/docs/en/permission-modes#eliminate-prompts-with-auto-mode)**: the classifier's Claude Sonnet 5 default applies only when the allowlist permits Sonnet 5. When it's excluded, the classifier runs on the session's model, which the allowlist already governs, or on an Opus model when the session runs on [Fable 5](#work-with-fable-5). On providers other than the Anthropic API, that Opus fallback runs on the provider's default Opus model without consulting the allowlist. Requires Claude Code v2.1.210 or later
* **[Fast mode](/docs/en/fast-mode)**: enabling fast mode is refused when the model the session would run on afterward is outside the allowlist
```json theme={null}
{
"availableModels": ["sonnet", "haiku"]
}
```
### Surface coverage
Every surface enforces the allowlist it receives. Which delivery mechanism reaches each surface differs:
| Delivery mechanism | CLI and IDE | Desktop local sessions | Web, mobile, and cloud sessions | Agent SDK and non-interactive | Cowork |
| :---------------------------------------------------------------------------- | :---------- | :--------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :---------------------------- | :---------------------- |
| [Server-managed settings](/docs/en/server-managed-settings) from the admin console | Enforced | Enforced | Enforced | Enforced | Not delivered |
| [MDM or managed settings files](/docs/en/settings#settings-files) | Enforced | Enforced | Not delivered in Anthropic-hosted environments; in [self-hosted environments](/docs/en/self-hosted-environments), enforced from the runner image when server-managed settings deliver no keys | Enforced | Enforced where deployed |
* Cloud sessions, on [Claude Code on the web](/docs/en/claude-code-on-the-web) or in the Desktop app, run on Anthropic-managed VMs by default: settings deployed to your device do not reach them, so deliver the allowlist through server-managed settings. Sessions your organization routes to a [self-hosted environment](/docs/en/self-hosted-environments) run on your own compute and fall back to the managed settings file in the runner image when your organization delivers no server-managed settings; when both exist, the server-managed payload takes precedence, with the per-key `env` merge exception described in [settings precedence](/docs/en/server-managed-settings#settings-precedence). A mid-session model switch in a cloud session is rejected when the requested model is excluded by the allowlist. Server-side rejection at session creation applies to [organization model restrictions](#organization-model-restrictions), not the `availableModels` settings key.
* Cowork, the agentic-work tab in the Claude Desktop app, is not a Claude Code surface and does not receive server-managed settings by design. A managed settings file applies to Cowork sessions when it is present where the session runs; remote Cowork sessions run on Anthropic-managed VMs, where a device-deployed file is not present.
* Sessions on [third-party providers](/docs/en/server-managed-settings#platform-availability) such as Amazon Bedrock, Google Cloud's Agent Platform, Microsoft Foundry, and [Claude Platform on AWS](/docs/en/claude-platform-on-aws) do not receive server-managed settings, so deliver the allowlist through MDM or managed settings files there.
* Server-managed delivery also requires the session to authenticate with an organization login or a directly configured API key. Fleets that generate keys only through an [`apiKeyHelper`](/docs/en/settings#available-settings) script should deliver the allowlist through MDM or managed settings files.
* The Desktop Code tab also hosts [SSH sessions](/docs/en/desktop#ssh-sessions), which read the managed settings file from the remote host they run on. See [Desktop managed settings](/docs/en/desktop#managed-settings).
* The model pickers on claude.ai and in the Desktop app hide or grey out models excluded by your organization's allowlist. The picker state is a convenience for users; enforcement happens in the session.
### Default model behavior
The Default option in the model picker is not affected by `availableModels` unless [`enforceAvailableModels`](#enforce-the-allowlist-for-the-default-model) is also set. On its own, `availableModels` leaves Default available, resolving to the system's [runtime default](#default-model-setting) for the account. If that default is a model you intend to restrict, set `enforceAvailableModels` as well.
An empty `availableModels` array never engages the Default-model enforcement: with `availableModels: []`, named model selections are blocked but the Default model for the account type remains usable regardless of `enforceAvailableModels`.
### Enforce the allowlist for the Default model
Set `enforceAvailableModels: true` alongside a non-empty `availableModels` in managed settings to extend the allowlist to the Default option. This requires Claude Code v2.1.175 or later.
```json theme={null}
{
"availableModels": ["sonnet", "haiku"],
"enforceAvailableModels": true
}
```
The Default option resolves to the account-type default, or to the [organization default model](#organization-default-model) when an admin has set one. When that model is not in the allowlist, the Default option instead resolves to the first `availableModels` entry that names an allowed, available model, and the `/model` picker's Default row shows that model. This applies everywhere the default is reached: session startup, selecting Default in `/model`, the `"default"` keyword in [fallback model chains](#fallback-model-chains), and the fallback used when an excluded selection is dropped.
`enforceAvailableModels` has no effect when `availableModels` is unset or empty: with `availableModels: []`, the Default model for the account type remains usable, so the setting cannot lock users out of every model. When `availableModels` is non-empty but no entry resolves to an allowed and available model, enforcement is skipped and Default resolves to the account-type default, with a warning visible only under `--debug`. Keep at least one guaranteed-available entry in the list to avoid this.
Deploy both keys in the [highest-precedence managed source](/docs/en/settings#precedence-within-the-managed-tier): these keys don't merge across managed sources, so a pair placed in a managed settings file is ignored when the admin console delivers any settings.
### Control the model users run on
The `model` setting is an initial selection, not enforcement. It sets which model is active when a session starts, but users can still open `/model` and pick Default, which resolves to the system's [runtime default](#default-model-setting) regardless of what `model` is set to, unless [`enforceAvailableModels`](#enforce-the-allowlist-for-the-default-model) redirects it.
To fully control the model experience, combine these settings:
* **`availableModels`**: restricts which named models users can switch to
* **`enforceAvailableModels`**: extends the `availableModels` allowlist to the Default option, so Default cannot resolve to a model outside the list
* **`model`**: sets the initial model selection when a session starts
* **`ANTHROPIC_DEFAULT_SONNET_MODEL`** / **`ANTHROPIC_DEFAULT_OPUS_MODEL`** / **`ANTHROPIC_DEFAULT_HAIKU_MODEL`** / **`ANTHROPIC_DEFAULT_FABLE_MODEL`**: control what the Default option and the `sonnet`, `opus`, `haiku`, and `fable` aliases resolve to
This example starts users on Sonnet 4.5, limits the picker to Sonnet and Haiku, and ensures Default resolves to a model on the allowlist rather than the tier default:
```json theme={null}
{
"model": "claude-sonnet-4-5",
"availableModels": ["claude-sonnet-4-5", "haiku"],
"enforceAvailableModels": true,
"env": {
"ANTHROPIC_DEFAULT_SONNET_MODEL": "claude-sonnet-4-5"
}
}
```
Without `enforceAvailableModels` or the `env` block, a user who selects Default in the picker would get the latest release for their tier, bypassing the version pin in `model` and `availableModels`. The two settings cover different scopes: `enforceAvailableModels` makes Default obey the allowlist, while the `env` block pins which version a permitted alias such as `sonnet` resolves to. Use `enforceAvailableModels` alone when restricting model families is enough; add the `env` block when you also need to pin a specific version.
### Merge behavior
When the [highest-precedence managed settings source](/docs/en/server-managed-settings#settings-precedence) defines `availableModels`, that list alone applies, apart from a [host platform that supplies its own](/docs/en/settings#exceptions-to-managed-settings-precedence): entries in user, project, or local settings cannot extend it, and `availableModels` doesn't merge across admin-deployed managed sources, so a list deployed in a managed settings file is ignored when server-managed settings deliver any keys. Otherwise, lists from user, project, and local settings are [concatenated and deduplicated](/docs/en/settings#settings-precedence) like other array settings. As of Claude Code v2.1.175, the managed list replaces lower-precedence entries; earlier versions merge them.
Within the effective list, an entry naming a specific model in a family, whether a version prefix or a full model ID, disables that family's wildcard entry: `["sonnet", "claude-sonnet-4-5"]` allows only Sonnet 4.5 versions, not every Sonnet model.
### Mantle model IDs
When the [Amazon Bedrock Mantle endpoint](/docs/en/amazon-bedrock#use-the-mantle-endpoint) is enabled, entries in `availableModels` that start with `anthropic.` are added to the `/model` picker as custom options and routed to the Mantle endpoint. This is an exception to the alias matching described in [Pin models for third-party deployments](#pin-models-for-third-party-deployments). The setting still restricts the picker to listed entries, and a Mantle ID embeds a family name, so it counts as a specific entry and disables that family's wildcard: alongside any Mantle IDs, list the version prefixes or full IDs you want to keep selectable. See [Merge behavior](#merge-behavior).
### Organization model restrictions
Organization admins on Claude Enterprise plans restrict which models members can run by disabling individual models in the claude.ai admin console. This restriction is delivered with the account's entitlements when Claude Code authenticates, separate from any `availableModels` list in settings, and the server enforces the same restriction independently when a session is created. Requires Claude Code v2.1.187 or later.
The restriction applies when a member signs in or uses their own API key. Organization-scoped credentials, such as organization service keys, are not tied to a user, so the restriction does not apply to them.
The Claude Console has no model restriction control. Organizations without a Claude Enterprise plan, including those whose members authenticate through the Anthropic API, restrict models with [`availableModels`](#restrict-model-selection) in [managed settings](/docs/en/settings#settings-files) instead, adding [`enforceAvailableModels`](#enforce-the-allowlist-for-the-default-model) to cover the Default option. These settings are enforced by Claude Code itself, not by the server.
A restricted model is hidden from the `/model` picker. Selecting it by name with `--model`, the `ANTHROPIC_MODEL` environment variable, or the `model` setting shows the notice `Model "<name>" is restricted by your organization's settings. Using <model> instead.` and the session starts on an allowed model. Typing `/model <name>` for a restricted model is rejected with `Model '<name>' is restricted by your organization's settings. Run /model to choose a different model.` and the session keeps its current model.
A [model family alias](#restrict-model-selection) such as `opus` resolves to the newest version of its family that the organization permits, with the same substitution notice. `/model <alias>` is rejected only when every version of its family is restricted; an alias set with `--model`, `ANTHROPIC_MODEL`, or the `model` setting is still replaced at startup in that case. Before v2.1.205, a family alias was substituted or rejected based on its newest released version alone, even when an older version was allowed.
Restrictions apply org-wide or per role:
* Disabling a model at the organization level removes it for every member.
* Role-level access grants different models to different custom roles, and a member who holds several roles can use any model that one of their roles grants.
* Haiku models are always available and can't be disabled, so every member keeps at least one usable model.
* An access change takes effect on new requests within about a minute; the `/model` picker reflects it the next time a session starts.
Both restrictions apply together: a model is selectable only when it is permitted by `availableModels` and not restricted by the organization. Organization restrictions reach sessions on the Anthropic API and [LLM gateway](/docs/en/llm-gateway) deployments only; on any other provider, use `availableModels` instead.
## Organization default model
Organization admins on Claude Enterprise plans can set a default model for Claude Code members from the claude.ai admin console, for the whole organization or per custom role. When one is set, the Default option resolves to that model instead of the [account-type default](#default-model-setting). Requires Claude Code v2.1.196 or later.
The Default row in the `/model` picker shows the organization default's name with the label Org default. The label reads Org default whether the admin set the default for the whole organization or for your role. A role default covers members of that custom role and takes precedence over the organization-wide default; when several of your roles set different defaults, the most capable model applies.
The organization default is a starting point, not a restriction, and any other model selection takes precedence over it:
Cut at 300 lines. The page has the rest.
monitoring-usage First recorded · 1333 lines, first recorded
# Monitoring ## Quick start ## Administrator configuration ### How managed settings lock the OTLP destination ## Configuration details ### Common configuration variables ### mTLS authentication ### Metrics cardinality control ### Traces (beta) #### Span hierarchy #### Span attributes ### Dynamic headers #### Settings configuration #### Script requirements #### Refresh behavior ### Multi-team organization support ### Example configurations ## Available metrics and events ### Standard attributes ### Metrics ### Metric details #### Session counter #### Lines of code counter #### Pull request counter #### Commit counter #### Cost counter #### Token counter #### Code edit tool decision counter #### Active time counter ### Events #### Event correlation attributes #### User prompt event #### Assistant response event #### Tool result event #### API request event #### API error event #### API refusal event #### API request body event #### API response body event #### Tool decision event #### Permission mode changed event #### Auth event #### MCP server connection event #### Internal error event #### Plugin installed event #### Plugin loaded event #### Skill activated event #### At mention event #### API retries exhausted event #### Hook registered event #### Hook execution start event #### Hook execution complete event #### Hook plugin metrics event #### Compaction event #### Subagent completed event #### Feedback survey event #### Retention sweep event ## Interpret metrics and events data ### Usage monitoring ### Cost monitoring ### Alerting and segmentation ### Detect retry exhaustion ### Event analysis ## Audit security events ### Attribute actions to users ### Audit MCP activity ### Map security questions to events ### Send events to a SIEM ## Backend considerations ### For metrics ### For events/logs ### For traces ## Service information ## ROI measurement resources ## Security and privacy ## Monitor Claude Code on Amazon Bedrock
The first capture of this source. The page was already there, and this is what it said.
# Monitoring
> Learn how to enable and configure OpenTelemetry for Claude Code.
Track Claude Code usage, costs, and tool activity across your organization by exporting telemetry data through OpenTelemetry (OTel). Claude Code exports metrics as time series data via the standard metrics protocol, events via the logs/events protocol, and optionally distributed traces via the [traces protocol](#traces-beta).
## Quick start
Configure OpenTelemetry using environment variables:
```bash theme={null}
# 1. Enable telemetry
export CLAUDE_CODE_ENABLE_TELEMETRY=1
# 2. Choose exporters (both are optional - configure only what you need)
export OTEL_METRICS_EXPORTER=otlp # Options: otlp, prometheus, console, none
export OTEL_LOGS_EXPORTER=otlp # Options: otlp, console, none
# 3. Configure OTLP endpoint (for OTLP exporter)
export OTEL_EXPORTER_OTLP_PROTOCOL=grpc
export OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4317
# 4. Set authentication (if required)
export OTEL_EXPORTER_OTLP_HEADERS="Authorization=Bearer your-token"
# 5. For debugging: reduce export intervals, and reset them for production use
export OTEL_METRIC_EXPORT_INTERVAL=10000 # 10 seconds (default: 60000ms)
export OTEL_LOGS_EXPORT_INTERVAL=5000 # 5 seconds (default: 5000ms)
# 6. Run Claude Code
claude
```
To verify a setup that exports metrics, check your backend for the `claude_code.session.count` metric, which Claude Code emits when a session starts. To verify a logs-only setup, submit a prompt and check for the `claude_code.user_prompt` event. If nothing arrives, run `claude --debug` and check the debug log for OTel export errors.
For full configuration options, see the [OpenTelemetry specification](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/protocol/exporter.md#configuration-options).
## Administrator configuration
Administrators can configure OpenTelemetry settings for all users through the [managed settings file](/docs/en/settings#settings-files). See the [settings precedence](/docs/en/settings#settings-precedence) for more information about how settings are applied.
Example managed settings configuration:
```json theme={null}
{
"env": {
"CLAUDE_CODE_ENABLE_TELEMETRY": "1",
"OTEL_METRICS_EXPORTER": "otlp",
"OTEL_LOGS_EXPORTER": "otlp",
"OTEL_EXPORTER_OTLP_PROTOCOL": "grpc",
"OTEL_EXPORTER_OTLP_ENDPOINT": "http://collector.example.com:4317",
"OTEL_EXPORTER_OTLP_HEADERS": "Authorization=Bearer example-token"
}
}
```
Claude Code doesn't pass `OTEL_*` environment variables to the subprocesses it spawns, including the Bash tool, hooks, MCP servers, and language servers. An OpenTelemetry-instrumented application that you run through the Bash tool doesn't inherit Claude Code's exporter endpoint or headers, so set those variables directly in the command if that application needs to export its own telemetry.
### How managed settings lock the OTLP destination
When you set an `OTEL_EXPORTER_OTLP_*` variable in managed settings, Claude Code removes conflicting developer-set variables at startup and logs a warning you can see with `claude --debug`. What it removes depends on which variable you set:
* **Endpoints**: when you set `OTEL_EXPORTER_OTLP_ENDPOINT`, Claude Code removes every developer-set per-signal endpoint. Developers can't point one signal at a different collector, so you don't need to also set the per-signal endpoint variables in managed settings.
* **Protocols**: when you set `OTEL_EXPORTER_OTLP_PROTOCOL`, Claude Code removes every developer-set per-signal protocol.
* **Credentials**: when you set `OTEL_EXPORTER_OTLP_HEADERS`, `OTEL_EXPORTER_OTLP_CLIENT_KEY`, or `OTEL_EXPORTER_OTLP_CLIENT_CERTIFICATE`, Claude Code removes the developer-set per-signal versions of that variable, plus every developer-set endpoint variable, generic or per-signal, since those credentials would otherwise reach a collector the managed settings didn't choose.
* **Exporter selectors**: `OTEL_METRICS_EXPORTER`, `OTEL_LOGS_EXPORTER`, and the beta `OTEL_TRACES_EXPORTER` follow normal per-key precedence. A developer's setting can still disable a signal or switch it to the console exporter, so set the selectors in managed settings too if you need them locked. Across [admin sources](/docs/en/settings#precedence-within-the-managed-tier), `OTEL_LOGS_EXPORTER` follows the [telemetry unit](/docs/en/server-managed-settings#per-key-exceptions-across-managed-sources) while the other two selectors merge per key. Requires Claude Code v2.1.223 or later.
Claude Code doesn't remove per-signal variables that you set in managed settings itself, so you can route one signal to a different collector by setting its variable there, as the [SIEM example](#send-events-to-a-siem) does. If you set a per-signal credential there, Claude Code removes the developer-set endpoint for that signal.
This removal behavior changes where telemetry is delivered, not what Claude Code collects.
Before v2.1.217, every variable followed per-key settings precedence independently, so a signal-specific endpoint set in user settings or the shell redirected that signal away from the managed collector.
## Configuration details
### Common configuration variables
These variables configure exporters, endpoints, and export behavior for all deployments. If you set a per-signal endpoint or protocol variable, such as `OTEL_EXPORTER_OTLP_METRICS_ENDPOINT`, Claude Code uses it instead of the generic variable for that signal. If you set a per-signal headers variable, such as `OTEL_EXPORTER_OTLP_METRICS_HEADERS`, Claude Code merges it with the generic `OTEL_EXPORTER_OTLP_HEADERS` for that signal. On machines with managed settings, see [How managed settings lock the OTLP destination](#how-managed-settings-lock-the-otlp-destination) for what Claude Code removes.
| Environment Variable | Description | Example Values |
| --------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `CLAUDE_CODE_ENABLE_TELEMETRY` | Enables telemetry collection (required) | `1` |
| `OTEL_METRICS_EXPORTER` | Metrics exporter types, comma-separated. Use `none` to disable | `console`, `otlp`, `prometheus`, `none` |
| `OTEL_LOGS_EXPORTER` | Logs/events exporter types, comma-separated. Use `none` to disable | `console`, `otlp`, `none` |
| `OTEL_EXPORTER_OTLP_PROTOCOL` | Protocol for OTLP exporter, applies to all signals. Claude Code has no default protocol, so set this or the signal-specific protocol variable for each `otlp` exporter you enable | `grpc`, `http/json`, `http/protobuf` |
| `OTEL_EXPORTER_OTLP_ENDPOINT` | OTLP collector endpoint for all signals | `http://localhost:4317` |
| `OTEL_EXPORTER_OTLP_METRICS_PROTOCOL` | Protocol for metrics, overrides general setting | `grpc`, `http/json`, `http/protobuf` |
| `OTEL_EXPORTER_OTLP_METRICS_ENDPOINT` | OTLP metrics endpoint, overrides general setting | `http://localhost:4318/v1/metrics` |
| `OTEL_EXPORTER_OTLP_LOGS_PROTOCOL` | Protocol for logs, overrides general setting | `grpc`, `http/json`, `http/protobuf` |
| `OTEL_EXPORTER_OTLP_LOGS_ENDPOINT` | OTLP logs endpoint, overrides general setting | `http://localhost:4318/v1/logs` |
| `OTEL_EXPORTER_OTLP_HEADERS` | Authentication headers for OTLP | `Authorization=Bearer token` |
| `OTEL_EXPORTER_OTLP_METRICS_HEADERS` | Authentication headers for metrics, merged with the general headers | `Authorization=Bearer token` |
| `OTEL_EXPORTER_OTLP_LOGS_HEADERS` | Authentication headers for logs, merged with the general headers | `Authorization=Bearer token` |
| `OTEL_METRIC_EXPORT_INTERVAL` | Export interval in milliseconds (default: 60000) | `5000`, `60000` |
| `OTEL_LOGS_EXPORT_INTERVAL` | Logs export interval in milliseconds (default: 5000) | `1000`, `10000` |
| `OTEL_LOG_USER_PROMPTS` | Enable logging of user prompt content (default: disabled) | `1` to enable |
| `OTEL_LOG_ASSISTANT_RESPONSES` | Enable logging of assistant response text on `assistant_response` events (default: disabled). When unset, falls back to the value of `OTEL_LOG_USER_PROMPTS`. Requires Claude Code v2.1.193 or later | `1` to enable, `0` to keep redacted |
| `OTEL_LOG_TOOL_DETAILS` | Enable logging of tool parameters and input arguments in tool events and trace span attributes: Bash commands, MCP server and tool names, skill names, user-authored workflow names, and tool input. Also enables custom, plugin, and MCP command names on `user_prompt` events (default: disabled). For Claude Desktop's built-in servers, in sessions Claude Desktop owns, `mcp_server_name`/`mcp_tool_name` emit on `tool_decision`/`tool_result` even with the flag off. The exception requires Claude Code v2.1.214 or later | `1` to enable |
| `OTEL_LOG_TOOL_CONTENT` | Enable logging of tool input and output content in span events (default: disabled). Requires [tracing](#traces-beta). Content is truncated at the content limit (60 KB by default) | `1` to enable |
| `OTEL_LOG_RAW_API_BODIES` | Emit the full Anthropic Messages API request and response JSON as `api_request_body` / `api_response_body` log events (default: disabled). Bodies include the entire conversation history. Enabling this implies consent to everything `OTEL_LOG_USER_PROMPTS`, `OTEL_LOG_TOOL_DETAILS`, and `OTEL_LOG_TOOL_CONTENT` would reveal | `1` for inline bodies truncated at the content limit (60 KB by default), or `file:<dir>` for untruncated bodies on disk with a `body_ref` pointer in the event |
| `CLAUDE_CODE_OTEL_CONTENT_MAX_LENGTH` | Content limit: the maximum length of content-bearing attributes such as model responses, tool content, system prompts, and raw API bodies, truncation marker included, in UTF-16 code units (default: 61440, i.e. 60 KB). The default is sized for backends that cap attribute values at 64 KB; raise it only if your backend accepts larger values, or lower it to cut telemetry volume. When an OpenTelemetry SDK attribute limit, `OTEL_ATTRIBUTE_VALUE_LENGTH_LIMIT` or one of its logrecord and span variants, is set lower, Claude Code truncates at that smaller value so the `[TRUNCATED ...]` marker stays within the SDK limit. Requires Claude Code v2.1.214 or later | `262144` |
| `OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE` | Metrics temporality preference (default: `delta`). Set to `cumulative` if your backend expects cumulative temporality | `delta`, `cumulative` |
| `CLAUDE_CODE_OTEL_HEADERS_HELPER_DEBOUNCE_MS` | Interval for refreshing dynamic headers (default: 1740000ms / 29 minutes) | `900000` |
For the `http/protobuf` and `http/json` protocols, Claude Code sends each export request with a `Content-Length` header. Before v2.1.212, Claude Code versions from v2.1.191 onward sent these requests with chunked transfer encoding; Azure Monitor and other endpoints that require a declared length rejected them with `411 Length Required` or `400` errors.
### mTLS authentication
How you configure client certificates for the OTLP exporter depends on the OTLP protocol in use for that signal, set via `OTEL_EXPORTER_OTLP_PROTOCOL` or the per-signal override. The same configuration applies to metrics, logs, and traces.
| Protocol | Client certificate variables | Trust the collector's CA with |
| :--------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | :------------------------------- |
| `http/protobuf`, `http/json` | `CLAUDE_CODE_CLIENT_CERT`, `CLAUDE_CODE_CLIENT_KEY`, and optionally `CLAUDE_CODE_CLIENT_KEY_PASSPHRASE`. See [Network configuration](/docs/en/network-config#mtls-authentication) | `NODE_EXTRA_CA_CERTS` |
| `grpc` | `OTEL_EXPORTER_OTLP_CLIENT_KEY` and `OTEL_EXPORTER_OTLP_CLIENT_CERTIFICATE`, or the per-signal variants such as `OTEL_EXPORTER_OTLP_METRICS_CLIENT_KEY` to use a different certificate per signal | `OTEL_EXPORTER_OTLP_CERTIFICATE` |
For `grpc`, the OpenTelemetry SDK reads the standard OTLP variables directly, so existing configurations that set the per-signal metrics variables continue to work. On machines with managed settings, Claude Code [may remove developer-set per-signal credentials and endpoints](#how-managed-settings-lock-the-otlp-destination) at startup.
### Metrics cardinality control
The following environment variables control which attributes are included in metrics to manage cardinality:
| Environment Variable | Description | Default Value | Example to Disable |
| ------------------------------------------ | ------------------------------------------------------------------------------- | ------------- | ------------------ |
| `OTEL_METRICS_INCLUDE_SESSION_ID` | Include session.id attribute in metrics | `true` | `false` |
| `OTEL_METRICS_INCLUDE_VERSION` | Include app.version attribute in metrics | `false` | `true` |
| `OTEL_METRICS_INCLUDE_ACCOUNT_UUID` | Include user.account\_uuid and user.account\_id attributes in metrics | `true` | `false` |
| `OTEL_METRICS_INCLUDE_ENTRYPOINT` | Include app.entrypoint attribute in metrics | `false` | `true` |
| `OTEL_METRICS_INCLUDE_RESOURCE_ATTRIBUTES` | Include keys from `OTEL_RESOURCE_ATTRIBUTES` as attributes on metric datapoints | `true` | `false` |
Lower cardinality generally means better performance and lower storage costs but less granular data for analysis.
### Traces (beta)
Distributed tracing exports spans that link each user prompt to the API requests and tool executions it triggers, so you can view a full request as a single trace in your tracing backend.
Tracing is off by default. To enable it, set both `CLAUDE_CODE_ENABLE_TELEMETRY=1` and `CLAUDE_CODE_ENHANCED_TELEMETRY_BETA=1`, then set `OTEL_TRACES_EXPORTER` to choose where spans are sent. Traces reuse the [common OTLP configuration](#common-configuration-variables) for endpoint, protocol, headers, and [mTLS](#mtls-authentication). On machines with managed settings, Claude Code [may remove developer-set per-signal credentials and endpoints](#how-managed-settings-lock-the-otlp-destination) at startup.
| Environment Variable | Description | Example Values |
| ------------------------------------- | --------------------------------------------------------------------------------- | ------------------------------------ |
| `CLAUDE_CODE_ENHANCED_TELEMETRY_BETA` | Enable span tracing (required). `ENABLE_ENHANCED_TELEMETRY_BETA` is also accepted | `1` |
| `OTEL_TRACES_EXPORTER` | Traces exporter types, comma-separated. Use `none` to disable | `console`, `otlp`, `none` |
| `OTEL_EXPORTER_OTLP_TRACES_PROTOCOL` | Protocol for traces, overrides `OTEL_EXPORTER_OTLP_PROTOCOL` | `grpc`, `http/json`, `http/protobuf` |
| `OTEL_EXPORTER_OTLP_TRACES_ENDPOINT` | OTLP traces endpoint, overrides `OTEL_EXPORTER_OTLP_ENDPOINT` | `http://localhost:4318/v1/traces` |
| `OTEL_EXPORTER_OTLP_TRACES_HEADERS` | Authentication headers for traces, merged with `OTEL_EXPORTER_OTLP_HEADERS` | `Authorization=Bearer token` |
| `OTEL_TRACES_EXPORT_INTERVAL` | Span batch export interval in milliseconds (default: 5000) | `1000`, `10000` |
Spans redact user prompt text, tool input details, and tool content by default. Set `OTEL_LOG_USER_PROMPTS=1`, `OTEL_LOG_TOOL_DETAILS=1`, and `OTEL_LOG_TOOL_CONTENT=1` to include them.
When tracing is active, Bash and PowerShell subprocesses automatically inherit a `TRACEPARENT` environment variable containing the W3C trace context of the active tool execution span. This lets any subprocess that reads `TRACEPARENT` parent its own spans under the same trace, enabling end-to-end distributed tracing through scripts and commands that Claude runs.
When tracing is active and Claude Code is connected directly to the Anthropic API, each model request carries a W3C `traceparent` header set to the `claude_code.llm_request` span's context, and the API's `traceresponse` header is recorded as a span link. Together these connect Claude Code's client-side spans to the server-side trace through any compliant intermediary. Outbound HTTP MCP requests carry `traceparent` the same way. The header is not sent to third-party providers.
By default, the `traceparent` header on model and HTTP MCP requests is sent only when `ANTHROPIC_BASE_URL` is unset or points at the Anthropic API, since some proxies reject unrecognized headers. The subprocess `TRACEPARENT` variable is controlled by the same switch for consistency. If you run Claude Code through a custom `ANTHROPIC_BASE_URL` proxy and want trace context propagated, set `CLAUDE_CODE_PROPAGATE_TRACEPARENT=1`.
In Agent SDK and non-interactive sessions started with `-p`, Claude Code also reads `TRACEPARENT` and `TRACESTATE` from its own environment when starting each interaction span. This lets an embedding process pass its active W3C trace context into the subprocess so Claude Code's spans appear as children of the caller's distributed trace. Interactive sessions ignore inbound `TRACEPARENT` to avoid accidentally inheriting ambient values from CI or container environments.
The inbound trace context also applies to [events](#events). In Agent SDK and `-p` sessions with `TRACEPARENT` set, each OTLP event log record carries `trace_id` and `span_id` values that join it to your application's trace, even when the traces exporter isn't configured, so your logging backend can correlate events with the rest of the trace.
A record emitted while an interaction is active carries the interaction span's IDs, even when Claude Code emits it outside the span's async context, such as in a permission prompt callback or for a record buffered during startup and exported later. A record emitted with no active interaction span carries the inbound `TRACEPARENT` IDs directly. Before v2.1.214, records emitted outside the span's async context carried the inbound `TRACEPARENT` IDs instead of the span's IDs. Before v2.1.212, event records emitted outside an active span didn't carry `trace_id` or `span_id`.
#### Span hierarchy
Each user prompt starts a `claude_code.interaction` root span. API calls, tool calls, and hook executions are recorded as its children. Tool spans have two child spans of their own: one for the time spent waiting on a permission decision and one for the execution itself. When the Agent tool, or legacy Task tool, spawns a subagent, the subagent's API and tool spans nest under the parent's `claude_code.tool` span.
```text theme={null}
claude_code.interaction
├── claude_code.llm_request
├── claude_code.hook (requires detailed beta tracing)
└── claude_code.tool
├── claude_code.tool.blocked_on_user
├── claude_code.tool.execution
└── (Agent tool) subagent claude_code.llm_request / claude_code.tool spans
```
In Agent SDK and `claude -p` sessions, `claude_code.interaction` itself becomes a child of the caller's span when `TRACEPARENT` is set in the environment.
#### Span attributes
Every span carries the [standard attributes](#standard-attributes) plus a `span.type` attribute matching its name. The tables below list the additional attributes set on each span. The `llm_request`, `tool.execution`, and `hook` spans set OpenTelemetry status `ERROR` when they record a failure; the other spans always end with status `UNSET`.
**`claude_code.interaction`**
| Attribute | Description | Gated by |
| ------------------------- | --------------------------------------------------------- | ----------------------- |
| `user_prompt` | Prompt text. Value is `<REDACTED>` unless the gate is set | `OTEL_LOG_USER_PROMPTS` |
| `user_prompt_length` | Prompt length in characters | |
| `interaction.sequence` | 1-based counter of interactions in this session | |
| `interaction.duration_ms` | Wall-clock duration of the turn | |
**`claude_code.llm_request`**
| Attribute | Description | Gated by |
| -------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------- |
| `model` | Model identifier | |
| `gen_ai.system` | Always `anthropic`. OpenTelemetry GenAI semantic convention | |
| `gen_ai.request.model` | Same value as `model`. OpenTelemetry GenAI semantic convention | |
| `query_source` | Subsystem that issued the request, such as `repl_main_thread` or a subagent name | |
| `agent_id` | Identifier of the subagent or teammate that issued the request. Absent on the main session | |
| `parent_agent_id` | Identifier of the agent that spawned this one. Absent for the main session and for agents spawned directly from it | |
| `workflow.run_id` | Run identifier of the [Workflow](/docs/en/workflows) tool run that spawned this agent, prefixed `wf_`. Absent for agents not spawned by a workflow | |
| `workflow.name` | Name of the workflow that spawned this agent. User-authored names are replaced with `custom` unless the gate is set | `OTEL_LOG_TOOL_DETAILS` |
| `speed` | `fast` or `normal` | |
| `llm_request.context` | `interaction`, `tool`, or `standalone` depending on the parent span | |
| `duration_ms` | Wall-clock duration including retries | |
| `ttft_ms` | Time to first token in milliseconds | |
| `input_tokens` | Input token count from the API usage block | |
| `output_tokens` | Output token count | |
| `cache_read_tokens` | Tokens read from prompt cache | |
| `cache_creation_tokens` | Tokens written to prompt cache | |
| `request_id` | Anthropic API request ID from the `request-id` response header | |
| `gen_ai.response.id` | Same value as `request_id`. OpenTelemetry GenAI semantic convention | |
| `client_request_id` | Client-generated `x-client-request-id` of the final attempt | |
| `attempt` | Total attempts made for this request | |
| `success` | `true` or `false` | |
| `status_code` | HTTP status code when the request failed | |
| `error` | Error message when the request failed | |
| `response.has_tool_call` | `true` when the response contained tool-use blocks | |
| `stop_reason` | API response `stop_reason`, such as `end_turn`, `tool_use`, `max_tokens`, `stop_sequence`, `pause_turn`, or `refusal` | |
| `gen_ai.response.finish_reasons` | Same value as `stop_reason`, wrapped in a string array. OpenTelemetry GenAI semantic convention | |
Each retry attempt is also recorded as a `gen_ai.request.attempt` span event with `attempt` and `client_request_id` attributes.
**`claude_code.tool`**
| Attribute | Description | Gated by |
| --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ----------------------- |
| `tool_name` | Tool name | |
| `duration_ms` | Wall-clock duration including permission wait and execution | |
| `result_tokens` | Approximate token size of the tool result | |
| `agent_id` | Identifier of the subagent or teammate that ran the tool. Absent on the main session | |
| `parent_agent_id` | Identifier of the agent that spawned this one. Absent for the main session and for agents spawned directly from it | |
| `workflow.run_id` | Run identifier of the Workflow tool run that spawned this agent, prefixed `wf_`. Absent for agents not spawned by a workflow | |
| `workflow.name` | Name of the workflow that spawned this agent. User-authored names are replaced with `custom` unless the gate is set | `OTEL_LOG_TOOL_DETAILS` |
| `tool_use_id` | The model's `tool_use` block id for this call. Matches the `tool_use_id` on the [tool\_result](#tool-result-event) and [tool\_decision](#tool-decision-event) events and in hook payloads, so you can join the span to those records | |
| `gen_ai.tool.call.id` | Same value as `tool_use_id`. OpenTelemetry GenAI semantic convention | |
| `file_path` | Target file path for Read, Edit, and Write tools | `OTEL_LOG_TOOL_DETAILS` |
| `full_command` | Command string for the Bash tool | `OTEL_LOG_TOOL_DETAILS` |
| `skill_name` | Skill name for the Skill tool | `OTEL_LOG_TOOL_DETAILS` |
| `subagent_type` | Subagent type for the Agent tool or legacy Task tool | `OTEL_LOG_TOOL_DETAILS` |
When `OTEL_LOG_TOOL_CONTENT=1`, this span also records a `tool.output` span event whose attributes contain the tool's input and output bodies, truncated at the content limit (60 KB by default) per attribute.
**`claude_code.tool.blocked_on_user`**
| Attribute | Description | Gated by |
| ------------- | ------------------------------------------------------------------------- | -------- |
| `duration_ms` | Time spent waiting for the permission decision | |
| `decision` | `accept` or `reject` | |
| `source` | Decision source, matching the [Tool decision event](#tool-decision-event) | |
**`claude_code.tool.execution`**
| Attribute | Description | Gated by |
| --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------- |
| `duration_ms` | Time spent running the tool body | |
| `tool_use_id` | Same value as on the parent `claude_code.tool` span | |
| `gen_ai.tool.call.id` | Same value as `tool_use_id`. OpenTelemetry GenAI semantic convention | |
| `success` | `true` or `false` | |
| `error` | Error category string when execution failed, such as `Error:ENOENT` or `ShellError`. Contains the full error message instead when the gate is set | `OTEL_LOG_TOOL_DETAILS` |
**`claude_code.hook`**
This span is emitted only when detailed beta tracing is active, which requires `ENABLE_BETA_TRACING_DETAILED=1` and `BETA_TRACING_ENDPOINT` in addition to the trace exporter configuration above. In interactive CLI sessions, this also requires your organization to be allowlisted for the feature. Agent SDK and non-interactive `-p` sessions are not gated. It is not emitted when only `CLAUDE_CODE_ENHANCED_TELEMETRY_BETA` is set.
| Attribute | Description | Gated by |
| ------------------------ | ------------------------------------------------ | ----------------------- |
| `hook_event` | Hook event type, such as `PreToolUse` | |
| `hook_name` | Full hook name, such as `PreToolUse:Write` | |
| `num_hooks` | Number of matching hook commands executed | |
| `hook_definitions` | JSON-serialized hook configuration | `OTEL_LOG_TOOL_DETAILS` |
| `duration_ms` | Wall-clock duration of all matching hooks | |
| `num_success` | Count of hooks that completed successfully | |
| `num_blocking` | Count of hooks that returned a blocking decision | |
| `num_non_blocking_error` | Count of hooks that failed without blocking | |
| `num_cancelled` | Count of hooks cancelled before completion | |
<Note>
Additional content-bearing attributes such as `new_context`, `system_prompt_preview`, `user_system_prompt`, `tool_input`, and `response.model_output` are emitted only when detailed beta tracing is active. They are not part of the stable span schema.
`user_system_prompt` additionally requires `OTEL_LOG_USER_PROMPTS=1`. It carries only the system prompt text you provide via the `systemPrompt` SDK option or `--system-prompt` and `--append-system-prompt` flags, truncated at the content limit (60 KB by default), and is emitted once per session rather than per request.
</Note>
### Dynamic headers
For enterprise environments that require dynamic authentication, you can configure a script to generate headers dynamically. Dynamic headers apply only to the `http/protobuf` and `http/json` protocols. With the `grpc` protocol, Claude Code uses only the static headers variables, `OTEL_EXPORTER_OTLP_HEADERS` and its per-signal variants.
#### Settings configuration
Add to your `.claude/settings.json`, replacing the path with your own script:
```json theme={null}
{
"otelHeadersHelper": "/path/to/generate-otel-headers.sh"
}
```
The value can be the path to an executable file, including a path that contains spaces, or a shell command line with arguments. On Windows, the value always runs through the shell, so quote a path that contains spaces inside the JSON value.
#### Script requirements
Cut at 300 lines. The page has the rest.
network-config First recorded · 228 lines, first recorded
# Enterprise network configuration ## Proxy configuration ### Environment variables ### Basic authentication ## CA certificate store ## Custom CA certificates ## mTLS authentication ## Verify your configuration ## Apply network settings to background agents ### Set network variables in settings, not the shell ### Configure a corporate launcher as a setting ## Streaming idle watchdogs ## Network access requirements ### Desktop and claude.ai ## Additional resources
The first capture of this source. The page was already there, and this is what it said.
# Enterprise network configuration
> Configure Claude Code for enterprise environments with proxy servers, custom Certificate Authorities (CA), and mutual Transport Layer Security (mTLS) authentication.
Claude Code supports various enterprise network and security configurations through environment variables. This includes routing traffic through corporate proxy servers, trusting custom Certificate Authorities (CA), and authenticating with mutual Transport Layer Security (mTLS) certificates for enhanced security.
Set these environment variables before you launch Claude Code. Variables exported in your shell are read once at startup, so a running session doesn't pick up later changes to your shell environment.
<Note>
All environment variables shown on this page can also be configured in [`settings.json`](/docs/en/settings).
</Note>
## Proxy configuration
### Environment variables
Claude Code respects standard proxy environment variables. In Claude Desktop sessions where the app manages the provider connection, Claude Code reads them only from managed settings and `~/.claude/settings.json`; see [mTLS authentication](#mtls-authentication) for the scope rules.
```bash theme={null}
# HTTPS proxy (recommended)
export HTTPS_PROXY=https://proxy.example.com:8080
# HTTP proxy (if HTTPS not available)
export HTTP_PROXY=http://proxy.example.com:8080
# Bypass proxy for specific requests - space-separated format
export NO_PROXY="localhost 192.168.1.1 example.com .example.com"
# Bypass proxy for specific requests - comma-separated format
export NO_PROXY="localhost,192.168.1.1,example.com,.example.com"
# Bypass proxy for all requests
export NO_PROXY="*"
```
Lowercase variants also work, and Claude Code uses the first one that's set in the order `https_proxy`, `HTTPS_PROXY`, `http_proxy`, `HTTP_PROXY`.
<Note>
Claude Code does not support SOCKS proxies.
</Note>
### Basic authentication
If your proxy requires basic authentication, include credentials in the proxy URL:
```bash theme={null}
export HTTPS_PROXY=http://username:[email protected]:8080
```
<Warning>
Avoid hardcoding passwords in scripts. Use environment variables or secure credential storage instead.
</Warning>
<Tip>
For proxies requiring advanced authentication (NTLM, Kerberos, etc.), consider using an LLM Gateway service that supports your authentication method.
</Tip>
## CA certificate store
By default, Claude Code trusts both its bundled Mozilla CA certificates and your operating system's certificate store. Reading the OS store requires a runtime with `tls.getCACertificates`: the native installer always has it, and npm installs need Node 22.15 or later. On older Node versions, only the bundled set and `NODE_EXTRA_CA_CERTS` apply. Enterprise TLS-inspection proxies such as CrowdStrike Falcon and Zscaler work without additional configuration when their root certificate is installed in the OS trust store and the runtime can read it.
`CLAUDE_CODE_CERT_STORE` accepts a comma-separated list of sources. Recognized values are `bundled` for the Mozilla CA set shipped with Claude Code and `system` for the operating system trust store. The default is `bundled,system`.
To trust only the bundled Mozilla CA set:
```bash theme={null}
export CLAUDE_CODE_CERT_STORE=bundled
```
To trust only the OS certificate store:
```bash theme={null}
export CLAUDE_CODE_CERT_STORE=system
```
<Note>
`CLAUDE_CODE_CERT_STORE` has no dedicated `settings.json` schema key. Set it via the `env` block in `~/.claude/settings.json` or directly in the process environment.
</Note>
## Custom CA certificates
If your enterprise environment uses a custom CA, configure Claude Code to trust it directly:
```bash theme={null}
export NODE_EXTRA_CA_CERTS=/path/to/ca-cert.pem
```
## mTLS authentication
For enterprise environments requiring client certificate authentication:
```bash theme={null}
# Client certificate for authentication
export CLAUDE_CODE_CLIENT_CERT=/path/to/client-cert.pem
# Client private key
export CLAUDE_CODE_CLIENT_KEY=/path/to/client-key.pem
# Optional: Passphrase for encrypted private key
export CLAUDE_CODE_CLIENT_KEY_PASSPHRASE="your-passphrase"
```
Claude Code reads the certificate and key files at startup and re-reads them each time it applies settings. It doesn't watch those paths during a session, so it keeps presenting the loaded pair until it applies settings again.
To rotate the certificate and key:
* **Running session**: replace the files at the same paths, then restart Claude Code so it picks up the new pair. A mid-session settings application, such as a remote managed-settings sync or running `/login`, also re-reads the files, but no such event is guaranteed to happen before the old pair expires and you see connection errors, so restart to be certain.
* **Future sessions**: replace the files before the current pair expires so Claude Code doesn't load an already-expired pair on its next start.
In [cloud sessions](/docs/en/claude-code-on-the-web), the hosting environment manages the connection to the API, so Claude Code ignores the following variables when they come from a settings file `env` block:
* `CLAUDE_CODE_CLIENT_CERT`
* `CLAUDE_CODE_CLIENT_KEY`
* `CLAUDE_CODE_CLIENT_KEY_PASSPHRASE`
* `NODE_EXTRA_CA_CERTS`
* `NODE_TLS_REJECT_UNAUTHORIZED`
* `CLAUDE_CODE_OAUTH_SCOPES`
Claude Code notes each ignored key in the session's debug log.
In [Claude Desktop](/docs/en/desktop) sessions where the app manages the provider connection, such as the Code tab on a [third-party provider](/docs/en/third-party-integrations) and Cowork sessions, Claude Code reads these variables and the proxy variables `HTTP_PROXY`, `HTTPS_PROXY`, and `NO_PROXY` only from [managed settings](/docs/en/settings#settings-files) and `~/.claude/settings.json`: it ignores them in a repository's own settings files, so a checked-out repository can't redirect the TLS or proxy path of a session whose credentials come from the app. In a local, SSH, or WSL Code tab session signed in through claude.ai, the app doesn't manage the connection, and Claude Code reads these variables from every settings scope, like any terminal session; [cloud sessions](/docs/en/claude-code-on-the-web) follow the cloud-session rules above wherever you start them. Before v2.1.217, Claude Code ignored these variables in every settings file when the app managed the connection.
## Verify your configuration
You usually find out about a wrong proxy address or a bad certificate path from a [connection or certificate error](/docs/en/errors#network-and-connection-errors) on a later request, since Claude Code doesn't validate most of these settings when it reads them. The one setting it checks at startup is the proxy URL: when it can't parse the value, such as one missing the `http://` scheme, Claude Code stops launch with an error naming the variable to fix.
To confirm your configuration loaded before you send a request, start Claude Code with debug logging:
```bash theme={null}
claude --debug
```
Debug output goes to `~/.claude/debug/<session-id>.txt` rather than the terminal, or to a path you set with `--debug-file <path>`. In the log, look for the lines that confirm each file loaded:
```text theme={null}
CA certs: Appended extra certificates from NODE_EXTRA_CA_CERTS (/etc/ssl/certs/corp-ca.pem)
mTLS: Loaded client certificate from CLAUDE_CODE_CLIENT_CERT
mTLS: Loaded client key from CLAUDE_CODE_CLIENT_KEY
```
If Claude Code can't read one of these files, the log shows a `Failed to read` or `Failed to load` line with the reason instead.
You can also run `/status` in an interactive session and check these rows:
* **Proxy**: shows the active proxy URL, and marks a value it can't parse as invalid and ignored.
* **mTLS client cert** and **mTLS client key**: appear only when the files loaded, so a missing row means the load failed and the debug log has the reason.
* **Additional CA cert(s)**: shows the `NODE_EXTRA_CA_CERTS` path without checking that the file loaded, so confirm this one in the debug log.
## Apply network settings to background agents
[Background agents](/docs/en/agent-view) don't run inside the terminal that dispatched them. A per-user supervisor process starts on demand, outlives your shell, and hosts every `claude agents`, `--bg`, and `/background` session. See [How background sessions are hosted](/docs/en/agent-view#how-background-sessions-are-hosted). This changes how the configuration on this page reaches those sessions.
### Set network variables in settings, not the shell
The supervisor is one process shared by every terminal. It inherits the environment of whichever shell starts it first, and an OS-installed supervisor receives no shell environment at all. If you export a proxy, CA path, or mTLS variable only in your shell, it reaches background agents when that shell happened to cold-start the supervisor, and silently doesn't when a different shell did.
Put the same variables in the `env` block of `~/.claude/settings.json` or [managed settings](/docs/en/settings) instead. Every variable on this page can be set there, and settings are the only configuration that reaches every background session on every machine.
### Configure a corporate launcher as a setting
Some organizations require every Claude Code process to start through a corporate launcher that applies sandboxing, network controls, or credential injection. The supervisor and its workers start Claude Code from a fixed path rather than by looking up `claude` on `PATH`, so every background agent bypasses a wrapper you place earlier on `PATH`.
Set the [`processWrapper`](/docs/en/settings#available-settings) setting to prefix the supervisor, its workers, and the other background processes listed under [What the launcher covers](/docs/en/corporate-launcher#what-the-launcher-covers) with your launcher. The equivalent [`CLAUDE_CODE_PROCESS_WRAPPER`](/docs/en/env-vars) environment variable takes precedence when both are set, and it is subject to the same rule: deliver it through managed settings or `~/.claude/settings.json`, not a shell export. [Run Claude Code behind a corporate launcher](/docs/en/corporate-launcher) covers the contract the launcher must satisfy, what it does and doesn't reach, and how to roll it out.
<Note>
An already-running supervisor keeps the launch configuration it started with. After deploying the launcher setting, run [`claude daemon stop --any`](/docs/en/agent-view#the-supervisor-process) so the next `claude agents` or `--bg` starts a supervisor that honors it. An installed service takes `claude daemon stop` without `--any`.
</Note>
## Streaming idle watchdogs
Claude Code runs three independent timers that abort a streaming model response when it goes quiet, so a dead connection fails and retries instead of hanging. Each timer watches a different signal:
| Timer | Aborts when | Runs on | Default timeout |
| :------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------- |
| Event-level watchdog | No response events parse. On connections where the byte-level watchdog runs, arriving bytes, including keep-alive pings, also reset this watchdog, for up to about five minutes without a parsed event | Every provider | 300 seconds |
| Byte-level watchdog | No bytes arrive on the wire, including SSE keep-alive pings | Direct Anthropic API, [Claude Platform on AWS](/docs/en/claude-platform-on-aws), and [gateway](/docs/en/gateways) connections, including a custom `ANTHROPIC_BASE_URL`. Opt-in on Amazon Bedrock `vnd.amazon.eventstream` responses with `CLAUDE_ENABLE_BYTE_WATCHDOG_BEDROCK=1`; doesn't run on Google Cloud's Agent Platform or Microsoft Foundry | 180 seconds on the direct Anthropic API, 300 seconds elsewhere |
| Body idle timeout | No bytes arrive for 5 minutes | Providers other than the direct Anthropic API and Claude Platform on AWS, unless [`API_FORCE_IDLE_TIMEOUT`](/docs/en/env-vars) changes that | 5 minutes |
Configure the timers with these variables, each detailed in the [environment variables reference](/docs/en/env-vars):
* `CLAUDE_ENABLE_STREAM_WATCHDOG` and `CLAUDE_ENABLE_BYTE_WATCHDOG` force the corresponding watchdog on with `1` or off with `0`, within the connections the table lists; neither variable extends a watchdog to a connection type it doesn't cover.
* `CLAUDE_STREAM_IDLE_TIMEOUT_MS` sets both watchdogs' timeout. Claude Code raises values below 5 minutes to 5 minutes, and caps the value at 30 minutes for the byte-level watchdog.
* `CLAUDE_BYTE_STREAM_IDLE_TIMEOUT_MS` sets the byte-level watchdog's timeout alone, clamped to between 10 seconds and 30 minutes, and takes precedence over `CLAUDE_STREAM_IDLE_TIMEOUT_MS` for that watchdog.
* `API_FORCE_IDLE_TIMEOUT` set to `0` turns the body idle timeout off, and set to `1` turns it on for every provider. The watchdogs run independently of it, so to let a stream pause longer than their thresholds, also raise or disable them.
When a timer aborts a stalled stream, Claude Code handles it like any other mid-stream failure: it retries, keeps completed output with an [incomplete-response notice](/docs/en/errors#the-response-above-may-be-incomplete), or ends the turn, depending on where the response stood.
## Network access requirements
Claude Code requires access to the following URLs. Allowlist these in your proxy configuration and firewall rules, especially in containerized or restricted network environments. The first-run setup connectivity check points here when it can't reach `api.anthropic.com` or `platform.claude.com`; see [Unable to connect to Anthropic services](/docs/en/errors#unable-to-connect-to-anthropic-services) for the check's messages and recovery steps.
| URL | Required for |
| ------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `api.anthropic.com` | Claude API requests, including the WebFetch [domain safety check](/docs/en/data-usage#webfetch-domain-safety-check), feature flag fetches, and telemetry event logging |
| `claude.ai` | claude.ai account authentication |
| `claude.com` | claude.ai account sign-in opens a `claude.com` page in the browser, which redirects to `claude.ai`; pre-approved WebFetch documentation lookups also reach this host from the CLI |
| `platform.claude.com` | Anthropic Console account authentication. OAuth token exchange, refresh, and revocation also go to this host for claude.ai accounts, so both Console and claude.ai sign-ins require it |
| `mcp-proxy.anthropic.com` | [MCP connectors from claude.ai](/docs/en/mcp#use-mcp-servers-from-claude-ai), including connectors an organization administrator configures. Connector traffic routes through this proxy; connectors are enabled by default for claude.ai-authenticated users. To disable, set [`ENABLE_CLAUDEAI_MCP_SERVERS=false`](/docs/en/env-vars) or the [`disableClaudeAiConnectors`](/docs/en/settings#available-settings) setting |
| `downloads.claude.ai` | Plugin executable downloads; native installer, native auto-updater, and update version checks |
| `storage.googleapis.com` | Install counts and plugin metadata shown in `/plugin`. Signed [artifact](/docs/en/artifacts) uploads try this host first; publishing falls back to `api.anthropic.com` when it is blocked |
| `storage.googleapis.com` | Native installer and native auto-updater on versions prior to 2.1.116 |
| `registry.npmjs.org` | Plugin installs (fetching npm-source plugin packages and installing plugins' Node.js package dependencies), `npx`-launched MCP servers, and the package registry for npm and bun installs of Claude Code itself |
| `bridge.claudeusercontent.com` | [Claude in Chrome](/docs/en/chrome) extension WebSocket bridge |
| `raw.githubusercontent.com` | Changelog feed for [`/release-notes`](/docs/en/commands) and the release notes shown after updating |
| `http-intake.logs.us5.datadoghq.com` | Operational telemetry events, sent only when the CLI uses the Anthropic API directly, never for Amazon Bedrock, Google Cloud's Agent Platform, or Microsoft Foundry. Optional: disable with [`DISABLE_TELEMETRY`](/docs/en/data-usage#telemetry-services) or `DO_NOT_TRACK` |
| `browser-intake-us5-datadoghq.com` | Operational error reports, sent when the CLI uses the Anthropic API directly and a server-side rollout gate enables them. Optional: disable with `DISABLE_ERROR_REPORTING` or `DISABLE_TELEMETRY`; see [Telemetry services](/docs/en/data-usage#telemetry-services) |
| `formulae.brew.sh` | Update version checks on Homebrew installs. Other install methods don't contact this host |
| `code.claude.com` | Claude Code documentation lookups by the built-in claude-code-guide agent and pre-approved WebFetch requests. Blocking this host only affects documentation lookups |
If you install Claude Code through npm or manage your own binary distribution, end users don't need the native installer and auto-updater uses of `downloads.claude.ai`, but npm and bun installs need their package registry, `registry.npmjs.org`, unless your organization mirrors it. The other uses in the table apply regardless of install method.
The two Datadog intake hosts carry only optional operational telemetry, and setting [`CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC`](/docs/en/env-vars) disables both. Sessions on third-party providers never send to these hosts, even when a platform sets [`CLAUDE_CODE_PROVIDER_MANAGED_BY_HOST`](/docs/en/env-vars) and telemetry metrics default on. See [Telemetry services](/docs/en/data-usage#telemetry-services) for everything Claude Code sends and how to disable it before finalizing your allowlist.
When using [Amazon Bedrock](/docs/en/amazon-bedrock), [Google Cloud's Agent Platform](/docs/en/google-vertex-ai), [Microsoft Foundry](/docs/en/microsoft-foundry), or a signed-in [Claude apps gateway](/docs/en/claude-apps-gateway) session, model traffic and authentication go to your provider or gateway instead of `api.anthropic.com`, `claude.ai`, or `platform.claude.com`. The WebFetch tool still calls `api.anthropic.com` for its [domain safety check](/docs/en/data-usage#webfetch-domain-safety-check) unless you set `skipWebFetchPreflight: true` in [settings](/docs/en/settings).
When routing through an [LLM gateway](/docs/en/llm-gateway) with [`ANTHROPIC_BASE_URL`](/docs/en/llm-gateway-connect#set-the-base-url-and-credential), the [fast mode](/docs/en/fast-mode) availability check still calls `api.anthropic.com` rather than the gateway base URL. The check does honor a configured HTTP proxy, so where a network block is the cause, an allowlist entry for `api.anthropic.com` in the proxy is the fix. A network block fails the check only where the host is unreachable even through the proxy, and fast mode then reports a connectivity error. The same connectivity error appears when the check presents a gateway-issued credential that Anthropic rejects; allowlisting doesn't help there, since nothing is blocked. See [use fast mode behind proxies and LLM gateways](/docs/en/fast-mode#use-fast-mode-behind-proxies-and-llm-gateways) for the variables that restore it.
[Claude Code on the web](/docs/en/claude-code-on-the-web) in Anthropic-hosted environments and [Code Review](/docs/en/code-review) connect to your repositories from Anthropic-managed infrastructure; sessions in a [self-hosted environment](/docs/en/self-hosted-environments) connect from inside your network, unless the runner opts into the [Anthropic git proxy](/docs/en/self-hosted-environments-deploy#use-the-anthropic-git-proxy), which fetches from Anthropic's side. If your GitHub Enterprise Cloud organization restricts access by IP address, enable [IP allow list inheritance for installed GitHub Apps](https://docs.github.com/en/enterprise-cloud@latest/organizations/keeping-your-organization-secure/managing-security-settings-for-your-organization/managing-allowed-ip-addresses-for-your-organization#allowing-access-by-github-apps). The Claude GitHub App registers its IP ranges, so enabling this setting allows access without manual configuration. To [add the ranges to your allow list manually](https://docs.github.com/en/enterprise-cloud@latest/organizations/keeping-your-organization-secure/managing-security-settings-for-your-organization/managing-allowed-ip-addresses-for-your-organization#adding-an-allowed-ip-address) instead, or to configure other firewalls, see the [Anthropic API IP addresses](https://platform.claude.com/docs/en/api/ip-addresses).
For self-hosted [GitHub Enterprise Server](/docs/en/github-enterprise-server) instances behind a firewall, allowlist the same [Anthropic API IP addresses](https://platform.claude.com/docs/en/api/ip-addresses) so Anthropic infrastructure can reach your GHES host to clone repositories and post review comments. Sessions in a [self-hosted environment](/docs/en/self-hosted-environments-deploy#configure-git) reach your GHES host from inside your network instead, so that exposure applies only to Anthropic-hosted sessions, to hosted pre-session flows such as the repository picker, and to self-hosted runners that opt into the [Anthropic git proxy](/docs/en/self-hosted-environments-deploy#use-the-anthropic-git-proxy), which fetches from Anthropic's side. For a GHES host that's only routable inside your network, the [SCM connector](/docs/en/self-hosted-environments-reference#scm-connector-flags) carries the hosted pre-session flows over an outbound connection instead, so the allowlist isn't needed for them.
### Desktop and claude.ai
The preceding table covers the standalone CLI. The Claude Desktop app and claude.ai in a browser load their application code and user content from additional Anthropic CDN hosts, including `assets-proxy.anthropic.com` and the `*.claudeusercontent.com` origins that serve [artifacts](/docs/en/artifacts). Allowing `claude.ai` while blocking those hosts produces a blank page rather than an error. See [network access requirements](/docs/en/desktop#network-access-requirements) on the Desktop page.
## Additional resources
* [Claude Code settings](/docs/en/settings)
* [Environment variables reference](/docs/en/env-vars)
* [Troubleshooting guide](/docs/en/troubleshooting)
output-styles First recorded · 124 lines, first recorded
# Output styles ## Built-in output styles ## Change your output style ## Create a custom output style ### Frontmatter ## How output styles work ## Comparisons to related features ## Related resources
The first capture of this source. The page was already there, and this is what it said.
# Output styles
> Adapt Claude Code for uses beyond software engineering
Output styles change how Claude responds, not what Claude knows. They modify the system prompt to set role, tone, and output format. Use one when you keep re-prompting for the same voice or format every turn, or when you want Claude to act as something other than a software engineer.
A custom output style adds your instructions to the system prompt and lets you choose whether to keep Claude Code's built-in software engineering instructions. Keep them when you're changing how Claude communicates but still coding, like always answering with a diagram. Leave them out when Claude isn't doing software engineering at all, like a writing assistant or data analyst.
For instructions about your project, conventions, or codebase, use [CLAUDE.md](/docs/en/memory) instead.
## Built-in output styles
Claude Code's **Default** output style is the existing system prompt, designed to help you complete software engineering tasks efficiently.
There are three additional built-in output styles:
* **Proactive**: Claude executes immediately, makes reasonable assumptions instead of pausing for routine decisions, and prefers action over planning. This is stronger autonomous-execution guidance than [auto mode](/docs/en/permission-modes#eliminate-prompts-with-auto-mode) applies, and it works without changing your permission mode, so you still see permission prompts before tools run.
* **Explanatory**: Provides educational "Insights" in between helping you complete software engineering tasks. Helps you understand implementation choices and codebase patterns.
* **Learning**: Collaborative, learn-by-doing mode where Claude will not only share "Insights" while coding, but also ask you to contribute small, strategic pieces of code yourself. Claude Code will add `TODO(human)` markers in your code for you to implement.
## Change your output style
Pick a style in one of these ways:
* **Terminal**: run `/config` and select **Output style** to pick a style from a menu. Claude Code saves your selection to `.claude/settings.local.json` at the [local project level](/docs/en/settings).
* **Desktop app**: set the `outputStyle` field in a settings file, for example `.claude/settings.local.json`, the file the terminal menu writes. When you run `/config` there, Claude Code [opens **Settings > Claude Code**](/docs/en/desktop#what’s-not-available-in-desktop) rather than a menu.
<Note>The standalone `/output-style` command was deprecated in v2.1.73 and removed in v2.1.91. Use `/config` or edit the `outputStyle` setting directly.</Note>
To set a style without the menu, edit the `outputStyle` field directly in a settings file:
```json theme={null}
{
"outputStyle": "Explanatory"
}
```
Output style is part of the system prompt, which Claude Code reads once at session start. Changes take effect after `/clear` or a new session. See [How Claude Code uses prompt caching](/docs/en/prompt-caching#changing-output-style) for what an output style change does to the cache.
## Create a custom output style
A custom output style is a Markdown file: frontmatter for metadata, then the instructions to add to the system prompt.
<Steps>
<Step title="Create a Markdown file">
Save it at one of three levels. The file name becomes the style name unless you set `name` in the frontmatter.
* User: `~/.claude/output-styles`
* Project: `.claude/output-styles`
* Managed policy: `.claude/output-styles` inside the [managed settings directory](/docs/en/settings#settings-files)
Project output styles load from every `.claude/output-styles/` between the working directory and the repository root. When more than one of these nested directories defines a style with the same name, Claude Code uses the one closest to the working directory.
</Step>
<Step title="Add frontmatter and instructions">
Decide whether to keep Claude Code's software engineering instructions. Set `keep-coding-instructions: true` if you're changing how Claude communicates but still want it coding the same way. Leave it out if Claude won't be doing software engineering.
This example leads every explanation with a diagram while keeping Claude's coding behavior:
```markdown theme={null}
---
name: Diagrams first
description: Lead every explanation with a diagram
keep-coding-instructions: true
---
When explaining code, architecture, or data flow, start with a Mermaid diagram showing the structure, then explain in prose.
## Diagram conventions
Use `flowchart TD` for control flow and `sequenceDiagram` for request paths. Keep diagrams under 15 nodes.
```
</Step>
<Step title="Switch to your style">
Run `/config` in the terminal and select your style under **Output style**, or set `outputStyle` in a settings file to the style's name. It takes effect after `/clear` or the next time you start a session.
</Step>
</Steps>
[Plugins](/docs/en/plugins-reference) can also ship output styles in an `output-styles/` directory.
### Frontmatter
Output style files support these frontmatter fields:
| Frontmatter | Purpose | Default |
| :------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :---------------------- |
| `name` | Name of the output style, if not the file name | Inherits from file name |
| `description` | Description of the output style, shown in the `/config` picker | None |
| `keep-coding-instructions` | Keep Claude Code's built-in software engineering instructions | `false` |
| `force-for-plugin` | Plugin output styles only: apply this style automatically whenever the plugin is enabled, without requiring users to select it. Overrides the user's `outputStyle` setting. If multiple enabled plugins set this, Claude Code uses the first one loaded. | `false` |
## How output styles work
Output styles directly modify Claude Code's system prompt.
* Claude Code adds each output style's custom instructions to the end of the system prompt.
* All output styles trigger reminders for Claude to adhere to the output style instructions during the conversation.
* Custom output styles leave out Claude Code's built-in software engineering instructions, such as how to scope changes, write comments, and verify work, unless `keep-coding-instructions` is set to `true`.
Output styles apply to the main conversation only: a [subagent runs its own system prompt](/docs/en/sub-agents#what-loads-at-startup), so styles don't change how subagents respond. A [fork](/docs/en/sub-agents#fork-the-current-conversation) is the exception, because it inherits the parent's full system prompt.
Token usage depends on the style. Adding instructions to the system prompt increases input tokens, though prompt caching reduces this cost after the first request in a session. The built-in Explanatory and Learning styles produce longer responses than Default by design, which increases output tokens. For custom styles, output token usage depends on what your instructions tell Claude to produce.
## Comparisons to related features
Several features customize how Claude Code behaves. Output styles modify the system prompt directly and apply to every response. The others add instructions without changing the default system prompt, or scope them to a specific task.
| Feature | How it works | Use it when |
| :----------------------- | :----------------------------------------------------------- | :---------------------------------------------------------------------- |
| Output styles | Modifies the system prompt | You want a different role, tone, or default response format every turn |
| [CLAUDE.md](/docs/en/memory) | Adds a user message after the system prompt | Claude should always know your project conventions and codebase context |
| `--append-system-prompt` | Appends to the system prompt without removing anything | You want a one-off addition for a single invocation |
| [Agents](/docs/en/sub-agents) | Runs a subagent with its own system prompt, model, and tools | You want a separately scoped helper for a focused task |
| [Skills](/docs/en/skills) | Loads task-specific instructions when invoked or relevant | You have a reusable workflow |
## Related resources
* [Settings](/docs/en/settings): where the `outputStyle` field lives and how settings precedence works
* [Permission modes](/docs/en/permission-modes): how the Proactive style compares to auto mode
* [Plugins](/docs/en/plugins): package and distribute output styles alongside skills, hooks, and agents
* [Debug your configuration](/docs/en/debug-your-config): diagnose why an output style isn't taking effect
overview First recorded · 240 lines, first recorded
# Overview ## Get started ## What you can do ## Use Claude Code everywhere ## Next steps
The first capture of this source. The page was already there, and this is what it said.
# Overview
> Claude Code is an agentic coding tool that reads your codebase, edits files, runs commands, and integrates with your development tools. Available in your terminal, IDE, desktop app, and browser.
Claude Code is an AI-powered coding assistant that helps you build features, fix bugs, and automate development tasks. It understands your entire codebase and can work across multiple files and tools to get things done.
## Get started
Claude Code runs on several surfaces: the terminal, IDE extensions, a desktop app, and the web. Choose one from the tabs below to get started. Most surfaces require a [Claude subscription](https://claude.com/pricing?utm_source=claude_code\&utm_medium=docs\&utm_content=overview_pricing) or [Anthropic Console](https://console.anthropic.com/) account. The Terminal CLI and VS Code also support [third-party providers](/docs/en/third-party-integrations).
<Tabs>
<Tab title="Terminal">
The full-featured CLI for working with Claude Code directly in your terminal. Edit files, run commands, and manage your entire project from the command line.
To install Claude Code, use one of the following methods:
<Tabs>
<Tab title="Native Install (Recommended)">
**macOS, Linux, WSL:**
```bash theme={null}
curl -fsSL https://claude.ai/install.sh | bash
```
**Windows PowerShell:**
```powershell theme={null}
irm https://claude.ai/install.ps1 | iex
```
**Windows CMD:**
```batch theme={null}
curl -fsSL https://claude.ai/install.cmd -o install.cmd && install.cmd && del install.cmd
```
If you see `The token '&&' is not a valid statement separator`, you're in PowerShell, not CMD. If you see `'irm' is not recognized as an internal or external command`, you're in CMD, not PowerShell. Your prompt shows `PS C:\` when you're in PowerShell and `C:\` without the `PS` when you're in CMD.
If the install command fails with `syntax error near unexpected token '<'`, a `403`, or another curl error, see [Troubleshoot installation](/docs/en/troubleshoot-install#find-your-error) to match the error to a fix and for alternative install methods.
[Git for Windows](https://git-scm.com/downloads/win) is recommended on native Windows so Claude Code can use the Bash tool. If Git for Windows is not installed, Claude Code uses PowerShell as the shell tool instead. WSL setups do not need Git for Windows.
<Info>
Native installations automatically update in the background to keep you on the latest version.
</Info>
</Tab>
<Tab title="Homebrew">
```bash theme={null}
brew install --cask claude-code
```
Homebrew offers two casks. `claude-code` tracks the stable release channel, which is typically about a week behind and skips releases with major regressions. `claude-code@latest` tracks the latest channel and receives new versions as soon as they ship.
<Info>
Homebrew installations do not auto-update. Run `brew upgrade claude-code` or `brew upgrade claude-code@latest`, depending on which cask you installed, to get the latest features and security fixes.
</Info>
</Tab>
<Tab title="WinGet">
```powershell theme={null}
winget install Anthropic.ClaudeCode
```
<Info>
WinGet installations do not auto-update. Run `winget upgrade Anthropic.ClaudeCode` periodically to get the latest features and security fixes.
</Info>
</Tab>
</Tabs>
You can also install with [apt, dnf, or apk](/docs/en/setup#install-with-linux-package-managers) on Debian, Fedora, RHEL, and Alpine.
Then start Claude Code in any project. Replace `your-project` with the path to a project directory on your machine:
```bash theme={null}
cd your-project
claude
```
You'll be prompted to log in on first use. If you've set the `ANTHROPIC_API_KEY` environment variable, Claude Code skips the login prompt and asks you to approve the key instead. That's it! [Continue with the Quickstart →](/docs/en/quickstart)
<Tip>
See [advanced setup](/docs/en/setup) for installation options, manual updates, or uninstallation instructions. Visit [installation troubleshooting](/docs/en/troubleshoot-install) if you hit issues.
</Tip>
</Tab>
<Tab title="VS Code">
The VS Code extension provides inline diffs, @-mentions, plan review, and conversation history directly in your editor.
* [Install for VS Code](vscode:extension/anthropic.claude-code)
* [Install for Cursor](cursor:extension/anthropic.claude-code)
Or search for "Claude Code" in the Extensions view (`Cmd+Shift+X` on Mac, `Ctrl+Shift+X` on Windows/Linux). After installing, open the Command Palette (`Cmd+Shift+P` / `Ctrl+Shift+P`), type "Claude Code", and select **Open in New Tab**.
[Get started with VS Code →](/docs/en/vs-code#get-started)
</Tab>
<Tab title="Desktop app">
A standalone app for running Claude Code outside your IDE or terminal. Review diffs visually, run multiple sessions side by side, schedule recurring tasks, and kick off cloud sessions.
Download and install:
* [macOS](https://claude.ai/api/desktop/darwin/universal/dmg/latest/redirect?utm_source=claude_code\&utm_medium=docs) (Intel and Apple Silicon)
* [Windows](https://claude.ai/api/desktop/win32/x64/setup/latest/redirect?utm_source=claude_code\&utm_medium=docs) (x64)
* [Windows ARM64](https://claude.ai/api/desktop/win32/arm64/setup/latest/redirect?utm_source=claude_code\&utm_medium=docs)
After installing, launch Claude, sign in, and click the **Code** tab to start coding. A [paid subscription](https://claude.com/pricing?utm_source=claude_code\&utm_medium=docs\&utm_content=overview_desktop_pricing) is required.
[Learn more about the desktop app →](/docs/en/desktop-quickstart)
</Tab>
<Tab title="Web">
Run Claude Code in your browser with no local setup. Kick off long-running tasks and check back when they're done, work on repos you don't have locally, or run multiple tasks in parallel. Available on desktop browsers and [the Claude app for iOS and Android](/docs/en/mobile).
Start coding at [claude.ai/code](https://claude.ai/code).
[Get started on the web →](/docs/en/web-quickstart)
</Tab>
<Tab title="JetBrains">
A plugin for IntelliJ IDEA, PyCharm, WebStorm, and other JetBrains IDEs with interactive diff viewing and selection context sharing.
Install the [Claude Code plugin](https://plugins.jetbrains.com/plugin/27310-claude-code-beta-) from the JetBrains Marketplace and restart your IDE. The plugin requires the Claude Code CLI, installed separately; see the [JetBrains setup steps](/docs/en/jetbrains#installation).
[Get started with JetBrains →](/docs/en/jetbrains)
</Tab>
</Tabs>
## What you can do
Here are some of the ways you can use Claude Code:
<AccordionGroup>
<Accordion title="Automate the work you keep putting off" icon="wand-magic-sparkles">
Claude Code handles the tedious tasks that eat up your day: writing tests for untested code, fixing lint errors across a project, resolving merge conflicts, updating dependencies, and writing release notes.
```bash theme={null}
claude "write tests for the auth module, run them, and fix any failures"
```
</Accordion>
<Accordion title="Build features and fix bugs" icon="hammer">
Describe what you want in plain language. Claude Code plans the approach, writes the code across multiple files, and verifies it works.
For bugs, paste an error message or describe the symptom. Claude Code traces the issue through your codebase, identifies the root cause, and implements a fix. See [common workflows](/docs/en/common-workflows) for more examples.
</Accordion>
<Accordion title="Create commits and pull requests" icon="code-branch">
Claude Code works directly with git. It stages changes, writes commit messages, creates branches, and opens pull requests.
```bash theme={null}
claude "commit my changes with a descriptive message"
```
In CI, you can automate code review and issue triage with [GitHub Actions](/docs/en/github-actions) or [GitLab CI/CD](/docs/en/gitlab-ci-cd).
</Accordion>
<Accordion title="Connect your tools with MCP" icon="plug">
The [Model Context Protocol (MCP)](/docs/en/mcp) is an open standard for connecting AI tools to external data sources. With MCP, Claude Code can read your design docs in Google Drive, update tickets in Jira, pull data from Slack, or use your own custom tooling. The [MCP quickstart](/docs/en/mcp-quickstart) connects your first server end to end.
</Accordion>
<Accordion title="Customize with instructions, skills, and hooks" icon="sliders">
[`CLAUDE.md`](/docs/en/memory) is a markdown file you add to your project root that Claude Code reads at the start of every session. Use it to set coding standards, architecture decisions, preferred libraries, and review checklists. Claude also builds [auto memory](/docs/en/memory#auto-memory) as it works, saving learnings like build commands and debugging insights across sessions without you writing anything.
Create [skills](/docs/en/skills) to package repeatable workflows your team can share, like `/review-pr` or `/deploy-staging`.
[Hooks](/docs/en/hooks) let you run shell commands before or after Claude Code actions, like auto-formatting after every file edit or running lint before a commit.
</Accordion>
<Accordion title="Run agent teams and build custom agents" icon="users">
Spawn [multiple Claude Code agents](/docs/en/sub-agents) that work on different parts of a task simultaneously. A lead agent coordinates the work, assigns subtasks, and merges results.
To run several full sessions in parallel and watch them from one screen, use [background agents](/docs/en/agent-view). For fully custom workflows, the [Agent SDK](/docs/en/agent-sdk/overview) lets you build your own agents powered by Claude Code's tools and capabilities, with full control over orchestration, tool access, and permissions.
</Accordion>
<Accordion title="Pipe, script, and automate with the CLI" icon="terminal">
Claude Code is composable and follows the Unix philosophy. Pipe logs into it, run it in CI, or chain it with other tools:
```bash theme={null}
# Analyze recent log output
tail -200 app.log | claude -p "Slack me if you see any anomalies"
# Automate translations in CI
claude -p "translate new strings into French and raise a PR for review"
# Bulk operations across files
git diff main --name-only | claude -p "review these changed files for security issues"
```
See the [CLI reference](/docs/en/cli-reference) for the full set of commands and flags.
</Accordion>
<Accordion title="Schedule recurring tasks" icon="clock">
Run Claude on a schedule to automate work that repeats: morning PR reviews, overnight CI failure analysis, weekly dependency audits, or syncing docs after PRs merge.
* [Routines](/docs/en/routines) run in the cloud, so they keep running even when your computer is off. They can also trigger on API calls or GitHub events. Create them from the web, the Desktop app, or by running `/schedule` in the CLI.
* [Desktop scheduled tasks](/docs/en/desktop-scheduled-tasks) run on your machine, with direct access to your local files and tools
* [`/loop`](/docs/en/scheduled-tasks) repeats a prompt within a CLI session for quick polling
</Accordion>
<Accordion title="Work from anywhere" icon="globe">
Sessions aren't tied to a single surface. Move work between them as your context changes:
* Step away from your desk and keep working from your phone or any browser with [Remote Control](/docs/en/remote-control)
* Message [Dispatch](/docs/en/desktop#sessions-from-dispatch) a task from your phone and open the Desktop session it creates
* Kick off a long-running task on the [web](/docs/en/claude-code-on-the-web) or the [Claude mobile app](/docs/en/mobile), then pull it into your terminal with `claude --teleport`. Teleport requires a claude.ai subscription.
* Run `/desktop` to continue your current terminal session in the [Desktop app](/docs/en/desktop), where you can review diffs visually. The `/desktop` handoff requires a claude.ai subscription. Available on macOS and x64 Windows.
* Route tasks from team chat: mention `@Claude` in [Slack](/docs/en/slack) with a bug report and get a pull request back
</Accordion>
</AccordionGroup>
## Use Claude Code everywhere
Each [surface](/docs/en/glossary#surface) connects to the same underlying Claude Code engine, so your CLAUDE.md files, settings, and MCP servers work across all of them.
Beyond the [Terminal](/docs/en/quickstart), [VS Code](/docs/en/vs-code), [JetBrains](/docs/en/jetbrains), [Desktop](/docs/en/desktop), and [Web](/docs/en/claude-code-on-the-web) surfaces above, Claude Code integrates with CI/CD, chat, and browser workflows:
| I want to... | Best option |
| ------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------- |
| Continue a local session from my phone or another device | [Remote Control](/docs/en/remote-control) |
| Push events from Telegram, Discord, iMessage, or my own webhooks into a session | [Channels](/docs/en/channels) |
| Start a task locally, continue on mobile | [`claude --cloud`](/docs/en/claude-code-on-the-web#from-terminal-to-web), then the [Claude mobile app](/docs/en/mobile) |
| Run Claude on a recurring schedule | [Routines](/docs/en/routines) or [Desktop scheduled tasks](/docs/en/desktop-scheduled-tasks) |
| Automate PR reviews and issue triage | [GitHub Actions](/docs/en/github-actions) or [GitLab CI/CD](/docs/en/gitlab-ci-cd) |
| Get automatic code review on every PR | [GitHub Code Review](/docs/en/code-review) |
| Route bug reports from Slack to pull requests | [Slack](/docs/en/slack) |
| Debug live web applications | [Chrome](/docs/en/chrome) |
| Build custom agents for your own workflows | [Agent SDK](/docs/en/agent-sdk/overview) |
## Next steps
Once you've installed Claude Code, these guides help you go deeper.
* [Quickstart](/docs/en/quickstart): walk through your first real task, from exploring a codebase to committing a fix
* [Store instructions and memories](/docs/en/memory): give Claude persistent instructions with CLAUDE.md files and auto memory
* [Common workflows](/docs/en/common-workflows) and [best practices](/docs/en/best-practices): patterns for getting the most out of Claude Code
* [A harness for every task](https://claude.com/blog/a-harness-for-every-task-dynamic-workflows-in-claude-code): how the Claude Code team uses [dynamic workflows](/docs/en/workflows) to orchestrate subagents at scale
* [Settings](/docs/en/settings): customize Claude Code for your workflow
* [Troubleshooting](/docs/en/troubleshooting): solutions for common issues
* [code.claude.com](https://code.claude.com/): demos, pricing, and product details
permission-modes First recorded · 484 lines, first recorded
# Choose a permission mode ## Available modes ## Switch permission modes ## Auto-approve file edits with acceptEdits mode ## Analyze before you edit with plan mode ### Review and approve a plan ### Set plan mode as the default ### What the classifier blocks by default ### Boundaries you state in conversation ### When auto mode falls back ## Allow only pre-approved tools with dontAsk mode ## Skip all checks with bypassPermissions mode ## Protected paths ## See also
The first capture of this source. The page was already there, and this is what it said.
# Choose a permission mode
> Control whether Claude asks before editing files or running commands. Cycle modes with Shift+Tab in the CLI or use the mode selector in VS Code, Desktop, and claude.ai.
When Claude wants to edit a file, run a shell command, or make a network request, it pauses and asks you to approve the action. Permission modes control how often that pause happens. The mode you pick shapes the flow of a session: Manual mode has you review each action as it comes, while looser modes let Claude work in longer uninterrupted stretches and report back when done. Pick more oversight for sensitive work, or fewer interruptions when you trust the direction.
## Available modes
Each mode makes a different tradeoff between convenience and oversight. The table below shows what Claude can do without a permission prompt in each mode.
| Mode | What runs without asking | Best for |
| :------------------------------------------------------------------ | :-------------------------------------------------------------------------------------------------------- | :-------------------------------------- |
| `default` | Reads only | Getting started, sensitive work |
| [`acceptEdits`](#auto-approve-file-edits-with-acceptedits-mode) | Reads, file edits, and common filesystem commands (`mkdir`, `touch`, `mv`, `cp`, etc.) | Iterating on code you're reviewing |
| [`plan`](#analyze-before-you-edit-with-plan-mode) | Reads, plus classifier-approved commands when [auto mode](#eliminate-prompts-with-auto-mode) is available | Exploring a codebase before changing it |
| [`auto`](#eliminate-prompts-with-auto-mode) | Everything, with background safety checks | Long tasks, reducing prompt fatigue |
| [`dontAsk`](#allow-only-pre-approved-tools-with-dontask-mode) | Only pre-approved tools | Locked-down CI and scripts |
| [`bypassPermissions`](#skip-all-checks-with-bypasspermissions-mode) | Everything | Isolated containers and VMs only |
The mode that reviews every action is named **Manual** in the CLI, in `claude --help`, in the VS Code and JetBrains extensions, and in the desktop app. Its config value is `default`, which is what hooks and SDK integrations use. The CLI accepts `manual` as an alias wherever you type the value, for example `claude --permission-mode manual` or `"defaultMode": "manual"`. The Manual label and the `manual` alias require Claude Code v2.1.200 or later. The desktop app's label doesn't depend on your CLI version.
Writes to [protected paths](#protected-paths) are never auto-approved except in `bypassPermissions` mode and in planning sessions with bypass permissions available.
Modes set the baseline. Layer [permission rules](/docs/en/permissions#manage-permissions) on top to pre-approve or block specific tools. These controls apply in every mode, including `bypassPermissions`:
* deny rules and explicit ask rules, which apply to every tool but can't block [`EndConversation`](/docs/en/tools-reference#endconversation-tool-behavior) while any other tool remains
* the [org `ask` setting on connector tools](/docs/en/mcp#organization-controls-on-connector-tools)
* the [`requiresUserInteraction`](/docs/en/mcp#require-approval-for-a-specific-tool) marker
Allow rules have no effect in `bypassPermissions` because everything else is already approved.
## Switch permission modes
You can switch modes mid-session, at startup, or as a persistent default. The mode is set through these controls, not by asking Claude in chat. Select your interface below to see how to change it.
<Tabs>
<Tab title="CLI">
**During a session**: press `Shift+Tab` to cycle `default` → `acceptEdits` → `plan`. The status bar shows the active mode as `⏸ plan mode on`, `⏵⏵ accept edits on`, `⏵⏵ auto mode on`, `⏵⏵ don't ask on`, or `⏵⏵ bypass permissions on`. Manual mode, `default` in that cycle, shows a gray `⏸ manual mode on` badge.
Not every mode is in the default cycle:
* `auto`: appears when your account meets the [auto mode requirements](#eliminate-prompts-with-auto-mode); cycling to it switches modes without a confirmation prompt
* `bypassPermissions`: appears after you start with `--permission-mode bypassPermissions`, `--dangerously-skip-permissions`, `--allow-dangerously-skip-permissions`, or `permissions.defaultMode: "bypassPermissions"` in [settings](/docs/en/settings#permission-settings); the `--allow-` variant adds the mode to the cycle without activating it
* `dontAsk`: never appears in the cycle; set it with `--permission-mode dontAsk`
Enabled optional modes slot in after `plan`, with `bypassPermissions` first and `auto` last. If you have both enabled, you will cycle through `bypassPermissions` on the way to `auto`.
**At startup**: pass the mode as a flag.
```bash theme={null}
claude --permission-mode plan
```
**As a default**: set `defaultMode` in a [settings file](/docs/en/settings#settings-files) such as `~/.claude/settings.json`:
```json theme={null}
{
"permissions": {
"defaultMode": "acceptEdits"
}
}
```
The same `--permission-mode` flag works with `-p` for [non-interactive runs](/docs/en/headless).
</Tab>
<Tab title="VS Code">
**During a session**: click the mode indicator at the bottom of the prompt box.
**As a default**: set `claudeCode.initialPermissionMode` in your VS Code user settings, or use the Claude Code extension settings panel.
The mode indicator shows these labels, mapped to the mode each one applies:
| UI label | Mode |
| :----------------- | :------------------ |
| Manual | `default` |
| Edit automatically | `acceptEdits` |
| Plan | `plan` |
| Auto | `auto` |
| Bypass permissions | `bypassPermissions` |
Auto mode appears in the mode indicator when your account meets every requirement listed in the [auto mode section](#eliminate-prompts-with-auto-mode). The `claudeCode.initialPermissionMode` setting does not accept `auto`. To switch a session into auto mode, select **Auto** from the mode indicator.
Bypass permissions requires the **Allow dangerously skip permissions** toggle in the extension settings before it appears in the mode indicator.
See the [VS Code guide](/docs/en/vs-code) for extension-specific details.
</Tab>
<Tab title="JetBrains">
The JetBrains plugin runs Claude Code in the IDE terminal, so switching modes works the same as in the CLI: press `Shift+Tab` to cycle, or pass `--permission-mode` when launching.
</Tab>
<Tab title="Desktop">
**During a session**: in the Code tab, use the mode selector next to the send button. Not every mode appears in the selector:
* **Auto**: appears when your account meets the [auto mode requirements](#eliminate-prompts-with-auto-mode)
* **Bypass permissions**: requires the **Allow bypass permissions mode** toggle in Desktop settings on Pro and Max plans; on Team and Enterprise plans, organization policy controls it instead
The Cowork tab doesn't use these modes. Cowork has its own permission modes, enabled separately, and the Cowork tab shows no mode selector at all until a mode beyond its default is enabled for your account. See the [Cowork docs](https://claude.com/docs/cowork/overview).
For desktop-specific details, see [Choose a permission mode](/docs/en/desktop#choose-a-permission-mode) in the Desktop guide.
**As a default**: set `defaultMode` in [settings](/docs/en/settings#settings-files). The desktop app reads the same settings files as the CLI and applies the mode to new local sessions.
A mode you pick in the mode selector is remembered per folder and takes precedence over `defaultMode` for that folder. Plan is the exception: picking it applies to the current session only.
This example sets Plan mode as the default for new local sessions:
```json theme={null}
{
"permissions": {
"defaultMode": "plan"
}
}
```
</Tab>
<Tab title="Web and mobile">
Use the mode dropdown next to the prompt box on [claude.ai/code](https://claude.ai/code) or in the mobile app. Permission prompts appear in claude.ai for approval. Which modes appear depends on where the session runs:
* **Cloud sessions** on [Claude Code on the web](/docs/en/claude-code-on-the-web): Accept edits, Plan, and Auto. Accept edits corresponds to `default` mode: cloud sessions pre-approve file edits regardless of mode, so the dropdown shows Accept edits instead of Manual. Cloud sessions still honor `defaultMode: "acceptEdits"` from settings. Auto mode appears only when your organization allows it and the selected model supports it. Bypass permissions isn't available.
* **[Remote Control](/docs/en/remote-control) sessions** on your local machine: Manual, Accept edits, and Plan. You can't select Auto or Bypass permissions from the app. The dropdown shows the mode the local session is in, including a mode set from the terminal, and updates when the mode changes in the app or in the terminal. The one exception is Bypass permissions: the session never reports that mode to claude.ai, so switching into it from the terminal doesn't change what the dropdown shows. Before v2.1.202, sessions connected with `/remote-control` or `claude --remote-control` didn't report their mode at all, so claude.ai and the mobile app could show a mode the session wasn't in. The mismatch affected only the label: Claude Code generated permission prompts from the session's actual mode, and they still appeared in the app for approval.
For Remote Control, the host must be signed in with your claude.ai account; API keys are not supported. You can also set the starting mode when launching the host:
```bash theme={null}
claude remote-control --permission-mode acceptEdits
```
</Tab>
</Tabs>
## Auto-approve file edits with acceptEdits mode
`acceptEdits` mode lets Claude create and edit files in your working directory without prompting. The status bar shows `⏵⏵ accept edits on` while this mode is active.
In addition to file edits, `acceptEdits` mode auto-approves common filesystem Bash commands: `mkdir`, `touch`, `rm`, `rmdir`, `mv`, `cp`, and `sed`. These commands are also auto-approved when prefixed with safe environment variables such as `LANG=C` or `NO_COLOR=1`, or process wrappers such as `timeout`, `nice`, or `nohup`. Like file edits, auto-approval applies only to paths inside your working directory or `additionalDirectories`. Paths outside that scope, writes to [protected paths](#protected-paths), and all other Bash commands except the [built-in read-only set](/docs/en/permissions#read-only-commands) still prompt.
When the [PowerShell tool](/docs/en/tools-reference#powershell-tool) is enabled, `acceptEdits` mode also auto-approves `Set-Content`, `Add-Content`, `Clear-Content`, and `Remove-Item` on in-scope paths, along with their common aliases. The same scope and protected-path rules apply. A positional argument that contains a quote character, such as the apostrophe in `Set-Content .\notes.txt "It's done"`, still prompts even on in-scope paths, because Claude Code can't statically validate an argument whose quoted and unquoted readings differ. Pass the content through a named parameter such as `-Value` to avoid the prompt.
Use `acceptEdits` when you want to review changes in your editor or via `git diff` after the fact rather than approving each edit inline.
Press `Shift+Tab` once from Manual mode to enter it, or start with it directly:
```bash theme={null}
claude --permission-mode acceptEdits
```
## Analyze before you edit with plan mode
Plan mode tells Claude to research and propose changes without making them. Claude reads files, runs shell commands to explore, and writes a plan, but does not edit your source. Except in sessions with [bypass permissions available](#skip-all-checks-with-bypasspermissions-mode), edits stay blocked until you approve the plan.
When [auto mode](/docs/en/auto-mode-config) is available and the `useAutoModeDuringPlan` setting is on, which it is by default, the classifier reviews shell commands during planning instead of prompting you. Approved commands run, and rejected ones are blocked. Otherwise, commands outside the [built-in read-only set](/docs/en/permissions#read-only-commands) prompt for approval, including when the sandbox's [auto-allow mode](/docs/en/sandboxing#sandbox-modes) is enabled. Sessions with [bypass permissions available](#skip-all-checks-with-bypasspermissions-mode) skip both paths; that section covers what still prompts there. In sessions without them, commands outside the read-only set prompted either way in v2.1.212 through v2.1.217.
Enter plan mode by pressing `Shift+Tab` or prefixing a single prompt with `/plan`. You can also start in plan mode from the CLI:
```bash theme={null}
claude --permission-mode plan
```
Press `Shift+Tab` again to leave plan mode without approving a plan.
### Review and approve a plan
When the plan is ready, Claude presents it and asks how to proceed. From that prompt you can choose:
* **Yes, and use auto mode**: approve and start in [auto mode](#eliminate-prompts-with-auto-mode). When auto mode is unavailable, this option reads **Yes, auto-accept edits**. Sessions started with bypass permissions enabled show **Yes, and bypass permissions** instead.
* **Yes, manually approve edits**: approve and review each edit individually.
* **No, keep planning**: stay in plan mode and tell Claude what to change.
Approving a plan exits plan mode and switches the session to the permission mode each approve option describes, so Claude starts editing. To plan again, cycle back to plan mode with `Shift+Tab`, or prefix your next prompt with `/plan`.
Press `Ctrl+G` to open the proposed plan in your default text editor and edit it directly before Claude proceeds. When [`showClearContextOnPlanAccept`](/docs/en/settings#available-settings) is enabled, the list gains a first option that approves the plan and clears the planning context.
Accepting a plan also names the session from the plan content automatically, unless you've already set a name with `--name` or `/rename`.
### Set plan mode as the default
In a session the [VS Code extension](/docs/en/vs-code) started, a settings-file `defaultMode` doesn't set the starting mode. Set `claudeCode.initialPermissionMode` to `plan` in your VS Code user settings instead. Elsewhere, to make plan mode the default for a project, set `defaultMode` in `.claude/settings.json`:
```json theme={null}
{
"permissions": {
"defaultMode": "plan"
}
}
```
<h2 id="eliminate-prompts-with-auto-mode">
Eliminate permission prompts with auto mode
</h2>
<Note>
Starting August 14, 2026, auto mode becomes the default permission mode for new sessions on Pro, Max, and Team plans. You can switch modes at any time. A default you set yourself stays in place unless you accept the one-time switch prompt, and a default your organization manages is unchanged. For details, see [the announcement](https://claude.com/blog/auto-mode-default-in-claude-code) on the blog.
</Note>
Auto mode lets Claude execute without routine permission prompts. A separate classifier model reviews actions before they run, blocking anything that escalates beyond your request, targets unrecognized infrastructure, or appears driven by hostile content Claude read. Explicit [ask rules](/docs/en/permissions#manage-permissions) still force a prompt.
The classifier also reviews each message Claude sends to another agent with [`SendMessage`](/docs/en/tools-reference), plain or structured, before Claude Code delivers it, both in auto mode and in [plan mode while the classifier reviews commands](#analyze-before-you-edit-with-plan-mode); the send review requires Claude Code v2.1.222 or later.
The classifier also decides removals targeting the filesystem root or home directory, such as `rm -rf /` and `rm -rf ~`, including when the removal sits inside command or process substitution. Before v2.1.218, the plain forms prompted for approval instead, and the substitution forms prompted in v2.1.208 through v2.1.217.
Auto mode also nudges Claude to keep working without stopping for clarifying questions, though Claude still asks when your prompt or a skill explicitly relies on it. For stronger autonomous behavior while keeping permission prompts, set the [Proactive output style](/docs/en/output-styles) instead.
<Warning>
Auto mode reduces permission prompts but does not guarantee safety. Use it for tasks where you trust the general direction, not as a replacement for review on sensitive operations.
</Warning>
Auto mode is available only when your account meets all of these requirements:
* **Plan**: All plans.
* **Organization**: on Team and Enterprise, auto mode is available by default. Administrators can turn it off for the organization by setting `permissions.disableAutoMode` to `"disable"` in [managed settings](/docs/en/permissions#managed-settings).
* **Model**: on the Anthropic API and [Claude Platform on AWS](/docs/en/claude-platform-on-aws), Claude Opus 4.6 or later, Sonnet 4.6 or later, or [Fable 5](/docs/en/model-config#work-with-fable-5). On Amazon Bedrock, Google Cloud's Agent Platform, Microsoft Foundry, and signed-in [Claude apps gateway](/docs/en/claude-apps-gateway) sessions, only Claude Sonnet 5, Opus 4.7 or later, and Fable 5. Older models, including Sonnet 4.5, Opus 4.5, Haiku, and claude-3 models, are not supported on any provider.
* **Provider**: available by default on the Anthropic API, Claude Platform on AWS, Amazon Bedrock, Google Cloud's Agent Platform, Microsoft Foundry, and signed-in Claude apps gateway sessions.
If Claude Code reports auto mode as unavailable, one of these requirements is unmet; this is not a transient outage. A separate message that names a model and says auto mode "cannot determine the safety" of an action means a classifier request failed; that failure is usually transient, but on Amazon Bedrock it can repeat until your account can invoke the named model. See the [error reference](/docs/en/errors#auto-mode-cannot-determine-the-safety-of-an-action) for the causes and what to do.
If you set `defaultMode: "auto"` in [settings](/docs/en/settings#available-settings) and the session starts in `default` mode with no error, the setting is likely in `.claude/settings.json` or `.claude/settings.local.json`. Claude Code v2.1.142 and later ignore `auto` from those files so a repository cannot grant itself auto mode. Move it to `~/.claude/settings.json`. In a session the [VS Code extension](/docs/en/vs-code) started, a settings-file `defaultMode` doesn't set the starting mode: select the mode from the extension's mode indicator instead.
<h3 id="enable-auto-mode-on-bedrock-agent-platform-or-foundry">
Auto mode on Bedrock, Agent Platform, or Foundry
</h3>
On [Amazon Bedrock](/docs/en/amazon-bedrock), [Google Cloud's Agent Platform](/docs/en/google-vertex-ai), [Microsoft Foundry](/docs/en/microsoft-foundry), and signed-in [Claude apps gateway](/docs/en/claude-apps-gateway) sessions, auto mode appears in the `Shift+Tab` cycle by default. Appearing in the cycle doesn't change the mode a session starts in. Except in sessions the [VS Code extension](/docs/en/vs-code) starts, where the extension resolves the starting mode, sessions still start in your [`defaultMode`](/docs/en/settings#available-settings), which is Manual unless you change it. Only Claude Sonnet 5, Opus 4.7 or later, and Fable 5 are supported on these providers.
To make auto mode the default starting mode, set `"permissions": {"defaultMode": "auto"}` in user or managed settings. In sessions the VS Code extension starts, select **Auto** from the mode indicator instead.
The [`/doctor`](/docs/en/commands#all-commands) checkup proposes this user-settings default on these providers the same way it does on the Anthropic API.
To prevent developers from using auto mode, set `disableAutoMode` to `"disable"` in [managed settings](/docs/en/permissions#managed-settings). This removes `auto` from the `Shift+Tab` cycle and rejects `--permission-mode auto` at startup.
In v2.1.158 through v2.1.206, auto mode was off on these providers until you set `CLAUDE_CODE_ENABLE_AUTO_MODE=1`, and Claude Code ignored `defaultMode: "auto"` on these providers unless the variable was also set. The variable is still accepted for compatibility and has no effect from v2.1.207 onward.
### What the classifier blocks by default
The classifier trusts your working directory and the remotes that were configured for it when the session started. A remote added or repointed during the session with `git remote add` or `git remote set-url` isn't trusted, and everything else is treated as external until you [configure trusted infrastructure](/docs/en/auto-mode-config). Before v2.1.200, remotes added mid-session were also trusted.
**Blocked by default**:
* Downloading and executing code, like `curl | bash`
* Sending sensitive data to external endpoints
* Production deploys and migrations
* Mass deletion on cloud storage
* Granting IAM or repo permissions
* Modifying shared infrastructure
* Irreversibly destroying files that existed before the session
* Force push
* Committing or pushing a change that would send secrets or sensitive data outside the repository when it runs, or widen what a deploy exposes. This covers a CI workflow or deploy configuration that hands a secret to a destination that doesn't already receive it, a script or setup step that reads a secret store and sends the data out, and a config change that widens what a deploy publishes, such as a registry, visibility, artifact, or sourcemap setting. The check applies on any branch, applies even when the repository is public, and fires when the change lands, whether or not that landing triggers the pipeline; clearing it requires naming the execution effect, not only the commit or push. Before v2.1.211, this check was scoped to the default branch instead: a push there was blocked when it carried sensitive content, changes concealed or misdescribed relative to what you asked for, content ported in from outside the repository, or routed around a review you asked for
* `git reset --hard`, `git checkout -- .`, `git restore .`, `git clean -fd`, `git stash drop`, or `git stash clear`, which the classifier presumes would discard uncommitted changes
* `git commit --amend` when the commit at HEAD was not created in this session
* From v2.1.198, `git commit --amend` when the commit at HEAD has already been pushed. A message-only reword is not blocked: `--amend -m` with nothing newly staged, on a commit that Claude created during this session
* `terraform destroy`, `pulumi destroy`, `cdk destroy`, or `terragrunt destroy`, and applying a plan that destroys resources
Claude Code v2.1.195 and later block more categories by default. Several depend on [environment](/docs/en/auto-mode-config#define-trusted-infrastructure) entries, such as sensitive remote targets and protected IaC scopes, that you can narrow to concrete names.
* Writing to a secret manager, or changing DNS records or TLS certificates
* Merging a pull request no human has approved, approving Claude's own pull request, or disabling CI checks
* Posting a comment that is itself a command to automation, such as `atlantis apply` or a bot's `/deploy` or `/merge`
* Toggling, ramping, or deleting a production feature flag
* Applying infrastructure changes to a protected IaC scope, or draining and removing cluster nodes
* Writes to a shared compute cluster that reach beyond the resource you named, such as a label selector or `--all` that catches other users' jobs
* Creating Kubernetes resources that run on every node or intercept cluster traffic, such as DaemonSets and admission webhooks
* Interactive shells or port-forwards into a sensitive remote target
* Opening a tunnel or reverse shell that makes a local service reachable from the public internet
* Printing a live credential or token into the transcript or a file
* Accessing a location listed as a sensitive data location in your [environment](/docs/en/auto-mode-config#define-trusted-infrastructure), or copying data out of one. As of v2.1.198 this also blocks sending data from one to an audience the entry excludes
* Routing a package install around your internal package registry to a public registry. As of v2.1.198, this also applies when you've told Claude an internal registry or mirror exists in the conversation, not only when one is listed in your environment
* Running a command with a flag that disarms a safety guard, like `--insecure`
* Launching an autonomous agent loop that runs without human approval or a sandbox, such as one started with `--dangerously-skip-permissions` or `--no-sandbox`. As of v2.1.198 this also covers running a third-party agent or eval harness with isolation and per-action approval disabled, such as a runner started with `--yes-always`
* [Claude in Chrome](/docs/en/chrome) browser actions that could send page content, cookies, or credentials off-origin
Claude Code v2.1.198 and later also block these by default:
* Deleting files in `/tmp`, `$TMPDIR`, or another shared scratch or cache directory by wildcard, glob, or age filter rather than by a specific named path
* Including sensitive details in content sent, uploaded, published, or written to other people or shared systems, when your own message didn't authorize those details for that recipient. PR and issue bodies, commit messages, and comments count as this kind of outbound content when the repository is outside the trust boundary or public, including your organization's own public repositories; internal file paths, code names, live API response data such as emails or account identifiers, and infrastructure identifiers count as sensitive details. The PR, issue, and commit-message scoping requires Claude Code v2.1.200 or later. Live personal data from an API response in a PR or issue body, such as an email address, an account or organization identifier, or a usage metric, requires you to name those details and the recipient regardless of the repository's visibility or trust boundary. That check requires Claude Code v2.1.203 or later
* Sending keystrokes to Claude Code's own tmux pane to drive its own interface, which the classifier treats as Claude changing its own permissions or oversight
Claude Code v2.1.200 and later also block these by default:
* Commenting out, deleting, or force-passing a test or assertion that guards security behavior, such as auth, access control, input validation, or sandboxing
* Deleting or tearing down a stateful resource Claude didn't create in the session, when no more specific deletion rule applies and you didn't name that resource
* Repointing an API base URL, proxy endpoint, webhook receiver, or registry mirror at a third-party host that doesn't fit the task, including in example files like `.env.example`
* Changing where pushes go with `git remote set-url` or `git remote add`, unless you named the new remote
* Pushing secrets or personal or entrusted data to a repository known to be public, or pushing confidential material there that isn't part of that repository's own work. A dotfiles repository's own subject matter is the one exception for personal or entrusted data, and content from a private repository reaching any public surface is blocked the same way; both refinements require Claude Code v2.1.203 or later. Before v2.1.203, personal data was grouped with confidential material and blocked only when it wasn't part of that repository's own work. When a repository's visibility isn't established, the classifier doesn't block on that alone; it judges the content against the other rules instead
* Opening a pull request against a different repository or organization, forking with `gh repo fork`, or pushing to a third-party repository, unless you named that external target
Claude Code v2.1.203 and later also block these by default:
* Content from a sensitive local store, or from a file whose name, path, or type marks it as sensitive, entering a commit, a push, PR or issue text, a gist or paste, or a package publish, unless you named both the source and the destination. Session transcripts and conversation logs, credential and configuration dot-folders such as SSH keys, cloud credentials, browser profiles, and shell history, and user-data exports all count, and the repository being private doesn't clear it
Claude Code v2.1.205 and later also block these by default:
* Writing to Claude Code session transcripts, the `.jsonl` history files under `~/.claude/projects/` or your configured config directory, whether directly or through a shell command. The rule also covers the metadata lines Claude Code appends to each transcript entry for its own checks. A transcript is session state that Claude Code writes, not a working file, and a tampered entry reaches every later check once you resume the session, so auto mode blocks these writes as defense in depth. Reading a transcript isn't blocked
* A recursive forced delete such as `rm -rf "$VAR"` or `Remove-Item -Recurse -Force $dir` whose target is a shell variable, or a glob rooted at one, that isn't assigned anywhere in the conversation the classifier sees. The value came only from earlier command output, which the classifier never receives, so the classifier can't verify the deletion target against the other deletion rules. The classifier reads the conversation rather than command output by design, so it blocks the call instead of guessing at the target. The block clears when you name the exact path being deleted, or when Claude re-runs the delete with the resolved literal path written into the command. Deletes whose target the classifier can resolve aren't affected
**Allowed by default**:
* Local file operations in your working directory
* Installing dependencies declared in your lock files or manifests
* Reading `.env` and sending credentials to their matching API
* Read-only HTTP requests
Cut at 300 lines. The page has the rest.
permissions First recorded · 564 lines, first recorded
# Configure permissions ## Permission system ## Manage permissions ## Permission modes ## Permission rule syntax ### Match all uses of a tool ### Use specifiers for fine-grained control ### Match by input parameter ### Wildcard patterns ### Tool name wildcards ## Tool-specific permission rules ### Bash #### Compound commands #### Read-only commands ### PowerShell ### Read and Edit ### WebFetch ### MCP ### Agent (subagents) ### Cd ## Extend permissions with hooks ## Working directories ### Additional directories grant file access, not configuration ## How permissions interact with sandboxing ## Managed settings ### Managed-only settings ## Settings precedence ## Project allow rules and workspace trust ### When your local settings file needs trust ### What runs before you trust a folder ## Example configurations ## See also
The first capture of this source. The page was already there, and this is what it said.
# Configure permissions
> Control what Claude Code can access and do with fine-grained permission rules, modes, and managed policies.
Claude Code supports fine-grained permissions so that you can specify exactly what the agent is allowed to do and what it can't. You can check permission settings into version control to share them with every developer in your organization, and each developer can customize their own.
## Permission system
Claude Code uses a tiered permission system to balance power and safety:
| Tool type | Example | Approval required | "Yes, don't ask again" behavior |
| :---------------- | :--------------- | :---------------------------------------------------------------------------------- | :------------------------------------- |
| Read-only | File reads, Grep | No, within the [working directory and additional directories](#working-directories) | N/A |
| Bash commands | Shell execution | Yes, except a built-in set of [read-only commands](#read-only-commands) | Permanently per repository and command |
| File modification | Edit/write files | Yes | Until session end |
When you choose "Yes, don't ask again" and the approval saves permanently, such as for a Bash command, Claude Code saves the rule to `.claude/settings.local.json` at the root of the git repository, resolved through [worktrees](/docs/en/worktrees) to the main checkout. The rule applies to future sessions anywhere in that repository, including sessions started in subdirectories and in worktrees. A file-modification approval isn't saved to the file: as the table shows, it lasts until the session ends. Outside a git repository, and when the repository root is your home directory, Claude Code saves the rule in the directory you started it from.
Before v2.1.211, Claude Code always saved the rule in the starting directory, so an approval granted in a worktree or subdirectory didn't apply to the rest of the repository. Rules that earlier versions saved in a subdirectory or worktree still apply to sessions started there.
On a Bash or PowerShell permission prompt, press `Ctrl+E` to show an explanation of the command: what it does, why Claude is running it, and what could go wrong, labeled **Low risk**, **Med risk**, or **High risk**. Claude Code sends the command and Claude's own description of the call to the model to generate the explanation only when you press `Ctrl+E`, not on every prompt. Showing the explanation doesn't run the command; press `Ctrl+E` again to hide it.
To turn the shortcut off, set [`permissionExplainerEnabled`](/docs/en/settings#global-config-settings) to `false` in `~/.claude.json`.
## Manage permissions
You can view and manage Claude Code's tool permissions with `/permissions`. This UI lists all permission rules and the `settings.json` file each rule comes from.
* **Allow** rules let Claude Code use the specified tool without manual approval.
* **Ask** rules prompt for confirmation whenever Claude Code tries to use the specified tool.
* **Deny** rules prevent Claude Code from using the specified tool.
Rules are evaluated in order: deny, then ask, then allow. The first match in that order determines the outcome, and rule specificity doesn't change the order.
A broad deny rule like `Bash(aws *)` blocks every matching call, including calls that also match a narrower allow rule like `Bash(aws s3 ls)`, so a deny rule can't carry allowlist exceptions. The same precedence applies between ask and allow: a matching ask rule prompts even when a more specific allow rule also matches the same call.
Deny rules behave differently depending on whether they name a tool or scope a pattern within one. A bare tool name like `Bash` removes the tool from Claude's context entirely, so Claude never sees it. Bare-name removal applies to every tool except [`EndConversation`](/docs/en/tools-reference#endconversation-tool-behavior): a deny rule can't remove it while any other tool remains, and an ask rule never prompts for it. A scoped rule like `Bash(rm *)` leaves the tool available and blocks matching calls when Claude attempts them.
<Note>
Permission rules are enforced by Claude Code, not by the model. Instructions in your prompt or `CLAUDE.md` shape what Claude tries to do, but they don't change what Claude Code allows. To grant or revoke access, use `/permissions`, the rules described here, a [permission mode](/docs/en/permission-modes), or a [PreToolUse hook](#extend-permissions-with-hooks).
</Note>
## Permission modes
Claude Code supports several permission modes that control how it approves tool calls. See [Permission modes](/docs/en/permission-modes) for when to use each one. Set the `defaultMode` in your [settings files](/docs/en/settings#settings-files). In sessions the VS Code extension starts, the extension resolves the starting mode instead. See [Switch permission modes](/docs/en/permission-modes#switch-permission-modes).
| Mode | Description |
| :------------------ | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `default` | Standard behavior: prompts for permission on first use of each tool. Labeled Manual in the CLI, the VS Code and JetBrains extensions, and the desktop app, and Claude Code accepts `manual` as an alias. The label and alias require Claude Code v2.1.200 or later. The desktop app's label doesn't depend on your CLI version |
| `acceptEdits` | Automatically accepts file edits and common filesystem commands such as `mkdir`, `touch`, `mv`, and `cp` for paths in the working directory or `additionalDirectories` |
| `plan` | Claude reads files and runs read-only shell commands to explore but doesn't edit your source files; with [auto mode](/docs/en/permission-modes#eliminate-prompts-with-auto-mode) available, classifier-approved commands also run. Labeled Plan in the CLI and the VS Code extension |
| `auto` | Auto-approves tool calls with background safety checks that verify actions align with your request |
| `dontAsk` | Auto-denies tools unless pre-approved via `/permissions` or `permissions.allow` rules. `AskUserQuestion`, connector tools [your organization set to `ask`](/docs/en/mcp#organization-controls-on-connector-tools), and MCP tools marked [`requiresUserInteraction`](/docs/en/mcp#require-approval-for-a-specific-tool) are denied even if you've allowed them |
| `bypassPermissions` | Skips permission prompts, except those forced by explicit `ask` rules, connector tools [your organization set to `ask`](/docs/en/mcp#organization-controls-on-connector-tools), and MCP tools marked [`requiresUserInteraction`](/docs/en/mcp#require-approval-for-a-specific-tool). Root and home directory removals such as `rm -rf /` also still prompt as a circuit breaker, and the [cross-session messaging safeguards](/docs/en/permission-modes#skip-all-checks-with-bypasspermissions-mode) still apply |
<Warning>
`bypassPermissions` mode skips permission prompts, including for writes to [protected paths](/docs/en/permission-modes#protected-paths) such as `.git` and `.claude`. The [cross-session messaging safeguards](/docs/en/permission-modes#skip-all-checks-with-bypasspermissions-mode) still apply. Only use this mode in isolated environments like containers or VMs where Claude Code can't cause damage.
A few prompts still fire in this mode. Explicit `ask` rules, connector tools [your organization set to `ask`](/docs/en/mcp#organization-controls-on-connector-tools), and MCP tools marked [`requiresUserInteraction`](/docs/en/mcp#require-approval-for-a-specific-tool) still prompt. Removals targeting the filesystem root or home directory, such as `rm -rf /` and `rm -rf ~`, also prompt as a circuit breaker against model error, including when the command contains command substitution with `$(...)` or backticks, or process substitution with `<(...)`.
</Warning>
To prevent `bypassPermissions` or `auto` mode from being used, set `permissions.disableBypassPermissionsMode` or `permissions.disableAutoMode` to `"disable"` in any [settings file](/docs/en/settings#settings-files). These are most useful in [managed settings](#managed-settings) where they can't be overridden.
## Permission rule syntax
Permission rules follow the format `Tool` or `Tool(specifier)`.
### Match all uses of a tool
To match all uses of a tool, use only the tool name without parentheses:
| Rule | Effect |
| :--------- | :----------------------------- |
| `Bash` | Matches all Bash commands |
| `WebFetch` | Matches all web fetch requests |
| `Read` | Matches all file reads |
`Bash(*)` is equivalent to `Bash` and matches all Bash commands. As a deny rule, both forms remove the tool from Claude's context.
### Use specifiers for fine-grained control
Add a specifier in parentheses to match specific tool uses:
| Rule | Effect |
| :----------------------------- | :------------------------------------------------------- |
| `Bash(npm run build)` | Matches the exact command `npm run build` |
| `Read(./.env)` | Matches reading the `.env` file in the current directory |
| `WebFetch(domain:example.com)` | Matches fetch requests to example.com |
### Match by input parameter
Deny and ask rules can match a top-level input parameter on any tool with `Tool(param:value)`. The rule matches when Claude calls the tool with that parameter set to that exact value. An allow rule for one parameter value wouldn't establish that the call is safe overall, so allow rules continue to use each tool's own specifier syntax. This works for any scalar parameter the tool accepts:
| Rule | Matches |
| :----------------------------- | :------------------------------------------- |
| `Agent(model:opus)` | Agent calls that request the Opus model tier |
| `Agent(isolation:worktree)` | Agent calls that request a git worktree |
| `Bash(run_in_background:true)` | Bash calls that run in the background |
Parameter matching follows these rules:
* The parameter name must be a direct field of the tool's input, such as `model` on the Agent tool. Fields nested inside an object or array are not matchable
* Each rule names one parameter. To gate on both `model` and `isolation`, write two rules, `Agent(model:opus)` and `Agent(isolation:worktree)`, rather than combining them in one rule
* The value supports `*` as a wildcard that matches any sequence of characters, so `Agent(isolation:*)` matches any explicit isolation value. Without `*` the match is exact
* A parameter the model omits is never matched, so `Agent(model:*)` doesn't match a call that leaves `model` unset
* The value is compared against the literal input Claude sends, before any normalization. `Agent(model:opus)` matches the alias `opus` but not a full model ID. Run with [`--verbose`](/docs/en/cli-reference) to see the exact parameter names and values in each tool call
* Whitespace around the colon is ignored
You can't match a tool's primary content field this way: `command` for Bash and PowerShell, `file_path` for Read, Edit, and Write, `path` for Grep and Glob, `notebook_path` for NotebookEdit, and `url` for WebFetch. A rule like `Bash(command:rm *)` would be bypassable by a compound command, so Claude Code ignores it and emits a startup warning. Use `Bash(rm *)`, `Read(./path)`, or `WebFetch(domain:host)` instead.
### Wildcard patterns
Bash rules support glob patterns with `*`. This configuration allows npm and git commit commands while blocking git push:
```json theme={null}
{
"permissions": {
"allow": [
"Bash(npm run *)",
"Bash(git commit *)",
"Bash(git * main)",
"Bash(* --version)",
"Bash(* --help *)"
],
"deny": [
"Bash(git push *)"
]
}
}
```
The `:*` suffix is an equivalent way to write a trailing wildcard, so `Bash(ls:*)` matches the same commands as `Bash(ls *)`.
The permission dialog writes the space-separated form when you select "Yes, don't ask again" for a command prefix. The `:*` form is only recognized at the end of a pattern. In a pattern like `Bash(git:* push)`, the colon is treated as a literal character and won't match git commands.
### Tool name wildcards
Deny and ask rules also accept glob patterns in the tool-name position. The pattern must match the full tool name: `"*"` matches every tool, and `"mcp__*"` matches every MCP tool across all servers. A tool matched by a bare-name glob deny rule is removed from Claude's context, the same as a bare tool name, including the [`EndConversation`](/docs/en/tools-reference#endconversation-tool-behavior) exception: a glob deny can't remove it while any other tool remains, and a glob ask never prompts for it. This configuration denies every MCP tool:
```json theme={null}
{
"permissions": {
"deny": [
"mcp__*"
]
}
}
```
Allow rules accept tool-name globs only after a literal `mcp__<server>__` prefix. The server segment must be glob-free so the rule names a specific server you configured. `mcp__puppeteer__*` matches every tool from the `puppeteer` server, and `mcp__github__get_*` matches its `get_` tools. An unanchored allow glob such as `"*"`, `"B*"`, or `"mcp__*"` is skipped with a warning and doesn't auto-approve anything.
A deny or ask rule whose tool name matches no known tool produces a startup warning to catch typos. Tool names containing `_` or `*` are exempt from the check.
The label shown for a tool in the transcript and permission dialog can differ from its canonical name. For example, the tool labeled `Stop Task` in the transcript has the canonical name `TaskStop`. Permission rules and [hook matchers](/docs/en/hooks) match the canonical name only, so a rule written as `Stop Task` doesn't match. For deny and ask rules, the startup warning above catches the mismatch. Use the canonical names listed in the [tools reference](/docs/en/tools-reference).
## Tool-specific permission rules
### Bash
Bash permission rules support wildcard matching with `*`. Wildcards can appear at any position in the command, including at the beginning, middle, or end:
* `Bash(npm run build)` matches the exact Bash command `npm run build`
* `Bash(npm run test *)` matches Bash commands starting with `npm run test`
* `Bash(npm *)` matches any command starting with `npm `
* `Bash(* install)` matches any command ending with ` install`
* `Bash(git * main)` matches commands like `git checkout main` and `git log --oneline main`
A single `*` matches any sequence of characters including spaces, so one wildcard can span multiple arguments. `Bash(git *)` matches `git log --oneline --all`, and `Bash(git * main)` matches `git push origin main` as well as `git merge main`.
When `*` appears at the end with a space before it (like `Bash(ls *)`), it enforces a word boundary, requiring the prefix to be followed by a space or end-of-string. For example, `Bash(ls *)` matches `ls -la` but not `lsof`. In contrast, `Bash(ls*)` without a space matches both `ls -la` and `lsof` because there's no word boundary constraint.
#### Compound commands
<Tip>
Claude Code is aware of shell operators, so a rule like `Bash(safe-cmd *)` won't give it permission to run the command `safe-cmd && other-cmd`. The recognized command separators are `&&`, `||`, `;`, `|`, `|&`, `&`, and newlines. A rule must match each subcommand independently.
</Tip>
When you approve a compound command with "Yes, don't ask again", Claude Code saves a separate rule for each subcommand that requires approval, rather than a single rule for the full compound string. For example, approving `git status && npm test` saves a rule for `npm test`, so future `npm test` invocations are recognized regardless of what precedes the `&&`. Subcommands like `cd` into a subdirectory generate their own Read rule for that path. Up to 5 rules may be saved for a single compound command.
<h4 id="process-wrappers">
Wrappers
</h4>
Before matching Bash rules, Claude Code strips a fixed set of wrappers, so a rule like `Bash(npm test *)` also matches `timeout 30 npm test`. The stripped wrappers are `timeout`, `time`, `nice`, `nohup`, and `stdbuf`, plus the shell builtins `command` and `builtin`, and zsh's `noglob`. Each runs its argument as the actual command. Two related forms aren't stripped: the query form `command -v`, which looks up a command rather than running one, and zsh's `nocorrect`.
Claude Code also strips a leading assignment of certain known-safe environment variables, so `Bash(npm test *)` matches `NODE_ENV=test npm test`. An allow rule won't match past an assignment of any other variable. A deny or ask rule matches past any leading assignment, so `Bash(rm *)` in deny still matches `FOO=bar rm -rf tmp/`.
Bare `xargs` is also stripped, so `Bash(grep *)` matches `xargs grep pattern`. Stripping applies only when `xargs` has no flags: an invocation like `xargs -n1 grep pattern` is matched as an `xargs` command, so rules written for the inner command do not cover it.
This wrapper list is built in and is not configurable. Development environment runners such as `direnv exec`, `devbox run`, `mise exec`, `npx`, and `docker exec` are not in the list. Because these tools execute their arguments as a command, a rule like `Bash(devbox run *)` matches whatever comes after `run`, including `devbox run rm -rf .`. To approve work inside an environment runner, write a specific rule that includes both the runner and the inner command, such as `Bash(devbox run npm test)`. Add one rule per inner command you want to allow.
Exec wrappers such as `watch`, `setsid`, `ionice`, and `flock` always prompt and can't be auto-approved by a prefix rule like `Bash(watch *)`. The same applies to `find` with `-exec` or `-delete`: a `Bash(find *)` rule doesn't cover these forms. To approve a specific invocation, write an exact-match rule for the full command string.
#### Read-only commands
Claude Code recognizes a built-in set of Bash commands as read-only and runs them without a permission prompt in every mode. These include `ls`, `cat`, `echo`, `pwd`, `head`, `tail`, `grep`, `find`, `wc`, `which`, `diff`, `stat`, `du`, `cd`, and read-only forms of `git`. The set is not configurable; to require a prompt for one of these commands, add an `ask` or `deny` rule for it.
Unquoted glob patterns are permitted for commands whose every flag is read-only, so `ls *.ts` and `wc -l src/*.py` run without a prompt.
Commands from this set still prompt in these cases:
* **Unquoted globs for commands with write-capable flags**: commands with write-capable or exec-capable flags, such as `find`, `sort`, `sed`, and `git`, prompt when an unquoted glob is present, because the glob could expand to a flag like `-delete`.
* **`docker` pointed at another daemon**: read-only forms of `docker` prompt when the command carries a flag that selects a different daemon, such as `-H`, `--context`, or Podman's `--url` and `--connection`.
* **`file` with path-opening flags**: `file` prompts when it passes `-m`/`--magic-file` or `-f`/`--files-from`, because those flags make `file` open the paths named in the flag's value.
* **Network paths on Windows**: a command whose arguments include a network (UNC) path, such as `\\server\share\file`, prompts because accessing a network path can send your Windows credentials to the host it names. The same check applies to [PowerShell tool](/docs/en/tools-reference#powershell-tool) commands.
* **Commands the analysis can't parse**: when Claude Code can't fully parse a command, it asks for approval instead of treating the command as read-only. Commands longer than 10,000 characters always prompt because they exceed what the analysis parses.
A `cd` into a path inside your working directory or an [additional directory](#working-directories) is also read-only, and a compound command like `cd packages/api && ls` runs without a prompt when each part qualifies on its own. Two combinations prompt even when each part is read-only:
* **`cd` with `git`**: prompts when the `cd` changes into a different directory, since running `git` in a new directory can execute that directory's hooks. A `cd` whose target resolves to the current working directory is a no-op and doesn't trigger the prompt.
* **`cd` with an output redirect**: prompts when Claude Code can't determine which directory the redirect target resolves against after the `cd` runs. A command whose only redirect target is `/dev/null`, such as `cd app; grep -r pattern . 2>/dev/null`, doesn't prompt, because `/dev/null` doesn't depend on the working directory.
<Warning>
Bash permission patterns that try to constrain command arguments are fragile. For example, `Bash(curl http://github.com/ *)` intends to restrict curl to GitHub URLs, but won't match variations like:
* Options before URL: `curl -X GET http://github.com/...`
* Different protocol: `curl https://github.com/...`
* Redirects: `curl -L http://short.example.com/xyz`, which redirects to GitHub
* Variables: `URL=http://github.com && curl $URL`
* Extra spaces: `curl http://github.com`
For more reliable URL filtering, consider:
* **Restrict Bash network tools**: use deny rules to block `curl`, `wget`, and similar commands, then use the WebFetch tool with `WebFetch(domain:github.com)` permission for allowed domains
* **Use PreToolUse hooks**: implement a hook that validates URLs in Bash commands and blocks disallowed domains
* **Add CLAUDE.md guidance**: describe your allowed curl patterns in `CLAUDE.md`. This shapes what Claude tries but doesn't enforce a boundary, so pair it with one of the options above
Note that using WebFetch alone doesn't prevent network access. If Bash is allowed, Claude can still use `curl`, `wget`, or other tools to reach any URL.
</Warning>
### PowerShell
PowerShell permission rules use the same shape as Bash rules. Wildcards with `*` match at any position, the `:*` suffix is equivalent to a trailing ` *`, and a bare `PowerShell` or `PowerShell(*)` matches every command. This configuration allows `Get-ChildItem` and `git commit` commands while blocking `Remove-Item`:
```json theme={null}
{
"permissions": {
"allow": [
"PowerShell(Get-ChildItem *)",
"PowerShell(git commit *)"
],
"deny": [
"PowerShell(Remove-Item *)"
]
}
}
```
Common aliases are canonicalized before matching. A rule written for the cmdlet name also matches its aliases, so `PowerShell(Get-ChildItem *)` matches `gci`, `ls`, and `dir` as well. Matching is case-insensitive.
Claude Code parses the PowerShell AST and checks each command in a compound command independently. Pipeline operators `|`, statement separators `;`, and on PowerShell 7+ the chain operators `&&` and `||` split a compound command into subcommands. A rule must match every subcommand for the compound command to be allowed.
### Read and Edit
`Edit` rules apply to all built-in tools that edit files. Claude makes a best-effort attempt to apply `Read` rules to all built-in tools that read files like Grep and Glob, to `@file` mentions in your prompts, and to the selection and open-file context that a connected [IDE](/docs/en/vs-code#the-built-in-ide-mcp-server) shares with Claude.
A `Read` deny rule also blocks the [Edit and Write tools](/docs/en/errors#file-is-covered-by-a-read-deny-rule) on the same path, including creating a new file there. NotebookEdit isn't covered, so add an `Edit` deny rule for paths no tool may change. The check requires Claude Code v2.1.208 or later on edits, and v2.1.228 or later on writes.
Claude Code checks file permissions against `Edit(path)` and `Read(path)` rules only. If you write a path rule for `Write`, `NotebookEdit`, `Glob`, or the legacy `MultiEdit` tool instead, Claude Code accepts the rule but never consults it, and [warns at startup](/docs/en/errors#is-not-matched-by-file-permission-checks), except for a `Glob` rule passed in `--allowedTools`. Use `Edit(docs/**)` in place of `Write(docs/**)`, `NotebookEdit(docs/**)`, or `MultiEdit(docs/**)`, and `Read(docs/**)` in place of `Glob(docs/**)`. Claude Code doesn't warn about a tool-name rule with no path, such as a deny rule for `Write`; it matches that rule at the tool level everywhere. Requires Claude Code v2.1.210 or later.
<Warning>
Read and Edit deny rules apply to Claude's built-in file tools and to file commands Claude Code recognizes in Bash, such as `cat`, `head`, `tail`, and `sed`. They don't apply to arbitrary subprocesses that read or write files indirectly, like a Python or Node script that opens files itself. For OS-level enforcement that blocks all processes from accessing a path, [enable the sandbox](/docs/en/sandboxing).
</Warning>
Read and Edit rules both use [gitignore](https://git-scm.com/docs/gitignore) pattern syntax with four distinct pattern types; for single-segment directory patterns, the matching depth also depends on the rule type, described later in this section:
| Pattern | Meaning | Example | Matches |
| ------------------ | ------------------------------------ | -------------------------------- | ------------------------------------------------ |
| `//path` | Absolute path from filesystem root | `Read(//Users/alice/secrets/**)` | `/Users/alice/secrets/**` |
| `~/path` | Path from home directory | `Read(~/Documents/*.pdf)` | `/Users/alice/Documents/*.pdf` |
| `/path` | Path relative to the settings source | `Edit(/src/**/*.ts)` | `<project root>/src/**/*.ts` in project settings |
| `path` or `./path` | Path relative to current directory | `Read(*.env)` | `<cwd>/*.env` |
<Warning>
A pattern like `/Users/alice/file` isn't an absolute path. The single leading slash anchors at the settings source, not the filesystem root. Use `//Users/alice/file` for absolute paths.
</Warning>
A `/path` pattern anchors at a directory associated with the settings source that defines it, so the same rule matches different locations depending on where you put it:
| Rule defined in | `/path` resolves to |
| :---------------------------------------------- | :------------------------- |
| Project settings at `.claude/settings.json` | `<project root>/path` |
| Local settings at `.claude/settings.local.json` | `<original cwd>/path` |
| User settings at `~/.claude/settings.json` | `~/.claude/path` |
| A file passed with `--settings <file>` | `<directory of file>/path` |
| CLI flags, `/permissions`, or session rules | `<original cwd>/path` |
Local settings rules anchor at the directory you started Claude Code from, not at the repository root where Claude Code [stores the file](#permission-system) in v2.1.211 and later. In a session started at the repository root, the two directories are the same; in a [worktree](/docs/en/worktrees) session, a shared rule such as `Edit(/src/**)` matches that worktree's own `src/` directory.
A deny rule such as `Read(/secrets/**)` in user settings blocks `~/.claude/secrets/**`, not a `secrets` directory in your project. To write a rule in user settings that applies inside every project, use a `//` absolute path or a `~/` home-relative path instead.
On Windows, paths are normalized to POSIX form before matching. `C:\Users\alice` becomes `/c/Users/alice`, so use `//c/**/.env` to match `.env` files anywhere on that drive. To match across all drives, use `//**/.env`.
Examples:
* `Edit(/docs/**)`: edits in `<project>/docs/`, not `/docs/` or `<project>/.claude/docs/`
* `Read(~/.zshrc)`: reads your home directory's `.zshrc`
* `Edit(//tmp/scratch.txt)`: edits the absolute path `/tmp/scratch.txt`
* `Read(src/**)`: as an allow rule, reads from `<current-directory>/src/` only; as a deny or ask rule, matches a `src` directory at any depth under the current directory
Cut at 300 lines. The page has the rest.
platforms First recorded · 80 lines, first recorded
# Platforms and integrations ## Where to run Claude Code ## Connect your tools ## Work when you are away from your terminal ## Related resources ### Platforms ### Integrations ### Remote access
The first capture of this source. The page was already there, and this is what it said.
# Platforms and integrations > Choose where to run Claude Code and what to connect it to. Compare the CLI, Desktop, VS Code, JetBrains, web, mobile, and integrations like Chrome, Slack, and CI/CD. Claude Code runs the same underlying engine everywhere, but each surface is tuned for a different way of working. This page helps you pick the right platform for your workflow and connect the tools you already use. ## Where to run Claude Code Choose a platform based on how you like to work and where your project lives. | Platform | Best for | What you get | | :-------------------------------- | :------------------------------------------------------------------------------------------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [CLI](/docs/en/quickstart) | Terminal workflows, scripting, remote servers | Full feature set, [Agent SDK](/docs/en/headless), [computer use](/docs/en/computer-use) on macOS (Pro and Max), third-party providers | | [Desktop](/docs/en/desktop) | Visual review, parallel sessions, managed setup | Diff viewer, app preview, [computer use](/docs/en/desktop#let-claude-use-your-computer) and [Dispatch](/docs/en/desktop#sessions-from-dispatch) on Pro and Max | | [VS Code](/docs/en/vs-code) | Working inside VS Code without switching to a terminal | Inline diffs, integrated terminal, file context | | [JetBrains](/docs/en/jetbrains) | Working inside IntelliJ, PyCharm, WebStorm, or other JetBrains IDEs | Diff viewer, selection sharing, terminal session | | [Web](/docs/en/claude-code-on-the-web) | Long-running tasks that don't need much steering, or work that should continue when you're offline | Cloud, Anthropic-managed by default; continues after you disconnect | | [Mobile](/docs/en/mobile) | Starting and monitoring tasks while away from your computer | Cloud sessions from the Claude app for iOS and Android, [Remote Control](/docs/en/remote-control) for local sessions, [Dispatch](/docs/en/desktop#sessions-from-dispatch) to Desktop on Pro and Max | The CLI is the most complete surface for terminal-native work: scripting and the Agent SDK are CLI-only. Third-party providers also work in [VS Code](/docs/en/vs-code#use-third-party-providers). Enterprise [Desktop](/docs/en/desktop) deployments support Google Cloud's Agent Platform, and Desktop supports [gateway providers](/docs/en/llm-gateway-connect#desktop-app); for Amazon Bedrock or Microsoft Foundry, use the CLI or VS Code, or [Claude Desktop on 3P](https://claude.com/docs/third-party/claude-desktop/overview), which runs the Code tab on those providers. Desktop and the IDE extensions trade some CLI-only features for visual review and tighter editor integration. The web runs in the cloud, so tasks keep going after you disconnect. Mobile is a thin client into those same cloud sessions or into a local session via Remote Control, and can send tasks to Desktop with Dispatch. You can mix surfaces on the same project. Configuration, project memory, and MCP servers are shared across the local surfaces. ## Connect your tools Integrations let Claude work with services outside your codebase. | Integration | What it does | Use it for | | :----------------------------------- | :--------------------------------------------------------------------------------- | :---------------------------------------------------------------------------------- | | [Chrome](/docs/en/chrome) | Controls your browser with your logged-in sessions | Testing web apps, filling forms, automating sites without an API | | [GitHub Actions](/docs/en/github-actions) | Runs Claude in your CI pipeline | Automated PR reviews, issue triage, scheduled maintenance | | [GitLab CI/CD](/docs/en/gitlab-ci-cd) | Same as GitHub Actions for GitLab | CI-driven automation on GitLab | | [Code Review](/docs/en/code-review) | Reviews every PR automatically | Catching bugs before human review | | [Slack](/docs/en/slack) | Responds to `@Claude` mentions in your channels | Turning bug reports into pull requests from team chat | | [Claude Tag](/docs/en/claude-tag) | Runs `@Claude` as your organization's shared identity with admin-configured access | Shared team access on Team and Enterprise plans, instead of per-user Slack sessions | For integrations not listed here, [MCP servers](/docs/en/mcp) and [connectors](/docs/en/desktop#connect-external-tools) let you connect almost anything: Linear, Notion, Google Drive, or your own internal APIs. ## Work when you are away from your terminal Claude Code offers several ways to work when you're not at your terminal. They differ in what triggers the work, where Claude runs, and how much you need to set up. | | Trigger | Claude runs on | Setup | Best for | | :------------------------------------------------------- | :--------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------ | | [Dispatch](/docs/en/desktop#sessions-from-dispatch) | Message a task from the Claude mobile app | Your machine (Desktop) | [Pair the mobile app with Desktop](https://support.claude.com/en/articles/13947068) | Delegating work while you're away, minimal setup | | [Remote Control](/docs/en/remote-control) | Drive a running session from [claude.ai/code](https://claude.ai/code) or the Claude mobile app | Your machine (CLI or VS Code) | Run `claude remote-control` | Steering in-progress work from another device | | [Channels](/docs/en/channels) | Push events from a chat app like Telegram or Discord, or your own server | Your machine (CLI) | [Install a channel plugin](/docs/en/channels#quickstart) or [build your own](/docs/en/channels-reference) | Reacting to external events like CI failures or chat messages | | [Slack](/docs/en/slack) | Mention `@Claude` in a team channel | Anthropic cloud | [Install the Slack app](/docs/en/slack#setting-up-claude-code-in-slack) with [Claude Code on the web](/docs/en/claude-code-on-the-web) enabled | PRs and reviews from team chat | | [Self-hosted environments](/docs/en/self-hosted-environments) | Start a [cloud session](/docs/en/claude-code-on-the-web) and pick your organization's environment | Your organization's infrastructure | [Deploy runners](/docs/en/self-hosted-environments-quickstart), on Team and Enterprise plans | Cloud sessions that must run inside your network | | [Scheduled tasks](/docs/en/scheduled-tasks) | Set a schedule | [CLI](/docs/en/scheduled-tasks), [Desktop](/docs/en/desktop-scheduled-tasks), or [cloud](/docs/en/routines) | Pick a frequency | Recurring automation like daily reviews | If you're not sure where to start, [install the CLI](/docs/en/quickstart) and run it in a project directory. If you'd rather not use a terminal, [Desktop](/docs/en/desktop-quickstart) gives you the same engine with a graphical interface. ## Related resources ### Platforms * [CLI quickstart](/docs/en/quickstart): install and run your first command in the terminal * [Desktop](/docs/en/desktop): visual diff review, parallel sessions, computer use, and Dispatch * [VS Code](/docs/en/vs-code): the Claude Code extension inside your editor * [JetBrains](/docs/en/jetbrains): the extension for IntelliJ, PyCharm, and other JetBrains IDEs * [Claude Code on the web](/docs/en/claude-code-on-the-web): cloud sessions that keep running when you disconnect * [Mobile](/docs/en/mobile): the Claude app for [iOS](https://apps.apple.com/us/app/claude-by-anthropic/id6473753684) and [Android](https://play.google.com/store/apps/details?id=com.anthropic.claude) for starting and monitoring tasks while away from your computer ### Integrations * [Chrome](/docs/en/chrome): automate browser tasks with your logged-in sessions * [Computer use](/docs/en/computer-use): let Claude open apps and control your screen on macOS * [GitHub Actions](/docs/en/github-actions): run Claude in your CI pipeline * [GitLab CI/CD](/docs/en/gitlab-ci-cd): the same for GitLab * [Code Review](/docs/en/code-review): automatic review on every pull request * [Slack](/docs/en/slack): send tasks from team chat, get PRs back * [Claude Tag](/docs/en/claude-tag): run `@Claude` as your organization's shared identity on Team and Enterprise plans ### Remote access * [Dispatch](/docs/en/desktop#sessions-from-dispatch): message a task from your phone and it can spawn a Desktop session * [Remote Control](/docs/en/remote-control): drive a running session from your phone or browser * [Channels](/docs/en/channels): push events from chat apps or your own servers into a session * [Scheduled tasks](/docs/en/scheduled-tasks): run prompts on a recurring schedule
plugin-dependencies First recorded · 220 lines, first recorded
# Constrain plugin dependency versions ## Why constrain dependency versions ## Declare a dependency with a version constraint ## Bundle plugins for a team ## Depend on a plugin from another marketplace ## Tag plugin releases for version resolution ## How constraints interact ## Enable or disable a plugin with dependencies ## Remove orphaned auto-installed dependencies ## Resolve dependency errors ## See also
The first capture of this source. The page was already there, and this is what it said.
# Constrain plugin dependency versions
> Declare version constraints on plugin dependencies, and bundle a curated plugin set behind one install.
A plugin can depend on other plugins by listing them in `plugin.json` or in its marketplace entry. By default, a dependency tracks the latest available version, so an upstream release can change the dependency under your plugin without warning. Version constraints let you hold a dependency at a tested version range until you choose to move.
When you install a plugin that declares dependencies, Claude Code resolves and installs them automatically. If a dependency later goes missing, `/reload-plugins` and the background plugin auto-update reinstall it, provided its marketplace is already in your configured marketplaces. Re-running `claude plugin install` on the dependent plugin, or adding a marketplace with `claude plugin marketplace add`, also resolves any outstanding missing dependencies. Dependencies from a marketplace you have not added are left unresolved.
This guide is for plugin authors who declare dependencies in `plugin.json` and for marketplace maintainers who tag releases. Dependencies here are other plugins; for the npm and Bun packages a plugin itself uses, see [Node.js package dependencies](/docs/en/plugins-reference#node-js-package-dependencies). To install plugins that have dependencies, see [Discover and install plugins](/docs/en/discover-plugins). For the full manifest schema, see the [Plugins reference](/docs/en/plugins-reference).
## Why constrain dependency versions
Consider an internal marketplace where two teams publish plugins. The platform team maintains `secrets-vault`, an MCP server that wraps a secrets backend. The deploy team maintains `deploy-kit`, which calls `secrets-vault` to fetch credentials during deploys.
`deploy-kit` is tested against `secrets-vault` v2.1.0. Without a version constraint, the next time the platform team tags a release that renames an MCP tool, auto-update moves every engineer's `secrets-vault` to the new version and `deploy-kit` breaks.
With a version constraint, `deploy-kit` declares that it needs `secrets-vault` in the `~2.1.0` range. Engineers with `deploy-kit` installed stay on the highest matching `2.1.x` patch. The deploy team upgrades on their own schedule by publishing a new `deploy-kit` version with a wider constraint.
## Declare a dependency with a version constraint
List dependencies in the `dependencies` array of your plugin's `.claude-plugin/plugin.json`.
The following manifest declares one unversioned dependency and one constrained dependency:
```json .claude-plugin/plugin.json theme={null}
{
"name": "deploy-kit",
"version": "3.1.0",
"dependencies": [
"audit-logger",
{ "name": "secrets-vault", "version": "~2.1.0" }
]
}
```
An entry can be a bare string with only the plugin name, like `"audit-logger"` in the example above, which depends on whatever version that plugin's marketplace provides. For more control, use an object with these fields:
| Field | Type | Description |
| :------------ | :----- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `name` | string | Plugin name. Resolves within the same marketplace as the declaring plugin. Required. |
| `version` | string | A [semver range](https://github.com/npm/node-semver#ranges) such as `~2.1.0`, `^2.0`, `>=1.4`, or `=2.1.0`. The dependency is fetched at the highest tagged version that satisfies this range. |
| `marketplace` | string | A different marketplace to resolve `name` in. Cross-marketplace dependencies are blocked unless the target marketplace is listed in [`allowCrossMarketplaceDependenciesOn`](#depend-on-a-plugin-from-another-marketplace) in the root marketplace's `marketplace.json`. |
Pre-release versions such as `2.0.0-beta.1` are excluded unless your range opts in with a pre-release suffix like `^2.0.0-0`.
## Bundle plugins for a team
Besides the required `name`, a plugin manifest can consist of only a `dependencies` array. Installing it pulls in every dependency, which makes it a way to package a curated plugin set behind one install.
For example, a platform team can publish role-specific bundles in an internal marketplace so engineers run one `claude plugin install` instead of installing each tool separately:
```json .claude-plugin/plugin.json theme={null}
{
"name": "backend-standard",
"version": "1.0.0",
"description": "Standard plugin set for backend engineers",
"dependencies": [
"secrets-vault",
"deploy-kit",
{ "name": "db-migrate", "version": "^3.0" },
"oncall-runbook"
]
}
```
Installing `backend-standard` resolves and installs all four dependencies.
To add a tool to the standard set later, publish a new `backend-standard` version with the extra dependency. Auto-update is off by default for non-Anthropic marketplaces, so engineers pick up the new version in one of two ways:
* Enable auto-update for the marketplace in `/plugin`. The next auto-update moves the bundle to the new version and installs any dependencies it adds.
* Run `claude plugin update backend-standard`, then `/reload-plugins` to install the newly added dependencies.
To roll bundles out across an organization, add the bundle plugin to `enabledPlugins` in [managed settings](/docs/en/settings#enabledplugins).
## Depend on a plugin from another marketplace
By default, Claude Code refuses to auto-install a dependency that lives in a different marketplace than the plugin declaring it. This prevents one marketplace from silently pulling in plugins from a source you have not reviewed.
To allow it, the maintainer of the root marketplace adds the target marketplace name to `allowCrossMarketplaceDependenciesOn` in `marketplace.json`. The root marketplace is the one that hosts the plugin the user is installing; only its allowlist is consulted, so trust does not chain through intermediate marketplaces.
The following `marketplace.json` allows `deploy-kit` to depend on a plugin from `acme-shared`:
```json .claude-plugin/marketplace.json theme={null}
{
"name": "acme-tools",
"owner": { "name": "Acme" },
"allowCrossMarketplaceDependenciesOn": ["acme-shared"],
"plugins": [
{
"name": "deploy-kit",
"source": "./deploy-kit",
"dependencies": [
{ "name": "audit-logger", "marketplace": "acme-shared" }
]
}
]
}
```
If the field is missing or does not include the target marketplace, install fails with a `cross-marketplace` error naming the field to set. Users can still install the dependency manually first, which satisfies the constraint without changing the allowlist.
## Tag plugin releases for version resolution
Claude Code resolves version constraints against git tags on the repository that hosts the dependency: the plugin's own repository for `github`, `url`, and `git-subdir` [plugin sources](/docs/en/plugin-marketplaces#plugin-sources), or the marketplace repository for a plugin the marketplace references by a relative path. For Claude Code to find a dependency's available versions, the upstream plugin's releases must be tagged using a specific naming convention.
Tag each release as `{plugin-name}--v{version}`, where `{version}` matches the `version` field in that commit's `plugin.json`. From the plugin directory, run:
```bash theme={null}
claude plugin tag --push
```
The `claude plugin tag` command derives the tag name from the plugin's manifest and the enclosing marketplace entry. Before creating the tag, it validates the plugin contents, checks that `plugin.json` and the marketplace entry agree on the version, requires a clean working tree under the plugin directory, and refuses if the tag already exists.
* `--push` pushes the tag to the `origin` remote, so the repository needs a configured `origin` remote. Pass `--remote` to push to a different one.
* If the push fails, the tag is still created locally and the command exits with an error.
* With `--push`, a successful run ends with `Created tag secrets-vault--v2.1.0` and `Pushed to origin`, where the last line names the remote it pushed to. Without `--push`, the command prints the `git push` command to run instead.
* `--dry-run` prints what would be tagged without creating it.
Running `git tag secrets-vault--v2.1.0` directly is equivalent if you keep `plugin.json` and the marketplace entry in sync yourself.
The plugin name prefix lets one marketplace repository host multiple plugins with independent version lines. The `--v` separator is parsed as a prefix match on the full plugin name, so plugin names that contain hyphens are handled correctly.
When you install a plugin that declares `{ "name": "secrets-vault", "version": "~2.1.0" }`, Claude Code lists the tags on the repository that hosts `secrets-vault`, filters to those starting with `secrets-vault--v`, and fetches the highest version satisfying `~2.1.0`. If no tag on the plugin's own repository satisfies the range, the install fails with `Dependency "secrets-vault@acme-tools" has no git tag satisfying ~2.1.0`, which names the dependency together with its marketplace. For a relative-path plugin with no matching tag, Claude Code installs the marketplace's current copy instead and checks the constraint when the plugin loads.
For a plugin the marketplace references by a relative path, a marketplace added as a local folder path resolves tags the same way when the folder is a git repository. This requires Claude Code v2.1.196 or later. In two cases Claude Code installs the dependency from the folder's current contents instead:
* Earlier versions don't read tags from a local-folder marketplace, so a constrained dependency loads only if that copy satisfies the range.
* A local folder that isn't a git repository has no tags, regardless of version.
The resolved tag's semver is recorded separately from `plugin.json`'s `version`, so constraint checks use the tag that was actually fetched even if `plugin.json` at that commit has a stale value. The cache directory name for a tag-resolved install includes a 12-character commit-SHA suffix, so if a maintainer force-moves a tag to a different commit, the next install gets a fresh cache directory instead of reusing stale content.
<Note>
For dependencies with an `npm`, `archive`, or `command` [plugin source](/docs/en/plugin-marketplaces#plugin-sources), the constraint does not control which version is fetched, since tag-based resolution applies only to git-backed sources. The constraint is still checked at load time, and the dependent plugin is disabled with `dependency-version-unsatisfied` if the installed version does not satisfy it. For a `command` source, Claude Code checks the version in the dependency's `plugin.json` and ignores the content-hash suffix; a dependency whose `plugin.json` sets no version satisfies no constraint, so set one before you constrain it. Claude Code never installs a dependency with a `command` source itself, so users [install it first](/docs/en/plugin-marketplaces#how-users-accept-the-command).
</Note>
## How constraints interact
When several installed plugins constrain the same dependency, Claude Code intersects their ranges and resolves the dependency to the highest version that satisfies all of them. The table below shows how common combinations resolve.
| Plugin A requires | Plugin B requires | Result |
| :---------------- | :---------------- | :---------------------------------------------------------------------------------------------- |
| `^2.0` | `>=2.1` | One install at the highest `2.x` tag at or above `2.1.0`. Both plugins load. |
| `~2.1` | `~3.0` | Install of plugin B fails with `range-conflict`. Plugin A and the dependency stay as they were. |
| `=2.1.0` | none | The dependency stays at `2.1.0`. Auto-update skips newer versions while plugin A is installed. |
Auto-update fetches a constrained dependency at the highest git tag that satisfies every installed plugin's range, rather than at the marketplace's latest version, so the dependency continues to receive updates within its allowed range. If no tag satisfies all ranges, auto-update skips that dependency and lists the skip in the `/plugin` Errors tab, naming the constraining plugin.
When you uninstall the last plugin that constrains a dependency, the dependency is no longer held and resumes tracking its marketplace entry on the next update.
## Enable or disable a plugin with dependencies
Enabling a plugin also enables the plugins it depends on, and disabling a plugin is blocked if another enabled plugin still needs it. Both behaviors require Claude Code v2.1.143 or later. Earlier versions enable or disable only the named plugin and surface a `dependency-unsatisfied` error on the next load.
When you enable a plugin, Claude Code also enables its dependencies at the same scope. If a dependency has its own dependencies, Claude Code enables those too. The success message lists what else was enabled along with the plugin you named. If a dependency can't be enabled, the command refuses and tells you what's blocking and how to fix it:
| Condition | Result |
| :------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------- |
| A dependency is not installed | Enable fails and prints the `claude plugin install` command for each missing dependency. |
| A dependency is blocked by your organization's plugin policy | Enable fails and names the blocked dependency. |
| A dependency is set to `false` at a scope with higher precedence than the target scope | Enable fails. Enable the dependency at that scope, or pass `--scope` to write there. |
| All dependencies are installed and allowed | Enable succeeds and writes `true` for the plugin and each dependency that was not already enabled at the target scope. |
This holds even when a dependency sets [`defaultEnabled: false`](/docs/en/plugins-reference#default-enablement) in its manifest, because Claude Code writes an explicit `true` for it. The same applies at install: a dependency pulled in to satisfy an active plugin installs with `true` regardless of its own default.
When you disable a plugin, Claude Code refuses if another enabled plugin still depends on it. The error names the plugins that depend on it and gives you a chained command that disables them in the right order, ending with the one you asked for.
For example, if `deploy-kit` depends on `secrets-vault`, disabling `secrets-vault` alone fails with output similar to the following:
```text theme={null}
secrets-vault is still required by deploy-kit. Disable that plugin first, or
disable everything together: claude plugin disable deploy-kit@acme-tools && claude plugin disable secrets-vault@acme-tools
```
Copy the chained command from the error to disable the full set in one step.
## Remove orphaned auto-installed dependencies
Auto-installed dependencies stay on disk after the plugins that installed them are uninstalled, in case you reinstall a dependent plugin or want to keep using the dependency directly. To clean them up, run `claude plugin prune` to list the auto-installed dependencies that no longer have any installed plugin requiring them and remove them after a confirmation prompt.
```bash theme={null}
claude plugin prune
```
If nothing qualifies for removal, the command prints `Nothing to prune` with the reason and exits. This is the expected output on a fresh install, not an error.
By default, prune operates at user scope and asks for confirmation before removing anything:
* `--scope project` or `--scope local` targets a different scope.
* `--dry-run` lists what would be removed without changing anything.
* `-y` skips the confirmation prompt. When stdin or stdout isn't a terminal, prune lists the orphans and exits without removing them unless you pass `-y`.
To prune as part of an uninstall, pass `--prune` to `claude plugin uninstall`. After removing the named plugin, Claude Code scans for and removes any auto-installed dependencies that are now orphaned. Plugins you installed yourself are never pruned, only those installed automatically through another plugin's `dependencies` array.
The same confirmation behavior applies. When stdin or stdout isn't a terminal, the uninstall still completes, but the prune step lists the orphans and removes nothing unless you pass `-y`.
For example, to uninstall `deploy-kit` and clean up the dependencies it leaves behind:
```bash theme={null}
claude plugin uninstall deploy-kit --prune
```
## Resolve dependency errors
Dependency problems appear in `claude plugin list` and in the `/plugin` interface, as descriptive error messages rather than the literal codes in this table. Claude Code disables the affected plugin until you resolve the error. The table below lists the most common errors and how to resolve them.
| Error | Meaning | How to resolve |
| :------------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `dependency-unsatisfied` | A declared dependency is not installed, or it is installed but disabled. | Run the `claude plugin install` command shown in the error message. If the dependency's marketplace is not yet configured, add it with `claude plugin marketplace add` and Claude Code resolves the dependency automatically. If the dependency is disabled, enable it. |
| `range-conflict` | The version requirements for a dependency cannot be combined. The error message names the cause: no version satisfies all of the ranges, a range is not valid semver syntax, or the combined ranges are too complex to intersect. | Uninstall or update one of the conflicting plugins, fix any invalid `version` string, simplify long `\|\|` chains, or ask the upstream author to widen its constraint. |
| `dependency-version-unsatisfied` | The installed dependency's version is outside this plugin's declared range. | Run `claude plugin install <dependency>@<marketplace>` to re-resolve the dependency against all current constraints. |
| `no-matching-tag` | The dependency's repository has no `{name}--v*` tag satisfying the range. | Check that the upstream has tagged releases using the convention above, or relax your range. |
To check for these errors programmatically, run `claude plugin list --json`. Plugins with problems include an `errors` field listing them. Plugins that loaded cleanly omit the field.
## See also
* [Create plugins](/docs/en/plugins): build plugins with skills, agents, and hooks
* [Create and distribute a plugin marketplace](/docs/en/plugin-marketplaces): host plugins for your team
* [Plugins reference](/docs/en/plugins-reference#plugin-manifest-schema): the full `plugin.json` schema
* [Version management](/docs/en/plugins-reference#version-management): how a plugin's own version is resolved and used as the cache key
plugin-hints First recorded · 151 lines, first recorded
# Recommend your plugin from your CLI ## How it works ## Emit the hint ## Choose where to emit ## What the user sees ## Hint format ## Requirements ## Get your plugin into the official marketplace ## See also
The first capture of this source. The page was already there, and this is what it said.
# Recommend your plugin from your CLI
> Emit a one-line marker from your CLI so Claude Code prompts users to install your official plugin.
If you maintain a CLI or SDK and have a plugin in the official Anthropic marketplace, your tool can prompt Claude Code users to install that plugin. Your CLI writes a one-line marker to stderr when it detects it is running inside Claude Code. Claude Code reads the marker, strips it from the output, and shows the user a one-time install prompt.
The protocol requires no extra commands and does not change what your CLI prints for users outside Claude Code.
This page is for CLI and SDK maintainers. If you are looking to install plugins, see [Discover and install plugins](/docs/en/discover-plugins).
## How it works
Claude Code sets the [`CLAUDECODE`](/docs/en/env-vars) environment variable to `1` for every command it runs through the Bash and PowerShell tools, and for [hook](/docs/en/hooks) commands. From v2.1.172 it also sets [`CLAUDE_CODE_CHILD_SESSION`](/docs/en/env-vars) to `1` in those same subprocesses. When your CLI sees one of these variables, it writes a self-closing `<claude-code-hint />` tag to stderr. In hook commands the hint tag is stripped and ignored. Only Bash and PowerShell tool output triggers the install prompt.
When Claude Code receives the command output, it:
1. Scans for hint lines and removes them before the output reaches the model
2. Checks that the hint targets a plugin in an official Anthropic marketplace
3. Checks that the plugin is not already installed and has not been prompted before
4. Shows the user an install prompt that names the command that emitted the hint
Claude Code never installs a plugin automatically. The user always confirms.
## Emit the hint
Hint prompts only fire for plugins listed in the official Anthropic marketplace. See [Get your plugin into the official marketplace](#get-your-plugin-into-the-official-marketplace) before you ship the integration.
Gate emission on an environment variable so the marker is unlikely to appear when a human runs your CLI directly, then write the tag to stderr on its own line. Choose which variable to check:
* `CLAUDECODE`: set on every Claude Code version, so it reaches the most sessions. It is also set in tmux sessions and stdio MCP server subprocesses that Claude Code starts. IDE extensions also set it in their integrated terminals, where a human may be running your CLI directly.
* `CLAUDE_CODE_CHILD_SESSION`: set only in subprocesses Claude Code itself spawns, such as tool calls, hook commands, and [status line](/docs/en/statusline) commands, so the tag does not normally reach a human terminal. A long-lived process that was started inside a session, such as a tmux server, captures the variable, so shells later launched from that process still show the raw tag. Requires Claude Code v2.1.172 or later, so sessions on older versions miss the hint.
The following examples gate on `CLAUDECODE` for maximum reach and emit a hint for a plugin named `example-cli` in the official marketplace:
<CodeGroup>
```javascript Node.js theme={null}
if (process.env.CLAUDECODE) {
process.stderr.write(
'<claude-code-hint v="1" type="plugin" value="example-cli@claude-plugins-official" />\n',
)
}
```
```python Python theme={null}
import os, sys
if os.environ.get("CLAUDECODE"):
print(
'<claude-code-hint v="1" type="plugin" value="example-cli@claude-plugins-official" />',
file=sys.stderr,
)
```
```go Go theme={null}
if os.Getenv("CLAUDECODE") != "" {
fmt.Fprintln(os.Stderr,
`<claude-code-hint v="1" type="plugin" value="example-cli@claude-plugins-official" />`)
}
```
```shell Shell theme={null}
if [ -n "$CLAUDECODE" ]; then
printf '%s\n' '<claude-code-hint v="1" type="plugin" value="example-cli@claude-plugins-official" />' >&2
fi
```
</CodeGroup>
Replace `example-cli` with your plugin's name in the official marketplace.
## Choose where to emit
You control which code paths emit the hint. Claude Code deduplicates by plugin, so emitting on every invocation has no downside. Touchpoints that work well include:
| Placement | Why it works |
| :------------------------ | :--------------------------------------------------------- |
| `--help` output | Claude often runs help when exploring an unfamiliar CLI |
| Unknown-subcommand errors | Reaches the moment Claude is confused about your interface |
| Login or auth success | The user is already in a setup mindset |
| First-run welcome message | A natural onboarding moment |
## What the user sees
When the hint passes all checks, Claude Code shows a prompt like the following:
```text theme={null}
─────────────────────────────────────────────────────────────
Plugin recommendation
The example-cli command suggests installing a plugin.
Plugin: example-cli
Marketplace: claude-plugins-official
Official integration for example-cli deployments
Would you like to install it?
❯ 1. Yes, install example-cli
2. No
3. No, and don't show plugin installation hints again
─────────────────────────────────────────────────────────────
```
The prompt names the command that produced the hint so users can spot a mismatch between the tool and the plugin it recommends. If the user doesn't respond within 30 seconds, Claude Code dismisses the prompt as **No**.
Prompt frequency is bounded, and some sessions never prompt:
* **Once per plugin**: after the prompt is shown, Claude Code records the plugin and never prompts for it again, regardless of the user's answer.
* **Once per session**: across all CLIs on the machine, at most one hint prompt appears per Claude Code session.
* **Telemetry opt-outs**: sessions where analytics are disabled never show hint prompts. This includes sessions with `DISABLE_TELEMETRY` or `CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC` set, and sessions on third-party providers such as Amazon Bedrock or Google Cloud's Agent Platform where the [automatic telemetry opt-out](/docs/en/data-usage#default-behaviors-by-api-provider) applies.
Selecting **Yes** installs the plugin to user scope. Selecting **No, and don't show plugin installation hints again** disables all future hint prompts for the user.
## Hint format
The hint is a self-closing tag with three required attributes.
```text theme={null}
<claude-code-hint v="1" type="plugin" value="example-cli@claude-plugins-official" />
```
| Attribute | Required | Description |
| :-------- | :------- | :------------------------------------------------ |
| `v` | Yes | Protocol version. `1` is the only supported value |
| `type` | Yes | Hint kind. `plugin` is the only supported value |
| `value` | Yes | Plugin identifier in `name@marketplace` form |
Attribute values may be quoted with double quotes or left unquoted. Unquoted values cannot contain whitespace. Escape sequences are not supported.
## Requirements
Claude Code enforces two conditions before acting on a hint. Hints that fail either check are dropped:
* **Own line**: the tag must occupy its own line. A tag embedded mid-line, for example inside a log statement, is ignored. Leading and trailing whitespace on the line is allowed.
* **Official marketplace**: the `value` must reference a plugin in an Anthropic-controlled marketplace such as `claude-plugins-official`. Hints that point to other marketplaces are silently dropped.
The hint line is always removed from the output before it reaches the model, even when the version or type is unrecognized, so the marker is never counted toward token usage.
The remaining guidance is recommended but not enforced. Claude Code cannot observe whether your CLI follows it:
* **Write to stderr**: stderr keeps the tag out of shell pipelines such as `example-cli deploy | jq`. Claude Code scans both streams, so stdout also works.
* **Gate on an environment variable**: only emit when `CLAUDECODE` or `CLAUDE_CODE_CHILD_SESSION` is set. See [Emit the hint](#emit-the-hint) for how the two variables differ.
## Get your plugin into the official marketplace
The hint protocol only takes effect for plugins listed in the official Anthropic marketplace, `claude-plugins-official`. Anthropic curates that marketplace at its discretion, and the in-app submission forms add plugins to the [community marketplace](/docs/en/plugins#submit-your-plugin-to-the-community-marketplace) instead, which the hint protocol does not check. If you are working with an Anthropic partner contact, reach out to them to coordinate an official-marketplace listing.
## See also
* [Create plugins](/docs/en/plugins): build the plugin your CLI recommends
* [Create and distribute a plugin marketplace](/docs/en/plugin-marketplaces): host plugins outside the official marketplace
* [Environment variables](/docs/en/env-vars): full reference for `CLAUDECODE` and related variables
plugin-marketplaces First recorded · 1314 lines, first recorded
# Create and distribute a plugin marketplace ## Overview ## Walkthrough: create a local marketplace ## Create the marketplace file ## Marketplace schema ### Required fields ### Owner fields ### Optional fields ## Plugin entries ### Required fields ### Optional plugin fields ## Plugin sources ### Relative paths ### GitHub repositories ### Git repositories ### Git subdirectories ### npm packages ### Zip archives ### Command sources #### Copy mode and link mode #### How users accept the command #### When Claude Code re-runs the command ### Advanced plugin entries ### Strict mode ## Host and distribute marketplaces ### Host on GitHub (recommended) ### Host on other git services ### Private repositories #### Commands you run #### Background auto-updates ### Require marketplaces for your team ### Pre-populate plugins for containers ### Managed marketplace restrictions #### Common configurations #### How restrictions work ### Version resolution and release channels #### Set up release channels ##### Example ##### Assign channels to user groups #### Pin dependency versions ### Rename or remove a plugin ## Validation and testing ## Manage marketplaces from the CLI ### Plugin marketplace add ### Plugin marketplace list ### Plugin marketplace remove ### Plugin marketplace update ## Troubleshooting ### Marketplace not loading ### Marketplace validation errors ### Plugin installation failures ### Private repository authentication fails ### Marketplace updates fail in offline environments ### Git operations time out ### Plugins with relative paths fail in URL-based marketplaces ### Files not found after installation ## See also
The first capture of this source. The page was already there, and this is what it said.
# Create and distribute a plugin marketplace
> Build and host plugin marketplaces to distribute Claude Code extensions across teams and communities.
A **plugin marketplace** is a catalog that lets you distribute plugins to others. Marketplaces provide centralized discovery, version tracking, automatic updates, and support for multiple source types, including git repositories and local paths. This guide shows you how to create your own marketplace to share plugins with your team or community.
Looking to install plugins from an existing marketplace? See [Discover and install prebuilt plugins](/docs/en/discover-plugins).
## Overview
Creating and distributing a marketplace involves:
1. **Create plugins**: build one or more plugins with skills, agents, hooks, MCP servers, or LSP servers. This guide assumes you already have plugins to distribute; see [Create plugins](/docs/en/plugins) for details on how to create them.
2. **Create the marketplace file**: define a `marketplace.json` that lists your plugins and where to find them. See [Create the marketplace file](#create-the-marketplace-file).
3. **Host the marketplace**: push to GitHub, GitLab, or another git host. See [Host and distribute marketplaces](#host-and-distribute-marketplaces).
4. **Share with users**: users add your marketplace with `/plugin marketplace add` and install individual plugins. See [Discover and install plugins](/docs/en/discover-plugins).
Once your marketplace is live, you can update it by pushing changes to your repository. Users refresh their local copy with `/plugin marketplace update`.
## Walkthrough: create a local marketplace
This example creates a marketplace with one plugin: a `quality-review` skill for code reviews. You'll create the directory structure, add a skill, create the plugin manifest and marketplace catalog, then install and test it.
<Steps>
<Step title="Create the directory structure">
```bash theme={null}
mkdir -p my-marketplace/.claude-plugin
mkdir -p my-marketplace/plugins/quality-review-plugin/.claude-plugin
mkdir -p my-marketplace/plugins/quality-review-plugin/skills/quality-review
```
</Step>
<Step title="Create the skill">
Create a `SKILL.md` file that defines what the `quality-review` skill does.
```markdown my-marketplace/plugins/quality-review-plugin/skills/quality-review/SKILL.md theme={null}
---
description: Review code for bugs, security, and performance
---
Review the code I've selected or the recent changes for:
- Potential bugs or edge cases
- Security concerns
- Performance issues
- Readability improvements
Be concise and actionable.
```
</Step>
<Step title="Create the plugin manifest">
Create a `plugin.json` file that describes the plugin. The manifest goes in the `.claude-plugin/` directory.
```json my-marketplace/plugins/quality-review-plugin/.claude-plugin/plugin.json theme={null}
{
"name": "quality-review-plugin",
"description": "Adds a quality-review skill for quick code reviews",
"version": "1.0.0",
"author": {
"name": "Your Name"
}
}
```
<Note>
Setting `version` means users only receive updates when you change this field, so bump it on every release. A plugin with a [`command` source](#command-sources) isn't pinned by this field. If you omit `version`, the version comes from the next source in [version management](/docs/en/plugins-reference#version-management).
</Note>
</Step>
<Step title="Create the marketplace file">
Create the marketplace catalog that lists your plugin.
```json my-marketplace/.claude-plugin/marketplace.json theme={null}
{
"name": "my-plugins",
"owner": {
"name": "Your Name"
},
"plugins": [
{
"name": "quality-review-plugin",
"source": "./plugins/quality-review-plugin",
"description": "Adds a quality-review skill for quick code reviews"
}
]
}
```
</Step>
<Step title="Add and install">
From the directory that contains `my-marketplace`, start Claude Code and run the following commands. The install command opens a plugin details view where you select an installation scope to confirm the install. Check the install summary: if it reports `Run /reload-plugins to activate.`, run that command.
```shell theme={null}
/plugin marketplace add ./my-marketplace
/plugin install quality-review-plugin@my-plugins
```
</Step>
<Step title="Try it out">
Select some code in your editor and run your new skill. Plugin skills are namespaced with the plugin name.
```shell theme={null}
/quality-review-plugin:quality-review
```
</Step>
</Steps>
To learn more about what plugins can do, including hooks, agents, MCP servers, and LSP servers, see [Plugins](/docs/en/plugins).
<Note>
**How plugins are installed**: when users install a plugin, Claude Code copies the plugin directory to a cache location, except for a [`command` source in link mode](#copy-mode-and-link-mode), which is used in place. Copied plugins can't reference files outside their directory using paths like `../shared-utils`, because those files won't be copied.
If you need to share files across plugins, use symlinks. See [Plugin caching and file resolution](/docs/en/plugins-reference#plugin-caching-and-file-resolution) for details.
</Note>
## Create the marketplace file
Create `.claude-plugin/marketplace.json` in your repository root. This file defines your marketplace's name, owner information, and a list of plugins with their sources.
Each plugin entry needs at minimum a `name` and a `source` that tells Claude Code where to fetch it from. See the [full schema](#marketplace-schema) below for all available fields.
```json theme={null}
{
"name": "company-tools",
"owner": {
"name": "DevTools Team",
"email": "[email protected]"
},
"plugins": [
{
"name": "code-formatter",
"source": "./plugins/formatter",
"description": "Automatic code formatting on save",
"version": "2.1.0",
"author": {
"name": "DevTools Team"
}
},
{
"name": "deployment-tools",
"source": {
"source": "github",
"repo": "company/deploy-plugin"
},
"description": "Deployment automation tools"
}
]
}
```
## Marketplace schema
### Required fields
| Field | Type | Description | Example |
| :-------- | :----- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | :------------- |
| `name` | string | Marketplace identifier (kebab-case, no spaces). This is public-facing: users see it when installing plugins (for example, `/plugin install my-tool@your-marketplace`). Each user can register only one marketplace per name: adding a second marketplace with the same name replaces the first. To publish multiple plugins under one marketplace name, list them all in a [single `marketplace.json`](#create-the-marketplace-file). | `"acme-tools"` |
| `owner` | object | Marketplace maintainer information ([see fields below](#owner-fields)) | |
| `plugins` | array | List of available plugins | See below |
<Note>
**Reserved names**: the following marketplace names are reserved for official Anthropic use and can't be used by third-party marketplaces: `claude-code-marketplace`, `claude-code-plugins`, `claude-plugins-official`, `claude-plugins-community`, `claude-community`, `anthropic-marketplace`, `anthropic-plugins`, `agent-skills`, `anthropic-agent-skills`, `knowledge-work-plugins`, `life-sciences`, `claude-for-legal`, `claude-for-financial-services`, `financial-services-plugins`, `first-party-plugins`, `healthcare`. Names that impersonate official marketplaces, such as `official-claude-plugins` or `anthropic-plugins-v2`, are also blocked. Reserving these names prevents a third-party marketplace from presenting itself as an Anthropic-published source.
Claude Code re-checks reserved names every time it loads a marketplace, not only when you add one. A marketplace that was registered under one of these names before the name became reserved stops loading and reports that it is [registered from an untrusted source](/docs/en/errors#marketplace-is-registered-from-an-untrusted-source). Remove that marketplace and re-add it from the official Anthropic source. A third-party marketplace affected by a newly reserved name loads again as soon as you re-add it under a different name. Before v2.1.205, `first-party-plugins` and `healthcare` weren't reserved, and a marketplace already registered under a reserved name kept loading.
</Note>
### Owner fields
| Field | Type | Required | Description |
| :------ | :----- | :------- | :------------------------------------------- |
| `name` | string | Yes | Name of the maintainer or team |
| `email` | string | No | Contact email for the maintainer |
| `url` | string | No | Website, GitHub profile, or organization URL |
### Optional fields
| Field | Type | Description |
| :------------------------------------ | :----- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `$schema` | string | JSON Schema URL for editor autocomplete and validation. Claude Code ignores this field at load time. |
| `description` | string | Brief marketplace description |
| `version` | string | Marketplace manifest version |
| `metadata.pluginRoot` | string | Base directory prepended to relative plugin source paths (for example, `"./plugins"` lets you write `"source": "formatter"` instead of `"source": "./plugins/formatter"`) |
| `allowCrossMarketplaceDependenciesOn` | array | Other marketplaces that plugins in this marketplace may depend on. Dependencies from a marketplace not listed here are blocked at install. See [Depend on a plugin from another marketplace](/docs/en/plugin-dependencies#depend-on-a-plugin-from-another-marketplace). |
| `renames` | object | Map from a former plugin `name` to its current name, or to `null` if the plugin was removed. Lets existing users migrate automatically when you rename or remove an entry in `plugins`. See [Rename or remove a plugin](#rename-or-remove-a-plugin). Requires Claude Code v2.1.193 or later. |
`description` and `version` are also accepted under `metadata` for backward compatibility.
## Plugin entries
Each plugin entry in the `plugins` array describes a plugin and where to find it. You can include any field from the [plugin manifest schema](/docs/en/plugins-reference#plugin-manifest-schema), such as `description`, `version`, `author`, `commands`, and `hooks`, plus these marketplace-specific fields: `source`, `category`, `tags`, `strict`, and `relevance`.
### Required fields
| Field | Type | Description |
| :------- | :------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------- |
| `name` | string | Plugin identifier (kebab-case, no spaces). This is public-facing: users see it when installing (for example, `/plugin install my-plugin@marketplace`). |
| `source` | string\|object | Where to fetch the plugin from (see [Plugin sources](#plugin-sources) below) |
### Optional plugin fields
**Standard metadata fields:**
| Field | Type | Description |
| :--------------- | :------ | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `displayName` | string | Human-readable name shown in UI surfaces. Falls back to `name` when omitted. May contain spaces and any casing. Not used for namespacing or lookup. Requires Claude Code v2.1.143 or later. |
| `description` | string | Brief plugin description |
| `version` | string | Plugin version. If set (here or in `plugin.json`), the plugin is pinned to this string and users only receive updates when it changes. A plugin with a [`command` source](#command-sources) isn't pinned by either field. If set in neither place, the version comes from the next source in [version management](/docs/en/plugins-reference#version-management). |
| `author` | object | Plugin author information (`name` required; `email` and `url` optional) |
| `homepage` | string | Plugin homepage or documentation URL |
| `repository` | string | Source code repository URL |
| `license` | string | SPDX license identifier (for example, MIT, Apache-2.0) |
| `keywords` | array | Tags for plugin discovery and categorization |
| `metadata` | object | Free-form object for your own fields, such as entitlement or catalog data. Claude Code doesn't read it. Before v2.1.222, `claude plugin validate` reported the key as an unrecognized field. |
| `category` | string | Plugin category for organization |
| `tags` | array | Tags for searchability |
| `strict` | boolean | Controls whether `plugin.json` is the authority for component definitions (default: true). See [Strict mode](#strict-mode) below. |
| `relevance` | object | Signals that tell Claude Code when to suggest this plugin to users. Takes effect only for marketplaces an administrator allowlists in managed settings. See [Recommend plugins for your org](/docs/en/plugin-relevance). Requires Claude Code v2.1.152 or later. |
| `defaultEnabled` | boolean | Whether the plugin is enabled after install (default: true). Set to `false` to install the plugin disabled until the user opts in. Takes precedence over the same field in the plugin's `plugin.json`. See [Default enablement](/docs/en/plugins-reference#default-enablement). Requires Claude Code v2.1.154 or later. |
**Component configuration fields:**
| Field | Type | Description |
| :----------- | :------------- | :------------------------------------------------------------- |
| `skills` | string\|array | Custom paths to skill directories containing `<name>/SKILL.md` |
| `commands` | string\|array | Custom paths to flat `.md` skill files or directories |
| `agents` | string\|array | Custom paths to agent files |
| `hooks` | string\|object | Custom hooks configuration or path to hooks file |
| `mcpServers` | string\|object | MCP server configurations or path to MCP config |
| `lspServers` | string\|object | LSP server configurations or path to LSP config |
## Plugin sources
Plugin sources tell Claude Code where to get each individual plugin listed in your marketplace. These are set in the `source` field of each plugin entry in `marketplace.json`.
Claude Code copies each installed plugin into the local versioned plugin cache at `~/.claude/plugins/cache`, except for a [`command` source in link mode](#copy-mode-and-link-mode), which Claude Code uses in place. Claude Code also [installs the plugin's eligible Node.js package dependencies](/docs/en/plugins-reference#node-js-package-dependencies) into the cached copy.
| Source | Type | Fields | Notes |
| ------------- | ------------------------------- | ---------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- |
| Relative path | `string` (e.g. `"./my-plugin"`) | none | Local directory within the marketplace repo. Must start with `./`. Resolved relative to the marketplace root, not the `.claude-plugin/` directory |
| `github` | object | `repo`, `ref?`, `sha?` | |
| `url` | object | `url`, `ref?`, `sha?` | Git URL source |
| `git-subdir` | object | `url`, `path`, `ref?`, `sha?` | Subdirectory within a git repo. Clones sparsely to minimize bandwidth for monorepos |
| `npm` | object | `package`, `version?`, `registry?` | Installed via `npm install` |
| `archive` | object | `url`, `sha256?` | Zip archive downloaded over HTTPS. Works without git or npm on the user's machine. Requires Claude Code v2.1.224 or later |
| `command` | object | `command`, `timeout?`, `mode?` | Plugin directory produced by running a local command, re-run once per session to pick up changes. Requires Claude Code v2.1.229 or later |
<Note>
**Marketplace sources vs plugin sources**: These are different concepts that control different things.
* **Marketplace source**: where to fetch the `marketplace.json` catalog itself. Set when users run `/plugin marketplace add` or in `extraKnownMarketplaces` settings. Git-based marketplace sources support `ref` (branch/tag) but not `sha`.
* **Plugin source**: where to fetch an individual plugin listed in the marketplace. Set in the `source` field of each plugin entry inside `marketplace.json`. Git-based plugin sources support both `ref` (branch/tag) and `sha` (exact commit).
For example, a marketplace hosted at `acme-corp/plugin-catalog` (marketplace source) can list a plugin fetched from `acme-corp/code-formatter` (plugin source). The marketplace source and plugin source point to different repositories and are pinned independently.
</Note>
The git-based source types below are `github`, `url`, and `git-subdir`. When both `ref` and `sha` are set on any of them, the `sha` is the effective pin. Claude Code fetches and checks out the pinned commit directly.
On most git hosts, including GitHub, GitLab, and Bitbucket, this means installation succeeds even if the branch or tag named by `ref` has since been deleted upstream, as long as the commit is still reachable from the repository. Some servers, such as AWS CodeCommit, don't support fetching commits by SHA. On those servers the `ref` must still exist and the pinned commit must be reachable from it.
<Note>
If you distribute this marketplace through [Organization settings > Plugins](https://claude.ai/admin-settings/plugins) on a Team or Enterprise plan, different source rules apply:
* The marketplace repository must be private or internal. Organization sync reads it through the Claude GitHub App or your organization's GitHub Enterprise App.
* Each plugin source must be of type `github`, `url`, or `git-subdir`, or a [relative path](#relative-paths) within the marketplace repository.
* A plugin source can be private in two cases: a github.com source that shares the marketplace repository's owner, or a source on your organization's GitHub Enterprise host with the GHE App installed on the repository. Organization sync fetches every other source without credentials, so github.com repositories under a different owner and repositories on other hosts, such as GitLab or Bitbucket, must be public.
To include private plugins, place the plugin folders inside the marketplace repository and reference them with a [relative path](#relative-paths). Organization sync packages each plugin during distribution, so users never need access to a separate source repository. See [Manage plugins for your organization](https://support.claude.com/en/articles/13837433) for the admin workflow.
</Note>
### Relative paths
For plugins in the same repository, use a path starting with `./`:
```json theme={null}
{
"name": "my-plugin",
"source": "./plugins/my-plugin"
}
```
Paths resolve relative to the marketplace root, which is the directory containing `.claude-plugin/`. In the example above, `./plugins/my-plugin` points to `<repo>/plugins/my-plugin`, even though `marketplace.json` lives at `<repo>/.claude-plugin/marketplace.json`. Don't use `../` to reference paths outside the marketplace root.
<Note>
Claude Code resolves relative paths against a local copy of the marketplace, so they work when users add your marketplace from a git source or a local directory. If users add your marketplace via a direct URL to the `marketplace.json` file, relative paths won't resolve, because Claude Code downloads only that file. For URL-based distribution, use any other [plugin source](#plugin-sources) instead. See [Troubleshooting](#plugins-with-relative-paths-fail-in-url-based-marketplaces) for details.
</Note>
### GitHub repositories
```json theme={null}
{
"name": "github-plugin",
"source": {
"source": "github",
"repo": "owner/plugin-repo"
}
}
```
You can pin to a specific branch, tag, or commit:
Cut at 300 lines. The page has the rest.
plugin-relevance First recorded · 164 lines, first recorded
# Recommend plugins for your org ## How it works ## Add relevance to a plugin entry ## Field reference ### `relevance` ### `relevance.signals` ## Enable suggestions in managed settings ## What the user sees ## Validate your marketplace ## See also
The first capture of this source. The page was already there, and this is what it said.
# Recommend plugins for your org
> Add a relevance block to marketplace plugin entries so Claude Code suggests them when a user's work matches.
If you operate a plugin marketplace for your organization, you can have Claude Code suggest specific plugins to users based on what they are working on. Add a `relevance` block to a plugin's entry in `marketplace.json`, then allowlist the marketplace in managed settings. When a user's session matches one of the declared signals, Claude Code surfaces an install suggestion for that plugin.
Marketplace-declared suggestions are opt-in per marketplace through [managed settings](/docs/en/settings#settings-files). No marketplace's `relevance` declarations produce suggestions until an administrator adds it to the allowlist, including the official Anthropic marketplace. Claude Code also includes one built-in suggestion that is independent of this allowlist; that tip and all marketplace-declared tips are disabled when [`spinnerTipsEnabled`](/docs/en/settings#available-settings) is set to `false`.
This feature requires Claude Code v2.1.152 or later. Older clients ignore the `relevance` field.
This page is for marketplace operators and enterprise administrators. If you are looking to install plugins, see [Discover and install plugins](/docs/en/discover-plugins).
## How it works
Each plugin entry in `marketplace.json` can carry a `relevance` object. The object names a topic and one or more signals. A signal is a pattern that Claude Code tests against the current session, such as the working directory or files Claude has read.
Signal matching happens locally on the user's machine. The matching adds no network traffic and does not report which signals matched, or their values, to Anthropic or to the marketplace operator.
When a signal matches and the plugin is not already installed, Claude Code shows the plugin in three places:
* **Spinner tip**: a "Working with *topic*? Install the *plugin* plugin" message with the `/plugin install` command appears below the spinner while Claude is responding.
* **Session-start suggestion**: if the `cwd` signal matches the working directory, a one-line `plugin suggestion: <name>@<marketplace> · /plugin` notification appears before the first turn. This surface requires Claude Code v2.1.153 or later.
* **`/plugin` Discover tab**: the plugin is pinned to the top of the Discover list with an annotation such as "suggested for this directory" or "suggested for stripe commands". This surface requires Claude Code v2.1.154 or later.
The spinner tip and the session-start notification are part of the spinner-tips system. Both are disabled when the user or project sets `spinnerTipsEnabled` to `false`, or when a custom `spinnerTipsOverride` is configured with `excludeDefault`. The Discover-tab pin is independent of tip settings.
Claude Code never installs a plugin automatically. The user always confirms.
## Add relevance to a plugin entry
Add a `relevance` object to the plugin's entry in your `marketplace.json`. The following example declares that the `terraform-helpers` plugin is relevant when Claude reads a `.tf` file or when Claude runs `terraform`:
```json theme={null}
{
"name": "acme-corp-plugins",
"owner": { "name": "Acme Platform Team" },
"plugins": [
{
"name": "terraform-helpers",
"source": "./plugins/terraform-helpers",
"description": "Acme conventions and helpers for Terraform",
"relevance": {
"topic": "Terraform",
"signals": {
"cli": ["terraform"],
"filesRead": ["**/*.tf"]
}
}
}
]
}
```
A plugin with a `relevance` block but no matching signal behaves like any other marketplace entry. It appears in the Discover list in its normal position and never surfaces as a spinner tip.
## Field reference
### `relevance`
| Field | Type | Description |
| :-------- | :----- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `topic` | string | Optional. The phrase that fills "Working with *topic*?" in the spinner tip. Often the product name, for example `Stripe`. Use a domain such as `design` when the plugin name does not read naturally as a topic. Defaults to the plugin name with each hyphen segment capitalized. The session-start notification does not use this value. Maximum 64 characters. |
| `signals` | object | Matchers that determine when the plugin is relevant. At least one signal is required for the plugin to be suggestible. See the table below. |
### `relevance.signals`
| Field | Type | Description |
| :------------- | :--------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `cwd` | array of strings | Glob patterns matched against the session's working directory. Matched as an absolute path and, when inside a git repository, as a path relative to the repository root. Forward-slash normalized and case-insensitive. Every pattern matches the directory itself and everything under it, so `infra`, `infra/`, and `infra/**` behave identically. This is the only signal that can match at session start, before the first turn. Maximum 10 patterns of 256 characters each. |
| `cli` | array of strings | Command names from shell commands Claude has run this session, for example `["stripe"]`. Applies on every platform: commands run on Windows through PowerShell or Git Bash are recorded the same way. Claude Code records one command name per shell tool invocation: the first token after any leading environment variable assignments and `sudo`. Compound commands contribute only their leading command, so `cd infra && terraform plan` records `cd`, not `terraform`. Exact match. Maximum 10 entries of 64 characters each. |
| `hosts` | array of strings | Hostnames seen in `http://` or `https://` URLs in Bash commands this session, for example `["api.stripe.com"]`. Bare lowercase hostname only: no scheme, port, or path. Exact case-insensitive match. Maximum 20 entries of 128 characters each. |
| `filesRead` | array of strings | Glob patterns matched against the paths of files Claude has read this session, for example `["**/*.tf"]`. Forward-slash normalized and case-insensitive. Maximum 10 patterns of 256 characters each. |
| `manifestDeps` | array of objects | Dependencies declared in package manifests Claude has read this session. Each entry is `{ "file": "...", "pattern": "..." }`, where `file` is a regular expression matched against the manifest file's path as recorded in session state, typically an absolute path, and `pattern` is a regular expression matched against that file's contents. Anchor `file` at the end, for example `[/\\\\]package\\.json$` in JSON-escaped form, because a start-anchored pattern never matches an absolute path. Paths are not separator-normalized for this signal, so Windows paths use backslashes. Manifest files larger than 512 KB are skipped. Both values are JavaScript `RegExp` source strings of at most 256 characters. `file` matches case-insensitively. `pattern` is case-sensitive. Maximum 10 entries. |
The `cli`, `hosts`, `filesRead`, and `manifestDeps` signals need session history, so they can only match on the spinner tip and the Discover tab. The `filesRead` and `manifestDeps` signals test the session's recorded file state, which also includes files Claude has written or edited and auto-loaded `CLAUDE.md` memory files.
The following example uses `manifestDeps` to suggest a Stripe plugin once Claude has read a `package.json` that depends on `stripe`. The `file` pattern uses `[/\\\\]` so it matches both forward-slash and backslash path separators, and `\\.` so the dot is literal. In JSON, each backslash in the regular expression is written twice.
```json theme={null}
{
"name": "stripe-helpers",
"source": "./plugins/stripe-helpers",
"relevance": {
"topic": "Stripe",
"signals": {
"manifestDeps": [
{
"file": "[/\\\\]package\\.json$",
"pattern": "\"stripe\"\\s*:"
}
]
}
}
}
```
<Note>
Claude Code ignores unknown fields under `relevance` and `relevance.signals` at load time, so older clients continue to load your marketplace.
</Note>
## Enable suggestions in managed settings
Declaring `relevance` in `marketplace.json` is not enough on its own. An administrator must allowlist the marketplace in [managed settings](/docs/en/settings#settings-files) before its suggestions appear to users.
Add the marketplace name to `pluginSuggestionMarketplaces`. For any marketplace other than the official Anthropic marketplace, also declare the marketplace source in the same managed settings, either as that name's entry in `extraKnownMarketplaces` or as an entry in `strictKnownMarketplaces`. The allowlisted name is ignored if the marketplace registered on the machine came from a different source. This prevents an unrelated source from registering under an allowlisted name to have its plugins suggested across your org.
The following `managed-settings.json` registers an org marketplace from a GitHub repository and enables its suggestions:
```json theme={null}
{
"extraKnownMarketplaces": {
"acme-corp-plugins": {
"source": {
"source": "github",
"repo": "acme-corp/claude-plugins"
}
}
},
"pluginSuggestionMarketplaces": ["acme-corp-plugins"]
}
```
The official marketplace is exempt from the source-declaration requirement because its name can only register from the official Anthropic source. Allowlisting the name alone is sufficient:
```json theme={null}
{
"pluginSuggestionMarketplaces": ["claude-plugins-official"]
}
```
## What the user sees
When a signal matches during a session, the spinner tip reads:
```text theme={null}
Working with Terraform? Install the terraform-helpers plugin:
/plugin install terraform-helpers@acme-corp-plugins
```
At session start, a matching `cwd` signal surfaces the one-line notification:
```text theme={null}
plugin suggestion: terraform-helpers@acme-corp-plugins · /plugin
```
A given plugin's suggestion appears at most once every three sessions across the spinner tip and the session-start notification combined, and neither repeats once the plugin is installed. The session-start notification additionally stops appearing after the suggestion has been shown twice.
In the `/plugin` Discover tab, the plugin is pinned above the other results with an annotation that names the matching signal, such as `suggested for this directory` or `suggested for terraform commands`. The Discover tab pins a given plugin once; later visits list it in normal order.
## Validate your marketplace
Run `claude plugin validate` against your marketplace directory to check the `relevance` block before publishing:
```
claude plugin validate ./my-marketplace
```
The validator reports unknown keys under `relevance` and `relevance.signals` as warnings, flags a `relevance` value that is not an object, and rejects a `signals.hosts` entry that includes a scheme, port, or path.
## See also
* [Create and distribute a plugin marketplace](/docs/en/plugin-marketplaces): build the marketplace that hosts your plugins
* [Recommend your plugin from your CLI](/docs/en/plugin-hints): prompt users from your own CLI instead of from Claude Code's session signals
* [Settings](/docs/en/settings): full reference for `pluginSuggestionMarketplaces` and `extraKnownMarketplaces`
plugins First recorded · 469 lines, first recorded
# Create plugins ## When to use plugins vs standalone configuration ## Quickstart ### Prerequisites ### Create your first plugin ## Develop a plugin in your skills directory ## Plugin structure overview ## Develop more complex plugins ### Add Skills to your plugin ### Add LSP servers to your plugin ### Add background monitors to your plugin ### Ship default settings with your plugin ### Organize complex plugins ### Test your plugins locally ### Debug plugin issues ### Share your plugins ### Submit your plugin to the community marketplace ## Convert existing configurations to plugins ### Migration steps ### What changes when migrating ## Next steps ### For plugin users ### For plugin developers
The first capture of this source. The page was already there, and this is what it said.
# Create plugins
> Create custom plugins to extend Claude Code with skills, agents, hooks, and MCP servers.
Plugins let you extend Claude Code with custom functionality that can be shared across projects and teams. This guide covers creating your own plugins with skills, agents, hooks, and MCP servers.
Looking to install existing plugins? See [Discover and install plugins](/docs/en/discover-plugins). For complete technical specifications, see [Plugins reference](/docs/en/plugins-reference).
## When to use plugins vs standalone configuration
Claude Code supports two ways to add custom skills, agents, and hooks:
| Approach | Skill names | Best for |
| :-------------------------------------------------------------------------------------------------------------- | :------------------- | :---------------------------------------------------------------------------------------------- |
| **Standalone** (`.claude/` directory) | `/hello` | Personal workflows, project-specific customizations, quick experiments |
| **Plugins** (self-contained directories with skills, agents, hooks, or a `.claude-plugin/plugin.json` manifest) | `/plugin-name:hello` | Sharing with teammates, distributing to community, versioned releases, reusable across projects |
<Tip>
Start with standalone configuration in `.claude/` for quick iteration, then [convert to a plugin](#convert-existing-configurations-to-plugins) when you're ready to share.
</Tip>
## Quickstart
This quickstart walks you through creating a plugin with a custom skill. You'll create a manifest (the configuration file that defines your plugin), add a skill, and test it locally using the `--plugin-dir` flag.
### Prerequisites
* Claude Code [installed and authenticated](/docs/en/quickstart#step-1-install-claude-code)
### Create your first plugin
<Steps>
<Step title="Create the plugin directory">
Every plugin lives in its own directory containing your skills, agents, or hooks, optionally alongside a `.claude-plugin/plugin.json` manifest. The location doesn't matter for this quickstart because you'll point Claude Code at the directory with `--plugin-dir` in the test step. Create it anywhere convenient, such as a scratch folder or a projects directory:
```bash theme={null}
mkdir my-first-plugin
```
The remaining steps run from the parent directory and reference paths like `my-first-plugin/...` relative to it.
</Step>
<Step title="Create the plugin manifest">
The manifest file at `.claude-plugin/plugin.json` defines your plugin's identity: its name, description, and version. Claude Code uses this metadata to display your plugin in the plugin manager.
Create the `.claude-plugin` directory inside your plugin folder:
```bash theme={null}
mkdir my-first-plugin/.claude-plugin
```
Then create `my-first-plugin/.claude-plugin/plugin.json` with this content:
```json my-first-plugin/.claude-plugin/plugin.json theme={null}
{
"name": "my-first-plugin",
"description": "A greeting plugin to learn the basics",
"version": "1.0.0",
"author": {
"name": "Your Name"
}
}
```
| Field | Purpose |
| :------------ | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `name` | Unique identifier and skill namespace. Skills are prefixed with this (e.g., `/my-first-plugin:hello`). |
| `description` | Shown in the plugin manager when browsing or installing plugins. |
| `version` | Optional. If set, users only receive updates when you bump this field, except for a [`command` source](/docs/en/plugin-marketplaces#command-sources); see [version management](/docs/en/plugins-reference#version-management). If omitted, the version comes from the next source in [version management](/docs/en/plugins-reference#version-management). |
| `author` | Optional. Helpful for attribution. |
For additional fields like `homepage`, `repository`, and `license`, see the [full manifest schema](/docs/en/plugins-reference#plugin-manifest-schema).
</Step>
<Step title="Add a skill">
Skills live in the `skills/` directory. Each skill is a folder containing a `SKILL.md` file. The folder name becomes the skill name, prefixed with the plugin's namespace (`hello/` in a plugin named `my-first-plugin` creates `/my-first-plugin:hello`).
Create a skill directory in your plugin folder:
```bash theme={null}
mkdir -p my-first-plugin/skills/hello
```
Then create `my-first-plugin/skills/hello/SKILL.md` with this content:
```markdown my-first-plugin/skills/hello/SKILL.md theme={null}
---
description: Greet the user with a friendly message
disable-model-invocation: true
---
Greet the user warmly and ask how you can help them today.
```
</Step>
<Step title="Test your plugin">
Run Claude Code with the `--plugin-dir` flag to load your plugin:
```bash theme={null}
claude --plugin-dir ./my-first-plugin
```
Once Claude Code starts, try your new skill:
```shell theme={null}
/my-first-plugin:hello
```
You'll see Claude respond with a greeting. Run `/help` and open the **Custom commands** tab to see your skill listed under the plugin namespace.
<Note>
**Why namespacing?** Plugin skills are always namespaced (like `/my-first-plugin:hello`) to prevent conflicts when multiple plugins have skills with the same name.
To change the namespace prefix, update the `name` field in `plugin.json`.
</Note>
</Step>
<Step title="Add skill arguments">
Make your skill dynamic by accepting user input. The `$ARGUMENTS` placeholder captures any text the user provides after the skill name.
Update your `SKILL.md` file:
```markdown my-first-plugin/skills/hello/SKILL.md theme={null}
---
description: Greet the user with a personalized message
---
# Hello Skill
Greet the user named "$ARGUMENTS" warmly and ask how you can help them today. Make the greeting personal and encouraging.
```
Run `/reload-plugins` to pick up the changes. The skills count in the summary covers only `commands/` directories, so it can report `0 skills` even though the skill you just edited reloaded. Then try the skill with your name:
```shell theme={null}
/my-first-plugin:hello Alex
```
Claude will greet you by name. For more on passing arguments to skills, see [Skills](/docs/en/skills#pass-arguments-to-skills).
</Step>
</Steps>
<Tip>
The `--plugin-dir` flag is useful for development and testing. When you're ready to share your plugin with others, see [Create and distribute a plugin marketplace](/docs/en/plugin-marketplaces).
</Tip>
## Develop a plugin in your skills directory
Instead of passing `--plugin-dir` on every launch, you can keep a plugin in your skills directory and have Claude Code load it automatically. `claude plugin init` scaffolds one:
```bash theme={null}
claude plugin init my-tool
```
This creates `~/.claude/skills/my-tool/` with a `.claude-plugin/plugin.json` manifest and a starter `SKILL.md`. On the next session it loads as `my-tool@skills-dir` with no marketplace or install step.
For the auto-load rules, personal vs. project scope, the workspace-trust requirement, and how to update or remove one, see [Skills-directory plugins](/docs/en/plugins-reference#skills-directory-plugins).
## Plugin structure overview
You've created a plugin with a skill, but plugins can include much more: custom agents, hooks, MCP servers, LSP servers, and background monitors.
<Warning>
**Common mistake**: Don't put `commands/`, `agents/`, `skills/`, or `hooks/` inside the `.claude-plugin/` directory. Only `plugin.json` goes inside `.claude-plugin/`. All other directories must be at the plugin root level.
The plugin root is the individual plugin's own directory: the one you pass to `--plugin-dir` or that contains `.claude-plugin/plugin.json`. It is never `~/.claude/`. For example, Claude Code doesn't read a `.mcp.json` placed at `~/.claude/.mcp.json`.
</Warning>
| Directory | Location | Purpose |
| :---------------- | :---------- | :----------------------------------------------------------------------------- |
| `.claude-plugin/` | Plugin root | Contains `plugin.json` manifest (optional if components use default locations) |
| `skills/` | Plugin root | Skills as `<name>/SKILL.md` directories |
| `commands/` | Plugin root | Skills as flat Markdown files. Use `skills/` for new plugins |
| `agents/` | Plugin root | Custom agent definitions |
| `hooks/` | Plugin root | Event handlers in `hooks.json` |
| `.mcp.json` | Plugin root | MCP server configurations |
| `.lsp.json` | Plugin root | LSP server configurations for code intelligence |
| `monitors/` | Plugin root | Background monitor configurations in `monitors.json` |
| `bin/` | Plugin root | Executables added to the Bash tool's `PATH` while the plugin is enabled |
| `settings.json` | Plugin root | Default [settings](/docs/en/settings) applied when the plugin is enabled |
A plugin that ships exactly one skill can place `SKILL.md` directly at the plugin root instead of creating a `skills/` directory. Claude Code loads it as a single skill and uses the frontmatter `name` field for the invocation name. Use the `skills/` layout for plugins that may grow to more than one skill.
## Develop more complex plugins
Once you're comfortable with basic plugins, you can create more sophisticated extensions.
### Add Skills to your plugin
Plugins can include [Agent Skills](/docs/en/skills) to extend Claude's capabilities. Skills are model-invoked: Claude automatically uses them based on the task context.
Add a `skills/` directory at your plugin root with Skill folders containing `SKILL.md` files:
```text theme={null}
my-plugin/
├── .claude-plugin/
│ └── plugin.json
└── skills/
└── code-review/
└── SKILL.md
```
Each `SKILL.md` contains YAML frontmatter and instructions. Include a `description` so Claude knows when to use the skill:
```yaml theme={null}
---
description: Reviews code for best practices and potential issues. Use when reviewing code, checking PRs, or analyzing code quality.
---
When reviewing code, check for:
1. Code organization and structure
2. Error handling
3. Security concerns
4. Test coverage
```
After you install the plugin, check the install summary: if it reports `Run /reload-plugins to activate.`, run that command to load the Skills. For complete Skill authoring guidance including progressive disclosure and tool restrictions, see [Agent Skills](/docs/en/skills).
### Add LSP servers to your plugin
<Tip>
For common languages like TypeScript, Python, and Rust, install the pre-built LSP plugins from the official marketplace. Create custom LSP plugins only when you need support for languages not already covered.
</Tip>
LSP (Language Server Protocol) plugins give Claude real-time code intelligence. If you need to support a language that doesn't have an official LSP plugin, you can create your own by adding an `.lsp.json` file to your plugin:
```json .lsp.json theme={null}
{
"go": {
"command": "gopls",
"args": ["serve"],
"extensionToLanguage": {
".go": "go"
}
}
}
```
Users installing your plugin must have the language server binary installed on their machine.
To confirm the server starts, launch Claude Code with the plugin enabled and check the `/plugin` Errors tab: a language server that fails to start appears there, for example with `Executable not found in $PATH` when the binary isn't installed. An entry with an invalid configuration is skipped instead; run `claude --debug` to see why.
For complete LSP configuration options, see [LSP servers](/docs/en/plugins-reference#lsp-servers).
### Add background monitors to your plugin
Background monitors let your plugin watch logs, files, or external status in the background and notify Claude as events arrive. Claude Code starts each monitor automatically when the plugin is active, so you don't need to instruct Claude to start the watch.
Add a `monitors/monitors.json` file at the plugin root with an array of monitor entries:
```json monitors/monitors.json theme={null}
[
{
"name": "error-log",
"command": "tail -F ./logs/error.log",
"description": "Application error log"
}
]
```
Each stdout line from `command` is delivered to Claude as a notification during the session. For the full schema, including the `when` trigger and variable substitution, see [Monitors](/docs/en/plugins-reference#monitors).
### Ship default settings with your plugin
Plugins can include a `settings.json` file at the plugin root to apply default configuration when the plugin is enabled. Currently, only the `agent` and `subagentStatusLine` keys are supported.
Setting `agent` activates one of the plugin's [custom agents](/docs/en/sub-agents) as the main thread, applying its system prompt, tool restrictions, and model. This lets a plugin change how Claude Code behaves by default when enabled.
```json settings.json theme={null}
{
"agent": "security-reviewer"
}
```
This example activates the `security-reviewer` agent defined in the plugin's `agents/` directory. Settings from `settings.json` take priority over `settings` declared in `plugin.json`. Unknown keys are silently ignored.
### Organize complex plugins
For plugins with many components, organize your directory structure by functionality. For complete directory layouts and organization patterns, see [Plugin directory structure](/docs/en/plugins-reference#plugin-directory-structure).
### Test your plugins locally
Use the `--plugin-dir` flag to test plugins during development. This loads your plugin directly without requiring installation.
```bash theme={null}
claude --plugin-dir ./my-plugin
```
The flag also accepts a `.zip` archive of the plugin directory.
```bash theme={null}
claude --plugin-dir ./my-plugin.zip
```
When a `--plugin-dir` plugin has the same name as an installed marketplace plugin, the local copy takes precedence for that session. This lets you test changes to a plugin you already have installed without uninstalling it first. The exception is plugins that managed settings force-enable or force-disable: `--plugin-dir` cannot override those.
As you make changes to your plugin, run `/reload-plugins` to pick up the updates without restarting. This reloads plugins, skills, agents, hooks, plugin MCP servers, and plugin LSP servers. Test your plugin components:
* Try your skills with `/plugin-name:skill-name`
* Check that agents appear in `/context` under Custom Agents, or @-mention one by its scoped name
Cut at 300 lines. The page has the rest.
plugins-reference First recorded · 1312 lines, first recorded
# Plugins reference ## Plugin components reference ### Skills ### Agents ### Hooks ### MCP servers ### LSP servers ### Monitors ### Themes ## Plugin installation scopes ## Skills-directory plugins ### Choose where the plugin loads from ### Edit, reload, and disable a skills-directory plugin ## Plugin manifest schema ### Complete schema ### Required fields ### Unrecognized fields ### Metadata fields ### Default enablement ### Component path fields ### Experimental components ### User configuration ### Channels ### Path behavior rules ### Environment variables #### Persistent data directory ## Plugin caching and file resolution ### Node.js package dependencies ### Path traversal limitations ### Share files within a marketplace with symlinks ## Plugin directory structure ### Standard plugin layout ### File locations reference ## CLI commands reference ### plugin init ### plugin install ### plugin uninstall ### plugin prune ### plugin enable ### plugin disable ### plugin update ### plugin list ### plugin details ### plugin tag ## Debugging and development tools ### Debugging commands ### Common issues ### Example error messages ### Hook troubleshooting ### MCP server troubleshooting ### Directory structure mistakes ## Distribution and versioning reference ### Version management ## See also
The first capture of this source. The page was already there, and this is what it said.
# Plugins reference
> Complete technical reference for Claude Code plugin system, including schemas, CLI commands, and component specifications.
<Tip>
Looking to install plugins? See [Discover and install plugins](/docs/en/discover-plugins). For creating plugins, see [Plugins](/docs/en/plugins). For distributing plugins, see [Plugin marketplaces](/docs/en/plugin-marketplaces).
</Tip>
A **plugin** is a self-contained directory of components that extends Claude Code with custom functionality. Plugin components include skills, agents, hooks, MCP servers, LSP servers, and monitors.
## Plugin components reference
### Skills
Plugins add skills to Claude Code, creating `/name` shortcuts that you or Claude can invoke.
**Location**: `skills/` or `commands/` directory in plugin root, or a single `SKILL.md` file at the plugin root
**File format**: Skills are directories with `SKILL.md`; commands are simple markdown files
**Skill structure**:
```text theme={null}
skills/
├── pdf-processor/
│ ├── SKILL.md
│ ├── reference.md (optional)
│ └── scripts/ (optional)
└── code-reviewer/
└── SKILL.md
```
Skills and commands are automatically discovered when the plugin is installed.
If a plugin has no `skills/` directory and no `skills` manifest field, a `SKILL.md` at the plugin root is loaded as a single skill. Set the frontmatter `name` field to control the skill's invocation name. Without it, Claude Code falls back to the install directory name, which for marketplace-installed plugins is a version string that changes on every update. For plugins that ship more than one skill, use the `skills/` directory layout shown above.
In plugin skills and commands, Boolean frontmatter fields such as `disable-model-invocation` accept `yes`, `no`, `on`, `off`, `1`, and `0` in any letter case, in addition to `true` and `false`. Before v2.1.218, Claude Code recognized only `true` and `false`.
For complete details, see [Skills](/docs/en/skills).
### Agents
Plugins can provide specialized subagents for specific tasks that Claude can invoke automatically when appropriate.
**Location**: `agents/` directory in plugin root
**File format**: Markdown files describing agent capabilities
**Agent structure**:
```markdown theme={null}
---
name: agent-name
description: What this agent specializes in and when Claude should invoke it
model: sonnet
effort: medium
maxTurns: 20
disallowedTools: Write, Edit
---
Detailed system prompt for the agent describing its role, expertise, and behavior.
```
Plugin agents support `name`, `description`, `model`, `effort`, `maxTurns`, `tools`, `disallowedTools`, `skills`, `memory`, `background`, and `isolation` frontmatter fields. The only valid `isolation` value is `"worktree"`. For security reasons, `hooks`, `mcpServers`, and `permissionMode` are not supported for plugin-shipped agents.
Agents appear in the [@-mention typeahead](/docs/en/sub-agents#invoke-subagents-explicitly) under their scoped name, such as `my-plugin:code-reviewer`, once the plugin is enabled.
For complete details, see [Subagents](/docs/en/sub-agents).
### Hooks
Plugins can provide event handlers that respond to Claude Code events automatically.
**Location**: `hooks/hooks.json` in plugin root, or inline in plugin.json
**Format**: JSON configuration with event matchers and actions
**Hook configuration**:
```json theme={null}
{
"hooks": {
"PostToolUse": [
{
"matcher": "Write|Edit",
"hooks": [
{
"type": "command",
"command": "\"${CLAUDE_PLUGIN_ROOT}\"/scripts/format-code.sh"
}
]
}
]
}
}
```
Plugin hooks respond to the same lifecycle events as [user-defined hooks](/docs/en/hooks):
| Event | When it fires |
| :-------------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `SessionStart` | When a session begins or resumes |
| `Setup` | When you start Claude Code with `--init-only`, or with `--init` or `--maintenance` in `-p` mode. For one-time preparation in CI or scripts |
| `UserPromptSubmit` | When you submit a prompt, before Claude processes it |
| `UserPromptExpansion` | When a user-typed command expands into a prompt, before it reaches Claude. Can block the expansion |
| `PreToolUse` | Before a tool call executes. Can block it |
| `PermissionRequest` | When a tool call needs a permission decision |
| `PermissionDenied` | When auto mode denies a tool call, including denials without a classifier verdict. Use JSON `hookSpecificOutput.retry: true` to tell the model it may retry the denied tool call. Claude Code ignores `retry` when the classifier produced no verdict |
| `PostToolUse` | After a tool call succeeds |
| `PostToolUseFailure` | After a tool call fails |
| `PostToolBatch` | After a full batch of parallel tool calls resolves, before the next model call |
| `Notification` | When Claude Code sends a notification |
| `MessageDisplay` | While assistant message text is displayed |
| `SubagentStart` | When a subagent is spawned |
| `SubagentStop` | When a subagent finishes |
| `TaskCreated` | When a task is being created via `TaskCreate` |
| `TaskCompleted` | When a task is being marked as completed |
| `Stop` | When Claude finishes responding |
| `StopFailure` | When the turn ends due to an API error |
| `TeammateIdle` | When an [agent team](/docs/en/agent-teams) teammate is about to go idle |
| `InstructionsLoaded` | When a CLAUDE.md or `.claude/rules/*.md` file is loaded into context. Fires at session start and when files are lazily loaded during a session |
| `ConfigChange` | When a configuration file changes during a session |
| `CwdChanged` | When the working directory changes, for example when Claude executes a `cd` command. Useful for reactive environment management with tools like direnv |
| `DirectoryAdded` | When a working directory is added mid-session via `/add-dir` or the SDK `register_repo_root` control request |
| `FileChanged` | When a watched file changes on disk. The `matcher` field specifies which filenames to watch |
| `WorktreeCreate` | When a worktree is being created via `--worktree`, `isolation: "worktree"`, or for a background session. Replaces default git behavior |
| `WorktreeRemove` | When a worktree is being removed at session exit, when a subagent finishes, or when you delete a background session |
| `PreCompact` | Before context compaction |
| `PostCompact` | After context compaction completes |
| `Elicitation` | When an MCP server requests user input during a tool call |
| `ElicitationResult` | After a user responds to an MCP elicitation, before the response is sent back to the server |
| `SessionEnd` | When a session terminates |
**Hook types**:
* `command`: execute shell commands or scripts
* `http`: send the event JSON as a POST request to a URL
* `mcp_tool`: call a tool on a configured [MCP server](/docs/en/mcp)
* `prompt`: evaluate a prompt with an LLM (uses `$ARGUMENTS` placeholder for context)
* `agent`: run an agentic verifier with tools for complex verification tasks
Hooks that target the plugin's own [bundled MCP server](#mcp-servers) must use its scoped names. Tool matchers and `if` fields take the scoped tool name `mcp__plugin_<plugin-name>_<server-name>__<tool>`, and an `mcp_tool` hook's `server` field takes `plugin:<plugin-name>:<server-name>`. A matcher written against the bare server key never fires. See [Match MCP tools](/docs/en/hooks#match-mcp-tools) and [Plugin-provided MCP servers](/docs/en/mcp#plugin-provided-mcp-servers).
### MCP servers
Plugins can bundle Model Context Protocol (MCP) servers to connect Claude Code with external tools and services.
**Location**: `.mcp.json` in plugin root, or inline in plugin.json
**Format**: Standard MCP server configuration
**MCP server configuration**:
```json theme={null}
{
"mcpServers": {
"plugin-database": {
"command": "${CLAUDE_PLUGIN_ROOT}/servers/db-server",
"args": ["--config", "${CLAUDE_PLUGIN_ROOT}/config.json"],
"env": {
"DB_PATH": "${CLAUDE_PLUGIN_ROOT}/data"
}
},
"plugin-api-client": {
"command": "npx",
"args": ["@company/mcp-server", "--plugin-mode"]
}
}
}
```
**Integration behavior**:
* Plugin MCP servers start automatically when the plugin is enabled
* Servers appear as standard MCP tools in Claude's toolkit
* Plugin servers can be configured independently of user MCP servers
* If you run [`/reload-plugins`](/docs/en/discover-plugins#apply-plugin-changes-without-restarting) mid-session, Claude Code keeps the live connections of servers whose configuration is unchanged
### LSP servers
<Tip>
Looking to use LSP plugins? Install them from the official marketplace: search for "lsp" in the `/plugin` Discover tab. This section documents how to create LSP plugins for languages not covered by the official marketplace.
</Tip>
Plugins can provide [Language Server Protocol](https://microsoft.github.io/language-server-protocol/) (LSP) servers to give Claude [real-time code intelligence](/docs/en/discover-plugins#code-intelligence) while working on your codebase.
**Location**: `.lsp.json` in plugin root, or inline in `plugin.json`
**Format**: JSON configuration mapping language server names to their configurations
**`.lsp.json` file format**:
```json theme={null}
{
"go": {
"command": "gopls",
"args": ["serve"],
"extensionToLanguage": {
".go": "go"
}
}
}
```
**Inline in `plugin.json`**:
```json theme={null}
{
"name": "my-plugin",
"lspServers": {
"go": {
"command": "gopls",
"args": ["serve"],
"extensionToLanguage": {
".go": "go"
}
}
}
}
```
**Required fields:**
| Field | Description |
| :-------------------- | :------------------------------------------- |
| `command` | The LSP binary to execute (must be in PATH) |
| `extensionToLanguage` | Maps file extensions to language identifiers |
**Optional fields:**
| Field | Description |
| :---------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `args` | Command-line arguments for the LSP server |
| `transport` | Communication transport: `stdio` (default) or `socket`. Claude Code accepts `socket` but runs every server over stdio, so the stdout protocol rules apply to all servers |
| `env` | Environment variables to set when starting the server |
| `initializationOptions` | Options passed to the server during initialization |
| `settings` | Settings passed via `workspace/didChangeConfiguration` |
| `workspaceFolder` | Workspace folder path for the server |
| `startupTimeout` | Max time to wait for server startup (milliseconds) |
| `shutdownTimeout` | Max time to wait for graceful shutdown (milliseconds). When the timeout elapses, Claude Code terminates the server process. When unset, no timeout applies |
| `restartOnCrash` | Whether to restart the server after it crashes. Defaults to `true`. Set to `false` to leave a crashed server stopped instead of restarting it |
| `maxRestarts` | Maximum number of restart attempts before giving up |
| `diagnostics` | Whether to push diagnostics into Claude's context after edits (default `true`). Set to `false` to keep code navigation but suppress automatic diagnostic injection. |
`restartOnCrash` and `shutdownTimeout` require Claude Code v2.1.205 or later. Before v2.1.205, the config schema accepted both options but setting either one caused Claude Code to skip that LSP server entirely at startup, with the reason visible only in `claude --debug` output.
**Multiple servers for the same extension**: when more than one enabled LSP server declares the same file extension in `extensionToLanguage`, whether the servers come from one plugin or from different plugins, the first server registered handles files with that extension and the others never start. The `/plugin` interface shows a warning naming the plugin whose server is active.
**Servers that fail to initialize**: Claude Code skips a server whose configuration is invalid, for example one missing `command` or `extensionToLanguage`, and the other configured servers still start. Run `claude --debug` to see why a server was skipped.
A skipped server doesn't claim its file extensions, so another valid server that declares the same extension, from the same or a different plugin, still handles those files.
**Send log output to stderr, not stdout**: Claude Code reads a server's stdout as protocol messages only, and accepts message headers up to 64 KiB and a message body up to 32 MiB. Claude Code disconnects a server that exceeds either limit or writes non-protocol output to stdout, and counts the disconnect as a crash for `restartOnCrash` and `maxRestarts`. When you run with `--debug`, Claude Code writes an error naming the cause to the debug log.
<Warning>
**You must install the language server binary separately.** LSP plugins configure how Claude Code connects to a language server, but they don't include the server itself. If you see `Executable not found in $PATH` in the `/plugin` Errors tab, install the required binary for your language.
</Warning>
**Available LSP plugins:**
| Plugin | Language server | Install command |
| :------------------ | :------------------------- | :----------------------------------------------------------------------------------------- |
| `pyright-lsp` | Pyright (Python) | `pip install pyright` or `npm install -g pyright` |
| `typescript-lsp` | TypeScript Language Server | `npm install -g typescript-language-server typescript` |
| `rust-analyzer-lsp` | rust-analyzer | [See rust-analyzer installation](https://rust-analyzer.github.io/manual.html#installation) |
Install the language server first, then install the plugin from the marketplace.
### Monitors
Plugins can declare background monitors that Claude Code starts automatically when the plugin is active. Each monitor runs a shell command for the lifetime of the session and delivers every stdout line to Claude as a notification, so Claude can react to log entries, status changes, or polled events without being asked to start the watch itself.
Plugin monitors use the same mechanism as the [Monitor tool](/docs/en/tools-reference#monitor-tool) and share its availability constraints. They run only in interactive CLI sessions, run unsandboxed at the same trust level as [hooks](#hooks), and are skipped on hosts where the Monitor tool is unavailable.
**Location**: `monitors/monitors.json` in the plugin root, or inline in `plugin.json`
**Format**: JSON array of monitor entries
The following `monitors/monitors.json` watches a deployment status endpoint and a local error log:
```json theme={null}
[
{
"name": "deploy-status",
"command": "\"${CLAUDE_PLUGIN_ROOT}\"/scripts/poll-deploy.sh",
"description": "Deployment status changes"
},
{
"name": "error-log",
"command": "tail -F ./logs/error.log",
"description": "Application error log",
"when": "on-skill-invoke:debug"
}
]
```
To declare monitors inline, set `experimental.monitors` in `plugin.json` to the same array. To load from a non-default path, set `experimental.monitors` to a relative path string such as `"./config/monitors.json"`. Monitors are an [experimental component](#experimental-components).
**Required fields:**
Cut at 300 lines. The page has the rest.
prompt-caching First recorded · 253 lines, first recorded
# How Claude Code uses prompt caching ## How the cache is organized ### Where the cache lives ## Actions that invalidate the cache ### Switching models ### Changing effort level ### Turning on fast mode ### Connecting or disconnecting an MCP server ### Enabling or disabling a plugin ### Denying an entire tool ### Compacting the conversation ### Upgrading Claude Code ## Actions that keep the cache ### Editing files in your repository ### Editing CLAUDE.md mid-session ### Changing output style ### Changing permission mode ### Invoking skills and commands ### Running `/recap` ### Rewinding the conversation ## Cache lifetime ### On a Claude subscription ### On an API key or third-party provider ### Override the TTL ## Cache scope ## Check cache performance ## Subagents and the cache ## Disable prompt caching ## Related resources
The first capture of this source. The page was already there, and this is what it said.
# How Claude Code uses prompt caching > Claude Code manages prompt caching automatically. See why a model switch triggers a slow uncached turn, what `/compact` costs, why CLAUDE.md edits don't apply mid-session, and how to check your cache hit rate. Prompt caching makes Claude Code faster and more cost-efficient. Without caching, the API would reprocess your full history on every turn. With caching, it reuses what it already processed, bills the re-read at the [cached token rate](https://platform.claude.com/docs/en/about-claude/pricing), and fully processes only what changed. Claude Code handles prompt caching for you, unless you [disable it](#disable-prompt-caching). It is still useful to know how prompt caching works, because some actions invalidate the cache and make the next response slower and more expensive while it rebuilds. This page covers which actions those are, why some settings wait for a restart to apply, and how to check cache performance when usage looks high. ## How the cache is organized Each time you send a message in Claude Code, it makes a new API request. The model doesn't remember anything between requests, so Claude Code re-sends the full context: the system prompt, your project context, every prior message and tool result, and your new message. New content is appended at the end, which means most of each request is identical to the one before it. Prompt caching is how the API avoids reprocessing the part that didn't change. The API caches by matching the start of each request, called the prefix, against content it recently processed. On a normal turn, the prefix is the entire previous request and only the latest exchange is new. The match is exact, so a change anywhere in the prefix recomputes everything after it. There is no per-file or per-segment caching. See [how prompt caching works](https://platform.claude.com/docs/en/build-with-claude/prompt-caching#how-prompt-caching-works) in the API reference for the underlying mechanism. <img src="https://mintcdn.com/claude-code/VbDJw--l6T9a9Wvm/images/prompt-caching-prefix.svg?fit=max&auto=format&n=VbDJw--l6T9a9Wvm&q=85&s=f2e8f0b8298a50305fe428ca3f1d1594" className="dark:hidden" alt="Four turns shown as growing horizontal bars. Each turn's request contains everything from the previous turn plus the latest exchange appended at the end. On turns two and three, the unchanged prefix is read from cache and only the new exchange is processed. On turn four, the system prompt changed, so the prefix no longer matches and the entire request is reprocessed and written." width="720" height="454" data-path="images/prompt-caching-prefix.svg" /> <img src="https://mintcdn.com/claude-code/_xqph1dUOslCOwsj/images/prompt-caching-prefix-dark.svg?fit=max&auto=format&n=_xqph1dUOslCOwsj&q=85&s=297dc1c639f0915cae858d0c4b6f3be5" className="hidden dark:block" alt="Four turns shown as growing horizontal bars. Each turn's request contains everything from the previous turn plus the latest exchange appended at the end. On turns two and three, the unchanged prefix is read from cache and only the new exchange is processed. On turn four, the system prompt changed, so the prefix no longer matches and the entire request is reprocessed and written." width="720" height="454" data-path="images/prompt-caching-prefix-dark.svg" /> To get the most out of prefix matching, Claude Code orders each request so content that rarely changes between turns comes first: | Layer | Content | Changes when | | --------------- | ------------------------------------------------- | ---------------------------------------------------------------------- | | System prompt | Core instructions, tool definitions, output style | The set of loaded tool definitions changes, or Claude Code is upgraded | | Project context | CLAUDE.md, auto memory, unscoped rules | Session starts, or after `/clear` or `/compact` | | Conversation | Your messages, Claude's responses, tool results | Every turn | A change to the conversation layer leaves the system prompt and project context cached. A change to the system prompt invalidates everything, because all later content now sits behind a different prefix. The third column gives common triggers rather than an exhaustive list, and the sections below cover the full set, including content such as output style that is fixed at session start. The prefix-match rule explains most of the behaviors on this page. [Plan mode](/docs/en/permission-modes#analyze-before-you-edit-with-plan-mode) and [skill loading](/docs/en/skills), for example, append their instructions as conversation messages, so the cached prefix stays intact. Two settings aren't part of the prompt text at all, so they don't appear in the layer table, but both are part of the cache key: * **Model**: each model has its own cache. Switching models recomputes the entire request even when the content is identical. See [Switching models](#switching-models) below. * **Effort level**: each effort level has its own cache for the same model. Changing it mid-session recomputes the entire request, and Claude Code asks you to confirm before applying the change. See [Changing effort level](#changing-effort-level) below. <Tip> Pick your model and effort level at the top of a session, then save `/compact` for natural breaks between tasks. The fewer changes you make mid-task, the higher your cache hit rate. </Tip> ### Where the cache lives Caching happens server-side, in whichever infrastructure serves your model. Where that is depends on how you authenticate: * **API key, Claude subscription, or [Claude Platform on AWS](/docs/en/claude-platform-on-aws)**: the cache lives in Anthropic's infrastructure, accessed through the [Claude API](https://platform.claude.com/docs) * **Amazon Bedrock or Google Cloud's Agent Platform**: the cache lives in your cloud provider's serving infrastructure * **Microsoft Foundry**: depends on the deployment's [hosting option](https://platform.claude.com/docs/en/build-with-claude/claude-in-microsoft-foundry#hosting-options). Hosted on Azure deployments are served on Azure infrastructure; Hosted on Anthropic deployments are served on Anthropic's infrastructure * **Custom `ANTHROPIC_BASE_URL` or [LLM gateway](/docs/en/llm-gateway)**: the cache lives wherever your requests are forwarded, and whether caching works depends on the gateway System context that Claude Code appends mid-conversation, such as file-change notices, is cached on Amazon Bedrock and its [Mantle endpoint](/docs/en/amazon-bedrock#use-the-mantle-endpoint), Google Cloud's Agent Platform, and Microsoft Foundry the same way it is on the Claude API. Before v2.1.211, these providers billed that appended system context as uncached input tokens on every request. When your requests pass through an [LLM gateway](/docs/en/llm-gateway) or a custom `ANTHROPIC_BASE_URL`, Claude Code marks that appended system context for caching the same way, and whether the cache takes effect depends on the gateway. If the gateway rejects the [cache breakpoint](https://platform.claude.com/docs/en/build-with-claude/prompt-caching#explicit-cache-breakpoints) on that block, Claude Code retries the request without it and leaves that block uncached for the rest of the conversation. For what each provider stores and processes, see [data usage](/docs/en/data-usage). Wherever the cache lives, entries expire after a period of inactivity, and [Cache lifetime](#cache-lifetime) below covers the TTL and how to extend it. ## Actions that invalidate the cache These actions cause the next request to miss part or all of the cache. You see a one-time slower, more expensive turn, after which the new prefix is cached. Most of them are avoidable mid-task once you know they have a cost. A model switch can feel free until you notice the slower turn that follows. * [Switching models](#switching-models) * [Changing effort level](#changing-effort-level) * [Turning on fast mode](#turning-on-fast-mode) * [Connecting or disconnecting an MCP server](#connecting-or-disconnecting-an-mcp-server) * [Enabling or disabling a plugin](#enabling-or-disabling-a-plugin) * [Denying an entire tool](#denying-an-entire-tool) * [Compacting the conversation](#compacting-the-conversation) * [Upgrading Claude Code](#upgrading-claude-code) ### Switching models Each model has its own cache. Switching with [`/model`](/docs/en/model-config#setting-your-model) means the next request reads the entire conversation history with no cache hits, even though the content is identical. The [`opusplan` model setting](/docs/en/model-config#opusplan-model-setting) resolves to Opus during plan mode and Sonnet during execution, so each plan-mode toggle is a model switch and starts a fresh cache. [Automatic model fallback](/docs/en/model-config#automatic-model-fallback) on Fable 5 and Opus 5 is also a model switch. When a safety classifier flags a request and the flagged category has a fallback model, Claude Code re-runs the request on that model and the session continues there. ### Changing effort level The cache is keyed by [effort level](/docs/en/model-config#adjust-effort-level) as well as model, so switching with `/effort` means the next request reads the entire conversation history with no cache hits. Once a conversation has started, Claude Code shows a confirmation dialog before applying an effort change that would invalidate the cache. A change that resolves to the same level already in effect, such as setting the model's default explicitly, skips the dialog and keeps the cache. ### Turning on fast mode Enabling [fast mode](/docs/en/fast-mode) adds a request header that is part of the cache key, so the next request reads the entire conversation history with no cache hits. Those uncached input tokens are billed at [fast mode rates](/docs/en/fast-mode#understand-the-cost-tradeoff), which is why turning it on at the start of a session costs less than turning it on deep into a long one. Enabling fast mode from a non-Opus model also [switches your model](#switching-models), which starts a fresh cache on its own. The cost applies once per conversation. After the first fast mode turn, Claude Code keeps sending the header and varies only the request's speed setting, which is not part of the cache key. Turning fast mode off, the [automatic fallback to standard speed](/docs/en/fast-mode#handle-rate-limits) after a rate limit, and turning it back on later all keep the cache. If you [run out of usage credits](/docs/en/fast-mode#handle-rate-limits) mid-session, Claude Code retries each rejected fast mode request at standard speed the same way, so this fallback also keeps the cache. `/clear` and `/compact` reset this, since they rebuild the cache at those points anyway. ### Connecting or disconnecting an MCP server Tool definitions sit in the system prompt layer, so the cache invalidates when the set of tool definitions in the request changes between turns. Toggling the [advisor tool](/docs/en/advisor) is an exception: its definition sits after the cache breakpoint, so enabling or disabling `/advisor` keeps the cached prefix intact. Whether an [MCP server](/docs/en/mcp) change does this depends on whether its tools are deferred by [tool search](/docs/en/mcp#scale-with-mcp-tool-search) or loaded into the prefix: * **Deferred tools**, the default on supported models: a server connecting, disconnecting, or changing its tool list only appends new content and doesn't disturb anything already cached. * **Tools loaded into the prefix**: any change to them invalidates the cache. This happens when [tool search is unavailable or disabled](/docs/en/mcp#configure-tool-search), such as on Google Cloud's Agent Platform models earlier than the Claude 4.5 generation, with a custom `ANTHROPIC_BASE_URL` gateway, or on a Microsoft Foundry [deployment hosted on Azure](https://platform.claude.com/docs/en/build-with-claude/claude-in-microsoft-foundry#hosting-options) once Claude Code detects that the deployment rejects tool search. It also happens for a server or tool marked [`alwaysLoad`](/docs/en/mcp#exempt-a-server-from-deferral), and for definitions kept upfront by [threshold-based loading](/docs/en/mcp#configure-tool-search). When tools load into the prefix, the most common cause of an invalidation is a server connecting or disconnecting mid-session, which can happen without any action on your part: a stdio server's process exits, an HTTP session expires, or a server [reconnects automatically after a transient failure](/docs/en/mcp#automatic-reconnection). A connected server can also push a [dynamic tool update](/docs/en/mcp#dynamic-tool-updates) that changes its tool list. Editing your MCP config does not by itself change the cache. The new config takes effect only after a restart, which is when the server connects or disconnects. ### Enabling or disabling a plugin [Plugins](/docs/en/plugins) bundle several component types, and the cost of a change depends on which components the plugin provides. Skills, commands, agents, hooks, LSP servers, monitors, and themes never invalidate the cache: anything they add to the request is appended after the existing conversation, so the next request pays for the new content but still reads everything before it from the cache. The exception is a plugin that provides [MCP servers](/docs/en/plugins-reference#mcp-servers). Enabling or disabling one follows the same rules as [connecting or disconnecting an MCP server](#connecting-or-disconnecting-an-mcp-server): the cache survives when the server's tools are deferred, and the next request re-reads the entire conversation when they load into the prefix. Plugin changes apply when you run [`/reload-plugins`](/docs/en/discover-plugins#apply-plugin-changes-without-restarting) or start a new session. For a plugin with a `command` source, Claude Code [can reload the plugin itself](/docs/en/plugin-marketplaces#when-claude-code-re-runs-the-command). Claude Code can also activate a plugin you [install from the `/plugin` interface](/docs/en/discover-plugins#install-plugins) during the install; the install summary tells you whether it did or whether to run `/reload-plugins`. If that reload would trigger the full re-read below, the command warns first and applies when you rerun it with `--force`. The cost, whether appended announcements or a full re-read, shows up on the first turn after the change applies, not when you run `/plugin enable` or `/plugin disable`. When a reload would trigger the full re-read, `/reload-plugins` shows a warning and doesn't apply the reload. Pass `--force` to apply anyway. Disabling a plugin you enabled earlier in the session restores the previous request shape. If that prefix is still within its [cache lifetime](#cache-lifetime), the next request reads the older cache entry instead of rebuilding. ### Denying an entire tool Adding a bare tool name like `Bash` or `WebFetch` as a [deny rule](/docs/en/permissions#manage-permissions) removes that tool from Claude's context entirely. Built-in tool definitions load into the system prompt layer, so adding or removing one of these rules mid-session invalidates the cache. The change takes effect on the next turn whether you add it through `/permissions` or by [editing a settings file directly](/docs/en/settings#when-edits-take-effect). Only a deny rule that matches in the tool-name position has this effect: a bare tool name, the equivalent `Bash(*)` form, or a [tool-name glob](/docs/en/permissions#tool-name-wildcards) like `"*"`. A glob that matches only MCP tools, such as `"mcp__*"`, removes those tools the same way but leaves the cache intact when the matched tools are [deferred](#connecting-or-disconnecting-an-mcp-server), the default, since deferred definitions were never in the cached prefix. Scoped deny rules like `Bash(rm *)`, and all allow and ask rules, don't change which tools Claude sees. Claude Code checks them when Claude attempts a call, leaving the prefix intact. ### Compacting the conversation [Compaction](/docs/en/context-window#what-survives-compaction) replaces your message history with a summary. By design, this invalidates the conversation layer, since the next request has a new, shorter history that doesn't share a prefix with the old one. Claude Code reuses the system prompt layer and reloads project context from disk, which cache-hits only if CLAUDE.md and memory are unchanged since the session started. To produce the summary, Claude Code sends a separate request with the same system prompt, tools, and history as your conversation, plus a summarization instruction appended as a final user message. While the cache is warm, that request reads your prefix from the cache, so a mid-session `/compact` costs a fraction of what the context size suggests and spends most of its time generating the summary. After a break longer than the [cache lifetime](#cache-lifetime), there is no cache left to read, so the summarization request reprocesses the full history as uncached input. This is why `/compact` costs the most when you [resume an old session](/docs/en/sessions#resume-from-a-summary). In both the warm and cold cases, the turn after compaction rebuilds the conversation cache for only the much shorter summary, so that turn is not the slow part. <Tip> Compaction works in your favor when the context you discard is content you no longer need. To choose when its overhead happens, run `/compact` at a natural break in your work, such as between tasks, instead of waiting for auto-compaction to trigger mid-task. If you've gone down a path you want to abandon entirely, [`/rewind`](#rewinding-the-conversation) to an earlier turn instead. Rewinding truncates back to a prefix that is already cached, rather than building a new one as compaction does. </Tip> ### Upgrading Claude Code A new Claude Code version typically updates the system prompt or tool definitions, so the first request after an upgrade rebuilds the cache from the top. [Auto-update](/docs/en/setup#auto-updates) downloads new versions in the background but applies them on the next launch, never mid-session, so you see this as an uncached first turn after restarting rather than a surprise during a session. Set `DISABLE_AUTOUPDATER=1` to control when upgrades apply. <Note> [Resuming a session](/docs/en/sessions#resume-a-session) after an upgrade reprocesses the entire conversation history with no cache hits, since the history now sits behind a different system prompt. The cost scales with how long the resumed conversation is, so the first turn back into a long session can be the most expensive request you send. </Note> ## Actions that keep the cache These actions either append to the end of the conversation or don't touch the request at all. Some of them, such as editing CLAUDE.md or changing output style, are also why a setting change waits for a restart to apply. * [Editing files in your repository](#editing-files-in-your-repository) * [Editing CLAUDE.md mid-session](#editing-claude-md-mid-session) * [Changing output style](#changing-output-style) * [Changing permission mode](#changing-permission-mode) * [Invoking skills and commands](#invoking-skills-and-commands) * [Running `/recap`](#running-%2Frecap) * [Rewinding the conversation](#rewinding-the-conversation) * [Spawning a subagent](#subagents-and-the-cache) ### Editing files in your repository File contents enter context only when Claude reads them, and reads append to the conversation. Editing a file Claude previously read does not retroactively change the earlier read in history. Instead, Claude Code appends a `<system-reminder>` noting the file changed, and Claude re-reads it if needed. ### Editing CLAUDE.md mid-session Your project-root and user-level CLAUDE.md files are read once at session start and held in memory. Editing them mid-session does not invalidate the cache, but the edit also doesn't apply. Claude keeps working with the version that was loaded at session start. The new content loads on the next `/clear`, `/compact`, or restart. [Nested CLAUDE.md files in subdirectories](/docs/en/memory) and [rules with `paths:` frontmatter](/docs/en/memory#path-specific-rules) load later, when Claude first reads a matching file. Editing one before it loads does take effect. After it loads, the content is part of the conversation history, so a mid-session edit doesn't retroactively change it. ### Changing output style [Output style](/docs/en/output-styles) is part of the system prompt, which Claude Code reads once at session start. Changing it via `/config` or the `outputStyle` setting mid-session does not invalidate the cache, but the change also doesn't apply. Claude keeps using the style that was loaded at session start. The new style loads on the next `/clear` or restart. ### Changing permission mode Switching between [permission modes](/docs/en/permission-modes), such as from default to accept edits, does not change the system prompt or tool definitions, so mode changes are cache-safe. The exception is plan mode with the [`opusplan`](/docs/en/model-config#opusplan-model-setting) model setting, which switches the model between Opus and Sonnet as you enter or leave plan mode. That makes the mode toggle a [model switch](#switching-models). ### Invoking skills and commands [Skills](/docs/en/skills) and [commands](/docs/en/commands) inject their instructions as user messages at the point of invocation. Nothing earlier in the conversation changes. ### Running `/recap` [`/recap`](/docs/en/interactive-mode#session-recap) generates a summary for display in your terminal. Unlike `/compact`, it appends the summary as command output rather than replacing your message history, so the cached prefix stays intact. ### Rewinding the conversation [`/rewind`](/docs/en/checkpointing) truncates your conversation back to an earlier turn. The remaining history is the same content the cache was built from at that point, and the system prompt and project context layers are unchanged, so the next request hits the earlier cache entry. Every turn since then has read through that prefix, which kept the entry warm even if the original turn was longer ago than the TTL. Restoring file checkpoints alongside the conversation has no separate effect on the cache. File contents enter context only when Claude reads them, the same as [editing files in your repository](#editing-files-in-your-repository). ## Cache lifetime Cached prefixes expire after a period of inactivity. Each request that hits the cache resets the timer, so the cache stays warm as long as you keep working. After a long enough gap, the next request recomputes the full input and re-establishes the cache, which is why the first turn back after stepping away can be noticeably slower. On a Pro or Max plan, when you resume a large session after a long break, Claude Code [offers to resume from a summary](/docs/en/sessions#resume-from-a-summary) so later requests don't carry the full history. The time to live (TTL) controls how long a gap the cache survives. The API offers two: a five-minute TTL, and a [one-hour TTL](https://platform.claude.com/docs/en/build-with-claude/prompt-caching#1-hour-cache-duration) that keeps the cache warm through longer breaks but [bills cache writes at a higher rate](https://platform.claude.com/docs/en/build-with-claude/prompt-caching#pricing). Claude Code picks the TTL for you based on how you authenticate, and you can override it with environment variables. ### On a Claude subscription On a Claude subscription, Claude Code requests the one-hour TTL automatically, so the cache survives breaks of up to an hour. If you've gone over your plan's usage limit and Claude Code is drawing on [usage credits](https://support.claude.com/en/articles/12429409-extra-usage-for-paid-claude-plans), you are billed for that usage. Cache writes cost more at the one-hour TTL than at the five-minute TTL, so Claude Code automatically drops to the shorter one. To keep the one-hour TTL while drawing on usage credits, set `ENABLE_PROMPT_CACHING_1H=1`. ### On an API key or third-party provider On an API key, Amazon Bedrock, Google Cloud's Agent Platform, Microsoft Foundry, or Claude Platform on AWS, you pay the per-token rates, so the TTL stays at the cheaper five minutes by default. To opt into the [one-hour TTL](https://platform.claude.com/docs/en/build-with-claude/prompt-caching#1-hour-cache-duration), set `ENABLE_PROMPT_CACHING_1H=1`. On Amazon Bedrock, prompt caching support, minimum cacheable prefix length, and one-hour TTL availability all vary by model. If cache token counts stay at zero, check [supported models, regions, and limits](https://docs.aws.amazon.com/bedrock/latest/userguide/prompt-caching.html#prompt-caching-models) in the Amazon Bedrock documentation. ### Override the TTL Set `FORCE_PROMPT_CACHING_5M=1` to force the five-minute TTL regardless of authentication. This is useful when you're debugging cache behavior, comparing the two TTLs, or overriding an `ENABLE_PROMPT_CACHING_1H` set in [managed settings](/docs/en/settings#settings-files). ## Cache scope In Claude Code, the cache is effectively scoped to one machine and directory. The system prompt embeds the working directory, platform, shell, OS version, and auto memory paths, so two sessions in different directories build different prefixes and miss each other's cache. That includes worktrees of the same repository, since each worktree has its own working directory. Sessions you run in parallel in the same directory build matching prefixes and read each other's cache. Sequential sessions share the prefix only when the git status snapshot at startup matches, since the system prompt also captures branch and recent commits. The underlying API cache is broader. Caches are isolated between organizations, and on some providers, [between workspaces within an organization](https://platform.claude.com/docs/en/build-with-claude/prompt-caching#cache-storage-and-sharing). Within those boundaries, any two requests with the same model and prefix read the same cache. For Agent SDK callers running fleets of automated processes, see [improve prompt caching across users and machines](/docs/en/agent-sdk/modifying-system-prompts#improve-prompt-caching-across-users-and-machines) to suppress the per-machine sections of the system prompt and share the cache across machines. ## Check cache performance Cache performance shows up as two token counts the API reports on every response. The most direct way to watch them live is a [statusline script](/docs/en/statusline) that reads the `current_usage` object: | Field | Meaning | | ----------------------------- | --------------------------------------------------------------------------------------- | | `cache_creation_input_tokens` | Tokens written to the cache on this turn, billed at the cache write rate | | `cache_read_input_tokens` | Tokens served from cache on this turn, billed at roughly 10% of the standard input rate | A high read-to-creation ratio means caching is working well. If creation stays high turn after turn, something is changing in your prefix. The [actions that invalidate the cache](#actions-that-invalidate-the-cache) section lists the usual causes. For visibility across an organization, the OpenTelemetry exporter reports cache read and creation tokens per user and session. See [Monitor usage](/docs/en/monitoring-usage) for the metric and event attribute reference. ## Subagents and the cache A [subagent](/docs/en/sub-agents) starts its own conversation with its own system prompt and tool set, separate from the parent's. Its first request doesn't read the parent's cache, because the two prefixes differ, and it warms a cache of its own across its turns. Subagents use the five-minute TTL even on a subscription, since the automatic one-hour TTL applies to the main conversation. The parent's cache is unaffected. From the parent's side, the subagent's call and result append to the conversation, leaving the parent's prefix intact. A [fork](/docs/en/sub-agents#fork-the-current-conversation), by contrast, inherits the parent's system prompt, tools, and conversation history exactly, so its first request reads the parent's cache. The compaction summarization call described in [Compacting the conversation](#compacting-the-conversation) uses the same prefix-sharing approach. In a [workflow fan-out](/docs/en/workflows#prompt-caching-in-a-fan-out) of same-prefix agents, Claude Code briefly holds all but the first so their first requests can read the prefix the first agent cached. ## Disable prompt caching Disabling caching is occasionally useful when debugging caching behavior with a specific model or provider. To turn it off, set one of these environment variables to `1`: | Variable | Effect | | ------------------------------- | ----------------------- | | `DISABLE_PROMPT_CACHING` | Disable for all models | | `DISABLE_PROMPT_CACHING_HAIKU` | Disable for Haiku only | | `DISABLE_PROMPT_CACHING_SONNET` | Disable for Sonnet only | | `DISABLE_PROMPT_CACHING_OPUS` | Disable for Opus only | | `DISABLE_PROMPT_CACHING_FABLE` | Disable for Fable only | To set caching policy across an organization, put any of these or the [TTL variables](#cache-lifetime) in the `env` block of [managed settings](/docs/en/settings#settings-files). For normal use, leave caching enabled. ## Related resources * [Lessons from building Claude Code: Prompt caching is everything](https://claude.com/blog/lessons-from-building-claude-code-prompt-caching-is-everything): the design rationale for plan mode, deferred tool loading, and compaction * [Explore the context window](/docs/en/context-window): what loads into context and when * [Reduce token usage](/docs/en/costs#reduce-token-usage): strategies beyond caching for managing context size * [Track and reduce costs](/docs/en/agent-sdk/cost-tracking): cache token tracking and TTL configuration for Agent SDK callers * [Prompt caching](https://platform.claude.com/docs/en/build-with-claude/prompt-caching): the underlying API mechanism, breakpoints, and pricing
prompt-library First recorded · 1385 lines, first recorded
# Prompt library ## What makes these prompts work ## Where these come from ## Related resources
The first capture of this source. The page was already there, and this is what it said.
# Prompt library
> Copy-paste prompts for Claude Code, tagged by task and role.
export const PromptLibrary = ({text = {}, labels = {}, tagLabels = {}, phaseLabels = {}, sourceLabels = {}, catLabels = {}}) => {
const RAW = useMemo(() => [{
id: 'get-oriented-in-a',
sdlc: 'discover',
cat: 'Onboard',
startN: 1,
roles: [],
prompt: 'give me an overview of this codebase: architecture, key directories, and how the pieces connect',
nextHref: '/en/memory',
src: 'workflows'
}, {
id: 'explain-unfamiliar-code',
sdlc: 'discover',
cat: 'Understand',
roles: [],
prompt: 'explain what {path} does and how data flows through it. write it up as {format}',
slots: {
path: 'src/scheduler/queue.ts',
format: 'an HTML page with a diagram, then open it in my browser'
},
nextHref: '/en/output-styles',
src: 'workflows'
}, {
id: 'find-where-something-happens',
sdlc: 'discover',
cat: 'Understand',
startN: 2,
roles: [],
prompt: 'where do we {behavior}?',
slots: {
behavior: 'validate uploaded file types'
},
src: 'workflows'
}, {
id: 'see-what-depends-on',
sdlc: 'discover',
cat: 'Understand',
roles: [],
prompt: 'what would break if I deleted {target}?',
slots: {
target: 'the retryWithBackoff helper'
},
src: 'workflows'
}, {
id: 'trace-how-code-evolved',
sdlc: 'discover',
cat: 'Understand',
roles: [],
prompt: 'look through the commit history of {path} and summarize how it evolved and why',
slots: {
path: 'internal/auth/session.go'
},
src: 'best-practices'
}, {
id: 'scope-a-change-before',
sdlc: 'discover',
cat: 'Understand',
roles: ['pm', 'design'],
prompt: 'which files would I need to touch to {change}?',
slots: {
change: 'add a dark mode toggle to settings'
},
src: 'teams'
}, {
id: 'ask-the-codebase-a',
sdlc: 'discover',
cat: 'Understand',
roles: ['pm'],
prompt: 'I am a {role}. walk me through what happens when a user {action}, from the UI down to the result',
slots: {
role: 'PM',
action: 'clicks Export to PDF'
},
nextHref: '/en/output-styles',
src: 'teams'
}, {
id: 'plan-a-multi-file',
sdlc: 'design',
cat: 'Plan',
roles: ['pm', 'design'],
prompt: 'plan how to refactor the {target} to {goal}. list the files you would change, but don\'t edit anything yet',
slots: {
target: 'payment module',
goal: 'support multiple currencies'
},
src: 'workflows'
}, {
id: 'draft-a-spec-by',
sdlc: 'design',
cat: 'Plan',
roles: ['pm'],
prompt: 'I want to build {feature}. interview me about implementation, UX, edge cases, and tradeoffs until we have covered everything, then write the spec to SPEC.md',
slots: {
feature: 'per-workspace rate limits'
},
nextHref: '/en/skills',
src: 'best-practices'
}, {
id: 'turn-a-meeting-into',
sdlc: 'design',
cat: 'Plan',
roles: ['pm'],
prompt: 'read {input} and write up the action items, then create a {tracker} ticket for each with acceptance criteria',
slots: {
input: '@meeting-notes.md',
tracker: 'Linear'
},
needs: 'tracker',
nextHref: '/en/skills',
src: 'teams'
}, {
id: 'map-edge-cases-before',
sdlc: 'design',
cat: 'Plan',
roles: ['design', 'pm'],
prompt: 'list the error states, empty states, and edge cases for {feature} that the design needs to cover',
slots: {
feature: 'the file upload flow'
},
src: 'teams'
}, {
id: 'turn-a-mockup-into',
sdlc: 'design',
cat: 'Prototype',
roles: ['design', 'pm', 'marketing'],
paste: 'mockup',
prompt: 'here is a mockup. build a working prototype I can click through, matching the layout and states shown',
src: 'teams'
}, {
id: 'implement-from-a-screenshot',
sdlc: 'design',
cat: 'Prototype',
roles: ['design'],
paste: 'design',
needs: 'browser',
prompt: 'implement this design, then take a screenshot of the result, compare it to the original, and fix any differences',
nextHref: '/en/goal',
src: 'best-practices'
}, {
id: 'follow-an-existing-pattern',
sdlc: 'build',
cat: 'Implement',
roles: [],
prompt: 'look at how {example} is implemented to understand the pattern, then build {new} the same way',
slots: {
example: 'the GitHub webhook handler',
new: 'a Stripe webhook handler'
},
nextHref: '/en/memory',
src: 'best-practices'
}, {
id: 'generate-docs-for-code',
sdlc: 'build',
cat: 'Implement',
roles: ['docs'],
prompt: 'find {scope} without {format} comments and add them, matching the style already used in the file',
slots: {
scope: 'the public functions in src/auth/',
format: 'JSDoc'
},
src: 'workflows'
}, {
id: 'add-a-small-well',
sdlc: 'build',
cat: 'Implement',
roles: [],
prompt: 'add a {endpoint} endpoint that returns {payload}',
slots: {
endpoint: '/health',
payload: 'the app version and uptime'
},
src: 'workflows'
}, {
id: 'build-a-small-internal',
sdlc: 'build',
cat: 'Implement',
roles: ['pm', 'design', 'marketing', 'docs'],
prompt: 'create a {tool} using HTML, CSS, and vanilla JavaScript, then open it in my browser',
slots: {
tool: 'drag-and-drop Kanban board with three columns'
},
src: 'teams'
}, {
id: 'work-an-issue-end',
sdlc: 'build',
cat: 'Implement',
roles: [],
prompt: 'read issue #{issue}, implement the fix, and run the tests',
slots: {
issue: '312'
},
needs: 'gh',
src: 'workflows'
}, {
id: 'find-and-update-copy',
sdlc: 'build',
cat: 'Implement',
roles: ['design', 'docs', 'marketing'],
prompt: 'find every place we say "{copy}" or a close variant, show me each one in context, then update them all to "{new}". leave tests and the changelog alone',
slots: {
copy: 'Sign up free',
new: 'Start free trial'
},
src: 'teams'
}, {
id: 'draft-from-past-examples',
sdlc: 'build',
cat: 'Implement',
roles: ['docs', 'marketing', 'pm'],
prompt: 'read the {examples} in {folder} to learn the structure and voice, then draft a new one for {topic}',
slots: {
examples: 'privacy impact assessments',
folder: 'legal/pia/',
topic: 'the new analytics integration'
},
nextHref: '/en/skills',
src: 'legal'
}, {
id: 'write-tests-run-them',
sdlc: 'build',
cat: 'Test',
startN: 4,
roles: [],
prompt: 'write tests for {path}, run them, and fix any failures',
slots: {
path: 'app/parsers/feed.py'
},
nextHref: '/en/memory',
src: 'workflows'
}, {
id: 'drive-implementation-from-tests',
sdlc: 'build',
cat: 'Test',
roles: [],
prompt: 'write tests for {feature} first, then implement it until they pass',
slots: {
feature: 'the password reset flow'
},
src: 'ebook'
}, {
id: 'fill-gaps-from-a',
sdlc: 'build',
cat: 'Test',
roles: [],
prompt: 'read {report} and add tests for the lowest-covered files until each is above {target}%',
slots: {
report: 'coverage/coverage-summary.json',
target: '80'
},
nextHref: '/en/goal',
src: 'workflows'
}, {
id: 'migrate-a-pattern-across',
sdlc: 'build',
cat: 'Refactor',
roles: [],
prompt: 'migrate everything from {from} to {to}: identify every place that needs to change, then make the changes',
slots: {
from: 'the old logging API',
to: 'the structured logger'
},
src: 'workflows'
}, {
id: 'port-code-between-languages',
sdlc: 'build',
cat: 'Refactor',
roles: [],
prompt: 'port {source} to {target}, keeping the same {keep}',
slots: {
source: 'this Python module',
target: 'Rust',
keep: 'public API and test behavior'
},
src: 'teams'
}, {
id: 'optimize-against-a-measurable',
sdlc: 'build',
cat: 'Refactor',
roles: ['data'],
prompt: 'optimize {target} to bring {metric} from {current} down to under {goal}',
slots: {
target: 'the search query',
metric: 'p95 latency',
current: '2s',
goal: '500ms'
},
nextHref: '/en/goal',
src: 'ebook'
}, {
id: 'fix-a-precise-visual',
sdlc: 'build',
cat: 'Refactor',
roles: ['design'],
prompt: 'the {element} extends {amount} beyond the {container} on {viewport}. fix it.',
slots: {
element: 'login button',
Cut at 300 lines. The page has the rest.
quickstart First recorded · 359 lines, first recorded
# Quickstart ## Before you begin ## Step 1: Install Claude Code ## Step 2: Log in to your account ## Step 3: Start your first session ## Step 4: Ask your first question ## Step 5: Make your first code change ## Step 6: Use Git with Claude Code ## Step 7: Fix a bug or add a feature ## Step 8: Test out other common workflows ## Essential commands ## Pro tips for beginners ## What's next? ## Getting help
The first capture of this source. The page was already there, and this is what it said.
# Quickstart
> Welcome to Claude Code!
This quickstart guide will have you using AI-powered coding assistance in a few minutes. By the end, you'll understand how to use Claude Code for common development tasks.
## Before you begin
Make sure you have:
* A terminal or command prompt open
* If you've never used the terminal before, check out the [terminal guide](/docs/en/terminal-guide)
* A code project to work with
* A [Claude subscription](https://claude.com/pricing?utm_source=claude_code\&utm_medium=docs\&utm_content=quickstart_prereq) (Pro, Max, Team, or Enterprise), [Claude Console](https://console.anthropic.com/) account, or access through a [supported cloud provider](/docs/en/third-party-integrations)
<Note>
This guide covers the terminal CLI. Claude Code is also available on the [web](https://claude.ai/code), as a [desktop app](/docs/en/desktop), in [VS Code](/docs/en/vs-code) and [JetBrains IDEs](/docs/en/jetbrains), in [Slack](/docs/en/slack), and in CI/CD with [GitHub Actions](/docs/en/github-actions) and [GitLab](/docs/en/gitlab-ci-cd). See [all interfaces](/docs/en/overview#use-claude-code-everywhere).
</Note>
## Step 1: Install Claude Code
To install Claude Code, use one of the following methods:
<Tabs>
<Tab title="Native Install (Recommended)">
**macOS, Linux, WSL:**
```bash theme={null}
curl -fsSL https://claude.ai/install.sh | bash
```
**Windows PowerShell:**
```powershell theme={null}
irm https://claude.ai/install.ps1 | iex
```
**Windows CMD:**
```batch theme={null}
curl -fsSL https://claude.ai/install.cmd -o install.cmd && install.cmd && del install.cmd
```
If you see `The token '&&' is not a valid statement separator`, you're in PowerShell, not CMD. If you see `'irm' is not recognized as an internal or external command`, you're in CMD, not PowerShell. Your prompt shows `PS C:\` when you're in PowerShell and `C:\` without the `PS` when you're in CMD.
If the install command fails with `syntax error near unexpected token '<'`, a `403`, or another curl error, see [Troubleshoot installation](/docs/en/troubleshoot-install#find-your-error) to match the error to a fix and for alternative install methods.
[Git for Windows](https://git-scm.com/downloads/win) is recommended on native Windows so Claude Code can use the Bash tool. If Git for Windows is not installed, Claude Code uses PowerShell as the shell tool instead. WSL setups do not need Git for Windows.
<Info>
Native installations automatically update in the background to keep you on the latest version.
</Info>
</Tab>
<Tab title="Homebrew">
```bash theme={null}
brew install --cask claude-code
```
Homebrew offers two casks. `claude-code` tracks the stable release channel, which is typically about a week behind and skips releases with major regressions. `claude-code@latest` tracks the latest channel and receives new versions as soon as they ship.
<Info>
Homebrew installations do not auto-update. Run `brew upgrade claude-code` or `brew upgrade claude-code@latest`, depending on which cask you installed, to get the latest features and security fixes.
</Info>
</Tab>
<Tab title="WinGet">
```powershell theme={null}
winget install Anthropic.ClaudeCode
```
<Info>
WinGet installations do not auto-update. Run `winget upgrade Anthropic.ClaudeCode` periodically to get the latest features and security fixes.
</Info>
</Tab>
</Tabs>
You can also install with [apt, dnf, or apk](/docs/en/setup#install-with-linux-package-managers) on Debian, Fedora, RHEL, and Alpine.
To confirm the installation worked, run:
```bash theme={null}
claude --version
```
The command prints a version number followed by `(Claude Code)`.
## Step 2: Log in to your account
Claude Code requires an account to use. Start an interactive session with the `claude` command and you'll be prompted to log in on first use:
```bash theme={null}
claude
```
For Claude subscription or Console accounts, follow the prompts to complete authentication in your browser. If you've set the `ANTHROPIC_API_KEY` environment variable, Claude Code skips the login prompt and asks you to approve the key instead. To switch accounts later or re-authenticate, type `/login` inside the running session:
```text wrap theme={null}
/login
```
You can log in using any of these account types:
* [Claude Pro, Max, Team, or Enterprise](https://claude.com/pricing?utm_source=claude_code\&utm_medium=docs\&utm_content=quickstart_login) (recommended)
* [Claude Console](https://console.anthropic.com/) (API access with pre-paid credits). On first login, a "Claude Code" workspace is automatically created in the Console for centralized cost tracking.
* [Amazon Bedrock, Google Cloud's Agent Platform, or Microsoft Foundry](/docs/en/third-party-integrations) (enterprise cloud providers)
* A self-hosted [Claude apps gateway](/docs/en/claude-apps-gateway), if your organization runs one: your admin pre-configures the gateway URL, and `/login` opens directly on the **Cloud gateway** screen for you to sign in with corporate SSO
Once logged in, your credentials are stored and you won't need to log in again.
## Step 3: Start your first session
Open your terminal in any project directory and start Claude Code:
```bash theme={null}
cd /path/to/your/project
claude
```
Replace `/path/to/your/project` with the path to the project you want to work on.
You'll see the Claude Code prompt with the version, current model, and working directory shown above it. Type `/help` for available commands or `/resume` to continue a previous conversation.
<Tip>
After logging in (Step 2), your credentials are stored on your system. Learn more in [Credential Management](/docs/en/authentication#credential-management).
</Tip>
## Step 4: Ask your first question
Let's start with understanding your codebase. Try one of these commands:
```text wrap theme={null}
what does this project do?
```
Claude will analyze your files and provide a summary. You can also ask more specific questions:
```text wrap theme={null}
what technologies does this project use?
```
```text wrap theme={null}
where is the main entry point?
```
```text wrap theme={null}
explain the folder structure
```
You can also ask Claude about its own capabilities:
```text wrap theme={null}
what can Claude Code do?
```
```text wrap theme={null}
how do I create custom skills in Claude Code?
```
```text wrap theme={null}
can Claude Code work with Docker?
```
<Note>
Claude Code reads your project files as needed. You don't have to manually add context.
</Note>
## Step 5: Make your first code change
Now let's make Claude Code do some actual coding. Try a simple task:
```text wrap theme={null}
add a hello world function to the main file
```
Claude Code will:
1. Find the appropriate file
2. Show you the proposed changes
3. Ask for your approval before changing files, depending on your permission mode
4. Make the edit
<Note>
Whether Claude Code asks before changing files depends on your [permission mode](/docs/en/permission-modes). In default mode, Claude asks for approval before each change. Press `Shift+Tab` to cycle through modes: `acceptEdits` auto-approves file edits, and `plan` lets Claude propose changes without editing. Some accounts also have an `auto` mode that runs a background safety check and blocks risky actions, returning to prompts only after repeated blocks.
</Note>
## Step 6: Use Git with Claude Code
Claude Code makes Git operations conversational:
```text wrap theme={null}
what files have I changed?
```
```text wrap theme={null}
commit my changes with a descriptive message
```
You can also prompt for more complex Git operations:
```text wrap theme={null}
create a new branch called feature/quickstart
```
```text wrap theme={null}
show me the last 5 commits
```
```text wrap theme={null}
help me resolve merge conflicts
```
## Step 7: Fix a bug or add a feature
Claude is proficient at debugging and feature implementation.
Describe what you want in natural language:
```text wrap theme={null}
add input validation to the user registration form
```
Or fix existing issues:
```text wrap theme={null}
there's a bug where users can submit empty forms - fix it
```
Claude Code will:
* Locate the relevant code
* Understand the context
* Implement a solution
* Run tests if available
## Step 8: Test out other common workflows
There are a number of ways to work with Claude:
**Refactor code**
```text wrap theme={null}
refactor the authentication module to use async/await instead of callbacks
```
**Write tests**
```text wrap theme={null}
write unit tests for the calculator functions
```
**Update documentation**
```text wrap theme={null}
update the README with installation instructions
```
**Code review**
```text wrap theme={null}
review my changes and suggest improvements
```
<Tip>
Talk to Claude like you would a helpful colleague. Describe what you want to achieve, and it will help you get there.
</Tip>
## Essential commands
Here are the most important commands for daily use. Shell commands run from your terminal to start or resume Claude Code. Session commands run inside Claude Code after it starts.
**Shell commands**
| Command | What it does | Example |
| ------------------- | ------------------------------------------------------ | ----------------------------------- |
| `claude` | Start interactive mode | `claude` |
| `claude "task"` | Run a one-time task | `claude "fix the build error"` |
| `claude -p "query"` | Run one-off query, then exit | `claude -p "explain this function"` |
| `claude -c` | Continue most recent conversation in current directory | `claude -c` |
| `claude -r` | Resume a previous conversation | `claude -r` |
**Session commands**
| Command | What it does | Example |
| ----------------------- | -------------------------- | -------- |
| `/clear` | Clear conversation history | `/clear` |
| `/help` | Show available commands | `/help` |
| `/exit` or Ctrl+D twice | Exit Claude Code | `/exit` |
See the [CLI reference](/docs/en/cli-reference) for the complete list of shell commands and the [commands reference](/docs/en/commands) for the complete list of session commands.
## Pro tips for beginners
For more, see [best practices](/docs/en/best-practices) and [common workflows](/docs/en/common-workflows).
<AccordionGroup>
<Accordion title="Be specific with your requests">
Instead of: "fix the bug"
Try: "fix the login bug where users see a blank screen after entering wrong credentials"
Cut at 300 lines. The page has the rest.
remote-control First recorded · 429 lines, first recorded
# Continue local sessions from any device with Remote Control ## Requirements ## Start a Remote Control session ### Check connection status ### Session URL reminders ### Connect from another device ### What connected devices see ### Enable Remote Control for all sessions ### Resume sessions after stopping the server ## Connection and security ## Trusted Devices ### Enable Trusted Devices for your organization ### What members see ### Manage enrolled devices ## Remote Control vs Claude Code on the web ## Mobile push notifications ## Limitations ## Troubleshooting ### "Remote Control requires a claude.ai subscription" ### "Remote Control requires a full-scope login token" ### "Unable to determine your organization for Remote Control eligibility" ### "Remote Control is not yet enabled for your account" ### "Couldn't verify Remote Control eligibility" ### "Remote Control requires feature-flag evaluation" ### "Remote Control is only available when using Claude via api.anthropic.com" ### "Remote Control is disabled by your organization's policy" ### "Remote Control isn't available for your organization due to its compliance policy" ### "Remote credentials fetch failed" ### "Couldn't reconnect to your Remote Control session" ### "Remote Control got an unexpected server response" ### "Your organization requires Trusted Devices for Remote Control, but this device is not enrolled" ### "session expired for trusted-device check" ## Choose the right approach ## Related resources
The first capture of this source. The page was already there, and this is what it said.
# Continue local sessions from any device with Remote Control
> Continue a local Claude Code session from your phone, tablet, or any browser using Remote Control. Works with claude.ai/code and the Claude mobile app.
<Note>
Remote Control is in research preview and available on all plans. On Team and Enterprise, it is off by default until an Owner enables the Remote Control toggle in [Claude Code admin settings](https://claude.ai/admin-settings/claude-code).
</Note>
Remote Control connects [claude.ai/code](https://claude.ai/code) or the Claude app for [iOS](https://apps.apple.com/us/app/claude-by-anthropic/id6473753684) and [Android](https://play.google.com/store/apps/details?id=com.anthropic.claude) to a Claude Code session running on your machine. Start a task at your desk, then pick it up from your phone on the couch or a browser on another computer.
When you start a Remote Control session on your machine, Claude keeps running locally the entire time, so your code execution and filesystem access stay on your machine. With Remote Control you can:
* **Use your full local environment remotely**: your filesystem, [MCP servers](/docs/en/mcp), tools, and project configuration all stay available, and typing `@` autocompletes file paths from your local project.
* **Work from both surfaces at once**: the conversation and the progress of [subagents](/docs/en/sub-agents) and [dynamic workflows](/docs/en/workflows) stay in sync across all connected devices, so you can send messages from your terminal, browser, and phone interchangeably.
* **Send images and files from your phone or browser**: attach a photo or file in the Claude app or at claude.ai/code, with or without a caption. Claude sees attached photos directly as part of your message. Claude Code downloads other files to your machine and passes them to Claude as `@` file references.
* **Survive interruptions**: if your laptop sleeps or your network drops, Claude Code reconnects automatically when your machine comes back online. Claude Code queues status updates from subagents and workflows while the connection is rebuilding and delivers them once it recovers.
Unlike [Claude Code on the web](/docs/en/claude-code-on-the-web), which runs on cloud infrastructure, Remote Control sessions run directly on your machine and interact with your local filesystem. The web and mobile interfaces are a window into that local session.
This page covers setup, how to start and connect to sessions, and how Remote Control compares to Claude Code on the web.
## Requirements
Before using Remote Control, confirm that your environment meets these conditions:
* **Subscription**: available on Pro, Max, Team, and Enterprise plans. API keys are not supported. On Team and Enterprise, an Owner must first enable the Remote Control toggle in [Claude Code admin settings](https://claude.ai/admin-settings/claude-code).
* **Authentication**: run `claude` and use `/login` to sign in through claude.ai if you haven't already. Without an eligible login, `claude remote-control` exits with an error, while `claude --remote-control` still starts an interactive session and shows a Remote Control failure notification shortly after launch.
* **API endpoint**: not available on Amazon Bedrock, Google Cloud's Agent Platform, or Microsoft Foundry. As of v2.1.196, Remote Control is also disabled when [`ANTHROPIC_BASE_URL`](/docs/en/env-vars) points at a host other than `api.anthropic.com`, such as an [LLM gateway](/docs/en/llm-gateway) or proxy. Unset the variable to use Remote Control.
* **Feature-flag evaluation**: [`DISABLE_TELEMETRY`, `DO_NOT_TRACK`, `CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC`, and `DISABLE_GROWTHBOOK`](/docs/en/env-vars) each disable the feature-flag evaluation that Remote Control availability depends on. Unset the variable wherever it's set, in your shell environment or in the `env` block of a [`settings.json` file](/docs/en/settings#available-settings), to use Remote Control.
* **Workspace trust**: run `claude` in your project directory at least once to accept the workspace trust dialog. The startup trust dialog never saves trust for your home directory, so start Remote Control from a project directory.
## Start a Remote Control session
You can start a Remote Control session from the CLI or the VS Code extension. The CLI offers three invocation modes; VS Code uses the `/remote-control` command.
<Tabs>
<Tab title="Server mode">
Navigate to your project directory and run:
```bash theme={null}
claude remote-control
```
The process stays running in your terminal in server mode, waiting for remote connections. It displays a session URL you can use to [connect from another device](#connect-from-another-device), and you can press spacebar to show a QR code for quick access from your phone. While a remote session is active, the terminal shows connection status and tool activity.
Available flags:
| Flag | Description |
| ----------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `--name "My Project"` | Set a custom session title visible in the session list at claude.ai/code. |
| `--remote-control-session-name-prefix <prefix>` | Prefix for auto-generated session names when no explicit name is set. Defaults to your machine's hostname, producing names like `myhost-graceful-unicorn`. Set `CLAUDE_REMOTE_CONTROL_SESSION_NAME_PREFIX` for the same effect. |
| `-c`, `--continue` | Bring back the session that the last server in this directory started with, instead of creating a new one. See [Resume sessions after stopping the server](#resume-sessions-after-stopping-the-server). Can't be combined with `--session-id`, `--spawn`, `--capacity`, or `--create-session-in-dir`. Requires Claude Code v2.1.200 or later; earlier versions reject the flag as an unknown argument. |
| `--session-id <id>` | Bring back one session by its ID. See [Resume sessions after stopping the server](#resume-sessions-after-stopping-the-server). Can't be combined with `--continue`, `--spawn`, `--capacity`, or `--create-session-in-dir`. Requires Claude Code v2.1.200 or later; earlier versions reject the flag as an unknown argument. |
| `--spawn <mode>` | How the server creates sessions.<br />• `same-dir` (default): all sessions share the current working directory, so they can conflict if editing the same files.<br />• `worktree`: each on-demand session gets its own [git worktree](/docs/en/worktrees). Requires a git repository.<br />• `session`: single-session mode. Serves exactly one session and rejects additional connections. Set at startup only.<br />Press `w` at runtime to toggle between `same-dir` and `worktree`. |
| `--capacity <N>` | Maximum number of concurrent sessions. Default is 32. Cannot be used with `--spawn=session`. |
| `--[no-]create-session-in-dir` | Pre-create one session in the current directory when the server starts, so you have somewhere to type immediately. In `worktree` mode this session stays in the current directory while on-demand sessions get isolated worktrees. On by default. If you pass `--no-create-session-in-dir` to start with none, Claude Code archives the server's sessions when you stop it, so there's nothing to [resume](#resume-sessions-after-stopping-the-server). |
| `--verbose` | Show detailed connection and session logs. |
| `--sandbox` / `--no-sandbox` | Enable or disable [sandboxing](/docs/en/sandboxing) for filesystem and network isolation. Off by default. |
Claude Code checks Remote Control eligibility before printing help, so `claude remote-control --help` returns an error instead of this flag list when you aren't signed in with an eligible account.
</Tab>
<Tab title="Interactive session">
To start a normal interactive Claude Code session with Remote Control enabled, use the `--remote-control` flag (or `--rc`):
```bash theme={null}
claude --remote-control
```
Optionally pass a name for the session:
```bash theme={null}
claude --remote-control "My Project"
```
This gives you a full interactive session in your terminal that you can also control from claude.ai or the Claude app. Unlike `claude remote-control` (server mode), you can type messages locally while the session is also available remotely.
</Tab>
<Tab title="From an existing session">
If you're already in a Claude Code session and want to continue it remotely, use the `/remote-control` (or `/rc`) command:
```text theme={null}
/remote-control
```
Pass a name as an argument to set a custom session title:
```text theme={null}
/remote-control My Project
```
This starts a Remote Control session that carries over your current conversation history.
The `--verbose`, `--sandbox`, and `--no-sandbox` flags are not available with this command.
</Tab>
<Tab title="VS Code">
In the [Claude Code VS Code extension](/docs/en/vs-code), type `/remote-control` or `/rc` in the prompt box, or open the command menu with `/` and select it.
```text theme={null}
/remote-control
```
A banner appears above the prompt box showing connection status. Once connected, click **claude.ai/code** in the banner to go directly to the session, or find it in the session list at [claude.ai/code](https://claude.ai/code). Claude Code also posts the session URL in the conversation.
To disconnect, click the close icon on the banner or run `/remote-control` again.
Unlike the CLI, the VS Code command does not accept a name argument or display a QR code. The session title is derived from your conversation history or first prompt.
</Tab>
</Tabs>
### Check connection status
In an interactive terminal session, a `/rc active` indicator sits in the footer below the input box while the connection is up, and is hidden if the terminal is too narrow to fit it. The indicator text is a link to the session on claude.ai. Select it with the down arrow key and press Enter, or run `/remote-control` again, to open a status panel with the session URL and a QR code you can use to [connect from another device](#connect-from-another-device). The status panel also offers a disconnect option. Select it to turn Remote Control off; your local session keeps running in the terminal.
If the connection fails, Claude Code shows a notification with the failure reason, and the indicator changes to a failure state that stays in the footer. Select the indicator and press Enter to review the details, or run `/remote-control` to retry.
### Session URL reminders
While Remote Control is connected, Claude Code reminds you of the session URL when switching to your phone or browser helps most, so you don't have to find the link in `/remote-control`. A reminder appears above the prompt box at either of these moments:
* **Long turn**: when a turn runs longer than a server-tuned threshold, Claude Code shows a **Still working** notification with a **Check in from your phone** link, so you can follow the turn from your phone or browser instead of waiting at the terminal. Claude Code removes it when the turn ends.
* **Repeated permission prompts**: after you answer several [permission prompts](/docs/en/permissions) in a session, an **Approve tool calls from your phone** notification shows the session URL. Claude Code removes it when your next turn starts.
The reminders can appear in any connected session, including ones where Remote Control [connects automatically](#enable-remote-control-for-all-sessions). They don't appear every time these conditions occur, and each one appears only a few times in total across sessions. You can't configure or turn them off; each clears on its own.
### Connect from another device
Once a Remote Control session is active, you have a few ways to connect from another device:
* **Open the session URL** in any browser to go directly to the session on [claude.ai/code](https://claude.ai/code).
* **Scan the QR code** shown alongside the session URL to open it directly in the Claude app. With `claude remote-control`, press spacebar to toggle the QR code display.
* **Open [claude.ai/code](https://claude.ai/code) or the Claude app** and find the session by name in the session list. In the Claude mobile app, tap **Code** in the navigation to reach the session list. Remote Control sessions show a computer icon with a green status dot when online.
When you connect, the device shows any subagents and workflows the session already has running in the background.
The remote session title is chosen in this order:
1. The name you passed to `--name`, `--remote-control`, or `/remote-control`
2. The title you set with `/rename`
3. The last meaningful message in existing conversation history
4. An auto-generated name like `myhost-graceful-unicorn`, where `myhost` is your machine's hostname or the prefix you set with `--remote-control-session-name-prefix`
If you didn't set an explicit name, Claude Code updates the title to reflect your prompt once you send one. Claude Code matches auto-generated titles to the language of your conversation, or to the [`language`](/docs/en/settings#available-settings) setting if one is configured; the language matching requires Claude Code v2.1.176 or later.
When you rename a session from claude.ai or the Claude app, Claude Code also updates the local title shown in `claude --resume`. Claude Code applies the same rename to the session name shown on the prompt bar, and in the `claude agents` listing when the session [runs in the background](/docs/en/agent-view). Before v2.1.221, renaming from the session list at claude.ai or in the Claude app updated only the title, and the CLI kept its previous session name; `/rename`, which runs in the CLI itself, set the name on any version.
If the environment already has an active session, you'll be asked whether to continue it or start a new one.
If you don't have the Claude app yet, use the `/mobile` command inside Claude Code to display a download QR code for [iOS](https://apps.apple.com/us/app/claude-by-anthropic/id6473753684) or [Android](https://play.google.com/store/apps/details?id=com.anthropic.claude).
### What connected devices see
A connected device shows the conversation in your terminal as it happens. These cases go beyond ordinary messages:
* **Compaction and `/clear`**: while Claude Code [compacts the conversation](/docs/en/context-window#what-survives-compaction), connected devices show the progress and then where the conversation was compacted. When you run `/clear`, the conversation resets on connected devices too.
* **Switching conversations with `/resume`**: the connected device doesn't receive the switched-to conversation's title or earlier history, but new messages in both directions go to and from whichever conversation is open in your terminal. To work on the original conversation from the device again, run `/resume` in your terminal and switch back to it.
* **Messages from your other sessions**: with [cross-session messaging](/docs/en/cross-session-messaging), the same connection carries messages between your own sessions on different machines and from your [Claude Code on the web](/docs/en/claude-code-on-the-web) sessions, through Anthropic servers like the rest of Remote Control traffic. [Message sessions on other machines](/docs/en/cross-session-messaging#message-sessions-on-other-machines) covers the delivery rules and [Control inbound messages](/docs/en/cross-session-messaging#control-inbound-messages) covers the inbound controls. Requires Claude Code v2.1.224 or later.
* **Reconnecting after a connection failure**: run `/remote-control` to reconnect. If compaction rewrote the conversation or you switched conversations with `/resume` in the meantime, Claude Code archives the server session it was using instead of leaving it in the session list. You can still find it by [filtering for archived sessions](/docs/en/claude-code-on-the-web#archive-sessions). Switching conversations while a device is still connected doesn't archive the session.
### Enable Remote Control for all sessions
Remote Control only activates when you explicitly run `claude remote-control`, `claude --remote-control`, or `/remote-control`, unless auto-connect is turned on. To turn auto-connect on for every interactive session, run `/config` inside Claude Code and set **Enable Remote Control for all sessions**. The toggle takes three values:
* **`true`**: connect automatically when an interactive session starts.
* **`false`**: turn auto-connect off, though a `true` from [managed settings](/docs/en/settings#settings-files) outranks it, because Claude Code saves the choice to your user settings. A `false` in project or local settings (`.claude/settings.json`, `.claude/settings.local.json`) turns auto-connect off even over a managed `true`.
* **`default`**: clear your choice and follow your organization's admin default if one is set, otherwise Claude Code's current default.
The same toggle appears outside the CLI:
* **Desktop app**: **Settings > Claude Code > Enable remote control by default**.
* **VS Code extension**: **Enable Remote Control for all sessions** in the [command menu's](/docs/en/vs-code#use-the-prompt-box) Settings section. Requires Claude Code v2.1.203 or later.
To turn auto-connect on from a settings file instead, set [`remoteControlAtStartup`](/docs/en/settings#available-settings) to `true` in your user `~/.claude/settings.json` or in [managed settings](/docs/en/settings#settings-files). In project or local settings (`.claude/settings.json`, `.claude/settings.local.json`), Claude Code honors a `false` and turns auto-connect off for that repository, but ignores a `true`, so a checked-in file can't turn on Remote Control for everyone who opens the repository.
Auto-connect signs in with your own claude.ai account, so a session it starts appears only in your own account's Claude apps and grants no one else access.
With this setting on, each interactive Claude Code process registers one remote session. If you run multiple instances, each one gets its own remote session. To run multiple concurrent sessions from a single process, use [server mode](#start-a-remote-control-session) instead.
### Resume sessions after stopping the server
When you stop `claude remote-control` with Ctrl+C, the sessions it was serving stop responding from your phone or browser. As long as you weren't running another `claude remote-control` in the same directory and didn't start this one with `--no-create-session-in-dir`, Claude Code doesn't archive them. To bring them back, run one of these commands in the same directory:
* **`claude remote-control`**: brings back every session the server was serving.
* **`claude remote-control --continue`**: brings back only the session the server started with, and exits when that session ends. If this directory has no record, Claude Code uses the newest one from this repository's other git worktrees.
* **`claude remote-control --session-id <id>`**: brings back only the session whose ID you pass, and exits when that session ends. The ID is the part of the session's URL at claude.ai/code between `/code/` and any `?`.
These commands work for about four hours after the server stopped. After that, run `claude remote-control` to start a new session. If you archived a session in the meantime, `--continue` and `--session-id` unarchive it on Claude Code v2.1.228 or later.
A session you started with `claude --remote-control` or `/remote-control` comes back when you resume the conversation with `claude --continue` or `claude --resume`, as long as Remote Control was still on when the session ended.
## Connection and security
Your local Claude Code session makes outbound HTTPS requests only and never opens inbound ports on your machine. When you start Remote Control, it registers with the Anthropic API and polls for work. When you connect from another device, the server routes messages between the web or mobile client and your local session over a streaming connection.
All traffic travels through the Anthropic API over TLS, the same transport security as any Claude Code session. The connection uses multiple short-lived credentials, each scoped to a single purpose and expiring independently.
While Remote Control is connected, the session transcript, including your messages, Claude's responses, and tool activity, is stored on Anthropic servers. The stored transcript keeps the conversation in sync across your devices and lets the session reconnect after a network drop. Execution and filesystem access stay on your machine, and stored transcripts are retained under the [Data usage](/docs/en/data-usage) policy.
To turn Remote Control off entirely, use the [`disableRemoteControl`](/docs/en/settings#available-settings) setting. Organizations with compliance requirements such as Zero Data Retention can't enable Remote Control.
## Trusted Devices
<Note>
Trusted Devices is currently in beta. Features and functionality may evolve as the experience is refined.
Trusted Devices is available on Team and Enterprise plans. It is off by default until an admin enables it.
</Note>
Trusted Devices is an organization-wide setting that requires members to verify their device before they can view or steer Remote Control sessions from claude.ai, the Claude mobile apps, or Claude Desktop. It ties Remote Control access to a known device and a recent authentication, not just a signed-in account.
When the setting is on, interacting with a Remote Control session requires both of the following:
* **An enrolled device**: each browser, phone, or desktop app a member uses for Remote Control enrolls its own credential. Enrollment is only offered shortly after a full sign-in, so a device joins the trusted list as part of a real authentication rather than silently in the background.
* **A recent sign-in**: the member's sign-in must be no more than 18 hours old. Instead of signing in again each day, members confirm presence with Face ID, Touch ID, Windows Hello, or a passkey. This biometric step-up refreshes the session immediately.
Biometric checks run on the device through the operating system or browser, the same mechanism as passkey sign-in. Anthropic never receives or stores fingerprints, face data, or any other biometric information. Only the device's public key and basic metadata such as display name, platform, and enrollment time are stored.
The setting applies only to Remote Control. Regular Claude chat, Claude Code in the terminal, and API usage are unaffected.
### Enable Trusted Devices for your organization
Admins enable the setting from the Claude Code admin console.
<Steps>
<Step title="Open Claude Code admin settings">
Go to [claude.ai/admin-settings/claude-code](https://claude.ai/admin-settings/claude-code). The **Require trusted devices** toggle appears under the Remote Control setting.
</Step>
<Step title="Turn on Require trusted devices">
The setting applies to every member of the organization and to Remote Control sessions started after you enable it. Sessions that were already running before the toggle was turned on are not retroactively protected and continue without the device requirement until they end. Per-team or per-project scoping is not available.
</Step>
<Step title="Tell members what to expect">
The first time a member views or steers a new Remote Control session from a browser, phone, or desktop app after the setting is enabled, they are prompted to enroll that device. Letting them know ahead of time avoids confusion.
</Step>
</Steps>
### What members see
Enrollment is a one-time step per device. After that, the only visible change is an occasional biometric prompt.
* **First use on each device**: the member is asked to enroll. If their sign-in is not recent, they sign in first through your normal flow, including SSO if configured, then confirm enrollment.
* **Day to day**: members with an enrolled device and a recent sign-in see no prompts. When the sign-in ages past 18 hours, the next Remote Control interaction shows a single Face ID, Touch ID, Windows Hello, or passkey prompt.
* **Unenrolled devices**: Remote Control sessions cannot be viewed or steered until the device is enrolled. Regular Claude chat on that device is unaffected.
* **No platform authenticator**: members on a machine without Face ID, Touch ID, or Windows Hello can use a hardware security key, or sign in again instead of stepping up.
* **In the terminal**: the machine running Claude Code receives its own credential automatically when the developer signs in to the CLI. There is no separate enrollment step in the terminal.
### Manage enrolled devices
Members can review and revoke their own devices from account settings.
Open [claude.ai/settings/account](https://claude.ai/settings/account#trusted-devices) and find the **Trusted devices** section to see every enrolled device with its name, platform, and enrollment date. Removing a device revokes its credential immediately, and the device can re-enroll later after a fresh sign-in. Credentials also expire on their own if not renewed, so an unused device drops off the trusted list automatically.
For a lost or stolen device, the member removes it from this page. If the member cannot sign in, an admin can use **Sign out everywhere** in the admin console to revoke every session and enrolled device for that member, after which the member re-enrolls the devices they still hold.
## Remote Control vs Claude Code on the web
Remote Control and [Claude Code on the web](/docs/en/claude-code-on-the-web) both use the claude.ai/code interface. The key difference is where the session runs: Remote Control executes on your machine, so your local MCP servers, tools, and project configuration stay available. Claude Code on the web executes in the cloud.
Use Remote Control when you're in the middle of local work and want to keep going from another device. Use Claude Code on the web when you want to kick off a task without any local setup, work on a repo you don't have cloned, or run multiple tasks in parallel.
## Mobile push notifications
When Remote Control is active, Claude can send push notifications to your phone.
Claude decides when to push. It typically sends one when a long-running task finishes or when it needs a decision from you to continue. You can also request a push in your prompt, for example `notify me when the tests finish`. Beyond the two on/off toggles below, there is no per-event configuration.
To set up mobile push notifications:
<Steps>
<Step title="Install the Claude mobile app">
Download the Claude app for [iOS](https://apps.apple.com/us/app/claude-by-anthropic/id6473753684) or [Android](https://play.google.com/store/apps/details?id=com.anthropic.claude).
</Step>
<Step title="Sign in with your Claude Code account">
Use the same account and organization you use for Claude Code in the terminal.
</Step>
<Step title="Allow notifications">
Accept the notification permission prompt from the operating system.
</Step>
<Step title="Enable push in Claude Code">
In your terminal, run `/config` and enable **Push when Claude decides** for proactive notifications, **Push when actions required** for permission prompts and questions, or both.
</Step>
</Steps>
If notifications don't arrive:
* If `/config` shows **No mobile registered**, open the Claude app on your phone so it can refresh its push token. The warning clears the next time Remote Control connects.
* On iOS, Focus modes and notification summaries can suppress or delay pushes. Check Settings → Notifications → Claude.
* On Android, aggressive battery optimization can delay delivery. Exempt the Claude app from battery optimization in system settings.
Claude Code skips mobile push notifications while you are typing in or focused on the connected terminal. As of v2.1.181, you can set [`CLAUDE_CLIENT_PRESENCE_FILE`](/docs/en/env-vars) to a marker file path to extend this to any time you are at the machine, even in another window: notifications are skipped while the file exists. Configure a screen-lock listener or similar tool to create the file when your screen unlocks and delete it when your screen locks.
## Limitations
* **One remote session per interactive process**: outside of server mode, each Claude Code instance supports one remote session at a time. Use [server mode](#start-a-remote-control-session) to run multiple concurrent sessions from a single process.
* **Local process must keep running**: Remote Control runs as a local process. If you close the terminal, quit VS Code, or otherwise stop the `claude` process, the session goes offline until you [bring it back](#resume-sessions-after-stopping-the-server). To keep a session running on a remote machine after you disconnect from SSH, start it inside `tmux` or `screen`.
Cut at 300 lines. The page has the rest.
routines First recorded · 404 lines, first recorded
# Automate work with routines ## Example use cases ## Create a routine ### Create from the web ### Create from the CLI ## Configure triggers ### Add a schedule trigger #### Schedule a one-off run ### Add an API trigger #### Trigger a routine #### API reference ### Add a GitHub trigger #### Supported events #### Filter pull requests ## Manage routines ### View and interact with runs ### Edit and control routines ### Manage routines from the CLI ### Repositories and branch permissions ### Connectors ### Environments and network access ## Usage and limits ## Troubleshooting ### `/schedule` returns "Unknown command" ### `/schedule` asks you to authenticate ## Related resources
The first capture of this source. The page was already there, and this is what it said.
# Automate work with routines
> Put Claude Code on autopilot. Define routines that run on a schedule, trigger on API calls, or react to GitHub events from cloud infrastructure.
<Note>
Routines are in research preview. Behavior, limits, and the API surface may change.
</Note>
A routine is a saved Claude Code configuration: a prompt, one or more repositories, and a set of [connectors](/docs/en/mcp), packaged once and run automatically. Routines execute on Anthropic-managed cloud infrastructure, or on your organization's [self-hosted environment](/docs/en/self-hosted-environments) when routed there, so they keep working when your laptop is closed.
Each routine can have one or more triggers attached to it:
* **Scheduled**: run on a recurring cadence like hourly, nightly, or weekly, or once at a specific future time
* **API**: trigger on demand by sending an HTTP POST to a per-routine endpoint with a bearer token
* **GitHub**: run automatically in response to repository events such as pull requests or releases
A single routine can combine triggers. For example, a PR review routine can run nightly, trigger from a deploy script, and also react to every new PR.
Routines are available on Pro, Max, Team, and Enterprise plans with [Claude Code on the web](/docs/en/claude-code-on-the-web) enabled. Create and manage them at [claude.ai/code/routines](https://claude.ai/code/routines), or from the CLI with `/schedule`.
Team and Enterprise Owners can disable routines for all members with the Routines toggle at [claude.ai/admin-settings/claude-code](https://claude.ai/admin-settings/claude-code). When disabled, existing routines stop running and members cannot create new ones.
This page covers creating a routine, configuring each trigger type, managing runs, and how usage limits apply.
## Example use cases
Each example pairs a trigger type with the kind of work routines are suited to: unattended, repeatable, and tied to a clear outcome.
**Backlog maintenance.** A schedule trigger runs every weeknight against your issue tracker via a connector. The routine reads issues opened since the last run, applies labels, assigns owners based on the area of code referenced, and posts a summary to Slack so the team starts the day with a groomed queue.
**Alert triage.** Your monitoring tool calls the routine's API endpoint when an error threshold is crossed, passing the alert body as `text`. The routine's prompt tells Claude to investigate the alert in the fire payload, so it pulls the stack trace, correlates it with recent commits in the repository, and opens a draft pull request with a proposed fix and a link back to the alert. On-call reviews the PR instead of starting from a blank terminal.
**Bespoke code review.** A GitHub trigger runs on `pull_request.opened`. The routine applies your team's own review checklist, leaves inline comments for security, performance, and style issues, and adds a summary comment so human reviewers can focus on design instead of mechanical checks.
**Deploy verification.** Your CD pipeline calls the routine's API endpoint after each production deploy. The routine runs smoke checks against the new build, scans error logs for regressions, and posts a go or no-go to the release channel before the deploy window closes.
**Docs drift.** A schedule trigger runs weekly. The routine scans merged PRs since the last run, flags documentation that references changed APIs, and opens update PRs against the docs repository for an editor to review.
**Library port.** A GitHub trigger runs on `pull_request.closed` filtered to merged PRs in one SDK repository. The routine ports the change to a parallel SDK in another language and opens a matching PR, keeping the two libraries in step without a human re-implementing each change.
## Create a routine
Create a routine from the web at [claude.ai/code/routines](https://claude.ai/code/routines), from the Desktop app, or from the CLI. All three surfaces write to the same cloud account, so a routine you create in one shows up in the others immediately. In the Desktop app, click **Routines** in the sidebar, then **New routine**, and choose **Cloud**; choosing **Local** instead creates a [Desktop scheduled task](/docs/en/desktop-scheduled-tasks), which runs on your machine rather than in the cloud.
The creation form sets up the routine's prompt, repositories, environment, connectors, and triggers.
Routines run autonomously as full Claude Code cloud sessions: there is no permission-mode picker and no approval prompts during a run. The session can run shell commands, use [skills](/docs/en/skills) committed to the cloned repository, and call any connectors you include. What a routine can reach is determined by the repositories you select, the [environment's](/docs/en/cloud-environments) network access and variables, and the connectors you include. Scope each of those to what the routine actually needs.
Routines belong to your individual claude.ai account. They are not shared with teammates, and they count against your account's daily run allowance. Anything a routine does through your connected GitHub identity or connectors appears as you: commits and pull requests carry your GitHub user, and Slack messages, Linear tickets, or other connector actions use your linked accounts for those services.
### Create from the web
<Steps>
<Step title="Open the creation form">
Visit [claude.ai/code/routines](https://claude.ai/code/routines) and click **New routine**.
</Step>
<Step title="Name the routine and write the prompt">
Give the routine a descriptive name and write the prompt Claude runs each time. The prompt is the most important part: the routine runs autonomously, so the prompt must be self-contained and explicit about what to do and what success looks like.
When a trigger fires, the session receives the routine's saved prompt as its assigned task and carries it out, rather than treating it as untrusted content that arrived mid-conversation. The trigger attests only that the prompt was stored ahead of time by an authorized session on your account, so the fired prompt is not live user input and can't act as approval or consent for actions during the run. Content the session fetches during the run keeps its normal handling. Before v2.1.213, the session received the same prompt framed as an untrusted background notification and could refuse to act on it.
The prompt input includes a model selector. Claude uses the selected model on every run.
</Step>
<Step title="Select repositories">
Add one or more GitHub repositories for Claude to work in. Each repository is cloned at the start of a run, starting from the default branch. Claude creates `claude/`-prefixed branches for its changes.
</Step>
<Step title="Select an environment">
Pick a [cloud environment](/docs/en/cloud-environments) for the routine. Environments control what the cloud session has access to:
* **Network access**: set the level of internet access available during each run
* **Environment variables**: provide values Claude can use during each run. They're [visible to anyone who uses the environment](/docs/en/cloud-environments#what-carries-over-from-your-setup), so add credentials with that in mind
* **Setup script**: install dependencies and tools the routine needs. The result is [cached](/docs/en/cloud-environments#environment-caching), so the script doesn't re-run on every session
A **Default** environment is provided with **Trusted** network access, which allows only the [default allowlist](/docs/en/cloud-environments#default-allowed-domains) of package registries, cloud provider APIs, container registries, and common development domains through the session's network. Connectors you add to the routine reach their services through Anthropic's servers, so they don't need allowlist changes. If your routine needs to reach your own services directly, or a domain outside that list, edit the environment's [network access](/docs/en/cloud-environments#network-access) before running. To use a separate environment, [create one](/docs/en/cloud-environments#configure-your-environment) first.
</Step>
<Step title="Select a trigger">
Under **Select a trigger**, choose how the routine starts. You can pick one trigger type or combine several.
<Tabs>
<Tab title="Schedule">
Pick a preset frequency for a recurring run, or schedule a single one-off run at a specific timestamp. See [Add a schedule trigger](#add-a-schedule-trigger) for timezone handling, stagger, custom cron intervals, and one-off runs.
</Tab>
<Tab title="GitHub event">
Select the repository, the event to react to, and optional filters. See [Add a GitHub trigger](#add-a-github-trigger) for the full list of supported events and filter fields.
</Tab>
<Tab title="API">
Select **API** here, then save the routine. The URL and token are generated after the routine is saved, since they depend on the routine ID. See [Add an API trigger](#add-an-api-trigger) to copy the URL and generate a token.
</Tab>
</Tabs>
</Step>
<Step title="Review connectors">
Under **Connectors** at the bottom of the form, all of your connected [MCP connectors](/docs/en/mcp) are included by default. Remove any the routine doesn't need: Claude can use every tool from an included connector, including writes, without asking for permission during a run.
</Step>
<Step title="Create the routine">
Click **Create**. The routine appears in the list and runs the next time one of its triggers matches. To start a run immediately, click **Run now** on the routine's detail page.
Each run creates a new session alongside your other sessions, where you can see what Claude did, review changes, and create a pull request.
</Step>
</Steps>
### Create from the CLI
Run `/schedule` in any session to create a scheduled routine conversationally. You can also pass a description directly, for a recurring routine like `/schedule daily PR review at 9am` or a one-off like `/schedule clean up feature flag in one week`. Claude walks through the same information the web form collects, then saves the routine to your account. The command is also available under the alias `/routines`.
A successful start looks like a conversation: Claude asks follow-up questions about the schedule, repositories, and prompt before saving. If Claude instead replies that you need to authenticate or that it can't connect to your remote claude.ai account, no routine was created; see [Troubleshooting](#troubleshooting).
`/schedule` in the CLI creates scheduled routines. To add an API trigger, edit the routine on the web at [claude.ai/code/routines](https://claude.ai/code/routines). You can add a [GitHub trigger](#add-a-github-trigger) from the web or from the CLI. The CLI path requires Claude Code v2.1.225 or later.
A routine with no schedule trigger, such as one started only by API calls or GitHub events, has no next run time, and the CLI shows none when Claude saves or updates it. Before v2.1.211, the CLI reported a next run time in the year 1 for these routines.
## Configure triggers
A routine starts when one of its triggers matches. You can attach any combination of schedule, API, and GitHub triggers to the same routine, and add or remove them at any time from the **Select a trigger** section of the routine's edit form.
### Add a schedule trigger
A schedule trigger runs the routine on a recurring cadence, or once at a specific future time. Pick a preset frequency in the **Select a trigger** section: hourly, daily, weekdays, or weekly. Times are entered in your local zone and converted automatically, so the routine runs at that wall-clock time regardless of where the cloud infrastructure is located.
Runs may start a few minutes after the scheduled time due to stagger. The offset is consistent for each routine.
For a custom interval such as every two hours or the first of each month, pick the closest preset in the form, then run `/schedule update` in the CLI to set a specific cron expression. The minimum interval is one hour; expressions that run more frequently are rejected.
#### Schedule a one-off run
A one-off schedule fires the routine a single time at a specific timestamp. Use it to remind yourself later in the week, to open a cleanup PR after a rollout finishes, or to kick off a follow-up task when an upstream change lands. After the routine fires, it auto-disables and the web UI marks it as **Ran**. To run it again, edit the routine and set a new one-off time.
<Note>
One-off scheduling from the CLI is rolling out gradually and may not be available on your account yet. If `/schedule` only offers recurring schedules, create the one-off run from the web at [claude.ai/code/routines](https://claude.ai/code/routines) instead.
</Note>
Create a one-off run from the CLI by describing the time in natural language. Claude resolves the phrase against the current time and confirms the absolute timestamp before saving.
```text theme={null}
/schedule tomorrow at 9am, summarize yesterday's merged PRs
```
```text theme={null}
/schedule in 2 weeks, open a cleanup PR that removes the feature flag
```
The same local-to-UTC conversion as recurring schedules applies to one-off timestamps.
One-off runs do not count against the daily routine run cap. See [Usage and limits](#usage-and-limits) for details.
### Add an API trigger
An API trigger gives a routine a dedicated HTTP endpoint. POSTing to the endpoint with the routine's bearer token starts a new session and returns a session URL. Use this to wire Claude Code into alerting systems, deploy pipelines, internal tools, or anywhere you can make an authenticated HTTP request.
API triggers are added to an existing routine from the web. The CLI cannot currently create or revoke tokens.
<Steps>
<Step title="Open the routine for editing">
Go to [claude.ai/code/routines](https://claude.ai/code/routines), click the routine you want to trigger via API, then click the pencil icon to open **Edit routine**.
</Step>
<Step title="Add an API trigger">
Scroll to the **Select a trigger** section below the **Instructions** box, click **Add another trigger**, and choose **API**.
</Step>
<Step title="Copy the URL and generate a token">
The modal shows the URL for this routine along with a sample curl command. Copy the URL, then click **Generate token** and copy the token immediately. The token is shown once and cannot be retrieved later, so store it somewhere secure such as your alerting tool's secret store.
</Step>
<Step title="Call the endpoint">
Send the token in the `Authorization: Bearer` header when you POST to the URL. The [Trigger a routine](#trigger-a-routine) section below shows a complete example.
</Step>
</Steps>
Each routine has its own token, scoped to triggering that routine only. To rotate or revoke it, return to the same modal and click **Regenerate** or **Revoke**.
#### Trigger a routine
Send a POST request to the `/fire` endpoint with the bearer token in the `Authorization` header. The request body accepts an optional `text` field for run-specific context such as an alert body or a failing log, passed to the routine alongside its saved prompt. The value is freeform text and is not parsed: if you send JSON or another structured payload, the routine receives it as a literal string.
The `text` value doesn't reach the routine as a bare message. It arrives wrapped in a `<routine-fire-payload>` block that labels it as untrusted data and tells Claude not to follow instructions inside it unless the routine's own prompt says to. The same wrapping applies to text supplied with **Run now** in the web UI.
This means a routine's saved prompt must opt in to acting on fire text: write the prompt to reference the payload explicitly, for example "Investigate the alert described in the routine-fire-payload block", or the routine treats the text as inert context. Anyone holding the bearer token can send `text`, so the wrapper makes fire text from a leaked token arrive labeled as untrusted data rather than as direct instructions to your routine.
The example below triggers a routine from a shell. The routine ID and token shown are placeholders: replace them with the URL and token you copied when [adding the API trigger](#add-an-api-trigger), or the request fails with a `401` authentication error:
```bash theme={null}
curl -X POST https://api.anthropic.com/v1/claude_code/routines/trig_01ABCDEFGHJKLMNOPQRSTUVW/fire \
-H "Authorization: Bearer sk-ant-oat01-xxxxx" \
-H "anthropic-beta: experimental-cc-routine-2026-04-01" \
-H "anthropic-version: 2023-06-01" \
-H "Content-Type: application/json" \
-d '{"text": "Sentry alert SEN-4521 fired in prod. Stack trace attached."}'
```
A successful request returns a JSON body with the new session ID and URL:
```json theme={null}
{
"type": "routine_fire",
"claude_code_session_id": "session_01HJKLMNOPQRSTUVWXYZ",
"claude_code_session_url": "https://claude.ai/code/session_01HJKLMNOPQRSTUVWXYZ"
}
```
Open the session URL in a browser to watch the run in real time, review changes, or continue the conversation manually.
<Warning>
The `/fire` endpoint ships under the `experimental-cc-routine-2026-04-01` beta header. Request and response shapes, rate limits, and token semantics may change while the feature is in research preview. Breaking changes ship behind new dated beta header versions, and the two most recent previous header versions continue to work so that callers have time to migrate.
</Warning>
#### API reference
For the full API reference, including all error responses, validation rules, and field limits, see [Trigger a routine via API](https://platform.claude.com/docs/en/api/claude-code/routines-fire) in the Claude Platform documentation.
The `/fire` endpoint is available to claude.ai users only and is not part of the Claude Platform API surface.
### Add a GitHub trigger
A GitHub trigger starts a new session automatically when a matching event occurs on a connected repository. Claude Code doesn't reuse sessions across events, so two PR updates produce two independent sessions.
<Note>
During the research preview, GitHub webhook events are subject to per-routine and per-account hourly caps. Events beyond the limit are dropped until the window resets. See your current limits at [claude.ai/code/routines](https://claude.ai/code/routines).
</Note>
The Claude GitHub App must be installed on the repository you want to subscribe to, whichever surface you configure the trigger from.
* Configure GitHub triggers from the web UI, which prompts you to install the app when it's missing. Follow the steps below to configure one on the web.
* From the CLI, install the app from the [GitHub App page](https://github.com/apps/claude) first, then ask Claude to attach a GitHub trigger to an existing routine, for example `/schedule add a GitHub trigger to my nightly review for pull requests opened in acme/webapp`. The CLI path requires Claude Code v2.1.225 or later. When Claude adds the trigger, it replies with a link to the routine the trigger fires.
<Steps>
<Step title="Open the routine for editing">
Go to [claude.ai/code/routines](https://claude.ai/code/routines), click the routine, then click the pencil icon to open **Edit routine**.
</Step>
<Step title="Add a GitHub event trigger">
Scroll to the **Select a trigger** section, click **Add another trigger**, and choose **GitHub event**.
<Note>
Running `/web-setup` in the CLI grants repository access for cloning, but it does not install the Claude GitHub App and does not enable webhook delivery.
</Note>
</Step>
<Step title="Configure the trigger">
Select the repository, choose an event from the [supported events](#supported-events) list, and optionally add filters. Save the trigger.
</Step>
</Steps>
#### Supported events
GitHub triggers can subscribe to either of the following event categories. Within each category you can pick a specific action, such as `pull_request.opened`, or react to all actions in the category.
| Event | Triggers when |
| :----------- | :---------------------------------------------------------------------------- |
| Pull request | A PR is opened, closed, assigned, labeled, synchronized, or otherwise updated |
| Release | A release is created, published, edited, or deleted |
#### Filter pull requests
Use filters to narrow which pull requests start a new session. All filter conditions must match for the routine to trigger. The available filter fields are:
| Filter | Matches |
| :---------- | :------------------------------- |
| Author | PR author's GitHub username |
| Title | PR title text |
| Body | PR description text |
| Base branch | Branch the PR targets |
| Head branch | Branch the PR comes from |
| Labels | Labels applied to the PR |
| Is draft | Whether the PR is in draft state |
| Is merged | Whether the PR has been merged |
Each filter pairs a field with an operator: equals, contains, starts with, is one of, is not one of, or matches regex.
The `matches regex` operator tests the entire field value, not a substring within it. To match any title containing `hotfix`, write `.*hotfix.*`. Without the surrounding `.*`, the filter matches only a title that is exactly `hotfix` with nothing before or after. For literal substring matching without regex syntax, use the `contains` operator instead.
A few example filter combinations:
* **Auth module review**: base branch `main`, head branch contains `auth-provider`. Sends any PR that touches authentication to a focused reviewer.
* **Ready-for-review only**: is draft is `false`. Skips drafts so the routine only runs when the PR is ready for review.
* **Label-gated backport**: labels include `needs-backport`. Triggers a port-to-another-branch routine only when a maintainer tags the PR.
## Manage routines
Click a routine in the list to open its detail page. The detail page shows the routine's repositories, connectors, prompt, schedule, API tokens, GitHub triggers, and a list of past runs.
### View and interact with runs
Click any run to open it as a full session. From there you can see what Claude did, review changes, create a pull request, or continue the conversation. Each run session works like any other session: use the dropdown menu next to the session title to rename, archive, or delete it.
<Note>
A green status in the run list means the session started and exited without an infrastructure error. It does not mean the task in your prompt succeeded. Open the run to read the transcript and confirm what Claude actually did. Blocked network requests, missing connector tools, and task-level failures all surface there rather than in the status indicator.
</Note>
### Edit and control routines
From the routine detail page you can:
Cut at 300 lines. The page has the rest.
sandbox-environments First recorded · 180 lines, first recorded
# Choose a sandbox environment ## Compare sandboxing approaches ## Choose an approach ### How isolation relates to permission modes ## Sandboxed Bash tool ## Sandbox runtime ### Set up and launch the runtime ### What the runtime blocks on its own ### After unattended runs ## Dev containers ## Custom container ## Virtual machine ## Claude Code on the web ## Enforce isolation across an organization ## See also
The first capture of this source. The page was already there, and this is what it said.
# Choose a sandbox environment
> Compare Claude Code sandbox options: the built-in sandboxed Bash tool, sandbox runtime, dev containers, Docker, and VMs. Choose the right isolation for your threat model.
Isolating Claude Code limits what a session can read, write, and reach on the network. This matters most when you let Claude work with fewer permission prompts, run it unattended, or point it at code you do not fully trust.
Claude Code can run in several kinds of isolated environments, ranging from a lightweight per-command sandbox to a fully separate virtual machine. This page compares them by what they isolate and what they require, helps you choose one for your threat model, and shows how to enforce that choice across an organization.
<Info>
For the broader security model, see [Security](/docs/en/security). For Agent SDK deployments, see [Secure deployment](/docs/en/agent-sdk/secure-deployment).
</Info>
## Compare sandboxing approaches
The first two approaches in the table below run on the host operating system without containers. The rest place Claude Code inside a container or virtual machine.
| Approach | What is isolated | Requires Docker | Setup effort |
| :------------------------------------------------ | :-------------------------------------------------------------------------- | :-------------- | :-------------------------------------------------------------------------------------- |
| [Sandboxed Bash tool](#sandboxed-bash-tool) | Bash commands and their child processes | No | Minimal on macOS; low on Linux and WSL2 |
| [Sandbox runtime](#sandbox-runtime) | The whole Claude Code process, including file tools, MCP servers, and hooks | No | Low |
| [Dev container](#dev-containers) | Full development environment | Yes | Medium |
| [Custom container](#custom-container) | Full development environment | Yes | Medium to high |
| [Virtual machine](#virtual-machine) | Full operating system | No | High |
| [Claude Code on the web](#claude-code-on-the-web) | Full operating system, hosted by Anthropic | No | None; requires a Claude subscription, and GitHub when you launch from the web interface |
The [sandboxed Bash tool](/docs/en/sandboxing) is built into Claude Code and restricts only Bash commands. Built-in file tools, MCP servers, and hooks still run directly on your host. Every other approach in the table puts the whole Claude Code process inside the isolation boundary, so file tools, MCP servers, and hooks are restricted too.
<Warning>
Sandbox isolation reduces the impact of a breach, but it does not eliminate risk. Any approach that allows network egress can still leak data the agent can read, and any approach that mounts your project directory writable can still modify that code. Review the [security limitations](/docs/en/sandboxing#security-limitations) before relying on a sandbox as a hard control.
Isolation also does not change what is sent to the model. Your prompts and the files Claude reads are transmitted to the Anthropic API or your configured provider with or without a sandbox. See [Data usage](/docs/en/data-usage) for what Claude Code sends and how to reduce it.
</Warning>
## Choose an approach
Match your goal to a row below, then read the detail section that follows.
| You want to | Start with |
| :---------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Reduce permission prompts during everyday work on your own machine | The [sandboxed Bash tool](/docs/en/sandboxing), configured with `/sandbox` |
| Let Claude work unattended with `--dangerously-skip-permissions` or auto mode | The preconfigured [dev container](/docs/en/devcontainer), any container or VM, or the [sandbox runtime](#sandbox-runtime) |
| Isolate MCP servers and hooks as well as Bash, without Docker | The sandbox runtime |
| Work on an untrusted repository | A dedicated virtual machine, or [Claude Code on the web](/docs/en/claude-code-on-the-web) if you have a Claude subscription; GitHub is only required when you launch from the web interface |
| Standardize a sandboxed environment across a team | The preconfigured [dev container](/docs/en/devcontainer), copied into your repository |
| Use Claude Code from a device with no local setup | [Claude Code on the web](/docs/en/claude-code-on-the-web), which requires a Claude subscription and a connected GitHub account |
| Require isolation for every developer in your organization | [Enforce isolation across an organization](#enforce-isolation-across-an-organization) |
| Work on a native Windows host | A container or VM, or run the Bash sandbox inside WSL2 |
### How isolation relates to permission modes
[Permission modes](/docs/en/permission-modes) decide whether a tool call runs and whether you are prompted first. Isolation restricts what a command can access once it runs. The two work together: when a permission mode lets actions run without asking you, an isolation boundary limits what those actions can reach.
When you pass `--dangerously-skip-permissions`, Claude acts without asking you first. Claude Code still prompts you only for:
* Explicit [ask rules](/docs/en/permissions#manage-permissions)
* Connector tools [your organization set to `ask`](/docs/en/mcp#organization-controls-on-connector-tools)
* MCP tools marked [`requiresUserInteraction`](/docs/en/mcp#require-approval-for-a-specific-tool)
* Removals targeting `/` or your home directory
* The [cross-session messaging safeguards](/docs/en/permission-modes#skip-all-checks-with-bypasspermissions-mode)
With no prompts to catch mistakes, the isolation boundary you choose is what protects your system. Always run `--dangerously-skip-permissions` sessions inside a container, a VM, or the [sandbox runtime](#sandbox-runtime), so that file tools, MCP servers, and hooks are also inside the boundary. On Linux and macOS, Claude Code refuses to start with this flag when running as root, so run the container, VM, or sandbox runtime as a non-root user.
[Auto mode](/docs/en/permission-modes#eliminate-prompts-with-auto-mode) replaces the prompt with a classifier that reviews actions. The classifier is a per-action control, not an isolation boundary, so an isolation boundary still adds defense in depth for unattended runs, and is not required the way it is for `--dangerously-skip-permissions`.
The [sandboxed Bash tool](#sandboxed-bash-tool) on its own constrains only Bash, so it is not sufficient for fully unattended runs in either mode. You can layer approaches: running the sandboxed Bash tool inside a container or VM gives you OS-level command restrictions on top of the outer environment boundary. For how the Bash sandbox itself interacts with permission rules and modes, see [How sandboxing relates to permissions and permission modes](/docs/en/sandboxing#how-sandboxing-relates-to-permissions-and-permission-modes).
## Sandboxed Bash tool
<Note>
This option does not support native Windows. On Windows hosts, use WSL2 or one of the container or VM approaches below.
</Note>
The sandboxed Bash tool is built into Claude Code. It uses operating system primitives to restrict the filesystem and network access of every Bash command Claude runs.
Run the `/sandbox` command to open the sandbox panel and choose a mode. The [Sandboxing](/docs/en/sandboxing) guide covers the approval modes, the default boundary, and how to widen or narrow it.
The per-command sandbox does not cover everything that runs in a session:
* Other [built-in tools](/docs/en/tools-reference) such as Read, Edit, and WebFetch run inside the Claude Code process and do not spawn arbitrary code. [Permission rules](/docs/en/permissions) for path or domain gate them instead.
* [MCP](/docs/en/mcp) servers and hooks are separate processes that run unconstrained on the host.
To put built-in tools, MCP servers, and hooks all behind one OS boundary, run the whole Claude Code process inside the [sandbox runtime](#sandbox-runtime), the [dev container](#dev-containers), or a [custom container](#custom-container).
## Sandbox runtime
The [`@anthropic-ai/sandbox-runtime`](https://github.com/anthropic-experimental/sandbox-runtime) package wraps an entire process in the same Seatbelt or bubblewrap isolation that the built-in Bash sandbox uses. Running Claude Code through the runtime constrains every tool, hook, and MCP server in the session, not only Bash. The runtime is a beta research preview, and its configuration format may change as the package evolves.
This section covers what you configure and what the runtime enforces on its own. For deploying the runtime in Agent SDK applications, see the [secure deployment guide](/docs/en/agent-sdk/secure-deployment#sandbox-runtime).
### Set up and launch the runtime
On Linux and WSL2, the runtime relies on the same `bubblewrap` and `socat` packages as the built-in sandbox, plus `ripgrep`, which Claude Code bundles but the standalone runtime resolves from your PATH. Install `bubblewrap` and `socat` as described in [Set up Linux and WSL2](/docs/en/sandboxing#set-up-linux-and-wsl2), and `ripgrep` from your distribution's package manager. On macOS you need no additional packages. The runtime uses the built-in Seatbelt sandbox there.
By default the runtime denies network access and confines writes to a small set of built-in runtime paths, so configure it before launching Claude Code through it. Put your configuration in `~/.srt-settings.json`, or in a file you pass with `--settings`. The package [README](https://github.com/anthropic-experimental/sandbox-runtime) documents the full configuration schema.
Allow write access to at least:
* Your project directory.
* Claude Code's configuration paths `~/.claude` and `~/.claude.json`.
* `/tmp`, where Claude Code writes runtime files.
Allow the network domains your session needs:
* `api.anthropic.com`, or your configured provider's endpoint. On a third-party provider, keep `api.anthropic.com` as well: the WebFetch domain safety check still calls it by default unless you set `skipWebFetchPreflight: true`.
* `claude.ai` and `platform.claude.com`, which [OAuth sign-in and token refresh](/docs/en/network-config#network-access-requirements) require. Runs authenticated with an API key can drop these two.
On Linux and WSL2, the runtime applies write grants only to paths that already exist. In a fresh environment, create Claude Code's configuration paths before the first launch:
```bash theme={null}
mkdir -p ~/.claude && echo '{}' > ~/.claude.json
```
Once the settings file is in place, launch Claude Code with `npx` and pass `claude` as the command to wrap:
```bash theme={null}
npx @anthropic-ai/sandbox-runtime claude
```
Claude Code starts inside the sandbox with the filesystem and network boundaries you configured. The same command works for sandboxing standalone MCP servers or other helper processes.
### What the runtime blocks on its own
The runtime blocks the highest-risk writes without any configuration from you:
* `denyWrite` takes precedence over `allowWrite`.
* At the project root, the runtime denies `.git/hooks`, denies `.git/config` unless you set `filesystem.allowGitConfig: true`, and denies `.mcp.json`, `.claude/commands`, `.claude/agents`, and shell startup files.
* On macOS, these denies are checked when a write happens, so they also cover nested files and repositories created during the session.
* On Linux and WSL2, the runtime builds the deny list once at launch. It reliably covers the project root, makes a best-effort shallow scan for nested copies that exist at that point, and does not cover anything the session creates later, such as `git init`, `git clone`, or scaffolding. The README's `mandatoryDenySearchDepth` section describes the scan's exact semantics.
* Without a valid `~/.srt-settings.json`, the runtime starts anyway, blocks network access, and confines writes to built-in runtime paths such as `/tmp/claude`, `~/.npm/_logs`, and `~/.claude/debug`. Don't take a clean start as proof your settings loaded.
* When you pass `--settings`, the runtime refuses to start if the file fails to load.
Your write grants still include other paths Claude Code loads configuration from, so deny those with `denyWrite`. A sandboxed session that can write them can persist hooks, permission rules, or MCP servers that run unsandboxed the next time you launch Claude Code.
### After unattended runs
Review the paths you kept writable. On Linux and WSL2, also review anything the session created.
## Dev containers
A dev container runs Claude Code inside a Docker container that VS Code or a compatible editor manages, with your project mounted in. You can define your own with a `.devcontainer/` directory in your repository.
The claude-code repository publishes an [example dev container](/docs/en/devcontainer) with a default-deny iptables firewall as a starting point. Copy it into your repository and adjust the firewall allowlist, base image, and pinned Claude Code version to fit your environment. Because the firewall blocks unapproved egress, a configuration like this supports running Claude Code with `--dangerously-skip-permissions` for unattended work.
## Custom container
You can run Claude Code in any Docker or OCI container image with your own network policies, mounted volumes, and seccomp profiles. This is the most common path for organizations with existing container infrastructure or CI runners.
Several managed sandbox and remote execution services can host the container for you. The same checklist applies as for any container you operate: review what is mounted writable, what credentials and tokens are reachable inside it, and what the network egress policy allows.
You can layer the built-in Bash sandbox inside the container for per-command restrictions. Unprivileged containers need the nested-sandbox setting described in [Sandboxing troubleshooting](/docs/en/sandboxing#troubleshooting).
## Virtual machine
A dedicated virtual machine provides the strongest separation, with its own kernel and, in cloud or microVM deployments, its own virtualized hardware. Options include cloud instances, local hypervisors, and microVMs such as Firecracker. Use this approach when you are evaluating untrusted code, when your security policy requires kernel-level separation between the agent and the host, or when no host-level approach meets your compliance requirements.
[Docker Sandboxes](https://docs.docker.com/ai/sandboxes/) provides a microVM with its own Docker daemon and workspace sync, which can run Claude Code on any host with Docker Sandboxes installed. It is a free, standalone product from Docker that does not require Docker Desktop.
## Claude Code on the web
[Claude Code on the web](/docs/en/claude-code-on-the-web) runs each session in an isolated, Anthropic-managed virtual machine. A network proxy enforces a default allowlist, and a separate proxy holds your GitHub token outside the sandbox while issuing scoped credentials for repository access inside it. Sessions your organization routes to a [self-hosted environment](/docs/en/self-hosted-environments) run on infrastructure you provision instead, where isolation, egress control, and git credentials are your deployment's responsibility.
Use this approach when you want full VM isolation without provisioning infrastructure yourself, or when you are delegating tasks from a device that does not have a local development environment. It requires a Claude subscription. When you launch a session from the web interface, you also need a connected GitHub account so the sandbox can clone your repository. When you launch from the CLI with `--cloud`, Claude Code can [bundle and upload your local repository](/docs/en/claude-code-on-the-web#send-local-repositories-without-github) instead if GitHub isn't connected. See [Claude Code on the web](/docs/en/claude-code-on-the-web) for plan availability and GitHub authentication options.
## Enforce isolation across an organization
Individual developers can opt into any approach above. What an organization can enforce, and with which tools, depends on the approach:
* **Built-in Bash sandbox**: the only approach Claude Code enforces itself. Deliver the `sandbox` settings keys through [managed settings](/docs/en/settings#settings-files), either as a file managed by your MDM or through [server-managed settings](/docs/en/server-managed-settings) on Claude.ai. See [Enforce sandboxing with managed settings](/docs/en/sandboxing#enforce-sandboxing-with-managed-settings) for the keys to deploy and how to keep developers from widening the policy.
* **Dev containers**: commit the [example dev container](/docs/en/devcontainer) to your repositories to standardize the environment across a team. This is a convention rather than an enforcement boundary, because Claude Code does not require a container. If developers should not be able to run Claude Code outside it, enforce that with your organization's device management or software allowlisting tools.
* **Custom containers and VMs**: distribute Claude Code through the approved image and use your organization's device management or software allowlisting tools to prevent installation outside it.
## See also
These pages cover configuration and policy details for the approaches above.
* [Sandboxing](/docs/en/sandboxing): configure the built-in sandboxed Bash tool
* [Dev container](/docs/en/devcontainer): the preconfigured Docker development container
* [Security](/docs/en/security): the full Claude Code security model
* [Secure deployment](/docs/en/agent-sdk/secure-deployment): isolation guidance for Agent SDK applications
* [Settings](/docs/en/settings#sandbox-settings): all sandbox configuration keys, including managed settings delivery
sandboxing First recorded · 667 lines, first recorded
# Configure the sandboxed Bash tool ## Get started ### Set up Linux and WSL2 ### Sandbox modes #### Auto-allow mode #### Regular permissions mode #### The unsandboxed retry escape hatch #### Temporary directories ## Configure sandboxing ### Disable filesystem isolation #### Which settings can disable it #### What changes when filesystem isolation is off ### Protect credentials ### Mask credentials #### Mask environment variables #### Re-sign AWS requests #### Mask credential files ## How sandboxing works ### Filesystem isolation ### Protected paths ### Network isolation #### IPv6 addresses in domain lists ### OS-level enforcement ## How sandboxing relates to permissions and permission modes ### Permission rules ### Permission modes ## Configure the sandbox for your organization ### Enforce sandboxing with managed settings ### Keep developers from widening the policy ### Custom proxy configuration ## Troubleshooting ## Limitations ### Security limitations ### Platform and tool compatibility ### Scope ## See also
The first capture of this source. The page was already there, and this is what it said.
# Configure the sandboxed Bash tool
> Learn how Claude Code's sandboxed Bash tool provides filesystem and network isolation for safer, more autonomous agent execution.
The Bash sandbox lets Claude run most shell commands without stopping to ask permission. Instead of approving each command, you define which files and network domains commands can touch, and the operating system enforces that boundary for every Bash command and its child processes.
<Note>
To compare other isolation approaches such as dev containers, custom containers, and virtual machines, see [Sandbox environments](/docs/en/sandbox-environments). To reduce permission prompts for tools other than Bash, see [permission modes](/docs/en/permission-modes).
</Note>
## Get started
The sandbox is built into Claude Code and runs on macOS, Linux, and WSL2. Native Windows is not supported. On Windows, run Claude Code inside a WSL2 distribution.
On macOS, there is nothing to install: sandboxing uses the built-in Seatbelt framework. On Linux and WSL2, the sandbox relies on two packages, covered in [Set up Linux and WSL2](#set-up-linux-and-wsl2). Even if you haven't installed them yet, you can start with `/sandbox`, because its panel shows whether anything is missing.
<Steps>
<Step title="Run /sandbox">
Start a Claude Code session and run the `/sandbox` command:
```text theme={null}
/sandbox
```
This opens the sandbox panel with three tabs, plus a Dependencies tab on Linux when the optional seccomp filter is missing:
* **Mode**: choose how sandboxed commands are approved, covered in the next step
* **Overrides**: choose whether commands that fail under the sandbox can fall back to running unsandboxed. This is the [`allowUnsandboxedCommands`](/docs/en/settings#sandbox-settings) setting
* **Config**: view the resolved sandbox settings
If the panel shows only a Dependencies tab, a required package is missing. Install it as described in [Set up Linux and WSL2](#set-up-linux-and-wsl2), restart Claude Code, and run `/sandbox` again.
</Step>
<Step title="Choose a mode">
On the Mode tab, select auto-allow or regular permissions. Auto-allow runs sandboxed commands without prompting, and regular permissions keeps the regular permission prompts even when commands are sandboxed. See [Sandbox modes](#sandbox-modes) for which commands still prompt in auto-allow mode.
</Step>
<Step title="Run a Bash command">
Ask Claude to run a command, such as a build or a test suite. By default, commands inside the sandbox can write only to the working directory and the session temp directory. The first time a command needs a new network domain, Claude Code prompts for approval.
Commands that cannot run sandboxed fall back to the regular permission flow. To widen or narrow these boundaries, see [Configure sandboxing](#configure-sandboxing).
</Step>
</Steps>
When you select a mode in the panel, Claude Code saves it to your project's local settings at `.claude/settings.local.json`, which apply to the current project. Claude Code adds that file to your global gitignore when it saves a setting there. To enable the sandbox across all of your projects, set [`sandbox.enabled`](/docs/en/settings#sandbox-settings) to `true` in your user settings at `~/.claude/settings.json`. To enforce sandboxing for every developer in an organization, use [managed settings](#enforce-sandboxing-with-managed-settings).
<Warning>
By default, if the sandbox cannot start because dependencies are missing or the platform is unsupported, Claude Code shows a warning and runs commands without sandboxing. To make this a hard failure instead, set [`sandbox.failIfUnavailable`](/docs/en/settings#sandbox-settings) to `true`. This is intended for managed deployments that require sandboxing as a security gate.
</Warning>
### Set up Linux and WSL2
On Linux and WSL2, the sandbox relies on two packages:
* [`bubblewrap`](https://github.com/containers/bubblewrap): the unprivileged sandboxing tool that enforces filesystem isolation
* [`socat`](http://www.dest-unreach.org/socat/): the relay used to route network traffic through the sandbox proxy
Install them with your distribution's package manager:
<Tabs>
<Tab title="Ubuntu/Debian">
```bash theme={null}
sudo apt-get install bubblewrap socat
```
</Tab>
<Tab title="Fedora">
```bash theme={null}
sudo dnf install bubblewrap socat
```
</Tab>
</Tabs>
When a dependency is missing, the Dependencies tab in `/sandbox` lists which of `ripgrep`, `bubblewrap`, `socat`, and the seccomp filter your platform lacks. If you don't see the tab after installing and restarting Claude Code, all dependencies are present.
Ripgrep is bundled with the native Claude Code binary. The seccomp filter is optional and adds Unix domain socket blocking. Install it with `npm install -g @anthropic-ai/sandbox-runtime` if it is missing.
When a required dependency is missing, the Dependencies tab is the only tab shown until you install it. When only the optional seccomp filter is missing, the Dependencies tab appears alongside the other tabs. The dependency check runs at startup, so restart Claude Code after installing packages for `/sandbox` to detect them.
<AccordionGroup>
<Accordion title="Ubuntu 24.04 and later: allow bubblewrap to create user namespaces">
On Ubuntu 24.04 and later, the default AppArmor policy prevents bubblewrap from creating the user namespaces it needs for isolation.
To check whether your environment enforces this restriction, including inside WSL2, run `sysctl kernel.apparmor_restrict_unprivileged_userns`. If the command returns `0`, skip this step. If it prints a `No such file or directory` error, the key doesn't exist and you can skip this step. If it returns `1`, add an AppArmor profile that grants `bwrap` this capability:
```bash theme={null}
sudo tee /etc/apparmor.d/bwrap > /dev/null <<'EOF'
abi <abi/4.0>,
include <tunables/global>
profile bwrap /usr/bin/bwrap flags=(unconfined) {
userns,
include if exists <local/bwrap>
}
EOF
```
The profile applies only to `bwrap` itself, not to the commands it runs inside the sandbox. Reload AppArmor to apply it:
```bash theme={null}
sudo systemctl reload apparmor
```
</Accordion>
<Accordion title="WSL2 notes">
Check your WSL version with `wsl -l -v` from PowerShell. If you see `Sandboxing requires WSL2`, your distribution is running WSL1. Upgrade it to WSL2 or run Claude Code without sandboxing.
On WSL2, WSL hands a launch of a Windows binary such as `cmd.exe`, `powershell.exe`, or anything under `/mnt/c/` to the Windows host over a Unix socket, so whether a sandboxed command can launch one follows the sandbox's [Unix-socket settings](/docs/en/settings#sandbox-settings): the optional seccomp filter has to be installed to block the socket in the first place. To allow these launches, set `allowAllUnixSockets`; to keep them out of the sandbox entirely, add the command to [`excludedCommands`](/docs/en/settings#sandbox-settings).
</Accordion>
</AccordionGroup>
### Sandbox modes
Claude Code offers two sandbox modes. In both, the sandbox enforces the same filesystem and network restrictions; the difference is only in whether sandboxed commands are auto-approved or require explicit permission.
#### Auto-allow mode
When a command can be sandboxed, Claude Code runs it inside the sandbox and approves it automatically, without asking your permission. Commands that cannot be sandboxed, such as those needing network access to non-allowed hosts, fall back to the regular permission flow, where Claude Code checks your [permission rules](/docs/en/permissions) and gates any command those rules do not already allow, with a prompt in default mode or the classifier in [auto mode](/docs/en/permission-modes#eliminate-prompts-with-auto-mode).
Even in auto-allow mode, the following still apply:
* Explicit [deny rules](/docs/en/permissions) are always respected
* `rm` or `rmdir` commands that target `/`, your home directory, or other critical system paths still go through the regular permission flow
* Content-scoped [ask rules](/docs/en/permissions) like `Bash(git push *)` still force a prompt even for sandboxed commands
* A bare `Bash` ask rule, or the equivalent `Bash(*)` form, is skipped for commands that run sandboxed; it still applies to commands that fall back to the regular permission flow. In [plan mode](/docs/en/permission-modes#analyze-before-you-edit-with-plan-mode), the rule isn't skipped: it prompts for sandboxed commands too, including read-only ones. Before v2.1.212, the skip applied in plan mode as well
<Info>
Auto-allow mode works independently of your permission mode setting, with one exception: [plan mode](/docs/en/permission-modes#analyze-before-you-edit-with-plan-mode). Even if you're not in "accept edits" mode, sandboxed Bash commands run automatically when auto-allow is enabled. This means Bash commands that modify files within the sandbox boundaries execute without prompting, even when file edit tools would normally require approval.
In plan mode, auto-allow doesn't widen approvals; see [plan mode](/docs/en/permission-modes#analyze-before-you-edit-with-plan-mode) for how Claude Code gates commands while you plan. Before v2.1.212, auto-allow ran sandboxed commands without a prompt in plan mode too.
</Info>
#### Regular permissions mode
All Bash commands go through the regular permission flow, even when sandboxed. This provides more control but requires more approvals.
#### The unsandboxed retry escape hatch
Some commands can't run inside the sandbox at all, such as tools that are incompatible with it or that need a host you haven't allowed. When a command fails after the sandbox denied it access, Claude Code appends the violation details to the failed command's output, so Claude sees which file path or network host the sandbox blocked. Rather than failing the task or requiring you to turn sandboxing off, Claude Code includes an escape hatch: when a command fails because of sandbox restrictions, Claude analyzes the failure and may retry the command with the `dangerouslyDisableSandbox` parameter.
The retried command runs outside the sandbox, so it goes through the regular permission flow: in default mode you get a confirmation prompt; in [auto mode](/docs/en/permission-modes#eliminate-prompts-with-auto-mode) the classifier evaluates the underlying command instead of prompting you. To be prompted on every unsandboxed retry even in auto mode, add an [ask rule](/docs/en/permissions#match-by-input-parameter) for `Bash(dangerouslyDisableSandbox:true)`.
You can disable this escape hatch by setting `"allowUnsandboxedCommands": false` in your [sandbox settings](/docs/en/settings#sandbox-settings). When disabled, which the `/sandbox` Overrides tab shows as **Strict sandbox mode**, the `dangerouslyDisableSandbox` parameter is completely ignored and all commands must run sandboxed or be explicitly listed in `excludedCommands`.
#### Temporary directories
The session temp directory is writable inside the sandbox by default, alongside the working directory. Unless you [disable filesystem isolation](#disable-filesystem-isolation), Claude Code sets `$TMPDIR` to this directory for sandboxed commands, so tools that write temporary files work without extra configuration. Unsandboxed commands inherit your shell's `$TMPDIR` unchanged, so while filesystem isolation is on, sandboxed and unsandboxed commands resolve `$TMPDIR` to different directories. To pass temporary files between the two, write them under the working directory instead.
## Configure sandboxing
Customize sandbox behavior through your `settings.json` file. See [Settings](/docs/en/settings#sandbox-settings) for the complete configuration reference.
By default, sandboxed commands can write only to the current working directory and the session temp directory. If subprocess commands like `kubectl`, `terraform`, or `npm` need to write outside those directories, use `sandbox.filesystem.allowWrite` to grant access to specific paths:
```json theme={null}
{
"sandbox": {
"enabled": true,
"filesystem": {
"allowWrite": ["~/.kube", "/tmp/build"]
}
}
}
```
These paths are enforced at the OS level, so all commands running inside the sandbox, including their child processes, respect them. This is the recommended approach when a tool needs write access to a specific location, rather than excluding the tool from the sandbox entirely with `excludedCommands`.
When the same filesystem array is defined in multiple [settings scopes](/docs/en/settings#settings-precedence), the arrays are merged: paths from every scope are combined, not replaced. When you edit these lists during a session, Claude Code [applies the change to the running session](/docs/en/settings#when-edits-take-effect), so the next sandboxed command runs under the new paths.
Path prefixes control how paths are resolved:
| Prefix | Meaning | Example |
| :---------------- | :------------------------------------------------------------------------------------- | :------------------------------------------------------------------------ |
| `/` | Absolute path from filesystem root | `/tmp/build` stays `/tmp/build` |
| `~/` | Relative to home directory | `~/.kube` becomes `$HOME/.kube` |
| `./` or no prefix | Relative to the project root for project settings, or to `~/.claude` for user settings | `./output` in `.claude/settings.json` resolves to `<project-root>/output` |
This syntax differs from [Read and Edit permission rules](/docs/en/permissions#read-and-edit), which use `//path` for absolute and `/path` for project-relative. Sandbox filesystem paths use standard conventions: `/tmp/build` is absolute. For how Claude Code treats a trailing slash or a wildcard in these paths, see [Sandbox path prefixes](/docs/en/settings#sandbox-path-prefixes).
You can also deny write or read access using `sandbox.filesystem.denyWrite` and `sandbox.filesystem.denyRead`, and re-allow specific paths within a denied region using `sandbox.filesystem.allowRead`. When read rules overlap, the more specific path wins:
| Example rules | Result |
| :------------------------------------------------------ | :------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `"denyRead": ["~/"]` with `"allowRead": ["~/projects"]` | `~/projects` is readable and the rest of the home directory stays blocked. The narrower allow re-opens that part of the denied region |
| `"allowRead": ["~/"]` with `"denyRead": ["~/.env"]` | `~/.env` stays blocked and the rest of the home directory is readable. An exact deny holds inside a wider allow, so a broad allow can't silently re-expose a secret |
The example below blocks reading from the entire home directory while still allowing reads from the current project. Place it in your project's `.claude/settings.json`, because the relative path `.` resolves to the project root only when the configuration lives in project settings:
```json theme={null}
{
"sandbox": {
"enabled": true,
"filesystem": {
"denyRead": ["~/"],
"allowRead": ["."]
}
}
}
```
If you placed the same configuration in `~/.claude/settings.json`, `.` would resolve to `~/.claude` instead, and project files would remain blocked by the `denyRead` rule.
### Disable filesystem isolation
Set `sandbox.filesystem.disabled` to `true` to skip filesystem isolation while keeping network isolation. The example below turns off filesystem isolation while keeping an allowlist of network domains:
```json theme={null}
{
"sandbox": {
"enabled": true,
"filesystem": {
"disabled": true
},
"network": {
"allowedDomains": ["github.com", "*.npmjs.org"]
}
}
}
```
The sandbox has two independent layers: [filesystem isolation](#filesystem-isolation) controls which paths sandboxed commands can read and write, and [network isolation](#network-isolation) controls which domains they can reach. With the filesystem layer off, sandboxed commands get unrestricted read and write access to the host filesystem, while their network egress stays confined to your allowed domains. Turn the layer off when you sandbox to control where commands connect rather than what they write.
The setting is off by default and applies on the platforms where the sandbox runs: macOS, Linux, and WSL2. Requires Claude Code v2.1.216 or later.
<Warning>
With filesystem isolation off and commands auto-allowed, a sandboxed command can write files that later commands run or read, such as shell startup files, executables on `$PATH`, or `~/.claude/settings.json`, and use them to widen its own access on the next run. Set `filesystem.disabled` to `true` only for workloads you trust not to escalate their own access. Locking network domains with [`allowManagedDomainsOnly`](#keep-developers-from-widening-the-policy) narrows the risk but doesn't remove it, since that lock applies only to commands running inside the sandbox.
</Warning>
#### Which settings can disable it
Because turning filesystem isolation off widens what sandboxed commands can do, Claude Code honors `filesystem.disabled` from these settings sources only:
* User settings, managed settings, and the `--settings` CLI flag can set it. Project settings in `.claude/settings.json` and `.claude/settings.local.json` can't, so a checked-out project can't switch filesystem isolation off.
* When managed settings configure `sandbox.filesystem` at all, or list any `sandbox.credentials.files` entry with `"mode": "deny"`, only managed settings can set the key. This keeps administrator-deployed filesystem restrictions in force; to relax such a deployment, set `"disabled": true` in managed settings.
* When [`CLAUDE_CODE_SUBPROCESS_ENV_SCRUB`](/docs/en/env-vars) is set, Claude Code ignores `filesystem.disabled` from every source, including managed settings, and keeps filesystem isolation on.
Whether a managed `credentials.files` entry pins `filesystem.disabled`, locking the key to managed settings so developers can't turn filesystem isolation off, depends on the entry's `mode` and what happens to the entry when the sandbox starts:
| Managed entry | Pins `filesystem.disabled` | What protects the file when isolation is off |
| ------------------------------------------------------------------------------------------------------ | ---------------------------- | ------------------------------------------------------------------------------------------------------------------------------- |
| `"mode": "deny"` | Yes | Nothing: the read block is part of the filesystem layer |
| `"mode": "mask"`, applied as a mask | No | Masking itself: the [sentinel copy and proxy](#mask-credential-files) on Linux and WSL2, the sandbox's own read rules on macOS |
| `"mode": "mask"`, [fallen back to `deny`](#mask-credential-files) at setup | No | Nothing, same as `deny`. List a path that can't be masked, such as a directory, as an explicit `deny` entry, which pins the key |
| `"mode": "mask"`, [degraded to `deny` by validation](/docs/en/settings#invalid-entries-in-managed-settings) | Yes, like an explicit `deny` | Nothing, same as `deny` |
A fallback happens when the sandbox starts, after Claude Code has already read the settings the pin check runs on, so a fallen-back entry never pins. Validation rewrites an invalid entry to `deny` while settings load, so a degraded entry pins like one you wrote as `deny`.
#### What changes when filesystem isolation is off
Setting `filesystem.disabled` lifts the protections the filesystem layer itself enforces. Protections that other layers enforce keep applying:
| Protection | With filesystem isolation off |
| ---------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `filesystem.denyRead` and [`credentials.files`](#protect-credentials) `deny` read blocks | Not enforced. The filesystem layer applies both |
| `credentials.envVars` `deny` and `mask` entries | Enforced. Environment variable scrubbing is independent of the filesystem layer |
| [`credentials.files` `mask` entries](#mask-credential-files) applied as masks | Enforced: masking is independent of the filesystem layer. An entry that [fell back to `deny`](#mask-credential-files) is not enforced, like any `deny` entry |
Two other things change:
* Sandboxed commands inherit your shell's `$TMPDIR` instead of the session temp directory, because every temp directory is writable and Claude Code no longer redirects commands to the session one. On Linux the variable is often unset in the parent shell, so it can expand empty inside sandboxed commands; Claude Code tells Claude through its Bash tool guidance to create scratch directories with `mktemp -d` instead of relying on `$TMPDIR`.
* [`autoAllowBashIfSandboxed`](/docs/en/settings#sandbox-settings) still defaults to `true`, so sandboxed commands keep running without prompts. Set it to `false` to prompt for sandboxed commands.
### Protect credentials
The `sandbox.credentials` setting declares credential files and environment variables to protect from sandboxed commands. Each entry names a file path or an environment variable and a `mode`. The dedicated `credentials` block keeps credential rules grouped together and separate from general filesystem rules. Requires Claude Code v2.1.187 or later.
For entries with `"mode": "deny"`, file paths are denied for reads inside the sandbox, the same restriction that `filesystem.denyRead` applies, and environment variables are unset before each sandboxed command runs. The file protection is part of the filesystem layer, so it doesn't apply if you [disable filesystem isolation](#disable-filesystem-isolation); the environment variable protection still does.
The example below blocks reads of the AWS credentials file and the SSH directory and removes `GITHUB_TOKEN` and `NPM_TOKEN` from the environment of sandboxed commands:
```json theme={null}
{
"sandbox": {
"enabled": true,
"credentials": {
"files": [
{ "path": "~/.aws/credentials", "mode": "deny" },
{ "path": "~/.ssh", "mode": "deny" }
],
"envVars": [
{ "name": "GITHUB_TOKEN", "mode": "deny" },
{ "name": "NPM_TOKEN", "mode": "deny" }
]
}
}
}
```
Environment variable entries and file entries also accept `"mode": "mask"`, described under [Mask credentials](#mask-credentials).
File paths follow the same [prefix rules](/docs/en/settings#sandbox-path-prefixes) as `sandbox.filesystem.*` settings, and `deny` entries from every [settings scope](/docs/en/settings#settings-precedence) are merged. A `deny` entry only ever narrows access, so any scope can add one, but no scope can remove one that another scope added.
There is no built-in credential deny list, so only the files and variables you list are restricted. The setting affects sandboxed Bash commands only. To strip Anthropic and cloud provider credentials from all subprocesses regardless of sandboxing, set [`CLAUDE_CODE_SUBPROCESS_ENV_SCRUB`](/docs/en/env-vars).
### Mask credentials
Masking goes further than a `deny` entry under [Protect credentials](#protect-credentials). Instead of blocking a credential, Claude Code shows sandboxed commands a placeholder, the sentinel, and the [sandbox proxy](#network-isolation) swaps in the real value on outbound requests to hosts you allow. For files, the substitution is Linux and WSL2 behavior; [macOS blocks the file instead](#mask-credential-files).
#### Mask environment variables
Cut at 300 lines. The page has the rest.
scheduled-tasks First recorded · 213 lines, first recorded
# Run prompts on a schedule ## Compare scheduling options ## Run a prompt repeatedly with /loop ### Run on a fixed interval ### Let Claude choose the interval ### Run the built-in maintenance prompt ### Customize the default prompt with loop.md ### Stop a loop ## Set a one-time reminder ## Manage scheduled tasks ## How scheduled tasks run ### Jitter ### Seven-day expiry ## Cron expression reference ## Disable scheduled tasks ## Limitations
The first capture of this source. The page was already there, and this is what it said.
# Run prompts on a schedule
> Use /loop and the cron scheduling tools to run prompts repeatedly, poll for status, or set one-time reminders within a Claude Code session.
Scheduled tasks let Claude re-run a prompt automatically on an interval. Use them to poll a deployment, babysit a PR, check back on a long-running build, or remind yourself to do something later in the session. To react to events as they happen instead of polling, see [Channels](/docs/en/channels): your CI can push the failure into the session directly. To keep the session working turn after turn until a condition is met rather than on an interval, see [`/goal`](/docs/en/goal).
Tasks are session-scoped: they live in the current conversation and stop when you start a new one. Resuming with `--resume` or `--continue` brings back any task that hasn't [expired](#seven-day-expiry): a recurring task created within the last 7 days, or a one-shot whose scheduled time hasn't passed yet. For scheduling that survives independently of any session, use [Routines](/docs/en/routines) to create a routine on the cloud, set up a [Desktop scheduled task](/docs/en/desktop-scheduled-tasks), or use [GitHub Actions](/docs/en/github-actions).
## Compare scheduling options
Claude Code offers three ways to schedule recurring or one-off work:
| | [Cloud](/docs/en/routines) | [Desktop](/docs/en/desktop-scheduled-tasks) | [`/loop`](/docs/en/scheduled-tasks) |
| :------------------------- | :---------------------------------- | :------------------------------------- | :---------------------------------- |
| Runs on | Cloud, Anthropic-managed by default | Your machine | Your machine |
| Requires machine on | No | Yes | Yes |
| Requires open session | No | No | Yes |
| Persistent across restarts | Yes | Yes | Restored on `--resume` if unexpired |
| Access to local files | No (fresh clone) | Yes | Yes |
| MCP servers | Connectors configured per task | [Config files](/docs/en/mcp) and connectors | Inherits from session |
| Permission prompts | No (runs autonomously) | Configurable per task | Inherits from session |
| Customizable schedule | Via `/schedule` in the CLI | Yes | Yes |
| Minimum interval | 1 hour | 1 minute | 1 minute |
<Tip>
Use **cloud tasks** for work that should run reliably without your machine. Use **Desktop tasks** when you need access to local files and tools. Use **`/loop`** for quick polling during a session.
</Tip>
## Run a prompt repeatedly with /loop
The `/loop` [bundled skill](/docs/en/commands) is the quickest way to run a prompt on repeat while the session stays open. Both the interval and the prompt are optional, and what you provide determines how the loop behaves.
| What you provide | Example | What happens |
| :------------------------ | :-------------------------- | :------------------------------------------------------------------------------------------------------------ |
| Interval and prompt | `/loop 5m check the deploy` | Your prompt runs on a [fixed schedule](#run-on-a-fixed-interval) |
| Prompt only | `/loop check the deploy` | Your prompt runs at an [interval Claude chooses](#let-claude-choose-the-interval) each iteration |
| Interval only, or nothing | `/loop` | The [built-in maintenance prompt](#run-the-built-in-maintenance-prompt) runs, or your `loop.md` if one exists |
You can also pass a skill as the prompt, for example `/loop 20m /review-pr 1234`, to re-run that skill each iteration. A scheduled fire only runs skills that Claude is [allowed to invoke on its own](/docs/en/skills#control-who-invokes-a-skill). The following reach Claude as plain text instead of executing:
* Built-in commands such as `/permissions`, `/model`, or `/clear`
* Skills marked [`disable-model-invocation: true`](/docs/en/skills#frontmatter-reference), including the bundled `/verify` skill
* Skills withheld from Claude by a [`skillOverrides`](/docs/en/skills#override-skill-visibility-from-settings) setting or a `Skill` [deny rule](/docs/en/skills#restrict-claude’s-skill-access)
* [MCP prompts](/docs/en/mcp#use-mcp-prompts-as-commands) such as `/mcp__github__list_prs`
### Run on a fixed interval
When you supply an interval, Claude converts it to a cron expression, schedules the job, and confirms the cadence and job ID.
```text theme={null}
/loop 5m check if the deployment finished and tell me what happened
```
The interval can lead the prompt as a bare token like `30m`, or trail it as a clause like `every 2 hours`. Supported units are `s` for seconds, `m` for minutes, `h` for hours, and `d` for days.
Seconds are rounded up to the nearest minute since cron has one-minute granularity. Intervals that don't map to a clean cron step, such as `7m` or `90m`, are rounded to the nearest interval that does and Claude tells you what it picked.
### Let Claude choose the interval
When you omit the interval, Claude chooses one dynamically instead of running on a fixed cron schedule. After each iteration it picks a delay between one minute and one hour based on what it observed: short waits while a build is finishing or a PR is active, longer waits when nothing is pending. The chosen delay and the reason for it are printed at the end of each iteration.
The example below checks CI and review comments, with Claude waiting longer between iterations once the PR goes quiet:
```text theme={null}
/loop check whether CI passed and address any review comments
```
When you ask for a dynamic `/loop` schedule, Claude may use the [Monitor tool](/docs/en/tools-reference#monitor-tool) directly. Monitor runs a background script and streams each output line back, which avoids polling altogether and is often more token-efficient and responsive than re-running a prompt on an interval.
A dynamically scheduled loop appears in your [scheduled task list](#manage-scheduled-tasks) like any other task, so you can list or cancel it the same way. The [jitter rules](#jitter) don't apply to it, but the [seven-day expiry](#seven-day-expiry) does.
<span id="loop-provider-differences" />
<Note>
On Amazon Bedrock, Claude Platform on AWS, Google Cloud's Agent Platform, and Microsoft Foundry, `/loop` behaves differently in two ways: a prompt with no interval runs on a fixed 10-minute schedule instead of a schedule Claude chooses, and `/loop` with no prompt prints the usage message instead of running the maintenance prompt or reading `loop.md`. The same happens when you turn off [feature-flag fetching](/docs/en/env-vars#features-that-need-feature-flag-fetching).
</Note>
### Run the built-in maintenance prompt
When you omit the prompt, Claude uses a built-in maintenance prompt instead of one you supply. On each iteration it works through the following, in order:
* continue any unfinished work from the conversation
* tend to the current branch's pull request: review comments, failed CI runs, merge conflicts
* run cleanup passes such as bug hunts or simplification when nothing else is pending
Claude does not start new initiatives outside that scope, and irreversible actions such as pushing or deleting only proceed when they continue something the transcript already authorized.
```text theme={null}
/loop
```
A bare `/loop` runs this prompt at a [dynamically chosen interval](#let-claude-choose-the-interval). Add an interval, for example `/loop 15m`, to run it on a fixed schedule instead. To replace the built-in prompt with your own default, see [Customize the default prompt with loop.md](#customize-the-default-prompt-with-loop-md).
### Customize the default prompt with loop.md
Where the [built-in maintenance prompt is available](#loop-provider-differences), a `loop.md` file replaces it with your own instructions. It defines a single default prompt for bare `/loop`, not a list of separate scheduled tasks, and is ignored whenever you supply a prompt on the command line. To schedule additional prompts alongside it, use `/loop <prompt>` or [ask Claude directly](#manage-scheduled-tasks).
Claude looks for the file in two locations and uses the first one it finds.
| Path | Scope |
| :------------------ | :--------------------------------------------------------------- |
| `.claude/loop.md` | Project-level. Takes precedence when both files exist. |
| `~/.claude/loop.md` | User-level. Applies in any project that does not define its own. |
The file is plain Markdown with no required structure. Write it as if you were typing the `/loop` prompt directly. The following example keeps a release branch healthy:
```markdown title=".claude/loop.md" theme={null}
Check the `release/next` PR. If CI is red, pull the failing job log,
diagnose, and push a minimal fix. If new review comments have arrived,
address each one and resolve the thread. If everything is green and
quiet, say so in one line.
```
Edits to `loop.md` take effect on the next iteration, so you can refine the instructions while a loop is running. When no `loop.md` exists in either location, the loop falls back to the built-in maintenance prompt. Keep the file concise: content beyond 25,000 bytes is truncated.
### Stop a loop
To stop a `/loop` while it is waiting for the next iteration, press `Esc`. This clears the pending wakeup so the loop does not fire again. Tasks you scheduled by [asking Claude directly](#manage-scheduled-tasks) are not affected by `Esc` and stay in place until you delete them.
In [self-paced mode](#let-claude-choose-the-interval), Claude can also end the loop on its own once the task is complete. Claude calls the [`ScheduleWakeup` tool](/docs/en/tools-reference) with `stop: true`, which cancels the pending wakeup immediately. If an iteration ends without either rescheduling or stopping, Claude Code schedules one fallback wakeup about 20 minutes later and ends the loop when that iteration doesn't reschedule either.
Loops on a fixed interval keep running until you stop them or [seven days elapse](#seven-day-expiry).
## Set a one-time reminder
For one-shot reminders, describe what you want in natural language instead of using `/loop`. Claude schedules a single-fire task that deletes itself after running.
```text theme={null}
remind me at 3pm to push the release branch
```
```text theme={null}
in 45 minutes, check whether the integration tests passed
```
Claude pins the fire time to a specific minute and hour using a cron expression and confirms when it will fire.
## Manage scheduled tasks
Ask Claude in natural language to list or cancel tasks, or reference the underlying tools directly.
```text theme={null}
what scheduled tasks do I have?
```
```text theme={null}
cancel the deploy check job
```
Under the hood, Claude uses these tools:
| Tool | Purpose |
| :----------- | :-------------------------------------------------------------------------------------------------------------- |
| `CronCreate` | Schedule a new task. Accepts a 5-field cron expression, the prompt to run, and whether it recurs or fires once. |
| `CronList` | List all scheduled tasks with their IDs, schedules, and prompts. |
| `CronDelete` | Cancel a task by ID. |
Each scheduled task has an 8-character ID you can pass to `CronDelete`. A session can hold up to 50 scheduled tasks at once.
## How scheduled tasks run
The scheduler checks every second for due tasks and enqueues them at low priority. A scheduled prompt fires between your turns, not while Claude is mid-response. If Claude is busy when a task comes due, the prompt waits until the current turn ends.
All times are interpreted in your local timezone. A cron expression like `0 9 * * *` means 9am wherever you're running Claude Code, not UTC.
### Jitter
To avoid every session hitting the API at the same wall-clock moment, the scheduler adds a deterministic offset to fire times:
* Recurring tasks fire up to 30 minutes after the scheduled time (or up to half the interval, for tasks that run more often than hourly). An hourly job scheduled for `:00` may fire anywhere up to `:30`.
* One-shot tasks scheduled for the top or bottom of the hour fire up to 90 seconds early.
The offset is derived from the task ID, so the same task always gets the same offset. If exact timing matters, pick a minute that is not `:00` or `:30`, for example `3 9 * * *` instead of `0 9 * * *`, and the one-shot jitter will not apply.
### Seven-day expiry
Recurring tasks automatically expire 7 days after creation. The task fires one final time, then deletes itself. This bounds how long a forgotten loop can run. If you need a recurring task to last longer, cancel and recreate it before it expires, or use [Routines](/docs/en/routines) or [Desktop scheduled tasks](/docs/en/desktop-scheduled-tasks) for durable scheduling.
## Cron expression reference
`CronCreate` accepts standard 5-field cron expressions: `minute hour day-of-month month day-of-week`. All fields support wildcards (`*`), single values (`5`), steps (`*/15`), ranges (`1-5`), and comma-separated lists (`1,15,30`).
| Example | Meaning |
| :------------- | :--------------------------- |
| `*/5 * * * *` | Every 5 minutes |
| `0 * * * *` | Every hour on the hour |
| `7 * * * *` | Every hour at 7 minutes past |
| `0 9 * * *` | Every day at 9am local |
| `0 9 * * 1-5` | Weekdays at 9am local |
| `30 14 15 3 *` | March 15 at 2:30pm local |
Day-of-week uses `0` or `7` for Sunday through `6` for Saturday. Extended syntax like `L`, `W`, `?`, and name aliases such as `MON` or `JAN` is not supported.
When both day-of-month and day-of-week are constrained, a date matches if either field matches. This follows standard vixie-cron semantics.
## Disable scheduled tasks
Set `CLAUDE_CODE_DISABLE_CRON=1` in your environment to disable the scheduler entirely. The cron tools and `/loop` become unavailable, and any already-scheduled tasks stop firing. See [Environment variables](/docs/en/env-vars) for the full list of disable flags.
## Limitations
Session-scoped scheduling has inherent constraints:
* Tasks only fire while Claude Code is running and idle. Closing the terminal or letting the session exit stops them firing. [Backgrounding the session](/docs/en/agent-view#from-inside-a-session) carries `/loop` tasks over to a background session, which keeps running without a terminal.
* No catch-up for missed fires. If a task's scheduled time passes while Claude is busy on a long-running request, it fires once when Claude becomes idle, not once per missed interval.
* Starting a fresh conversation clears all session-scoped tasks. Resuming with `claude --resume` or `claude --continue` restores recurring tasks that have not [expired](#seven-day-expiry) and one-shot tasks whose scheduled time has not yet passed. Background Bash and monitor tasks are never restored on resume.
* Claude Code stores the scheduled task list in the project's `.claude` directory, and scheduling a task fails with an error when that directory, or the task file inside it, is a symlink.
For cron-driven automation that needs to run unattended:
* [Routines](/docs/en/routines): run in the cloud on a schedule, via API call, or on GitHub events
* [GitHub Actions](/docs/en/github-actions): use a `schedule` trigger in CI
* [Desktop scheduled tasks](/docs/en/desktop-scheduled-tasks): run locally on your machine
security First recorded · 144 lines, first recorded
# Security ## How we approach security ### Security foundation ### Permission-based architecture ### Built-in protections ### User responsibility ## Protect against prompt injection ### Core protections ### Privacy safeguards ### Additional safeguards ## MCP security ## IDE security ## Cloud execution security ## Security best practices ### Working with sensitive code ### Team security ### Reporting security issues ## Related resources
The first capture of this source. The page was already there, and this is what it said.
# Security > Learn about Claude Code's security safeguards and best practices for safe usage. ## How we approach security ### Security foundation Your code's security is paramount. Claude Code is built with security at its core, developed according to Anthropic's comprehensive security program. Learn more and access resources (SOC 2 Type 2 report, ISO 27001 certificate, etc.) at [Anthropic Trust Center](https://trust.anthropic.com). ### Permission-based architecture Claude Code uses strict read-only permissions by default. When additional actions are needed (editing files, running tests, executing commands), Claude Code requests explicit permission. Users control whether to approve actions once or allow them automatically. Claude Code requires approval before running Bash commands that can modify your system. A built-in set of [read-only commands](/docs/en/permissions#read-only-commands) such as `ls`, `cat`, and `git status` runs without a prompt. This approach lets users and organizations configure permissions directly. For detailed permission configuration, see [Permissions](/docs/en/permissions). ### Built-in protections To mitigate risks in agentic systems: * **Sandboxed bash tool**: [Sandbox](/docs/en/sandboxing) bash commands with filesystem and network isolation, reducing permission prompts while maintaining security. Configure with `/sandbox` to define boundaries where Claude Code can work autonomously * **Working directory boundary**: Claude Code can only write to the folder where it was started and its subfolders, and cannot modify files in parent directories without explicit permission. Reading paths outside this boundary with the Read, Grep, and Glob tools is possible after an approval prompt. Extend the boundary with [additional directories](/docs/en/permissions#working-directories) to skip the prompt, or restrict the broader read access available to read-only Bash commands with [sandbox `denyRead` rules](/docs/en/sandboxing#filesystem-isolation), which apply only when sandboxing is enabled * **Prompt fatigue mitigation**: Support for allowlisting frequently used safe commands per-user, per-codebase, or per-organization * **Accept Edits mode**: Auto-approves file edits and a fixed set of filesystem Bash commands like `mkdir`, `touch`, `rm`, `mv`, `cp`, and `sed` for paths in the working directory. Other Bash commands and out-of-scope paths still prompt ### User responsibility Claude Code only has the permissions you grant it. You're responsible for reviewing proposed code and commands for safety before approval. ## Protect against prompt injection Prompt injection is a technique where an attacker attempts to override or manipulate an AI assistant's instructions by inserting malicious text. Claude Code includes several safeguards against these attacks: ### Core protections * **Permission system**: Sensitive operations require explicit approval * **Context-aware analysis**: Detects potentially harmful instructions by analyzing the full request * **Input sanitization**: Prevents command injection by processing user inputs * **Network command approval**: Commands that fetch content from the web such as `curl` and `wget` are not auto-approved by default. They prompt like any other non-read-only Bash command, so you can still approve once or add an explicit allow rule like `Bash(curl *)`. To block them entirely, add them to [`permissions.deny`](/docs/en/permissions#tool-specific-permission-rules) ### Privacy safeguards We have implemented several safeguards to protect your data, including: * Limited retention periods for sensitive information (see the [Privacy Center](https://privacy.anthropic.com/en/articles/10023548-how-long-do-you-store-my-data) to learn more) * Restricted access to user session data * User control over data training preferences. Consumer users can change their [privacy settings](https://claude.ai/settings/privacy) at any time. For full details, please review our [Commercial Terms of Service](https://www.anthropic.com/legal/commercial-terms) (for Team, Enterprise, and API users) or [Consumer Terms](https://www.anthropic.com/legal/consumer-terms) (for Free, Pro, and Max users) and [Privacy Policy](https://www.anthropic.com/legal/privacy). ### Additional safeguards * **Network request approval**: Tools that make network requests require user approval by default * **Isolated context windows**: Web fetch uses a separate context window to avoid injecting potentially malicious prompts * **Trust verification**: First-time codebase runs and new MCP servers require trust verification * Note: Trust verification is disabled when running non-interactively with the `-p` flag * Note: When you start Claude Code directly in your home directory, trust acceptance is held for the current session only and is not written to disk, so the prompt reappears on each launch. There is no setting to persist it. Start Claude Code from a project subdirectory instead, where trust acceptance is saved per directory * **Command injection detection**: Suspicious bash commands require manual approval even if previously allowlisted * **Fail-closed matching**: Unmatched commands default to requiring manual approval * **Natural language descriptions**: Complex bash commands include explanations for user understanding * **Secure credential storage**: API keys and tokens are stored in the macOS Keychain when available, and protected by file permissions on Windows and Linux. See [Credential Management](/docs/en/authentication#credential-management) <Warning> **Windows WebDAV security risk**: When running Claude Code on Windows, we recommend against enabling WebDAV or allowing Claude Code to access paths such as `\\*` that may contain WebDAV subdirectories. [WebDAV has been deprecated by Microsoft](https://learn.microsoft.com/en-us/windows/whats-new/deprecated-features#:~:text=The%20Webclient%20\(WebDAV\)%20service%20is%20deprecated) due to security risks. Enabling WebDAV may allow Claude Code to trigger network requests to remote hosts, bypassing the permission system. </Warning> **Best practices for working with untrusted content**: 1. Review suggested commands before approval 2. Avoid piping untrusted content directly to Claude 3. Verify proposed changes to critical files 4. Use virtual machines (VMs) to run scripts and make tool calls, especially when interacting with external web services 5. Report suspicious behavior with `/feedback` <Warning> While these protections significantly reduce risk, no system is completely immune to all attacks. Always maintain good security practices when working with any AI tool. </Warning> ## MCP security Claude Code allows users to configure Model Context Protocol (MCP) servers. The list of allowed MCP servers is configured in your source code, as part of Claude Code settings engineers check into source control. We encourage either writing your own MCP servers or using MCP servers from providers that you trust. You are able to configure Claude Code permissions for MCP servers. Anthropic reviews connectors against its [listing criteria](https://claude.com/docs/connectors/building/review-criteria) before adding them to the [Anthropic Directory](https://claude.ai/directory), but does not security-audit or manage any MCP server. ## IDE security See [VS Code security and privacy](/docs/en/vs-code#security-and-privacy) for more information on running Claude Code in an IDE. ## Cloud execution security When using [Claude Code on the web](/docs/en/claude-code-on-the-web), additional security controls are in place. Sessions your organization routes to a [self-hosted environment](/docs/en/self-hosted-environments) run on your own infrastructure, where isolation, network egress, and git credentials are your deployment's responsibility. In Anthropic-hosted environments: * **Isolated virtual machines**: Each cloud session runs in an isolated, Anthropic-managed VM * **Network access controls**: Network access is limited by default and can be configured to be disabled or allow only specific domains * **Credential protection**: Authentication is handled through a secure proxy that uses a scoped credential inside the sandbox, which is then translated to your actual GitHub authentication token * **Branch restrictions**: Git push operations are restricted to the current working branch * **Audit logging**: All operations in cloud sessions are logged for compliance and audit purposes * **Automatic cleanup**: Session VMs are reclaimed after a period of inactivity For more details on cloud execution, see [Claude Code on the web](/docs/en/claude-code-on-the-web); to configure network access for cloud sessions, see [Configure cloud environments](/docs/en/cloud-environments#network-access). [Remote Control](/docs/en/remote-control) sessions work differently: the web interface connects to a Claude Code process running on your local machine. All code execution and file access stays local, and session traffic travels through the Anthropic API over TLS; while connected, the session transcript is stored on Anthropic servers to sync the conversation across devices, as described in [Connection and security](/docs/en/remote-control#connection-and-security). No cloud VMs or sandboxing are involved. The connection uses multiple short-lived, narrowly scoped credentials, each limited to a specific purpose and expiring independently, to limit the blast radius of any single compromised credential. ## Security best practices ### Working with sensitive code * Review all suggested changes before approval * Use project-specific permission settings for sensitive repositories * Consider using [dev containers](/docs/en/devcontainer) for additional isolation * Regularly audit your permission settings with `/permissions` ### Team security * Use [managed settings](/docs/en/settings#settings-files) to enforce organizational standards * Share approved permission configurations through version control * Train team members on security best practices * Monitor Claude Code usage through [OpenTelemetry metrics](/docs/en/monitoring-usage) * Audit or block settings changes during sessions with [`ConfigChange` hooks](/docs/en/hooks#configchange) ### Reporting security issues If you discover a security vulnerability in Claude Code: 1. Do not disclose it publicly 2. Report it through our [HackerOne program](https://hackerone.com/4f1f16ba-10d3-4d09-9ecc-c721aad90f24/embedded_submissions/new) 3. Include detailed reproduction steps 4. Allow time for us to address the issue before public disclosure ## Related resources * [Security guidance plugin](/docs/en/security-guidance): have Claude review and fix vulnerabilities in its own code changes during the session * [`/security-review`](/docs/en/commands#all-commands): run an on-demand security pass over the changes on your current branch * [Sandbox environments](/docs/en/sandbox-environments): compare isolation approaches and choose one for your threat model * [Sandboxing](/docs/en/sandboxing): filesystem and network isolation for Bash commands * [Permissions](/docs/en/permissions): configure permissions and access controls * [Monitoring usage](/docs/en/monitoring-usage): track and audit Claude Code activity * [Development containers](/docs/en/devcontainer): secure, isolated environments * [Anthropic Trust Center](https://trust.anthropic.com): security certifications and compliance * [CISO's guide to agentic AI](https://claude.com/blog/ciso-guide-to-agentic-ai): a security leader's framework for assessing agentic AI deployments
security-guidance First recorded · 246 lines, first recorded
# Catch security issues as Claude writes code ## Prerequisites ## Install the plugin ### Enable in cloud sessions and shared repositories ## What the plugin checks ### On each file edit ### At the end of each turn ### On each commit or push Claude makes ### Review independence and limits ## Add your own rules ### Add guidance for the model-backed reviews ### Add custom per-edit patterns ### Rule file lookup locations ## Usage cost ## Disable or uninstall ## How the plugin integrates with Claude Code ## How this fits with other security tools ## Troubleshooting ## Related resources
The first capture of this source. The page was already there, and this is what it said.
# Catch security issues as Claude writes code
> Install the security-guidance plugin to have Claude review its own code changes for vulnerabilities and fix them in the same session.
The security guidance plugin makes Claude review its own code changes for common vulnerabilities while it works and fix what it finds in the same session. The plugin catches issues such as injection, unsafe deserialization, and unsafe DOM APIs before the code reaches a pull request, reducing how much security review falls to human reviewers downstream.
Once installed, the plugin runs automatically. There is nothing to invoke and no separate command to remember.
The plugin is the in-session companion to [Code Review](/docs/en/code-review), which runs on pull requests. This plugin reduces what reaches the PR. Code Review catches what does. For how the plugin layers with on-demand review and CI scanning, or to scan code you already have rather than changes Claude is writing, see [How this fits with other security tools](#how-this-fits-with-other-security-tools).
## Prerequisites
* Claude Code CLI version 2.1.144 or later
* Python 3.7 or later on your `PATH`. The agentic commit review needs Python 3.10 or later, as do all model-backed reviews when Claude Code uses a third-party provider such as Amazon Bedrock or Google Cloud's Agent Platform. The plugin prefers the versioned interpreters `python3.13` through `python3.10`, then falls back to `python3`, `python`, and `py -3`
* A git repository for the directory you work in. The end-of-turn and commit reviews diff against git state and skip silently outside a repository. The per-edit pattern check works anywhere
On first run the plugin creates a virtual environment under `~/.claude/security/` and installs the Claude Agent SDK into it, which requires `pip` and network access. If that install fails, or the available Python is older than 3.10, the commit review on first-party authentication falls back to a single-shot review instead of the agentic one; on a third-party provider such as Amazon Bedrock or Google Cloud's Agent Platform the model-backed reviews need the SDK themselves, so they skip. The plugin shows a one-time notice when an older Python is the cause.
## Install the plugin
In a terminal Claude Code session, install from the [official Anthropic marketplace](/docs/en/discover-plugins#official-anthropic-marketplace):
```text theme={null}
/plugin install security-guidance@claude-plugins-official
```
`/plugin` opens an interactive panel and is available only in the terminal CLI. If Claude replies that `/plugin` isn't available in this environment, install another way:
* **Claude desktop app, local or SSH session**: open the [plugin browser](/docs/en/desktop#install-plugins) by clicking the **+** button next to the prompt, then **Plugins**, then **Add plugin**
* **Claude Code on the web or a desktop cloud session**: declare the plugin in `.claude/settings.json` as shown under [Enable in cloud sessions](#enable-in-cloud-sessions-and-shared-repositories)
The terminal install prompts for a scope. Choose user scope to write the plugin to your user settings, so it loads in every new local session you start on this machine.
If the install fails, match the message Claude Code reports:
* `Marketplace "claude-plugins-official" not found`: add the marketplace with `/plugin marketplace add anthropics/claude-plugins-official`, then retry the install.
* The plugin is [not found in the marketplace](/docs/en/discover-plugins#install-plugins): check the plugin name.
Check the install summary. If it reports `Run /reload-plugins to activate.`, apply the pending change without a restart:
```text theme={null}
/reload-plugins
```
### Enable in cloud sessions and shared repositories
User-scoped plugins do not carry into [Claude Code on the web](/docs/en/claude-code-on-the-web), because those sessions run in the cloud rather than on your machine. To enable the plugin there, or to turn it on for everyone who clones a repository, declare it in the project's checked-in settings:
```json .claude/settings.json theme={null}
{
"enabledPlugins": {
"security-guidance@claude-plugins-official": true
}
}
```
Administrators can enable the plugin organization-wide by setting [`enabledPlugins`](/docs/en/settings#plugin-settings) in [managed settings](/docs/en/admin-setup).
## What the plugin checks
The plugin reviews Claude's work at three points, each at a different depth:
* [On each file edit](#on-each-file-edit): a fast pattern match for risky calls, with no model call
* [At the end of each turn](#at-the-end-of-each-turn): a background model review of everything that turn changed
* [On each commit or push Claude makes](#on-each-commit-or-push-claude-makes): a deeper agentic review that reads surrounding code
You can extend each layer by [adding your own rules](#add-your-own-rules). Built-in checks cannot be removed individually, but you can [disable each layer](#disable-or-uninstall) independently.
### On each file edit
When Claude writes to a file, the plugin scans the new content for known risky patterns. This is a pattern match with no model call, so it adds no usage cost.
Example pattern categories:
* Dynamic code execution: `eval(`, `new Function`, `os.system`, `child_process.exec`
* Unsafe deserialization: `pickle`
* DOM injection: `dangerouslySetInnerHTML`, `.innerHTML =`, `document.write`
* Workflow files: edits under `.github/workflows/`, which can grant repository-level permissions
The check runs after the edit lands and appends the warning to Claude's context for the next step. Each warning fires once per pattern per file per session, so repeat matches in the same file do not flood the conversation.
You can [add your own patterns](#add-custom-per-edit-patterns) to this layer with a `security-patterns.yaml` file.
### At the end of each turn
A turn is one round of Claude responding: you send a message, Claude works and replies, and the turn ends. After each turn, the plugin computes a git diff of everything that changed in the working tree during the turn, including changes from Claude's edit tools, Bash commands, and subagents, and sends it to a separate Claude review focused on security. The review runs in the background, so Claude's reply is not delayed. If the review finds issues, Claude is re-prompted with the findings and addresses them as a follow-up.
This catches issues a string match cannot, such as:
* Authorization bypass
* Insecure direct object references
* Injection
* Server-side request forgery
* Weak cryptography
You see both the finding and Claude's resolution directly in your session. The review covers up to 30 changed files per turn and fires at most three times in a row before yielding back to you.
### On each commit or push Claude makes
When Claude runs `git commit` or `git push` through its Bash tool, the plugin runs a deeper agentic review of the change in the background. This review reads surrounding code, including callers, sanitizers, and related files, to decide whether a finding is real before reporting it. The extra context keeps false positives low on patterns that look dangerous in isolation but are safe in your codebase.
This layer fires only on commits and pushes Claude makes through its Bash tool. Commits you run from your own shell, including the `!` shell escape inside a session, are not reviewed. Commit and push reviews are capped at 20 per rolling hour. If the commit review's findings duplicate what the end-of-turn review already reported, Claude is not re-prompted, so a clean commit produces no visible output from this layer.
### Review independence and limits
The plugin does not ask the same Claude instance that wrote the code to grade itself. The per-edit check is a deterministic string match with no model involved. The end-of-turn and commit reviews run as a separate Claude call with a fresh context and a security-focused prompt: the reviewer starts from the diff, has no investment in the original approach, and is instructed only to find problems.
None of the layers block writes or commits. Findings reach the writing Claude as instructions, Claude addresses them in the conversation, and the review model can miss issues. Treat the plugin as one layer of defense in depth, not a complete security solution. See [How this fits with other security tools](#how-this-fits-with-other-security-tools).
## Add your own rules
The plugin has two extension points: a Markdown guidance file for the model-backed reviews, and a YAML or JSON patterns file for the per-edit string match. Both are additive. You can add checks but cannot disable built-in ones from these files.
### Add guidance for the model-backed reviews
Create `.claude/claude-security-guidance.md` in your project and describe your threat model and review checklist in plain language. The model-backed reviews load it as additional context alongside the built-in vulnerability checklist.
The following example is for a web service with role-gated admin routes and a customer-data logging policy:
```markdown .claude/claude-security-guidance.md theme={null}
# Security guidance for this repo
- Do not log `customer_id` or `account_number` at INFO level or above.
- All routes under `/admin` must call `require_role("admin")` before any database read.
- Use `crypto.timingSafeEqual` for token comparison instead of `===`.
```
These rules are guidance for the reviewer, not deterministic guardrails. The plugin surfaces violations as findings for Claude to fix, but it does not block writes or guarantee every violation is caught. The guidance is additive only: a rule that says to ignore a vulnerability class does not suppress those findings. For hard enforcement, pair the plugin with a [hook that blocks the edit](/docs/en/hooks-guide#block-edits-to-protected-files) or a CI check.
### Add custom per-edit patterns
Create `.claude/security-patterns.yaml` to add regex or substring rules to the [per-edit pattern check](#on-each-file-edit). These run as deterministic string matches alongside the built-in patterns:
```yaml .claude/security-patterns.yaml theme={null}
patterns:
- rule_name: internal_api_key
substrings: ["sk_live_", "AKIA"]
reminder: "Hardcoded API key prefix. Load credentials from the secret manager."
- rule_name: tenant_unfiltered_query
regex: "\\.objects\\.all\\(\\)"
paths: ["**/src/tenants/**"]
reminder: "Multi-tenant code must filter by org_id."
```
| Field | Type | Description |
| :-------------- | :----- | :------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `rule_name` | string | Identifier shown in the warning |
| `reminder` | string | Warning text appended to Claude's context, capped at 1 KB |
| `regex` | string | Python regex matched against the edited content |
| `substrings` | list | Literal substrings; provide this or `regex` |
| `paths` | list | Optional glob patterns; the rule applies only to matching files. Globs match against the full file path, so prefix project-relative patterns with `**/` |
| `exclude_paths` | list | Optional glob patterns to skip; same matching as `paths` |
The plugin also reads `.claude/security-patterns.yml` and `.claude/security-patterns.json` with the same schema. JSON works on any Python install. The YAML forms require PyYAML to be importable, which the plugin does not install for you. The plugin loads up to 50 custom rules and skips regexes that look prone to catastrophic backtracking.
### Rule file lookup locations
The plugin looks for `claude-security-guidance.md` and `security-patterns.yaml` in the same locations, independently of how the plugin was enabled:
| Scope | Path | Notes |
| :------------ | :------------------------------------------ | :-------------------------------------------------- |
| User | `~/.claude/claude-security-guidance.md` | Applies to every project on your machine |
| Project | `.claude/claude-security-guidance.md` | Checked in with the repository |
| Project local | `.claude/claude-security-guidance.local.md` | For personal overrides; add it to your `.gitignore` |
The plugin loads all locations that exist and concatenates them, with a combined cap of 8 KB for the guidance file. Administrators can distribute organization-wide rules by pushing the user-scope file to `~/.claude/` through device management. The same paths apply to `security-patterns.yaml`.
## Usage cost
The [per-edit pattern check](#on-each-file-edit) makes no model call and adds no cost. The [end-of-turn](#at-the-end-of-each-turn) and [commit](#on-each-commit-or-push-claude-makes) reviews each spend additional model usage that counts toward your [usage](/docs/en/costs) like any other Claude request. The commit review is agentic and may take several model turns per commit. Expect roughly one review call per turn that changes files and one deeper review per commit, both subject to the caps above.
Both model-backed reviews use Claude Opus 4.7 by default. Set `SECURITY_REVIEW_MODEL` to choose a different model for the end-of-turn review and `SG_AGENTIC_MODEL` for the commit review.
The plugin is available on all plans.
## Disable or uninstall
To turn off individual layers while keeping the rest, set the matching environment variable:
| Variable | Effect |
| :------------------------------ | :------------------------------------------------------------------------- |
| `ENABLE_PATTERN_RULES=0` | Disable the [per-edit pattern check](#on-each-file-edit) |
| `ENABLE_STOP_REVIEW=0` | Disable the [end-of-turn diff review](#at-the-end-of-each-turn) |
| `ENABLE_COMMIT_REVIEW=0` | Disable the [commit and push review](#on-each-commit-or-push-claude-makes) |
| `ENABLE_CODE_SECURITY_REVIEW=0` | Disable all model-backed reviews at once |
| `SECURITY_GUIDANCE_DISABLE=1` | Disable the plugin entirely without uninstalling |
To pause the plugin in your user scope:
```text theme={null}
/plugin disable security-guidance@claude-plugins-official
```
To remove it from your user scope:
```text theme={null}
/plugin uninstall security-guidance@claude-plugins-official
```
If the plugin was enabled through a project's `.claude/settings.json`, disabling it from `/plugin` writes an override to your `.claude/settings.local.json` rather than editing the checked-in file, so the plugin stays off for you while teammates are unaffected. The same dialog also offers to uninstall the plugin for everyone by removing it from the shared `.claude/settings.json`. If it was enabled through [managed settings](/docs/en/admin-setup), only an administrator can disable it.
## How the plugin integrates with Claude Code
The plugin is built entirely on [hooks](/docs/en/hooks), the mechanism for running your own code at specific points in Claude's loop. It registers:
| Hook event | Purpose |
| :--------------------------------------------------------------- | :-------------------------------------------------------------------------- |
| `SessionStart` | Bootstrap the plugin's Python environment |
| `UserPromptSubmit` | Capture the working-tree baseline that the end-of-turn review diffs against |
| `PostToolUse` on `Edit`, `Write`, and `NotebookEdit` | Per-edit pattern match |
| `Stop` | End-of-turn diff review, run in the background |
| `PostToolUse` on `Bash`, filtered to `git commit` and `git push` | Commit and push review, run in the background |
If you build your own hooks, the [plugin's source](https://github.com/anthropics/claude-plugins-official/tree/main/plugins/security-guidance) is a working example of running a separate model call from a hook and feeding the result back to the session.
## How this fits with other security tools
The plugin is one layer in a defense-in-depth approach. It catches issues earliest, while code is still in the editor, but it is not a guarantee and does not replace later checks. A typical stack:
| Stage | Tool | What it covers |
| :--------------------- | :-------------------------------------------------------- | :------------------------------------------------------------------------------------------------------- |
| In session | Security guidance plugin | Common vulnerabilities in code Claude writes, fixed in the same session |
| On demand, single pass | [`/security-review`](/docs/en/commands#all-commands) | One-time security pass on the current branch, run when you ask |
| On demand, deep scan | [Claude Security plugin](/docs/en/claude-security) | Multi-agent vulnerability scan of a repository or diff, with independently reviewed findings and patches |
| On pull request | [Code Review](/docs/en/code-review), Team and Enterprise plans | Multi-agent correctness and security review with full codebase context |
| In CI | Your existing static analysis and dependency scanners | Language-specific rules, supply-chain checks, and policy enforcement the plugin does not attempt |
To find security issues in code you already have, rather than in changes Claude is writing, ask Claude in a session to review a specific file or directory for vulnerabilities, or use the [Claude Security plugin](/docs/en/claude-security) for a deeper multi-agent scan of the whole repository; [`/security-review`](/docs/en/commands#all-commands) covers only the changes on your current branch. Either way, the review reads the source code in your checkout, not a running site or deployed service.
## Troubleshooting
The plugin writes runtime diagnostics to `~/.claude/security/log.txt`. Check there first if reviews are not appearing.
Common reasons a review layer skips without a message in the conversation:
* The directory is not a git repository: the end-of-turn and commit reviews require git state and skip outside a repository
* The session has no Anthropic authentication and no third-party provider configured: the model-backed reviews skip and only the per-edit pattern check runs
* A `security-patterns.yaml` file is present but PyYAML is not importable: the file is ignored. Use `security-patterns.json` instead
## Related resources
To go deeper on the pieces this page touches:
* [Code Review](/docs/en/code-review): set up the PR-time multi-agent review
* [Automate actions with hooks](/docs/en/hooks-guide): build your own checks at the same lifecycle points
* [Discover and install plugins](/docs/en/discover-plugins#official-anthropic-marketplace): browse other official plugins
self-hosted-environments First recorded · 131 lines, first recorded
# Self-hosted environments ## How self-hosted environments work ## Availability and limitations ## Why self-host ## Environments, runners, and sessions ### Key concepts ### Session lifecycle ### Runner lifecycle ### Network paths ## What stays on your infrastructure ## Get started
The first capture of this source. The page was already there, and this is what it said.
# Self-hosted environments
> Run Claude Code cloud sessions on infrastructure you control: set up a self-hosted environment, deploy runners, and route sessions to your own compute.
<Note>
Self-hosted environments are in public beta on Team and Enterprise plans and are off by default. See [Availability and limitations](#availability-and-limitations) for the enablement path and what's excluded.
</Note>
A self-hosted environment executes Claude Code cloud sessions on infrastructure your organization operates. A [cloud session](/docs/en/claude-code-on-the-web) is any session that runs somewhere other than the developer's machine: developers start them from claude.ai, the mobile and desktop apps, the terminal with [`claude --cloud`](/docs/en/claude-code-on-the-web#from-terminal-to-web), and [scheduled routines](/docs/en/routines), and by default they execute on Anthropic's infrastructure. In a self-hosted environment, those same sessions execute inside your network, and the developer experience is otherwise the same apart from the differences in [Availability and limitations](#availability-and-limitations) and the deploy page's [known issues](/docs/en/self-hosted-environments-deploy#known-issues-and-limitations).
If your team doesn't use cloud sessions, there's nothing here to configure: sessions in a terminal or IDE always run on the developer's own machine. If you want to run Claude Code on your own always-on machine and drive it from other devices, use [Remote Control](/docs/en/remote-control), which is also available on Pro and Max plans. When you're ready to set up, go straight to the [quickstart](/docs/en/self-hosted-environments-quickstart); to review the security posture first, start with [Deploy to production](/docs/en/self-hosted-environments-deploy). The rest of this page explains how self-hosting works and when to choose it.
## How self-hosted environments work
Self-hosting has three parts:
* **Environment**: a named destination that cloud sessions can be sent to. Your organization creates environments in claude.ai admin settings, and each one groups a set of runners.
* **Runner**: a program running on hosts inside your network. Runners execute the sessions; the idea is the same as a self-hosted CI runner.
* **Session**: one Claude Code task a developer started.
When a developer starts a cloud session, the session-start UI shows an environment picker listing Anthropic-hosted environments alongside any your organization has created. If they choose yours, Anthropic's control plane places the session on your environment's queue, where a runner claims it, clones the repository the developer chose, and starts a Claude Code process on your host to run it. The runner authenticates to your git host with credentials you configure; [Configure git](/docs/en/self-hosted-environments-deploy#configure-git) covers the options. Sessions reach your internal services from inside your network, and your git host the same way when it's internal; the traffic to Anthropic, queue polling, the session's event stream, and model inference, is outbound HTTPS to `api.anthropic.com`, with the short list of further hosts sessions can reach in [Network requirements](/docs/en/self-hosted-environments-deploy#network-requirements). Anthropic never connects into your network.
<div style={{maxWidth: "640px", margin: "0 auto"}}>
<Frame>
<img src="https://mintcdn.com/claude-code/Y0sJ2uDoOVbOVZrQ/images/self-hosted-network-paths.svg?fit=max&auto=format&n=Y0sJ2uDoOVbOVZrQ&q=85&s=8056103fc1c5564c7f0ef219d260b99d" className="dark:hidden" alt="Architecture diagram of a self-hosted environment: your network boundary contains a runner, two Claude Code session processes inside it, and your git host, with api.anthropic.com outside holding queue, session stream, and inference. The runner polls the queue and reaches the git host, each session process opens its own stream, inference, and git connections, and every connection is outbound from your network, with none inbound." width="680" height="320" data-path="images/self-hosted-network-paths.svg" />
<img src="https://mintcdn.com/claude-code/Y0sJ2uDoOVbOVZrQ/images/self-hosted-network-paths-dark.svg?fit=max&auto=format&n=Y0sJ2uDoOVbOVZrQ&q=85&s=fec6aef3b0740d80eaf6d6a7000a2233" className="hidden dark:block" alt="Architecture diagram of a self-hosted environment: your network boundary contains a runner, two Claude Code session processes inside it, and your git host, with api.anthropic.com outside holding queue, session stream, and inference. The runner polls the queue and reaches the git host, each session process opens its own stream, inference, and git connections, and every connection is outbound from your network, with none inbound." width="680" height="320" data-path="images/self-hosted-network-paths-dark.svg" />
</Frame>
</div>
The two Claude Code boxes in the diagram are session processes: one runner executing two sessions at once, up to its configured capacity. A runner serves one user at a time, locking to that user's account when it claims its first session, so checked-out code never mixes between users; [Runner lifecycle](#runner-lifecycle) covers the rule.
You can start runners yourself and keep them running, or run the [autoscaling orchestrator](/docs/en/self-hosted-environments-configuration#on-demand-runners), a second process you host, which starts runners as sessions queue; each runner exits on its own when its work finishes. Either way, you set the environment up once, and it appears in the picker on every supported surface.
## Availability and limitations
Check these before planning a rollout:
* **Plans**: public beta for Team and Enterprise organizations. Self-hosted environments are off by default; an [Owner or admin](/docs/en/cloud-environments#organization-shared-environments) turns on **Allow self-hosted environments** on the [**Cloud environments** admin page](https://claude.ai/admin-settings/cloud-environments), which requires [Claude Code on the web](/docs/en/claude-code-on-the-web) to be enabled for the organization.
* **Zero Data Retention**: unavailable for organizations with [Zero Data Retention](/docs/en/zero-data-retention) enabled.
* **Model inference**: sessions use the Anthropic API, and inference can't be routed through [Amazon Bedrock, Google Cloud's Agent Platform, Microsoft Foundry](/docs/en/third-party-integrations), or an [LLM gateway](/docs/en/llm-gateway).
* **Surfaces**: sessions started from [Claude Code on the web](/docs/en/claude-code-on-the-web), the mobile and desktop apps, [scheduled routines](/docs/en/routines), and the terminal, with [`claude --cloud`](/docs/en/claude-code-on-the-web#from-terminal-to-web) or an [`--environment` dispatch](/docs/en/self-hosted-environments-testing#run-the-test-loop), can run in self-hosted environments. [Claude Tag](https://claude.com/docs/claude-tag/overview), [Claude Security](/docs/en/claude-security), and [Code Review](/docs/en/code-review) sessions don't route to them yet. Support for those surfaces follows separately.
* **Repositories**: sessions check out repositories from GitHub; see [GitHub authentication options](/docs/en/claude-code-on-the-web#github-authentication-options).
* **Billing**: sessions in a self-hosted environment consume your organization's Claude Code usage the same way sessions in Anthropic-hosted environments do.
## Why self-host
Most teams are better served by Anthropic-hosted environments, which need no infrastructure to run or maintain. Self-hosting is for teams whose network, tooling, or compliance requirements call for keeping session execution on infrastructure they control. If that's you, plan for the operational ownership it carries: you build and maintain the runner image, operate the fleet, and control its network.
In exchange, self-hosting gives you network access, custom tooling, and compliance control:
* **Network access**: sessions run inside your network and can reach internal services, databases, and registries without exposing them to the public internet
* **Custom tooling**: pre-install compilers, SDKs, and internal CLIs in your runner image so every session starts ready to build
* **Compliance**: repository checkouts and build artifacts stay on infrastructure you control. Session content still goes to `api.anthropic.com` for model inference.
## Environments, runners, and sessions
Environments are managed on the **Cloud environments** page in claude.ai admin settings; runners are processes you start and manage on your own infrastructure.
### Key concepts
These terms appear throughout the self-hosted pages:
| Term | What it is |
| :----------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| Environment | A named group of your runners, created in claude.ai settings. Sessions are routed to an environment, not to an individual runner. |
| Environment secret | The single shared credential runners use to authenticate and register with the environment. Shown once at environment creation, labeled **environment key** in the admin UI. |
| Runner | The long-lived process you deploy. A runner registers with the environment, receives a runner token, and polls for sessions. |
| Session | One Claude Code task, started from claude.ai, the mobile app, or another Anthropic surface such as a scheduled routine or an agent. Each session runs as a child Claude Code process the runner spawns. |
In API fields, token claims, and metric names, the environment appears as `pool`, and the environment ID is the `pool_id`. The [reference](/docs/en/self-hosted-environments-reference) maps the two spellings, including the deprecated `pool` flag names.
A runner serves one user at a time. The first session a runner picks up locks the runner to that user, and the runner then runs sessions only for that user, up to a configured capacity. The minimum fleet size is therefore the number of users you expect to be active at once.
### Session lifecycle
When a developer starts a session and selects your environment, Anthropic's control plane places the session on the environment's queue. From there:
1. A runner with free capacity claims the session and holds a lease on it.
2. The runner clones the repository into its working directory and spawns a child Claude Code process.
3. The child streams events back over HTTPS while the runner keeps polling; each poll refreshes the lease and doubles as the heartbeat.
4. If the runner stops polling for about 60 seconds, the server requeues the session for another runner.
### Runner lifecycle
The first session a runner picks up locks the runner to the account of the user who started that session, and the runner runs up to `--capacity` concurrent sessions for that account. While the runner has active sessions, the runner keeps claiming the locked account's queued work. What happens once they finish depends on [`--drain-grace-sec`](/docs/en/self-hosted-environments-reference#runner-cli-flags):
* **At the default of `0`**: the runner exits as soon as its active sessions finish, without polling for more, so the orchestrator you deploy it under, such as Kubernetes, can restart it with a fresh disk, ready to serve any account.
* **At a positive value**: the runner keeps polling the locked account's queue for that many seconds before exiting.
This lifecycle isolates each user's checked-out code without requiring the runner to delete disk state between users.
A kill that delivers `SIGTERM` needs no flag: the runner drains as [Shutdown timing](/docs/en/self-hosted-environments-deploy#shutdown-timing) describes. If your infrastructure instead destroys hosts at a known wall-clock time without a signal, or with a grace period too short to drain, such as a sandbox lifetime cap or spot-instance reclamation, pass `--retire-at <epoch-seconds>` set to a few minutes before that time. At the retire time:
1. The runner stops taking new work.
2. The runner releases each active session through the same release path the [`--release-idle-session-min`](/docs/en/self-hosted-environments-reference#runner-cli-flags) flag uses, so the session resumes on a fresh runner when the user sends their next message. When the runner releases each session depends on its state:
* The runner releases a session that's mid-turn as soon as that turn finishes.
* When a turn finishes and leaves background tasks running, the runner waits up to 60 seconds for them, then releases the session even if they're still running. If the tasks have finished but the follow-up turn that reads their results hasn't run yet, the runner keeps the session until that turn finishes, and waits no longer than [`SELF_HOSTED_RUNNER_BG_RESULT_GRACE_MS`](/docs/en/self-hosted-environments-reference#environment-variable-only-settings) for that turn to start.
3. The runner exits 0 once all its sessions are released.
A turn that outlives the kill is still lost; [Shutdown timing](/docs/en/self-hosted-environments-deploy#shutdown-timing) covers sizing the margin. Without `--retire-at`, a signal-less host kill is indistinguishable from a crash: the control plane records a lost worker rather than a clean release, and the session requeues to another runner.
### Network paths
The runner and its sessions make several kinds of outbound connection, and no inbound connectivity from Anthropic is required:
* **Control plane**: the runner polls `api.anthropic.com` for work and posts setup-progress and failure events, all outbound HTTPS. Polling doubles as the runner's heartbeat.
* **SCM connector**: the optional orchestrator [SCM connector](/docs/en/self-hosted-environments-reference#scm-connector-flags) tunnel is the only WebSocket connection.
* **Git**: the runner clones from and pushes to your git host over HTTPS or SSH, authenticated with credentials your deployment provides; [Configure git](/docs/en/self-hosted-environments-deploy#configure-git) covers the options, including per-session minted credentials and the [Anthropic git proxy](/docs/en/self-hosted-environments-deploy#use-the-anthropic-git-proxy), which routes git through `api.anthropic.com` instead.
* **Session child**: the child Claude Code process holds the session's event stream to `api.anthropic.com`, and makes its own outbound calls for model inference and for git commands run during the session. See [Network requirements](/docs/en/self-hosted-environments-deploy#network-requirements) for the full egress list. The [diagram above](#how-self-hosted-environments-work) shows these paths, apart from the optional SCM connector.
Model inference uses the Anthropic API. The control plane delivers the API endpoint to each session, and the session authenticates with an Anthropic-issued, session-scoped OAuth token, so inference can't be routed through [Amazon Bedrock, Google Cloud's Agent Platform, Microsoft Foundry](/docs/en/third-party-integrations), or an [LLM gateway](/docs/en/llm-gateway) in self-hosted environments.
Corporate egress proxies are supported. The runner and the optional [autoscaling orchestrator](/docs/en/self-hosted-environments-configuration#on-demand-runners) honor the proxy and mTLS environment variables described in [Network configuration](/docs/en/network-config), such as `HTTPS_PROXY` and `NO_PROXY`; set them in each process's environment. The variables cover control-plane calls, the orchestrator's [SCM connector](/docs/en/self-hosted-environments-reference#scm-connector-flags) WebSocket, and the built-in clone for HTTPS remotes, and sessions inherit them from the runner. Session streaming uses server-sent events over HTTPS, so a proxy in the path must not buffer responses.
## What stays on your infrastructure
Repository checkouts, build artifacts, secrets, and any files a session creates or modifies stay on the machines you provision. The conversation itself, including prompts, responses, and tool results, goes to `api.anthropic.com` for model inference, and Anthropic stores the session transcript so you can resume the session from another [supported surface](#availability-and-limitations).
A self-hosted environment moves session execution into your network. The control plane remains Anthropic-hosted: session orchestration, queueing, and the claude.ai interface continue to run on Anthropic's infrastructure.
## Get started
The self-hosted environments pages are organized by what you're doing:
* [Quickstart](/docs/en/self-hosted-environments-quickstart): install Claude Code, create an environment, start a runner, and route your first session
* [Deploy to production](/docs/en/self-hosted-environments-deploy): security hardening, network egress, git credentials, Kubernetes and Compose recipes, known issues, and troubleshooting
* [Customize sessions](/docs/en/self-hosted-environments-configuration): wrapper scripts for per-session credentials, lifecycle hooks, on-demand runners, MCP servers, and permissions
* [Test end to end](/docs/en/self-hosted-environments-testing): a CI smoke test that verifies a runner image before you promote it
* [Reference](/docs/en/self-hosted-environments-reference): every CLI flag, environment variable, metric, and the health endpoint
* [Verify session identity](/docs/en/self-hosted-environments-identity): validate the session token from your own services before granting access
self-hosted-environments-configuration First recorded · 387 lines, first recorded
# Customize sessions in self-hosted environments ## Wrapper scripts ### Keep stdin and file descriptor 3 attached ### Provision credentials scoped to the session creator ## Lifecycle hooks ### checkout ### post-session ### command ## On-demand runners ### The spawn-runner hook ## MCP servers ## Prompt sessions to push their work ## Permissions and tool approval ### How each session's config is assembled ### Repository-committed permission rules ## What's next
The first capture of this source. The page was already there, and this is what it said.
# Customize sessions in self-hosted environments
> Customize self-hosted environment sessions with wrapper scripts for per-session credentials, lifecycle hooks, and on-demand runner spawning.
<Note>
Self-hosted environments are in public beta on Team and Enterprise plans; an [Owner or admin](/docs/en/cloud-environments#organization-shared-environments) enables them by turning on **Allow self-hosted environments** on the [**Cloud environments** admin page](https://claude.ai/admin-settings/cloud-environments). This page assumes a working runner; see the [quickstart](/docs/en/self-hosted-environments-quickstart) for setup and [Deploy to production](/docs/en/self-hosted-environments-deploy) for the fleet recipes.
</Note>
A [self-hosted environment](/docs/en/self-hosted-environments) runs Claude Code [cloud sessions](/docs/en/claude-code-on-the-web) on your own infrastructure, executed by a runner process you deploy. With no configuration, that runner clones the session's repository, spawns Claude Code, and cleans up. This page is for the platform engineer operating the runners: it covers the extension points for when those defaults don't fit, from per-session credential provisioning to replacing checkout entirely. Wrappers and hooks run as executable files on the runner host, which is Linux or macOS, and the examples on this page assume a POSIX shell.
A few hook environment variables on this page still use `pool`, such as `CLAUDE_RUNNER_POOL_ID`; the CLI flag and env var names use `environment`, such as `--environment-secret-file`.
## Wrapper scripts
Use a wrapper script when each session needs setup the runner can't do on its own: provisioning short-lived credentials scoped to the session creator, exporting environment-specific secrets, preparing language toolchains, or applying resource limits around the child process. The runner starts your wrapper in place of the Claude Code binary, once per session. End the wrapper by `exec`-ing into `$CLAUDE_RUNNER_CLAUDE_BIN`, the runner's own binary, so signals and exit codes propagate correctly.
Point `--exec-path`, or `SELF_HOSTED_RUNNER_EXEC_PATH`, at the wrapper when you start the runner:
```bash theme={null}
claude self-hosted-runner --environment-secret-file /etc/claude/environment-secret --exec-path /etc/claude/session-wrapper.sh
```
The runner sets the following in the wrapper's environment:
| Variable | Description |
| :---------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `CLAUDE_CODE_SESSION_ACCESS_TOKEN` | The session JWT, prefixed `sk-ant-cc-`. Its `act` claim identifies the session creator, with the creator's email and upstream identity-provider subject when the creating surface recorded them. The value is the token at spawn time; refreshes arrive over the child's stdin, so a wrapper sees only the initial value. See [Verify session identity](/docs/en/self-hosted-environments-identity). |
| `CCR_SESSION_ACCOUNT_EMAIL` | The session creator's email, pre-extracted by the runner from the token's `act.email` claim without signature verification. Suitable for labelling, such as commit trailers. When the email gates credential issuance, verify the token and read the claim from it instead; see [Provision credentials scoped to the session creator](#provision-credentials-scoped-to-the-session-creator). Unset when the token carries no creator email. Treat as personally identifiable information. |
| `CLAUDE_RUNNER_CLIENT_PLATFORM` | The client surface that created the session, such as `web_claude_ai`, `desktop_app`, `ios`, `claude_code_cli`, or `scheduled_trigger`. Anthropic records the value once at session creation, so the wrapper and every lifecycle hook see the same value. Use it for adoption analytics and labelling only, not as an authorization signal. Unset when the session has no recorded or recognized surface, so reference it as `${CLAUDE_RUNNER_CLIENT_PLATFORM:-}` under `set -u`. Requires Claude Code v2.1.229 or later. |
| `CLAUDE_RUNNER_CLAUDE_BIN` | Absolute path to the runner's own Claude Code binary. End your wrapper with `exec "$CLAUDE_RUNNER_CLAUDE_BIN" "$@"` to hand off to the pinned binary without hardcoding an install path. |
| `CLAUDE_CODE_REMOTE_SESSION_ID` | Session ID in the tagged `cse_...` form. This is the same session the [lifecycle hooks](#lifecycle-hooks) see as `CLAUDE_RUNNER_SESSION_ID` in `session_...` form; the UUID variables match across both, and substituting the `cse_` prefix with `session_` yields the ID shown in the session URL. |
| `CLAUDE_CODE_REMOTE_SESSION_UUID` | The same session ID in canonical UUID form, for systems that key on UUIDs. |
| `CLAUDE_SESSION_INGRESS_TOKEN_FILE` | Absolute path to a per-session file holding the current session JWT, kept fresh across token refreshes. Shell subprocesses read it for their `Authorization` header when downloading attachments the user added to the session. `exec` preserves the variable automatically; a wrapper that rebuilds the child's environment must carry the variable over, or attachment downloads silently stop working. |
| `CLAUDE_CONFIG_DIR` | Per-session Claude config directory, written at session start from the snapshot of the runner host's config that the runner captures at startup; see [Permissions and tool approval](#permissions-and-tool-approval). Writes here are isolated to this session. |
| `ANTHROPIC_BASE_URL` | The API base URL the child will use, delivered by the control plane per session and normally `https://api.anthropic.com`. Don't override it: the session's inference credential is an Anthropic-issued OAuth token that other providers don't accept, so inference in self-hosted environments isn't routable elsewhere. |
| `CLAUDE_CODE_OAUTH_TOKEN` | The short-lived OAuth access token the child uses for model inference, scoped to model inference and file upload only, with a lifetime of about 30 minutes. The runner re-mints it before expiry and delivers the rotation over the child's stdin, so a wrapper that doesn't [keep stdin attached](#keep-stdin-and-file-descriptor-3-attached) sees only the initial value. Don't rely on your organization's IP allowlist to bound this token's use: treat it as a bearer credential that stays usable for roughly 30 minutes if it leaks, and don't log it, write it to disk, or forward it outside the session container. |
The wrapper also inherits the rest of the child's managed environment, including any server-provided environment variables. `exec` propagates all of it automatically; if your wrapper spawns the child another way, forward the full environment.
### Keep stdin and file descriptor 3 attached
The child's stdin is the runner's control channel. Token rotations and session-end signals arrive on it. The runner also opens a pipe on file descriptor 3 and reads the child's activity signals from it to drive idle and startup timeouts. A plain `exec "$CLAUDE_RUNNER_CLAUDE_BIN" "$@"` preserves both automatically.
If your wrapper backgrounds the child with a bare `&`, it severs the child's stdin: the session looks healthy until the initial OAuth token's roughly 30-minute lifetime expires, then every API call fails with `401 authentication_error`. If your wrapper must background the child, for example to keep a teardown trap alive, save stdin on file descriptor 4 or higher and re-attach it explicitly:
```bash theme={null}
exec 4<&0
"$CLAUDE_RUNNER_CLAUDE_BIN" "$@" <&4 4<&- &
CHILD=$!
trap 'teardown' EXIT
wait "$CHILD"
```
Don't close or reuse file descriptor 3 in the wrapper. Redirecting the child's stdout and stderr is fine.
### Provision credentials scoped to the session creator
Use the `decode-token` subcommand to read claims from the session JWT. It reads the token from an argument, from `CLAUDE_CODE_SESSION_ACCESS_TOKEN`, or from stdin, in that order; see [Verify the token inside the session](/docs/en/self-hosted-environments-identity#verify-the-token-inside-the-session) for what it checks. The example below decodes the creator identity, exchanges it for short-lived AWS credentials, and execs into Claude Code:
```bash theme={null}
#!/bin/bash
# Key on the stable Anthropic user ID and require a human creator.
CREATOR_SUB=$("$CLAUDE_RUNNER_CLAUDE_BIN" self-hosted-runner decode-token \
| jq -re '.act.sub // "" | select(startswith("user:"))') \
|| { echo "decode-token: verification failed or no human creator" >&2; exit 1; }
creds=$(your-sts-helper assume-role --subject "$CREATOR_SUB") \
|| { echo "credential exchange failed" >&2; exit 1; }
eval "$creds"
exec "$CLAUDE_RUNNER_CLAUDE_BIN" "$@"
```
Use `jq -re` rather than `jq -r` when the extracted claim gates an auth decision, so an absent claim exits non-zero instead of passing the literal string `null` downstream. Sessions created by an organization service identity, such as bot and agent sessions, carry an `agent:` subject rather than `user:`, so this example refuses them; if your environment serves those sessions, decide explicitly whether the wrapper falls back to a default credential for them instead of exiting. When your credential exchange needs the SSO subject or email instead, read `.act.attested_by.sub` or `.act.email` and handle their absence: the token carries them only when the creating surface recorded them, and a [CLI-dispatched session](/docs/en/self-hosted-environments-testing#run-the-test-loop) can lack both. For the full claim reference and verification from services outside the runner, see [Verify session identity](/docs/en/self-hosted-environments-identity).
## Lifecycle hooks
Lifecycle hooks replace stages of the runner's per-session pipeline with your own scripts. Point the runner at a directory of hooks with `--hooks-dir <path>`, or `SELF_HOSTED_RUNNER_HOOKS_DIR`. The runner looks for executable files with well-known names; any hook that isn't present falls through to the built-in behavior, so you only write the ones you need. Hooks run with the runner's own privileges, and session children share that UID, so mount the hooks directory read-only, or bake it into the image, so session code can't modify it; see the [hardening section](/docs/en/self-hosted-environments-deploy#harden-your-deployment).
These hooks are distinct from [Claude Code hooks](/docs/en/hooks), which run inside the session; lifecycle hooks run on the runner, around the session.
### checkout
Runs once per repository, in place of the runner's built-in clone and fetch. Use the hook to clone from a read-through mirror, seed a working tree from an archive, or apply per-session git auth. The runner sets:
| Variable | Description |
| :--------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `CLAUDE_RUNNER_REPO_URL` | Repository URL to clone, after any `--git-host-rewrite` and `--git-ssh-rewrite` have been applied |
| `CLAUDE_RUNNER_REPO_REF` | Revision to check out: branch, tag, or commit SHA as the session requested it. Empty means the repository's default branch. |
| `CLAUDE_RUNNER_CHECKOUT_PATH` | Absolute path where the working tree must be left |
| `CLAUDE_RUNNER_SESSION_ID` | Session ID in the tagged `session_...` form, for logging and correlation |
| `CLAUDE_RUNNER_SESSION_UUID` | The same session ID in canonical UUID form |
| `CLAUDE_RUNNER_API_BASE_URL` | Anthropic API base URL for session-scoped calls |
| `CLAUDE_RUNNER_CLIENT_PLATFORM` | The client surface that created the session, such as `web_claude_ai`, `desktop_app`, or `ios`. Unset when the session has no recorded or recognized surface. |
| `CLAUDE_CODE_SESSION_ACCESS_TOKEN` | The session access token, for session-scoped API calls |
The script must leave a working tree at `CLAUDE_RUNNER_CHECKOUT_PATH` checked out at the requested revision. Detached HEAD is fine; the runner creates the session's working branch on top. The runner verifies the path contains a `.git` afterwards; if your hook materializes a non-git source such as Perforce or an unpacked tarball, set `CLAUDE_RUNNER_SKIP_GIT_VERIFY=1` in the runner's environment to skip that check. Git-based flows such as working-branch creation and pushing results require a git checkout, so export outcomes from non-git trees with a [`post-session` hook](#post-session).
The runner doesn't pass a git credential to the hook. Instead, mint a per-session clone credential from the session's identity: verify `CLAUDE_CODE_SESSION_ACCESS_TOKEN` with a standard JWT library against the JWKS endpoint under `CLAUDE_RUNNER_API_BASE_URL`, as described in [Verify the token from your service](/docs/en/self-hosted-environments-identity#verify-the-token-from-your-service), then have your credential service issue a short-lived clone credential for the identity in the token's `act` claim. `CLAUDE_RUNNER_CLAUDE_BIN` isn't set in the checkout-hook environment, so the `decode-token` subcommand isn't available here. Falling back to whatever git authentication the host already has, such as an SSH agent, credential helper, or `.netrc`, is also an option.
When the hook exits non-zero, or exits 0 without leaving a usable checkout behind, what the runner does depends on the repository:
* **A repository the session pushes results to**: the runner fails the session, and on a non-zero exit surfaces the tail of the script's stderr to the user.
* **A repository the session only reads from**, such as a repository added to a running session: the runner logs a `[runner:warn]` line with the failure detail, posts a `Skipped` step to the session, removes whatever the hook left at the checkout path, and continues with the remaining repositories. When the runner can't remove the path immediately, it retries the removal at session end. If skipping leaves the session with no repository at all, the runner fails the session anyway.
Before v2.1.228, the runner failed the session on a hook failure for any repository, so a read-only repository the hook couldn't serve failed the session again on every fresh runner the session resumed on.
The runner removes the checkout path after the session ends.
### post-session
Runs once per session, after the Claude Code child has exited and before the runner tears the workspace down. This hook is your only chance to save uncommitted work: at `--capacity` above one, the runner deletes per-session worktrees right after the hook returns, and at `--capacity 1` the reused [canonical clone](/docs/en/self-hosted-environments-deploy#reuse-a-pre-warmed-checkout) is hard-reset when the next session starts, so uncommitted tracked changes don't survive on either path. Typical uses are pushing a snapshot branch of uncommitted changes, archiving logs, or emitting a session-ended event to your own systems.
The hook fires on every session end where a child process was spawned, whatever the cause; the `CLAUDE_RUNNER_EXIT_REASON` values below enumerate the cases. It can't fire when the runner terminates abruptly, such as a VM preemption or a power loss; if you need guarantees against abrupt termination, snapshot periodically from inside the session with a Claude Code `PostToolUse` hook instead. The runner sets:
| Variable | Description |
| :--------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `CLAUDE_RUNNER_SESSION_ID` | Session ID in the tagged `session_...` form |
| `CLAUDE_RUNNER_SESSION_UUID` | The same session ID in canonical UUID form |
| `CLAUDE_RUNNER_EXIT_REASON` | How the session ended; see the values below the table |
| `CLAUDE_RUNNER_WORKSPACE_PATHS` | Colon-separated absolute paths of the session's working trees. Empty for zero-repo sessions. |
| `CLAUDE_RUNNER_DEBUG_LOG_PATH` | Path to the session's debug log, still on disk while the hook runs |
| `CLAUDE_RUNNER_API_BASE_URL` | Anthropic API base URL for session-scoped calls |
| `CLAUDE_RUNNER_CLIENT_PLATFORM` | The client surface that created the session, such as `web_claude_ai`, `desktop_app`, or `ios`. Unset when the session has no recorded or recognized surface. |
| `CLAUDE_CODE_SESSION_ACCESS_TOKEN` | The session access token, for session-scoped API calls |
`CLAUDE_RUNNER_EXIT_REASON` takes one of four values:
* `completed`: a clean exit, including a session archived or deleted while the child was still connected.
* `failed`: a child crash or a setup failure after spawn.
* `interrupted`: an idle release, startup timeout, server deassign, drain, watchdog kill, or the [`released=false` backstop](/docs/en/self-hosted-environments-reference#session-lifecycle-counter-semantics).
* `abandoned`: reserved for sessions another runner claimed; the hook doesn't currently fire in that case.
The [session lifecycle counter semantics](/docs/en/self-hosted-environments-reference#session-lifecycle-counter-semantics) classify an idle release, a startup timeout, and a server deassign as `completed` instead: those are clean handoffs from the session's perspective even though this hook reports them as `interrupted`.
The hook's exit status never affects the session outcome; a failure is logged and ignored. The runner waits up to `--post-session-hook-timeout-sec`, 60 seconds by default, on every session end including runner shutdown. This example saves uncommitted work to a rescue branch:
```bash theme={null}
#!/usr/bin/env bash
set -u
IFS=':'
# Pin config the session could have planted in the checkout's .git/config:
# -c overrides beat repo-local settings, blocking session-written fsmonitor,
# hook-path, and gpg-program config from executing code with the hook's
# privileges. Repo-local credential.helper, core.sshCommand, and pushurl
# still apply; if the hook holds credentials the session didn't, pin the
# push URL and helper too (see the note below the script).
g() { git -c core.fsmonitor=false -c core.hooksPath=/dev/null \
-c commit.gpgsign=false "$@"; }
for ws in $CLAUDE_RUNNER_WORKSPACE_PATHS; do
cd "$ws" 2>/dev/null || continue
[ -z "$(g status --porcelain 2>/dev/null)" ] && continue
g add -A
g commit -q -m "runner snapshot: $CLAUDE_RUNNER_SESSION_ID ($CLAUDE_RUNNER_EXIT_REASON)" || continue
g push -q origin "HEAD:refs/heads/rescue/$CLAUDE_RUNNER_SESSION_ID" || true
done
```
The hook pushes with whatever git credentials are available in its own environment on the runner host. Under the [no-credentials-in-the-image posture](/docs/en/self-hosted-environments-deploy#configure-git), including when the built-in clone goes through the Anthropic git proxy, there are none, so mint a short-lived push credential inside the hook before pushing: exchange the session token the hook receives in `CLAUDE_CODE_SESSION_ACCESS_TOKEN` with your own token service, verifying it as [Verify session identity](/docs/en/self-hosted-environments-identity) describes. When the hook holds a credential the session didn't, also pin where it pushes: replace `origin` with an operator-supplied URL and pass `-c credential.helper=` plus your own helper, so repo-local config the session wrote can't redirect the credentialed push.
### command
Runs once per session after checkout, in place of the built-in child spawn. The hook receives the same environment as a [wrapper script](#wrapper-scripts) and should `exec` into `"$CLAUDE_RUNNER_CLAUDE_BIN"` the same way. Use the `command` hook to keep all customization in one hooks directory; use `--exec-path` when the wrapper lives elsewhere. If `--exec-path` is also set, the flag takes precedence and the `command` hook is ignored.
Always `exec` the runner's own binary rather than a PATH-resolved `claude`; otherwise you defeat [version pinning](/docs/en/self-hosted-environments-deploy#pin-the-version).
## On-demand runners
Instead of running a fixed fleet, you can boot one runner per session. The orchestrator is a separate, stateless subcommand that polls Anthropic for spawn requests, one per session that's queued with no runner available, and runs your `spawn-runner` hook for each. Your hook submits a workload to your platform: a Kubernetes Job, an EC2 instance, a Nomad dispatch.
On-demand runners improve credential hygiene. On a fixed fleet, the environment secret lives on every runner host, which is the same host that runs user sessions. With the orchestrator, the environment secret stays only on the orchestrator host, which never runs user code; each spawned runner receives a single-use work order that registers exactly one runner and then expires.
To start the orchestrator, pass the environment secret and a hooks directory containing an executable `spawn-runner` script:
```bash theme={null}
claude self-hosted-runner orchestrator \
--environment-secret-file /etc/claude/environment-secret \
--hooks-dir /etc/claude/hooks
```
The orchestrator keeps no state between polls, so you can run two or more replicas against the same environment for availability. Each spawn request is claimed server-side by exactly one replica. All replicas must use the same `--expected-spawn-seconds` value; see the [hook contract](#the-spawn-runner-hook).
### The spawn-runner hook
The orchestrator runs `${hooks-dir}/spawn-runner` once per spawn request. The hook must submit work asynchronously, without waiting for the runner to boot, and return within `--hook-timeout`, 60 seconds by default. The hook receives:
| Variable | Description |
| :------------------------------------ | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `CLAUDE_RUNNER_WORK_ORDER_FILE` | Path to a temp file containing the signed work-order JWT the new runner registers with. Deleted after the hook exits. Don't log the file's contents. |
| `CLAUDE_RUNNER_ORDER_ID` | Opaque idempotency key, unique per spawn request and safe for Kubernetes resource names. Use it as your provisioner's dedup key. |
| `CLAUDE_RUNNER_SESSION_ID` | The session this request is for. Empty for pre-warming requests, which boot a standby runner ahead of any specific session when [`--min-idle`](/docs/en/self-hosted-environments-reference#orchestrator-cli-flags) is set, so don't assume the variable is set. |
| `CLAUDE_RUNNER_SESSION_UUID` | The same session ID in canonical UUID form. Empty for pre-warming requests. |
| `CLAUDE_RUNNER_ATTEMPT` | How many spawn requests this session has had. `0` for pre-warming requests. |
| `CLAUDE_RUNNER_ORDER_SERVER_TIME` | Server time from the poll response's HTTP `Date` header. When the hook verifies the work-order JWT's `exp`, compare against this value instead of the local clock to tolerate skew. Empty when the gateway omitted the header. |
| `CLAUDE_RUNNER_POOL_ID` | The ID of the environment the new runner should join, in `ccpool_...` form |
| `CLAUDE_RUNNER_ACCOUNT_ID` | Tagged ID of the account that enqueued the session, for per-account routing, quota, or chargeback. Empty when unavailable. |
| `CLAUDE_RUNNER_ACCOUNT_EMAIL` | Email of the account that enqueued the session. Empty when unavailable. Treat the email as personally identifiable information and don't log it. |
| `CLAUDE_RUNNER_PRIMARY_REPO_URL` | URL of the session's first git source, for routing to a runner with that repository pre-warmed. Empty when the session has no git sources. |
| `CLAUDE_RUNNER_PRIMARY_REPO_REVISION` | Revision of the session's first git source: branch, SHA, or tag. Empty when unspecified. |
| `CLAUDE_RUNNER_REPO_SOURCES` | JSON array of `{url, revision}` for all the session's git sources, for hooks that route on a secondary repository. Empty when there are no sources. |
| `CLAUDE_RUNNER_CORRELATION_ID` | The correlation ID supplied at session create, echoed back so the hook can map this work order to the request that created the session. Empty when the session has none. |
| `CLAUDE_RUNNER_CLIENT_PLATFORM` | The client surface that created the session, such as `web_claude_ai`, `desktop_app`, `ios`, or `scheduled_trigger`, for adoption analytics. Unset when the session has no recorded or recognized surface, and for pre-warming requests; check it with `[ -n "${CLAUDE_RUNNER_CLIENT_PLATFORM:-}" ]`, which stays safe under `set -u`. |
The spawned runner registers with the work order in place of the environment secret:
* **Start it with the work order**: point [`--environment-secret-file`](/docs/en/self-hosted-environments-reference#runner-cli-flags) at a file containing the work-order JWT, or set `SELF_HOSTED_RUNNER_ENVIRONMENT_SECRET` to the JWT value.
* **Copy the JWT before the hook exits**: the orchestrator deletes the work-order file after the hook exits, so copy the JWT into the workload you submit, such as a Kubernetes Secret on the spawned Job, rather than passing the file path through.
* **Use `--capacity 1` on spawned runners**: a session-bound work order registers exactly one runner bound to that session, so a higher capacity adds slots that never receive work, and the runner logs a warning at startup.
* **Pre-warming work orders register unbound**: the standby runner isn't bound to a session and claims queued work like a fixed-fleet runner.
The contract has four provisioner-agnostic rules:
1. **Be idempotent on `CLAUDE_RUNNER_ORDER_ID`.** Redelivery of the same request must spawn at most one runner. Derive a deterministic resource name from the ID and let your platform reject the duplicate.
2. **Don't retry the workload.** One order ID means at most one created workload. If the runner never registers, Anthropic re-requests with a fresh order ID after `--expected-spawn-seconds`.
3. **Use the exit-code contract.** Exit 0 means submitted. Exit 1 means retryable failure; the session backs off and is re-offered. Exit 2 or higher means non-retryable; the session is blocked from spawning again until an [Owner or admin](/docs/en/cloud-environments#organization-shared-environments) selects **Retry** on it in the environment's **Activity** tab. On non-zero exit, the tail of the hook's stderr appears there as the failure reason, so write the actionable error to stderr and never secrets. For a pre-warming request there is no session to fail: the orchestrator logs a non-zero exit locally only, and the server re-requests the spawn after the lease.
4. **Set `--expected-spawn-seconds` to at least your p99 boot time.** This is the server-side lease. All orchestrator replicas must use the same value.
Everything the hook writes to stdout or stderr appears in the orchestrator's log with credentials automatically redacted. If sessions stay queued, check the orchestrator's `/healthz` body for queue counts, then open your environment's **Activity** tab on the [**Cloud environments** admin page](https://claude.ai/admin-settings/cloud-environments): expand a failed session there for its spawn error, and select **Retry** to re-request it.
## MCP servers
To make [MCP servers](/docs/en/mcp) available in every session, add them at image build time with the same `claude mcp add` command used on a desktop install. If your runner is a bare process rather than a container, run the same command as the runner's user on the host, then restart the runner: it reads host config once at startup. The `--scope user` flag is required; the default local scope writes under a per-directory key that the runner doesn't seed into sessions. For example, in your Dockerfile:
```dockerfile theme={null}
RUN claude mcp add --scope user sidecar -- /usr/local/bin/mcp-sidecar
RUN claude mcp add --scope user --transport http internal http://mcp-gateway.svc.cluster.local:8080
```
The runner snapshots the host's config once at startup. The snapshot captures the `mcpServers` key from the host's `.claude.json`, which lives next to rather than inside `~/.claude/`, and the runner seeds only that key into each session's isolated config; account state and project history are dropped. To confirm the servers reached sessions, start a session on the environment and ask Claude to list its MCP tools; the runner also logs a startup warning for any captured entry whose `type` it doesn't recognize and drops the entry, so you can see why that server is missing from sessions. When `SELF_HOSTED_RUNNER_HOST_CONFIG_DIR` is set, the runner reads `.claude.json` from that directory instead, so pointing the variable at an empty directory disables MCP seeding too.
Two other sources work as well:
* The enterprise-scope [managed MCP file](/docs/en/managed-mcp) at its standard system path: `/etc/claude-code/managed-mcp.json` on Linux runner hosts, `/Library/Application Support/ClaudeCode/managed-mcp.json` on macOS hosts. Use it for locked-down fleets where only administrator-listed servers may load. See [exclusive control with managed-mcp.json](/docs/en/managed-mcp#exclusive-control-with-managed-mcp-json) for the precedence rules. When this file is on the runner host, Claude Code skips the MCP servers Anthropic's control plane delivers to a session, including claude.ai connectors, and names them in a warning on the session child's stderr, which the runner records at the `debug` log level. Before v2.1.229, those sessions exited at startup with `You cannot dynamically configure MCP servers when an enterprise MCP config is present`.
* `<repo>/.mcp.json`: project scope. Commit the file to the repository; its servers are auto-approved in cloud sessions.
When connector delivery is enabled for your organization, Anthropic's control plane delivers the connectors you've configured on claude.ai to interactively-created sessions through server-provided MCP configuration, routed through `api.anthropic.com`. Sessions created programmatically, such as [CLI dispatches](/docs/en/self-hosted-environments-testing#run-the-test-loop), don't receive connector delivery; give them MCP servers through the host snapshot, the [managed MCP file](/docs/en/managed-mcp), or `<repo>/.mcp.json` instead. The child's OAuth token doesn't carry a scope for fetching connectors directly, so the child doesn't attempt that fetch itself; delivery is server-driven.
`settings.json` and `managed-settings.json` don't carry MCP server definitions; there is no top-level `mcpServers` field in the settings schema.
Sessions inherit the runner's environment, so set [`ENABLE_TOOL_SEARCH`](/docs/en/mcp#scale-with-mcp-tool-search) there to control MCP tool search for every session a runner spawns; the MCP page covers the values.
## Prompt sessions to push their work
Anthropic-hosted sessions run a [`Stop` hook](/docs/en/hooks#stop), the Claude Code hook that runs when Claude finishes responding, that prompts Claude to commit and push its work. The runner doesn't install one. Without it, a session that ends with uncommitted changes leaves that work only on the runner's disk, and the **Create PR** button in claude.ai/code stays inactive until the branch exists on the remote.
The reference implementation below has two parts. Merge the settings block into `~/.claude/settings.json` on the runner host, which the runner seeds into every session, and save the script as `~/.claude/hooks/stop-hook-nudge.sh` on the runner host and make it executable:
```json theme={null}
{
"hooks": {
"Stop": [
{
"hooks": [
{
"type": "command",
"timeout": 10,
"command": "\"$CLAUDE_CONFIG_DIR/hooks/stop-hook-nudge.sh\""
}
]
}
]
}
}
```
```sh theme={null}
#!/bin/sh
# Stop-hook reference implementation for self-hosted runners.
#
# Nudges Claude once per turn if the project directory has uncommitted
# changes OR unpushed commits, so work isn't lost when an idle session
# is released and so the "Create PR" button on claude.ai/code lights up.
#
# Runner-level (no repo changes): drop this file at ~/.claude/hooks/ on
# the runner host and merge the accompanying Stop-hook settings block
# into ~/.claude/settings.json — the runner seeds both into every session.
# Repo-level alternative: commit to <repo>/.claude/hooks/ and change the
# settings.json command path to $CLAUDE_PROJECT_DIR/.claude/hooks/.
#
# stdin: hook JSON payload (see https://code.claude.com/docs/en/hooks)
# stdout: {"decision":"block","reason":"..."} to nudge, or nothing to allow stop.
# Re-entry guard: the harness sets stop_hook_active=true when re-invoking
# the Stop hook after a block. Bail so we only nudge once per turn. The
# harness emits compact JSON (no space after the colon), which this
# pattern relies on; use jq if you need a whitespace-tolerant check.
in=$(cat)
case "$in" in *'"stop_hook_active":true'*) exit 0 ;; esac
d="$CLAUDE_PROJECT_DIR"
# Not a git repo → nothing to nudge.
git -C "$d" rev-parse --git-dir >/dev/null 2>&1 || exit 0
# No remote → "push to the remote" is unsatisfiable; bail.
[ -z "$(git -C "$d" remote 2>/dev/null)" ] && exit 0
# Uncommitted changes (staged, unstaged, or untracked). Exclude .claude/
# entirely — operator-seeded settings and CLI-written runtime state
# (scheduler lock, worktrees, routine state) live there and neither is
Cut at 300 lines. The page has the rest.
self-hosted-environments-deploy First recorded · 365 lines, first recorded
# Deploy self-hosted environments to production ## Harden your deployment ## Network requirements ### Default-deny egress ## Configure git ### Let the runner configure git ### Ship git config in your image ### Use the Anthropic git proxy ### Rewrite git URLs for private networks ## Build the runner image ## Kubernetes ## Docker Compose ## Shutdown timing ### What reaches a running post-session hook ## Keep the base directory and capacity identical across runners ## Reuse a pre-warmed checkout ## Pin the version ## Scale the fleet ## Known issues and limitations ### Connector traffic leaves your network ### Some sessions don't count as idle ### Additional limitations ### Report an issue ## Troubleshooting ## What's next
The first capture of this source. The page was already there, and this is what it said.
# Deploy self-hosted environments to production
> Run self-hosted runners in production: security hardening, network egress control, git credentials, Kubernetes and Compose recipes, and troubleshooting.
<Note>
Self-hosted environments are in public beta on Team and Enterprise plans; [Availability and limitations](/docs/en/self-hosted-environments#availability-and-limitations) covers the enablement path. This page covers running the fleet in production; see the [quickstart](/docs/en/self-hosted-environments-quickstart) for your first runner and session.
</Note>
A [self-hosted environment](/docs/en/self-hosted-environments) runs Claude Code [cloud sessions](/docs/en/claude-code-on-the-web) on runners you deploy inside your network, and in production those sessions execute model-directed code on behalf of anyone in your organization. This page is for the operator taking a working environment to production. It works through the deployment in order: what to lock down before connecting real systems, the egress the fleet needs, how sessions authenticate to your git host, the deployment recipes themselves, and what to check when sessions misbehave.
## Harden your deployment
A self-hosted runner executes arbitrary, model-directed code on your infrastructure on behalf of any member of your Anthropic organization. Work through each item before you connect an environment to production systems:
* **Ephemeral, per-session containers**: run each runner process in a fresh container or VM that's destroyed when the process exits, with `--capacity 1` and the default `--drain-grace-sec 0` so each container serves exactly one session. At a higher capacity, or with a positive drain grace, one container serves multiple sessions from the same locked account; see [Runner lifecycle](/docs/en/self-hosted-environments#runner-lifecycle). Don't reuse a filesystem between runner restarts, except in the deliberate [pre-warmed checkout](#reuse-a-pre-warmed-checkout) setup, and never across accounts.
* **No broad credentials in the image**: don't include long-lived SSH keys, cloud-provider credentials, or personal access tokens that grant more than a session needs. Mint credentials used during a session, such as push or API tokens, per session from your [wrapper script](/docs/en/self-hosted-environments-configuration#wrapper-scripts). For the initial clone, which happens before the wrapper runs, use a [`checkout` lifecycle hook](/docs/en/self-hosted-environments-configuration#checkout) or [`--use-anthropic-git-proxy`](#use-the-anthropic-git-proxy); see [Configure git](#configure-git).
* **Keep the environment secret off session-running hosts**: the environment secret can register runners and pick up any org member's queued sessions. On a fixed fleet it lives on every runner host, where any session's code can read the secret file. Prefer [on-demand runners](/docs/en/self-hosted-environments-configuration#on-demand-runners), where the secret stays on the orchestrator host, which never runs user code, and each runner receives a single-use work order that registers exactly one runner. On a fixed fleet, treat the environment-secret file as readable by every session and rotate the secret after any suspected session compromise.
* **Default-deny network egress**: restrict runner and session container outbound traffic at your own network boundary on every environment; [Default-deny egress](#default-deny-egress) covers what to allow and why.
* **Least-privilege host IAM**: the compute identity attached to the runner host, such as an instance profile or node service account, should grant only what the runner itself needs. Sessions should obtain their own credentials through your wrapper script rather than inheriting the host's.
* **Block the cloud metadata endpoint from sessions**: keeping sessions off the host identity requires blocking their access to the cloud metadata endpoint, and subnet-level egress policies don't intercept link-local metadata traffic, so block it in the container itself:
* IMDSv2 with a hop limit of one
* GKE Workload Identity with metadata concealment
* An explicit deny for `169.254.169.254` in the session container's network namespace
The block applies to your wrapper script and lifecycle hooks too, since they share the container. Authenticate any token exchange with the [session JWT](/docs/en/self-hosted-environments-identity) against your own token service over allowlisted egress, or use a file-based web identity such as IAM Roles for Service Accounts (IRSA) on Amazon EKS.
* **Per-runner filesystem isolation**: each runner process gets its own working directory that no other process on the host can read or write. Make `--hooks-dir`, the wrapper script, and the host's `~/.claude/` read-only to the session, either built into the image or mounted read-only.
* **Dispatch is organization-wide**: any member of your Anthropic organization can dispatch a session to any of its environments, and there's no per-environment access control on dispatch. Treat every runner host as reachable for code execution by every org member, and place data or credentials on a runner host only if every org member is allowed to read them. [`--lock-to-account`](/docs/en/self-hosted-environments-reference#runner-cli-flags) bounds which account's sessions a given host executes, but dispatch into the environment itself stays organization-wide. To make self-hosted environments the only picker option, an [Owner or admin](/docs/en/cloud-environments#organization-shared-environments) can hide Anthropic-hosted environments for the whole organization from the [**Cloud environments** page](https://claude.ai/admin-settings/cloud-environments).
* **Enforce the repo-settings guard**: choose the guard mode with [`--confine-repo-settings`](/docs/en/self-hosted-environments-reference#runner-cli-flags). The default `warn` logs a violation and still spawns the session, `enforce` refuses the session, and `off` disables the scan. The runner scans each repository's committed settings for:
* A grant that resolves outside that session's own workspace: an `additionalDirectories` entry, an `Edit`, `Write`, or `NotebookEdit` rule in `permissions.allow`, or a `sandbox.filesystem.allowWrite` or `allowRead` entry
* A non-empty `env` block
* An operator-posture override such as `sandbox.enabled: false`
The guard runs regardless of [`--trust-workspace`](/docs/en/self-hosted-environments-reference#runner-cli-flags), and doesn't cover repository hooks, `.mcp.json`, or Bash rules; see [Permissions and tool approval](/docs/en/self-hosted-environments-configuration#permissions-and-tool-approval) for where those grants belong.
<Note>
Your organization's IP allowlist doesn't cover self-hosted runner traffic by default. Don't rely on it as a network control for runner or session traffic; apply default-deny egress at your own network boundary instead, and contact your Anthropic account team if you want IP-allowlist enforcement for your organization.
</Note>
## Network requirements
The runner and the session children it spawns make outbound connections to the hosts below. Restrict session-container egress to these hosts and the specific internal services sessions need to reach; [Default-deny egress](#default-deny-egress) covers how and why.
These hosts are always required:
| Host | Port | Used for |
| :----------------------------------------------------------------- | :----------------------------------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `api.anthropic.com` | 443, HTTPS; WSS for the SCM connector only | Runner control plane and session streaming, model inference, feature flags, product analytics, [JWKS](/docs/en/self-hosted-environments-identity) key fetches, commit signing, the git proxy when `--use-anthropic-git-proxy` is set, and the orchestrator's [SCM connector](/docs/en/self-hosted-environments-reference#scm-connector-flags) tunnel when `--scm-connector-host` is set |
| Your git host, such as `github.com` or your GitHub Enterprise host | 443 or 22 | Cloning and pushing repositories. Not needed if the runner uses `--use-anthropic-git-proxy`, which routes git traffic through `api.anthropic.com`. |
Whether these hosts are needed depends on your configuration:
| Host | Port | When required |
| :----------------------------------- | :--- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `downloads.claude.ai` | 443 | At install time, when you install or update Claude Code on the host with the native installer; the `install.sh` script itself is served from `claude.ai`. At session runtime, only when sessions install plugins from the official Anthropic marketplace. |
| `storage.googleapis.com` | 443 | At session runtime, for marketplace plugin catalog fetches and Artifact publishing when the Artifact tool is enabled; Artifact publishing falls back to `api.anthropic.com` when this host is blocked. |
| `code.claude.com` and `claude.com` | 443 | Documentation lookups by the built-in claude-code-guide agent and pre-approved WebFetch requests during sessions. Blocking these hosts only affects documentation lookups. |
| `*.frame.claudeusercontent.com` | 443 | Only when the [Artifact tool](/docs/en/artifacts#availability) is available for sessions in your organization; defaults vary by plan, per the availability table there. Set `CLAUDE_CODE_DISABLE_ARTIFACT=1` on the runner to keep the tool disabled regardless of the organization setting. |
| `raw.githubusercontent.com` | 443 | Only for the changelog fetch behind `/release-notes` and the release notes shown after a CLI version change. Suppressed by `CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1`. |
| `registry.npmjs.org` | 443 | When a session installs a plugin, both for fetching npm-source plugin packages and for installing a plugin's Node.js dependencies, or when an `npx`-launched MCP server runs |
| `http-intake.logs.us5.datadoghq.com` | 443 | Anthropic operational metrics. Only when `CLAUDE_CODE_BYOC_ENABLE_DATADOG=1` is set; off by default in self-hosted environments. |
| `browser-intake-us5-datadoghq.com` | 443 | Anthropic error-report uploads, sent only when [error reporting](/docs/en/data-usage#telemetry-services) is enabled for the session's account. Suppressed by `DISABLE_ERROR_REPORTING=1` or `DISABLE_TELEMETRY=1`. |
The runner doesn't reach `statsig.anthropic.com`, `*.sentry.io`, `claude.ai`, or `platform.claude.com`. These hosts appear in some older enterprise network checklists, but you don't need to allowlist them for runner or session traffic: feature-flag fetches go to `api.anthropic.com`, and the runner authenticates with the environment secret rather than interactive OAuth. Two host-side flows do reach `claude.ai`, so run them from a host whose egress allows it rather than widening session-container egress: the one-line installer fetches `install.sh` from `claude.ai` at install time, and interactive `claude auth login`, which the [guided setup](/docs/en/self-hosted-environments-quickstart#set-up-an-environment-and-runner), `doctor`'s signed-in mode, and [CI dispatch](/docs/en/self-hosted-environments-testing#authenticate-from-ci) use, signs in through `claude.ai`, `claude.com`, and `platform.claude.com`. `mcp-proxy.anthropic.com` isn't required either: self-hosted sessions don't use it, and delivery of your organization's claude.ai connectors to sessions, when enabled for your organization, routes through `api.anthropic.com`. See [MCP servers](/docs/en/self-hosted-environments-configuration#mcp-servers).
### Default-deny egress
Deploy runner and session containers in a network segment or namespace whose outbound traffic is limited to the hosts in the [network requirements table](#network-requirements), your git host, and the specific internal services sessions need to reach. The product can't verify or enforce this, so apply it at your own network boundary on every environment. Session code is model-directed and can attempt connections to arbitrary hosts; default-deny egress at the network layer bounds where those attempts can land. This applies regardless of permission mode: the default pre-approved tool set already includes `Bash`, so shell egress runs without a prompt even without [auto mode](/docs/en/self-hosted-environments-configuration#permissions-and-tool-approval).
For details on which telemetry each session emits and how to turn it off, see [Telemetry](/docs/en/self-hosted-environments-reference#telemetry).
## Configure git
The runner manages repository checkouts but doesn't configure git identity or credentials by default. You control the runner's image and process environment, so you control the git config. Choose one of two approaches:
* **Let the runner configure git**: start the runner with `--configure-git` to have it write the same identity and commit-signing config that Anthropic-hosted sessions use
* **Ship git config in your image**: set identity and push credentials yourself, for example to commit under your own bot identity
Git version floors on the runner host: [`--configure-git`](#let-the-runner-configure-git) SSH commit signing requires Git 2.34 or newer, [`--use-anthropic-git-proxy`](#use-the-anthropic-git-proxy) requires 2.32 or newer, and resuming sessions from branches pushed by [`--push-outcome-on-release`](/docs/en/self-hosted-environments-reference#runner-cli-flags) requires 2.29 or newer. Git 2.24 is sufficient if you omit all three and manage git identity yourself.
### Let the runner configure git
Start the runner with `--configure-git`, or set `SELF_HOSTED_RUNNER_CONFIGURE_GIT=1`, to have it write global git config at startup:
* `user.name = Claude` and `user.email = [email protected]`, matching Anthropic-hosted sessions
* SSH-format commit and tag signing, routed through a runner-managed shim that signs each commit via Anthropic's signing service using the session's own credentials. Signatures are verifiable on GitHub against Anthropic's published SSH signing key.
Commit signing requires git 2.34 or newer; the runner checks at startup and exits with an error if your git is older. This flag doesn't configure push credentials, which you still provide in the image.
### Ship git config in your image
Git identity is required for any commit. Set it system-wide in your Dockerfile so the config applies regardless of which user the runner process runs as:
```dockerfile theme={null}
RUN git config --system user.name "Claude" && \
git config --system user.email "[email protected]"
```
Without an identity, `git commit` fails with `Please tell me who you are` and sessions can't make progress. You can use your own bot identity instead; the runner doesn't override these values.
Don't bake long-lived or broadly-scoped push credentials into a shared runner image: a credential in the image is available to every session the image runs, across every member of your organization. Instead, mint a short-lived, least-scoped token per session from your [wrapper script](/docs/en/self-hosted-environments-configuration#wrapper-scripts), using the session creator's identity decoded from the session JWT. Pair it with an ephemeral per-session container, which requires `--capacity 1`, so no credential outlives the session that minted it; see the [hardening section](#harden-your-deployment).
If you must configure push credentials at the image level, for example for a read-only deploy key, scope them as tightly as your git host allows:
* An SSH deploy key limited to one repository with a `url.<base>.insteadOf` rewrite
* A `credential.helper` that returns a minimally-scoped token
* `GIT_SSH_COMMAND` pointing at a narrowly-scoped key
Whichever mechanism you configure must work without a prompt, because the runner's built-in clone and fetch disable the prompts that git, SSH, and Git Credential Manager would otherwise show:
* The runner sets `GIT_TERMINAL_PROMPT=0`, so git doesn't ask for a username or password.
* The runner runs SSH with `BatchMode=yes`, appended to your `GIT_SSH_COMMAND` if you set one, so SSH doesn't ask for a passphrase or host confirmation.
* The runner sets `GCM_INTERACTIVE=never`, so Git Credential Manager doesn't open a sign-in dialog.
* The runner clears `core.askPass`, so if you use an askpass helper, set it through the `GIT_ASKPASS` environment variable instead.
If your git host rejects the credential, or you didn't configure one, the runner retries a few times and then fails repository preparation. The runner doesn't pass these settings into the session's environment.
If checkout directories are owned by a different uid than the runner process, git refuses to operate on them; add `safe.directory`:
```dockerfile theme={null}
RUN git config --system --add safe.directory '*'
```
### Use the Anthropic git proxy
Start the runner with `--use-anthropic-git-proxy`, or set `CLAUDE_RUNNER_USE_GIT_PROXY=1`, to have it clone through Anthropic's git proxy, authenticated with the session's own short-lived token. For ordinary user sessions, the proxy uses the GitHub or GitHub Enterprise OAuth token stored for the session creator; for bot and agent sessions, it uses your organization's GitHub App installation token. Either way, the runner image needs no git credentials at all: no SSH keys, no credential helper, no `.netrc`. This is the same auth path Anthropic-hosted environments use.
The proxy requires `--capacity 1` because the proxy URL is per-session, and git 2.32 or newer because older git ignores the configuration mechanism the proxy uses to isolate sessions from each other. The runner refuses to start if either requirement is unmet. Because the proxy fetches from Anthropic's side, your git host must be reachable from Anthropic infrastructure, the same requirement Anthropic-hosted sessions have; for a git host that's only routable inside your network, use a [`checkout` lifecycle hook](/docs/en/self-hosted-environments-configuration#checkout) instead. Each runner process handles one session at a time, so run more replicas for parallelism. When the proxy is enabled, `--git-host-rewrite` and `--git-ssh-rewrite` have no effect: the proxy URL points at `api.anthropic.com`, not your git host.
### Rewrite git URLs for private networks
Repository URLs arrive from the control plane as HTTPS, with the hostname of your git host; for GitHub Enterprise, that's the hostname you configured for the [GitHub Enterprise integration](/docs/en/github-enterprise-server) in Claude Code admin settings on claude.ai. Two repeatable flags rewrite those URLs before clone:
* `--git-host-rewrite <from>=<to>`: for split-horizon DNS, where Anthropic reaches your git host via an external hostname but runners must use an internal one
* `--git-ssh-rewrite <host>`: for git hosts that only accept SSH, rewriting `https://<host>/owner/repo` to `git@<host>:owner/repo`
Host rewriting runs first, so list the internal hostname in `--git-ssh-rewrite` if you need both. For full control over checkout, use a [`checkout` lifecycle hook](/docs/en/self-hosted-environments-configuration#checkout).
## Build the runner image
Anthropic doesn't publish a pre-built runner image. Build your own around the `claude` binary, layering in whatever toolchain your repositories need: language runtimes, compilers, package managers, and [MCP](/docs/en/mcp) sidecars.
The recipes below use `--capacity 4`, so one container serves up to four concurrent sessions from the same locked account. That doesn't provide the per-session container isolation in the [hardening section](#harden-your-deployment): before connecting an environment to production systems, either run the recipes at `--capacity 1` with one container per session, or use [on-demand runners](/docs/en/self-hosted-environments-configuration#on-demand-runners), which also keep the environment secret off session-running hosts.
This Dockerfile is a minimal starting point:
```dockerfile theme={null}
FROM debian:bookworm-slim
ARG CLAUDE_CODE_VERSION
RUN apt-get update && apt-get install -y --no-install-recommends git curl ca-certificates openssh-client \
&& rm -rf /var/lib/apt/lists/*
RUN curl -fsSL "https://downloads.claude.ai/claude-code-releases/${CLAUDE_CODE_VERSION:?set with --build-arg CLAUDE_CODE_VERSION}/linux-x64/claude" \
-o /usr/local/bin/claude && chmod +x /usr/local/bin/claude
RUN git config --system user.name "Claude" \
&& git config --system user.email "[email protected]" \
&& git config --system --add safe.directory '*'
ENTRYPOINT ["claude"]
```
Swap `linux-x64` for `linux-arm64` if your nodes are ARM, or for `linux-x64-musl` or `linux-arm64-musl` on a musl-based image such as Alpine; see [Alpine Linux setup](/docs/en/setup#alpine-linux-and-musl-based-distributions) for the extra packages musl images need. The URL is the standard Claude Code release location, so you can verify the downloaded binary against the release's signed manifest as described in [Binary integrity and code signing](/docs/en/setup#binary-integrity-and-code-signing). Build the image with Claude Code version 2.1.224 or later, then push it to your registry and reference it in the recipes below:
```bash theme={null}
docker build --build-arg CLAUDE_CODE_VERSION=2.1.224 -t <your-registry>/claude-runner:latest .
```
## Kubernetes
The runner serves `GET /healthz` on port 8080 by default, configurable with `--health-port`, so Kubernetes probes work with no extra setup. The endpoint returns `200` whenever the process is alive, so the probes below detect a dead process, not a stuck one; to catch a runner that stopped polling, alert on the `last_poll_age_seconds` series from [`/metrics`](/docs/en/self-hosted-environments-reference#prometheus-metrics). The Deployment below mounts the environment secret from a Kubernetes Secret, points the liveness and readiness probes at `/healthz`, and sets a 90-second termination grace period. See [Shutdown timing](#shutdown-timing) for why the grace period matters.
```yaml theme={null}
apiVersion: apps/v1
kind: Deployment
metadata:
name: claude-runner
namespace: claude-runners
spec:
replicas: 3
selector:
matchLabels:
app: claude-runner
template:
metadata:
labels:
app: claude-runner
app.kubernetes.io/part-of: claude-code-self-hosted-runner
spec:
terminationGracePeriodSeconds: 90
containers:
- name: runner
image: <your-registry>/claude-runner:latest
args:
- self-hosted-runner
- --environment-secret-file
- /etc/claude/environment-secret
- --capacity
- "4"
volumeMounts:
- name: environment-secret
mountPath: /etc/claude
readOnly: true
ports:
- name: health
containerPort: 8080
readinessProbe:
httpGet:
path: /healthz
port: 8080
initialDelaySeconds: 5
periodSeconds: 10
livenessProbe:
httpGet:
path: /healthz
port: 8080
initialDelaySeconds: 30
periodSeconds: 30
volumes:
- name: environment-secret
secret:
secretName: claude-runner-environment-secret
```
The Deployment above lives in a `claude-runners` namespace. Create the namespace first:
```bash theme={null}
kubectl create namespace claude-runners
```
Create the backing Secret from a local file holding the value you copied in the admin UI's [**Copy environment key** step](/docs/en/self-hosted-environments-quickstart#set-up-an-environment-and-runner), so the secret never appears in your shell history. Run `(umask 077 && cat > ./environment-secret)`, paste the secret, press Enter, then Ctrl-D. Then create the Secret and delete the file:
```bash theme={null}
kubectl create secret generic claude-runner-environment-secret -n claude-runners --from-file=environment-secret=./environment-secret
```
## Docker Compose
The Compose service below restarts the runner whenever it exits, which covers both crashes and the normal exit after draining. A Docker restart policy restarts the same container with its writable layer intact, so the runner comes back on a reused filesystem rather than the fresh one the [hardening posture](#harden-your-deployment) recommends; use this recipe for evaluation, and for production either recreate the container per run or use an orchestrator that does.
```yaml theme={null}
services:
claude-runner:
image: <your-registry>/claude-runner:latest
command:
- self-hosted-runner
- --environment-secret-file
- /run/secrets/environment-secret
- --capacity
- "4"
secrets:
- environment-secret
restart: always
stop_grace_period: 90s
secrets:
environment-secret:
file: ./environment-secret
```
## Shutdown timing
On `SIGTERM`, the runner stops taking new work, waits up to `--drain-wait-sec`, zero by default, for in-flight turns to finish, terminates each child process, and runs the [`post-session` lifecycle hook](/docs/en/self-hosted-environments-configuration#post-session). The full drain path needs up to `--session-stop-grace-sec` + `--drain-wait-sec` + `--post-session-hook-timeout-sec`, plus 15 seconds of fixed overhead for process cleanup, plus 30 more seconds when [`--push-outcome-on-release`](/docs/en/self-hosted-environments-reference#runner-cli-flags) is set. That is 80 seconds at defaults, and the runner logs the total at startup. Sessions drain in parallel under this one budget, so the total doesn't grow with `--capacity`.
At the default `--drain-wait-sec 0`, a rolling restart interrupts in-flight turns; each session resumes on another runner, losing unpushed work as described under [Known issues](#additional-limitations). Set `--drain-wait-sec`, and raise the grace period to match, to let turns finish first.
Throughout that whole path, the runner keeps heartbeating to the control plane at zero capacity, so the session lease doesn't expire and get requeued to another runner while the `post-session` hook is still writing out uncommitted work. The heartbeat stops just before the runner deregisters.
Give the runner at least the total it logs at startup before the host stops it. Where you set that depends on how your hosts stop:
* **With a `SIGTERM` grace period**: set `terminationGracePeriodSeconds` on Kubernetes, `stop_grace_period` on Docker Compose, or your orchestrator's equivalent to at least that total. The Kubernetes default of 30 seconds is shorter than the runner's drain path, so Kubernetes stops the pod before the runner finishes draining.
* **With [`--retire-at`](/docs/en/self-hosted-environments-reference#runner-cli-flags)**: size the margin between the retire time and the host's stop time to cover typical turns, plus the background-task hold that [Runner lifecycle](/docs/en/self-hosted-environments#runner-lifecycle) describes, plus that same total. Compute the retire time at each launch, for example `date +%s` plus the runner's intended lifetime.
### What reaches a running post-session hook
The `post-session` hook and the Claude session child each run in their own POSIX process group, separate from the runner's, so stop mechanisms reach them differently:
* **A second `SIGTERM` to the runner**: force-exits the runner immediately, skipping whatever remains of the drain path. Nothing signals a mid-run `post-session` hook, so on a bare host where an init process adopts orphans, it finishes on its own, but unsupervised: its timeout budget no longer applies, and a write to the closed log pipe can kill it with `SIGPIPE`, so a hook that needs to survive a forced exit there should redirect its own output to a file. In the container recipes on this page the runner is the container's PID 1 and its exit ends the container, and under systemd's default `KillMode=control-group` the cgroup kill in the third bullet applies; in both, treat a forced exit as fatal to the hook and rely on the grace period instead.
* **Process-group-wide signals**, such as `kill -- -<pid>` in a wrapper script, shell job control, or a group-wide watchdog: reach the runner and a mid-`checkout`-hook subprocess, which stays group-attached deliberately, but not a mid-run `post-session` hook or the session child.
* **Cgroup-wide kills**, such as systemd's default `KillMode=control-group` or the `SIGKILL` Kubernetes delivers to the whole container when `terminationGracePeriodSeconds` expires: reach everything, including the hook. Process-group isolation doesn't protect against these, which is why the grace period must cover the full drain path.
* **The hook's own timeout**: when a hook exceeds `--post-session-hook-timeout-sec`, the runner sends `SIGTERM` to the hook's whole process group, then `SIGKILL` two seconds later, so a worker the hook forked, such as tar, rsync, or git, terminates with the wrapper shell instead of surviving as an orphan. The runner's supervision ends once the hook's stdio closes: a worker that redirected its own output to a file and outlives the `SIGTERM` stage is past the runner's reach.
On the first `SIGTERM`, and again on a forced exit, the runner logs how many `post-session` hooks are still running, so you can tell a quiet drain from one that's mid-snapshot.
## Keep the base directory and capacity identical across runners
If a runner dies mid-session, the server requeues the session and another runner in the environment picks it up. That runner derives the checkout path from its own `--base-dir` and `--capacity`: `--capacity 1` checks out directly under `--base-dir`, and a `--capacity` above `1` uses per-session worktrees instead. When runners in the same environment use different values for either flag, the resumed session's working directory changes, and absolute paths the agent recorded earlier, in edits, tool calls, or its own notes, point at a location that no longer exists.
Use the same `--base-dir` and `--capacity` on every runner in an environment, and don't use a per-host value such as an instance ID or hostname.
The base directory defaults to `/workspace`, with the exception the [`--base-dir` reference row](/docs/en/self-hosted-environments-reference#runner-cli-flags) records. The runner needs write access to it. At startup, before registering, the runner creates the directory and confirms it can write to it, and exits with `cannot create or write to base directory` when it can't. A runner started as root creates the default `/workspace` itself. For a non-root runner, create the directory and give the runner's user ownership before starting the runner, or point `--base-dir` at a directory that user already owns.
## Reuse a pre-warmed checkout
For large repositories, the clone can dominate session startup. At `--capacity 1` with no [`checkout` hook](/docs/en/self-hosted-environments-configuration#checkout), the runner keeps one canonical clone per repository at `<base-dir>/<owner>/<repo>` and reuses it across sessions: it fetches the requested ref, detaches `HEAD`, and resets hard to it, which is near-instant when little has changed. To skip the cold clone, bake a clone into your runner image at that path, which gives every fresh container the warm clone without reusing a disk, or point `--base-dir` at a persistent volume paired with [`--lock-to-account`](/docs/en/self-hosted-environments-reference#runner-cli-flags) so the disk only ever serves one account.
What the reuse path does and doesn't guarantee:
* **Any clone shape works**: a full, shallow, or single-branch clone at the path is used as-is. The runner never passes `--depth` when fetching into an existing clone, so a full pre-warm keeps its full history and a shallow one stays shallow. `CLAUDE_RUNNER_FETCH_DEPTH` (`full`, `0`, or a number; default 50) controls only the cold clone the runner makes when no clone exists yet.
* **Tracked changes reset, untracked files persist**: each session starts from a hard reset that wipes the previous session's tracked modifications, but the runner never runs `git clean`, so untracked files from the locked account's earlier sessions stay in the tree.
* **With the git proxy, the reset becomes a checkout**: with [`--use-anthropic-git-proxy`](#use-the-anthropic-git-proxy), the runner sanitizes the clone's `.git/` before each session, keeping the object store, refs, and shallow state but deleting the index, so each session pays a full working-tree checkout instead of a near-instant reset; it still never re-clones. Submodule pre-warms aren't supported under the proxy.
* **Long clones need no workaround**: the runner bounds each git operation with a 120-second no-progress watchdog and a 30-minute hard cap, not a flat timeout, so a slow cold clone that keeps reporting progress completes.
Cut at 300 lines. The page has the rest.
self-hosted-environments-identity First recorded · 244 lines, first recorded
# Verify session identity in self-hosted environments ## The session token ### What the token proves ### Token format ## Verify the token ### Verify the token from your service ### Verify the token inside the session ## Claims reference ### The `act` chain ## Scope derived credentials ## Related environment variables ## What's next
The first capture of this source. The page was already there, and this is what it said.
# Verify session identity in self-hosted environments
> Verify the CLAUDE_CODE_SESSION_ACCESS_TOKEN JWT so services on your network can trust requests from sessions in your self-hosted environment.
<Note>
Self-hosted environments are in public beta on Team and Enterprise plans; an [Owner or admin](/docs/en/cloud-environments#organization-shared-environments) enables them by turning on **Allow self-hosted environments** on the [**Cloud environments** admin page](https://claude.ai/admin-settings/cloud-environments). This page covers session identity verification; see the [quickstart](/docs/en/self-hosted-environments-quickstart) for setup and [Deploy to production](/docs/en/self-hosted-environments-deploy) for the fleet recipes.
</Note>
A [self-hosted environment](/docs/en/self-hosted-environments) lets [Claude Code on the web](/docs/en/claude-code-on-the-web) sessions run on infrastructure you operate instead of on Anthropic's. Because the session runs inside your network, Claude can call your internal services directly. Those services need a way to confirm that a request really came from a Claude Code session in your environment, and to identify which user created that session.
Every session in a self-hosted environment receives a signed JSON Web Token (JWT) in the `CLAUDE_CODE_SESSION_ACCESS_TOKEN` environment variable. A session presents the token like any bearer credential; for example, a script Claude runs can call your service with `curl -H "Authorization: Bearer $CLAUDE_CODE_SESSION_ACCESS_TOKEN"`. Anthropic signs the token and publishes the verification keys at a public JWKS endpoint. Your services fetch those keys, verify the signature, and read the claims to decide what access to grant.
## The session token
Before you write verification code, know what the token establishes and the shape your JWT library will see.
### What the token proves
A valid token establishes some facts and deliberately not others:
* **Proves**: Anthropic issued the token for a specific session in a specific environment, and how the session was created: by a user in your organization, or with an organization service key
* **Doesn't prove**: which process on the runner host presents it. The token sits in an environment variable inside the session, so any code Claude runs, and any tool or MCP server the session starts, can read and present it.
Two consequences for your services:
* Verify the `aud` claim against your environment ID, the `ccpool_...` value shown with your environment on the [**Cloud environments** admin page](https://claude.ai/admin-settings/cloud-environments), to reject tokens issued to any other organization's environment.
* Scope credentials you derive from the token to what a single coding session should be able to do, not to everything the creating user can do. See [Scope derived credentials](#scope-derived-credentials).
### Token format
The value of `CLAUDE_CODE_SESSION_ACCESS_TOKEN` has an `sk-ant-cc-` prefix followed by a standard three-part JWT:
```text theme={null}
sk-ant-cc-<base64url header>.<base64url payload>.<base64url signature>
```
Strip the prefix before passing the value to a JWT library. Tokens issued to Anthropic-hosted cloud sessions carry an `sk-ant-si-` prefix instead and are signed by a different key set, so reject any value that doesn't start with `sk-ant-cc-`.
The signature algorithm is `ES256`, which is ECDSA on the P-256 curve with SHA-256. The token header carries a `kid` that identifies which key in the JWKS signed it.
## Verify the token
Verification runs in one of two places. Services on your network verify the token cryptographically against Anthropic's published keys, and wrapper scripts inside the session can use the runner binary's built-in decoder instead.
### Verify the token from your service
Anthropic publishes the verification keys at a public, unauthenticated endpoint:
```text theme={null}
https://api.anthropic.com/v1/code/.well-known/jwks.json
```
The response is a standard [JSON Web Key Set](https://www.rfc-editor.org/rfc/rfc7517). Anthropic rotates the signing keys periodically, and keys from before a rotation remain in the set long enough that tokens they signed continue to verify, so don't pin a single key. The endpoint sets `Cache-Control: public, max-age=300`, so caching the key set and refetching every five minutes is safe.
Verify each incoming token against these checks:
<Steps>
<Step title="Check the prefix">
Reject the value if it doesn't start with `sk-ant-cc-`, then remove that prefix. The remainder is a standard compact JWT.
</Step>
<Step title="Verify the signature">
Fetch the JWKS, select the key whose `kid` matches the token header, and verify the `ES256` signature. Reject tokens whose `alg` header is not `ES256`. If a token arrives with a `kid` that isn't in your cached key set, refetch the JWKS once before rejecting it: after a rotation, new tokens are signed with a key your cached set doesn't have yet.
</Step>
<Step title="Verify the issuer">
Reject the token if `iss` is not exactly `ccr`.
</Step>
<Step title="Verify the audience against your environment">
The `aud` claim is an array. Reject the token unless it contains your environment ID, which has the form `ccpool_...`. The environment ID is shown in your environment's detail dialog on the [**Cloud environments** admin page](https://claude.ai/admin-settings/cloud-environments), and appears as the `ccr:pool_id` claim in any of the environment's session tokens. This check is what scopes the token to your environment and rejects tokens issued to other organizations.
</Step>
<Step title="Verify the role">
Reject the token if `ccr:role` is not exactly `session_worker`. Other tokens issued for self-hosted environments, such as environment secrets, runner tokens, and work orders, are signed by the same key set but carry different roles.
</Step>
<Step title="Verify expiry">
Reject the token if `exp` is in the past. Anthropic issues session tokens with a four-hour lifetime by default and a maximum of eight hours. The runner refreshes the token before expiry and pushes the new value to the session, so subprocesses that Claude starts after a refresh inherit it. One session can therefore present several distinct valid tokens to your service over its lifetime.
</Step>
<Step title="Read the identity">
The creating user's identity is in the `act` claim: `act.sub` is their Anthropic user ID in the prefixed form `user:<id>`, and `act.email`, when the creating surface recorded one, is their email address. Sessions created with an organization service key carry no user identity, so treat a session as user-created only when `act.sub` carries the `user:` prefix, rather than testing whether identity claims are absent. See the [claims reference](#claims-reference) for the full structure and the flat duplicate claims.
</Step>
</Steps>
The checks map directly onto standard JWT libraries. The examples below implement the full sequence in Node.js with [`jose`](https://www.npmjs.com/package/jose), which handles JWKS fetching, caching, and `kid` selection, and in Python with [`PyJWT`](https://pyjwt.readthedocs.io/) and its built-in JWKS client.
<Tabs>
<Tab title="Node.js (jose)">
```typescript theme={null}
import { createRemoteJWKSet, jwtVerify } from "jose";
const JWKS = createRemoteJWKSet(
new URL("https://api.anthropic.com/v1/code/.well-known/jwks.json")
);
const PREFIX = "sk-ant-cc-";
const EXPECTED_POOL_ID = "ccpool_...";
export async function verifySessionToken(raw: string) {
if (!raw.startsWith(PREFIX)) {
throw new Error("not a self-hosted runner session token");
}
const jwt = raw.slice(PREFIX.length);
const { payload } = await jwtVerify(jwt, JWKS, {
issuer: "ccr",
audience: EXPECTED_POOL_ID,
algorithms: ["ES256"],
});
if (payload["ccr:role"] !== "session_worker") {
throw new Error("token is not a session_worker token");
}
const act = payload.act as { email?: string; sub?: string };
return {
sessionId: payload["ccr:session_id"] as string,
poolId: payload["ccr:pool_id"] as string,
orgId: payload["ccr:org_id"] as string,
creatorEmail: act?.email,
creatorSub: act?.sub,
};
}
```
</Tab>
<Tab title="Python (PyJWT)">
```python theme={null}
import jwt
from jwt import PyJWKClient
JWKS_URL = "https://api.anthropic.com/v1/code/.well-known/jwks.json"
PREFIX = "sk-ant-cc-"
EXPECTED_POOL_ID = "ccpool_..."
jwks = PyJWKClient(JWKS_URL)
def verify_session_token(raw: str) -> dict:
if not raw.startswith(PREFIX):
raise ValueError("not a self-hosted runner session token")
token = raw.removeprefix(PREFIX)
signing_key = jwks.get_signing_key_from_jwt(token)
payload = jwt.decode(
token,
signing_key.key,
algorithms=["ES256"],
issuer="ccr",
audience=EXPECTED_POOL_ID,
)
if payload.get("ccr:role") != "session_worker":
raise ValueError("token is not a session_worker token")
act = payload.get("act") or {}
return {
"session_id": payload["ccr:session_id"],
"pool_id": payload["ccr:pool_id"],
"org_id": payload["ccr:org_id"],
"creator_email": act.get("email"),
"creator_sub": act.get("sub"),
}
```
</Tab>
</Tabs>
### Verify the token inside the session
[Wrapper scripts](/docs/en/self-hosted-environments-configuration#wrapper-scripts) run inside the session, before Claude starts. Instead of calling a JWT library, they can run the runner binary's `self-hosted-runner decode-token` subcommand. The subcommand reads the token from a positional argument, from `CLAUDE_CODE_SESSION_ACCESS_TOKEN`, or from piped stdin, in that order, then strips the prefix, verifies the signature against the JWKS endpoint, checks expiry, and prints the claims as JSON. The subcommand performs the signature and expiry checks only; it doesn't check `iss`, `aud`, or `ccr:role`. When your wrapper's auth decision depends on those claims, read them from the printed JSON and compare them explicitly.
This command extracts the creator identity, preferring the SSO provider's subject, then the email address, then the always-present Anthropic user ID:
```bash theme={null}
"$CLAUDE_RUNNER_CLAUDE_BIN" self-hosted-runner decode-token | jq -re '.act.attested_by.sub // .act.email // .act.sub'
```
Wrappers receive the absolute path to the runner's own binary in `CLAUDE_RUNNER_CLAUDE_BIN`; use that path rather than a PATH-resolved `claude` so the decode runs on the same binary the runner itself uses.
Use `jq -re` rather than `jq -r` so a missing claim causes a non-zero exit. With `-r` alone, a missing claim prints the literal string `null` and exits zero, which silently passes a bad value downstream. Pass `--no-verify` to `decode-token` only for offline inspection where the JWKS endpoint is unreachable.
## Claims reference
The table below lists the session token claims relevant to verification. Read identity from the `ccr:*` namespace and the `act` chain; the flat `account_email`, `organization_uuid`, and `account_uuid` claims are backward-compatibility duplicates that may be removed. Sessions created with an organization service key omit `act.email`, `ccr:account_id`, `account_email`, and `account_uuid`. The two email claims are optional for user-created sessions too: Anthropic records them at session creation only when the creating request's credentials carry an email, and a session dispatched from the CLI can lack both, so key identity on `act.sub` or `ccr:account_id` rather than on email. Tokens can also carry additional claims beyond this table; ignore claims you don't recognize.
| Claim | Type | Description |
| :------------------ | :--------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `iss` | string | Always `ccr`. |
| `sub` | string | `ccr:session:<session_id>`. |
| `aud` | array of strings | Always contains `anthropic-api`. For sessions in self-hosted environments the array also contains your environment ID, such as `ccpool_...`. Verify the environment ID, not `anthropic-api`. |
| `exp` | number | Expiry as a Unix timestamp. Four-hour default lifetime, eight-hour maximum. |
| `iat` | number | Issued-at as a Unix timestamp. |
| `jti` | string | Unique token identifier. |
| `ccr:role` | string | Always `session_worker` for session tokens. |
| `ccr:session_id` | string | The session ID. Same value as the suffix of `sub`. |
| `ccr:pool_id` | string | Your environment ID. Same value that appears in `aud`. |
| `ccr:org_id` | string | Your Anthropic organization ID. |
| `ccr:account_id` | string | The creating user's Anthropic account ID: the value of `act.sub` without the `user:` prefix, a tagged `user_...` ID. The same value the [spawn-runner hook](/docs/en/self-hosted-environments-configuration#the-spawn-runner-hook)'s `CLAUDE_RUNNER_ACCOUNT_ID` carries and [`--lock-to-account`](/docs/en/self-hosted-environments-reference#runner-cli-flags) accepts, so the three compare as equal strings. |
| `account_email` | string | Duplicate of `act.email`; absent whenever `act.email` is. |
| `organization_uuid` | string | Your Anthropic organization UUID. |
| `account_uuid` | string | The creating user's Anthropic account UUID. |
| `act` | object | [RFC 8693](https://www.rfc-editor.org/rfc/rfc8693) delegation chain. See [The `act` chain](#the-act-chain). |
### The `act` chain
The `act` claim records the full delegation path from the user who created the session down to the [environment](/docs/en/self-hosted-environments#key-concepts) whose secret admitted the runner, and the identity that created that secret. The creating user is the outermost actor, so `act.sub` identifies them directly.
| Path | Description |
| :---------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `act.sub` | The creating user's Anthropic user ID, in the form `user:<id>`. |
| `act.email` | The creating user's email address, when one was recorded at session creation. Don't require it; key on `act.sub`. |
| `act.attested_by` | The upstream identity provider's attestation for the creating user, when available. `act.attested_by.sub` is the subject your SSO provider, such as Google or Okta, issued. Prefer this over `act.email` when mapping to identities in your own systems. |
| `act.act` | The runner that spawned the session. `act.act.sub` is `ccr:runner:<runner_id>`. |
| `act.act.act` | The environment. `act.act.act.sub` is `ccr:pool:<pool_id>`. |
| `act.act.act.act` | The identity that created the environment secret the runner registered with. The chain ends here. |
## Scope derived credentials
The session token identifies the creating user, but don't treat it as equivalent to that user logging in directly. The token sits in an environment variable inside the session, so any code Claude runs, and any tool or MCP server the session starts, can read and present it.
Verification is also offline: a token that verifies against the JWKS stays valid until its `exp`, whatever has happened to the session since, and Anthropic doesn't publish a revocation feed for session tokens. Bound anything you derive from the token accordingly.
When your service exchanges the token for internal credentials, issue credentials scoped to what one coding session should reach:
* **Limit capabilities**: grant read and write access to the resources the session needs for coding tasks, not administrative capabilities the user holds elsewhere.
* **Limit lifetime**: bound derived credentials to the token's `exp`, or shorter.
* **Audit as the session**: record the `ccr:session_id` and `jti` alongside the user identity so you can trace actions back to a specific session.
## Related environment variables
The creator identity also appears in plain environment variables on two surfaces that never verify the token:
* **The [`spawn-runner` hook](/docs/en/self-hosted-environments-configuration#the-spawn-runner-hook), on the orchestrator**: the hook runs before any runner exists for a queued session and receives the creator identity in variables such as `CLAUDE_RUNNER_ACCOUNT_EMAIL` and `CLAUDE_RUNNER_ACCOUNT_ID`. The orchestrator reads them from the work order, the signed single-use token that authorizes spawning one runner, without verifying the work order's signature itself; the claims are trusted because the work order arrives over the orchestrator's connection to Anthropic, which the environment secret authenticates.
* **[Wrapper scripts](/docs/en/self-hosted-environments-configuration#wrapper-scripts), inside the session**: wrappers receive `CCR_SESSION_ACCOUNT_EMAIL`, the creator's email pre-extracted from the token without signature verification. The variable is suitable for labelling, such as commit trailers, not for auth decisions.
Use the plain variables for orchestrator-side decisions such as selecting a machine image. Use `CLAUDE_CODE_SESSION_ACCESS_TOKEN` when a downstream service needs independent cryptographic proof rather than trusting the runner's environment.
## What's next
* [Self-hosted environments](/docs/en/self-hosted-environments): the environment, runner, and session model; the [quickstart](/docs/en/self-hosted-environments-quickstart) and [Deploy to production](/docs/en/self-hosted-environments-deploy) hold setup and operations
* [Customize sessions](/docs/en/self-hosted-environments-configuration): wrapper scripts that consume the token, and the `spawn-runner` hook
* [Reference](/docs/en/self-hosted-environments-reference): CLI flags, environment variables, and metrics
self-hosted-environments-quickstart First recorded · 110 lines, first recorded
# Self-hosted environments quickstart ## Prerequisites ### Organization and roles ### Host and network ### Software on the runner host ## Set up an environment and runner ## Send a follow-up message to a running session ## What's next
The first capture of this source. The page was already there, and this is what it said.
# Self-hosted environments quickstart
> Set up your first self-hosted environment: install Claude Code, create the environment, start a runner, and route a session to it.
<Note>
Self-hosted environments are in public beta on Team and Enterprise plans; [Availability and limitations](/docs/en/self-hosted-environments#availability-and-limitations) covers the enablement path. This page gets your first session running; see [Self-hosted environments](/docs/en/self-hosted-environments) for what they are and [Deploy to production](/docs/en/self-hosted-environments-deploy) for hardening and fleet recipes.
</Note>
A [self-hosted environment](/docs/en/self-hosted-environments) runs Claude Code [cloud sessions](/docs/en/claude-code-on-the-web) on infrastructure your organization operates, executed by runner processes you deploy. This quickstart stands up your first one, the smallest that works: one runner on a single host, running one test session. There are two steps: [create the environment, start a runner, and route a session to it](#set-up-an-environment-and-runner), then [message that session from your terminal](#send-a-follow-up-message-to-a-running-session). You'll move between two surfaces: claude.ai for creating the environment, checking its status, and routing a session, and a terminal on the host for everything the runner does.
By the end you'll have an environment on the [**Cloud environments** admin page](https://claude.ai/admin-settings/cloud-environments), a runner polling for work, and a session running on your host. Before you connect real repositories or internal systems, work through [Deploy to production](/docs/en/self-hosted-environments-deploy), which covers the security posture, egress control, git credentials, and orchestration.
## Prerequisites
### Organization and roles
The claude.ai side needs:
* **Allow self-hosted environments** turned on by an [Owner or admin](/docs/en/cloud-environments#organization-shared-environments) on the [**Cloud environments** admin page](https://claude.ai/admin-settings/cloud-environments); the **New** button doesn't appear until it is. If you don't hold the role, someone who does can create the environment and hand you its secret; the runner and terminal steps on this page need no claude.ai role, and where a step checks status in the admin UI, the runner's own log lines give you the same signal.
* A [GitHub connection](/docs/en/claude-code-on-the-web#github-authentication-options) for your organization, so developers can pick repositories when they start sessions.
### Host and network
The runner host needs:
* A Linux or macOS host or container with outbound HTTPS to `api.anthropic.com`, to `claude.ai` and the download hosts it redirects to for the install step below, and to your git host for the clone; the [network requirements table](/docs/en/self-hosted-environments-deploy#network-requirements) has the full list. Windows isn't supported as a runner host; run the runner in a Linux container instead. Developer workstations aren't affected, since sessions start from claude.ai in a browser.
* A clock synchronized to real time, for example with NTP. Authentication fails when the clock is more than five minutes off; see [Troubleshooting](/docs/en/self-hosted-environments-deploy#troubleshooting).
### Software on the runner host
Install on the host before you start:
* **Claude Code v2.1.224 or later**, with any of the [standard install methods](/docs/en/setup). The runner is part of the standard `claude` binary, and earlier versions don't recognize the `self-hosted-runner` subcommand. The native installer's default `latest` channel carries each release as soon as it's published; the `stable` channel, the Homebrew `claude-code` cask, and the stable apt, dnf, and apk repositories trail by about a week. To pin the exact version your fleet runs, see [Install a specific version](/docs/en/setup#install-a-specific-version). For container images, see the Dockerfile in [Deploy to production](/docs/en/self-hosted-environments-deploy#build-the-runner-image).
* **Git 2.24 or newer**. Some git options on the deploy page need newer versions; [Configure git](/docs/en/self-hosted-environments-deploy#configure-git) states each floor.
Confirm the host is ready:
```bash theme={null}
claude self-hosted-runner --help
```
A ready host prints the runner's usage text, listing flags such as `--environment-secret-file`. On versions older than 2.1.224, the command prints the general `claude --help` output instead; upgrade with `claude update` or reinstall from the `latest` channel.
## Set up an environment and runner
Claude Code includes a guided setup: an interactive Claude Code session that walks you through creating the environment in the admin UI, starts a local runner with the secret file you save, confirms that the runner registers, and writes a cheat sheet to `./runner-setup/CHEAT-SHEET.md`. Run it on a machine where you've signed in with `claude auth login` using an account that holds an Owner or admin role; it isn't available with API keys or third-party model providers. On hosts where an interactive session isn't possible, use the manual steps below instead. Confirm the [version check](#software-on-the-runner-host) passed first: on versions older than 2.1.224, this command starts an ordinary Claude session with the words as the prompt instead of the guided setup. To start the guided setup, run the setup subcommand and follow the prompts:
```bash theme={null}
claude self-hosted-runner setup
```
To set up manually instead:
<Steps>
<Step title="Create an environment">
Go to the [**Cloud environments** page](https://claude.ai/admin-settings/cloud-environments) in admin settings. Under **Self-hosted environments**, select **New**, name the environment, and select **Create**. On the wizard's second step, select **Copy environment key** to copy the environment secret, which the admin UI labels an environment key. claude.ai shows the secret once, and you can't retrieve it later; it expires 365 days after creation. The environment's `ccpool_...` ID stays visible in its detail dialog; you'll need it for the `aud` check in [token verification](/docs/en/self-hosted-environments-identity) and for dispatching [test sessions from CI](/docs/en/self-hosted-environments-testing#run-the-test-loop).
If you lose the secret or need to rotate it, create a new secret from the environment's **Configuration** tab, roll the new secret out to your runners, then revoke the old one. Runners holding a revoked secret fail their next authenticated poll and exit, logging `poll auth failed`, and your orchestrator restarts them with the new secret.
</Step>
<Step title="Start a runner">
Create the secret directory. This step and the next need root for the `/etc/claude` path; any path the runner process can read works, so adjust both commands and the `--environment-secret-file` value together if you use a different one.
```bash theme={null}
mkdir -p /etc/claude
```
Write the environment secret to a file. The command below reads from your terminal so the secret stays out of shell history: paste the value you copied, press Enter, then Ctrl-D, and the subshell's `umask` makes the file readable only by its owner.
```bash theme={null}
(umask 077 && cat > /etc/claude/environment-secret)
```
Choose a base directory, replacing `<writable-dir>` in the runner command below with an absolute path that the runner can write to or create. The runner creates the directory at startup, then checks repositories out and creates per-session directories under it. Without `--base-dir` it uses `/workspace`, which only works if that directory already exists and is writable or you start the runner as root.
If the runner can't create or write to the path, it exits at startup with an error naming the directory instead of registering. See [Troubleshooting](/docs/en/self-hosted-environments-deploy#troubleshooting).
Then start the runner with `--environment-secret-file` and `--base-dir`. The runner registers with your environment and begins polling for work. If the runner exits, restart it by hand. Production deployments run the runner under an orchestrator that restarts exited runners, normally with a fresh filesystem per restart; [Reuse a pre-warmed checkout](/docs/en/self-hosted-environments-deploy#reuse-a-pre-warmed-checkout) covers the supported persistent-disk setup.
```bash theme={null}
claude self-hosted-runner --environment-secret-file '/etc/claude/environment-secret' --base-dir '<writable-dir>'
```
</Step>
<Step title="Verify the runner appears">
Return to the [**Cloud environments** page](https://claude.ai/admin-settings/cloud-environments). Your environment's status changes from **No runners deployed** to **Healthy** within a few seconds of the runner starting; open the environment and select **Activity** to see the runner itself.
</Step>
<Step title="Route a session to the environment">
Start a session at claude.ai/code and select your environment from the environment picker, where self-hosted environments appear alongside Anthropic-hosted ones. The runner clones with whatever git credentials the host already has, so pick a repository this host can already clone, or a public one; credential options for private repositories in production are on [Configure git](/docs/en/self-hosted-environments-deploy#configure-git). The next available runner picks up the queued session and logs `Picked up session <session-id>` along with its active count and capacity, so you can confirm from the runner's own output which host took the session. Watch the session work and read Claude's replies at [claude.ai/code](https://claude.ai/code). If the session sits queued instead, see [Troubleshooting](/docs/en/self-hosted-environments-deploy#troubleshooting).
</Step>
</Steps>
The runner exits by design once its active sessions finish; see [Runner lifecycle](/docs/en/self-hosted-environments#runner-lifecycle). For production, deploy it under an orchestrator that restarts it on exit. See [Deploy to production](/docs/en/self-hosted-environments-deploy).
## Send a follow-up message to a running session
Once a session is running on your environment, send it a follow-up from the `claude` CLI on any machine where you're logged in with `claude auth login`; the command doesn't need to run from the machine that started the session. The queue-and-exit form posts one message:
```bash theme={null}
claude -p "your message" --cloud <session-id>
```
For `<session-id>`, pass the bare `session_...` or `cse_...` ID or the session's claude.ai/code URL. A successful send prints `Sent to cloud session.` with the session ID and a view link. Accepted ID forms, JSON output, the interactive attach form, the account and policy requirements, and the error reference are on [Send follow-ups from the CLI](/docs/en/claude-code-on-the-web#send-follow-ups-from-the-cli), since the command works the same against Anthropic-hosted sessions.
## What's next
* [Deploy to production](/docs/en/self-hosted-environments-deploy): harden the deployment, control egress, configure git credentials, and run the fleet under Kubernetes or Compose
* [Customize sessions](/docs/en/self-hosted-environments-configuration): wrapper scripts, lifecycle hooks, on-demand runners, MCP servers, and permissions
* [Test end to end](/docs/en/self-hosted-environments-testing): a CI smoke test that dispatches a session and reads Claude's replies
self-hosted-environments-reference First recorded · 310 lines, first recorded
# Self-hosted environments reference ## Runner CLI flags ## Orchestrator CLI flags ### SCM connector flags ## Environment-variable-only settings ## Telemetry ## Health endpoint ## Prometheus metrics ### Pass through session-child metrics ### Session lifecycle counter semantics ## What's next
The first capture of this source. The page was already there, and this is what it said.
# Self-hosted environments reference
> Complete reference for the self-hosted runner and orchestrator: CLI flags, environment variables, and Prometheus metrics.
<Note>
Self-hosted environments are in public beta on Team and Enterprise plans; an [Owner or admin](/docs/en/cloud-environments#organization-shared-environments) enables them by turning on **Allow self-hosted environments** on the [**Cloud environments** admin page](https://claude.ai/admin-settings/cloud-environments). This page is the flag and metric reference; see the [quickstart](/docs/en/self-hosted-environments-quickstart) for setup and [Deploy to production](/docs/en/self-hosted-environments-deploy) for the fleet recipes.
</Note>
This page is the reference for the two processes you run in a [self-hosted environment](/docs/en/self-hosted-environments): the runner, which executes Claude Code [cloud sessions](/docs/en/claude-code-on-the-web) on your hosts, and the optional autoscaling orchestrator, which starts runners as sessions queue. Each has its own flag table. Both run on Linux or macOS hosts, which the defaults such as `/workspace` and `~/.claude` assume. Run `claude self-hosted-runner --help` for the authoritative list on your installed version.
Metric series and a few API fields still use `pool` for what these pages call an environment; both terms name the same thing. The environment ID is the `pool_id` field, with the form `ccpool_...`: wherever these pages show a `pool` identifier, it names the environment. CLI flags and environment variables spell it `environment`, such as `--environment-secret-file`; the deprecated `pool` spellings still work, as the [`--environment-secret-file` row](#runner-cli-flags) describes.
## Runner CLI flags
Most flags have a corresponding environment variable. When both are set, the flag takes precedence. Duration flags take minutes or seconds on the CLI, but the paired environment variable is always in milliseconds, indicated by the `_MS` suffix, and the Default column shows the flag's unit: `--exit-if-unused-min 10` is equivalent to `SELF_HOSTED_RUNNER_IDLE_SHUTDOWN_MS=600000`, and a Helm value like `SELF_HOSTED_RUNNER_STARTUP_TIMEOUT_MS: "15"` means 15 milliseconds, not the 15-minute default.
| Flag | Env var | Default | Description |
| :------------------------------------ | :------------------------------------------------ | :---------------------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `--api-url <url>` | none | `https://api.anthropic.com` | API base URL. Override only for testing. |
| `--base-dir <path>` | `SELF_HOSTED_RUNNER_BASE_DIR` | `/workspace`; none on Windows | Directory for repository checkouts and per-session working directories. The runner needs write access to this path or its parent. The runner creates the directory at startup and exits with `cannot create or write to base directory` when it can't create or write to it. Before v2.1.225, the runner created the directory when the first session started, so an unusable path failed sessions rather than startup. On Windows, which isn't a supported runner host, there is no default: the runner exits at startup unless you pass the flag or set the variable. Use the same value on every runner in an environment. See [Keep the base directory and capacity identical across runners](/docs/en/self-hosted-environments-deploy#keep-the-base-directory-and-capacity-identical-across-runners). |
| `--capacity <n>` | none | `1` | Maximum concurrent sessions this runner handles. All sessions belong to the same locked account. Use the same value on every runner in an environment; see [Keep the base directory and capacity identical across runners](/docs/en/self-hosted-environments-deploy#keep-the-base-directory-and-capacity-identical-across-runners). |
| `--configure-git` | `SELF_HOSTED_RUNNER_CONFIGURE_GIT=1` | off | Write global git identity and enable Anthropic commit signing at startup. See [Configure git](/docs/en/self-hosted-environments-deploy#configure-git). |
| `--confine-repo-settings <mode>` | `SELF_HOSTED_RUNNER_CONFINE_REPO_SETTINGS` | `warn` | Sets the mode of the guard that flags a session when a repository's committed settings try to grant write or read access outside that session's own workspace, set environment variables, or override the operator's sandbox or hooks posture, such as `sandbox.enabled: false` or `disableAllHooks`. The default `warn` logs the violation and still starts the session, `enforce` refuses the session, and `off` disables the scan. See [Harden your deployment](/docs/en/self-hosted-environments-deploy#harden-your-deployment). |
| `--debug-token-dir <path>` | `SELF_HOSTED_RUNNER_DEBUG_TOKEN_DIR` | unset | Write live tokens to disk for inspection. Debug only; don't use in production. |
| `--drain-grace-sec <n>` | `SELF_HOSTED_RUNNER_DRAIN_GRACE_MS` | `0` | Controls when the runner exits after its active sessions finish: `0` exits immediately without polling for more, and a positive value keeps the runner alive and re-polling the locked account's queue for that many seconds first, at the cost of the per-session container isolation described in the [hardening section](/docs/en/self-hosted-environments-deploy#harden-your-deployment) |
| `--drain-wait-sec <n>` | `SELF_HOSTED_RUNNER_DRAIN_WAIT_MS` | `0` | On `SIGTERM`, wait up to N seconds for each session's in-flight turn and background tasks to finish before terminating the child. During this wait, the runner counts a background task that has just finished as still running until the follow-up turn that reads its result starts, for at most the [`SELF_HOSTED_RUNNER_BG_RESULT_GRACE_MS`](#environment-variable-only-settings) window. |
| `--environment-secret-file <path>` | `SELF_HOSTED_RUNNER_ENVIRONMENT_SECRET` | required | Path to a file containing the environment secret, or, for runners spawned by the [orchestrator](/docs/en/self-hosted-environments-configuration#on-demand-runners), the single-use work-order JWT. `SELF_HOSTED_RUNNER_ENVIRONMENT_SECRET` carries the secret value directly, not a file path. The older `--pool-secret-file` flag and `SELF_HOSTED_RUNNER_POOL_SECRET` variable still work and print a deprecation notice to stderr; preview-program runner builds older than 2.1.216 only recognize those older names. |
| `--exec-path <path>` | `SELF_HOSTED_RUNNER_EXEC_PATH` | own binary | Binary or wrapper script to spawn for each session. See [Wrapper scripts](/docs/en/self-hosted-environments-configuration#wrapper-scripts). |
| `--exit-if-unused-min <n>` | `SELF_HOSTED_RUNNER_IDLE_SHUTDOWN_MS` | `0` | Exit after N minutes of polling with no work ever assigned, for autoscaler scale-down. `0` disables. |
| `--git-host-rewrite <from>=<to>` | none | unset | Rewrite `https://<from>/...` source URLs to `https://<to>/...` before cloning, for split-horizon DNS. Repeatable; flag only. |
| `--git-ssh-rewrite <host>` | none | unset | Rewrite `https://<host>/...` source URLs to `git@<host>:...` before cloning, for SSH-only git hosts. Repeatable; flag only. |
| `--health-port <port>` | `SELF_HOSTED_RUNNER_HEALTH_PORT` | `8080` | Port for the `/healthz` and `/metrics` listener. Set `0` to disable. |
| `--hooks-dir <path>` | `SELF_HOSTED_RUNNER_HOOKS_DIR` | unset | Directory of lifecycle hook scripts. See [Lifecycle hooks](/docs/en/self-hosted-environments-configuration#lifecycle-hooks). |
| `--kill-session-after-min <n>` | `SELF_HOSTED_RUNNER_MAX_LIFETIME_MS` | `0` | Terminate a session child once it has lived N minutes wall-clock, as a safety limit for stuck sessions. A kill that falls mid-turn is deferred until the turn finishes, bounded by a grace window. `0` disables. |
| `--lock-to-account <id>` | `SELF_HOSTED_RUNNER_LOCK_TO_ACCOUNT` | unset | Pre-lock the runner to a specific account at startup instead of locking on first session. Accepts an email address or `user_...` ID in the environment's organization. |
| `--log-file <path>` | `SELF_HOSTED_RUNNER_LOG_FILE` | unset | Mirror runner logs to a file in addition to stdout and stderr, created with `0600` permissions. Required for `self-hosted-runner doctor` to tail logs locally. |
| `--log-level <level>` | none | `info` | `info` or `debug` |
| `--post-session-hook-timeout-sec <n>` | `SELF_HOSTED_RUNNER_POST_SESSION_HOOK_TIMEOUT_MS` | `60` | Budget for the [`post-session` hook](/docs/en/self-hosted-environments-configuration#post-session) on every session end, including runner shutdown |
| `--push-outcome-on-release` | `SELF_HOSTED_RUNNER_PUSH_OUTCOME_ON_RELEASE` | off | On a runner-initiated session end such as a drain or idle release, push tracked outcome branches to `origin` before deleting the workspace, so in-flight commits survive a restart. Best-effort; adds 30 seconds to the shutdown budget, and requires git 2.29 or newer to resume from the pushed branch. Restrict push access to `claude/*` refs before enabling; see [Resumed sessions lose unpushed work](/docs/en/self-hosted-environments-deploy#additional-limitations). Repositories checked out via a `checkout` lifecycle hook aren't pushed; snapshot those from the [`post-session` hook](/docs/en/self-hosted-environments-configuration#post-session) instead. |
| `--release-idle-session-min <n>` | `SELF_HOSTED_RUNNER_SESSION_IDLE_MS` | `0` | Release a session slot after N minutes of inactivity once a turn finishes or the session waits for the user's action. A session that's still mid-turn, including one holding a never-finishing background task or an approval requested from inside a running tool call, doesn't count as idle; pair with `--kill-session-after-min` as the hard backstop. After a session's background task finishes, the runner considers the session busy until the follow-up turn that reads the result starts, for at most the [`SELF_HOSTED_RUNNER_BG_RESULT_GRACE_MS`](#environment-variable-only-settings) window. A release that leaves the runner with no active sessions starts the same exit path as a normal drain, governed by `--drain-grace-sec`. `0` disables. |
| `--retire-at <epoch-seconds>` | `SELF_HOSTED_RUNNER_RETIRE_AT` | unset | Retire the runner at an absolute Unix timestamp in seconds, for infrastructure that kills the runner at a known time; [Runner lifecycle](/docs/en/self-hosted-environments#runner-lifecycle) describes the release sequence and how to size the margin. Values before 2001 or after the year 5138 are rejected by the flag and ignored by the environment variable. |
| `--session-stop-grace-sec <n>` | `SELF_HOSTED_RUNNER_SESSION_STOP_GRACE_MS` | `5` | How long to wait for the Claude process to exit cleanly after a session ends, before force-killing it. Raise the value if the child's own `SessionEnd` hooks need more time. |
| `--startup-timeout-min <n>` | `SELF_HOSTED_RUNNER_STARTUP_TIMEOUT_MS` | `15` | Release a session slot if the child hasn't signaled that it initialized within N minutes of spawn. Cleared by the child's init signal on the [activity channel](/docs/en/self-hosted-environments-configuration#keep-stdin-and-file-descriptor-3-attached), not by ordinary output, after which `--release-idle-session-min` takes over. `0` disables. |
| `--trust-workspace [bool]` | `SELF_HOSTED_RUNNER_TRUST_WORKSPACE` | on | Seed persisted trust for each session's repository paths so repo-committed `permissions.allow` and `additionalDirectories` are honored. Set `false` to drop repo-committed permission grants and configure allow rules in the host config's `settings.json` instead; repository-committed `sandbox.*` settings still apply either way, which is why the [repo-settings guard](/docs/en/self-hosted-environments-deploy#harden-your-deployment) scans them regardless of this flag. |
| `--use-anthropic-git-proxy` | `CLAUDE_RUNNER_USE_GIT_PROXY=1` | off | Clone via Anthropic's git proxy instead of customer-managed git auth. Requires `--capacity 1` and git 2.32 or newer; the runner refuses to start otherwise. Supersedes the rewrite flags. |
Most duration flags have a maximum, chosen to keep each timeout inside the runtime's 32-bit timer ceiling of roughly 24.85 days. The `--*-min` flags cap at 10080 minutes, 7 days; `--drain-grace-sec` at 604800 seconds, also 7 days; and `--drain-wait-sec` at 86400 seconds, 24 hours. `--session-stop-grace-sec` and `--post-session-hook-timeout-sec` are uncapped. Overrunning a cap behaves differently per surface:
* **Flag**: startup fails with an error.
* **Environment variable**: the runner clamps the value to the timer ceiling rather than rejecting it.
## Orchestrator CLI flags
The `self-hosted-runner orchestrator` subcommand, which spawns [on-demand runners](/docs/en/self-hosted-environments-configuration#on-demand-runners), accepts `--api-url`, `--environment-secret-file`, `--hooks-dir`, `--health-port`, and `--log-level` with the same defaults as the runner and, where the runner's flag has one, the same environment variable, except that `--hooks-dir` is required and must contain a `spawn-runner` hook. It also takes its own flags:
| Flag | Default | Description |
| :------------------------------- | :------ | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `--hook-concurrency <n>` | `4` | Maximum `spawn-runner` hooks running in parallel. Also caps how many spawn requests are claimed per poll. |
| `--hook-timeout <sec>` | `60` | Terminate the hook's process tree after this many seconds. The timeout plus its 5-second kill grace must stay below `--expected-spawn-seconds`; the orchestrator enforces this at startup. |
| `--expected-spawn-seconds <sec>` | `120` | Expected p99 boot time for spawned runners, in the server-enforced range 10 to 3600. Sent on every poll as the server-side lease; if no runner registers before it elapses, the session is re-offered with a fresh order ID. All replicas must share this value. |
| `--min-idle <n>` | `0` | Keep at least N idle session slots free by spawning standby runners proactively. `0` disables pre-warming. Pair with the runner's `--exit-if-unused-min` so surplus standby runners reclaim themselves. |
| `--debug-dir <path>` | unset | Write each spawn request's work order and hook stderr to disk. Debug only; never set in production. |
### SCM connector flags
The orchestrator can hold a standing WebSocket connection to Anthropic's control plane so that hosted pre-session flows, such as the repository picker and the branch or ref resolver, can reach a GitHub Enterprise Server host that's only routable from inside your network. The connector stays off unless you set `--scm-connector-host`.
| Flag | Default | Description |
| :------------------------------------------------------ | :----------------------------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `--scm-connector-host <host[:port]>` | unset | GitHub Enterprise Server hostname to forward requests to. Port defaults to `443`. Setting this flag enables the connector. |
| `--scm-connector-id <n>` | required with `--scm-connector-host` | The numeric ID of your organization's GitHub Enterprise Server connection. Contact your Anthropic account team for the value when you enable the connector. |
| `--scm-connector-provider <slug>` | `ghe` | Path segment identifying the provider, matching `^[a-z0-9-]{1,32}$`. |
| `--scm-connector-ca-file <path>` | unset | Extra CA bundle, in PEM format, for TLS connections to the GitHub Enterprise Server host. |
| `--scm-connector-host-rewrite <from>=<to_host:to_port>` | unset | For end-to-end testing only: redirects the TCP connection while keeping the Host header and TLS SNI as `--scm-connector-host`. |
The connector authenticates with the orchestrator's existing environment secret and reconnects automatically: with exponential backoff on a dropped connection, or a fixed 30-second delay when the control plane closes the connection because another orchestrator replica already holds it.
## Environment-variable-only settings
These runner settings are read from the environment only and cover behavior most deployments leave at the default:
| Env var | Default | Description |
| :----------------------------------------- | :---------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `SELF_HOSTED_RUNNER_BG_RESULT_GRACE_MS` | `30000` | How long the runner considers a session busy after a background task finishes while the follow-up turn that reads the result hasn't started. The [`--drain-wait-sec` and `--release-idle-session-min` rows](#runner-cli-flags) describe where the hold applies on drain and idle release, and [Runner lifecycle](/docs/en/self-hosted-environments#runner-lifecycle) describes where it applies on `--retire-at` retirement. `0` or an unusable value falls back to the default, so the hold can't be turned off. Requires Claude Code v2.1.228 or later. |
| `SELF_HOSTED_RUNNER_HOST_CONFIG_DIR` | `~/.claude` | Directory captured into the runner's startup snapshot and seeded into each session's `CLAUDE_CONFIG_DIR`; changes on disk apply after a runner restart. Setting the variable also moves where the runner reads `.claude.json` for [MCP seeding](/docs/en/self-hosted-environments-configuration#mcp-servers), so setting it, including to its own default, relocates that lookup; point at an empty directory to disable seeding entirely. |
| `SELF_HOSTED_RUNNER_MAX_LIFETIME_GRACE_MS` | `900000` | Bounds how long a `--kill-session-after-min` kill is deferred while waiting for an in-flight turn to finish |
| `SELF_HOSTED_RUNNER_SIGKILL_GRACE_MS` | `30000` | How long the runner waits for the OS to deliver `SIGKILL` to a child stuck in uninterruptible I/O before exiting itself. Floored at `--post-session-hook-timeout-sec` plus 15 seconds, and 30 more when `--push-outcome-on-release` is set, so the effective minimum is 75 seconds at defaults. |
| `CLAUDE_RUNNER_FETCH_DEPTH` | `50` | Git fetch depth for fresh clones. Set a positive integer, or `full` or `0` for a complete fetch. Repositories already present in the workspace keep their existing depth. |
| `CLAUDE_RUNNER_SKIP_GIT_VERIFY` | unset | When `1`, skip the `.git` presence check after a `checkout` hook runs. Set this when your hook materializes a non-git source. |
| `FORCE_AUTOUPDATE_PLUGINS` | unset | When `1`, let plugin marketplaces auto-update even though the binary is pinned |
| `CLAUDE_CODE_DISABLE_ARTIFACT` | unset | When `1`, disable the Artifact tool in sessions regardless of the organization's admin setting, and drop the `*.frame.claudeusercontent.com` egress requirement |
## Telemetry
Session children send operational telemetry to Anthropic unless you turn it off. No code or repository content is sent. Set telemetry variables on the runner process; the runner re-asserts them after applying server-provided environment variables, so the operator's setting always takes precedence.
One control is specific to self-hosted environments: `CLAUDE_CODE_BYOC_ENABLE_DATADOG=1` opts in to Datadog operational metrics, which are off by default in self-hosted environments. The general Claude Code telemetry controls, `DISABLE_TELEMETRY`, `DO_NOT_TRACK`, `DISABLE_ERROR_REPORTING`, and `CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC`, apply to session children as documented in the [environment variable reference](/docs/en/env-vars). `DISABLE_GROWTHBOOK` is related but different: setting `DISABLE_GROWTHBOOK=1` disables feature-flag fetching, and telemetry stays on unless `DISABLE_TELEMETRY` is also set.
`CLAUDE_CODE_ENABLE_TELEMETRY` is unrelated: it enables OpenTelemetry export to your own collector, as described in [Monitoring](/docs/en/monitoring-usage), and doesn't control Anthropic's analytics.
## Health endpoint
The runner serves `GET /healthz` on the configured health port. The response is `200 OK` whenever the process is alive, whatever state the poll loop is in, so an HTTP probe on this endpoint detects a dead process only. The JSON body describes current state:
```json theme={null}
{
"status": "ok",
"runner_id": "ccrunner_...",
"active_sessions": 2,
"last_poll_at": "2026-03-31T18:04:11.220Z",
"last_poll_age_ms": 842
}
```
Use `last_poll_age_ms` as a liveness signal in custom probes; a value that grows unbounded indicates the poll loop is stuck. Both `last_poll_at` and `last_poll_age_ms` are `null` until the first poll completes.
The orchestrator serves its own `/healthz` on its health port. Its endpoint always returns `200`, and the body carries a `connected` field reporting whether the most recent poll succeeded, plus per-state spawn-queue counts in `queue_counts`. Gate readiness and alerting on `connected` rather than the status code.
When the [SCM connector](#scm-connector-flags) is configured, the orchestrator's `/healthz` body also carries `scm_connector_connected` and a `scm_connector` object with `connected`, `last_connected_at`, `last_error`, `reconnects`, and `requests_forwarded`. Both fields are `null` when `--scm-connector-host` isn't set.
## Prometheus metrics
Each runner serves Prometheus metrics at `GET /metrics` on the same port as `/healthz`. Key series:
| Series | Notes |
| :-------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `claude_code_self_hosted_runner_info{runner_id,version,client_label}` | Always `1`; useful for fleet inventory and version-drift detection |
| `claude_code_self_hosted_runner_capacity` | Configured `--capacity` |
| `claude_code_self_hosted_runner_active_sessions` | Sessions currently running |
| `claude_code_self_hosted_runner_locked_account{email}` | Present once the runner has locked to a user. The label value is the account email, and the runner always emits it once locked; if your metrics store is broadly readable, drop or hash the label at scrape time, for example with Prometheus `metric_relabel_configs`. |
| `claude_code_self_hosted_runner_last_poll_age_seconds` | Seconds since the last successful poll. Alert if over 60. |
| `claude_code_self_hosted_runner_poll_errors_total{error_kind}` | Cumulative PollWork failures by kind: `transport`, `timeout`, `5xx`, `429`, or `4xx`. All five series are present from process start; alert on `rate(...[5m]) > 0`. |
| `claude_code_self_hosted_runner_sessions_started_total{client_platform}` | Session child processes spawned over the runner's lifetime, one series per session origin such as `web_claude_ai`, `ios`, `android`, `desktop_app`, or `claude_code_cli`, or `unknown` when the server didn't send one. The Slack values `claude_in_slack` and `claude-in-slack` are reserved: [Claude Tag](https://claude.com/docs/claude-tag/overview) sessions can't route to self-hosted environments yet, so neither value appears until that support ships. Use `sum()` for the fleet total. |
| `claude_code_self_hosted_runner_sessions_completed_total{client_platform}` | Sessions that ended cleanly, labeled the same way. Broader than a plain clean exit: see [session lifecycle counter semantics](#session-lifecycle-counter-semantics) for what counts. |
| `claude_code_self_hosted_runner_sessions_failed_total{client_platform}` | Sessions that ended in failure, labeled the same way. Same caveat: see [session lifecycle counter semantics](#session-lifecycle-counter-semantics). |
| `claude_code_self_hosted_runner_sessions_interrupted_total{client_platform}` | Sessions the runner terminated for an operational reason rather than a session outcome, labeled the same way. See [session lifecycle counter semantics](#session-lifecycle-counter-semantics). |
| `claude_code_self_hosted_runner_initializing_sessions` | Sessions currently in the init phase, from assignment until the child's init event |
| `claude_code_self_hosted_runner_session_init_duration_seconds` | Histogram of session init durations |
| `claude_code_self_hosted_runner_session_init_errors_total` | Sessions that failed before reaching init: a checkout hook failure, git prep, token issue, or a pre-init child crash |
| `claude_code_self_hosted_runner_session_start_hook_errors_total` | `SessionStart` hooks that reported an error outcome, one per failing hook execution |
| `claude_code_self_hosted_runner_session_idle_seconds{session_id,client_platform}` | Per-session gauge of seconds since the session went idle. Useful for terminating sessions stuck on an unanswered permission prompt. |
The orchestrator serves its own series at `GET /metrics` on the same port as its `/healthz`:
| Series | Notes |
| :-------------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `claude_code_self_hosted_orchestrator_info{version,pool_id,orchestrator_uuid,hostname}` | Always `1` |
| `claude_code_self_hosted_orchestrator_connected` | `1` when the most recent poll succeeded; drops to `0` after any failed poll, whatever the failure kind |
| `claude_code_self_hosted_orchestrator_last_poll_age_seconds` | Seconds since the last poll attempt, success or failure, unlike the runner's identically-named metric, which measures since the last success; pair with `connected` to catch failing polls. The orchestrator's poll loop waits on hook execution, so alert above `--hook-timeout` plus a margin, around 90 seconds at defaults, rather than a flat 60. |
| `claude_code_self_hosted_orchestrator_poll_errors_total{error_kind}` | Cumulative PollSpawnHints failures by kind: `transport`, `timeout`, `5xx`, `429`, or `4xx`. All five series are present from process start; alert on `rate(...[5m]) > 0`. |
| `claude_code_self_hosted_orchestrator_queue_pending_sessions` | Spawn requests claimable right now |
| `claude_code_self_hosted_orchestrator_queue_backing_off_sessions` | Spawn requests in retry backoff after a retryable hook failure |
| `claude_code_self_hosted_orchestrator_queue_circuit_broken_sessions` | Spawn requests blocked until an Owner or admin retries them from the environment's **Activity** tab; alert if above zero |
| `claude_code_self_hosted_orchestrator_pool_pending_sessions` | Total sessions waiting on a runner for this environment. Environment-wide aggregate, identical on every orchestrator instance: use `MAX` rather than `SUM` across instances. |
| `claude_code_self_hosted_orchestrator_pool_active_sessions` | Sessions currently assigned to an alive runner in this environment. Environment-wide aggregate, identical on every orchestrator instance: use `MAX` rather than `SUM` across instances. |
| `claude_code_self_hosted_orchestrator_spawn_hooks_total{result}` | Cumulative `spawn-runner` hook outcomes: `ok`, `retryable`, `non_retryable`. Counts orchestrator hook invocations, not session children the runners spawn: not comparable to `sessions_started_total`, since capacity above one, warm pools, and runners spawned again for the same session all diverge the two. |
| `claude_code_self_hosted_orchestrator_spawn_hook_duration_seconds` | Histogram of hook durations |
| `claude_code_self_hosted_orchestrator_warm_hints_dispatched_total` | Standby spawn requests dispatched since process start |
| `claude_code_self_hosted_orchestrator_session_queue_wait_seconds` | Histogram of seconds each session waited in the queue before the orchestrator claimed it for spawn, recorded from the queue-wait timestamp the control plane sends with each session's spawn request. Use for p50/p99 queue-time alerting. Pre-warming spawns aren't sampled. |
| `claude_code_self_hosted_orchestrator_clock_skew_seconds` | Local-minus-server clock skew; diagnostic, present once measured |
| `claude_code_self_hosted_orchestrator_scm_connector_connected` | `1` when the [SCM connector](#scm-connector-flags)'s WebSocket is open; `0` while dialing or backing off. Absent when `--scm-connector-host` isn't set. |
| `claude_code_self_hosted_orchestrator_scm_connector_requests_forwarded_total` | Cumulative HTTP requests proxied to the configured SCM host since process start. Absent when `--scm-connector-host` isn't set. |
For autoscaling, pick the series that matches your scaling style and gate it before it feeds the scaler:
* **Queue-depth scaling**: feed `claude_code_self_hosted_orchestrator_pool_pending_sessions` into your HPA or KEDA scaler, not `queue_pending_sessions`.
* **Capacity scaling**: scale on the ratio of the runner's `active_sessions` to `capacity`.
* **Gate on `connected`**: filter the query with `claude_code_self_hosted_orchestrator_connected == 1` per instance, so a disconnected replica's stale value doesn't feed the scaler.
During a full poll outage, every replica disconnected, the gated query returns no data. HPA holds the current replica count on a missing metric, but KEDA's Prometheus scaler at its default `ignoreNullValues: "true"` reads the empty result as zero and scales in; set `ignoreNullValues: "false"` on the ScaledObject, optionally with a `fallback` replica floor.
The following Prometheus Operator `PodMonitor` covers both processes. It selects pods by the `app.kubernetes.io/part-of: claude-code-self-hosted-runner` label and the named `health` port that the [Kubernetes recipe](/docs/en/self-hosted-environments-deploy#kubernetes) sets; adjust the namespaces to match your deployment:
```yaml theme={null}
# Example Prometheus Operator PodMonitor for the Claude Code self-hosted
# runner + orchestrator. Adjust the namespace and label selectors to match
# your deployment. Both the runner and the orchestrator serve /metrics on
# their --health-port (default 8080).
apiVersion: monitoring.coreos.com/v1
kind: PodMonitor
metadata:
name: claude-code-self-hosted-runner
namespace: monitoring
spec:
namespaceSelector:
matchNames:
- claude-runners
selector:
matchExpressions:
# Matches the runner Deployment from the Kubernetes recipe, plus any
# on-demand runner Jobs and orchestrator pods you label the same way
# and give a named 'health' containerPort.
- key: app.kubernetes.io/part-of
operator: In
values: [claude-code-self-hosted-runner]
podMetricsEndpoints:
- port: health
path: /metrics
interval: 30s
```
These sample alert rules are a starting point; tune the thresholds for your fleet size:
```yaml theme={null}
# Example Prometheus alert rules for the Claude Code self-hosted runner
# + orchestrator. Tune thresholds for your fleet size and SLOs.
groups:
- name: claude-code-self-hosted-runner
rules:
- alert: ClaudeRunnerPollStale
expr: claude_code_self_hosted_runner_last_poll_age_seconds > 60
for: 2m
labels: {severity: warning}
annotations:
summary: "Runner {{ $labels.pod }} has not polled in >60s"
- alert: ClaudeRunnerVersionDrift
expr: count(count by (version) (claude_code_self_hosted_runner_info)) > 1
for: 30m
labels: {severity: info}
annotations:
summary: "Runners are running mixed versions"
- alert: ClaudeRunnerInitErrorsHigh
expr: increase(claude_code_self_hosted_runner_session_init_errors_total[10m]) > 3
for: 5m
labels: {severity: warning}
annotations:
summary: "Runner {{ $labels.pod }}: >3 session init failures in 10m (checkout hook / git / token / pre-init crash)"
- alert: ClaudeRunnerPollErrors
expr: sum by (pod) (rate(claude_code_self_hosted_runner_poll_errors_total[5m])) > 0
for: 2m
labels: {severity: warning}
annotations:
summary: "Runner {{ $labels.pod }}: PollWork failing ({{ $value | humanize }}/s over 5m)"
- alert: ClaudeRunnerSessionStartHookErrors
expr: increase(claude_code_self_hosted_runner_session_start_hook_errors_total[10m]) > 3
for: 5m
labels: {severity: warning}
annotations:
summary: "Runner {{ $labels.pod }}: >3 SessionStart hook failures in 10m"
- name: claude-code-self-hosted-orchestrator
rules:
- alert: ClaudeOrchestratorDisconnected
expr: claude_code_self_hosted_orchestrator_connected == 0
for: 2m
labels: {severity: critical}
annotations:
summary: "Orchestrator {{ $labels.pod }} cannot reach the Anthropic control plane"
- alert: ClaudeOrchestratorPollStale
expr: claude_code_self_hosted_orchestrator_last_poll_age_seconds > 90
for: 2m
labels: {severity: warning}
annotations:
summary: "Orchestrator {{ $labels.pod }} has not polled in >90s (poll loop waits on hook execution)"
- alert: ClaudeOrchestratorCircuitBroken
expr: claude_code_self_hosted_orchestrator_queue_circuit_broken_sessions > 0
for: 1m
labels: {severity: critical}
annotations:
summary: "{{ $value }} sessions circuit-broken — spawn-runner hook is repeatedly non-retryable; fix infra then retry from the Activity tab"
- alert: ClaudeOrchestratorPollErrors
expr: sum by (pod) (rate(claude_code_self_hosted_orchestrator_poll_errors_total[5m])) > 0
for: 2m
labels: {severity: warning}
annotations:
summary: "Orchestrator {{ $labels.pod }}: PollSpawnHints failing ({{ $value | humanize }}/s over 5m)"
- alert: ClaudeOrchestratorSpawnHookFailing
expr: sum by (pod) (increase(claude_code_self_hosted_orchestrator_spawn_hooks_total{result!="ok"}[5m])) > 3
for: 5m
labels: {severity: warning}
annotations:
summary: "Orchestrator {{ $labels.pod }}: >3 spawn-runner hook failures in 5m"
```
### Pass through session-child metrics
Each session runs in its own child process with its own OpenTelemetry metrics; at `--capacity` above one, the runner rewrites how those child metrics are exposed. Setting `OTEL_METRICS_EXPORTER=prometheus` on the runner host and `CLAUDE_CODE_ENABLE_TELEMETRY=1` in the session's environment, for example from your [wrapper script](/docs/en/self-hosted-environments-configuration#wrapper-scripts) or the runner's own environment, which sessions inherit, re-exposes each child's counter and gauge instruments on the runner's own `/metrics` endpoint, alongside the runner's series. The runner rewrites the child's exporter to push over OTLP to a loopback-only receiver on the health port, tags each series with `session_id` and `client_platform` labels, and evicts a session's series when that session ends. Histograms don't pass through, and a child metric whose name would collide with the runner's own prefix is dropped.
At the default `--capacity 1`, the rewrite doesn't apply: the session's child binds its own Prometheus endpoint on port 9464 as usual.
### Session lifecycle counter semantics
The `sessions_started_total`, `sessions_completed_total`, `sessions_failed_total`, and `sessions_interrupted_total` counters classify each session by how it ended. Every spawned session child increments `sessions_started_total` at spawn time, and exactly one of the other three increments at exit, so `sessions_started_total` minus the sum of the other three equals the number of session children currently running.
* `completed`: the session ended cleanly. This covers the child exiting on its own with code `0`, the session being archived or deleted while the child was still connected, and the runner releasing the slot as a clean handoff: an idle release, a startup timeout, or a server-side deassign the poll loop noticed before the child exited. Increments `sessions_completed_total`.
* `failed`: the child exited on its own with a non-zero code, either a crash or a setup failure after spawn. Increments `sessions_failed_total`.
* `interrupted`: the runner terminated the child for an operational reason that's neither a session success nor a runner fault, such as a drain, for example a Kubernetes rolling restart sending `SIGTERM`, the max-lifetime watchdog `--kill-session-after-min`, or the `released=false` backstop: the runner terminates the child after the control plane declines three consecutive idle-release requests, each because a user message was still waiting to be processed. Increments `sessions_interrupted_total`.
The [`post-session` hook](/docs/en/self-hosted-environments-configuration#post-session)'s `CLAUDE_RUNNER_EXIT_REASON` doesn't use this classification for clean handoffs. The hook reports an idle release, a startup timeout, and a server deassign as `interrupted`, since from the hook's perspective the runner killed the child, while the counters above record those same events as `completed`, since nothing went wrong and the slot was handed back cleanly. If you reconcile hook receipts against `sessions_completed_total` directly, you undercount completions. Use the hook for per-session guarantees and the counters for aggregate rates.
On a one-shot environment, `--capacity 1` with the default `--drain-grace-sec 0`, each runner process exits moments after its one session ends. `sessions_completed_total`, `sessions_failed_total`, and `sessions_interrupted_total` increment only at session end, right before that exit, so a Prometheus scrape every 15 to 60 seconds rarely catches the increment before the runner's series disappears; these three end-of-session counters are the terminal counters the rest of this section refers to. `sessions_started_total` increments at spawn and stays visible for the life of the session, so it reliably shows up, but on a one-shot environment it reads closer to "sessions currently running" than a cumulative count.
Use the series in this table for the corresponding goal instead of the terminal counters:
| Goal | Use |
| :---------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Throughput | `claude_code_self_hosted_orchestrator_spawn_hooks_total{result="ok"}`, a counter on the long-lived orchestrator that increments once per successful `spawn-runner` hook and stays meaningful under `rate()`. It counts hook invocations rather than sessions, so pre-warming and repeated spawns for the same session diverge it from session counts. |
| Utilization | `sum(claude_code_self_hosted_runner_active_sessions)` against `sum(claude_code_self_hosted_runner_capacity)`, both gauges valid at every scrape regardless of runner lifetime |
| Backlog | `claude_code_self_hosted_orchestrator_pool_pending_sessions` for queue depth, and `claude_code_self_hosted_orchestrator_queue_circuit_broken_sessions`, alerting if above zero |
| Failures | `claude_code_self_hosted_runner_sessions_failed_total`, best effort: real crashes after spawn do increment it, and `rate()` is meaningful on runners that outlive their sessions with `--drain-grace-sec` above `0`. A one-shot environment has the same scrape-window problem as the other terminal counters, so treat any non-zero value you do see as worth investigating. Failures before spawn, such as a checkout hook failure, git preparation, or a token issue, appear only in `session_init_errors_total`. |
Cut at 300 lines. The page has the rest.
self-hosted-environments-testing First recorded · 235 lines, first recorded
# Test self-hosted environments end to end ## Install the capture hook on your test runner ### Save the hook files ### Before you start the runner ## Run the test loop ### `--environment` dispatch behavior ## Example script ## Remote test runners ## Authenticate from CI ### Long-lived CI host ### Ephemeral CI runners ## Create a dedicated test environment ### Mint the admin token ### Create the environment ### Delete the environment
The first capture of this source. The page was already there, and this is what it said.
# Test self-hosted environments end to end
> Verify a self-hosted runner image from CI: dispatch a session with the CLI, read Claude's replies through a Stop hook, and script the full loop.
<Note>
Self-hosted environments are in public beta on Team and Enterprise plans; [Availability and limitations](/docs/en/self-hosted-environments#availability-and-limitations) covers the enablement path. This page is the CI test recipe; see the [quickstart](/docs/en/self-hosted-environments-quickstart) for setup and [Deploy to production](/docs/en/self-hosted-environments-deploy) for the fleet recipes.
</Note>
In a [self-hosted environment](/docs/en/self-hosted-environments), Claude Code [cloud sessions](/docs/en/claude-code-on-the-web) run on a runner image you build and maintain. Before rolling a new image to your production environment, drive a full session against a test environment from a script: create a session, read Claude's reply, send a follow-up, and read that reply too. This is the shape of a CI smoke test that verifies your runner image, git access, and any custom tools before you promote a change.
This recipe assumes you've already [set up an environment and a runner](/docs/en/self-hosted-environments-quickstart#set-up-an-environment-and-runner), and that your CI job starts the runner process on the same host as the test script, the natural setup for testing a new runner image. A Stop hook you install on the runner writes each turn's final reply to a local file, and the script reads it from there, so the only calls to the Anthropic API are the two dispatches themselves. If your test runners are on separate infrastructure, see [Remote test runners](#remote-test-runners).
## Install the capture hook on your test runner
The read-back works through a Claude Code [Stop hook](/docs/en/hooks#stop): when Claude finishes a turn, the hook receives the final assistant message as `last_assistant_message` in its stdin JSON and appends it to `$E2E_REPLY_DIR/<session_id>.txt`. Install it the same way as the [commit-nudge Stop hook](/docs/en/self-hosted-environments-configuration#prompt-sessions-to-push-their-work), on the runner host's `~/.claude/`, which the runner seeds into every session.
### Save the hook files
Save the two files below on the runner host:
* The settings block: merge into `~/.claude/settings.json` on the runner host
* The script: save as `~/.claude/hooks/e2e-stop-hook-capture.sh` on the runner host and make it executable
```json theme={null}
{
"hooks": {
"Stop": [
{
"hooks": [
{
"type": "command",
"timeout": 10,
"command": "\"$CLAUDE_CONFIG_DIR/hooks/e2e-stop-hook-capture.sh\""
}
]
}
]
}
}
```
```sh theme={null}
#!/bin/sh
# Stop hook for testing a self-hosted environment end to end: writes each
# turn's final assistant reply to $E2E_REPLY_DIR/<session_id>.txt so a
# co-located test driver can read it without calling the Anthropic API.
# Install on the TEST runner only. Requires jq.
# No-op unless the driver is listening. Never fail the turn.
[ -n "${E2E_REPLY_DIR:-}" ] && [ -d "$E2E_REPLY_DIR" ] || exit 0
# CLAUDE_CODE_REMOTE_SESSION_ID is exported in cse_... form; the session
# id the dispatch CLI prints is in session_... form. Same id, different
# prefix.
sid=$(printf '%s' "${CLAUDE_CODE_REMOTE_SESSION_ID:-}" | sed 's/^cse_/session_/')
[ -n "$sid" ] || exit 0
# last_assistant_message is absent when the final assistant turn had no
# text, such as a tool-use-only turn. The `// empty` filter makes that a
# zero-byte write rather than the literal string "null".
jq -r '.last_assistant_message // empty' >> "$E2E_REPLY_DIR/$sid.txt" 2>/dev/null
exit 0
```
### Before you start the runner
Two things the hook depends on:
* Install it before you start the runner. The runner snapshots `~/.claude/` once at startup, so a hook added to a running runner takes effect only after a restart.
* Export `E2E_REPLY_DIR` to the runner process. The hook is a no-op when the variable is unset or the directory doesn't exist, so set it wherever you start the runner, such as the systemd unit, pod spec, or CI step. The test script below requires it too.
Install this hook only on runners serving your test environment. It writes every session's final reply to disk whenever `E2E_REPLY_DIR` exists, which is harmless on a throwaway CI runner but not something to carry into a production-environment runner image where the variable might be set by accident.
## Run the test loop
The `--environment` and `--ref` dispatch flags require Claude Code v2.1.224 or later on the machine that runs the script, the same floor as the runner itself. With the hook in place and a runner started on this host, the test script:
1. Creates a session on the test environment with `claude -p "<prompt>" --environment <environment-id> --output-format json`, run from a git checkout so the CLI can auto-detect the repository from the `origin` remote. The optional `--ref <branch>` bases the session's checkout on a named ref instead of local HEAD. The command creates the session, prints one line of JSON containing `session_id`, and exits without waiting for Claude's reply.
2. Waits for the reply to appear in `$E2E_REPLY_DIR/<session_id>.txt`, written by the Stop hook on the runner once the turn completes.
3. Sends a follow-up with `claude -p "<message>" --cloud <session_id> --output-format json` (see [Send a follow-up message to a running session](/docs/en/claude-code-on-the-web#send-follow-ups-from-the-cli)), which posts a user event to the existing session and exits.
4. Waits for the follow-up's reply the same way as step 2.
### `--environment` dispatch behavior
In a non-interactive run, with `-p` or a piped prompt, Claude Code creates the session, prints the session ID and a link to it, and exits. From a terminal, `claude --environment <id> "task"` starts an attached interactive cloud session on the environment instead.
The flag takes precedence over the [`remote.defaultEnvironmentId`](/docs/en/settings#available-settings) setting. It doesn't support `--output-format stream-json`, and can't be combined with flags that resume, attach to, or preconfigure a session, such as `--resume`, `--continue`, `--teleport`, `--session-id`, or `--init-only`. `--cloud` is rejected with a session ID or URL, and in non-interactive runs when it carries a description. A bare `--cloud` is treated as absent. From a terminal, you can pass the task as the `--cloud` description instead of a positional prompt.
## Example script
The script below runs the full loop against `$CLAUDE_TEST_ENVIRONMENT_ID`, your test environment's `ccpool_...` ID, shown in the environment's detail dialog on the admin page or returned by the [create-environment call](#create-a-dedicated-test-environment), and asserts on a sentinel phrase in each reply. Run it from a git checkout of the repository you want the session to work in, after starting a runner on this host with the capture hook installed and `E2E_REPLY_DIR` exported.
```bash theme={null}
#!/usr/bin/env bash
# End-to-end test against a self-hosted environment, using Stop-hook read-back.
# Prereqs: `claude auth login` has been run on this machine (see "Authenticate
# from CI" below); jq is installed; CLAUDE_TEST_ENVIRONMENT_ID names an
# environment whose runner is the one on this host, with the capture hook
# installed and E2E_REPLY_DIR in its environment.
set -euo pipefail
: "${CLAUDE_TEST_ENVIRONMENT_ID:=${CLAUDE_TEST_POOL_ID:-}}" # CLAUDE_TEST_POOL_ID is the legacy spelling
: "${CLAUDE_TEST_ENVIRONMENT_ID:?set CLAUDE_TEST_ENVIRONMENT_ID to a ccpool_... id served by a runner on this host}"
: "${E2E_REPLY_DIR:?set E2E_REPLY_DIR to the directory the Stop hook on your test runner writes to, and export it to the runner process}"
: "${TEST_REPO_REF:=main}"
[ -d "$E2E_REPLY_DIR" ] || {
echo "FAIL: E2E_REPLY_DIR ($E2E_REPLY_DIR) does not exist. The Stop hook on the runner needs it." >&2
exit 1
}
# Waits until $E2E_REPLY_DIR/<session_id>.txt contains $2, or fails after
# 90 seconds. Tune the timeout to your environment's cold-start time. The
# file is written by the Stop hook on the runner.
await_reply() {
local expect="$2" f="$E2E_REPLY_DIR/$1.txt"
local deadline=$(($(date +%s) + 90))
while :; do
if [ -f "$f" ] && grep -qF -- "$expect" "$f"; then
return
fi
[ "$(date +%s)" -lt "$deadline" ] || {
echo "FAIL: '$expect' not in $f within 90s. The Stop hook on the runner did not write it." >&2
echo "-- $E2E_REPLY_DIR contents --" >&2; ls -la "$E2E_REPLY_DIR" >&2
[ -f "$f" ] && { echo "-- $f --" >&2; cat "$f" >&2; }
exit 1
}
sleep 1
done
}
# 1. Create the session on the test environment. Run from a git checkout
# so the CLI can auto-detect the repo. --ref pins the checkout to a named
# ref regardless of local HEAD.
TURN1="e2e-probe-$(date +%s)-$$: say exactly 'ok: custom tools are reachable' and nothing else"
EXPECT1="ok: custom tools are reachable"
create_json=$(claude -p "$TURN1" --environment "$CLAUDE_TEST_ENVIRONMENT_ID" \
--ref "$TEST_REPO_REF" --output-format json)
echo "create: $create_json"
SESSION_ID=$(jq -er '.session_id' <<<"$create_json")
# 2. Wait for the turn-1 reply.
await_reply "$SESSION_ID" "$EXPECT1"
echo "turn-1 reply ok"
# 3. Post a follow-up via the CLI.
TURN2="e2e-probe-followup-$(date +%s): say exactly 'ok: follow-up delivered' and nothing else"
EXPECT2="ok: follow-up delivered"
followup_json=$(claude -p "$TURN2" --cloud "$SESSION_ID" --output-format json)
echo "followup: $followup_json"
jq -e '.ok == true' <<<"$followup_json" >/dev/null
# 4. Wait for the turn-2 reply.
await_reply "$SESSION_ID" "$EXPECT2"
echo "turn-2 reply ok"
echo "PASS: test-environment round-trip (session $SESSION_ID)"
```
Replace the `TURN1`/`TURN2` prompts and `EXPECT1`/`EXPECT2` sentinels with whatever exercises your setup, such as asking Claude to run one of your custom MCP tools and asserting on its output.
## Remote test runners
If your test runners are on separate infrastructure, such as a persistent Kubernetes fleet your CI job can't share a filesystem with, swap the file write in the Stop hook for a POST to an endpoint your driver listens on:
```sh theme={null}
#!/bin/sh
# Variant of the capture hook for runners on separate infrastructure.
# Set E2E_REPLY_URL on the runner to an endpoint the driver controls.
[ -n "${E2E_REPLY_URL:-}" ] || exit 0
sid=$(printf '%s' "${CLAUDE_CODE_REMOTE_SESSION_ID:-}" | sed 's/^cse_/session_/')
[ -n "$sid" ] || exit 0
jq -r '.last_assistant_message // empty' | \
curl -fsS -X POST --data-binary @- "$E2E_REPLY_URL/$sid" >/dev/null 2>&1
exit 0
```
On the driver side, run anything that accepts the POST and holds the reply until the test asks for it, such as a small HTTP listener inside the CI job or a webhook receiver you already run. The hook runs on your infrastructure, so the endpoint only needs to be reachable from your runners.
## Authenticate from CI
Both `claude -p ... --environment` and `claude -p ... --cloud` authenticate with a claude.ai OAuth token; API keys, such as `sk-ant-xxxxx`, aren't accepted for either call. Two approaches make a token available in CI.
### Long-lived CI host
Run `claude auth login` once interactively on the machine that executes the script, using a dedicated user account for automation. The token lives in the OS keychain on macOS, or in `~/.claude/.credentials.json` on Linux and Windows. The CLI refreshes the short-lived access token automatically on each invocation, but the underlying refresh-token grant is capped at 30 days from the initial login, so re-run `claude auth login` interactively on that host every 30 days.
### Ephemeral CI runners
There is no long-lived CI token for this today. The scope that grants remote-session control, `user:sessions:claude_code`, is capped server-side at 30 days, so `claude setup-token`, which mints a one-year inference-only token, doesn't cover it. The [environment secret](/docs/en/self-hosted-environments-quickstart#set-up-an-environment-and-runner) isn't accepted either, since it only authorizes a runner to register with the environment, not to create sessions.
To provision a stored login onto an ephemeral runner, set [`CLAUDE_CODE_OAUTH_REFRESH_TOKEN` and `CLAUDE_CODE_OAUTH_SCOPES`](/docs/en/env-vars#variables) so `claude auth login` exchanges the token without a browser; the same 30-day cap applies to the refresh grant. Contact your Anthropic account team if you need a machine-identity path that isn't bound to a human account.
## Create a dedicated test environment
Create and delete environments programmatically so each CI run gets a clean one; the runner your CI job starts registers into the fresh environment. The create and delete calls below are the same endpoints that the **Cloud environments** admin page on claude.ai uses, and they require the `anthropic-beta: ccr-byoc-2025-07-29` header.
### Mint the admin token
`$ADMIN_TOKEN` is a claude.ai OAuth access token for an account that holds an Owner or admin role, minted the same way as [Authenticate from CI](#authenticate-from-ci):
* **Mint it**: run `claude auth login` with an account that holds an Owner or admin role, then read the current access token from the OS keychain on macOS or `~/.claude/.credentials.json` on Linux and Windows.
* **Read it fresh each run**: the CLI rotates the access token, and the same 30-day refresh-grant cap applies, so don't store a copy.
* **Pass it via stdin**: as the example does, so the token never lands in curl's argument list or your build log.
### Create the environment
Capture the response without echoing it: `pool_secret` is a long-lived credential that can register runners into the environment, so store it as a masked CI secret and print only the environment ID. The `-H @-` form that keeps the token out of the process list requires curl 7.55 or later; older curl treats `@-` as a literal header and sends the request without authorization.
```bash theme={null}
create=$(curl -fsS -X POST -H @- \
-H "anthropic-beta: ccr-byoc-2025-07-29" -H "anthropic-version: 2023-06-01" \
-H "content-type: application/json" \
-d '{"name":"ci-test-environment"}' \
https://api.anthropic.com/v1/code/runners/self-hosted/pools \
<<<"Authorization: Bearer $ADMIN_TOKEN")
ENVIRONMENT_ID=$(jq -er .pool.pool_id <<<"$create")
ENVIRONMENT_SECRET=$(jq -er .pool_secret <<<"$create")
```
Until an [Owner or admin turns on **Allow self-hosted environments**](/docs/en/self-hosted-environments#availability-and-limitations) for the organization, the call fails with a `403` `permission_error` reading `self-hosted runners are disabled by your organization's policy`.
Start a runner on this host with `SELF_HOSTED_RUNNER_ENVIRONMENT_SECRET=$ENVIRONMENT_SECRET`, plus the capture hook and `E2E_REPLY_DIR` per [Install the capture hook](#install-the-capture-hook-on-your-test-runner), then run the test script.
### Delete the environment
Delete the environment when the run finishes, so each CI run starts clean:
```bash theme={null}
curl -fsS -X DELETE -H @- \
-H "anthropic-beta: ccr-byoc-2025-07-29" -H "anthropic-version: 2023-06-01" \
"https://api.anthropic.com/v1/code/runners/self-hosted/pools/$ENVIRONMENT_ID" \
<<<"Authorization: Bearer $ADMIN_TOKEN"
```
server-managed-settings First recorded · 307 lines, first recorded
# Configure server-managed settings ## Requirements ## Choose between server-managed and endpoint-managed settings ## Configure server-managed settings ### Verify settings delivery ### Access control ### Managed-only settings ### Current limitations ## Settings delivery ### Settings precedence ### Per-key exceptions across managed sources ### Fetch and caching behavior ### Invalid entries in delivered settings ### Enforce fail-closed startup ### Security approval dialogs #### Approval memory #### Environment variables and the approval dialog ## Platform availability ## Audit logging ## Security considerations ## See also
The first capture of this source. The page was already there, and this is what it said.
# Configure server-managed settings
> Centrally configure Claude Code for your organization through server-delivered settings, without requiring device management infrastructure.
Server-managed settings let organization Owners centrally configure Claude Code from [**Admin Settings > Claude Code > Managed settings**](https://claude.ai/admin-settings/claude-code) in the claude.ai console. Claude Code clients fetch these settings automatically when users authenticate with an organization OAuth login or a directly configured API key, on platforms where server-managed delivery is supported. See [Platform availability](#platform-availability).
This approach is designed for organizations that don't have device management infrastructure in place, or that need to manage settings for users on unmanaged devices.
<Note>
Server-managed settings are available for [Claude for Teams](https://claude.com/pricing?utm_source=claude_code\&utm_medium=docs\&utm_content=server_settings_teams#team-&-enterprise) and [Claude for Enterprise](https://anthropic.com/contact-sales?utm_source=claude_code\&utm_medium=docs\&utm_content=server_settings_enterprise) customers.
</Note>
## Requirements
To use server-managed settings, you need:
* Claude for Teams or Claude for Enterprise plan
* The Owner or Primary Owner role in your Claude organization, to view and edit the configuration
* Network access to `api.anthropic.com`
## Choose between server-managed and endpoint-managed settings
Claude Code supports two approaches for centralized configuration. Server-managed settings deliver configuration from Anthropic's servers. [Endpoint-managed settings](/docs/en/settings#settings-files) are deployed directly to devices through native OS policies (macOS managed preferences, Windows registry) or managed settings files.
| Approach | Best for | Security model |
| :----------------------------------------------------------- | :------------------------------------------------------- | :-------------------------------------------------------------------------------------------------------- |
| **Server-managed settings** | Organizations without MDM, or users on unmanaged devices | Settings delivered from Anthropic's servers at authentication time |
| **[Endpoint-managed settings](/docs/en/settings#settings-files)** | Organizations with MDM or endpoint management | Settings deployed to devices via MDM configuration profiles, registry policies, or managed settings files |
If your devices are enrolled in an MDM or endpoint management solution, endpoint-managed settings provide stronger security guarantees because the settings file can be protected from user modification at the OS level. Endpoint-managed settings don't reach [cloud sessions](/docs/en/model-config#surface-coverage) in Anthropic-hosted environments, so organizations using Claude Code on the web should configure server-managed settings as well. Sessions in a [self-hosted environment](/docs/en/self-hosted-environments) read the managed settings file in the runner image, but only when server-managed settings deliver no keys, per the [settings precedence](#settings-precedence) below and its [per-key exceptions](#per-key-exceptions-across-managed-sources).
## Configure server-managed settings
<Steps>
<Step title="Open the admin console">
In the claude.ai console, go to [**Admin Settings > Claude Code > Managed settings**](https://claude.ai/admin-settings/claude-code).
If the link redirects you to a different Admin Settings page instead of the Claude Code page, your account doesn't have the required role. Admin and other non-Owner roles can't view or edit managed settings, so ask an Owner or Primary Owner in your organization to make the change. See [Access control](#access-control).
</Step>
<Step title="Define your settings">
Add your configuration as JSON. All [settings available in `settings.json`](/docs/en/settings#available-settings) are supported except those restricted to OS-level policy delivery; see [Current limitations](#current-limitations) for that short list. This includes [hooks](/docs/en/hooks), [environment variables](/docs/en/env-vars), and [managed-only settings](/docs/en/permissions#managed-only-settings) like `allowManagedPermissionRulesOnly`.
This example enforces a permission deny list, prevents users from bypassing permissions, and restricts permission rules to those defined in managed settings:
```json theme={null}
{
"permissions": {
"deny": [
"Bash(curl *)",
"Read(./.env)",
"Read(./.env.*)",
"Read(./secrets/**)"
],
"disableBypassPermissionsMode": "disable"
},
"allowManagedPermissionRulesOnly": true
}
```
Hooks use the same format as in `settings.json`.
This example runs an audit script after every file edit across the organization:
```json theme={null}
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{ "type": "command", "command": "/usr/local/bin/audit-edit.sh" }
]
}
]
}
}
```
To configure the [auto mode](/docs/en/permission-modes#eliminate-prompts-with-auto-mode) classifier so it knows which repos, buckets, and domains your organization trusts:
```json theme={null}
{
"autoMode": {
"environment": [
"Source control: github.example.com/acme-corp and all repos under it",
"Trusted cloud buckets: s3://acme-build-artifacts, gs://acme-ml-datasets",
"Trusted internal domains: *.corp.example.com"
]
}
}
```
Because hooks execute shell commands, users see a [security approval dialog](#security-approval-dialogs) before they're applied. See [Configure auto mode](/docs/en/auto-mode-config) for how the `autoMode` entries affect what the classifier blocks and important warnings about the `environment`, `allow`, `soft_deny`, and `hard_deny` fields.
</Step>
<Step title="Save and deploy">
Save your changes. Claude Code clients receive the updated settings on their next startup or hourly polling cycle.
</Step>
</Steps>
### Verify settings delivery
To confirm that settings are being applied, ask a user to restart Claude Code. If the configuration includes settings that trigger the [security approval dialog](#security-approval-dialogs), the user sees a prompt describing the managed settings on startup. You can also verify that managed permission rules are active by having a user run `/permissions` to view their effective permission rules.
### Access control
The following roles can manage server-managed settings:
* **Primary Owner**
* **Owner**
Restrict access to trusted personnel, as settings changes apply to all users in the organization.
### Managed-only settings
Most [settings keys](/docs/en/settings#available-settings) work in any scope. A handful of keys are only read from managed settings and have no effect when placed in user or project settings files. See [managed-only settings](/docs/en/permissions#managed-only-settings) for the full list.
### Current limitations
Server-managed settings have the following limitations:
* Settings apply uniformly to all users in the organization. Per-group configurations are not yet supported.
* A [`managed-mcp.json`](/docs/en/managed-mcp) file can't be distributed through server-managed settings. Deliver the `allowedMcpServers` and `deniedMcpServers` policy keys there instead.
* Settings restricted to OS-level policy sources, such as `policyHelper` and `wslInheritsWindowsSettings`, are not honored. Deploy them through MDM or a system `managed-settings.json` file instead.
## Settings delivery
### Settings precedence
Server-managed settings and [endpoint-managed settings](/docs/en/settings#settings-files) both occupy the highest tier in the Claude Code [settings hierarchy](/docs/en/settings#settings-precedence). No other settings level can override them, including command line arguments, apart from the [exceptions to managed settings precedence](/docs/en/settings#exceptions-to-managed-settings-precedence).
Within the managed tier, a configured [`policyHelper`](/docs/en/settings#compute-managed-settings-with-a-policy-helper) preempts every other managed source, including server-managed settings: its output becomes the only managed configuration for the run.
Otherwise, Claude Code uses the first source that delivers a non-empty configuration. Server-managed settings are checked first, then endpoint-managed settings. Apart from the [exception keys covered next](#per-key-exceptions-across-managed-sources), sources don't merge: if server-managed settings deliver any keys at all, other endpoint-managed settings are ignored. If server-managed settings deliver nothing, endpoint-managed settings apply.
If you clear your server-managed configuration in the admin console with the intent of falling back to an endpoint-managed plist or registry policy, be aware that [cached settings](#fetch-and-caching-behavior) persist on client machines until the next successful fetch. Run `/status` to see which managed source is active.
### Per-key exceptions across managed sources
Two kinds of keys are exceptions to the no-merge rule:
* **Cross-source lock keys**: a small set of keys, such as the sandbox allowlist locks, [listed in the settings reference](/docs/en/settings#precedence-within-the-managed-tier). They are honored when any admin-controlled managed source sets them; the user-writable HKCU registry tier is excluded, and when a [`policyHelper`](/docs/en/settings#compute-managed-settings-with-a-policy-helper) is configured, its output is the only source these checks read.
* **The `env` block**: apart from the telemetry unit and routing variables paired with a credential key, both covered below, it merges per key across the admin-controlled sources. For each environment variable, the highest-priority source defining it wins, and lower admin sources fill in variables the higher sources leave unset. An endpoint-managed `env` entry therefore applies whenever the server-managed configuration leaves that variable unset, or while a cached server value for it is [withheld pending server confirmation](#fetch-and-caching-behavior). Requires Claude Code v2.1.223 or later. Before v2.1.223, Claude Code applies the winning source's whole `env` block only.
* **Telemetry unit**: the `OTEL_EXPORTER_OTLP_*` exporter keys, the `OTEL_LOG_*` content-capture toggles, `OTEL_LOGS_EXPORTER`, and the beta tracing variables `ENABLE_BETA_TRACING_DETAILED` and `BETA_TRACING_ENDPOINT` follow the highest source that sets any of them as a unit. A source that delivers the `otelHeadersHelper` credential key claims the unit too, but lands these variables only when it is the winning source: a non-winning source that delivers the key contributes none of them and still blocks lower sources from filling them in. Either way, an exporter endpoint from one source can never pair with credentials from another.
* **Credential-paired routing**: a source that pairs routing variables with a winner-only credential key, such as `apiKeyHelper` or `otelHeadersHelper`, contributes those routing variables only when it wins the slot.
### Fetch and caching behavior
Claude Code fetches settings from Anthropic's servers at startup and polls for updates hourly during active sessions.
**First launch without cached settings:**
* Claude Code fetches settings asynchronously
* If the fetch fails, Claude Code continues without managed settings
* There is a brief window before settings load where restrictions are not yet enforced
**Subsequent launches with cached settings:**
* Cached settings apply immediately at startup, except for the withheld environment variables described below
* Claude Code fetches fresh settings in the background
* Cached settings persist through network failures. The withheld environment variables remain withheld until a fetch succeeds
Claude Code withholds the following categories of variables in the cached `env` block until the server confirms the payload for the session. This keeps a cached proxy, certificate authority, endpoint, or credential value from redirecting, intercepting, or re-authenticating the settings fetch that confirms the payload. The hardening applies only to the server-fetched settings cache: [endpoint-managed settings](/docs/en/settings#settings-files) deployed through MDM or `managed-settings.json` are unaffected. The withholding requires Claude Code v2.1.198 or later; before v2.1.198, the whole cached `env` block applies at startup. The withheld categories are:
* Proxy and TLS configuration, such as `HTTPS_PROXY`, `NODE_EXTRA_CA_CERTS`, and the mTLS client certificate variables `CLAUDE_CODE_CLIENT_CERT` and `CLAUDE_CODE_CLIENT_KEY`
* API routing and provider selection, including `ANTHROPIC_BASE_URL`, the provider selection variables such as `CLAUDE_CODE_USE_BEDROCK` and `CLAUDE_CODE_USE_VERTEX`, and the provider endpoint URLs such as `ANTHROPIC_BEDROCK_BASE_URL`
* Authentication credentials, such as `ANTHROPIC_API_KEY`, `ANTHROPIC_AUTH_TOKEN`, and `CLAUDE_CODE_OAUTH_TOKEN`
* The configuration-directory selector `CLAUDE_CONFIG_DIR`
* Credential-source and configuration-directory selectors, in Claude Code v2.1.223 or later: the Workload Identity Federation variables such as `ANTHROPIC_FEDERATION_RULE_ID` and `ANTHROPIC_IDENTITY_TOKEN`, the profile and configuration-directory selectors `ANTHROPIC_PROFILE` and `ANTHROPIC_CONFIG_DIR`, and the operating-system directory variables `HOME`, `XDG_CONFIG_HOME`, `APPDATA`, and `USERPROFILE`
Claude Code reads the Workload Identity Federation variables and the `ANTHROPIC_PROFILE` and `ANTHROPIC_CONFIG_DIR` selectors only at startup, so a server-delivered value for them doesn't switch the session's credential source even after the fetch succeeds. To deliver those selectors on Claude Code v2.1.223 or later, use [endpoint-managed settings](/docs/en/settings#settings-files) such as MDM or `managed-settings.json`. For `CLAUDE_CONFIG_DIR` and the operating-system directory variables, the withholding itself is the protection: the cached value stays out of the environment until the server confirms the payload.
Every other key in the cached `env` block, such as telemetry and OpenTelemetry configuration, applies at startup as before. Once the fetch succeeds, the withheld variables apply for the rest of the session; the startup-only selectors covered above reach the environment but don't switch the running session's credential source.
If your organization needs a proxy to reach `api.anthropic.com`, the withholding only affects the server-delivered `env` block itself: a proxy set in an [endpoint-managed](/docs/en/settings#settings-files) `env` block through MDM or `managed-settings.json`, in the shell environment, or in [user settings](/docs/en/settings#settings-files) reaches the settings fetch. The endpoint-managed source requires Claude Code v2.1.223 or later: the cached server-managed proxy value is withheld until the fetch confirms it, so the endpoint-managed value fills in per key and reaches the fetch itself. Before v2.1.223, use the shell environment or user settings so the proxy applies alongside a cached server payload. The first launch has no cache, so an endpoint-managed source, the shell environment, or user settings is still required for the initial fetch.
Claude Code applies settings updates automatically without a restart, except for advanced settings like OpenTelemetry configuration, which require a full restart to take effect.
### Invalid entries in delivered settings
Delivered payloads parse tolerantly with the same rules as the other managed sources. When a payload contains an entry that fails schema validation, Claude Code strips that entry, surfaces a validation error, and applies every remaining valid setting. See [Invalid entries in managed settings](/docs/en/settings#invalid-entries-in-managed-settings) for the field-level behavior, including how security-enforcement fields are handled. Requires Claude Code v2.1.169 or later.
Server-managed delivery adds these behaviors:
* The cache at `~/.claude/remote-settings.json` stores the salvaged payload with invalid entries removed. The raw invalid payload is never persisted.
* When no field in the payload can be salvaged, Claude Code keeps the last-accepted cached settings and records a fatal error.
* The [security approval dialog](#security-approval-dialogs) evaluates the salvaged payload, so a stripped invalid entry is never presented for approval and never executes.
To debug delivery issues, run `claude --debug-file <path>` and search the log for `Remote settings`. Validate a payload change with `claude doctor` on a test machine before rolling it out to the organization.
### Enforce fail-closed startup
By default, if the remote settings fetch fails at startup, the CLI continues without managed settings. For environments where this brief unenforced window is unacceptable, set `forceRemoteSettingsRefresh: true` in your managed settings.
When this setting is active, the CLI blocks at startup until remote settings are freshly fetched. If the fetch fails, the CLI exits rather than proceeding without the policy. This setting self-perpetuates: once delivered from the server, it is also cached locally so that subsequent startups enforce the same behavior even before the first successful fetch of a new session.
To enable this, add the key to your managed settings configuration:
```json theme={null}
{
"forceRemoteSettingsRefresh": true
}
```
You can also set this key in an [endpoint-managed](/docs/en/settings#settings-files) MDM profile or system `managed-settings.json` file to enforce fail-closed behavior on first launch, before any server payload has been delivered. As of v2.1.191, this flag is an exception to the [precedence rule](#settings-precedence) above: it is honored when set in any admin-controlled managed source even if a cached server-managed payload is also present, so an MDM-delivered value is not ignored when server-managed settings exist. When a [`policyHelper`](/docs/en/settings#compute-managed-settings-with-a-policy-helper) is configured, its output replaces every other managed source, this key included.
The settings fetch also sends a `Cache-Control: no-cache` header so intermediate HTTP proxies don't serve a stale response.
Before enabling this setting, ensure your network policies allow connectivity to `api.anthropic.com`. If that endpoint is unreachable, the CLI exits at startup and users cannot start Claude Code.
The `claude auth` subcommands such as `claude auth login` are exempt from this check, so users can re-authenticate when expired credentials are the reason the settings fetch fails.
### Security approval dialogs
Certain settings that could pose security risks require explicit user approval before Claude Code applies them:
* **Shell command settings**: settings that execute shell commands
* **Custom environment variables**: delivered `env` variables that require the user's approval, such as proxy and base-URL variables; see [Environment variables and the approval dialog](#environment-variables-and-the-approval-dialog)
* **Hook configurations**: any hook definition
* **Managed CLAUDE.md content**: a `claudeMd` value delivered through managed settings
When these settings are present, users see a security dialog explaining what is being configured. Users must approve to proceed. If a user rejects the settings, Claude Code exits.
#### Approval memory
Claude Code records your approval in your configuration directory, `~/.claude` unless you set [`CLAUDE_CONFIG_DIR`](/docs/en/env-vars). What it records depends on the credential the settings fetch uses:
* **A claude.ai login saved by `/login` or `claude auth login`**: one approval per organization, held by the account that approved most recently.
* **Any other credential**, such as a [Claude apps gateway](/docs/en/claude-apps-gateway), an API key, or `CLAUDE_CODE_OAUTH_TOKEN`: one approval for the delivered settings, kept with the cached copy of the settings in that configuration directory. Claude Code shows the dialog again when the settings that require approval change, and after you run `/logout` or `claude auth logout`, which delete the cached copy.
With a saved claude.ai login:
* If you sign out and back in, or switch to another organization and later return, Claude Code doesn't show the dialog again while those settings are unchanged, unless another account approved them for that organization in the same configuration directory in between.
* If you sign in to the same organization with a different account, Claude Code shows the dialog again even when the settings are unchanged. That account's approval replaces the previous one, so when you switch back, Claude Code shows the dialog once more.
If an interactive session can't show the dialog, Claude Code doesn't apply the delivered settings and keeps the last-approved settings; the dialog appears in the next session that can show it. Requires Claude Code v2.1.211 or later.
<Note>
A non-interactive run, such as `claude -p` or an Agent SDK session, can't show the dialog. When the delivered settings would require approval, Claude Code applies them for that run only: it doesn't record them as approved or write them to the [local cache](#fetch-and-caching-behavior), and the next interactive session shows the dialog. Until a user approves in an interactive session, each non-interactive run fetches the settings again at startup. Before v2.1.207, a non-interactive run saved the settings as approved, so later interactive sessions never showed the dialog for them.
</Note>
#### Environment variables and the approval dialog
Claude Code applies some delivered `env` variables without showing the user the approval dialog, including:
* Feature and command toggles
* Model selection and behavior settings, such as `ANTHROPIC_MODEL`, `DISABLE_PROMPT_CACHING`, and `CLAUDE_CODE_EFFORT_LEVEL`
* Context window and compaction settings, such as `DISABLE_AUTO_COMPACT`
* Terminal UI and accessibility options
* Numeric limits, budgets, and timeouts
Other delivered variables can require the user's approval before they take effect; a non-empty proxy, base-URL, or `OTEL_EXPORTER_OTLP_ENDPOINT` value always does. When a delivered variable needs approval, the dialog names it, so the user sees exactly what the policy is asking to set. Before v2.1.218, Claude Code applied fewer variables without asking the user, so settings such as `DISABLE_AUTO_COMPACT` triggered the dialog at any non-empty value.
Claude Code decides whether four privacy toggles need approval by the delivered value rather than by the variable name: `CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC`, `DISABLE_ERROR_REPORTING`, `DISABLE_TELEMETRY`, and `DO_NOT_TRACK`. A truthy value such as `1` or `true` only turns tracking, reporting, or other nonessential traffic off, so Claude Code applies it without asking the user. For any other non-empty value, Claude Code shows the dialog. Before v2.1.218, all of them except `DO_NOT_TRACK` applied without approval at any value, and `DO_NOT_TRACK` triggered the dialog at any non-empty value.
## Platform availability
Server-managed settings require a direct connection to `api.anthropic.com`, and delivery requires the session to authenticate with an organization OAuth login or a directly configured API key. Keys returned by an [`apiKeyHelper`](/docs/en/settings#available-settings) script don't trigger the settings fetch.
Server-managed settings are not available when using third-party model providers:
* Amazon Bedrock
* Google Cloud's Agent Platform
* Microsoft Foundry
* [Claude Platform on AWS](/docs/en/claude-platform-on-aws)
* Custom API endpoints via `ANTHROPIC_BASE_URL` or third-party [LLM gateways](/docs/en/llm-gateway)
If you export a `CLAUDE_CODE_USE_*` provider variable or a non-default `ANTHROPIC_BASE_URL` in your shell, Claude Code skips the settings fetch for your sessions. You can't clear the export with a server-managed `env` block, because the block arrives through the fetch that the export prevents. An [endpoint-managed settings](/docs/en/settings#settings-files) `env` block doesn't restore the fetch either: Claude Code checks eligibility before it applies managed `env` blocks, so the override changes the session's provider selection but the fetch stays skipped.
To restore server-managed delivery, remove the export from your shell, or set the variable to `""` in your user settings `env` block, which applies before the eligibility check. To enforce policy without relying on users to change their shells, deliver the settings through the endpoint-managed channel instead.
For Amazon Bedrock, Google Cloud's Agent Platform, and Microsoft Foundry deployments, a self-hosted [Claude apps gateway](/docs/en/claude-apps-gateway) provides the equivalent remote managed-settings delivery: gateway-signed-in clients fetch managed settings from the gateway instead of `api.anthropic.com`. The failure semantics differ at startup: a gateway client that can't reach the gateway exits with an error instead of falling back to cached settings, while the hourly background refresh is fail-open on both channels.
## Audit logging
Audit log events for settings changes are available through the compliance API or audit log export. Contact your Anthropic account team for access.
Audit events include the type of action performed, the account and device that performed the action, and references to the previous and new values.
## Security considerations
Server-managed settings provide centralized policy enforcement, but they operate as a client-side control, not a security boundary. On unmanaged devices, a user doesn't need admin or sudo access to bypass them.
| Scenario | Behavior |
| :--------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| User edits the cached settings file | Tampered file applies at startup, but correct settings restore on the next server fetch. In Claude Code v2.1.198 or later, the [withheld environment variables](#fetch-and-caching-behavior) in the `env` block don't apply until the server confirms the payload |
| User deletes the cached settings file | First-launch behavior occurs: settings fetch asynchronously with a brief unenforced window |
| User runs a modified Claude Code binary | A user who can run a modified client can bypass any client-side control |
| User runs an older Claude Code version | Versions that predate server-managed settings don't fetch or apply them |
| API is unavailable | Cached settings apply if available, otherwise managed settings are not enforced until the next successful fetch. In Claude Code v2.1.198 or later, the [withheld environment variables](#fetch-and-caching-behavior) in the cached `env` block don't apply on fetch failure; the rest of the cache still applies. With `forceRemoteSettingsRefresh: true`, the CLI exits instead of continuing, except for [`claude auth` subcommands](#enforce-fail-closed-startup) |
| User authenticates with a different organization | Settings are not delivered for accounts outside the managed organization |
| User configures a [third-party model provider](#platform-availability) | Server-managed settings are bypassed. This includes setting `CLAUDE_CODE_USE_BEDROCK`, `CLAUDE_CODE_USE_MANTLE`, `CLAUDE_CODE_USE_VERTEX`, `CLAUDE_CODE_USE_FOUNDRY`, `CLAUDE_CODE_USE_ANTHROPIC_AWS`, or a non-default `ANTHROPIC_BASE_URL` |
| Network traffic is intercepted or redirected | Disabled TLS validation or intercepted traffic can alter the settings the client receives |
To detect runtime configuration changes, use [`ConfigChange` hooks](/docs/en/hooks#configchange) to log modifications or block unauthorized changes before they take effect.
To restrict which organizations your users can access with credentials the client supplies, see [Enforce network-level access control with Tenant Restrictions](https://support.claude.com/en/articles/13198485-enforce-network-level-access-control-with-tenant-restrictions) in the Claude Help Center. For stronger enforcement guarantees, use [endpoint-managed settings](/docs/en/settings#settings-files) on devices enrolled in an MDM solution.
## See also
Cut at 300 lines. The page has the rest.
sessions First recorded · 206 lines, first recorded
# Manage sessions ## Resume a session ### What a resumed session restores ### Resume from a summary ### Where the session picker looks ## Name your sessions ## Use the session picker ## Branch a session ## Manage context within a session ## Export and locate session data ### Access conversations from scripts ### Where transcripts are stored ## See also
The first capture of this source. The page was already there, and this is what it said.
# Manage sessions
> Name, resume, branch, and switch between Claude Code conversations. Covers `--continue`, `--resume`, `--from-pr`, the `/resume` picker, session naming, exporting transcripts, and where transcripts are stored.
A session is a saved conversation tied to a project directory. Claude Code stores it locally as you work, so you can resume where you left off, branch to try a different approach, or switch between tasks.
The [desktop app](/docs/en/desktop#work-in-parallel-with-sessions), [Claude Code on the web](/docs/en/claude-code-on-the-web), and the [VS Code extension](/docs/en/vs-code#resume-past-conversations) each maintain their own session history. This page covers the CLI.
## Resume a session
Sessions are saved continuously to [local transcript files](#export-and-locate-session-data) as you work, so you can return to one after exiting or running `/clear`. Use these entry points:
| Command | What it does |
| :-------------------------- | :------------------------------------------------------------------------ |
| `claude --continue` | Resumes the most recent session in the current directory |
| `claude --resume` | Opens the [session picker](#use-the-session-picker) |
| `claude --resume <name>` | Resumes the named session directly |
| `claude --from-pr <number>` | Opens the session picker filtered to sessions linked to that pull request |
| `/resume` | Switches to a different conversation from inside an active session |
Sessions created with [`claude -p`](/docs/en/headless) or the [Agent SDK](/docs/en/agent-sdk/overview) don't appear in the session picker, but you can still resume one by passing its session ID to `claude --resume <session-id>`.
You can run `claude --resume <session-id>` from any directory: Claude Code looks for the ID in the current project directory and its git worktrees first, then in every other project on this machine, so it finds a session that started elsewhere or moved with [`/cd`](/docs/en/commands). The cross-project search resolves the ID only when exactly one other project holds a transcript with messages for it, so a hand-copied duplicate makes Claude Code report not-found rather than resume an arbitrary copy. If no stored session matches the ID, Claude Code reports `No conversation found with session ID: <session-id>`. Before v2.1.223, the lookup stopped at the current project directory and its git worktrees, so you had to resume from the directory the session last worked in.
### What a resumed session restores
A resumed session restores the conversation along with the state saved in it:
* Conversation history: the full history, including tool calls and results.
* Model: the session continues on the model it was using. The model isn't restored when it has been retired or isn't allowed by `availableModels`, when a `--model` flag or `ANTHROPIC_MODEL`-family environment variable picks one at launch, or on providers that use provider-specific deployment IDs, such as [Amazon Bedrock, Google Cloud's Agent Platform, and Microsoft Foundry](/docs/en/third-party-integrations); see [model configuration](/docs/en/model-config#setting-your-model) for the resolution order.
* Agent: a session started with [`--agent`](/docs/en/sub-agents#invoke-subagents-explicitly) or the `agent` setting continues as that agent, keeping its system prompt, tool restrictions, and model. Pass `--agent` when resuming to pick a different one. Claude Code looks for the agent in two places: the session's original directory, provided you have [trusted that workspace](/docs/en/permissions#project-allow-rules-and-workspace-trust), and then the directory you resume from, so a project-scoped agent still loads when you resume from another directory. If Claude Code doesn't find the agent in either place, the session resumes with the default tools and system prompt and shows a [warning naming the agent](/docs/en/errors#session-agent-no-longer-available).
* Permission mode: the mode the session was in. `plan` and `bypassPermissions` are never restored; [bypassing permissions](/docs/en/permission-modes#skip-all-checks-with-bypasspermissions-mode) must be enabled again at launch, with one of its launch flags or `permissions.defaultMode: "bypassPermissions"` in [settings](/docs/en/settings#permission-settings). `auto` is restored only when your account still meets the [auto mode requirements](/docs/en/permission-modes#eliminate-prompts-with-auto-mode). Pass `--permission-mode` to override the restored mode.
* Active goal: a [goal](/docs/en/goal#resume-with-an-active-goal) that was still active when the session ended carries over; its turn count, timer, and token-spend baseline reset.
* Scheduled tasks: [tasks that haven't expired](/docs/en/scheduled-tasks#limitations) are restored. Background Bash and monitor tasks aren't.
Not every configuration flag from the original launch is restored. If the session depended on `--mcp-config`, `--settings`, `--plugin-dir`, `--fallback-model`, or directories added with `--add-dir`, pass them again when you resume; directories added mid-session with `/add-dir` aren't restored either, though the session picker still uses them to locate the session. The standard settings files, such as `settings.json` and `settings.local.json`, are re-read at launch, so configuration that lives in them doesn't need to be passed again.
### Resume from a summary
On a Pro or Max plan, when you resume a session that has been inactive for more than about an hour and is over 100,000 tokens, Claude Code restores the conversation and then opens a dialog before you send your first message. The session's [prompt cache](/docs/en/prompt-caching#cache-lifetime) has expired by then, so the next request processes the full history once no matter which of the dialog's options you pick.
The dialog offers three ways to continue the session. They differ in how much of the conversation each one carries forward into later requests, which is a tradeoff between keeping every detail and sending fewer tokens per request:
* **Resume from summary**: runs [`/compact`](/docs/en/context-window#what-survives-compaction) immediately. Claude Code sends one summarization request over the full history, then replaces the history with the summary, your most recent exchanges, and up to five recently read files. Later requests carry the summary instead of the full history.
* **Resume full session as-is**: loads the conversation unchanged. After you send your first message, Claude Code reprocesses and re-caches the full history, then re-reads it from the cache on later requests while the cache stays warm.
* **Don't ask me again**: resumes the full session and stops showing the dialog on all future resumes.
Resuming as-is keeps every detail of the conversation available, at a per-request cost that scales with the conversation's size. Resuming from the summary costs less on each later request because it carries the summary instead of the full history, but whatever the summary leaves out is no longer in Claude's context. See [why usage climbs in a long session](/docs/en/costs#why-usage-climbs-in-a-long-session) for where that per-request cost comes from.
### Where the session picker looks
Claude Code stores sessions per project directory. By default the session picker shows:
* Sessions from the current worktree, including [background sessions](/docs/en/agent-view), which are marked `bg` in the list
* Sessions started elsewhere that added the current directory with `/add-dir`
Use `Ctrl+W` to widen to all worktrees of the repository or `Ctrl+A` to widen to every project on this machine.
Sessions whose first prompt was a [`/loop`](/docs/en/scheduled-tasks#run-a-prompt-repeatedly-with-%2Floop) command don't appear in the picker; running `/loop` later in a conversation doesn't hide the session. Before v2.1.211, a `/loop` run early in a conversation hid the session from the picker permanently.
From v2.1.169, moving a session with [`/cd`](/docs/en/commands) relocates it to the new directory's project storage, so it appears in that directory's picker afterward. As of v2.1.196, a moved session stays out of the old directory's picker even after a crash or forced exit. On earlier versions, it could also reappear in the old directory's list after an exit that wasn't clean when the old path contained special characters such as underscores.
When you select a session from another worktree of the same repository, Claude Code resumes it in place. When you select a session from an unrelated project, Claude Code copies a `cd` and resume command to your clipboard instead.
Resuming by name resolves across the current repository and its worktrees. Both forms look for an exact match and resume it directly even if it lives in a different worktree:
| Command | Exact match | Ambiguous name |
| :----------------------- | :--------------- | :-------------------------------------------------------------------------- |
| `claude --resume <name>` | Resumes directly | Opens the session picker with the name pre-filled as a search term |
| `/resume <name>` | Resumes directly | Reports an error; run `/resume` with no argument to open the session picker |
## Name your sessions
Give sessions descriptive names so they're findable in the session picker and resumable by name. This matters most when you're working on several tasks in parallel.
| When | How to set the name |
| :------------------------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| At startup | `claude -n auth-refactor` |
| During a session | `/rename auth-refactor`. The name also appears on the prompt bar |
| From the session picker | Highlight a session and press `Ctrl+R` |
| On plan accept | Accepting a plan in [plan mode](/docs/en/permission-modes#analyze-before-you-edit-with-plan-mode) names the session from the plan content unless you've already set one |
| From claude.ai or the Claude app | Rename a [Remote Control session](/docs/en/remote-control#connect-from-another-device); Claude Code applies the same name in the CLI. Requires Claude Code v2.1.221 or later |
| From the desktop app | Rename a session in the [desktop app](/docs/en/desktop#work-in-parallel-with-sessions) |
Once you name a session through a CLI route or from claude.ai, return to it with `claude --resume <name>` or `/resume <name>`; a desktop-app session resumes in the app, which keeps its own session history. See [Resume a session](#resume-a-session) for how name resolution behaves across worktrees.
When you start or resume an interactive session with a name that another live session on this machine already uses, or rename a session into such a name, Claude Code leaves the name with the session that already has it, renames yours to a variant with a two-word suffix, such as `auth-refactor-graceful-unicorn`, and tells you. Run `/rename` with a new name if you'd rather pick one yourself. Before v2.1.232, both sessions kept the name.
In three cases Claude Code doesn't rename the duplicate, so you can still see two sessions with the same name in listings:
* It doesn't check AI-generated titles or default display names.
* It doesn't check the `--name` of a [background](/docs/en/agent-view#from-your-shell) or `-p` session at startup.
* It can't rename a session on an earlier version of Claude Code.
Interactive sessions you never name still get a default display name when they start. Requires Claude Code v2.1.196 or later. The default combines the working directory's name with a two-character suffix, for example `my-app-3f`, and identifies the session in listings of running sessions, such as [agent view](/docs/en/agent-view) and `claude agents --json` output.
The default isn't a resume handle: `claude --resume <name>`, `/resume <name>`, and the session picker match only names you set. Naming the session replaces the default.
If you don't name a session, Claude Code generates a session title for it: a short summary of your first prompt, written by a background request to the small/fast model, normally a Haiku-class model. Naming the session with `--name` or `/rename` replaces the generated title. You see the generated title in the [session picker](#use-the-session-picker) and in the statusline [`session_name`](/docs/en/statusline) field when no name is set; like the default display name, it isn't a resume handle.
## Use the session picker
Run `/resume` inside a session, or `claude --resume` with no arguments, to open the interactive session picker. Use these keyboard shortcuts to navigate, search, and widen the list:
| Shortcut | Action |
| :------------------------------------------------ | :----------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `↑` / `↓` | Navigate between sessions |
| `→` / `←` | Expand or collapse grouped sessions |
| `Enter` | Resume the highlighted session |
| `Space` | Preview the session content. `Ctrl+V` also works on terminals that don't capture it as paste |
| `Ctrl+R` | Rename the highlighted session |
| `/` or any printable character other than `Space` | Enter search mode and filter sessions. Paste a GitHub, GitHub Enterprise, GitLab, or Bitbucket pull or merge request URL to find the session that created it |
| `Ctrl+A` | Show sessions from all projects on this machine. Press again to return to the current repository |
| `Ctrl+W` | Show sessions from all worktrees of the current repository. Press again to return to the current worktree. Only shown in multi-worktree repositories |
| `Ctrl+B` | Filter to sessions from the current git branch. Press again to show all branches |
| `Esc` | Exit the session picker or search mode |
Each row shows the session name if you set one, otherwise the AI-generated session title, conversation summary, or first prompt, along with time since last activity, git branch, and file size. Widen to all projects with `Ctrl+A` to also see each session's project path.
Sessions created with `/branch` or `--fork-session` get their own session IDs and appear as separate rows. When the picker finds more than one entry for the same session, it groups them under a single row. Press `→` to expand a group.
If Claude Code can't load the session you select from the `claude --resume` picker, it prints [`Failed to resume the conversation`](/docs/en/errors#failed-to-resume-the-conversation) with a command to retry, then exits with code 1. From the `/resume` picker inside a session, Claude Code reports the failure and your current conversation keeps running.
## Branch a session
Branching creates a copy of the conversation so far and switches you into it, leaving the original intact. Use it to try a different approach without losing the path you were on.
From inside a session, run `/branch` with an optional name:
```text theme={null}
/branch try-streaming-approach
```
If you omit the name, Claude Code names the new branch after the first prompt in the conversation. As of v2.1.198 this also applies after [compaction](/docs/en/how-claude-code-works#when-context-fills-up); earlier versions fell back to the literal name `Branched conversation` instead of looking past the compaction summary to the original first prompt.
From the command line, combine `--continue` or `--resume` with `--fork-session`:
```bash theme={null}
claude --continue --fork-session
```
The `/branch` confirmation prints two session IDs: the new branch you are now in and the original. The original is unchanged on disk and remains in the session picker; return to it with `/resume <original-name>` or by passing its ID to `/resume`.
`/branch` copies the transcript and switches the running Claude Code process to write to it. That distinction determines what the branch inherits:
| State | After `/branch` |
| :----------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Conversation history | Copied into the branch up to the point you ran `/branch` |
| "Allow for this session" permission grants | Carried over; the branch runs in the same process, so your existing grants still apply. If you fork into a separate process with `--fork-session`, the new process starts without them and you re-approve there |
| In-flight [background subagents](/docs/en/sub-agents#run-subagents-in-foreground-or-background) and [background Bash commands](/docs/en/interactive-mode#background-bash-commands) | Keep running. Their output appears in the new branch you switched into, not in the original session |
| [Remote Control](/docs/en/remote-control) connection | Stays connected. A phone or browser connected to the session follows you into the branch and keeps receiving new messages there |
If you resume the same session in two terminals without forking, messages from both interleave into one transcript. For checkpoint-based rewind within a single session, see [Checkpointing](/docs/en/checkpointing).
## Manage context within a session
These commands control what's in the context window without leaving the session:
* **`/clear`**: start fresh with an empty context. Claude Code saves the previous conversation; resume it with `/resume`, or, in the same Claude Code process, from [the rewind menu's previous-session entry](/docs/en/checkpointing#rewind-past-a-cleared-conversation). You keep a name you set with `--name` or `/rename` in the new conversation, but not an AI-generated session title
* **`/compact [instructions]`**: replace history with a summary, optionally focused on what you specify
* **`/context`**: show what is currently consuming context
For how compaction interacts with CLAUDE.md, skills, and rules, see the [context window guide](/docs/en/context-window). For strategies on when to clear versus compact, see [Best practices](/docs/en/best-practices#manage-your-session).
## Export and locate session data
Run `/export` to open a menu that lets you copy the current conversation to your clipboard or save it as a plain-text file, with messages and tool outputs rendered as readable text. Pass a filename to skip the menu and write directly to that file.
### Access conversations from scripts
`/export` produces a rendered transcript for a person to read. The interfaces below produce structured data for a script to parse: a JSON result from a run, the path to a session's transcript file, or a live stream of events. Pick by what triggers the script:
* **Run Claude once and capture the result**: invoke `claude -p` with [`--output-format json` or `stream-json`](/docs/en/headless#get-structured-output) to capture the result, session ID, usage, and cost of a non-interactive run as structured JSON.
* **Ask an existing session a question**: pass a session ID to [`claude -p --resume`](/docs/en/headless#continue-conversations) to send a follow-up prompt, such as a summary request, and capture the structured response.
* **React to session events**: read the `transcript_path` field that [hooks](/docs/en/hooks#common-input-fields) and [status line commands](/docs/en/statusline#available-data) receive as input. A `SessionEnd` hook can archive the transcript when a session ends.
* **Embed Claude in a TypeScript or Python app**: use the [Agent SDK](/docs/en/agent-sdk/overview) to receive each message programmatically.
The example below uses the second interface. It sends a follow-up prompt to an existing session and reads the answer with `jq`:
```bash theme={null}
claude -p --resume <session-id> --output-format json "summarize what we changed" | jq -r '.result'
```
### Where transcripts are stored
By default, Claude Code stores transcripts as JSONL at `~/.claude/projects/<project>/<session-id>.jsonl`, where `<project>` is your working directory path with non-alphanumeric characters replaced by `-`. For a working directory whose converted name exceeds 200 characters, Claude Code truncates the name to 200 characters and appends a hash of the full path, so the directory name stays within filesystem limits.
Each line is a JSON object for a message, tool use, or metadata entry. The entry format is internal to Claude Code and changes between versions, so scripts that parse these files directly can break on any release. To build on session data, use `/export` or the [script interfaces](#access-conversations-from-scripts) instead.
The location, retention, and write behavior are configurable:
| To | Set | Where |
| ------------------------------------------- | ------------------------------------------------------ | ------------------------- |
| Move storage off `~/.claude` | [`CLAUDE_CONFIG_DIR`](/docs/en/env-vars) | Environment variable |
| Change the 30-day retention | [`cleanupPeriodDays`](/docs/en/settings#available-settings) | `settings.json` |
| Suppress transcript writes in all modes | [`CLAUDE_CODE_SKIP_PROMPT_HISTORY`](/docs/en/env-vars) | Environment variable |
| Suppress writes for one non-interactive run | [`--no-session-persistence`](/docs/en/cli-reference) | CLI flag with `claude -p` |
## See also
These pages cover related session and parallelism mechanics:
* [Worktrees](/docs/en/worktrees): run isolated parallel sessions on separate branches
* [Checkpointing](/docs/en/checkpointing): rewind code and conversation to an earlier point
* [Context window](/docs/en/context-window): what fills context and what survives compaction
* [Non-interactive mode](/docs/en/headless): session behavior under `claude -p`
settings First recorded · 1299 lines, first recorded
# Claude Code settings ## Configuration scopes ### Available scopes ### When to use each scope ### How scopes interact ### What uses scopes ## Settings files ### When edits take effect ### Invalid entries in managed settings ### Available settings ### Global config settings ### Worktree settings ### Permission settings ### Permission rule syntax ### Sandbox settings #### Sandbox path prefixes ### Attribution settings ### File suggestion settings ### Footer link badges ### Hook configuration ### Compute managed settings with a policy helper ### Settings precedence #### Exceptions to managed settings precedence #### Precedence within the managed tier #### Parent settings from embedding hosts ### Verify active settings ### Key points about the configuration system ### System prompt ### Exclude sensitive files ## Subagent configuration ## Plugin configuration ### Plugin settings #### `enabledPlugins` #### `pluginConfigs` #### `extraKnownMarketplaces` #### `strictKnownMarketplaces` #### `strictPluginOnlyCustomization` ### Manage plugins ## Environment variables ## Tools available to Claude ## See also
This page is larger than the 256 KiB this site keeps, so one side of the diff below stops where the stored text does.
The first capture of this source. The page was already there, and this is what it said.
# Claude Code settings
> Configure Claude Code with global and project-level settings, and environment variables.
Claude Code offers a variety of settings to configure its behavior to meet your needs. You can configure Claude Code by running the `/config` command in an interactive session, which opens a tabbed Settings interface where you can view status information and modify configuration options. From v2.1.181, you can change a single option without opening the interface by passing `key=value` to `/config`, for example `/config verbose=true`.
## Configuration scopes
Claude Code uses a scope system to determine where configurations apply and who they're shared with. Understanding scopes helps you decide how to configure Claude Code for personal use, team collaboration, or enterprise deployment.
### Available scopes
| Scope | Location | Who it affects | Shared with team? |
| :---------- | :--------------------------------------------------------------------------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------------------------------------------------- |
| **Managed** | Server-managed settings, plist / registry, or system-level `managed-settings.json` | All organization members for server-managed delivery; all users on the machine for plist, HKLM registry, and file delivery; the current user for HKCU registry delivery | Yes (deployed by IT) |
| **User** | `~/.claude/` directory | You, across all projects | No |
| **Project** | `.claude/` in repository | All collaborators on this repository | Yes (committed to git) |
| **Local** | `.claude/settings.local.json` at the repository root | You, in this repository only | No (gitignored when Claude Code saves a setting to it) |
### When to use each scope
**Managed scope** is for:
* Security policies that must be enforced organization-wide
* Compliance requirements that can't be overridden
* Standardized configurations deployed by IT/DevOps
**User scope** is best for:
* Personal preferences you want everywhere (themes, editor settings)
* Tools and plugins you use across all projects
* API keys and authentication (stored securely)
**Project scope** is best for:
* Team-shared settings (permissions, hooks, MCP servers)
* Plugins the whole team should have
* Standardizing tooling across collaborators
**Local scope** is best for:
* Personal overrides for a specific project
* Testing configurations before sharing with the team
* Machine-specific settings that won't work for others
### How scopes interact
When the same setting appears in multiple scopes, Claude Code applies them in priority order:
1. **Managed** (highest): can't be overridden by any other scope, apart from the [exceptions to managed settings precedence](#exceptions-to-managed-settings-precedence)
2. **Command line arguments**: temporary session overrides
3. **Local**: overrides project and user settings
4. **Project**: overrides user settings
5. **User** (lowest): applies when nothing else specifies the setting
For example, if your user settings set `spinnerTipsEnabled` to `true` and project settings set it to `false`, the project value applies. Permission rules merge across scopes instead, and a few security-sensitive keys are exceptions. See [Settings precedence](#settings-precedence).
### What uses scopes
Scopes apply to many Claude Code features:
| Feature | User location | Project location | Local location |
| :-------------- | :------------------------ | :--------------------------------- | :----------------------------- |
| **Settings** | `~/.claude/settings.json` | `.claude/settings.json` | `.claude/settings.local.json` |
| **Subagents** | `~/.claude/agents/` | `.claude/agents/` | None |
| **MCP servers** | `~/.claude.json` | `.mcp.json` | `~/.claude.json` (per-project) |
| **Plugins** | `~/.claude/settings.json` | `.claude/settings.json` | `.claude/settings.local.json` |
| **CLAUDE.md** | `~/.claude/CLAUDE.md` | `CLAUDE.md` or `.claude/CLAUDE.md` | `CLAUDE.local.md` |
On Windows, paths shown as `~/.claude` resolve to `%USERPROFILE%\.claude`.
***
## Settings files
The `settings.json` file is the official mechanism for configuring Claude
Code through hierarchical settings:
* **User settings** are defined in `~/.claude/settings.json` and apply to all
projects.
* **Project settings** are saved in your project directory:
* `.claude/settings.json` for settings that are checked into source control and shared with your team
* `.claude/settings.local.json` for settings that are not checked in, useful for personal preferences and experimentation. When Claude Code saves a setting to this file in a repository that doesn't already ignore it, Claude Code adds `**/.claude/settings.local.json` to your global git excludes file. That excludes file is `core.excludesFile` from your global git config when it's set to an absolute or `~`-prefixed path, otherwise `$XDG_CONFIG_HOME/git/ignore`, or `~/.config/git/ignore`. If you create the file by hand or have Claude write it with the Write tool, add it to your gitignore yourself.
Claude Code reads and writes this file at the root of the git repository, resolved through [worktrees](/docs/en/worktrees) to the main checkout, so one file covers sessions started in any subdirectory or worktree of the repository. The file stays in the directory you start Claude Code from in three cases: outside a git repository, when the repository root is your home directory, and in [Agent SDK](/docs/en/agent-sdk/claude-code-features#control-filesystem-settings-with-settingsources) sessions.
<Info>Before v2.1.211, the file always lived in the starting directory. Claude Code still reads a `.claude/settings.local.json` that an earlier version left there. When both files set the same key, the repository root's value wins, except that permission rules from both files stay in effect.</Info>
Claude Code also saves permanent "don't ask again" [permission approvals](/docs/en/permissions#permission-system), such as Bash command approvals, to this file.
Because this file is yours rather than the repository's, its permission `allow` rules take effect without the [workspace trust](/docs/en/permissions#project-allow-rules-and-workspace-trust) step that `.claude/settings.json` allow rules require. If the repository supplies the file, for example by committing it, workspace trust still applies.
* **Managed settings**: For organizations that need centralized control, Claude Code supports multiple delivery mechanisms for managed settings. All use the same JSON format and cannot be overridden by user or project settings:
* **Server-managed settings**: delivered remotely at sign-in, either from Anthropic's servers via the claude.ai admin console or from a self-hosted [Claude apps gateway](/docs/en/claude-apps-gateway). See [server-managed settings](/docs/en/server-managed-settings).
* **MDM/OS-level policies**: delivered through native device management on macOS and Windows:
* macOS: `com.anthropic.claudecode` managed preferences domain. The plist's top-level keys mirror `managed-settings.json`, with nested settings as dictionaries and arrays as plist arrays. Deploy via configuration profiles in Jamf, Iru (Kandji), or similar MDM tools.
* Windows: `HKLM\SOFTWARE\Policies\ClaudeCode` registry key with a `Settings` value (REG\_SZ or REG\_EXPAND\_SZ) containing JSON (deployed via Group Policy or Intune)
* Windows (user-level): `HKCU\SOFTWARE\Policies\ClaudeCode` (lowest policy priority, only used when no admin-level source exists)
* **File-based**: `managed-settings.json` and `managed-mcp.json` deployed to system directories:
* macOS: `/Library/Application Support/ClaudeCode/`
* Linux and WSL: `/etc/claude-code/`
* Windows: `C:\Program Files\ClaudeCode\`
<Warning>
The legacy Windows path `C:\ProgramData\ClaudeCode\managed-settings.json` is no longer supported as of v2.1.75. Administrators who deployed settings to that location must migrate files to `C:\Program Files\ClaudeCode\managed-settings.json`.
</Warning>
File-based managed settings also support a drop-in directory at `managed-settings.d/` in the same system directory alongside `managed-settings.json`. This lets separate teams deploy independent policy fragments without coordinating edits to a single file.
Following the systemd convention, Claude Code merges `managed-settings.json` first as the base, then sorts all `*.json` files in the drop-in directory alphabetically and merges them on top. For scalar values, Claude Code lets later files override earlier ones; it concatenates and de-duplicates arrays and deep-merges objects. A later file's `fallbackModel` chain replaces an earlier one instead of merging with it, and a later file's [`extraKnownMarketplaces`](#extraknownmarketplaces) entry replaces an earlier file's same-name entry whole. Claude Code ignores hidden files starting with `.`.
Use numeric prefixes to control merge order, for example `10-telemetry.json` and `20-security.json`.
See [managed settings](/docs/en/permissions#managed-only-settings) and [Managed MCP configuration](/docs/en/managed-mcp) for details.
This [repository](https://github.com/anthropics/claude-code/tree/main/examples/mdm) includes starter deployment templates for Jamf, Iru (Kandji), Intune, and Group Policy. Use these as starting points and adjust them to fit your needs.
<Note>
Managed deployments can also restrict **plugin marketplace additions** using
`strictKnownMarketplaces`. For more information, see [Managed marketplace restrictions](/docs/en/plugin-marketplaces#managed-marketplace-restrictions).
</Note>
* **Other configuration** is stored in `~/.claude.json`. This file contains your OAuth session, [MCP server](/docs/en/mcp) configurations for user and local scopes, per-project state (allowed tools, trust settings), and various caches. Project-scoped MCP servers are stored separately in `.mcp.json`.
<Note>
Claude Code automatically creates timestamped backups of configuration files and retains the five most recent backups to prevent data loss.
</Note>
The following example works in any of the settings file locations above. Where you save the file determines where it applies:
* To apply it to all of your projects, save it as `~/.claude/settings.json`. This file lives in your home directory rather than in any project, so Claude Code reads it in every session regardless of which project you open.
* To share it with collaborators on one project, save it as `.claude/settings.json` in that project. Claude Code reads this file from the directory the session runs in, so it applies only to that project, and checking it into source control gives every collaborator the same settings.
```JSON Example settings.json theme={null}
{
"$schema": "https://json.schemastore.org/claude-code-settings.json",
"permissions": {
"allow": [
"Bash(npm run lint)",
"Bash(npm run test *)",
"Read(~/.zshrc)"
],
"deny": [
"Bash(curl *)",
"Read(./.env)",
"Read(./.env.*)",
"Read(./secrets/**)"
]
},
"env": {
"CLAUDE_CODE_ENABLE_TELEMETRY": "1",
"OTEL_METRICS_EXPORTER": "otlp",
"OTEL_EXPORTER_OTLP_PROTOCOL": "http/protobuf"
},
"companyAnnouncements": [
"Welcome to Acme Corp! Review our code guidelines at docs.acme.com",
"Reminder: Code reviews required for all PRs",
"New security policy in effect"
]
}
```
The `$schema` line in the example above points to the [official JSON schema](https://json.schemastore.org/claude-code-settings.json) for Claude Code settings. Adding it to your `settings.json` enables autocomplete and inline validation in VS Code, Cursor, and any other editor that supports JSON schema validation.
The published schema is updated periodically and may not include settings added in the most recent CLI releases, so a validation warning on a recently documented field does not necessarily mean your configuration is invalid.
<Tip>
After you edit a settings file, run `/status` inside Claude Code to confirm it was loaded. The `Setting sources` line lists each settings source loaded for the current session; a source appears once it loads with at least one setting, so a file with broken JSON doesn't appear even if it contains settings. See [Verify active settings](#verify-active-settings).
</Tip>
### When edits take effect
Claude Code watches your settings files and reloads them when they change, so edits to most keys apply to the running session without a restart. This includes `permissions`, `hooks`, and credential helpers like `apiKeyHelper`. The reload covers user, project, local, and managed settings, and the [`ConfigChange` hook](/docs/en/hooks#configchange) fires for each detected change.
A few keys are read once at session start and apply on the next restart instead:
* `model`: use [`/model`](/docs/en/model-config#setting-your-model) to switch mid-session
* [`outputStyle`](/docs/en/output-styles): part of the system prompt, which is rebuilt on `/clear` or restart
### Invalid entries in managed settings
Managed settings parse tolerantly. When a managed configuration contains an entry that fails schema validation, Claude Code strips that entry, records a warning, and enforces every remaining valid policy. A single typo cannot disable the rest of your organization's policy. Run [`/doctor`](/docs/en/debug-your-config#check-resolved-settings) to list stripped entries with their source file and field.
This behavior is consistent across all three delivery mechanisms: [server-managed settings](/docs/en/server-managed-settings), plist and registry policies deployed through MDM, and `managed-settings.json` files. Requires Claude Code v2.1.169 or later.
Security-enforcement fields are handled per field instead of being stripped wholesale when they are present but invalid:
| Field | Behavior when present but invalid |
| :---------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `allowedMcpServers` | Enforced as an empty allowlist, so no MCP servers are admitted until the value is fixed. An individual invalid entry is stripped and the valid subset is enforced. |
| `allowManagedHooksOnly` | Treated as `true`, so the [hook restrictions](#hook-configuration) apply until the value is fixed and, unless `disableCommandPluginSources` is explicitly `false`, command-sourced plugins are disabled. Applies in v2.1.229 and later. |
| `allowManagedMcpServersOnly` | Treated as `true`. |
| `disableCommandPluginSources` | Treated as `true`, so command-sourced plugins stay disabled until the value is fixed. Applies in v2.1.229 and later. |
| `availableModels` | Enforced as an empty allowlist, so only the Default model is available until the value is fixed. An individual non-string entry is stripped and the valid subset is enforced. Applies in v2.1.175 and later. |
| `enforceAvailableModels` | Treated as `true`. Applies in v2.1.175 and later. |
| `forceLoginOrgUUID` | No organization is permitted to log in until the value is fixed. |
| `deniedMcpServers` | An individual invalid entry is stripped and the valid subset is enforced. A wholly invalid value is dropped with a warning, since denying every server would block servers the policy never named. |
| `sandbox.credentials` | An invalid entry in `files` or `envVars` that still has a valid `path` or `name` and a `mode` of `mask` or `deny`, such as one whose `extract` pattern has no capturing group, is degraded to `mode: "deny"` with a warning, so the credential stays blocked, not masked, until you fix the entry. A degraded `files` entry pins [`filesystem.disabled`](/docs/en/sandboxing#disable-filesystem-isolation) like an explicit `deny` entry, and the warning notes that its read block isn't enforced if managed settings turn filesystem isolation off. An entry with an unknown `mode` or an invalid `path` or `name` is stripped. Each case warns; whether an entry is degraded or stripped, the remaining valid entries are still enforced, and a wholly invalid `credentials` value is dropped while the rest of `sandbox` still applies. Applies in v2.1.191 and later; before v2.1.221, every invalid entry was stripped. |
`requiredMinimumVersion` and `requiredMaximumVersion` fail open by design: an invalid value is stripped rather than enforced, so a bad policy push cannot prevent Claude Code from starting.
Validation errors surface in three places:
* Interactive sessions show a dialog at startup listing the invalid entries.
* Headless runs with `-p` print a summary to stderr.
* [`claude doctor`](/docs/en/debug-your-config) lists each invalid entry with its source and field.
Validate policy changes by running `claude doctor` on a test machine before deploying them fleet-wide.
This tolerance applies only to managed settings. User, project, and local settings files remain strict: a file that fails validation is rejected as a whole and reported.
### Available settings
`settings.json` supports a number of options:
| Key | Description | Example |
| :--------------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------ |
| `advisorModel` | Model for the server-side [advisor tool](/docs/en/advisor). Accepts the model aliases `"fable"`, `"opus"`, and `"sonnet"`, or a full model ID. `"fable"` requires [Fable 5 access](/docs/en/advisor#choose-an-advisor-model). Written automatically when you run `/advisor`. Unset to disable the advisor. | `"opus"` |
| `agent` | Run the main thread as a named subagent, and set the default agent for sessions dispatched from `claude agents`. Applies that subagent's system prompt, tool restrictions, and model. See [Invoke subagents explicitly](/docs/en/sub-agents#invoke-subagents-explicitly) | `"code-reviewer"` |
| `agentPushNotifEnabled` | **Default**: `false`. When [Remote Control](/docs/en/remote-control) is connected, allow Claude to send proactive push notifications to your phone, for example when a long task finishes. Appears in `/config` as **Push when Claude decides**. See [Mobile push notifications](/docs/en/remote-control#mobile-push-notifications) | `true` |
| `allowAllClaudeAiMcps` | (Managed settings only) Load the claude.ai connectors Claude Code fetches itself alongside a deployed `managed-mcp.json`, which otherwise takes exclusive control and suppresses them. Connectors delivered to cloud sessions stay suppressed. See [Managed MCP configuration](/docs/en/managed-mcp#allow-claude-ai-connectors-alongside-the-managed-set) | `true` |
| `allowedChannelPlugins` | (Managed settings only) Allowlist of channel plugins that may push messages. Replaces the default Anthropic allowlist when set. Undefined = fall back to the default, empty array = block all channel plugins. Requires `channelsEnabled: true`. See [Restrict which channel plugins can run](/docs/en/channels#restrict-which-channel-plugins-can-run) | `[{ "marketplace": "claude-plugins-official", "plugin": "telegram" }]` |
| `allowedHttpHookUrls` | Allowlist of URL patterns that HTTP hooks may target. Supports `*` as a wildcard. When set, hooks with non-matching URLs are blocked. Undefined = no restrictions, empty array = block all HTTP hooks. Arrays merge across settings sources. See [Hook configuration](#hook-configuration) | `["https://hooks.example.com/*"]` |
| `allowedMcpServers` | When set in managed-settings.json, allowlist of MCP servers users can configure. Undefined = no restrictions, empty array = lockdown. Applies to all scopes. Denylist takes precedence. See [Managed MCP configuration](/docs/en/managed-mcp) | `[{ "serverName": "github" }]` |
| `allowManagedHooksOnly` | (Managed settings only) Restrict which hooks run; see [Hook configuration](#hook-configuration) for the full effect list | `true` |
| `allowManagedMcpServersOnly` | (Managed settings only) Only `allowedMcpServers` from managed settings are respected. `deniedMcpServers` still merges from all sources. Users can still add MCP servers, but only the admin-defined allowlist applies. See [Managed MCP configuration](/docs/en/managed-mcp) | `true` |
| `allowManagedPermissionRulesOnly` | (Managed settings only) Prevent user and project settings from defining `allow`, `ask`, or `deny` permission rules. Only rules in managed settings apply. See [Managed-only settings](/docs/en/permissions#managed-only-settings) | `true` |
| `alwaysThinkingEnabled` | Enable [extended thinking](/docs/en/model-config#extended-thinking) by default for all sessions. Typically configured via the `/config` command rather than editing directly. To force thinking off regardless of this setting, set [`MAX_THINKING_TOKENS=0`](/docs/en/env-vars) in `env`, which disables thinking on the Anthropic API except on Fable 5, which cannot have thinking turned off. On [third-party providers](/docs/en/third-party-integrations) this omits the `thinking` parameter instead, and adaptive-reasoning models may still think | `true` |
| `apiKeyHelper` | Custom command, run through the system shell (`/bin/sh` on macOS and Linux, `cmd` on Windows), to generate an auth value. This value will be sent as `X-Api-Key` and `Authorization: Bearer` headers for model requests. Set the refresh interval with [`CLAUDE_CODE_API_KEY_HELPER_TTL_MS`](/docs/en/env-vars) | `/bin/generate_temp_api_key.sh` |
| `askUserQuestionTimeout` | **Default**: `"never"`. Idle time before an unanswered [`AskUserQuestion`](/docs/en/tools-reference) dialog auto-continues with whatever options you'd already selected. Accepts `"60s"`, `"5m"`, `"10m"`, or `"never"`. With the default, questions wait until you answer them. Appears in `/config` as **Question auto-continue timeout**, which writes this key to user settings. Not read from project or local settings. Requires Claude Code v2.1.200 or later | `"5m"` |
| `attribution` | Customize attribution for git commits and pull requests. See [Attribution settings](#attribution-settings) | `{"commit": "🤖 Generated with Claude Code", "pr": ""}` |
| `autoCompactEnabled` | **Default**: `true`. Automatically compact the conversation when context approaches the limit. Appears in `/config` as **Auto-compact**. To disable via environment variable, set [`DISABLE_AUTO_COMPACT`](/docs/en/env-vars) in `env` | `false` |
| `autoCompactWindow` | How full the context window gets before Claude Code [compacts automatically](/docs/en/context-window#when-your-context-fills-up), in tokens from `100000` to `1000000`. When unset, Claude Code uses a window tuned for your model. Set it with the [`/autocompact`](/docs/en/commands#all-commands) command, which writes this key to user settings; the [`--autocompact`](/docs/en/cli-reference#cli-flags) flag and the [`CLAUDE_CODE_AUTO_COMPACT_WINDOW`](/docs/en/env-vars) environment variable can override it. [Set the auto-compact window](/docs/en/model-config#set-the-auto-compact-window) covers how they interact | `500000` |
| `autoMemoryDirectory` | Custom directory for [auto memory](/docs/en/memory#storage-location) storage. Accepts an absolute path or a `~/`-prefixed path. From project or local settings, Claude Code honors it under the same [workspace trust rule as hooks](/docs/en/permissions#what-runs-before-you-trust-a-folder), since a cloned repository can supply this file | `"~/my-memory-dir"` |
| `autoMemoryEnabled` | **Default**: `true`. Enable [auto memory](/docs/en/memory#enable-or-disable-auto-memory). When `false`, Claude does not read from or write to the auto memory directory. You can also toggle this with `/memory` during a session. To disable via environment variable, set [`CLAUDE_CODE_DISABLE_AUTO_MEMORY`](/docs/en/env-vars) in `env` | `false` |
| `autoMode` | Customize what the [auto mode](/docs/en/permission-modes#eliminate-prompts-with-auto-mode) classifier blocks and allows. Contains `environment`, `allow`, `soft_deny`, and `hard_deny` arrays of prose rules. Include the literal string `"$defaults"` in an array to inherit the built-in rules at that position. See [Configure auto mode](/docs/en/auto-mode-config). Read from user settings, the `--settings` flag, and managed settings only. Ignored in project `.claude/settings.json` and local `.claude/settings.local.json`. Before v2.1.207, `.claude/settings.local.json` was also read | `{"soft_deny": ["$defaults", "Never run terraform apply"]}` |
| `autoMode.classifyAllShell` | **Default**: `false`. When `true`, suspends every Bash and PowerShell allow rule while auto mode is active so all shell commands route through the classifier, not only rules that match arbitrary-code-execution patterns. See [Route all shell commands through the classifier](/docs/en/auto-mode-config#route-all-shell-commands-through-the-classifier). Requires Claude Code v2.1.193 or later | `true` |
| `autoScrollEnabled` | **Default**: `true`. In [fullscreen rendering](/docs/en/fullscreen), follow new output to the bottom of the conversation. Appears in `/config` as **Auto-scroll**. Permission prompts still scroll into view when this is off | `false` |
| `autoUpdatesChannel` | **Default**: `"latest"`. Release channel to follow for updates. Use `"stable"` for a version that is typically about one week old and skips versions with major regressions, or `"latest"` for the most recent release. To disable auto-updates entirely, set [`DISABLE_AUTOUPDATER`](/docs/en/setup#disable-auto-updates) in `env` | `"stable"` |
| `availableModels` | Restrict which models users can select for the main session, [subagents](/docs/en/sub-agents), [skills](/docs/en/skills), and the [advisor](/docs/en/advisor). Does not affect the Default option unless `enforceAvailableModels` is also set. See [Restrict model selection](/docs/en/model-config#restrict-model-selection) | `["sonnet", "haiku"]` |
| `awaySummaryEnabled` | Show a one-line session recap when you return to the terminal after a few minutes away. Set to `false` or turn off Session recap in `/config` to disable. Same as [`CLAUDE_CODE_ENABLE_AWAY_SUMMARY`](/docs/en/env-vars) | `true` |
| `awsAuthRefresh` | Custom script that modifies the `.aws` directory (see [advanced credential configuration](/docs/en/amazon-bedrock#advanced-credential-configuration)) | `aws sso login --profile myprofile` |
| `awsCredentialExport` | Custom script that outputs JSON with AWS credentials (see [advanced credential configuration](/docs/en/amazon-bedrock#advanced-credential-configuration)) | `/bin/generate_aws_grant.sh` |
| `axScreenReader` | Render screen-reader friendly output: flat text without decorative borders or animations. Screen-reader mode uses the classic renderer, so the `tui` setting has no effect while it is active; attached [background sessions](/docs/en/agent-view) still render fullscreen. The [`CLAUDE_AX_SCREEN_READER`](/docs/en/env-vars) environment variable and the [`--ax-screen-reader`](/docs/en/cli-reference#cli-flags) flag take precedence. Requires Claude Code v2.1.181 or later | `true` |
| `blockedMarketplaces` | (Managed settings only) Blocklist of marketplace sources. Enforced on marketplace add and on plugin install, update, refresh, and auto-update, so a marketplace added before the policy was set cannot be used to fetch plugins. Blocked sources are checked before downloading, so they never touch the filesystem. A `github` entry may use the [owner-wildcard form](#owner-wildcards) `"owner/*"` to block every repository under that GitHub owner. Requires Claude Code v2.1.223 or later. See [Managed marketplace restrictions](/docs/en/plugin-marketplaces#managed-marketplace-restrictions) | `[{ "source": "github", "repo": "untrusted/plugins" }]` |
| `browserExternalPageTools` | (Managed settings only) Set to `"disabled"` to prevent Claude from using tools to read or act on external pages in the desktop app's [Browser pane](/docs/en/desktop#browse-external-sites). Users can still navigate to external sites themselves, and local dev server previews are unaffected | `"disabled"` |
| `channelsEnabled` | (Managed settings only) Allow [channels](/docs/en/channels) for the organization. On claude.ai Team and Enterprise plans, channels are blocked when this is unset or `false`. For [Anthropic Console](/docs/en/authentication#claude-console-authentication) accounts using API key authentication, channels are allowed by default unless your organization deploys managed settings, in which case this key must be set to `true` | `true` |
| `claudeMd` | (Managed settings only) CLAUDE.md-style instructions injected as organization-managed memory. Only honored when set in managed or policy settings and ignored in user, project, and local settings. See [organization-wide CLAUDE.md](/docs/en/memory#deploy-organization-wide-claude-md) | `"Always run make lint before committing."` |
| `claudeMdExcludes` | Glob patterns or absolute paths of `CLAUDE.md` files to skip when loading [memory](/docs/en/memory). Patterns match against absolute file paths. Only applies to user, project, and local memory; managed policy files cannot be excluded | `["**/vendor/**/CLAUDE.md"]` |
| `cleanupPeriodDays` | **Default**: `30` days, minimum `1`. Claude Code deletes [session files and other application data](/docs/en/claude-directory#cleaned-up-automatically) older than this period at startup, as long as it can safely determine the retention period. To disable transcript writes entirely, see [Plaintext storage](/docs/en/claude-directory#plaintext-storage). | `20` |
| `companyAnnouncements` | Announcement to display to users at startup. If multiple announcements are provided, they will be cycled through at random. | `["Welcome to Acme Corp! Review our code guidelines at docs.acme.com"]` |
| `crossSessionInbound` | How this session treats inbound [cross-session messages](/docs/en/cross-session-messaging#control-inbound-messages) from your other Claude Code sessions: `"accept"` delivers them to Claude, `"hold"` shows a notice for each message without delivering it, and `"refuse"` drops them. When no value applies, Claude Code decides per message from the two sessions' permission-mode classes; see [Control inbound messages](/docs/en/cross-session-messaging#control-inbound-messages) for the rules. Claude Code reads managed settings first, then the `--settings` flag, then user settings, and applies the first value found; a value in project or local settings applies only when it's stricter, on the `accept` \< `hold` \< `refuse` ladder, than the value those trusted sources give. When none of the trusted sources sets a value, a project or local `hold` or `refuse` still applies, replacing the per-message default. Requires Claude Code v2.1.224 or later. In sessions with cross-session messaging, appears in `/config` as **Messages from your other sessions**, which writes this key to user settings. The row requires Claude Code v2.1.232 or later, and Claude Code hides it while the `--settings` flag or managed settings set the key | `"hold"` |
| `defaultShell` | **Default**: `"bash"`, or `"powershell"` on Windows when Bash isn't available. Default shell for input-box `!` commands. Accepts `"bash"` or `"powershell"`. Setting `"powershell"` routes interactive `!` commands through PowerShell when the [PowerShell tool](/docs/en/tools-reference#powershell-tool) is enabled: it's on by default on Windows without Git Bash, and `CLAUDE_CODE_USE_POWERSHELL_TOOL=1` enables it elsewhere | `"powershell"` |
| `deniedMcpServers` | When set in managed-settings.json, denylist of MCP servers that are explicitly blocked. Applies to all scopes including managed servers. Denylist takes precedence over allowlist. See [Managed MCP configuration](/docs/en/managed-mcp) | `[{ "serverName": "filesystem" }]` |
| `dialogExpiry` | **Default**: `"5m"`. Deadline for dialogs Claude Code [forwards to a remote client](/docs/en/remote-control#limitations), such as a Remote Control or SDK host, and for the approval dialog for a [held cross-session message](/docs/en/cross-session-messaging#control-inbound-messages). When no answer arrives before the deadline, Claude Code cancels the dialog and continues with its no-action default. Permission prompts and [`AskUserQuestion`](/docs/en/tools-reference#askuserquestion-tool-behavior) questions use their own flows and aren't governed by this deadline. Accepts `"60s"`, `"5m"`, `"10m"`, or `"never"`, which disables the deadline. The [`CLAUDE_CODE_USER_DIALOG_TIMEOUT_MS`](/docs/en/env-vars) environment variable overrides this setting. Read from user, managed, and `--settings` sources only. Requires Claude Code v2.1.224 or later. Appears in `/config` as **Dialog expiry**, which writes this key to user settings. The row requires Claude Code v2.1.232 or later, and Claude Code hides it while the `--settings` flag or managed settings set the key | `"10m"` |
| `disableAgentView` | Set to `true` to turn off [background agents and agent view](/docs/en/agent-view): `claude agents`, `--bg`, `/background`, and the on-demand supervisor. Typically set in [managed settings](/docs/en/permissions#managed-settings). Equivalent to setting `CLAUDE_CODE_DISABLE_AGENT_VIEW` to `1` | `true` |
| `disableAllHooks` | Disable all [hooks](/docs/en/hooks#disable-or-remove-hooks), any custom [status line](/docs/en/statusline), and any custom [file suggestion](#file-suggestion-settings) command | `true` |
| `disableArtifact` | Set to `true` to disable the [Artifact](/docs/en/artifacts) tool, which publishes session output as a private web page on claude.ai. Equivalent to setting `CLAUDE_CODE_DISABLE_ARTIFACT` to `1` | `true` |
| `disableAutoMode` | Set to `"disable"` to prevent [auto mode](/docs/en/permission-modes#eliminate-prompts-with-auto-mode) from being activated. Removes `auto` from the `Shift+Tab` cycle and rejects `--permission-mode auto` at startup. Also accepted under `permissions` as `permissions.disableAutoMode`. Most useful in [managed settings](/docs/en/permissions#managed-settings) where users cannot override it | `"disable"` |
| `disableBrowserExternalNavigation` | (Managed settings only) Set to `true` to turn off external browsing in the desktop app's [Browser pane](/docs/en/desktop#browse-external-sites). Neither users nor Claude can navigate to external sites, and localhost dev server previews are unaffected. The value must be the JSON boolean `true`; the string `"true"` is ignored | `true` |
| `disableBundledSkills` | Set to `true` to disable the [skills](/docs/en/skills) and workflows included with Claude Code: bundled skills and workflows are removed entirely, while built-in commands like `/init` stay typable but are hidden from the model. `/doctor` stays typable like the built-in commands; hide it with [`DISABLE_DOCTOR_COMMAND`](/docs/en/env-vars) instead. Skills from plugins, `.claude/skills/`, and `.claude/commands/` are unaffected. Equivalent to setting `CLAUDE_CODE_DISABLE_BUNDLED_SKILLS` to `1` | `true` |
| `disableClaudeAiConnectors` | Disable [claude.ai MCP connectors](/docs/en/mcp#use-mcp-servers-from-claude-ai) so they are not auto-fetched or connected. Set in any settings scope. `true` in any source takes precedence, so a checked-in project `.claude/settings.json` can opt a repo out of cloud connectors, but a project-level `false` cannot override a user- or policy-level `true`. Servers passed explicitly via `--mcp-config` are unaffected. To deny individual connectors instead of all of them, use [`deniedMcpServers`](/docs/en/managed-mcp). Requires Claude Code v2.1.182 or later | `true` |
| `disableCommandPluginSources` | (Managed settings only) Control the [`command` plugin source](/docs/en/plugin-marketplaces#command-sources), which installs a plugin by running a marketplace-declared command on the user's machine. Set to `true` to block command-sourced plugins entirely. Claude Code never runs the command, doesn't install or update those plugins, and stops loading the ones already installed. Set to `false` to allow them explicitly. When unset, Claude Code follows [`allowManagedHooksOnly`](#hook-configuration), so an organization that restricts hook execution to managed settings gets command sources disabled too. Requires Claude Code v2.1.229 or later | `true` |
| `disableDeepLinkRegistration` | Set to `"disable"` to prevent Claude Code from registering the `claude-cli://` protocol handler with the operating system when you send the first prompt of an interactive session. [Deep links](/docs/en/deep-links) let external tools open a Claude Code session with a pre-filled prompt. Useful in environments where protocol handler registration is restricted or managed separately | `"disable"` |
| `disabledMcpjsonServers` | List of specific MCP servers from `.mcp.json` files to reject | `["filesystem"]` |
| `disableMobileSimulatorTools` | (Managed settings only) Set to `true` to block Claude's tools for the desktop app's [iOS Simulator pane](/docs/en/desktop-ios-simulator#turn-off-simulator-access). Users keep manual use of the pane; only Claude's access is removed. The value must be the JSON boolean `true`; any other value is ignored, and a malformed value such as `"true"` or `1` logs a warning | `true` |
| `disableRemoteControl` | Disable [Remote Control](/docs/en/remote-control): blocks `claude remote-control`, the `--remote-control` flag, auto-start, and the in-session toggle. Typically placed in [managed settings](/docs/en/permissions#managed-settings) for per-device MDM enforcement, but works from any scope | `true` |
| `disableSideloadFlags` | (Managed settings only) Reject the `--plugin-dir`, `--plugin-url`, `--agents`, and `--mcp-config` CLI flags at startup, which users could otherwise pass to bypass [`strictKnownMarketplaces`](#strictknownmarketplaces) for a single run. Also rejects these flags from any surface that spawns the CLI with them internally, currently [Cowork](/docs/en/desktop) local sessions in the desktop app. A `--mcp-config` whose servers are all in-process `type: "sdk"` entries is still accepted, so the Agent SDK and VS Code extension keep working. Doesn't block `claude mcp add`, `.mcp.json`, or SDK `setMcpServers()`; pair with [`allowedMcpServers`](/docs/en/managed-mcp) for per-server MCP control. Requires Claude Code v2.1.193 or later | `true` |
| `disableSkillShellExecution` | Disable inline shell execution for `` !`...` `` and ` ```! ` blocks in [skills](/en/skills) and custom commands from user, project, plugin, or additional-directory sources. Commands are replaced with `[shell command execution disabled by policy]` instead of being run. Bundled and managed skills are not affected. Most useful in [managed settings](/en/permissions#managed-settings) where users cannot override it | `true` |
| `disableWorkflows` | **Default**: `false`. Disable [dynamic workflows](/docs/en/workflows#turn-workflows-off) and the bundled workflow commands. Equivalent to setting `CLAUDE_CODE_DISABLE_WORKFLOWS` to `1` | `true` |
| `editorMode` | **Default**: `"normal"`. Key binding mode for the input prompt: `"normal"` or `"vim"`. Appears in `/config` as **Editor mode** | `"vim"` |
| `effortLevel` | Persist the [effort level](/docs/en/model-config#adjust-effort-level) across sessions. Accepts `"low"`, `"medium"`, `"high"`, or `"xhigh"`. Written automatically when you run `/effort` with one of those values. `--effort` and [`CLAUDE_CODE_EFFORT_LEVEL`](/docs/en/env-vars) override this for one session. See [Adjust effort level](/docs/en/model-config#adjust-effort-level) for supported models | `"xhigh"` |
| `emojiCompletionEnabled` | **Default**: `true`. Show emoji suggestions when you type `:` plus a shortcode in the prompt input, and replace a completed shortcode such as `:heart:` with its emoji. Set to `false` to disable both. See [Emoji shortcodes](/docs/en/interactive-mode#emoji-shortcodes). Requires Claude Code v2.1.217 or later | `false` |
| `enableAllProjectMcpServers` | Automatically approve all MCP servers defined in project `.mcp.json` files. As of v2.1.196, `claude mcp list` and `claude mcp get` honor this key in an untrusted folder only from [settings files that aren't checked into the repository](/docs/en/mcp#managing-your-servers) | `true` |
| `enableArtifact` | Enable or disable the [Artifact](/docs/en/artifacts) tool for this user. When unset, the default follows the feature's [availability](/docs/en/artifacts#availability) for your account. The **Artifacts** row in `/config` writes this key. A managed `disableArtifact` and your organization's [admin setting](/docs/en/artifacts#manage-artifacts-for-your-organization) take precedence, and the key is ignored in project and local settings (`.claude/settings.json`, `.claude/settings.local.json`), which a repository could otherwise commit. Requires Claude Code v2.1.196 or later | `true` |
| `enabledMcpjsonServers` | List of specific MCP servers from `.mcp.json` files to approve. As of v2.1.196, `claude mcp list` and `claude mcp get` honor this key in an untrusted folder only from [settings files that aren't checked into the repository](/docs/en/mcp#managing-your-servers) | `["memory", "github"]` |
| `enforceAvailableModels` | Extend the `availableModels` allowlist to the Default model. When `true` in managed settings and `availableModels` is a non-empty array, the Default option falls back to the first allowlisted entry that is available, but only when the model Default would resolve to (the [organization default](/docs/en/model-config#organization-default-model) when one applies, otherwise the account-type default) is not in the allowlist; an allowlisted default is kept as-is. Has no effect when `availableModels` is unset or empty. See [Enforce the allowlist for the Default model](/docs/en/model-config#enforce-the-allowlist-for-the-default-model). Requires Claude Code v2.1.175 or later | `true` |
| `env` | Environment variables applied to every session and to subprocesses Claude Code spawns from it. Set a variable to `""` to override a shell export with an empty string, which Claude Code treats as unset for provider selection. Subprocesses still inherit the empty value. `NO_COLOR` and `FORCE_COLOR` set here reach only subprocesses; to change Claude Code's own interface colors, set them in your shell before launching `claude`. Claude Code ignores identity variables set here that its hosting environments own, such as `CLAUDE_CODE_REMOTE` and `CLAUDE_CODE_ACCOUNT_UUID`. It also ignores [`CLAUDE_CODE_MESSAGING_SOCKET` and `CLAUDE_CODE_MESSAGING_TOKEN`](/docs/en/env-vars#variables), which it exports itself. Ignoring the socket variable requires Claude Code v2.1.224 or later, and ignoring the token requires v2.1.228 or later | `{"FOO": "bar"}` |
| `fallbackModel` | Fallback model(s) to try in order when the primary model is overloaded or unavailable. Claude Code switches to the next available model in the chain for the rest of the turn and shows a notice. `"default"` expands to the default model. Chains are capped at three models; extra entries are ignored. Unlike most array settings, this key does not merge across settings files: the highest-precedence file that defines it supplies the entire chain. The [`--fallback-model`](/docs/en/cli-reference#cli-flags) flag overrides this for one session. See [Fallback model chains](/docs/en/model-config#fallback-model-chains) | `["claude-sonnet-5", "claude-haiku-4-5"]` |
| `fastMode` | Turn [fast mode](/docs/en/fast-mode) on for sessions where it's available. Toggling with `/fast` writes `true` here in user settings and removes the key when you turn fast mode off | `true` |
| `fastModePerSessionOptIn` | When `true`, fast mode does not persist across sessions. Each session starts with fast mode off, requiring users to enable it with `/fast`. The user's fast mode preference is still saved. See [Require per-session opt-in](/docs/en/fast-mode#require-per-session-opt-in) | `true` |
| `feedbackSurveyRate` | Probability (0–1) that the [session quality survey](/docs/en/data-usage#session-quality-surveys) appears when eligible. Set to `0` to suppress entirely, or set [`CLAUDE_CODE_DISABLE_FEEDBACK_SURVEY`](/docs/en/env-vars) in `env`. Useful when using Amazon Bedrock, Google Cloud's Agent Platform, or Microsoft Foundry where the default sample rate does not apply | `0.05` |
| `fileCheckpointingEnabled` | **Default**: `true`. Snapshot files before each edit so [`/rewind`](/docs/en/checkpointing) can restore them. Appears in `/config` as **Rewind code (checkpoints)**. To disable via environment variable, set [`CLAUDE_CODE_DISABLE_FILE_CHECKPOINTING`](/docs/en/env-vars) in `env` | `false` |
| `fileSuggestion` | Configure a custom script for `@` file autocomplete. See [File suggestion settings](#file-suggestion-settings) | `{"type": "command", "command": "~/.claude/file-suggestion.sh"}` |
| `footerLinksRegexes` | Render extra clickable badges in the footer when a regex matches turn output. Each entry has a `pattern`, a `url` template with `{name}` placeholders filled from named capture groups, and an optional `label`. Read from user, `--settings` flag, and managed settings only. See [Footer link badges](#footer-link-badges) for URL constraints, scheme allowlist, and limits. Requires Claude Code v2.1.176 or later | `[{"type": "regex", "pattern": "\\b(?<key>PROJ-\\d+)\\b", "url": "https://issues.example.com/browse/{key}", "label": "{key}"}]` |
| `forceLoginMethod` | Use `claudeai` to restrict login to claude.ai accounts, `console` to restrict login to Claude Console accounts, or `gateway` to restrict login to a cloud gateway; see [Claude apps gateway](/docs/en/claude-apps-gateway). On Claude Code v2.1.212 or later, every first-party login path applies the restriction, including the [VS Code extension](/docs/en/vs-code), the Agent SDK, `claude setup-token`, and `/install-github-app`; before v2.1.212, only terminal logins applied it. See [Restrict login to your organization](/docs/en/authentication#restrict-login-to-your-organization) for how each login path, environment credentials, and third-party providers are handled | `claudeai` |
| `forceLoginGatewayUrl` | Pre-fills and locks the gateway URL on the `/login` Cloud gateway screen. Either this key or `forceLoginMethod: "gateway"` surfaces that screen; set both so the URL is filled in. Honored only at the managed policy tier; ignored in user and project settings. See [Claude apps gateway](/docs/en/claude-apps-gateway#set-the-gateway-url) | `"https://claude-gateway.example.com"` |
| `forceLoginOrgUUID` | Require claude.ai account logins to belong to a specific Anthropic organization. Accepts a single UUID string, which also pre-selects that organization during a claude.ai or Claude Console login, or an array of UUIDs where any listed organization is accepted without pre-selection. An empty array fails closed and blocks login with a misconfiguration message. See [Restrict login to your organization](/docs/en/authentication#restrict-login-to-your-organization) for how Claude Code treats Claude Console logins, the other login paths, and environment credentials | `"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"` or `["xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", "yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy"]` |
| `forceRemoteSettingsRefresh` | (Managed settings only) Block CLI startup until remote managed settings are freshly fetched from the server. If the fetch fails, the CLI exits rather than continuing with cached or no settings. When not set, startup continues without waiting for remote settings. See [fail-closed enforcement](/docs/en/server-managed-settings#enforce-fail-closed-startup) | `true` |
| `gcpAuthRefresh` | Custom script that refreshes GCP Application Default Credentials when they expire or cannot be loaded. See [advanced credential configuration](/docs/en/google-vertex-ai#advanced-credential-configuration) | `gcloud auth application-default login` |
| `hooks` | Configure custom commands to run at lifecycle events. See [hooks documentation](/docs/en/hooks) for format | See [hooks](/docs/en/hooks) |
| `httpHookAllowedEnvVars` | Allowlist of environment variable names HTTP hooks may interpolate into headers. When set, each hook's effective `allowedEnvVars` is the intersection with this list. Undefined = no restriction. Arrays merge across settings sources. See [Hook configuration](#hook-configuration) | `["MY_TOKEN", "HOOK_SECRET"]` |
| `includeGitInstructions` | **Default**: `true`. Include built-in commit and PR workflow instructions and the git status snapshot in Claude's system prompt. Set to `false` to remove both, for example when using your own git workflow skills. The `CLAUDE_CODE_DISABLE_GIT_INSTRUCTIONS` environment variable takes precedence over this setting when set | `false` |
| `inputNeededNotifEnabled` | **Default**: `false`. When [Remote Control](/docs/en/remote-control) is connected, send a push notification to your phone when a permission prompt or question is waiting for your input. Appears in `/config` as **Push when actions required**. See [Mobile push notifications](/docs/en/remote-control#mobile-push-notifications) | `true` |
| `isolatePeerMachines` | Require your explicit approval before Claude's `SendMessage` reaches one of your sessions beyond this machine; see [cross-session messaging](/docs/en/cross-session-messaging#require-approval-for-cross-machine-messages). The approval prompt appears even in [`bypassPermissions` mode](/docs/en/permission-modes#skip-all-checks-with-bypasspermissions-mode). A `true` from any settings scope applies, so a checked-in project file can turn the requirement on but not off. The cross-machine `SendMessage` approval requires Claude Code v2.1.224 or later | `true` |
| `language` | Configure Claude's preferred response language (e.g., `"japanese"`, `"spanish"`, `"french"`). Claude will respond in this language by default. Also sets the language for [voice dictation](/docs/en/voice-dictation#change-the-dictation-language) and auto-generated session titles. As of v2.1.176, when not set, session titles match the language of your conversation | `"japanese"` |
| `minimumVersion` | Floor that prevents background auto-updates and `claude update` from installing a version below this one. Switching from the `"latest"` channel to `"stable"` via `/config` prompts you to stay on the current version or allow the downgrade. Choosing to stay sets this value. Also useful in [managed settings](/docs/en/permissions#managed-settings) to pin an organization-wide minimum. For a hard floor that blocks startup entirely, see `requiredMinimumVersion` | `"2.1.100"` |
| `model` | Override the default model to use for Claude Code. `--model` and [`ANTHROPIC_MODEL`](/docs/en/model-config#environment-variables) override this for one session | `"claude-sonnet-5"` |
| `modelOverrides` | Map Anthropic model IDs to provider-specific model IDs such as Amazon Bedrock inference profile ARNs. Each model picker entry uses its mapped value when calling the provider API. See [Override model IDs per version](/docs/en/model-config#override-model-ids-per-version) | `{"claude-opus-4-6": "arn:aws:bedrock:..."}` |
| `otelHeadersHelper` | Script to generate dynamic OpenTelemetry headers. Runs at startup and periodically. Set the refresh interval with [`CLAUDE_CODE_OTEL_HEADERS_HELPER_DEBOUNCE_MS`](/docs/en/env-vars). See [Dynamic headers](/docs/en/monitoring-usage#dynamic-headers) | `/bin/generate_otel_headers.sh` |
Cut at 300 lines. The page has the rest.
setup First recorded · 680 lines, first recorded
# Advanced setup ## System requirements ### Additional dependencies ## Install Claude Code ### Set up on Windows ### Alpine Linux and musl-based distributions ## Verify your installation ## Authenticate ## Update Claude Code ### Auto-updates ### Configure release channel ### Pin a minimum version ### Disable auto-updates ### Update manually ## Advanced installation options ### Install a specific version ### Install with Linux package managers ### Install with npm ### Binary integrity and code signing #### Verify the manifest signature #### Platform code signatures ## Uninstall Claude Code ### Native installation ### Homebrew installation ### WinGet installation ### apt / dnf / apk ### npm ### Remove configuration files
The first capture of this source. The page was already there, and this is what it said.
# Advanced setup
> System requirements, platform-specific installation, version management, and uninstallation for Claude Code.
This page covers system requirements, platform-specific installation details, updates, and uninstallation. For a guided walkthrough of your first session, see the [quickstart](/docs/en/quickstart). If you've never used a terminal before, see the [terminal guide](/docs/en/terminal-guide).
## System requirements
Claude Code runs on the following platforms and configurations:
* **Operating system**:
* macOS 13.0+
* Windows 10 1809+ or Windows Server 2019+
* Ubuntu 20.04+
* Debian 10+
* Alpine Linux 3.19+
* **Hardware**: 4 GB+ RAM, x64 or ARM64 processor
* **Network**: internet connection required. See [network configuration](/docs/en/network-config#network-access-requirements).
* **Shell**: Bash, Zsh, PowerShell, or CMD.
* **Location**: [Anthropic supported countries](https://www.anthropic.com/supported-countries)
### Additional dependencies
* **ripgrep**: usually included with Claude Code. If search fails, see [search troubleshooting](/docs/en/troubleshooting#search-and-discovery-issues).
## Install Claude Code
<Tip>
Prefer a graphical interface? The [Desktop app](/docs/en/desktop-quickstart) lets you use Claude Code without the terminal. Download it for [macOS](https://claude.ai/api/desktop/darwin/universal/dmg/latest/redirect?utm_source=claude_code\&utm_medium=docs), [Windows](https://claude.com/download?utm_source=claude_code\&utm_medium=docs), or [Linux](/docs/en/desktop-linux).
New to the terminal? See the [terminal guide](/docs/en/terminal-guide) for step-by-step instructions.
</Tip>
To install Claude Code, use one of the following methods:
<Tabs>
<Tab title="Native Install (Recommended)">
**macOS, Linux, WSL:**
```bash theme={null}
curl -fsSL https://claude.ai/install.sh | bash
```
**Windows PowerShell:**
```powershell theme={null}
irm https://claude.ai/install.ps1 | iex
```
**Windows CMD:**
```batch theme={null}
curl -fsSL https://claude.ai/install.cmd -o install.cmd && install.cmd && del install.cmd
```
If you see `The token '&&' is not a valid statement separator`, you're in PowerShell, not CMD. If you see `'irm' is not recognized as an internal or external command`, you're in CMD, not PowerShell. Your prompt shows `PS C:\` when you're in PowerShell and `C:\` without the `PS` when you're in CMD.
If the install command fails with `syntax error near unexpected token '<'`, a `403`, or another curl error, see [Troubleshoot installation](/docs/en/troubleshoot-install#find-your-error) to match the error to a fix and for alternative install methods.
[Git for Windows](https://git-scm.com/downloads/win) is recommended on native Windows so Claude Code can use the Bash tool. If Git for Windows is not installed, Claude Code uses PowerShell as the shell tool instead. WSL setups do not need Git for Windows.
<Info>
Native installations automatically update in the background to keep you on the latest version.
</Info>
</Tab>
<Tab title="Homebrew">
```bash theme={null}
brew install --cask claude-code
```
Homebrew offers two casks. `claude-code` tracks the stable release channel, which is typically about a week behind and skips releases with major regressions. `claude-code@latest` tracks the latest channel and receives new versions as soon as they ship.
<Info>
Homebrew installations do not auto-update. Run `brew upgrade claude-code` or `brew upgrade claude-code@latest`, depending on which cask you installed, to get the latest features and security fixes.
</Info>
</Tab>
<Tab title="WinGet">
```powershell theme={null}
winget install Anthropic.ClaudeCode
```
<Info>
WinGet installations do not auto-update. Run `winget upgrade Anthropic.ClaudeCode` periodically to get the latest features and security fixes.
</Info>
</Tab>
</Tabs>
You can also install with [apt, dnf, or apk](/docs/en/setup#install-with-linux-package-managers) on Debian, Fedora, RHEL, and Alpine.
After installation completes, open a terminal in the project you want to work in and start Claude Code:
```bash theme={null}
claude
```
Claude Code opens an interactive session in your terminal.
If you encounter any issues during installation, see [Troubleshoot installation and login](/docs/en/troubleshoot-install).
### Set up on Windows
You can run Claude Code natively on Windows or inside WSL. Pick based on where your projects are located and which features you need:
| Option | Requires | [Sandboxing](/docs/en/sandboxing) | When to use |
| -------------- | ---------------------------------------------------------------------- | ---------------------------- | ----------------------------------------------- |
| Native Windows | None; [Git for Windows](https://git-scm.com/downloads/win) is optional | Not supported | Windows-native projects and tools |
| WSL 2 | WSL 2 enabled | Supported | Linux toolchains or sandboxed command execution |
| WSL 1 | WSL 1 enabled | Not supported | If WSL 2 is unavailable |
**Option 1: Native Windows**
Run the install command from PowerShell or CMD. You do not need to run as Administrator. Installing [Git for Windows](https://git-scm.com/downloads/win) is optional. It enables the [Bash tool](/docs/en/tools-reference#bash-tool-behavior) by providing Git Bash.
Whether you install from PowerShell or CMD only affects which install command you run. Your prompt shows `PS C:\Users\YourName>` in PowerShell and `C:\Users\YourName>` without the `PS` in CMD. If you're new to the terminal, the [terminal guide](/docs/en/terminal-guide#windows) walks through each step.
After installation, launch `claude` from any terminal.
* **Without Git for Windows**, Claude Code runs shell commands via the [PowerShell tool](/docs/en/tools-reference#powershell-tool).
* **With Git for Windows**, Claude Code uses Git Bash for the [Bash tool](/docs/en/tools-reference#bash-tool-behavior). If Claude Code can't find Git Bash, set the path in your [settings.json file](/docs/en/settings):
```json theme={null}
{
"env": {
"CLAUDE_CODE_GIT_BASH_PATH": "C:\\Program Files\\Git\\bin\\bash.exe"
}
}
```
When Git for Windows is installed, the PowerShell tool is rolling out progressively as an additional option alongside Bash. Set `CLAUDE_CODE_USE_POWERSHELL_TOOL=1` to opt in or `0` to opt out. See [PowerShell tool](/docs/en/tools-reference#powershell-tool) for setup and limitations.
**Option 2: WSL**
Open your WSL distribution and run the Linux installer from the [install instructions](#install-claude-code) above. You install and launch `claude` inside the WSL terminal, not from PowerShell or CMD.
### Alpine Linux and musl-based distributions
Installing Claude Code on Alpine and other musl/uClibc-based distributions requires `bash` and `curl` for the install command, and `libgcc`, `libstdc++`, and `ripgrep` at runtime. Alpine doesn't include `bash` or `curl` by default, so the documented install command fails with a `not found` error until you install them. Install these packages using your distribution's package manager, then set `USE_BUILTIN_RIPGREP=0`.
This example installs the required packages on Alpine:
```bash theme={null}
apk add bash curl libgcc libstdc++ ripgrep
```
On Alpine, `ripgrep` is in the community repository. If `apk` reports that the package is missing, add the community repository to `/etc/apk/repositories`, using your Alpine version:
```bash theme={null}
echo "https://dl-cdn.alpinelinux.org/alpine/v3.22/community" >> /etc/apk/repositories
```
Run `apk update` to refresh the package index, and retry the `apk add` command.
Then set `USE_BUILTIN_RIPGREP` to `0` in your [`settings.json`](/docs/en/settings#available-settings) file:
```json theme={null}
{
"env": {
"USE_BUILTIN_RIPGREP": "0"
}
}
```
## Verify your installation
After installing, confirm Claude Code is working:
```bash theme={null}
claude --version
```
A working installation prints a version number such as `2.1.211 (Claude Code)`.
If this fails with `command not found` or another error, see [Troubleshoot installation and login](/docs/en/troubleshoot-install).
For a more detailed check of your installation and configuration, run [`claude doctor`](/docs/en/troubleshooting#get-more-help):
```bash theme={null}
claude doctor
```
`claude doctor` prints read-only installation and settings diagnostics without starting a session, including install health, settings-file validation errors, and any warnings with suggested fixes.
## Authenticate
Claude Code requires a Pro, Max, Team, Enterprise, or Console account. The free Claude.ai plan does not include Claude Code access. You can also use Claude Code with a third-party API provider like [Amazon Bedrock](/docs/en/amazon-bedrock), [Google Cloud's Agent Platform](/docs/en/google-vertex-ai), or [Microsoft Foundry](/docs/en/microsoft-foundry).
After installing, log in by running `claude` and following the browser prompts. If the `ANTHROPIC_API_KEY` environment variable is set, Claude Code prompts you once to approve the key instead of opening a browser. See [Authentication](/docs/en/authentication) for all account types and team setup options.
## Update Claude Code
Native installations automatically update in the background. You can [configure the release channel](#configure-release-channel) to control whether you receive updates immediately or on a delayed stable schedule, or [disable auto-updates](#disable-auto-updates) entirely. Homebrew, WinGet, and [Linux package manager](#install-with-linux-package-managers) installations require manual updates by default.
### Auto-updates
Claude Code checks for updates on startup and periodically while running. Updates download and install in the background, then take effect the next time you start Claude Code.
Run `claude doctor` to see the result of the most recent update attempt.
On macOS and Linux, the native installer manages the launcher at `~/.local/bin/claude` as a symlink into `~/.local/share/claude/versions/`. If you replace that launcher with your own script or symlink, auto-update and `claude update` leave it in place: new versions still install under the `versions/` directory, and your launcher decides which version runs. Before v2.1.207, the auto-updater replaced a custom launcher at that path with its own symlink on every update.
With a custom launcher, Claude Code also keeps every installed version on disk because it can't tell which version the launcher needs. `claude doctor` reports a launcher that the native installer didn't create.
To let Claude Code manage the launcher again, remove `~/.local/bin/claude` and run `claude update`.
If an npm global install can't auto-update because the npm global directory isn't writable, Claude Code shows a one-time notice at startup, and `claude doctor` lists the available fixes. See [permission errors during installation](/docs/en/troubleshoot-install#permission-errors-during-installation) for details.
<Note>
Homebrew, WinGet, apt, dnf, and apk installations do not auto-update by default; see below to opt in for Homebrew and WinGet. To upgrade Homebrew manually, run `brew upgrade claude-code` or `brew upgrade claude-code@latest`, depending on which cask you installed. For WinGet, run `winget upgrade Anthropic.ClaudeCode`. For Linux package managers, see the upgrade commands in [Install with Linux package managers](#install-with-linux-package-managers).
To have Claude Code run the upgrade command for you on Homebrew or WinGet, set [`CLAUDE_CODE_PACKAGE_MANAGER_AUTO_UPDATE`](/docs/en/env-vars) to `1`. Claude Code then runs the upgrade in the background when a new version is available and shows a restart prompt on success. The upgrade targets only the Claude Code package and does not affect other software you have installed.
On WinGet the upgrade may fail while Claude Code is running because Windows locks the executable. In that case Claude Code shows the manual command instead. apt, dnf, and apk continue to require a manual upgrade because those commands need elevated privileges.
**Known issue:** Claude Code may notify you of updates before the new version is available in these package managers. If an upgrade fails, wait and try again later.
Homebrew keeps old versions on disk after upgrades. Run `brew cleanup` periodically to reclaim disk space.
</Note>
### Configure release channel
Control which release channel Claude Code follows for auto-updates and `claude update` with the `autoUpdatesChannel` setting:
* `"latest"`, the default: receive new features as soon as they're released
* `"stable"`: use a version that is typically about one week old, skipping releases with major regressions
Configure this via `/config` → **Auto-update channel**, or add it to your [settings.json file](/docs/en/settings):
```json theme={null}
{
"autoUpdatesChannel": "stable"
}
```
For enterprise deployments, you can enforce a consistent release channel across your organization using [managed settings](/docs/en/permissions#managed-settings).
Homebrew installations choose a channel by cask name instead of this setting: `claude-code` tracks stable and `claude-code@latest` tracks latest.
### Pin a minimum version
The `minimumVersion` setting establishes a floor. Background auto-updates and `claude update` refuse to install any version below this value, so moving to the `"stable"` channel does not downgrade you if you are already on a newer `"latest"` build.
Switching from `"latest"` to `"stable"` via `/config` prompts you to either stay on the current version or allow the downgrade. Choosing to stay sets `minimumVersion` to that version. Switching back to `"latest"` clears it.
Add it to your [settings.json file](/docs/en/settings) to pin a floor explicitly:
```json theme={null}
{
"autoUpdatesChannel": "stable",
"minimumVersion": "2.1.100"
}
```
In [managed settings](/docs/en/permissions#managed-settings), this enforces an organization-wide minimum that user and project settings cannot override.
The `minimumVersion` pin only constrains updates. To make Claude Code refuse to start outside a version range, use the managed settings `requiredMinimumVersion` and `requiredMaximumVersion` instead. Updates also respect the `requiredMaximumVersion` ceiling. See [available settings](/docs/en/settings#available-settings).
### Disable auto-updates
Set `DISABLE_AUTOUPDATER` to `"1"` in the `env` key of your [`settings.json`](/docs/en/settings#available-settings) file:
```json theme={null}
{
"env": {
"DISABLE_AUTOUPDATER": "1"
}
}
```
`DISABLE_AUTOUPDATER` only stops the background check; `claude update` and `claude install` still work. To block all update paths, including manual updates, set [`DISABLE_UPDATES`](/docs/en/env-vars) instead. Use this when you distribute Claude Code through your own channels and need users to stay on the version you provide.
### Update manually
To apply an update immediately without waiting for the next background check, run:
```bash theme={null}
claude update
```
When an update installs, the command reports `Successfully updated from <old version> to version <new version>`. If you're already on the newest version, it reports `Claude Code is up to date (<version>)`. Installs managed by Homebrew, WinGet, or apk report `Claude is up to date!` instead.
## Advanced installation options
These options are for version pinning, Linux package managers, npm, and verifying binary integrity.
### Install a specific version
The native installer accepts either a specific version number or a release channel (`latest` or `stable`). The channel you choose at install time becomes your default for auto-updates. See [configure release channel](#configure-release-channel) for more information.
To install the latest version (default):
<Tabs>
<Tab title="macOS, Linux, WSL">
```bash theme={null}
curl -fsSL https://claude.ai/install.sh | bash
```
</Tab>
<Tab title="Windows PowerShell">
Cut at 300 lines. The page has the rest.
skills First recorded · 1054 lines, first recorded
# Extend Claude with skills ## Bundled skills ### Run and verify your app ## Getting started ### Create your first skill ### Where skills live #### Live change detection #### Discovery from parent and nested directories #### Skills from additional directories #### Skills in Cowork and cloud sessions #### Where synced skills load #### When a synced skill name matches another command #### How Claude Code handles the frontmatter of a synced skill #### How Claude Code handles the body of a synced skill ## Configure skills ### Types of skill content ### Frontmatter reference #### Using skill frontmatter outside Claude Code #### How a skill gets its command name #### Available string substitutions ### Add supporting files ### Control who invokes a skill ### Skill content lifecycle ### Pre-approve tools for a skill ### Pass arguments to skills ## Advanced patterns ### Inject dynamic context #### How injected commands run #### When an injected command fails ### Run skills in a subagent #### Example: Research skill using Explore agent ### Restrict Claude's skill access ### Override skill visibility from settings ## Evaluate and iterate on a skill ### Run evals with skill-creator ## Share skills ### Generate visual output ## Troubleshooting ### Skill not triggering ### Skill triggers too often ### Skill descriptions are cut short ## Related resources
The first capture of this source. The page was already there, and this is what it said.
# Extend Claude with skills
> Create, manage, and share skills to extend Claude's capabilities in Claude Code. Includes custom commands and bundled skills.
Skills extend what Claude can do. Create a `SKILL.md` file with instructions, and Claude adds it to its toolkit. Claude uses skills when relevant, or you can invoke one directly with `/skill-name`.
Create a skill when you keep pasting the same instructions, checklist, or multi-step procedure into chat, or when a section of CLAUDE.md has grown into a procedure rather than a fact. Unlike CLAUDE.md content, a skill's body loads only when it's used, so long reference material costs almost nothing until you need it.
<Note>
For built-in commands like `/help` and `/compact`, and bundled skills like `/debug` and `/code-review`, see the [commands reference](/docs/en/commands).
**Custom commands have been merged into skills.** A file at `.claude/commands/deploy.md` and a skill at `.claude/skills/deploy/SKILL.md` both create `/deploy` and work the same way. Your existing `.claude/commands/` files keep working. Skills add optional features: a directory for supporting files, frontmatter to [control whether you or Claude invokes them](#control-who-invokes-a-skill), and the ability for Claude to load them automatically when relevant.
</Note>
Claude Code skills follow the [Agent Skills](https://agentskills.io) open standard, which works across multiple AI tools. Claude Code extends the standard with additional features like [invocation control](#control-who-invokes-a-skill), [subagent execution](#run-skills-in-a-subagent), and [dynamic context injection](#inject-dynamic-context). See [Using skill frontmatter outside Claude Code](#using-skill-frontmatter-outside-claude-code) for which frontmatter fields are part of the standard and which are Claude Code extensions.
## Bundled skills
Claude Code includes a set of bundled skills, such as `/doctor`, `/code-review`, `/batch`, `/debug`, `/loop`, and `/claude-api`. Bundled skills are prompt-based: they give Claude detailed instructions and let it orchestrate the work using its tools. Most built-in commands instead execute fixed logic directly.
You invoke a bundled skill the same way as any other skill, by typing `/` followed by the skill name. Claude invokes some bundled skills automatically when relevant; others, including `/verify`, run only when you invoke them, which keeps you in control of when these longer-running checks spend time and tokens.
Bundled skills are available in every session. To turn them off, use the [`disableBundledSkills`](/docs/en/settings#available-settings) setting, which disables every bundled skill except `/doctor`.
<Note>
The [`/doctor`](/docs/en/commands#all-commands) setup checkup stays typable when `disableBundledSkills` is on, in Claude Code v2.1.205 and later. To hide it, set the `DISABLE_DOCTOR_COMMAND` environment variable or a [`skillOverrides`](#override-skill-visibility-from-settings) entry of `"doctor": "off"`. Before v2.1.205, `/doctor` was a built-in command rather than a bundled skill.
</Note>
Bundled skills are listed alongside built-in commands in the [commands reference](/docs/en/commands), marked **Skill** in the Purpose column.
### Run and verify your app
Three bundled skills work together to launch your app and confirm changes against the running app instead of just tests:
| Skill | Purpose |
| :--------------------- | :---------------------------------------------------------------------------------------------------------------- |
| `/run` | Launch and drive your app to see a change working |
| `/verify` | Build and run your app to confirm a code change does what it should, without falling back to tests or type checks |
| `/run-skill-generator` | Teach `/run` and `/verify` how to build and launch your project |
All three skills require Claude Code v2.1.145 or later. Check your version with `claude --version` or the `/status` command.
`/run` and `/verify` work without setup. They infer the launch from your project type (CLI, server, TUI, browser-driven) and from what's in your README, `package.json`, or `Makefile`. That inference gets unreliable for projects that need anything beyond a standard launch: a database, an env file, a graphical session, a multi-step build.
`/run-skill-generator` records the recipe instead. It gets your app running from a clean environment, captures what worked (the install commands, the env vars, the launch script), and commits it as a per-project skill at `.claude/skills/run-<name>/`. After that, `/run`, `/verify`, and any other agent in the repo follow the recorded recipe instead of rediscovering it. Run `/run-skill-generator` once per project, and again if the build or launch process changes.
`/verify` can also record its own recipe. When it has to build and drive your app without a recorded recipe, it writes what worked to `.claude/skills/verify/SKILL.md` at the repo root, or in the touched package directory in a monorepo, so later runs and other agents follow the same steps. At the repo root, the recorded skill replaces the bundled `/verify`. This requires Claude Code v2.1.200 or later.
Claude edits the recorded file only when it steered a run wrong, such as a command that failed or a missing step, so you can commit the file without per-session diffs. Before v2.1.205, the bundled skill told Claude to fold in anything a run learned, which caused frequent merge conflicts.
## Getting started
### Create your first skill
This example creates a skill that summarizes the uncommitted changes in your git repository and flags anything risky. It pulls the live diff into the prompt before Claude reads it, so the response is grounded in your actual working tree rather than what Claude can guess from open files. Claude loads the skill automatically when you ask about your changes, or you can invoke it directly with `/summarize-changes`.
<Steps>
<Step title="Create the skill directory">
Create a directory for the skill in your personal skills folder. Personal skills are available across all your projects.
```bash theme={null}
mkdir -p ~/.claude/skills/summarize-changes
```
</Step>
<Step title="Write SKILL.md">
Every skill needs a `SKILL.md` file with two parts: YAML frontmatter between `---` markers that tells Claude when to use the skill, and markdown content with the instructions Claude follows when the skill runs. The directory name becomes the command you type, and the `description` helps Claude decide when to load the skill automatically.
Save this to `~/.claude/skills/summarize-changes/SKILL.md`:
```yaml theme={null}
---
description: Summarizes uncommitted changes and flags anything risky. Use when the user asks what changed, wants a commit message, or asks to review their diff.
---
## Current changes
!`git diff HEAD`
## Instructions
Summarize the changes above in two or three bullet points, then list any risks you notice such as missing error handling, hardcoded values, or tests that need updating. If the diff is empty, say there are no uncommitted changes.
```
The `` !`git diff HEAD` `` line uses [dynamic context injection](#inject-dynamic-context): Claude Code runs the command and replaces the line with its output before Claude sees the skill content, so the instructions arrive with the current diff already inlined.
</Step>
<Step title="Test the skill">
Open a git project, make a small edit to any file, and start Claude Code by running `claude`. You can test the skill two ways.
**Let Claude invoke it automatically** by asking something that matches the description:
```text theme={null}
What did I change?
```
**Or invoke it directly** with the skill name:
```text theme={null}
/summarize-changes
```
Either way, Claude should respond with a short summary of your edit and a list of risks.
</Step>
</Steps>
### Where skills live
Where you store a skill determines who can use it:
| Location | Path | Applies to |
| :--------- | :-------------------------------------------------- | :----------------------------- |
| Enterprise | See [managed settings](/docs/en/settings#settings-files) | All users in your organization |
| Personal | `~/.claude/skills/<skill-name>/SKILL.md` | All your projects |
| Project | `.claude/skills/<skill-name>/SKILL.md` | This project only |
| Plugin | `<plugin>/skills/<skill-name>/SKILL.md` | Where plugin is enabled |
When skills share the same name, Claude Code resolves the conflict by source:
* Across levels, enterprise overrides personal, and personal overrides project.
* For example, with a `deploy` skill in both `~/.claude/skills/` and your project's `.claude/skills/`, `/deploy` runs the personal one.
* A skill at any of these levels also overrides a bundled skill with the same name.
* For example, a `code-review` skill in your project's `.claude/skills/` replaces the bundled `/code-review`.
* Plugin skills use a `plugin-name:skill-name` namespace, so they can't conflict with other levels.
* For example, `my-plugin/skills/deploy/SKILL.md` becomes `/my-plugin:deploy` and loads alongside a `deploy` skill in your project's `.claude/skills/`.
* If you have files in `.claude/commands/`, those work the same way, but if a skill and a command share the same name, the skill takes precedence.
* For example, with both `.claude/commands/deploy.md` and `.claude/skills/deploy/SKILL.md`, `/deploy` runs the skill.
* A skill or command from any of these sources overrides a skill [synced from your claude.ai account](#when-a-synced-skill-name-matches-another-command) with the same name.
* For example, with a `deploy` skill enabled on claude.ai and another in your project's `.claude/skills/`, `/deploy` runs the project one.
Skills also load from nested `.claude/skills/` directories below your working directory. When Claude reads or edits a file in a subdirectory, skills from that subdirectory's `.claude/skills/` become available. This lets a monorepo package provide its own skills that apply when working on that package, even if the session started at the repo root.
If a nested skill shares a name with another skill, both stay available. For example, with a `deploy` skill at the project root and another in `apps/web/.claude/skills/`:
* The nested one appears under a directory-qualified name, `apps/web:deploy`.
* Its description says which directory it applies to.
* Claude picks the variant that matches the files it is working on.
Typing `/deploy` runs the project-root skill. Type the qualified name `/apps/web:deploy` to run the nested variant explicitly.
When you or Claude invoke the unqualified name, the project-root skill loads, and Claude Code appends a list of the directory-qualified variants to its content with an instruction to also invoke any variant whose directory holds the files Claude is working on. A nested skill therefore still applies to work in its directory when only the unqualified name is invoked.
The folder name `synced` is reserved in the enterprise, personal, and project skills locations, in any capitalization. Claude Code [downloads the skills you enable on claude.ai](/docs/en/env-vars#variables) into `~/.claude/skills/synced/` when `CLAUDE_CODE_SYNC_SKILLS` is set in non-interactive mode, and skips a skill you author at that name.
A `<skill-name>` entry in the enterprise, personal, or project locations can be a symlink to a directory elsewhere on disk. Claude Code follows the symlink and reads `SKILL.md` from the target directory, and if the same target is reachable from more than one location, Claude Code loads the skill once. Plugin skills handle symlinks differently; see [Share files within a marketplace with symlinks](/docs/en/plugins-reference#share-files-within-a-marketplace-with-symlinks).
<Note>
Add a `.claude-plugin/plugin.json` to a skill folder and it loads as a [plugin](/docs/en/plugins-reference#skills-directory-plugins) named `<name>@skills-dir`, so it can bundle agents, hooks, and MCP servers. In a project's `.claude/skills/`, this requires accepting the workspace trust dialog first.
</Note>
#### Live change detection
Claude Code watches skill directories for file changes. When you add, edit, or remove a skill under `~/.claude/skills/`, the project `.claude/skills/`, or a `.claude/skills/` inside an `--add-dir` directory, Claude Code picks up the change within the current session, without a restart. If you create a top-level skills directory that didn't exist when the session started, restart Claude Code so it can watch the new directory.
<Note>
Live change detection covers `SKILL.md` text only. For a skill folder that is also a [plugin](/docs/en/plugins-reference#skills-directory-plugins), changes to `hooks/`, `.mcp.json`, `agents/`, and `output-styles/` need `/reload-plugins` to take effect.
</Note>
#### Discovery from parent and nested directories
Project skills load from `.claude/skills/` in the directory where you start Claude Code and in every parent directory up to the repository root. Starting Claude in a subdirectory still picks up skills defined at the root. To load skills from a directory outside that path at startup, pass it with [`--add-dir`](/docs/en/cli-reference). Claude Code reads `.claude/skills/` inside each added directory alongside the project skills.
Skills in nested `.claude/skills/` directories below your starting directory aren't loaded at startup. They load the first time Claude reads or edits a file inside that subdirectory, and stay available for the rest of the session. For example, after Claude edits a file under `packages/frontend/`, skills in `packages/frontend/.claude/skills/` become available. Until then, those skills don't appear in autocomplete and can't be invoked by name.
Each skill is a directory with `SKILL.md` as the entrypoint:
```text theme={null}
my-skill/
├── SKILL.md # Main instructions (required)
├── template.md # Template for Claude to fill in
├── examples/
│ └── sample.md # Example output showing expected format
└── scripts/
└── validate.sh # Script Claude can execute
```
The `SKILL.md` contains the main instructions and is required. Other files are optional and let you build more powerful skills: templates for Claude to fill in, example outputs showing the expected format, scripts Claude can execute, or detailed reference documentation. Reference these files from your `SKILL.md` so Claude knows what they contain and when to load them. See [Add supporting files](#add-supporting-files) for more details.
<Note>
Files in `.claude/commands/` support the same [frontmatter](#frontmatter-reference). Skills are recommended since they support additional features like supporting files.
</Note>
#### Skills from additional directories
The `--add-dir` flag and `/add-dir` command [grant file access](/docs/en/permissions#additional-directories-grant-file-access-not-configuration) rather than configuration discovery, but skills are an exception: `.claude/skills/` within an added directory is loaded automatically. This exception applies only to `--add-dir` and `/add-dir`. The `permissions.additionalDirectories` setting in `settings.json` grants file access only and does not load skills. See [Live change detection](#live-change-detection) for how edits are picked up during a session.
Other `.claude/` configuration such as commands and output styles is not loaded from additional directories. See the [exceptions table](/docs/en/permissions#additional-directories-grant-file-access-not-configuration) for the complete list of what is and isn't loaded, and the recommended ways to share configuration across projects.
<Note>
CLAUDE.md files from `--add-dir` directories are not loaded by default. To load them, set `CLAUDE_CODE_ADDITIONAL_DIRECTORIES_CLAUDE_MD=1`. See [Load from additional directories](/docs/en/memory#load-from-additional-directories).
</Note>
#### Skills in Cowork and cloud sessions
[Cowork](https://claude.com/product/cowork) sessions and [cloud sessions](/docs/en/cloud-environments#what-carries-over-from-your-setup), including [routines](/docs/en/routines), don't read `~/.claude/skills/` on your machine. Both interactive and scheduled Cowork sessions load the skills enabled for your claude.ai account, synced at session start; manage them from **Customize** in the Desktop app sidebar or from the skills settings on claude.ai. Cloud sessions additionally load project skills committed to the cloned repository's `.claude/skills/`.
If a skill exists only in `~/.claude/skills/` on your machine, Claude Code reports that the skill was not found when a [routine](/docs/en/routines) invokes it, because each routine run starts as a fresh remote session. To make a personal skill available in these sessions:
* For Cowork and cloud sessions, enable the skill for your claude.ai account.
* For cloud sessions, you can instead commit the skill to the repository's `.claude/skills/`, or ship it in a plugin declared in the repository's `.claude/settings.json`. Repo-declared plugins [install at session start](/docs/en/cloud-environments#what-carries-over-from-your-setup); plugins enabled only in your user settings don't transfer.
[Desktop scheduled tasks](/docs/en/desktop-scheduled-tasks) are different: they run locally on your machine and load skills from the same locations as any other local session.
<h3 id="how-synced-skills-behave">
Skills synced from claude.ai
</h3>
This section applies to you if you enabled skills for your claude.ai account. In Cowork and cloud sessions, Claude Code loads those skills without any setup on your machine. In any other session on your machine, Claude Code loads them only after you turn syncing on with [`CLAUDE_CODE_SYNC_SKILLS`](/docs/en/env-vars#variables) in a non-interactive run, as [Where synced skills load](#where-synced-skills-load) describes.
Claude Code downloads a synced skill from your account rather than reading a file you wrote on the machine where the session runs, so it applies rules to synced skills that don't apply to the skills you store in the [skills locations](#where-skills-live).
#### Where synced skills load
In a Cowork or cloud session, Claude Code loads the skills enabled for your claude.ai account, and [Skills in Cowork and cloud sessions](#skills-in-cowork-and-cloud-sessions) says how to choose which skills those sessions get.
In any other session on your machine, Claude Code loads them only after you download them once in a non-interactive run:
<Steps>
<Step title="Enable the skills for your claude.ai account">
Enable each skill you want for your claude.ai account, as [Skills in Cowork and cloud sessions](#skills-in-cowork-and-cloud-sessions) describes. Claude Code downloads only the skills you enabled, and it needs your claude.ai sign-in to download them.
</Step>
<Step title="Run Claude Code in non-interactive mode with syncing turned on">
Claude Code downloads synced skills only when you run it in [non-interactive mode](/docs/en/headless) with the `-p` flag and set [`CLAUDE_CODE_SYNC_SKILLS`](/docs/en/env-vars#variables) to `1`. The prompt you pass doesn't affect the download.
```bash theme={null}
CLAUDE_CODE_SYNC_SKILLS=1 claude -p "List the skills you have available"
```
Claude Code downloads the skills into `~/.claude/skills/synced/`, answers the prompt, and exits like any other non-interactive run. The downloaded skills stay on disk after it exits, so you don't need to keep the run open. Claude Code downloads skills only during a run with `CLAUDE_CODE_SYNC_SKILLS` set, so after you enable or change a skill on claude.ai, run the command again. To change how long the run waits for the sync before it answers the prompt, set [`CLAUDE_CODE_SYNC_SKILLS_WAIT_TIMEOUT_MS`](/docs/en/env-vars#variables).
</Step>
<Step title="Confirm the skills load in a local session">
Start an interactive session, without `CLAUDE_CODE_SYNC_SKILLS` set, and run `/skills`. The menu lists the downloaded skills under `claude.ai sync`. Every local session you start afterwards loads them from `~/.claude/skills/synced/` too.
</Step>
</Steps>
#### When a synced skill name matches another command
Claude Code skips a synced skill whose name matches any other command, and that other command runs. The other command can be a built-in command, a [bundled skill](#bundled-skills), a skill at any [local level](#where-skills-live), a plugin skill, a file in `.claude/commands/`, or an [MCP prompt](/docs/en/mcp#use-mcp-prompts-as-commands). Claude Code also reserves the names of its own built-in commands and bundled skills even when they're unavailable in your session, for example after you turn bundled skills off, so it skips a synced skill with one of those names too.
Claude Code labels synced skills so you can tell where they came from. The `/skills` menu and `/context` group synced skills under `claude.ai sync`, and the `/` command menu marks them as coming from claude.ai.
When it compares names, Claude Code ignores case, spacing, and invisible characters, and treats compatibility forms such as fullwidth letters and dash variants as their plain equivalents, so a synced `Commit` can't load beside a local `commit`. A name that differs only by a look-alike letter from another alphabet counts as a different name, and the `claude.ai sync` label is how you tell the two apart.
#### How Claude Code handles the frontmatter of a synced skill
Claude Code applies two rules to a synced skill's frontmatter:
* Claude Code honors the frontmatter in every kind of session, so an `allowed-tools` grant goes through the normal [permission flow](/docs/en/permissions).
* Claude Code sanitizes the display text the skill supplies, such as its description. It removes control characters, and in text that reaches Claude, such as the description, it also escapes angle brackets so the text can't imitate Claude Code's internal formatting.
#### How Claude Code handles the body of a synced skill
What Claude Code does with a synced skill's body depends on where the session runs:
* In a cloud session, the body keeps the behavior a local skill has, because the session runs in an isolated container.
* In a Cowork session on your desktop, the body keeps the behavior a local skill has, except that Claude Code replaces every `!` command line with the [`disableSkillShellExecution` placeholder](#inject-dynamic-context), as it does for every skill you supply there.
* In any other session on your machine, Claude Code doesn't run [`!` commands](#inject-dynamic-context), doesn't attach the files that `@` references name the way it does for a local skill, and doesn't substitute the `${CLAUDE_PROJECT_DIR}` and `${CLAUDE_SESSION_ID}` placeholders, so the `@` references and both placeholders reach Claude as literal text. A `!` command line reaches Claude as literal text too, or as that placeholder when `disableSkillShellExecution` is on.
## Configure skills
Skills are configured through YAML frontmatter at the top of `SKILL.md` and the markdown content that follows.
### Types of skill content
Skill files can contain any instructions, but thinking about how you want to invoke them helps guide what to include:
**Reference content** adds knowledge Claude applies to your current work. Conventions, patterns, style guides, domain knowledge. This content runs inline so Claude can use it alongside your conversation context.
```yaml theme={null}
---
name: api-conventions
description: API design patterns for this codebase
---
When writing API endpoints:
- Use RESTful naming conventions
- Return consistent error formats
- Include request validation
```
**Task content** gives Claude step-by-step instructions for a specific action, like deployments, commits, or code generation. These are often actions you want to invoke directly with `/skill-name` rather than letting Claude decide when to run them. Add `disable-model-invocation: true` to prevent Claude from triggering it automatically. The example below adds `context: fork`, which runs the skill in its own subagent context; see [Run skills in a subagent](#run-skills-in-a-subagent).
```yaml theme={null}
---
name: deploy
description: Deploy the application to production
context: fork
disable-model-invocation: true
---
Deploy the application:
1. Run the test suite
2. Build the application
3. Push to the deployment target
```
Keep the body itself concise. Once a skill loads, its content [stays in context across turns](#skill-content-lifecycle), so every line is a recurring token cost. State what to do rather than narrating how or why, and apply the same conciseness test you would for [CLAUDE.md content](/docs/en/best-practices#write-an-effective-claude-md).
Cut at 300 lines. The page has the rest.
slack First recorded · 244 lines, first recorded
# Claude Code in Slack ## Use cases ## Prerequisites ## Setting up Claude Code in Slack ## How it works ### Automatic detection ### Context gathering ### Session flow ## User interface elements ### App Home ### Message actions ### Repository selection ## Access and permissions ### User-level access ### Workspace-level access ### Channel-based access control ## What's accessible where ## Best practices ### Writing effective requests ### When to use Slack vs. web ## Troubleshooting ### "Claude Code is not enabled for your account" ### Sessions not starting ### Sessions from a Claude Tag channel fail to start ### Repository not showing ### Wrong repository selected ### Authentication errors ## Current limitations ## Related resources
The first capture of this source. The page was already there, and this is what it said.
# Claude Code in Slack
> Delegate coding tasks directly from your Slack workspace. Anthropic is retiring this earlier version for Team and Enterprise workspaces in favor of Claude Tag; it remains the setup path on Pro and Max plans.
<Warning>
This page documents the earlier Claude Code in Slack, which runs each session under an individual user's account.
* **Team and Enterprise plans:** Anthropic is retiring this version in favor of [Claude Tag](https://claude.com/product/tag), which runs @Claude as your organization's shared identity with admin-configured access. Your existing Slack app and @Claude handle stay, and your Anthropic account team can tell you the cutover date. [Set up Claude Tag](https://claude.com/docs/claude-tag/overview) for a new workspace; to move one that already uses this version, see [Migrate from the earlier Claude in Slack](https://claude.com/docs/claude-tag/admins/migrate-from-earlier).
* **Pro and Max plans:** Claude Tag isn't available on individual plans, so this page remains the setup path.
</Warning>
Claude Code in Slack brings the power of Claude Code directly into your Slack workspace. When you mention `@Claude` with a coding task, Claude automatically detects the intent and creates a Claude Code session on the web, allowing you to delegate development work without leaving your team conversations.
This integration is built on the existing Claude for Slack app but adds intelligent routing to Claude Code on the web for coding-related requests. Each session runs under your own Claude account, using your connected repositories and your plan limits.
## Use cases
* **Bug investigation and fixes**: Ask Claude to investigate and fix bugs as soon as they're reported in Slack channels.
* **Quick code reviews and modifications**: Have Claude implement small features or refactor code based on team feedback.
* **Collaborative debugging**: When team discussions provide crucial context (e.g., error reproductions or user reports), Claude can use that information to inform its debugging approach.
* **Parallel task execution**: Kick off coding tasks in Slack while you continue other work, receiving notifications when complete.
## Prerequisites
Before using Claude Code in Slack, ensure you have the following:
| Requirement | Details |
| :--------------------- | :------------------------------------------------------------------------------------------------ |
| Claude Plan | Pro, Max, Team, or Enterprise with Claude Code access (premium seats or Chat + Claude Code seats) |
| Claude Code on the web | Access to [Claude Code on the web](/docs/en/claude-code-on-the-web) must be enabled |
| GitHub Account | Connected to Claude Code on the web with at least one repository authenticated |
| Slack Authentication | Your Slack account linked to your Claude account via the Claude app |
## Setting up Claude Code in Slack
<Steps>
<Step title="Install the Claude App in Slack">
A workspace administrator must install the Claude app from the Slack App Marketplace. Visit the [Slack App Marketplace](https://slack.com/marketplace/A08SF47R6P4) and click "Add to Slack" to begin the installation process.
</Step>
<Step title="Connect your Claude account">
After the app is installed, authenticate your individual Claude account:
1. Open the Claude app in Slack by clicking on "Claude" in your Apps section
2. Navigate to the App Home tab
3. Click "Connect" to link your Slack account with your Claude account
4. Complete the authentication flow in your browser
</Step>
<Step title="Configure Claude Code on the web">
Ensure your Claude Code on the web is properly configured:
* Visit [claude.ai/code](https://claude.ai/code) and sign in with the same account you connected to Slack
* Connect your GitHub account if not already connected
* Authenticate at least one repository that you want Claude to work with
</Step>
<Step title="Choose your routing mode">
After connecting your accounts, configure how Claude handles your messages in Slack. Navigate to the Claude App Home in Slack to find the **Routing Mode** setting.
| Mode | Behavior |
| :-------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Code only** | Claude routes all @mentions to Claude Code sessions. Best for teams using Claude in Slack exclusively for development tasks. |
| **Code + Chat** | Claude analyzes each message and intelligently routes between Claude Code (for coding tasks) and Claude Chat (for writing, analysis, and general questions). Best for teams who want a single @Claude entry point for all types of work. |
<Note>
In Code + Chat mode, if Claude routes a message to Chat but you wanted a coding session, you can click "Retry as Code" to create a Claude Code session instead. Similarly, if it's routed to Code but you wanted a Chat session, you can choose that option in that thread.
</Note>
</Step>
<Step title="Add Claude to channels">
Claude is not automatically added to any channels after installation. To use Claude in a channel, invite it by typing `/invite @Claude` in that channel. Claude can only respond to @mentions in channels where it has been added.
</Step>
</Steps>
## How it works
### Automatic detection
When you mention @Claude in a Slack channel or thread, Claude automatically analyzes your message to determine if it's a coding task. If Claude detects coding intent, it will route your request to Claude Code on the web instead of responding as a regular chat assistant.
You can also explicitly tell Claude to handle a request as a coding task, even if it doesn't automatically detect it.
<Note>
Claude Code in Slack only works in channels (public or private). It does not work in direct messages (DMs).
</Note>
### Context gathering
**From threads**: When you @mention Claude in a thread, it gathers context from all messages in that thread to understand the full conversation.
**From channels**: When mentioned directly in a channel, Claude looks at recent channel messages for relevant context.
This context helps Claude understand the problem, select the appropriate repository, and inform its approach to the task.
<Warning>
When @Claude is invoked in Slack, Claude is given access to the conversation context to better understand your request. Claude may follow directions from other messages in the context, so users should make sure to only use Claude in trusted Slack conversations.
</Warning>
### Session flow
1. **Initiation**: You @mention Claude with a coding request
2. **Detection**: Claude analyzes your message and detects coding intent
3. **Session creation**: A new Claude Code session is created on claude.ai/code
4. **Progress updates**: Claude posts status updates to your Slack thread as work progresses
5. **Completion**: When finished, Claude @mentions you with a summary and action buttons
6. **Review**: Click "View Session" to see the full transcript, or "Create PR" to open a pull request
## User interface elements
### App Home
The App Home tab shows your connection status and allows you to connect or disconnect your Claude account from Slack.
### Message actions
* **View Session**: Opens the full Claude Code session in your browser where you can see all work performed, continue the session, or make additional requests.
* **Create PR**: Creates a pull request directly from the session's changes.
* **Retry as Code**: If Claude initially responds as a chat assistant but you wanted a coding session, click this button to retry the request as a Claude Code task.
* **Change Repo**: Allows you to select a different repository if Claude chose incorrectly.
### Repository selection
Claude automatically selects a repository based on context from your Slack conversation. If multiple repositories could apply, Claude may display a dropdown allowing you to choose the correct one.
## Access and permissions
### User-level access
| Access Type | Requirement |
| :------------------- | :-------------------------------------------------------------- |
| Claude Code Sessions | Each user runs sessions under their own Claude account |
| Usage & Rate Limits | Sessions count against the individual user's plan limits |
| Repository Access | Users can only access repositories they've personally connected |
| Session History | Sessions appear in your Claude Code history on claude.ai/code |
### Workspace-level access
Slack workspace administrators control whether the Claude app is available in their workspace:
| Control | Description |
| :--------------------------- | :---------------------------------------------------------------------------------------------------------------- |
| App installation | Workspace admins decide whether to install the Claude app from the Slack App Marketplace |
| Enterprise Grid distribution | For Enterprise Grid organizations, organization admins can control which workspaces have access to the Claude app |
| App removal | Removing the app from a workspace immediately revokes access for all users in that workspace |
### Channel-based access control
Installing the app doesn't add Claude to any channels. Claude responds to @mentions only in channels where it has been added; invite it with `/invite @Claude`. It works in both public and private channels. Admins can control who uses Claude Code by managing which channels Claude is invited to and who has access to those channels. This adds a layer of access control beyond workspace-level permissions.
## What's accessible where
**In Slack**: You'll see status updates, completion summaries, and action buttons. The full transcript is preserved and always accessible.
**On the web**: The complete Claude Code session with full conversation history, all code changes, and file operations. Sessions stay in your Claude Code history at [claude.ai/code](https://claude.ai/code), where you can continue past sessions, reference them, or create pull requests.
For Enterprise and Team accounts, sessions created from Claude in Slack are
automatically visible to the organization. See [Claude Code on the Web sharing](/docs/en/claude-code-on-the-web#share-sessions)
for more details.
## Best practices
### Writing effective requests
* **Be specific**: Include file names, function names, or error messages when relevant.
* **Provide context**: Mention the repository or project if it's not clear from the conversation.
* **Define success**: Explain what "done" looks like—should Claude write tests? Update documentation? Create a PR?
* **Use threads**: Reply in threads when discussing bugs or features so Claude can gather the full context.
### When to use Slack vs. web
**Use Slack when**: Context already exists in a Slack discussion, you want to kick off a task asynchronously, or you're collaborating with teammates who need visibility.
**Use the web directly when**: You need to upload files, want real-time interaction during development, or are working on longer, more complex tasks.
## Troubleshooting
### "Claude Code is not enabled for your account"
This error means your Claude account has no cloud environment yet, not that an admin needs to enable anything. Sign in at [claude.ai/code](https://claude.ai/code) once with the same account you connected to Slack. The first visit creates your default cloud environment, and the error clears on your next mention. Each user must do this individually.
### Sessions not starting
1. Verify your Claude account is connected in the Claude App Home
2. Check that you have Claude Code on the web access enabled
3. Ensure you have at least one GitHub repository connected to Claude Code
### Sessions from a Claude Tag channel fail to start
This entry applies to workspaces using [Claude Tag](https://claude.com/docs/claude-tag/overview), where Claude works in channels as your organization's shared identity, not as any member's account. If you created the channel's cloud environment at [claude.ai/code](https://claude.ai/code), it belongs to your personal account, and Claude can't start channel sessions in a personal environment. Claude Code fails the session immediately, and retrying doesn't help.
If you're an Owner or admin, recreate the environment as an [organization-shared environment](/docs/en/cloud-environments#organization-shared-environments) from the **Cloud environments** page in [admin settings](https://claude.ai/admin-settings). You can apply it in two ways:
* Set it as the organization default at [claude.ai/admin-settings/claude-code](https://claude.ai/admin-settings/claude-code).
* [Set it on the channel](https://claude.com/docs/claude-tag/admins/troubleshooting#channel-sessions-use-the-wrong-environment-or-can%E2%80%99t-find-one) in the Claude Tag admin settings.
If you're not an Owner or admin, send this entry to one.
### Repository not showing
1. Connect the repository in Claude Code on the web at [claude.ai/code](https://claude.ai/code)
2. Verify your GitHub permissions for that repository
3. Try disconnecting and reconnecting your GitHub account
### Wrong repository selected
1. Click the "Change Repo" button to select a different repository
2. Include the repository name in your request for more accurate selection
### Authentication errors
1. Disconnect and reconnect your Claude account in the App Home
2. Ensure you're signed into the correct Claude account in your browser
3. Check that your Claude plan includes Claude Code access
## Current limitations
* **GitHub only**: repositories must be on GitHub.
* **One PR at a time**: each session can create one pull request.
* **Web access required**: users need access to Claude Code on the web; without it, Claude replies with standard chat responses.
## Related resources
<CardGroup>
<Card title="Claude Code on the web" icon="globe" href="/docs/en/claude-code-on-the-web">
Learn more about Claude Code on the web
</Card>
<Card title="Claude for Slack" icon="slack" href="https://claude.com/claude-and-slack">
General Claude for Slack documentation
</Card>
<Card title="Claude Tag" icon="users" href="https://claude.com/docs/claude-tag/overview">
Organization-managed @Claude in Slack with admin-configured access
</Card>
<Card title="Slack App Marketplace" icon="store" href="https://slack.com/marketplace/A08SF47R6P4">
Install the Claude app from the Slack Marketplace
</Card>
<Card title="Claude Help Center" icon="circle-question" href="https://support.claude.com">
Get additional support
</Card>
</CardGroup>
statusline First recorded · 1108 lines, first recorded
# Customize your status line ## Set up a status line ### Use the /statusline command ### Manually configure a status line ### Disable the status line ## Build a status line step by step ## How status lines work ## Available data ### Context window fields ## Examples ### Context window usage ### Git status with colors ### Cost and duration tracking ### Display multiple lines ### Clickable links ### Rate limit usage ### Cache expensive operations ### Windows configuration ## Subagent status lines ## Tips ## Troubleshooting
The first capture of this source. The page was already there, and this is what it said.
# Customize your status line
> Configure a custom status bar to monitor context window usage, costs, and git status in Claude Code
The status line is a customizable bar at the bottom of Claude Code that runs any shell script you configure. It receives JSON session data on stdin and displays whatever your script prints, giving you a persistent, at-a-glance view of context usage, costs, git status, or anything else you want to track.
Status lines are useful when you:
* Want to monitor context window usage as you work
* Need to track session costs
* Work across multiple sessions and need to distinguish them
* Want git branch and status always visible
The status line renders in its own row above the built-in footer badges and does not replace them. With a custom status line configured, Claude Code stops showing most of the footer's keyboard hints, including `esc to interrupt`, the `? for shortcuts` fallback, and the `hold space to speak` [voice dictation](/docs/en/voice-dictation) hint. To add clickable link badges to the footer when an ID appears in the conversation, without writing a script, configure [`footerLinksRegexes`](/docs/en/settings#footer-link-badges) instead.
Here's an example of a [multi-line status line](#display-multiple-lines) that displays git info on the first line and a color-coded context bar on the second.
<Frame>
<img src="https://mintcdn.com/claude-code/nibzesLaJVh4ydOq/images/statusline-multiline.png?fit=max&auto=format&n=nibzesLaJVh4ydOq&q=85&s=60f11387658acc9ff75158ae85f2ac87" alt="A multi-line status line showing model name, directory, git branch on the first line, and a context usage progress bar with cost and duration on the second line" width="776" height="212" data-path="images/statusline-multiline.png" />
</Frame>
This page walks through [setting up a basic status line](#set-up-a-status-line), explains [how the data flows](#how-status-lines-work) from Claude Code to your script, lists [all the fields you can display](#available-data), and provides [ready-to-use examples](#examples) for common patterns like git status, cost tracking, and progress bars.
## Set up a status line
Use the [`/statusline` command](#use-the-%2Fstatusline-command) to have Claude Code generate a script for you, or [manually create a script](#manually-configure-a-status-line) and add it to your settings.
### Use the /statusline command
The `/statusline` command accepts natural language instructions describing what you want displayed. Claude Code generates a script file in `~/.claude/` and updates your settings automatically:
```text theme={null}
/statusline show model name and context percentage with a progress bar
```
Approve the file edit prompts if Claude Code asks for permission during setup.
### Manually configure a status line
Add a `statusLine` field to your user settings (`~/.claude/settings.json`, where `~` is your home directory) or [project settings](/docs/en/settings#settings-files). Set `type` to `"command"` and point `command` to a script path or an inline shell command. For a full walkthrough of creating a script, see [Build a status line step by step](#build-a-status-line-step-by-step).
```json theme={null}
{
"statusLine": {
"type": "command",
"command": "~/.claude/statusline.sh",
"padding": 2
}
}
```
The `command` field runs in a shell, so you can also use inline commands instead of a script file. This example uses `jq` to parse the JSON input and display the model name and context percentage:
```json theme={null}
{
"statusLine": {
"type": "command",
"command": "jq -r '\"[\\(.model.display_name)] \\(.context_window.used_percentage // 0)% context\"'"
}
}
```
The optional `padding` field adds extra horizontal spacing (in characters) to the status line content. Defaults to `0`. This padding is in addition to the interface's built-in spacing, so it controls relative indentation rather than absolute distance from the terminal edge.
The optional `refreshInterval` field re-runs your command every N seconds in addition to the [event-driven updates](#how-status-lines-work). The minimum is `1`. Set this when your status line shows time-based data such as a clock, or when background subagents change git state while the main session is idle. Leave it unset to run only on events.
The optional `hideVimModeIndicator` field suppresses the built-in `-- INSERT --` text below the prompt. Set this to `true` when your script renders [`vim.mode`](#available-data) itself, so the mode is not shown twice.
### Disable the status line
Run `/statusline` and ask it to remove or clear your status line (e.g., `/statusline delete`, `/statusline clear`, `/statusline remove it`). You can also manually delete the `statusLine` field from your settings.json.
## Build a status line step by step
This walkthrough shows what's happening under the hood by manually creating a status line that displays the current model, working directory, and context window usage percentage.
<Note>Running [`/statusline`](#use-the-%2Fstatusline-command) with a description of what you want configures all of this for you automatically.</Note>
These examples use Bash scripts, which work on macOS and Linux. On Windows, see [Windows configuration](#windows-configuration) for PowerShell and Git Bash examples.
<Frame>
<img src="https://mintcdn.com/claude-code/nibzesLaJVh4ydOq/images/statusline-quickstart.png?fit=max&auto=format&n=nibzesLaJVh4ydOq&q=85&s=696445e59ca0059213250651ad23db6b" alt="A status line showing model name, directory, and context percentage" width="726" height="164" data-path="images/statusline-quickstart.png" />
</Frame>
<Steps>
<Step title="Create a script that reads JSON and prints output">
Claude Code sends JSON data to your script via stdin. This script uses [`jq`](https://jqlang.org/), a command-line JSON parser you may need to install, to extract the model name, directory, and context percentage, then prints a formatted line.
Save this to `~/.claude/statusline.sh` (where `~` is your home directory, such as `/Users/username` on macOS or `/home/username` on Linux):
```bash theme={null}
#!/bin/bash
# Read JSON data that Claude Code sends to stdin
input=$(cat)
# Extract fields using jq
MODEL=$(echo "$input" | jq -r '.model.display_name')
DIR=$(echo "$input" | jq -r '.workspace.current_dir')
# The "// 0" provides a fallback if the field is null
PCT=$(echo "$input" | jq -r '.context_window.used_percentage // 0' | cut -d. -f1)
# Output the status line - ${DIR##*/} extracts just the folder name
echo "[$MODEL] 📁 ${DIR##*/} | ${PCT}% context"
```
</Step>
<Step title="Make it executable">
Mark the script as executable so your shell can run it:
```bash theme={null}
chmod +x ~/.claude/statusline.sh
```
</Step>
<Step title="Add to settings">
Tell Claude Code to run your script as the status line. Add this configuration to `~/.claude/settings.json`, which sets `type` to `"command"` (meaning "run this shell command") and points `command` to your script:
```json theme={null}
{
"statusLine": {
"type": "command",
"command": "~/.claude/statusline.sh"
}
}
```
Your status line appears at the bottom of the interface. Settings reload automatically, but changes won't appear until your next interaction with Claude Code.
</Step>
</Steps>
## How status lines work
Claude Code runs your script and pipes [JSON session data](#available-data) to it via stdin. Your script reads the JSON, extracts what it needs, and prints text to stdout. Claude Code displays whatever your script prints.
**When it updates**
Your script runs once when a session starts, including when you resume one. After that, it runs again when:
* A new assistant message arrives
* `/compact` finishes
* The permission mode changes
* Vim mode toggles
* A [`refreshInterval`](#manually-configure-a-status-line) timer elapses, if you set one
Claude Code debounces updates at 300ms, so rapid changes batch together and your script runs once after the changes stop. If a new update triggers while your script is still running, Claude Code cancels the in-flight script. If you edit your script, the changes appear the next time an update trigger re-runs it.
The event-driven triggers can go quiet when the main session is idle, for example while a coordinator waits on background subagents. To keep time-based or externally-sourced segments current during idle periods, set [`refreshInterval`](#manually-configure-a-status-line) to also re-run the command on a fixed timer.
**What your script can output**
* **Multiple lines**: each `echo` or `print` statement displays as a separate row. See the [multi-line example](#display-multiple-lines).
* **Colors**: use [ANSI escape codes](https://en.wikipedia.org/wiki/ANSI_escape_code#Colors) like `\033[32m` for green (terminal must support them). See the [git status example](#git-status-with-colors).
* **Links**: use [OSC 8 escape sequences](https://en.wikipedia.org/wiki/ANSI_escape_code#OSC) to make text clickable (Cmd+click on macOS, Ctrl+click on Windows/Linux). Requires a terminal that supports hyperlinks like iTerm2, Kitty, or WezTerm. See the [clickable links example](#clickable-links).
**Sizing output to the terminal**
Claude Code captures your script's output instead of connecting it directly to the terminal, so `tput cols` and language-level width detection cannot read the terminal size from inside the script. Read the `COLUMNS` and `LINES` environment variables instead. Claude Code sets these to the current terminal dimensions before running your script. Requires Claude Code v2.1.153 or later.
<Note>The status line runs locally and does not consume API tokens. It temporarily hides during certain UI interactions, including autocomplete suggestions, the help menu, and permission prompts.</Note>
## Available data
Claude Code sends the following JSON fields to your script via stdin:
| Field | Description |
| -------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `model.id`, `model.display_name` | Current model identifier and display name |
| `cwd`, `workspace.current_dir` | Current working directory. Both fields contain the same value; `workspace.current_dir` is preferred for consistency with `workspace.project_dir`. |
| `workspace.project_dir` | Directory where Claude Code was launched, which may differ from `cwd` if the working directory changes during a session |
| `workspace.added_dirs` | Additional directories added via `/add-dir` or `--add-dir`. Empty array if none have been added |
| `workspace.git_worktree` | Git worktree name when the current directory is inside a linked worktree created with `git worktree add`. Absent in the main working tree. Populated for any git worktree, unlike `worktree.*` which applies only to `--worktree` sessions |
| `workspace.repo.host`, `workspace.repo.owner`, `workspace.repo.name` | Repository identity parsed from the `origin` remote, for example `"github.com"`, `"anthropics"`, `"claude-code"`. Absent outside a git repository or when no `origin` remote is configured |
| `cost.total_cost_usd` | Estimated session cost in USD, computed client-side. May differ from your actual bill. Resets to \$0 when `/clear` starts a new session |
| `cost.total_duration_ms` | Total wall-clock time since the session started, in milliseconds |
| `cost.total_api_duration_ms` | Total time spent waiting for API responses in milliseconds |
| `cost.total_lines_added`, `cost.total_lines_removed` | Lines of code changed |
| `context_window.total_input_tokens`, `context_window.total_output_tokens` | Token counts currently in the context window, from the most recent API response. Input includes cache reads and writes |
| `context_window.context_window_size` | Maximum context window size in tokens. 200000 by default, or 1000000 for models with extended context. |
| `context_window.used_percentage` | Pre-calculated percentage of context window used |
| `context_window.remaining_percentage` | Pre-calculated percentage of context window remaining |
| `context_window.current_usage` | Token counts from the last API call, described in [context window fields](#context-window-fields) |
| `exceeds_200k_tokens` | Whether the total token count (input, cache, and output tokens combined) from the most recent API response exceeds 200k. This is a fixed threshold regardless of actual context window size. |
| `fast_mode` | Whether [fast mode](/docs/en/fast-mode) is enabled for the session |
| `effort.level` | Current reasoning effort (`low`, `medium`, `high`, `xhigh`, or `max`). Reflects the live session value, including mid-session `/effort` changes. Ultracode is not a distinct level and reports as `xhigh`. Absent when the current model does not support the effort parameter |
| `thinking.enabled` | Whether extended thinking is enabled for the session |
| `rate_limits.five_hour.used_percentage`, `rate_limits.seven_day.used_percentage` | Percentage of the 5-hour or 7-day rate limit consumed, from 0 to 100 |
| `rate_limits.five_hour.resets_at`, `rate_limits.seven_day.resets_at` | Unix epoch seconds when the 5-hour or 7-day rate limit window resets |
| `session_id` | Unique session identifier |
| `session_name` | Session name. Uses the custom name set with the `--name` flag or `/rename` when one exists, otherwise the AI-generated session title. The [default display name](/docs/en/sessions#name-your-sessions), such as `my-app-3f`, doesn't populate this field. Absent when the session has neither a custom name nor an AI-generated title |
| `prompt_id` | UUID identifying the user prompt currently being processed. Matches the [`prompt.id` attribute on OpenTelemetry events](/docs/en/monitoring-usage#event-correlation-attributes). Absent until the first user input. Requires Claude Code v2.1.196 or later |
| `transcript_path` | Path to conversation transcript file |
| `version` | Claude Code version |
| `output_style.name` | Name of the current output style |
| `vim.mode` | Current vim mode (`NORMAL`, `INSERT`, `VISUAL`, or `VISUAL LINE`) when [vim mode](/docs/en/interactive-mode#vim-editor-mode) is enabled |
| `agent.name` | Agent name when running with the `--agent` flag or agent settings configured |
| `pr.number`, `pr.url` | Open pull request for the current branch. Mirrors the PR badge in the bottom status bar. Absent until a PR is found, when not in a git repository, or once the PR merges or closes |
| `pr.review_state` | Review status of the open PR: `approved`, `pending`, `changes_requested`, or `draft`. May be independently absent even when `pr` is present |
| `worktree.name` | Name of the active worktree. Present only during `--worktree` sessions |
| `worktree.path` | Absolute path to the worktree directory |
| `worktree.branch` | Git branch name for the worktree (for example, `"worktree-my-feature"`). Absent for hook-based worktrees |
| `worktree.original_cwd` | The directory Claude was in before entering the worktree |
| `worktree.original_branch` | Git branch checked out before entering the worktree. Absent for hook-based worktrees |
<Accordion title="Full JSON schema">
Your status line command receives this JSON structure via stdin:
```json theme={null}
{
"cwd": "/current/working/directory",
"session_id": "abc123...",
"session_name": "my-session",
"prompt_id": "550e8400-e29b-41d4-a716-446655440000",
"transcript_path": "/path/to/transcript.jsonl",
"model": {
"id": "claude-opus-5",
"display_name": "Opus"
},
"workspace": {
"current_dir": "/current/working/directory",
"project_dir": "/original/project/directory",
"added_dirs": [],
"git_worktree": "feature-xyz",
"repo": {
"host": "github.com",
"owner": "anthropics",
"name": "claude-code"
}
},
"version": "2.1.90",
"output_style": {
"name": "default"
},
"cost": {
"total_cost_usd": 0.01234,
"total_duration_ms": 45000,
"total_api_duration_ms": 2300,
"total_lines_added": 156,
"total_lines_removed": 23
},
"context_window": {
"total_input_tokens": 15500,
"total_output_tokens": 1200,
"context_window_size": 200000,
"used_percentage": 8,
"remaining_percentage": 92,
"current_usage": {
"input_tokens": 8500,
"output_tokens": 1200,
"cache_creation_input_tokens": 5000,
"cache_read_input_tokens": 2000
}
},
"exceeds_200k_tokens": false,
"fast_mode": false,
"effort": {
"level": "high"
},
"thinking": {
"enabled": true
},
"rate_limits": {
"five_hour": {
"used_percentage": 23.5,
"resets_at": 1738425600
},
"seven_day": {
"used_percentage": 41.2,
"resets_at": 1738857600
}
},
"vim": {
"mode": "NORMAL"
},
"agent": {
"name": "security-reviewer"
},
"pr": {
"number": 1234,
"url": "https://github.com/anthropics/claude-code/pull/1234",
"review_state": "pending"
},
"worktree": {
"name": "my-feature",
"path": "/path/to/.claude/worktrees/my-feature",
"branch": "worktree-my-feature",
"original_cwd": "/path/to/project",
"original_branch": "main"
}
}
```
**Fields that may be absent** (not present in JSON):
* `session_name`: appears when a custom name has been set with `--name` or `/rename`, or once an AI-generated session title exists. The default display name, such as `my-app-3f`, doesn't populate it
* `prompt_id`: appears only after the first user input
* `workspace.git_worktree`: appears only when the current directory is inside a linked git worktree
* `workspace.repo`: appears only inside a git repository with an `origin` remote configured
* `effort`: appears only when the current model supports the reasoning effort parameter
* `vim`: appears only when vim mode is enabled
* `agent`: appears only when running with the `--agent` flag or agent settings configured
Cut at 300 lines. The page has the rest.
sub-agents First recorded · 1265 lines, first recorded
# Create custom subagents ## Built-in subagents ## Quickstart: create your first subagent ## Configure subagents ### Choose the subagent scope ### Write subagent files #### Supported frontmatter fields ### Choose a model ### Control subagent capabilities #### Available tools #### Restrict which subagents can be spawned #### Scope MCP servers to a subagent #### Permission modes #### Preload skills into subagents #### Enable persistent memory ##### Persistent memory tips #### Conditional rules with hooks #### Disable specific subagents ### Define hooks for subagents #### Hooks in subagent frontmatter #### Project-level hooks for subagent events ## Work with subagents ### Understand automatic delegation ### Invoke subagents explicitly ### Run subagents in foreground or background ### API errors in subagents ### Subagent output scanning ### Common patterns #### Isolate high-volume operations #### Run parallel research #### Chain subagents ### Choose between subagents and main conversation ### Let subagents spawn their own subagents ### Concurrent subagent limit ### Manage subagent context #### What loads at startup #### Resume subagents #### Auto-compaction ## Fork the current conversation ### Observe and steer running forks ### How forks differ from named subagents ### Turn fork mode on or off ## Example subagents ### Code reviewer ### Debugger ### Data scientist ### Database query validator ## Next steps
The first capture of this source. The page was already there, and this is what it said.
# Create custom subagents
> Create and use specialized AI subagents in Claude Code for task-specific workflows and improved context management.
Subagents are specialized AI assistants that handle specific types of tasks. Use one when a side task would flood your main conversation with search results, logs, or file contents you won't reference again: the subagent does that work in its own context and returns only the summary. Define a custom subagent when you keep spawning the same kind of worker with the same instructions.
Each subagent runs in its own context window with a custom system prompt, specific tool access, and independent permissions. When Claude encounters a task that matches a subagent's description, it delegates to that subagent, which works independently and returns results. To see the context savings in practice, the [context window visualization](/docs/en/context-window) walks through a session where a subagent handles research in its own separate window.
<Note>
Subagents work within a single session. To run many independent sessions in parallel and monitor them from one place, see [background agents](/docs/en/agent-view). For separate sessions that pass messages to each other, see [cross-session messaging](/docs/en/cross-session-messaging). For a coordinated team of sessions Claude spawns and supervises, see [agent teams](/docs/en/agent-teams).
</Note>
Subagents help you:
* **Preserve context** by keeping exploration and implementation out of your main conversation
* **Enforce constraints** by limiting which tools a subagent can use
* **Reuse configurations** across projects with user-level subagents
* **Specialize behavior** with focused system prompts for specific domains
* **Control costs** by routing tasks to faster, cheaper models like Haiku
Claude uses each subagent's description to decide when to delegate tasks. When you create a subagent, write a clear description so Claude knows when to use it.
## Built-in subagents
Claude Code includes built-in subagents that Claude automatically uses when appropriate. Each inherits the parent conversation's permissions; most run with a restricted tool set.
Explore and Plan skip your CLAUDE.md files and the parent session's git status to keep research fast and inexpensive. Every other built-in and [custom subagent](#configure-subagents) loads both. For the full breakdown of what reaches a subagent, see [what loads at startup](#what-loads-at-startup).
<Tabs>
<Tab title="Explore">
A fast, read-only agent optimized for searching and analyzing codebases.
* **Model**: inherits from the main conversation, capped at Opus on the Claude API, so Explore never runs on a more expensive model than the one you already chose for the session
* **Tools**: read-only tools; Write and Edit are denied
* **Purpose**: file discovery, code search, codebase exploration
As of v2.1.198, Explore inherits the main conversation's model instead of always running on Haiku. On the Claude API, the inherited model is capped at Opus: a main conversation on a higher tier runs Explore on Opus, and a main conversation on Sonnet or Haiku runs Explore on that same model. On any other provider, such as [Amazon Bedrock, Google Cloud's Agent Platform, Microsoft Foundry, or Claude Platform on AWS](/docs/en/third-party-integrations), Explore inherits the main conversation's model directly.
A [user or project subagent](#choose-the-subagent-scope) named `Explore` overrides the built-in and keeps its own `model` field, so define one with `model: haiku` to keep exploration on a lower-cost model.
Claude delegates to Explore when it needs to search or understand a codebase without making changes. This keeps exploration results out of your main conversation context.
When invoking Explore, Claude specifies a thoroughness level: **quick** for targeted lookups, **medium** for balanced exploration, or **very thorough** for comprehensive analysis.
</Tab>
<Tab title="Plan">
A research agent used during [plan mode](/docs/en/permission-modes#analyze-before-you-edit-with-plan-mode) to gather context before presenting a plan.
* **Model**: inherits from the main conversation
* **Tools**: read-only tools; Write and Edit are denied
* **Purpose**: codebase research for planning
When you're in plan mode and Claude needs to understand your codebase, it delegates research to the Plan subagent so that exploration output stays in a separate context window while the main conversation remains read-only.
</Tab>
<Tab title="General-purpose">
A capable agent for complex, multi-step tasks that require both exploration and action.
* **Model**: inherits from the main conversation
* **Tools**: every tool [available to subagents](#available-tools)
* **Purpose**: complex research, multi-step operations, code modifications
Claude delegates to general-purpose when the task requires both exploration and modification, complex reasoning to interpret results, or multiple dependent steps.
</Tab>
<Tab title="Other">
Claude Code includes additional helper agents for specific tasks. These are typically invoked automatically, so you don't need to use them directly.
| Agent | Model | When Claude uses it |
| :---------------- | :------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| claude | Inherits | When a task doesn't fit a more specialized agent. A catch-all with every tool [available to subagents](#available-tools). Also the [default agent](/docs/en/agent-view#permission-mode-model-and-effort) for a dispatched [background session](/docs/en/agent-view), which runs with your settings' permission mode rather than a parent conversation's |
| statusline-setup | Sonnet | When you run `/statusline` to configure your status line |
| claude-code-guide | Haiku | When you ask questions about Claude Code features |
</Tab>
</Tabs>
Built-in subagents are registered by default in interactive sessions. To restrict them:
* To block a specific built-in type, add it to `permissions.deny` as shown in [Disable specific subagents](#disable-specific-subagents).
* To prevent Claude from delegating to any subagent, deny the `Agent` tool itself with [`permissions.deny`](/docs/en/permissions#tool-specific-permission-rules).
* To remove only the built-in `Explore` and `Plan` subagents, set [`CLAUDE_CODE_DISABLE_EXPLORE_PLAN_AGENTS=1`](/docs/en/env-vars). Claude reads and explores files directly instead of delegating to them. Requires Claude Code v2.1.198 or later.
* In [non-interactive mode](/docs/en/headless) and the [Agent SDK](/docs/en/agent-sdk/overview), set [`CLAUDE_AGENT_SDK_DISABLE_BUILTIN_AGENTS=1`](/docs/en/env-vars) to remove all built-in types and supply only your own.
Beyond these built-in subagents, you can create your own with custom prompts, tool restrictions, permission modes, hooks, and skills. The following sections show how to get started and customize subagents.
## Quickstart: create your first subagent
Subagents are Markdown files with YAML frontmatter. To create one, ask Claude to write it for you, or [write the file yourself](#write-subagent-files).
As of v2.1.198, the `/agents` command no longer opens the interactive creation wizard; running it prints a reminder to ask Claude or edit `.claude/agents/` directly. Subagent files, frontmatter fields, and the `.claude/agents/` and `~/.claude/agents/` locations are unchanged; only the terminal wizard is removed.
This walkthrough creates a user-level subagent that reviews code and suggests improvements.
<Steps>
<Step title="Ask Claude to create the subagent">
In Claude Code, describe the subagent you want and where to save it:
```text wrap theme={null}
Create a personal code-improver subagent in ~/.claude/agents/ that scans
files and suggests improvements for readability, performance, and best
practices. It should explain each issue, show the current code, and
provide an improved version. Make it read-only and have it use Sonnet.
```
Claude writes the file with a `name`, a `description`, a `tools` list, a `model`, and a system prompt.
</Step>
<Step title="Review the file">
Open `~/.claude/agents/code-improver.md` and confirm the frontmatter matches what you asked for. The result looks like this:
```markdown theme={null}
---
name: code-improver
description: Scans files and suggests improvements for readability, performance, and best practices. Use after writing or modifying code.
tools: Read, Grep, Glob
model: sonnet
---
You are a code improvement specialist. For each issue you find, explain
the problem, show the current code, and provide an improved version.
```
Because the file lives in `~/.claude/agents/`, the subagent is available in every project on your machine. To scope it to one project instead, move it to that project's `.claude/agents/` directory. [Choose the subagent scope](#choose-the-subagent-scope) compares the two.
</Step>
<Step title="Try it out">
Ask Claude to delegate to the new subagent:
```text wrap theme={null}
Use the code-improver agent to suggest improvements in this project
```
Claude delegates to your new subagent, which scans the codebase and returns improvement suggestions. In the transcript, the delegation appears as a tool call row showing the subagent's name followed by a short task description, such as `code-improver (Suggest code improvements)`.
If Claude can't find the new subagent, restart Claude Code and try again. This happens only when `~/.claude/agents/` didn't exist before the session started, because a running session doesn't detect a newly created `agents` directory.
</Step>
</Steps>
You now have a subagent you can use in any project on your machine to analyze codebases and suggest improvements.
You can also write subagent files by hand, define them via CLI flags, or distribute them through plugins. The following sections cover all configuration options.
<Note>
On Claude Code v2.1.197 and earlier, `/agents` opens an interactive wizard with a **Running** tab that lists live subagents and a **Library** tab for creating, editing, and deleting them. 
</Note>
## Configure subagents
A subagent's file location determines who it's available to, and its frontmatter determines what it can do. This section covers where subagent files live and every field they support.
### Choose the subagent scope
Store subagent files in different locations depending on scope. When multiple subagents share the same name, Claude Code uses the one from the higher-priority location.
| Location | Scope | Priority | How to create |
| :--------------------------- | :---------------------- | :---------- | :-------------------------------------------- |
| Managed settings | Organization-wide | 1 (highest) | Deployed via [managed settings](/docs/en/settings) |
| `--agents` CLI flag | Current session | 2 | Pass JSON when launching Claude Code |
| `.claude/agents/` | Current project | 3 | Ask Claude, or create the file manually |
| `~/.claude/agents/` | All your projects | 4 | Ask Claude, or create the file manually |
| Plugin's `agents/` directory | Where plugin is enabled | 5 (lowest) | Installed with [plugins](/docs/en/plugins) |
**Project subagents** (`.claude/agents/`) are ideal for subagents specific to a codebase. Check them into version control so your team can use and improve them collaboratively.
Project subagents are discovered by walking up from the current working directory, so every `.claude/agents/` between there and the repository root is scanned. As of v2.1.178, when more than one of these nested directories defines the same `name`, Claude Code uses the definition closest to the working directory.
Directories added with `--add-dir` are also scanned: a `.claude/agents/` folder inside an added directory loads alongside project subagents. See [Additional directories](/docs/en/permissions#additional-directories-grant-file-access-not-configuration) for which other configuration types load from `--add-dir`. To share subagents across projects without `--add-dir`, use `~/.claude/agents/` or a [plugin](/docs/en/plugins).
**User subagents** (`~/.claude/agents/`) are personal subagents available in all your projects.
Claude Code scans `.claude/agents/` and `~/.claude/agents/` recursively, so you can organize definitions into subfolders such as `agents/review/` or `agents/research/`. The subdirectory path doesn't affect how a subagent is identified or invoked, because identity comes only from the `name` frontmatter field.
Keep `name` values unique across the whole tree: if two files under the same `.claude/agents/` directory, including its subfolders, declare the same name, Claude Code loads only one of them, chosen by filesystem read order rather than a documented precedence. Across nested project directories, the definition closest to the working directory wins, as described above. The [`/doctor`](/docs/en/commands#all-commands) setup checkup reports files in the same directory that share a name and proposes renaming or removing all but one. Before v2.1.205, `/doctor` opened a diagnostics screen that listed duplicates and showed which definition was active.
Plugin `agents/` directories are also scanned recursively. Unlike project and user scopes, a subfolder inside a plugin's `agents/` directory becomes part of the [scoped identifier](#invoke-subagents-explicitly): a file at `agents/review/security.md` in plugin `my-plugin` registers as `my-plugin:review:security`.
**CLI-defined subagents** are passed as JSON when launching Claude Code. They exist only for that session and aren't saved to disk, making them useful for quick testing or automation scripts. You can define multiple subagents in a single `--agents` call:
<Tabs>
<Tab title="macOS, Linux, WSL">
```bash theme={null}
claude --agents '{
"code-reviewer": {
"description": "Expert code reviewer. Use proactively after code changes.",
"prompt": "You are a senior code reviewer. Focus on code quality, security, and best practices.",
"tools": ["Read", "Grep", "Glob", "Bash"],
"model": "sonnet"
},
"debugger": {
"description": "Debugging specialist for errors and test failures.",
"prompt": "You are an expert debugger. Analyze errors, identify root causes, and provide fixes."
}
}'
```
</Tab>
<Tab title="Windows PowerShell">
```powershell theme={null}
claude --agents @'
{
"code-reviewer": {
"description": "Expert code reviewer. Use proactively after code changes.",
"prompt": "You are a senior code reviewer. Focus on code quality, security, and best practices.",
"tools": ["Read", "Grep", "Glob", "Bash"],
"model": "sonnet"
},
"debugger": {
"description": "Debugging specialist for errors and test failures.",
"prompt": "You are an expert debugger. Analyze errors, identify root causes, and provide fixes."
}
}
'@
```
</Tab>
</Tabs>
The `--agents` flag accepts JSON with the same [frontmatter](#supported-frontmatter-fields) fields as file-based subagents: `description`, `prompt`, `tools`, `disallowedTools`, `model`, `permissionMode`, `mcpServers`, `hooks`, `maxTurns`, `skills`, `initialPrompt`, `memory`, `effort`, `background`, `isolation`, and `color`. Use `prompt` for the system prompt, equivalent to the markdown body in file-based subagents.
**Managed subagents** are deployed by organization administrators. Place markdown files in `.claude/agents/` inside the [managed settings directory](/docs/en/settings#settings-files), using the same frontmatter format as project and user subagents. Managed definitions take precedence over project and user subagents with the same name.
**Plugin subagents** come from [plugins](/docs/en/plugins) you've installed. They load automatically alongside your custom subagents and appear in the @-mention typeahead under their scoped name. See the [plugin components reference](/docs/en/plugins-reference#agents) for details on creating plugin subagents.
<Note>
For security reasons, plugin subagents don't support the `hooks`, `mcpServers`, or `permissionMode` frontmatter fields. These fields are ignored when loading agents from a plugin. If you need them, copy the agent file into `.claude/agents/` or `~/.claude/agents/`. You can also add rules to [`permissions.allow`](/docs/en/settings#permission-settings) in `settings.json` or `settings.local.json`, but these rules apply to the entire session, not only the plugin subagent.
</Note>
Subagent definitions from any of these scopes are also available to [agent teams](/docs/en/agent-teams#use-subagent-definitions-for-teammates): when spawning a teammate, you can reference a subagent type and the teammate uses its `tools` and `model`, with the definition's body appended to the teammate's system prompt as additional instructions. See [agent teams](/docs/en/agent-teams#use-subagent-definitions-for-teammates) for which frontmatter fields apply on that path.
### Write subagent files
Subagent files use YAML frontmatter for configuration, followed by the system prompt in Markdown:
<Note>
Claude Code watches `~/.claude/agents/` and `.claude/agents/`. When you add or edit a subagent file on disk, or ask Claude to write one for you, Claude Code detects the change within a few seconds and the next delegation uses the updated definition, with no restart needed.
Two cases still need a restart:
* The watcher covers only directories that existed when the session started, so after creating a scope's first agent file in a new `agents` directory, restart to load it.
* Sessions started with `--disable-slash-commands` don't watch these directories at all.
</Note>
```markdown .claude/agents/code-reviewer.md theme={null}
---
name: code-reviewer
description: Reviews code for quality and best practices
tools: Read, Glob, Grep
model: sonnet
---
You are a code reviewer. When invoked, analyze the code and provide
specific, actionable feedback on quality, security, and best practices.
```
The frontmatter defines the subagent's metadata and configuration. The body becomes the system prompt that guides the subagent's behavior. Subagents receive only this system prompt plus basic environment details like the working directory, not the full Claude Code system prompt.
In [non-interactive mode](/docs/en/headless), the [`--append-subagent-system-prompt`](/docs/en/cli-reference#cli-flags) flag appends the text you provide to the end of every subagent's system prompt, including nested subagents. Requires Claude Code v2.1.205 or later.
A subagent starts in the main conversation's current working directory. Within a subagent, `cd` commands don't persist between Bash or PowerShell tool calls and don't affect the main conversation's working directory. To give the subagent an isolated copy of the repository instead, set [`isolation: worktree`](#supported-frontmatter-fields).
A subagent with `isolation: worktree` runs its Bash and PowerShell commands inside its worktree. A command whose working directory resolves to your main checkout instead, for example because the worktree directory was removed while the subagent was running, fails with an error. Before v2.1.203, such a command could run in the main checkout.
This working-directory check covers the whole repository containing the directory you launched Claude Code from. When your session runs in a linked [worktree](/docs/en/worktrees) of its own, the check also covers the main checkout that worktree is linked from. Before v2.1.210, the check covered only the launch directory itself. A command whose working directory resolved elsewhere in the same repository, such as the repository root when you launched Claude Code from a monorepo subdirectory, ran there instead of failing.
For Bash commands, Claude Code also checks the command itself in two ways:
* It blocks a command that redirects git into the main checkout.
* It refuses a command whose shape it can't verify stays inside the worktree. This refusal applies even to a command that runs no git.
The redirect vectors and the shape rules are listed under [How Claude Code enforces isolation](/docs/en/worktrees#how-claude-code-enforces-isolation). PowerShell commands get only the working-directory check.
[Monitor](/docs/en/tools-reference#monitor-tool) commands go through the same working-directory and command-content checks as Bash commands.
When the main conversation itself runs isolated in a worktree, Claude Code applies the same checks to the session and to every subagent it spawns, including subagents without `isolation: worktree`; see [How Claude Code enforces isolation](/docs/en/worktrees#how-claude-code-enforces-isolation).
#### Supported frontmatter fields
The following fields can be used in the YAML frontmatter. Only `name` and `description` are required.
| Field | Required | Description |
| :---------------- | :------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `name` | Yes | Unique identifier using lowercase letters and hyphens. [Hooks](/docs/en/hooks#subagentstart) receive this value as `agent_type`. The filename doesn't have to match. Names can't contain `:`, which is reserved for [plugin-scoped identifiers](/docs/en/plugins) such as `my-plugin:reviewer`. Claude Code doesn't load a file whose name contains one and logs an error to the debug log. Before v2.1.218, such names were accepted |
| `description` | Yes | When Claude should delegate to this subagent |
| `tools` | No | [Tools](#available-tools) the subagent can use. Inherits every tool available to subagents if omitted. If no entry in the list resolves to a tool, the subagent usually [fails to launch](/docs/en/errors#agent-would-be-spawned-with-zero-tools) with an error naming the entries. To preload Skills into context, use the `skills` field rather than listing `Skill` here |
| `disallowedTools` | No | Tools to deny, removed from inherited or specified list |
| `model` | No | [Model](#choose-a-model) to use: `sonnet`, `opus`, `haiku`, `fable`, a full model ID (for example, `claude-opus-5`), or `inherit`. Defaults to `inherit` |
| `permissionMode` | No | [Permission mode](#permission-modes): `default`, `acceptEdits`, `auto`, `dontAsk`, `bypassPermissions`, `plan`, or `manual` as an alias for `default`. The `manual` alias requires Claude Code v2.1.200 or later. Ignored for [plugin subagents](#choose-the-subagent-scope) |
| `maxTurns` | No | Maximum number of agentic turns before the subagent stops |
| `skills` | No | [Skills](/docs/en/skills) to preload into the subagent's context at startup. The full skill content is injected, not only the description. Subagents can still invoke unlisted project, user, and plugin skills through the Skill tool |
| `mcpServers` | No | [MCP servers](/docs/en/mcp) available to this subagent. Each entry is either a server name referencing an already-configured server (e.g., `"slack"`) or an inline definition with the server name as key and a full [MCP server config](/docs/en/mcp#installing-mcp-servers) as value. Ignored for [plugin subagents](#choose-the-subagent-scope) |
| `hooks` | No | [Lifecycle hooks](#define-hooks-for-subagents) scoped to this subagent. Ignored for [plugin subagents](#choose-the-subagent-scope) |
| `memory` | No | [Persistent memory scope](#enable-persistent-memory): `user`, `project`, or `local`. Enables cross-session learning |
| `background` | No | Set to `true` to keep this subagent in the background even when Claude asks to run it in the foreground. Where [fork mode](#turn-fork-mode-on-or-off) is on, Claude Code already runs the subagents Claude spawns [in the background](#run-subagents-in-foreground-or-background) |
| `effort` | No | Effort level when this subagent is active. Overrides the session effort level. Default: inherits from session. Options: `low`, `medium`, `high`, `xhigh`, `max`; available levels depend on the model |
| `isolation` | No | Set to `worktree` to run the subagent in a temporary [git worktree](/docs/en/worktrees), giving it an isolated copy of the repository branched by default from your [default branch](/docs/en/worktrees#choose-the-base-branch) rather than the parent session's `HEAD`. The worktree is automatically cleaned up if the subagent makes no changes |
| `color` | No | Display color for the subagent in the task list and transcript. Accepts `red`, `blue`, `green`, `yellow`, `purple`, `orange`, `pink`, or `cyan` |
| `initialPrompt` | No | Auto-submitted as the first user turn when this agent runs as the main session agent (via `--agent` or the `agent` setting). [Commands](/docs/en/commands) and [skills](/docs/en/skills) are processed. Prepended to any user-provided prompt |
### Choose a model
The `model` field controls which [AI model](/docs/en/model-config) the subagent uses:
Cut at 300 lines. The page has the rest.
terminal-config First recorded · 321 lines, first recorded
# Configure your terminal for Claude Code ## Enter multiline prompts ## Enable Option key shortcuts on macOS ## Get a terminal bell or notification ### Play a sound with a Notification hook ## Configure tmux ## Match the color theme ### Create a custom theme ## Switch to fullscreen rendering ## Paste large content ## Edit prompts with Vim keybindings ## Related resources
The first capture of this source. The page was already there, and this is what it said.
# Configure your terminal for Claude Code
> Fix Shift+Enter for newlines, get a terminal bell when Claude finishes, configure tmux, match the color theme, and enable Vim mode in the Claude Code CLI.
Claude Code works in any terminal without configuration. This page is for when something specific is not behaving the way you expect. Find your symptom below. If everything already feels right, you do not need this page.
* [Shift+Enter submits instead of inserting a newline](#enter-multiline-prompts)
* [Option-key shortcuts do nothing on macOS](#enable-option-key-shortcuts-on-macos)
* [No sound or alert when Claude finishes](#get-a-terminal-bell-or-notification)
* [You run Claude Code inside tmux](#configure-tmux)
* [Display flickers or scrollback jumps](#switch-to-fullscreen-rendering)
* [You want Vim keys in the prompt](#edit-prompts-with-vim-keybindings)
This page is about getting your terminal to send the right signals to Claude Code. To change which keys Claude Code itself responds to, see [keybindings](/docs/en/keybindings) instead.
## Enter multiline prompts
Pressing Enter submits your message. To add a line break without submitting, press Ctrl+J, or type `\` and then press Enter. Both work in every terminal with no setup.
In most terminals you can also press Shift+Enter, but support varies by terminal emulator:
| Terminal | Shift+Enter for newline |
| :---------------------------------------------------------------------- | :------------------------------------------ |
| Ghostty, Kitty, iTerm2, WezTerm, Warp, Apple Terminal, Windows Terminal | Works without setup |
| VS Code, Cursor, Devin Desktop, Alacritty, Zed | Run `/terminal-setup` once |
| gnome-terminal, JetBrains IDEs such as PyCharm and Android Studio | Not available; use Ctrl+J or `\` then Enter |
For VS Code, Cursor, Devin Desktop, Alacritty, and Zed, `/terminal-setup` writes Shift+Enter and other keybindings into the terminal's configuration file. On the first run you see a confirmation such as `Installed VSCode terminal Shift+Enter key binding`. Existing bindings are left in place; if you see a message such as `VSCode terminal Shift+Enter key binding already configured`, no change was made. Run `/terminal-setup` directly in the host terminal rather than inside tmux or screen, since it needs to write to the host terminal's configuration.
In VS Code, Cursor, and Devin Desktop, `/terminal-setup` also updates two editor settings: it sets `terminal.integrated.gpuAcceleration` to `"off"` to prevent garbled text in the integrated terminal, and it sets `terminal.integrated.mouseWheelScrollSensitivity` for smoother scrolling in [fullscreen mode](/docs/en/fullscreen). To undo the GPU acceleration change, set it back to `"auto"` and reload the editor window.
If you are running inside tmux, Shift+Enter also requires the [tmux configuration below](#configure-tmux) even when the outer terminal supports it.
To bind newline to a different key, or to swap behavior so Enter inserts a newline and Shift+Enter submits, map the `chat:newline` and `chat:submit` actions in your [keybindings file](/docs/en/keybindings).
## Enable Option key shortcuts on macOS
Some Claude Code shortcuts use the Option key, such as Option+Enter for a newline or Option+P to switch models. On macOS, most terminals do not send Option as a modifier by default, so these shortcuts do nothing until you enable it. The terminal setting for this is usually labeled "Use Option as Meta Key"; Meta is the historical Unix name for the key now labeled Option or Alt.
<Tabs>
<Tab title="Apple Terminal">
Open Settings → Profiles → Keyboard and check "Use Option as Meta Key".
If you accepted Claude Code's first-run terminal setup prompt, this is already done. That prompt runs `/terminal-setup` for you, which enables Option as Meta and turns off the audible bell in your Apple Terminal profile.
In [screen reader mode](/docs/en/accessibility), `/terminal-setup` leaves the bell setting unchanged so the terminal bell stays audible. Before v2.1.211, `/terminal-setup` turned the bell off even in screen reader mode. If an earlier run turned the bell off, turn it back on under Settings → Profiles → Advanced → "Audible bell".
</Tab>
<Tab title="iTerm2">
Open Settings → Profiles → Keys → General and set Left Option key and Right Option key to "Esc+".
Running `/terminal-setup` in iTerm2 enables "Applications in terminal may access clipboard" under Settings → General → Selection so the `/copy` command can write to your system clipboard. The command detects iTerm2 even when run from inside tmux. Restart iTerm2 for the change to take effect.
</Tab>
<Tab title="VS Code">
Add `"terminal.integrated.macOptionIsMeta": true` to your VS Code settings.
</Tab>
</Tabs>
For Ghostty, Kitty, and other terminals, look for an Option-as-Alt or Option-as-Meta setting in the terminal's configuration file.
## Get a terminal bell or notification
When Claude finishes a task or pauses for a permission prompt, and you appear to be away from the terminal, it fires a notification event. See [when each notification type fires](/docs/en/hooks#notification) for the exact timing. Surfacing this as a terminal bell or desktop notification lets you switch to other work while a long task runs.
By default Claude Code sends a desktop notification only in Ghostty, Kitty, and iTerm2. In other terminals, set [`preferredNotifChannel`](/docs/en/settings#available-settings) to `"terminal_bell"` to ring the terminal bell instead, or configure a [Notification hook](#play-a-sound-with-a-notification-hook) for a custom sound or command. The following settings entry turns on the terminal bell:
```json ~/.claude/settings.json theme={null}
{
"preferredNotifChannel": "terminal_bell"
}
```
The desktop notification reaches your local machine over SSH, so a remote session can still alert you. Ghostty and Kitty forward it to your OS notification center without further setup. iTerm2 requires you to enable forwarding:
<Steps>
<Step title="Open iTerm2 notification settings">
Go to Settings → Profiles → Terminal.
</Step>
<Step title="Enable alerts">
Check "Notification Center Alerts", then click "Filter Alerts" and enable "Send escape sequence-generated alerts".
</Step>
</Steps>
If notifications still do not appear, confirm that your terminal application has notification permission in your OS settings, and if you are running inside tmux, [enable passthrough](#configure-tmux).
### Play a sound with a Notification hook
In any terminal you can configure a [Notification hook](/docs/en/hooks-guide#get-notified-when-claude-needs-input) to play a sound or run a custom command when Claude needs your attention. Hooks run alongside the built-in notification rather than replacing it, so terminals that do not receive a desktop notification, such as Warp or the VS Code integrated terminal, can use a hook or set `preferredNotifChannel` to `"terminal_bell"` instead.
The example below plays a system sound on macOS. The linked guide has desktop notification commands for macOS, Linux, and Windows.
```json ~/.claude/settings.json theme={null}
{
"hooks": {
"Notification": [
{
"hooks": [{ "type": "command", "command": "afplay /System/Library/Sounds/Glass.aiff" }]
}
]
}
}
```
## Configure tmux
When Claude Code runs inside tmux, two things break by default: Shift+Enter submits instead of inserting a newline, and desktop notifications and the [progress bar](/docs/en/settings#available-settings) never reach the outer terminal. Add these lines to `~/.tmux.conf`, then run `tmux source-file ~/.tmux.conf` to apply them to the running server:
```bash ~/.tmux.conf theme={null}
set -g allow-passthrough on
set -s extended-keys on
set -as terminal-features 'xterm*:extkeys'
```
The `allow-passthrough` line lets notifications and progress updates reach the outer terminal instead of being swallowed by tmux. The `extended-keys` lines let tmux distinguish Shift+Enter from plain Enter so the newline shortcut works.
## Match the color theme
Use the `/theme` command, or the theme picker in `/config`, to choose a Claude Code theme that matches your terminal. Selecting the auto option detects your terminal's light or dark background, so the theme follows OS appearance changes whenever your terminal does. Claude Code does not control the terminal's own color scheme, which is set by the terminal application.
To customize what appears at the bottom of the interface, configure a [custom status line](/docs/en/statusline) that shows the current model, working directory, git branch, or other context.
### Create a custom theme
In addition to the built-in presets, `/theme` lists any custom themes you have defined and any themes contributed by installed [plugins](/docs/en/plugins-reference#themes). Select **New custom theme…** at the end of the list to create one interactively: you name the theme, then pick individual color tokens to override. Press `Ctrl+E` while a custom theme is highlighted to edit it.
Each custom theme is a JSON file in `~/.claude/themes/`. The filename without the `.json` extension is the theme's slug, and selecting the theme stores `custom:<slug>` as your theme preference. The file has three optional fields:
| Field | Type | Description |
| :---------- | :----- | :---------------------------------------------------------------------------------------------------------------------------------------------- |
| `name` | string | Display label shown in `/theme`. Defaults to the filename slug |
| `base` | string | Built-in preset the theme starts from: `dark`, `light`, `dark-daltonized`, `light-daltonized`, `dark-ansi`, or `light-ansi`. Defaults to `dark` |
| `overrides` | object | Map of color token names to color values. Tokens not listed here fall through to the base preset |
Color values accept `#rrggbb`, `#rgb`, `rgb(r,g,b)`, `ansi256(n)`, or `ansi:<name>` where `<name>` is one of the 16 standard ANSI color names such as `red` or `cyanBright`. Unknown tokens and invalid color values are ignored, so a typo cannot break rendering.
The following example defines a theme that keeps the dark preset but recolors the prompt accent, error text, and success text:
```json ~/.claude/themes/dracula.json theme={null}
{
"name": "Dracula",
"base": "dark",
"overrides": {
"claude": "#bd93f9",
"error": "#ff5555",
"success": "#50fa7b"
}
}
```
Claude Code watches `~/.claude/themes/` and reloads when a file is added or changed, so edits made in your editor apply to a running session without a restart. If the `~/.claude/themes/` folder itself didn't exist when Claude Code started, restart once after creating your first theme file. After that, changes apply without a restart.
The reference below covers the tokens you can set in `overrides`. The interactive editor in `/theme` shows the same tokens with a live preview, plus a few single-purpose accents such as onboarding screen colors that are omitted here.
<Accordion title="Color token reference">
The following example combines tokens from several of the groups below: the brand accent, the plan mode border, the diff backgrounds, and the fullscreen message background.
```json ~/.claude/themes/midnight.json theme={null}
{
"name": "Midnight",
"base": "dark",
"overrides": {
"claude": "#a78bfa",
"planMode": "#38bdf8",
"diffAdded": "#14532d",
"diffRemoved": "#7f1d1d",
"userMessageBackground": "#1e1b4b"
}
}
```
#### Text and accent colors
Control the primary brand accent and the foreground text shades used throughout the interface.
| Token | Controls |
| :------------ | :--------------------------------------------------------------- |
| `claude` | Primary brand accent, used for the spinner and assistant label |
| `text` | Default foreground text |
| `inverseText` | Text drawn on top of a colored background, such as status badges |
| `inactive` | Secondary text such as hints, timestamps, and disabled items |
| `subtle` | Faint borders and de-emphasized secondary text |
| `suggestion` | Autocomplete suggestions and selection highlight in pickers |
| `permission` | Dialog borders, including permission prompts and pickers |
| `remember` | Memory and `CLAUDE.md` indicators |
#### Status colors
Signal success, failure, and warning states across messages and indicators.
| Token | Controls |
| :-------- | :--------------------------------------------------- |
| `success` | Success messages and passing checks |
| `error` | Error messages and failures |
| `warning` | Warnings, caution messages, and the auto mode border |
| `merged` | Merged pull request status |
#### Input box and mode indicators
Set the input box border color and the accent shown while a permission mode or indicator is active.
| Token | Controls |
| :------------- | :------------------------------------------------- |
| `promptBorder` | Input box border in the default permission mode |
| `planMode` | Plan mode accent and border |
| `autoAccept` | Accept-edits mode accent and border |
| `bashBorder` | Input box border when entering a `!` shell command |
| `ide` | IDE connection indicator |
| `fastMode` | Fast mode indicator |
#### Diff rendering
Color added and removed code in file edits and reviews.
| Token | Controls |
| :------------------ | :------------------------------------------------- |
| `diffAdded` | Background of added lines |
| `diffRemoved` | Background of removed lines |
| `diffAddedDimmed` | Background of unchanged context near added lines |
| `diffRemovedDimmed` | Background of unchanged context near removed lines |
| `diffAddedWord` | Word-level highlight within an added line |
| `diffRemovedWord` | Word-level highlight within a removed line |
#### Fullscreen mode
Apply only in [fullscreen rendering mode](/docs/en/fullscreen), where messages have a background fill.
| Token | Controls |
| :--------------------------- | :------------------------------------------------------------ |
| `userMessageBackground` | Background behind your messages in the transcript |
| `userMessageBackgroundHover` | Background behind a message while hovered or expanded |
| `bashMessageBackgroundColor` | Background behind `!` shell command entries in the transcript |
| `memoryBackgroundColor` | Background behind `#` memory entries in the transcript |
| `selectionBg` | Background of text selected with the mouse |
#### Usage meter and speaker labels
Adjust the bar shown in the `/usage` view and the labels that distinguish your messages from Claude's.
| Token | Controls |
| :----------------- | :------------------------------------------------ |
| `rate_limit_fill` | Filled portion of the usage meter |
| `rate_limit_empty` | Unfilled portion of the usage meter |
| `briefLabelYou` | Color of the `You` label on your messages |
| `briefLabelClaude` | Color of the `Claude` label on assistant messages |
#### Shimmer variants and subagent colors
Several tokens have a paired shimmer variant that supplies the lighter color used in the spinner's animated gradient. Override the shimmer alongside its base token if the animation looks mismatched.
* `claude` and `claudeShimmer`
* `warning` and `warningShimmer`
* `permission` and `permissionShimmer`
* `promptBorder` and `promptBorderShimmer`
* `inactive` and `inactiveShimmer`
* `fastMode` and `fastModeShimmer`
Each [subagent](/docs/en/sub-agents) and parallel task is shown in one of eight named colors so you can tell them apart in the transcript. The token names follow the pattern `<color>_FOR_SUBAGENTS_ONLY`, where `<color>` is `red`, `blue`, `green`, `yellow`, `purple`, `orange`, `pink`, or `cyan`. Override these to change what each named color looks like. For example, a subagent with `color: blue` in its definition is drawn using the `blue_FOR_SUBAGENTS_ONLY` value.
Claude Code renders the [`ultrathink`](/docs/en/model-config#use-ultrathink-for-one-off-deep-reasoning) keyword in the prompt input with a seven-color rainbow gradient. The token names follow the pattern `rainbow_<color>` and `rainbow_<color>_shimmer`, where `<color>` is `red`, `orange`, `yellow`, `green`, `blue`, `indigo`, or `violet`.
</Accordion>
## Switch to fullscreen rendering
In [screen reader mode](/docs/en/accessibility), this section doesn't apply. Claude Code always renders as plain scrolling text except in attached [background sessions](/docs/en/agent-view), and if you run `/tui fullscreen` in any other session, Claude Code prints an explanation instead of switching.
If the display flickers or the scroll position jumps while Claude is working, switch to [fullscreen rendering mode](/docs/en/fullscreen). In this mode you scroll with the mouse or PageUp inside Claude Code rather than with your terminal's native scrollback; see the [fullscreen page](/docs/en/fullscreen#search-and-review-the-conversation) for how to search and copy.
If flicker is the only problem and your terminal supports synchronized output but isn't auto-detected, such as Emacs `eat`, set [`CLAUDE_CODE_FORCE_SYNC_OUTPUT=1`](/docs/en/env-vars) to stop the flicker without changing renderers.
Run `/tui fullscreen` to switch and save the preference. Your conversation relaunches intact and future sessions start in fullscreen. You can also set the `CLAUDE_CODE_NO_FLICKER` environment variable before starting Claude Code:
<CodeGroup>
```bash Bash and Zsh theme={null}
CLAUDE_CODE_NO_FLICKER=1 claude
```
```powershell PowerShell theme={null}
$env:CLAUDE_CODE_NO_FLICKER = "1"; claude
```
```json ~/.claude/settings.json theme={null}
{
"env": {
"CLAUDE_CODE_NO_FLICKER": "1"
}
}
```
</CodeGroup>
## Paste large content
When you paste more than 800 characters or more than two lines into the prompt, Claude Code collapses the input to a placeholder such as `[Pasted text #1 +120 lines]` so the input box stays usable. The full content is still sent to Claude when you submit.
Claude Code keeps the collapsed content under `~/.claude/paste-cache/`, so when you recall a prompt from [command history](/docs/en/interactive-mode#command-history) and resubmit it, Claude Code sends the full pasted content again, including in a later session, until the retention sweep removes the cache file.
Claude Code deletes cache files older than [`cleanupPeriodDays`](/docs/en/settings#available-settings), following the [retention sweep rules](/docs/en/claude-directory#cleaned-up-automatically), so a recalled prompt can reference pasted text that no longer exists. When you submit such a prompt, Claude Code never sends the literal `[Pasted text #N]` string, and shows a notification naming the missing paste:
* In a plain prompt with text remaining, Claude Code removes the placeholder and sends the remaining text.
Cut at 300 lines. The page has the rest.
third-party-integrations First recorded · 249 lines, first recorded
# Enterprise deployment overview ## Compare deployment options ## Configure proxies and gateways ## Best practices for organizations ### Invest in documentation and memory ### Simplify deployment ### Start with guided usage ### Pin model versions for cloud providers ### Configure security policies ### Leverage MCP for integrations ## Next steps
The first capture of this source. The page was already there, and this is what it said.
# Enterprise deployment overview
> Learn how Claude Code can integrate with various third-party services and infrastructure to meet enterprise deployment requirements.
export const ContactSalesCard = ({surface}) => {
const utm = content => `utm_source=claude_code&utm_medium=docs&utm_content=${surface}_${content}`;
const iconArrowRight = (size = 13) => <svg width={size} height={size} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
<line x1="5" y1="12" x2="19" y2="12" />
<polyline points="12 5 19 12 12 19" />
</svg>;
const STYLES = `
.cc-cs {
--cs-slate: #141413;
--cs-clay: #d97757;
--cs-clay-deep: #c6613f;
--cs-gray-000: #ffffff;
--cs-gray-700: #3d3d3a;
--cs-border-default: rgba(31, 30, 29, 0.15);
font-family: inherit;
}
.dark .cc-cs {
--cs-slate: #f0eee6;
--cs-gray-000: #262624;
--cs-gray-700: #bfbdb4;
--cs-border-default: rgba(240, 238, 230, 0.14);
}
.cc-cs-card {
display: flex; align-items: center; justify-content: space-between;
gap: 16px; padding: 14px 16px; margin: 0;
background: var(--cs-gray-000); border: 0.5px solid var(--cs-border-default);
border-radius: 8px; flex-wrap: wrap;
}
.cc-cs-text { font-size: 13px; color: var(--cs-gray-700); line-height: 1.5; flex: 1; min-width: 240px; }
.cc-cs-text strong { font-weight: 550; color: var(--cs-slate); }
.cc-cs-actions { display: flex; align-items: center; gap: 8px; flex-shrink: 0; }
.cc-cs-btn-clay {
display: inline-flex; align-items: center; gap: 8px;
background: var(--cs-clay-deep); color: #fff; border: none;
border-radius: 8px; padding: 8px 14px;
font-size: 13px; font-weight: 500;
transition: background-color 0.15s; white-space: nowrap;
}
.cc-cs-btn-clay:hover { background: var(--cs-clay); }
.cc-cs-btn-ghost {
display: inline-flex; align-items: center; gap: 8px;
background: transparent; color: var(--cs-gray-700);
border: 0.5px solid var(--cs-border-default);
border-radius: 8px; padding: 8px 14px;
font-size: 13px; font-weight: 500;
}
.cc-cs-btn-ghost:hover { background: rgba(0, 0, 0, 0.04); }
.dark .cc-cs-btn-ghost:hover { background: rgba(255, 255, 255, 0.04); }
@media (max-width: 720px) {
.cc-cs-actions { width: 100%; }
}
`;
return <div className="cc-cs not-prose">
<style>{STYLES}</style>
<div className="cc-cs-card">
<div className="cc-cs-text">
<strong>Deploying Claude Code across your organization?</strong> Talk to sales about enterprise plans, SSO, and centralized billing.
</div>
<div className="cc-cs-actions">
<a href={`https://claude.com/pricing?${utm('view_plans')}#plans-business`} className="cc-cs-btn-ghost">
View plans
</a>
<a href={`https://claude.com/contact-sales?${utm('contact_sales')}`} className="cc-cs-btn-clay">
Contact sales {iconArrowRight()}
</a>
</div>
</div>
</div>;
};
Organizations can deploy Claude Code through Anthropic directly or through a cloud provider. This page helps you choose the right configuration.
<ContactSalesCard surface="third_party_overview" />
## Compare deployment options
For most organizations, Claude for Teams or Claude for Enterprise provides the best experience. Team members get access to both Claude Code and Claude on the web with a single subscription, centralized billing, and no infrastructure setup required.
**Claude for Teams** is self-service and includes collaboration features, admin tools, and billing management. Best for smaller teams that need to get started quickly.
**Claude for Enterprise** adds SSO and domain capture, role-based permissions, compliance API access, and managed policy settings for deploying organization-wide Claude Code configurations. Best for larger organizations with security and compliance requirements.
Learn more about [Team plans](https://support.claude.com/en/articles/9266767-what-is-the-team-plan) and [Enterprise plans](https://support.claude.com/en/articles/9797531-what-is-the-enterprise-plan).
The deployment options compared below cover where model inference runs. To run [Claude Code on the web](/docs/en/claude-code-on-the-web) sessions on compute your organization operates, see [self-hosted environments](/docs/en/self-hosted-environments).
If your organization has specific infrastructure requirements, compare the options below:
<table>
<thead>
<tr>
<th>Feature</th>
<th>Claude for Teams/Enterprise</th>
<th>Anthropic Console</th>
<th>Amazon Bedrock</th>
<th>Claude Platform on AWS</th>
<th>Google Cloud's Agent Platform, formerly Vertex AI</th>
<th>Microsoft Foundry</th>
</tr>
</thead>
<tbody>
<tr>
<td>Best for</td>
<td>Most organizations (recommended)</td>
<td>Individual developers</td>
<td>AWS-native deployments</td>
<td>AWS Marketplace billing with Claude API features</td>
<td>GCP-native deployments</td>
<td>Azure-native deployments</td>
</tr>
<tr>
<td>Billing</td>
<td><strong>Teams:</strong> \$150/seat (Premium) with PAYG available<br /><strong>Enterprise:</strong> <a href="https://claude.com/contact-sales?utm_source=claude_code&utm_medium=docs&utm_content=third_party_enterprise">Contact Sales</a></td>
<td>PAYG</td>
<td>PAYG through AWS</td>
<td>PAYG through AWS Marketplace</td>
<td>PAYG through GCP</td>
<td>PAYG through Azure</td>
</tr>
<tr>
<td>Regions</td>
<td>Supported [countries](https://www.anthropic.com/supported-countries)</td>
<td>Supported [countries](https://www.anthropic.com/supported-countries)</td>
<td>Multiple AWS [regions](https://docs.aws.amazon.com/bedrock/latest/userguide/models-regions.html)</td>
<td>Multiple AWS regions</td>
<td>Multiple GCP [regions](https://cloud.google.com/vertex-ai/generative-ai/docs/learn/locations)</td>
<td>Multiple Azure [regions](https://azure.microsoft.com/en-us/explore/global-infrastructure/products-by-region/)</td>
</tr>
<tr>
<td>Prompt caching</td>
<td>Enabled by default</td>
<td>Enabled by default</td>
<td>Enabled by default</td>
<td>Enabled by default</td>
<td>Enabled by default</td>
<td>Enabled by default</td>
</tr>
<tr>
<td>Authentication</td>
<td>Claude.ai SSO or email</td>
<td>API key</td>
<td>API key or AWS credentials</td>
<td>API key or AWS credentials</td>
<td>GCP credentials</td>
<td>API key or Microsoft Entra ID</td>
</tr>
<tr>
<td>Cost tracking</td>
<td>Usage dashboard</td>
<td>Usage dashboard</td>
<td>AWS Cost Explorer</td>
<td>AWS Cost Explorer</td>
<td>GCP Billing</td>
<td>Azure Cost Management</td>
</tr>
<tr>
<td>Includes Claude on web</td>
<td>Yes</td>
<td>No</td>
<td>No</td>
<td>No</td>
<td>No</td>
<td>No</td>
</tr>
<tr>
<td>Enterprise features</td>
<td>Team management, SSO, usage monitoring</td>
<td>None</td>
<td>IAM policies, CloudTrail</td>
<td>IAM policies, CloudTrail</td>
<td>IAM roles, Cloud Audit Logs</td>
<td>RBAC policies, Azure Monitor</td>
</tr>
</tbody>
</table>
For a feature-by-feature breakdown of what's available on each option, see [Feature availability](/docs/en/feature-availability).
Select a deployment option to view setup instructions:
* [Claude for Teams or Enterprise](/docs/en/authentication#claude-for-teams-or-enterprise)
* [Anthropic Console](/docs/en/authentication#claude-console-authentication)
* [Claude apps gateway](/docs/en/claude-apps-gateway), a self-hosted gateway that adds IdP sign-in in front of Amazon Bedrock, Claude Platform on AWS, Google Cloud's Agent Platform, Microsoft Foundry, or the Anthropic API
* [Amazon Bedrock](/docs/en/amazon-bedrock)
* [Claude Platform on AWS](/docs/en/claude-platform-on-aws)
* [Google Cloud's Agent Platform](/docs/en/google-vertex-ai)
* [Microsoft Foundry](/docs/en/microsoft-foundry)
For Amazon Bedrock and Google Vertex AI, you can also run `claude` and select **3rd-party platform** at the login prompt to launch an interactive setup wizard.
## Configure proxies and gateways
Most organizations can use a cloud provider directly without additional configuration. However, you may need to configure a corporate proxy or LLM gateway if your organization has specific network or management requirements. These are different configurations that can be used together:
* **Corporate proxy**: Routes traffic through an HTTP/HTTPS proxy. Use this if your organization requires all outbound traffic to pass through a proxy server for security monitoring, compliance, or network policy enforcement. Configure with the `HTTPS_PROXY` or `HTTP_PROXY` environment variables. Learn more in [Enterprise network configuration](/docs/en/network-config).
* **LLM Gateway**: A service that sits between Claude Code and the cloud provider to handle authentication and routing. Use this if you need centralized usage tracking across teams, custom rate limiting or budgets, or centralized authentication management. Configure with the `ANTHROPIC_BASE_URL`, `ANTHROPIC_BEDROCK_BASE_URL`, `ANTHROPIC_AWS_BASE_URL`, `ANTHROPIC_VERTEX_BASE_URL`, or `ANTHROPIC_FOUNDRY_BASE_URL` environment variables. Learn more in [LLM gateways](/docs/en/llm-gateway).
For the per-provider environment variables that route Amazon Bedrock, Microsoft Foundry, or Google Cloud's Agent Platform through an LLM gateway, see [route to a cloud provider through a gateway](/docs/en/llm-gateway-connect#route-to-a-cloud-provider-through-a-gateway). Run `/status` in Claude Code to verify which provider, base URL, and proxy a session is using.
## Best practices for organizations
### Invest in documentation and memory
We strongly recommend investing in documentation so that Claude Code understands your codebase. Organizations can deploy CLAUDE.md files at multiple levels:
* **Organization-wide**: Deploy to system directories such as `/Library/Application Support/ClaudeCode/CLAUDE.md` (macOS), `/etc/claude-code/CLAUDE.md` (Linux and WSL), or `C:\Program Files\ClaudeCode\CLAUDE.md` (Windows) for company-wide standards
* **Repository-level**: Create `CLAUDE.md` files in repository roots containing project architecture, build commands, and contribution guidelines. Check these into source control so all users benefit
Learn more in [Memory and CLAUDE.md files](/docs/en/memory).
### Simplify deployment
If you have a custom development environment, we find that creating a "one click" way to install Claude Code is key to growing adoption across an organization.
### Start with guided usage
Encourage new users to try Claude Code for codebase Q\&A, or on smaller bug fixes or feature requests. Ask Claude Code to make a plan. Check Claude's suggestions and give feedback if it's off-track. Over time, as users understand this new paradigm better, then they'll be more effective at letting Claude Code run more agentically.
### Pin model versions for cloud providers
If you deploy through [Amazon Bedrock](/docs/en/amazon-bedrock), [Google Cloud's Agent Platform](/docs/en/google-vertex-ai), [Microsoft Foundry](/docs/en/microsoft-foundry), or [Claude Platform on AWS](/docs/en/claude-platform-on-aws), pin specific model versions using `ANTHROPIC_DEFAULT_FABLE_MODEL`, `ANTHROPIC_DEFAULT_OPUS_MODEL`, `ANTHROPIC_DEFAULT_SONNET_MODEL`, and `ANTHROPIC_DEFAULT_HAIKU_MODEL`. Without pinning, model aliases resolve to Claude Code's built-in default for that provider, which can lag the newest release and may not yet be enabled in your account. Pinning lets you control when your users move to a new model. See [Model configuration](/docs/en/model-config#pin-models-for-third-party-deployments) for what each provider does when the default is unavailable.
### Configure security policies
Security teams can configure managed permissions for what Claude Code is and is not allowed to do, which cannot be overwritten by local configuration. [Learn more](/docs/en/security).
### Leverage MCP for integrations
MCP is a great way to give Claude Code more information, such as connecting to ticket management systems or error logs. We recommend that one central team configures MCP servers and checks a `.mcp.json` configuration into the codebase so that all users benefit. [Learn more](/docs/en/mcp).
## Next steps
Once you've chosen a deployment option and configured access for your team:
1. **Roll out to your team**: Share installation instructions and have team members [install Claude Code](/docs/en/setup) and authenticate with their credentials.
2. **Set up shared configuration**: Create a [CLAUDE.md file](/docs/en/memory) in your repositories to help Claude Code understand your codebase and coding standards.
3. **Configure permissions**: Review [security settings](/docs/en/security) to define what Claude Code can and cannot do in your environment.
tools-reference First recorded · 493 lines, first recorded
# Tools reference ## Configure tools with permission rules and hooks ## Agent tool behavior ## AskUserQuestion tool behavior ### Question auto-continue timeout ## Bash tool behavior ### What persists between commands ### Timeout and output limits #### Output limits ### Background commands ## Edit tool behavior ## EndConversation tool behavior ## Glob tool behavior ## Grep tool behavior ## LSP tool behavior ## Monitor tool ### WebSocket source ## NotebookEdit tool behavior ## PowerShell tool ### Enable the PowerShell tool ### Shell selection in settings, hooks, and skills ### Windows encoding and exit codes ### Preview limitations ## Read tool behavior ## WebFetch tool behavior ## WebSearch tool behavior ### Session search limit ## Write tool behavior ## Check which tools are available ## See also
The first capture of this source. The page was already there, and this is what it said.
# Tools reference
> Complete reference for the tools Claude Code can use, including permission requirements and per-tool behavior.
Claude Code has access to a set of built-in tools that help it understand and modify your codebase. The tool names are the exact strings you use in [permission rules](/docs/en/permissions#tool-specific-permission-rules), [subagent tool lists](/docs/en/sub-agents), and [hook matchers](/docs/en/hooks).
To control which tools Claude can use and when it asks first, configure [permission rules](/docs/en/permissions#tool-specific-permission-rules) in your settings, [hooks](/docs/en/hooks), or a [subagent's tool list](/docs/en/sub-agents#supported-frontmatter-fields). See [Configure tools with permission rules and hooks](#configure-tools-with-permission-rules-and-hooks) for each place that accepts a tool name.
To add custom tools, connect an [MCP server](/docs/en/mcp). To extend Claude with reusable prompt-based workflows, write a [skill](/docs/en/skills), which runs through the existing `Skill` tool rather than adding a new tool entry.
<Info>
The `Permission required` column shows whether the tool prompts in the default permission mode for paths inside the working directory. File-access tools marked No, including `Read`, `Grep`, and `Glob`, still prompt for paths outside the [working directory and additional directories](/docs/en/permissions#working-directories). `Bash` is marked Yes but runs a built-in set of [read-only commands](/docs/en/permissions#read-only-commands) without prompting.
</Info>
| Tool | Description | Permission required |
| :--------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | :------------------ |
| `Agent` | Spawns a [subagent](/docs/en/sub-agents) with its own context window to handle a task. See [Agent tool behavior](#agent-tool-behavior) | No |
| `Artifact` | Publishes an HTML or Markdown file as an [artifact](/docs/en/artifacts): a private, interactive page on claude.ai. You can share it with a public link, or inside your organization on Team and Enterprise plans, where public sharing requires an Owner to [enable it](/docs/en/artifacts#control-public-sharing). Requires a Pro, Max, Team, or Enterprise plan and `/login` authentication; see [Availability](/docs/en/artifacts#availability) | Yes |
| `AskUserQuestion` | Asks multiple-choice questions to gather requirements or clarify ambiguity. Questions stay open until you answer them by default. See [AskUserQuestion tool behavior](#askuserquestion-tool-behavior) | No |
| `Bash` | Executes shell commands in your environment. See [Bash tool behavior](#bash-tool-behavior) | Yes |
| `CronCreate` | Schedules a recurring or one-shot prompt within the current session. Tasks are session-scoped and restored on `--resume` or `--continue` if unexpired. See [scheduled tasks](/docs/en/scheduled-tasks) | No |
| `CronDelete` | Cancels a scheduled task by ID | No |
| `CronList` | Lists all scheduled tasks in the session | No |
| `Edit` | Makes targeted edits to specific files. See [Edit tool behavior](#edit-tool-behavior) | Yes |
| `EndConversation` | Ends the session, in rare cases of sustained abusive input or when you ask Claude to demonstrate the tool. Requires Claude Code v2.1.213 or later. See [EndConversation tool behavior](#endconversation-tool-behavior) | No |
| `EnterPlanMode` | Switches to plan mode to design an approach before coding | No |
| `EnterWorktree` | Creates an isolated [git worktree](/docs/en/worktrees) and switches into it. Pass a `path` to switch into an existing worktree instead of creating a new one. On first entry the target may be a worktree of the current repository or, in a multi-repo workspace, of a repository nested inside it. Before v2.1.203, a nested repository's worktree was rejected. A `path` outside `.claude/worktrees/` prompts for your approval before entering, since it moves the session's working directory and write access to that location. New-worktree creation and paths under `.claude/worktrees/` don't prompt. Before v2.1.206, Claude entered paths outside `.claude/worktrees/` without a prompt. From within a worktree session, or from a subagent with a pinned working directory such as [`isolation: worktree`](/docs/en/sub-agents#supported-frontmatter-fields), only the `path` form is available and the target must be under `.claude/worktrees/` of the session's repository | Yes |
| `ExitPlanMode` | Presents a plan for approval and exits plan mode | Yes |
| `ExitWorktree` | Exits a worktree session and returns to the original directory. Not available to subagents that already run in their own working directory, such as with [`isolation: worktree`](/docs/en/sub-agents#supported-frontmatter-fields) | No |
| `Glob` | Finds files based on pattern matching. See [Glob tool behavior](#glob-tool-behavior) | No |
| `Grep` | Searches for patterns in file contents. See [Grep tool behavior](#grep-tool-behavior) | No |
| `ListAgents` | Lists the agents Claude can message with `SendMessage`, apart from [agent team](/docs/en/agent-teams) teammates, which Claude reaches through the team's roster: subagents in the session, your other local Claude Code sessions, and, while this session is connected to [Remote Control](/docs/en/remote-control), your [Claude Code on the web](/docs/en/claude-code-on-the-web) sessions and your Remote Control sessions on other machines. Backs the `/list-agents` command. See [cross-session messaging](/docs/en/cross-session-messaging). Requires Claude Code v2.1.224 or later, and appears only in sessions where [cross-session messaging is enabled](/docs/en/cross-session-messaging#availability) | No |
| `ListMcpResourcesTool` | Lists resources exposed by connected [MCP servers](/docs/en/mcp) | No |
| `LSP` | Code intelligence via language servers: jump to definitions, find references, report type errors and warnings. See [LSP tool behavior](#lsp-tool-behavior) | No |
| `Monitor` | Runs a command in the background and feeds each output line back to Claude, so it can react to log entries, file changes, or polled status mid-conversation. Can also open a WebSocket and treat each incoming message as an event. See [Monitor tool](#monitor-tool) | Yes |
| `NotebookEdit` | Modifies Jupyter notebook cells. See [NotebookEdit tool behavior](#notebookedit-tool-behavior) | Yes |
| `PowerShell` | Executes PowerShell commands natively. See [PowerShell tool](#powershell-tool) for availability | Yes |
| `PushNotification` | Sends a desktop notification, and a phone push when [Remote Control](/docs/en/remote-control) is connected, so a long-running task or [scheduled task](/docs/en/scheduled-tasks) can reach you when you step away. Push delivery runs through Anthropic-hosted infrastructure, which is not accessible from Amazon Bedrock, Claude Platform on AWS, Google Cloud's Agent Platform, or Microsoft Foundry | No |
| `Read` | Reads the contents of files. See [Read tool behavior](#read-tool-behavior) | No |
| `ReadMcpResourceTool` | Reads a specific MCP resource by URI | No |
| `RemoteTrigger` | Creates, updates, runs, and lists [Routines](/docs/en/routines) on claude.ai. Backs the `/schedule` command. The [`RemoteTrigger` input reference](/docs/en/agent-sdk/typescript#remotetrigger) documents every action and the organization policies that remove the tool. Routines live on claude.ai and require a Pro, Max, Team, or Enterprise plan, so this tool is not accessible from Amazon Bedrock, Claude Platform on AWS, Google Cloud's Agent Platform, or Microsoft Foundry. Also unavailable when you turn off [feature-flag fetching](/docs/en/env-vars#features-that-need-feature-flag-fetching) | No |
| `ReportFindings` | Reports code-review findings as a structured list, with a file, summary, and failure scenario per finding, so Claude Code can render them instead of printing them as text. Claude calls it when active code-review instructions tell it to. Requires Claude Code v2.1.196 or later. As of v2.1.199, a finding can also carry an optional `category` slug, such as `correctness` or `test-coverage`, shown next to the file location in the rendered list | No |
| `ScheduleWakeup` | Reschedules the next iteration of a [self-paced `/loop`](/docs/en/scheduled-tasks#let-claude-choose-the-interval). Claude calls this at the end of each iteration to pick when the next one runs, between one minute and one hour out; you don't call it directly. To end the loop instead, Claude calls it with `stop: true`, which cancels the pending wakeup. The `stop` field requires Claude Code v2.1.202 or later. The pending wakeup appears in `session_crons` in [Stop hook input](/docs/en/hooks#stop-input). Not available on Amazon Bedrock, Claude Platform on AWS, Google Cloud's Agent Platform, or Microsoft Foundry, where a `/loop` prompt with no interval runs on a fixed schedule instead. The same happens when you turn off [feature-flag fetching](/docs/en/env-vars#features-that-need-feature-flag-fetching) | No |
| `SendMessage` | Sends a message to another agent: an [agent team](/docs/en/agent-teams) teammate, a [subagent it resumes](/docs/en/sub-agents#resume-subagents) by agent ID or name, or one of your other Claude Code sessions, on this machine or beyond it. Messaging other sessions requires Claude Code v2.1.224 or later; [cross-session messaging](/docs/en/cross-session-messaging) covers which sessions Claude can reach and each case's requirements. A receiver never treats a message from another agent as your consent or approval. Claude can include an optional `summary` input, typically 5-10 words, that Claude Code shows as a one-line preview. When Claude omits it on a [plain-text message](/docs/en/cross-session-messaging#limitations), Claude Code uses the first line of the message as the summary. Claude Code truncates a summary longer than 200 characters with an ellipsis | No |
| `SendUserFile` | Sends files from the session to you with an optional caption, so a generated report, diagram, screenshot, or built artifact reaches your device instead of only being mentioned in the transcript. As of v2.1.196, the optional `display` input controls presentation: `render` opens the file inline in the client, `attach` shows a download card only, and when unset the client decides by file type. Available when a [Remote Control](/docs/en/remote-control) client is connected or the session runs in a managed cloud environment such as [Claude Code on the web](/docs/en/claude-code-on-the-web). Delivery runs through Anthropic-hosted infrastructure, so the tool is not available on Amazon Bedrock, Google Cloud's Agent Platform, or Microsoft Foundry | No |
| `ShareOnboardingGuide` | Uploads `ONBOARDING.md` and returns a share link teammates can open in Claude Code. Called from `/team-onboarding` after the guide is written. Available to claude.ai subscribers on Pro, Max, Team, and Enterprise plans | Yes |
| `Skill` | Executes a [skill](/docs/en/skills#control-who-invokes-a-skill) within the main conversation | Yes |
| `TaskCreate` | Creates a new task in the task list | No |
| `TaskGet` | Retrieves full details for a specific task | No |
| `TaskList` | Lists all tasks with their current status | No |
| `TaskOutput` | Retrieves output from a background task. Deprecated in favor of `Read` on the task's output file path. When no task matches the ID, the error lists the running background agents by ID and description. Before v2.1.203, the error named only the missing ID | No |
| `TaskStop` | Stops a running background task by ID. It also accepts an [agent-team teammate](/docs/en/agent-teams) or a named background agent by agent ID or name. Before v2.1.198, it accepted only a background task ID. When no task matches the ID, the error lists the running background agents by ID and description, including agents that another agent spawned. Before v2.1.203, the error listed running teammates and named agents but not background agents another agent spawned, so those couldn't be identified or stopped from the main conversation | No |
| `TaskUpdate` | Updates task status, dependencies, details, or deletes tasks | No |
| `TodoWrite` | Manages the session task checklist. Disabled by default as of v2.1.142 in favor of `TaskCreate`, `TaskGet`, `TaskList`, and `TaskUpdate`. Set `CLAUDE_CODE_ENABLE_TASKS=0` to re-enable | No |
| `ToolSearch` | Searches for and loads deferred tools when [tool search](/docs/en/mcp#scale-with-mcp-tool-search) is enabled | No |
| `WaitForMcpServers` | Waits for one or more [MCP servers](/docs/en/mcp) that are still connecting in the background, so a request can use their tools without restarting the session. Claude calls it when a needed server isn't connected yet. Only appears when [tool search](/docs/en/mcp#scale-with-mcp-tool-search) is disabled, since `ToolSearch` handles the wait when it's enabled | No |
| `WebFetch` | Fetches content from a specified URL. See [WebFetch tool behavior](#webfetch-tool-behavior) | Yes |
| `WebSearch` | Performs web searches. See [WebSearch tool behavior](#websearch-tool-behavior) | Yes |
| `Workflow` | Runs a [dynamic workflow](/docs/en/workflows): a script that orchestrates many subagents in the background and returns one consolidated result | Yes |
| `Write` | Creates or overwrites files. See [Write tool behavior](#write-tool-behavior) | Yes |
## Configure tools with permission rules and hooks
For the most part, Claude decides when to use these tools and you don't need to name them yourself when interacting with Claude. You reference tool names directly when defining permissions and other configuration:
* in [`permissions.allow` and `permissions.deny`](/docs/en/settings#available-settings) in settings, and the `/permissions` interface
* in the `--allowedTools` and `--disallowedTools` [CLI flags](/docs/en/cli-reference)
* in the Agent SDK's [`allowedTools` and `disallowedTools`](/docs/en/agent-sdk/permissions#allow-and-deny-rules) options
* in a [subagent's `tools` or `disallowedTools`](/docs/en/sub-agents#supported-frontmatter-fields) frontmatter
* in a [skill's `allowed-tools`](/docs/en/skills#frontmatter-reference) frontmatter
* in a hook's [`if` condition](/docs/en/hooks-guide#filter-by-tool-name-and-arguments-with-the-if-field)
All of these accept the same rule format, `ToolName(specifier)`. The specifier depends on the tool, and several tools share a format:
| Rule format | Applies to | Details |
| :----------------------------- | :------------------------ | :--------------------------------------------------------------- |
| `Bash(npm run *)` | Bash, Monitor | [Command pattern matching](/docs/en/permissions#bash) |
| `PowerShell(Get-ChildItem *)` | PowerShell | [Command pattern matching](/docs/en/permissions#powershell) |
| `Read(~/secrets/**)` | Read, Grep, Glob, LSP | [Path pattern matching](/docs/en/permissions#read-and-edit) |
| `Edit(/src/**)` | Edit, Write, NotebookEdit | [Path pattern matching](/docs/en/permissions#read-and-edit) |
| `Skill(deploy *)` | Skill | [Skill name matching](/docs/en/skills#restrict-claude’s-skill-access) |
| `Agent(Explore)` | Agent | [Subagent type matching](/docs/en/permissions#agent-subagents) |
| `WebFetch(domain:example.com)` | WebFetch | [Domain matching](/docs/en/permissions#webfetch) |
| `WebSearch` | WebSearch | No specifier; allow or deny the tool as a whole |
Tools not listed here, such as `ExitPlanMode` or `ShareOnboardingGuide`, accept only the bare tool name with no specifier.
An `Edit(...)` allow rule also grants read access to the same path, so you don't need a matching `Read(...)` rule. A `Read(...)` deny rule also blocks the Edit and Write tools on the same path, including creating a new file there, because both tools change content Claude has to be able to read back. The `Read` deny check requires Claude Code v2.1.208 or later on edits, and v2.1.228 or later on writes.
Hook `matcher` fields use bare tool names, not the parenthesized rule format. See [matcher patterns](/docs/en/hooks#matcher-patterns) for the matching rules. For the field names each tool passes to `tool_input` in hooks, see the [PreToolUse input reference](/docs/en/hooks#pretooluse-input).
## Agent tool behavior
The Agent tool spawns a subagent in a separate context window. The subagent works through its task autonomously, then returns a single text result to the parent conversation. The parent doesn't see the subagent's intermediate tool calls or outputs, only that final result.
To cap how many turns a subagent runs, set `maxTurns` in the [subagent definition](/docs/en/sub-agents#supported-frontmatter-fields).
The same Agent tool also launches [forked subagents](/docs/en/sub-agents#fork-the-current-conversation) wherever [fork mode](/docs/en/sub-agents#turn-fork-mode-on-or-off) is on. A fork inherits the full parent conversation instead of starting fresh, runs in the background apart from the [cases that stay in the foreground](/docs/en/sub-agents#run-subagents-in-foreground-or-background), and still surfaces permission prompts in your terminal. The rest of this section describes named subagents.
Which tools a named subagent can use depends on the `tools` and `disallowedTools` fields in the [subagent definition](/docs/en/sub-agents):
* **Neither field set**: the subagent inherits every [tool available to subagents](/docs/en/sub-agents#available-tools).
* **`tools` only**: the subagent gets only the listed tools.
* **`disallowedTools` only**: the subagent gets every parent tool except the listed ones.
* **Both set**: `disallowedTools` takes precedence. A tool listed in both is removed.
In every case, the resolved set is limited to the [tools available to subagents](/docs/en/sub-agents#available-tools): a tool that isn't available to subagents is never granted, even when listed in `tools`.
If every entry in a subagent's `tools` list fails to match a usable tool, the Agent tool usually returns an error naming the entries instead of launching the subagent; see [Agent would be spawned with zero tools](/docs/en/errors#agent-would-be-spawned-with-zero-tools) for the message and how to fix each entry.
Launching the subagent doesn't itself prompt for permission. Claude Code checks the subagent's own tool calls against your permission rules as it runs.
Where a subagent's permission prompts appear depends on whether it runs in the foreground or the background. Claude Code runs subagents in the background by default. For the cases where one runs in the foreground, see [Run subagents in foreground or background](/docs/en/sub-agents#run-subagents-in-foreground-or-background).
* **Foreground subagents** show the same permission prompts you would see in the main conversation, at the moment each tool call happens.
* **Background subagents** surface permission prompts in your main session as of v2.1.186. The prompt names which subagent is asking, and pressing Esc denies that one tool call without stopping the subagent. Before v2.1.186, background subagents auto-denied any tool call that would otherwise prompt and continued without that tool.
To limit what a subagent can reach in the first place, narrow its `tools` field, leave Bash off the list, or set deny rules in your settings, as described in [Control subagent capabilities](/docs/en/sub-agents#control-subagent-capabilities).
## AskUserQuestion tool behavior
Claude uses `AskUserQuestion` to ask you multiple-choice questions when it needs a decision or a clarification. Answer by picking an option, or type your own text through the `Other` row or the notes field.
When you answer by typing your own text, Claude Code relays the answer with neutral wording so Claude follows what you wrote, including a request to wait or explain first.
### Question auto-continue timeout
Questions stay open until you answer them. If you want a question you leave unanswered to eventually close and let Claude continue without you, set the [`askUserQuestionTimeout`](/docs/en/settings#available-settings) setting to `60s`, `5m`, or `10m`, either in your user `settings.json` or from the **Question auto-continue timeout** row in `/config`.
After a question sits that long with no input, the dialog closes on its own: it submits any options you'd already selected and tells Claude you may be away from your keyboard, so Claude proceeds on its own judgment and can re-ask later. You see a countdown for the last 20 seconds. Press any key to restart the timer; on terminals that report focus, switching to the window restarts it too.
The timeout applies only to `AskUserQuestion`'s multiple-choice questions; permission prompts, including plan approval, never auto-resolve on idle.
## Bash tool behavior
The Bash tool runs each command in a separate process.
### What persists between commands
* When Claude runs `cd` in the main session, the new working directory carries over to later Bash commands as long as it stays inside the project directory or an [additional working directory](/docs/en/permissions#working-directories) you added with `--add-dir`, `/add-dir`, or `additionalDirectories` in settings. Subagent sessions never carry over working directory changes.
* If `cd` lands outside those directories, Claude Code resets to the project directory and appends `Shell cwd was reset to <dir>` to the tool result.
* To disable this carry-over so every Bash command starts in the project directory, set `CLAUDE_BASH_MAINTAIN_PROJECT_WORKING_DIR=1`.
* Environment variables don't persist. An `export` in one command won't be available in the next.
* Aliases and shell functions defined in your shell startup file are available. At session start, Claude Code sources `~/.zshrc`, `~/.bashrc`, or `~/.profile` depending on your shell, captures the resulting aliases, functions, and shell options, and applies them to every Bash command.
Activate your virtualenv or conda environment before launching Claude Code. To make environment variables persist across Bash commands, set [`CLAUDE_ENV_FILE`](/docs/en/env-vars) to a shell script before launching Claude Code, or use a [SessionStart hook](/docs/en/hooks#persist-environment-variables) to populate it dynamically.
### Timeout and output limits
Each command runs under a timeout, and Claude manages it: when it wants longer than the default for a command, it passes the `timeout` parameter with that call — you never set a per-command timeout. Two [environment variables](/docs/en/env-vars) bound what Claude gets:
* `BASH_DEFAULT_TIMEOUT_MS` — the default when Claude passes no timeout; two minutes out of the box
* `BASH_MAX_TIMEOUT_MS` — with the default, sets the ceiling that caps whatever Claude requests: the effective ceiling is the larger of the two, ten minutes out of the box
#### Output limits
Claude Code streams a command's output to a working file as the command runs; a command whose output passes 5 GB is killed. When the command finishes, Claude Code reads the output back from that file, up to the read-back window described below. How much of the output reaches Claude inline depends on whether Claude Code treats the result as a failure:
| Result | What Claude gets |
| :------ | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Valid | Inline up to roughly 30,000 characters; past that, the path of a file saved to the session directory, truncated past 64 MiB, plus a short preview from the start, and Claude reads or searches the file when it needs the rest |
| Failure | Inline up to roughly 10,000 characters; past that, a head-and-tail excerpt of that size cut from the read-back window, with no file path |
A command that exits 1 counts as a valid result for the Bash tool only when Claude Code recognizes exit code 1 as a benign outcome for that command: `grep`, `rg`, `egrep`, `fgrep`, `find`, `diff`, `test`, and `[`, plus `git diff` and `git grep`. Every other command that exits 1 counts as a failure, even when exit 1 is a benign informational outcome: no matches for `pgrep` and `jq -e`, files that differ for `cmp`.
[`BASH_MAX_OUTPUT_LENGTH`](/docs/en/env-vars) sets how many characters of output Claude Code reads back from the working file into a command's result: 30,000 by default, up to a hard ceiling of 150,000. Raise it when your commands routinely overflow that window, such as a verbose build or a full test-suite log. Raising it enlarges the read-back window, and the window a failing command's excerpt is cut from. It does not raise the inline ceilings above: a valid result over roughly 30,000 characters arrives as a file path plus preview regardless of this variable.
### Background commands
For long-running processes such as dev servers or watch builds, Claude can set `run_in_background: true` to start the command as a background task and continue working while it runs. List and stop background tasks with `/tasks`. When a [subagent running in the foreground](/docs/en/sub-agents#run-subagents-in-foreground-or-background) started the command, Claude Code ends it when that subagent gives its final response. Commands started by the main conversation or by a background subagent keep running. In non-interactive mode with the `-p` flag, [background tasks end shortly after the run's final result](/docs/en/headless#background-tasks-at-exit).
When a command reaches its timeout without finishing, Claude Code moves it to the background instead of stopping it. Claude keeps working while the command continues. Claude Code applies the same lifetime rules to a moved command as to any other background command, so it still ends a foreground subagent's command at that subagent's final response. Setting [`CLAUDE_CODE_DISABLE_BACKGROUND_TASKS=1`](/docs/en/env-vars#variables) disables auto-backgrounding along with the rest of the background task functionality.
Claude Code never auto-backgrounds three kinds of command. It stops them at the timeout instead:
* A command that starts with `sleep`.
* A command that runs `git` anywhere in it.
* A compound command Claude Code can't fully parse into simple commands.
The result of a command moved to the background states what happened:
* When the timeout triggers the move, the result reports it explicitly: `Command did not complete within its 120s timeout and was moved to the background`, with the seconds matching the timeout that applied, followed by the task ID and the path of the file the output is being written to.
* A `cd`, `pushd`, `popd`, or `chdir` inside a command that is moved to the background never carries over: the result states `Session cwd remains <dir>; directory changes made by the backgrounded command do not apply to subsequent commands.`, so Claude doesn't act on a directory change that didn't happen.
## Edit tool behavior
The Edit tool performs exact string replacement. It takes an `old_string` and a `new_string` and replaces the first with the second. It doesn't use regex or fuzzy matching.
Three checks must pass for an edit to apply. Before any of them, a path matched by a [`Read` deny rule](/docs/en/permissions#tool-specific-permission-rules) is refused, including creating a new file there. The refusal requires Claude Code v2.1.208 or later.
* **Read-before-edit**: Claude reads the file in the current conversation before editing it, and a read cut short with a [`PARTIAL view` notice](#read-tool-behavior) doesn't count. Claude Opus 4.6, Claude Haiku 4.5, and older models always require the read. Newer models can edit an unread file when reading it wouldn't need a permission prompt and the Read tool is available.
* **Match**: `old_string` must appear in the file exactly as written. A single character of whitespace or indentation difference is enough to miss.
* **Uniqueness**: `old_string` must appear exactly once. When it appears more than once, Claude either supplies a longer string with enough surrounding context to pin down one occurrence, or sets `replace_all: true` to replace them all.
A file that changed on disk after Claude last read it can still be edited when `old_string` matches the current content exactly and unambiguously and Claude Code can read the file without prompting. Matching against the file's current content keeps this safe, and the result notes that the file carries other changes so Claude re-reads it before edits that depend on surrounding content. In any other case, such as a stale `old_string` or one that matches more than once without `replace_all`, Claude reads the file again before editing. The relaxed handling of unread and changed files requires Claude Code v2.1.208 or later; before that, Claude Code refused any edit to a file it hadn't read in the conversation or that changed on disk after the read.
Viewing a file with Bash also satisfies the read-before-edit requirement when the command is `cat`, `head`, `tail`, `sed -n 'X,Yp'`, `grep`, `egrep`, or `fgrep` on a single file with no pipes or redirects. Piped output and other Bash commands don't count toward the read-before-edit check.
This affects edit eligibility only, not permissions. [Read and Edit deny rules](/docs/en/permissions#tool-specific-permission-rules) also apply to file commands Claude Code recognizes in Bash, such as `cat`, `head`, `tail`, `sed`, and `grep`, but not to arbitrary subprocesses that read or write files indirectly, like a Python or Node script that opens files itself. The set of commands recognized for deny rules is not the same as the read-before-edit list above: for example, `egrep` and `fgrep` count for read-before-edit but are not checked against Read deny rules. For OS-level enforcement that covers every process, [enable the sandbox](/docs/en/sandboxing).
## EndConversation tool behavior
The EndConversation tool ends the current session. Claude uses it only in two situations:
* as a last resort against sustained abusive input, after attempts to redirect the conversation have failed and after a clear warning in an earlier message
* when you explicitly ask to see the tool demonstrated and confirm that you want the session to end
General frustration, profanity, or a task going badly don't qualify, and neither do requests for harmful content, which Claude declines instead of ending the session. Claude Code follows the same approach as claude.ai, which can [end a rare subset of chats](https://www.anthropic.com/research/end-subset-conversations).
After Claude ends an interactive session, the session locks. New prompts and most commands return `Claude ended this conversation. Start a new session (or /clear) to continue.`, and only `/clear`, `/resume`, `/help`, `/exit`, and `/feedback` still run. Claude Code records the end in the session's transcript, so resuming an ended session restores the lock; the session's history isn't deleted.
Resuming an ended session in [non-interactive mode](/docs/en/headless) with the `-p` flag errors and exits with code 1, so a script doesn't read the ended run as a success.
The tool never prompts for permission, and [PreToolUse hooks](/docs/en/hooks#pretooluse) don't run for it. While any other tool remains, you can't block it either: [deny and ask rules](/docs/en/permissions#tool-specific-permission-rules) naming `EndConversation` have no effect, and neither `--disallowedTools` nor a `--tools` list can remove it. The exemption is deliberate: the tool does nothing except end the conversation, never reading or modifying files or data, and a safeguard of this kind holds only if the session it applies to can't turn it off. When your deny rules remove every other tool and also match `EndConversation`, as `"*"` does, Claude Code removes it too rather than leaving it as the only tool, unless an allow rule names `EndConversation` explicitly. A deny list that removes every other tool without matching `EndConversation` leaves it in place.
[Subagents](/docs/en/sub-agents) never get the tool. Background tasks that share the main conversation's tool list see it, but calling it there ends nothing.
The tool appears only when all of the following hold:
* **Version**: Claude Code v2.1.213 or later.
* **Model**: the session's model is Claude Opus 4.8, Claude Sonnet 5, Claude Fable 5, or a later version of one of those families.
* **Surface**: an interactive terminal session, including a `claude` session in an IDE's integrated terminal, which is how the [JetBrains plugin](/docs/en/jetbrains) runs it. Other surfaces don't include the tool, such as:
* non-interactive `-p` runs
* sessions through the [Agent SDK](/docs/en/agent-sdk/overview) TypeScript and Python packages
* the [VS Code extension](/docs/en/vs-code) panel, which bundles its own CLI
* [GitHub Actions](/docs/en/github-actions)
* [Claude Code on the web](/docs/en/claude-code-on-the-web)
* **Startup mode**: not a [`--bare`](/docs/en/headless#start-faster-with-bare-mode) session. Bare mode loads only shell and file tools, so the tool is never registered there.
* **Provider**: not available on [Amazon Bedrock](/docs/en/amazon-bedrock), [Claude Platform on AWS](/docs/en/claude-platform-on-aws), [Google Cloud's Agent Platform](/docs/en/google-vertex-ai), or [Microsoft Foundry](/docs/en/microsoft-foundry), or on sessions signed in through a [cloud gateway](/docs/en/claude-apps-gateway).
## Glob tool behavior
The Glob tool finds files by name pattern. It supports standard glob syntax including `**` for recursive directory matching:
* `**/*.js` matches all `.js` files at any depth
* `src/**/*.ts` matches all `.ts` files under `src/`
* `*.{json,yaml}` matches `.json` and `.yaml` files in the current directory
Results are sorted by modification time and capped at 100 files. If the cap is hit, Claude sees a truncation flag in the result and can narrow the pattern.
Glob doesn't respect `.gitignore` by default, so it finds gitignored files alongside tracked ones. This differs from [Grep](#grep-tool-behavior), which skips gitignored files. To make Glob respect `.gitignore`, set `CLAUDE_CODE_GLOB_NO_IGNORE=false` before launching Claude Code.
A `pattern` or `path` value that contains a null byte returns an error asking Claude to remove it. 
## Grep tool behavior
The Grep tool searches file contents for patterns. Where [Glob](#glob-tool-behavior) finds files by name, Grep finds lines inside them.
Grep is built on [ripgrep](https://github.com/BurntSushi/ripgrep) and uses ripgrep's regex syntax, not POSIX grep. Patterns that include regex metacharacters need escaping. For example, finding `interface{}` in Go code takes the pattern `interface\{\}`.
A pattern, glob, or file type that ripgrep rejects returns an error that includes ripgrep's diagnostic, so Claude can correct the input and search again. Before v2.1.208, Claude Code reported a rejected input as `No files found` instead of an error, even when the searched-for text existed in the target files.
Three output modes control what comes back:
* `files_with_matches`: file paths only, no line content. This is the default.
* `content`: matching lines with file and line number. When the tool's `offset` parameter points past the last match for a pattern that has matches, Grep returns `No entries at this offset`, so Claude widens or resets the offset instead of concluding the pattern doesn't match.
* `count`: match count per file, followed by a total across all matching files. The total covers every match even when the tool's `head_limit` or `offset` parameters truncate the listed per-file entries. Before v2.1.208, the total only summed the listed entries.
Claude can scope results by file with the `glob` parameter, such as `**/*.tsx`, or by language with the `type` parameter, such as `py` or `rust`. By default, patterns match within a single line. Claude can set `multiline: true` to match across line boundaries.
Grep respects `.gitignore`, so gitignored files are skipped. To search a gitignored file, Claude passes its path directly.
## LSP tool behavior
The LSP tool gives Claude code intelligence from a running language server. After each file edit, it automatically reports type errors and warnings so Claude can fix issues without a separate build step. Claude can also call it directly to navigate code:
* Jump to a symbol's definition
* Find all references to a symbol
* Get type information at a position
* List symbols in a file
* Search for a symbol by name across the workspace
* Find implementations of an interface
* Trace call hierarchies
The tool is inactive until you install a [code intelligence plugin](/docs/en/discover-plugins#code-intelligence) for your language. The plugin bundles the language server configuration, and you install the server binary separately.
## Monitor tool
The Monitor tool lets Claude watch something in the background and react when it changes, without pausing the conversation. Ask Claude to:
* Tail a log file and flag errors as they appear
* Poll a PR or CI job and report when its status changes
* Watch a directory for file changes
* Track output from any long-running script you point it at
* Connect to a WebSocket feed and report each message as it arrives
For most watches, Claude writes a small script, runs it in the background, and receives each output line as it arrives. For a server that already pushes events, Claude can open a [WebSocket](#websocket-source) instead of running a script.
You keep working in the same session and Claude interjects when an event arrives. Stop a monitor by asking Claude to cancel it or by ending the session.
When Monitor runs a command, it uses the same [permission rules as Bash](/docs/en/permissions#tool-specific-permission-rules), so `allow` and `deny` patterns you have set for Bash apply here too. The [WebSocket source](#websocket-source) has its own approval prompt.
The tool is not available on Amazon Bedrock, Google Cloud's Agent Platform, or Microsoft Foundry. It is also not available when `DISABLE_TELEMETRY` or `CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC` is set.
Plugins can declare monitors that start automatically when the plugin is active, instead of asking Claude to start them. See [plugin monitors](/docs/en/plugins-reference#monitors).
### WebSocket source
<Note>
The WebSocket source requires Claude Code v2.1.195 or later.
Cut at 300 lines. The page has the rest.
troubleshoot-install First recorded · 1053 lines, first recorded
# Troubleshoot installation and login ## Find your error ## Run diagnostic checks ### Check network connectivity ### Verify your PATH ### Check for conflicting installations ### Check directory permissions ### Verify the binary works ## Common installation issues ### Install script returns HTML instead of a shell script ### `command not found: claude` after installation ### `curl: (56) Failure writing output to destination` ### Homebrew cask unavailable or outdated ### TLS or SSL connection errors ### `Failed to fetch version from downloads.claude.ai` ### Wrong install command on Windows ### `The process cannot access the file` during Windows install ### Install killed on low-memory Linux servers ### Install hangs in Docker ### `claude update` or `claude doctor` hangs ### Claude Desktop overrides the `claude` command on Windows ### Claude Code on Windows requires either Git for Windows (for bash) or PowerShell ### Claude Code does not support 32-bit Windows ### Linux musl or glibc binary mismatch ### `Illegal instruction` ### `dyld: cannot load` on macOS ### `Exec format error` on WSL1 ### npm install errors in WSL ### Permission errors during installation ### Native binary not found after npm install ## Login and authentication ### Reset your login ### OAuth error: Invalid code ### 403 Forbidden after login ### This organization has been disabled with an active subscription ### OAuth login fails in WSL2, SSH, or containers ### Not logged in or token expired ### Bedrock, Agent Platform, or Foundry credentials not loading ## Still stuck
The first capture of this source. The page was already there, and this is what it said.
# Troubleshoot installation and login
> Fix command not found, PATH, permission, network, and authentication errors when installing or signing in to Claude Code.
If installation fails or you can't sign in, find your error below. For runtime issues after Claude Code is working, see [Troubleshooting](/docs/en/troubleshooting). For configuration problems such as settings not applying or hooks not firing, see [Debug your configuration](/docs/en/debug-your-config).
## Find your error
Match the error message or symptom you're seeing to a fix:
| What you see | Solution |
| :--------------------------------------------------------------------------------------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------- |
| `command not found: claude` or `'claude' is not recognized` | [Fix your PATH](#command-not-found-claude-after-installation) |
| `syntax error near unexpected token '<'` | [Install script returns HTML](#install-script-returns-html-instead-of-a-shell-script) |
| `curl: (22) The requested URL returned error: 403` | [Install script returned 403](#install-script-returns-html-instead-of-a-shell-script) |
| `curl: (23)` or `curl: (56) Failure writing output to destination` | [Check connectivity or use an alternative installer](#curl-56-failure-writing-output-to-destination) |
| `Killed` during install on Linux, or `Installation was killed before it could finish (exit code 137)` | [Free memory or add swap space](#install-killed-on-low-memory-linux-servers) |
| `TLS connect error` or `SSL/TLS secure channel` | [Update CA certificates](#tls-or-ssl-connection-errors) |
| `Failed to fetch version` or can't reach download server | [Check network and proxy settings](#check-network-connectivity) |
| `irm is not recognized` or `&& is not valid` | [Use the right command for your shell](#wrong-install-command-on-windows) |
| `Cask 'claude-code' is unavailable: No Cask with this name exists` | [Update Homebrew](#homebrew-cask-unavailable-or-outdated) |
| `'bash' is not recognized as the name of a cmdlet` | [Use the Windows installer command](#wrong-install-command-on-windows) |
| `A parameter cannot be found that matches parameter name 'fsSL'` | [Use the Windows installer command](#wrong-install-command-on-windows) |
| `Claude Code on Windows requires either Git for Windows (for bash) or PowerShell` | [Install a shell](#claude-code-on-windows-requires-either-git-for-windows-for-bash-or-powershell) |
| `Claude Code does not support 32-bit Windows` | [Open Windows PowerShell, not the x86 entry](#claude-code-does-not-support-32-bit-windows) |
| `The process cannot access the file ... because it is being used by another process` | [Clear the downloads folder and retry](#the-process-cannot-access-the-file-during-windows-install) |
| `Error loading shared library` | [Wrong binary variant for your system](#linux-musl-or-glibc-binary-mismatch) |
| `Illegal instruction` | [Architecture or CPU instruction set mismatch](#illegal-instruction) |
| `cannot execute binary file: Exec format error` in WSL | [WSL1 native-binary regression](#exec-format-error-on-wsl1) |
| PowerShell installer completes but `claude` is not found or shows an old version | [Add the install directory to your PATH](#verify-your-path), then open a new terminal |
| `dyld: Symbol not found`, `dyld: cannot load`, or `Abort trap` on macOS | [Binary incompatibility](#dyld-cannot-load-on-macos) |
| `claude update` hangs after `Checking for updates`, or `claude doctor` hangs with no output | [Move the directory at a shell config path](#claude-update-or-claude-doctor-hangs) |
| `Invoke-Expression` or `iex` parse errors quoting HTML tags or CSS, or `ParserError` with `ParseException` | [Install script returns HTML](#install-script-returns-html-instead-of-a-shell-script) |
| `running scripts is disabled on this system` or `PSSecurityException` | [Allow the npm shims to run](#running-scripts-is-disabled-on-this-system) |
| `Error: claude native binary not installed` | [Complete the npm install](#native-binary-not-found-after-npm-install) |
| `npm error code ENOTEMPTY` during update or reinstall | [Remove the leftover package directory](#npm-enotempty-during-update-or-reinstall) |
| On Windows, the install command prints script text and nothing installs | [Run the complete install command](#wrong-install-command-on-windows) |
| `App unavailable in region` | Claude Code is not available in your country. See [supported countries](https://www.anthropic.com/supported-countries). |
| `unable to get local issuer certificate` | [Configure corporate CA certificates](#tls-or-ssl-connection-errors) |
| `OAuth error` or `403 Forbidden` | [Fix authentication](#login-and-authentication) |
| `Unable to connect to Anthropic services` during setup | See [Unable to connect to Anthropic services](/docs/en/errors#unable-to-connect-to-anthropic-services) in the Error reference |
| `Could not load the default credentials` or `Could not load credentials from any providers` | [Amazon Bedrock, Google Cloud's Agent Platform, or Microsoft Foundry credentials](#bedrock-agent-platform-or-foundry-credentials-not-loading) |
| `ChainedTokenCredential authentication failed` or `CredentialUnavailableError` | [Amazon Bedrock, Google Cloud's Agent Platform, or Microsoft Foundry credentials](#bedrock-agent-platform-or-foundry-credentials-not-loading) |
| `API Error: 500`, `529 Overloaded`, `429`, or other 4xx and 5xx errors not listed above | See the [Error reference](/docs/en/errors) |
If your issue isn't listed, work through the diagnostic checks below to narrow down the cause.
<Tip>
If you'd rather skip the terminal entirely, the [Claude Code Desktop app](/docs/en/desktop-quickstart) lets you install and use Claude Code through a graphical interface. Download it for [macOS](https://claude.ai/api/desktop/darwin/universal/dmg/latest/redirect?utm_source=claude_code\&utm_medium=docs) or [Windows](https://claude.com/download?utm_source=claude_code\&utm_medium=docs) and start coding without any command-line setup. On Linux, install the app with apt by following the [Linux install instructions](/docs/en/desktop-linux).
</Tip>
## Run diagnostic checks
### Check network connectivity
The installer downloads from `downloads.claude.ai`. Verify you can reach it:
<Tabs>
<Tab title="macOS/Linux">
```bash theme={null}
curl -sI https://downloads.claude.ai/claude-code-releases/latest
```
</Tab>
<Tab title="Windows PowerShell">
```powershell theme={null}
curl.exe -sI https://downloads.claude.ai/claude-code-releases/latest
```
PowerShell aliases `curl` to `Invoke-WebRequest`, which rejects the `-sI` flags, so call `curl.exe` explicitly.
</Tab>
</Tabs>
You reached the server if the first line shows a `200` status. You see `HTTP/2 200` on macOS and Linux, and `HTTP/1.1 200 OK` from the `curl.exe` included with Windows. Other results point to the cause:
* `403`: usually a proxy or network filter blocking the host, or Claude Code is [not available in your region](https://www.anthropic.com/supported-countries)
* `5xx`: usually a temporary service issue; wait a few minutes and retry
If you see no output, `Could not resolve host`, or a connection timeout, your network is blocking the connection. Common causes:
* Corporate firewalls or proxies blocking `downloads.claude.ai`
* Regional network restrictions: try a VPN or alternative network
* TLS/SSL issues: update your system's CA certificates, or check if `HTTPS_PROXY` is configured
If you're behind a corporate proxy, set `HTTPS_PROXY` and `HTTP_PROXY` to your proxy's address before installing. Ask your IT team for the proxy URL if you don't know it, or check your browser's proxy settings.
This example sets both proxy variables, then runs the installer through your proxy:
<Tabs>
<Tab title="macOS/Linux">
```bash theme={null}
export HTTP_PROXY=http://proxy.example.com:8080
export HTTPS_PROXY=http://proxy.example.com:8080
curl -fsSL https://claude.ai/install.sh | bash
```
</Tab>
<Tab title="Windows PowerShell">
```powershell theme={null}
$env:HTTP_PROXY = 'http://proxy.example.com:8080'
$env:HTTPS_PROXY = 'http://proxy.example.com:8080'
irm https://claude.ai/install.ps1 | iex
```
</Tab>
</Tabs>
### Verify your PATH
If installation succeeded but you get a `command not found` or `not recognized` error when running `claude`, the install directory isn't in your PATH. Your shell searches for programs in directories listed in PATH, and the installer places `claude` at `~/.local/bin/claude` on macOS/Linux or `%USERPROFILE%\.local\bin\claude.exe` on Windows.
<Note>
The [VS Code extension](/docs/en/vs-code) does not place `claude` at this location. It bundles a private copy of the CLI inside the extension directory for its own chat panel and does not add it to PATH. If you have only installed the extension, `~/.local/bin/claude` will not exist. Run the [standalone install](/docs/en/setup) to use `claude` from a terminal, then continue below.
</Note>
Check if the install directory is in your PATH by listing your PATH entries and filtering for `local/bin`:
<Tabs>
<Tab title="macOS/Linux">
```bash theme={null}
echo $PATH | tr ':' '\n' | grep -Fx "$HOME/.local/bin"
```
If this prints `/Users/you/.local/bin` or `/home/you/.local/bin`, the directory is in your PATH and you can skip to [Check for conflicting installations](#check-for-conflicting-installations). If there's no output, add it to your shell configuration.
For Zsh, the default on macOS:
```bash theme={null}
echo 'export PATH="$HOME/.local/bin:$PATH"' >> ~/.zshrc
source ~/.zshrc
```
For Bash, the default on most Linux distributions:
```bash theme={null}
echo 'export PATH="$HOME/.local/bin:$PATH"' >> ~/.bashrc
source ~/.bashrc
```
Alternatively, close and reopen your terminal.
For other shells such as fish or Nushell, add `~/.local/bin` to your PATH using your shell's own configuration syntax, then restart your terminal.
Verify the fix worked:
```bash theme={null}
claude --version
```
</Tab>
<Tab title="Windows PowerShell">
```powershell theme={null}
$env:PATH -split ';' | Select-String '\.local\\bin'
```
If there's no output, add the install directory to your User PATH:
```powershell theme={null}
$currentPath = [Environment]::GetEnvironmentVariable('PATH', 'User')
[Environment]::SetEnvironmentVariable('PATH', "$currentPath;$env:USERPROFILE\.local\bin", 'User')
```
Restart your terminal for the change to take effect.
Verify the fix worked:
```powershell theme={null}
claude --version
```
</Tab>
<Tab title="Windows CMD">
```batch theme={null}
echo %PATH% | findstr /i "local\bin"
```
If there's no output, open System Settings, go to Environment Variables, and add `%USERPROFILE%\.local\bin` to your User PATH variable. Restart your terminal.
Verify the fix worked:
```batch theme={null}
claude --version
```
</Tab>
</Tabs>
### Check for conflicting installations
Multiple Claude Code installations can cause version mismatches or unexpected behavior. Check what's installed:
<Tabs>
<Tab title="macOS/Linux">
List all `claude` binaries found in your PATH:
```bash theme={null}
which -a claude
```
If this prints nothing, no `claude` is on your PATH yet. Go back to [Verify your PATH](#verify-your-path).
Check the three locations a `claude` binary can come from. `~/.local/bin/claude` is the native installer, `~/.claude/local/` is a legacy local npm install created by older versions of Claude Code, and the npm global list shows a `-g` install:
```bash theme={null}
ls -la ~/.local/bin/claude
```
A native install shows a symlink into `~/.local/share/claude/versions/`. A script or a symlink you created yourself at this path is a custom launcher, which [auto-update leaves in place](/docs/en/setup#auto-updates).
If either `ls` command prints `No such file or directory`, that's not an error. It means nothing is installed at that location, so move on to the next check.
```bash theme={null}
ls -la ~/.claude/local/
```
```bash theme={null}
npm -g ls @anthropic-ai/claude-code 2>/dev/null
```
</Tab>
<Tab title="Windows PowerShell">
List all `claude` binaries found in your PATH:
```powershell theme={null}
where.exe claude
```
Check whether the native installer placed a binary:
```powershell theme={null}
Test-Path "$env:USERPROFILE\.local\bin\claude.exe"
```
</Tab>
</Tabs>
If you find multiple installations, keep only one. The native install at `~/.local/bin/claude` on macOS/Linux or `%USERPROFILE%\.local\bin\claude.exe` on Windows is recommended. Remove the extras:
Uninstall an npm global install:
```bash theme={null}
npm uninstall -g @anthropic-ai/claude-code
```
Remove the legacy local npm install:
<Tabs>
<Tab title="macOS/Linux">
```bash theme={null}
rm -rf ~/.claude/local
```
</Tab>
<Tab title="Windows PowerShell">
```powershell theme={null}
Remove-Item -Recurse -Force "$env:USERPROFILE\.claude\local"
```
</Tab>
</Tabs>
Remove a Homebrew install on macOS. If you installed the `claude-code@latest` cask, substitute that name:
```bash theme={null}
brew uninstall --cask claude-code
```
Remove a WinGet install on Windows:
```powershell theme={null}
winget uninstall Anthropic.ClaudeCode
```
### Check directory permissions
The installer needs write access to `~/.local/bin/` and `~/.claude/` on macOS and Linux. On Windows the install location is under `%USERPROFILE%`, which is writable by your user by default, so this section rarely applies there.
Check whether the directories are writable:
```bash theme={null}
test -w ~/.local/bin && echo "writable" || echo "not writable"
test -w ~/.claude && echo "writable" || echo "not writable"
```
If either directory isn't writable, create the install directory and set your user as the owner:
```bash theme={null}
sudo mkdir -p ~/.local/bin
sudo chown -R $(whoami) ~/.local
```
### Verify the binary works
If `claude --version` prints a version but `claude` crashes or hangs on startup, run these checks to narrow down the cause. If `claude --version` says command not found, go to [Verify your PATH](#verify-your-path) first; the commands below assume `claude` is on your PATH.
Confirm the binary exists and is executable:
<Tabs>
<Tab title="macOS/Linux">
```bash theme={null}
ls -la "$(command -v claude)"
```
</Tab>
Cut at 300 lines. The page has the rest.
troubleshooting First recorded · 153 lines, first recorded
# Troubleshooting ## Performance and stability ### High CPU or memory usage ### Large tables are cut off in the terminal ### Auto-compaction stops with a thrashing error ### Command hangs or freezes ### Garbled or corrupted text in an editor's integrated terminal ### Search and discovery issues ### Slow or incomplete search results on WSL ## Get more help
The first capture of this source. The page was already there, and this is what it said.
# Troubleshooting
> Fix high CPU or memory usage, hangs, auto-compact thrashing, and search problems in Claude Code, and find the right page for other issues.
This page covers performance, stability, and search problems once Claude Code is running. For other issues, start with the page that matches where you're stuck:
| Symptom | Go to |
| :--------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------- |
| `command not found`, install fails, PATH issues, `EACCES`, TLS errors | [Troubleshoot installation and login](/docs/en/troubleshoot-install) |
| Update or install download fails with `The connection dropped while downloading the update` or `aborted` | [Error reference](/docs/en/errors#the-connection-dropped-while-downloading-the-update) |
| Login loops, OAuth errors, `403 Forbidden`, "organization disabled", Amazon Bedrock, Google Cloud's Agent Platform, or Microsoft Foundry credentials | [Troubleshoot installation and login](/docs/en/troubleshoot-install#login-and-authentication) |
| Settings not applying, hooks not firing, MCP servers not loading | [Debug your configuration](/docs/en/debug-your-config) |
| `API Error: 5xx`, `529 Overloaded`, `429`, request validation errors | [Error reference](/docs/en/errors) |
| `model not found` or `you may not have access to it` | [Error reference](/docs/en/errors#theres-an-issue-with-the-selected-model) |
| VS Code extension not connecting or detecting Claude | [VS Code integration](/docs/en/vs-code#fix-common-issues) |
| `Claude Code process exited with code 1` in VS Code or an SDK app | [Error reference](/docs/en/errors#claude-code-process-exited-with-code-n) |
| JetBrains plugin or IDE not detected | [JetBrains integration](/docs/en/jetbrains#troubleshooting) |
| High CPU or memory, slow responses, hangs, search not finding files | [Performance and stability](#performance-and-stability) below |
If you're not sure which applies, run `/doctor` inside Claude Code for an automated check of your installation, settings, extensions, and context usage; it proposes fixes it can apply after you confirm. If `claude` won't start at all, run `claude doctor` from your shell instead. Run `/mcp` to check MCP server status.
## Performance and stability
These sections cover issues related to resource usage, responsiveness, and search behavior.
### High CPU or memory usage
Claude Code is designed to work with most development environments, but may consume significant resources when processing large codebases. If you're experiencing performance issues:
1. Use `/compact` regularly to reduce context size. If it returns `Not enough messages to compact.`, the conversation has too few turns to summarize; that can happen even with a full context when a single large paste filled it
2. Close and restart Claude Code between major tasks
3. Consider adding large build directories to your `.gitignore` file
4. Restart with [`claude --safe-mode`](/docs/en/cli-reference#cli-flags) to check whether a plugin, MCP server, or hook is the source. It disables all customizations for the session; if usage drops, see [Debug your configuration](/docs/en/debug-your-config#test-against-a-clean-configuration) to find which one
If memory usage stays high after these steps, run `/heapdump` to write two files to `~/Desktop`: a JavaScript heap snapshot named `<session-id>.heapsnapshot` and a memory breakdown named `<session-id>-diagnostics.json`. The command doesn't appear in the command menu; type it in full. On Linux without a Desktop folder, the files are written to your home directory.
<Warning>
The `.heapsnapshot` file contains every string in the process, including your full conversation and credentials. Don't attach it to a public issue or share it.
</Warning>
The command also prints a summary in the conversation, showing resident set size, JS heap, array buffers, and unaccounted native memory, plus any leak indicators it detected, such as a high memory growth rate or an unusually high number of open handles. The summary says whether most memory is in the JS heap, which the snapshot captures, or in native memory, which it doesn't.
Do one of two things with the output:
* **Report it**: open a [GitHub issue](https://github.com/anthropics/claude-code/issues) and attach only the `-diagnostics.json` file, which carries the statistics behind the printed summary and no conversation content or credentials
* **Investigate it yourself**: if the summary says most memory is JS heap, open the `.heapsnapshot` file in Chrome DevTools under Memory → Load and sort by retained size to see what's holding the memory
If the summary says most memory is native, the snapshot can't show it; include the summary's leak indicators in your report instead.
### Large tables are cut off in the terminal
A Markdown table with more than 200 rows renders its first 200 rows followed by a `… N more rows not shown` line. Only the display is capped: the full table stays in the conversation, and [`/copy`](/docs/en/commands) copies every row. For a table too large to read in the terminal, ask Claude to write it to a file instead. Before v2.1.208, Claude Code rendered every row, so resuming a session that contained a very large table could stall while it re-rendered.
### Auto-compaction stops with a thrashing error
If you see `Autocompact is thrashing: the context refilled to the limit...`, automatic compaction succeeded but a file or tool output immediately refilled the context window several times in a row. Claude Code stops retrying to avoid wasting API calls on a loop that isn't making progress.
To recover:
1. Ask Claude to read the oversized file in smaller chunks, such as a specific line range or function, instead of the whole file
2. Run `/compact` with a focus that drops the large output, for example `/compact keep only the plan and the diff`
3. Move the large-file work to a [subagent](/docs/en/sub-agents) so it runs in a separate context window
4. Run `/clear` if the earlier conversation is no longer needed
### Command hangs or freezes
If Claude Code seems unresponsive:
1. Press Ctrl+C to attempt to cancel the current operation
2. If unresponsive, you may need to close the terminal and restart
Restarting doesn't lose your conversation. Run `claude --resume` in the same directory to pick the session back up.
### Garbled or corrupted text in an editor's integrated terminal
If characters render as boxes, smears, or the wrong glyphs when running Claude Code in the VS Code, Cursor, or Devin Desktop integrated terminal, the terminal's GPU renderer is likely the cause. Run `/terminal-setup` inside Claude Code to set `terminal.integrated.gpuAcceleration` to `"off"`, or set it manually in your editor settings and reload the window. See [Terminal configuration](/docs/en/terminal-config) for the other settings `/terminal-setup` writes.
### Search and discovery issues
If the Search tool, `@file` mentions, custom agents, or custom skills aren't finding files, the bundled `ripgrep` binary may not run on your system. Install your platform's `ripgrep` package and tell Claude Code to use it instead:
<Tabs>
<Tab title="macOS">
```bash theme={null}
brew install ripgrep
```
</Tab>
<Tab title="Ubuntu/Debian">
```bash theme={null}
sudo apt install ripgrep
```
</Tab>
<Tab title="Alpine">
```bash theme={null}
apk add ripgrep
```
`ripgrep` is in Alpine's community repository. If `apk` reports that the package is missing, see [Alpine Linux setup](/docs/en/setup#alpine-linux-and-musl-based-distributions).
</Tab>
<Tab title="Arch">
```bash theme={null}
pacman -S ripgrep
```
</Tab>
<Tab title="Windows">
```powershell theme={null}
winget install BurntSushi.ripgrep.MSVC
```
</Tab>
</Tabs>
Then set `USE_BUILTIN_RIPGREP` to `0`, either in your shell [environment](/docs/en/env-vars) or in the `env` block of your [`settings.json`](/docs/en/settings#available-settings):
```json theme={null}
{
"env": {
"USE_BUILTIN_RIPGREP": "0"
}
}
```
To confirm the switch took effect, run `claude doctor` in your terminal and check that the Search line shows the path of your system ripgrep instead of `OK (bundled)`.
### Slow or incomplete search results on WSL
Disk read performance penalties when [working across file systems on WSL](https://learn.microsoft.com/en-us/windows/wsl/filesystems) may result in fewer-than-expected matches when using Claude Code on WSL. Search still functions, but returns fewer results than on a native filesystem.
<Note>
`claude doctor` shows Search as OK in this case.
</Note>
**Solutions:**
1. **Submit more specific searches**: reduce the number of files searched by specifying directories or file types: "Search for JWT validation logic in the auth-service package" or "Find use of md5 hash in JS files".
2. **Move project to Linux filesystem**: if possible, ensure your project is located on the Linux filesystem (`/home/`) rather than the Windows filesystem (`/mnt/c/`).
3. **Use native Windows instead**: consider running Claude Code natively on Windows instead of through WSL, for better file system performance.
## Get more help
If you're experiencing issues not covered here:
1. Run `/doctor` for a setup checkup and `/mcp` to check MCP server status
2. Use the `/feedback` command within Claude Code to report problems directly to Anthropic
3. Check the [GitHub repository](https://github.com/anthropics/claude-code) for known issues
4. Ask Claude directly about its capabilities and features. Claude has built-in access to its documentation.
For account, billing, or subscription problems, contact Anthropic support instead: sign in at [claude.ai](https://claude.ai) (Console users: [platform.claude.com](https://platform.claude.com)), click your initials in the lower left, and select **Get help**. See [How to get support](https://support.claude.com/en/articles/9015913-how-to-get-support) for the full flow, including who can reach a human agent on each plan.
ultrareview First recorded · 172 lines, first recorded
# Find bugs with ultrareview ## Run ultrareview from the CLI ### Review against a different base ### Review a pull request ### Post findings to the pull request ### Pass a request in plain words ### Diff limits and fallbacks ## Pricing and free runs ## Track a running review ## Run ultrareview non-interactively ## How ultrareview compares to /code-review ## Related resources
The first capture of this source. The page was already there, and this is what it said.
# Find bugs with ultrareview
> Run a deep, multi-agent code review in the cloud with /code-review ultra to find and verify bugs before you merge.
<Note>
Ultrareview is a research preview feature. The feature, pricing, and availability may change based on feedback. The command is `/code-review ultra`. When ultrareview is available to your account, `/ultrareview` is an alias.
</Note>
Ultrareview is a deep code review that runs on Claude Code on the web infrastructure. When you run `/code-review ultra`, Claude Code launches a fleet of reviewer agents in a remote sandbox to find bugs in your branch or pull request.
Compared to a local `/code-review`, ultrareview offers:
* **Higher signal**: every reported finding is independently reproduced and verified, so the results focus on real bugs rather than style suggestions
* **Broader coverage**: a larger fleet of reviewer agents explores the change in parallel, which surfaces issues that a local review can miss
* **No local resource use**: the review runs entirely in a remote sandbox, so your terminal stays free for other work while it runs
Ultrareview requires authentication with a claude.ai account because it runs on Claude Code on the web infrastructure. If you are signed in with an API key only, run `/login` and authenticate with claude.ai first. Ultrareview is not available when using Claude Code with Amazon Bedrock, Google Cloud's Agent Platform, or Microsoft Foundry, and it is not available to organizations that have enabled Zero Data Retention. When ultrareview is not available, `/code-review ultra` runs a local review in your session instead.
## Run ultrareview from the CLI
Start a review from any git repository:
```text theme={null}
/code-review ultra
```
Without arguments, ultrareview reviews the diff between your current branch and the default branch, including uncommitted and staged changes. For a branch review, Claude Code bundles the repository state and uploads it to a remote sandbox; when you [review a pull request](#review-a-pull-request), Claude Code uploads nothing from your machine.
Before launching, Claude Code shows a confirmation dialog with the review scope, your remaining free runs, and the estimated cost; for a branch review, the scope includes the file and line count. After you confirm, the review continues in the background while you keep using your session.
The command runs only when you invoke it with `/code-review ultra`; Claude doesn't start an ultrareview on its own.
### Review against a different base
To compare against a base other than the default branch, pass the branch name. This example reviews your current branch against `develop` instead:
```text theme={null}
/code-review ultra develop
```
The base branch doesn't need to exist in your local clone; Claude Code fetches it from `origin`. If the name has a typo, Claude Code suggests the closest branch name in the error.
### Review a pull request
To review a GitHub pull request instead of a local branch, pass the PR number:
```text theme={null}
/code-review ultra 1234
```
The command also accepts `#1234`, `PR 1234`, and pasted PR URLs; a pasted URL must point to the repository in your current directory.
In PR mode, the remote sandbox clones the pull request directly from the host rather than bundling your local working tree. PR mode works with repositories on `github.com` and on [GitHub Enterprise Server](/docs/en/github-enterprise-server) instances that an Owner has connected to Claude Code.
### Post findings to the pull request
On Claude Code v2.1.227 or later, when you review a pull request on `github.com`, you can have Claude post the finished findings to the PR as a single plain comment from your own GitHub account. The comment isn't a review or an approval, and it ends with a "Generated by Claude Code" note. When you review a branch or a GitHub Enterprise Server pull request, Claude Code shows the findings in your session only.
Claude Code never posts unless you choose to on that run, and `--no-post` is the default. Posting is a choice you make for each run:
* **Interactive**: in the launch dialog, select **Run and post the findings to the PR as me**. If you add `--post` to the command, as in `/code-review ultra 1234 --post`, Claude Code preselects that choice and still asks before launching.
* **Non-interactive**: run the [`claude ultrareview` subcommand](#run-ultrareview-non-interactively) with `--post`. You consent to the post by running the subcommand with the flag, so Claude Code posts without asking. In a `claude -p '/code-review ultra'` run, Claude Code exits before the findings arrive, so it posts nothing; use the subcommand instead.
Claude Code doesn't post from your machine. It sends the findings to a session on [Claude Code on the web](/docs/en/claude-code-on-the-web), which posts the comment through the GitHub account you've connected to Claude. Posting requires the same claude.ai sign-in as the review itself. Because posting runs through Claude Code on the web, it isn't available on third-party providers or when you set [`CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC`](/docs/en/env-vars).
In an interactive session, Claude Code starts the post when the findings arrive, so keep the session open until the review finishes. Claude Code keeps the posting choice only in that session. If the session ends before the review finishes, Claude Code posts nothing, even if you resume the conversation later.
When the post can't start while your session is open, Claude tells you that nothing went to the PR and why, and the findings stay in your terminal so you can post them by hand.
### Pass a request in plain words
On Claude Code v2.1.218 or later, you can also describe what you're working on in plain words:
```text theme={null}
/code-review ultra check my auth changes
```
The review still covers your current branch, the same scope as running with no argument. Claude keeps your text as a note, shown in the launch dialog, and relates the findings to it when they arrive.
Claude Code treats your text as a note only when it has more than one word and isn't a branch name or PR reference. It reads a single word as a branch name or PR reference, so a mistyped branch name gets the closest-branch error from [Review against a different base](#review-against-a-different-base) instead of launching with a note. If your text combines a PR reference with other words, such as `check PR 123 again`, Claude Code doesn't launch either; it asks you to rerun with the PR number alone to review that PR, or without the reference to review your current branch.
<Tip>
If your repository is too large to bundle, Claude Code prompts you to use PR mode instead. Push your branch and open a draft PR, then run `/code-review ultra <PR-number>`.
</Tip>
### Diff limits and fallbacks
Ultrareview checks the diff before any review work runs and tells you when it can't review it as-is:
* **Diff too large**: a branch review can include up to 500 changed files and 8,000 changed lines by default. The exact values can change, and the [refusal](/docs/en/errors#diff-is-too-large-for-ultrareview) names the ones in effect, the size of your diff, and the files with the most changed lines. Claude Code refuses a too-large pull request the same way, naming its file and line counts but not the per-file breakdown
* **Nothing to review**: when the diff against the base is empty, Claude Code says so and suggests staging or committing local edits, or passing a different base
* **No merge base**: when your branch shares no history with the base branch, Claude Code falls back to reviewing every tracked file in the repository instead; the fallback requires a full clone and applies the same size limits. On a checkout with no branches or other refs, such as a detached HEAD created by checking out `FETCH_HEAD` after fetching a URL, Claude Code [refuses the review](/docs/en/errors#your-checkout-has-no-branches) and suggests creating a branch first
## Pricing and free runs
Ultrareview is a premium feature that bills against usage credits rather than your plan's included usage.
| Plan | Included free runs | After free runs |
| ------------------- | ------------------ | ------------------------------------------------------------------------------------------------------------ |
| Pro | 3 free runs | billed as [usage credits](https://support.claude.com/en/articles/12429409-extra-usage-for-paid-claude-plans) |
| Max | 3 free runs | billed as [usage credits](https://support.claude.com/en/articles/12429409-extra-usage-for-paid-claude-plans) |
| Team and Enterprise | none | billed as [usage credits](https://support.claude.com/en/articles/12429409-extra-usage-for-paid-claude-plans) |
* **Free runs**: the three Pro and Max runs are a one-time allotment per account and don't refresh.
* **Cost per review**: after you use the free runs, typically \$5 to \$25 in usage credits depending on the size of the change, matching the estimate the launch dialog shows before each run.
* **When a run counts**: once the cloud session starts. A review you stop early or that fails to complete still uses a free run; a paid review bills only for the portion that ran.
Because ultrareview always bills as usage credits outside the free runs, your account or organization must have usage credits turned on before you can launch a paid review. If usage credits aren't turned on, Claude Code blocks the launch, and how you turn them on depends on your billing access:
* If you can manage billing for your account, Claude Code links you to the billing settings where you can turn on usage credits.
* On Team and Enterprise plans, members without billing access send a request from the CLI asking their admin to turn on usage credits.
You can also run `/usage-credits` to check or change your usage-credits setting.
Claude Code asks you to confirm usage-credits billing once per conversation: when you start a new conversation, for example with `/clear`, Claude Code shows the confirmation again for the next paid review.
## Track a running review
A review typically takes 5 to 10 minutes. The review runs as a background task, so you can keep working in your session, start other commands, or close the terminal entirely. If you chose to [post the findings to the pull request](#post-findings-to-the-pull-request), keep the session open until the review finishes; if the session ends first, Claude Code posts nothing.
Use `/tasks` to see running and completed reviews, open the detail view for a review, or stop a review that is in progress. If you stop a review, Claude Code archives the cloud session and doesn't return partial findings. When the review finishes, the verified findings appear as a notification in your session. Each finding includes the file location and an explanation of the issue so you can ask Claude to fix it directly.
## Run ultrareview non-interactively
Use the `claude ultrareview` subcommand to start an ultrareview from CI or a script without an interactive session. The subcommand launches the same review as `/code-review ultra`, blocks until the remote review finishes, and prints the findings to stdout.
```bash theme={null}
claude ultrareview
claude ultrareview 1234
claude ultrareview origin/main
```
Without arguments, the subcommand reviews the diff between your current branch and the default branch, with the same [whole-repository fallback](#diff-limits-and-fallbacks) as `/code-review ultra` when no merge base exists. Pass a PR number to review a pull request, or a base branch to review against it; [base-branch handling](#review-against-a-different-base) matches the interactive command.
You consent to the whole-repository fallback and to the billing and terms prompt when you run the subcommand, so the run starts without waiting for input.
On Claude Code v2.1.218 or later, you can also start the cloud review by running `/code-review ultra` in a non-interactive session, for example `claude -p '/code-review ultra'`. Claude Code launches the review and prints a tracking link without waiting for the findings, unlike `claude ultrareview`, which blocks until they arrive. When the review would bill usage credits, Claude Code stops before launching and points you to `claude ultrareview`, because the billing confirmation needs an interactive session. Before v2.1.218, `/code-review ultra` in a non-interactive session ran a local review.
Progress messages and the live session URL go to stderr so stdout stays parseable. Use these flags to control the output, the timeout, and whether to post the findings:
| Flag | Description |
| --------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `--json` | Print the raw `bugs.json` payload instead of the formatted findings |
| `--timeout <minutes>` | Maximum minutes to wait for the review to finish. Defaults to 30 |
| `--post` | [Post the finished findings](#post-findings-to-the-pull-request) to the pull request as one plain comment from your GitHub account. Works on `github.com` pull request targets; on other targets, Claude Code ignores the flag and says so. Requires Claude Code v2.1.227 or later |
| `--no-post` | Don't post the findings. This is the default, and if you pass both flags, Claude Code doesn't post. Requires Claude Code v2.1.227 or later |
Running `claude ultrareview` requires the same authentication and usage credit configuration as `/code-review ultra`. The subcommand exits with code 0 when the review completes with or without findings, code 1 when the review fails to launch, the cloud session errors, or the timeout elapses, and code 130 when interrupted with Ctrl-C. The remote review keeps running if you interrupt the subcommand; follow the session URL printed to stderr to watch it in the browser.
With `--post`, the subcommand starts the post right after it prints the findings. If the run fails, times out, or you interrupt it, the subcommand posts nothing. If the review completes but the post can't start, Claude Code prints the reason to stderr, and the findings stay on stdout so you can post them by hand.
For automatic reviews on GitHub pull requests, [Code Review](/docs/en/code-review) integrates with your repository directly and posts findings as inline PR comments without a CLI step.
## How ultrareview compares to /code-review
Both reviews examine code, but you use them at different stages of your workflow.
| | `/code-review` | `/code-review ultra` |
| -------- | ------------------------------------------------------ | --------------------------------------------------------------- |
| Target | your working diff, a pull request, a branch, or a path | your working diff or a pull request |
| Runs | locally in your session | remotely in a cloud sandbox |
| Depth | scales with the effort argument | multi-agent fleet with independent verification |
| Duration | seconds to a few minutes | roughly 5 to 10 minutes |
| Cost | counts toward normal usage | free runs, then roughly \$5 to \$25 per review as usage credits |
| Best for | quick feedback while iterating | pre-merge confidence on substantial changes |
Use `/code-review` for fast feedback as you work, or pass a PR number to review a teammate's pull request before approving it. Use `/code-review ultra` before merging a substantial change when you want a deeper pass that catches issues a local review might miss.
## Related resources
* [Claude Code on the web](/docs/en/claude-code-on-the-web): learn how cloud sessions and cloud sandboxes work
* [Manage costs effectively](/docs/en/costs): track usage and set spending limits
voice-dictation First recorded · 199 lines, first recorded
# Voice dictation ## Requirements ## Enable voice dictation ## Hold to record ## Tap to record and send ## Change the dictation language ## Rebind the dictation key ## Troubleshooting ### Terminal not listed in macOS Microphone settings ## See also
The first capture of this source. The page was already there, and this is what it said.
# Voice dictation
> Speak your prompts in the Claude Code CLI with hold-to-record or tap-to-record voice dictation.
Speak your prompts instead of typing them in the Claude Code CLI. Your speech is transcribed live into the prompt input, so you can mix voice and typing in the same message. Enable dictation with `/voice`, then either hold a key while you speak or tap once to start and again to send.
Dictation also works in [agent view](/docs/en/agent-view#peek-and-reply). Hold or tap your push-to-talk key while the dispatch input or a peek-panel reply is focused to dictate to a background session.
## Requirements
Voice dictation streams your recorded audio to Anthropic's servers for transcription. Audio is not processed locally. It needs all of the following:
* **A Claude.ai account**: the speech-to-text service is only available when you authenticate with one, and is not available when Claude Code is configured to use an Anthropic API key directly, Amazon Bedrock, Google Cloud's Agent Platform, or Microsoft Foundry.
* **An organization without HIPAA compliance enabled**: `/voice` shows `Voice mode is disabled by your organization's policy` when this restriction applies.
* **A local microphone**: voice dictation does not work in remote environments such as [Claude Code on the web](/docs/en/claude-code-on-the-web) or SSH sessions.
* **WSLg, if you run Claude Code in WSL**: WSLg is included with WSL2 when installed from the Microsoft Store on Windows 10 or 11. If WSLg is not available, for example on WSL1, run Claude Code in native Windows instead.
Transcription does not consume Claude messages or tokens and does not count toward the limits shown in `/usage`. See [data usage](/docs/en/data-usage) for how Anthropic handles your data.
Audio recording uses a built-in native module on macOS, Linux, and Windows. On Linux, if the native module cannot load, Claude Code falls back to `arecord` from ALSA utils or `rec` from SoX. If neither is available, `/voice` prints an install command for your package manager.
The Claude Code [VS Code extension](/docs/en/vs-code) also supports voice dictation with the same Claude.ai account requirement. It is not available in VS Code Remote sessions, including SSH, Dev Containers, and Codespaces, because the microphone is on your local machine and the extension runs on the remote host.
## Enable voice dictation
Run `/voice` to enable dictation. The first time you enable it, Claude Code runs a microphone check. On macOS, this triggers the system microphone permission prompt for your terminal if it has never been granted.
```
/voice
Voice mode enabled (hold). Hold space to record. Dictation language: en (/config to change).
```
`/voice` accepts an optional mode argument:
| Command | Effect |
| :------------ | :-------------------------------------------- |
| `/voice` | Toggle on or off, keep the current mode |
| `/voice hold` | Enable in [hold mode](#hold-to-record) |
| `/voice tap` | Enable in [tap mode](#tap-to-record-and-send) |
| `/voice off` | Disable |
Voice dictation persists across sessions. Set it directly in your [user settings file](/docs/en/settings) instead of running `/voice`:
```json theme={null}
{
"voice": {
"enabled": true,
"mode": "tap"
}
}
```
While voice dictation is enabled, the input footer shows a `hold space to speak` hint when the prompt is empty. The hint reflects your current `voice:pushToTalk` binding and updates if you [rebind the dictation key](#rebind-the-dictation-key). The hint text is the same in both modes, and it does not appear if you have a [custom status line](/docs/en/statusline) configured.
Transcription is tuned for coding vocabulary in both modes. Common development terms like `regex`, `OAuth`, `JSON`, and `localhost` are recognized correctly, and your current project name and git branch name are added as recognition hints automatically.
## Hold to record
Hold mode is push-to-talk: recording runs while you hold the key and stops when you release it. This is the default mode.
Hold `Space` to start recording. Claude Code detects a held key by watching for rapid key-repeat events from your terminal, so there is a brief warmup before recording begins. The footer shows `keep holding…` during warmup, then switches to a live waveform once recording is active.
The first couple of key-repeat characters type into the input during warmup and are removed automatically when recording activates. A single `Space` tap still types a space, since hold detection only triggers on rapid repeat.
<Tip>
To skip the warmup, switch to [tap mode](#tap-to-record-and-send) with `/voice tap`, or [rebind to a modifier combination](#rebind-the-dictation-key) like `meta+k`. Modifier combos start recording on the first keypress.
</Tip>
Your speech appears in the prompt as you speak, dimmed until the transcript is finalized. Release `Space` to stop recording and finalize the text. The transcript is inserted at your cursor position and the cursor stays at the end of the inserted text, so you can mix typing and dictation in any order. Hold `Space` again to append another recording, or move the cursor first to insert speech elsewhere in the prompt:
```
> refactor the auth middleware to ▮
# hold space, speak "use the new token validation helper"
> refactor the auth middleware to use the new token validation helper▮
```
By default, when you release the key, Claude Code inserts the transcript and waits for you to press `Enter`. Set `"autoSubmit": true` in the `voice` settings object to send the prompt automatically when you release the key, as long as the transcript is at least three words long.
## Tap to record and send
Tap mode toggles recording with a single keypress: tap once to start, speak, then tap again to send the prompt. There is no warmup, and you don't need to keep the key held.
Enable tap mode with `/voice tap`. With the prompt input empty, tap `Space` to start recording. The footer shows a live waveform while recording. Tap `Space` again to stop.
Claude Code inserts the transcript and submits the prompt automatically when the transcript is at least three words long. Shorter transcripts are inserted but not submitted, so an accidental tap does not send a stray word.
The three-word threshold counts words for languages written without spaces. Japanese, Chinese, and Thai transcripts count individual words, so they auto-submit in tap mode and in hold mode with `autoSubmit`.
The first tap only starts recording when the prompt input is empty, so you can still type spaces normally while composing a message. The second tap stops recording regardless of input contents. Recording also stops automatically after 15 seconds of silence or two minutes total.
## Change the dictation language
Voice dictation uses the same [`language` setting](/docs/en/settings) that controls Claude's response language. If that setting is empty, dictation defaults to English. In the VS Code extension, if `language` is empty, dictation uses VS Code's `accessibility.voice.speechLanguage` setting before defaulting to English.
<Accordion title="Supported dictation languages">
| Language | Code |
| :--------- | :--- |
| Czech | `cs` |
| Danish | `da` |
| Dutch | `nl` |
| English | `en` |
| French | `fr` |
| German | `de` |
| Greek | `el` |
| Hindi | `hi` |
| Indonesian | `id` |
| Italian | `it` |
| Japanese | `ja` |
| Korean | `ko` |
| Norwegian | `no` |
| Polish | `pl` |
| Portuguese | `pt` |
| Russian | `ru` |
| Spanish | `es` |
| Swedish | `sv` |
| Turkish | `tr` |
| Ukrainian | `uk` |
</Accordion>
Set the language in `/config` or directly in settings. You can use either the [BCP 47 language code](https://en.wikipedia.org/wiki/IETF_language_tag) or the language name:
```json theme={null}
{
"language": "japanese"
}
```
If your `language` setting is not in the supported list, `/voice` warns you on enable and falls back to English for dictation. Claude's text responses are not affected by this fallback.
## Rebind the dictation key
The dictation key is bound to `voice:pushToTalk` in the `Chat` context and defaults to `Space`. The same binding controls both hold and tap modes. Rebind it in [`~/.claude/keybindings.json`](/docs/en/keybindings):
```json theme={null}
{
"bindings": [
{
"context": "Chat",
"bindings": {
"meta+k": "voice:pushToTalk",
"space": null
}
}
]
}
```
The `voice:pushToTalk` action uses one key at a time. When you bind a custom key, it replaces the default `Space` binding rather than adding a second trigger, so the `"space": null` line in this example is for clarity and can be omitted without changing behavior.
In hold mode, avoid binding a bare letter key like `v` since hold detection relies on key-repeat and the letter types into the prompt during warmup. Use `Space`, or use a modifier combination like `meta+k` to start recording on the first keypress with no warmup. Tap mode has no warmup, so most keys work.
Some keys are not delivered to terminal applications and can't be bound at all. For example, `Caps Lock` shows an error if you try to bind it. See [customize keyboard shortcuts](/docs/en/keybindings) for the full keybinding syntax and the list of reserved shortcuts.
## Troubleshooting
Common issues when voice dictation does not activate or record:
* **`Voice mode requires a Claude.ai account`**: you are authenticated with an API key or a third-party provider. Run `/login` to sign in with a Claude.ai account.
* **`Voice mode is disabled by your organization's policy`**: your organization's compliance configuration disables voice dictation, as described in [Requirements](#requirements). Contact your organization administrator to confirm whether voice dictation is available for your organization.
* **`Microphone access is denied`**: grant microphone permission to your terminal in system settings. On macOS, go to System Settings → Privacy & Security → Microphone and enable your terminal app, then run `/voice` again. On Windows, go to Settings → Privacy & security → Microphone and turn on microphone access for desktop apps, then run `/voice` again. If your terminal isn't listed in the macOS settings, see [Terminal not listed in macOS Microphone settings](#terminal-not-listed-in-macos-microphone-settings).
* **`No audio recording tool found` on Linux**: the native audio module could not load and no fallback is installed. Install SoX with the command shown in the error message, for example `sudo apt-get install sox`.
* **`Voice mode requires a microphone, but SoX could not open an audio capture device`**: SoX is installed, but the host has no audio capture device, for example a headless server or a container. Run Claude Code on a machine with a microphone. As of v2.1.195, Claude Code on Linux reports this message in that situation; earlier versions asked you to install SoX even when it was already installed.
* **`Voice mode could not find a working audio recorder in WSL`**: WSLg routes audio through PulseAudio rather than an ALSA device, so SoX needs its PulseAudio backend installed explicitly. Run `sudo apt install sox libsox-fmt-pulse`. Installing `sox` alone pulls in the ALSA backend, which cannot record on WSL because there is no `/dev/snd` device.
* **`Voice input is failing repeatedly and has been paused`**: voice dictation hit several capture failures in a row and stopped attempting new sessions until one succeeds. A failure counts whether the microphone fails to start or the recorder starts and then stops without producing any audio. This usually means the microphone or audio stack on this host can't capture audio, for example a headless server, a remote shell with no audio passthrough, or a denied microphone permission. Confirm a working input device, fix the underlying cause from the entries above, then trigger voice again. Before v2.1.202, only start-up failures counted toward the pause.
* **Nothing happens when holding `Space` in hold mode**: watch the prompt input while you hold. If spaces keep accumulating, voice dictation is likely off; run `/voice hold` to enable it. If only one or two spaces appear and then nothing, voice dictation is on but hold detection is not triggering. Hold detection requires your terminal to send key-repeat events, so it can't detect a held key if key-repeat is disabled at the OS level. Switch to tap mode with `/voice tap` to avoid the key-repeat requirement.
* **Tapping `Space` types a space instead of recording in tap mode**: the first tap only starts recording when the prompt input is empty. Clear the input first, or check that you are in tap mode by running `/voice tap`.
* **`No audio detected from microphone`**: recording started but captured silence. Confirm the correct input device is set as the system default and that its input level is not muted or near zero. On Windows, open Settings → System → Sound → Input and select your microphone. On macOS, open System Settings → Sound → Input.
* **`Voice connection failed`**: your recording never reached the transcription service because the connection failed. Check your network and try again. A recording that captures no audio reports `No audio detected from microphone` instead of this message. Before v2.1.200, a silent microphone could report a connection failure, which suggested a network problem when the actual issue was the input device.
* **`No speech detected`**: audio reached the transcription service but no words were recognized. Speak closer to the microphone, reduce background noise, and confirm your [dictation language](#change-the-dictation-language) matches the language you are speaking.
* **Transcription is garbled or in the wrong language**: dictation defaults to English. If you are dictating in another language, set it in `/config` first. See [Change the dictation language](#change-the-dictation-language).
### Terminal not listed in macOS Microphone settings
If your terminal app does not appear under System Settings → Privacy & Security → Microphone, there is no toggle you can enable. Reset the permission state for your terminal so the next `/voice` run triggers a fresh macOS permission prompt.
<Steps>
<Step title="Reset the microphone permission for your terminal">
Run `tccutil reset Microphone <bundle-id>`, replacing `<bundle-id>` with your terminal's identifier: `com.apple.Terminal` for the built-in Terminal, or `com.googlecode.iterm2` for iTerm2. For other terminals, look up the identifier with `osascript -e 'id of app "AppName"'`.
<Warning>
You can run `tccutil reset Microphone` without a bundle ID, but it revokes microphone access from every app on your Mac, including apps like Zoom or Slack. Each app will need to re-request access on next use, so don't run it during an active call.
</Warning>
</Step>
<Step title="Quit and relaunch your terminal">
macOS won't re-prompt a process that is already running. Quit the terminal app with Cmd+Q, not just close its windows, then open it again.
</Step>
<Step title="Trigger a fresh prompt">
Start Claude Code and run `/voice`. macOS prompts for microphone access; allow it.
</Step>
</Steps>
## See also
* [Customize keyboard shortcuts](/docs/en/keybindings): rebind `voice:pushToTalk` and other CLI keyboard actions
* [Configure settings](/docs/en/settings): full reference for `voice`, `language`, and other settings keys
* [Interactive mode](/docs/en/interactive-mode): keyboard shortcuts, input modes, and session controls
* [Commands](/docs/en/commands): reference for `/voice`, `/config`, and all other commands
vs-code First recorded · 574 lines, first recorded
# Use Claude Code in VS Code ## Prerequisites ## Install the extension ## Get started ## Use the prompt box ### Reference files and folders ### Resume past conversations ### Resume cloud sessions from Claude.ai ### Check account and usage ## Customize your workflow ### Choose where Claude lives ### Run multiple conversations ### Organize sessions into groups ### Switch to terminal mode ## Manage plugins ### Install plugins ### Manage marketplaces ## Automate browser tasks with Chrome ## VS Code commands and shortcuts ### Launch a VS Code tab from other tools ## Configure settings ### Extension settings ## VS Code extension vs. Claude Code CLI ### Rewind with checkpoints ### Run CLI in VS Code ### Switch between extension and CLI ### Include terminal output in prompts ### Monitor background processes ### Connect to external tools with MCP ## Work with git ### Create commits and pull requests ### Use git worktrees for parallel tasks ## Use third-party providers ## Security and privacy ### The built-in IDE MCP server ## Fix common issues ### Extension won't install ### Spark icon not visible ### Cmd+Esc does nothing on macOS ### Claude Code never responds ## Uninstall the extension ## Next steps
The first capture of this source. The page was already there, and this is what it said.
# Use Claude Code in VS Code
> Install and configure the Claude Code extension for VS Code. Get AI coding assistance with inline diffs, @-mentions, plan review, and keyboard shortcuts.
<img src="https://mintcdn.com/claude-code/-YhHHmtSxwr7W8gy/images/vs-code-extension-interface.jpg?fit=max&auto=format&n=-YhHHmtSxwr7W8gy&q=85&s=300652d5678c63905e6b0ea9e50835f8" alt="VS Code editor with the Claude Code extension panel open on the right side, showing a conversation with Claude" width="2500" height="1155" data-path="images/vs-code-extension-interface.jpg" />
The VS Code extension provides a native graphical interface for Claude Code, integrated directly into your IDE. This is the recommended way to use Claude Code in VS Code.
With the extension, you can review and edit Claude's plans before accepting them, auto-accept edits as they're made, @-mention files with specific line ranges from your selection, access conversation history, and open multiple conversations in separate tabs or windows.
## Prerequisites
Before installing, make sure you have:
* VS Code 1.94.0 or higher
* An Anthropic account: any paid Claude subscription (Pro, Max, Team, or Enterprise) or a Claude Console account works, and no API key is required. You'll [sign in](/docs/en/authentication#log-in-to-claude-code) with this account when you first open the extension. If you access Claude through a third-party provider like Amazon Bedrock or Google Cloud's Agent Platform, see [Use third-party providers](#use-third-party-providers) for setup instructions.
<Tip>
The extension bundles its own copy of the CLI (command-line interface) for the chat panel. To run `claude` in VS Code's integrated terminal, you also need the [standalone CLI install](/docs/en/setup). See [VS Code extension vs. Claude Code CLI](#vs-code-extension-vs-claude-code-cli) for details.
</Tip>
## Install the extension
Click the link for your IDE to install directly:
* [Install for VS Code](vscode:extension/anthropic.claude-code)
* [Install for Cursor](cursor:extension/anthropic.claude-code)
Or in VS Code, press `Cmd+Shift+X` (Mac) or `Ctrl+Shift+X` (Windows/Linux) to open the Extensions view, search for "Claude Code", and click **Install**.
The extension also installs in other VS Code forks like Devin Desktop or Kiro. Search for "Claude Code" in the editor's Extensions view, or install from the [Open VSX registry](https://open-vsx.org/extension/Anthropic/claude-code). If your editor can't install the extension, [install the CLI](/docs/en/quickstart) and run `claude` in its integrated terminal instead. The CLI works in any terminal.
<Note>If the extension doesn't appear after installation, restart VS Code or run "Developer: Reload Window" from the Command Palette.</Note>
## Get started
Once installed, you can start using Claude Code through the VS Code interface:
<Steps>
<Step title="Open the Claude Code panel">
Throughout VS Code, the Spark icon indicates Claude Code: <img src="https://mintcdn.com/claude-code/c5r9_6tjPMzFdDDT/images/vs-code-spark-icon.svg?fit=max&auto=format&n=c5r9_6tjPMzFdDDT&q=85&s=3ca45e00deadec8c8f4b4f807da94505" alt="Spark icon" style={{display: "inline", height: "0.85em", verticalAlign: "middle"}} width="16" height="16" data-path="images/vs-code-spark-icon.svg" />
The quickest way to open Claude is to click the Spark icon in the **Editor Toolbar** (top-right corner of the editor). The icon only appears when you have a file open.
<img src="https://mintcdn.com/claude-code/mfM-EyoZGnQv8JTc/images/vs-code-editor-icon.png?fit=max&auto=format&n=mfM-EyoZGnQv8JTc&q=85&s=eb4540325d94664c51776dbbfec4cf02" alt="VS Code editor showing the Spark icon in the Editor Toolbar" width="2796" height="734" data-path="images/vs-code-editor-icon.png" />
Other ways to open Claude Code:
* **Activity Bar**: click the Spark icon in the left sidebar to open the sessions list. Click any session to open it as a full editor tab, or start a new one. This icon is always visible in the Activity Bar.
* **Command Palette**: `Cmd+Shift+P` (Mac) or `Ctrl+Shift+P` (Windows/Linux), type "Claude Code", and select an option like "Open in New Tab"
* **Status Bar**: click **✱ Claude Code** in the bottom-right corner of the window. This works even when no file is open.
You can drag the Claude panel to reposition it anywhere in VS Code. See [Customize your workflow](#customize-your-workflow) for details.
</Step>
<Step title="Sign in">
The first time you open the panel, a sign-in screen appears. Click **Sign in** and complete authorization in your browser.
If you see **Not logged in · Please run /login** later, the extension reopens the sign-in screen automatically. If it doesn't appear, reload the window from the Command Palette with **Developer: Reload Window**.
If you have `ANTHROPIC_API_KEY` set in your shell but still see the sign-in prompt, VS Code may not have inherited your shell environment. Launch VS Code from a terminal with `code .` so it inherits your environment variables, or sign in with your Claude account instead.
After you sign in, a **Learn Claude Code** checklist appears. Work through each item by clicking **Show me**, or dismiss it with the X. To reopen it later, uncheck **Hide Onboarding** in VS Code settings under Extensions → Claude Code.
</Step>
<Step title="Send a prompt">
Ask Claude to help with your code or files, whether that's explaining how something works, debugging an issue, or making changes.
<Tip>Claude automatically sees your selected text. Press `Option+K` (Mac) / `Alt+K` (Windows/Linux) to also insert an @-mention reference (like `@file.ts#5-10`) into your prompt.</Tip>
Here's an example of asking about a particular line in a file:
<img src="https://mintcdn.com/claude-code/FVYz38sRY-VuoGHA/images/vs-code-send-prompt.png?fit=max&auto=format&n=FVYz38sRY-VuoGHA&q=85&s=ede3ed8d8d5f940e01c5de636d009cfd" alt="VS Code editor with lines 2-3 selected in a Python file, and the Claude Code panel showing a question about those lines with an @-mention reference" width="3288" height="1876" data-path="images/vs-code-send-prompt.png" />
</Step>
<Step title="Review changes">
When Claude wants to edit a file, it shows a side-by-side comparison of the original and proposed changes, then asks for permission. You can accept, reject, or tell Claude what to do instead. If you edit the proposed content directly in the diff view before accepting, Claude is told that you modified it so it does not assume the file matches its original proposal.
<img src="https://mintcdn.com/claude-code/FVYz38sRY-VuoGHA/images/vs-code-edits.png?fit=max&auto=format&n=FVYz38sRY-VuoGHA&q=85&s=e005f9b41c541c5c7c59c082f7c4841c" alt="VS Code showing a diff of Claude's proposed changes with a permission prompt asking whether to make the edit" width="3292" height="1876" data-path="images/vs-code-edits.png" />
</Step>
</Steps>
For more ideas on what you can do with Claude Code, see [Common workflows](/docs/en/common-workflows).
<Tip>
Run "Claude Code: Open Walkthrough" from the Command Palette for a guided tour of the basics.
</Tip>
## Use the prompt box
The prompt box supports several features:
* **Permission modes**: click the mode indicator at the bottom of the prompt box to switch modes, or set the default in your VS Code user settings under `claudeCode.initialPermissionMode`. See [permission modes](/docs/en/permission-modes#switch-permission-modes) for every mode the indicator offers.
* **Manual**: Claude asks permission before file edits and most shell commands.
* **Plan**: Claude describes what it will do and waits for approval before making changes. VS Code automatically opens the plan as a full Markdown document where you can add inline comments to give feedback before Claude begins.
* **Edit automatically**: Claude makes edits without asking.
* **Command menu**: click `/` or type `/` to open the command menu. Options include attaching files, switching models, toggling extended thinking, viewing plan usage (`/usage`), and starting a [Remote Control](/docs/en/remote-control) session (`/remote-control`). The Customize section provides access to MCP servers, hooks, memory, permissions, and plugins. Items with a terminal icon open in the integrated terminal.
* The Settings section includes **Enable Remote Control for all sessions**, which sets [`remoteControlAtStartup`](/docs/en/settings#available-settings) to control whether [new interactive sessions connect to Remote Control automatically](/docs/en/remote-control#enable-remote-control-for-all-sessions). Requires Claude Code v2.1.203 or later.
* The Settings section also includes **Focus view**, which hides tool calls, tool results, and thinking behind expandable rows, leaving your prompts and Claude's responses. Claude's latest to-do list stays visible, and so does the text a pending question from Claude is asking about. Toggle it there, with `Ctrl+Option+F` (Mac) / `Ctrl+Alt+F` (Windows/Linux), or from the Command Palette with **Claude Code: Toggle Focus view**. The change applies to every open session and persists across sessions. Requires Claude Code v2.1.221 or later.
* To report a bug, click **Report a problem** at the bottom of the menu, or type `/bug` or `/feedback` with an optional description that prefills the report. When you submit the report and you're signed in to Anthropic on a first-party connection, Claude Code sends it to Anthropic. On a third-party provider, or without Anthropic credentials, the dialog still opens, but submitting shows an error and sends nothing: unlike the CLI's `/bug`, the extension doesn't write a local archive. Requires Claude Code v2.1.229 or later.
* **Side questions**: type `/btw` followed by a question, or pick it from the command menu, to ask about your session [without adding to the conversation](/docs/en/interactive-mode#side-questions-with-%2Fbtw). The answer opens in a panel beside the chat, where you can ask follow-up questions. The thread survives window reloads. Claude Code keeps the newest 20 exchanges and expires stored threads on the [`cleanupPeriodDays`](/docs/en/settings#available-settings) schedule, as long as Claude Code can [safely determine the retention period](/docs/en/claude-directory#cleaned-up-automatically). To clear a thread, click the trash icon in the panel. Requires Claude Code v2.1.227 or later.
* **Context indicator**: the prompt box shows how much of Claude's context window you're using. Claude automatically compacts when needed, or you can run `/compact` manually.
* **Extended thinking**: lets Claude spend more time reasoning through complex problems. Toggle it on via the command menu (`/`). Claude's reasoning appears in the conversation as collapsed blocks: click a block to read it, or press `Ctrl+O` to expand or collapse every thinking block in the session. See [Extended thinking](/docs/en/model-config#extended-thinking) for details.
* **Multi-line input**: press `Shift+Enter` to add a new line without sending. This also works in the "Other" free-text input of question dialogs.
### Reference files and folders
Use @-mentions to give Claude context about specific files or folders. When you type `@` followed by a file or folder name, Claude reads that content and can answer questions about it or make changes to it. Claude Code supports fuzzy matching, so you can type partial names to find what you need:
```text wrap theme={null}
Explain the logic in @auth (fuzzy matches auth.js, AuthService.ts, etc.)
What's in @src/components/ (include a trailing slash for folders)
```
For large PDFs, you can ask Claude to read specific pages instead of the whole file: a single page, a range like pages 1-10, or an open-ended range like page 3 onward.
When you select text in the editor, Claude can see your highlighted code automatically. The prompt box footer shows how many lines are selected. Press `Option+K` (Mac) / `Alt+K` (Windows/Linux) to insert an @-mention with the file path and line numbers (e.g., `@app.ts#5-10`). Click the selection indicator to toggle whether Claude can see your highlighted text - the eye-slash icon means the selection is hidden from Claude.
You can also hold `Shift` while dragging files into the prompt box to add them as attachments. Click the X on any attachment to remove it from context.
### Resume past conversations
Click the **Session history** button at the top of the Claude Code panel to access your conversation history. You can search by keyword or browse by time (Today, Yesterday, Last 7 days, etc.). Click any conversation to resume it with the full message history. New sessions receive AI-generated titles based on your first message. Hover over a session to reveal rename and remove actions: rename to give it a descriptive title, or remove to delete it from the list. For more on resuming sessions, see [Manage sessions](/docs/en/sessions).
### Resume cloud sessions from Claude.ai
If you use [Claude Code on the web](/docs/en/claude-code-on-the-web), you can resume those cloud sessions directly in VS Code. This requires signing in with **Claude.ai Subscription**, not Anthropic Console.
<Steps>
<Step title="Open session history">
Click the **Session history** button at the top of the Claude Code panel.
</Step>
<Step title="Select the Web tab">
The dialog shows two tabs: Local and Web. Click **Web** to see sessions from claude.ai.
</Step>
<Step title="Select a session to resume">
Browse or search your cloud sessions. Click any session to download it and continue the conversation locally.
</Step>
</Steps>
<Note>
Only web sessions started with a GitHub repository appear in the Web tab. Resuming loads the conversation history locally; changes are not synced back to claude.ai.
</Note>
### Check account and usage
Run `/usage` from the command menu to open the Account & usage dialog. It shows your signed-in account, plan, and usage bars for the current session and week with how long until each limit resets.
The dialog also breaks down what is contributing to your plan limits. It flags behaviors that account for 10% or more of recent usage, such as cache misses, long context, and subagent-heavy or highly parallel sessions, each with a tip to reduce it. Attribution tables show how much usage came from each skill, subagent, plugin, and MCP server. Requires Claude Code v2.1.174 or later.
Use the Day and Week toggle to switch between the last 24 hours and the last 7 days. The figures are approximate and computed from local sessions on this machine, so usage from other devices or claude.ai is not included. For more on tracking and reducing usage, see [Track your costs](/docs/en/costs#track-your-costs).
## Customize your workflow
You can reposition the Claude panel, run multiple conversations, organize the sessions list into groups, or switch to terminal mode.
### Choose where Claude lives
You can drag the Claude panel to reposition it anywhere in VS Code. Grab the panel's tab or title bar and drag it to:
* **Secondary sidebar**: the right side of the window. Keeps Claude visible while you code.
* **Primary sidebar**: the left sidebar with icons for Explorer, Search, etc.
* **Editor area**: opens Claude as a tab alongside your files. Useful for side tasks.
<Tip>
Use the sidebar for your main Claude session and open additional tabs for side tasks. Claude remembers your preferred location. The Activity Bar sessions list icon is separate from the Claude panel: the sessions list is always visible in the Activity Bar, while the Claude panel icon only appears there when the panel is docked to the left sidebar.
</Tip>
### Run multiple conversations
Use **Open in New Tab** or **Open in New Window** from the Command Palette to start additional conversations. Each conversation maintains its own history and context, allowing you to work on different tasks in parallel.
When using tabs, a small colored dot on the spark icon indicates status: blue means a permission request is pending, orange means Claude finished while the tab was hidden.
### Organize sessions into groups
In the sessions list in the Activity Bar, you can collect related sessions into named, collapsible groups. Requires Claude Code v2.1.229 or later.
* **Group or ungroup a session**: right-click a session to create a group from it, move it into an existing group, or remove it from its group. Each session belongs to one group at a time, so moving it into another group removes it from the first.
* **Move several sessions at once**: `Cmd`-click (Mac) / `Ctrl`-click (Windows/Linux) each session, or `Shift`-click to select a range, then right-click the selection.
* **Rename or delete a group**: right-click a group header. Deleting a group removes only the group, and its sessions return to the ungrouped list.
The extension saves groups per workspace folder, so they survive window reloads and appear in every window where you open the same folder. When you search the list, the extension shows matches in one flat list across all groups.
### Switch to terminal mode
By default, the extension opens a graphical chat panel. If you prefer the CLI-style interface, open the [Use Terminal setting](vscode://settings/claudeCode.useTerminal) and check the box.
You can also open VS Code settings (`Cmd+,` on Mac or `Ctrl+,` on Windows/Linux), go to Extensions → Claude Code, and check **Use Terminal**.
## Manage plugins
The VS Code extension includes a graphical interface for installing and managing [plugins](/docs/en/plugins). Type `/plugins` in the prompt box to open the **Manage plugins** interface.
### Install plugins
The plugin dialog shows two tabs: **Plugins** and **Marketplaces**.
In the Plugins tab:
* **Installed plugins** appear at the top with toggle switches to enable or disable them
* **Available plugins** from your configured marketplaces appear below
* Search to filter plugins by name or description
* Click **Install** on any available plugin
When you install a plugin, choose the installation scope:
* **Install for you**: available in all your projects (user scope)
* **Install for this project**: shared with project collaborators (project scope)
* **Install locally**: only for you, only in this repository (local scope)
### Manage marketplaces
Switch to the **Marketplaces** tab to add or remove plugin sources:
* Enter a GitHub repo, URL, or local path to add a new marketplace
* Click the refresh icon to update a marketplace's plugin list
* Click the trash icon to remove a marketplace
After you make changes, a banner prompts you to restart Claude Code to apply them.
<Note>
Plugin management in VS Code uses the same CLI commands under the hood. Plugins and marketplaces you configure in the extension are also available in the CLI, and vice versa.
</Note>
For more about the plugin system, see [Plugins](/docs/en/plugins) and [Plugin marketplaces](/docs/en/plugin-marketplaces).
## Automate browser tasks with Chrome
Connect Claude to your Chrome browser to test web apps, debug with console logs, and automate browser workflows without leaving VS Code. This requires the [Claude in Chrome extension](https://chromewebstore.google.com/detail/claude/fcoeoabgfenejglbffodgkkbkcdhcgfn) version 1.0.36 or higher.
Type `@browser` in the prompt box followed by what you want Claude to do:
```text wrap theme={null}
@browser go to localhost:3000 and check the console for errors
```
You can also open the attachment menu to select specific browser tools like opening a new tab or reading page content.
Claude opens new tabs for browser tasks and shares your browser's login state, so it can access any site you're already signed into.
For setup instructions, the full list of capabilities, and troubleshooting, see [Use Claude Code with Chrome](/docs/en/chrome).
## VS Code commands and shortcuts
Open the Command Palette (`Cmd+Shift+P` on Mac or `Ctrl+Shift+P` on Windows/Linux) and type "Claude Code" to see all available VS Code commands for the Claude Code extension.
Some shortcuts depend on which panel is "focused" (receiving keyboard input). When your cursor is in a code file, the editor is focused. When your cursor is in Claude's prompt box, Claude is focused. Use `Cmd+Esc` / `Ctrl+Esc` to toggle between them.
<Note>
These are VS Code commands for controlling the extension. Not all built-in Claude Code commands are available in the extension. See [VS Code extension vs. Claude Code CLI](#vs-code-extension-vs-claude-code-cli) for details.
</Note>
| Command | Shortcut | Description |
| -------------------------- | -------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Focus Input | `Cmd+Esc` (Mac) / `Ctrl+Esc` (Windows/Linux) | Toggle focus between editor and Claude |
| Open in Side Bar | - | Open Claude in the left sidebar |
| Open in Terminal | - | Open Claude in terminal mode |
| Open in New Tab | `Cmd+Shift+Esc` (Mac) / `Ctrl+Shift+Esc` (Windows/Linux) | Open a new conversation as an editor tab |
| Open in New Window | - | Open a new conversation in a separate window |
| New Conversation | `Cmd+N` (Mac) / `Ctrl+N` (Windows/Linux) | Start a new conversation. Requires Claude to be focused and `enableNewConversationShortcut` set to `true` |
| Reopen Closed Session | `Cmd+Shift+T` (Mac) / `Ctrl+Shift+T` (Windows/Linux) | Reopen the most recently closed Claude session tab. Falls through to VS Code's normal reopen-closed-editor when the last closed tab wasn't a Claude session. Disable with `enableReopenClosedSessionShortcut` |
| Insert @-Mention Reference | `Option+K` (Mac) / `Alt+K` (Windows/Linux) | Insert a reference to the current file and selection (requires editor to be focused) |
| Toggle Focus view | `Ctrl+Option+F` (Mac) / `Ctrl+Alt+F` (Windows/Linux) | Hide or show tool activity in the conversation. Works while a Claude panel or sidebar is visible |
| Show Logs | - | View extension debug logs |
| Logout | - | Sign out of your Anthropic account |
### Launch a VS Code tab from other tools
The extension registers a URI handler at `vscode://anthropic.claude-code/open`. Use it to open a new Claude Code tab from your own tooling: a shell alias, a browser bookmarklet, or any script that can open a URL. If VS Code isn't already running, opening the URL launches it first. If VS Code is already running, the URL opens in whichever window is currently focused.
Invoke the handler with your operating system's URL opener.
<Tabs>
<Tab title="macOS">
```bash theme={null}
open "vscode://anthropic.claude-code/open"
```
</Tab>
<Tab title="Linux">
```bash theme={null}
xdg-open "vscode://anthropic.claude-code/open"
```
The `xdg-open` command comes from the `xdg-utils` package. If the shell reports it isn't found, see [xdg-open is not found on Linux](/docs/en/deep-links#xdg-open-is-not-found-on-linux).
</Tab>
<Tab title="Windows">
In PowerShell:
```powershell theme={null}
Start-Process "vscode://anthropic.claude-code/open"
```
In `cmd.exe`, `start` treats its first quoted argument as a window title, so pass an empty title before the URL:
```cmd theme={null}
Cut at 300 lines. The page has the rest.
web-quickstart First recorded · 230 lines, first recorded
# Get started with Claude Code on the web ## How sessions run ## Compare ways to run Claude Code ## Connect GitHub ### Connect from your terminal ## Start a task ## Pre-fill sessions ## Review and iterate ## Troubleshoot setup ### No repositories appear after connecting GitHub ### The page only shows a GitHub login button ### "Not available for the selected organization" ### `/web-setup` says "Not signed in to Claude" ### `/web-setup` shows "No commands match" or "Unknown command" ### "Could not create a cloud environment" or "No cloud environment available" when using `--cloud` ### Setup script failed ### New sessions hang or time out during setup ### Session keeps running after closing the tab ## Next steps
The first capture of this source. The page was already there, and this is what it said.
# Get started with Claude Code on the web
> Run Claude Code in the cloud from your browser or phone. Connect a GitHub repository, submit a task, and review the PR without local setup.
<Note>
Claude Code on the web is in research preview for Pro, Max, and Team users, and for Enterprise users with premium seats or Chat + Claude Code seats.
</Note>
Claude Code on the web runs on cloud infrastructure instead of your machine, Anthropic-managed by default. Submit tasks from [claude.ai/code](https://claude.ai/code) in your browser or the Claude mobile app.
You'll need a GitHub repository to [get started](#connect-github). Claude clones it into an isolated virtual machine, makes changes, and pushes a branch for you to review. Sessions persist across devices, so a task you start on your laptop is ready to review from your phone later.
Claude Code on the web works well for:
* **Parallel tasks**: run several independent tasks at once, each in its own session and branch, without managing multiple worktrees
* **Repos you don't have locally**: Claude clones the repo fresh every session, so you don't need it checked out
* **Tasks that don't need frequent steering**: submit a well-defined task, do something else, and review the result when Claude is done
* **Code questions and exploration**: understand a codebase or trace how a feature is implemented without a local checkout
For work that needs your local config, tools, or environment, running Claude Code locally or using [Remote Control](/docs/en/remote-control) is a better fit.
## How sessions run
The steps below describe Anthropic-hosted sessions. In a [self-hosted environment](/docs/en/self-hosted-environments), the clone and everything after it run on your organization's own runners, where network boundaries, setup, and push behavior are operator-configured. When you submit a task:
1. **Clone and prepare**: your repository is cloned to an Anthropic-managed VM, and your [setup script](/docs/en/cloud-environments#setup-scripts) runs if configured.
2. **Configure network**: internet access is set based on your environment's [access level](/docs/en/cloud-environments#access-levels).
3. **Work**: Claude analyzes code, makes changes, runs tests, and checks its work. You can watch and steer throughout, or step away and come back when it's done.
4. **Push the branch**: when Claude reaches a stopping point, it pushes its branch to GitHub. You review the diff, leave inline comments, create a PR, or send another message to keep going.
The session doesn't close when the branch is pushed. PR creation and further edits all happen within the same conversation.
## Compare ways to run Claude Code
Claude Code behaves the same everywhere. What changes is where code executes and whether your local config is available. The Desktop app offers both local and cloud sessions, so its answers below depend on which you choose:
| | On the web | Remote Control | Terminal CLI | Desktop app |
| :------------------------------------------- | :------------------------------------------------------------------------------------------------------------- | :------------------------- | :--------------------- | :-------------------------- |
| **Code runs on** | Cloud VM, Anthropic-managed by default | Your machine | Your machine | Your machine or cloud VM |
| **You chat from** | claude.ai or mobile app | claude.ai or mobile app | Your terminal | The Desktop UI |
| **Uses your local config** | No, repo only | Yes | Yes | Yes for local, no for cloud |
| **Requires GitHub** | Yes, or [bundle a local repo](/docs/en/claude-code-on-the-web#send-local-repositories-without-github) via `--cloud` | No | No | Only for cloud sessions |
| **Keeps running if you disconnect** | Yes | While terminal stays open | No | Depends on session type |
| **[Permission modes](/docs/en/permission-modes)** | Accept edits, Plan, Auto | Manual, Accept edits, Plan | All modes | Depends on session type |
| **Network access** | Configurable per environment | Your machine's network | Your machine's network | Depends on session type |
See the [terminal quickstart](/docs/en/quickstart), [Desktop app](/docs/en/desktop), or [Remote Control](/docs/en/remote-control) docs to set those up.
## Connect GitHub
Setup is a one-time process. If you already use the GitHub CLI, you can [do this from your terminal](#connect-from-your-terminal) instead of the browser.
<Steps>
<Step title="Visit claude.ai/code">
Go to [claude.ai/code](https://claude.ai/code) and sign in with your Anthropic account.
</Step>
<Step title="Install the Claude GitHub App">
After signing in, claude.ai/code prompts you to connect GitHub. Follow the prompt to install the Claude GitHub App and grant it access to your repositories. Cloud sessions work with existing GitHub repositories, so to start a new project, [create an empty repository on GitHub](https://github.com/new) first.
</Step>
<Step title="Confirm your Default environment">
When you finish connecting GitHub, onboarding creates a [cloud environment](/docs/en/cloud-environments) named **Default** for you; if onboarding shows an environment form instead, keep its defaults to create the same **Default** environment. The environment controls what network access Claude has during sessions and what runs when a new session is created. **Default** uses [`Trusted` network access](/docs/en/cloud-environments#access-levels): sessions reach [common package registries](/docs/en/cloud-environments#default-allowed-domains) and other allowlisted domains, and nothing else through the session's network. See [Installed tools](/docs/en/cloud-environments#installed-tools) for what's available without any configuration.
For a first project, the **Default** environment works as is. To change its network access, add environment variables, or run a [setup script](/docs/en/cloud-environments#setup-scripts) before sessions start, [edit it or create additional environments](/docs/en/cloud-environments#configure-your-environment).
</Step>
</Steps>
### Connect from your terminal
If you already use the GitHub CLI (`gh`), you can set up Claude Code on the web without opening a browser. This requires the [Claude Code CLI](/docs/en/quickstart). `/web-setup` reads your local `gh` token, links it to your Claude account, and creates a default cloud environment if you don't have one.
<Note>
Organizations with [Zero Data Retention](/docs/en/zero-data-retention) enabled cannot use `/web-setup` or other cloud session features. If the GitHub CLI isn't installed or authenticated, `/web-setup` opens the browser onboarding flow instead.
</Note>
<Steps>
<Step title="Authenticate with the GitHub CLI">
In your shell, authenticate the GitHub CLI if you haven't already:
```bash theme={null}
gh auth login
```
</Step>
<Step title="Sign in to Claude">
In the Claude Code CLI, run `/login` to sign in with your claude.ai account. Skip this step if you're already signed in with a claude.ai account. Authenticating with an API key doesn't count. To check, run `/status` and confirm the **Login method** row shows a claude.ai account.
</Step>
<Step title="Run /web-setup">
In the Claude Code CLI, run:
```text theme={null}
/web-setup
```
This syncs your `gh` token to your Claude account. On success, Claude Code prints `Connected as <your-github-username>` and opens [claude.ai/code](https://claude.ai/code) in your browser. If you don't have a cloud environment yet, `/web-setup` creates one with Trusted network access and no setup script. You can [edit the environment or add variables](/docs/en/cloud-environments#configure-your-environment) afterward. Once `/web-setup` completes, you can start cloud sessions from your terminal with [`--cloud`](/docs/en/claude-code-on-the-web#from-terminal-to-web) or set up recurring tasks with [`/schedule`](/docs/en/routines).
</Step>
</Steps>
## Start a task
With GitHub connected and an environment created, you're ready to submit tasks.
<Steps>
<Step title="Select a repository and branch">
From [claude.ai/code](https://claude.ai/code) or the Code tab in the Claude mobile app, click the repository selector below the input box and choose a repository for Claude to work in. Each repository shows a branch selector. Change it to start Claude from a feature branch instead of the default. You can add multiple repositories to work across them in one session.
</Step>
<Step title="Choose a permission mode">
The mode dropdown next to the input defaults to **Accept edits**, where Claude makes changes and pushes a branch without stopping for approval. Switch to **Plan** if you want Claude to propose an approach and wait for you to approve it before editing files. Cloud sessions don't offer Manual or Bypass permissions. See the [full list of permission modes](/docs/en/permission-modes#available-modes) for what each one allows.
</Step>
<Step title="Describe the task and submit">
Type a description of what you want and press Enter. Be specific:
* Name the file or function: "Add a README with setup instructions" or "Fix the failing auth test in `tests/test_auth.py`" is better than "fix tests"
* Paste error output if you have it
* Describe the expected behavior, not just the symptom
Claude clones the repositories, runs your setup script if configured, and starts working. Each task gets its own session and its own branch, so you don't need to wait for one to finish before starting another.
</Step>
</Steps>
## Pre-fill sessions
You can prefill the prompt, repositories, and environment for a new session by adding query parameters to the [claude.ai/code](https://claude.ai/code) URL. Use this to build integrations such as a button in your issue tracker that opens Claude Code with the issue description as the prompt.
| Parameter | Description |
| :------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `prompt` | Prompt text to prefill in the input box. The alias `q` is also accepted. |
| `prompt_url` | URL to fetch the prompt text from, for prompts too long to embed in a query string. The URL must allow cross-origin requests. Ignored when `prompt` is also set. |
| `repositories` | Comma-separated list of `owner/repo` slugs to preselect. The alias `repo` is also accepted. |
| `environment` | Name or ID of the [environment](#connect-github) to preselect. |
URL-encode each value. The example below opens the form with a prompt and a repository already selected:
```text theme={null}
https://claude.ai/code?prompt=Fix%20the%20login%20bug&repositories=acme/webapp
```
## Review and iterate
When Claude finishes, review the changes, leave feedback on specific lines, and keep going until the diff looks right.
<Steps>
<Step title="Open the diff view">
A diff indicator shows lines added and removed across the session, for example `+42 -18`. Select it to open the diff view, with a file list on the left and changes on the right.
</Step>
<Step title="Leave inline comments">
Select any line in the diff, type your feedback, and press Enter. Comments queue up until you send your next message, then they're bundled with it. Claude sees "at `src/auth.ts:47`, don't catch the error here" alongside your main instruction, so you don't have to describe where the problem is.
</Step>
<Step title="Create a pull request">
When the diff looks right, select **Create PR** at the top of the diff view. You can open it as a full PR, a draft, or jump to GitHub's compose page with a generated title and description.
</Step>
<Step title="Keep iterating after the PR">
The session stays live after the PR is created. Paste CI failure output or reviewer comments into the chat and ask Claude to address them. To have Claude monitor the PR automatically, see [Auto-fix pull requests](/docs/en/claude-code-on-the-web#auto-fix-pull-requests).
</Step>
</Steps>
## Troubleshoot setup
### No repositories appear after connecting GitHub
A cloud session can use any repository the connected GitHub account can see, regardless of which repositories the Claude GitHub App is installed on. If a repository is missing, verify the connected GitHub account has access to it on GitHub. If you also want [Auto-fix](/docs/en/claude-code-on-the-web#auto-fix-pull-requests) for a repository, install the App on it: on github.com, open **Settings → Applications → Claude → Configure** and verify the repository is listed under **Repository access**. Private repositories need the same authorization as public ones.
### The page only shows a GitHub login button
Cloud sessions require a connected GitHub account. Connect via the browser flow above, or run `/web-setup` from your terminal if you use the GitHub CLI. If you'd rather not connect GitHub at all, see [Remote Control](/docs/en/remote-control) to run Claude Code on your own machine and monitor it from the web.
### "Not available for the selected organization"
Enterprise organizations may need an Owner to enable Claude Code on the web. Contact your Anthropic account team.
### `/web-setup` says "Not signed in to Claude"
If `/web-setup` responds with "Not signed in to Claude. Run /login first.", the CLI doesn't have a valid claude.ai sign-in. This can also happen when a previous sign-in has expired. Run `/login`, sign in with your claude.ai account, then run `/web-setup` again.
### `/web-setup` shows "No commands match" or "Unknown command"
`/web-setup` runs inside the Claude Code CLI, not your shell. Launch `claude` first, then type `/web-setup` at the prompt.
If you typed it inside Claude Code and the command menu shows `No commands match "/web-setup"`, or submitting it returns `Unknown command: /web-setup`, the command is hidden because a requirement isn't met. The cause is usually that you're authenticated with an API key or third-party provider instead of a claude.ai subscription. Run `/login` to sign in with your claude.ai account. Team and Enterprise Owners can also [disable `/web-setup`](/docs/en/claude-code-on-the-web#github-authentication-options) for their organization, which hides the command. In that case, use the browser flow above instead.
On Team and Enterprise plans, the command is also hidden when any of the following apply:
* an administrator has disabled Claude Code on the web for your organization
* an administrator has disabled the [Quick web setup toggle](/docs/en/claude-code-on-the-web#github-authentication-options)
* your Enterprise organization has [Zero Data Retention](/docs/en/zero-data-retention) enabled, which makes Claude Code on the web unavailable
### "Could not create a cloud environment" or "No cloud environment available" when using `--cloud`
Remote-session features create a default cloud environment automatically if you don't have one. If you see "Could not create a cloud environment", automatic creation failed. If you see "No cloud environment available", your CLI predates automatic creation. In either case, run `/web-setup` in the Claude Code CLI, or add an environment from the [environment selector](/docs/en/cloud-environments#configure-your-environment) at [claude.ai/code](https://claude.ai/code).
### Setup script failed
The setup script exited with a non-zero status, which blocks the session from starting. Common causes:
* A package install failed because the registry isn't in your [network access level](/docs/en/cloud-environments#access-levels). `Trusted` covers most package managers; `None` blocks them all.
* The script references a file or path that doesn't exist in a fresh clone.
* A command that works locally needs a different invocation on Ubuntu.
To debug, add `set -x` at the top of the script to see which command failed. For non-critical commands, append `|| true` so they don't block session start.
### New sessions hang or time out during setup
If new sessions stall on the setup script step or fail with a generic container error before the script finishes, the script is likely exceeding the roughly five-minute time budget for building the [environment cache](/docs/en/cloud-environments#environment-caching). Heavy steps such as pulling large Docker images, syncing full dependency trees, or downloading model weights often push the total over the limit, especially when they run one after another.
To fix this, trim the script so it reliably finishes in under five minutes:
* Run independent installs in parallel with `&` and a final `wait` instead of running them serially.
* Move the largest downloads out of the setup script and into a [SessionStart hook](/docs/en/cloud-environments#setup-scripts-vs-sessionstart-hooks) that launches them in the background, so the session becomes usable while they finish.
* Remove long retry sleeps from the setup script, since a stalled retry loop counts against the budget.
### Session keeps running after closing the tab
This is by design. Closing the tab or navigating away doesn't stop the session. It continues running in the background until Claude finishes the current task, then idles. From the sidebar, you can [archive a session](/docs/en/claude-code-on-the-web#archive-sessions) to hide it from your list, or [delete it](/docs/en/claude-code-on-the-web#delete-sessions) to remove it permanently.
## Next steps
Now that you can submit and review tasks, these pages cover what comes next: starting cloud sessions from your terminal, scheduling recurring work, and giving Claude standing instructions.
* [Use Claude Code on the web](/docs/en/claude-code-on-the-web): the full reference, including teleporting sessions to your terminal, session sharing, and auto-fixing pull requests
* [Configure cloud environments](/docs/en/cloud-environments): network access levels, environment variables, and setup scripts for cloud sessions
* [Routines](/docs/en/routines): automate work on a schedule, via API call, or in response to GitHub events
* [CLAUDE.md](/docs/en/memory): give Claude persistent instructions and context that load at the start of every session
* Install the Claude mobile app for [iOS](https://apps.apple.com/us/app/claude-by-anthropic/id6473753684) or [Android](https://play.google.com/store/apps/details?id=com.anthropic.claude) to monitor sessions from your phone. From the Claude Code CLI, `/mobile` shows a QR code.
whats-new/2026-w13 First recorded · 160 lines, first recorded
# Week 13 · March 23–27, 2026
The first capture of this source. The page was already there, and this is what it said.
# Week 13 · March 23–27, 2026
> Auto mode for hands-off permissions, computer use built in, PR auto-fix in the cloud, transcript search, and a PowerShell tool for Windows.
<div className="digest-meta">
<span>Releases <a href="/docs/docs/en/changelog#2-1-83">v2.1.83 → v2.1.85</a></span>
<span>6 features · March 23–27</span>
</div>
<div className="digest-feature">
<div className="digest-feature-header">
<span className="digest-feature-title">Auto mode</span>
<span className="digest-feature-pill">research preview</span>
</div>
<p className="digest-feature-lede">Auto mode hands your permission prompts to a classifier. Safe edits and commands run without interrupting you; anything destructive or suspicious gets blocked and surfaced. It's the middle ground between approving every file write and running with <code>--dangerously-skip-permissions</code>.</p>
<Frame>
<img src="https://mintcdn.com/claude-code/CfffsX01JHFnIKvD/images/whats-new/auto-mode.png?fit=max&auto=format&n=CfffsX01JHFnIKvD&q=85&s=367c9e9d4ba5bc57ec4b935154bf1fbb" alt="Claude Code prompt footer showing 'auto mode on (shift+tab to cycle)' indicator in yellow" width="2400" height="691" data-path="images/whats-new/auto-mode.png" />
</Frame>
<p className="digest-feature-try">Cycle to auto with Shift+Tab, or set it as your default:</p>
```json ~/.claude/settings.json {3} theme={null}
{
"permissions": {
"defaultMode": "auto"
}
}
```
<a className="digest-feature-link" href="/docs/docs/en/permission-modes">Permission modes guide</a>
</div>
<div className="digest-feature">
<div className="digest-feature-header">
<span className="digest-feature-title">Computer use</span>
<span className="digest-feature-pill">Desktop</span>
</div>
<p className="digest-feature-lede">Claude can now control your actual desktop from the Claude Code Desktop app: open native apps, click through the iOS simulator, drive hardware control panels, and verify changes on screen. It's off by default and asks before each action. Best for the things nothing else can reach: apps without an API, proprietary tools, anything that only exists as a GUI.</p>
<Frame>
<img src="https://mintcdn.com/claude-code/CfffsX01JHFnIKvD/images/whats-new/computer-use.png?fit=max&auto=format&n=CfffsX01JHFnIKvD&q=85&s=d631de2017edafff463505f8ddbc0f51" alt="Claude Desktop settings with the Computer use toggle enabled, showing the option to let Claude take screenshots and control your keyboard and mouse in apps you allow" width="2376" height="1210" data-path="images/whats-new/computer-use.png" />
</Frame>
<p className="digest-feature-try">Enable it in Settings, grant the OS permissions, then ask Claude to verify a change end to end:</p>
```text title="Claude Code" wrap theme={null}
Open the iOS simulator, tap through the onboarding flow, and screenshot each step
```
<a className="digest-feature-link" href="/docs/docs/en/desktop#let-claude-use-your-computer">Computer use guide</a>
</div>
<div className="digest-feature">
<div className="digest-feature-header">
<span className="digest-feature-title">PR auto-fix</span>
<span className="digest-feature-pill">Web</span>
</div>
<p className="digest-feature-lede">Flip a switch when you open a PR and walk away. Claude watches CI, fixes the failures, handles the nits, and pushes until it's green. No more babysitting a PR through six rounds of lint errors.</p>
<Frame>
<img src="https://mintcdn.com/claude-code/CfffsX01JHFnIKvD/images/whats-new/auto-fix.png?fit=max&auto=format&n=CfffsX01JHFnIKvD&q=85&s=c62b181c6c5d96929f0b43525f9f3584" alt="Claude Code web CI panel showing the Auto fix toggle enabled, with description 'Proactively fix CI failures and review comments'" width="960" height="444" data-path="images/whats-new/auto-fix.png" />
</Frame>
<p className="digest-feature-try">After creating a PR on Claude Code web, toggle Auto fix in the CI panel.</p>
<a className="digest-feature-link" href="/docs/docs/en/claude-code-on-the-web#auto-fix-pull-requests">Auto-fix pull requests</a>
</div>
<div className="digest-feature">
<div className="digest-feature-header">
<span className="digest-feature-title">Transcript search</span>
<span className="digest-feature-pill">v2.1.83</span>
</div>
<p className="digest-feature-lede">Press <code>/</code> in transcript mode to search your conversation. <code>n</code> and <code>N</code> step through matches. Finally a way to find that one Bash command Claude ran 400 messages ago.</p>
<p className="digest-feature-try">Open transcript mode and search:</p>
```text Claude Code theme={null}
Ctrl+O # open transcript
/migrate # search for "migrate"
n # next match
N # previous match
```
<a className="digest-feature-link" href="/docs/docs/en/fullscreen#search-and-review-the-conversation">Fullscreen guide</a>
</div>
<div className="digest-feature">
<div className="digest-feature-header">
<span className="digest-feature-title">PowerShell tool</span>
<span className="digest-feature-pill">preview</span>
<span className="digest-feature-pill">v2.1.84</span>
</div>
<p className="digest-feature-lede">Windows gets a native PowerShell tool alongside Bash. Claude can run cmdlets, pipe objects, and work with Windows-native paths without translating everything through Git Bash.</p>
<p className="digest-feature-try">Opt in from settings:</p>
```json .claude/settings.json {3} theme={null}
{
"env": {
"CLAUDE_CODE_USE_POWERSHELL_TOOL": "1"
}
}
```
<a className="digest-feature-link" href="/docs/docs/en/tools-reference#powershell-tool">PowerShell tool docs</a>
</div>
<div className="digest-feature">
<div className="digest-feature-header">
<span className="digest-feature-title">Conditional hooks</span>
<span className="digest-feature-pill">v2.1.85</span>
</div>
<p className="digest-feature-lede">Hooks can now declare an <code>if</code> field using permission rule syntax. Your pre-commit check only spawns for <code>Bash(git commit \*)</code> instead of every bash call, cutting the process overhead on busy sessions.</p>
<p className="digest-feature-try">Scope a hook to git commits only:</p>
```json .claude/settings.json {5} theme={null}
{
"hooks": {
"PreToolUse": [{
"hooks": [{
"if": "Bash(git commit *)",
"type": "command",
"command": ".claude/hooks/lint-staged.sh"
}]
}]
}
}
```
<a className="digest-feature-link" href="/docs/docs/en/hooks">Hooks reference</a>
</div>
<div className="digest-wins">
<p className="digest-wins-title">Other wins</p>
<div className="digest-wins-grid">
<div>Plugin <code>userConfig</code> now public: prompt for settings at enable time, keychain-backed secrets</div>
<div>Pasted images insert <code>\[Image #N]</code> chips you can reference positionally</div>
<div><code>managed-settings.d/</code> drop-in directory for layered policy fragments</div>
<div><code>CwdChanged</code> and <code>FileChanged</code> hook events for direnv-style setups</div>
<div>Agents can declare <code>initialPrompt</code> in frontmatter to auto-submit a first turn</div>
<div><code>Ctrl+X Ctrl+E</code> opens your external editor, matching readline</div>
<div>Interrupting before any response restores your input automatically</div>
<div><code>/status</code> now works while Claude is responding</div>
<div>Deep links open in your preferred terminal, not first-detected</div>
<div>Idle-return nudge to <code>/clear</code> after 75+ minutes away</div>
<div>VS Code: rate limit banner, Esc-twice rewind picker</div>
</div>
</div>
[Full changelog for v2.1.83–v2.1.85 →](/docs/en/changelog#2-1-83)
whats-new/2026-w14 First recorded · 134 lines, first recorded
# Week 14 · March 30 – April 3, 2026
The first capture of this source. The page was already there, and this is what it said.
# Week 14 · March 30 – April 3, 2026
> Computer use in the CLI, interactive in-product lessons, flicker-free rendering, per-tool MCP result-size overrides, and plugin executables on PATH.
<div className="digest-meta">
<span>Releases <a href="/docs/docs/en/changelog#2-1-86">v2.1.86 → v2.1.91</a></span>
<span>5 features · March 30 – April 3</span>
</div>
<div className="digest-feature">
<div className="digest-feature-header">
<span className="digest-feature-title">Computer use in the CLI</span>
<span className="digest-feature-pill">research preview</span>
</div>
<p className="digest-feature-lede">Last week computer use landed in the Desktop app. This week it's in the CLI: Claude can open native apps, click through UI, test its own changes, and fix what breaks, all from your terminal. Web apps already had verification loops; native iOS, macOS, and other GUI-only apps didn't. Now they do. Best for closing the loop on apps and tools where there's no API to call. Still early; expect rough edges.</p>
<Frame>
<video autoPlay muted loop playsInline className="w-full" src="https://mintcdn.com/claude-code/CfffsX01JHFnIKvD/images/whats-new/cli-computer-use.mp4?fit=max&auto=format&n=CfffsX01JHFnIKvD&q=85&s=c17a337902308d7c9121013ded0494db" data-path="images/whats-new/cli-computer-use.mp4" />
</Frame>
<p className="digest-feature-try">Requires macOS and a Pro or Max plan; otherwise, <code>computer-use</code> won't appear in <code>/mcp</code>. Run <code>/mcp</code>, find <code>computer-use</code>, and toggle it on. Then ask Claude to verify a change end to end:</p>
```text title="Claude Code" wrap theme={null}
Open the iOS simulator, tap through onboarding, and screenshot each step
```
<a className="digest-feature-link" href="/docs/docs/en/computer-use">Computer use guide</a>
</div>
<div className="digest-feature">
<div className="digest-feature-header">
<span className="digest-feature-title">/powerup</span>
<span className="digest-feature-pill">v2.1.90</span>
</div>
<p className="digest-feature-lede">Interactive lessons that teach Claude Code features through animated demos, right inside your terminal. Claude Code releases frequently, and features that would have changed how you work last month can slip by. Run <code>/powerup</code> once and you'll know what's there.</p>
<Frame>
<video autoPlay muted loop playsInline className="w-full" src="https://mintcdn.com/claude-code/CfffsX01JHFnIKvD/images/whats-new/powerup.mp4?fit=max&auto=format&n=CfffsX01JHFnIKvD&q=85&s=fb88beddc0ecc8029da5ab029e4b28f1" data-path="images/whats-new/powerup.mp4" />
</Frame>
<p className="digest-feature-try">Run it:</p>
```text title="Claude Code" wrap theme={null}
/powerup
```
<a className="digest-feature-link" href="/docs/docs/en/commands">Commands reference</a>
</div>
<div className="digest-feature">
<div className="digest-feature-header">
<span className="digest-feature-title">Flicker-free rendering</span>
<span className="digest-feature-pill">v2.1.89</span>
</div>
<p className="digest-feature-lede">Opt into a new alt-screen renderer with virtualized scrollback. The prompt input stays pinned to the bottom, mouse selection works across long conversations, and the flicker on redraw is gone. Unset <code>CLAUDE\_CODE\_NO\_FLICKER</code> to roll back.</p>
<Frame>
<video autoPlay muted loop playsInline className="w-full" src="https://mintcdn.com/claude-code/CfffsX01JHFnIKvD/images/whats-new/flicker-free.mp4?fit=max&auto=format&n=CfffsX01JHFnIKvD&q=85&s=7719e35e52a3f9734b0cf69edac333ad" data-path="images/whats-new/flicker-free.mp4" />
</Frame>
<p className="digest-feature-try">Set the env var and restart Claude Code:</p>
```bash theme={null}
export CLAUDE_CODE_NO_FLICKER=1
claude
```
<a className="digest-feature-link" href="/docs/docs/en/fullscreen">Fullscreen rendering</a>
</div>
<div className="digest-feature">
<div className="digest-feature-header">
<span className="digest-feature-title">MCP result-size override</span>
<span className="digest-feature-pill">v2.1.91</span>
</div>
<p className="digest-feature-lede">MCP server authors can now raise the truncation cap on a specific tool by setting <code>anthropic/maxResultSizeChars</code> in the tool's <code>tools/list</code> entry, up to a hard ceiling of 500K characters. The cap used to be global, so tools that occasionally returned inherently large payloads like database schemas or full file trees hit the default limit and got persisted to disk with a file reference. Per-tool overrides keep those results inline when the tool really needs them.</p>
<p className="digest-feature-try">Annotate the tool in your server's <code>tools/list</code> response:</p>
```json highlight={5} theme={null}
{
"name": "get_schema",
"description": "Returns the full database schema",
"_meta": {
"anthropic/maxResultSizeChars": 500000
}
}
```
<a className="digest-feature-link" href="/docs/docs/en/mcp#raise-the-limit-for-a-specific-tool">MCP reference</a>
</div>
<div className="digest-feature">
<div className="digest-feature-header">
<span className="digest-feature-title">Plugin executables on PATH</span>
<span className="digest-feature-pill">v2.1.91</span>
</div>
<p className="digest-feature-lede">Place an executable in a <code>bin/</code> directory at your plugin root and Claude Code adds that directory to the Bash tool's <code>PATH</code> while the plugin is enabled. Claude can then invoke the binary as a bare command from any Bash tool call, with no absolute path or wrapper script needed. Handy for packaging CLI helpers next to the commands, agents, and hooks that call them.</p>
<p className="digest-feature-try">Add a <code>bin/</code> directory at the plugin root:</p>
```text highlight={4, 5} theme={null}
my-plugin/
├── .claude-plugin/
│ └── plugin.json
└── bin/
└── my-tool
```
<a className="digest-feature-link" href="/docs/docs/en/plugins-reference#file-locations-reference">Plugins reference</a>
</div>
<div className="digest-wins">
<p className="digest-wins-title">Other wins</p>
<div className="digest-wins-grid">
<div>Auto mode follow-ups: new <code>PermissionDenied</code> hook fires on classifier denials (return <code>retry: true</code> to let Claude try a different approach), and <code>/permissions</code> → Recently denied lets you retry manually with <code>r</code></div>
<div>New <code>defer</code> value for <code>permissionDecision</code> in <code>PreToolUse</code> hooks: <code>-p</code> sessions pause at a tool call and exit with a <code>deferred\_tool\_use</code> payload so an SDK app or custom UI can surface it, then resume with <code>--resume</code></div>
<div><code>/buddy</code>: hatch a small creature that watches you code. An April Fools' joke, no longer available</div>
<div><code>disableSkillShellExecution</code> setting blocks inline shell from skills, slash commands, and plugin commands</div>
<div>Edit tool now works on files viewed via <code>cat</code> or <code>sed -n</code> without a separate Read</div>
<div>Hook output over 50K saved to disk with a path + preview instead of injected into context</div>
<div>Thinking summaries off by default in interactive sessions (<code>showThinkingSummaries: true</code> to restore)</div>
<div>Voice mode: push-to-talk modifier combos, Windows WebSocket, macOS Apple Silicon mic permission</div>
<div><code>claude-cli://</code> deep links accept multi-line prompts (encoded <code>%0A</code>)</div>
</div>
</div>
[Full changelog for v2.1.86–v2.1.91 →](/docs/en/changelog#2-1-86)
whats-new/2026-w15 First recorded · 115 lines, first recorded
# Week 15 · April 6–10, 2026
The first capture of this source. The page was already there, and this is what it said.
# Week 15 · April 6–10, 2026
> Ultraplan cloud planning, the Monitor tool with self-pacing /loop, /team-onboarding for packaging your setup, and /autofix-pr from your terminal.
<div className="digest-meta">
<span>Releases <a href="/docs/docs/en/changelog#2-1-92">v2.1.92 → v2.1.101</a></span>
<span>4 features · April 6–10</span>
</div>
<div className="digest-feature">
<div className="digest-feature-header">
<span className="digest-feature-title">Ultraplan</span>
<span className="digest-feature-pill">research preview</span>
</div>
<p className="digest-feature-lede">Kick off plan mode in the cloud from your terminal, then review the result in your browser. Claude drafts the plan in a Claude Code on the web session while your terminal stays free; when it's ready you comment on individual sections, ask for revisions, and choose to execute remotely or send it back to your CLI. As of v2.1.101 the first run auto-creates a default cloud environment, so there's no web setup step before you can try it.</p>
<Frame>
<video autoPlay muted loop playsInline className="w-full" src="https://mintcdn.com/claude-code/aFXPQxiBOW99MHS3/images/whats-new/ultraplan.mp4?fit=max&auto=format&n=aFXPQxiBOW99MHS3&q=85&s=e8f2f23730c6a5c289dbf3e7b13eadf6" data-path="images/whats-new/ultraplan.mp4" />
</Frame>
<p className="digest-feature-try">Run the command, or just include the keyword in any prompt:</p>
```text title="Claude Code" wrap theme={null}
/ultraplan migrate the auth service from sessions to JWTs
```
<a className="digest-feature-link" href="/docs/docs/en/ultraplan">Ultraplan guide</a>
</div>
<div className="digest-feature">
<div className="digest-feature-header">
<span className="digest-feature-title">Monitor tool</span>
<span className="digest-feature-pill">v2.1.98</span>
</div>
<p className="digest-feature-lede">A new built-in tool that spawns a background watcher and streams its events into the conversation: each event lands as a new transcript message that Claude reacts to immediately. Tail a training run, babysit a PR's CI, or auto-fix a dev server crash the moment it happens, all without a Bash sleep loop holding the turn open.</p>
<Frame>
<video autoPlay muted loop playsInline className="w-full" src="https://mintcdn.com/claude-code/aFXPQxiBOW99MHS3/images/whats-new/monitor-tool.mp4?fit=max&auto=format&n=aFXPQxiBOW99MHS3&q=85&s=f4156c15a0999de5c5157f54a3117c89" data-path="images/whats-new/monitor-tool.mp4" />
</Frame>
<p className="digest-feature-try">Ask Claude to watch something while you keep working:</p>
```text title="Claude Code" wrap theme={null}
Tail server.log in the background and tell me the moment a 5xx shows up
```
<p className="digest-feature-try">This pairs with <code>/loop</code>, which now self-paces: omit the interval and Claude schedules the next tick based on the task, or reaches for the Monitor tool to skip polling altogether.</p>
```text title="Claude Code" wrap theme={null}
/loop check CI on my PR
```
<a className="digest-feature-link" href="/docs/docs/en/tools-reference#monitor-tool">Monitor tool reference</a>
</div>
<div className="digest-feature">
<div className="digest-feature-header">
<span className="digest-feature-title">/autofix-pr</span>
<span className="digest-feature-pill">CLI</span>
</div>
<p className="digest-feature-lede">PR auto-fix landed on the web in Week 13. Now you can turn it on without leaving your terminal: <code>/autofix-pr</code> infers the open PR for your current branch and enables auto-fix for it on Claude Code on the web in one step. Push your branch, run the command, walk away; Claude watches CI and review comments and pushes fixes until it's green.</p>
<Frame>
<video autoPlay muted loop playsInline className="w-full" src="https://mintcdn.com/claude-code/aFXPQxiBOW99MHS3/images/whats-new/autofix-pr.mp4?fit=max&auto=format&n=aFXPQxiBOW99MHS3&q=85&s=95f191eb4711130a128aec3f6b720527" data-path="images/whats-new/autofix-pr.mp4" />
</Frame>
<p className="digest-feature-try">Run it from the PR's branch:</p>
```text title="Claude Code" wrap theme={null}
/autofix-pr
```
<a className="digest-feature-link" href="/docs/docs/en/claude-code-on-the-web#auto-fix-pull-requests">Auto-fix pull requests</a>
</div>
<div className="digest-feature">
<div className="digest-feature-header">
<span className="digest-feature-title">/team-onboarding</span>
<span className="digest-feature-pill">v2.1.101</span>
</div>
<p className="digest-feature-lede">Generates a teammate ramp-up guide from your local Claude Code usage. Run it in a project you know well and hand the output to a new teammate so they can replay your setup instead of starting from defaults.</p>
<p className="digest-feature-try">Run it from a project you've spent real time in:</p>
```text title="Claude Code" wrap theme={null}
/team-onboarding
```
<a className="digest-feature-link" href="/docs/docs/en/commands">Commands reference</a>
</div>
<div className="digest-wins">
<p className="digest-wins-title">Other wins</p>
<div className="digest-wins-grid">
<div>Focus view: press <code>Ctrl+O</code> in flicker-free mode to collapse the view to your last prompt, a one-line tool summary with diffstats, and Claude's final response</div>
<div>Guided <a href="/docs/docs/en/amazon-bedrock">Amazon Bedrock</a> and <a href="/docs/docs/en/google-vertex-ai">Google Cloud's Agent Platform</a> setup wizards on the login screen: pick "3rd-party platform" for step-by-step auth, region, credential check, and model pinning</div>
<div><code>/agents</code> gets a tabbed layout: a Running tab shows live subagents with a <code>● N running</code> count, plus Run agent and View running instance actions in the Library tab</div>
<div>Default effort level is now <code>high</code> for API-key, Amazon Bedrock, Google Cloud's Agent Platform, Microsoft Foundry, Team, and Enterprise users (control with <code>/effort</code>)</div>
<div><code>/cost</code> shows a per-model and cache-hit breakdown for subscription users</div>
<div><code>/release-notes</code> is now an interactive version picker</div>
<div>Status line: new <code>refreshInterval</code> setting re-runs the command every N seconds, and <code>workspace.git\_worktree</code> in the JSON input</div>
<div><code>CLAUDE\_CODE\_PERFORCE\_MODE</code>: Edit/Write fail on read-only files with a <code>p4 edit</code> hint instead of silently overwriting</div>
<div>OS CA certificate store is now trusted by default, so enterprise TLS proxies work without extra setup (<code>CLAUDE\_CODE\_CERT\_STORE=bundled</code> to opt out)</div>
<div>Amazon Bedrock powered by Mantle: set <code>CLAUDE\_CODE\_USE\_MANTLE=1</code></div>
<div>Hardened Bash tool permissions: backslash-escaped flags, env-var prefixes, <code>/dev/tcp</code> redirects, and compound commands now prompt correctly</div>
<div><code>UserPromptSubmit</code> hooks can set the session title via <code>hookSpecificOutput.sessionTitle</code></div>
</div>
</div>
[Full changelog for v2.1.92–v2.1.101 →](/docs/en/changelog#2-1-92)
whats-new/2026-w16 First recorded · 129 lines, first recorded
# Week 16 · April 13–17, 2026
The first capture of this source. The page was already there, and this is what it said.
# Week 16 · April 13–17, 2026
> Claude Opus 4.7 with the new xhigh effort level, Routines on Claude Code on the web, mobile push notifications that ping your phone when Claude needs you, a /usage breakdown that shows what's driving your limits, and native binaries replacing the bundled JavaScript.
<div className="digest-meta">
<span>Releases <a href="/docs/docs/en/changelog#2-1-105">v2.1.105 → v2.1.113</a></span>
<span>5 features · April 13–17</span>
</div>
<div className="digest-feature">
<div className="digest-feature-header">
<span className="digest-feature-title">Claude Opus 4.7</span>
<span className="digest-feature-pill">new model</span>
</div>
<p className="digest-feature-lede">Anthropic's strongest coding model yet is now the default on Max and Team Premium, and available everywhere else from <code>/model</code>. It adds a new <code>xhigh</code> effort level that sits between <code>high</code> and <code>max</code>: best results for most coding and agentic tasks, applied as the default the first time you switch to 4.7. <code>/effort</code> now opens an interactive arrow-key slider when you call it without arguments, so you can dial intelligence against speed without remembering the level names.</p>
<p className="digest-feature-try">Switch model and effort in one go:</p>
```text Claude Code theme={null}
> /model opus
> /effort xhigh
```
<a className="digest-feature-link" href="/docs/docs/en/model-config#adjust-effort-level">Model config: effort levels</a>
</div>
<div className="digest-feature">
<div className="digest-feature-header">
<span className="digest-feature-title">Routines</span>
<span className="digest-feature-pill">web</span>
</div>
<p className="digest-feature-lede">Templated cloud agents that fire on a schedule, a GitHub event, or an API call. Define a routine once on Claude Code on the web with a prompt, the repos it can touch, and the connectors it needs, then let PR-opened, release-published, or your own webhook trigger it without your machine running. The trigger picker now covers GitHub events with optional filters and gives every routine a tokened <code>/fire</code> endpoint for external systems.</p>
<Frame>
<img className="w-full" src="https://mintcdn.com/claude-code/FTi4SBJ9YRs7d-5X/images/whats-new/routines.png?fit=max&auto=format&n=FTi4SBJ9YRs7d-5X&q=85&s=2ba818ea9280c549511cb48b9b4d1dc5" alt="Creating a routine on Claude Code on the web with schedule, GitHub event, and API triggers" width="1440" height="810" data-path="images/whats-new/routines.png" />
</Frame>
<p className="digest-feature-try">Create one from the web UI, or scaffold from your terminal:</p>
```text Claude Code theme={null}
> /schedule daily PR review at 9am
```
<a className="digest-feature-link" href="/docs/docs/en/routines">Routines guide</a>
</div>
<div className="digest-feature">
<div className="digest-feature-header">
<span className="digest-feature-title">/usage breakdown</span>
<span className="digest-feature-pill">CLI</span>
</div>
<p className="digest-feature-lede">More visibility into where your Claude Code usage goes. <code>/usage</code> now shows what's driving your limits: parallel sessions, subagents, cache misses, and long context, each with a percentage of your last 24 hours and a tip to optimize it. Press <code>d</code> or <code>w</code> to switch between day and week views.</p>
<Frame>
<img className="w-full" src="https://mintcdn.com/claude-code/FTi4SBJ9YRs7d-5X/images/whats-new/usage.png?fit=max&auto=format&n=FTi4SBJ9YRs7d-5X&q=85&s=792a4b43cbef4e2931974831f076bca6" alt="The /usage command showing a breakdown of what's contributing to limits usage" width="1204" height="1182" data-path="images/whats-new/usage.png" />
</Frame>
<p className="digest-feature-try">Run it any time:</p>
```text Claude Code theme={null}
> /usage
```
<a className="digest-feature-link" href="/docs/docs/en/commands">Commands reference</a>
</div>
<div className="digest-feature">
<div className="digest-feature-header">
<span className="digest-feature-title">Mobile push notifications</span>
<span className="digest-feature-pill">mobile</span>
</div>
<p className="digest-feature-lede">With <a href="/docs/docs/en/remote-control">Remote Control</a> connected, Claude can send a push notification to your phone when a long task finishes or it needs a decision to keep going. Turn it on with "Push when Claude decides" in <code>/config</code>, or ask for one in your prompt. Useful when you kick off a long agent run and want to step away from the terminal.</p>
<Frame>
<video autoPlay muted loop playsInline className="w-full" src="https://mintcdn.com/claude-code/uII1TETOZxBUZ3lB/images/whats-new/push-notifications.mp4?fit=max&auto=format&n=uII1TETOZxBUZ3lB&q=85&s=c91a967139596500cbdb581a53822ac1" data-path="images/whats-new/push-notifications.mp4" />
</Frame>
<p className="digest-feature-try">Ask Claude to ping you when it's done:</p>
```text Claude Code theme={null}
> notify me when the tests pass
```
<a className="digest-feature-link" href="/docs/docs/en/remote-control#mobile-push-notifications">Remote Control: mobile push notifications</a>
</div>
<div className="digest-feature">
<div className="digest-feature-header">
<span className="digest-feature-title">Native binaries</span>
<span className="digest-feature-pill">v2.1.113</span>
</div>
<p className="digest-feature-lede">The <code>claude</code> CLI now spawns a native per-platform binary instead of bundled JavaScript, so the installed <code>claude</code> command no longer invokes Node. The npm package pulls the right binary in through an optional dependency such as <code>@anthropic-ai/claude-code-darwin-arm64</code>, so your install command doesn't change. The standalone installer already shipped this binary; npm now matches it.</p>
<p className="digest-feature-try">Upgrade and check what you're running:</p>
```bash theme={null}
claude update
claude --version
```
<a className="digest-feature-link" href="/docs/docs/en/setup">Setup guide</a>
</div>
<div className="digest-wins">
<p className="digest-wins-title">Other wins</p>
<div className="digest-wins-grid">
<div>New <a href="/docs/docs/en/ultrareview"><code>/ultrareview</code></a>: comprehensive code review in the cloud using parallel multi-agent analysis and an adversarial critique pass. Run it bare to review your current branch, or <code>/ultrareview \<PR#></code> for a specific PR</div>
<div><a href="/docs/docs/en/permission-modes#eliminate-prompts-with-auto-mode">Auto mode</a> is now available for Max subscribers on Opus 4.7, and the <code>--enable-auto-mode</code> flag is no longer required</div>
<div><a href="/docs/docs/en/interactive-mode#session-recap">Session recap</a> shows a one-line summary of what happened while you were away; run <code>/recap</code> on demand or turn it off from <code>/config</code></div>
<div>New <code>/tui</code> command and <code>tui</code> setting switch between classic and flicker-free rendering mid-conversation; focus view moved from <code>Ctrl+O</code> to its own <code>/focus</code> command</div>
<div>Plugins can ship background watchers via a top-level <code>monitors</code> manifest key that auto-arms at session start or on skill invoke</div>
<div>"Auto (match terminal)" option in <code>/theme</code> follows your terminal's dark/light mode</div>
<div><code>/fewer-permission-prompts</code> scans your transcripts for common read-only Bash and MCP calls and proposes an allowlist for <code>.claude/settings.json</code></div>
<div>Claude can now discover and run built-in commands like <code>/init</code>, <code>/review</code>, and <code>/security-review</code> via the Skill tool</div>
<div><code>PreCompact</code> hooks can block compaction by exiting with code 2 or returning <code>{"{"}"decision":"block"{"}"}</code></div>
<div><code>ENABLE\_PROMPT\_CACHING\_1H</code> opts API key, Amazon Bedrock, Google Cloud's Agent Platform, and Microsoft Foundry users into 1-hour prompt cache TTL</div>
<div><code>sandbox.network.deniedDomains</code> setting carves specific domains out of a broader <code>allowedDomains</code> wildcard</div>
<div><code>/undo</code> is now an alias for <code>/rewind</code>, and <code>/proactive</code> is an alias for <code>/loop</code></div>
<div>Hardened Bash permissions: deny rules now match through <code>env</code>/<code>sudo</code>/<code>watch</code> wrappers, and <code>Bash(find:\*)</code> allow rules no longer auto-approve <code>-exec</code> or <code>-delete</code></div>
</div>
</div>
[Full changelog for v2.1.105–v2.1.113 →](/docs/en/changelog#2-1-105)
whats-new/2026-w17 First recorded · 109 lines, first recorded
# Week 17 · April 20–24, 2026
The first capture of this source. The page was already there, and this is what it said.
# Week 17 · April 20–24, 2026
> /ultrareview opens as a research preview, automatic session recaps when you return to a terminal, custom color themes you can build and ship in plugins, and a redesigned Claude Code on the web.
<div className="digest-meta">
<span>Releases <a href="/docs/docs/en/changelog#2-1-114">v2.1.114 → v2.1.119</a></span>
<span>4 features · April 20–24</span>
</div>
<div className="digest-feature">
<div className="digest-feature-header">
<span className="digest-feature-title">/ultrareview</span>
<span className="digest-feature-pill">research preview</span>
</div>
<p className="digest-feature-lede">Now in public research preview. Ultrareview runs a fleet of bug-hunting agents in the cloud against your branch or a PR, and findings land back in the CLI or Desktop automatically. Run it before merging critical changes such as auth or data migrations.</p>
<Frame>
<video autoPlay muted loop playsInline className="w-full" src="https://mintcdn.com/claude-code/FTi4SBJ9YRs7d-5X/images/whats-new/ultrareview.mp4?fit=max&auto=format&n=FTi4SBJ9YRs7d-5X&q=85&s=0fb1271365d38f414ad155aeb8edb08e" data-path="images/whats-new/ultrareview.mp4" />
</Frame>
<p className="digest-feature-try">Review the branch you're on:</p>
```text Claude Code theme={null}
> /ultrareview
```
<p className="digest-feature-try">Or point it at a PR:</p>
```text Claude Code theme={null}
> /ultrareview 1234
```
<a className="digest-feature-link" href="/docs/docs/en/ultrareview">Ultrareview guide</a>
</div>
<div className="digest-feature">
<div className="digest-feature-header">
<span className="digest-feature-title">Session recap</span>
<span className="digest-feature-pill">CLI</span>
</div>
<p className="digest-feature-lede">Switch focus away from a session and come back to a one-line recap of what happened while you were gone. Helpful for staying in flow while running several Claude sessions at once.</p>
<Frame>
<video autoPlay muted loop playsInline className="w-full" src="https://mintcdn.com/claude-code/FTi4SBJ9YRs7d-5X/images/whats-new/session-recap.mp4?fit=max&auto=format&n=FTi4SBJ9YRs7d-5X&q=85&s=0a8db1470bd0161a47efeb2f322af76f" data-path="images/whats-new/session-recap.mp4" />
</Frame>
<p className="digest-feature-try">Generate a recap on demand, or turn the automatic one off from <code>/config</code>:</p>
```text Claude Code theme={null}
> /recap
```
<a className="digest-feature-link" href="/docs/docs/en/interactive-mode#session-recap">Interactive mode: session recap</a>
</div>
<div className="digest-feature">
<div className="digest-feature-header">
<span className="digest-feature-title">Custom themes</span>
<span className="digest-feature-pill">v2.1.118</span>
</div>
<p className="digest-feature-lede">Build and switch between named color themes from <code>/theme</code>, or hand-edit JSON files in <code>\~/.claude/themes/</code>. Each theme picks a base preset and overrides only the tokens you care about. Plugins can ship themes too.</p>
<p className="digest-feature-try">Open the theme picker and create a new one:</p>
```text Claude Code theme={null}
> /theme
```
<a className="digest-feature-link" href="/docs/docs/en/terminal-config#create-a-custom-theme">Terminal config: create a custom theme</a>
</div>
<div className="digest-feature">
<div className="digest-feature-header">
<span className="digest-feature-title">Claude Code on the web</span>
<span className="digest-feature-pill">web</span>
</div>
<p className="digest-feature-lede">A new look for <a href="https://claude.ai/code">claude.ai/code</a> that matches the redesigned desktop app: sessions sidebar, drag-and-drop layout, and a refreshed routines view. Key parts were rebuilt for quicker responses and a more reliable experience.</p>
<Frame>
<img className="w-full" src="https://mintcdn.com/claude-code/FTi4SBJ9YRs7d-5X/images/whats-new/web-redesign.jpeg?fit=max&auto=format&n=FTi4SBJ9YRs7d-5X&q=85&s=a2aca1b49e295b7337f5779038db8e2c" alt="Claude Code on the web redesign overview: new UI, speed and reliability, work across web, mobile, and CLI" width="1602" height="1610" data-path="images/whats-new/web-redesign.jpeg" />
</Frame>
<a className="digest-feature-link" href="/docs/docs/en/claude-code-on-the-web">Claude Code on the web</a>
</div>
<div className="digest-wins">
<p className="digest-wins-title">Other wins</p>
<div className="digest-wins-grid">
<div><a href="/docs/docs/en/interactive-mode#vim-editor-mode">Vim visual mode</a>: press <code>v</code> for character selection or <code>V</code> for line selection in the prompt input, with operators and visual feedback</div>
<div>Hooks can now call MCP tools directly via <a href="/docs/docs/en/hooks#mcp-tool-hook-fields"><code>type: "mcp\_tool"</code></a>, so a hook can hit an already-connected server without spawning a process</div>
<div><code>/cost</code> and <code>/stats</code> are merged into <a href="/docs/docs/en/commands"><code>/usage</code></a>; the old names still work as typing shortcuts that open the relevant tab</div>
<div><code>/config</code> changes (theme, editor mode, verbose, and similar) now persist to <code>\~/.claude/settings.json</code> and follow the same project/local/policy precedence as other <a href="/docs/docs/en/settings">settings</a></div>
<div><a href="/docs/docs/en/sub-agents#fork-the-current-conversation">Forked subagents</a> can be enabled on external builds with <code>CLAUDE\_CODE\_FORK\_SUBAGENT=1</code>: a fork inherits your full conversation context instead of starting fresh</div>
<div>Default <a href="/docs/docs/en/model-config#adjust-effort-level">effort level</a> for Pro and Max subscribers on Opus 4.6 and Sonnet 4.6 is now <code>high</code> (was <code>medium</code>)</div>
<div>Native macOS and Linux builds replace the <code>Glob</code> and <code>Grep</code> tools with embedded <code>bfs</code> and <code>ugrep</code> available through Bash, for faster searches without a separate tool round-trip</div>
<div><code>--from-pr</code> now accepts GitLab merge request, Bitbucket pull request, and GitHub Enterprise PR URLs in addition to github.com</div>
<div>Auto mode: include <code>"\$defaults"</code> in <a href="/docs/docs/en/auto-mode-config"><code>autoMode.allow</code>, <code>soft\_deny</code>, or <code>environment</code></a> to add custom rules alongside the built-in list instead of replacing it</div>
<div>New <a href="/docs/docs/en/plugin-dependencies#tag-plugin-releases-for-version-resolution"><code>claude plugin tag</code></a> command creates release git tags for plugins with version validation</div>
<div>Opus 4.7 sessions now compute against the model's native 1M context window, fixing inflated <code>/context</code> percentages and premature autocompaction</div>
<div><code>/resume</code> on large sessions is up to 67% faster and now offers to summarize stale, large sessions before re-reading them</div>
</div>
</div>
[Full changelog for v2.1.114–v2.1.119 →](/docs/en/changelog#2-1-114)
whats-new/2026-w18 First recorded · 109 lines, first recorded
# Week 18 · April 27 – May 1, 2026
The first capture of this source. The page was already there, and this is what it said.
# Week 18 · April 27 – May 1, 2026
> Claude Code on Windows runs without Git Bash, claude auth login accepts a pasted OAuth code when the browser callback can't reach localhost, claude project purge cleans up local state per project, and pasting a PR URL into /resume finds the session that created it.
<div className="digest-meta">
<span>Releases <a href="/docs/docs/en/changelog#2-1-120">v2.1.120 → v2.1.126</a></span>
<span>4 features · April 27 – May 1</span>
</div>
<div className="digest-feature">
<div className="digest-feature-header">
<span className="digest-feature-title">Sign in without a browser callback</span>
<span className="digest-feature-pill">v2.1.126</span>
</div>
<p className="digest-feature-lede"><code>claude auth login</code> now accepts the OAuth code pasted directly into the terminal when the browser callback can't reach localhost. That covers WSL2, SSH sessions, and containers, where the redirect to a local port doesn't work. The same release also fixes login timeouts on slow or proxied connections and in IPv6-only devcontainers.</p>
<p className="digest-feature-try">Sign in, then paste the code from the browser:</p>
```bash theme={null}
claude auth login
```
<a className="digest-feature-link" href="/docs/docs/en/cli-reference#cli-commands">CLI reference</a>
</div>
<div className="digest-feature">
<div className="digest-feature-header">
<span className="digest-feature-title">claude project purge</span>
<span className="digest-feature-pill">v2.1.124</span>
</div>
<p className="digest-feature-lede">Delete all Claude Code state for a project: transcripts, tasks, file history, and the project's config entry. Supports `--dry-run` to preview, `-y`/`--yes` to skip confirmation, `-i`/`--interactive` to choose, and `--all` to clear every project.</p>
<p className="digest-feature-try">Preview what would be removed:</p>
```bash theme={null}
claude project purge --dry-run
```
<p className="digest-feature-try">Then run it for real:</p>
```bash theme={null}
claude project purge
```
<a className="digest-feature-link" href="/docs/docs/en/cli-reference">CLI reference</a>
</div>
<div className="digest-feature">
<div className="digest-feature-header">
<span className="digest-feature-title">Resume by PR URL</span>
<span className="digest-feature-pill">v2.1.122</span>
</div>
<p className="digest-feature-lede">When you create a pull request with <code>gh pr create</code>, Claude Code links it to the session that produced it. Now you can get back to that session from the PR URL alone, without remembering its name.</p>
<p className="digest-feature-try">Open the session picker:</p>
```text Claude Code theme={null}
> /resume
```
<p className="digest-feature-try">Paste the PR URL into the picker. The first character of the paste drops you into search mode, and the list filters to the session that created that PR. Press Enter to resume it. GitHub, GitHub Enterprise, GitLab, and Bitbucket pull and merge request URLs all work.</p>
```text Claude Code theme={null}
https://github.com/your-org/your-repo/pull/1234
```
<p className="digest-feature-try">To open the picker already filtered to the PR, pass the PR number on the command line:</p>
```bash theme={null}
claude --from-pr 1234
```
<a className="digest-feature-link" href="/docs/docs/en/sessions#use-the-session-picker">Sessions: use the session picker</a>
</div>
<div className="digest-feature">
<div className="digest-feature-header">
<span className="digest-feature-title">Windows without Git Bash</span>
<span className="digest-feature-pill">Windows</span>
</div>
<p className="digest-feature-lede">Git for Windows is no longer required. When Bash is absent, Claude Code uses PowerShell as the shell tool, and when the PowerShell tool is enabled it is treated as the primary shell. PowerShell 7 installed via the Microsoft Store, MSI without PATH, or a <code>.NET</code> global tool is now detected automatically.</p>
<a className="digest-feature-link" href="/docs/docs/en/setup">Setup guide</a>
</div>
<div className="digest-wins">
<p className="digest-wins-title">Other wins</p>
<div className="digest-wins-grid">
<div>MCP servers can opt out of tool-search deferral with <code>alwaysLoad: true</code> in their config so all of that server's tools are always available</div>
<div>New <code>claude plugin prune</code> removes orphaned auto-installed plugin dependencies, and <code>plugin uninstall --prune</code> cascades</div>
<div><code>/skills</code> now has a type-to-filter search box so you can find a skill in a long list without scrolling</div>
<div><code>PostToolUse</code> hooks can replace tool output for any tool via <code>hookSpecificOutput.updatedToolOutput</code>, not only MCP tools</div>
<div>New <a href="/docs/docs/en/ultrareview"><code>claude ultrareview</code></a> subcommand runs <code>/ultrareview</code> non-interactively from CI or scripts: prints findings to stdout (<code>--json</code> for raw output) and exits 0 on completion or 1 on failure</div>
<div><code>--dangerously-skip-permissions</code> now bypasses prompts for writes to <code>.claude/</code>, <code>.git/</code>, <code>.vscode/</code>, shell config files, and other previously protected paths, while catastrophic removal commands still prompt as a safety net</div>
<div>The <code>/model</code> picker can list models from your gateway's <code>/v1/models</code> endpoint when <code>ANTHROPIC\_BASE\_URL</code> points at an Anthropic-compatible gateway; opt in with <code>CLAUDE\_CODE\_ENABLE\_GATEWAY\_MODEL\_DISCOVERY=1</code> since v2.1.129</div>
<div>MCP servers that hit a transient error during startup now auto-retry up to 3 times instead of staying disconnected</div>
<div><code>ANTHROPIC\_BEDROCK\_SERVICE\_TIER</code> selects an Amazon Bedrock service tier: <code>default</code>, <code>flex</code>, or <code>priority</code></div>
<div><code>/terminal-setup</code> enables iTerm2's clipboard access setting so <code>/copy</code> works, including from tmux</div>
<div>Google Cloud's Agent Platform now supports X.509 certificate-based Workload Identity Federation (mTLS ADC)</div>
<div>Significant memory leak fixes: image-heavy sessions, <code>/usage</code> on large transcript histories, and long-running tools without progress events</div>
</div>
</div>
[Full changelog for v2.1.120–v2.1.126 →](/docs/en/changelog#2-1-120)
whats-new/2026-w19 First recorded · 56 lines, first recorded
# Week 19 · May 4–8, 2026
The first capture of this source. The page was already there, and this is what it said.
# Week 19 · May 4–8, 2026
> Load plugins from .zip archives and URLs, search command history across every project with Ctrl+R, branch new worktrees from local HEAD or the remote default, and block actions unconditionally with auto mode hard deny rules.
<div className="digest-meta">
<span>Releases <a href="/docs/docs/en/changelog#2-1-128">v2.1.128 → v2.1.136</a></span>
<span>2 features · May 4–8</span>
</div>
<div className="digest-feature">
<div className="digest-feature-header">
<span className="digest-feature-title">Plugins from .zip archives and URLs</span>
</div>
<p className="digest-feature-lede">`--plugin-dir` now accepts a <code>.zip</code> plugin archive in addition to a directory, and the new `--plugin-url` flag fetches a plugin archive from a URL for the current session. Useful for trying a plugin before adding it to a marketplace, or for shipping internal plugins from an artifact store.</p>
<p className="digest-feature-try">Load a plugin straight from a URL:</p>
```bash terminal theme={null}
claude --plugin-url https://example.com/my-plugin.zip
```
<a className="digest-feature-link" href="/docs/docs/en/plugins">Plugins guide</a>
</div>
<div className="digest-feature">
<div className="digest-feature-header">
<span className="digest-feature-title">History search across all your projects</span>
<span className="digest-feature-pill">v2.1.129</span>
</div>
<p className="digest-feature-lede"><code>Ctrl+R</code> reverse-search now defaults to all prompts across every project, restoring the behavior from before v2.1.124. Press <code>Ctrl+S</code> while searching to narrow back to the current project or session. Handy when you remember a command you ran in another repo last week and don't want to go digging for it.</p>
<a className="digest-feature-link" href="/docs/docs/en/interactive-mode#command-history">Interactive mode: command history</a>
</div>
<div className="digest-wins">
<p className="digest-wins-title">Other wins</p>
<div className="digest-wins-grid">
<div>New <code>worktree.baseRef</code> setting (<code>fresh</code> | <code>head</code>) controls whether <code>--worktree</code>, the <code>EnterWorktree</code> tool, and agent-isolation worktrees branch from the remote default branch or local <code>HEAD</code>; the default <code>fresh</code> keeps unpushed commits out of new worktrees</div>
<div>New <code>settings.autoMode.hard\_deny</code> rules block matching actions unconditionally in auto mode, regardless of allow exceptions, for actions that should never run automatically even when broader allow rules apply</div>
<div>Hooks now receive the active effort level via the `effort.level` JSON input field and the `$CLAUDE_EFFORT` environment variable, and Bash tool commands can read <code>\$CLAUDE\_EFFORT</code></div>
<div><code>CLAUDE\_CODE\_DISABLE\_ALTERNATE\_SCREEN=1</code> opts out of the fullscreen alternate-screen renderer and keeps the conversation in the terminal's native scrollback</div>
<div><code>CLAUDE\_CODE\_PACKAGE\_MANAGER\_AUTO\_UPDATE</code> lets Homebrew or WinGet installations run the upgrade in the background and prompt to restart</div>
<div><code>CLAUDE\_CODE\_SESSION\_ID</code> is now in the Bash tool subprocess environment, matching the <code>session\_id</code> passed to hooks</div>
<div><code>/mcp</code> now shows the tool count for connected servers and flags servers that connected with 0 tools</div>
<div><code>--channels</code> now works with console (API key) authentication</div>
<div>Subprocesses such as Bash, hooks, MCP, and LSP no longer inherit <code>OTEL\_\*</code> environment variables, so OTEL-instrumented apps run via the Bash tool no longer pick up the CLI's own OTLP endpoint</div>
<div>Sub-agent progress summaries now hit the prompt cache, cutting <code>cache\_creation</code> token cost by roughly 3x</div>
<div>Several OAuth and credential reliability fixes: parallel sessions no longer dead-end at 401 after a refresh-token race, MCP OAuth refresh tokens are no longer lost when multiple servers refresh concurrently, and a rare login loop from a concurrent credential write is fixed</div>
<div>New <code>parentSettingsBehavior</code> admin key lets admins opt SDK <code>managedSettings</code> into the policy merge</div>
</div>
</div>
[Full changelog for v2.1.128–v2.1.136 →](/docs/en/changelog#2-1-128)
whats-new/2026-w20 First recorded · 91 lines, first recorded
# Week 20 · May 11–15, 2026
The first capture of this source. The page was already there, and this is what it said.
# Week 20 · May 11–15, 2026
> Manage every Claude Code session from one screen with agent view, keep Claude working toward a goal until a condition holds, and run fast mode on Opus 4.7 by default.
<div className="digest-meta">
<span>Releases <a href="/docs/docs/en/changelog#2-1-139">v2.1.139 → v2.1.142</a></span>
<span>3 features · May 11–15</span>
</div>
<div className="digest-feature">
<div className="digest-feature-header">
<span className="digest-feature-title">Agent view</span>
<span className="digest-feature-pill">research preview</span>
</div>
<p className="digest-feature-lede"><code>claude agents</code> opens one screen for every Claude Code session: what's running, what's blocked on your input, and what's done. Dispatch a bug fix, a pull request review, and a flaky-test investigation as three rows, keep working in another window, and step in only when a row needs you. Attach to any row to drop into its full conversation, then press <code>←</code> to return to the list. Each background session keeps running without a terminal attached.</p>
<Frame>
<video autoPlay muted loop playsInline className="w-full" src="https://mintcdn.com/claude-code/ITvjicPxe1SM3GX7/images/whats-new/agent-view.mp4?fit=max&auto=format&n=ITvjicPxe1SM3GX7&q=85&s=0eefe6cbe75464c8f7902bba630ab7a4" data-path="images/whats-new/agent-view.mp4" />
</Frame>
<p className="digest-feature-try">Open the dashboard from your shell:</p>
```bash terminal theme={null}
claude agents
```
<a className="digest-feature-link" href="/docs/docs/en/agent-view">Agent view</a>
</div>
<div className="digest-feature">
<div className="digest-feature-header">
<span className="digest-feature-title">/goal</span>
<span className="digest-feature-pill">v2.1.139</span>
</div>
<p className="digest-feature-lede">Set a completion condition and Claude keeps working toward it across turns without you prompting each step. After every turn, a fast model checks whether the condition holds; if not, Claude starts another turn instead of handing control back. Useful for substantial work with a verifiable end state, like migrating a module until every call site compiles and tests pass. The goal clears once the condition is met, and works in interactive, <code>-p</code>, and Remote Control.</p>
<Frame>
<video autoPlay muted loop playsInline className="w-full" src="https://mintcdn.com/claude-code/ITvjicPxe1SM3GX7/images/whats-new/goal.mp4?fit=max&auto=format&n=ITvjicPxe1SM3GX7&q=85&s=6806df3780c548b93a02d6fa71da276b" data-path="images/whats-new/goal.mp4" />
</Frame>
<p className="digest-feature-try">Set a goal and let Claude run until it holds:</p>
```text Claude Code theme={null}
> /goal all tests in test/auth pass and the lint step is clean
```
<a className="digest-feature-link" href="/docs/docs/en/goal">Goals</a>
</div>
<div className="digest-feature">
<div className="digest-feature-header">
<span className="digest-feature-title">Fast mode on Opus 4.7</span>
<span className="digest-feature-pill">research preview</span>
</div>
<p className="digest-feature-lede"><code>/fast</code> now runs on Opus 4.7 by default instead of Opus 4.6. Fast mode is a high-speed Opus configuration: the same model quality at about 2.5x the speed for a higher per-token cost, useful for rapid iteration and live debugging. Pricing is unchanged at \$30/\$150 per MTok, the same as Opus 4.6 fast mode. To pin fast mode to Opus 4.6, set <code>CLAUDE\_CODE\_OPUS\_4\_6\_FAST\_MODE\_OVERRIDE=1</code>.</p>
<Frame>
<img className="w-full" src="https://mintcdn.com/claude-code/ITvjicPxe1SM3GX7/images/whats-new/fast-mode-opus-47.png?fit=max&auto=format&n=ITvjicPxe1SM3GX7&q=85&s=6b6d92f7748ce5328a1ee9a269fb1a87" alt="The Claude Code model picker showing Opus 4.7 Fast 1M as the default with the Fast toggle on" width="3840" height="2160" data-path="images/whats-new/fast-mode-opus-47.png" />
</Frame>
<p className="digest-feature-try">Toggle fast mode, now running on Opus 4.7:</p>
```text Claude Code theme={null}
> /fast
```
<a className="digest-feature-link" href="/docs/docs/en/fast-mode#understand-the-cost-tradeoff">Fast mode on Opus 4.7</a>
</div>
<div className="digest-wins">
<p className="digest-wins-title">Other wins</p>
<div className="digest-wins-grid">
<div><code>claude agents</code> gained dispatch flags (<code>--add-dir</code>, <code>--settings</code>, <code>--mcp-config</code>, <code>--plugin-dir</code>, <code>--permission-mode</code>, <code>--model</code>, <code>--effort</code>, <code>--dangerously-skip-permissions</code>) to configure background sessions, and <code>claude agents --cwd \<path></code> scopes the session list to a directory</div>
<div>New hook <code>args: string\[]</code> exec form spawns the command directly without a shell, so path placeholders never need quoting</div>
<div>New <code>continueOnBlock</code> config option for <code>PostToolUse</code> hooks feeds the hook's rejection reason back to Claude and continues the turn instead of ending it</div>
<div>New <code>terminalSequence</code> field in hook JSON output lets hooks emit desktop notifications, window titles, and bells without a controlling terminal</div>
<div>The Rewind menu added "Summarize up to here" to compress earlier context while keeping recent turns intact</div>
<div>Remote Control, <code>/schedule</code>, Claude.ai MCP connectors, and notification preferences are now disabled when <code>ANTHROPIC\_API\_KEY</code>, <code>apiKeyHelper</code>, or <code>ANTHROPIC\_AUTH\_TOKEN</code> is set, even alongside a Claude.ai login; unset the API key to use these features</div>
<div>MCP stdio servers now receive <code>CLAUDE\_PROJECT\_DIR</code> in their environment, matching hooks, and plugin configs can reference <code>\${"{"}CLAUDE\_PROJECT\_DIR{"}"}</code> in commands</div>
<div><code>claude plugin details \<name></code> shows a plugin's component inventory and projected per-session token cost, and the <code>/plugin</code> details pane now also lists the LSP servers a plugin provides</div>
<div>Plugins with a root-level <code>SKILL.md</code> and no <code>skills/</code> subdirectory are now surfaced as a skill</div>
<div><code>/feedback</code> can now include recent sessions from the last 24 hours or 7 days for issues spanning more than the current session</div>
<div>Agent tool <code>subagent\_type</code> now matches case- and separator-insensitively, so <code>"Code Reviewer"</code> resolves to <code>code-reviewer</code></div>
</div>
</div>
[Full changelog for v2.1.139–v2.1.142 →](/docs/en/changelog#2-1-139)
whats-new/2026-w21 First recorded · 48 lines, first recorded
# Week 21 · May 18–22, 2026
The first capture of this source. The page was already there, and this is what it said.
# Week 21 · May 18–22, 2026
> Use auto mode on the Pro plan and with Sonnet 4.6, see which skills, subagents, and MCP servers drive your plan limits in /usage, and review diffs with the new /code-review command.
<div className="digest-meta">
<span>Releases <a href="/docs/docs/en/changelog#2-1-143">v2.1.143 → v2.1.149</a></span>
<span>1 feature · May 18–22</span>
</div>
<div className="digest-feature">
<div className="digest-feature-header">
<span className="digest-feature-title">Auto mode on the Pro plan</span>
<span className="digest-feature-pill">CLI</span>
</div>
<p className="digest-feature-lede">Auto mode is now available on the Pro plan and supports Sonnet 4.6 alongside Opus. It replaces permission prompts with background safety checks: routine actions run without interrupting you, and destructive or suspicious ones are blocked and surfaced.</p>
<p className="digest-feature-try">Update Claude Code, then cycle modes with Shift+Tab; auto mode appears in the cycle once your account meets the requirements:</p>
```bash terminal theme={null}
claude update
```
<p className="digest-feature-try">The command prints <code>Successfully updated</code> with the new version number, or <code>Claude Code is up to date</code> if no update is needed. Once auto mode is active, the prompt footer shows <code>auto mode on</code>.</p>
<a className="digest-feature-link" href="/docs/docs/en/permission-modes#eliminate-prompts-with-auto-mode">Auto mode requirements</a>
</div>
<div className="digest-wins">
<p className="digest-wins-title">Other wins</p>
<div className="digest-wins-grid">
<div><a href="/docs/docs/en/costs#track-your-costs"><code>/usage</code></a> now shows a per-category breakdown of what's driving your plan limits, attributing recent usage to skills, subagents, plugins, and individual MCP servers</div>
<div>"Extra usage" is renamed to "usage credits" across the CLI, and <code>/extra-usage</code> is now <code>/usage-credits</code>. The old name still works. The command requires signing in with your claude.ai subscription through <code>/login</code> and isn't available with API key authentication.</div>
<div>New <a href="/docs/docs/en/code-review"><code>/code-review</code></a> command reports correctness bugs at a chosen effort level such as <code>/code-review high</code>, and <code>--comment</code> posts findings as inline GitHub PR comments. <code>/simplify</code> remains as a separate cleanup-only review.</div>
<div>Background sessions now appear in <code>/resume</code> alongside interactive ones, marked with <code>bg</code>, and sessions pinned with <code>Ctrl+T</code> in <code>claude agents</code> stay alive when idle</div>
<div><code>claude agents --json</code> lists live sessions as JSON for scripting, such as status bars and session pickers</div>
<div>The PowerShell tool is now enabled by default on Windows for Amazon Bedrock, Google Cloud's Agent Platform, and Microsoft Foundry users; opt out with <code>CLAUDE\_CODE\_USE\_POWERSHELL\_TOOL=0</code></div>
<div><code>claude plugin disable</code> now refuses when another enabled plugin depends on the target, and <code>claude plugin enable</code> force-enables transitive dependencies</div>
<div>The <code>/plugin</code> marketplace browse pane shows projected context cost, and the Discover and Browse screens list a plugin's commands, agents, skills, hooks, and MCP/LSP servers before installation</div>
<div>New <code>worktree.bgIsolation: "none"</code> setting lets background sessions edit the working copy directly without <code>EnterWorktree</code>, for repos where worktrees are impractical</div>
<div>Markdown output renders GFM task list checkboxes, and the <code>/diff</code> detail view scrolls with the keyboard</div>
<div>Status line JSON input now includes GitHub repo and PR information when detected</div>
<div>Enterprise: the <code>allowAllClaudeAiMcps</code> managed setting loads claude.ai cloud MCP connectors alongside <code>managed-mcp.json</code></div>
</div>
</div>
[Full changelog for v2.1.143–v2.1.149 →](/docs/en/changelog#2-1-143)
whats-new/2026-w22 First recorded · 115 lines, first recorded
# Week 22 · May 25–29, 2026
The first capture of this source. The page was already there, and this is what it said.
# Week 22 · May 25–29, 2026
> Run Claude Code on Claude Opus 4.8, orchestrate large tasks with dynamic workflows, catch security issues with the security-guidance plugin, and use fast mode on Opus 4.8 at a lower price.
<div className="digest-meta">
<span>Releases <a href="/docs/docs/en/changelog#2-1-150">v2.1.150 → v2.1.157</a></span>
<span>4 features · May 25–29</span>
</div>
<div className="digest-feature">
<div className="digest-feature-header">
<span className="digest-feature-title">Claude Opus 4.8</span>
<span className="digest-feature-pill">new model</span>
</div>
<p className="digest-feature-lede">Opus 4.8 is now the default on Max, Team Premium, Enterprise pay-as-you-go, and the Anthropic API. It defaults to high effort; use <code>/effort xhigh</code> for harder tasks. Requires v2.1.154 or later.</p>
<Frame>
<video autoPlay muted loop playsInline className="w-full" src="https://mintcdn.com/claude-code/QsIrGXGFg6xd7joy/images/whats-new/opus-4-8.mp4?fit=max&auto=format&n=QsIrGXGFg6xd7joy&q=85&s=6ebcf5fe136467da2b254de1fe749ea7" data-path="images/whats-new/opus-4-8.mp4" />
</Frame>
<p className="digest-feature-try">Switch to Opus 4.8 by name, or pick it from the model picker:</p>
```text title="Claude Code" wrap theme={null}
/model claude-opus-4-8
```
<a className="digest-feature-link" href="/docs/docs/en/model-config#available-models">Model configuration</a>
</div>
<div className="digest-feature">
<div className="digest-feature-header">
<span className="digest-feature-title">Dynamic workflows</span>
<span className="digest-feature-pill">research preview</span>
</div>
<p className="digest-feature-lede">A workflow is an orchestration script Claude writes for your task and runs across many subagents in the background. Use one when a task is too large for one conversation to coordinate: a codebase-wide audit, a large migration, a research question that needs cross-checking. Manage runs with <code>/workflows</code>.</p>
<Frame>
<img className="w-full" src="https://mintcdn.com/claude-code/QsIrGXGFg6xd7joy/images/whats-new/dynamic-workflows.png?fit=max&auto=format&n=QsIrGXGFg6xd7joy&q=85&s=26671fa8607cec3453ed9753f821bd4f" alt="Claude Code on Opus 4.8 showing a Dynamic workflow requested indicator for a prompt that asks for a workflow to migrate every internal fetch() call" width="3840" height="2160" data-path="images/whats-new/dynamic-workflows.png" />
</Frame>
<p className="digest-feature-try">Describe the task and ask for a workflow:</p>
```text title="Claude Code" wrap theme={null}
create a workflow that migrates every internal fetch() call to the new HttpClient wrapper
```
<a className="digest-feature-link" href="/docs/docs/en/workflows">Dynamic workflows</a>
</div>
<div className="digest-feature">
<div className="digest-feature-header">
<span className="digest-feature-title">Security guidance plugin</span>
<span className="digest-feature-pill">plugin</span>
</div>
<p className="digest-feature-lede">The security-guidance plugin reviews Claude's code changes for vulnerabilities and fixes them in the same session. It runs a fast pattern check on each edit, a model review at the end of each turn, and a deeper agentic review on commit or push. Add project rules in <code>.claude/claude-security-guidance.md</code>.</p>
<Frame>
<video autoPlay muted loop playsInline className="w-full" src="https://mintcdn.com/claude-code/QsIrGXGFg6xd7joy/images/whats-new/security-guidance.mp4?fit=max&auto=format&n=QsIrGXGFg6xd7joy&q=85&s=c91d865936411586f42b24c558bcdd1d" data-path="images/whats-new/security-guidance.mp4" />
</Frame>
<p className="digest-feature-try">Install it from the official Anthropic marketplace:</p>
```text title="Claude Code" wrap theme={null}
/plugin install security-guidance@claude-plugins-official
```
<p className="digest-feature-try">Then activate it in the current session:</p>
```text title="Claude Code" wrap theme={null}
/reload-plugins
```
<a className="digest-feature-link" href="/docs/docs/en/security-guidance">Security guidance plugin</a>
</div>
<div className="digest-feature">
<div className="digest-feature-header">
<span className="digest-feature-title">Fast mode on Opus 4.8</span>
<span className="digest-feature-pill">research preview</span>
</div>
<p className="digest-feature-lede">Fast mode now defaults to Opus 4.8 at \$10/\$50 per MTok: 2x the standard rate for about 2.5x the speed. Opus 4.7 and 4.6 stay at \$30/\$150. Opus 4.6 fast mode is deprecated.</p>
<p className="digest-feature-try">Toggle fast mode, now on Opus 4.8:</p>
```text title="Claude Code" wrap theme={null}
/fast
```
<a className="digest-feature-link" href="/docs/docs/en/fast-mode#understand-the-cost-tradeoff">Fast mode pricing</a>
</div>
<div className="digest-wins">
<p className="digest-wins-title">Other wins</p>
<div className="digest-wins-grid">
<div>In <code>claude agents</code>, prefix a shell command with <code>!</code> to run it as a background job you can attach to and detach from; also available as <code>claude --bg --exec 'pytest -x'</code></div>
<div>Plugins in <code>.claude/skills</code> directories are now loaded automatically, no marketplace required, and <code>claude plugin init \<name></code> scaffolds a new plugin</div>
<div>New <code>/reload-skills</code> command re-scans skill directories without restarting, and <code>SessionStart</code> hooks can return <code>reloadSkills: true</code> to make skills they install available in the same session</div>
<div>Skills and commands can set <code>disallowed-tools</code> in frontmatter to remove tools from the model while the skill is active</div>
<div>New <code>MessageDisplay</code> hook event lets hooks transform or hide assistant message text as it is displayed</div>
<div>Claude Code now switches to your configured <code>--fallback-model</code> for the rest of the session when the primary model is not found, instead of failing every request</div>
<div>Plugins can declare <code>defaultEnabled: false</code> in <code>plugin.json</code> or a marketplace entry, so they install without turning on until you enable them</div>
<div>Vim mode: <code>/</code> in NORMAL mode opens reverse history search, matching Bash and Zsh vi-mode</div>
<div>Streaming tool execution is now always enabled, including with telemetry disabled and on Amazon Bedrock, Google Cloud's Agent Platform, and Microsoft Foundry</div>
<div><code>←←</code> to open the agents view now works on Amazon Bedrock, Google Cloud's Agent Platform, Microsoft Foundry, and with telemetry disabled</div>
<div>Claude in Chrome: pick which connected browser to use via <code>/chrome</code> → "Select browser…", or in-chat when a browser action runs with multiple connected</div>
<div><code>claude mcp list</code> and <code>claude mcp get</code> now show unapproved <code>.mcp.json</code> servers as pending approval instead of auto-approving and connecting when output is piped</div>
</div>
</div>
[Full changelog for v2.1.150–v2.1.157 →](/docs/en/changelog#2-1-150)
whats-new/2026-w23 First recorded · 98 lines, first recorded
# Week 23 · June 1–5, 2026
The first capture of this source. The page was already there, and this is what it said.
# Week 23 · June 1–5, 2026
> Run auto mode on Amazon Bedrock, Google Cloud's Agent Platform, and Microsoft Foundry, prompt before writing files that can run code in acceptEdits mode, list installed plugins with /plugin list, and require an approved version range for managed deployments.
<div className="digest-meta">
<span>Releases <a href="/docs/docs/en/changelog#2-1-158">v2.1.158 → v2.1.165</a></span>
<span>4 features · June 1–5</span>
</div>
<div className="digest-feature">
<div className="digest-feature-header">
<span className="digest-feature-title">Auto mode on Amazon Bedrock, Google Cloud's Agent Platform, and Microsoft Foundry</span>
<span className="digest-feature-pill">v2.1.158</span>
</div>
<p className="digest-feature-lede">Auto mode is now available on Amazon Bedrock, Google Cloud's Agent Platform, and Microsoft Foundry for Opus 4.7 and Opus 4.8, replacing permission prompts with background safety checks on third-party providers. Opt in by setting <code>CLAUDE\_CODE\_ENABLE\_AUTO\_MODE=1</code>.</p>
<p className="digest-feature-try">Opt in on a third-party provider, then cycle to auto mode with Shift+Tab:</p>
```bash terminal theme={null}
export CLAUDE_CODE_ENABLE_AUTO_MODE=1
```
<a className="digest-feature-link" href="/docs/docs/en/permission-modes#enable-auto-mode-on-bedrock-agent-platform-or-foundry">Auto mode on third-party providers</a>
</div>
<div className="digest-feature">
<div className="digest-feature-header">
<span className="digest-feature-title">Safer automatic edits</span>
<span className="digest-feature-pill">v2.1.160</span>
</div>
<p className="digest-feature-lede">Claude Code now prompts before writing files that can run code, even in <code>acceptEdits</code> mode. The protected set covers shell startup files such as <code>.zshenv</code> and <code>.bash\_login</code>, git config under <code>\~/.config/git/</code>, and build-tool configs such as <code>.npmrc</code>, <code>.bazelrc</code>, and <code>.pre-commit-config.yaml</code>. These writes are never auto-approved in any mode except <code>bypassPermissions</code>.</p>
<p className="digest-feature-try">Work in acceptEdits mode; Claude now pauses before writing these files:</p>
```bash terminal theme={null}
claude --permission-mode acceptEdits
```
<a className="digest-feature-link" href="/docs/docs/en/permission-modes#protected-paths">Protected paths</a>
</div>
<div className="digest-feature">
<div className="digest-feature-header">
<span className="digest-feature-title">List installed plugins with /plugin list</span>
<span className="digest-feature-pill">v2.1.163</span>
</div>
<p className="digest-feature-lede">The new <code>/plugin list</code> command prints your installed plugins inline, without opening the <code>/plugin</code> menu, and is also available as <code>claude plugin list</code> from the shell. In the interactive form, add `--enabled` or `--disabled` to show only plugins in that state.</p>
<p className="digest-feature-try">List the plugins that are currently turned on:</p>
```text Claude Code theme={null}
> /plugin list --enabled
```
<a className="digest-feature-link" href="/docs/docs/en/plugins-reference#plugin-list">Plugin commands</a>
</div>
<div className="digest-feature">
<div className="digest-feature-header">
<span className="digest-feature-title">Version requirements for managed deployments</span>
<span className="digest-feature-pill">v2.1.163</span>
</div>
<p className="digest-feature-lede">Two managed settings, <code>requiredMinimumVersion</code> and <code>requiredMaximumVersion</code>, let your organization require an approved Claude Code version range. A client outside the range exits at startup and tells the user to update through the organization's method. <code>claude update</code>, <code>claude install</code>, and <code>claude doctor</code> keep working so users can still recover.</p>
<p className="digest-feature-try">Add a floor to your managed settings so older clients refuse to start:</p>
```json managed-settings.json theme={null}
{
"requiredMinimumVersion": "2.1.163"
}
```
<a className="digest-feature-link" href="/docs/docs/en/admin-setup#decide-what-to-enforce">Decide what to enforce</a>
</div>
<div className="digest-wins">
<p className="digest-wins-title">Other wins</p>
<div className="digest-wins-grid">
<div>The trigger keyword for <a href="/docs/docs/en/workflows">dynamic workflows</a> changed from <code>workflow</code> to <code>ultracode</code>; asking for a workflow in your own words still works, and the keyword is highlighted in violet in the prompt</div>
<div><a href="/docs/docs/en/hooks">Stop and SubagentStop hooks</a> can return <code>hookSpecificOutput.additionalContext</code> to give Claude feedback and keep the turn going instead of being treated as an error</div>
<div><code>claude mcp</code> list, get, and add no longer print secrets: environment-variable references are not expanded, and credential headers and URL secrets are redacted</div>
<div>A failed Bash command in a parallel tool batch no longer cancels the others; each tool returns its own result independently</div>
<div>Editing a file no longer needs a separate Read first when you viewed it with a single-file <code>grep</code>, <code>egrep</code>, or <code>fgrep</code></div>
<div>Clicking a command in the autocomplete menu now fills it into your prompt instead of running it immediately; press Enter to run</div>
<div>Listing <code>Grep</code> or <code>Glob</code> in `--tools` now provides the dedicated search tools on native builds with embedded search, instead of silently ignoring those names</div>
<div><code>/effort</code> now confirms when your chosen level will persist as the default for new sessions</div>
<div><code>OTEL\_RESOURCE\_ATTRIBUTES</code> values are now attached as labels on metric datapoints, so you can slice usage metrics by custom dimensions like team or repo</div>
<div>Windsurf is renamed to Devin Desktop in <code>/ide</code>, <code>/terminal-setup</code>, and <code>/scroll-speed</code>, following the editor's rebrand</div>
<div><code>/btw</code> gains a <code>c to copy</code> shortcut that copies the raw markdown answer to the clipboard</div>
</div>
</div>
[Full changelog for v2.1.158–v2.1.165 →](/docs/en/changelog#2-1-158)
whats-new/2026-w24 First recorded · 80 lines, first recorded
# Week 24 · June 8–12, 2026
The first capture of this source. The page was already there, and this is what it said.
# Week 24 · June 8–12, 2026
> Move a session to a new directory with /cd, let subagents spawn their own subagents, and troubleshoot a broken configuration with safe mode.
<div className="digest-meta">
<span>Releases <a href="/docs/docs/en/changelog#2-1-166">v2.1.166 → v2.1.176</a></span>
<span>3 features · June 8–12</span>
</div>
<div className="digest-feature">
<div className="digest-feature-header">
<span className="digest-feature-title">Move a session with /cd</span>
<span className="digest-feature-pill">v2.1.169</span>
</div>
<p className="digest-feature-lede">The new <code>/cd</code> command moves the current session to a different working directory without rebuilding the prompt cache: the new directory's <code>CLAUDE.md</code> is appended as a message instead of replacing the system prompt. The session relocates to the new directory's project storage, so `--resume` and `--continue` find it there. Claude prompts you to trust the directory if you haven't worked in it before.</p>
<p className="digest-feature-try">Move the session into another project without restarting:</p>
```text Claude Code theme={null}
> /cd ../other-project
```
<a className="digest-feature-link" href="/docs/docs/en/commands#all-commands">Commands reference</a>
</div>
<div className="digest-feature">
<div className="digest-feature-header">
<span className="digest-feature-title">Subagents can spawn subagents</span>
<span className="digest-feature-pill">v2.1.172</span>
</div>
<p className="digest-feature-lede">Subagents can now spawn their own subagents. The subagent panel below the prompt shows the full tree: each row carries a count of its descendants and a path back to <code>main</code>. Subagent chains are capped at five levels deep to prevent runaway concurrent trees.</p>
<p className="digest-feature-try">Open the agents view to watch the nested tree as work fans out:</p>
```text Claude Code theme={null}
> /agents
```
<a className="digest-feature-link" href="/docs/docs/en/sub-agents#let-subagents-spawn-their-own-subagents">Spawn nested subagents</a>
</div>
<div className="digest-feature">
<div className="digest-feature-header">
<span className="digest-feature-title">Troubleshoot with safe mode</span>
<span className="digest-feature-pill">v2.1.169</span>
</div>
<p className="digest-feature-lede">Start Claude Code with `--safe-mode`, or set <code>CLAUDE\_CODE\_SAFE\_MODE</code>, to launch with all customizations disabled: <code>CLAUDE.md</code>, skills, plugins, hooks, MCP servers, and custom commands and agents do not load. Authentication, model selection, built-in tools, and permissions still work. If a problem disappears in safe mode, one of those surfaces is the cause.</p>
<p className="digest-feature-try">Launch a clean session to isolate a broken configuration:</p>
```bash terminal theme={null}
claude --safe-mode
```
<a className="digest-feature-link" href="/docs/docs/en/debug-your-config#test-against-a-clean-configuration">Test against a clean configuration</a>
</div>
<div className="digest-wins">
<p className="digest-wins-title">Other wins</p>
<div className="digest-wins-grid">
<div><a href="/docs/docs/en/model-config#fallback-model-chains"><code>fallbackModel</code></a> configures up to three fallback models tried in order when the primary is overloaded or unavailable, and `--fallback-model` now applies to interactive sessions too</div>
<div>Session titles are now generated in the language of your conversation; pin a specific one with the <code>language</code> setting</div>
<div>`claude agents --json` adds `--all` to include completed sessions plus new <code>id</code> and <code>state</code> fields, and no longer omits blocked or newly dispatched sessions</div>
<div>Browsing a marketplace's plugins in <code>/plugin</code> now has a search bar</div>
<div>New <code>disableBundledSkills</code> setting and <code>CLAUDE\_CODE\_DISABLE\_BUNDLED\_SKILLS</code> hide bundled skills, workflows, and built-in commands from the model</div>
<div>Deny rules accept a glob in the tool-name position, so <code>"\*"</code> denies all tools, and unknown tool names in deny rules now warn at startup</div>
<div>Agent messaging is hardened: messages relayed via <code>SendMessage</code> from other agents no longer carry user authority, and auto mode blocks them</div>
<div>Amazon Bedrock reads the AWS region from <code>\~/.aws</code> config files when <code>AWS\_REGION</code> is unset, and <code>/status</code> shows where the region came from</div>
<div>New <code>enforceAvailableModels</code> managed setting makes the <code>availableModels</code> allowlist also constrain the Default model</div>
<div>Claude in Chrome browser tools now load in a single batched call instead of one per tool</div>
<div><code>claude update</code> announces the target version before downloading instead of going silent</div>
<div>New <code>footerLinksRegexes</code> setting adds regex-matched link badges to the footer row</div>
</div>
</div>
[Full changelog for v2.1.166–v2.1.176 →](/docs/en/changelog#2-1-166)
whats-new/2026-w25 First recorded · 86 lines, first recorded
# Week 25 · June 15–19, 2026
The first capture of this source. The page was already there, and this is what it said.
# Week 25 · June 15–19, 2026
> Publish a live, shareable page from your session with Artifacts, match tool parameters in deny and ask rules, and set any setting from the prompt with /config.
<div className="digest-meta">
<span>Releases <a href="/docs/docs/en/changelog#2-1-178">v2.1.178 → v2.1.183</a></span>
<span>3 features · June 15–19</span>
</div>
<div className="digest-feature">
<div className="digest-feature-header">
<span className="digest-feature-title">Artifacts</span>
</div>
<p className="digest-feature-lede">An artifact is a live, interactive page that Claude Code publishes from your session to a private URL on claude.ai, and it updates in place as the session keeps working. Ask for one when terminal text is the wrong medium, such as a PR walkthrough with the diff annotated inline or a dashboard built from session data. Artifacts are in beta on Team and Enterprise plans.</p>
<Frame>
<video autoPlay muted loop playsInline className="w-full" src="https://mintcdn.com/claude-code/1ylKDoQynT1UgfEK/images/whats-new/artifacts.mp4?fit=max&auto=format&n=1ylKDoQynT1UgfEK&q=85&s=7f5391559d2bc69989621b36322fcff1" data-path="images/whats-new/artifacts.mp4" />
</Frame>
<p className="digest-feature-try">Ask Claude for a page, then approve the publish prompt:</p>
```text title="Claude Code" wrap theme={null}
Make an artifact that walks through this PR with the diff annotated inline.
```
<a className="digest-feature-link" href="/docs/docs/en/artifacts#create-an-artifact">Create an artifact</a>
</div>
<div className="digest-feature">
<div className="digest-feature-header">
<span className="digest-feature-title">Match by input parameter</span>
<span className="digest-feature-pill">v2.1.178</span>
</div>
<p className="digest-feature-lede">Deny and ask permission rules can now match a tool's input parameters with the <code>Tool(param:value)</code> syntax. For example, <code>Agent(model:opus)</code> matches subagent spawns that request the Opus model tier. The value accepts `*` as a wildcard, so `Agent(isolation:*)` matches any explicit isolation value.</p>
<p className="digest-feature-try">Add a parameter rule to the deny list in <code>settings.json</code>:</p>
```json .claude/settings.json {3} theme={null}
{
"permissions": {
"deny": ["Agent(model:opus)"]
}
}
```
<a className="digest-feature-link" href="/docs/docs/en/permissions#match-by-input-parameter">Match by input parameter</a>
</div>
<div className="digest-feature">
<div className="digest-feature-header">
<span className="digest-feature-title">Set any setting from the prompt</span>
<span className="digest-feature-pill">v2.1.181</span>
</div>
<p className="digest-feature-lede">Pass <code>key=value</code> to <code>/config</code> to change a setting directly without opening the Settings interface. The syntax also works in non-interactive mode with the <code>-p</code> flag and from Remote Control.</p>
<p className="digest-feature-try">Set the <code>thinking</code> setting from the prompt:</p>
```text title="Claude Code" wrap theme={null}
/config thinking=false
```
<a className="digest-feature-link" href="/docs/docs/en/commands#all-commands">Commands reference</a>
</div>
<div className="digest-wins">
<p className="digest-wins-title">Other wins</p>
<div className="digest-wins-grid">
<div>Auto mode now blocks destructive git commands (`git reset --hard`, `git clean -fd`, `git stash drop`) when you didn't ask to discard local work, and blocks <code>terraform destroy</code> unless you asked for the specific stack</div>
<div>Set the new <code>attribution.sessionUrl</code> setting to <code>false</code> to omit the claude.ai session link from commits and PRs in web and Remote Control sessions</div>
<div>In the <code>/config</code> interface, Enter and Space both change the selected setting, and Esc now saves and closes instead of reverting</div>
<div>New <code>sandbox.allowAppleEvents</code> opt-in setting lets sandboxed commands send Apple Events on macOS</div>
<div>Point <code>CLAUDE\_CLIENT\_PRESENCE\_FILE</code> at a marker file to suppress mobile push notifications while you're at the machine</div>
<div>Long paragraphs now stream line by line instead of waiting for the first line break</div>
<div>API connection drops mid-thinking now retry automatically instead of showing "Connection closed while thinking"</div>
<div>With <code>CLAUDE\_CODE\_EXPERIMENTAL\_AGENT\_TEAMS=1</code> set, every session has one implicit team, so you spawn teammates directly with the Agent tool's <code>name</code> parameter</div>
<div>Skills in nested <code>.claude/skills</code> directories load when working on files there; on a name clash the nested skill appears as `<dir>:<name>` so both stay available</div>
<div>Fixed prompt caching not reading on a custom <code>ANTHROPIC\_BASE\_URL</code> and on Microsoft Foundry</div>
<div>Fixed Write and Edit producing zero-byte or truncated files on network drives and cloud-synced folders</div>
</div>
</div>
[Full changelog for v2.1.178–v2.1.183 →](/docs/en/changelog#2-1-178)
whats-new/2026-w26 First recorded · 62 lines, first recorded
# Week 26 · June 22–26, 2026
The first capture of this source. The page was already there, and this is what it said.
# Week 26 · June 22–26, 2026
> Authenticate MCP servers from your shell with claude mcp login, get a response to shell mode command output with the ! prefix, and resume a conversation from before /clear with /rewind.
<div className="digest-meta">
<span>Releases <a href="/docs/docs/en/changelog#2-1-185">v2.1.185 → v2.1.193</a></span>
<span>2 features · June 22–26</span>
</div>
<div className="digest-feature">
<div className="digest-feature-header">
<span className="digest-feature-title">Authenticate MCP servers from the CLI</span>
<span className="digest-feature-pill">v2.1.186</span>
</div>
<p className="digest-feature-lede">New `claude mcp login <name>` and `claude mcp logout <name>` commands authenticate a configured MCP server from your shell instead of the interactive <code>/mcp</code> menu. `claude mcp login` runs the server's OAuth flow directly, and `claude mcp logout` clears the stored credentials.</p>
<p className="digest-feature-try">Run the OAuth flow for a configured server without opening a session:</p>
```bash terminal theme={null}
claude mcp login sentry
```
<a className="digest-feature-link" href="/docs/docs/en/mcp#authenticate-from-the-command-line">Authenticate from the command line</a>
</div>
<div className="digest-feature">
<div className="digest-feature-header">
<span className="digest-feature-title">Shell mode responds to command output</span>
<span className="digest-feature-pill">v2.1.186</span>
</div>
<p className="digest-feature-lede">Commands you run with the <code>!</code> prefix now get a response from Claude once the output lands in the transcript, so you can run <code>! npm test</code> and get an explanation of the failures without a second prompt. The response costs the same as sending a normal prompt. To keep the earlier behavior, where the output is added to context without a response, set <code>respondToBashCommands</code> to <code>false</code> in <code>settings.json</code>.</p>
<p className="digest-feature-try">Run a command and get a response to its output:</p>
```text Claude Code theme={null}
> ! npm test
```
<a className="digest-feature-link" href="/docs/docs/en/interactive-mode#shell-mode-with-prefix">Shell mode with the ! prefix</a>
</div>
<div className="digest-wins">
<p className="digest-wins-title">Other wins</p>
<div className="digest-wins-grid">
<div><code>/rewind</code> can now resume a conversation from before <code>/clear</code> was run</div>
<div>New <code>sandbox.credentials</code> setting blocks sandboxed commands from reading credential files and secret environment variables</div>
<div>Org-configured model restrictions now apply to the model picker, `--model`, <code>/model</code>, and <code>ANTHROPIC\_MODEL</code>, with a "restricted by your organization's settings" message when a restricted model is selected</div>
<div>New <code>autoMode.classifyAllShell</code> setting routes all Bash and PowerShell commands through the auto-mode classifier, and denial reasons now show in the transcript, the denial toast, and <code>/permissions</code></div>
<div>New <code>claude\_code.assistant\_response</code> OpenTelemetry log event carries the model's response text; deployments that already log prompt content start receiving it on upgrade, so set <code>OTEL\_LOG\_ASSISTANT\_RESPONSES=0</code> to keep prompts only</div>
<div>Background subagents now surface permission prompts in the main session instead of auto-denying; the dialog shows which agent is asking, and Esc denies only that tool</div>
<div><code>/install-github-app</code> can now install only the GitHub App and skip the Actions workflow and secret steps</div>
<div>Hosts you allow in the sandbox network permission dialog are remembered for the rest of the session instead of re-prompting on every connection</div>
<div>Streaming responses use about 37% less CPU, and long-session memory growth from the terminal output cache is reduced</div>
<div>`/review <pr>` now uses the same review engine as <code>/code-review medium</code></div>
<div>Bash mode <code>!</code> commands get live file path autocomplete</div>
</div>
</div>
[Full changelog for v2.1.185–v2.1.193 →](/docs/en/changelog#2-1-185)
whats-new/2026-w27 First recorded · 99 lines, first recorded
# Week 27 · June 29 – July 3, 2026
The first capture of this source. The page was already there, and this is what it said.
# Week 27 · June 29 – July 3, 2026
> Claude Sonnet 5 becomes the default model, Claude in Chrome reaches general availability, subagents run in the background by default, Claude Desktop arrives on Linux in beta, and /radio tunes into Claude FM.
<div className="digest-meta">
<span>Releases <a href="/docs/docs/en/changelog#2-1-195">v2.1.195 → v2.1.201</a></span>
<span>5 features · June 29 – July 3</span>
</div>
<div className="digest-feature">
<div className="digest-feature-header">
<span className="digest-feature-title">Claude Sonnet 5</span>
<span className="digest-feature-pill">new model</span>
</div>
<p className="digest-feature-lede">Sonnet 5 is the new default model for Pro, Team Standard, and Enterprise subscription seats: top-tier coding and tool use at Sonnet pricing, with a native 1M-token context window and adaptive thinking on by default. API pricing is promotional at \$2/\$10 per MTok through August 31. Requires v2.1.197 or later.</p>
<p className="digest-feature-try">Switch to Sonnet 5 by name, or pick it from the model picker:</p>
```text Claude Code theme={null}
> /model claude-sonnet-5
```
<a className="digest-feature-link" href="/docs/docs/en/model-config#available-models">Model configuration</a>
</div>
<div className="digest-feature">
<div className="digest-feature-header">
<span className="digest-feature-title">Claude in Chrome is generally available</span>
<span className="digest-feature-pill">v2.1.198</span>
</div>
<p className="digest-feature-lede">The Chrome integration is out of preview for everyone on a direct Anthropic plan. Claude Code drives your browser through the Claude in Chrome extension: it opens tabs, clicks through pages, fills forms, reads console logs, and shares your login state, so it can test the app it builds without you switching contexts.</p>
<a className="digest-feature-link" href="/docs/docs/en/chrome">Use Claude Code with Chrome</a>
</div>
<div className="digest-feature">
<div className="digest-feature-header">
<span className="digest-feature-title">Subagents run in the background by default</span>
<span className="digest-feature-pill">v2.1.198</span>
</div>
<p className="digest-feature-lede">Claude now keeps working while subagents run and picks up their results when they finish, instead of pausing the conversation to wait. Claude still runs a subagent in the foreground when it needs the result before continuing, and background subagents surface every permission prompt in your main session. Pin a subagent's behavior with the <code>background</code> frontmatter field.</p>
<a className="digest-feature-link" href="/docs/docs/en/sub-agents#run-subagents-in-foreground-or-background">Run subagents in foreground or background</a>
</div>
<div className="digest-feature">
<div className="digest-feature-header">
<span className="digest-feature-title">Claude Desktop on Linux</span>
<span className="digest-feature-pill">Desktop</span>
</div>
<p className="digest-feature-lede">The Claude desktop app is now available on Ubuntu 22.04+ and Debian 12+ in beta, on x86\_64 and arm64. You get the same Chat, Cowork, and Claude Code experience as macOS and Windows: parallel sessions, visual diff review, an integrated terminal and editor, and live app preview. Installs from Anthropic's apt repository, so updates arrive through regular package updates.</p>
<a className="digest-feature-link" href="/docs/docs/en/desktop-linux">Claude Desktop on Linux</a>
</div>
<div className="digest-feature">
<div className="digest-feature-header">
<span className="digest-feature-title">/radio</span>
<span className="digest-feature-pill">CLI</span>
</div>
<p className="digest-feature-lede">Claude FM is on the air. <code>/radio</code> opens the lo-fi radio stream in your browser for something to code to, and prints the stream URL when no browser is available. Not available on Amazon Bedrock, Google Cloud's Agent Platform, or Microsoft Foundry.</p>
<Frame>
<video autoPlay muted loop playsInline className="w-full" src="https://mintcdn.com/claude-code/x358isu_VzLnyTEN/images/whats-new/radio.mp4?fit=max&auto=format&n=x358isu_VzLnyTEN&q=85&s=36a0c33859cef119c7192dceea8bcbd3" data-path="images/whats-new/radio.mp4" />
</Frame>
<p className="digest-feature-try">Tune in from any session:</p>
```text Claude Code theme={null}
> /radio
```
<a className="digest-feature-link" href="/docs/docs/en/commands#all-commands">All commands</a>
</div>
<div className="digest-wins">
<p className="digest-wins-title">Other wins</p>
<div className="digest-wins-grid">
<div><a href="/docs/docs/en/artifacts">Artifacts</a> are now generally available and included on Pro and Max plans, joining Team and Enterprise</div>
<div>Admins can set an <a href="/docs/docs/en/model-config#organization-default-model">organization default model</a> in the org console; it shows as "Org default" in <code>/model</code> when you haven't picked a model yourself</div>
<div>Stacked skill invocations like <code>/skill-a /skill-b do XYZ</code> now load all leading skills (up to 5), not only the first</div>
<div><code>AskUserQuestion</code> dialogs no longer auto-continue by default; opt into an idle timeout via <code>/config</code></div>
<div>The "default" permission mode is now named "Manual" across the CLI, `--help`, VS Code, and JetBrains; `--permission-mode manual` is accepted alongside `default`</div>
<div>New <code>/dataviz</code> skill gives chart and dashboard design guidance, with a runnable color-palette validator</div>
<div>The built-in Explore agent now inherits the main session's model (capped at Opus) instead of running on Haiku</div>
<div>Background agents launched from <code>claude agents</code> now commit, push, and open a draft PR when they finish code work in a worktree, instead of stopping to ask</div>
<div>Hook matchers with hyphenated identifiers like <code>code-reviewer</code> now exact-match instead of substring-matching; use <code>mcp\_\_brave-search\_\_.\*</code> to match all tools from a hyphenated MCP server</div>
<div>Transient server rate-limit errors unrelated to your usage limit are now retried automatically with backoff for subscribers instead of failing the turn</div>
<div>The streaming idle watchdog is now on by default for all providers: it aborts and retries when a response stream produces no events for 5 minutes (<code>CLAUDE\_ENABLE\_STREAM\_WATCHDOG=0</code> to disable)</div>
</div>
</div>
[Full changelog for v2.1.195–v2.1.201 →](/docs/en/changelog#2-1-195)
whats-new/2026-w28 First recorded · 59 lines, first recorded
# Week 28 · July 6–10, 2026
The first capture of this source. The page was already there, and this is what it said.
# Week 28 · July 6–10, 2026
> Browse external sites from the Desktop app's built-in browser, run a full setup checkup with /doctor, and pick up auto mode transcript protections and agent view upgrades.
<div className="digest-meta">
<span>Releases <a href="/docs/docs/en/changelog#2-1-202">v2.1.202 → v2.1.206</a></span>
<span>2 features · July 6–10</span>
</div>
<div className="digest-feature">
<div className="digest-feature-header">
<span className="digest-feature-title">In-app browser on Desktop</span>
<span className="digest-feature-pill">Desktop</span>
</div>
<p className="digest-feature-lede">Claude Code on desktop now has a built-in browser. Claude can pull up docs, designs, or any other site, and read, click through, and interact with pages the same way it does with your local dev server previews. The browser is sandboxed and configurable: you choose whether browsing sessions persist, and safety classifiers review actions on external sites.</p>
<Frame>
<video autoPlay muted loop playsInline className="w-full" src="https://mintcdn.com/claude-code/x358isu_VzLnyTEN/images/whats-new/desktop-browser.mp4?fit=max&auto=format&n=x358isu_VzLnyTEN&q=85&s=8033e85a1cb0a37870a79e702c18f4e4" data-path="images/whats-new/desktop-browser.mp4" />
</Frame>
<a className="digest-feature-link" href="/docs/docs/en/desktop#browse-external-sites">Browse external sites</a>
</div>
<div className="digest-feature">
<div className="digest-feature-header">
<span className="digest-feature-title">/doctor is a full setup checkup</span>
<span className="digest-feature-pill">v2.1.205</span>
</div>
<p className="digest-feature-lede"><code>/doctor</code> now diagnoses issues and can fix them, instead of printing a read-only report. It checks installation health, finds unused skills, MCP servers, and plugins versus their context cost, deduplicates local <code>CLAUDE.md</code> files against checked-in ones, proposes trimming <code>CLAUDE.md</code> content Claude could derive from the codebase, and flags slow hooks. It reports findings first and asks for confirmation before changing anything. <code>/checkup</code> is its alias.</p>
<p className="digest-feature-try">Run a checkup from any session:</p>
```text Claude Code theme={null}
> /doctor
```
<a className="digest-feature-link" href="/docs/docs/en/commands#all-commands">All commands</a>
</div>
<div className="digest-wins">
<p className="digest-wins-title">Other wins</p>
<div className="digest-wins-grid">
<div>Auto mode now blocks tampering with session transcript files, and asks before running <code>rm -rf</code> on a variable it can't resolve from context</div>
<div><code>/cd</code> now suggests directory paths as you type, matching <code>/add-dir</code></div>
<div><code>/commit-push-pr</code> auto-allows <code>git push</code> to the repo's configured push remote in addition to <code>origin</code></div>
<div>Gateway: <code>/login</code> now supports Anthropic-operated public gateway endpoints</div>
<div><code>EnterWorktree</code> asks for confirmation before entering a git worktree outside the project's <code>.claude/worktrees/</code> directory</div>
<div>Background agents upgrade to a new version in the background right after a Claude Code update, instead of paying a slow stale-session upgrade when you attach</div>
<div>Agent view rows now show a colored state word and a classifier-written headline instead of raw tool call text, and sessions that edit, merge, comment on, or push to an existing PR link it in <code>claude agents</code></div>
<div>Auto-update binary downloads now stream to disk instead of buffering in memory, cutting the updater's peak memory usage by roughly 400 MB</div>
<div>Background task notifications now explicitly state that no human input has occurred, preventing fabricated in-transcript approvals from being acted on</div>
<div>Improved <code>/code-review</code> findings quality on Opus 4.8 across all effort levels</div>
</div>
</div>
[Full changelog for v2.1.202–v2.1.206 →](/docs/en/changelog#2-1-202)
whats-new/2026-w29 First recorded · 66 lines, first recorded
# Week 29 · July 13–17, 2026
The first capture of this source. The page was already there, and this is what it said.
# Week 29 · July 13–17, 2026
> Pull live data into published artifacts through MCP connectors, and use Claude Code with a screen reader in the new screen reader mode.
<div className="digest-meta">
<span>Releases <a href="/docs/docs/en/changelog#2-1-207">v2.1.207 → v2.1.212</a></span>
<span>2 features · July 13–17</span>
</div>
<div className="digest-feature">
<div className="digest-feature-header">
<span className="digest-feature-title">Artifacts call your MCP connectors</span>
<span className="digest-feature-pill">web</span>
</div>
<p className="digest-feature-lede">A published artifact can now call MCP connectors each time someone views it, so a dashboard shows live data and can take actions on demand rather than a snapshot from the session that built it. Each call runs through the viewing account's own connections, and viewers approve access before the page's first connector call. This week also adds public sharing links, editor roles for shared editing on Team and Enterprise plans, and artifacts created from Claude Tag sessions.</p>
<Frame>
<video autoPlay muted loop playsInline className="w-full" src="https://mintcdn.com/claude-code/ItzF3QVI6L0QypjJ/images/whats-new/artifacts-mcp.mp4?fit=max&auto=format&n=ItzF3QVI6L0QypjJ&q=85&s=ff8b81ed52b26c773899dc28cec959e6" data-path="images/whats-new/artifacts-mcp.mp4" />
</Frame>
<p className="digest-feature-try">Name the connector and the data you want in your prompt:</p>
```text title="Claude Code" wrap theme={null}
Build a dashboard artifact of open pull requests that pulls the live list through my GitHub connector when the page loads.
```
<a className="digest-feature-link" href="/docs/docs/en/artifacts#pull-live-data-with-mcp-connectors">Pull live data with MCP connectors</a>
</div>
<div className="digest-feature">
<div className="digest-feature-header">
<span className="digest-feature-title">Screen reader mode</span>
<span className="digest-feature-pill">CLI</span>
</div>
<p className="digest-feature-lede">Screen reader mode replaces the visual terminal interface with plain, linear text: instead of boxes, spinners, and in-place redraws, Claude Code prints labeled lines that a screen reader such as VoiceOver or NVDA reads in order, so you can approve permissions and review output end to end. Turn it on per session with a flag, per shell with the <code>CLAUDE\_AX\_SCREEN\_READER</code> environment variable, or everywhere with the <code>axScreenReader</code> setting.</p>
<p className="digest-feature-try">Start a session in screen reader mode:</p>
```bash terminal theme={null}
claude --ax-screen-reader
```
<a className="digest-feature-link" href="/docs/docs/en/accessibility#turn-on-screen-reader-mode">Turn on screen reader mode</a>
</div>
<div className="digest-wins">
<p className="digest-wins-title">Other wins</p>
<div className="digest-wins-grid">
<div><code>/fork</code> now copies your conversation into a new background session with its own row in <code>claude agents</code> while you keep working; the in-session forked subagent it used to launch is now <code>/subtask</code></div>
<div><a href="/docs/docs/en/permission-modes#enable-auto-mode-on-bedrock-agent-platform-or-foundry">Auto mode</a> no longer needs the <code>CLAUDE\_CODE\_ENABLE\_AUTO\_MODE</code> opt-in on Amazon Bedrock, Google Cloud's Agent Platform, and Microsoft Foundry; administrators can turn it off with <code>disableAutoMode</code></div>
<div>MCP tool calls that run longer than two minutes now move to the background automatically so the session stays usable; tune or disable the threshold with <code>CLAUDE\_CODE\_MCP\_AUTO\_BACKGROUND\_MS</code></div>
<div>New <code>claude auto-mode reset</code> restores the default auto-mode configuration, and `--yes` skips the confirmation prompt</div>
<div>New <a href="/docs/docs/en/corporate-launcher">corporate launcher</a> support: <code>CLAUDE\_CODE\_PROCESS\_WRAPPER</code> or the <code>processWrapper</code> setting runs the processes Claude Code starts from its own binary, such as the background service and agent view sessions, through a required wrapper executable</div>
<div><code>vimInsertModeRemaps</code> setting maps two-key insert-mode sequences such as <code>jj</code> to Escape in vim mode</div>
<div>`--forward-subagent-text` and <code>CLAUDE\_CODE\_FORWARD\_SUBAGENT\_TEXT</code> include subagent text and thinking blocks in <a href="/docs/docs/en/headless">stream-json output</a></div>
<div>Session-wide caps stop runaway loops: WebSearch calls and subagent spawns each default to 200, tunable with <code>CLAUDE\_CODE\_MAX\_WEB\_SEARCHES\_PER\_SESSION</code> and <code>CLAUDE\_CODE\_MAX\_SUBAGENTS\_PER\_SESSION</code></div>
<div>"Always allow" permission rules save at the repository root, so approvals granted in a git worktree persist across sessions and worktrees</div>
<div>Amazon Bedrock, Google Cloud's Agent Platform, and Claude Platform on AWS now default to Claude Opus 4.8</div>
<div>The collapsed tool summary line shows a live elapsed-time counter, so long-running tool calls visibly tick instead of looking stuck</div>
</div>
</div>
[Full changelog for v2.1.207–v2.1.212 →](/docs/en/changelog#2-1-207)
whats-new/2026-w30 First recorded · 87 lines, first recorded
# Week 30 · July 20–24, 2026
The first capture of this source. The page was already there, and this is what it said.
# Week 30 · July 20–24, 2026
> Opus 5 becomes the default Opus model, Claude Code Desktop adds an iOS Simulator pane, and the Claude Security plugin scans your code for vulnerabilities.
<div className="digest-meta">
<span>Releases <a href="/docs/docs/en/changelog#2-1-214">v2.1.214 → v2.1.219</a></span>
<span>3 features · July 20–24</span>
</div>
<div className="digest-feature">
<div className="digest-feature-header">
<span className="digest-feature-title">Claude Opus 5</span>
<span className="digest-feature-pill">new model</span>
</div>
<p className="digest-feature-lede">Claude Opus 5 is the new default Opus model in Claude Code. It's the default on Max, Team Premium, Enterprise pay-as-you-go, and the Anthropic API, and on Claude Platform on AWS, Amazon Bedrock, and Google Cloud's Agent Platform. On the Anthropic API and on Max, Team, and Enterprise plans, Opus 5 runs with a <a href="/docs/docs/en/model-config#extended-context">1M-token context window</a>; on Amazon Bedrock and Google Cloud's Agent Platform, select the 1M model variant. Fast mode moves to Opus 5 at \$10/\$50 per MTok. Requires v2.1.219 or later.</p>
<Frame>
<video autoPlay muted loop playsInline className="w-full" src="https://mintcdn.com/claude-code/N3yEaTYPXMXFrF6k/images/whats-new/opus-5.mp4?fit=max&auto=format&n=N3yEaTYPXMXFrF6k&q=85&s=8536b1cb3180e539008f39930403e47b" data-path="images/whats-new/opus-5.mp4" />
</Frame>
<p className="digest-feature-try">Switch to Opus 5 by name, or pick it from the model picker:</p>
```text Claude Code theme={null}
> /model claude-opus-5
```
<a className="digest-feature-link" href="/docs/docs/en/model-config#available-models">Model configuration</a>
</div>
<div className="digest-feature">
<div className="digest-feature-header">
<span className="digest-feature-title">iOS Simulator in Claude Code Desktop</span>
<span className="digest-feature-pill">Desktop</span>
</div>
<p className="digest-feature-lede">Claude Code Desktop on macOS gets an iOS Simulator pane, in public beta on Pro, Max, and Team plans. When Claude builds, launches, or checks your app in a simulator, the pane opens next to the conversation and streams the device screen live, so you can watch Claude tap through the app to verify its changes or drive the device yourself. Requires Xcode with the iOS platform installed, and Claude Desktop v1.24012.0 or later.</p>
<Frame>
<img className="w-full" src="https://mintcdn.com/claude-code/N3yEaTYPXMXFrF6k/images/whats-new/ios-simulator.jpg?fit=max&auto=format&n=N3yEaTYPXMXFrF6k&q=85&s=6c88418ed14ed0fb12cc1af75b17f2ee" alt="Claude Code Desktop with the iOS Simulator pane showing an iPhone app next to the conversation" width="2048" height="1152" data-path="images/whats-new/ios-simulator.jpg" />
</Frame>
<p className="digest-feature-try">Ask Claude to run or test your app, and the pane opens when the app launches:</p>
```text Claude Code theme={null}
> Build the app and run it in the simulator to check the onboarding flow.
```
<a className="digest-feature-link" href="/docs/docs/en/desktop-ios-simulator#run-your-app-in-the-simulator">Test iOS apps in the simulator</a>
</div>
<div className="digest-feature">
<div className="digest-feature-header">
<span className="digest-feature-title">Claude Security plugin</span>
<span className="digest-feature-pill">plugin</span>
</div>
<p className="digest-feature-lede">The Claude Security plugin runs a multi-agent vulnerability scan of your codebase inside a Claude Code session: agents map your architecture, build a threat model, hunt for vulnerabilities, and independently review every finding before writing the report to a <code>CLAUDE-SECURITY-\<timestamp>/</code> directory. Scan a whole repository or only a branch's diff, a pull request, or a single commit, then turn the findings you choose into reviewed patches that you apply yourself.</p>
<p className="digest-feature-try">Install the plugin from the official Anthropic marketplace, run <code>/reload-plugins</code>, then start a scan with <code>/claude-security</code>:</p>
```text Claude Code theme={null}
> /plugin install claude-security@claude-plugins-official
```
<a className="digest-feature-link" href="/docs/docs/en/claude-security#scan-and-fix-your-codebase">Scan and fix your codebase</a>
</div>
<div className="digest-wins">
<p className="digest-wins-title">Other wins</p>
<div className="digest-wins-grid">
<div><a href="/docs/docs/en/code-review#review-a-diff-locally"><code>/code-review</code></a> now runs as a background subagent with its own context window, so review work stays out of your conversation and the findings arrive when it completes</div>
<div><code>/verify</code>, <code>/code-review</code>, and <code>/deep-research</code> run only when you invoke them; Claude no longer launches them on its own</div>
<div><a href="/docs/docs/en/interactive-mode#emoji-shortcodes">Emoji shortcodes</a> autocomplete in the prompt input: type <code>:heart:</code> to insert an emoji, or two or more characters after <code>:</code> for suggestions; turn it off with <code>emojiCompletionEnabled</code></div>
<div>Skills with <code>context: fork</code> <a href="/docs/docs/en/skills#run-skills-in-a-subagent">run in the background</a> by default, and <code>background: false</code> in the skill's frontmatter waits for the result in the same turn</div>
<div>A session runs up to 20 subagents concurrently by default; change the <a href="/docs/docs/en/sub-agents#concurrent-subagent-limit">limit</a> with <code>CLAUDE\_CODE\_MAX\_CONCURRENT\_SUBAGENTS</code></div>
<div>`--max-budget-usd` now enforces the cap on subagents: once spend reaches it, Claude can't start more and running background subagents stop</div>
<div>New <a href="/docs/docs/en/sandboxing#disable-filesystem-isolation"><code>sandbox.filesystem.disabled</code></a> setting skips filesystem isolation while keeping network egress control</div>
<div>In auto mode, the checks for dangerous <code>rm</code> commands, background jobs, and suspicious Windows paths no longer open permission dialogs; the auto-mode classifier adjudicates them instead</div>
<div>Bash permission checks fail closed on more shell forms, including file-descriptor redirects, Zsh variable subscripts in <code>\[\[ ]]</code> comparisons, <code>help</code> and <code>man</code> invocations that could run unsafe options, and commands over 10,000 characters</div>
<div><a href="/docs/docs/en/fast-mode">Fast mode</a> no longer supports Opus 4.7: <code>/fast</code> now applies to Opus 5 and Opus 4.8</div>
<div>Long-running tool calls emit a periodic progress heartbeat instead of going silent</div>
</div>
</div>
[Full changelog for v2.1.214–v2.1.219 →](/docs/en/changelog#2-1-214)
whats-new/2026-w32 First recorded · 99 lines, first recorded
# Week 32 · August 3–7, 2026
The first capture of this source. The page was already there, and this is what it said.
# Week 32 · August 3–7, 2026
> Claude Code sessions message each other, self-hosted environments run cloud sessions on your infrastructure, and auto mode becomes the default permission mode.
<div className="digest-meta">
<span>Releases <a href="/docs/docs/en/changelog#2-1-220">v2.1.220 → v2.1.224</a></span>
<span>3 features · August 3–7</span>
</div>
<div className="digest-feature">
<div className="digest-feature-header">
<span className="digest-feature-title">Cross-session messaging</span>
<span className="digest-feature-pill">v2.1.224</span>
</div>
<p className="digest-feature-lede">Your Claude Code sessions can now message each other. Claude discovers your other sessions with the <code>ListAgents</code> tool and sends with <code>SendMessage</code>, either when you ask it to or on its own, such as after a change in one session affects what another is working on. A message is text Claude writes for the other session, never your conversation history or files. Available on macOS and Linux. Requires v2.1.224 or later.</p>
<Frame>
<video autoPlay muted loop playsInline className="w-full" src="https://mintcdn.com/claude-code/N3yEaTYPXMXFrF6k/images/whats-new/cross-session-messaging.mp4?fit=max&auto=format&n=N3yEaTYPXMXFrF6k&q=85&s=8f33c3390f78660a4a26dc980f46159f" data-path="images/whats-new/cross-session-messaging.mp4" />
</Frame>
<p className="digest-feature-try">With two sessions open on the same machine, ask one of them to pass something along:</p>
```text title="Claude Code" wrap theme={null}
Tell the session working on the payments API that users.name is now users.display_name
```
<p className="digest-feature-try">The other session shows a <code>Message from</code> row once Claude has read the message; press <code>Ctrl+O</code> to expand it. To see which sessions Claude can reach, run <code>/list-agents</code>.</p>
<a className="digest-feature-link" href="/docs/docs/en/cross-session-messaging#message-another-session">Message another session</a>
</div>
<div className="digest-feature">
<div className="digest-feature-header">
<span className="digest-feature-title">Self-hosted environments</span>
<span className="digest-feature-pill">v2.1.224</span>
</div>
<p className="digest-feature-lede">Self-hosted environments run Claude Code cloud sessions on your organization's own infrastructure, in public beta on Team and Enterprise plans. Run <code>claude self-hosted-runner</code> on your machines or containers to turn them into runners. When someone picks your environment while starting a session from claude.ai, the mobile or desktop apps, or `claude --cloud`, that session runs inside your network, with access to your internal services. An Owner or admin turns on <strong>Allow self-hosted environments</strong> in <a href="https://claude.ai/admin-settings/cloud-environments">admin settings</a> first.</p>
<Frame>
<img className="w-full" src="https://mintcdn.com/claude-code/N3yEaTYPXMXFrF6k/images/whats-new/self-hosted-environments.jpg?fit=max&auto=format&n=N3yEaTYPXMXFrF6k&q=85&s=ae9152cb1670c8af517d1aee57689b14" alt="The self-hosted environments admin page listing environments such as linux-dev and macos-prod with their status and active session counts" width="2048" height="1152" data-path="images/whats-new/self-hosted-environments.jpg" />
</Frame>
<p className="digest-feature-try">Signed in as an Owner or admin, run the guided setup, which walks you through creating the environment and starts a runner:</p>
```bash terminal theme={null}
claude self-hosted-runner setup
```
<p className="digest-feature-try">The environment shows <strong>Healthy</strong> in admin settings once the runner registers.</p>
<a className="digest-feature-link" href="/docs/docs/en/self-hosted-environments-quickstart#set-up-an-environment-and-runner">Self-hosted environments quickstart</a>
</div>
<div className="digest-feature">
<div className="digest-feature-header">
<span className="digest-feature-title">Auto mode becomes the default</span>
<span className="digest-feature-pill">CLI</span>
</div>
<p className="digest-feature-lede">Starting August 14, auto mode is the default permission mode for new sessions on Pro, Max, and Team plans. If you set a default mode yourself, it stays in place unless you accept the one-time switch prompt, and a default your organization manages doesn't change. You can still switch modes at any time. Already in effect on those plans: the classifier calls auto mode makes no longer count toward your usage limits.</p>
<p className="digest-feature-try">To start every session in auto mode before the switch, set it as your default in your user settings:</p>
```json ~/.claude/settings.json {3} theme={null}
{
"permissions": {
"defaultMode": "auto"
}
}
```
<p className="digest-feature-try">New sessions then show <code>auto mode on</code> in the status bar.</p>
<a className="digest-feature-link" href="/docs/docs/en/permission-modes#eliminate-prompts-with-auto-mode">Auto mode requirements and controls</a>
</div>
<div className="digest-wins">
<p className="digest-wins-title">Other wins</p>
<div className="digest-wins-grid">
<div>The VS Code extension gets <a href="/docs/docs/en/vs-code#extension-settings">Focus view</a>, which hides tool activity behind one expandable row per turn; toggle it from the command menu or with <code>Ctrl+Alt+F</code> (<code>Ctrl+Option+F</code> on Mac)</div>
<div>Sandbox credential files accept <a href="/docs/docs/en/sandboxing#mask-credential-files"><code>mode: "mask"</code></a> on Linux and WSL2, so sandboxed commands read a sentinel copy while the sandbox proxy substitutes the real value on egress; credential masking also gains <code>extract</code>, JWT-aware <code>decode</code>, and AWS SigV4 re-signing options</div>
<div>Marketplaces can distribute a plugin as a <a href="/docs/docs/en/plugin-marketplaces#zip-archives">zip archive</a> with the new <code>archive</code> source, downloaded over HTTPS with an optional SHA-256 pin, so installs work without git or npm</div>
<div><code>/review</code> is now an alias of <a href="/docs/docs/en/code-review#review-a-diff-locally"><code>/code-review</code></a>, and <code>/code-review</code> with no effort level reuses the level you typed last</div>
<div>A session you copy with <a href="/docs/docs/en/agent-view#copy-the-session-with-%2Ffork"><code>/fork</code></a> now makes its code changes in a worktree of its own instead of the original session's checkout</div>
<div>Plugins you install from <a href="/docs/docs/en/discover-plugins#install-plugins"><code>/plugin</code></a> activate in the current session when it's safe to do so; the install summary reports <code>Plugin is now active.</code> or tells you to run <code>/reload-plugins</code></div>
<div><a href="/docs/docs/en/agent-view#how-file-edits-are-isolated">Background sessions</a> that changed code in a worktree now commit and push before finishing, open a draft pull request only when the task calls for one, and follow the git instructions in your <code>CLAUDE.md</code></div>
<div>The 200-subagent-per-session cap is removed, so long-running sessions no longer refuse new subagents; the <a href="/docs/docs/en/sub-agents#concurrent-subagent-limit">concurrency</a> and depth limits still apply</div>
<div>A repository's checked-in settings can no longer turn on <a href="/docs/docs/en/remote-control#enable-remote-control-for-all-sessions">Remote Control auto-connect</a>; set <code>remoteControlAtStartup</code> in your user or managed settings instead, and project and local settings can only turn it off</div>
<div><a href="/docs/docs/en/worktrees#how-claude-code-enforces-isolation">Worktree isolation</a> now blocks not only file edits but also Bash commands and git redirects that reach the main checkout, in every session type and in the session's subagents</div>
<div>A Bash command can no longer hide part of itself from permission checks, and tab or invisible-Unicode padding no longer hides part of a command from the approval dialog</div>
<div>PreToolUse auto-allow hooks no longer bypass tool restrictions in Claude Code's internal side tasks such as summaries and compaction</div>
<div>The <a href="/docs/docs/en/ultraplan">Ultraplan</a> research preview is removed, including the <code>/ultraplan</code> command and the <code>ultraplan</code> keyword; use plan mode or Claude Code on the web instead</div>
</div>
</div>
[Full changelog for v2.1.220–v2.1.224 →](/docs/en/changelog#2-1-220)
whats-new/index First recorded · 157 lines, first recorded
# What's new
The first capture of this source. The page was already there, and this is what it said.
# What's new
> A weekly digest of notable Claude Code features, with code snippets, demos, and context on why they matter.
The weekly dev digest highlights the features most likely to change how you work. Each entry includes runnable code, a short demo, and a link to the full docs. For every bug fix and minor improvement, see the [changelog](/docs/en/changelog).
<Update label="Week 32" description="August 3–7, 2026" tags={["v2.1.220–v2.1.224"]}>
**Cross-session messaging**: on macOS and Linux, your Claude Code sessions can now message each other, so Claude passes a finding or a decision from one session to another instead of you re-explaining it.
Also this week: **self-hosted environments** run Claude Code cloud sessions on infrastructure your organization operates, in public beta on Team and Enterprise plans; **auto mode** becomes the default permission mode for new sessions on Pro, Max, and Team plans starting August 14; and the **VS Code extension** gets Focus view.
[Read the Week 32 digest →](/docs/en/whats-new/2026-w32)
</Update>
<Update label="Week 30" description="July 20–24, 2026" tags={["v2.1.214–v2.1.219"]}>
**Claude Opus 5**: the new default Opus model in Claude Code, with a 1M-token context window and fast mode at \$10/\$50 per MTok.
Also this week: **Claude Code Desktop** opens an iOS Simulator pane in public beta so Claude can run your app and tap through it while you watch; the **Claude Security plugin** runs a multi-agent vulnerability scan of your codebase and turns the findings you pick into patches you apply yourself; and **`/code-review`** runs as a background subagent.
[Read the Week 30 digest →](/docs/en/whats-new/2026-w30)
</Update>
<Update label="Week 29" description="July 13–17, 2026" tags={["v2.1.207–v2.1.212"]}>
**Artifacts call your MCP connectors**: a published artifact can pull live data and take actions through each viewer's own MCP connectors when they open the page, and this week also adds public sharing links, editor roles on Team and Enterprise, and artifacts created from Claude Tag sessions.
Also this week: **screen reader mode** replaces the visual terminal interface with plain, linear text for screen readers such as VoiceOver and NVDA; **`/fork`** copies your conversation into a new background session while you keep working; and **auto mode** no longer needs an opt-in variable on Amazon Bedrock, Google Cloud's Agent Platform, and Microsoft Foundry.
[Read the Week 29 digest →](/docs/en/whats-new/2026-w29)
</Update>
<Update label="Week 28" description="July 6–10, 2026" tags={["v2.1.202–v2.1.206"]}>
**In-app browser on Desktop**: Claude Code on desktop gets a built-in browser, so Claude can pull up docs, designs, or any other site and interact with pages the same way it does with your local dev server previews.
Also this week: **`/doctor`** is a full setup checkup that diagnoses issues and can fix them, with `/checkup` as its alias; **auto mode** blocks transcript tampering and asks before `rm -rf` on unresolved variables; and **agent view rows** show a colored state word and a classifier-written headline.
[Read the Week 28 digest →](/docs/en/whats-new/2026-w28)
</Update>
<Update label="Week 27" description="June 29 – July 3, 2026" tags={["v2.1.195–v2.1.201"]}>
**Claude Sonnet 5**: the new default model for Pro, Team Standard, and Enterprise subscription seats, with top-tier coding and tool use at Sonnet pricing, a native 1M-token context window, and adaptive thinking on by default.
Also this week: **Claude in Chrome** is generally available on all direct Anthropic plans; **subagents run in the background by default** so Claude keeps working while they run; **Claude Desktop on Linux** lands in beta on Ubuntu and Debian; and **`/radio`** tunes into Claude FM lo-fi radio.
[Read the Week 27 digest →](/docs/en/whats-new/2026-w27)
</Update>
<Update label="Week 26" description="June 22–26, 2026" tags={["v2.1.185–v2.1.193"]}>
**`claude mcp login`**: authenticate a configured MCP server from your shell instead of the interactive `/mcp` menu, and clear its stored credentials later with `claude mcp logout`.
Also this week: **shell mode responds to command output** (`! npm test` gets an explanation without a second prompt); **`/rewind`** can resume a conversation from before `/clear` was run; and **background subagents** now surface permission prompts in the main session instead of auto-denying.
[Read the Week 26 digest →](/docs/en/whats-new/2026-w26)
</Update>
<Update label="Week 25" description="June 15–19, 2026" tags={["v2.1.178–v2.1.183"]}>
**Artifacts**: turn a session's output into a live, shareable page on claude.ai that updates in place as the session works, now in beta on Team and Enterprise plans.
Also this week: **deny and ask rules match tool parameters** with `Tool(param:value)`, for example `Agent(model:opus)`; **`/config key=value`** sets any setting from the prompt, in `-p` mode, and from Remote Control; and **auto mode blocks destructive git commands** when you didn't ask to discard local work.
[Read the Week 25 digest →](/docs/en/whats-new/2026-w25)
</Update>
<Update label="Week 24" description="June 8–12, 2026" tags={["v2.1.166–v2.1.176"]}>
**`/cd`**: move the current session to a new working directory mid-conversation without rebuilding the prompt cache.
Also this week: **sub-agents can spawn their own sub-agents** (background chains are capped at five levels deep); **`--safe-mode`** starts Claude Code with all customizations disabled for troubleshooting; and **`fallbackModel`** configures up to three fallback models tried in order.
[Read the Week 24 digest →](/docs/en/whats-new/2026-w24)
</Update>
<Update label="Week 23" description="June 1–5, 2026" tags={["v2.1.158–v2.1.165"]}>
**Auto mode on Amazon Bedrock, Google Cloud's Agent Platform, and Microsoft Foundry**: auto mode is now available on third-party providers for Opus 4.7 and Opus 4.8, replacing permission prompts with background safety checks.
Also this week: **safer automatic edits** prompt before writing files that can run code in `acceptEdits` mode; **`/plugin list`** prints your installed plugins inline; and **version requirements** let managed deployments require an approved Claude Code version range.
[Read the Week 23 digest →](/docs/en/whats-new/2026-w23)
</Update>
<Update label="Week 22" description="May 25–29, 2026" tags={["v2.1.150–v2.1.157"]}>
**Claude Opus 4.8**: the new default model for Max, Team Premium, Enterprise pay-as-you-go, and Anthropic API accounts, with high effort by default and `/effort xhigh` for the hardest tasks.
Also this week: **dynamic workflows** orchestrate dozens to hundreds of subagents from a script Claude writes; the **security-guidance plugin** reviews Claude's changes for vulnerabilities as it works; and **fast mode** runs on Opus 4.8 at \$10/\$50 per MTok.
[Read the Week 22 digest →](/docs/en/whats-new/2026-w22)
</Update>
<Update label="Week 21" description="May 18–22, 2026" tags={["v2.1.143–v2.1.149"]}>
**Auto mode on the Pro plan**: auto mode now runs on Pro accounts and supports Sonnet 4.6 alongside Opus, replacing permission prompts with background safety checks.
Also this week: **`/usage`** breaks down what drives your plan limits by skill, subagent, plugin, and MCP server; the new **`/code-review`** command reports correctness bugs; and **background sessions** appear in `/resume` and stay alive when pinned.
[Read the Week 21 digest →](/docs/en/whats-new/2026-w21)
</Update>
<Update label="Week 20" description="May 11–15, 2026" tags={["v2.1.139–v2.1.142"]}>
**Agent view**: `claude agents` opens one screen for every Claude Code session, showing what's running, what's blocked on you, and what's done.
Also this week: **`/goal`** keeps Claude working across turns until a completion condition holds; **fast mode** now runs on Opus 4.7 by default; and the **Rewind menu** can compress earlier context with "Summarize up to here".
[Read the Week 20 digest →](/docs/en/whats-new/2026-w20)
</Update>
<Update label="Week 19" description="May 4–8, 2026" tags={["v2.1.128–v2.1.136"]}>
**Plugins load from `.zip` archives and URLs**: `--plugin-dir` now accepts `.zip` files, and `--plugin-url` fetches a plugin archive for the current session.
Also this week: **`worktree.baseRef`** chooses whether new worktrees branch from the remote default or local `HEAD`; **auto mode hard deny rules** block actions unconditionally regardless of allow exceptions; and **hooks see the active effort level** via `effort.level` and `$CLAUDE_EFFORT`.
[Read the Week 19 digest →](/docs/en/whats-new/2026-w19)
</Update>
<Update label="Week 18" description="April 27 – May 1, 2026" tags={["v2.1.120–v2.1.126"]}>
**Windows without Git Bash**: Git for Windows is no longer required, and Claude Code uses PowerShell as the shell tool when Bash is absent.
Also this week: **`claude ultrareview`** brings cloud code review to CI and scripts; **`claude project purge`** cleans up local state for a project; and pasting a **PR URL into `/resume`** finds the session that created it.
[Read the Week 18 digest →](/docs/en/whats-new/2026-w18)
</Update>
<Update label="Week 17" description="April 20–24, 2026" tags={["v2.1.114–v2.1.119"]}>
**`/ultrareview`** opens as a public research preview: a fleet of bug-hunting agents runs in the cloud and findings land back in your CLI or Desktop automatically.
Also this week: **session recap** shows you what happened while a terminal was unfocused; **custom themes** let you build and ship color palettes from `/theme` or a plugin; and **Claude Code on the web** gets a redesign with a new sessions sidebar and drag-and-drop layout.
[Read the Week 17 digest →](/docs/en/whats-new/2026-w17)
</Update>
<Update label="Week 16" description="April 13–17, 2026" tags={["v2.1.105–v2.1.113"]}>
**Claude Opus 4.7** lands as the new default on Max and Team Premium, with a new `xhigh` effort level that's the recommended setting for most coding work and an interactive `/effort` slider to dial it in.
Also this week: **Routines** on Claude Code on the web fire templated cloud agents from a schedule, GitHub event, or API call; **mobile push notifications** ping your phone when a long task finishes or Claude needs you; `/usage` shows what's driving your limits; and the CLI moves to native binaries.
[Read the Week 16 digest →](/docs/en/whats-new/2026-w16)
</Update>
<Update label="Week 15" description="April 6–10, 2026" tags={["v2.1.92–v2.1.101"]}>
**Ultraplan** enters early preview: draft a plan in the cloud from your CLI, review and comment on it in a web editor, then run it remotely or pull it back local. The first run now auto-creates a cloud environment for you.
Also this week: the **Monitor** tool streams background events into the conversation so Claude can tail logs and react live, `/loop` self-paces when you omit the interval, `/team-onboarding` packages your setup into a replayable guide, and `/autofix-pr` turns on PR auto-fix from your terminal.
[Read the Week 15 digest →](/docs/en/whats-new/2026-w15)
</Update>
<Update label="Week 14" description="March 30 – April 3, 2026" tags={["v2.1.86–v2.1.91"]}>
**Computer use** comes to the CLI in research preview: Claude can open native apps, click through UI, and verify changes from your terminal. Best for closing the loop on things only a GUI can verify.
Also this week: `/powerup` interactive lessons, flicker-free alt-screen rendering, a per-tool MCP result-size override up to 500K, and plugin executables on the Bash tool's `PATH`.
[Read the Week 14 digest →](/docs/en/whats-new/2026-w14)
</Update>
<Update label="Week 13" description="March 23–27, 2026" tags={["v2.1.83–v2.1.85"]}>
**Auto mode** lands in research preview: a classifier handles your permission prompts so safe actions run without interruption and risky ones get blocked. The middle ground between approving everything and `--dangerously-skip-permissions`.
Also this week: computer use in the Desktop app, PR auto-fix on Web, transcript search with `/`, a native PowerShell tool for Windows, and conditional `if` hooks.
[Read the Week 13 digest →](/docs/en/whats-new/2026-w13)
</Update>
workflows First recorded · 407 lines, first recorded
# Orchestrate subagents at scale with dynamic workflows ## When to use a workflow ## Run a bundled workflow ### Bundled workflows ### Watch the run ## Have Claude write a workflow ### Ask for a workflow in your prompt #### Dismiss or turn off the keyword #### Where the keyword works ### Let Claude decide with ultracode ### Approve the plan before it runs ### Save the workflow for reuse ### Distribute a workflow in a plugin ### Pass input to a saved workflow ## Example workflow prompts ### Audit many files for the same issue ### Keep fixing until a check passes ### Migrate many files in parallel ### Review every changed file and write one summary ### Research a topic across many sources ### Find issues until the list stops growing ### What the saved script looks like ## How a workflow runs ### Prompt caching in a fan-out ### Behavior and limits ## Manage runs ### Resume after a pause ### Cost ### Set a size guideline ### Turn workflows off ## Related resources
The first capture of this source. The page was already there, and this is what it said.
# Orchestrate subagents at scale with dynamic workflows
> Dynamic workflows orchestrate many subagents from a script Claude writes and you can rerun. Use them for codebase audits, large migrations, and cross-checked research.
<Note>
Dynamic workflows require Claude Code v2.1.154 or later and are available on all paid plans, with Anthropic API access, and on Amazon Bedrock, Google Cloud's Agent Platform, and Microsoft Foundry. On Pro, turn them on from the Dynamic workflows row in `/config`.
</Note>
A dynamic workflow is a JavaScript script that orchestrates [subagents](/docs/en/sub-agents) at scale. Claude writes the script for the task you describe, and a runtime executes it in the background while your session stays responsive.
Reach for a workflow when a task needs more agents than one conversation can coordinate, or when you want the orchestration codified as a script you can read and rerun. Examples include a codebase-wide bug sweep, a 500-file migration, a research question that needs sources cross-checked against each other, and a hard plan worth drafting from several independent angles before you commit to one.
## When to use a workflow
[Subagents](/docs/en/sub-agents), [skills](/docs/en/skills), [agent teams](/docs/en/agent-teams), and workflows can all run a multi-step task. The difference is who holds the plan:
| | Subagents | Skills | Agent teams | Workflows |
| :------------------------------ | :----------------------------- | :--------------------------- | :------------------------------------- | :----------------------------------- |
| What it is | A worker Claude spawns | Instructions Claude follows | A lead agent supervising peer sessions | A script the runtime executes |
| Who decides what runs next | Claude, turn by turn | Claude, following the prompt | The lead agent, turn by turn | The script |
| Where intermediate results live | Claude's context window | Claude's context window | A shared task list | Script variables |
| What's repeatable | The worker definition | The instructions | The team definition | The orchestration itself |
| Scale | A few delegated tasks per turn | Same as subagents | A handful of long-running peers | Dozens to hundreds of agents per run |
| Interruption | Restarts the turn | Restarts the turn | Teammates keep running | Resumable in the same session |
A workflow moves the plan into code. With subagents, skills, and agent teams, Claude is the orchestrator: it decides turn by turn what to spawn or assign next, and every result lands in a context window. A workflow script holds the loop, the branching, and the intermediate results itself, so Claude's context holds only the final answer.
Moving the plan into code also lets a workflow apply a repeatable quality pattern, not just run more agents: it can have independent agents adversarially review each other's findings before they're reported, or draft a plan from several angles and weigh them against each other, so you get a more trustworthy result than a single pass.
## Run a bundled workflow
The quickest way to see a workflow in action is to run `/deep-research`, the [built-in workflow](#bundled-workflows) Claude Code includes for investigating a question across many sources. You'll see agents work through a set of phases in the background while your session stays free, and get one report at the end instead of a turn-by-turn transcript.
<Steps>
<Step title="Run the workflow">
Run `/deep-research` with a question you want investigated. It fans out web searches across several angles, fetches and cross-checks the sources it finds, and synthesizes a cited report.
```text wrap theme={null}
/deep-research What changed in the Node.js permission model between v20 and v22?
```
</Step>
<Step title="Allow workflows">
Claude Code asks whether to allow the workflow. Select **Yes** to continue. The exact prompt depends on your permission mode. See [Approve the plan before it runs](#approve-the-plan-before-it-runs) for the per-mode options.
</Step>
<Step title="Watch progress">
The run starts in the background. Run `/workflows`, use the arrow keys to select the run, and press Enter to open its progress view:
```text wrap theme={null}
/workflows
```
The view shows each phase with its agent count, token total, and elapsed time. Drill into any phase to see its agents and what each one found. See [Watch the run](#watch-the-run) for the full set of controls.
You can also watch from the task panel below the input box: a one-line progress summary appears there while the run is going. Press the down arrow to focus it, then Enter to expand.
</Step>
<Step title="Read the report">
When the run finishes, the report lands in your session. It cites the sources each claim came from, with claims that didn't survive cross-checking already filtered out.
When the verifier agents can't check a claim, such as after a rate limit or API error, the report lists that claim as unverified instead of counting it as refuted.
</Step>
</Steps>
To run a workflow for your own task, [have Claude write one](#have-claude-write-a-workflow), and once a run does what you wanted you can [save it](#save-the-workflow-for-reuse) as a command of your own.
### Bundled workflows
Claude Code includes `/deep-research` as a built-in workflow:
| Command | What it does |
| :-------------------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `/deep-research <question>` | Fans out web searches on a question across several angles, fetches and cross-checks the sources it finds, votes on each claim, and returns a cited report with claims that didn't survive cross-checking filtered out. Requires the [WebSearch tool](/docs/en/tools-reference#websearch-tool-behavior) to be available |
`/deep-research` runs only when you invoke it.
[Workflows you save](#save-the-workflow-for-reuse) yourself become commands the same way and appear in `/` autocomplete alongside the bundled ones.
### Watch the run
Workflows run in the background, so the session stays responsive while agents work. Run `/workflows` at any time to list running and completed workflows, then select one to open its progress view.
```text wrap theme={null}
/workflows
```
The progress view shows each phase with its agent counts, token totals, and elapsed time. The footer lists the key for each action:
| Key | Action |
| :------------- | :-------------------------------------------------------------------------------------------------------------------------- |
| `↑` / `↓` | Select a phase or agent |
| `Enter` or `→` | Drill into the selected phase, then into an agent to read its prompt, recent tool calls, and result |
| `Esc` or `←` | Back out one level. In v2.1.203 through v2.1.205, `←` didn't step back out of a phase or agent; use `Esc` on those versions |
| `j` / `k` | Scroll within the agent detail when it overflows |
| `f` | Filter the agent list in the selected phase by status. Press again to cycle |
| `p` | Pause or resume the run |
| `x` | Stop the selected agent, or stop the whole workflow when focus is on the run |
| `r` | Restart the selected running agent |
| `s` | [Save](#save-the-workflow-for-reuse) the run's script as a command |
## Have Claude write a workflow
You can have Claude write a workflow for your task in two ways:
* [Ask for a workflow](#ask-for-a-workflow-in-your-prompt) in your prompt, either in your own words or by including the keyword `ultracode`, and Claude writes one for the task.
* [Let Claude decide with ultracode](#let-claude-decide-with-ultracode): set `/effort ultracode` and Claude plans a workflow for every substantive task in the session.
You can also run a workflow command that already exists: a [bundled workflow](#bundled-workflows) like `/deep-research`, or one you've [saved](#save-the-workflow-for-reuse).
### Ask for a workflow in your prompt
To run a single task as a workflow without changing the session's effort level, include the keyword `ultracode` in your prompt. Asking in your own words, for example "use a workflow" or "run a workflow", also works: Claude treats a direct request as the same opt-in. Before v2.1.160 the literal trigger keyword was `workflow`; natural-language requests work in both versions.
```text wrap theme={null}
ultracode: audit every API endpoint under src/routes/ for missing auth checks
```
Claude Code highlights the keyword in your input and Claude writes a workflow script for the task instead of working through it turn by turn. The keyword only chooses how Claude structures the work: a workflow started this way runs inside the session's existing [permission mode](/docs/en/permission-modes), and its agents' tool calls receive the same permission checks and [sandboxing](/docs/en/sandboxing) as any other tool call in the session.
If the run does what you wanted, you can [save it as a command](#save-the-workflow-for-reuse) afterward. If you already have an orchestrator built another way, such as a folder of subagent prompts or a skill that fans work out, you can point Claude at it and ask for a workflow that does the same thing.
#### Dismiss or turn off the keyword
If you didn't mean to start a workflow, press `Option+W` on macOS or `Alt+W` on Windows and Linux to dismiss the highlight for this prompt, or press backspace while the cursor is right after the highlighted keyword. To stop the keyword from triggering at all, turn off Ultracode keyword trigger in `/config`.
#### Where the keyword works
The keyword is an opt-in only in a prompt you type yourself: at the interactive prompt, in an IDE extension panel, in a [Remote Control](/docs/en/remote-control) client, or in an Agent SDK application that stamps your keyboard input's [`origin`](/docs/en/agent-sdk/typescript#sdkmessageorigin) as `{ kind: "human" }`. It doesn't start a workflow when it reaches the session another way:
* a prompt passed with `-p`
* a prompt an Agent SDK application sends without stamping it as human input
* a scheduled task prompt
* a webhook payload or pull request comment relayed into the conversation
<Note>
Before v2.1.210, the keyword started a workflow from any of these routes too, including a webhook payload or pull request comment relayed into the conversation.
</Note>
### Let Claude decide with ultracode
Ultracode is a Claude Code setting that combines `xhigh` [reasoning effort](/docs/en/model-config#adjust-effort-level) with automatic workflow orchestration. With it on, Claude plans a workflow for each substantive task instead of waiting for you to ask.
```text wrap theme={null}
/effort ultracode
```
To start a session with ultracode already on, launch with `claude --effort ultracode`. Requires Claude Code v2.1.203 or later.
With ultracode on, Claude decides when a task warrants a workflow. A single request can turn into several workflows in a row: one to understand the code, one to make the change, and one to verify it. This applies to every task in the session, so each request uses more tokens and takes longer than at lower effort levels.
Ultracode lasts for the current session and resets when you start a new one. Drop back with `/effort high` when you return to routine work. It's available on models that support `xhigh` [effort](/docs/en/model-config#adjust-effort-level); on other models the `/effort` menu doesn't offer it.
### Approve the plan before it runs
In the CLI, the per-run prompt shows the planned phases and these options:
* **Yes, run it**: start the run
* **Yes, and don't ask again for `<name>` in `<path>`**: start, and skip this prompt for this workflow in this project from now on
* **View raw script**: read the script before deciding
* **No**: cancel
`Ctrl+G` opens the script in your editor. `Tab` lets you adjust the prompt before the run starts.
Whether you see this prompt depends on your [permission mode](/docs/en/permission-modes):
| Permission mode | When you're prompted |
| :----------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------ |
| Default, accept edits | Every run, unless you've selected **Yes, and don't ask again** for that workflow in this project |
| Auto | First launch only. Any **Yes** records consent in your user settings, and later launches start without prompting. Skipped entirely when ultracode is on |
| Bypass permissions, `claude -p`, Agent SDK | Never. The run starts immediately |
In the Desktop app, an approval card shows the workflow name, the phase list, and a token-usage caution, with **Once**, **Always**, and **Deny** actions. The progress view appears in the Background tasks side pane.
Your permission mode controls only the launch prompt above. The subagents the workflow spawns always run in `acceptEdits` mode and inherit your [tool allowlist](/docs/en/settings#permission-settings), regardless of your session's mode. File edits are auto-approved.
Shell commands, web fetches, and MCP tools that aren't in your allowlist can still prompt you mid-run. To avoid this on a long run, add the commands the agents need to your allowlist before starting.
In `claude -p` and the Agent SDK there is no one to prompt, so tool calls follow your configured permission rules without interactive confirmation.
### Save the workflow for reuse
When Claude writes a workflow for a task you'll repeat, you can save that run's script as a command. A process like a review you run on every branch then runs the same orchestration each time.
Run `/workflows`, select the run you want to keep, and press `s`. In the save dialog, Tab toggles between the two save locations:
* `.claude/workflows/` in your project: shared with everyone who clones the repo
* `~/.claude/workflows/` in your home directory: available in every project, visible only to you. If you set [`CLAUDE_CONFIG_DIR`](/docs/en/env-vars), this location is the `workflows/` directory under that path.
The save dialog shows the resolved path for the personal location.
Press Enter to save. The workflow runs as `/<name>` in future sessions from either location.
Claude Code checks the save location for symlinks before writing, and shows an error instead of writing through one. What it checks depends on where you save:
* Project location: Claude Code refuses if `.claude`, `.claude/workflows`, or the target file is a symlink.
* Personal location: Claude Code refuses only if the target file itself is a symlink, so a `~/.claude` directory managed by a dotfiles tool still works.
Before v2.1.216, Claude Code followed the link, which could place the file outside the location you chose.
In a monorepo with several `.claude/` directories, you can keep workflows alongside the package they apply to. As of v2.1.178, saving to the project location writes to the closest `.claude/workflows/` directory that already exists between your working directory and the repository root, or to the repository root if none exists yet. Project workflows also load from every `.claude/workflows/` along that path, and when more than one defines the same name Claude Code runs the one closest to the working directory.
If a project workflow and a personal workflow share a name, the project one runs.
### Distribute a workflow in a plugin
To share a workflow across teams or repositories, include it in a [plugin](/docs/en/plugins). Place the script in a `workflows/` directory at the plugin root, or point to a different location with the [`workflows` manifest field](/docs/en/plugins-reference#component-path-fields).
Plugin workflows are namespaced by the plugin name. A plugin called `acme-tools` containing a script whose `meta.name` is `release-audit` runs as `/acme-tools:release-audit`.
### Pass input to a saved workflow
A saved workflow can accept input through the `args` parameter. The script reads it as a global named `args`. Use this to supply a research question, a list of target paths, or a configuration object at invocation time instead of editing the script for each run.
The following prompt runs a saved workflow with a list of issue numbers:
```text wrap theme={null}
Run /triage-issues on issues 1024, 1025, and 1030
```
Claude passes the list as structured data, so the script can call array and object methods on `args` directly without parsing it first. If `args` is omitted, the global is `undefined` inside the script.
## Example workflow prompts
A workflow fits best when the task is larger than one agent can hold in context, or when the same step needs to run across many items. The prompts below show common shapes. Each one asks Claude to write and run a workflow for that task; you don't write the script yourself.
### Audit many files for the same issue
Fan out one agent per file, then collect and verify the findings.
```text wrap theme={null}
use a workflow to audit every route handler under src/routes/ for missing authentication checks, and adversarially verify each finding before reporting it
```
### Keep fixing until a check passes
Run a checker, fix what failed, and repeat until it passes or stops making progress.
```text wrap theme={null}
use a workflow to run npx tsc --noEmit and keep fixing the reported errors until the type check passes or two rounds in a row make no progress
```
### Migrate many files in parallel
Discover the files to migrate, transform each one in an isolated copy so edits don't conflict, and verify each result.
```text wrap theme={null}
use a workflow to migrate every component under src/components/ from styled-components to Tailwind, working on each file in its own isolated copy
```
### Review every changed file and write one summary
Run a reviewer per file, then hand all the findings to one agent that ranks and deduplicates them.
```text wrap theme={null}
use a workflow to review every file changed in this PR for correctness issues, then merge the per-file findings into one ranked summary
```
### Research a topic across many sources
Fan out readers across changelogs, issues, and docs, then synthesize. The bundled `/deep-research` workflow does this; you can also describe a narrower version.
```text wrap theme={null}
use a workflow to research how our three competitors handle rate limiting: read their public docs and recent changelog entries in parallel, then compare the approaches
```
### Find issues until the list stops growing
Keep searching in rounds and stop when new rounds turn up nothing new.
```text wrap theme={null}
use a workflow to find flaky tests in this repo: run the suite repeatedly, record which tests fail intermittently, and stop once two rounds in a row find nothing new
```
### What the saved script looks like
When you [save a workflow](#save-the-workflow-for-reuse), the file in `.claude/workflows/` holds a `meta` block followed by a script body that orchestrates subagents. You usually don't need to edit it, but here is the shape of a small one so you can recognize what Claude generated:
```javascript theme={null}
export const meta = {
name: 'audit-routes',
description: 'Audit every route handler for missing auth checks',
}
const found = await agent('List every .ts file under src/routes/.', {
schema: { type: 'object', required: ['files'], properties: { files: { type: 'array', items: { type: 'string' } } } },
})
const audits = await pipeline(found.files, file =>
agent(`Audit ${file} for missing authentication checks.`, { label: file }),
)
return audits.filter(Boolean)
```
The body is plain JavaScript with top-level `await`. `agent()` spawns one subagent and `pipeline()` runs one per item in a list.
An `agent()` call resolves to `null` if you stop it mid-run or it hits an unrecoverable API error. `pipeline()` keeps that `null` in the results array, which is why the example ends with `.filter(Boolean)` to drop those entries.
If you want to edit a script by hand, ask Claude to walk you through the change, or see the Workflow tool entry in the [Agent SDK reference](/docs/en/agent-sdk/typescript) for the full set of options.
Cut at 300 lines. The page has the rest.
worktrees First recorded · 311 lines, first recorded
# Run parallel sessions with worktrees ## Start Claude in a worktree ### Set up the worktree environment ### Ask Claude to create a worktree ## Clean up worktrees ## Resume a worktree session ## How Claude Code enforces isolation ## Isolate subagents with worktrees ### Clean up subagent and background-session worktrees ## Customize worktree creation ### Choose the base branch ### Branch from a pull request ### Copy gitignored files into worktrees ### Reuse a worktree name ### Replace worktree creation with a hook ## What worktrees share with the main checkout ## Manage worktrees manually ## Non-git version control ## Troubleshooting ### Claude Code can't enter the worktree at startup ### Worktree creation fails on a symlinked path ### Claude Code refuses to use a worktree ### The session resumes outside its worktree ## See also
The first capture of this source. The page was already there, and this is what it said.
# Run parallel sessions with worktrees
> Isolate parallel Claude Code sessions in separate git worktrees so changes don't collide. Covers the `--worktree` flag, subagent isolation, `.worktreeinclude`, cleanup, and non-git VCS hooks.
A [git worktree](https://git-scm.com/docs/git-worktree) is a separate working directory with its own files and branch, sharing the same repository history and remote as your main checkout. Running each Claude Code session in its own worktree means edits in one session never touch files in another, so one session can build a feature while a second fixes a bug.
<Note>
Worktrees require a git repository; for other version control systems, [configure hooks to replace the git logic](#non-git-version-control). In the [desktop app](/docs/en/desktop#work-in-parallel-with-sessions), every new session gets its own worktree automatically.
</Note>
Worktrees are one of several ways to run Claude in parallel. They isolate file edits, while [subagents](/docs/en/sub-agents) and [agent teams](/docs/en/agent-teams) coordinate the work itself. See [Run agents in parallel](/docs/en/agents) to compare the approaches, or skip ahead to [Isolate subagents with worktrees](#isolate-subagents-with-worktrees) to use worktrees and subagents together.
Most sessions need only the first two sections: [start Claude in a worktree](#start-claude-in-a-worktree), then [clean up when you exit](#clean-up-worktrees). Return to the rest of the page when you need to [resume a session](#resume-a-worktree-session), [change how worktrees are created](#customize-worktree-creation), or [debug a failure](#troubleshooting).
## Start Claude in a worktree
Pass `--worktree` or `-w` with a name to create an isolated worktree and start Claude in it. By default, the worktree is created under `.claude/worktrees/<name>/` at your repository root, on a new branch named `worktree-<name>`:
```bash theme={null}
claude --worktree feature-auth
```
Run the command again with a different name in another terminal to start a second isolated session. If you omit the name, Claude generates one such as `bright-running-fox`.
Interactive runs require [workspace trust](/docs/en/security): if you haven't run Claude in the directory before, run `claude` once there to accept the trust dialog, or `--worktree` exits with an error prompting you to. Non-interactive runs with `-p` skip the trust check, so `claude -p --worktree` proceeds without it.
<Tip>
Add `.claude/worktrees/` to your `.gitignore` so worktree contents don't appear as untracked files in your main checkout.
</Tip>
### Set up the worktree environment
A worktree is a fresh checkout, so initialize your development environment there: ask Claude to install dependencies, or run your project's setup yourself in the worktree directory under `.claude/worktrees/`. To carry gitignored files such as `.env` into every new worktree automatically, add a [`.worktreeinclude` file](#copy-gitignored-files-into-worktrees).
### Ask Claude to create a worktree
You can also ask Claude to "work in a worktree" during a session, and it creates one with the [`EnterWorktree`](/docs/en/tools-reference) tool. Once in a worktree, Claude can switch directly to another one under `.claude/worktrees/` by calling `EnterWorktree` with the target path; the previous worktree stays on disk untouched.
When Claude enters a path outside the repository's `.claude/worktrees/` directory, Claude Code asks for your approval first, because the move takes the session's working directory, write access, and project configuration such as `CLAUDE.md` and settings to that location. An `EnterWorktree` [permission rule](/docs/en/permissions) or choosing "don't ask again" doesn't suppress this prompt; only `bypassPermissions` mode skips it. Before v2.1.206, Claude could enter any existing worktree path without asking.
## Clean up worktrees
When you exit an interactive worktree session, Claude checks the worktree for work that removal would delete: changed or untracked files, and new commits.
* **The worktree is clean**: for an unnamed session, Claude removes the worktree and its branch automatically. A [named](/docs/en/sessions#name-your-sessions) session prompts you first so you can keep the worktree for later
* **The worktree has work in it**: Claude prompts you to keep or remove the worktree. Keeping preserves the directory and branch so you can return later. Removing deletes the worktree directory and its branch, along with all the work in them
Non-interactive runs with `-p` have no exit prompt, so Claude doesn't clean up their worktrees. Remove them with `git worktree remove`.
On Windows, removing a worktree doesn't delete files outside it. If a folder inside the worktree is really a link to somewhere else, such as an NTFS junction or a directory symlink, Claude Code deletes only the link and keeps the folder it points to. Before v2.1.205, removing a worktree with a link nested in a subdirectory could delete the folder it pointed to.
## Resume a worktree session
When you resume a session that was inside a worktree, Claude Code returns the session to that worktree. This holds for interactive resumes, for `--continue` and `--resume` in [non-interactive mode](/docs/en/headless) with `-p`, and for the Agent SDK. Back inside the worktree, Claude can still exit it with the [`ExitWorktree`](/docs/en/tools-reference) tool.
Before returning the session to its worktree, Claude Code verifies that the worktree is still a checkout separate from the main one, and declines to re-enter a worktree that fails the check. For a git worktree, the check reads its git metadata. A worktree without git metadata, such as one a [`WorktreeCreate` hook](#non-git-version-control) created, can pass the check; the cases Claude Code still refuses are listed with their recoveries under [Claude Code refuses to use a worktree](#claude-code-refuses-to-use-a-worktree). For the messages and how to recover from each, see [The session resumes outside its worktree](#the-session-resumes-outside-its-worktree).
Where you launch from, and how you resume, change what Claude Code re-enters:
* **Launch directory**: resume from the main checkout or another directory of the repository. Claude Code re-enters a worktree it created with git under `.claude/worktrees/` even when you launch from inside it. When you launch from inside any other worktree, Claude Code re-enters it only if it can vouch for it from there: a worktree that is its own repository, one without git metadata, or a launch from a subdirectory of a worktree you created with `git worktree add` declines, so launch those from the main checkout.
* **`--fork-session`**: the forked session starts in the directory you launched Claude from, and Claude Code leaves the original session's worktree untouched.
* **Deleted worktree**: if the worktree directory no longer exists, Claude Code resumes the session in the directory you launched Claude from. It tells you the worktree is gone and clears the session's worktree binding.
<Note>
Before v2.1.212, a non-interactive resume stayed in the starting directory and `ExitWorktree` reported that there was no active worktree session to exit.
</Note>
When Claude enters or exits a worktree that Claude Code created with git, the transcript follows: Claude Code records the session under the session's new working directory, the same way [`/cd`](/docs/en/commands) does, so `/desktop` and `--resume` find it there. Exiting moves it back the same way. A worktree created by a [`WorktreeCreate` hook](#non-git-version-control) keeps its transcript at the launch directory. Requires Claude Code v2.1.198 or later.
## How Claude Code enforces isolation
While a session is isolated in a worktree, Claude Code blocks the tool calls the checks below define. The same rules apply whether you started the session with `--worktree`, Claude entered a worktree with `EnterWorktree`, or you resumed a worktree session.
The same enforcement covers every subagent Claude spawns from the isolated session. It applies whether the session is interactive or runs in the [background](/docs/en/agent-view#how-file-edits-are-isolated). [Subagents that run in their own worktree](#isolate-subagents-with-worktrees) carry the same checks. Their version history is under [Write subagent files](/docs/en/sub-agents#write-subagent-files).
Claude Code applies four checks:
* **File edits**: Claude Code blocks an `Edit`, `Write`, or `NotebookEdit` that targets a path in the main checkout.
* **Command working directory**: Claude Code blocks a Bash, PowerShell, or Monitor command whose working directory resolves to the main checkout, or whose working directory it can't verify stays outside it.
* **Git redirects**: Claude Code blocks a Bash or Monitor command that redirects git into the main checkout. The redirect can come through `git -C`, `--git-dir`, a `GIT_DIR` or `GIT_WORK_TREE` variable, or a `cd` into the main checkout before running git.
* **Command shape**: Claude Code blocks a Bash or Monitor command it can't verify stays inside the worktree. The block applies even when the command runs no git at all. Claude Code refuses shell constructs it can't statically trace, such as brace expansion and heredocs with unquoted delimiters. It tells Claude to break the command into plain, separate commands. You can't turn this check off.
The checks apply to the repository you launched Claude Code from. They also cover the main checkout a linked worktree is linked from. For PowerShell commands, Claude Code applies only the working-directory check.
Claude sees each refusal as a tool error that names the worktree and says how to proceed.
## Isolate subagents with worktrees
Subagents can run in their own worktrees so parallel edits don't conflict. Ask Claude to "use worktrees for your agents", or make the isolation permanent for a [custom subagent](/docs/en/sub-agents#supported-frontmatter-fields) by adding `isolation: worktree` to its frontmatter.
This subagent in `.claude/agents/` always runs in its own worktree:
```markdown theme={null}
---
name: refactorer
description: Applies mechanical refactors across many files
isolation: worktree
---
Apply the requested refactor across every affected file, then run the tests
and report the results.
```
Each subagent gets a temporary worktree that Claude Code removes automatically when the subagent finishes without changes; a worktree with changes stays on disk until the [periodic sweep below](#clean-up-subagent-and-background-session-worktrees) can remove it without losing work.
Subagent worktrees use the same [base branch](#choose-the-base-branch) as `--worktree`, so they branch from your repository's default branch unless `worktree.baseRef` is set to `"head"`.
### Clean up subagent and background-session worktrees
A periodic sweep removes worktrees that Claude created for subagents and [background sessions](/docs/en/agent-view#how-file-edits-are-isolated) once they are older than your [`cleanupPeriodDays`](/docs/en/settings#available-settings) setting, following the [retention sweep rules](/docs/en/claude-directory#cleaned-up-automatically). The sweep skips a worktree that still holds work: changed or untracked files, or unpushed commits. It never removes worktrees you create with `--worktree`.
While an agent is running, Claude runs `git worktree lock` on its worktree so that concurrent cleanup cannot remove it. The lock is released when the agent finishes.
The sweep also releases a lock Claude Code set for a session whose process has exited, so a killed background session doesn't leave its worktree permanently locked. The sweep never releases a lock you set yourself with `git worktree lock`. Before v2.1.210, a lock left by a killed session stayed in place until you ran `git worktree unlock`.
To clean up a worktree that the sweep keeps, run `git worktree remove`, adding `--force` if the worktree has uncommitted changes or untracked files.
## Customize worktree creation
Claude Code's defaults for creating worktrees cover most sessions: it creates them under `.claude/worktrees/`, branches them from your repository's default branch, and checks out only tracked files. The options in this section change those defaults.
### Choose the base branch
New worktrees branch from the repository's default branch, so most sessions don't need this setting. Set `worktree.baseRef` in [settings](/docs/en/settings#worktree-settings) to branch from your current work instead. The setting accepts two values:
* `"fresh"` (default): branch from the repository's default branch on the remote, usually `main`, so the worktree starts from a clean tree matching the remote.
* `"head"`: branch from your current local `HEAD`, so the worktree carries your unpushed commits and feature-branch state. Use this when isolating subagents that need to operate on in-progress work. Inside a worktree, `"head"` resolves to that worktree's `HEAD`, not the main checkout's.
You can't set `worktree.baseRef` to a branch name. To start a worktree from a specific existing branch, [create it with git directly](#manage-worktrees-manually).
For a `"fresh"` base, Claude Code keeps `origin/HEAD` current: when the repository hasn't been fetched in the last 24 hours, it fetches the default branch, capped at five seconds, and uses the locally cached ref if the fetch fails. If no remote is configured, or `origin/HEAD` isn't cached locally and can't be fetched, the worktree falls back to your current local `HEAD`. Before v2.1.208, a fresh worktree used whatever `origin/HEAD` was already cached locally.
This example makes every new worktree branch from your current work:
```json theme={null}
{
"worktree": {
"baseRef": "head"
}
}
```
### Branch from a pull request
To branch from a specific pull request, pass `--worktree` the PR number prefixed with `#`, or a full GitHub pull request URL. Claude Code fetches `pull/<number>/head` from `origin` and creates the worktree at `.claude/worktrees/pr-<number>`. Quote the argument so your shell doesn't treat `#` as the start of a comment:
```bash theme={null}
claude --worktree "#1234"
```
### Copy gitignored files into worktrees
A worktree is a fresh checkout, so untracked files like `.env` or `.env.local` from your main repository are not present. To copy them automatically when Claude creates a worktree, add a `.worktreeinclude` file to your project root.
The file uses `.gitignore` syntax. Only files that match a pattern and are also gitignored are copied, so tracked files are never duplicated.
This `.worktreeinclude` copies two env files and a secrets config into each new worktree:
```text .worktreeinclude theme={null}
.env
.env.local
config/secrets.json
```
This applies to every worktree Claude Code creates with git: `--worktree` worktrees, [subagent worktrees](#isolate-subagents-with-worktrees), and parallel sessions in the [desktop app](/docs/en/desktop#work-in-parallel-with-sessions). With a [`WorktreeCreate` hook](#non-git-version-control), copy the files inside the hook script.
### Reuse a worktree name
Passing `--worktree` a name whose directory already exists opens that existing worktree instead of creating a new one.
With the default `"fresh"` [base](#choose-the-base-branch), a reopened worktree resets to the repository's default branch instead of continuing at its old tip when all of the following hold:
* It has no uncommitted changes or untracked files.
* It is still on the branch Claude Code created for it.
* It has no commits of its own, or its pull request was merged and its remote branch deleted.
Claude Code detects the merged case from git state alone: the remote branch the worktree pushed to no longer exists, and every commit in the worktree is already on the default branch.
Anything else reopens at the old tip: a worktree that fails any of the conditions, one whose state can't be verified, and any reuse when `worktree.baseRef` is `"head"` or the name is a pull request number. Before v2.1.208, a reused name always reopened the old worktree at its old tip.
### Replace worktree creation with a hook
Configure a [`WorktreeCreate` hook](/docs/en/hooks#worktreecreate) to replace the default `git worktree` logic entirely, including placing worktrees somewhere other than `.claude/worktrees/`. For a complete example, see [Non-git version control](#non-git-version-control).
## What worktrees share with the main checkout
A worktree gets its own files and branch, but it shares the repository's `.git` directory, project-scope plugins, and saved permission approvals with the main checkout:
* **The repository's `.git` directory**: git commands in a worktree write to the main repository's shared `.git` directory, and [sandboxing](/docs/en/sandboxing#filesystem-isolation) allows those writes, so commands such as `git commit` work from inside a worktree with the sandbox enabled.
* **Plugins**: plugins installed at [project scope](/docs/en/plugins-reference#plugin-installation-scopes) from the main checkout also load in worktrees of the same repository, so you don't need to reinstall them per worktree. Requires Claude Code v2.1.200 or later.
* **Permission approvals**: choosing "Yes, don't ask again" for a Bash command in a worktree session saves the rule to the main checkout's `.claude/settings.local.json`, so it applies in the main checkout and in every other worktree of the repository, and it survives the worktree's removal. Before v2.1.211, an approval granted in a worktree was saved inside that worktree, didn't apply elsewhere, and was lost when the worktree was removed. See [where approvals are saved](/docs/en/permissions#permission-system).
All three apply whether you create the worktree with `--worktree`, with `git worktree add`, or through the [desktop app](/docs/en/desktop#work-in-parallel-with-sessions).
## Manage worktrees manually
Create worktrees with Git directly when you need to check out a specific existing branch or place the worktree outside the repository.
Create a worktree on a new branch:
```bash theme={null}
git worktree add ../project-feature-a -b feature-a
```
Create a worktree from an existing branch, replacing `fix-issue-456` with a branch that already exists in your repository:
```bash theme={null}
git worktree add ../project-bugfix fix-issue-456
```
Start Claude in the worktree:
```bash theme={null}
cd ../project-feature-a
claude
```
List your worktrees:
```bash theme={null}
git worktree list
```
Remove one when you're done with it:
```bash theme={null}
git worktree remove ../project-feature-a
```
See the [Git worktree documentation](https://git-scm.com/docs/git-worktree) for the full command reference.
## Non-git version control
Worktree isolation uses git by default. For SVN, Perforce, Mercurial, or other systems, configure [`WorktreeCreate` and `WorktreeRemove` hooks](/docs/en/hooks#worktreecreate) to provide custom creation and cleanup logic. Because the hook replaces the default git behavior, [`.worktreeinclude`](#copy-gitignored-files-into-worktrees) is not processed when you use `--worktree`. Copy any local configuration files inside your hook script instead.
This `WorktreeCreate` hook reads the worktree name from the JSON on stdin with `jq`, checks out a fresh SVN working copy, and prints the directory path so Claude Code can use it as the session's working directory. Add the configuration to your [`settings.json`](/docs/en/settings#settings-files):
```json theme={null}
{
"hooks": {
"WorktreeCreate": [
{
"hooks": [
{
"type": "command",
"command": "bash -c 'NAME=$(jq -r .name); DIR=\"$HOME/.claude/worktrees/$NAME\"; svn checkout https://svn.example.com/repo/trunk \"$DIR\" >&2 && echo \"$DIR\"'"
}
]
}
]
}
}
```
Pair it with a `WorktreeRemove` hook to clean up when the session ends. See the [hooks reference](/docs/en/hooks#worktreecreate) for the input schema and a removal example.
## Troubleshooting
Claude Code reports the errors below when it creates a worktree, enters one at startup, or returns a resumed session to one.
### Claude Code can't enter the worktree at startup
When Claude Code can't enter the worktree directory at startup, it prints an error naming the path and exits with code 1. This can happen when a [`WorktreeCreate` hook](/docs/en/hooks#worktreecreate) prints something other than the directory it created, or when the directory was deleted after it was set up.
### Worktree creation fails on a symlinked path
Claude Code refuses to create a worktree when `.claude`, `.claude/worktrees`, or the worktree directory itself is a symlink, and the error names the symlinked path. Remove the symlink and retry. Before v2.1.212, if the repository already contained a committed symlink at one of those paths, worktree creation followed it and could create files outside the repository.
### Claude Code refuses to use a worktree
An error starting `Refusing to use <path> as an isolation worktree` means Claude Code checked the directory's git identity before adopting it as a session's or subagent's isolated checkout, and declined it. The check runs whether Claude Code is creating the worktree, entering an existing one, or reusing one from an earlier run.
In most cases the rest of the message says the directory's git metadata resolves into the main checkout: for example, its `.git` file points at the main repository's own `.git` directory, or git resolves its working tree to the main checkout through a `core.worktree` redirect. From such a directory, an ordinary git command such as `git reset --hard` would act on the main checkout instead of the worktree. Claude Code also refuses when the directory has a `.git` entry it can't read, rather than assuming the worktree is safe.
Claude Code leaves the refused directory in place, since it may hold work. Match the message to its recovery, whether it follows `Refusing to use <path>` or appears in a [resume message](#the-session-resumes-outside-its-worktree); some endings occur only in resume messages:
* **Says `launch from the parent checkout` or `Run the resume from the project checkout`**: you launched Claude Code from inside the worktree. Launch from the main checkout instead; the worktree needs no recreation.
* **Says `it cannot be resumed or re-entered`**: nothing in this session vouches for the worktree from where you launched. Recreate it; the directory and its work remain on disk for manual recovery, and when the worktree has a parent checkout, resuming from there also works.
* **Says `it contains the protected checkout`**: the refused directory is a parent of your main checkout, such as your home directory. Don't delete it. Change the worktree path, such as the path your `WorktreeCreate` hook returns or the `EnterWorktree` target, so the worktree doesn't contain the checkout.
* **Says `the protected checkout <path> has a .git entry that could not be examined` or `has git metadata that could not be resolved`**: the problem is the main checkout's git metadata, not the worktree's. Don't delete the worktree, and ignore the message's trailing advice to recreate it, which doesn't apply to these two endings. Repair the main checkout, for example a permissions problem or a git `dubious ownership` refusal on its `.git`, and retry.
* **Says `its recorded path has a network spelling`**: Claude Code never resumes into a worktree at a network path. Recreate the worktree at a local path.
* **Any other ending**: the message names the problem and its fix, such as removing a `core.worktree` redirect or recreating the worktree; follow it. Before deleting a directory whose message says its git identity could not be verified, address the named cause first, for example a symbolic link in the worktree's path or git itself failing to run, since the directory may be healthy. When you do recreate, salvage any changes you need from the old directory first; it stays on disk.
### The session resumes outside its worktree
When an interactive resume can't return the session to its worktree, Claude Code says so with one of the messages below.
| Message starts with | What happened and what to do |
| :------------------------------------------------ | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `Your worktree <path> no longer exists` | The worktree directory was removed. The session continues in the current directory without isolation, and Claude Code clears the worktree binding. No action needed. |
| `Could not verify your worktree <path> this time` | Claude Code couldn't verify the worktree, usually for a transient reason; the binding is kept, and the session continues in the current directory without isolation. Resume again to retry; if it keeps happening, enter the worktree in a new session and match the refusal message under [Claude Code refuses to use a worktree](#claude-code-refuses-to-use-a-worktree), which can name the main checkout's metadata rather than the worktree's. |
| `Did not re-enter your worktree <path>` | Claude Code refused the worktree binding as unsafe; it clears the binding and the session continues without isolation. The message includes the specific refusal: match it under [Claude Code refuses to use a worktree](#claude-code-refuses-to-use-a-worktree), since the fix is recreation for some refusals and a path change for others. |
| `Could not re-enter your worktree <path>` | Claude Code couldn't vouch for the worktree from where you launched, most commonly because you launched from inside it; the binding is kept. The rest of the message names the fix; match it under [Claude Code refuses to use a worktree](#claude-code-refuses-to-use-a-worktree). |
In [non-interactive mode](/docs/en/headless) with `-p`, and on resumes the [Agent SDK](/docs/en/agent-sdk/sessions) runs, Claude Code stops the resume with a stderr error for every refusal except a gone worktree, instead of continuing without isolation, and the messages take different shapes from the ones in the table above:
* `Error: cannot resume into worktree <path>: ...This session was not started.` for a refusal the table shows as `Did not re-enter`
* `Error: could not verify worktree <path> for this resume, so the resume was aborted...` for `Could not verify`
* `Error: ...The worktree binding is kept.` for `Could not re-enter`
* `Notice: the worktree <path> for this session no longer exists...` for a gone worktree; Claude Code prints it and continues the session, as an interactive resume does
Cut at 300 lines. The page has the rest.
zero-data-retention First recorded · 78 lines, first recorded
# Zero data retention ## ZDR scope ### Route Claude Code traffic to your ZDR organization ### What ZDR covers ### What ZDR does not cover ## Features disabled under ZDR ### Model availability under ZDR ## Data retention for policy violations ## Request ZDR
The first capture of this source. The page was already there, and this is what it said.
# Zero data retention > Learn about Zero Data Retention (ZDR) for Claude Code, available to qualified accounts on Claude for Enterprise, including scope, disabled features, and how to request enablement. Zero Data Retention (ZDR) for Claude Code is available to qualified accounts on Claude for Enterprise. When ZDR is enabled, prompts and model responses generated during Claude Code sessions are processed in real time and not stored by Anthropic after the response is returned, except where needed to comply with law or combat misuse. <Note> ZDR is not included in the standard Claude for Enterprise plan and cannot be enabled from your admin settings. It is available to qualified accounts and requires separate enablement by Anthropic. If your organization requires ZDR, [contact sales](https://www.anthropic.com/contact-sales?utm_source=claude_code\&utm_medium=docs\&utm_content=zero_data_retention_request) or your Anthropic account team to confirm eligibility. </Note> ZDR on Claude for Enterprise gives enterprise customers the ability to use Claude Code with zero data retention and access administrative capabilities: * Cost controls per user * [Analytics](/docs/en/analytics) dashboard * [Server-managed settings](/docs/en/server-managed-settings) * Audit logs ZDR for Claude Code on Claude for Enterprise applies only to Anthropic's direct platform. For Claude deployments on Amazon Bedrock, Google Cloud's Agent Platform, or Microsoft Foundry, refer to those platforms' data retention policies. ## ZDR scope ZDR covers Claude Code inference on Claude for Enterprise. <Warning> ZDR is enabled on a per-organization basis. Each new organization requires ZDR to be enabled separately by your Anthropic account team. ZDR does not automatically apply to new organizations created under the same account. Contact your account team to enable ZDR for any new organizations. </Warning> ### Route Claude Code traffic to your ZDR organization ZDR applies to requests that authenticate into a ZDR-enabled organization. If a developer signs in to Claude Code with a personal account or with an API key from a different organization, those sessions are not covered. To require that developers' claude.ai logins belong to your ZDR organization, deploy the `forceLoginMethod` and `forceLoginOrgUUID` managed settings; see [Restrict login to your organization](/docs/en/authentication#restrict-login-to-your-organization), which also explains how these keys treat Claude Console logins. ### What ZDR covers ZDR covers model inference calls made through Claude Code on Claude for Enterprise. When you use Claude Code in your terminal, the prompts you send and the responses Claude generates are not retained by Anthropic. This applies to every model available to ZDR organizations. Some models require data retention and are not available under ZDR; see [Model availability under ZDR](#model-availability-under-zdr). ### What ZDR does not cover ZDR does not extend to the following, even for organizations with ZDR enabled. These features follow [standard data retention policies](/docs/en/data-usage#data-retention): | Feature | Details | | ------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Chat on claude.ai | Chat conversations through the Claude for Enterprise web interface are not covered by ZDR. | | Cowork | Cowork sessions are not covered by ZDR. | | Claude Code Analytics | Does not store prompts or model responses, but collects productivity metadata such as account emails and usage statistics. Contribution metrics are not available for ZDR organizations; the [analytics dashboard](/docs/en/analytics) shows usage metrics only. | | User and seat management | Administrative data such as account emails and seat assignments is retained under standard policies. | | Third-party integrations | Data processed by third-party tools, MCP servers, or other external integrations is not covered by ZDR. Review those services' data handling practices independently. | ## Features disabled under ZDR When ZDR is enabled for a Claude Code organization on Claude for Enterprise, certain features that require storing prompts or completions are automatically disabled at the backend level: | Feature | Reason | | ----------------------------------------------------------------- | ------------------------------------------------------------------------------------------- | | [Claude Code on the Web](/docs/en/claude-code-on-the-web) | Requires server-side storage of conversation history. | | [Cloud sessions](/docs/en/desktop#cloud-sessions) from the Desktop app | Requires persistent session data that includes prompts and completions. | | [Artifacts](/docs/en/artifacts) | Requires storing published page content on Anthropic-operated infrastructure. | | Feedback submission (`/feedback`, `/bug`, `/share`) | Submitting feedback sends conversation data to Anthropic. | | [Remote Control](/docs/en/remote-control) | Stores the session transcript on Anthropic servers to sync the conversation across devices. | These features are blocked in the backend regardless of client-side display. If you see a disabled feature in the Claude Code terminal during startup, attempting to use it returns an error indicating the organization's policies do not allow that action. Future features may also be disabled if they require storing prompts or completions. ### Model availability under ZDR Claude Fable 5 is not available for organizations with zero data retention enabled. This model class [requires data retention](https://platform.claude.com/docs/en/manage-claude/api-and-data-retention#model-specific-data-retention-requirements), so requests from ZDR organizations cannot be served by it. The model is either absent from the `/model` picker for ZDR organizations or shown as disabled with a notice that disabling ZDR is required, and the server rejects requests for it regardless of client configuration. Other models remain available under ZDR. Fable 5 is not the default model, and the `best` alias, which resolves to Fable 5 where it is available, resolves to Opus for organizations where it is not, including ZDR organizations. ## Data retention for policy violations Even with ZDR enabled, Anthropic may retain data where required by law or to address Usage Policy violations. If a session is flagged for a policy violation, Anthropic may retain the associated inputs and outputs for up to 2 years, consistent with Anthropic's standard ZDR policy. ## Request ZDR To request ZDR for Claude Code on Claude for Enterprise, [contact sales](https://www.anthropic.com/contact-sales?utm_source=claude_code\&utm_medium=docs\&utm_content=zero_data_retention_request) or your Anthropic account team. Your account team will submit the request internally, and Anthropic will review and enable ZDR on your organization after confirming eligibility. All enablement actions are audit-logged. If you are currently using ZDR for Claude Code via pay-as-you-go API keys, you can transition to Claude for Enterprise to gain access to administrative features while maintaining ZDR for Claude Code. Contact your account team to coordinate the migration.