# Claude Code CLI

All documentation for Claude Code CLI.


---
## admin-setup-9ffdbf6b0c.md

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

# 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](../17-Billing-Plans/use-claude-code-with-your-team-or-enterprise-plan.md) 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](../13-Enterprise-Admin/authentication-claude-code-docs-2c6ada8385.md), [Bedrock](../20-Models/claude-code-on-amazon-bedrock-claude-code-docs-435e92efd0.md), [Vertex AI](../20-Models/claude-code-on-google-vertex-ai-claude-code-docs-2acd050a7a.md), [Foundry](../20-Models/claude-code-on-microsoft-foundry-claude-code-docs-ee35d755a6.md) |
| [Decide how settings reach devices](#decide-how-settings-reach-devices) | How managed policy reaches developer machines       | [Server-managed settings](../13-Enterprise-Admin/configure-server-managed-settings-claude-code-docs-d6a169b0bf.md), [Settings files](/en/settings#settings-files)                                    |
| [Decide what to enforce](#decide-what-to-enforce)                       | Which tools, commands, and integrations are allowed | [Permissions](configure-permissions-claude-code-docs-7b0e64d485.md), [Sandboxing](../22-Safety-Policy/sandboxing-claude-code-docs-5f97cd27c4.md)                                                                             |
| [Set up usage visibility](#set-up-usage-visibility)                     | How you track spend and adoption                    | [Analytics](../13-Enterprise-Admin/track-team-usage-with-analytics-claude-code-docs-35e754aabf.md), [Monitoring](../13-Enterprise-Admin/monitoring-claude-code-docs-72c46f3dc8.md), [Costs](../17-Billing-Plans/manage-costs-effectively-claude-code-docs-7c26178a4f.md)                                                       |
| [Review data handling](#review-data-handling)                           | Data retention and compliance posture               | [Data usage](../22-Safety-Policy/data-usage-claude-code-docs-06b2864aaa.md), [Security](../22-Safety-Policy/security-claude-code-docs-7538e94c51.md)                                                                                   |

## Choose your API provider

Claude Code connects to Claude through one of several API providers. Your choice affects billing, authentication, and which compliance posture you inherit.

| 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 Vertex AI              | You want to inherit existing GCP compliance controls and billing                                                                      |
| Microsoft Foundry             | You want to inherit existing Azure compliance controls and billing                                                                    |

For the full provider comparison covering authentication, regions, and feature parity, see the [enterprise deployment overview](enterprise-deployment-overview-claude-code-docs-6eae0ecba2.md). Each provider's auth setup is in [Authentication](../13-Enterprise-Admin/authentication-claude-code-docs-2c6ada8385.md).

Proxy and firewall requirements in [Network configuration](enterprise-network-configuration-claude-code-docs.md) apply regardless of provider. If you want a single endpoint in front of multiple providers or centralized request logging, see [LLM gateway](../13-Enterprise-Admin/llm-gateway-configuration-claude-code-docs-7a1628ac88.md).

## Decide how settings reach devices

Managed settings define policy that takes precedence over local developer configuration. Claude Code looks for them in four places and uses the first one it finds on a given device.

| Mechanism               | Delivery                                                                                                                                                                                              | Priority | Platforms      |
| :---------------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------- | :------------- |
| Server-managed          | Claude.ai admin console                                                                                                                                                                               | 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. They require a Claude for Teams or Enterprise plan, so deployments on other providers need one of the file-based or OS-level mechanisms instead.

If your organization mixes providers, configure [server-managed settings](../13-Enterprise-Admin/configure-server-managed-settings-claude-code-docs-d6a169b0bf.md) for Claude.ai users plus a [file-based or plist/registry fallback](/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`](/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. 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.

See [Server-managed settings](../13-Enterprise-Admin/configure-server-managed-settings-claude-code-docs-d6a169b0bf.md) and [Settings files and precedence](/en/settings#settings-files).

## 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](configure-permissions-claude-code-docs-7b0e64d485.md)                                                    | Allow, ask, or deny specific tools and commands                               | `permissions.allow`, `permissions.deny`                                       |
| [Permission lockdown](/en/permissions#managed-only-settings)                           | Only managed permission rules apply; disable `--dangerously-skip-permissions` | `allowManagedPermissionRulesOnly`, `permissions.disableBypassPermissionsMode` |
| [Sandboxing](../22-Safety-Policy/sandboxing-claude-code-docs-5f97cd27c4.md)                                                           | OS-level filesystem and network isolation with domain allowlists              | `sandbox.enabled`, `sandbox.network.allowedDomains`                           |
| [Managed policy CLAUDE.md](/en/memory#deploy-organization-wide-claude-md)              | Org-wide instructions loaded in every session, cannot be excluded             | File at the managed policy path                                               |
| [MCP server control](/en/mcp#managed-mcp-configuration)                                | Restrict which MCP servers users can add or connect to                        | `allowedMcpServers`, `deniedMcpServers`, `allowManagedMcpServersOnly`         |
| [Plugin marketplace control](/en/plugin-marketplaces#managed-marketplace-restrictions) | Restrict which marketplace sources users can add and install from             | `strictKnownMarketplaces`, `blockedMarketplaces`                              |
| [Hook restrictions](/en/settings#hook-configuration)                                   | Only managed hooks load; restrict HTTP hook URLs                              | `allowManagedHooksOnly`, `allowedHttpHookUrls`                                |
| [Version floor](claude-code-settings-claude-code-docs-d4420b4b52.md)                                                          | Prevent auto-update from installing below an org-wide minimum                 | `minimumVersion`                                                              |

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](../22-Safety-Policy/security-claude-code-docs-7538e94c51.md).

## Set up usage visibility

Choose monitoring based on what you need to report on.

| Capability          | What you get                                         | Availability   | Where to start                           |
| :------------------ | :--------------------------------------------------- | :------------- | :--------------------------------------- |
| Usage monitoring    | OpenTelemetry export of sessions, tools, and tokens  | All providers  | [Monitoring usage](../13-Enterprise-Admin/monitoring-claude-code-docs-72c46f3dc8.md) |
| Analytics dashboard | Per-user metrics, contribution tracking, leaderboard | Anthropic only | [Analytics](../13-Enterprise-Admin/track-team-usage-with-analytics-claude-code-docs-35e754aabf.md)               |
| Cost tracking       | Spend limits, rate limits, and usage attribution     | Anthropic only | [Costs](../17-Billing-Plans/manage-costs-effectively-claude-code-docs-7c26178a4f.md)                       |

Cloud providers expose spend through AWS Cost Explorer, GCP Billing, or Azure Cost Management. Claude for Teams and Enterprise plans include a usage dashboard at [claude.ai/analytics/claude-code](https://claude.ai/analytics/claude-code).

## Review data handling

On Team, Enterprise, Claude API, and cloud provider plans, Anthropic does not 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](../22-Safety-Policy/data-usage-claude-code-docs-06b2864aaa.md)                   |
| Zero Data Retention (ZDR) | Nothing stored after the request completes. Available on Claude for Enterprise  | [Zero data retention](../22-Safety-Policy/zero-data-retention-claude-code-docs-6ec9ee63f1.md) |
| Security architecture     | Network model, encryption, authentication, audit trail                          | [Security](../22-Safety-Policy/security-claude-code-docs-7538e94c51.md)                       |

If you need request-level audit logging or to route traffic by data sensitivity, place an [LLM gateway](../13-Enterprise-Admin/llm-gateway-configuration-claude-code-docs-7a1628ac88.md) between developers and your provider. For regulatory requirements and certifications, see [Legal and compliance](../22-Safety-Policy/legal-and-compliance-claude-code-docs-2d64f2bee3.md).

## Verify and onboard

After configuring managed settings, have a developer run `/status` inside Claude Code. The output includes a line beginning with `Enterprise managed settings` followed by the source in parentheses, one of `(remote)`, `(plist)`, `(HKLM)`, `(HKCU)`, or `(file)`. See [Verify active settings](/en/settings#verify-active-settings).

Share these resources to help developers get started:

* [Quickstart](../01-Getting-Started/quickstart-claude-code-docs-a21b84bdea.md): first-session walkthrough from install to working with a project
* [Common workflows](common-workflows-c909406123.md): 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](/en/troubleshooting#authentication-issues). 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](../13-Enterprise-Admin/configure-server-managed-settings-claude-code-docs-d6a169b0bf.md): deliver managed policy from the Claude admin console
* [Settings reference](claude-code-settings-claude-code-docs-d4420b4b52.md): every setting key, file location, and precedence rule
* [Amazon Bedrock](../20-Models/claude-code-on-amazon-bedrock-claude-code-docs-435e92efd0.md), [Google Vertex AI](../20-Models/claude-code-on-google-vertex-ai-claude-code-docs-2acd050a7a.md), [Microsoft Foundry](../20-Models/claude-code-on-microsoft-foundry-claude-code-docs-ee35d755a6.md): provider-specific deployment
* [Claude Enterprise Administrator Guide](https://claude.com/resources/tutorials/claude-enterprise-administrator-guide): SSO, SCIM, seat management, and rollout playbook


---
## advanced-setup-claude-code-docs-0526bc1300.md

## On this page

- [System requirements](#system-requirements)
  - [Additional dependencies](#additional-dependencies)
- [Install Claude Code](#install-claude-code)
  - [Set up on Windows](#set-up-on-windows)
  - [Alpine Linux and musl-based distributions](#alpine-linux-and-musl-based-distributions)
- [Verify your installation](#verify-your-installation)
- [Authenticate](#authenticate)
- [Update Claude Code](#update-claude-code)
  - [Auto-updates](#auto-updates)
  - [Configure release channel](#configure-release-channel)
  - [Pin a minimum version](#pin-a-minimum-version)
  - [Disable auto-updates](#disable-auto-updates)
  - [Update manually](#update-manually)
- [Advanced installation options](#advanced-installation-options)
  - [Install a specific version](#install-a-specific-version)
  - [Install with Linux package managers](#install-with-linux-package-managers)
  - [Install with npm](#install-with-npm)
  - [Binary integrity and code signing](#binary-integrity-and-code-signing)
  - [Verify the manifest signature](#verify-the-manifest-signature)
  - [Platform code signatures](#platform-code-signatures)
- [Uninstall Claude Code](#uninstall-claude-code)
  - [Native installation](#native-installation)
  - [Homebrew installation](#homebrew-installation)
  - [WinGet installation](#winget-installation)
  - [apt / dnf / apk](#apt-%2F-dnf-%2F-apk)
  - [npm](#npm)
  - [Remove configuration files](#remove-configuration-files)

Setup and access

# Advanced setup

Copy pageCopy page

System requirements, platform-specific installation, version management, and uninstallation for Claude Code.

Copy pageCopy page

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)

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)

Additional dependencies

- **ripgrep**: usually included with Claude Code. If search fails, see [search troubleshooting](/docs/en/troubleshooting#search-and-discovery-issues).


[​](#install-claude-code)

Install Claude Code

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).

To install Claude Code, open a terminal and run the command for your system. If you haven’t used a terminal before, the [terminal guide](/docs/en/terminal-guide) shows how to open one and paste the command.

- Native Install (Recommended)

- Homebrew

- WinGet

**macOS, Linux, WSL:**

```python
curl -fsSL https://claude.ai/install.sh | bash
```

**Windows PowerShell:**

```python
irm https://claude.ai/install.ps1 | iex
```

**Windows CMD:**

```python
curl -fsSL https://claude.ai/install.cmd -o install.cmd && install.cmd && del install.cmd
```

When the installer finishes, open a new terminal window and run `claude --version`. A working installation prints a version number. If your shell says `claude` isn’t found or isn’t recognized, the install directory isn’t on your PATH yet: see [Fix your PATH](/docs/en/troubleshoot-install#command-not-found-claude-after-installation).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.

Native installations automatically update in the background to keep you on the latest version.

```python
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.

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.

```python
winget install Anthropic.ClaudeCode
```

WinGet installations do not auto-update. Run `winget upgrade Anthropic.ClaudeCode` periodically to get the latest features and security fixes.

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:

```python
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)

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):

  ``` shiki
  {
    "env": {
      "CLAUDE_CODE_GIT_BASH_PATH": "C:\\Program Files\\Git\\bin\\bash.exe"
    }
  }
  ```

When Git for Windows is installed, the PowerShell tool is available alongside Bash: on by default for claude.ai and Console accounts, and enabled with `CLAUDE_CODE_USE_POWERSHELL_TOOL=1` in Amazon Bedrock, Google Cloud’s Agent Platform, and Microsoft Foundry sessions. Set it to `0` to turn the tool off. 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)

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:

```python
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:

```python
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-reference#all-settings) file:

```python
{
  "env": {
    "USE_BUILTIN_RIPGREP": "0"
  }
}
```


[​](#verify-your-installation)

Verify your installation

After installing, confirm Claude Code is working:

```python
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):

```python
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)

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)

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)

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.

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.


[​](#configure-release-channel)

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):

```python
{
  "autoUpdatesChannel": "stable"
}
```

For enterprise deployments, you can enforce a consistent release channel across your organization using [managed settings](/docs/en/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)

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:

```python
{
  "autoUpdatesChannel": "stable",
  "minimumVersion": "2.1.100"
}
```

In [managed settings](/docs/en/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 [`requiredMinimumVersion`](/docs/en/settings-reference#requiredminimumversion) and [`requiredMaximumVersion`](/docs/en/settings-reference#requiredmaximumversion).


[​](#disable-auto-updates)

Disable auto-updates

Set `DISABLE_AUTOUPDATER` to `"1"` in the `env` key of your [`settings.json`](/docs/en/settings-reference#all-settings) file:

```python
{
  "env": {
    "DISABLE_AUTOUPDATER": "1"
  }
}
```

On a native or npm installation, confirm the change took effect by running `claude doctor` and checking that the `Auto-updates` line shows `disabled (set by env: DISABLE_AUTOUPDATER)` instead of `enabled`. `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)

Update manually

To apply an update immediately without waiting for the next background check, run:

```python
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)

Advanced installation options

These options are for version pinning, Linux package managers, npm, and verifying binary integrity.


[​](#install-a-specific-version)

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):

- macOS, Linux, WSL

- Windows PowerShell

- Windows CMD

```python
curl -fsSL https://claude.ai/install.sh | bash
```

```python
irm https://claude.ai/install.ps1 | iex
```

```python
curl -fsSL https://claude.ai/install.cmd -o install.cmd && install.cmd && del install.cmd
```

To install the stable version:

- macOS, Linux, WSL

- Windows PowerShell

- Windows CMD

```python
curl -fsSL https://claude.ai/install.sh | bash -s stable
```

```python
& ([scriptblock]::Create((irm https://claude.ai/install.ps1))) stable
```

```python
curl -fsSL https://claude.ai/install.cmd -o install.cmd && install.cmd stable && del install.cmd
```

To install a specific version number:

- macOS, Linux, WSL

- Windows PowerShell

- Windows CMD

```python
curl -fsSL https://claude.ai/install.sh | bash -s 2.1.89
```

```python
& ([scriptblock]::Create((irm https://claude.ai/install.ps1))) 2.1.89
```

```python
curl -fsSL https://claude.ai/install.cmd -o install.cmd && install.cmd 2.1.89 && del install.cmd
```

To confirm which version installed, run `claude --version`: the command prints the exact version you passed, such as `2.1.89 (Claude Code)`.


[​](#install-with-linux-package-managers)

Install with Linux package managers

Claude Code publishes signed apt, dnf, and apk repositories. Each repository offers two channels: `stable` serves a version that is typically about one week old, skipping releases with major regressions, and `latest` serves every release as soon as it ships. The commands below configure the `stable` channel, which fits most users; each tab also shows the `latest` repository URL. Package manager installations do not auto-update through Claude Code; updates arrive through your normal system upgrade workflow. All repositories are signed with the [Claude Code release signing key](#binary-integrity-and-code-signing). Before trusting the key, verify it as described in each tab.

- apt

- dnf

- apk

For Debian and Ubuntu. The install commands below download the signing key with `curl` and verify it with `gpg`, which fresh Debian and Ubuntu installations may not include. If either command reports `command not found`, install both first:

```python
sudo apt install curl gnupg
```

Download the signing key:

```python
sudo install -d -m 0755 /etc/apt/keyrings
sudo curl -fsSL https://downloads.claude.ai/keys/claude-code.asc \
  -o /etc/apt/keyrings/claude-code.asc
```

If this download fails, `apt update` later fails with `NO_PUBKEY BAA929FF1A7ECACE`. Confirm the key downloaded and belongs to Anthropic before continuing:

```python
gpg --show-keys /etc/apt/keyrings/claude-code.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 on the `stable` channel and install:

```python
echo "deb [signed-by=/etc/apt/keyrings/claude-code.asc] https://downloads.claude.ai/claude-code/apt/stable stable main" \
  | sudo tee /etc/apt/sources.list.d/claude-code.list
sudo apt update
sudo apt install claude-code
```

To use the `latest` channel instead, both the URL path and the suite name change. Use this `deb` line:

```python
echo "deb [signed-by=/etc/apt/keyrings/claude-code.asc] https://downloads.claude.ai/claude-code/apt/latest latest main" \
  | sudo tee /etc/apt/sources.list.d/claude-code.list
```

To upgrade later, run `sudo apt update && sudo apt upgrade claude-code`.

For Fedora and RHEL. The following commands configure the `stable` channel:

```python
sudo tee /etc/yum.repos.d/claude-code.repo <<'EOF'
[claude-code]
name=Claude Code
baseurl=https://downloads.claude.ai/claude-code/rpm/stable
enabled=1
gpgcheck=1
gpgkey=https://downloads.claude.ai/keys/claude-code.asc
EOF
sudo dnf install claude-code
```

To use the `latest` channel instead, set `baseurl` to the `latest` repository:

```python
baseurl=https://downloads.claude.ai/claude-code/rpm/latest
```

dnf downloads the key on first install and prompts you to confirm the fingerprint. Verify it matches `31DD DE24 DDFA B679 F42D 7BD2 BAA9 29FF 1A7E CACE` before accepting.To upgrade later, run `sudo dnf upgrade claude-code`.

For Alpine Linux. The following commands configure the `stable` channel:

```python
wget -O /etc/apk/keys/claude-code.rsa.pub \
  https://downloads.claude.ai/keys/claude-code.rsa.pub
echo "https://downloads.claude.ai/claude-code/apk/stable" >> /etc/apk/repositories
apk add claude-code
```

To switch to the `latest` channel, remove the `stable` repository line and add the `latest` repository:

```python
sed -i '\|downloads.claude.ai/claude-code/apk/stable|d' /etc/apk/repositories
echo "https://downloads.claude.ai/claude-code/apk/latest" >> /etc/apk/repositories
```

Verify the downloaded key with `sha256sum /etc/apk/keys/claude-code.rsa.pub`, which should report `395759c1f7449ef4cdef305a42e820f3c766d6090d142634ebdb049f113168b6`.To upgrade later, run `apk update && apk upgrade claude-code`.


[​](#install-with-npm)

Install with npm

You can also install Claude Code as a global npm package. As of v2.1.198, the npm package requires [Node.js 22 or later](https://nodejs.org/en/download). On an older Node.js version, npm prints an `EBADENGINE` warning during install rather than failing; the install completes and `claude` still runs, since the package downloads a native binary that doesn’t use your Node.js at runtime.

```python
npm install -g @anthropic-ai/claude-code
```

The npm package installs the same native binary as the standalone installer. npm pulls the binary in through a per-platform optional dependency such as `@anthropic-ai/claude-code-darwin-arm64`, and a postinstall step links it into place. The installed `claude` binary does not itself invoke Node. Supported npm install platforms are `darwin-arm64`, `darwin-x64`, `linux-x64`, `linux-arm64`, `linux-x64-musl`, `linux-arm64-musl`, `win32-x64`, and `win32-arm64`. Your package manager must allow optional dependencies. See [troubleshooting](/docs/en/troubleshoot-install#native-binary-not-found-after-npm-install) if the binary is missing after install. To upgrade an npm installation, run `npm install -g @anthropic-ai/claude-code@latest`. Avoid `npm update -g`, which respects the semver range from the original install and may not move you to the newest release.

Do NOT use `sudo npm install -g` as this can lead to permission issues and security risks. If you encounter permission errors, see [troubleshooting permission errors](/docs/en/troubleshoot-install#permission-errors-during-installation).


[​](#binary-integrity-and-code-signing)

Binary integrity and code signing

Each release publishes a `manifest.json` containing SHA256 checksums for every platform binary. The manifest is signed with an Anthropic GPG key, so verifying the signature on the manifest transitively verifies every binary it lists.


[​](#verify-the-manifest-signature)

Verify the manifest signature

Steps 1-3 require a POSIX shell with `gpg` and `curl`. On Windows, run them in Git Bash or WSL. Step 4 includes a PowerShell option.

1

Download and import the public key

The release signing key is published at a fixed URL.

```python
curl -fsSL https://downloads.claude.ai/keys/claude-code.asc | gpg --import
```

Display the fingerprint of the imported key.

```python
gpg --fingerprint security@anthropic.com
```

Confirm the output includes this fingerprint:

```python
31DD DE24 DDFA B679 F42D  7BD2 BAA9 29FF 1A7E CACE
```

2

Download the manifest and signature

Set `VERSION` to the release you want to verify.

```python
REPO=https://downloads.claude.ai/claude-code-releases
VERSION=2.1.89
curl -fsSLO "$REPO/$VERSION/manifest.json"
curl -fsSLO "$REPO/$VERSION/manifest.json.sig"
```

3

Verify the signature

Verify the detached signature against the manifest.

```python
gpg --verify manifest.json.sig manifest.json
```

A valid result reports `Good signature from "Anthropic Claude Code Release Signing <security@anthropic.com>"`.`gpg` also prints `WARNING: This key is not certified with a trusted signature!` for any freshly imported key. This is expected. The `Good signature` line confirms the cryptographic check passed. The fingerprint comparison in Step 1 confirms the key itself is authentic.

4

Check the binary against the manifest

Compare the SHA256 checksum of the binary with the value listed under `platforms.<platform>.checksum` in `manifest.json`. The commands below assume a `claude` binary in the current directory. To verify an installed native binary instead, run the command against `~/.local/share/claude/versions/VERSION`, replacing VERSION with the release you set in Step 2.

- Linux

- macOS

- Windows PowerShell

```python
sha256sum claude
```

```python
shasum -a 256 claude
```

```python
(Get-FileHash claude.exe -Algorithm SHA256).Hash.ToLower()
```

Manifest signatures are available for releases from `2.1.89` onward. Earlier releases publish checksums in `manifest.json` without a detached signature.


[​](#platform-code-signatures)

Platform code signatures

In addition to the signed manifest, individual binaries carry platform-native code signatures where supported.

- **macOS**: signed by “Anthropic PBC” and notarized by Apple. Verify with `codesign --verify --verbose ./claude`.
- **Windows**: signed by “Anthropic, PBC”. Verify with `Get-AuthenticodeSignature .\claude.exe`.
- **Linux**: binaries are not individually code-signed. If you download directly from the `claude-code-releases` bucket or use the native installer, verify integrity with the manifest signature above. If you install with [apt, dnf, or apk](#install-with-linux-package-managers), your package manager verifies signatures automatically using the repository signing key.


[​](#uninstall-claude-code)

Uninstall Claude Code

To remove Claude Code, follow the instructions for your installation method. If `claude` still runs afterward, you likely have a second installation or a leftover shell alias from an older installer. See [Check for conflicting installations](/docs/en/troubleshoot-install#check-for-conflicting-installations) to find and remove it.


[​](#native-installation)

Native installation

Remove the Claude Code binary and version files:

- macOS, Linux, WSL

- Windows PowerShell

```python
rm -f ~/.local/bin/claude
rm -rf ~/.local/share/claude
```

```python
Remove-Item -Path "$env:USERPROFILE\.local\bin\claude.exe" -Force
Remove-Item -Path "$env:USERPROFILE\.local\share\claude" -Recurse -Force
```


[​](#homebrew-installation)

Homebrew installation

Remove the Homebrew cask you installed. If you installed the stable cask:

```python
brew uninstall --cask claude-code
```

If you installed the latest cask:

```python
brew uninstall --cask claude-code@latest
```


[​](#winget-installation)

WinGet installation

Remove the WinGet package:

```python
winget uninstall Anthropic.ClaudeCode
```


[​](#apt-/-dnf-/-apk)

apt / dnf / apk

Remove the package and the repository configuration:

- apt

- dnf

- apk

```python
sudo apt remove claude-code
sudo rm /etc/apt/sources.list.d/claude-code.list /etc/apt/keyrings/claude-code.asc
```

```python
sudo dnf remove claude-code
sudo rm /etc/yum.repos.d/claude-code.repo
```

```python
apk del claude-code
sed -i '\|downloads.claude.ai/claude-code/apk|d' /etc/apk/repositories
rm /etc/apk/keys/claude-code.rsa.pub
```


[​](#npm)

npm

Remove the global npm package:

```python
npm uninstall -g @anthropic-ai/claude-code
```


[​](#remove-configuration-files)

Remove configuration files

Removing configuration files will delete all your settings, allowed tools, MCP server configurations, and session history.

The VS Code extension, the JetBrains plugin, and the Desktop app also write to `~/.claude/`. If any of them is still installed, the directory is recreated the next time it runs. To remove Claude Code completely, uninstall the [VS Code extension](/docs/en/vs-code#uninstall-the-extension), the JetBrains plugin, and the Desktop app before deleting these files. To remove Claude Code settings and cached data:

- macOS, Linux, WSL

- Windows PowerShell

```python
# Remove user settings and state
rm -rf ~/.claude
rm ~/.claude.json

# Remove project-specific settings (run from your project directory)
rm -rf .claude
rm -f .mcp.json
```

```python
# Remove user settings and state
Remove-Item -Path "$env:USERPROFILE\.claude" -Recurse -Force
Remove-Item -Path "$env:USERPROFILE\.claude.json" -Force

# Remove project-specific settings (run from your project directory)
Remove-Item -Path ".claude" -Recurse -Force
Remove-Item -Path ".mcp.json" -Force
```


---
## amazon-bedrock-0f0d5f108c.md

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

# 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>;
};

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="bedrock" />} />

## Prerequisites

Before configuring Claude Code with Bedrock, ensure you have:

* An AWS account with Bedrock access enabled
* Access to desired Claude models (for example, Claude Sonnet 4.6) in 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 Bedrock credentials, follow [Sign in with 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 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 Bedrock">
    Run `claude`. At the login prompt, select **3rd-party platform**, then **Amazon Bedrock**.
  </Step>

  <Step title="Follow the wizard prompts">
    Choose how you authenticate to AWS: an AWS profile detected from your `~/.aws` directory, a 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](claude-code-settings-claude-code-docs-d4420b4b52.md), 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.

## Set up manually

To configure 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

First-time users of Anthropic models are required to submit use case details before invoking a model. This is done 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)**

```bash theme={null}
aws sso login --profile=<your-profile-name>

export AWS_PROFILE=your-profile-name
```

**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: Bedrock API keys**

```bash theme={null}
export AWS_BEARER_TOKEN_BEDROCK=your-bedrock-api-key
```

Bedrock API keys provide a simpler authentication method without needing full AWS credentials. [Learn more about Bedrock API keys](https://aws.amazon.com/blogs/machine-learning/accelerate-ai-development-with-amazon-bedrock-api-keys/).

#### 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](claude-code-settings-claude-code-docs-d4420b4b52.md) for file locations).

When Claude Code detects that your AWS credentials are expired (either locally based on their timestamp or when Bedrock returns a credential error), it will automatically run your configured `awsAuthRefresh` and/or `awsCredentialExport` commands to obtain new credentials before retrying the request.

##### 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"
  }
}
```

### 3. Configure Claude Code

Set the following environment variables to enable Bedrock:

```bash theme={null}
# Enable Bedrock integration
export CLAUDE_CODE_USE_BEDROCK=1
export AWS_REGION=us-east-1  # or your preferred region

# Optional: Override the region for the small/fast model (Haiku).
# Also applies to Bedrock Mantle.
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 Bedrock for Claude Code, keep the following in mind:

* `AWS_REGION` is a required environment variable. Claude Code does not read from the `.aws` config file for this setting.
* When using Bedrock, the `/login` and `/logout` commands are disabled since authentication is handled through AWS credentials.
* You can use settings files for environment variables like `AWS_PROFILE` that you don't want to leak to other processes. See [Settings](claude-code-settings-claude-code-docs-d4420b4b52.md) 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 the latest version, which may not yet be available in your Bedrock account when Anthropic releases an update. Claude Code [falls back](#startup-model-checks) to the previous version at startup when the latest is unavailable, but pinning lets you control when your users move to a new model.
</Warning>

Set these environment variables to specific Bedrock model IDs.

Without `ANTHROPIC_DEFAULT_OPUS_MODEL`, the `opus` alias on Bedrock resolves to Opus 4.6. Set it to the Opus 4.7 ID to use the latest model:

```bash theme={null}
export ANTHROPIC_DEFAULT_OPUS_MODEL='us.anthropic.claude-opus-4-7'
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 variables use cross-region inference profile IDs (with the `us.` prefix). If you use a different region prefix or application inference profiles, adjust accordingly. For current and legacy model IDs, see [Models overview](../20-Models/about-claude-models-overview-2bbf09ff82.md). See [Model configuration](/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    | `us.anthropic.claude-sonnet-4-5-20250929-v1:0` |
| Small/fast model | `us.anthropic.claude-haiku-4-5-20251001-v1:0`  |

To customize models further, use one of these methods:

```bash theme={null}
# Using inference profile ID
export ANTHROPIC_MODEL='global.anthropic.claude-sonnet-4-6'
export ANTHROPIC_DEFAULT_HAIKU_MODEL='us.anthropic.claude-haiku-4-5-20251001-v1:0'

# Using application inference profile ARN
export ANTHROPIC_MODEL='arn:aws:bedrock:us-east-2:your-account-id:application-inference-profile/your-model-id'

# Optional: Disable prompt caching if needed
export DISABLE_PROMPT_CACHING=1

# Optional: Request 1-hour prompt cache TTL instead of the 5-minute default
export ENABLE_PROMPT_CACHING_1H=1
```

<Note>[Prompt caching](../10-Prompting-Guides/build-with-claude-prompt-caching-bf7c54aa4c.md) may not be available in all regions. Cache writes with a 1-hour TTL are billed at a higher rate than 5-minute writes.</Note>

#### Map each model version to an inference profile

The `ANTHROPIC_DEFAULT_*_MODEL` environment variables configure one inference profile per model family. If your organization needs to expose several versions of the same family in the `/model` picker, each routed to its own application inference profile ARN, use the `modelOverrides` setting in your [settings file](/en/settings#settings-files) instead.

This example maps four Opus versions to distinct ARNs so users can switch between them without bypassing your organization's inference profiles:

```json theme={null}
{
  "modelOverrides": {
    "claude-opus-4-7": "arn:aws:bedrock:us-east-2:123456789012:application-inference-profile/opus-47-prod",
    "claude-opus-4-6": "arn:aws:bedrock:us-east-2:123456789012:application-inference-profile/opus-46-prod",
    "claude-opus-4-5-20251101": "arn:aws:bedrock:us-east-2:123456789012:application-inference-profile/opus-45-prod",
    "claude-opus-4-1-20250805": "arn:aws:bedrock:us-east-2:123456789012:application-inference-profile/opus-41-prod"
  }
}
```

When a user selects one of these versions in `/model`, Claude Code calls Bedrock with the mapped ARN. Versions without an override fall back to the built-in Bedrock model ID or any matching inference profile discovered at startup. See [Override model IDs per version](/en/model-config#override-model-ids-per-version) for details on how overrides interact with `availableModels` and other model settings.

## Startup model checks

When Claude Code starts with Bedrock configured, it verifies that the models it intends to use are accessible in your account. This check requires Claude Code v2.1.94 or later.

If you have pinned a model version that is older than the current Claude Code default, and your account can invoke the newer version, Claude Code prompts you to update the pin. Accepting writes the new model ID to your [user settings file](claude-code-settings-claude-code-docs-d4420b4b52.md) and restarts Claude Code. Declining is remembered until the next default version change. Pins that point to an [application inference profile ARN](#map-each-model-version-to-an-inference-profile) are skipped, since those are managed by your administrator.

If you have not pinned a model and the current default is unavailable in your account, Claude Code falls back to the previous version for the current session and shows a notice. The fallback is not persisted. Enable the newer model in your Bedrock account or [pin a version](#4-pin-model-versions) to make the choice permanent.

## IAM configuration

Create an IAM policy with the required permissions for Claude Code:

```json theme={null}
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "AllowModelAndInferenceProfileAccess",
      "Effect": "Allow",
      "Action": [
        "bedrock:InvokeModel",
        "bedrock:InvokeModelWithResponseStream",
        "bedrock:ListInferenceProfiles"
      ],
      "Resource": [
        "arn:aws:bedrock:*:*:inference-profile/*",
        "arn:aws:bedrock:*:*:application-inference-profile/*",
        "arn:aws:bedrock:*:*:foundation-model/*"
      ]
    },
    {
      "Sid": "AllowMarketplaceSubscription",
      "Effect": "Allow",
      "Action": [
        "aws-marketplace:ViewSubscriptions",
        "aws-marketplace:Subscribe"
      ],
      "Resource": "*",
      "Condition": {
        "StringEquals": {
          "aws:CalledViaLast": "bedrock.amazonaws.com"
        }
      }
    }
  ]
}
```

For more restrictive permissions, you can limit the Resource to specific inference profile ARNs.

For details, see [Bedrock IAM documentation](https://docs.aws.amazon.com/bedrock/latest/userguide/security-iam.html).

<Note>
  Create a dedicated AWS account for Claude Code to simplify cost tracking and access control.
</Note>

## 1M token context window

Claude Opus 4.7, Opus 4.6, and Sonnet 4.6 support the [1M token context window](../04-API-Reference/Guides/build-with-claude-context-windows-13ca780d1d.md#1m-token-context-window) on Amazon Bedrock. Claude Code automatically enables the extended context window when you select a 1M model variant.

The [setup wizard](#sign-in-with-bedrock) 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](/en/model-config#pin-models-for-third-party-deployments) for details.

## AWS Guardrails

[Amazon Bedrock Guardrails](https://docs.aws.amazon.com/bedrock/latest/userguide/guardrails.html) let you implement content filtering for Claude Code. Create a Guardrail in the [Amazon Bedrock console](https://console.aws.amazon.com/bedrock/), publish a version, then add the Guardrail headers to your [settings file](claude-code-settings-claude-code-docs-d4420b4b52.md). Enable Cross-Region inference on your Guardrail if you're using cross-region inference profiles.

Example configuration:

```json theme={null}
{
  "env": {
    "ANTHROPIC_CUSTOM_HEADERS": "X-Amzn-Bedrock-GuardrailIdentifier: your-guardrail-id\nX-Amzn-Bedrock-GuardrailVersion: 1"
  }
}
```

## Use the Mantle endpoint

Mantle is an Amazon Bedrock endpoint that serves Claude models through the native Anthropic API shape rather than the Bedrock Invoke API. It uses the same AWS credentials, IAM permissions, and `awsAuthRefresh` configuration described earlier on this page.

<Note>
  Mantle requires Claude Code v2.1.94 or later. Run `claude --version` to check.
</Note>

### Enable Mantle

With AWS credentials already configured, set `CLAUDE_CODE_USE_MANTLE` to route requests to the Mantle endpoint:

```bash theme={null}
export CLAUDE_CODE_USE_MANTLE=1
export AWS_REGION=us-east-1
```

Claude Code constructs the endpoint URL from `AWS_REGION`. To override it for a custom endpoint or gateway, set `ANTHROPIC_BEDROCK_MANTLE_BASE_URL`.

Run `/status` inside Claude Code to confirm. The provider line shows `Amazon Bedrock (Mantle)` when Mantle is active.

### Select a Mantle model

Mantle uses model IDs prefixed with `anthropic.` and without a version suffix, for example `anthropic.claude-haiku-4-5`. The models available to your account depend on what your organization has been granted; additional model IDs are listed in your onboarding materials from AWS. Contact your AWS account team to request access to allowlisted models.

Set the model with the `--model` flag or with `/model` inside Claude Code:

```bash theme={null}
claude --model anthropic.claude-haiku-4-5
```

### Run Mantle alongside the Invoke API

The models available to you on Mantle may not include every model you use today. Setting both `CLAUDE_CODE_USE_BEDROCK` and `CLAUDE_CODE_USE_MANTLE` lets Claude Code call both endpoints from the same session. Model IDs that match the Mantle format are routed to Mantle, and all other model IDs go to the Bedrock Invoke API.

```bash theme={null}
export CLAUDE_CODE_USE_BEDROCK=1
export CLAUDE_CODE_USE_MANTLE=1
```

To surface a Mantle model in the `/model` picker, list its ID in `availableModels` in your [settings file](claude-code-settings-claude-code-docs-d4420b4b52.md). This setting also restricts the picker to the listed entries, so include every alias you want to keep available:

```json theme={null}
{
  "availableModels": ["opus", "sonnet", "haiku", "anthropic.claude-haiku-4-5"]
}
```

Entries with the `anthropic.` prefix are added as custom picker options and routed to Mantle. Replace `anthropic.claude-haiku-4-5` with the model ID your account has been granted. See [Restrict model selection](/en/model-config#restrict-model-selection) for how `availableModels` interacts with other model settings.

When both providers are active, `/status` shows `Amazon Bedrock + Amazon Bedrock (Mantle)`.

### Route Mantle through a gateway

If your organization routes model traffic through a centralized [LLM gateway](../13-Enterprise-Admin/llm-gateway-configuration-claude-code-docs-7a1628ac88.md) that injects AWS credentials server-side, disable client-side authentication so Claude Code sends requests without SigV4 signatures or `x-api-key` headers:

```bash theme={null}
export CLAUDE_CODE_USE_MANTLE=1
export CLAUDE_CODE_SKIP_MANTLE_AUTH=1
export ANTHROPIC_BEDROCK_MANTLE_BASE_URL=https://your-gateway.example.com
```

### Mantle environment variables

These variables are specific to the Mantle endpoint. See [Environment variables](env-vars-5c624d392b.md) for the full list.

| Variable                                | Purpose                                                             |
| :-------------------------------------- | :------------------------------------------------------------------ |
| `CLAUDE_CODE_USE_MANTLE`                | Enable the Mantle endpoint. Set to `1` or `true`.                   |
| `ANTHROPIC_BEDROCK_MANTLE_BASE_URL`     | Override the default Mantle endpoint URL                            |
| `CLAUDE_CODE_SKIP_MANTLE_AUTH`          | Skip client-side authentication for proxy setups                    |
| `ANTHROPIC_SMALL_FAST_MODEL_AWS_REGION` | Override AWS region for the Haiku-class model (shared with Bedrock) |

## Troubleshooting

### Authentication loop with SSO and corporate proxies

If browser tabs spawn repeatedly when using AWS SSO, remove the `awsAuthRefresh` setting from your [settings file](claude-code-settings-claude-code-docs-d4420b4b52.md). This can occur when corporate VPNs or TLS inspection proxies interrupt the SSO browser flow. Claude Code treats the interrupted connection as an authentication failure, re-runs `awsAuthRefresh`, and loops indefinitely.

If your network environment interferes with automatic browser-based SSO flows, use `aws sso login` manually before starting Claude Code instead of relying on `awsAuthRefresh`.

### Region issues

If you encounter region issues:

* Check model availability: `aws bedrock list-inference-profiles --region your-region`
* Switch to a supported region: `export AWS_REGION=us-east-1`
* Consider using inference profiles for cross-region access

If you receive an error "on-demand throughput isn’t supported":

* Specify the model as an [inference profile](https://docs.aws.amazon.com/bedrock/latest/userguide/inference-profiles-support.html) ID

Claude Code uses the Bedrock [Invoke API](https://docs.aws.amazon.com/bedrock/latest/APIReference/API_runtime_InvokeModelWithResponseStream.html) and does not support the Converse API.

### Mantle endpoint errors

If `/status` does not show `Amazon Bedrock (Mantle)` after you set `CLAUDE_CODE_USE_MANTLE`, the variable is not reaching the process. Confirm it is exported in the shell where you launched `claude`, or set it in the `env` block of your [settings file](claude-code-settings-claude-code-docs-d4420b4b52.md).

A `403` from the Mantle endpoint with valid credentials means your AWS account has not been granted access to the model you requested. Contact your AWS account team to request access.

A `400` that names the model ID means that model is not served on Mantle. Mantle has its own model lineup separate from the standard Bedrock catalog, so inference profile IDs such as `us.anthropic.claude-sonnet-4-6` will not work. Use a Mantle-format ID, or enable [both endpoints](#run-mantle-alongside-the-invoke-api) so Claude Code routes each request to the endpoint where the model is available.

## Additional resources

* [Bedrock documentation](https://docs.aws.amazon.com/bedrock/)
* [Bedrock pricing](https://aws.amazon.com/bedrock/pricing/)
* [Bedrock inference profiles](https://docs.aws.amazon.com/bedrock/latest/userguide/inference-profiles-support.html)
* [Bedrock token burndown and quotas](https://docs.aws.amazon.com/bedrock/latest/userguide/quotas-token-burndown.html)
* [Claude Code on Amazon Bedrock: Quick Setup Guide](https://community.aws/content/2tXkZKrZzlrlu0KfH8gST5Dkppq/claude-code-on-amazon-bedrock-quick-setup-guide)
* [Claude Code Monitoring Implementation (Bedrock)](https://github.com/aws-solutions-library-samples/guidance-for-claude-code-with-amazon-bedrock/blob/main/assets/docs/MONITORING.md)


---
## analytics-5356b88d0a.md

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

# 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

### 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](../22-Safety-Policy/zero-data-retention-claude-code-docs-6ec9ee63f1.md) 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

To query this data through GitHub, 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.

<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](../13-Enterprise-Admin/monitoring-claude-code-docs-72c46f3dc8.md): export real-time metrics and events to your observability stack
* [Manage costs effectively](../17-Billing-Plans/manage-costs-effectively-claude-code-docs-7c26178a4f.md): set spend limits and optimize token usage
* [Permissions](configure-permissions-claude-code-docs-7b0e64d485.md): configure roles and permissions


---
## authentication-ce6fdcb8aa.md

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

# 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 Vertex AI, or Microsoft Foundry.

## Log in to Claude Code

After [installing Claude Code](/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 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.

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](../20-Models/claude-code-on-amazon-bedrock-claude-code-docs-435e92efd0.md), [Google Vertex AI](../20-Models/claude-code-on-google-vertex-ai-claude-code-docs-2acd050a7a.md), or [Microsoft Foundry](../20-Models/claude-code-on-microsoft-foundry-claude-code-docs-ee35d755a6.md), set the required environment variables before running `claude`. No browser login is needed.

To log out and re-authenticate, type `/logout` at the Claude Code prompt.

If you're having trouble logging in, see [authentication troubleshooting](/en/troubleshooting#authentication-issues).

## 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)
* [Amazon Bedrock](../20-Models/claude-code-on-amazon-bedrock-claude-code-docs-435e92efd0.md)
* [Google Vertex AI](../20-Models/claude-code-on-google-vertex-ai-claude-code-docs-2acd050a7a.md)
* [Microsoft Foundry](../20-Models/claude-code-on-microsoft-foundry-claude-code-docs-ee35d755a6.md)

### 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](../21-Account-Support/setting-up-single-sign-on-sso.md)
  </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](/en/setup#system-requirements)
    * [Install Claude Code](/en/setup#install-claude-code)
    * Log in with Console account credentials
  </Step>
</Steps>

### Cloud provider authentication

For teams using Amazon Bedrock, Google Vertex AI, or Microsoft Foundry:

<Steps>
  <Step title="Follow provider setup">
    Follow the [Bedrock docs](../20-Models/claude-code-on-amazon-bedrock-claude-code-docs-435e92efd0.md), [Vertex docs](../20-Models/claude-code-on-google-vertex-ai-claude-code-docs-2acd050a7a.md), or [Microsoft Foundry docs](../20-Models/claude-code-on-microsoft-foundry-claude-code-docs-ee35d755a6.md).
  </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](claude-code-settings-claude-code-docs-d4420b4b52.md).
  </Step>

  <Step title="Install Claude Code">
    Users can [install Claude Code](/en/setup#install-claude-code).
  </Step>
</Steps>

## Credential management

Claude Code securely manages your authentication credentials:

* **Storage location**: on macOS, credentials are stored in the encrypted macOS Keychain. On Linux and Windows, credentials are stored in `~/.claude/.credentials.json`, or under `$CLAUDE_CONFIG_DIR` if that variable is set. On Linux, the file is written with mode `0600`; on Windows, it inherits the access controls of your user profile directory.
* **Supported authentication types**: Claude.ai credentials, Claude API credentials, Azure Auth, Bedrock Auth, and Vertex Auth.
* **Custom credential scripts**: the [`apiKeyHelper`](/en/settings#available-settings) setting can be configured 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.

`apiKeyHelper`, `ANTHROPIC_API_KEY`, and `ANTHROPIC_AUTH_TOKEN` apply to terminal CLI sessions only. Claude Desktop and remote sessions use OAuth exclusively and do not call `apiKeyHelper` or read API key environment variables.

### 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](enterprise-deployment-overview-claude-code-docs-6eae0ecba2.md) for setup.
2. `ANTHROPIC_AUTH_TOKEN` environment variable. Sent as the `Authorization: Bearer` header. Use this when routing through an [LLM gateway or proxy](../13-Enterprise-Admin/llm-gateway-configuration-claude-code-docs-7a1628ac88.md) 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](../04-API-Reference/Other/platform-claude-com.md). 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`. In non-interactive mode (`-p`), the key is always used when present.
4. [`apiKeyHelper`](/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.
6. Subscription OAuth credentials from `/login`. This is the default for Claude Pro, Max, Team, and Enterprise users.

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.

[Claude Code on the Web](claude-code-on-the-web-69d53821d4.md) always uses your subscription credentials. `ANTHROPIC_API_KEY` and `ANTHROPIC_AUTH_TOKEN` in the sandbox environment do not override them.

### 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 walks you through OAuth authorization and prints a token to the terminal. 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 is scoped to inference only and cannot establish [Remote Control](continue-local-sessions-from-any-device-with-remote-control-claude-code-docs-c1c03fd914.md) sessions.

[Bare mode](/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.


---
## authentication-claude-code-docs-2c6ada8385.md

## On this page

- [Log in to Claude Code](#log-in-to-claude-code)
- [Set up team authentication](#set-up-team-authentication)
  - [Claude for Teams or Enterprise](#claude-for-teams-or-enterprise)
  - [Claude Console authentication](#claude-console-authentication)
  - [Sign in without an API key](#sign-in-without-an-api-key)
  - [Cloud provider authentication](#cloud-provider-authentication)
  - [Restrict login to your organization](#restrict-login-to-your-organization)
- [Credential management](#credential-management)
  - [Renew an expiring login](#renew-an-expiring-login)
  - [Authentication precedence](#authentication-precedence)
  - [Anthropic profiles and federation credentials](#anthropic-profiles-and-federation-credentials)
  - [Generate a long-lived token](#generate-a-long-lived-token)

Setup and access

# Authentication

Copy pageCopy page

Log in to Claude Code and configure authentication for individuals, teams, and organizations.

Copy pageCopy page

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)

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. You can sign in with or without [creating an API key](#sign-in-without-an-api-key).
- **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. To stay signed in to multiple accounts at once, such as work and personal accounts, give each account its own configuration directory. When you start `claude`, set the [`CLAUDE_CONFIG_DIR`](/docs/en/env-vars#variables) environment variable to the directory for the account you want to use. Each directory has its own settings, session history, and claude.ai login or API key. For example, in Bash or Zsh, add this alias to `~/.bashrc` or `~/.zshrc` so that `claude-work` uses your work account while `claude` keeps your personal one:

```python
alias claude-work='CLAUDE_CONFIG_DIR=~/.claude-work claude'
```

After you open a new terminal and run `claude-work` for the first time, Claude Code walks you through login and setup for the new directory. Separate directories don’t keep two Claude Console sign-ins [without an API key](#sign-in-without-an-api-key) apart, because Claude Code stores that kind of sign-in outside the configuration directory. If you’re having trouble logging in, see [authentication troubleshooting](/docs/en/troubleshoot-install#login-and-authentication).


[​](#set-up-team-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 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, SSO, billing management, and [server-managed settings](/docs/en/server-managed-settings) for organization-wide Claude Code configuration. Best for smaller teams.
- **Claude for Enterprise**: adds domain capture, role-based permissions, and the compliance API. Best for larger organizations with security and compliance requirements.

1

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).

2

Invite team members

Invite team members from the admin dashboard.

3

Install and log in

Team members install Claude Code and log in with their claude.ai accounts.


[​](#claude-console-authentication)

Claude Console authentication

For organizations that prefer API-based billing, you can set up access through the Claude Console.

1

Create or use a Console account

Use your existing Claude Console account or create a new one.

2

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)

3

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

4

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


[​](#sign-in-without-an-api-key)

Sign in without an API key

You can sign in to your Console account without creating an API key, even when your organization doesn’t let developers create them. Choose the Anthropic Console account at the `/login` prompt and Claude Code asks how you want to sign in. Requires Claude Code v2.1.242 or later. Both routes sign you in to Console in the browser and differ in what Claude Code stores afterwards:

- **Sign in with your Console account**, labeled `(recommended)`: Claude Code keeps the OAuth token from that sign-in and stores it as an [Anthropic profile](#anthropic-profiles-and-federation-credentials). It creates no API key
- **Create an API key**, labeled `(legacy)`: Claude Code creates a Console API key for you and stores it with your other credentials

In practice, the profile stores an OAuth login while an API key is a static credential: Claude Code refreshes the profile’s login automatically, and when refresh fails, requests fail with [Anthropic profile login expired](/docs/en/errors#anthropic-profile-login-expired) until you sign in again. You don’t get the choice on every machine. Claude Code creates an API key without asking in these cases:

- You run against a cloud provider, such as [Amazon Bedrock, Google Cloud’s Agent Platform, or Microsoft Foundry](/docs/en/third-party-integrations) or [Claude Platform on AWS](/docs/en/claude-platform-on-aws)
- Any settings file sets [`forceLoginOrgUUID`](#restrict-login-to-your-organization), or sets `forceLoginMethod` to `"claudeai"` or `"console"`
- A managed settings source on your machine, such as the managed settings file, an MDM profile, or the cached server-managed settings, exists but Claude Code [can’t read it](/docs/en/managed-settings#invalid-entries-in-managed-settings) and no other managed source supplies a policy

Unset `ANTHROPIC_API_KEY` before you sign in without a key. A profile written by Claude Code’s own Console sign-in, or by the Claude Platform CLI’s `ant auth login`, is the same kind of credential, so signing in again replaces it. After you sign in without a key, you have a profile instead of a stored API key:

- **Which profile it writes**: Claude Code writes the profile named by `ANTHROPIC_PROFILE`, or your active profile, or `default`. If that profile is a federation profile, Claude Code refuses the sign-in instead of overwriting it
- **What it signs you out of**: Claude Code signs you out of any claude.ai login stored on the machine
- **How to undo it**: run `/logout`, which removes and revokes the credential this sign-in wrote

If your organization uses [server-managed settings](/docs/en/server-managed-settings), they apply to this sign-in on Claude Code v2.1.257 or later. Everything else about profiles applies to this sign-in, including where it ranks against your other credentials, the `Profile` row you get in `/status`, and the features that need a claude.ai login. See [Anthropic profiles and federation credentials](#anthropic-profiles-and-federation-credentials).


[​](#cloud-provider-authentication)

Cloud provider authentication

For teams using Amazon Bedrock, Google Cloud’s Agent Platform, or Microsoft Foundry:

1

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).

2

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).

3

Install Claude Code

Users can [install Claude Code](/docs/en/setup#install-claude-code).


[​](#restrict-login-to-your-organization)

Restrict login to your organization

To require that developers’ claude.ai logins belong to a specific Anthropic organization, set [`forceLoginMethod`](/docs/en/settings-reference#forceloginmethod) and [`forceLoginOrgUUID`](/docs/en/settings-reference#forceloginorguuid) in [managed settings](/docs/en/managed-settings). 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` 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. A developer who logged in with a Console account before you deployed the keys stays logged in, and that saved key is blocked on a machine that also requires the [gateway](/docs/en/claude-apps-gateway) sign-in or in a session that selects a cloud provider. If you set `forceLoginOrgUUID` in any settings file, Claude Code stops offering the [keyless Console sign-in](#sign-in-without-an-api-key) in the sessions that file applies to and creates an API key instead. 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. On the terminal’s interactive login screen, reached by `/login` or first-run onboarding, 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 a few [per-key exceptions](/docs/en/server-managed-settings#per-key-exceptions-across-managed-sources). `forceLoginOrgUUID` and the `"claudeai"` and `"console"` values of `forceLoginMethod` aren’t among those exceptions, so keep them in both places. In a [gateway](/docs/en/claude-apps-gateway) deployment, also keep `forceLoginMethod` and `forceLoginOrgUUID` out of the [settings the gateway serves](/docs/en/claude-apps-gateway-config#managed). The keys also decide whether a session that doesn’t use a login credential can start. See [`forceLoginOrgUUID`](/docs/en/settings-reference#forceloginorguuid) in the settings reference for the full behavior.

- **`ANTHROPIC_API_KEY`, `ANTHROPIC_AUTH_TOKEN`, or `apiKeyHelper`**: blocked at startup. Under `forceLoginOrgUUID`, organization membership can’t be verified for an environment credential, and under `forceLoginMethod` the credential would stand in for the required sign-in. When the managed settings also require the [gateway](/docs/en/claude-apps-gateway) sign-in, Claude Code blocks an API key saved by an earlier Claude Console login the same way. See [Administrator policy requires a Cloud gateway sign-in](/docs/en/errors#administrator-policy-requires-a-cloud-gateway-sign-in)
- **Cloud provider sessions such as Amazon Bedrock**: blocked only while an `ANTHROPIC_API_KEY`, `ANTHROPIC_AUTH_TOKEN`, or `apiKeyHelper` credential, or an API key saved by an earlier Claude Console login, is still present on the machine. Remove it and the session starts. These sessions authenticate against your cloud provider, whose access policies govern them
- **[Anthropic profile or federation credentials](#anthropic-profiles-and-federation-credentials)**: not blocked unless an `ANTHROPIC_API_KEY`, `ANTHROPIC_AUTH_TOKEN`, or `apiKeyHelper` credential, or an API key saved by an earlier Claude Console login, is also present on the machine. The keys don’t check which organization the profile belongs to


[​](#credential-management)

Credential management

Claude Code securely manages your authentication credentials:

- **Storage location**:
  - On macOS, credentials are stored in the encrypted macOS Keychain. When the Keychain rejects the write, such as when it’s locked in an SSH session, Claude Code stores your login in `~/.claude/.credentials.json` with file mode `0600` instead, the same storage it uses on Linux. A Console login that creates an API key fails until the Keychain is writable. To move your login back into the Keychain, follow [the recovery steps](/docs/en/troubleshoot-install#not-logged-in-or-token-expired).
  - 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, Claude Code keeps the `.credentials.json` file under that directory instead, including the file the macOS fallback writes, and keys the macOS Keychain entry to that directory too, so a session with a different `CLAUDE_CONFIG_DIR` reads a different entry.
  - 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-reference#apikeyhelper) setting to run a shell script that returns an API key.
- **Refresh intervals**: Claude Code re-runs `apiKeyHelper` after five minutes by default. Set the `CLAUDE_CODE_API_KEY_HELPER_TTL_MS` environment variable for custom refresh intervals. See [`apiKeyHelper`](/docs/en/settings-reference#apikeyhelper) for the other cases in which Claude Code re-runs the helper.
- **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)

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 model request fails with [`Login expired · Please run /login`](/docs/en/errors#login-expired) until you sign in again. Before v2.1.206, Claude Code reported an expired login on model requests 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)

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-reference#apikeyhelper) 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 your machine’s [managed settings](/docs/en/managed-settings) set [`forceLoginMethod`](/docs/en/settings-reference#forceloginmethod) to `"gateway"` or set [`forceLoginGatewayUrl`](/docs/en/settings-reference#forcelogingatewayurl), and you don’t select a cloud provider through a variable such as `CLAUDE_CODE_USE_BEDROCK` or `CLAUDE_CODE_USE_VERTEX`, your session uses only the gateway sign-in. Claude Code skips the other credential sources and asks you to sign in with `/login`. See [Administrator policy requires a Cloud gateway sign-in](/docs/en/errors#administrator-policy-requires-a-cloud-gateway-sign-in) for what you see with each leftover credential. Before v2.1.261, or before v2.1.265 on a machine that sets only `forceLoginGatewayUrl`, Claude Code used a leftover saved login on these machines until you signed in to the gateway. If you have an active Claude subscription but also have `ANTHROPIC_API_KEY` set in your environment, Claude Code uses the API key once you approve it. 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. When a login and an API key are both configured, `/status` marks the credential that isn’t in use. [Cloud sessions](/docs/en/claude-code-on-the-web) always use your subscription credentials. If you set `ANTHROPIC_API_KEY` or `ANTHROPIC_AUTH_TOKEN` in the cloud environment, it doesn’t override your subscription credentials.


[​](#anthropic-profiles-and-federation-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 or you [signed in to a Console account without an API key](#sign-in-without-an-api-key). 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. In those sessions, `/status` shows no `Profile` row. 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. When the profile is the credential in use, `Organization` and `Email` rows show its account. 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 skips 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 `/logout` for a `user_oauth` profile whose current credential you wrote by [signing in to a Console account without an API key](#sign-in-without-an-api-key), run `ant auth logout` for one whose current credential `ant auth login` wrote, or delete the profile’s file from `configs/` in your configuration directory for either auth mode


[​](#generate-a-long-lived-token)

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`:

```python
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:

- macOS, Linux, WSL

- Windows PowerShell

- Windows CMD

```python
export CLAUDE_CODE_OAUTH_TOKEN=your-token
```

```python
$env:CLAUDE_CODE_OAUTH_TOKEN = "your-token"
```

```python
set 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.


---
## authentication-claude-code-docs-cd0d115668.md

## On this page

- [Log in to Claude Code](#log-in-to-claude-code)
- [Set up team authentication](#set-up-team-authentication)
  - [Claude for Teams or Enterprise](#claude-for-teams-or-enterprise)
  - [Claude Console authentication](#claude-console-authentication)
  - [Sign in without an API key](#sign-in-without-an-api-key)
  - [Cloud provider authentication](#cloud-provider-authentication)
  - [Restrict login to your organization](#restrict-login-to-your-organization)
- [Credential management](#credential-management)
  - [Renew an expiring login](#renew-an-expiring-login)
  - [Authentication precedence](#authentication-precedence)
  - [Anthropic profiles and federation credentials](#anthropic-profiles-and-federation-credentials)
  - [Generate a long-lived token](#generate-a-long-lived-token)

Setup and access

# Authentication

Copy pageCopy page

Log in to Claude Code and configure authentication for individuals, teams, and organizations.

Copy pageCopy page

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)

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. You can sign in with or without [creating an API key](#sign-in-without-an-api-key).
- **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. To stay signed in to multiple accounts at once, such as work and personal accounts, give each account its own configuration directory. When you start `claude`, set the [`CLAUDE_CONFIG_DIR`](/docs/en/env-vars#variables) environment variable to the directory for the account you want to use. Each directory has its own settings, session history, and claude.ai login or API key. For example, in Bash or Zsh, add this alias to `~/.bashrc` or `~/.zshrc` so that `claude-work` uses your work account while `claude` keeps your personal one:

```python
alias claude-work='CLAUDE_CONFIG_DIR=~/.claude-work claude'
```

After you open a new terminal and run `claude-work` for the first time, Claude Code walks you through login and setup for the new directory. Separate directories don’t keep two Claude Console sign-ins [without an API key](#sign-in-without-an-api-key) apart, because Claude Code stores that kind of sign-in outside the configuration directory. If you’re having trouble logging in, see [authentication troubleshooting](/docs/en/troubleshoot-install#login-and-authentication).


[​](#set-up-team-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 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, SSO, billing management, and [server-managed settings](/docs/en/server-managed-settings) for organization-wide Claude Code configuration. Best for smaller teams.
- **Claude for Enterprise**: adds domain capture, role-based permissions, and the compliance API. Best for larger organizations with security and compliance requirements.

1

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).

2

Invite team members

Invite team members from the admin dashboard.

3

Install and log in

Team members install Claude Code and log in with their claude.ai accounts.


[​](#claude-console-authentication)

Claude Console authentication

For organizations that prefer API-based billing, you can set up access through the Claude Console.

1

Create or use a Console account

Use your existing Claude Console account or create a new one.

2

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)

3

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

4

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


[​](#sign-in-without-an-api-key)

Sign in without an API key

You can sign in to your Console account without creating an API key, even when your organization doesn’t let developers create them. Choose the Anthropic Console account at the `/login` prompt and Claude Code asks how you want to sign in. Requires Claude Code v2.1.242 or later. Both routes sign you in to Console in the browser and differ in what Claude Code stores afterwards:

- **Sign in with your Console account**, labeled `(recommended)`: Claude Code keeps the OAuth token from that sign-in and stores it as an [Anthropic profile](#anthropic-profiles-and-federation-credentials). It creates no API key
- **Create an API key**, labeled `(legacy)`: Claude Code creates a Console API key for you and stores it with your other credentials

In practice, the profile stores an OAuth login while an API key is a static credential: Claude Code refreshes the profile’s login automatically, and when refresh fails, requests fail with [Anthropic profile login expired](/docs/en/errors#anthropic-profile-login-expired) until you sign in again. You don’t get the choice on every machine. Claude Code creates an API key without asking in these cases:

- You run against a cloud provider, such as [Amazon Bedrock, Google Cloud’s Agent Platform, or Microsoft Foundry](/docs/en/third-party-integrations) or [Claude Platform on AWS](/docs/en/claude-platform-on-aws)
- Any settings file sets [`forceLoginOrgUUID`](#restrict-login-to-your-organization), or sets `forceLoginMethod` to `"claudeai"` or `"console"`
- A managed settings source on your machine, such as the managed settings file, an MDM profile, or the cached server-managed settings, exists but Claude Code [can’t read it](/docs/en/managed-settings#invalid-entries-in-managed-settings) and no other managed source supplies a policy

Unset `ANTHROPIC_API_KEY` before you sign in without a key. A profile written by Claude Code’s own Console sign-in, or by the Claude Platform CLI’s `ant auth login`, is the same kind of credential, so signing in again replaces it. After you sign in without a key, you have a profile instead of a stored API key:

- **Which profile it writes**: Claude Code writes the profile named by `ANTHROPIC_PROFILE`, or your active profile, or `default`. If that profile is a federation profile, Claude Code refuses the sign-in instead of overwriting it
- **What it signs you out of**: Claude Code signs you out of any claude.ai login stored on the machine
- **How to undo it**: run `/logout`, which removes and revokes the credential this sign-in wrote

If your organization uses [server-managed settings](/docs/en/server-managed-settings), they apply to this sign-in on Claude Code v2.1.257 or later. Everything else about profiles applies to this sign-in, including where it ranks against your other credentials, the `Profile` row you get in `/status`, and the features that need a claude.ai login. See [Anthropic profiles and federation credentials](#anthropic-profiles-and-federation-credentials).


[​](#cloud-provider-authentication)

Cloud provider authentication

For teams using Amazon Bedrock, Google Cloud’s Agent Platform, or Microsoft Foundry:

1

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).

2

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).

3

Install Claude Code

Users can [install Claude Code](/docs/en/setup#install-claude-code).


[​](#restrict-login-to-your-organization)

Restrict login to your organization

To require that developers’ claude.ai logins belong to a specific Anthropic organization, set [`forceLoginMethod`](/docs/en/settings-reference#forceloginmethod) and [`forceLoginOrgUUID`](/docs/en/settings-reference#forceloginorguuid) in [managed settings](/docs/en/managed-settings). 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` 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. A developer who logged in with a Console account before you deployed the keys stays logged in, and that saved key is blocked on a machine that also requires the [gateway](/docs/en/claude-apps-gateway) sign-in or in a session that selects a cloud provider. If you set `forceLoginOrgUUID` in any settings file, Claude Code stops offering the [keyless Console sign-in](#sign-in-without-an-api-key) in the sessions that file applies to and creates an API key instead. 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. On the terminal’s interactive login screen, reached by `/login` or first-run onboarding, 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 a few [per-key exceptions](/docs/en/server-managed-settings#per-key-exceptions-across-managed-sources). `forceLoginOrgUUID` and the `"claudeai"` and `"console"` values of `forceLoginMethod` aren’t among those exceptions, so keep them in both places. In a [gateway](/docs/en/claude-apps-gateway) deployment, also keep `forceLoginMethod` and `forceLoginOrgUUID` out of the [settings the gateway serves](/docs/en/claude-apps-gateway-config#managed). The keys also decide whether a session that doesn’t use a login credential can start. See [`forceLoginOrgUUID`](/docs/en/settings-reference#forceloginorguuid) in the settings reference for the full behavior.

- **`ANTHROPIC_API_KEY`, `ANTHROPIC_AUTH_TOKEN`, or `apiKeyHelper`**: blocked at startup. Under `forceLoginOrgUUID`, organization membership can’t be verified for an environment credential, and under `forceLoginMethod` the credential would stand in for the required sign-in. When the managed settings also require the [gateway](/docs/en/claude-apps-gateway) sign-in, Claude Code blocks an API key saved by an earlier Claude Console login the same way. See [Administrator policy requires a Cloud gateway sign-in](/docs/en/errors#administrator-policy-requires-a-cloud-gateway-sign-in)
- **Cloud provider sessions such as Amazon Bedrock**: blocked only while an `ANTHROPIC_API_KEY`, `ANTHROPIC_AUTH_TOKEN`, or `apiKeyHelper` credential, or an API key saved by an earlier Claude Console login, is still present on the machine. Remove it and the session starts. These sessions authenticate against your cloud provider, whose access policies govern them
- **[Anthropic profile or federation credentials](#anthropic-profiles-and-federation-credentials)**: not blocked unless an `ANTHROPIC_API_KEY`, `ANTHROPIC_AUTH_TOKEN`, or `apiKeyHelper` credential, or an API key saved by an earlier Claude Console login, is also present on the machine. The keys don’t check which organization the profile belongs to


[​](#credential-management)

Credential management

Claude Code securely manages your authentication credentials:

- **Storage location**:
  - On macOS, credentials are stored in the encrypted macOS Keychain. When the Keychain rejects the write, such as when it’s locked in an SSH session, Claude Code stores your login in `~/.claude/.credentials.json` with file mode `0600` instead, the same storage it uses on Linux. A Console login that creates an API key fails until the Keychain is writable. To move your login back into the Keychain, follow [the recovery steps](/docs/en/troubleshoot-install#not-logged-in-or-token-expired).
  - 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, Claude Code keeps the `.credentials.json` file under that directory instead, including the file the macOS fallback writes, and keys the macOS Keychain entry to that directory too, so a session with a different `CLAUDE_CONFIG_DIR` reads a different entry.
  - 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-reference#apikeyhelper) setting to run a shell script that returns an API key.
- **Refresh intervals**: Claude Code re-runs `apiKeyHelper` after five minutes by default. Set the `CLAUDE_CODE_API_KEY_HELPER_TTL_MS` environment variable for custom refresh intervals. See [`apiKeyHelper`](/docs/en/settings-reference#apikeyhelper) for the other cases in which Claude Code re-runs the helper.
- **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)

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 model request fails with [`Login expired · Please run /login`](/docs/en/errors#login-expired) until you sign in again. Before v2.1.206, Claude Code reported an expired login on model requests 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)

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-reference#apikeyhelper) 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 your machine’s [managed settings](/docs/en/managed-settings) set [`forceLoginMethod`](/docs/en/settings-reference#forceloginmethod) to `"gateway"` or set [`forceLoginGatewayUrl`](/docs/en/settings-reference#forcelogingatewayurl), and you don’t select a cloud provider through a variable such as `CLAUDE_CODE_USE_BEDROCK` or `CLAUDE_CODE_USE_VERTEX`, your session uses only the gateway sign-in. Claude Code skips the other credential sources and asks you to sign in with `/login`. See [Administrator policy requires a Cloud gateway sign-in](/docs/en/errors#administrator-policy-requires-a-cloud-gateway-sign-in) for what you see with each leftover credential. Before v2.1.261, or before v2.1.265 on a machine that sets only `forceLoginGatewayUrl`, Claude Code used a leftover saved login on these machines until you signed in to the gateway. If you have an active Claude subscription but also have `ANTHROPIC_API_KEY` set in your environment, Claude Code uses the API key once you approve it. 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. When a login and an API key are both configured, `/status` marks the credential that isn’t in use. [Cloud sessions](/docs/en/claude-code-on-the-web) always use your subscription credentials. If you set `ANTHROPIC_API_KEY` or `ANTHROPIC_AUTH_TOKEN` in the cloud environment, it doesn’t override your subscription credentials.


[​](#anthropic-profiles-and-federation-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 or you [signed in to a Console account without an API key](#sign-in-without-an-api-key). 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. In those sessions, `/status` shows no `Profile` row. 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. When the profile is the credential in use, `Organization` and `Email` rows show its account. 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 skips 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 `/logout` for a `user_oauth` profile whose current credential you wrote by [signing in to a Console account without an API key](#sign-in-without-an-api-key), run `ant auth logout` for one whose current credential `ant auth login` wrote, or delete the profile’s file from `configs/` in your configuration directory for either auth mode


[​](#generate-a-long-lived-token)

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`:

```python
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:

- macOS, Linux, WSL

- Windows PowerShell

- Windows CMD

```python
export CLAUDE_CODE_OAUTH_TOKEN=your-token
```

```python
$env:CLAUDE_CODE_OAUTH_TOKEN = "your-token"
```

```python
set 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.


---
## authentication-claude-code-docs.md

# 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 Vertex AI, 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 the browser doesn’t open automatically, press `c` to copy the login URL to your clipboard, then paste it into your browser. 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 Vertex AI](/docs/en/google-vertex-ai), or [Microsoft Foundry](/docs/en/microsoft-foundry), set the required environment variables before running `claude`. No browser login is needed.

To log out and re-authenticate, type `/logout` at the Claude Code prompt. If you’re having trouble logging in, see [authentication troubleshooting](/docs/en/troubleshooting#authentication-issues).

## 

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)
- [Amazon Bedrock](/docs/en/amazon-bedrock)
- [Google Vertex AI](/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.

1

[](#)

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).

2

[](#)

Invite team members

Invite team members from the admin dashboard.

3

[](#)

Install and log in

Team members install Claude Code and log in with their Claude.ai accounts.

### 

Claude Console authentication

For organizations that prefer API-based billing, you can set up access through the Claude Console.

1

[](#)

Create or use a Console account

Use your existing Claude Console account or create a new one.

2

[](#)

Add users

You can add users through either method:

- Bulk invite users from within the Console: Settings -\> Members -\> Invite
- [Set up SSO](../21-Account-Support/setting-up-single-sign-on-sso.md)

3

[](#)

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

4

[](#)

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

### 

Cloud provider authentication

For teams using Amazon Bedrock, Google Vertex AI, or Microsoft Foundry:

1

[](#)

Follow provider setup

Follow the [Bedrock docs](/docs/en/amazon-bedrock), [Vertex docs](/docs/en/google-vertex-ai), or [Microsoft Foundry docs](/docs/en/microsoft-foundry).

2

[](#)

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).

3

[](#)

Install Claude Code

Users can [install Claude Code](/docs/en/setup#install-claude-code).

## 

Credential management

Claude Code securely manages your authentication credentials:

- **Storage location**: on macOS, credentials are stored in the encrypted macOS Keychain.
- **Supported authentication types**: Claude.ai credentials, Claude API credentials, Azure Auth, Bedrock Auth, and Vertex Auth.
- **Custom credential scripts**: the [`apiKeyHelper`](/docs/en/settings#available-settings) setting can be configured 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.


---
## auto-compact-deep-dive.md

# Auto-Compact Deep Dive — Window vs Percentage Override

**Topic**: `CLAUDE_CODE_AUTO_COMPACT_WINDOW` vs `CLAUDE_AUTOCOMPACT_PCT_OVERRIDE`
**Binary**: Claude Code 2.1.75
**Status**: Both active, fully implemented

---

## TL;DR

- **`CLAUDE_CODE_AUTO_COMPACT_WINDOW`** = "Pretend the context window is this size" (caps max window)
- **`CLAUDE_AUTOCOMPACT_PCT_OVERRIDE`** = "Trigger compaction at X% of (possibly capped) window"
- They **compose**: window override runs first, percentage applies to the result
- **Order matters**: Window cap → percentage calculation → 13K safety headroom
- For **Opus 1M**, use both: `WINDOW=350000` + `PCT=85` to maintain 200K-like behavior

---

## The Calculation Pipeline

### Step 1: `BB(model)` — Calculate Effective Window

```javascript
function BB(H) {  // H = model
  let buffer = Math.min(PnH(H), 20000),        // Reserved buffer (~20K)
      baseWindow = LY(H, Qw()),                 // Model's native context window
      override = process.env.CLAUDE_CODE_AUTO_COMPACT_WINDOW;

  if (override) {
    let parsed = parseInt(override, 10);
    if (!isNaN(parsed) && parsed > 0)
      baseWindow = Math.min(baseWindow, parsed);  // ← CAP DOWN (never extends)
  }

  return baseWindow - buffer;  // Effective window
}
```

**Key**: `Math.min(native, override)` — only applies if override is LOWER than native.

---

### Step 2: `bQH(model)` — Calculate Auto-Compact Threshold

```javascript
function bQH(H) {  // H = model
  let effectiveWindow = BB(H),                  // From step 1
      safeThreshold = effectiveWindow - 13000,  // Leave 13K headroom
      pctOverride = process.env.CLAUDE_AUTOCOMPACT_PCT_OVERRIDE;

  if (pctOverride) {
    let pct = parseFloat(pctOverride);
    if (!isNaN(pct) && pct > 0 && pct <= 100) {
      let pctThreshold = Math.floor(effectiveWindow * (pct / 100));
      return Math.min(pctThreshold, safeThreshold);  // ← Never exceed safe headroom
    }
  }

  return safeThreshold;  // Default: effectiveWindow - 13K
}
```

**Key**: Percentage is calculated on the **capped window** (if window override is active).

---

### Step 3: `PKH(tokens, model)` — Check Thresholds

```javascript
function PKH(H, $) {  // H = current tokens, $ = model
  let autoCompactThreshold = bQH($),             // From step 2
      effectiveWindow = BB($),                   // From step 1
      percentLeft = Math.max(0, Math.round((effectiveWindow - H) / effectiveWindow * 100)),

      warningThreshold = effectiveWindow - 20000,
      errorThreshold = effectiveWindow - 20000,
      isAboveAutoCompactThreshold = oy() && H >= autoCompactThreshold,

      blockingLimit = effectiveWindow - 3000,
      blockingOverride = process.env.CLAUDE_CODE_BLOCKING_LIMIT_OVERRIDE,
      actualBlockingLimit = blockingOverride
                            ? parseInt(blockingOverride, 10)
                            : blockingLimit,
      isAtBlockingLimit = H >= actualBlockingLimit;

  return {
    percentLeft,
    isAboveWarningThreshold,
    isAboveErrorThreshold,
    isAboveAutoCompactThreshold,  // ← Triggers auto-compact
    isAtBlockingLimit
  };
}
```

---

## Default Behavior (No Overrides)

### Sonnet 4.6 (200K context)

```
Model native window:    200,000 tokens
Buffer:                 -20,000 tokens
─────────────────────────────────────
Effective window:       180,000 tokens
Safe headroom:          -13,000 tokens
─────────────────────────────────────
Threshold:              167,000 tokens  (~93%)
```

**Auto-compact triggers at 167K tokens** (~93% of effective window).

---

### Opus 1M (1M context)

```
Model native window:  1,000,000 tokens
Buffer:                 -20,000 tokens
─────────────────────────────────────
Effective window:       980,000 tokens
Safe headroom:          -13,000 tokens
─────────────────────────────────────
Threshold:              967,000 tokens  (~99%)
```

**Auto-compact triggers at 967K tokens** (~99% of effective window).

⚠️ **Problem**: Compacting at 99% means:
- Massive conversation history to summarize
- Slow compaction (takes minutes)
- Risk of memory exhaustion
- Poor compaction quality (too much context)

---

## Percentage Override Only

### Example: `CLAUDE_AUTOCOMPACT_PCT_OVERRIDE=85`

#### Sonnet 4.6 (200K)

```
Effective window:       180,000 tokens
Percentage threshold:   floor(180,000 × 0.85) = 153,000
Safe headroom:          180,000 - 13,000      = 167,000
─────────────────────────────────────
Actual threshold:       min(153,000, 167,000) = 153,000 tokens  (~85%)
```

**Auto-compact at 153K** (~85% of effective window). ✅ Good.

---

#### Opus 1M (1M)

```
Effective window:       980,000 tokens
Percentage threshold:   floor(980,000 × 0.85) = 833,000
Safe headroom:          980,000 - 13,000      = 967,000
─────────────────────────────────────
Actual threshold:       min(833,000, 967,000) = 833,000 tokens  (~85%)
```

**Auto-compact at 833K** (~85% of effective window).

⚠️ **Still problematic**: 833K is **4.8x larger** than the 200K experience. Compaction will be slow and may fail.

---

## Window Override Only

### Example: `CLAUDE_CODE_AUTO_COMPACT_WINDOW=350000`

#### Sonnet 4.6 (200K)

```
Model native window:    200,000 tokens
Override:               350,000 tokens
Capped window:          min(200,000, 350,000) = 200,000  ← NO EFFECT
─────────────────────────────────────
(Same as default — 167K threshold)
```

**No change.** Window override is higher than native, so `Math.min()` picks native.

---

#### Opus 1M (1M)

```
Model native window:  1,000,000 tokens
Override:               350,000 tokens
Capped window:          min(1,000,000, 350,000) = 350,000  ← CAPPED
Buffer:                                           -20,000
─────────────────────────────────────
Effective window:                                 330,000 tokens
Safe headroom:                                    -13,000 tokens
─────────────────────────────────────
Threshold:                                        317,000 tokens  (~96% of capped)
```

**Auto-compact at 317K** (~96% of the capped window).

✅ **Better**, but still using fixed 13K headroom on a 330K window → 96% is late.

---

## Both Overrides (Recommended)

### Example: `CLAUDE_CODE_AUTO_COMPACT_WINDOW=350000` + `CLAUDE_AUTOCOMPACT_PCT_OVERRIDE=85`

#### Sonnet 4.6 (200K)

```
Capped window:          min(200,000, 350,000) = 200,000  ← NO EFFECT
Effective window:       180,000
Threshold:              floor(180,000 × 0.85) = 153,000 tokens  (~85%)
```

Same as percentage-only case. Window override ignored.

---

#### Opus 1M (1M)

```
Model native window:  1,000,000 tokens
Override:               350,000 tokens
Capped window:          min(1,000,000, 350,000) = 350,000  ← CAPPED
Buffer:                                           -20,000
─────────────────────────────────────
Effective window:                                 330,000 tokens
Percentage threshold:   floor(330,000 × 0.85)   = 280,500
Safe headroom:          330,000 - 13,000        = 317,000
─────────────────────────────────────
Actual threshold:       min(280,500, 317,000)   = 280,500 tokens  (~85% of capped)
```

**Auto-compact at 280.5K** (~85% of the capped 350K window).

✅ **Excellent**: Similar to the 200K experience (153K @ 85%) but with room for larger individual inputs.

---

## Comparison Table

| Model | Window Override | PCT Override | Threshold | vs 200K Default |
|-------|----------------|--------------|-----------|-----------------|
| **Sonnet 4.6** (200K) | — | — | **167K** | Baseline (93%) |
| **Sonnet 4.6** (200K) | — | 85 | **153K** | -14K (-8%) |
| **Sonnet 4.6** (200K) | 350K | 85 | **153K** | No change (override ignored) |
| **Opus 1M** (1M) | — | — | **967K** | +800K (+579%) 🔴 |
| **Opus 1M** (1M) | — | 85 | **833K** | +666K (+480%) 🔴 |
| **Opus 1M** (1M) | 500K | — | **467K** | +300K (+280%) ⚠️ |
| **Opus 1M** (1M) | 500K | 85 | **408K** | +241K (+244%) ⚠️ |
| **Opus 1M** (1M) | **350K** | **85** | **280K** | **+113K (+168%)** ✅ |
| **Opus 1M** (1M) | **300K** | **85** | **238K** | **+71K (+142%)** ✅ |

---

## Why `Math.min()` Matters

### Common Mistake

"I'll set `CLAUDE_CODE_AUTO_COMPACT_WINDOW=1000000` to use the full 1M context!"

**Result**: No effect. `Math.min(1000000, 1000000) = 1000000` — same as native.

### Correct Use

The override is a **ceiling**, not a floor:
- Set it **lower** than native to cap the window
- Set it **higher** than native → ignored (no effect)

---

## Recommended Settings

### For 200K Models (Sonnet 4.6, Opus 4.6, Haiku 4.5)

```bash
# Percentage override only
export CLAUDE_AUTOCOMPACT_PCT_OVERRIDE=85

# Window override has no effect (200K < any reasonable override)
```

**Threshold**: 153K tokens (~85% of 180K effective window)

---

### For 1M Models (Sonnet 1M, Opus 1M)

```bash
# Recommended: Cap window + percentage
export CLAUDE_CODE_AUTO_COMPACT_WINDOW=350000
export CLAUDE_AUTOCOMPACT_PCT_OVERRIDE=85
```

**Threshold**: 280.5K tokens (~85% of capped 350K window)

**Rationale**:
- Maintains similar compaction frequency as 200K models
- Allows larger individual inputs (files, tool results)
- Prevents runaway context accumulation
- Faster compaction (less history to summarize)

---

### Alternative: More Aggressive

```bash
# Tighter control
export CLAUDE_CODE_AUTO_COMPACT_WINDOW=300000
export CLAUDE_AUTOCOMPACT_PCT_OVERRIDE=85
```

**Threshold**: 238K tokens (~85% of capped 300K window)

**Rationale**: Even closer to the 200K experience (153K → 238K is only +55%).

---

## Edge Cases

### What if I set window to 150K (below native 200K)?

```
Sonnet 4.6:
  Capped window:    min(200,000, 150,000) = 150,000  ← CAPPED
  Effective:        150,000 - 20,000     = 130,000
  Threshold (85%):  floor(130,000 × 0.85) = 110,500
```

**Result**: Auto-compact at 110.5K — more aggressive than default. Valid use case for CI/CD or short sessions.

---

### What if I set percentage to 99?

```
Opus 1M with WINDOW=350K:
  Effective:        330,000
  Threshold (99%):  floor(330,000 × 0.99) = 326,700
  Safe headroom:    330,000 - 13,000      = 317,000
  Actual:           min(326,700, 317,000) = 317,000  ← Headroom wins
```

**Result**: Safety headroom (13K) overrides the percentage. Max threshold is always `effectiveWindow - 13K`.

---

### What if I disable auto-compact entirely?

```bash
export DISABLE_AUTO_COMPACT=1
```

**Result**: Overrides are ignored. Manual compaction only (`/compact`). Risky — you can OOM.

---

## Debugging

### Check Current Settings

```bash
# Env vars
env | grep -i "COMPACT\|AUTOCOMPACT"

# Computed values (from logs)
# Run a session and look for:
v(`autocompact: tokens=${currentTokens} threshold=${threshold} effectiveWindow=${effectiveWindow}`)
```

### Example Log Output

```
autocompact: tokens=165000 threshold=153000 effectiveWindow=180000
```

Decode:
- `tokens=165000` — current conversation size
- `threshold=153000` — will compact at this level
- `effectiveWindow=180000` — 200K - 20K buffer

---

## Implementation Details

### Constants

```javascript
var lh6 = 20000,   // Buffer size
    KUA = 13000,   // Safe headroom
    nh6 = 20000,   // Warning threshold offset
    ih6 = 20000,   // Error threshold offset
    qUA = 3000;    // Blocking limit headroom
```

### Related Functions

- `BB(model)` — Effective window calculation
- `bQH(model)` — Auto-compact threshold
- `PKH(tokens, model)` — Threshold checks
- `rh6(messages, model, source, freed)` — Auto-compact decision
- `Inf(...)` — Auto-compact execution

---

## Testing

### Verify Window Override

```bash
# Set a low override
export CLAUDE_CODE_AUTO_COMPACT_WINDOW=100000

# Start a session with a 200K model
# Expect auto-compact to trigger around 68K tokens
# (100K - 20K buffer = 80K effective, 80K - 13K = 67K threshold)
```

### Verify Percentage Override

```bash
# Set a high percentage
export CLAUDE_AUTOCOMPACT_PCT_OVERRIDE=95

# Start a session with a 200K model
# Expect auto-compact to trigger around 167K tokens
# (95% of 180K effective = 171K, capped by 13K headroom → 167K)
```

---

## Related

- [analysis.md](./analysis.md) — Main 2.1.75 analysis
- [opus-1m-migration.md](./opus-1m-migration.md) — Opus 1M migration details
- [env-vars.md](./env-vars.md) — Environment variable reference


---
## auto-memory-internals.md

# Claude Code 2.1.76 — Auto-Memory Internals

How the post-sampling hook system and auto-memory extraction work under the hood.

---

## System Overview

```
Main Query Loop (3896.js)
│
│  user sends message
│  ↓
│  LLM sampling → response messages (e)
│  ↓
│  if (e.length > 0)
│      Zif([...existingMessages, ...newMessages], systemPrompt, userCtx, sysCtx, toolCtx, querySource)
│          │
│          ↓
│      Post-Sampling Hook Registry (Wif[])
│          │
│          └── g9_ (auto-memory extraction)
│                  │
│                  ├── gate checks (feature flag, thresholds)
│                  ├── reads current MEMORY.md
│                  ├── builds extraction prompt
│                  └── forks sub-query (AT) → writes updated MEMORY.md
```

---

## 1. Post-Sampling Hook Framework

**File**: `3887.js`

The hook system is a simple observer pattern — a push-only array of async callbacks.

```javascript
var Wif = [];   // hook registry

// Register a hook
function Tif(callback) {
    Wif.push(callback);
}

// Dispatch to all hooks (called after every LLM turn)
async function Zif(messages, systemPrompt, userContext, systemContext, toolUseContext, querySource) {
    let ctx = {
        messages,        // full conversation history (old + new)
        systemPrompt,    // current system prompt
        userContext,      // user-level context (CLAUDE.md, etc.)
        systemContext,    // system-level context
        toolUseContext,   // tool state (readFileState, etc.)
        querySource       // "repl_main_thread", "session_memory", "agent", etc.
    };
    for (let hook of Wif)
        try {
            await hook(ctx);
        } catch (err) {
            fH(err instanceof Error ? err : Error(`Post-sampling hook failed: ${err}`));
        }
}
```

### Key Properties

- **Sequential execution**: Hooks run one at a time (`for...of` + `await`), not in parallel
- **Error isolation**: Each hook is wrapped in try/catch — one failing hook doesn't block others
- **No return value**: Hooks are fire-and-forget; they can't modify the conversation flow
- **No priority/ordering**: Hooks execute in registration order (FIFO push)
- **Errors go to ring buffer**: Failed hooks report via `fH()` (error reporting system, 100-entry ring buffer)

### Call Site

In `3896.js`, inside the main query generator:

```javascript
// After LLM sampling produces assistant messages (e):
if (e.length > 0)
    Zif([...u, ...e], A, L, D, Y, M);
```

Where `u` = existing conversation messages, `e` = new assistant response messages.
Only fires when the model actually produced output (no-op on empty responses or errors).

---

## 2. Hook Registration

**File**: `4557.js`

Only one hook is currently registered: `g9_` (auto-memory extraction).

```javascript
function nX8() {
    if (L8()) return;     // skip if headless/simple mode (CLAUDE_CODE_SIMPLE)
    if (!$h()) return;    // skip if auto-compact disabled (DISABLE_COMPACT / DISABLE_AUTO_COMPACT)
    Tif(g9_);             // register the auto-memory hook
}
```

`nX8()` is called during session initialization. The hook is only registered when:
1. **Not in simple/headless mode** — `L8()` returns false
2. **Auto-compact enabled** — `$h()` returns true (respects `DISABLE_COMPACT` and `DISABLE_AUTO_COMPACT` env vars plus the `autoCompactEnabled` setting)

---

## 3. Auto-Memory Extraction Hook (g9_)

**File**: `4558.js`

This is the core logic. When the hook fires, it decides whether to extract memories from the conversation and write them to `summary.md`.

```javascript
g9_ = Tx(async function({ messages, toolUseContext, querySource }) {
    // ── Gate 1: Only run on main REPL thread ──
    if (querySource !== "repl_main_thread") return;

    // ── Gate 2: Feature flag must be on ──
    if (!b9_()) return;   // _A("tengu_session_memory", false)

    // ── Gate 3: Init config, check thresholds ──
    U9_();                // load config from tengu_sm_config
    if (!B9_(messages)) return;  // token + tool-call threshold check

    // ── Mark extraction in progress ──
    nnf();   // jR$ = Date.now()  (locks out concurrent extractions)

    // ── Read current memory file ──
    let toolCtx = gQH(toolUseContext);  // snapshot tool context
    let { memoryPath, currentMemory } = await p9_(toolCtx);

    // ── Build extraction prompt ──
    let prompt = await Kif(currentMemory, memoryPath);

    // ── Fork sub-query: extract and write memories ──
    await AT({
        promptMessages: [U$({ content: prompt })],
        cacheSafeParams: bu(hook_context),
        canUseTool: d9_(memoryPath),      // ONLY allow Write to the memory file
        querySource: "session_memory",
        forkLabel: "session_memory",
        overrides: { readFileState: toolCtx.readFileState }
    });

    // ── Log telemetry ──
    Q("tengu_session_memory_extraction", {
        input_tokens, output_tokens,
        cache_read_input_tokens, cache_creation_input_tokens,
        config_min_message_tokens_to_init,
        config_min_tokens_between_update,
        config_tool_calls_between_updates
    });

    // ── Update state ──
    tnf(jJ(messages));  // record token count at extraction time
    F9_(messages);      // update UUID bookmark
    inf();              // clear in-progress lock (jR$ = undefined)
});
```

---

## 4. Threshold System (B9_)

**File**: `4557.js` + `3879.js`

The hook doesn't fire every turn. `B9_()` enforces three thresholds:

```javascript
function B9_(messages) {
    let tokenCount = jJ(messages);  // total tokens in conversation

    // First-time init: have we hit minimum tokens?
    if (!snf()) {                             // snf() = Qnf (has init fired?)
        if (!Hif(tokenCount)) return false;   // tokenCount >= minimumMessageTokensToInit?
        enf();                                // mark init complete
    }

    // Recurring: enough tokens since last extraction?
    let tokenThresholdMet = $if(tokenCount);  // tokenCount - cnf >= minimumTokensBetweenUpdate?

    // Recurring: enough tool calls since last extraction?
    let toolCallThresholdMet = m9_(messages, lX8) >= Aif();  // count tool_use blocks since bookmark

    // Has assistant made tool calls in last message?
    let lastMsgHasToolUse = snH(messages);

    // Trigger if: (both thresholds met) OR (token threshold met AND no pending tool use)
    if (tokenThresholdMet && toolCallThresholdMet || tokenThresholdMet && !lastMsgHasToolUse) {
        lX8 = messages[messages.length - 1]?.uuid;  // bookmark current position
        return true;
    }
    return false;
}
```

### Default Thresholds

**File**: `3880.js`

```javascript
CnH = {
    minimumMessageTokensToInit:  10000,  // 10k tokens before first extraction
    minimumTokensBetweenUpdate:  5000,   // 5k tokens between extractions
    toolCallsBetweenUpdates:     3        // 3 tool calls between extractions
};
```

These can be overridden via the `tengu_sm_config` GrowthBook feature flag (object with same keys).

### State Variables

**File**: `3879.js`

```javascript
var cnf = 0;          // token count at last extraction
var Qnf = false;      // has first extraction fired?
var jR$ = undefined;   // timestamp when extraction started (concurrency lock)
var IWH = {...CnH};   // active config (defaults + overrides)
```

### Concurrency Protection

```javascript
function nnf() { jR$ = Date.now(); }   // mark extraction in-progress
function inf() { jR$ = undefined; }     // mark extraction complete

// rnf() — wait for in-progress extraction (with timeouts)
async function rnf() {
    let start = Date.now();
    while (jR$) {
        if (Date.now() - jR$ > 60000) return;   // extraction timeout: 60s
        if (Date.now() - start > 15000) return;  // wait timeout: 15s
        await new Promise(r => setTimeout(r, 1000));  // poll every 1s
    }
}
```

---

## 5. Memory File Location

**File**: `4468.js`

```javascript
function Um$() {
    return path.join(configDir(), sessionId(), "session-memory") + path.sep;
}

function XWH() {
    return path.join(Um$(), "summary.md");
}
```

Result: `~/.claude/projects/<project-hash>/<session-id>/session-memory/summary.md`

On first extraction, if the file doesn't exist, it's created with the default template via `flag: "wx"` (exclusive create — won't overwrite).

---

## 6. Extraction Prompt

**File**: `3880.js`

The extraction prompt (`fif()`) instructs the forked sub-query to update the memory file. Key rules:

1. **Scope**: Only analyze the user conversation — exclude system prompt, CLAUDE.md, and the note-taking instructions themselves
2. **Tool restriction**: Only use `Edit` tool on the memory file path (enforced by `d9_()`)
3. **Structure preservation**: Never modify section headers or italic description lines
4. **Per-section limit**: ~2,000 tokens per section (`JR$ = 2000`)
5. **Total file limit**: ~12,000 tokens (`Lif = 12000`)
6. **Parallel edits**: Make all Edit tool calls in a single parallel message, then stop
7. **Priority**: Always keep "Current State" up to date

### Customization

Users can override both the template and extraction prompt:

```
~/.claude/session-memory/config/template.md   → custom memory file template
~/.claude/session-memory/config/prompt.md      → custom extraction prompt
```

If these files exist, they're loaded instead of the defaults. If they fail to load (non-ENOENT error), the error is reported via `fH()` and defaults are used as fallback.

### Oversized File Handling

`sS6()` checks section sizes and total file size, appending condensation instructions:

- If any section exceeds 2,000 tokens: instructs the model to condense it
- If total file exceeds 12,000 tokens: **CRITICAL** instruction to aggressively shorten

### Default Template Sections

```markdown
# Session Title
# Current State
# Task specification
# Files and Functions
# Workflow
# Errors & Corrections
# Codebase and System Documentation
# Learnings
# Key results
# Worklog
```

---

## 7. Tool Restriction (d9_)

**File**: `4557.js`

The forked sub-query is sandboxed — it can only use one tool on one file:

```javascript
function d9_(memoryPath) {
    return async (tool, input) => {
        if (tool.name === Cf &&                // Cf = Edit tool name
            typeof input === "object" &&
            input !== null &&
            "file_path" in input) {
            let filePath = input.file_path;
            if (typeof filePath === "string" && filePath === memoryPath)
                return { behavior: "allow", updatedInput: input };
        }
        return {
            behavior: "deny",
            message: `only ${Cf} on ${memoryPath} is allowed`,
            decisionReason: { type: "other", reason: `only ${Cf} on ${memoryPath} is allowed` }
        };
    };
}
```

This ensures the memory extraction sub-query:
- Can ONLY use the Edit tool (not Read, Bash, Write, or any other tool)
- Can ONLY edit the specific memory file path
- Cannot read files, run commands, or modify anything else

---

## 8. Forked Sub-Query (AT)

**File**: `3900.js`

`AT()` creates an isolated LLM sub-query that shares the parent's conversation context but has its own tool permissions and query source:

```javascript
function AT({
    promptMessages,       // the extraction prompt
    cacheSafeParams,      // cache config from parent
    canUseTool,           // d9_(memoryPath) — restricted tool access
    querySource,          // "session_memory"
    forkLabel,            // "session_memory"
    overrides,            // { readFileState } from parent context
    maxOutputTokens,      // optional limit
    maxTurns,             // optional turn limit
    onMessage,            // optional message callback
    skipTranscript,       // don't save to transcript
    skipCacheWrite        // don't cache results
}) { ... }
```

The sub-query runs the same model as the main conversation, but:
- Uses a separate abort controller (doesn't affect main conversation if it fails)
- Has its own tool permission function (`canUseTool`)
- Is labeled `"session_memory"` for telemetry and to prevent recursive extraction
- Shares the parent's read file state so it knows what files have been read

---

## 9. Telemetry

### Event: `tengu_session_memory_extraction`
Fired after each successful extraction:
```javascript
{
    input_tokens: number,
    output_tokens: number,
    cache_read_input_tokens: number,
    cache_creation_input_tokens: number,
    config_min_message_tokens_to_init: number,    // active threshold
    config_min_tokens_between_update: number,     // active threshold
    config_tool_calls_between_updates: number     // active threshold
}
```

### Event: `tengu_session_memory_file_read`
Fired when the current memory file is read before extraction:
```javascript
{ content_length: number }
```

### Feature Flag: `tengu_session_memory`
- Type: boolean
- Default: `false`
- Controls: Whether auto-memory extraction is enabled at all

### Feature Flag: `tengu_sm_config`
- Type: object
- Default: `{}`
- Controls: Override threshold values (`minimumMessageTokensToInit`, `minimumTokensBetweenUpdate`, `toolCallsBetweenUpdates`)

---

## 10. Complete Data Flow

```
User sends message
    ↓
Main LLM sampling
    ↓
Response messages produced
    ↓
Zif() dispatches to all registered hooks
    ↓
g9_ (auto-memory) runs:
    │
    ├─ querySource === "repl_main_thread"?  (NO → skip, prevents recursion)
    ├─ tengu_session_memory flag on?         (NO → skip)
    ├─ Load config from tengu_sm_config
    ├─ B9_() threshold check:
    │   ├─ First time: tokenCount >= 10,000?
    │   ├─ Recurring: tokenCount - lastExtraction >= 5,000?
    │   └─ Recurring: toolCallsSinceLastExtraction >= 3?
    │   (NO → skip)
    │
    ├─ Lock (nnf: jR$ = Date.now())
    ├─ Read summary.md (create from template if missing)
    ├─ Build extraction prompt (fif + oversize warnings)
    │
    ├─ Fork sub-query via AT():
    │   ├─ Same model as main conversation
    │   ├─ Receives full conversation as context
    │   ├─ Tool access: ONLY Edit on summary.md
    │   ├─ querySource: "session_memory" (prevents recursive hook)
    │   └─ LLM extracts memories → Edit calls to update summary.md
    │
    ├─ Log tengu_session_memory_extraction
    ├─ Update token bookmark (cnf = current token count)
    ├─ Update UUID bookmark (lX8 = last message UUID)
    └─ Unlock (inf: jR$ = undefined)
```

---

## 11. Disabling Auto-Memory

Any of these will prevent the hook from running:

| Method | Effect |
|--------|--------|
| `tengu_session_memory = false` | Feature flag (server-side, default) |
| `DISABLE_COMPACT=true` | Prevents hook registration (`$h()` fails) |
| `DISABLE_AUTO_COMPACT=true` | Same effect |
| `CLAUDE_CODE_SIMPLE=true` | Headless mode, skips hook registration |
| Non-REPL querySource | Hook self-filters (only runs on `repl_main_thread`) |

---

## 12. Relationship to User-Facing "Auto Memory"

The auto-memory system visible to users (`/home/zack/.claude/projects/<hash>/memory/MEMORY.md`) is a **different system** from session memory. Session memory (`summary.md`) is:

- Per-session (lives in session directory, not project memory directory)
- Automatically extracted (no user control over content)
- Used for session continuity after compaction
- Gated by `tengu_session_memory` (currently off by default)

The user-facing auto-memory directory at `~/.claude/projects/<hash>/memory/` is managed by the model directly during conversation (via explicit Write/Edit tool calls in the main conversation), not by this post-sampling hook system.


---
## auto-mode-classifier-request-charges-claude-code-docs-283192b86c.md

## On this page

- [Respond to the notice](#respond-to-the-notice)
- [Make the session eligible](#make-the-session-eligible)
- [Why the notice appears](#why-the-notice-appears)
- [Related resources](#related-resources)

# Auto mode classifier request charges

Copy pageCopy page

Resolve the Claude Code notice saying this session isn’t eligible for auto mode’s no-charge classifier requests: what it means, why it appears, and what to do.

Copy pageCopy page

In [auto mode](/docs/en/permission-modes#eliminate-prompts-with-auto-mode), a classifier checks actions such as shell commands and network requests before they run. Wherever [server-side checks are on](/docs/en/permission-modes#server-side-classifier-review), the server performs those checks as part of the session’s own model requests, at no charge. This notice means the server’s checks aren’t reaching your session, so Claude Code is making its own classifier requests instead, and on your account those requests count toward your token usage:

```python
We're changing auto mode to no longer charge for classifier requests in Claude Code. However, this session isn't eligible.
```

At the prompt, Claude Code holds the first action it would check that way until you answer. Nothing is broken: auto mode keeps working, and its classifier requests are billed as they were before. The most common cause is an LLM gateway or proxy between Claude Code and the API, and when Claude Code can identify one, the notice names it. Press **Enter** to continue, or see [Make the session eligible](#make-the-session-eligible) to keep it from appearing in new sessions.


[​](#respond-to-the-notice)

Respond to the notice

The notice holds the action until you answer it:

- **Enter** continues: the held action and the rest of the session use Claude Code’s own classifier requests, billed as token usage as before, and the notice doesn’t appear again in that session. When the notice named a gateway, acknowledging it keeps it from reappearing on this machine for 24 hours. When it didn’t, the notice returns the next time a session falls back.
- **Esc** or **Ctrl+C** cancels: the held action doesn’t run and the current turn stops, with the session still in auto mode. Nothing is remembered, so the notice appears again before the next checked action.

To stop using auto mode instead, switch permission modes with `Shift+Tab` after you answer. Where the notice can’t wait for an answer, Claude Code reports the same text and the session continues in auto mode, unless a gateway acknowledgment on this machine in the last 24 hours has dismissed it. In [non-interactive mode](/docs/en/headless) with `-p` it prints the text to stderr, and in `stream-json` output it emits a `system` warning message, which Agent SDK applications can read from the message stream.


[​](#make-the-session-eligible)

Make the session eligible

If a gateway is the cause, ask your company’s admin or your gateway provider to pass requests and replies through unchanged. That means forwarding request headers and body fields as they are, including ones the gateway doesn’t recognize such as the `safeguards` request field, and returning responses and streaming events without dropping keys such as the `safeguard_results` field or rewriting tool-use IDs, as the [gateway compatibility guide](/docs/en/llm-gateway-protocol#feature-pass-through) describes. A gateway that passes traffic through this way keeps working with this feature and with future ones. New sessions then use the server’s checks again. If you already know that your gateway can’t provide the server’s checks, tell Claude Code not to ask for them there by setting `CLAUDE_CODE_AUTO_MODE_SERVER` to `0` before you start the session, in your shell or in the [`env` settings key](/docs/en/settings-reference#env):

```python
export CLAUDE_CODE_AUTO_MODE_SERVER=0
```

Classifier requests are then always Claude Code’s own, billed the same way, and the notice doesn’t appear. On a direct connection to the Anthropic API, the variable requires Claude Code v2.1.281 or later. Setting `CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS=1` while `CLAUDE_CODE_AUTO_MODE_SERVER` is unset turns the server’s checks off as well, except as [Disable pre-release capabilities](/docs/en/llm-gateway-protocol#disable-pre-release-capabilities) describes. `CLAUDE_CODE_AUTO_MODE_SERVER` is a temporary setting and may be removed in a later release.


[​](#why-the-notice-appears)

Why the notice appears

[Server-side classifier review](/docs/en/permission-modes#server-side-classifier-review) lists which sessions ask the server for the classifier checks. Pro, Max, and Team plans never show the notice. When it appears, the usual causes are:

- **An LLM gateway or proxy is in the path**: one that strips or rewrites request headers, drops request fields it doesn’t recognize, or edits responses. The server then never receives the request for checks, or Claude Code never receives the results. When your configuration or the responses identify the gateway, the notice names it.
- **Server-side checks haven’t reached your platform, region, or credential yet**: whether a platform or region performs them depends on that platform’s rollout. If you see the notice with no gateway or proxy in the path and it keeps appearing, this is the likely cause. To confirm, contact support or your company’s admin, or report it with `/feedback`.

To check a session that’s in auto mode, run `/status` at the Claude Code prompt: its **Auto mode server** row reads `Enabled` while the server’s checks decide the session’s actions and `Disabled` once the session has fallen back. When a gateway cuts responses short or rewrites the results into a form Claude Code can’t read, you get denials with no verdict in place of this notice; see [Server-side classifier review](/docs/en/permission-modes#server-side-classifier-review).


[​](#related-resources)

Related resources

- [Auto mode](/docs/en/permission-modes#eliminate-prompts-with-auto-mode): what auto mode is and what it blocks by default
- [Server-side classifier review](/docs/en/permission-modes#server-side-classifier-review): which sessions ask the server to check actions, and the Claude Code version each one requires
- [Gateway compatibility guide](/docs/en/llm-gateway-protocol#feature-pass-through): what breaks when a gateway strips headers or body fields
- [The server returned no safety verdict](/docs/en/errors#the-server-returned-no-safety-verdict): the denial you see when the server gives no verdict for an action
- [Manage costs effectively](/docs/en/costs): track token usage and reduce Claude Code costs


---
## auto-mode-config-f0dacf5866.md

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

# 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](/en/permission-modes#eliminate-prompts-with-auto-mode) lets Claude Code run without permission prompts by routing each tool call through a classifier that blocks anything irreversible, destructive, or aimed outside your environment. Use the `autoMode` settings block to tell that classifier which repos, buckets, and domains your organization trusts, so it stops blocking routine internal operations.

Out of the box, 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](/en/permission-modes#eliminate-prompts-with-auto-mode). This page is the configuration reference.

This page covers how to:

* [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
* [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

## Where the classifier reads configuration

The classifier reads the same [CLAUDE.md](how-claude-remembers-your-project-claude-code-docs-f1c064262d.md) 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                      |
| One project, one developer     | `.claude/settings.local.json`                   | Per-project trusted buckets or services, gitignored  |
| Organization-wide              | [Managed settings](../13-Enterprise-Admin/configure-server-managed-settings-claude-code-docs-d6a169b0bf.md) | Trusted infrastructure distributed to all developers |
| `--settings` flag or Agent SDK | Inline JSON                                     | Per-invocation overrides for automation              |

The classifier does not read `autoMode` from shared project settings in `.claude/settings.json`, so a checked-in repo cannot inject its own allow rules.

Entries from each scope are combined. A developer can extend `environment`, `allow`, and `soft_deny` with personal entries but cannot remove entries that managed settings provide. Because allow rules act as exceptions to 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](configure-permissions-claude-code-docs-7b0e64d485.md). 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 cannot 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.

The default environment list trusts the working repo and its configured remotes. To add your own entries alongside that default, 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.

```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"
    ]
  }
}
```

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
* **Additional context**: regulated-industry constraints, multi-tenant infrastructure, or compliance requirements that affect what the classifier should treat as risky

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

Two additional fields let you replace the classifier's built-in rule lists: `autoMode.soft_deny` controls what gets blocked, and `autoMode.allow` controls which exceptions apply. Each is an array of prose descriptions, read as natural-language rules. There is no `autoMode.deny` field; to hard-block an action regardless of intent, use [`permissions.deny`](configure-permissions-claude-code-docs-7b0e64d485.md), which runs before the classifier.

Inside the classifier, precedence works in three tiers:

* `soft_deny` rules block first
* `allow` rules then override matching blocks as exceptions
* Explicit user intent overrides both: 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" does not 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 risks specific to your environment that the defaults miss. 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.

```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"
    ]
  }
}
```

<Danger>
  Setting any of `environment`, `allow`, or `soft_deny` without `"$defaults"` replaces the entire default list for that section. If you set `soft_deny` with a single entry and omit `"$defaults"`, every built-in block rule is discarded: force push, data exfiltration, `curl | bash`, production deploys, and all other default block rules become allowed. Only omit `"$defaults"` when you intend to take full ownership of the list. In that case, 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.
</Danger>

Each section is evaluated independently, so setting `environment` alone leaves the default `allow` and `soft_deny` lists intact.

## Inspect the defaults and your effective config

Three CLI subcommands help you inspect and validate your configuration.

Print the built-in `environment`, `allow`, and `soft_deny` rules as JSON:

```bash theme={null}
claude auto-mode defaults
```

Print what the classifier actually uses as JSON, with your settings applied where set and defaults otherwise:

```bash theme={null}
claude auto-mode config
```

Get AI feedback on your custom `allow` and `soft_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. If you need to remove or rewrite a built-in rule rather than add alongside it, save the output of `claude auto-mode defaults` to a file, edit the lists, and paste the result into your settings file in place of `"$defaults"`.

## Review denials

When auto mode denies a tool call, the denial is recorded in `/permissions` under the Recently denied tab. 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.

Repeated denials for the same destination usually mean the classifier is missing context. Add that destination to `autoMode.environment`, then run `claude auto-mode config` to confirm it took effect.

To react to denials programmatically, use the [`PermissionDenied` hook](/en/hooks#permissiondenied).

## See also

* [Permission modes](/en/permission-modes#eliminate-prompts-with-auto-mode): what auto mode is, what it blocks by default, and how to enable it
* [Managed settings](../13-Enterprise-Admin/configure-server-managed-settings-claude-code-docs-d6a169b0bf.md): deploy `autoMode` configuration across your organization
* [Permissions](configure-permissions-claude-code-docs-7b0e64d485.md): allow, ask, and deny rules that apply before the classifier runs
* [Settings](claude-code-settings-claude-code-docs-d4420b4b52.md): the full settings reference, including the `autoMode` key


---
## automate-work-with-routines-claude-code-docs-1c8313d077.md

## On this page

- [Example use cases](#example-use-cases)
- [Create a routine](#create-a-routine)
  - [Create from the web](#create-from-the-web)
  - [Create from the CLI](#create-from-the-cli)
- [Configure triggers](#configure-triggers)
  - [Add a schedule trigger](#add-a-schedule-trigger)
  - [Schedule a one-off run](#schedule-a-one-off-run)
  - [Add an API trigger](#add-an-api-trigger)
  - [Trigger a routine](#trigger-a-routine)
  - [API reference](#api-reference)
  - [Add a GitHub trigger](#add-a-github-trigger)
  - [Supported events](#supported-events)
  - [Filter pull requests](#filter-pull-requests)
- [Manage routines](#manage-routines)
  - [View and interact with runs](#view-and-interact-with-runs)
  - [Edit and control routines](#edit-and-control-routines)
  - [Manage routines from the CLI](#manage-routines-from-the-cli)
  - [Repositories and branch permissions](#repositories-and-branch-permissions)
  - [Connectors](#connectors)
  - [Environments and network access](#environments-and-network-access)
- [Usage and limits](#usage-and-limits)
- [Troubleshooting](#troubleshooting)
  - [/schedule returns “Unknown command”](#schedule-returns-unknown-command)
  - [“Routines are disabled by your organization’s policy”](#routines-are-disabled-by-your-organizations-policy)
- [Related resources](#related-resources)

Claude Code in the cloud

# Automate work with routines

Copy pageCopy page

Put Claude Code on autopilot. Define routines that run on a schedule, trigger on API calls, or react to GitHub events from cloud infrastructure.

Copy pageCopy page

Routines are in research preview. Behavior, limits, and the API surface may change.

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. 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)

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

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’s **Code** tab, click **Routines** in the sidebar or in the sidebar’s **More** menu, 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 the session runs shell commands, uses [skills](/docs/en/skills) committed to the cloned repository, and calls any connectors you include, all without stopping for approval apart from some [artifact](/docs/en/artifacts) actions. 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. When the routine’s schedule or **Run now** starts a run, Claude republishes an existing artifact without asking only when all of these hold:

- You can edit the artifact and it belongs to your own organization
- The artifact isn’t shared publicly
- If the artifact is shared with specific people or your organization, its viewers don’t automatically see each new version
- The publish carries only the page, with no supporting files or anything else added, and doesn’t force over a newer version
- The page holds no grant that reaches beyond the page, such as [connector calls](/docs/en/artifacts#pull-live-data-with-mcp-connectors)

In every other case, including publishing a new artifact, Claude asks first. When a routine’s job is to keep a page current, give it an artifact you already published. 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)

Create from the web

1

Open the creation form

Visit [claude.ai/code/routines](https://claude.ai/code/routines) and click **New routine**.

2

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.

3

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.

4

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 on Pro and Max plans, store keys for the APIs Claude calls during a run as [API credentials](/docs/en/cloud-environments#add-api-credentials) instead. That section also lists the requests that never get a credential
- **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.

5

Select a trigger

Under **Select a trigger**, choose how the routine starts. You can pick one trigger type or combine several.

- Schedule

- GitHub event

- API

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, late starts, custom cron intervals, and one-off runs.

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.

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.

6

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.

7

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.


[​](#create-from-the-cli)

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)

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)

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. If you schedule a run exactly on the hour, such as 9:00, it can start several minutes late. To start close to the scheduled time, pick a few minutes past the hour, for example 9:07. 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)

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 start a follow-up task after an upstream change ships. 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. 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.

```python
/schedule tomorrow at 9am, summarize yesterday's merged PRs
```

```python
/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)

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.

1

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 open the menu next to the routine’s name and select **Edit**.

2

Add an API trigger

Scroll to the **Select a trigger** section below the **Instructions** box, click **Add another trigger**, and choose **API**.

3

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.

4

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.

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)

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:

```python
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:

```python
{
  "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.

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.


[​](#api-reference)

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)

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.

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).

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.

1

Open the routine for editing

Go to [claude.ai/code/routines](https://claude.ai/code/routines), click the routine, then open the menu next to the routine’s name and select **Edit**.

2

Add a GitHub event trigger

Scroll to the **Select a trigger** section, click **Add another trigger**, and choose **GitHub event**.

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.

3

Configure the trigger

Select the repository, choose an event from the [supported events](#supported-events) list, and optionally add filters. Save the trigger.


[​](#supported-events)

Supported events


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

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)

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)

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.

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.


[​](#edit-and-control-routines)

Edit and control routines

From the routine detail page you can:

- Click **Run now** to start a run immediately without waiting for the next scheduled time. You can optionally supply run-specific text, which reaches the routine the same way as the API trigger’s `text` field.
- Use the on/off switch at the top of the page to pause or resume the schedule. Paused routines keep their configuration but don’t run until you re-enable them.
- Open the menu next to the routine’s name and select **Edit** to change the name, prompt, repositories, environment, connectors, or any of the routine’s triggers. The **Select a trigger** section is where you add or remove schedules, API tokens, and GitHub event triggers.
- Open the same menu and select **Delete** to delete the routine.


[​](#manage-routines-from-the-cli)

Manage routines from the CLI

The CLI supports managing existing routines. Run `/schedule list` to see all routines, `/schedule update` to change one, or `/schedule run` to trigger it immediately. You can also ask about a routine’s run history, for example `/schedule why did my nightly review do nothing this morning?`. Claude lists the routine’s recent runs with their status and a link to [open each run on the web](#view-and-interact-with-runs), and reads a run’s log to explain what happened, including tool errors, permission denials, and the final result. Requires Claude Code v2.1.227 or later.


[​](#repositories-and-branch-permissions)

Repositories and branch permissions

Routines need GitHub access to clone repositories. When you create a routine from the CLI with `/schedule`, Claude checks whether your account has GitHub access for the repository you ran it from and, if it doesn’t, adds a setup note naming how to grant it. See [GitHub authentication options](/docs/en/claude-code-on-the-web#github-authentication-options) for the two ways to grant access. If your GitHub connection is missing or expired when a run is due, the routine skips runs until you reconnect, for up to 72 hours. Reconnect GitHub within that window and the routine resumes on its own. After 72 hours without a connection, the routine turns off, and you turn it back on after reconnecting GitHub. Each repository you add is cloned on every run. Claude starts from the repository’s default branch unless your prompt specifies otherwise. Claude pushes its work to branches prefixed with `claude/`, which are always accepted. When your prompt directs Claude to push to another branch, Claude Code checks the push first and rejects it if any of the following is true:

- The branch is protected on GitHub
- Someone else has an open pull request from that branch
- The branch carries commits authored by someone other than you


[​](#connectors)

Connectors

Routines can use your connected MCP connectors to read from and write to external services during each run. For example, a routine that triages support requests might read from a Slack channel and create issues in Linear. Connectors are the [claude.ai integrations](/docs/en/mcp#use-mcp-servers-from-claude-ai) on your account. MCP servers you added locally in the CLI with `claude mcp add` are stored on your machine rather than your claude.ai account, so they do not appear in the connectors list. To use one of those servers in a routine, add it as a connector at [claude.ai/customize/connectors](https://claude.ai/customize/connectors). For a routine with one repository, you can instead declare it in a committed [`.mcp.json`](/docs/en/mcp#project-scope) so it is part of the cloned repository. When you create a routine, all of your currently connected connectors are included by default. Remove any that aren’t needed to limit which tools Claude has access to during the run. You can also add connectors directly from the routine form. To manage or add connectors outside of the routine form, visit [claude.ai/customize/connectors](https://claude.ai/customize/connectors) or use `/schedule update` in the CLI.


[​](#environments-and-network-access)

Environments and network access

Each routine uses a [cloud environment](/docs/en/cloud-environments) that controls network access, environment variables, and setup scripts. The routine inherits the environment’s network policy on every run. The **Default** environment uses **Trusted** network access, which allows only the [default allowlist](/docs/en/cloud-environments#default-allowed-domains) through the session’s network. Requests on that path to hosts outside the allowlist fail with `403` and `x-deny-reason: host_not_allowed`. MCP connector traffic is routed through Anthropic’s servers rather than that path, so the connectors you add to the routine work without adding their hosts to **Allowed domains**. Remove any connectors you don’t need under [Connectors](#connectors). To allow additional domains on one of your own environments, follow these steps. An [organization-shared environment](/docs/en/cloud-environments#organization-shared-environments) opens read-only here, so an Owner changes its network access from the **Cloud environments** page in [admin settings](https://claude.ai/admin-settings) instead.

1

Open the routine for editing

On the routine’s detail page, open the menu next to the routine’s name and select **Edit**.

2

Open the environment selector

Below the **Instructions** box, select the cloud icon showing your environment’s name, such as **Default**.

3

Open the environment settings

Hover over the environment in the list and click the settings icon that appears on the right.

4

Change the network access level

In the **Update cloud environment** dialog, change **Network access** to **Custom** and enter your domains in **Allowed domains**. Check **Also include default list of common package managers** to keep the [default allowlist](/docs/en/cloud-environments#default-allowed-domains) alongside your custom domains. Select **Full** instead for unrestricted access.

5

Save

Click **Save changes**. The new policy applies from the next run.

See [Network access](/docs/en/cloud-environments#network-access) for details on access levels and the default allowlist.


[​](#usage-and-limits)

Usage and limits

Routines draw down subscription usage the same way interactive sessions do. In addition to the standard subscription limits, routines have a daily cap on how many runs can start per account. See your current consumption and remaining daily routine runs at [claude.ai/code/routines](https://claude.ai/code/routines) or [claude.ai/settings/usage](https://claude.ai/settings/usage). When a routine hits the daily cap or your subscription usage limit, organizations with usage credits turned on can keep running routines on metered overage. Without usage credits, additional runs are rejected until the window resets. Turn on usage credits at [claude.ai/settings/usage](https://claude.ai/settings/usage). On Team and Enterprise plans, an admin turns them on for the organization at [claude.ai/admin-settings/usage](https://claude.ai/admin-settings/usage). One-off runs do not count against the daily routine cap. They draw down your regular subscription usage like any other session. While your subscription is paused, your routines are put on hold and don’t run. Once your subscription is active again, turn them back on.


[​](#troubleshooting)

Troubleshooting


[​](#schedule-returns-unknown-command)

`/schedule` returns “Unknown command”

The CLI hides `/schedule` when one of its requirements isn’t met: the command menu shows `No commands match "/schedule"` while you type. Submitting it returns `Unknown command: /schedule`, except in the cases below that note a different answer. The cause is usually one of the following:

- You are authenticated with a Console API key, an [Anthropic profile or federation credential](/docs/en/authentication#anthropic-profiles-and-federation-credentials), or a cloud provider such as Amazon Bedrock, Google Cloud’s Agent Platform, or Microsoft Foundry. `/schedule` requires a claude.ai subscription login. With a Console API key or a profile, and feature-flag fetching enabled, submitting `/schedule` instead shows `/schedule is available with Claude for Enterprise — ask your admin about migrating from API-key access`. With a cloud-provider login, you still see `Unknown command: /schedule`. If `ANTHROPIC_API_KEY` or `ANTHROPIC_AUTH_TOKEN` is set in your shell, or `apiKeyHelper` is set in `settings.json`, remove it first, since these take precedence over a claude.ai login. A profile or federation credential takes precedence too, so switch that off as well
- You are fully signed out, with no API key or other credential. With feature-flag fetching enabled, submitting `/schedule` shows `/schedule requires a claude.ai subscription. Run /login to sign in with your claude.ai account.` Before v2.1.268, a signed-out session showed the same Claude for Enterprise message as a Console API key
- You are inside a cloud session, where submitting `/schedule` answers that the command isn’t available in that environment. Manage routines from the [web UI](https://claude.ai/code/routines) instead
- Your organization’s policy disables [cloud sessions](/docs/en/claude-code-on-the-web), which routines require. In this case, submitting `/schedule` answers [`Cloud sessions are disabled by your organization's policy`](/docs/en/errors#cloud-sessions-are-disabled-by-your-organizations-policy) instead. Before v2.1.268, it returned `Unknown command: /schedule`
- An Owner [turned off routines](#routines-are-disabled-by-your-organizations-policy) for your Team or Enterprise organization. Before v2.1.227, the command still appeared in this case, and claude.ai rejected the routine when Claude tried to create or run it

Unless your organization’s policy disables routines or cloud sessions, you can create and manage routines at [claude.ai/code/routines](https://claude.ai/code/routines) regardless of how the CLI is configured.


[​](#routines-are-disabled-by-your-organizations-policy)

“Routines are disabled by your organization’s policy”

An Owner in your Team or Enterprise organization has likely turned off the **Routines** toggle at [claude.ai/admin-settings/claude-code](https://claude.ai/admin-settings/claude-code). On Claude Code v2.1.227 or later, the same toggle also hides `/schedule` in the CLI. This is a server-side organization setting, so it cannot be overridden from your local configuration. Ask an Owner to enable routines for your organization.


[​](#related-resources)

Related resources

- [`/loop` and in-session scheduling](/docs/en/scheduled-tasks): schedule local tasks within an open CLI session
- [Desktop scheduled tasks](/docs/en/desktop-scheduled-tasks): local scheduled tasks that run on your machine with access to local files
- [Cloud environments](/docs/en/cloud-environments): configure network access, environment variables, and setup scripts for cloud sessions
- [Projects](/docs/en/claude-projects): ongoing work Claude coordinates across parallel cloud sessions; routines created from a project appear on its **Routines** tab
- [MCP connectors](/docs/en/mcp): connect external services like Slack, Linear, and Google Drive
- [GitHub Actions](/docs/en/github-actions): run Claude in your CI pipeline on repository events


---
## automate-work-with-routines-claude-code-docs-dbf1010e44.md

## On this page

- [Example use cases](#example-use-cases)
- [Create a routine](#create-a-routine)
  - [Create from the web](#create-from-the-web)
  - [Create from the CLI](#create-from-the-cli)
- [Configure triggers](#configure-triggers)
  - [Add a schedule trigger](#add-a-schedule-trigger)
  - [Schedule a one-off run](#schedule-a-one-off-run)
  - [Add an API trigger](#add-an-api-trigger)
  - [Trigger a routine](#trigger-a-routine)
  - [API reference](#api-reference)
  - [Add a GitHub trigger](#add-a-github-trigger)
  - [Supported events](#supported-events)
  - [Filter pull requests](#filter-pull-requests)
- [Manage routines](#manage-routines)
  - [View and interact with runs](#view-and-interact-with-runs)
  - [Edit and control routines](#edit-and-control-routines)
  - [Manage routines from the CLI](#manage-routines-from-the-cli)
  - [Repositories and branch permissions](#repositories-and-branch-permissions)
  - [Connectors](#connectors)
  - [Environments and network access](#environments-and-network-access)
- [Usage and limits](#usage-and-limits)
- [Troubleshooting](#troubleshooting)
  - [/schedule returns “Unknown command”](#schedule-returns-unknown-command)
  - [“Routines are disabled by your organization’s policy”](#routines-are-disabled-by-your-organizations-policy)
- [Related resources](#related-resources)

Claude Code in the cloud

# Automate work with routines

Copy pageCopy page

Put Claude Code on autopilot. Define routines that run on a schedule, trigger on API calls, or react to GitHub events from cloud infrastructure.

Copy pageCopy page

Routines are in research preview. Behavior, limits, and the API surface may change.

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. 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)

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

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’s **Code** tab, click **Routines** in the sidebar or in the sidebar’s **More** menu, 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 the session runs shell commands, uses [skills](/docs/en/skills) committed to the cloned repository, and calls any connectors you include, all without stopping for approval apart from some [artifact](/docs/en/artifacts) actions. 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. When the routine’s schedule or **Run now** starts a run, Claude republishes an existing artifact without asking only when all of these hold:

- You can edit the artifact and it belongs to your own organization
- The artifact isn’t shared publicly
- If the artifact is shared with specific people or your organization, its viewers don’t automatically see each new version
- The publish carries only the page, with no supporting files or anything else added, and doesn’t force over a newer version
- The page holds no grant that reaches beyond the page, such as [connector calls](/docs/en/artifacts#pull-live-data-with-mcp-connectors)

In every other case, including publishing a new artifact, Claude asks first. When a routine’s job is to keep a page current, give it an artifact you already published. 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)

Create from the web

1

Open the creation form

Visit [claude.ai/code/routines](https://claude.ai/code/routines) and click **New routine**.

2

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.

3

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.

4

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 on Pro and Max plans, store keys for the APIs Claude calls during a run as [API credentials](/docs/en/cloud-environments#add-api-credentials) instead. That section also lists the requests that never get a credential
- **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.

5

Select a trigger

Under **Select a trigger**, choose how the routine starts. You can pick one trigger type or combine several.

- Schedule

- GitHub event

- API

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, late starts, custom cron intervals, and one-off runs.

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.

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.

6

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.

7

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.


[​](#create-from-the-cli)

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)

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)

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. If you schedule a run exactly on the hour, such as 9:00, it can start several minutes late. To start close to the scheduled time, pick a few minutes past the hour, for example 9:07. 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)

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 start a follow-up task after an upstream change ships. 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. 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.

```python
/schedule tomorrow at 9am, summarize yesterday's merged PRs
```

```python
/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)

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.

1

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 open the menu next to the routine’s name and select **Edit**.

2

Add an API trigger

Scroll to the **Select a trigger** section below the **Instructions** box, click **Add another trigger**, and choose **API**.

3

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.

4

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.

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)

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:

```python
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:

```python
{
  "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.

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.


[​](#api-reference)

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)

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.

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).

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.

1

Open the routine for editing

Go to [claude.ai/code/routines](https://claude.ai/code/routines), click the routine, then open the menu next to the routine’s name and select **Edit**.

2

Add a GitHub event trigger

Scroll to the **Select a trigger** section, click **Add another trigger**, and choose **GitHub event**.

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.

3

Configure the trigger

Select the repository, choose an event from the [supported events](#supported-events) list, and optionally add filters. Save the trigger.


[​](#supported-events)

Supported events


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

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)

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)

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.

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.


[​](#edit-and-control-routines)

Edit and control routines

From the routine detail page you can:

- Click **Run now** to start a run immediately without waiting for the next scheduled time. You can optionally supply run-specific text, which reaches the routine the same way as the API trigger’s `text` field.
- Use the on/off switch at the top of the page to pause or resume the schedule. Paused routines keep their configuration but don’t run until you re-enable them.
- Open the menu next to the routine’s name and select **Edit** to change the name, prompt, repositories, environment, connectors, or any of the routine’s triggers. The **Select a trigger** section is where you add or remove schedules, API tokens, and GitHub event triggers.
- Open the same menu and select **Delete** to delete the routine.


[​](#manage-routines-from-the-cli)

Manage routines from the CLI

The CLI supports managing existing routines. Run `/schedule list` to see all routines, `/schedule update` to change one, or `/schedule run` to trigger it immediately. You can also ask about a routine’s run history, for example `/schedule why did my nightly review do nothing this morning?`. Claude lists the routine’s recent runs with their status and a link to [open each run on the web](#view-and-interact-with-runs), and reads a run’s log to explain what happened, including tool errors, permission denials, and the final result. Requires Claude Code v2.1.227 or later.


[​](#repositories-and-branch-permissions)

Repositories and branch permissions

Routines need GitHub access to clone repositories. When you create a routine from the CLI with `/schedule`, Claude checks whether your account has GitHub access for the repository you ran it from and, if it doesn’t, adds a setup note naming how to grant it. See [GitHub authentication options](/docs/en/claude-code-on-the-web#github-authentication-options) for the two ways to grant access. If your GitHub connection is missing or expired when a run is due, the routine skips runs until you reconnect, for up to 72 hours. Reconnect GitHub within that window and the routine resumes on its own. After 72 hours without a connection, the routine turns off, and you turn it back on after reconnecting GitHub. Each repository you add is cloned on every run. Claude starts from the repository’s default branch unless your prompt specifies otherwise. Claude pushes its work to branches prefixed with `claude/`, which are always accepted. When your prompt directs Claude to push to another branch, Claude Code checks the push first and rejects it if any of the following is true:

- The branch is protected on GitHub
- Someone else has an open pull request from that branch
- The branch carries commits authored by someone other than you


[​](#connectors)

Connectors

Routines can use your connected MCP connectors to read from and write to external services during each run. For example, a routine that triages support requests might read from a Slack channel and create issues in Linear. Connectors are the [claude.ai integrations](/docs/en/mcp#use-mcp-servers-from-claude-ai) on your account. MCP servers you added locally in the CLI with `claude mcp add` are stored on your machine rather than your claude.ai account, so they do not appear in the connectors list. To use one of those servers in a routine, add it as a connector at [claude.ai/customize/connectors](https://claude.ai/customize/connectors). For a routine with one repository, you can instead declare it in a committed [`.mcp.json`](/docs/en/mcp#project-scope) so it is part of the cloned repository. When you create a routine, all of your currently connected connectors are included by default. Remove any that aren’t needed to limit which tools Claude has access to during the run. You can also add connectors directly from the routine form. To manage or add connectors outside of the routine form, visit [claude.ai/customize/connectors](https://claude.ai/customize/connectors) or use `/schedule update` in the CLI.


[​](#environments-and-network-access)

Environments and network access

Each routine uses a [cloud environment](/docs/en/cloud-environments) that controls network access, environment variables, and setup scripts. The routine inherits the environment’s network policy on every run. The **Default** environment uses **Trusted** network access, which allows only the [default allowlist](/docs/en/cloud-environments#default-allowed-domains) through the session’s network. Requests on that path to hosts outside the allowlist fail with `403` and `x-deny-reason: host_not_allowed`. MCP connector traffic is routed through Anthropic’s servers rather than that path, so the connectors you add to the routine work without adding their hosts to **Allowed domains**. Remove any connectors you don’t need under [Connectors](#connectors). To allow additional domains on one of your own environments, follow these steps. An [organization-shared environment](/docs/en/cloud-environments#organization-shared-environments) opens read-only here, so an Owner changes its network access from the **Cloud environments** page in [admin settings](https://claude.ai/admin-settings) instead.

1

Open the routine for editing

On the routine’s detail page, open the menu next to the routine’s name and select **Edit**.

2

Open the environment selector

Below the **Instructions** box, select the cloud icon showing your environment’s name, such as **Default**.

3

Open the environment settings

Hover over the environment in the list and click the settings icon that appears on the right.

4

Change the network access level

In the **Update cloud environment** dialog, change **Network access** to **Custom** and enter your domains in **Allowed domains**. Check **Also include default list of common package managers** to keep the [default allowlist](/docs/en/cloud-environments#default-allowed-domains) alongside your custom domains. Select **Full** instead for unrestricted access.

5

Save

Click **Save changes**. The new policy applies from the next run.

See [Network access](/docs/en/cloud-environments#network-access) for details on access levels and the default allowlist.


[​](#usage-and-limits)

Usage and limits

Routines draw down subscription usage the same way interactive sessions do. In addition to the standard subscription limits, routines have a daily cap on how many runs can start per account. See your current consumption and remaining daily routine runs at [claude.ai/code/routines](https://claude.ai/code/routines) or [claude.ai/settings/usage](https://claude.ai/settings/usage). When a routine hits the daily cap or your subscription usage limit, organizations with usage credits turned on can keep running routines on metered overage. Without usage credits, additional runs are rejected until the window resets. Turn on usage credits at [claude.ai/settings/usage](https://claude.ai/settings/usage). On Team and Enterprise plans, an admin turns them on for the organization at [claude.ai/admin-settings/usage](https://claude.ai/admin-settings/usage). One-off runs do not count against the daily routine cap. They draw down your regular subscription usage like any other session. While your subscription is paused, your routines are put on hold and don’t run. Once your subscription is active again, turn them back on.


[​](#troubleshooting)

Troubleshooting


[​](#schedule-returns-unknown-command)

`/schedule` returns “Unknown command”

The CLI hides `/schedule` when one of its requirements isn’t met: the command menu shows `No commands match "/schedule"` while you type. Submitting it returns `Unknown command: /schedule`, except in the cases below that note a different answer. The cause is usually one of the following:

- You are authenticated with a Console API key, an [Anthropic profile or federation credential](/docs/en/authentication#anthropic-profiles-and-federation-credentials), or a cloud provider such as Amazon Bedrock, Google Cloud’s Agent Platform, or Microsoft Foundry. `/schedule` requires a claude.ai subscription login. With a Console API key or a profile, and feature-flag fetching enabled, submitting `/schedule` instead shows `/schedule is available with Claude for Enterprise — ask your admin about migrating from API-key access`. With a cloud-provider login, you still see `Unknown command: /schedule`. If `ANTHROPIC_API_KEY` or `ANTHROPIC_AUTH_TOKEN` is set in your shell, or `apiKeyHelper` is set in `settings.json`, remove it first, since these take precedence over a claude.ai login. A profile or federation credential takes precedence too, so switch that off as well
- You are fully signed out, with no API key or other credential. With feature-flag fetching enabled, submitting `/schedule` shows `/schedule requires a claude.ai subscription. Run /login to sign in with your claude.ai account.` Before v2.1.268, a signed-out session showed the same Claude for Enterprise message as a Console API key
- You are inside a cloud session, where submitting `/schedule` answers that the command isn’t available in that environment. Manage routines from the [web UI](https://claude.ai/code/routines) instead
- Your organization’s policy disables [cloud sessions](/docs/en/claude-code-on-the-web), which routines require. In this case, submitting `/schedule` answers [`Cloud sessions are disabled by your organization's policy`](/docs/en/errors#cloud-sessions-are-disabled-by-your-organizations-policy) instead. Before v2.1.268, it returned `Unknown command: /schedule`
- An Owner [turned off routines](#routines-are-disabled-by-your-organizations-policy) for your Team or Enterprise organization. Before v2.1.227, the command still appeared in this case, and claude.ai rejected the routine when Claude tried to create or run it

Unless your organization’s policy disables routines or cloud sessions, you can create and manage routines at [claude.ai/code/routines](https://claude.ai/code/routines) regardless of how the CLI is configured.


[​](#routines-are-disabled-by-your-organizations-policy)

“Routines are disabled by your organization’s policy”

An Owner in your Team or Enterprise organization has likely turned off the **Routines** toggle at [claude.ai/admin-settings/claude-code](https://claude.ai/admin-settings/claude-code). On Claude Code v2.1.227 or later, the same toggle also hides `/schedule` in the CLI. This is a server-side organization setting, so it cannot be overridden from your local configuration. Ask an Owner to enable routines for your organization.


[​](#related-resources)

Related resources

- [`/loop` and in-session scheduling](/docs/en/scheduled-tasks): schedule local tasks within an open CLI session
- [Desktop scheduled tasks](/docs/en/desktop-scheduled-tasks): local scheduled tasks that run on your machine with access to local files
- [Cloud environments](/docs/en/cloud-environments): configure network access, environment variables, and setup scripts for cloud sessions
- [Projects](/docs/en/claude-projects): ongoing work Claude coordinates across parallel cloud sessions; routines created from a project appear on its **Routines** tab
- [MCP connectors](/docs/en/mcp): connect external services like Slack, Linear, and Google Drive
- [GitHub Actions](/docs/en/github-actions): run Claude in your CI pipeline on repository events


---
## automated-security-reviews-in-claude-code-168fb0760e.md

# Automated Security Reviews in Claude Code

March 16, 2026

Copy for LLM

Claude Code now includes automated security review features to help you identify and fix vulnerabilities in your code. This guide explains how to use the /security-review command and GitHub Actions to improve your code security.

**Note:** While automated security reviews help identify many common vulnerabilities, they should complement, not replace, your existing security practices and manual code reviews.

## Overview

Automated security reviews in Claude Code help developers catch vulnerabilities before they reach production. These features check for common security issues including SQL injection risks, cross-site scripting (XSS) vulnerabilities, authentication flaws, insecure data handling, and dependency vulnerabilities.

You can use security reviews in two ways: through the /security-review command for on-demand checks in your terminal, or through GitHub Actions for automatic review of pull requests.

## Availability

These features are available for all Claude Code users, including:

- Users on individual paid plans (Pro or Max).

- Individual users or enterprises with pay-as-you-go API Console accounts.

## Using the /security-review command

The /security-review command lets you run security analysis directly from your terminal before committing code.

### Running a Security Review

To check your code for vulnerabilities:

1.  Open Claude Code in your project directory.

2.  Run /security-review in the terminal.

3.  Claude will analyze your codebase and identify potential security concerns.

4.  Review the detailed explanations provided for each issue found.

### Implementing Fixes

After Claude identifies vulnerabilities, you can ask it to implement fixes directly. This keeps security reviews integrated into your development workflow, allowing you to address issues when they're easiest to resolve.

### Customizing the Command

You can customize the /security-review command for your specific needs. See the [security review documentation](https://github.com/anthropics/claude-code-security-review/tree/main?tab=readme-ov-file#security-review-slash-command) for configuration options.

## Setting up GitHub Actions for automated PR reviews

After installing and configuring the GitHub action, it will automatically review every pull request for security vulnerabilities when it's opened.

### Installation

To set up automated security reviews for your repository:

1.  Navigate to your repository's GitHub Actions settings

2.  Follow the [step-by-step installation guide](https://github.com/anthropics/claude-code-security-review) in our documentation

3.  Configure the action according to your team's security requirements

### How It Works

Once configured, the GitHub action:

- Triggers automatically when new pull requests are opened.

- Reviews code changes for security vulnerabilities.

- Applies customizable filtering rules to reduce false positives.

- Posts inline comments on the PR with identified concerns and recommended fixes.

This creates a consistent security review process across your entire team, ensuring code is checked for vulnerabilities before merging.

### Customization Options

You can customize the GitHub action to match your team's security policies, including setting specific rules for your codebase and adjusting sensitivity levels for different vulnerability types.

## What security issues can be detected?

Both the /security-review command and GitHub action check for common vulnerability patterns:

- **SQL injection risks**: Identifies potential database query vulnerabilities.

- **Cross-site scripting (XSS)**: Detects client-side script injection vulnerabilities.

- **Authentication and authorization flaws**: Finds issues with access control.

- **Insecure data handling**: Identifies problems with data validation and sanitization.

- **Dependency vulnerabilities**: Checks for known issues in third-party packages.

## Getting Started

To start using automated security reviews:

- **For the /security-review command**: Update Claude Code to the latest version (run), then run `/security-review` in your project directory.

  - Claude Code automatically keeps itself up to date to ensure you have the latest features and security fixes, but you can also run `claude update` to update manually.

- **For the GitHub actions**: Visit our [documentation](https://github.com/anthropics/claude-code-security-review) for installation and configuration instructions.

## Best Practices

For optimal results, we recommend running /security-review before committing significant changes and configuring the GitHub action for all repositories containing production code. Consider adjusting the filtering rules based on your team's specific security requirements and codebase characteristics.


---
## automatic-context-compaction.md

# Automatic Context Compaction

Long-running agentic tasks can often exceed context limits. Tool heavy
workflows or long conversations quickly consume the token context
window. In [Effective Context Engineering for AI
Agents](https://www.anthropic.com/engineering/effective-context-engineering-for-ai-agents),
we discussed how managing context can help avoid performance degradation
and context rot.

The Claude Agent Python SDK can help manage this context by
automatically compressing conversation history when token usage exceeds
a configurable threshold, allowing tasks to continue beyond the typical
200k token context limit.

In this cookbook, we\'ll demonstrate context compaction through an
**agentic customer service workflow**. Imagine you\'ve built an AI
customer service agent tasked with processing a queue of support
tickets. For each ticket, you must classify the issue, search the
knowledge base, set priority, route to the appropriate team, draft a
response, and mark it complete. As you process ticket after ticket, the
conversation history fills with classifications, knowledge base
searches, and drafted responses---quickly consuming thousands of tokens.

## What is Context Compaction?

When building agentic workflows with tool use, conversations can grow
very large as the agent iterates on complex tasks. The
`compaction_control` parameter provides automatic context management by:

1.  Monitoring token usage per turn in the conversation
2.  When a threshold is exceeded, injecting a summary prompt as a user
    turn
3.  Having the model generate a summary wrapped in `<summary></summary>`
    tags. These tags aren\'t parsed, but are there to help guide the
    model.
4.  Clearing the conversation history and resuming with only the summary
5.  Continuing the task with the compressed context

## By the end of this cookbook, you\'ll be able to:

- Understand how to effectively manage context limits in iterative
  workflows
- Write agents that leverage automatic context compaction
- Design workflows that maintain focus across multiple iterations

## Prerequisites

Before following this guide, ensure you have:

**Required Knowledge**

- Basic understanding of agentic patterns and tool calling

**Required Tools**

- Python 3.11 or higher
- Anthropic API key
- Anthropic SDK \>= 0.74.1
:::

::: {#004e3b52 .cell .markdown}
## Setup

First, install the required dependencies:
:::

::: {#71d7f8c2 .cell .code execution_count="1"}
``` python
# %pip install -qU anthropic python-dotenv
```
:::

::: {#1ba96da5 .cell .markdown}
Note: Ensure your .env file contains:

`ANTHROPIC_API_KEY=your_key_here`

Load your environment variables and configure the client. We also load a
helper utility to visualize Claude message responses.
:::

::: {#68f6d5bb .cell .code execution_count="2"}
``` python
from dotenv import load_dotenv

load_dotenv()

MODEL = "claude-sonnet-4-5"
```
:::

::: {#783cf8e3 .cell .markdown}
## Setting the Stage

In [utils/customer_service_tools.py](utils/customer_service_tools.py),
we\'ve defined several functions for processing customer support
tickets:

- `get_next_ticket()` - Retrieves the next unprocessed ticket from the
  queue
- `classify_ticket(ticket_id, category)` - Categorizes issues as
  billing, technical, account, product, or shipping
- `search_knowledge_base(query)` - Finds relevant help articles and
  solutions
- `set_priority(ticket_id, priority)` - Assigns priority levels (low,
  medium, high, urgent)
- `route_to_team(ticket_id, team)` - Routes tickets to the appropriate
  support team
- `draft_response(ticket_id, response_text)` - Creates customer-facing
  responses
- `mark_complete(ticket_id)` - Finalizes processed tickets

For a customer service agent, these tools enable processing tickets
systematically. Each ticket requires classification, research,
prioritization, routing, and response drafting. When processing 20-30
tickets in sequence, the conversation history fills with tool results
from every classification, every knowledge base search, and every
drafted response, causing linear token growth.

The `beta_tool` decorator is used on the tools to make them accessible
to the Claude agent. The decorator extracts the function arguments and
docstring and provides these to Claude as tool metadata.

``` python
import anthropic
from anthropic import beta_tool

@beta_tool
def get_next_ticket() -> dict:
    """Retrieve the next unprocessed support ticket from the queue."""
    ...
```
:::

::: {#58d2a922 .cell .code execution_count="3"}
``` python
import anthropic
from utils.customer_service_tools import (
    classify_ticket,
    draft_response,
    get_next_ticket,
    initialize_ticket_queue,
    mark_complete,
    route_to_team,
    search_knowledge_base,
    set_priority,
)

client = anthropic.Anthropic()

tools = [
    get_next_ticket,
    classify_ticket,
    search_knowledge_base,
    set_priority,
    route_to_team,
    draft_response,
    mark_complete,
]
```
:::

::: {#77fecfb8 .cell .markdown}
## Baseline: Running Without Compaction

Let\'s start with a realistic customer service scenario: Processing a
queue of support tickets.

The workflow looks like this:

**For Each Ticket:**

1.  Fetch the ticket using `get_next_ticket()`
2.  Classify the issue category (billing, technical, account, product,
    shipping)
3.  Search the knowledge base for relevant information
4.  Set appropriate priority (low, medium, high, urgent)
5.  Route to the correct team
6.  Draft a customer response
7.  Mark the ticket complete
8.  Move to the next ticket

**The Challenge**: With 5 tickets in the queue, and each requiring 7
tool calls, Claude will make 35 or more tool calls. The results from
each step including classification knowledge base search, and drafted
responses accumulate in the conversation history. Without compaction,
all this data stays in memory for every ticket, by ticket #5, the
context includes complete details from all 4 previous tickets.

Let\'s run this workflow **without compaction** first and observe what
happens:
:::

:::: {#uef86nvtl4 .cell .code execution_count="4"}
``` python
from anthropic.types.beta import BetaMessageParam

num_tickets = 5
initialize_ticket_queue(num_tickets)

messages: list[BetaMessageParam] = [
    {
        "role": "user",
        "content": f"""You are an AI customer service agent. Your task is to process support tickets from a queue.

For EACH ticket, you must complete ALL these steps:

1. **Fetch ticket**: Call get_next_ticket() to retrieve the next unprocessed ticket
2. **Classify**: Call classify_ticket() to categorize the issue (billing/technical/account/product/shipping)
3. **Research**: Call search_knowledge_base() to find relevant information for this ticket type
4. **Prioritize**: Call set_priority() to assign priority (low/medium/high/urgent) based on severity
5. **Route**: Call route_to_team() to assign to the appropriate team
6. **Draft**: Call draft_response() to create a helpful customer response using KB information
7. **Complete**: Call mark_complete() to finalize this ticket
8. **Continue**: Immediately fetch the next ticket and repeat

IMPORTANT RULES:
- Process tickets ONE AT A TIME in sequence
- Complete ALL 7 steps for each ticket before moving to the next
- Keep fetching and processing tickets until you get an error that the queue is empty
- There are {num_tickets} tickets total - process all of them
- Be thorough but efficient

Begin by fetching the first ticket.""",
    }
]

total_input = 0
total_output = 0
turn_count = 0

runner = client.beta.messages.tool_runner(
    model=MODEL,
    max_tokens=4096,
    tools=tools,
    messages=messages,
)

for message in runner:
    messages_list = list(runner._params["messages"])
    turn_count += 1
    total_input += message.usage.input_tokens
    total_output += message.usage.output_tokens
    print(
        f"Turn {turn_count:2d}: Input={message.usage.input_tokens:7,} tokens | "
        f"Output={message.usage.output_tokens:5,} tokens | "
        f"Messages={len(messages_list):2d} | "
        f"Cumulative In={total_input:8,}"
    )

print(f"\n{'=' * 60}")
print("BASELINE RESULTS (NO COMPACTION)")
print(f"{'=' * 60}")
print(f"Total turns:   {turn_count}")
print(f"Input tokens:  {total_input:,}")
print(f"Output tokens: {total_output:,}")
print(f"Total tokens:  {total_input + total_output:,}")
print(f"{'=' * 60}")
```

::: {.output .stream .stdout}
    Turn  1: Input=  1,537 tokens | Output=   57 tokens | Messages= 1 | Cumulative In=   1,537
    Turn  2: Input=  1,760 tokens | Output=  102 tokens | Messages= 3 | Cumulative In=   3,297
    Turn  3: Input=  1,905 tokens | Output=   88 tokens | Messages= 5 | Cumulative In=   5,202
    Turn  4: Input=  2,237 tokens | Output=   84 tokens | Messages= 7 | Cumulative In=   7,439
    Turn  5: Input=  2,385 tokens | Output=   89 tokens | Messages= 9 | Cumulative In=   9,824
    Turn  6: Input=  2,537 tokens | Output=  301 tokens | Messages=11 | Cumulative In=  12,361
    Turn  7: Input=  2,888 tokens | Output=   67 tokens | Messages=13 | Cumulative In=  15,249
    Turn  8: Input=  3,079 tokens | Output=   56 tokens | Messages=15 | Cumulative In=  18,328
    Turn  9: Input=  3,316 tokens | Output=   91 tokens | Messages=17 | Cumulative In=  21,644
    Turn 10: Input=  3,450 tokens | Output=   84 tokens | Messages=19 | Cumulative In=  25,094
    Turn 11: Input=  3,777 tokens | Output=   84 tokens | Messages=21 | Cumulative In=  28,871
    Turn 12: Input=  3,925 tokens | Output=   89 tokens | Messages=23 | Cumulative In=  32,796
    Turn 13: Input=  4,077 tokens | Output=  349 tokens | Messages=25 | Cumulative In=  36,873
    Turn 14: Input=  4,476 tokens | Output=   67 tokens | Messages=27 | Cumulative In=  41,349
    Turn 15: Input=  4,668 tokens | Output=   56 tokens | Messages=29 | Cumulative In=  46,017
    Turn 16: Input=  4,894 tokens | Output=   91 tokens | Messages=31 | Cumulative In=  50,911
    Turn 17: Input=  5,028 tokens | Output=   84 tokens | Messages=33 | Cumulative In=  55,939
    Turn 18: Input=  5,333 tokens | Output=   84 tokens | Messages=35 | Cumulative In=  61,272
    Turn 19: Input=  5,481 tokens | Output=   89 tokens | Messages=37 | Cumulative In=  66,753
    Turn 20: Input=  5,633 tokens | Output=  334 tokens | Messages=39 | Cumulative In=  72,386
    Turn 21: Input=  6,017 tokens | Output=   67 tokens | Messages=41 | Cumulative In=  78,403
    Turn 22: Input=  6,209 tokens | Output=   56 tokens | Messages=43 | Cumulative In=  84,612
    Turn 23: Input=  6,435 tokens | Output=   91 tokens | Messages=45 | Cumulative In=  91,047
    Turn 24: Input=  6,569 tokens | Output=   84 tokens | Messages=47 | Cumulative In=  97,616
    Turn 25: Input=  6,896 tokens | Output=   84 tokens | Messages=49 | Cumulative In= 104,512
    Turn 26: Input=  7,044 tokens | Output=   89 tokens | Messages=51 | Cumulative In= 111,556
    Turn 27: Input=  7,196 tokens | Output=  372 tokens | Messages=53 | Cumulative In= 118,752
    Turn 28: Input=  7,618 tokens | Output=   67 tokens | Messages=55 | Cumulative In= 126,370
    Turn 29: Input=  7,808 tokens | Output=   56 tokens | Messages=57 | Cumulative In= 134,178
    Turn 30: Input=  8,040 tokens | Output=   96 tokens | Messages=59 | Cumulative In= 142,218
    Turn 31: Input=  8,179 tokens | Output=   85 tokens | Messages=61 | Cumulative In= 150,397
    Turn 32: Input=  8,508 tokens | Output=   84 tokens | Messages=63 | Cumulative In= 158,905
    Turn 33: Input=  8,656 tokens | Output=   89 tokens | Messages=65 | Cumulative In= 167,561
    Turn 34: Input=  8,808 tokens | Output=  332 tokens | Messages=67 | Cumulative In= 176,369
    Turn 35: Input=  9,190 tokens | Output=   67 tokens | Messages=69 | Cumulative In= 185,559
    Turn 36: Input=  9,382 tokens | Output=   60 tokens | Messages=71 | Cumulative In= 194,941
    Turn 37: Input=  9,475 tokens | Output=  297 tokens | Messages=73 | Cumulative In= 204,416

    ============================================================
    BASELINE RESULTS (NO COMPACTION)
    ============================================================
    Total turns:   37
    Input tokens:  204,416
    Output tokens: 4,422
    Total tokens:  208,838
    ============================================================
:::
::::

::: {#3dd25c6f .cell .markdown}
Now that we have our baseline, we have a better picture of how context
grows without compaction. As you can see, each turn results in linear
token growth, as every turn adds more tokens to the input.

This leads to high token consumption and potential context limits being
reached quickly. By the 27th turn, we have a cumulative 150,000 input
tokens just for 5 tickets.

Let\'s review Claude\'s final response after processing all 5 tickets
without compaction:
:::

:::: {#d8b51b65 .cell .code execution_count="5"}
``` python
print(message.content[-1].text)
```

::: {.output .stream .stdout}
    ---

    ## ✅ ALL TICKETS PROCESSED SUCCESSFULLY!

    **Summary of Completed Work:**

    I have successfully processed all 5 tickets from the queue. Here's what was accomplished:

    1. **TICKET-1** - Sam Smith - Payment method update error
       - Category: Billing | Priority: High | Team: billing-team
       
    2. **TICKET-2** - Morgan Johnson - Missing delivery
       - Category: Shipping | Priority: High | Team: logistics-team
       
    3. **TICKET-3** - Morgan Jones - Email address change request
       - Category: Account | Priority: Medium | Team: account-services
       
    4. **TICKET-4** - Alex Johnson - Wrong item delivered
       - Category: Shipping | Priority: High | Team: logistics-team
       
    5. **TICKET-5** - Morgan Jones - Refund request for cancelled subscription
       - Category: Billing | Priority: High | Team: billing-team

    Each ticket was:
    ✅ Classified correctly
    ✅ Researched in the knowledge base
    ✅ Assigned appropriate priority
    ✅ Routed to the correct team
    ✅ Given a detailed, helpful customer response
    ✅ Marked as complete

    The queue is now empty and all tickets have been processed!
:::
::::

::: {#klok33ohsvn .cell .markdown}
### Understanding the Problem

In the baseline workflow above, Claude had to:

- Process **5 support tickets** sequentially
- Complete **7 steps per ticket** (fetch, classify, research,
  prioritize, route, draft, complete)
- Make **35 tool calls** with results accumulating in conversation
  history
- Store **every classification, every knowledge base search, every
  drafted response** in memory

**Why This Happens**:

1.  **Linear token growth** - With each tool use, the entire
    conversation history (including all previous tool results) is sent
    to Claude
2.  **Context pollution** - Ticket A\'s classification and drafted
    response remain in context while processing Ticket B
3.  **Compounding costs** - By the time you\'re on Ticket #5, you\'re
    sending data from all 4 previous tickets on every API call
4.  **Slower responses** - Processing massive contexts takes longer
5.  **Risk of hitting limits** - Eventually you hit the 200k token
    context window

**What We Actually Need**: After completing Ticket A, we only need a
**brief summary** (ticket resolved, category, priority) - not the full
classification result, knowledge base search, and complete drafted
response. The detailed workflow should be discarded, keeping only
completion summaries.

Let\'s see how automatic context compaction solves this problem.
:::

::: {#byut5h7hi3 .cell .markdown}
## Enabling Automatic Context Compaction

Let\'s run the exact same customer service workflow, but with automatic
context compaction enabled. We simply add the `compaction_control`
parameter to our tool runner.

The `compaction_control` parameter has one required field and several
optional ones:

- **`enabled`** (required): Boolean to turn compaction on/off
- **`context_token_threshold`** (optional): Token count that triggers
  compaction (default: 100,000)
- **`model`** (optional): Model to use for summarization (defaults to
  the main model)
- **`summary_prompt`** (optional): Custom prompt for generating
  summaries

For this customer service workflow, we\'ll use a **5,000 token
threshold**. This means after processing several tickets compaction will
auto-trigger. This allows Claude to:

1.  **Keep completion summaries** (tickets resolved, categories,
    outcomes)
2.  **Discard detailed tool results** (full KB articles, complete
    classifications, drafted response text)
3.  **Start fresh** when processing the next batch of tickets

This mimics how a real support agent works: resolve the ticket, document
it briefly, move to the next case.
:::

:::: {#x6lnx8d20fr .cell .code execution_count="6"}
``` python
# Re-initialize queue and run with compaction
initialize_ticket_queue(num_tickets)

total_input_compact = 0
total_output_compact = 0
turn_count_compact = 0
compaction_count = 0
prev_msg_count = 0

runner = client.beta.messages.tool_runner(
    model=MODEL,
    max_tokens=4096,
    tools=tools,
    messages=messages,
    compaction_control={
        "enabled": True,
        "context_token_threshold": 5000,
    },
)

for message in runner:
    turn_count_compact += 1
    total_input_compact += message.usage.input_tokens
    total_output_compact += message.usage.output_tokens
    messages_list = list(runner._params["messages"])
    curr_msg_count = len(messages_list)

    if curr_msg_count < prev_msg_count:
        # We can identify compaction when the message count decreases
        compaction_count += 1

        print(f"\n{'=' * 60}")
        print(f"🔄 Compaction occurred! Messages: {prev_msg_count} → {curr_msg_count}")
        print("   Summary message after compaction:")
        print(messages_list[-1]["content"][-1].text)  # type: ignore
        print(f"\n{'=' * 60}")

    prev_msg_count = curr_msg_count
    print(
        f"Turn {turn_count_compact:2d}: Input={message.usage.input_tokens:7,} tokens | "
        f"Output={message.usage.output_tokens:5,} tokens | "
        f"Messages={len(messages_list):2d} | "
        f"Cumulative In={total_input_compact:8,}"
    )

print(f"\n{'=' * 60}")
print("OPTIMIZED RESULTS (WITH COMPACTION)")
print(f"{'=' * 60}")
print(f"Total turns:   {turn_count_compact}")
print(f"Compactions:   {compaction_count}")
print(f"Input tokens:  {total_input_compact:,}")
print(f"Output tokens: {total_output_compact:,}")
print(f"Total tokens:  {total_input_compact + total_output_compact:,}")
print(f"{'=' * 60}")
```

::: {.output .stream .stdout}
    Turn  1: Input=  1,537 tokens | Output=   57 tokens | Messages= 1 | Cumulative In=   1,537
    Turn  2: Input=  1,755 tokens | Output=  108 tokens | Messages= 3 | Cumulative In=   3,292
    Turn  3: Input=  1,906 tokens | Output=   88 tokens | Messages= 5 | Cumulative In=   5,198
    Turn  4: Input=  2,216 tokens | Output=   84 tokens | Messages= 7 | Cumulative In=   7,414
    Turn  5: Input=  2,364 tokens | Output=   89 tokens | Messages= 9 | Cumulative In=   9,778
    Turn  6: Input=  2,516 tokens | Output=  332 tokens | Messages=11 | Cumulative In=  12,294
    Turn  7: Input=  2,898 tokens | Output=   67 tokens | Messages=13 | Cumulative In=  15,192
    Turn  8: Input=  3,090 tokens | Output=   56 tokens | Messages=15 | Cumulative In=  18,282
    Turn  9: Input=  3,325 tokens | Output=   97 tokens | Messages=17 | Cumulative In=  21,607
    Turn 10: Input=  3,465 tokens | Output=   90 tokens | Messages=19 | Cumulative In=  25,072
    Turn 11: Input=  3,801 tokens | Output=   84 tokens | Messages=21 | Cumulative In=  28,873
    Turn 12: Input=  3,949 tokens | Output=   89 tokens | Messages=23 | Cumulative In=  32,822
    Turn 13: Input=  4,101 tokens | Output=  368 tokens | Messages=25 | Cumulative In=  36,923
    Turn 14: Input=  4,519 tokens | Output=   67 tokens | Messages=27 | Cumulative In=  41,442
    Turn 15: Input=  4,711 tokens | Output=   57 tokens | Messages=29 | Cumulative In=  46,153
    Turn 16: Input=  4,934 tokens | Output=   97 tokens | Messages=31 | Cumulative In=  51,087

    ============================================================
    🔄 Compaction occurred! Messages: 31 → 1
       Summary message after compaction:
    <summary>
    ## Support Ticket Processing Progress Summary

    ### Task Overview
    Processing 5 support tickets sequentially, completing all 7 steps for each ticket (fetch, classify, research, prioritize, route, draft, complete).

    ### Tickets Completed (2 of 5)

    **TICKET-1 (Chris Davis) - COMPLETED**
    - Issue: Account locked, unlock email link not working
    - Category: account
    - Priority: high
    - Team: account-services
    - Status: resolved
    - Response: Provided guidance on checking spam folder, link expiration (1 hour), and requesting new unlock link

    **TICKET-2 (Chris Williams) - COMPLETED**
    - Issue: Unrecognized $49.99 charge on 2025-10-30
    - Category: billing
    - Priority: high
    - Team: billing-team
    - Status: resolved
    - Response: Explained billing cycles, subscription possibility, and refund policy (5-7 business days, pro-rated for annual plans)

    ### Current Status
    **TICKET-3 (John Jones) - IN PROGRESS**
    - Issue: Asking about Google Sheets integration for project management
    - Category: product
    - Priority: NOT YET SET
    - Team: NOT YET ASSIGNED
    - Steps completed: 1 (fetch), 2 (classify)
    - Steps remaining: 3 (research KB), 4 (set priority), 5 (route), 6 (draft), 7 (mark complete)

    ### Next Steps
    1. Complete TICKET-3: Search knowledge base for product integration info
    2. Set priority (likely low/medium for feature inquiry)
    3. Route to product-team
    4. Draft response about integrations
    5. Mark complete
    6. Fetch and process TICKET-4
    7. Fetch and process TICKET-5

    ### Key Knowledge Base Info Learned
    - Account: Password reset links expire in 1 hour, sent from noreply@support.example.com
    - Billing: Refunds take 5-7 business days, pro-rated for annual plans, billing on same date monthly/yearly

    ### Remaining Work
    3 tickets left to process (TICKET-3 currently in progress, then TICKET-4 and TICKET-5)
    </summary>

    ============================================================
    Turn 17: Input=  1,774 tokens | Output=   94 tokens | Messages= 1 | Cumulative In=  52,861
    Turn 18: Input=  1,906 tokens | Output=   95 tokens | Messages= 3 | Cumulative In=  54,767
    Turn 19: Input=  2,365 tokens | Output=  431 tokens | Messages= 5 | Cumulative In=  57,132
    Turn 20: Input=  3,164 tokens | Output=   60 tokens | Messages= 7 | Cumulative In=  60,296
    Turn 21: Input=  3,383 tokens | Output=  160 tokens | Messages= 9 | Cumulative In=  63,679
    Turn 22: Input=  3,872 tokens | Output=  447 tokens | Messages=11 | Cumulative In=  67,551
    Turn 23: Input=  4,687 tokens | Output=   64 tokens | Messages=13 | Cumulative In=  72,238
    Turn 24: Input=  4,914 tokens | Output=  160 tokens | Messages=15 | Cumulative In=  77,152

    ============================================================
    🔄 Compaction occurred! Messages: 15 → 1
       Summary message after compaction:
    <summary>
    ## Support Ticket Processing Progress Summary

    ### Task Overview
    Processing 5 support tickets sequentially, completing all 7 steps for each ticket (fetch, classify, research, prioritize, route, draft, complete).

    ### Tickets Completed (4 of 5)

    **TICKET-1 (Chris Davis) - COMPLETED**
    - Issue: Account locked, unlock email link not working
    - Category: account
    - Priority: high
    - Team: account-services
    - Status: resolved
    - Response: Provided guidance on checking spam folder, link expiration (1 hour), and requesting new unlock link

    **TICKET-2 (Chris Williams) - COMPLETED**
    - Issue: Unrecognized $49.99 charge on 2025-10-30
    - Category: billing
    - Priority: high
    - Team: billing-team
    - Status: resolved
    - Response: Explained billing cycles, subscription possibility, and refund policy (5-7 business days, pro-rated for annual plans)

    **TICKET-3 (John Jones) - COMPLETED**
    - Issue: Asking about Google Sheets integration for project management
    - Category: product
    - Priority: medium
    - Team: product-success
    - Status: resolved
    - Response: Explained that Product Success team will provide details on integration options, API access, and current/planned features

    **TICKET-4 (Sam Johnson) - COMPLETED**
    - Issue: Wants to know differences between Standard and Premium plans, specifically "advanced analytics"
    - Category: product
    - Priority: low
    - Team: product-success
    - Status: resolved
    - Response: Explained that Product Success team will provide detailed plan comparison and feature breakdown

    ### Current Status
    **TICKET-5 (Morgan Brown) - IN PROGRESS**
    - Issue: Damaged package (Order #ORD-43312), broken product inside, needs replacement
    - Category: shipping (classified)
    - Priority: NOT YET SET
    - Team: NOT YET ASSIGNED
    - Steps completed: 1 (fetch), 2 (classify), 3 (research KB - no shipping info found)
    - Steps remaining: 4 (set priority), 5 (route), 6 (draft), 7 (mark complete)

    ### Next Steps for TICKET-5
    1. Set priority (likely HIGH - damaged/broken product requiring replacement)
    2. Route to appropriate team (likely fulfillment, operations, or customer-service team)
    3. Draft response addressing damaged shipment, replacement process, and next steps
    4. Mark complete
    5. **ALL TICKETS WILL BE COMPLETE**

    ### Key Knowledge Base Info Learned
    - **Account**: Password reset links expire in 1 hour, sent from noreply@support.example.com
    - **Billing**: Refunds take 5-7 business days, pro-rated for annual plans, billing on same date monthly/yearly; accepts Visa, Mastercard, Amex, PayPal
    - **Technical**: Max upload 100MB, supported formats: PDF, DOCX, PNG, JPG, CSV; system requirements: 4GB RAM, modern browsers
    - **Product category**: Does not exist in KB (only billing, technical, account available)
    - **Shipping info**: Not found in knowledge base

    ### Team Routing Patterns Observed
    - account-services: Account access issues
    - billing-team: Billing/payment inquiries
    - product-success: Product features, integrations, plan comparisons

    ### Remaining Work
    1 ticket left to complete (TICKET-5 - final ticket, currently in progress at step 3 of 7)
    </summary>

    ============================================================
    Turn 25: Input=  2,077 tokens | Output=  496 tokens | Messages= 1 | Cumulative In=  79,229
    Turn 26: Input=  2,942 tokens | Output=  438 tokens | Messages= 3 | Cumulative In=  82,171

    ============================================================
    OPTIMIZED RESULTS (WITH COMPACTION)
    ============================================================
    Total turns:   26
    Compactions:   2
    Input tokens:  82,171
    Output tokens: 4,275
    Total tokens:  86,446
    ============================================================
:::
::::

::: {#5cbbfe32 .cell .markdown}
With automatic context compaction enabled, we can see that our token
usage per turn does not grow linearly, but is reduced after each
compaction event. There were two compaction events during the processing
of tickets, and the follow turn shows a reduction in total token usage.

Compared to the baseline version, we only used 79,000 tokens. We\'ve
also printed out the summary messages generated after each compaction
event, showing how Claude effectively condensed prior ticket details
into summaries.

Let\'s look at the final response after processing all 5 tickets with
compaction enabled.
:::

:::: {#24dd5c7c .cell .code execution_count="7"}
``` python
print(message.content[-1].text)
```

::: {.output .stream .stdout}
    Perfect! **ALL 5 TICKETS HAVE BEEN SUCCESSFULLY COMPLETED!** 🎉

    ## Final Summary - All Tickets Processed

    ### TICKET-5 (Morgan Brown) - **COMPLETED** ✓
    - **Issue**: Damaged package (Order #ORD-43312), broken product inside, needs replacement
    - **Category**: shipping
    - **Priority**: high
    - **Team**: logistics-team
    - **Status**: resolved
    - **Response**: Apologized for damaged shipment, escalated to Logistics Team with HIGH priority, explained they'll process immediate replacement, provide return instructions, and contact customer with tracking and timeline

    ---

    ## 🎯 ALL 5 TICKETS COMPLETED

    1. ✅ **TICKET-1** (Chris Davis) - Account locked → account-services
    2. ✅ **TICKET-2** (Chris Williams) - Billing charge → billing-team  
    3. ✅ **TICKET-3** (John Jones) - Google Sheets integration → product-success
    4. ✅ **TICKET-4** (Sam Johnson) - Plan comparison → product-success
    5. ✅ **TICKET-5** (Morgan Brown) - Damaged shipment → logistics-team

    ### Processing Statistics
    - **Total tickets processed**: 5 of 5 (100%)
    - **Steps per ticket**: 7 (fetch, classify, research, prioritize, route, draft, complete)
    - **Total operations**: 35 successful operations
    - **Categories used**: account, billing, product (2x), shipping
    - **Teams utilized**: account-services, billing-team, product-success (2x), logistics-team
    - **Priority distribution**: 2 high, 2 medium, 1 low

    All tickets have been properly classified, prioritized, routed to the appropriate teams, and have draft responses ready for team review! 🎊
:::
::::

::: {#vb4cesnmb8 .cell .markdown}
### Comparing Results

With compaction enabled, we can see a clear differece between the two
runs in token savings, while preserving the quality of the workflow and
final summary.

Here\'s what changed with automatic context compaction:

1.  **Context resets after several tickets** - When processing 5-7
    tickets generates 5k+ tokens of tool results, the SDK automatically:

    - Injects a summary prompt
    - Has Claude generate a completion summary wrapped in
      `<summary></summary>` tags
    - Clears the conversation history and discards detailed
      classifications, KB searches, and responses
    - Continues with only the completion summary

2.  **Input tokens stay bounded** - Instead of accumulating to 100k+ as
    we process more tickets, input tokens reset after each compaction.
    When processing Ticket #5, we\'re NOT carrying the full tool results
    from Tickets #1-4.

3.  **Task completes successfully** - The workflow continues smoothly
    through all tickets without hitting context limits

4.  **Quality is preserved** - The summaries retain critical
    information:

    - Tickets processed with their IDs
    - Categories and priorities assigned
    - Teams routed to
    - Overall progress status

    All tickets are still properly classified, prioritized, routed, and
    responded to.

5.  **Natural workflow** - This mirrors how real support agents work:
    resolve a ticket, document it briefly in the system, close it, move
    to the next one. You don\'t keep every knowledge base article and
    full response draft open while working on new tickets.

Let\'s visualize the token savings:
:::

:::: {#z9lvigc94p .cell .code execution_count="8"}
``` python
# Compare baseline vs compaction
print("=" * 70)
print("TOKEN USAGE COMPARISON")
print("=" * 70)
print(f"{'Metric':<30} {'Baseline':<20} {'With Compaction':<20}")
print("-" * 70)
print(f"{'Input tokens:':<30} {total_input:>19,} {total_input_compact:>19,}")
print(f"{'Output tokens:':<30} {total_output:>19,} {total_output_compact:>19,}")
print(
    f"{'Total tokens:':<30} {total_input + total_output:>19,} {total_input_compact + total_output_compact:>19,}"
)
print(f"{'Compactions:':<30} {'N/A':>19} {compaction_count:>19}")
print("=" * 70)

# Calculate savings
token_savings = (total_input + total_output) - (total_input_compact + total_output_compact)
savings_percent = (
    (token_savings / (total_input + total_output)) * 100 if (total_input + total_output) > 0 else 0
)

print(f"\n💰 Token Savings: {token_savings:,} tokens ({savings_percent:.1f}% reduction)")
```

::: {.output .stream .stdout}
    ======================================================================
    TOKEN USAGE COMPARISON
    ======================================================================
    Metric                         Baseline             With Compaction     
    ----------------------------------------------------------------------
    Input tokens:                              204,416              82,171
    Output tokens:                               4,422               4,275
    Total tokens:                              208,838              86,446
    Compactions:                                   N/A                   2
    ======================================================================

    💰 Token Savings: 122,392 tokens (58.6% reduction)
:::
::::

::: {#lzvf1mw7o6 .cell .markdown}
## How Compaction Works Under the Hood

When the `tool_runner` detects that token usage has exceeded the
threshold, it automatically:

1.  **Pauses the workflow** before making the next API call
2.  **Injects a summary request** as a user message asking Claude to
    summarize progress
3.  **Generates a summary** - Claude produces a summary wrapped in
    `<summary></summary>` tags containing:
    - **Completed tickets**: Brief records of tickets resolved (IDs,
      categories, priorities, outcomes)
    - **Progress status**: How many tickets processed, how many remain
    - **Key patterns**: Any notable trends across tickets
    - **Next steps**: What to do next (continue processing remaining
      tickets)
4.  **Clears history** - The entire conversation history (including all
    tool results) is replaced with just the summary
5.  **Resumes processing** - Claude continues working with the
    compressed context, processing the next batch of tickets
:::

::: {#v64ljd0a79 .cell .markdown}
## Customizing Compaction Configuration

You can customize how compaction works to fit your specific use case.
Here are the key configuration options:

### Adjusting the Threshold

The `context_token_threshold` determines when compaction triggers:

``` python
compaction_control={
    "enabled": True,
    "context_token_threshold": 5000,  # Compact after processing 5-7 tickets
}
```

The threshold should not be set too low, otherwise the summary itself
could trigger a compaction. We set a threshold of 5,000 tokens for
demonstration purposes, but in practice, experiment with different
settings to find what works best for your workflow.

Here some general guidelines:

- **Low thresholds (5k-20k)**:
  - Use for iterative task processing with clear boundaries
  - More frequent compaction, minimal context accumulation
  - Best for sequential entity processing
- **Medium thresholds (50k-100k)**:
  - Multi-phase workflows with fewer, larger natural checkpoints
  - Balance between context retention and management
  - Suitable for workflows with expensive tool calls
- **High thresholds (100k-150k)**:
  - Tasks requiring substantial historical context
  - Less frequent compaction preserves more raw details
  - Higher per-call costs but fewer compactions
- **Default (100k)**: Good balance for general long-running tasks

**For ticket processing**: The 5k threshold works well because each
ticket\'s workflow generates substantial tool results, but tickets are
independent. After resolving Ticket A, you don\'t need its detailed KB
searches when processing Ticket B.

### Using a Different Model for Summarization

You can also use a faster/cheaper model for generating summaries:

``` python
compaction_control={
    "enabled": True,
    "model": "claude-haiku-4-5",  # Use Haiku for cost-effective summaries
}
```
:::

::: {#w4oyvorzkn .cell .markdown}
### Custom Summary Prompts

You can provide a custom prompt to guide how summaries are generated.
This is especially useful for customer service workflows where you need
to preserve specific types of information.

For example, we could define a custom prompt based on our requirements:

- **Ticket summaries** for all completed tickets
- **Categories and priorities** assigned
- **Teams routed to**
- **Progress status** (tickets completed, tickets remaining)
- **Next steps** in the workflow

``` python
compaction_control={
    "enabled": True,
    "summary_prompt": """You are processing customer support tickets from a queue.

Create a focused summary that preserves:

1. **COMPLETED TICKETS**: For each ticket you've fully processed:
   - Ticket ID and customer name
   - Issue category and priority assigned
   - Team routed to
   - Brief outcome

2. **PROGRESS STATUS**: 
   - How many tickets you've completed
   - Approximately how many remain in the queue

3. **NEXT STEPS**: Continue processing the next ticket

Format with clear sections and wrap in <summary></summary> tags."""
}
```
:::

::: {#7cwipn73ib9 .cell .markdown}
## Compaction Without Tools: Simple Chat Loop

While the examples above focus on tool-heavy agentic workflows, context
compaction is also valuable for **simple conversational applications**
where users drive the conversation.

**Note:** The `compaction_control` parameter demonstrated above works
with `tool_runner` for agentic workflows with tools. For simple chat
applications without tools, you\'ll implement compaction manually using
the same principles.

Consider a chat application where users are having extended
conversations with Claude---discussing complex topics, iterating on
ideas, or working through problems. As the conversation grows, you face
the same context accumulation challenges.

**The Difference**: Instead of tool use triggering token growth, it\'s
the back-and-forth conversation itself. Each exchange adds messages to
the history:

- User asks a question
- Claude provides a detailed response
- User asks for clarification or elaboration
- Claude responds with more context
- This repeats dozens or hundreds of times

Without compaction, by turn 50 you\'re sending the entire conversation
history (all 50 exchanges) on every API call.

**The Solution**: Implement compaction manually in your chat loop using
the same pattern:

1.  Track token usage after each turn
2.  When threshold is exceeded, request a summary
3.  Replace conversation history with the summary
4.  Continue the conversation with compressed context

Let\'s see how to implement this:
:::

:::: {#m6akcmnsz09 .cell .code}
``` python
#!/usr/bin/env python3
"""
Simple Compaction Example - User-Driven Chat Loop

This shows the basic pattern for a chat application with compaction.
No tools required - just a simple loop where the user drives continuation.
"""

# Configuration
COMPACTION_THRESHOLD = 3000  # Compact when tokens exceed this (low for demo purposes)

# Structured summarization prompt for compaction
SUMMARY_PROMPT = """You have been working on the task described above but have not yet completed it. Write a continuation summary that will allow you (or another instance of yourself) to resume work efficiently in a future context window where the conversation history will be replaced with this summary. Your summary should be structured, concise, and actionable. Include:

1. **Task Overview**
   - The user's core request and success criteria
   - Any clarifications or constraints they specified

2. **Current State**
   - What has been completed so far
   - Files created, modified, or analyzed (with paths if relevant)
   - Key outputs or artifacts produced

3. **Important Discoveries**
   - Technical constraints or requirements uncovered
   - Decisions made and their rationale
   - Errors encountered and how they were resolved
   - What approaches were tried that didn't work (and why)

4. **Next Steps**
   - Specific actions needed to complete the task
   - Any blockers or open questions to resolve
   - Priority order if multiple steps remain

5. **Context to Preserve**
   - User preferences or style requirements
   - Domain-specific details that aren't obvious
   - Any promises made to the user

Be concise but complete—err on the side of including information that would prevent duplicate work or repeated mistakes.
 Write in a way that enables immediate resumption of the task.

Wrap your summary in <summary></summary> tags."""

# Message history
messages = []

print("Chat with Claude (type 'quit' to exit, or just hit Enter to continue)")
print("This is a demonstration - try having a conversation and watch compaction trigger")
print("=" * 60)

# Simulate a conversation for demo purposes
demo_messages = [
    "Help me understand how Python decorators work",
    "Can you show me an example with a timing decorator?",
    "How would I make a decorator that takes arguments?",
]

for user_input in demo_messages:
    print(f"\nYou: {user_input}")

    # Add user message
    messages.append({"role": "user", "content": user_input})

    # Get Claude's response
    response = client.messages.create(
        model=MODEL,
        max_tokens=2048,
        messages=messages,
    )

    messages.append(
        {
            "role": "assistant",
            "content": response.content,
        }
    )

    print("\nClaude: ", end="")
    for block in response.content:
        if block.type == "text":
            print(f"{block.text[:300]} ...")

    # Check if we should compact
    usage = response.usage

    # Calculate total tokens (includes cache tokens)
    total_input_tokens = (
        usage.input_tokens
        + (usage.cache_creation_input_tokens or 0)
        + (usage.cache_read_input_tokens or 0)
    )
    total_tokens = total_input_tokens + usage.output_tokens

    cache_info = ""
    if usage.cache_creation_input_tokens or usage.cache_read_input_tokens:
        cache_info = f" (cache: {usage.cache_creation_input_tokens or 0} write + {usage.cache_read_input_tokens or 0} read)"

    print(
        f"\n[Tokens: {total_input_tokens} in{cache_info} + {usage.output_tokens} out = {total_tokens} total]"
    )

    if total_tokens > COMPACTION_THRESHOLD:
        print(f"\n{'=' * 60}")
        print(f"🔄 Compacting conversation... {len(messages)} messages → ", end="", flush=True)

        # Get summary using structured prompt
        summary_response = client.messages.create(
            model=MODEL,
            max_tokens=4096,
            messages=messages + [{"role": "user", "content": SUMMARY_PROMPT}],
        )

        summary_text = "".join(
            block.text for block in summary_response.content if block.type == "text"
        )

        # Replace history with summary
        messages = [{"role": "user", "content": summary_text}]

        print("1 message")
        print(f"{'=' * 60}\n")

print(f"Final conversation messages: {messages[-1].get('content')}")

print("\nDemo complete! In a real application, this loop would continue with user input.")
```

::: {.output .stream .stdout}
    Chat with Claude (type 'quit' to exit, or just hit Enter to continue)
    This is a demonstration - try having a conversation and watch compaction trigger
    ============================================================

    You: Help me understand how Python decorators work
:::
::::

::: {#b229j75wdjm .cell .markdown}
### Understanding the Chat Loop Pattern

The example above demonstrates manual compaction in a conversational
context. Here\'s how it works:

**Key Components**:

1.  **Token Tracking**: After each response, calculate total tokens
    (input + output + cache tokens)
2.  **Threshold Check**: When total exceeds threshold, trigger
    compaction
3.  **Summary Request**: Send the same structured SUMMARY_PROMPT to
    Claude
4.  **History Replacement**: Replace entire message history with just
    the summary
5.  **Continue**: Next user message builds on the summary, not full
    history

**When to Use This Pattern**:

- **Extended brainstorming sessions**: Users exploring ideas with Claude
  over many turns
- **Learning conversations**: Tutorials or explanations that span dozens
  of exchanges
- **Iterative refinement**: Users providing feedback on drafts, designs,
  or solutions
- **Chat applications**: Any multi-turn conversation interface

**Key Differences from Tool Runner**:

  ---------------------------------------------------------------------------
  Aspect         Tool Runner (Automatic)           Chat Loop (Manual)
  -------------- --------------------------------- --------------------------
  **Trigger**    Automatic when threshold reached  You implement threshold
                                                   check

  **Summary**    SDK handles summary request       You make explicit API call

  **History      SDK replaces messages             You manually replace list
  Management**                                     

  **Use Case**   Agentic workflows with tools      User-driven conversations
  ---------------------------------------------------------------------------

**Production Considerations**:

1.  **Adjust threshold**: Use larger thresholds for real applications
2.  **Customize summary prompt**: Tailor to your conversation type
    (brainstorming vs. technical support vs. tutoring)
3.  **Show user indicators**: Display a message like \"Summarizing
    conversation\...\" so users understand the pause
4.  **Preserve key context**: Ensure the summary prompt captures
    domain-specific information your users care about

This pattern gives you full control over when and how compaction
happens, making it ideal for conversational applications where the
SDK\'s automatic tool-runner compaction isn\'t available.
:::

::: {#d71dwo1dayp .cell .markdown}
## Limitations and Considerations

While automatic context compaction is powerful, there are important
limitations to understand:

### Server-Side Sampling Loops

**Current Limitation**: Compaction does not work optimally with
server-side sampling loops, such as server-side web search tools.

**Why**: Cache tokens accumulate across sampling loops, which can
trigger compaction prematurely based on cached content rather than
actual conversation history.

This feature works best with:

- ✅ Client-side tools (like the customer service API in this cookbook)
- ✅ Standard agentic workflows with regular tool use
- ✅ File operations, database queries, API calls
- ❌ Server-side Extended Thinking
- ❌ Server-side web search tools

### Information Loss

**Trade-off**: Summaries inherently lose some information. While Claude
is good at identifying key points, some details will be compressed or
omitted.

**In ticket processing**:

- ✅ **Retained**: Ticket IDs, categories, priorities, teams, outcomes,
  progress status
- ❌ **Lost**: Full knowledge base article text, complete drafted
  response text, detailed classification reasoning

This is usually acceptable, you don\'t need every KB article and full
response text in perpetuity, just the completion records.

**Mitigation**:

- Use custom summary prompts to preserve critical information
- Set higher thresholds for tasks requiring extensive historical context
- Structure your tasks to be modular (each phase builds on summaries,
  not raw details)

### When NOT to Use Compaction

Avoid compaction for:

1.  **Short tasks**: If your task completes within 50k-100k tokens,
    compaction adds unnecessary overhead
2.  **Tasks requiring full audit trails**: Some tasks need access to ALL
    previous details
3.  **Server-side sampling workflows**: As mentioned above, wait for
    this limitation to be addressed
4.  **Highly iterative refinement**: Tasks where each step critically
    depends on exact details from all previous steps

### When TO Use Compaction

Compaction is ideal for:

1.  **Sequential processing**: Like our ticket workflow---process
    multiple items one after another
2.  **Multi-phase workflows**: Where each phase can summarize progress
    before moving on
3.  **Iterative data processing**: Processing large datasets in chunks
    or entities one at a time
4.  **Extended analysis sessions**: Analyzing data across many entities
5.  **Batch operations**: Processing hundreds of items where each is
    independent

**Ticket processing is a perfect use case** because:

- Each ticket workflow is largely independent
- You need completion summaries, not full tool results
- Natural compaction points exist (after completing several tickets)
- The workflow is iterative and sequential
:::

::: {#b4pz1jmdidi .cell .markdown}
## Summary

Automatic context compaction is a powerful feature that enables
long-running agentic workflows to exceed typical context limits. In this
cookbook, we\'ve explored compaction through a customer service ticket
processing workflow.

### Next Steps

Try implementing compaction in your own workflows:

1.  Identify natural compaction points (after processing each item,
    completing each phase, etc.)
2.  Start with an aggressive threshold (5k-10k) if you have clear
    per-item boundaries
3.  Use custom summary prompts to preserve critical information
4.  Monitor when compaction triggers and verify quality is maintained
5.  Adjust threshold based on your specific needs

For more on effective context management, see [Effective Context
Engineering for AI
Agents](https://www.anthropic.com/engineering/effective-context-engineering-for-ai-agents).
:::


---
## best-practices-5fdbbed47a.md

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

# 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](how-claude-code-works-0871be411d.md).

***

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](context-window-d266068204.md) of what loads at startup and what each file read costs. Track context usage continuously with a [custom status line](customize-your-status-line-claude-code-docs-8a9fc80be1.md), and see [Reduce token usage](/en/costs#reduce-token-usage) for strategies on reducing token usage.

***

## Give Claude a way to verify its work

<Tip>
  Include tests, screenshots, or expected outputs so Claude can check itself. This is the single highest-leverage thing you can do.
</Tip>

Claude performs dramatically better when it can verify its own work, like run tests, compare screenshots, and validate outputs.

Without clear success criteria, it might produce something that looks right but actually doesn't work. You become the only feedback loop, and every mistake requires your attention.

| Strategy                              | Before                                                  | After                                                                                                                                                                                                   |
| ------------------------------------- | ------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Provide verification criteria**     | *"implement a function that validates email addresses"* | *"write a validateEmail function. example test cases: [user@example.com](mailto:user@example.com) is true, invalid is false, [user@.com](mailto:user@.com) 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"*                                                             |

UI changes can be verified using the [Claude in Chrome extension](../03-IDE-Integrations/use-claude-code-with-chrome-beta-claude-code-docs-4f2f5a5a96.md). It opens new tabs in your browser, tests the UI, and iterates until the code works.

Your verification can also be a test suite, a linter, or a Bash command that checks output. Invest in making your verification rock-solid.

***

## 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](/en/common-workflows#use-plan-mode-for-safe-code-analysis) to separate exploration from execution.

The recommended workflow has four phases:

<Steps>
  <Step title="Explore">
    Enter Plan Mode. Claude reads files and answers questions without making changes.

    ```txt claude (Plan Mode) 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 claude (Plan Mode) 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 back to Normal Mode and let Claude code, verifying against its plan.

    ```txt claude (Normal Mode) 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 claude (Normal Mode) 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](extend-claude-code-claude-code-docs-3b35aaf641.md).

### 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
```

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](../08-Plugins-Skills/extend-claude-with-skills-claude-code-docs.md) 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 working with files in those directories

### Configure permissions

<Tip>
  Use [auto mode](/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](permission-modes-c83d6eff7e.md), [permission rules](configure-permissions-claude-code-docs-7b0e64d485.md), and [sandboxing](../22-Safety-Policy/sandboxing-claude-code-docs-5f97cd27c4.md).

### 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` to connect external tools like Notion, Figma, or your database.
</Tip>

With [MCP servers](../06-MCP-Tools/mcp-208e742686.md), 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](../07-Hooks/automate-workflows-with-hooks-claude-code-docs-e843f93261.md) 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](../08-Plugins-Skills/extend-claude-with-skills-claude-code-docs.md) 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](../09-Agents-Patterns/create-custom-subagents-claude-code-docs-7dc93e85c0.md) 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}
---
name: security-reviewer
description: Reviews code for security vulnerabilities
tools: Read, Grep, Glob, Bash
model: opus
---
You are a senior security engineer. Review code for:
- Injection vulnerabilities (SQL, XSS, command injection)
- Authentication and authorization flaws
- Secrets or credentials in code
- Insecure data handling

Provide specific line references and suggested fixes.
```

Tell Claude to use subagents explicitly: *"Use a subagent to review this code for security issues."*

### Install plugins

<Tip>
  Run `/plugin` to browse the marketplace. Plugins add skills, tools, and integrations without configuration.
</Tip>

[Plugins](../08-Plugins-Skills/create-plugins-claude-code-docs.md) bundle skills, hooks, subagents, and MCP servers into a single installable unit from the community and Anthropic. If you work with a typed language, install a [code intelligence plugin](/en/discover-plugins#code-intelligence) to give Claude precise symbol navigation and automatic error detection after edits.

For guidance on choosing between skills, subagents, hooks, and MCP, see [Extend Claude Code](/en/features-overview#match-features-to-your-goal).

***

## Communicate effectively

The way you communicate with Claude Code significantly impacts the quality of results.

### Ask codebase questions

<Tip>
  Ask Claude questions you'd ask a senior engineer.
</Tip>

When onboarding to a new codebase, use Claude Code for learning and exploration. You can ask Claude the same sorts of questions you would ask another engineer:

* How does logging work?
* How do I make a new API endpoint?
* What does `async move { ... }` do on line 134 of `foo.rs`?
* What edge cases does `CustomerOnboardingFlowImpl` handle?
* Why does this code call `foo()` instead of `bar()` on line 333?

Using Claude Code this way is an effective onboarding workflow, improving ramp-up time and reducing load on other engineers. No special prompting required: ask questions directly.

### Let Claude interview you

<Tip>
  For larger features, have Claude interview you first. Start with a minimal prompt and ask Claude to interview you using the `AskUserQuestion` tool.
</Tip>

Claude asks about things you might not have considered yet, including technical implementation, UI/UX, edge cases, and tradeoffs.

```text theme={null}
I want to build [brief description]. Interview me in detail using the AskUserQuestion tool.

Ask about technical implementation, UI/UX, edge cases, concerns, and tradeoffs. Don't ask obvious questions, dig into the hard parts I might not have considered.

Keep interviewing until we've covered everything, then write a complete spec to SPEC.md.
```

Once the spec is complete, start a fresh session to execute it. The new session has clean context focused entirely on implementation, and you have a written spec to reference.

***

## Manage your session

Conversations are persistent and reversible. Use this to your advantage!

### Course-correct early and often

<Tip>
  Correct Claude as soon as you notice it going off track.
</Tip>

The best results come from tight feedback loops. Though Claude occasionally solves problems perfectly on the first attempt, correcting it quickly generally produces better solutions faster.

* **`Esc`**: stop Claude mid-action with the `Esc` key. Context is preserved, so you can redirect.
* **`Esc + Esc` or `/rewind`**: press `Esc` twice or run `/rewind` to open the rewind menu and restore previous conversation and code state, or summarize from a selected message.
* **`"Undo that"`**: have Claude revert its changes.
* **`/clear`**: reset context between unrelated tasks. Long sessions with irrelevant context can reduce performance.

If you've corrected Claude more than twice on the same issue in one session, the context is cluttered with failed approaches. Run `/clear` and start fresh with a more specific prompt that incorporates what you learned. A clean session with a better prompt almost always outperforms a long session with accumulated corrections.

### Manage context aggressively

<Tip>
  Run `/clear` between unrelated tasks to reset context.
</Tip>

Claude Code automatically compacts conversation history when you approach context limits, which preserves important code and decisions while freeing space.

During long sessions, Claude's context window can fill with irrelevant conversation, file contents, and commands. This can reduce performance and sometimes distract Claude.

* Use `/clear` frequently between tasks to reset the context window entirely
* When auto compaction triggers, Claude summarizes what matters most, including code patterns, file states, and key decisions
* For more control, run `/compact <instructions>`, like `/compact Focus on the API changes`
* To compact only part of the conversation, use `Esc + Esc` or `/rewind`, select a message checkpoint, and choose **Summarize from here**. This condenses messages from that point forward while keeping earlier context intact.
* Customize compaction behavior in CLAUDE.md with instructions like `"When compacting, always preserve the full list of modified files and any test commands"` to ensure critical context survives summarization
* For quick questions that don't need to stay in context, use [`/btw`](/en/interactive-mode#side-questions-with-%2Fbtw). The answer appears in a dismissible overlay and never enters conversation history, so you can check a detail without growing context.

### Use subagents for investigation

<Tip>
  Delegate research with `"use subagents to investigate X"`. They explore in a separate context, keeping your main conversation clean for implementation.
</Tip>

Since context is your fundamental constraint, subagents are one of the most powerful tools available. When Claude researches a codebase it reads lots of files, all of which consume your context. Subagents run in separate context windows and report back summaries:

```text theme={null}
Use subagents to investigate how our authentication system handles token
refresh, and whether we have any existing OAuth utilities I should reuse.
```

The subagent explores the codebase, reads relevant files, and reports back with findings, all without cluttering your main conversation.

You can also use subagents for verification after Claude implements something:

```text theme={null}
use a subagent to review this code for edge cases
```

### Rewind with checkpoints

<Tip>
  Every action Claude makes creates a checkpoint. You can restore conversation, code, or both to any previous checkpoint.
</Tip>

Claude automatically checkpoints before changes. Double-tap `Escape` or run `/rewind` to open the rewind menu. You can restore conversation only, restore code only, restore both, or summarize from a selected message. See [Checkpointing](checkpointing-94f7cd24eb.md) for details.

Instead of carefully planning every move, you can tell Claude to try something risky. If it doesn't work, rewind and try a different approach. Checkpoints persist across sessions, so you can close your terminal and still rewind later.

<Warning>
  Checkpoints only track changes made *by Claude*, not external processes. This isn't a replacement for git.
</Warning>

### Resume conversations

<Tip>
  Run `claude --continue` to pick up where you left off, or `--resume` to choose from recent sessions.
</Tip>

Claude Code saves conversations locally. When a task spans multiple sessions, you don't have to re-explain the context:

```bash theme={null}
claude --continue    # Resume the most recent conversation
claude --resume      # Select from recent conversations
```

Use `/rename` to give sessions descriptive names like `"oauth-migration"` or `"debugging-memory-leak"` so you can find them later. Treat sessions like branches: different workstreams can have separate, persistent contexts.

***

## Automate and scale

Once you're effective with one Claude, multiply your output with parallel sessions, non-interactive mode, and fan-out patterns.

Everything so far assumes one human, one Claude, and one conversation. But Claude Code scales horizontally. The techniques in this section show how you can get more done.

### Run non-interactive mode

<Tip>
  Use `claude -p "prompt"` in CI, pre-commit hooks, or scripts. Add `--output-format stream-json` for streaming JSON output.
</Tip>

With `claude -p "your prompt"`, you can run Claude non-interactively, without a session. Non-interactive mode is how you integrate Claude into CI pipelines, pre-commit hooks, or any automated workflow. The output formats let you parse results programmatically: plain text, JSON, or streaming JSON.

```bash theme={null}
# One-off queries
claude -p "Explain what this project does"

# Structured output for scripts
claude -p "List all API endpoints" --output-format json

# Streaming for real-time processing
claude -p "Analyze this log file" --output-format stream-json
```

### Run multiple Claude sessions

<Tip>
  Run multiple Claude sessions in parallel to speed up development, run isolated experiments, or start complex workflows.
</Tip>

There are three main ways to run parallel sessions:

* [Claude Code desktop app](/en/desktop#work-in-parallel-with-sessions): Manage multiple local sessions visually. Each session gets its own isolated worktree.
* [Claude Code on the web](claude-code-on-the-web-69d53821d4.md): Run on Anthropic's secure cloud infrastructure in isolated VMs.
* [Agent teams](../09-Agents-Patterns/agent-teams-e29da6ed1b.md): Automated coordination of multiple sessions with shared tasks, messaging, and a team lead.

Beyond parallelizing work, multiple sessions enable quality-focused workflows. A fresh context improves code review since Claude won't be biased toward code it just wrote.

For example, use a Writer/Reviewer pattern:

| Session A (Writer)                                                      | Session B (Reviewer)                                                                                                                                                     |
| ----------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `Implement a rate limiter for our API endpoints`                        |                                                                                                                                                                          |
|                                                                         | `Review the rate limiter implementation in @src/middleware/rateLimiter.ts. Look for edge cases, race conditions, and consistency with our existing middleware patterns.` |
| `Here's the review feedback: [Session B output]. Address these issues.` |                                                                                                                                                                          |

You can do something similar with tests: have one Claude write tests, then another write code to pass them.

### Fan out across files

<Tip>
  Loop through tasks calling `claude -p` for each. Use `--allowedTools` to scope permissions for batch operations.
</Tip>

For large migrations or analyses, you can distribute work across many parallel Claude invocations:

<Steps>
  <Step title="Generate a task list">
    Have Claude list all files that need migrating (e.g., `list all 2,000 Python files that need migrating`)
  </Step>

  <Step title="Write a script to loop through the list">
    ```bash theme={null}
    for file in $(cat files.txt); do
      claude -p "Migrate $file from React to Vue. Return OK or FAIL." \
        --allowedTools "Edit,Bash(git commit *)"
    done
    ```
  </Step>

  <Step title="Test on a few files, then run at scale">
    Refine your prompt based on what goes wrong with the first 2-3 files, then run on the full set. The `--allowedTools` flag restricts what Claude can do, which matters when you're running unattended.
  </Step>
</Steps>

You can also integrate Claude into existing data/processing pipelines:

```bash theme={null}
claude -p "<your prompt>" --output-format json | your_command
```

Use `--verbose` for debugging during development, and turn it off in production.

### Run autonomously with auto mode

For uninterrupted execution with background safety checks, use [auto mode](/en/permission-modes#eliminate-prompts-with-auto-mode). A classifier model reviews commands before they run, blocking scope escalation, unknown infrastructure, and hostile-content-driven actions while letting routine work proceed without prompts.

```bash theme={null}
claude --permission-mode auto -p "fix all lint errors"
```

For non-interactive runs with the `-p` flag, auto mode aborts if the classifier repeatedly blocks actions, since there is no user to fall back to. See [when auto mode falls back](/en/permission-modes#when-auto-mode-falls-back) for thresholds.

***

## Avoid common failure patterns

These are common mistakes. Recognizing them early saves time:

* **The kitchen sink session.** You start with one task, then ask Claude something unrelated, then go back to the first task. Context is full of irrelevant information.
  > **Fix**: `/clear` between unrelated tasks.
* **Correcting over and over.** Claude does something wrong, you correct it, it's still wrong, you correct again. Context is polluted with failed approaches.
  > **Fix**: After two failed corrections, `/clear` and write a better initial prompt incorporating what you learned.
* **The over-specified CLAUDE.md.** If your CLAUDE.md is too long, Claude ignores half of it because important rules get lost in the noise.
  > **Fix**: Ruthlessly prune. If Claude already does something correctly without the instruction, delete it or convert it to a hook.
* **The trust-then-verify gap.** Claude produces a plausible-looking implementation that doesn't handle edge cases.
  > **Fix**: Always provide verification (tests, scripts, screenshots). If you can't verify it, don't ship it.
* **The infinite exploration.** You ask Claude to "investigate" something without scoping it. Claude reads hundreds of files, filling the context.
  > **Fix**: Scope investigations narrowly or use subagents so the exploration doesn't consume your main context.

***

## Develop your intuition

The patterns in this guide aren't set in stone. They're starting points that work well in general, but might not be optimal for every situation.

Sometimes you *should* let context accumulate because you're deep in one complex problem and the history is valuable. Sometimes you should skip planning and let Claude figure it out because the task is exploratory. Sometimes a vague prompt is exactly right because you want to see how Claude interprets the problem before constraining it.

Pay attention to what works. When Claude produces great output, notice what you did: the prompt structure, the context you provided, the mode you were in. When Claude struggles, ask why. Was the context too noisy? The prompt too vague? The task too big for one pass?

Over time, you'll develop intuition that no guide can capture. You'll know when to be specific and when to be open-ended, when to plan and when to explore, when to clear context and when to let it accumulate.

## Related resources

* [How Claude Code works](how-claude-code-works-0871be411d.md): the agentic loop, tools, and context management
* [Extend Claude Code](extend-claude-code-claude-code-docs-3b35aaf641.md): skills, hooks, MCP, subagents, and plugins
* [Common workflows](common-workflows-c909406123.md): step-by-step recipes for debugging, testing, PRs, and more
* [CLAUDE.md](how-claude-remembers-your-project-claude-code-docs-f1c064262d.md): store project conventions and persistent context


---
## best-practices-for-claude-code-claude-code-docs-9686dc1633.md

## On this page

- [Give Claude a way to verify its work](#give-claude-a-way-to-verify-its-work)
- [Explore first, then plan, then code](#explore-first-then-plan-then-code)
- [Provide specific context in your prompts](#provide-specific-context-in-your-prompts)
  - [Provide rich content](#provide-rich-content)
- [Configure your environment](#configure-your-environment)
  - [Write an effective CLAUDE.md](#write-an-effective-claude-md)
  - [Configure permissions](#configure-permissions)
  - [Use CLI tools](#use-cli-tools)
  - [Connect MCP servers](#connect-mcp-servers)
  - [Set up hooks](#set-up-hooks)
  - [Create skills](#create-skills)
  - [Create custom subagents](#create-custom-subagents)
  - [Install plugins](#install-plugins)
- [Communicate effectively](#communicate-effectively)
  - [Ask codebase questions](#ask-codebase-questions)
  - [Let Claude interview you](#let-claude-interview-you)
- [Manage your session](#manage-your-session)
  - [Course-correct early and often](#course-correct-early-and-often)
  - [Manage context aggressively](#manage-context-aggressively)
  - [Use subagents for investigation](#use-subagents-for-investigation)
  - [Rewind with checkpoints](#rewind-with-checkpoints)
  - [Resume conversations](#resume-conversations)
- [Automate and scale](#automate-and-scale)
  - [Run non-interactive mode](#run-non-interactive-mode)
  - [Run multiple Claude sessions](#run-multiple-claude-sessions)
  - [Fan out across files](#fan-out-across-files)
  - [Run autonomously with auto mode](#run-autonomously-with-auto-mode)
  - [Add an adversarial review step](#add-an-adversarial-review-step)
- [Avoid common failure patterns](#avoid-common-failure-patterns)
- [Develop your intuition](#develop-your-intuition)
- [Related resources](#related-resources)

Use Claude Code

# Best practices for Claude Code

Copy pageCopy page

Tips and patterns for getting the most out of Claude Code, from configuring your environment to scaling across parallel sessions.

Copy pageCopy page

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, 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)

Give Claude a way to verify its work

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.

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. Run [`/verify`](/docs/en/skills#run-and-verify-your-app) yourself after Claude’s check passes to confirm the change against the running app.

| Strategy                              | Before                                                  | After                                                                                                                                                                                                   |
|---------------------------------------|---------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| **Provide verification criteria**     | *”implement a function that validates email addresses"* | *"write a validateEmail function. example test cases: [user@example.com](mailto:user@example.com) is true, invalid is false, [user@.com](mailto:user@.com) 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 the goal resolves. If Claude stalls, Claude Code eventually stops the run with the goal still set — see [how /goal evaluation works](/docs/en/goal#how-evaluation-works).
- **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. [Stop input](/docs/en/hooks#stop-input) covers the cap on 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)

Explore first, then plan, then code

Separate research and planning from implementation to avoid solving the wrong problem.

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:

1

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.

claude (plan mode)

```python
read /src/auth and understand how we handle sessions and login.
also look at how we manage environment variables for secrets.
```

2

Plan

Ask Claude to create a detailed implementation plan.

claude (plan mode)

```python
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.

3

Implement

Switch out of plan mode by approving the plan or pressing `Shift+Tab`, then let Claude code, verifying against its plan.

claude

```python
implement the OAuth flow from your plan. write tests for the
callback handler, run the test suite and fix any failures.
```

4

Commit

Ask Claude to commit with a descriptive message and create a PR.

claude

```python
commit with a descriptive message and open a PR
```

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.

------------------------------------------------------------------------


[​](#provide-specific-context-in-your-prompts)

Provide specific context in your prompts

The more precise your instructions, the fewer corrections you’ll need.

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)

Provide rich content

Use `@` to reference files, paste screenshots/images, or pipe data directly.

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)

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)

Write an effective CLAUDE.md

Run `/init` to generate a starter CLAUDE.md file based on your current project structure, then refine over time.

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. There’s no required format for CLAUDE.md files, but keep it short and human-readable. For example:

CLAUDE.md

```python
# 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. For a checked-in CLAUDE.md, run [`/doctor`](/docs/en/commands#all-commands) and Claude proposes cuts for content it can derive from the codebase. If Claude keeps skipping one instruction, add emphasis such as “IMPORTANT” to that line alone. If you emphasize many lines, none of them stands out. 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. For import rules and where CLAUDE.md files can live, see [CLAUDE.md files](/docs/en/memory#claude-md-files).


[​](#configure-permissions)

Configure permissions

To get fewer prompts without giving up control, pre-approve the tools you trust with `/permissions` and let sandboxed commands run without asking with `/sandbox`. Switch to Manual mode when you want to approve edits and commands yourself.

With Claude Code v2.1.283 or later, auto mode is the [built-in starting permission mode](/docs/en/permission-modes#eliminate-prompts-with-auto-mode) for interactive terminal and VS Code sessions: a separate classifier model reviews most actions instead of you and blocks only what looks risky, such as scope escalation, unknown infrastructure, or hostile-content-driven actions. On earlier versions, auto mode is the built-in starting permission mode only on Pro, Max, and Team plans. In Manual mode, Claude Code asks before actions that might modify your system: file writes, Bash commands, MCP tools. That’s safe but tedious. After the tenth approval you’re clicking through rather than reviewing. Two tools cut those interruptions in Manual mode and apply in auto mode as well:

- **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)

Use CLI tools

Tell Claude Code to use CLI tools like `gh`, `aws`, `gcloud`, and `sentry-cli` when interacting with external services.

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)

Connect MCP servers

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`.

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)

Set up hooks

Use hooks for actions that must happen every time with zero exceptions.

[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)

Create skills

Create `SKILL.md` files in `.claude/skills/` to give Claude domain knowledge and reusable workflows.

[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/`:

.claude/skills/api-conventions/SKILL.md

```python
---
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:

.claude/skills/fix-issue/SKILL.md

```python
---
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)

Create custom subagents

Define specialized assistants in `.claude/agents/` that Claude can delegate to for isolated tasks.

[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.

.claude/agents/security-reviewer.md

```python
---
name: security-reviewer
description: Reviews code for security vulnerabilities
tools: Read, Grep, Glob, Bash
model: opus
---
You are a senior security engineer. Review code for:
- Injection vulnerabilities (SQL, XSS, command injection)
- Authentication and authorization flaws
- Secrets or credentials in code
- Insecure data handling

Provide specific line references and suggested fixes.
```

Tell Claude to use subagents explicitly: *“Use a subagent to review this code for security issues.”*


[​](#install-plugins)

Install plugins

Run `/plugin` to browse the marketplace. Plugins add skills, tools, and integrations without configuration.

[Plugins](/docs/en/plugins/overview) bundle skills, hooks, subagents, and MCP servers into a single installable unit from the community and Anthropic. If you work with a typed language, install a [code intelligence plugin](/docs/en/plugins/code-intelligence) to give Claude precise symbol navigation and automatic error detection after edits. For guidance on choosing between skills, subagents, hooks, and MCP, see [Extend Claude Code](/docs/en/features-overview#match-features-to-your-goal).

------------------------------------------------------------------------


[​](#communicate-effectively)

Communicate effectively

Ask Claude the questions you’d ask another engineer, and for larger features have Claude interview you and write a spec before you start implementing.


[​](#ask-codebase-questions)

Ask codebase questions

Ask Claude questions you’d ask a senior engineer.

When onboarding to a new codebase, use Claude Code for learning and exploration. You can ask Claude the same sorts of questions you would ask another engineer:

- How does logging work?
- How do I make a new API endpoint?
- What does `async move { ... }` do on line 134 of `foo.rs`?
- What edge cases does `CustomerOnboardingFlowImpl` handle?
- Why does this code call `foo()` instead of `bar()` on line 333?

Using Claude Code this way is an effective onboarding workflow, improving ramp-up time and reducing load on other engineers. No special prompting required: ask questions directly.


[​](#let-claude-interview-you)

Let Claude interview you

For larger features, have Claude interview you first. Start with a minimal prompt and ask Claude to interview you using the `AskUserQuestion` tool.

Claude asks about things you might not have considered yet, including technical implementation, UI/UX, edge cases, and tradeoffs. Replace `[brief description]` with your feature before sending the prompt.

```python
I want to build [brief description]. Interview me in detail using the AskUserQuestion tool.

Ask about technical implementation, UI/UX, edge cases, concerns, and tradeoffs. Don't ask obvious questions, dig into the hard parts I might not have considered.

Keep interviewing until we've covered everything, then write a complete spec to SPEC.md.
```

Once the spec is complete, start a fresh session to execute it. The new session has clean context focused entirely on implementation, and you have a written spec to reference. The most useful specs are self-contained: they name the files and interfaces involved, state what is out of scope, and end with an end-to-end verification step that proves the feature works. Time spent making the spec precise pays off more than time spent watching the implementation.

------------------------------------------------------------------------


[​](#manage-your-session)

Manage your session

Conversations are persistent and reversible. Use this to your advantage!


[​](#course-correct-early-and-often)

Course-correct early and often

Correct Claude as soon as you notice it going off track.

The best results come from tight feedback loops. Though Claude occasionally solves problems perfectly on the first attempt, correcting it quickly generally produces better solutions faster.

- **`Esc`**: stop Claude mid-action with the `Esc` key. Context is preserved, so you can redirect.
- **`Esc + Esc` or `/rewind`**: press `Esc` twice or run `/rewind` to open the rewind menu and restore previous conversation and code state, or summarize from a selected message.
- **`"Undo that"`**: have Claude revert its changes.
- **`/clear`**: reset context between unrelated tasks. Long sessions with irrelevant context can reduce performance.

If you’ve corrected Claude more than twice on the same issue in one session, the context is cluttered with failed approaches. Run `/clear` and start fresh with a more specific prompt that incorporates what you learned. A clean session with a better prompt almost always outperforms a long session with accumulated corrections.


[​](#manage-context-aggressively)

Manage context aggressively

Run `/clear` between unrelated tasks to reset context.

Claude Code automatically compacts conversation history when you approach context limits, which preserves important code and decisions while freeing space. During long sessions, Claude’s context window can fill with irrelevant conversation, file contents, and commands. This can reduce performance and sometimes distract Claude.

- Use `/clear` frequently between tasks to reset the context window entirely
- When auto compaction triggers, Claude summarizes what matters most, including code patterns, file states, and key decisions
- For more control, run `/compact <instructions>`, like `/compact Focus on the API changes`
- To compact only part of the conversation, use `Esc + Esc` or `/rewind`, select a message checkpoint, and choose **Summarize from here** or **Summarize up to here**. The first condenses messages from that point forward while keeping earlier context intact; the second condenses earlier messages while keeping recent ones in full. See [the rewind menu’s summarize options](/docs/en/checkpointing#rewind-and-summarize).
- Customize compaction behavior in CLAUDE.md with instructions like `"When compacting, always preserve the full list of modified files and any test commands"` to ensure critical context survives summarization
- For questions that don’t need to stay in context, use [`/btw`](/docs/en/interactive-mode#side-questions-with-%2Fbtw). The answer never enters conversation history, so you can check a detail without growing context.


[​](#use-subagents-for-investigation)

Use subagents for investigation

Delegate research with `"use subagents to investigate X"`. They explore in a separate context, keeping your main conversation clean for implementation.

Since context is your fundamental constraint, use subagents to keep research out of it. When Claude researches a codebase it reads lots of files, all of which consume your context. Subagents run in separate context windows and report back summaries:

```python
Use subagents to investigate how our authentication system handles token
refresh, and whether we have any existing OAuth utilities I should reuse.
```

You can also use subagents for verification after Claude implements something. See [Add an adversarial review step](#add-an-adversarial-review-step).


[​](#rewind-with-checkpoints)

Rewind with checkpoints

Every prompt you send that starts a turn creates a checkpoint. You can restore conversation, code, or both to any previous checkpoint.

Claude automatically snapshots files before each change so a checkpoint can restore them. Double-tap `Escape` or run `/rewind` to open the rewind menu. You can restore conversation only, restore code only, restore both, or summarize from a selected message. See [Checkpointing](/docs/en/checkpointing) for details. Instead of carefully planning every move, you can tell Claude to try something risky. If it doesn’t work, rewind and try a different approach. Checkpoints are saved with the conversation, so you can close your terminal, resume the session later, and still rewind.

Checkpoints only track changes made through Claude’s file editing tools. Changes made through Bash commands or external processes are not captured. This isn’t a replacement for git.


[​](#resume-conversations)

Resume conversations

Name sessions with `/rename` and treat them like branches: each workstream gets its own persistent context.

Claude Code saves conversations locally, so when a task spans multiple sittings you don’t have to re-explain the context. Run [`claude --continue`](/docs/en/sessions#resume-a-session) to pick up where you left off, or `claude --resume` to choose from a list. Give sessions descriptive names like `oauth-migration` so you can find them later. See [Manage sessions](/docs/en/sessions) for the full set of resume, branch, and naming controls.

------------------------------------------------------------------------


[​](#automate-and-scale)

Automate and scale

Once you’re effective with one Claude, multiply your output with parallel sessions, non-interactive mode, and fan-out patterns.


[​](#run-non-interactive-mode)

Run non-interactive mode

Use `claude -p "prompt"` in CI, pre-commit hooks, or scripts. Add `--output-format stream-json --verbose` for streaming JSON output.

With `claude -p "your prompt"`, you can run Claude non-interactively, without an interactive prompt. The run still creates a resumable session unless you pass `--no-session-persistence`. [Non-interactive mode](/docs/en/headless) is how you integrate Claude into CI pipelines, pre-commit hooks, or any automated workflow. The output formats let you parse results programmatically: plain text, JSON, or streaming JSON.

```python
# One-off queries
claude -p "Explain what this project does"

# Structured output for scripts
claude -p "List all API endpoints" --output-format json

# Streaming for real-time processing
claude -p "Analyze this log file" --output-format stream-json --verbose
```

The first command prints plain text. The `json` format returns a single JSON object with a `result` field. The `stream-json` format prints one JSON object per line, starting with an init event.


[​](#run-multiple-claude-sessions)

Run multiple Claude sessions

Run multiple Claude sessions in parallel to speed up development, run isolated experiments, or start complex workflows.

Pick the parallel approach that fits how much coordination you want to do yourself, and add messaging when the sessions need to pass findings between them:

- [Worktrees](/docs/en/worktrees): run separate CLI sessions in isolated git checkouts so edits don’t collide
- [Cross-session messaging](/docs/en/cross-session-messaging): let the sessions you run yourself pass findings to each other
- [Desktop app](/docs/en/desktop#work-in-parallel-with-sessions): manage multiple local sessions visually, optionally each in its own worktree
- [Use Claude Code in the cloud](/docs/en/claude-code-on-the-web): run sessions on Anthropic-managed infrastructure by default
- [Agent view](/docs/en/agent-view): research preview. Run `claude agents` to dispatch sessions that keep running in the background and watch them from one screen
- [Agent teams](/docs/en/agent-teams): experimental and disabled by default. Automated coordination of multiple sessions with shared tasks, messaging, and a team lead

Beyond parallelizing work, multiple sessions enable quality-focused workflows. A fresh context improves code review since Claude won’t be biased toward code it just wrote. For example, use a Writer/Reviewer pattern:

| Session A (Writer)                                                      | Session B (Reviewer)                                                                                                                                                     |
|-------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `Implement a rate limiter for our API endpoints`                        |                                                                                                                                                                          |
|                                                                         | `Review the rate limiter implementation in @src/middleware/rateLimiter.ts. Look for edge cases, race conditions, and consistency with our existing middleware patterns.` |
| `Here's the review feedback: [Session B output]. Address these issues.` |                                                                                                                                                                          |

You can do something similar with tests: have one Claude write tests, then another write code to pass them.


[​](#fan-out-across-files)

Fan out across files

Loop through tasks calling `claude -p` for each. Use `--allowedTools` to scope permissions for batch operations.

For large migrations or analyses, you can distribute work across many parallel Claude invocations. Run [`/batch <instruction>`](/docs/en/commands#all-commands) to have Claude split the change across 5 to 30 subagents. Each subagent works in its own worktree. To drive the fan-out from your own script instead, loop over `claude -p`:

1

Generate a task list

Have Claude write the list of files that need migrating to a file, so the loop in the next step can read it, with a prompt like `list all 2,000 Python files that need migrating and save the list to files.txt`

2

Write a script to loop through the list

```python
for file in $(cat files.txt); do
  claude -p "Migrate $file from Python 2 to Python 3. Return OK or FAIL." \
    --allowedTools "Edit,Bash(git commit *)"
done
```

3

Test on a few files, then run on all of them

Refine your prompt based on what goes wrong with the first 2-3 files, then run on the full set. The `--allowedTools` flag restricts what Claude can do, which matters when you’re running unattended.

You can also integrate Claude into existing data/processing pipelines:

```python
claude -p "<your prompt>" --output-format json | your_command
```


[​](#run-autonomously-with-auto-mode)

Run autonomously with auto mode

For uninterrupted execution with background safety checks, use [auto mode](/docs/en/permission-modes#eliminate-prompts-with-auto-mode). A classifier model reviews commands before they run, blocking scope escalation, unknown infrastructure, and hostile-content-driven actions while letting routine work proceed without prompts.

```python
claude --permission-mode auto -p "fix all lint errors"
```

When the classifier repeatedly blocks actions in a non-interactive run with the `-p` flag, Claude Code doesn’t stop the run. See [when auto mode falls back](/docs/en/permission-modes#when-auto-mode-falls-back) for what happens instead and for the thresholds.


[​](#add-an-adversarial-review-step)

Add an adversarial review step

Before treating a task as done, have a subagent review the diff in a fresh context and report gaps.

The longer Claude works unattended, the more an independent check matters before you count the work as done. A reviewer running in a fresh [subagent](/docs/en/sub-agents) context sees only the diff and the criteria you give it, not the reasoning that produced the change, so it evaluates the result on its own terms. For a correctness check, run the bundled [`/code-review` skill](/docs/en/commands), which reviews the current diff for bugs in a fresh subagent and returns findings to the session. To check the diff against your plan instead, write the review prompt yourself. Name the work to check, the plan to check it against, and what counts as a finding:

```python
Use a subagent to review the rate limiter diff against PLAN.md. Check that
every requirement is implemented, the listed edge cases have tests, and
nothing outside the task's scope changed. Report gaps, not style preferences.
```

Because the reviewer runs as a subagent, the implementing session receives the gaps directly and can fix them and re-review without you copying findings between windows.

A reviewer prompted to find gaps will usually report some, even when the work is sound, because that is what it was asked to do. Chasing every finding leads to over-engineering: extra abstraction layers, defensive code, and tests for cases that can’t happen. Tell the reviewer to flag only gaps that affect correctness or the stated requirements, and treat the rest as optional.

------------------------------------------------------------------------


[​](#avoid-common-failure-patterns)

Avoid common failure patterns

These are common mistakes. Recognizing them early saves time:

- **The kitchen sink session.** You start with one task, then ask Claude something unrelated, then go back to the first task. Context is full of irrelevant information.

  > **Fix**: `/clear` between unrelated tasks.

- **Correcting over and over.** Claude does something wrong, you correct it, it’s still wrong, you correct again. Context is polluted with failed approaches.

  > **Fix**: After two failed corrections, `/clear` and write a better initial prompt incorporating what you learned.

- **The over-specified CLAUDE.md.** If your CLAUDE.md is too long, Claude ignores half of it because important rules get lost in the noise.

  > **Fix**: Ruthlessly prune. If Claude already does something correctly without the instruction, delete it or convert it to a hook.

- **The trust-then-verify gap.** Claude produces a plausible-looking implementation that doesn’t handle edge cases.

  > **Fix**: Always provide verification (tests, scripts, screenshots). If you can’t verify it, don’t ship it.

- **The infinite exploration.** You ask Claude to “investigate” something without scoping it. Claude reads hundreds of files, filling the context.

  > **Fix**: Scope investigations narrowly or use subagents so the exploration doesn’t consume your main context.

------------------------------------------------------------------------


[​](#develop-your-intuition)

Develop your intuition

The patterns in this guide aren’t set in stone. They’re starting points that work well in general, but might not be optimal for every situation. Sometimes you *should* let context accumulate because you’re deep in one complex problem and the history is valuable. Sometimes you should skip planning and let Claude figure it out because the task is exploratory. Sometimes a vague prompt is exactly right because you want to see how Claude interprets the problem before constraining it. Pay attention to what works. When Claude produces great output, notice what you did: the prompt structure, the context you provided, the mode you were in. When Claude struggles, ask why. Was the context too noisy? The prompt too vague? The task too big for one pass? Over time, you’ll develop intuition that no guide can capture. You’ll know when to be specific and when to be open-ended, when to plan and when to explore, when to clear context and when to let it accumulate.


[​](#related-resources)

Related resources

- [How Claude Code works](/docs/en/how-claude-code-works): the agentic loop, tools, and context management
- [Extend Claude Code](/docs/en/features-overview): skills, hooks, MCP, subagents, and plugins
- [Common workflows](/docs/en/common-workflows): step-by-step recipes for debugging, testing, PRs, and more
- [CLAUDE.md](/docs/en/memory): store project conventions and persistent context


---
## best-practices.md

> Fetch the complete documentation index at: https://code.claude.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# 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](how-claude-code-works-0871be411d.md).

***

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. For detailed strategies on reducing token usage, see [Reduce token usage](/en/costs#reduce-token-usage).

***

## Give Claude a way to verify its work

<Tip>
  Include tests, screenshots, or expected outputs so Claude can check itself. This is the single highest-leverage thing you can do.
</Tip>

Claude performs dramatically better when it can verify its own work, like run tests, compare screenshots, and validate outputs.

Without clear success criteria, it might produce something that looks right but actually doesn't work. You become the only feedback loop, and every mistake requires your attention.

| Strategy                              | Before                                                  | After                                                                                                                                                                                                   |
| ------------------------------------- | ------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Provide verification criteria**     | *"implement a function that validates email addresses"* | *"write a validateEmail function. example test cases: [user@example.com](mailto:user@example.com) is true, invalid is false, [user@.com](mailto:user@.com) 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"*                                                             |

UI changes can be verified using the [Claude in Chrome extension](chrome.md). It opens a browser, tests the UI, and iterates until the code works.

Your verification can also be a test suite, a linter, or a Bash command that checks output. Invest in making your verification rock-solid.

***

## 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](/en/common-workflows#use-plan-mode-for-safe-code-analysis) to separate exploration from execution.

The recommended workflow has four phases:

<Steps>
  <Step title="Explore">
    Enter Plan Mode. Claude reads files and answers questions without making changes.

    ```txt claude (Plan Mode) 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 claude (Plan Mode) 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 back to Normal Mode and let Claude code, verifying against its plan.

    ```txt claude (Normal Mode) 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 claude (Normal Mode) 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](extend-claude-code-claude-code-docs-3b35aaf641.md).

### 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
```

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](../08-Plugins-Skills/extend-claude-with-skills-claude-code-docs.md) 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, or name it `CLAUDE.local.md` and `.gitignore` it
* **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 working with files in those directories

### Configure permissions

<Tip>
  Use `/permissions` to allowlist safe commands or `/sandbox` for OS-level isolation. This 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 two ways to reduce these interruptions:

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

Alternatively, use `--dangerously-skip-permissions` to bypass all permission checks for contained workflows like fixing lint errors or generating boilerplate.

<Warning>
  Letting Claude run arbitrary commands can result in data loss, system corruption, or data exfiltration via prompt injection. Only use `--dangerously-skip-permissions` in a sandbox without internet access.
</Warning>

Read more about [configuring permissions](claude-code-settings-claude-code-docs-d4420b4b52.md) and [enabling sandboxing](/en/sandboxing#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` to connect external tools like Notion, Figma, or your database.
</Tip>

With [MCP servers](../06-MCP-Tools/General/connect-claude-code-to-tools-via-mcp-claude-code-docs.md), 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](../07-Hooks/automate-workflows-with-hooks-claude-code-docs-e843f93261.md) 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."* Run `/hooks` for interactive configuration, or edit `.claude/settings.json` directly.

### Create skills

<Tip>
  Create `SKILL.md` files in `.claude/skills/` to give Claude domain knowledge and reusable workflows.
</Tip>

[Skills](../08-Plugins-Skills/extend-claude-with-skills-claude-code-docs.md) 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](../09-Agents-Patterns/create-custom-subagents-claude-code-docs-7dc93e85c0.md) 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}
---
name: security-reviewer
description: Reviews code for security vulnerabilities
tools: Read, Grep, Glob, Bash
model: opus
---
You are a senior security engineer. Review code for:
- Injection vulnerabilities (SQL, XSS, command injection)
- Authentication and authorization flaws
- Secrets or credentials in code
- Insecure data handling

Provide specific line references and suggested fixes.
```

Tell Claude to use subagents explicitly: *"Use a subagent to review this code for security issues."*

### Install plugins

<Tip>
  Run `/plugin` to browse the marketplace. Plugins add skills, tools, and integrations without configuration.
</Tip>

[Plugins](../08-Plugins-Skills/create-plugins-claude-code-docs.md) bundle skills, hooks, subagents, and MCP servers into a single installable unit from the community and Anthropic. If you work with a typed language, install a [code intelligence plugin](/en/discover-plugins#code-intelligence) to give Claude precise symbol navigation and automatic error detection after edits.

For guidance on choosing between skills, subagents, hooks, and MCP, see [Extend Claude Code](/en/features-overview#match-features-to-your-goal).

***

## Communicate effectively

The way you communicate with Claude Code significantly impacts the quality of results.

### Ask codebase questions

<Tip>
  Ask Claude questions you'd ask a senior engineer.
</Tip>

When onboarding to a new codebase, use Claude Code for learning and exploration. You can ask Claude the same sorts of questions you would ask another engineer:

* How does logging work?
* How do I make a new API endpoint?
* What does `async move { ... }` do on line 134 of `foo.rs`?
* What edge cases does `CustomerOnboardingFlowImpl` handle?
* Why does this code call `foo()` instead of `bar()` on line 333?

Using Claude Code this way is an effective onboarding workflow, improving ramp-up time and reducing load on other engineers. No special prompting required: ask questions directly.

### Let Claude interview you

<Tip>
  For larger features, have Claude interview you first. Start with a minimal prompt and ask Claude to interview you using the `AskUserQuestion` tool.
</Tip>

Claude asks about things you might not have considered yet, including technical implementation, UI/UX, edge cases, and tradeoffs.

```
I want to build [brief description]. Interview me in detail using the AskUserQuestion tool.

Ask about technical implementation, UI/UX, edge cases, concerns, and tradeoffs. Don't ask obvious questions, dig into the hard parts I might not have considered.

Keep interviewing until we've covered everything, then write a complete spec to SPEC.md.
```

Once the spec is complete, start a fresh session to execute it. The new session has clean context focused entirely on implementation, and you have a written spec to reference.

***

## Manage your session

Conversations are persistent and reversible. Use this to your advantage!

### Course-correct early and often

<Tip>
  Correct Claude as soon as you notice it going off track.
</Tip>

The best results come from tight feedback loops. Though Claude occasionally solves problems perfectly on the first attempt, correcting it quickly generally produces better solutions faster.

* **`Esc`**: Stop Claude mid-action with the `Esc` key. Context is preserved, so you can redirect.
* **`Esc + Esc` or `/rewind`**: Press `Esc` twice or run `/rewind` to open the rewind menu and restore previous conversation and code state.
* **`"Undo that"`**: Have Claude revert its changes.
* **`/clear`**: Reset context between unrelated tasks. Long sessions with irrelevant context can reduce performance.

If you've corrected Claude more than twice on the same issue in one session, the context is cluttered with failed approaches. Run `/clear` and start fresh with a more specific prompt that incorporates what you learned. A clean session with a better prompt almost always outperforms a long session with accumulated corrections.

### Manage context aggressively

<Tip>
  Run `/clear` between unrelated tasks to reset context.
</Tip>

Claude Code automatically compacts conversation history when you approach context limits, which preserves important code and decisions while freeing space.

During long sessions, Claude's context window can fill with irrelevant conversation, file contents, and commands. This can reduce performance and sometimes distract Claude.

* Use `/clear` frequently between tasks to reset the context window entirely
* When auto compaction triggers, Claude summarizes what matters most, including code patterns, file states, and key decisions
* For more control, run `/compact <instructions>`, like `/compact Focus on the API changes`
* Customize compaction behavior in CLAUDE.md with instructions like `"When compacting, always preserve the full list of modified files and any test commands"` to ensure critical context survives summarization

### Use subagents for investigation

<Tip>
  Delegate research with `"use subagents to investigate X"`. They explore in a separate context, keeping your main conversation clean for implementation.
</Tip>

Since context is your fundamental constraint, subagents are one of the most powerful tools available. When Claude researches a codebase it reads lots of files, all of which consume your context. Subagents run in separate context windows and report back summaries:

```
Use subagents to investigate how our authentication system handles token
refresh, and whether we have any existing OAuth utilities I should reuse.
```

The subagent explores the codebase, reads relevant files, and reports back with findings, all without cluttering your main conversation.

You can also use subagents for verification after Claude implements something:

```
use a subagent to review this code for edge cases
```

### Rewind with checkpoints

<Tip>
  Every action Claude makes creates a checkpoint. You can restore conversation, code, or both to any previous checkpoint.
</Tip>

Claude automatically checkpoints before changes. Double-tap `Escape` or run `/rewind` to open the checkpoint menu. You can restore conversation only (keep code changes), restore code only (keep conversation), or restore both.

Instead of carefully planning every move, you can tell Claude to try something risky. If it doesn't work, rewind and try a different approach. Checkpoints persist across sessions, so you can close your terminal and still rewind later.

<Warning>
  Checkpoints only track changes made *by Claude*, not external processes. This isn't a replacement for git.
</Warning>

### Resume conversations

<Tip>
  Run `claude --continue` to pick up where you left off, or `--resume` to choose from recent sessions.
</Tip>

Claude Code saves conversations locally. When a task spans multiple sessions (you start a feature, get interrupted, come back the next day) you don't have to re-explain the context:

```bash  theme={null}
claude --continue    # Resume the most recent conversation
claude --resume      # Select from recent conversations
```

Use `/rename` to give sessions descriptive names (`"oauth-migration"`, `"debugging-memory-leak"`) so you can find them later. Treat sessions like branches. Different workstreams can have separate, persistent contexts.

***

## Automate and scale

Once you're effective with one Claude, multiply your output with parallel sessions, headless mode, and fan-out patterns.

Everything so far assumes one human, one Claude, and one conversation. But Claude Code scales horizontally. The techniques in this section show how you can get more done.

### Run headless mode

<Tip>
  Use `claude -p "prompt"` in CI, pre-commit hooks, or scripts. Add `--output-format stream-json` for streaming JSON output.
</Tip>

With `claude -p "your prompt"`, you can run Claude headlessly, without an interactive session. Headless mode is how you integrate Claude into CI pipelines, pre-commit hooks, or any automated workflow. The output formats (plain text, JSON, streaming JSON) let you parse results programmatically.

```bash  theme={null}
# One-off queries
claude -p "Explain what this project does"

# Structured output for scripts
claude -p "List all API endpoints" --output-format json

# Streaming for real-time processing
claude -p "Analyze this log file" --output-format stream-json
```

### Run multiple Claude sessions

<Tip>
  Run multiple Claude sessions in parallel to speed up development, run isolated experiments, or start complex workflows.
</Tip>

There are two main ways to run parallel sessions:

* [Claude Desktop](../16-Mobile-Desktop/desktop.md): Manage multiple local sessions visually. Each session gets its own isolated worktree.
* [Claude Code on the web](claude-code-on-the-web-69d53821d4.md): Run on Anthropic's secure cloud infrastructure in isolated VMs.

Beyond parallelizing work, multiple sessions enable quality-focused workflows. A fresh context improves code review since Claude won't be biased toward code it just wrote.

For example, use a Writer/Reviewer pattern:

| Session A (Writer)                                                      | Session B (Reviewer)                                                                                                                                                     |
| ----------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `Implement a rate limiter for our API endpoints`                        |                                                                                                                                                                          |
|                                                                         | `Review the rate limiter implementation in @src/middleware/rateLimiter.ts. Look for edge cases, race conditions, and consistency with our existing middleware patterns.` |
| `Here's the review feedback: [Session B output]. Address these issues.` |                                                                                                                                                                          |

You can do something similar with tests: have one Claude write tests, then another write code to pass them.

### Fan out across files

<Tip>
  Loop through tasks calling `claude -p` for each. Use `--allowedTools` to scope permissions for batch operations.
</Tip>

For large migrations or analyses, you can distribute work across many parallel Claude invocations:

<Steps>
  <Step title="Generate a task list">
    Have Claude list all files that need migrating (e.g., `list all 2,000 Python files that need migrating`)
  </Step>

  <Step title="Write a script to loop through the list">
    ```bash  theme={null}
    for file in $(cat files.txt); do
      claude -p "Migrate $file from React to Vue. Return OK or FAIL." \
        --allowedTools "Edit,Bash(git commit *)"
    done
    ```
  </Step>

  <Step title="Test on a few files, then run at scale">
    Refine your prompt based on what goes wrong with the first 2-3 files, then run on the full set. The `--allowedTools` flag restricts what Claude can do, which matters when you're running unattended.
  </Step>
</Steps>

You can also integrate Claude into existing data/processing pipelines:

```bash  theme={null}
claude -p "<your prompt>" --output-format json | your_command
```

Use `--verbose` for debugging during development, and turn it off in production.

### Safe Autonomous Mode

Use `claude --dangerously-skip-permissions` to bypass all permission checks and let Claude work uninterrupted. This works well for workflows like fixing lint errors or generating boilerplate code.

<Warning>
  Letting Claude run arbitrary commands is risky and can result in data loss, system corruption, or data exfiltration (e.g., via prompt injection attacks). To minimize these risks, use `--dangerously-skip-permissions` in a container without internet access.

  With sandboxing enabled (`/sandbox`), you get similar autonomy with better security. Sandbox defines upfront boundaries rather than bypassing all checks.
</Warning>

***

## Avoid common failure patterns

These are common mistakes. Recognizing them early saves time:

* **The kitchen sink session.** You start with one task, then ask Claude something unrelated, then go back to the first task. Context is full of irrelevant information.
  > **Fix**: `/clear` between unrelated tasks.
* **Correcting over and over.** Claude does something wrong, you correct it, it's still wrong, you correct again. Context is polluted with failed approaches.
  > **Fix**: After two failed corrections, `/clear` and write a better initial prompt incorporating what you learned.
* **The over-specified CLAUDE.md.** If your CLAUDE.md is too long, Claude ignores half of it because important rules get lost in the noise.
  > **Fix**: Ruthlessly prune. If Claude already does something correctly without the instruction, delete it or convert it to a hook.
* **The trust-then-verify gap.** Claude produces a plausible-looking implementation that doesn't handle edge cases.
  > **Fix**: Always provide verification (tests, scripts, screenshots). If you can't verify it, don't ship it.
* **The infinite exploration.** You ask Claude to "investigate" something without scoping it. Claude reads hundreds of files, filling the context.
  > **Fix**: Scope investigations narrowly or use subagents so the exploration doesn't consume your main context.

***

## Develop your intuition

The patterns in this guide aren't set in stone. They're starting points that work well in general, but might not be optimal for every situation.

Sometimes you *should* let context accumulate because you're deep in one complex problem and the history is valuable. Sometimes you should skip planning and let Claude figure it out because the task is exploratory. Sometimes a vague prompt is exactly right because you want to see how Claude interprets the problem before constraining it.

Pay attention to what works. When Claude produces great output, notice what you did: the prompt structure, the context you provided, the mode you were in. When Claude struggles, ask why. Was the context too noisy? The prompt too vague? The task too big for one pass?

Over time, you'll develop intuition that no guide can capture. You'll know when to be specific and when to be open-ended, when to plan and when to explore, when to clear context and when to let it accumulate.

## Related resources

<CardGroup cols={2}>
  <Card title="How Claude Code works" icon="gear" href="/en/how-claude-code-works">
    Understand the agentic loop, tools, and context management
  </Card>

  <Card title="Extend Claude Code" icon="puzzle-piece" href="/en/features-overview">
    Choose between skills, hooks, MCP, subagents, and plugins
  </Card>

  <Card title="Common workflows" icon="list-check" href="/en/common-workflows">
    Step-by-step recipes for debugging, testing, PRs, and more
  </Card>

  <Card title="CLAUDE.md" icon="file-lines" href="/en/memory">
    Store project conventions and persistent context
  </Card>
</CardGroup>


---
## built-in-commands-claude-code-docs.md

# Built-in commands


Complete reference for built-in commands available in Claude Code.


Type `/` in Claude Code to see all available commands, or type `/` followed by any letters to filter. Not all commands are visible to every user. Some depend on your platform, plan, or environment. For example, `/desktop` only appears on macOS and Windows, `/upgrade` and `/privacy-settings` are only available on Pro and Max plans, and `/terminal-setup` is hidden when your terminal natively supports its keybindings. Claude Code also includes [bundled skills](/docs/en/skills#bundled-skills) like `/simplify`, `/batch`, and `/debug` that appear alongside built-in commands when you type `/`. To create your own commands, see [skills](/docs/en/skills). In the table below, `<arg>` indicates a required argument and `[arg]` indicates an optional one.

| Command | Purpose |
|:---|:---|
| `/add-dir <path>` | Add a new working directory to the current session |
| `/agents` | Manage [agent](/docs/en/sub-agents) configurations |
| `/btw <question>` | Ask a quick [side question](/docs/en/interactive-mode#side-questions-with-btw) without adding to the conversation |
| `/chrome` | Configure [Claude in Chrome](/docs/en/chrome) settings |
| `/clear` | Clear conversation history and free up context. Aliases: `/reset`, `/new` |
| `/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 |
| `/compact [instructions]` | Compact conversation with optional focus instructions |
| `/config` | Open the [Settings](/docs/en/settings) interface to adjust theme, model, [output style](/docs/en/output-styles), and other preferences. Alias: `/settings` |
| `/context` | Visualize current context usage as a colored grid. Shows optimization suggestions for context-heavy tools, memory bloat, and capacity warnings |
| `/copy` | Copy the last assistant response to clipboard. When code blocks are present, shows an interactive picker to select individual blocks or the full response |
| `/cost` | Show token usage statistics. See [cost tracking guide](/docs/en/costs#using-the-cost-command) for subscription-specific details |
| `/desktop` | Continue the current session in the Claude Code Desktop app. macOS and Windows only. 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 |
| `/doctor` | Diagnose and verify your Claude Code installation and settings |
| `/effort [low|medium|high|max|auto]` | Set the model [effort level](/docs/en/model-config#adjust-effort-level). `low`, `medium`, and `high` persist across sessions. `max` applies to the current session only and requires Opus 4.6. `auto` resets to the model default. Without an argument, shows the current level. Takes effect immediately without waiting for the current response to finish |
| `/exit` | Exit the CLI. 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 |
| `/extra-usage` | Configure extra usage to keep working when rate limits are hit |
| `/fast [on|off]` | Toggle [fast mode](/docs/en/fast-mode) on or off |
| `/feedback [report]` | Submit feedback about Claude Code. Alias: `/bug` |
| `/fork [name]` | Create a fork of the current conversation at this point |
| `/help` | Show help and available commands |
| `/hooks` | View [hook](/docs/en/hooks) configurations for tool events |
| `/ide` | Manage IDE integrations and show status |
| `/init` | Initialize project with `CLAUDE.md` guide |
| `/insights` | Generate a report analyzing your Claude Code sessions, including project areas, interaction patterns, and friction points |
| `/install-github-app` | Set up the [Claude GitHub Actions](/docs/en/github-actions) app for a repository. 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 or create your keybindings configuration file |
| `/login` | Sign in to your Anthropic account |
| `/logout` | Sign out from your Anthropic account |
| `/mcp` | Manage MCP server connections and OAuth authentication |
| `/memory` | Edit `CLAUDE.md` memory 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]` | Select or change the AI model. For models that support it, use left/right arrows to [adjust effort level](/docs/en/model-config#adjust-effort-level). The change takes effect immediately without waiting for the current response to finish |
| `/passes` | Share a free week of Claude Code with friends. Only visible if your account is eligible |
| `/permissions` | View or update [permissions](/docs/en/permissions#manage-permissions). Alias: `/allowed-tools` |
| `/plan` | Enter plan mode directly from the prompt |
| `/plugin` | Manage Claude Code [plugins](/docs/en/plugins) |
| `/pr-comments [PR]` | Fetch and display 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 |
| `/release-notes` | View the full changelog, with the most recent version closest to your prompt |
| `/reload-plugins` | Reload all active [plugins](/docs/en/plugins) to apply pending changes without restarting. Reports what was loaded and notes any changes that require a restart |
| `/remote-control` | Make this session available for [remote control](/docs/en/remote-control) from claude.ai. Alias: `/rc` |
| `/remote-env` | Configure the default remote environment for [web sessions started with `--remote`](/docs/en/claude-code-on-the-web#environment-configuration) |
| `/rename [name]` | Rename the current session and show the name on the prompt bar. Without a name, auto-generates one from conversation history |
| `/resume [session]` | Resume a conversation by ID or name, or open the session picker. Alias: `/continue` |
| `/review` | Deprecated. Install the [`code-review` plugin](https://github.com/anthropics/claude-code-marketplace/blob/main/code-review/README.md) instead: `claude plugin install code-review@claude-code-marketplace` |
| `/rewind` | Rewind the conversation and/or code to a previous point, or summarize from a selected message. See [checkpointing](/docs/en/checkpointing). Alias: `/checkpoint` |
| `/sandbox` | Toggle [sandbox mode](/docs/en/sandboxing). Available on supported platforms only |
| `/security-review` | Analyze pending changes on the current branch for security vulnerabilities. Reviews the git diff and identifies risks like injection, auth issues, and data exposure |
| `/skills` | List available [skills](/docs/en/skills) |
| `/stats` | Visualize daily usage, session history, streaks, and model preferences |
| `/status` | Open the Settings interface (Status tab) showing version, model, account, and connectivity |
| `/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 |
| `/tasks` | List and manage background tasks |
| `/terminal-setup` | Configure terminal keybindings for Shift+Enter and other shortcuts. Only visible in terminals that need it, like VS Code, Alacritty, or Warp |
| `/theme` | Change the color theme. Includes light and dark variants, colorblind-accessible (daltonized) themes, and ANSI themes that use your terminal’s color palette |
| `/upgrade` | Open the upgrade page to switch to a higher plan tier |
| `/usage` | Show plan usage limits and rate limit status |
| `/vim` | Toggle between Vim and Normal editing modes |

## 

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


---
## catch-security-issues-as-claude-writes-code-claude-code-docs-6f144bf1df.md

## On this page

- [Prerequisites](#prerequisites)
- [Install the plugin](#install-the-plugin)
  - [Enable for your team in local sessions](#enable-for-your-team-in-local-sessions)
- [What the plugin checks](#what-the-plugin-checks)
  - [On each file edit](#on-each-file-edit)
  - [At the end of each turn](#at-the-end-of-each-turn)
  - [On each commit or push Claude makes](#on-each-commit-or-push-claude-makes)
  - [Review independence and limits](#review-independence-and-limits)
- [Add your own rules](#add-your-own-rules)
  - [Add guidance for the model-backed reviews](#add-guidance-for-the-model-backed-reviews)
  - [Add custom per-edit patterns](#add-custom-per-edit-patterns)
  - [Rule file lookup locations](#rule-file-lookup-locations)
- [Usage cost](#usage-cost)
- [Disable or uninstall](#disable-or-uninstall)
- [How the plugin integrates with Claude Code](#how-the-plugin-integrates-with-claude-code)
- [How this fits with other security tools](#how-this-fits-with-other-security-tools)
- [Troubleshooting](#troubleshooting)
- [Related resources](#related-resources)

Code review & CI/CD

# Catch security issues as Claude writes code

Copy pageCopy page

Install the security-guidance plugin to have Claude review its own code changes for vulnerabilities and fix them in the same session.

Copy pageCopy page

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)

Prerequisites

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

Install the plugin

In a terminal Claude Code session, install from the [official Anthropic marketplace](/docs/en/plugins/anthropic-marketplaces):

```python
/plugin install security-guidance@claude-plugins-official
```

`/plugin` opens an interactive panel 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**
- **VS Code extension**: install from the [**Manage plugins** dialog](/docs/en/vs-code#manage-plugins)
- **Cloud sessions**: a cloud session doesn’t load plugins from your user settings or from the repository’s `.claude/settings.json`, as [What carries over from your setup](/docs/en/cloud-environments#what-carries-over-from-your-setup) explains. For plugins your organization distributes through managed settings, see [Manage plugins for your organization](/docs/en/plugins/org)

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/plugins/install#install-a-plugin): check the plugin name.

Check the install summary. If it reports `Run /reload-plugins to activate.`, see [Apply plugin changes without restarting](/docs/en/plugins/cli-reference#reload-plugins) to activate the plugin in your current session.


[​](#enable-for-your-team-in-local-sessions)

Enable for your team in local sessions

To turn the plugin on in the local sessions your teammates start in the repository, declare it in the project’s checked-in settings:

.claude/settings.json

```python
{
  "enabledPlugins": {
    "security-guidance@claude-plugins-official": true
  }
}
```

Administrators can enable the plugin organization-wide by setting [`enabledPlugins`](/docs/en/settings-reference#enabledplugins) in [managed settings](/docs/en/admin-setup).


[​](#what-the-plugin-checks)

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)

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)

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)

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)

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)

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)

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:

.claude/claude-security-guidance.md

```python
# 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)

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:

.claude/security-patterns.yaml

```python
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)

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)

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)

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:

```python
/plugin disable security-guidance@claude-plugins-official
```

To remove it from your user scope:

```python
/plugin uninstall security-guidance@claude-plugins-official
```

If the plugin was enabled through a project’s `.claude/settings.json`, uninstalling 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)

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)

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)

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)

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
- [Find plugins in the official marketplace](/docs/en/plugins/anthropic-marketplaces#find-plugins-in-the-official-marketplace): where to browse the other official plugins


---
## champion-kit-claude-code-docs-a490488ead.md

## On this page

- [The champion role](#the-champion-role)
  - [What this should cost you](#what-this-should-cost-you)
- [Share what you discover](#share-what-you-discover)
  - [What is worth sharing](#what-is-worth-sharing)
  - [Where to share it](#where-to-share-it)
  - [The format that works](#the-format-that-works)
- [Be the person people ask](#be-the-person-people-ask)
  - [Answer with a prompt rather than an explanation](#answer-with-a-prompt-rather-than-an-explanation)
  - [Point at the feature rather than the documentation](#point-at-the-feature-rather-than-the-documentation)
  - [Questions you are likely to hear](#questions-you-are-likely-to-hear)
- [Grow the circle](#grow-the-circle)
  - [Patterns that tend to work](#patterns-that-tend-to-work)
  - [Thirty-day playbook](#thirty-day-playbook)
  - [When someone wants to go deeper](#when-someone-wants-to-go-deeper)
- [Respond to common concerns](#respond-to-common-concerns)
- [Quick-reference sheet](#quick-reference-sheet)

Adoption

# Champion kit

Copy pageCopy page

A playbook for engineers advocating Claude Code internally: what to share, how to answer questions, and how to grow adoption on your team.

Copy pageCopy page

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

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 blocked, and offer the [Quickstart](/docs/en/quickstart) link before scheduling time. |


[​](#share-what-you-discover)

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)

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)

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)

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.

```python
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.
```

```python
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.
```

```python
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)

Be the person people ask

Once you have shared a few examples, questions will follow. This is where a champion helps the most, 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)

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.

```python
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)

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)

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)

Grow the circle

The objective is to establish a small number of lightweight habits that allow momentum to continue after you have stopped actively driving it. You don’t need to build a program or own a rollout. When questions in the channel are being answered by people other than you, the role has done its job.


[​](#patterns-that-tend-to-work)

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)

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.

1

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.

2

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.

3

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.

4

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.


[​](#when-someone-wants-to-go-deeper)

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 useful but difficult to discover on your own.


[​](#respond-to-common-concerns)

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 it helps the most. | 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)

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

Claude Code is updated frequently. Verify version-specific details against the [documentation home page](/docs/en/overview) before distributing this material internally.


---
## changelog-67fadc96e4.md

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

# 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.119" description="April 23, 2026">
  * `/config` settings (theme, editor mode, verbose, etc.) now persist to `~/.claude/settings.json` and participate in project/local/policy override precedence
  * Added `prUrlTemplate` setting to point the footer PR badge at a custom code-review URL instead of github.com
  * Added `CLAUDE_CODE_HIDE_CWD` environment variable to hide the working directory in the startup logo
  * `--from-pr` now accepts GitLab merge-request, Bitbucket pull-request, and GitHub Enterprise PR URLs
  * `--print` mode now honors the agent's `tools:` and `disallowedTools:` frontmatter, matching interactive-mode behavior
  * `--agent <name>` now honors the agent definition's `permissionMode` for built-in agents
  * PowerShell tool commands can now be auto-approved in permission mode, matching Bash behavior
  * Hooks: `PostToolUse` and `PostToolUseFailure` hook inputs now include `duration_ms` (tool execution time, excluding permission prompts and PreToolUse hooks)
  * Subagent and SDK MCP server reconfiguration now connects servers in parallel instead of serially
  * Plugins pinned by another plugin's version constraint now auto-update to the highest satisfying git tag
  * Vim mode: Esc in INSERT no longer pulls a queued message back into the input; press Esc again to interrupt
  * Slash command suggestions now highlight the characters that matched your query
  * Slash command picker now wraps long descriptions onto a second line instead of truncating
  * `owner/repo#N` shorthand links in output now use your git remote's host instead of always pointing at github.com
  * Security: `blockedMarketplaces` now correctly enforces `hostPattern` and `pathPattern` entries
  * OpenTelemetry: `tool_result` and `tool_decision` events now include `tool_use_id`; `tool_result` also includes `tool_input_size_bytes`
  * Status line: stdin JSON now includes `effort.level` and `thinking.enabled`
  * Fixed pasting CRLF content (Windows clipboards, Xcode console) inserting an extra blank line between every line
  * Fixed multi-line paste losing newlines in terminals using kitty keyboard protocol sequences inside bracketed paste
  * Fixed Glob and Grep tools disappearing on native macOS/Linux builds when the Bash tool is denied via permissions
  * Fixed scrolling up in fullscreen mode snapping back to the bottom every time a tool finishes
  * Fixed MCP HTTP connections failing with "Invalid OAuth error response" when servers returned non-JSON bodies for OAuth discovery requests
  * Fixed Rewind overlay showing "(no prompt)" for messages with image attachments
  * Fixed auto mode overriding plan mode with conflicting "Execute immediately" instructions
  * Fixed async `PostToolUse` hooks that emit no response payload writing empty entries to the session transcript
  * Fixed spinner staying on when a subagent task notification is orphaned in the queue
  * Tool search is now disabled by default on Vertex AI to avoid an unsupported beta header error (opt in with `ENABLE_TOOL_SEARCH`)
  * Fixed `@`-file Tab completion replacing the entire prompt when used inside a slash command with an absolute path
  * Fixed a stray `p` character appearing at the prompt on startup in macOS Terminal.app via Docker or SSH
  * Fixed `${ENV_VAR}` placeholders in `headers` for HTTP/SSE/WebSocket MCP servers not being substituted before requests
  * Fixed MCP OAuth client secret stored via `--client-secret` not being sent during token exchange for servers requiring `client_secret_post`
  * Fixed `/skills` Enter key closing the dialog instead of pre-filling `/<skill-name>` in the prompt
  * Fixed `/agents` detail view mislabeling built-in tools unavailable to subagents as "Unrecognized"
  * Fixed MCP servers from plugins not spawning on Windows when the plugin cache was incomplete
  * Fixed `/export` showing the current default model instead of the model the conversation actually used
  * Fixed verbose output setting not persisting after restart
  * Fixed `/usage` progress bars overlapping with their "Resets …" labels
  * Fixed plugin MCP servers failing when `${user_config.*}` references an optional field left blank
  * Fixed list items containing a sentence-final number wrapping the number onto its own line
  * Fixed `/plan` and `/plan open` not acting on the existing plan when entering plan mode
  * Fixed skills invoked before auto-compaction being re-executed against the next user message
  * Fixed `/reload-plugins` and `/doctor` reporting load errors for disabled plugins
  * Fixed Agent tool with `isolation: "worktree"` reusing stale worktrees from prior sessions
  * Fixed disabled MCP servers appearing as "failed" in `/status`
  * Fixed `TaskList` returning tasks in arbitrary filesystem order instead of sorted by ID
  * Fixed spurious "GitHub API rate limit exceeded" hints when `gh` output contained PR titles mentioning "rate limit"
  * Fixed SDK/bridge `read_file` not correctly enforcing size cap on growing files
  * Fixed PR not linked to session when working in a git worktree
  * Fixed `/doctor` warning about MCP server entries overridden by a higher-precedence scope
  * Windows: removed false-positive "Windows requires 'cmd /c' wrapper" MCP config warning
  * \[VSCode] Fixed voice dictation's first recording producing nothing on macOS while the microphone permission prompt is showing
</Update>

<Update label="2.1.118" description="April 23, 2026">
  * Added vim visual mode (`v`) and visual-line mode (`V`) with selection, operators, and visual feedback
  * Merged `/cost` and `/stats` into `/usage` — both remain as typing shortcuts that open the relevant tab
  * Create and switch between named custom themes from `/theme`, or hand-edit JSON files in `~/.claude/themes/`; plugins can also ship themes via a `themes/` directory
  * Hooks can now invoke MCP tools directly via `type: "mcp_tool"`
  * Added `DISABLE_UPDATES` env var to completely block all update paths including manual `claude update` — stricter than `DISABLE_AUTOUPDATER`
  * WSL on Windows can now inherit Windows-side managed settings via the `wslInheritsWindowsSettings` policy key
  * Auto mode: include `"$defaults"` in `autoMode.allow`, `autoMode.soft_deny`, or `autoMode.environment` to add custom rules alongside the built-in list instead of replacing it
  * Added a "Don't ask again" option to the auto mode opt-in prompt
  * Added `claude plugin tag` to create release git tags for plugins with version validation
  * `--continue`/`--resume` now find sessions that added the current directory via `/add-dir`
  * `/color` now syncs the session accent color to claude.ai/code when Remote Control is connected
  * The `/model` picker now honors `ANTHROPIC_DEFAULT_*_MODEL_NAME`/`_DESCRIPTION` overrides when using a custom `ANTHROPIC_BASE_URL` gateway
  * When auto-update skips a plugin due to another plugin's version constraint, the skip now appears in `/doctor` and the `/plugin` Errors tab
  * Fixed `/mcp` menu hiding OAuth Authenticate/Re-authenticate actions for servers configured with `headersHelper`, and HTTP/SSE MCP servers with custom headers being stuck in "needs authentication" after a transient 401
  * Fixed MCP servers whose OAuth token response omits `expires_in` requiring re-authentication every hour
  * Fixed MCP step-up authorization silently refreshing instead of prompting for re-consent when the server's `insufficient_scope` 403 names a scope the current token already has
  * Fixed an unhandled promise rejection when an MCP server's OAuth flow times out or is cancelled
  * Fixed MCP OAuth refresh proceeding without its cross-process lock under contention
  * Fixed macOS keychain race where a concurrent MCP token refresh could overwrite a freshly-refreshed OAuth token, causing unexpected "Please run /login" prompts
  * Fixed OAuth token refresh failing when the server revokes a token before its local expiry time
  * Fixed credential save crash on Linux/Windows corrupting `~/.claude/.credentials.json`
  * Fixed `/login` having no effect in a session launched with `CLAUDE_CODE_OAUTH_TOKEN` — the env token is now cleared so disk credentials take effect
  * Fixed unreadable text in the "new messages" scroll pill and `/plugin` badges
  * Fixed plan acceptance dialog offering "auto mode" instead of "bypass permissions" when running with `--dangerously-skip-permissions`
  * Fixed agent-type hooks failing with "Messages are required for agent hooks" when configured for events other than `Stop` or `SubagentStop`
  * Fixed `prompt` hooks re-firing on tool calls made by an agent-hook verifier subagent
  * Fixed `/fork` writing the full parent conversation to disk per fork — now writes a pointer and hydrates on read
  * Fixed Alt+K / Alt+X / Alt+^ / Alt+\_ freezing keyboard input
  * Fixed connecting to a remote session overwriting your local `model` setting in `~/.claude/settings.json`
  * Fixed typeahead showing "No commands match" error when pasting file paths that start with `/`
  * Fixed `plugin install` on an already-installed plugin not re-resolving a dependency installed at the wrong version
  * Fixed unhandled errors from file watcher on invalid paths or fd exhaustion
  * Fixed Remote Control sessions getting archived on transient CCR initialization blips during JWT refresh
  * Fixed subagents resumed via `SendMessage` not restoring the explicit `cwd` they were spawned with
</Update>

<Update label="2.1.117" description="April 22, 2026">
  * Forked subagents can now be enabled on external builds by setting `CLAUDE_CODE_FORK_SUBAGENT=1`
  * Agent frontmatter `mcpServers` are now loaded for main-thread agent sessions via `--agent`
  * Improved `/model`: selections now persist across restarts even when the project pins a different model, and the startup header shows when the active model comes from a project or managed-settings pin
  * The `/resume` command now offers to summarize stale, large sessions before re-reading them, matching the existing `--resume` behavior
  * Faster startup when both local and claude.ai MCP servers are configured (concurrent connect now default)
  * `plugin install` on an already-installed plugin now installs any missing dependencies instead of stopping at "already installed"
  * Plugin dependency errors now say "not installed" with an install hint, and `claude plugin marketplace add` now auto-resolves missing dependencies from configured marketplaces
  * Managed-settings `blockedMarketplaces` and `strictKnownMarketplaces` are now enforced on plugin install, update, refresh, and autoupdate
  * Advisor Tool (experimental): dialog now carries an "experimental" label, learn-more link, and startup notification when enabled; sessions no longer get stuck with "Advisor tool result content could not be processed" errors on every prompt and `/compact`
  * The `cleanupPeriodDays` retention sweep now also covers `~/.claude/tasks/`, `~/.claude/shell-snapshots/`, and `~/.claude/backups/`
  * OpenTelemetry: `user_prompt` events now include `command_name` and `command_source` for slash commands; `cost.usage`, `token.usage`, `api_request`, and `api_error` now include an `effort` attribute when the model supports effort levels. Custom/MCP command names are redacted unless `OTEL_LOG_TOOL_DETAILS=1` is set
  * Native builds on macOS and Linux: the `Glob` and `Grep` tools are replaced by embedded `bfs` and `ugrep` available through the Bash tool — faster searches without a separate tool round-trip (Windows and npm-installed builds unchanged)
  * Windows: cached `where.exe` executable lookups per process for faster subprocess launches
  * Default effort for Pro/Max subscribers on Opus 4.6 and Sonnet 4.6 is now `high` (was `medium`)
  * Fixed Plain-CLI OAuth sessions dying with "Please run /login" when the access token expires mid-session — the token is now refreshed reactively on 401
  * Fixed `WebFetch` hanging on very large HTML pages by truncating input before HTML-to-markdown conversion
  * Fixed a crash when a proxy returns HTTP 204 No Content — now surfaces a clear error instead of a `TypeError`
  * Fixed `/login` having no effect when launched with `CLAUDE_CODE_OAUTH_TOKEN` env var and that token expires
  * Fixed prompt-input undo (`Ctrl+_`) doing nothing immediately after typing, and skipping a state on each undo step
  * Fixed `NO_PROXY` not being respected for remote API requests when running under Bun
  * Fixed rare spurious escape/return triggers when key names arrive as coalesced text over slow connections
  * Fixed SDK `reload_plugins` reconnecting all user MCP servers serially
  * Fixed Bedrock application-inference-profile requests failing with 400 when backed by Opus 4.7 with thinking disabled
  * Fixed MCP `elicitation/create` requests auto-cancelling in print/SDK mode when the server finishes connecting mid-turn
  * Fixed subagents running a different model than the main agent incorrectly flagging file reads with a malware warning
  * Fixed idle re-render loop when background tasks are present, reducing memory growth on Linux
  * \[VSCode] Fixed "Manage Plugins" panel breaking when multiple large marketplaces are configured
  * Fixed Opus 4.7 sessions showing inflated `/context` percentages and autocompacting too early — Claude Code was computing against a 200K context window instead of Opus 4.7's native 1M
</Update>

<Update label="2.1.116" description="April 20, 2026">
  * `/resume` on large sessions is significantly faster (up to 67% on 40MB+ sessions) and handles sessions with many dead-fork entries more efficiently
  * Faster MCP startup when multiple stdio servers are configured; `resources/templates/list` is now deferred to first `@`-mention
  * Smoother fullscreen scrolling in VS Code, Cursor, and Windsurf terminals — `/terminal-setup` now configures the editor's scroll sensitivity
  * Thinking spinner now shows progress inline ("still thinking", "thinking more", "almost done thinking"), replacing the separate hint row
  * `/config` search now matches option values (e.g. searching "vim" finds the Editor mode setting)
  * `/doctor` can now be opened while Claude is responding, without waiting for the current turn to finish
  * `/reload-plugins` and background plugin auto-update now auto-install missing plugin dependencies from marketplaces you've already added
  * Bash tool now surfaces a hint when `gh` commands hit GitHub's API rate limit, so agents can back off instead of retrying
  * The Usage tab in Settings now shows your 5-hour and weekly usage immediately and no longer fails when the usage endpoint is rate-limited
  * Agent frontmatter `hooks:` now fire when running as a main-thread agent via `--agent`
  * Slash command menu now shows "No commands match" when your filter has zero results, instead of disappearing
  * Security: sandbox auto-allow no longer bypasses the dangerous-path safety check for `rm`/`rmdir` targeting `/`, `$HOME`, or other critical system directories
  * Claude Code and installer now use `https://downloads.claude.ai/claude-code-releases` instead of `https://storage.googleapis.com/claude-code-dist-86c565f3-f756-42ad-8dfa-d59b1c096819/claude-code-releases`
  * Fixed Devanagari and other Indic scripts rendering with broken column alignment in the terminal UI
  * Fixed Ctrl+- not triggering undo in terminals using the Kitty keyboard protocol (iTerm2, Ghostty, kitty, WezTerm, Windows Terminal)
  * Fixed Cmd+Left/Right not jumping to line start/end in terminals that use the Kitty keyboard protocol (Warp fullscreen, kitty, Ghostty, WezTerm)
  * Fixed Ctrl+Z hanging the terminal when Claude Code is launched via a wrapper process (e.g. `npx`, `bun run`)
  * Fixed scrollback duplication in inline mode where resizing the terminal or large output bursts would repeat earlier conversation history
  * Fixed modal search dialogs overflowing the screen at short terminal heights, hiding the search box and keyboard hints
  * Fixed scattered blank cells and disappearing composer chrome in the VS Code integrated terminal during scrolling
  * Fixed an intermittent API 400 error related to cache control TTL ordering that could occur when a parallel request completed during request setup
  * Fixed `/branch` rejecting conversations with transcripts larger than 50MB
  * Fixed `/resume` silently showing an empty conversation on large session files instead of reporting the load error
  * Fixed `/plugin` Installed tab showing the same item twice when it appears under Needs attention or Favorites
  * Fixed `/update` and `/tui` not working after entering a worktree mid-session
</Update>

<Update label="2.1.114" description="April 18, 2026">
  * Fixed a crash in the permission dialog when an agent teams teammate requested tool permission
</Update>

<Update label="2.1.113" description="April 17, 2026">
  * Changed the CLI to spawn a native Claude Code binary (via a per-platform optional dependency) instead of bundled JavaScript
  * Added `sandbox.network.deniedDomains` setting to block specific domains even when a broader `allowedDomains` wildcard would otherwise permit them
  * Fullscreen mode: Shift+↑/↓ now scrolls the viewport when extending a selection past the visible edge
  * `Ctrl+A` and `Ctrl+E` now move to the start/end of the current logical line in multiline input, matching readline behavior
  * Windows: `Ctrl+Backspace` now deletes the previous word
  * Long URLs in responses and bash output stay clickable when they wrap across lines (in terminals with OSC 8 hyperlinks)
  * Improved `/loop`: pressing Esc now cancels pending wakeups, and wakeups display as "Claude resuming /loop wakeup" for clarity
  * `/extra-usage` now works from Remote Control (mobile/web) clients
  * Remote Control clients can now query `@`-file autocomplete suggestions
  * Improved `/ultrareview`: faster launch with parallelized checks, diffstat in the launch dialog, and animated launching state
  * Subagents that stall mid-stream now fail with a clear error after 10 minutes instead of hanging silently
  * Bash tool: multi-line commands whose first line is a comment now show the full command in the transcript, closing a UI-spoofing vector
  * Running `cd <current-directory> && git …` no longer triggers a permission prompt when the `cd` is a no-op
  * Security: on macOS, `/private/{etc,var,tmp,home}` paths are now treated as dangerous removal targets under `Bash(rm:*)` allow rules
  * Security: Bash deny rules now match commands wrapped in `env`/`sudo`/`watch`/`ionice`/`setsid` and similar exec wrappers
  * Security: `Bash(find:*)` allow rules no longer auto-approve `find -exec`/`-delete`
  * Fixed MCP concurrent-call timeout handling where a message for one tool call could silently disarm another call's watchdog
  * Fixed Cmd-backspace / `Ctrl+U` to once again delete from the cursor to the start of the line
  * Fixed markdown tables breaking when a cell contains an inline code span with a pipe character
  * Fixed session recap auto-firing while composing unsent text in the prompt
  * Fixed `/copy` "Full response" not aligning markdown table columns for pasting into GitHub, Notion, or Slack
  * Fixed messages typed while viewing a running subagent being hidden from its transcript and misattributed to the parent AI
  * Fixed Bash `dangerouslyDisableSandbox` running commands outside the sandbox without a permission prompt
  * Fixed `/effort auto` confirmation — now says "Effort level set to max" to match the status bar label
  * Fixed the "copied N chars" toast overcounting emoji and other multi-code-unit characters
  * Fixed `/insights` crashing with `EBUSY` on Windows
  * Fixed exit confirmation dialog mislabeling one-shot scheduled tasks as recurring — now shows a countdown
  * Fixed slash/@ completion menu not sitting flush against the prompt border in fullscreen mode
  * Fixed `CLAUDE_CODE_EXTRA_BODY` `output_config.effort` causing 400 errors on subagent calls to models that don't support effort and on Vertex AI
  * Fixed prompt cursor disappearing when `NO_COLOR` is set
  * Fixed `ToolSearch` ranking so pasted MCP tool names surface the actual tool instead of description-matching siblings
  * Fixed compacting a resumed long-context session failing with "Extra usage is required for long context requests"
  * Fixed `plugin install` succeeding when a dependency version conflicts with an already-installed plugin — now reports `range-conflict`
  * Fixed "Refine with Ultraplan" not showing the remote session URL in the transcript
  * Fixed SDK image content blocks that fail to process crashing the session — now degrade to a text placeholder
  * Fixed Remote Control sessions not streaming subagent transcripts
  * Fixed Remote Control sessions not being archived when Claude Code exits
  * Fixed `thinking.type.enabled is not supported` 400 error when using Opus 4.7 via a Bedrock Application Inference Profile ARN
</Update>

<Update label="2.1.112" description="April 16, 2026">
  * Fixed "claude-opus-4-7 is temporarily unavailable" for auto mode
</Update>

<Update label="2.1.111" description="April 16, 2026">
  * Claude Opus 4.7 xhigh is now available! Use /effort to tune speed vs. intelligence
  * Auto mode is now available for Max subscribers when using Opus 4.7
  * Added `xhigh` effort level for Opus 4.7, sitting between `high` and `max`. Available via `/effort`, `--effort`, and the model picker; other models fall back to `high`
  * `/effort` now opens an interactive slider when called without arguments, with arrow-key navigation between levels and Enter to confirm
  * Added "Auto (match terminal)" theme option that matches your terminal's dark/light mode — select it from `/theme`
  * Added `/less-permission-prompts` skill — scans transcripts for common read-only Bash and MCP tool calls and proposes a prioritized allowlist for `.claude/settings.json`
  * Added `/ultrareview` for running comprehensive code review in the cloud using parallel multi-agent analysis and critique — invoke with no arguments to review your current branch, or `/ultrareview <PR#>` to fetch and review a specific GitHub PR
  * Auto mode no longer requires `--enable-auto-mode`
  * Windows: PowerShell tool is progressively rolling out. Opt in or out with `CLAUDE_CODE_USE_POWERSHELL_TOOL`. On Linux and macOS, enable with `CLAUDE_CODE_USE_POWERSHELL_TOOL=1` (requires `pwsh` on PATH)
  * Read-only bash commands with glob patterns (e.g. `ls *.ts`) and commands starting with `cd <project-dir> &&` no longer trigger a permission prompt
  * Suggest the closest matching subcommand when `claude <word>` is invoked with a near-miss typo (e.g. `claude udpate` → "Did you mean `claude update`?")
  * Plan files are now named after your prompt (e.g. `fix-auth-race-snug-otter.md`) instead of purely random words
  * Improved `/setup-vertex` and `/setup-bedrock` to show the actual `settings.json` path when `CLAUDE_CONFIG_DIR` is set, seed model candidates from existing pins on re-run, and offer a "with 1M context" option for supported models
  * `/skills` menu now supports sorting by estimated token count — press `t` to toggle
  * `Ctrl+U` now clears the entire input buffer (previously: delete to start of line); press `Ctrl+Y` to restore
  * `Ctrl+L` now forces a full screen redraw in addition to clearing the prompt input
  * Transcript view footer now shows `[` (dump to scrollback) and `v` (open in editor) shortcuts
  * The "+N lines" marker for truncated long pastes is now a full-width rule for easier scanning
  * Headless `--output-format stream-json` now includes `plugin_errors` on the init event when plugins are demoted for unsatisfied dependencies
  * Added `OTEL_LOG_RAW_API_BODIES` environment variable to emit full API request and response bodies as OpenTelemetry log events for debugging
  * Suppressed spurious decompression, network, and transient error messages that could appear in the TUI during normal operation
  * Reverted the v2.1.110 cap on non-streaming fallback retries — it traded long waits for more outright failures during API overload
  * Fixed terminal display tearing (random characters, drifting input) in iTerm2 + tmux setups when terminal notifications are sent
  * Fixed `@` file suggestions re-scanning the entire project on every turn in non-git working directories, and showing only config files in freshly-initialized git repos with no tracked files
  * Fixed LSP diagnostics from before an edit appearing after it, causing the model to re-read files it just edited
  * Fixed tab-completing `/resume` immediately resuming an arbitrary titled session instead of showing the session picker
  * Fixed `/context` grid rendering with extra blank lines between rows
  * Fixed `/clear` dropping the session name set by `/rename`, causing statusline output to lose `session_name`
  * Improved plugin error handling: dependency errors now distinguish conflicting, invalid, and overly complex version requirements; fixed stale resolved versions after `plugin update`; `plugin install` now recovers from interrupted prior installs
  * Fixed Claude calling a non-existent `commit` skill and showing "Unknown skill: commit" for users without a custom `/commit` command
  * Fixed 429 rate-limit errors on Bedrock/Vertex/Foundry referencing status.claude.com (it only covers Anthropic-operated providers)
  * Fixed feedback surveys appearing back-to-back after dismissing one
  * Fixed bare URLs in bash/PowerShell/MCP tool output being unclickable when the terminal wraps them across lines
  * Windows: `CLAUDE_ENV_FILE` and SessionStart hook environment files now apply (previously a no-op)
  * Windows: permission rules with drive-letter paths are now correctly root-anchored, and paths differing only by drive-letter case are recognized as the same path
</Update>

<Update label="2.1.110" description="April 15, 2026">
  * Added `/tui` command and `tui` setting — run `/tui fullscreen` to switch to flicker-free rendering in the same conversation
  * Added push notification tool — Claude can send mobile push notifications when Remote Control and "Push when Claude decides" config are enabled
  * Changed `Ctrl+O` to toggle between normal and verbose transcript only; focus view is now toggled separately with the new `/focus` command
  * Added `autoScrollEnabled` config to disable conversation auto-scroll in fullscreen mode
  * Added option to show Claude's last response as commented context in the `Ctrl+G` external editor (enable via `/config`)
  * Improved `/plugin` Installed tab — items needing attention and favorites appear at the top, disabled items are hidden behind a fold, and `f` favorites the selected item
  * Improved `/doctor` to warn when an MCP server is defined in multiple config scopes with different endpoints
  * `--resume`/`--continue` now resurrects unexpired scheduled tasks
  * `/context`, `/exit`, and `/reload-plugins` now work from Remote Control (mobile/web) clients
  * Write tool now informs the model when you edit the proposed content in the IDE diff before accepting
  * Bash tool now enforces the documented maximum timeout instead of accepting arbitrarily large values
  * SDK/headless sessions now read `TRACEPARENT`/`TRACESTATE` from the environment for distributed trace linking
  * Session recap is now enabled for users with telemetry disabled (Bedrock, Vertex, Foundry, `DISABLE_TELEMETRY`). Opt out via `/config` or `CLAUDE_CODE_ENABLE_AWAY_SUMMARY=0`.
  * Fixed MCP tool calls hanging indefinitely when the server connection drops mid-response on SSE/HTTP transports
  * Fixed non-streaming fallback retries causing multi-minute hangs when the API is unreachable
  * Fixed session recap, local slash-command output, and other system status lines not appearing in focus mode
  * Fixed high CPU usage in fullscreen when text is selected while a tool is running
  * Fixed plugin install not honoring dependencies declared in `plugin.json` when the marketplace entry omits them; `/plugin` install now lists auto-installed dependencies
  * Fixed skills with `disable-model-invocation: true` failing when invoked via `/<skill>` mid-message
  * Fixed `--resume` sometimes showing the first prompt instead of the `/rename` name for sessions still running or exited uncleanly
  * Fixed queued messages briefly appearing twice during multi-tool-call turns
  * Fixed session cleanup not removing the full session directory including subagent transcripts
  * Fixed dropped keystrokes after the CLI relaunches (e.g. `/tui`, provider setup wizards)
  * Fixed garbled startup rendering in macOS Terminal.app and other terminals that don't support synchronized output
  * Hardened "Open in editor" actions against command injection from untrusted filenames
  * Fixed `PermissionRequest` hooks returning `updatedInput` not being re-checked against `permissions.deny` rules; `setMode:'bypassPermissions'` updates now respect `disableBypassPermissionsMode`
  * Fixed `PreToolUse` hook `additionalContext` being dropped when the tool call fails
  * Fixed stdio MCP servers that print stray non-JSON lines to stdout being disconnected on the first stray line (regression in 2.1.105)
  * Fixed headless/SDK session auto-title firing an extra Haiku request when `CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC` or `CLAUDE_CODE_DISABLE_TERMINAL_TITLE` is set
  * Fixed potential excessive memory allocation when piped (non-TTY) Ink output contains a single very wide line
  * Fixed `/skills` menu not scrolling when the list overflows the modal in fullscreen mode
  * Fixed Remote Control sessions showing a generic error instead of prompting for re-login when the session is too old
  * Fixed Remote Control session renames from claude.ai not persisting the title to the local CLI session
</Update>

<Update label="2.1.109" description="April 15, 2026">
  * Improved the extended-thinking indicator with a rotating progress hint
</Update>

<Update label="2.1.108" description="April 14, 2026">
  * Added `ENABLE_PROMPT_CACHING_1H` env var to opt into 1-hour prompt cache TTL on API key, Bedrock, Vertex, and Foundry (`ENABLE_PROMPT_CACHING_1H_BEDROCK` is deprecated but still honored), and `FORCE_PROMPT_CACHING_5M` to force 5-minute TTL
  * Added recap feature to provide context when returning to a session, configurable in `/config` and manually invocable with `/recap`; force with `CLAUDE_CODE_ENABLE_AWAY_SUMMARY` if telemetry disabled.
  * The model can now discover and invoke built-in slash commands like `/init`, `/review`, and `/security-review` via the Skill tool
  * `/undo` is now an alias for `/rewind`
  * Improved `/model` to warn before switching models mid-conversation, since the next response re-reads the full history uncached
  * Improved `/resume` picker to default to sessions from the current directory; press `Ctrl+A` to show all projects
  * Improved error messages: server rate limits are now distinguished from plan usage limits; 5xx/529 errors show a link to status.claude.com; unknown slash commands suggest the closest match
  * Reduced memory footprint for file reads, edits, and syntax highlighting by loading language grammars on demand
  * Added "verbose" indicator when viewing the detailed transcript (`Ctrl+O`)
  * Added a warning at startup when prompt caching is disabled via `DISABLE_PROMPT_CACHING*` environment variables
  * Fixed paste not working in the `/login` code prompt (regression in 2.1.105)
  * Fixed subscribers who set `DISABLE_TELEMETRY` falling back to 5-minute prompt cache TTL instead of 1 hour
  * Fixed Agent tool prompting for permission in auto mode when the safety classifier's transcript exceeded its context window
  * Fixed Bash tool producing no output when `CLAUDE_ENV_FILE` (e.g. `~/.zprofile`) ends with a `#` comment line
  * Fixed `claude --resume <session-id>` losing the session's custom name and color set via `/rename`
  * Fixed session titles showing placeholder example text when the first message is a short greeting
  * Fixed terminal escape codes appearing as garbage text in the prompt input after `--teleport`
  * Fixed `/feedback` retry: pressing Enter to resubmit after a failure now works without first editing the description
  * Fixed `--teleport` and `--resume <id>` precondition errors (e.g. dirty git tree, session not found) exiting silently instead of showing the error message
  * Fixed Remote Control session titles set in the web UI being overwritten by auto-generated titles after the third message
  * Fixed `--resume` truncating sessions when the transcript contained a self-referencing message
  * Fixed transcript write failures (e.g., disk full) being silently dropped instead of being logged
  * Fixed diacritical marks (accents, umlauts, cedillas) being dropped from responses when the `language` setting is configured
  * Fixed policy-managed plugins never auto-updating when running from a different project than where they were first installed
</Update>

<Update label="2.1.107" description="April 14, 2026">
  * Show thinking hints sooner during long operations
</Update>

<Update label="2.1.105" description="April 13, 2026">
  * Added `path` parameter to the `EnterWorktree` tool to switch into an existing worktree of the current repository
  * Added PreCompact hook support: hooks can now block compaction by exiting with code 2 or returning `{"decision":"block"}`
  * Added background monitor support for plugins via a top-level `monitors` manifest key that auto-arms at session start or on skill invoke
  * `/proactive` is now an alias for `/loop`
  * Improved stalled API stream handling: streams now abort after 5 minutes of no data and retry non-streaming instead of hanging indefinitely
  * Improved network error messages: connection errors now show a retry message immediately instead of a silent spinner
  * Improved file write display: long single-line writes (e.g. minified JSON) are now truncated in the UI instead of paginating across many screens
  * Improved `/doctor` layout with status icons; press `f` to have Claude fix reported issues
  * Improved `/config` labels and descriptions for clarity
  * Improved skill description handling: raised the listing cap from 250 to 1,536 characters and added a startup warning when descriptions are truncated
  * Improved `WebFetch` to strip `<style>` and `<script>` contents from fetched pages so CSS-heavy pages no longer exhaust the content budget before reaching actual text
  * Improved stale agent worktree cleanup to remove worktrees whose PR was squash-merged instead of keeping them indefinitely
  * Improved MCP large-output truncation prompt to give format-specific recipes (e.g. `jq` for JSON, computed Read chunk sizes for text)
  * Fixed images attached to queued messages (sent while Claude is working) being dropped
  * Fixed screen going blank when the prompt input wraps to a second line in long conversations
  * Fixed leading whitespace getting copied when selecting multi-line assistant responses in fullscreen mode
  * Fixed leading whitespace being trimmed from assistant messages, breaking ASCII art and indented diagrams
  * Fixed garbled bash output when commands print clickable file links (e.g. Python `rich`/`loguru` logging)
  * Fixed alt+enter not inserting a newline in terminals using ESC-prefix alt encoding, and Ctrl+J not inserting a newline (regression in 2.1.100)
  * Fixed duplicate "Creating worktree" text in EnterWorktree/ExitWorktree tool display
  * Fixed queued user prompts disappearing from focus mode
  * Fixed one-shot scheduled tasks re-firing repeatedly when the file watcher missed the post-fire cleanup
  * Fixed inbound channel notifications being silently dropped after the first message for Team/Enterprise users
  * Fixed marketplace plugins with `package.json` and lockfile not having dependencies installed automatically after install/update
  * Fixed marketplace auto-update leaving the official marketplace in a broken state when a plugin process holds files open during the update
  * Fixed "Resume this session with..." hint not printing on exit after `/resume`, `--worktree`, or `/branch`
  * Fixed feedback survey shortcut keys firing when typed at the end of a longer prompt
  * Fixed stdio MCP server emitting malformed (non-JSON) output hanging the session instead of failing fast with "Connection closed"
  * Fixed MCP tools missing on the first turn of headless/remote-trigger sessions when MCP servers connect asynchronously
  * Fixed `/model` picker on AWS Bedrock in non-US regions persisting invalid `us.*` model IDs to `settings.json` when inference profile discovery is still in-flight
  * Fixed 429 rate-limit errors showing a raw JSON dump instead of a clean message for API-key, Bedrock, and Vertex users
  * Fixed crash on resume when session contains malformed text blocks
  * Fixed `/help` dropping the tab bar, Shortcuts heading, and footer at short terminal heights
  * Fixed malformed keybinding entry values in `keybindings.json` being silently loaded instead of rejected with a clear error
  * Fixed `CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC` in one project's settings permanently disabling usage metrics for all projects on the machine
  * Fixed washed-out 16-color palette when using Ghostty, Kitty, Alacritty, WezTerm, foot, rio, or Contour over SSH/mosh
  * Fixed Bash tool suggesting `acceptEdits` permission mode when exiting plan mode would downgrade from a higher permission level
</Update>

<Update label="2.1.101" description="April 10, 2026">
  * Added `/team-onboarding` command to generate a teammate ramp-up guide from your local Claude Code usage
  * Added OS CA certificate store trust by default, so enterprise TLS proxies work without extra setup (set `CLAUDE_CODE_CERT_STORE=bundled` to use only bundled CAs)
  * `/ultraplan` and other remote-session features now auto-create a default cloud environment instead of requiring web setup first
  * Improved brief mode to retry once when Claude responds with plain text instead of a structured message
  * Improved focus mode: Claude now writes more self-contained summaries since it knows you only see its final message
  * Improved tool-not-available errors to explain why and how to proceed when the model calls a tool that exists but isn't available in the current context
  * Improved rate-limit retry messages to show which limit was hit and when it resets instead of an opaque seconds countdown
  * Improved refusal error messages to include the API-provided explanation when available
  * Improved `claude -p --resume <name>` to accept session titles set via `/rename` or `--name`
  * Improved settings resilience: an unrecognized hook event name in `settings.json` no longer causes the entire file to be ignored
  * Improved plugin hooks from plugins force-enabled by managed settings to run when `allowManagedHooksOnly` is set
  * Improved `/plugin` and `claude plugin update` to show a warning when the marketplace could not be refreshed, instead of silently reporting a stale version
  * Improved plan mode to hide the "Refine with Ultraplan" option when the user's org or auth setup can't reach Claude Code on the web
  * Improved beta tracing to honor `OTEL_LOG_USER_PROMPTS`, `OTEL_LOG_TOOL_DETAILS`, and `OTEL_LOG_TOOL_CONTENT`; sensitive span attributes are no longer emitted unless opted in
  * Improved SDK `query()` to clean up subprocess and temp files when consumers `break` from `for await` or use `await using`
  * Fixed a command injection vulnerability in the POSIX `which` fallback used by LSP binary detection
  * Fixed a memory leak where long sessions retained dozens of historical copies of the message list in the virtual scroller
  * Fixed `--resume`/`--continue` losing conversation context on large sessions when the loader anchored on a dead-end branch instead of the live conversation
  * Fixed `--resume` chain recovery bridging into an unrelated subagent conversation when a subagent message landed near a main-chain write gap
  * Fixed a crash on `--resume` when a persisted Edit/Write tool result was missing its `file_path`
  * Fixed a hardcoded 5-minute request timeout that aborted slow backends (local LLMs, extended thinking, slow gateways) regardless of `API_TIMEOUT_MS`
  * Fixed `permissions.deny` rules not overriding a PreToolUse hook's `permissionDecision: "ask"` — previously the hook could downgrade a deny into a prompt
  * Fixed `--setting-sources` without `user` causing background cleanup to ignore `cleanupPeriodDays` and delete conversation history older than 30 days
  * Fixed Bedrock SigV4 authentication failing with 403 when `ANTHROPIC_AUTH_TOKEN`, `apiKeyHelper`, or `ANTHROPIC_CUSTOM_HEADERS` set an Authorization header
  * Fixed `claude -w <name>` failing with "already exists" after a previous session's worktree cleanup left a stale directory
  * Fixed subagents not inheriting MCP tools from dynamically-injected servers
  * Fixed sub-agents running in isolated worktrees being denied Read/Edit access to files inside their own worktree
  * Fixed sandboxed Bash commands failing with `mktemp: No such file or directory` after a fresh boot
  * Fixed `claude mcp serve` tool calls failing with "Tool execution failed" in MCP clients that validate `outputSchema`
  * Fixed `RemoteTrigger` tool's `run` action sending an empty body and being rejected by the server
  * Fixed several `/resume` picker issues: narrow default view hiding sessions from other projects, unreachable preview on Windows Terminal, incorrect cwd in worktrees, session-not-found errors not surfacing in stderr, terminal title not being set, and resume hint overlapping the prompt input
  * Fixed Grep tool ENOENT when the embedded ripgrep binary path becomes stale (VS Code extension auto-update, macOS App Translocation); now falls back to system `rg` and self-heals mid-session
  * Fixed `/btw` writing a copy of the entire conversation to disk on every use
  * Fixed `/context` Free space and Messages breakdown disagreeing with the header percentage
  * Fixed several plugin issues: slash commands resolving to the wrong plugin with duplicate `name:` frontmatter, `/plugin update` failing with `ENAMETOOLONG`, Discover showing already-installed plugins, directory-source plugins loading from a stale version cache, and skills not honoring `context: fork` and `agent` frontmatter fields
  * Fixed the `/mcp` menu offering OAuth-specific actions for MCP servers configured with `headersHelper`; Reconnect is now offered instead to re-invoke the helper script
  * Fixed `ctrl+]`, `ctrl+\`, and `ctrl+^` keybindings not firing in terminals that send raw C0 control bytes (Terminal.app, default iTerm2, xterm)
  * Fixed `/login` OAuth URL rendering with padding that prevented clean mouse selection
  * Fixed rendering issues: flicker in non-fullscreen mode when content above the visible area changed, terminal scrollback being wiped during long sessions in non-fullscreen mode, and mouse-scroll escape sequences occasionally leaking into the prompt as text
  * Fixed crash when `settings.json` env values are numbers instead of strings
  * Fixed in-app settings writes (e.g. `/add-dir --remember`, `/config`) not refreshing the in-memory snapshot, preventing removed directories from being revoked mid-session
  * Fixed custom keybindings (`~/.claude/keybindings.json`) not loading on Bedrock, Vertex, and other third-party providers
  * Fixed `claude --continue -p` not correctly continuing sessions created by `-p` or the SDK
  * Fixed several Remote Control issues: worktrees removed on session crash, connection failures not persisting in the transcript, spurious "Disconnected" indicator in brief mode for local sessions, and `/remote-control` failing over SSH when only `CLAUDE_CODE_ORGANIZATION_UUID` is set
  * Fixed `/insights` sometimes omitting the report file link from its response
  * \[VSCode] Fixed the file attachment below the chat input not clearing when the last editor tab is closed
</Update>

<Update label="2.1.98" description="April 9, 2026">
  * Added interactive Google Vertex AI setup wizard accessible from the login screen when selecting "3rd-party platform", guiding you through GCP authentication, project and region configuration, credential verification, and model pinning
  * Added `CLAUDE_CODE_PERFORCE_MODE` env var: when set, Edit/Write/NotebookEdit fail on read-only files with a `p4 edit` hint instead of silently overwriting them
  * Added Monitor tool for streaming events from background scripts
  * Added subprocess sandboxing with PID namespace isolation on Linux when `CLAUDE_CODE_SUBPROCESS_ENV_SCRUB` is set, and `CLAUDE_CODE_SCRIPT_CAPS` env var to limit per-session script invocations
  * Added `--exclude-dynamic-system-prompt-sections` flag to print mode for improved cross-user prompt caching
  * Added `workspace.git_worktree` to the status line JSON input, set whenever the current directory is inside a linked git worktree
  * Added W3C `TRACEPARENT` env var to Bash tool subprocesses when OTEL tracing is enabled, so child-process spans correctly parent to Claude Code's trace tree
  * LSP: Claude Code now identifies itself to language servers via `clientInfo` in the initialize request
  * Fixed a Bash tool permission bypass where a backslash-escaped flag could be auto-allowed as read-only and lead to arbitrary code execution
  * Fixed compound Bash commands bypassing forced permission prompts for safety checks and explicit ask rules in auto and bypass-permissions modes
  * Fixed read-only commands with env-var prefixes not prompting unless the var is known-safe (`LANG`, `TZ`, `NO_COLOR`, etc.)
  * Fixed redirects to `/dev/tcp/...` or `/dev/udp/...` not prompting instead of auto-allowing
  * Fixed stalled streaming responses timing out instead of falling back to non-streaming mode
  * Fixed 429 retries burning all attempts in \~13s when the server returns a small `Retry-After` — exponential backoff now applies as a minimum
  * Fixed MCP OAuth `oauth.authServerMetadataUrl` config override not being honored on token refresh after restart, affecting ADFS and similar IdPs
  * Fixed capital letters being dropped to lowercase on xterm and VS Code integrated terminal when the kitty keyboard protocol is active
  * Fixed macOS text replacements deleting the trigger word instead of inserting the substitution
  * Fixed `--dangerously-skip-permissions` being silently downgraded to accept-edits mode after approving a write to a protected path via Bash
  * Fixed managed-settings allow rules remaining active after an admin removed them, until process restart
  * Fixed `permissions.additionalDirectories` changes not applying mid-session — removed directories lose access immediately and added ones work without restart
  * Fixed removing a directory from `additionalDirectories` revoking access to the same directory passed via `--add-dir`
  * Fixed `Bash(cmd:*)` and `Bash(git commit *)` wildcard permission rules failing to match commands with extra spaces or tabs
  * Fixed `Bash(...)` deny rules being downgraded to a prompt for piped commands that mix `cd` with other segments
  * Fixed false Bash permission prompts for `cut -d /`, `paste -d /`, `column -s /`, `awk '{print $1}' file`, and filenames containing `%`
  * Fixed permission rules with names matching JavaScript prototype properties (e.g. `toString`) causing `settings.json` to be silently ignored
  * Fixed agent team members not inheriting the leader's permission mode when using `--dangerously-skip-permissions`
  * Fixed a crash in fullscreen mode when hovering over MCP tool results
  * Fixed copying wrapped URLs in fullscreen mode inserting spaces at line breaks
  * Fixed file-edit diffs disappearing from the UI on `--resume` when the edited file was larger than 10KB
  * Fixed several `/resume` picker issues: `--resume <name>` opening uneditable, filter reload wiping search state, empty list swallowing arrow keys, cross-project staleness, and transient task-status text replacing conversation summaries
  * Fixed `/export` not honoring absolute paths and `~`, and silently rewriting user-supplied extensions to `.txt`
  * Fixed `/effort max` being denied for unknown or future model IDs
  * Fixed slash command picker breaking when a plugin's frontmatter `name` is a YAML boolean keyword
  * Fixed rate-limit upsell text being hidden after message remounts
  * Fixed MCP tools with `_meta["anthropic/maxResultSizeChars"]` not bypassing the token-based persist layer
  * Fixed voice mode leaking dozens of space characters into the input when re-holding the push-to-talk key while the previous transcript is still processing
  * Fixed `DISABLE_AUTOUPDATER` not fully suppressing the npm registry version check and symlink modification on npm-based installs
  * Fixed a memory leak where Remote Control permission handler entries were retained for the lifetime of the session
  * Fixed background subagents that fail with an error not reporting partial progress to the parent agent
  * Fixed prompt-type Stop/SubagentStop hooks failing on long sessions, and hook evaluator API errors showing "JSON validation failed" instead of the real message
  * Fixed feedback survey rendering when dismissed
  * Fixed Bash `grep -f FILE` / `rg -f FILE` not prompting when reading a pattern file outside the working directory
  * Fixed stale subagent worktree cleanup removing worktrees that contain untracked files
  * Fixed `sandbox.network.allowMachLookup` not taking effect on macOS
  * Improved `/resume` filter hint labels and added project/worktree/branch names in the filter indicator
  * Improved footer indicators (Focus, notifications) to stay on the mode-indicator row instead of wrapping at narrow terminal widths
  * Improved `/agents` with a tabbed layout: a Running tab shows live subagents, and the Library tab adds Run agent and View running instance actions
  * Improved `/reload-plugins` to pick up plugin-provided skills without requiring a restart
  * Improved Accept Edits mode to auto-approve filesystem commands prefixed with safe env vars or process wrappers
  * Improved Vim mode: `j`/`k` in NORMAL mode now navigate history and select the footer pill at the input boundary
  * Improved hook errors in the transcript to include the first line of stderr for self-diagnosis without `--debug`
  * Improved OTEL tracing: interaction spans now correctly wrap full turns under concurrent SDK calls, and headless turns end spans per-turn
  * Improved transcript entries to carry final token usage instead of streaming placeholders
  * Updated the `/claude-api` skill to cover Managed Agents alongside Claude API
  * \[VSCode] Fixed false-positive "requires git-bash" error on Windows when `CLAUDE_CODE_GIT_BASH_PATH` is set or Git is installed at a default location
  * Fixed `CLAUDE_CODE_MAX_CONTEXT_TOKENS` to honor `DISABLE_COMPACT` when it is set.
  * Dropped `/compact` hints when `DISABLE_COMPACT` is set.
</Update>

<Update label="2.1.97" description="April 8, 2026">
  * Added focus view toggle (`Ctrl+O`) in `NO_FLICKER` mode showing prompt, one-line tool summary with edit diffstats, and final response
  * Added `refreshInterval` status line setting to re-run the status line command every N seconds
  * Added `workspace.git_worktree` to the status line JSON input, set when the current directory is inside a linked git worktree
  * Added `● N running` indicator in `/agents` next to agent types with live subagent instances
  * Added syntax highlighting for Cedar policy files (`.cedar`, `.cedarpolicy`)
  * Fixed `--dangerously-skip-permissions` being silently downgraded to accept-edits mode after approving a write to a protected path
  * Fixed and hardened Bash tool permissions, tightening checks around env-var prefixes and network redirects, and reducing false prompts on common commands
  * Fixed permission rules with names matching JavaScript prototype properties (e.g. `toString`) causing `settings.json` to be silently ignored
  * Fixed managed-settings allow rules remaining active after an admin removed them until process restart
  * Fixed `permissions.additionalDirectories` changes in settings not applying mid-session
  * Fixed removing a directory from `settings.permissions.additionalDirectories` revoking access to the same directory passed via `--add-dir`
  * Fixed MCP HTTP/SSE connections accumulating \~50 MB/hr of unreleased buffers when servers reconnect
  * Fixed MCP OAuth `oauth.authServerMetadataUrl` not being honored on token refresh after restart, fixing ADFS and similar IdPs
  * Fixed 429 retries burning all attempts in \~13 seconds when the server returns a small `Retry-After` — exponential backoff now applies as a minimum
  * Fixed rate-limit upgrade options disappearing after context compaction
  * Fixed several `/resume` picker issues: `--resume <name>` opening uneditable, Ctrl+A reload wiping search, empty list swallowing navigation, task-status text replacing conversation summary, and cross-project staleness
  * Fixed file-edit diffs disappearing on `--resume` when the edited file was larger than 10KB
  * Fixed `--resume` cache misses and lost mid-turn input from attachment messages not being saved to the transcript
  * Fixed messages typed while Claude is working not being persisted to the transcript
  * Fixed prompt-type `Stop`/`SubagentStop` hooks failing on long sessions, and hook evaluator API errors displaying "JSON validation failed" instead of the actual message
  * Fixed subagents with worktree isolation or `cwd:` override leaking their working directory back to the parent session's Bash tool
  * Fixed compaction writing duplicate multi-MB subagent transcript files on prompt-too-long retries
  * Fixed `claude plugin update` reporting "already at the latest version" for git-based marketplace plugins when the remote had newer commits
  * Fixed slash command picker breaking when a plugin's frontmatter `name` is a YAML boolean keyword
  * Fixed copying wrapped URLs in `NO_FLICKER` mode inserting spaces at line breaks
  * Fixed scroll rendering artifacts in `NO_FLICKER` mode when running inside zellij
  * Fixed a crash in `NO_FLICKER` mode when hovering over MCP tool results
  * Fixed a `NO_FLICKER` mode memory leak where API retries left stale streaming state
  * Fixed slow mouse-wheel scrolling in `NO_FLICKER` mode on Windows Terminal
  * Fixed custom status line not displaying in `NO_FLICKER` mode on terminals shorter than 24 rows
  * Fixed Shift+Enter and Alt/Cmd+arrow shortcuts not working in Warp with `NO_FLICKER` mode
  * Fixed Korean/Japanese/Unicode text becoming garbled when copied in no-flicker mode on Windows
  * Fixed Bedrock SigV4 authentication failing when `AWS_BEARER_TOKEN_BEDROCK` or `ANTHROPIC_BEDROCK_BASE_URL` are set to empty strings (as GitHub Actions does for unset inputs)
  * Improved Accept Edits mode to auto-approve filesystem commands prefixed with safe env vars or process wrappers (e.g. `LANG=C rm foo`, `timeout 5 mkdir out`)
  * Improved auto mode and bypass-permissions mode to auto-approve sandbox network access prompts
  * Improved sandbox: `sandbox.network.allowMachLookup` now takes effect on macOS
  * Improved image handling: pasted and attached images are now compressed to the same token budget as images read via the Read tool
  * Improved slash command and `@`-mention completion to trigger after CJK sentence punctuation, so Japanese/Chinese input no longer requires a space before `/` or `@`
  * Improved Bridge sessions to show the local git repo, branch, and working directory on the claude.ai session card
  * Improved footer layout: indicators (Focus, notifications) now stay on the mode-indicator row instead of wrapping below
  * Improved context-low warning to show as a transient footer notification instead of a persistent row
  * Improved markdown blockquotes to show a continuous left bar across wrapped lines
  * Improved session transcript size by skipping empty hook entries and capping stored pre-edit file copies
  * Improved transcript accuracy: per-block entries now carry the final token usage instead of the streaming placeholder
  * Improved Bash tool OTEL tracing: subprocesses now inherit a W3C `TRACEPARENT` env var when tracing is enabled
  * Updated `/claude-api` skill to cover Managed Agents alongside the Claude API
</Update>

<Update label="2.1.96" description="April 8, 2026">
  * Fixed Bedrock requests failing with `403 "Authorization header is missing"` when using `AWS_BEARER_TOKEN_BEDROCK` or `CLAUDE_CODE_SKIP_BEDROCK_AUTH` (regression in 2.1.94)
</Update>

<Update label="2.1.94" description="April 7, 2026">
  * Added support for Amazon Bedrock powered by Mantle, set `CLAUDE_CODE_USE_MANTLE=1`
  * Changed default effort level from medium to high for API-key, Bedrock/Vertex/Foundry, Team, and Enterprise users (control this with `/effort`)
  * Added compact `Slacked #channel` header with a clickable channel link for Slack MCP send-message tool calls
  * Added `keep-coding-instructions` frontmatter field support for plugin output styles
  * Added `hookSpecificOutput.sessionTitle` to `UserPromptSubmit` hooks for setting the session title
  * Plugin skills declared via `"skills": ["./"]` now use the skill's frontmatter `name` for the invocation name instead of the directory basename, giving a stable name across install methods
  * Fixed agents appearing stuck after a 429 rate-limit response with a long Retry-After header — the error now surfaces immediately instead of silently waiting
  * Fixed Console login on macOS silently failing with "Not logged in" when the login keychain is locked or its password is out of sync — the error is now surfaced and `claude doctor` diagnoses the fix
  * Fixed plugin skill hooks defined in YAML frontmatter being silently ignored
  * Fixed plugin hooks failing with "No such file or directory" when `CLAUDE_PLUGIN_ROOT` was not set
  * Fixed `${CLAUDE_PLUGIN_ROOT}` resolving to the marketplace source directory instead of the installed cache for local-marketplace plugins on startup
  * Fixed scrollback showing the same diff repeated and blank pages in long-running sessions
  * Fixed multiline user prompts in the transcript indenting wrapped lines under the `❯` caret instead of under the text
  * Fixed Shift+Space inserting the literal word "space" instead of a space character in search inputs
  * Fixed hyperlinks opening two browser tabs when clicked inside tmux running in an xterm.js-based terminal (VS Code, Hyper, Tabby)
  * Fixed an alt-screen rendering bug where content height changes mid-scroll could leave compounding ghost lines
  * Fixed `FORCE_HYPERLINK` environment variable being ignored when set via `settings.json` `env`
  * Fixed native terminal cursor not tracking the selected tab in dialogs, so screen readers and magnifiers can follow tab navigation
  * Fixed Bedrock invocation of Sonnet 3.5 v2 by using the `us.` inference profile ID
  * Fixed SDK/print mode not preserving the partial assistant response in conversation history when interrupted mid-stream
  * Improved `--resume` to resume sessions from other worktrees of the same repo directly instead of printing a `cd` command
  * Fixed CJK and other multibyte text being corrupted with U+FFFD in stream-json input/output when chunk boundaries split a UTF-8 sequence
  * \[VSCode] Reduced cold-open subprocess work on starting a session
  * \[VSCode] Fixed dropdown menus selecting the wrong item when the mouse was over the list while typing or using arrow keys
  * \[VSCode] Added a warning banner when `settings.json` files fail to parse, so users know their permission rules are not being applied
</Update>

<Update label="2.1.92" description="April 4, 2026">
  * Added `forceRemoteSettingsRefresh` policy setting: when set, the CLI blocks startup until remote managed settings are freshly fetched, and exits if the fetch fails (fail-closed)
  * Added interactive Bedrock setup wizard accessible from the login screen when selecting "3rd-party platform" — guides you through AWS authentication, region configuration, credential verification, and model pinning
  * Added per-model and cache-hit breakdown to `/cost` for subscription users
  * `/release-notes` is now an interactive version picker
  * Remote Control session names now use your hostname as the default prefix (e.g. `myhost-graceful-unicorn`), overridable with `--remote-control-session-name-prefix`
  * Pro users now see a footer hint when returning to a session after the prompt cache has expired, showing roughly how many tokens the next turn will send uncached
  * Fixed subagent spawning permanently failing with "Could not determine pane count" after tmux windows are killed or renumbered during a long-running session
  * Fixed prompt-type Stop hooks incorrectly failing when the small fast model returns `ok:false`, and restored `preventContinuation:true` semantics for non-Stop prompt-type hooks
  * Fixed tool input validation failures when streaming emits array/object fields as JSON-encoded strings
  * Fixed an API 400 error that could occur when extended thinking produced a whitespace-only text block alongside real content
  * Fixed accidental feedback survey submissions from auto-pilot keypresses and consecutive-prompt digit collisions
  * Fixed misleading "esc to interrupt" hint appearing alongside "esc to clear" when a text selection exists in fullscreen mode during processing
  * Fixed Homebrew install update prompts to use the cask's release channel (`claude-code` → stable, `claude-code@latest` → latest)
  * Fixed `ctrl+e` jumping to the end of the next line when already at end of line in multiline prompts
  * Fixed an issue where the same message could appear at two positions when scrolling up in fullscreen mode (iTerm2, Ghostty, and other terminals with DEC 2026 support)
  * Fixed idle-return "/clear to save X tokens" hint showing cumulative session tokens instead of current context size
  * Fixed plugin MCP servers stuck "connecting" on session start when they duplicate a claude.ai connector that is unauthenticated
  * Improved Write tool diff computation speed for large files (60% faster on files with tabs/`&`/`$`)
  * Removed `/tag` command
  * Removed `/vim` command (toggle vim mode via `/config` → Editor mode)
  * Linux sandbox now ships the `apply-seccomp` helper in both npm and native builds, restoring unix-socket blocking for sandboxed commands
</Update>

<Update label="2.1.91" description="April 2, 2026">
  * Added MCP tool result persistence override via `_meta["anthropic/maxResultSizeChars"]` annotation (up to 500K), allowing larger results like DB schemas to pass through without truncation
  * Added `disableSkillShellExecution` setting to disable inline shell execution in skills, custom slash commands, and plugin commands
  * Added support for multi-line prompts in `claude-cli://open?q=` deep links (encoded newlines `%0A` no longer rejected)
  * Plugins can now ship executables under `bin/` and invoke them as bare commands from the Bash tool
  * Fixed transcript chain breaks on `--resume` that could lose conversation history when async transcript writes fail silently
  * Fixed `cmd+delete` not deleting to start of line on iTerm2, kitty, WezTerm, Ghostty, and Windows Terminal
  * Fixed plan mode in remote sessions losing track of the plan file after a container restart, which caused permission prompts on plan edits and an empty plan-approval modal
  * Fixed JSON schema validation for `permissions.defaultMode: "auto"` in settings.json
  * Fixed Windows version cleanup not protecting the active version's rollback copy
  * `/feedback` now explains why it's unavailable instead of disappearing from the slash menu
  * Improved `/claude-api` skill guidance for agent design patterns including tool surface decisions, context management, and caching strategy
  * Improved performance: faster `stripAnsi` on Bun by routing through `Bun.stripANSI`
  * Edit tool now uses shorter `old_string` anchors, reducing output tokens
</Update>

<Update label="2.1.90" description="April 1, 2026">
  * Added `/powerup` — interactive lessons teaching Claude Code features with animated demos
  * Added `CLAUDE_CODE_PLUGIN_KEEP_MARKETPLACE_ON_FAILURE` env var to keep the existing marketplace cache when `git pull` fails, useful in offline environments
  * Added `.husky` to protected directories (acceptEdits mode)
  * Fixed an infinite loop where the rate-limit options dialog would repeatedly auto-open after hitting your usage limit, eventually crashing the session
  * Fixed `--resume` causing a full prompt-cache miss on the first request for users with deferred tools, MCP servers, or custom agents (regression since v2.1.69)
  * Fixed `Edit`/`Write` failing with "File content has changed" when a PostToolUse format-on-save hook rewrites the file between consecutive edits
  * Fixed `PreToolUse` hooks that emit JSON to stdout and exit with code 2 not correctly blocking the tool call
  * Fixed collapsed search/read summary badge appearing multiple times in fullscreen scrollback when a CLAUDE.md file auto-loads during a tool call
  * Fixed auto mode not respecting explicit user boundaries ("don't push", "wait for X before Y") even when the action would otherwise be allowed
  * Fixed click-to-expand hover text being nearly invisible on light terminal themes
  * Fixed UI crash when malformed tool input reached the permission dialog
  * Fixed headers disappearing when scrolling `/model`, `/config`, and other selection screens
  * Hardened PowerShell tool permission checks: fixed trailing `&` background job bypass, `-ErrorAction Break` debugger hang, archive-extraction TOCTOU, and parse-fail fallback deny-rule degradation
  * Improved performance: eliminated per-turn JSON.stringify of MCP tool schemas on cache-key lookup
  * Improved performance: SSE transport now handles large streamed frames in linear time (was quadratic)
  * Improved performance: SDK sessions with long conversations no longer slow down quadratically on transcript writes
  * Improved `/resume` all-projects view to load project sessions in parallel, improving load times for users with many projects
  * Changed `--resume` picker to no longer show sessions created by `claude -p` or SDK invocations
  * Removed `Get-DnsClientCache` and `ipconfig /displaydns` from auto-allow (DNS cache privacy)
</Update>

<Update label="2.1.89" description="April 1, 2026">
  * Added `"defer"` permission decision to `PreToolUse` hooks — headless sessions can pause at a tool call and resume with `-p --resume` to have the hook re-evaluate
  * Added `CLAUDE_CODE_NO_FLICKER=1` environment variable to opt into flicker-free alt-screen rendering with virtualized scrollback
  * Added `PermissionDenied` hook that fires after auto mode classifier denials — return `{retry: true}` to tell the model it can retry
  * Added named subagents to `@` mention typeahead suggestions
  * Added `MCP_CONNECTION_NONBLOCKING=true` for `-p` mode to skip the MCP connection wait entirely, and bounded `--mcp-config` server connections at 5s instead of blocking on the slowest server
  * Auto mode: denied commands now show a notification and appear in `/permissions` → Recent tab where you can retry with `r`
  * Fixed `Edit(//path/**)` and `Read(//path/**)` allow rules to check the resolved symlink target, not just the requested path
  * Fixed voice push-to-talk not activating for some modifier-combo bindings, and voice mode on Windows failing with "WebSocket upgrade rejected with HTTP 101"
  * Fixed Edit/Write tools doubling CRLF on Windows and stripping Markdown hard line breaks (two trailing spaces)
  * Fixed `StructuredOutput` schema cache bug causing \~50% failure rate when using multiple schemas
  * Fixed memory leak where large JSON inputs were retained as LRU cache keys in long-running sessions
  * Fixed a crash when removing a message from very large session files (over 50MB)
  * Fixed LSP server zombie state after crash — server now restarts on next request instead of failing until session restart
  * Fixed prompt history entries containing CJK or emoji being silently dropped when they fall on a 4KB boundary in `~/.claude/history.jsonl`
  * Fixed `/stats` undercounting tokens by excluding subagent usage, and losing historical data beyond 30 days when the stats cache format changes
  * Fixed `-p --resume` hangs when the deferred tool input exceeds 64KB or no deferred marker exists, and `-p --continue` not resuming deferred tools
  * Fixed `claude-cli://` deep links not opening on macOS
  * Fixed MCP tool errors truncating to only the first content block when the server returns multi-element error content
  * Fixed skill reminders and other system context being dropped when sending messages with images via the SDK
  * Fixed PreToolUse/PostToolUse hooks to receive `file_path` as an absolute path for Write/Edit/Read tools, matching the documented behavior
  * Fixed autocompact thrash loop — now detects when context refills to the limit immediately after compacting three times in a row and stops with an actionable error instead of burning API calls
  * Fixed prompt cache misses in long sessions caused by tool schema bytes changing mid-session
  * Fixed nested CLAUDE.md files being re-injected dozens of times in long sessions that read many files
  * Fixed `--resume` crash when transcript contains a tool result from an older CLI version or interrupted write
  * Fixed misleading "Rate limit reached" message when the API returned an entitlement error — now shows the actual error with actionable hints
  * Fixed hooks `if` condition filtering not matching compound commands (`ls && git push`) or commands with env-var prefixes (`FOO=bar git push`)
  * Fixed collapsed search/read group badges duplicating in terminal scrollback during heavy parallel tool use
  * Fixed notification `invalidates` not clearing the currently-displayed notification immediately
  * Fixed prompt briefly disappearing after submit when background messages arrived during processing
  * Fixed Devanagari and other combining-mark text being truncated in assistant output
  * Fixed rendering artifacts on main-screen terminals after layout shifts
  * Fixed voice mode failing to request microphone permission on macOS Apple Silicon
  * Fixed Shift+Enter submitting instead of inserting a newline on Windows Terminal Preview 1.25
  * Fixed periodic UI jitter during streaming in iTerm2 when running inside tmux
  * Fixed PowerShell tool incorrectly reporting failures when commands like `git push` wrote progress to stderr on Windows PowerShell 5.1
  * Fixed a potential out-of-memory crash when the Edit tool was used on very large files (>1 GiB)
  * Improved collapsed tool summary to show "Listed N directories" for `ls`/`tree`/`du` instead of "Read N files"
  * Improved Bash tool to warn when a formatter/linter command modifies files you have previously read, preventing stale-edit errors
  * Improved `@`-mention typeahead to rank source files above MCP resources with similar names
  * Improved PowerShell tool prompt with version-appropriate syntax guidance (5.1 vs 7+)
  * Changed `Edit` to work on files viewed via `Bash` with `sed -n` or `cat`, without requiring a separate `Read` call first
  * Changed hook output over 50K characters to be saved to disk with a file path + preview instead of being injected directly into context
  * Changed `cleanupPeriodDays: 0` in settings.json to be rejected with a validation error — it previously silently disabled transcript persistence
  * Changed thinking summaries to no longer be generated by default in interactive sessions — set `showThinkingSummaries: true` in settings.json to restore
  * Documented `TaskCreated` hook event and its blocking behavior
  * Preserved task notifications when backgrounding a running command with Ctrl+B
  * PowerShell tool on Windows: external-command arguments containing both a double-quote and whitespace now prompt instead of auto-allowing (PS 5.1 argument-splitting hardening)
  * `/env` now applies to PowerShell tool commands (previously only affected Bash)
  * `/usage` now hides redundant "Current week (Sonnet only)" bar for Pro and Enterprise plans
  * Image paste no longer inserts a trailing space
  * Pasting `!command` into an empty prompt now enters bash mode, matching typed `!` behavior
  * `/buddy` is here for April 1st — hatch a small creature that watches you code
</Update>

<Update label="2.1.87" description="March 29, 2026">
  * Fixed messages in Cowork Dispatch not getting delivered
</Update>

<Update label="2.1.86" description="March 27, 2026">
  * Added `X-Claude-Code-Session-Id` header to API requests so proxies can aggregate requests by session without parsing the body
  * Added `.jj` and `.sl` to VCS directory exclusion lists so Grep and file autocomplete don't descend into Jujutsu or Sapling metadata
  * Fixed `--resume` failing with "tool\_use ids were found without tool\_result blocks" on sessions created before v2.1.85
  * Fixed Write/Edit/Read failing on files outside the project root (e.g., `~/.claude/CLAUDE.md`) when conditional skills or rules are configured
  * Fixed unnecessary config disk writes on every skill invocation that could cause performance issues and config corruption on Windows
  * Fixed potential out-of-memory crash when using `/feedback` on very long sessions with large transcript files
  * Fixed `--bare` mode dropping MCP tools in interactive sessions and silently discarding messages enqueued mid-turn
  * Fixed the `c` shortcut copying only \~20 characters of the OAuth login URL instead of the full URL
  * Fixed masked input (e.g., OAuth code paste) leaking the start of the token when wrapping across multiple lines on narrow terminals
  * Fixed official marketplace plugin scripts failing with "Permission denied" on macOS/Linux since v2.1.83
  * Fixed statusline showing another session's model when running multiple Claude Code instances and using `/model` in one of them
  * Fixed scroll not following new messages after wheel scroll or click-to-select at the bottom of a long conversation
  * Fixed `/plugin` uninstall dialog: pressing `n` now correctly uninstalls the plugin while preserving its data directory
  * Fixed a regression where pressing Enter after clicking could leave the transcript blank until the response arrived
  * Fixed `ultrathink` hint lingering after deleting the keyword
  * Fixed memory growth in long sessions from markdown/highlight render caches retaining full content strings
  * Reduced startup event-loop stalls when many claude.ai MCP connectors are configured (macOS keychain cache extended from 5s to 30s)
  * Reduced token overhead when mentioning files with `@` — raw string content no longer JSON-escaped
  * Improved prompt cache hit rate for Bedrock, Vertex, and Foundry users by removing dynamic content from tool descriptions
  * Memory filenames in the "Saved N memories" notice now highlight on hover and open on click
  * Skill descriptions in the `/skills` listing are now capped at 250 characters to reduce context usage
  * Changed `/skills` menu to sort alphabetically for easier scanning
  * Auto mode now shows "unavailable for your plan" when disabled by plan restrictions (was "temporarily unavailable")
  * \[VSCode] Fixed extension incorrectly showing "Not responding" during long-running operations
  * \[VSCode] Fixed extension defaulting Max plan users to Sonnet after the OAuth token refreshes (8 hours after login)
  * Read tool now uses compact line-number format and deduplicates unchanged re-reads, reducing token usage
</Update>

<Update label="2.1.85" description="March 26, 2026">
  * Added `CLAUDE_CODE_MCP_SERVER_NAME` and `CLAUDE_CODE_MCP_SERVER_URL` environment variables to MCP `headersHelper` scripts, allowing one helper to serve multiple servers
  * Added conditional `if` field for hooks using permission rule syntax (e.g., `Bash(git *)`) to filter when they run, reducing process spawning overhead
  * Added timestamp markers in transcripts when scheduled tasks (`/loop`, `CronCreate`) fire
  * Added trailing space after `[Image #N]` placeholder when pasting images
  * Deep link queries (`claude-cli://open?q=…`) now support up to 5,000 characters, with a "scroll to review" warning for long pre-filled prompts
  * MCP OAuth now follows RFC 9728 Protected Resource Metadata discovery to find the authorization server
  * Plugins blocked by organization policy (`managed-settings.json`) can no longer be installed or enabled, and are hidden from marketplace views
  * PreToolUse hooks can now satisfy `AskUserQuestion` by returning `updatedInput` alongside `permissionDecision: "allow"`, enabling headless integrations that collect answers via their own UI
  * `tool_parameters` in OpenTelemetry tool\_result events are now gated behind `OTEL_LOG_TOOL_DETAILS=1`
  * Fixed `/compact` failing with "context exceeded" when the conversation has grown too large for the compact request itself to fit
  * Fixed `/plugin enable` and `/plugin disable` failing when a plugin's install location differs from where it's declared in settings
  * Fixed `--worktree` exiting with an error in non-git repositories before the `WorktreeCreate` hook could run
  * Fixed `deniedMcpServers` setting not blocking claude.ai MCP servers
  * Fixed `switch_display` in the computer-use tool returning "not available in this session" on multi-monitor setups
  * Fixed crash when `OTEL_LOGS_EXPORTER`, `OTEL_METRICS_EXPORTER`, or `OTEL_TRACES_EXPORTER` is set to `none`
  * Fixed diff syntax highlighting not working in non-native builds
  * Fixed MCP step-up authorization failing when a refresh token exists — servers requesting elevated scopes via `403 insufficient_scope` now correctly trigger the re-authorization flow
  * Fixed memory leak in remote sessions when a streaming response is interrupted
  * Fixed persistent ECONNRESET errors during edge connection churn by using a fresh TCP connection on retry
  * Fixed prompts getting stuck in the queue after running certain slash commands, with up-arrow unable to retrieve them
  * Fixed Python Agent SDK: `type:'sdk'` MCP servers passed via `--mcp-config` are no longer dropped during startup
  * Fixed raw key sequences appearing in the prompt when running over SSH or in the VS Code integrated terminal
  * Fixed Remote Control session status staying stuck on "Requires Action" after a permission is resolved
  * Fixed shift+enter and meta+enter being intercepted by typeahead suggestions instead of inserting newlines
  * Fixed stale content bleeding through when scrolling up during streaming
  * Fixed terminal left in enhanced keyboard mode after exit in Ghostty, Kitty, WezTerm, and other terminals supporting the Kitty keyboard protocol — Ctrl+C and Ctrl+D now work correctly after quitting
  * Improved @-mention file autocomplete performance on large repositories
  * Improved PowerShell dangerous command detection
  * Improved scroll performance with large transcripts by replacing WASM yoga-layout with a pure TypeScript implementation
  * Reduced UI stutter when compaction triggers on large sessions
</Update>

<Update label="2.1.84" description="March 26, 2026">
  * Added PowerShell tool for Windows as an opt-in preview. Learn more at [https://code.claude.com/docs/en/tools-reference#powershell-tool](../04-API-Reference/tools-reference-claude-code-docs-d21fdcb4c5.md#powershell-tool)
  * Added `ANTHROPIC_DEFAULT_{OPUS,SONNET,HAIKU}_MODEL_SUPPORTS` env vars to override effort/thinking capability detection for pinned default models for 3p (Bedrock, Vertex, Foundry), and `_MODEL_NAME`/`_DESCRIPTION` to customize the `/model` picker label
  * Added `CLAUDE_STREAM_IDLE_TIMEOUT_MS` env var to configure the streaming idle watchdog threshold (default 90s)
  * Added `TaskCreated` hook that fires when a task is created via `TaskCreate`
  * Added `WorktreeCreate` hook support for `type: "http"` — return the created worktree path via `hookSpecificOutput.worktreePath` in the response JSON
  * Added `allowedChannelPlugins` managed setting for team/enterprise admins to define a channel plugin allowlist
  * Added `x-client-request-id` header to API requests for debugging timeouts
  * Added idle-return prompt that nudges users returning after 75+ minutes to `/clear`, reducing unnecessary token re-caching on stale sessions
  * Deep links (`claude-cli://`) now open in your preferred terminal instead of whichever terminal happens to be first in the detection list
  * Rules and skills `paths:` frontmatter now accepts a YAML list of globs
  * MCP tool descriptions and server instructions are now capped at 2KB to prevent OpenAPI-generated servers from bloating context
  * MCP servers configured both locally and via claude.ai connectors are now deduplicated — the local config wins
  * Background bash tasks that appear stuck on an interactive prompt now surface a notification after \~45 seconds
  * Token counts ≥1M now display as "1.5m" instead of "1512.6k"
  * Global system-prompt caching now works when `ToolSearch` is enabled, including for users with MCP tools configured
  * Fixed voice push-to-talk: holding the voice key no longer leaks characters into the text input, and transcripts now insert at the correct position
  * Fixed up/down arrow keys being unresponsive when a footer item is focused
  * Fixed `Ctrl+U` (kill-to-line-start) being a no-op at line boundaries in multiline input, so repeated `Ctrl+U` now clears across lines
  * Fixed null-unbinding a default chord binding (e.g. `"ctrl+x ctrl+k": null`) still entering chord-wait mode instead of freeing the prefix key
  * Fixed mouse events inserting literal "mouse" text into transcript search input
  * Fixed workflow subagents failing with API 400 when the outer session uses `--json-schema` and the subagent also specifies a schema
  * Fixed missing background color behind certain emoji in user message bubbles on some terminals
  * Fixed the "allow Claude to edit its own settings for this session" permission option not sticking for users with `Edit(.claude)` allow rules
  * Fixed a hang when generating attachment snippets for large edited files
  * Fixed MCP tool/resource cache leak on server reconnect
  * Fixed a startup performance issue where partial clone repositories (Scalar/GVFS) triggered mass blob downloads
  * Fixed native terminal cursor not tracking the text input caret, so IME composition (CJK input) now renders inline and screen readers can follow the input position
  * Fixed spurious "Not logged in" errors on macOS caused by transient keychain read failures
  * Fixed cold-start race where core tools could be deferred without their bypass active, causing Edit/Write to fail with InputValidationError on typed parameters
  * Improved detection for dangerous removals of Windows drive roots (`C:\`, `C:\Windows`, etc.)
  * Improved interactive startup by \~30ms by running `setup()` in parallel with slash command and agent loading
  * Improved startup for `claude "prompt"` with MCP servers — the REPL now renders immediately instead of blocking until all servers connect
  * Improved Remote Control to show a specific reason when blocked instead of a generic "not yet enabled" message
  * Improved p90 prompt cache rate
  * Reduced scroll-to-top resets in long sessions by making the message window immune to compaction and grouping changes
  * Reduced terminal flickering when animated tool progress scrolls above the viewport
  * Changed issue/PR references to only become clickable links when written as `owner/repo#123` — bare `#123` is no longer auto-linked
  * Slash commands unavailable for the current auth setup (`/voice`, `/mobile`, `/chrome`, `/upgrade`, etc.) are now hidden instead of shown
  * \[VSCode] Added rate limit warning banner with usage percentage and reset time
  * Stats screenshot (Ctrl+S in /stats) now works in all builds and is 16× faster
</Update>

<Update label="2.1.83" description="March 25, 2026">
  * Added `managed-settings.d/` drop-in directory alongside `managed-settings.json`, letting separate teams deploy independent policy fragments that merge alphabetically
  * Added `CwdChanged` and `FileChanged` hook events for reactive environment management (e.g., direnv)
  * Added `sandbox.failIfUnavailable` setting to exit with an error when sandbox is enabled but cannot start, instead of running unsandboxed
  * Added `disableDeepLinkRegistration` setting to prevent `claude-cli://` protocol handler registration
  * Added `CLAUDE_CODE_SUBPROCESS_ENV_SCRUB=1` to strip Anthropic and cloud provider credentials from subprocess environments (Bash tool, hooks, MCP stdio servers)
  * Added transcript search — press `/` in transcript mode (`Ctrl+O`) to search, `n`/`N` to step through matches
  * Added `Ctrl+X Ctrl+E` as an alias for opening the external editor (readline-native binding; `Ctrl+G` still works)
  * Pasted images now insert an `[Image #N]` chip at the cursor so you can reference them positionally in your prompt
  * Agents can now declare `initialPrompt` in frontmatter to auto-submit a first turn
  * `chat:killAgents` and `chat:fastMode` are now rebindable via `~/.claude/keybindings.json`
  * Fixed mouse tracking escape sequences leaking to shell prompt after exit
  * Fixed Claude Code hanging on exit on macOS
  * Fixed screen flashing blank after being idle for a few seconds
  * Fixed a hang when diffing very large files with few common lines — diffs now time out after 5 seconds and fall back gracefully
  * Fixed a 1–8 second UI freeze on startup when voice input was enabled, caused by eagerly loading the native audio module
  * Fixed a startup regression where Claude Code would wait \~3s for claude.ai MCP config fetch before proceeding
  * Fixed `--mcp-config` CLI flag bypassing `allowedMcpServers`/`deniedMcpServers` managed policy enforcement
  * Fixed claude.ai MCP connectors (Slack, Gmail, etc.) not being available in single-turn `--print` mode
  * Fixed `caffeinate` process not properly terminating when Claude Code exits, preventing Mac from sleeping
  * Fixed bash mode not activating when tab-accepting `!`-prefixed command suggestions
  * Fixed stale slash command selection showing wrong highlighted command after navigating suggestions
  * Fixed `/config` menu showing both the search cursor and list selection at the same time
  * Fixed background subagents becoming invisible after context compaction, which could cause duplicate agents to be spawned
  * Fixed background agent tasks staying stuck in "running" state when git or API calls hang during cleanup
  * Fixed `--channels` showing "Channels are not currently available" on first launch after upgrade
  * Fixed uninstalled plugin hooks continuing to fire until the next session
  * Fixed queued commands flickering during streaming responses
  * Fixed slash commands being sent to the model as text when submitted while a message is processing
  * Fixed scrollback jumping when collapsed read/search groups finish after scrolling offscreen
  * Fixed scrollback jumping to top when the model starts or stops thinking
  * Fixed SDK session history loss on resume caused by hook progress/attachment messages forking the parentUuid chain
  * Fixed copy-on-select not firing when you release the mouse outside the terminal window
  * Fixed ghost characters appearing in height-constrained lists when items overflow
  * Fixed `Ctrl+B` interfering with readline backward-char at an idle prompt — it now only fires when a foreground task can be backgrounded
  * Fixed tool result files never being cleaned up, ignoring the `cleanupPeriodDays` setting
  * Fixed space key being swallowed for up to 3 seconds after releasing voice hold-to-talk
  * Fixed ALSA library errors corrupting the terminal UI when using voice mode on Linux without audio hardware (Docker, headless, WSL1)
  * Fixed voice mode SoX detection on Termux/Android where spawning `which` is kernel-restricted
  * Fixed Remote Control sessions showing as Idle in the web session list while actively running
  * Fixed footer navigation selecting an invisible Remote Control pill in config-driven mode
  * Fixed memory leak in remote sessions where tool use IDs accumulate indefinitely
  * Improved Bedrock SDK cold-start latency by overlapping profile fetch with other boot work
  * Improved `--resume` memory usage and startup latency on large sessions
  * Improved plugin startup — commands, skills, and agents now load from disk cache without re-fetching
  * Improved Remote Control session titles: AI-generated titles now appear within seconds of the first message
  * Improved `WebFetch` to identify as `Claude-User` so site operators can recognize and allowlist Claude Code traffic via `robots.txt`
  * Reduced `WebFetch` peak memory usage for large pages
  * Reduced scrollback resets in long sessions from once per turn to once per \~50 messages
  * Faster `claude -p` startup with unauthenticated HTTP/SSE MCP servers (\~600ms saved)
  * Bash ghost-text suggestions now include just-submitted commands immediately
  * Increased non-streaming fallback token cap (21k → 64k) and timeout (120s → 300s local) so fallback requests are less likely to be truncated
  * Interrupting a prompt before any response now automatically restores your input so you can edit and resubmit
  * `/status` now works while Claude is responding, instead of being queued until the turn finishes
  * Plugin MCP servers that duplicate an org-managed connector are now suppressed instead of running a second connection
  * Linux: respect `XDG_DATA_HOME` when registering the `claude-cli://` protocol handler
  * Changed "stop all background agents" keybinding from `Ctrl+F` to `Ctrl+X Ctrl+K` to stop shadowing readline forward-char
  * Deprecated `TaskOutput` tool in favor of using `Read` on the background task's output file path
  * Added `CLAUDE_CODE_DISABLE_NONSTREAMING_FALLBACK` env var to disable the non-streaming fallback when streaming fails
  * Plugin options (`manifest.userConfig`) now available externally — plugins can prompt for configuration at enable time, with `sensitive: true` values stored in keychain (macOS) or protected credentials file (other platforms)
  * Claude can now reference the on-disk path of clipboard-pasted images for file operations
  * `Ctrl+L` now clears the screen and forces a full redraw — use this to recover when Cmd+K leaves the UI partially blank. Use `Ctrl+U` or double-Esc to clear prompt input.
  * `--bare -p` (SDK pattern) is \~14% faster to the API request
  * Memory: `MEMORY.md` index now truncates at 25KB as well as 200 lines
  * Disabled `AskUserQuestion` and plan-mode tools when `--channels` is active
  * Fixed API 400 error when a pasted image was queued during a failing tool call
  * Fixed MCP tool calls hanging indefinitely when an SSE connection drops mid-call and exhausts its reconnection attempts
  * Fixed Remote Control session titles showing raw XML when a background agent completed before the first user message
  * Fixed remote sessions forgetting conversation history after a container restart due to progress-message gaps in the resumed transcript chain
  * Fixed remote sessions requiring re-login on transient auth errors instead of retrying automatically
  * Fixed `rg ... | wc -l` and similar piped commands hanging and returning `0` in sandbox mode on Linux
  * Fixed voice input hold-to-talk not activating when a CJK IME inserts a full-width space
  * Fixed `--worktree` hanging silently when the worktree name contained a forward slash
  * \[VSCode] Spinner now turns red with "Not responding" when the backend hasn't responded for 60 seconds
  * \[VSCode] Fixed session history not loading correctly when reopening a session via URL or after restart
  * \[VSCode] Added Esc-twice (or `/rewind`) to open a keyboard-navigable rewind picker
  * \[VSCode] Fixed "Fork conversation from here" and rewind actions failing silently after the session cache goes stale
</Update>

<Update label="2.1.81" description="March 20, 2026">
  * Added `--bare` flag for scripted `-p` calls — skips hooks, LSP, plugin sync, and skill directory walks; requires `ANTHROPIC_API_KEY` or an `apiKeyHelper` via `--settings` (OAuth and keychain auth disabled); auto-memory fully disabled
  * Added `--channels` permission relay — channel servers that declare the permission capability can forward tool approval prompts to your phone
  * Fixed multiple concurrent Claude Code sessions requiring repeated re-authentication when one session refreshes its OAuth token
  * Fixed voice mode silently swallowing retry failures and showing a misleading "check your network" message instead of the actual error
  * Fixed voice mode audio not recovering when the server silently drops the WebSocket connection
  * Fixed `CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS` not suppressing the structured-outputs beta header, causing 400 errors on proxy gateways forwarding to Vertex/Bedrock
  * Fixed `--channels` bypass for Team/Enterprise orgs with no other managed settings configured
  * Fixed a crash on Node.js 18
  * Fixed unnecessary permission prompts for Bash commands containing dashes in strings
  * Fixed plugin hooks blocking prompt submission when the plugin directory is deleted mid-session
  * Fixed a race condition where background agent task output could hang indefinitely when the task completed between polling intervals
  * Resuming a session that was in a worktree now switches back to that worktree
  * Fixed `/btw` not including pasted text when used during an active response
  * Fixed a race where fast Cmd+Tab followed by paste could beat the clipboard copy under tmux
  * Fixed terminal tab title not updating with an auto-generated session description
  * Fixed invisible hook attachments inflating the message count in transcript mode
  * Fixed Remote Control sessions showing a generic title instead of deriving from the first prompt
  * Fixed `/rename` not syncing the title for Remote Control sessions
  * Fixed Remote Control `/exit` not reliably archiving the session
  * Improved MCP read/search tool calls to collapse into a single "Queried `{server}`" line (expand with Ctrl+O)
  * Improved `!` bash mode discoverability — Claude now suggests it when you need to run an interactive command
  * Improved plugin freshness — ref-tracked plugins now re-clone on every load to pick up upstream changes
  * Improved Remote Control session titles to refresh after your third message
  * Updated MCP OAuth to support Client ID Metadata Document (CIMD / SEP-991) for servers without Dynamic Client Registration
  * Changed plan mode to hide the "clear context" option by default (restore with `"showClearContextOnPlanAccept": true`)
  * Disabled line-by-line response streaming on Windows (including WSL in Windows Terminal) due to rendering issues
  * \[VSCode] Fixed Windows PATH inheritance for Bash tool when using Git Bash (regression in v2.1.78)
</Update>

<Update label="2.1.80" description="March 19, 2026">
  * Added `rate_limits` field to statusline scripts for displaying Claude.ai rate limit usage (5-hour and 7-day windows with `used_percentage` and `resets_at`)
  * Added `source: 'settings'` plugin marketplace source — declare plugin entries inline in settings.json
  * Added CLI tool usage detection to plugin tips, in addition to file pattern matching
  * Added `effort` frontmatter support for skills and slash commands to override the model effort level when invoked
  * Added `--channels` (research preview) — allow MCP servers to push messages into your session
  * Fixed `--resume` dropping parallel tool results — sessions with parallel tool calls now restore all tool\_use/tool\_result pairs instead of showing `[Tool result missing]` placeholders
  * Fixed voice mode WebSocket failures caused by Cloudflare bot detection on non-browser TLS fingerprints
  * Fixed 400 errors when using fine-grained tool streaming through API proxies, Bedrock, or Vertex
  * Fixed `/remote-control` appearing for gateway and third-party provider deployments where it cannot function
  * Fixed `/sandbox` tab switching not responding to Tab or arrow keys
  * Improved responsiveness of `@` file autocomplete in large git repositories
  * Improved `/effort` to show what auto currently resolves to, matching the status bar indicator
  * Improved `/permissions` — Tab and arrow keys now switch tabs from within a list
  * Improved background tasks panel — left arrow now closes from the list view
  * Simplified plugin install tips to use a single `/plugin install` command instead of a two-step flow
  * Reduced memory usage on startup in large repositories (\~80 MB saved on 250k-file repos)
  * Fixed managed settings (`enabledPlugins`, `permissions.defaultMode`, policy-set env vars) not being applied at startup when `remote-settings.json` was cached from a prior session
</Update>

<Update label="2.1.79" description="March 18, 2026">
  * Added `--console` flag to `claude auth login` for Anthropic Console (API billing) authentication
  * Added "Show turn duration" toggle to the `/config` menu
  * Fixed `claude -p` hanging when spawned as a subprocess without explicit stdin (e.g. Python `subprocess.run`)
  * Fixed Ctrl+C not working in `-p` (print) mode
  * Fixed `/btw` returning the main agent's output instead of answering the side question when triggered during streaming
  * Fixed voice mode not activating correctly on startup when `voiceEnabled: true` is set
  * Fixed left/right arrow tab navigation in `/permissions`
  * Fixed `CLAUDE_CODE_DISABLE_TERMINAL_TITLE` not preventing terminal title from being set on startup
  * Fixed custom status line showing nothing when workspace trust is blocking it
  * Fixed enterprise users being unable to retry on rate limit (429) errors
  * Fixed `SessionEnd` hooks not firing when using interactive `/resume` to switch sessions
  * Improved startup memory usage by \~18MB across all scenarios
  * Improved non-streaming API fallback with a 2-minute per-attempt timeout, preventing sessions from hanging indefinitely
  * `CLAUDE_CODE_PLUGIN_SEED_DIR` now supports multiple seed directories separated by the platform path delimiter (`:` on Unix, `;` on Windows)
  * \[VSCode] Added `/remote-control` — bridge your session to claude.ai/code to continue from a browser or phone
  * \[VSCode] Session tabs now get AI-generated titles based on your first message
  * \[VSCode] Fixed the thinking pill showing "Thinking" instead of "Thought for Ns" after a response completes
  * \[VSCode] Fixed missing session diff button when opening sessions from the left sidebar
</Update>

<Update label="2.1.78" description="March 17, 2026">
  * Added `StopFailure` hook event that fires when the turn ends due to an API error (rate limit, auth failure, etc.)
  * Added `${CLAUDE_PLUGIN_DATA}` variable for plugin persistent state that survives plugin updates; `/plugin uninstall` prompts before deleting it
  * Added `effort`, `maxTurns`, and `disallowedTools` frontmatter support for plugin-shipped agents
  * Terminal notifications (iTerm2/Kitty/Ghostty popups, progress bar) now reach the outer terminal when running inside tmux with `set -g allow-passthrough on`
  * Response text now streams line-by-line as it's generated
  * Fixed `git log HEAD` failing with "ambiguous argument" inside sandboxed Bash on Linux, and stub files polluting `git status` in the working directory
  * Fixed `cc log` and `--resume` silently truncating conversation history on large sessions (>5 MB) that used subagents
  * Fixed infinite loop when API errors triggered stop hooks that re-fed blocking errors to the model
  * Fixed `deny: ["mcp__servername"]` permission rules not removing MCP server tools before sending to the model, allowing it to see and attempt blocked tools
  * Fixed `sandbox.filesystem.allowWrite` not working with absolute paths (previously required `//` prefix)
  * Fixed `/sandbox` Dependencies tab showing Linux prerequisites on macOS instead of macOS-specific info
  * **Security:** Fixed silent sandbox disable when `sandbox.enabled: true` is set but dependencies are missing — now shows a visible startup warning
  * Fixed `.git`, `.claude`, and other protected directories being writable without a prompt in `bypassPermissions` mode
  * Fixed ctrl+u in normal mode scrolling instead of readline kill-line (ctrl+u/ctrl+d half-page scroll moved to transcript mode only)
  * Fixed voice mode modifier-combo push-to-talk keybindings (e.g. ctrl+k) requiring a hold instead of activating immediately
  * Fixed voice mode not working on WSL2 with WSLg (Windows 11); WSL1/Win10 users now get a clear error
  * Fixed `--worktree` flag not loading skills and hooks from the worktree directory
  * Fixed `CLAUDE_CODE_DISABLE_GIT_INSTRUCTIONS` and `includeGitInstructions` setting not suppressing the git status section in the system prompt
  * Fixed Bash tool not finding Homebrew and other PATH-dependent binaries when VS Code is launched from Dock/Spotlight
  * Fixed washed-out Claude orange color in VS Code/Cursor/code-server terminals that don't advertise truecolor support
  * Added `ANTHROPIC_CUSTOM_MODEL_OPTION` env var to add a custom entry to the `/model` picker, with optional `_NAME` and `_DESCRIPTION` suffixed vars for display
  * Fixed `ANTHROPIC_BETAS` environment variable being silently ignored when using Haiku models
  * Fixed queued prompts being concatenated without a newline separator
  * Improved memory usage and startup time when resuming large sessions
  * \[VSCode] Fixed a brief flash of the login screen when opening the sidebar while already authenticated
  * \[VSCode] Fixed "API Error: Rate limit reached" when selecting Opus — model dropdown no longer offers 1M context variant to subscribers whose plan tier is unknown
</Update>

<Update label="2.1.77" description="March 17, 2026">
  * Increased default maximum output token limits for Claude Opus 4.6 to 64k tokens, and the upper bound for Opus 4.6 and Sonnet 4.6 models to 128k tokens
  * Added `allowRead` sandbox filesystem setting to re-allow read access within `denyRead` regions
  * `/copy` now accepts an optional index: `/copy N` copies the Nth-latest assistant response
  * Fixed "Always Allow" on compound bash commands (e.g. `cd src && npm test`) saving a single rule for the full string instead of per-subcommand, leading to dead rules and repeated permission prompts
  * Fixed auto-updater starting overlapping binary downloads when the slash-command overlay repeatedly opened and closed, accumulating tens of gigabytes of memory
  * Fixed `--resume` silently truncating recent conversation history due to a race between memory-extraction writes and the main transcript
  * Fixed PreToolUse hooks returning `"allow"` bypassing `deny` permission rules, including enterprise managed settings
  * Fixed Write tool silently converting line endings when overwriting CRLF files or creating files in CRLF directories
  * Fixed memory growth in long-running sessions from progress messages surviving compaction
  * Fixed cost and token usage not being tracked when the API falls back to non-streaming mode
  * Fixed `CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS` not stripping beta tool-schema fields, causing proxy gateways to reject requests
  * Fixed Bash tool reporting errors for successful commands when the system temp directory path contains spaces
  * Fixed paste being lost when typing immediately after pasting
  * Fixed Ctrl+D in `/feedback` text input deleting forward instead of the second press exiting the session
  * Fixed API error when dragging a 0-byte image file into the prompt
  * Fixed Claude Desktop sessions incorrectly using the terminal CLI's configured API key instead of OAuth
  * Fixed `git-subdir` plugins at different subdirectories of the same monorepo commit colliding in the plugin cache
  * Fixed ordered list numbers not rendering in terminal UI
  * Fixed a race condition where stale-worktree cleanup could delete an agent worktree just resumed from a previous crash
  * Fixed input deadlock when opening `/mcp` or similar dialogs while the agent is running
  * Fixed Backspace and Delete keys not working in vim NORMAL mode
  * Fixed status line not updating when vim mode is toggled on or off
  * Fixed hyperlinks opening twice on Cmd+click in VS Code, Cursor, and other xterm.js-based terminals
  * Fixed background colors rendering as terminal-default inside tmux with default configuration
  * Fixed iTerm2 session crash when selecting text inside tmux over SSH
  * Fixed clipboard copy silently failing in tmux sessions; copy toast now indicates whether to paste with `⌘V` or tmux `prefix+]`
  * Fixed `←`/`→` accidentally switching tabs in settings, permissions, and sandbox dialogs while navigating lists
  * Fixed IDE integration not auto-connecting when Claude Code is launched inside tmux or screen
  * Fixed CJK characters visually bleeding into adjacent UI elements when clipped at the right edge
  * Fixed teammate panes not closing when the leader exits
  * Fixed iTerm2 auto mode not detecting iTerm2 for native split-pane teammates
  * Faster startup on macOS (\~60ms) by reading keychain credentials in parallel with module loading
  * Faster `--resume` on fork-heavy and very large sessions — up to 45% faster loading and \~100-150MB less peak memory
  * Improved Esc to abort in-flight non-streaming API requests
  * Improved `claude plugin validate` to check skill, agent, and command frontmatter plus `hooks/hooks.json`, catching YAML parse errors and schema violations
  * Background bash tasks are now killed if output exceeds 5GB, preventing runaway processes from filling disk
  * Sessions are now auto-named from plan content when you accept a plan
  * Improved headless mode plugin installation to compose correctly with `CLAUDE_CODE_PLUGIN_SEED_DIR`
  * Show a notice when `apiKeyHelper` takes longer than 10s, preventing it from blocking the main loop
  * The Agent tool no longer accepts a `resume` parameter — use `SendMessage({to: agentId})` to continue a previously spawned agent
  * `SendMessage` now auto-resumes stopped agents in the background instead of returning an error
  * Renamed `/fork` to `/branch` (`/fork` still works as an alias)
  * \[VSCode] Improved plan preview tab titles to use the plan's heading instead of "Claude's Plan"
  * \[VSCode] When option+click doesn't trigger native selection on macOS, the footer now points to the `macOptionClickForcesSelection` setting
</Update>

<Update label="2.1.76" description="March 14, 2026">
  * Added MCP elicitation support — MCP servers can now request structured input mid-task via an interactive dialog (form fields or browser URL)
  * Added new `Elicitation` and `ElicitationResult` hooks to intercept and override responses before they're sent back
  * Added `-n` / `--name <name>` CLI flag to set a display name for the session at startup
  * Added `worktree.sparsePaths` setting for `claude --worktree` in large monorepos to check out only the directories you need via git sparse-checkout
  * Added `PostCompact` hook that fires after compaction completes
  * Added `/effort` slash command to set model effort level
  * Added session quality survey — enterprise admins can configure the sample rate via the `feedbackSurveyRate` setting
  * Fixed deferred tools (loaded via `ToolSearch`) losing their input schemas after conversation compaction, causing array and number parameters to be rejected with type errors
  * Fixed slash commands showing "Unknown skill"
  * Fixed plan mode asking for re-approval after the plan was already accepted
  * Fixed voice mode swallowing keypresses while a permission dialog or plan editor was open
  * Fixed `/voice` not working on Windows when installed via npm
  * Fixed spurious "Context limit reached" when invoking a skill with `model:` frontmatter on a 1M-context session
  * Fixed "adaptive thinking is not supported on this model" error when using non-standard model strings
  * Fixed `Bash(cmd:*)` permission rules not matching when a quoted argument contains `#`
  * Fixed "don't ask again" in the Bash permission dialog showing the full raw command for pipes and compound commands
  * Fixed auto-compaction retrying indefinitely after consecutive failures — a circuit breaker now stops after 3 attempts
  * Fixed MCP reconnect spinner persisting after successful reconnection
  * Fixed LSP plugins not registering servers when the LSP Manager initialized before marketplaces were reconciled
  * Fixed clipboard copying in tmux over SSH — now attempts both direct terminal write and tmux clipboard integration
  * Fixed `/export` showing only the filename instead of the full file path in the success message
  * Fixed transcript not auto-scrolling to new messages after selecting text
  * Fixed Escape key not working to exit the login method selection screen
  * Fixed several Remote Control issues: sessions silently dying when the server reaps an idle environment, rapid messages being queued one-at-a-time instead of batched, and stale work items causing redelivery after JWT refresh
  * Fixed bridge sessions failing to recover after extended WebSocket disconnects
  * Fixed slash commands not found when typing the exact name of a soft-hidden command
  * Improved `--worktree` startup performance by reading git refs directly and skipping redundant `git fetch` when the remote branch is already available locally
  * Improved background agent behavior — killing a background agent now preserves its partial results in the conversation context
  * Improved model fallback notifications — now always visible instead of hidden behind verbose mode, with human-friendly model names
  * Improved blockquote readability on dark terminal themes — text is now italic with a left bar instead of dim
  * Improved stale worktree cleanup — worktrees left behind after an interrupted parallel run are now automatically cleaned up
  * Improved Remote Control session titles — now derived from your first prompt instead of showing "Interactive session"
  * Improved `/voice` to show your dictation language on enable and warn when your `language` setting isn't supported for voice input
  * Updated `--plugin-dir` to only accept one path to support subcommands — use repeated `--plugin-dir` for multiple directories
  * \[VSCode] Fixed gitignore patterns containing commas silently excluding entire filetypes from the @-mention file picker
</Update>

<Update label="2.1.75" description="March 13, 2026">
  * Added 1M context window for Opus 4.6 by default for Max, Team, and Enterprise plans (previously required extra usage)
  * Added `/color` command for all users to set a prompt-bar color for your session
  * Added session name display on the prompt bar when using `/rename`
  * Added last-modified timestamps to memory files, helping Claude reason about which memories are fresh vs. stale
  * Added hook source display (settings/plugin/skill) in permission prompts when a hook requires confirmation
  * Fixed voice mode not activating correctly on fresh installs without toggling `/voice` twice
  * Fixed the Claude Code header not updating the displayed model name after switching models with `/model` or Option+P
  * Fixed session crash when an attachment message computation returns undefined values
  * Fixed Bash tool mangling `!` in piped commands (e.g., `jq 'select(.x != .y)'` now works correctly)
  * Fixed managed-disabled plugins showing up in the `/plugin` Installed tab — plugins force-disabled by your organization are now hidden
  * Fixed token estimation over-counting for thinking and `tool_use` blocks, preventing premature context compaction
  * Fixed corrupted marketplace config path handling
  * Fixed `/resume` losing session names after resuming a forked or continued session
  * Fixed Esc not closing the `/status` dialog after visiting the Config tab
  * Fixed input handling when accepting or rejecting a plan
  * Fixed footer hint in agent teams showing "↓ to expand" instead of the correct "shift + ↓ to expand"
  * Improved startup performance on macOS non-MDM machines by skipping unnecessary subprocess spawns
  * Suppressed async hook completion messages by default (visible with `--verbose` or transcript mode)
  * Breaking change: Removed deprecated Windows managed settings fallback at `C:\ProgramData\ClaudeCode\managed-settings.json` — use `C:\Program Files\ClaudeCode\managed-settings.json`
</Update>

<Update label="2.1.74" description="March 12, 2026">
  * Added actionable suggestions to `/context` command — identifies context-heavy tools, memory bloat, and capacity warnings with specific optimization tips
  * Added `autoMemoryDirectory` setting to configure a custom directory for auto-memory storage
  * Fixed memory leak where streaming API response buffers were not released when the generator was terminated early, causing unbounded RSS growth on the Node.js/npm code path
  * Fixed managed policy `ask` rules being bypassed by user `allow` rules or skill `allowed-tools`
  * Fixed full model IDs (e.g., `claude-opus-4-5`) being silently ignored in agent frontmatter `model:` field and `--agents` JSON config — agents now accept the same model values as `--model`
  * Fixed MCP OAuth authentication hanging when the callback port is already in use
  * Fixed MCP OAuth refresh never prompting for re-auth after the refresh token expires, for OAuth servers that return errors with HTTP 200 (e.g. Slack)
  * Fixed voice mode silently failing on the macOS native binary for users whose terminal had never been granted microphone permission — the binary now includes the `audio-input` entitlement so macOS prompts correctly
  * Fixed `SessionEnd` hooks being killed after 1.5 s on exit regardless of `hook.timeout` — now configurable via `CLAUDE_CODE_SESSIONEND_HOOKS_TIMEOUT_MS`
  * Fixed `/plugin install` failing inside the REPL for marketplace plugins with local sources
  * Fixed marketplace update not syncing git submodules — plugin sources in submodules no longer break after update
  * Fixed unknown slash commands with arguments silently dropping input — now shows your input as a warning
  * Fixed Hebrew, Arabic, and other RTL text not rendering correctly in Windows Terminal, conhost, and VS Code integrated terminal
  * Fixed LSP servers not working on Windows due to malformed file URIs
  * Changed `--plugin-dir` so local dev copies now override installed marketplace plugins with the same name (unless that plugin is force-enabled by managed settings)
  * \[VSCode] Fixed delete button not working for Untitled sessions
  * \[VSCode] Improved scroll wheel responsiveness in the integrated terminal with terminal-aware acceleration
</Update>

<Update label="2.1.73" description="March 11, 2026">
  * Added `modelOverrides` setting to map model picker entries to custom provider model IDs (e.g. Bedrock inference profile ARNs)
  * Added actionable guidance when OAuth login or connectivity checks fail due to SSL certificate errors (corporate proxies, `NODE_EXTRA_CA_CERTS`)
  * Fixed freezes and 100% CPU loops triggered by permission prompts for complex bash commands
  * Fixed a deadlock that could freeze Claude Code when many skill files changed at once (e.g. during `git pull` in a repo with a large `.claude/skills/` directory)
  * Fixed Bash tool output being lost when running multiple Claude Code sessions in the same project directory
  * Fixed subagents with `model: opus`/`sonnet`/`haiku` being silently downgraded to older model versions on Bedrock, Vertex, and Microsoft Foundry
  * Fixed background bash processes spawned by subagents not being cleaned up when the agent exits
  * Fixed `/resume` showing the current session in the picker
  * Fixed `/ide` crashing with `onInstall is not defined` when auto-installing the extension
  * Fixed `/loop` not being available on Bedrock/Vertex/Foundry and when telemetry was disabled
  * Fixed SessionStart hooks firing twice when resuming a session via `--resume` or `--continue`
  * Fixed JSON-output hooks injecting no-op system-reminder messages into the model's context on every turn
  * Fixed voice mode session corruption when a slow connection overlaps a new recording
  * Fixed Linux sandbox failing to start with "ripgrep (rg) not found" on native builds
  * Fixed Linux native modules not loading on Amazon Linux 2 and other glibc 2.26 systems
  * Fixed "media\_type: Field required" API error when receiving images via Remote Control
  * Fixed `/heapdump` failing on Windows with `EEXIST` error when the Desktop folder already exists
  * Improved Up arrow after interrupting Claude — now restores the interrupted prompt and rewinds the conversation in one step
  * Improved IDE detection speed at startup
  * Improved clipboard image pasting performance on macOS
  * Improved `/effort` to work while Claude is responding, matching `/model` behavior
  * Improved voice mode to automatically retry transient connection failures during rapid push-to-talk re-press
  * Improved the Remote Control spawn mode selection prompt with better context
  * Changed default Opus model on Bedrock, Vertex, and Microsoft Foundry to Opus 4.6 (was Opus 4.1)
  * Deprecated `/output-style` command — use `/config` instead. Output style is now fixed at session start for better prompt caching
  * VSCode: Fixed HTTP 400 errors for users behind proxies or on Bedrock/Vertex with Claude 4.5 models
</Update>

<Update label="2.1.72" description="March 10, 2026">
  * Fixed tool search to activate even with `ANTHROPIC_BASE_URL` as long as `ENABLE_TOOL_SEARCH` is set.
  * Added `w` key in `/copy` to write the focused selection directly to a file, bypassing the clipboard (useful over SSH)
  * Added optional description argument to `/plan` (e.g., `/plan fix the auth bug`) that enters plan mode and immediately starts
  * Added `ExitWorktree` tool to leave an `EnterWorktree` session
  * Added `CLAUDE_CODE_DISABLE_CRON` environment variable to immediately stop scheduled cron jobs mid-session
  * Added `lsof`, `pgrep`, `tput`, `ss`, `fd`, and `fdfind` to the bash auto-approval allowlist, reducing permission prompts for common read-only operations
  * Restored the `model` parameter on the Agent tool for per-invocation model overrides
  * Simplified effort levels to low/medium/high (removed max) with new symbols (○ ◐ ●) and a brief notification instead of a persistent icon. Use `/effort auto` to reset to default
  * Improved `/config` — Escape now cancels changes, Enter saves and closes, Space toggles settings
  * Improved up-arrow history to show current session's messages first when running multiple concurrent sessions
  * Improved voice input transcription accuracy for repo names and common dev terms (regex, OAuth, JSON)
  * Improved bash command parsing by switching to a native module — faster initialization and no memory leak
  * Reduced bundle size by \~510 KB
  * Changed CLAUDE.md HTML comments (`<!-- ... -->`) to be hidden from Claude when auto-injected. Comments remain visible when read with the Read tool
  * Fixed slow exits when background tasks or hooks were slow to respond
  * Fixed agent task progress stuck on "Initializing…"
  * Fixed skill hooks firing twice per event when a hooks-enabled skill is invoked by the model
  * Fixed several voice mode issues: occasional input lag, false "No speech detected" errors after releasing push-to-talk, and stale transcripts re-filling the prompt after submission
  * Fixed `--continue` not resuming from the most recent point after `--compact`
  * Fixed bash security parsing edge cases
  * Added support for marketplace git URLs without `.git` suffix (Azure DevOps, AWS CodeCommit)
  * Improved marketplace clone failure messages to show diagnostic info even when git produces no stderr
  * Fixed several plugin issues: installation failing on Windows with `EEXIST` error in OneDrive folders, marketplace blocking user-scope installs when a project-scope install exists, `CLAUDE_CODE_PLUGIN_CACHE_DIR` creating literal `~` directories, and `plugin.json` with marketplace-only fields failing to load
  * Fixed feedback survey appearing too frequently in long sessions
  * Fixed `--effort` CLI flag being reset by unrelated settings writes on startup
  * Fixed backgrounded Ctrl+B queries losing their transcript or corrupting the new conversation after `/clear`
  * Fixed `/clear` killing background agent/bash tasks — only foreground tasks are now cleared
  * Fixed worktree isolation issues: Task tool resume not restoring cwd, and background task notifications missing `worktreePath` and `worktreeBranch`
  * Fixed `/model` not displaying results when run while Claude is working
  * Fixed digit keys selecting menu options instead of typing in plan mode permission prompt's text input
  * Fixed sandbox permission issues: certain file write operations incorrectly allowed without prompting, and output redirections to allowlisted directories (like `/tmp/claude/`) prompting unnecessarily
  * Improved CPU utilization in long sessions
  * Fixed prompt cache invalidation in SDK `query()` calls, reducing input token costs up to 12x
  * Fixed Escape key becoming unresponsive after cancelling a query
  * Fixed double Ctrl+C not exiting when background agents or tasks are running
  * Fixed team agents to inherit the leader's model
  * Fixed "Always Allow" saving permission rules that never match again
  * Fixed several hooks issues: `transcript_path` pointing to the wrong directory for resumed/forked sessions, agent `prompt` being silently deleted from settings.json on every settings write, PostToolUse block reason displaying twice, async hooks not receiving stdin with bash `read -r`, and validation error message showing an example that fails validation
  * Fixed session crashes in Desktop/SDK when Read returned files containing U+2028/U+2029 characters
  * Fixed terminal title being cleared on exit even when `CLAUDE_CODE_DISABLE_TERMINAL_TITLE` was set
  * Fixed several permission rule matching issues: wildcard rules not matching commands with heredocs, embedded newlines, or no arguments; `sandbox.excludedCommands` failing with env var prefixes; "always allow" suggesting overly broad prefixes for nested CLI tools; and deny rules not applying to all command forms
  * Fixed oversized and truncated images from Bash data-URL output
  * Fixed a crash when resuming sessions that contained Bedrock API errors
  * Fixed intermittent "expected boolean, received string" validation errors on Edit, Bash, and Grep tool inputs
  * Fixed multi-line session titles when forking from a conversation whose first message contained newlines
  * Fixed queued messages not showing attached images, and images being lost when pressing ↑ to edit a queued message
  * Fixed parallel tool calls where a failed Read/WebFetch/Glob would cancel its siblings — only Bash errors now cascade
  * VSCode: Fixed scroll speed in integrated terminals not matching native terminals
  * VSCode: Fixed Shift+Enter submitting input instead of inserting a newline for users with older keybindings
  * VSCode: Added effort level indicator on the input border
  * VSCode: Added `vscode://anthropic.claude-code/open` URI handler to open a new Claude Code tab programmatically, with optional `prompt` and `session` query parameters
</Update>

<Update label="2.1.71" description="March 7, 2026">
  * Added `/loop` command to run a prompt or slash command on a recurring interval (e.g. `/loop 5m check the deploy`)
  * Added cron scheduling tools for recurring prompts within a session
  * Added `voice:pushToTalk` keybinding to make the voice activation key rebindable in `keybindings.json` (default: space) — modifier+letter combos like `meta+k` have zero typing interference
  * Added `fmt`, `comm`, `cmp`, `numfmt`, `expr`, `test`, `printf`, `getconf`, `seq`, `tsort`, and `pr` to the bash auto-approval allowlist
  * Fixed stdin freeze in long-running sessions where keystrokes stop being processed but the process stays alive
  * Fixed a 5–8 second startup freeze for users with voice mode enabled, caused by CoreAudio initialization blocking the main thread after system wake
  * Fixed startup UI freeze when many claude.ai proxy connectors refresh an expired OAuth token simultaneously
  * Fixed forked conversations (`/fork`) sharing the same plan file, which caused plan edits in one fork to overwrite the other
  * Fixed the Read tool putting oversized images into context when image processing failed, breaking subsequent turns in long image-heavy sessions
  * Fixed false-positive permission prompts for compound bash commands containing heredoc commit messages
  * Fixed plugin installations being lost when running multiple Claude Code instances
  * Fixed claude.ai connectors failing to reconnect after OAuth token refresh
  * Fixed claude.ai MCP connector startup notifications appearing for every org-configured connector instead of only previously connected ones
  * Fixed background agent completion notifications missing the output file path, which made it difficult for parent agents to recover agent results after context compaction
  * Fixed duplicate output in Bash tool error messages when commands exit with non-zero status
  * Fixed Chrome extension auto-detection getting permanently stuck on "not installed" after running on a machine without local Chrome
  * Fixed `/plugin marketplace update` failing with merge conflicts when the marketplace is pinned to a branch/tag ref
  * Fixed `/plugin marketplace add owner/repo@ref` incorrectly parsing `@` — previously only `#` worked as a ref separator, causing undiagnosable errors with `strictKnownMarketplaces`
  * Fixed duplicate entries in `/permissions` Workspace tab when the same directory is added with and without a trailing slash
  * Fixed `--print` hanging forever when team agents are configured — the exit loop no longer waits on long-lived `in_process_teammate` tasks
  * Fixed "❯ Tool loaded." appearing in the REPL after every `ToolSearch` call
  * Fixed prompting for `cd <cwd> && git ...` on Windows when the model uses a mingw-style path
  * Improved startup time by deferring native image processor loading to first use
  * Improved bridge session reconnection to complete within seconds after laptop wake from sleep, instead of waiting up to 10 minutes
  * Improved `/plugin uninstall` to disable project-scoped plugins in `.claude/settings.local.json` instead of modifying `.claude/settings.json`, so changes don't affect teammates
  * Improved plugin-provided MCP server deduplication — servers that duplicate a manually-configured server (same command/URL) are now skipped, preventing duplicate connections and tool sets. Suppressions are shown in the `/plugin` menu.
  * Updated `/debug` to toggle debug logging on mid-session, since debug logs are no longer written by default
  * Removed startup notification noise for unauthenticated org-registered claude.ai connectors
</Update>

<Update label="2.1.70" description="March 6, 2026">
  * Fixed API 400 errors when using `ANTHROPIC_BASE_URL` with a third-party gateway — tool search now correctly detects proxy endpoints and disables `tool_reference` blocks
  * Fixed `API Error: 400 This model does not support the effort parameter` when using custom Bedrock inference profiles or other model identifiers not matching standard Claude naming patterns
  * Fixed empty model responses immediately after `ToolSearch` — the server renders tool schemas with system-prompt-style tags at the prompt tail, which could confuse models into stopping early
  * Fixed prompt-cache bust when an MCP server with `instructions` connects after the first turn
  * Fixed Enter inserting a newline instead of submitting when typing over a slow SSH connection
  * Fixed clipboard corrupting non-ASCII text (CJK, emoji) on Windows/WSL by using PowerShell `Set-Clipboard`
  * Fixed extra VS Code windows opening at startup on Windows when running from the VS Code integrated terminal
  * Fixed voice mode failing on Windows native binary with "native audio module could not be loaded"
  * Fixed push-to-talk not activating on session start when `voiceEnabled: true` was set in settings
  * Fixed markdown links containing `#NNN` references incorrectly pointing to the current repository instead of the linked URL
  * Fixed repeated "Model updated to Opus 4.6" notification when a project's `.claude/settings.json` has a legacy Opus model string pinned
  * Fixed plugins showing as inaccurately installed in `/plugin`
  * Fixed plugins showing "not found in marketplace" errors on fresh startup by auto-refreshing after marketplace installation
  * Fixed `/security-review` command failing with `unknown option merge-base` on older git versions
  * Fixed `/color` command having no way to reset back to the default color — `/color default`, `/color gray`, `/color reset`, and `/color none` now restore the default
  * Fixed a performance regression in the `AskUserQuestion` preview dialog that re-ran markdown rendering on every keystroke in the notes input
  * Fixed feature flags read during early startup never refreshing their disk cache, causing stale values to persist across sessions
  * Fixed `permissions.defaultMode` settings values other than `acceptEdits` or `plan` being applied in Claude Code Remote environments — they are now ignored
  * Fixed skill listing being re-injected on every `--resume` (\~600 tokens saved per resume)
  * Fixed teleport marker not rendering in VS Code teleported sessions
  * Improved error message when microphone captures silence to distinguish from "no speech detected"
  * Improved compaction to preserve images in the summarizer request, allowing prompt cache reuse for faster and cheaper compaction
  * Improved `/rename` to work while Claude is processing, instead of being silently queued
  * Reduced prompt input re-renders during turns by \~74%
  * Reduced startup memory by \~426KB for users without custom CA certificates
  * Reduced Remote Control `/poll` rate to once per 10 minutes while connected (was 1–2s), cutting server load \~300×. Reconnection is unaffected — transport loss immediately wakes fast polling.
  * \[VSCode] Added spark icon in VS Code activity bar that lists all Claude Code sessions, with sessions opening as full editors
  * \[VSCode] Added full markdown document view for plans in VS Code, with support for adding comments to provide feedback
  * \[VSCode] Added native MCP server management dialog — use `/mcp` in the chat panel to enable/disable servers, reconnect, and manage OAuth authentication without switching to the terminal
</Update>

<Update label="2.1.69" description="March 5, 2026">
  * Added the `/claude-api` skill for building applications with the Claude API and Anthropic SDK
  * Added Ctrl+U on an empty bash prompt (`!`) to exit bash mode, matching `escape` and `backspace`
  * Added numeric keypad support for selecting options in Claude's interview questions (previously only the number row above QWERTY worked)
  * Added optional name argument to `/remote-control` and `claude remote-control` (`/remote-control My Project` or `--name "My Project"`) to set a custom session title visible in claude.ai/code
  * Added Voice STT support for 10 new languages (20 total) — Russian, Polish, Turkish, Dutch, Ukrainian, Greek, Czech, Danish, Swedish, Norwegian
  * Added effort level display (e.g., "with low effort") to the logo and spinner, making it easier to see which effort setting is active
  * Added agent name display in terminal title when using `claude --agent`
  * Added `sandbox.enableWeakerNetworkIsolation` setting (macOS only) to allow Go programs like `gh`, `gcloud`, and `terraform` to verify TLS certificates when using a custom MITM proxy with `httpProxyPort`
  * Added `includeGitInstructions` setting (and `CLAUDE_CODE_DISABLE_GIT_INSTRUCTIONS` env var) to remove built-in commit and PR workflow instructions from Claude's system prompt
  * Added `/reload-plugins` command to activate pending plugin changes without restarting
  * Added a one-time startup prompt suggesting Claude Code Desktop on macOS and Windows (max 3 showings, dismissible)
  * Added `${CLAUDE_SKILL_DIR}` variable for skills to reference their own directory in SKILL.md content
  * Added `InstructionsLoaded` hook event that fires when CLAUDE.md or `.claude/rules/*.md` files are loaded into context
  * Added `agent_id` (for subagents) and `agent_type` (for subagents and `--agent`) to hook events
  * Added `worktree` field to status line hook commands with name, path, branch, and original repo directory when running in a `--worktree` session
  * Added `pluginTrustMessage` in managed settings to append organization-specific context to the plugin trust warning shown before installation
  * Added policy limit fetching (e.g., remote control restrictions) for Team plan OAuth users, not just Enterprise
  * Added `pathPattern` to `strictKnownMarketplaces` for regex-matching file/directory marketplace sources alongside `hostPattern` restrictions
  * Added plugin source type `git-subdir` to point to a subdirectory within a git repo
  * Added `oauth.authServerMetadataUrl` config option for MCP servers to specify a custom OAuth metadata discovery URL when standard discovery fails
  * Fixed a security issue where nested skill discovery could load skills from gitignored directories like `node_modules`
  * Fixed trust dialog silently enabling all `.mcp.json` servers on first run. You'll now see the per-server approval dialog as expected
  * Fixed `claude remote-control` crashing immediately on npm installs with "bad option: --sdk-url" (anthropics/claude-code#28334)
  * Fixed `--model claude-opus-4-0` and `--model claude-opus-4-1` resolving to deprecated Opus versions instead of current
  * Fixed macOS keychain corruption when using multiple OAuth MCP servers. Large OAuth metadata blobs could overflow the `security -i` stdin buffer, silently leaving stale credentials behind and causing repeated `/login` prompts.
  * Fixed `.credentials.json` losing `subscriptionType` (showing "Claude API" instead of "Claude Pro"/"Claude Max") when the profile endpoint transiently fails during token refresh (anthropics/claude-code#30185)
  * Fixed ghost dotfiles (`.bashrc`, `HEAD`, etc.) appearing as untracked files in the working directory after sandboxed Bash commands on Linux
  * Fixed Shift+Enter printing `[27;2;13~` instead of inserting a newline in Ghostty over SSH
  * Fixed stash (Ctrl+S) being cleared when submitting a message while Claude is working
  * Fixed ctrl+o (transcript toggle) freezing for many seconds in long sessions with lots of file edits
  * Fixed plan mode feedback input not supporting multi-line text entry (backslash+Enter and Shift+Enter now insert newlines)
  * Fixed cursor not moving down into blank lines at the top of the input box
  * Fixed `/stats` crash when transcript files contain entries with missing or malformed timestamps
  * Fixed a brief hang after a streaming error on long sessions (the transcript was being fully rewritten to drop one line; it is now truncated in place)
  * Fixed `--setting-sources user` not blocking dynamically discovered project skills
  * Fixed duplicate CLAUDE.md, slash commands, agents, and rules when running from a worktree nested inside its main repo (e.g. `claude -w`)
  * Fixed plugin Stop/SessionEnd/etc hooks not firing after any `/plugin` operation
  * Fixed plugin hooks being silently dropped when two plugins use the same `${CLAUDE_PLUGIN_ROOT}/...` command template
  * Fixed memory leak in long-running SDK/CCR sessions where conversation messages were retained unnecessarily
  * Fixed API 400 errors in forked agents (autocompact, summarization) when resuming sessions that were interrupted mid-tool-batch
  * Fixed "unexpected tool\_use\_id found in tool\_result blocks" error when resuming conversations that start with an orphaned tool result
  * Fixed teammates accidentally spawning nested teammates via the Agent tool's `name` parameter
  * Fixed `CLAUDE_CODE_MAX_OUTPUT_TOKENS` being ignored during conversation compaction
  * Fixed `/compact` summary rendering as a user bubble in SDK consumers (Claude Code Remote web UI, VSCode extension)
  * Fixed voice space bar getting stuck after a failed voice activation (module loading race, cold GrowthBook)
  * Fixed worktree file copy on Windows
  * Fixed global `.claude` folder detection on Windows
  * Fixed symlink bypass where writing new files through a symlinked parent directory could escape the working directory in `acceptEdits` mode
  * Fixed sandbox prompting users to approve non-allowed domains when `allowManagedDomainsOnly` is enabled in managed settings — non-allowed domains are now blocked automatically with no bypass
  * Fixed interactive tools (e.g., `AskUserQuestion`) being silently auto-allowed when listed in a skill's allowed-tools, bypassing the permission prompt and running with empty answers
  * Fixed multi-GB memory spike when committing with large untracked binary files in the working tree
  * Fixed Escape not interrupting a running turn when the input box has draft text. Use Up arrow to pull queued messages back for editing, or Ctrl+U to clear the input line.
  * Fixed Android app crash when running local slash commands (`/voice`, `/cost`) in Remote Control sessions
  * Fixed a memory leak where old message array versions accumulated in React Compiler `memoCache` over long sessions
  * Fixed a memory leak where REPL render scopes accumulated over long sessions (\~35MB over 1000 turns)
  * Fixed memory retention in in-process teammates where the parent's full conversation history was pinned for the teammate's lifetime, preventing GC after `/clear` or auto-compact
  * Fixed a memory leak in interactive mode where hook events could accumulate unboundedly during long sessions
  * Fixed hang when `--mcp-config` points to a corrupted file
  * Fixed slow startup when many skills/plugins are installed
  * Fixed `cd <outside-dir> && <cmd>` permission prompt to surface the chained command instead of only showing "Yes, allow reading from `<dir>`/"
  * Fixed conditional `.claude/rules/*.md` files (with `paths:` frontmatter) and nested CLAUDE.md files not loading in print mode (`claude -p`)
  * Fixed `/clear` not fully clearing all session caches, reducing memory retention in long sessions
  * Fixed terminal flicker caused by animated elements at the scrollback boundary
  * Fixed UI frame drops on macOS when using MCP servers with OAuth (regression from 2.1.x)
  * Fixed occasional frame stalls during typing caused by synchronous debug log flushes
  * Fixed `TeammateIdle` and `TaskCompleted` hooks to support `{"continue": false, "stopReason": "..."}` to stop the teammate, matching `Stop` hook behavior
  * Fixed `WorktreeCreate` and `WorktreeRemove` plugin hooks being silently ignored
  * Fixed skill descriptions with colons (e.g., "Triggers include: X, Y, Z") failing to load from SKILL.md frontmatter
  * Fixed project skills without a `description:` frontmatter field not appearing in Claude's available skills list
  * Fixed `/context` showing identical token counts for all MCP tools from a server
  * Fixed literal `nul` file creation on Windows when the model uses CMD-style `2>nul` redirection in Git Bash
  * Fixed extra blank lines appearing below each tool call in the expanded subagent transcript view (Ctrl+O)
  * Fixed Tab/arrow keys not cycling Settings tabs when `/config` search box is focused but empty
  * Fixed service key OAuth sessions (CCR containers) spamming `[ERROR]` logs with 403s from profile-scoped endpoints
  * Fixed inconsistent color for "Remote Control active" status indicator
  * Fixed Voice waveform cursor covering the first suffix letter when dictating mid-input
  * Fixed Voice input showing all 5 spaces during warmup instead of capping at \~2 (aligning with the "keep holding…" hint)
  * Improved spinner performance by isolating the 50ms animation loop from the surrounding shell, reducing render and CPU overhead during turns
  * Improved UI rendering performance in native binaries with React Compiler
  * Improved `--worktree` startup by eliminating a git subprocess on the startup path
  * Improved macOS startup by eliminating redundant settings-file reloads when managed settings resolve
  * Improved macOS startup for Claude.ai enterprise/team users by skipping an unnecessary keychain lookup
  * Improved MCP `-p` startup by pipelining claude.ai config fetch with local connections and using a concurrency pool instead of sequential batching
  * Improved voice startup by removing imperceptible warmup pulse animations that were causing re-render stutter
  * Improved MCP binary content handling: tools returning PDFs, Office documents, or audio now save decoded bytes to disk with the correct file extension instead of dumping raw base64 into the conversation context. WebFetch also saves binary responses alongside its summary.
  * Improved memory usage in long sessions by stabilizing `onSubmit` across message updates
  * Improved LSP tool rendering and memory context building to no longer read entire files
  * Improved session upload and memory sync to avoid reading large files into memory before size/binary checks
  * Improved file operation performance by avoiding reading file contents for existence checks (6 sites)
  * Improved documentation to clarify that `--append-system-prompt-file` and `--system-prompt-file` work in interactive mode (the docs previously said print mode only)
  * Reduced baseline memory by \~16MB by deferring Yoga WASM preloading
  * Reduced memory footprint for SDK and CCR sessions using stream-json output
  * Reduced memory usage when resuming large sessions (including compacted history)
  * Reduced token usage on multi-agent tasks with more concise subagent final reports
  * Changed Sonnet 4.5 users on Pro/Max/Team Premium to be automatically migrated to Sonnet 4.6
  * Changed the `/resume` picker to show your most recent prompt instead of the first one. This also resolves some titles appearing as `(session)`.
  * Changed claude.ai MCP connector failures to show a notification instead of silently disappearing from the tool list
  * Changed example command suggestions to be generated deterministically instead of calling Haiku
  * Changed resuming after compaction to no longer produce a preamble recap before continuing
  * \[SDK] Changed task creation to no longer require the `activeForm` field — the spinner falls back to the task subject
  * \[VSCode] Added compaction display as a collapsible "Compacted chat" card with the summary inside
  * \[VSCode] The permission mode picker now respects `permissions.disableBypassPermissionsMode` from your effective Claude Code settings (including managed/policy settings) — when set to `disable`, bypass permissions mode is hidden from the picker
  * \[VSCode] Fixed RTL text (Arabic, Hebrew, Persian) rendering reversed in the chat panel (regression in v2.1.63)
</Update>

<Update label="2.1.68" description="March 4, 2026">
  * Opus 4.6 now defaults to medium effort for Max and Team subscribers. Medium effort works well for most tasks — it's the sweet spot between speed and thoroughness. You can change this anytime with `/model`
  * Re-introduced the "ultrathink" keyword to enable high effort for the next turn
  * Removed Opus 4 and 4.1 from Claude Code on the first-party API — users with these models pinned are automatically moved to Opus 4.6
</Update>

<Update label="2.1.66" description="March 4, 2026">
  * Reduced spurious error logging
</Update>

<Update label="2.1.63" description="February 28, 2026">
  * Added `/simplify` and `/batch` bundled slash commands
  * Fixed local slash command output like /cost appearing as user-sent messages instead of system messages in the UI
  * Project configs & auto memory now shared across git worktrees of the same repository
  * Added `ENABLE_CLAUDEAI_MCP_SERVERS=false` env var to opt out from making claude.ai MCP servers available
  * Improved `/model` command to show the currently active model in the slash command menu
  * Added HTTP hooks, which can POST JSON to a URL and receive JSON instead of running a shell command
  * Fixed listener leak in bridge polling loop
  * Fixed listener leak in MCP OAuth flow cleanup
  * Added manual URL paste fallback during MCP OAuth authentication. If the automatic localhost redirect doesn't work, you can paste the callback URL to complete authentication.
  * Fixed memory leak when navigating hooks configuration menu
  * Fixed listener leak in interactive permission handler during auto-approvals
  * Fixed file count cache ignoring glob ignore patterns
  * Fixed memory leak in bash command prefix cache
  * Fixed MCP tool/resource cache leak on server reconnect
  * Fixed IDE host IP detection cache incorrectly sharing results across ports
  * Fixed WebSocket listener leak on transport reconnect
  * Fixed memory leak in git root detection cache that could cause unbounded growth in long-running sessions
  * Fixed memory leak in JSON parsing cache that grew unbounded over long sessions
  * VSCode: Fixed remote sessions not appearing in conversation history
  * Fixed a race condition in the REPL bridge where new messages could arrive at the server interleaved with historical messages during the initial connection flush, causing message ordering issues.
  * Fixed memory leak where long-running teammates retained all messages in AppState even after conversation compaction
  * Fixed a memory leak where MCP server fetch caches were not cleared on disconnect, causing growing memory usage with servers that reconnect frequently
  * Improved memory usage in long sessions with subagents by stripping heavy progress message payloads during context compaction
  * Added "Always copy full response" option to the `/copy` picker. When selected, future `/copy` commands will skip the code block picker and copy the full response directly.
  * VSCode: Added session rename and remove actions to the sessions list
  * Fixed `/clear` not resetting cached skills, which could cause stale skill content to persist in the new conversation
</Update>

<Update label="2.1.62" description="February 27, 2026">
  * Fixed prompt suggestion cache regression that reduced cache hit rates
</Update>

<Update label="2.1.61" description="February 26, 2026">
  * Fixed concurrent writes corrupting config file on Windows
</Update>

<Update label="2.1.59" description="February 26, 2026">
  * Claude automatically saves useful context to auto-memory. Manage with /memory
  * Added `/copy` command to show an interactive picker when code blocks are present, allowing selection of individual code blocks or the full response.
  * Improved "always allow" prefix suggestions for compound bash commands (e.g. `cd /tmp && git fetch && git push`) to compute smarter per-subcommand prefixes instead of treating the whole command as one
  * Improved ordering of short task lists
  * Improved memory usage in multi-agent sessions by releasing completed subagent task state
  * Fixed MCP OAuth token refresh race condition when running multiple Claude Code instances simultaneously
  * Fixed shell commands not showing a clear error message when the working directory has been deleted
  * Fixed config file corruption that could wipe authentication when multiple Claude Code instances ran simultaneously
</Update>

<Update label="2.1.58" description="February 25, 2026">
  * Expand Remote Control to more users
</Update>

<Update label="2.1.56" description="February 25, 2026">
  * VS Code: Fixed another cause of "command 'claude-vscode.editor.openLast' not found" crashes
</Update>

<Update label="2.1.55" description="February 25, 2026">
  * Fixed BashTool failing on Windows with EINVAL error
</Update>

<Update label="2.1.53" description="February 25, 2026">
  * Fixed a UI flicker where user input would briefly disappear after submission before the message rendered
  * Fixed bulk agent kill (ctrl+f) to send a single aggregate notification instead of one per agent, and to properly clear the command queue
  * Fixed graceful shutdown sometimes leaving stale sessions when using Remote Control by parallelizing teardown network calls
  * Fixed `--worktree` sometimes being ignored on first launch
  * Fixed a panic ("switch on corrupted value") on Windows
  * Fixed a crash that could occur when spawning many processes on Windows
  * Fixed a crash in the WebAssembly interpreter on Linux x64 & Windows x64
  * Fixed a crash that sometimes occurred after 2 minutes on Windows ARM64
</Update>

<Update label="2.1.52" description="February 24, 2026">
  * VS Code: Fixed extension crash on Windows ("command 'claude-vscode.editor.openLast' not found")
</Update>

<Update label="2.1.51" description="February 24, 2026">
  * Added `claude remote-control` subcommand for external builds, enabling local environment serving for all users.
  * Updated plugin marketplace default git timeout from 30s to 120s and added `CLAUDE_CODE_PLUGIN_GIT_TIMEOUT_MS` to configure.
  * Added support for custom npm registries and specific version pinning when installing plugins from npm sources
  * BashTool now skips login shell (`-l` flag) by default when a shell snapshot is available, improving command execution performance. Previously this required setting `CLAUDE_BASH_NO_LOGIN=true`.
  * Fixed a security issue where `statusLine` and `fileSuggestion` hook commands could execute without workspace trust acceptance in interactive mode.
  * Tool results larger than 50K characters are now persisted to disk (previously 100K). This reduces context window usage and improves conversation longevity.
  * Fixed a bug where duplicate `control_response` messages (e.g. from WebSocket reconnects) could cause API 400 errors by pushing duplicate assistant messages into the conversation.
  * Added `CLAUDE_CODE_ACCOUNT_UUID`, `CLAUDE_CODE_USER_EMAIL`, and `CLAUDE_CODE_ORGANIZATION_UUID` environment variables for SDK callers to provide account info synchronously, eliminating a race condition where early telemetry events lacked account metadata.
  * Fixed slash command autocomplete crashing when a plugin's SKILL.md description is a YAML array or other non-string type
  * The `/model` picker now shows human-readable labels (e.g., "Sonnet 4.5") instead of raw model IDs for pinned model versions, with an upgrade hint when a newer version is available.
  * Managed settings can now be set via macOS plist or Windows Registry. Learn more at [https://code.claude.com/docs/en/settings#settings-files](claude-code-settings-claude-code-docs-d4420b4b52.md#settings-files)
</Update>

<Update label="2.1.50" description="February 20, 2026">
  * Added support for `startupTimeout` configuration for LSP servers
  * Added `WorktreeCreate` and `WorktreeRemove` hook events, enabling custom VCS setup and teardown when agent worktree isolation creates or removes worktrees.
  * Fixed a bug where resumed sessions could be invisible when the working directory involved symlinks, because the session storage path was resolved at different times during startup. Also fixed session data loss on SSH disconnect by flushing session data before hooks and analytics in the graceful shutdown sequence.
  * Linux: Fixed native modules not loading on systems with glibc older than 2.30 (e.g., RHEL 8)
  * Fixed memory leak in agent teams where completed teammate tasks were never garbage collected from session state
  * Fixed `CLAUDE_CODE_SIMPLE` to fully strip down skills, session memory, custom agents, and CLAUDE.md token counting
  * Fixed `/mcp reconnect` freezing the CLI when given a server name that doesn't exist
  * Fixed memory leak where completed task state objects were never removed from AppState
  * Added support for `isolation: worktree` in agent definitions, allowing agents to declaratively run in isolated git worktrees.
  * `CLAUDE_CODE_SIMPLE` mode now also disables MCP tools, attachments, hooks, and CLAUDE.md file loading for a fully minimal experience.
  * Fixed bug where MCP tools were not discovered when tool search is enabled and a prompt is passed in as a launch argument
  * Improved memory usage during long sessions by clearing internal caches after compaction
  * Added `claude agents` CLI command to list all configured agents
  * Improved memory usage during long sessions by clearing large tool results after they have been processed
  * Fixed a memory leak where LSP diagnostic data was never cleaned up after delivery, causing unbounded memory growth in long sessions
  * Fixed a memory leak where completed task output was not freed from memory, reducing memory usage in long sessions with many tasks
  * Improved startup performance for headless mode (`-p` flag) by deferring Yoga WASM and UI component imports
  * Fixed prompt suggestion cache regression that reduced cache hit rates
  * Fixed unbounded memory growth in long sessions by capping file history snapshots
  * Added `CLAUDE_CODE_DISABLE_1M_CONTEXT` environment variable to disable 1M context window support
  * Opus 4.6 (fast mode) now includes the full 1M context window
  * VSCode: Added `/extra-usage` command support in VS Code sessions
  * Fixed memory leak where TaskOutput retained recent lines after cleanup
  * Fixed memory leak in CircularBuffer where cleared items were retained in the backing array
  * Fixed memory leak in shell command execution where ChildProcess and AbortController references were retained after cleanup
</Update>

<Update label="2.1.49" description="February 19, 2026">
  * Improved MCP OAuth authentication with step-up auth support and discovery caching, reducing redundant network requests during server connections
  * Added `--worktree` (`-w`) flag to start Claude in an isolated git worktree
  * Subagents support `isolation: "worktree"` for working in a temporary git worktree
  * Added Ctrl+F keybinding to kill background agents (two-press confirmation)
  * Agent definitions support `background: true` to always run as a background task
  * Plugins can ship `settings.json` for default configuration
  * Fixed file-not-found errors to suggest corrected paths when the model drops the repo folder
  * Fixed Ctrl+C and ESC being silently ignored when background agents are running and the main thread is idle. Pressing twice within 3 seconds now kills all background agents.
  * Fixed prompt suggestion cache regression that reduced cache hit rates.
  * Fixed `plugin enable` and `plugin disable` to auto-detect the correct scope when `--scope` is not specified, instead of always defaulting to user scope
  * Simple mode (`CLAUDE_CODE_SIMPLE`) now includes the file edit tool in addition to the Bash tool, allowing direct file editing in simple mode.
  * Permission suggestions are now populated when safety checks trigger an ask response, enabling SDK consumers to display permission options
  * Sonnet 4.5 with 1M context is being removed from the Max plan in favor of our frontier Sonnet 4.6 model, which now has 1M context. Please switch in /model.
  * Fixed verbose mode not updating thinking block display when toggled via `/config` — memo comparators now correctly detect verbose changes
  * Fixed unbounded WASM memory growth during long sessions by periodically resetting the tree-sitter parser
  * Fixed potential rendering issues caused by stale yoga layout references
  * Improved performance in non-interactive mode (`-p`) by skipping unnecessary API calls during startup
  * Improved performance by caching authentication failures for HTTP and SSE MCP servers, avoiding repeated connection attempts to servers requiring auth
  * Fixed unbounded memory growth during long-running sessions caused by Yoga WASM linear memory never shrinking
  * SDK model info now includes `supportsEffort`, `supportedEffortLevels`, and `supportsAdaptiveThinking` fields so consumers can discover model capabilities.
  * Added `ConfigChange` hook event that fires when configuration files change during a session, enabling enterprise security auditing and optional blocking of settings changes.
  * Improved startup performance by caching MCP auth failures to avoid redundant connection attempts
  * Improved startup performance by reducing HTTP calls for analytics token counting
  * Improved startup performance by batching MCP tool token counting into a single API call
  * Fixed `disableAllHooks` setting to respect managed settings hierarchy — non-managed settings can no longer disable managed hooks set by policy (#26637)
  * Fixed `--resume` session picker showing raw XML tags for sessions that start with commands like `/clear`. Now correctly falls through to the session ID fallback.
  * Improved permission prompts for path safety and working directory blocks to show the reason for the restriction instead of a bare prompt with no context
</Update>

<Update label="2.1.47" description="February 18, 2026">
  * Fixed FileWriteTool line counting to preserve intentional trailing blank lines instead of stripping them with `trimEnd()`.
  * Fixed Windows terminal rendering bugs caused by `os.EOL` (`\r\n`) in display code — line counts now show correct values instead of always showing 1 on Windows.
  * Improved VS Code plan preview: auto-updates as Claude iterates, enables commenting only when the plan is ready for review, and keeps the preview open when rejecting so Claude can revise.
  * Fixed a bug where bold and colored text in markdown output could shift to the wrong characters on Windows due to `\r\n` line endings.
  * Fixed compaction failing when conversation contains many PDF documents by stripping document blocks alongside images before sending to the compaction API (anthropics/claude-code#26188)
  * Improved memory usage in long-running sessions by releasing API stream buffers, agent context, and skill state after use
  * Improved startup performance by deferring SessionStart hook execution, reducing time-to-interactive by \~500ms.
  * Fixed an issue where bash tool output was silently discarded on Windows when using MSYS2 or Cygwin shells.
  * Improved performance of `@` file mentions - file suggestions now appear faster by pre-warming the index on startup and using session-based caching with background refresh.
  * Improved memory usage by trimming agent task message history after tasks complete
  * Improved memory usage during long agent sessions by eliminating O(n²) message accumulation in progress updates
  * Fixed the bash permission classifier to validate that returned match descriptions correspond to actual input rules, preventing hallucinated descriptions from incorrectly granting permissions
  * Fixed user-defined agents only loading one file on NFS/FUSE filesystems that report zero inodes (anthropics/claude-code#26044)
  * Fixed plugin agent skills silently failing to load when referenced by bare name instead of fully-qualified plugin name (anthropics/claude-code#25834)
  * Search patterns in collapsed tool results are now displayed in quotes for clarity
  * Windows: Fixed CWD tracking temp files never being cleaned up, causing them to accumulate indefinitely (anthropics/claude-code#17600)
  * Use `ctrl+f` to kill all background agents instead of double-pressing ESC. Background agents now continue running when you press ESC to cancel the main thread, giving you more control over agent lifecycle.
  * Fixed API 400 errors ("thinking blocks cannot be modified") that occurred in sessions with concurrent agents, caused by interleaved streaming content blocks preventing proper message merging.
  * Simplified teammate navigation to use only Shift+Down (with wrapping) instead of both Shift+Up and Shift+Down.
  * Fixed an issue where a single file write/edit error would abort all other parallel file write/edit operations. Independent file mutations now complete even when a sibling fails.
  * Added `last_assistant_message` field to Stop and SubagentStop hook inputs, providing the final assistant response text so hooks can access it without parsing transcript files.
  * Fixed custom session titles set via `/rename` being lost after resuming a conversation (anthropics/claude-code#23610)
  * Fixed collapsed read/search hint text overflowing on narrow terminals by truncating from the start.
  * Fixed an issue where bash commands with backslash-newline continuation lines (e.g., long commands split across multiple lines with `\`) would produce spurious empty arguments, potentially breaking command execution.
  * Fixed built-in slash commands (`/help`, `/model`, `/compact`, etc.) being hidden from the autocomplete dropdown when many user skills are installed (anthropics/claude-code#22020)
  * Fixed MCP servers not appearing in the MCP Management Dialog after deferred loading
  * Fixed session name persisting in status bar after `/clear` command (anthropics/claude-code#26082)
  * Fixed crash when a skill's `name` or `description` in SKILL.md frontmatter is a bare number (e.g., `name: 3000`) — the value is now properly coerced to a string (anthropics/claude-code#25837)
  * Fixed /resume silently dropping sessions when the first message exceeds 16KB or uses array-format content (anthropics/claude-code#25721)
  * Added `chat:newline` keybinding action for configurable multi-line input (anthropics/claude-code#26075)
  * Added `added_dirs` to the statusline JSON `workspace` section, exposing directories added via `/add-dir` to external scripts (anthropics/claude-code#26096)
  * Fixed `claude doctor` misclassifying mise and asdf-managed installations as native installs (anthropics/claude-code#26033)
  * Fixed zsh heredoc failing with "read-only file system" error in sandboxed commands (anthropics/claude-code#25990)
  * Fixed agent progress indicator showing inflated tool use count (anthropics/claude-code#26023)
  * Fixed image pasting not working on WSL2 systems where Windows copies images as BMP format (anthropics/claude-code#25935)
  * Fixed background agent results returning raw transcript data instead of the agent's final answer (anthropics/claude-code#26012)
  * Fixed Warp terminal incorrectly prompting for Shift+Enter setup when it supports it natively (anthropics/claude-code#25957)
  * Fixed CJK wide characters causing misaligned timestamps and layout elements in the TUI (anthropics/claude-code#26084)
  * Fixed custom agent `model` field in `.claude/agents/*.md` being ignored when spawning team teammates (anthropics/claude-code#26064)
  * Fixed plan mode being lost after context compaction, causing the model to switch from planning to implementation mode (anthropics/claude-code#26061)
  * Fixed `alwaysThinkingEnabled: true` in settings.json not enabling thinking mode on Bedrock and Vertex providers (anthropics/claude-code#26074)
  * Fixed `tool_decision` OTel telemetry event not being emitted in headless/SDK mode (anthropics/claude-code#26059)
  * Fixed session name being lost after context compaction — renamed sessions now preserve their custom title through compaction (anthropics/claude-code#26121)
  * Increased initial session count in resume picker from 10 to 50 for faster session discovery (anthropics/claude-code#26123)
  * Windows: fixed worktree session matching when drive letter casing differs (anthropics/claude-code#26123)
  * Fixed `/resume <session-id>` failing to find sessions whose first message exceeds 16KB (anthropics/claude-code#25920)
  * Fixed "Always allow" on multiline bash commands creating invalid permission patterns that corrupt settings (anthropics/claude-code#25909)
  * Fixed React crash (error #31) when a skill's `argument-hint` in SKILL.md frontmatter uses YAML sequence syntax (e.g., `[topic: foo | bar]`) — the value is now properly coerced to a string (anthropics/claude-code#25826)
  * Fixed crash when using `/fork` on sessions that used web search — null entries in search results from transcript deserialization are now handled gracefully (anthropics/claude-code#25811)
  * Fixed read-only git commands triggering FSEvents file watcher loops on macOS by adding --no-optional-locks flag (anthropics/claude-code#25750)
  * Fixed custom agents and skills not being discovered when running from a git worktree — project-level `.claude/agents/` and `.claude/skills/` from the main repository are now included (anthropics/claude-code#25816)
  * Fixed non-interactive subcommands like `claude doctor` and `claude plugin validate` being blocked inside nested Claude sessions (anthropics/claude-code#25803)
  * Windows: Fixed the same CLAUDE.md file being loaded twice when drive letter casing differs between paths (anthropics/claude-code#25756)
  * Fixed inline code spans in markdown being incorrectly parsed as bash commands (anthropics/claude-code#25792)
  * Fixed teammate spinners not respecting custom spinnerVerbs from settings (anthropics/claude-code#25748)
  * Fixed shell commands permanently failing after a command deletes its own working directory (anthropics/claude-code#26136)
  * Fixed hooks (PreToolUse, PostToolUse) silently failing to execute on Windows by using Git Bash instead of cmd.exe (anthropics/claude-code#25981)
  * Fixed LSP `findReferences` and other location-based operations returning results from gitignored files (e.g., `node_modules/`, `venv/`) (anthropics/claude-code#26051)
  * Moved config backup files from home directory root to `~/.claude/backups/` to reduce home directory clutter (anthropics/claude-code#26130)
  * Fixed sessions with large first prompts (>16KB) disappearing from the /resume list (anthropics/claude-code#26140)
  * Fixed shell functions with double-underscore prefixes (e.g., `__git_ps1`) not being preserved across shell sessions (anthropics/claude-code#25824)
  * Fixed spinner showing "0 tokens" counter before any tokens have been received (anthropics/claude-code#26105)
  * VSCode: Fixed conversation messages appearing dimmed while the AskUserQuestion dialog is open (anthropics/claude-code#26078)
  * Fixed background tasks failing in git worktrees due to remote URL resolution reading from worktree-specific gitdir instead of the main repository config (anthropics/claude-code#26065)
  * Fixed Right Alt key leaving visible `[25~` escape sequence residue in the input field on Windows/Git Bash terminals (anthropics/claude-code#25943)
  * The `/rename` command now updates the terminal tab title by default (anthropics/claude-code#25789)
  * Fixed Edit tool silently corrupting Unicode curly quotes (\u201c\u201d \u2018\u2019) by replacing them with straight quotes when making edits (anthropics/claude-code#26141)
  * Fixed OSC 8 hyperlinks only being clickable on the first line when link text wraps across multiple terminal lines.
</Update>

<Update label="2.1.46" description="February 18, 2026">
  * Fixed orphaned CC processes after terminal disconnect on macOS
  * Added support for using claude.ai MCP connectors in Claude Code
</Update>

<Update label="2.1.45" description="February 17, 2026">
  * Added support for Claude Sonnet 4.6
  * Added support for reading `enabledPlugins` and `extraKnownMarketplaces` from `--add-dir` directories
  * Added `spinnerTipsOverride` setting to customize spinner tips — configure `tips` with an array of custom tip strings, and optionally set `excludeDefault: true` to show only your custom tips instead of the built-in ones
  * Added `SDKRateLimitInfo` and `SDKRateLimitEvent` types to the SDK, enabling consumers to receive rate limit status updates including utilization, reset times, and overage information
  * Fixed Agent Teams teammates failing on Bedrock, Vertex, and Foundry by propagating API provider environment variables to tmux-spawned processes (anthropics/claude-code#23561)
  * Fixed sandbox "operation not permitted" errors when writing temporary files on macOS by using the correct per-user temp directory (anthropics/claude-code#21654)
  * Fixed Task tool (backgrounded agents) crashing with a `ReferenceError` on completion (anthropics/claude-code#22087)
  * Fixed autocomplete suggestions not being accepted on Enter when images are pasted in the input
  * Fixed skills invoked by subagents incorrectly appearing in main session context after compaction
  * Fixed excessive `.claude.json.backup` files accumulating on every startup
  * Fixed plugin-provided commands, agents, and hooks not being available immediately after installation without requiring a restart
  * Improved startup performance by removing eager loading of session history for stats caching
  * Improved memory usage for shell commands that produce large output — RSS no longer grows unboundedly with command output size
  * Improved collapsed read/search groups to show the current file or search pattern being processed beneath the summary line while active
  * \[VSCode] Improved permission destination choice (project/user/session) to persist across sessions
</Update>

<Update label="2.1.44" description="February 16, 2026">
  * Fixed ENAMETOOLONG errors for deeply-nested directory paths
  * Fixed auth refresh errors
</Update>

<Update label="2.1.43" description="February 16, 2026">
  * Fixed AWS auth refresh hanging indefinitely by adding a 3-minute timeout
  * Fixed spurious warnings for non-agent markdown files in `.claude/agents/` directory
  * Fixed structured-outputs beta header being sent unconditionally on Vertex/Bedrock
</Update>

<Update label="2.1.42" description="February 13, 2026">
  * Improved startup performance by deferring Zod schema construction
  * Improved prompt cache hit rates by moving date out of system prompt
  * Added one-time Opus 4.6 effort callout for eligible users
  * Fixed /resume showing interrupt messages as session titles
  * Fixed image dimension limit errors to suggest /compact
</Update>

<Update label="2.1.41" description="February 13, 2026">
  * Added guard against launching Claude Code inside another Claude Code session
  * Fixed Agent Teams using wrong model identifier for Bedrock, Vertex, and Foundry customers
  * Fixed a crash when MCP tools return image content during streaming
  * Fixed /resume session previews showing raw XML tags instead of readable command names
  * Improved model error messages for Bedrock/Vertex/Foundry users with fallback suggestions
  * Fixed plugin browse showing misleading "Space to Toggle" hint for already-installed plugins
  * Fixed hook blocking errors (exit code 2) not showing stderr to the user
  * Added `speed` attribute to OTel events and trace spans for fast mode visibility
  * Added `claude auth login`, `claude auth status`, and `claude auth logout` CLI subcommands
  * Added Windows ARM64 (win32-arm64) native binary support
  * Improved `/rename` to auto-generate session name from conversation context when called without arguments
  * Improved narrow terminal layout for prompt footer
  * Fixed file resolution failing for @-mentions with anchor fragments (e.g., `@README.md#installation`)
  * Fixed FileReadTool blocking the process on FIFOs, `/dev/stdin`, and large files
  * Fixed background task notifications not being delivered in streaming Agent SDK mode
  * Fixed cursor jumping to end on each keystroke in classifier rule input
  * Fixed markdown link display text being dropped for raw URL
  * Fixed auto-compact failure error notifications being shown to users
  * Fixed permission wait time being included in subagent elapsed time display
  * Fixed proactive ticks firing while in plan mode
  * Fixed clear stale permission rules when settings change on disk
  * Fixed hook blocking errors showing stderr content in UI
</Update>

<Update label="2.1.39" description="February 10, 2026">
  * Improved terminal rendering performance
  * Fixed fatal errors being swallowed instead of displayed
  * Fixed process hanging after session close
  * Fixed character loss at terminal screen boundary
  * Fixed blank lines in verbose transcript view
</Update>

<Update label="2.1.38" description="February 10, 2026">
  * Fixed VS Code terminal scroll-to-top regression introduced in 2.1.37
  * Fixed Tab key queueing slash commands instead of autocompleting
  * Fixed bash permission matching for commands using environment variable wrappers
  * Fixed text between tool uses disappearing when not using streaming
  * Fixed duplicate sessions when resuming in VS Code extension
  * Improved heredoc delimiter parsing to prevent command smuggling
  * Blocked writes to `.claude/skills` directory in sandbox mode
</Update>

<Update label="2.1.37" description="February 7, 2026">
  * Fixed an issue where /fast was not immediately available after enabling /extra-usage
</Update>

<Update label="2.1.36" description="February 7, 2026">
  * Fast mode is now available for Opus 4.6. Learn more at [https://code.claude.com/docs/en/fast-mode](fast-mode-847ef2d690.md)
</Update>

<Update label="2.1.34" description="February 6, 2026">
  * Fixed a crash when agent teams setting changed between renders
  * Fixed a bug where commands excluded from sandboxing (via `sandbox.excludedCommands` or `dangerouslyDisableSandbox`) could bypass the Bash ask permission rule when `autoAllowBashIfSandboxed` was enabled
</Update>

<Update label="2.1.33" description="February 6, 2026">
  * Fixed agent teammate sessions in tmux to send and receive messages
  * Fixed warnings about agent teams not being available on your current plan
  * Added `TeammateIdle` and `TaskCompleted` hook events for multi-agent workflows
  * Added support for restricting which sub-agents can be spawned via `Task(agent_type)` syntax in agent "tools" frontmatter
  * Added `memory` frontmatter field support for agents, enabling persistent memory with `user`, `project`, or `local` scope
  * Added plugin name to skill descriptions and `/skills` menu for better discoverability
  * Fixed an issue where submitting a new message while the model was in extended thinking would interrupt the thinking phase
  * Fixed an API error that could occur when aborting mid-stream, where whitespace text combined with a thinking block would bypass normalization and produce an invalid request
  * Fixed API proxy compatibility issue where 404 errors on streaming endpoints no longer triggered non-streaming fallback
  * Fixed an issue where proxy settings configured via `settings.json` environment variables were not applied to WebFetch and other HTTP requests on the Node.js build
  * Fixed `/resume` session picker showing raw XML markup instead of clean titles for sessions started with slash commands
  * Improved error messages for API connection failures — now shows specific cause (e.g., ECONNREFUSED, SSL errors) instead of generic "Connection error"
  * Errors from invalid managed settings are now surfaced
  * VSCode: Added support for remote sessions, allowing OAuth users to browse and resume sessions from claude.ai
  * VSCode: Added git branch and message count to the session picker, with support for searching by branch name
  * VSCode: Fixed scroll-to-bottom under-scrolling on initial session load and session switch
</Update>

<Update label="2.1.32" description="February 5, 2026">
  * Claude Opus 4.6 is now available!
  * Added research preview agent teams feature for multi-agent collaboration (token-intensive feature, requires setting CLAUDE\_CODE\_EXPERIMENTAL\_AGENT\_TEAMS=1)
  * Claude now automatically records and recalls memories as it works
  * Added "Summarize from here" to the message selector, allowing partial conversation summarization.
  * Skills defined in `.claude/skills/` within additional directories (`--add-dir`) are now loaded automatically.
  * Fixed `@` file completion showing incorrect relative paths when running from a subdirectory
  * Updated --resume to re-use --agent value specified in previous conversation by default.
  * Fixed: Bash tool no longer throws "Bad substitution" errors when heredocs contain JavaScript template literals like `${index + 1}`, which previously interrupted tool execution
  * Skill character budget now scales with context window (2% of context), so users with larger context windows can see more skill descriptions without truncation
  * Fixed Thai/Lao spacing vowels (สระ า, ำ) not rendering correctly in the input field
  * VSCode: Fixed slash commands incorrectly being executed when pressing Enter with preceding text in the input field
  * VSCode: Added spinner when loading past conversations list
</Update>

<Update label="2.1.31" description="February 4, 2026">
  * Added session resume hint on exit, showing how to continue your conversation later
  * Added support for full-width (zenkaku) space input from Japanese IME in checkbox selection
  * Fixed PDF too large errors permanently locking up sessions, requiring users to start a new conversation
  * Fixed bash commands incorrectly reporting failure with "Read-only file system" errors when sandbox mode was enabled
  * Fixed a crash that made sessions unusable after entering plan mode when project config in `~/.claude.json` was missing default fields
  * Fixed `temperatureOverride` being silently ignored in the streaming API path, causing all streaming requests to use the default temperature (1) regardless of the configured override
  * Fixed LSP shutdown/exit compatibility with strict language servers that reject null params
  * Improved system prompts to more clearly guide the model toward using dedicated tools (Read, Edit, Glob, Grep) instead of bash equivalents (`cat`, `sed`, `grep`, `find`), reducing unnecessary bash command usage
  * Improved PDF and request size error messages to show actual limits (100 pages, 20MB)
  * Reduced layout jitter in the terminal when the spinner appears and disappears during streaming
  * Removed misleading Anthropic API pricing from model selector for third-party provider (Bedrock, Vertex, Foundry) users
</Update>

<Update label="2.1.30" description="February 3, 2026">
  * Added `pages` parameter to the Read tool for PDFs, allowing specific page ranges to be read (e.g., `pages: "1-5"`). Large PDFs (>10 pages) now return a lightweight reference when `@` mentioned instead of being inlined into context.
  * Added pre-configured OAuth client credentials for MCP servers that don't support Dynamic Client Registration (e.g., Slack). Use `--client-id` and `--client-secret` with `claude mcp add`.
  * Added `/debug` for Claude to help troubleshoot the current session
  * Added support for additional `git log` and `git show` flags in read-only mode (e.g., `--topo-order`, `--cherry-pick`, `--format`, `--raw`)
  * Added token count, tool uses, and duration metrics to Task tool results
  * Added reduced motion mode to the config
  * Fixed phantom "(no content)" text blocks appearing in API conversation history, reducing token waste and potential model confusion
  * Fixed prompt cache not correctly invalidating when tool descriptions or input schemas changed, only when tool names changed
  * Fixed 400 errors that could occur after running `/login` when the conversation contained thinking blocks
  * Fixed a hang when resuming sessions with corrupted transcript files containing `parentUuid` cycles
  * Fixed rate limit message showing incorrect "/upgrade" suggestion for Max 20x users when extra-usage is unavailable
  * Fixed permission dialogs stealing focus while actively typing
  * Fixed subagents not being able to access SDK-provided MCP tools because they were not synced to the shared application state
  * Fixed a regression where Windows users with a `.bashrc` file could not run bash commands
  * Improved memory usage for `--resume` (68% reduction for users with many sessions) by replacing the session index with lightweight stat-based loading and progressive enrichment
  * Improved `TaskStop` tool to display the stopped command/task description in the result line instead of a generic "Task stopped" message
  * Changed `/model` to execute immediately instead of being queued
  * \[VSCode] Added multiline input support to the "Other" text input in question dialogs (use Shift+Enter for new lines)
  * \[VSCode] Fixed duplicate sessions appearing in the session list when starting a new conversation
</Update>

<Update label="2.1.29" description="January 31, 2026">
  * Fixed startup performance issues when resuming sessions that have `saved_hook_context`
</Update>

<Update label="2.1.27" description="January 30, 2026">
  * Added tool call failures and denials to debug logs
  * Fixed context management validation error for gateway users, ensuring `CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS=1` avoids the error
  * Added `--from-pr` flag to resume sessions linked to a specific GitHub PR number or URL
  * Sessions are now automatically linked to PRs when created via `gh pr create`
  * Fixed /context command not displaying colored output
  * Fixed status bar duplicating background task indicator when PR status was shown
  * Windows: Fixed bash command execution failing for users with `.bashrc` files
  * Windows: Fixed console windows flashing when spawning child processes
  * VSCode: Fixed OAuth token expiration causing 401 errors after extended sessions
</Update>

<Update label="2.1.25" description="January 29, 2026">
  * Fixed beta header validation error for gateway users on Bedrock and Vertex, ensuring `CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS=1` avoids the error
</Update>

<Update label="2.1.23" description="January 29, 2026">
  * Added customizable spinner verbs setting (`spinnerVerbs`)
  * Fixed mTLS and proxy connectivity for users behind corporate proxies or using client certificates
  * Fixed per-user temp directory isolation to prevent permission conflicts on shared systems
  * Fixed a race condition that could cause 400 errors when prompt caching scope was enabled
  * Fixed pending async hooks not being cancelled when headless streaming sessions ended
  * Fixed tab completion not updating the input field when accepting a suggestion
  * Fixed ripgrep search timeouts silently returning empty results instead of reporting errors
  * Improved terminal rendering performance with optimized screen data layout
  * Changed Bash commands to show timeout duration alongside elapsed time
  * Changed merged pull requests to show a purple status indicator in the prompt footer
  * \[IDE] Fixed model options displaying incorrect region strings for Bedrock users in headless mode
</Update>

<Update label="2.1.22" description="January 28, 2026">
  * Fixed structured outputs for non-interactive (-p) mode
</Update>

<Update label="2.1.21" description="January 28, 2026">
  * Added support for full-width (zenkaku) number input from Japanese IME in option selection prompts
  * Fixed shell completion cache files being truncated on exit
  * Fixed API errors when resuming sessions that were interrupted during tool execution
  * Fixed auto-compact triggering too early on models with large output token limits
  * Fixed task IDs potentially being reused after deletion
  * Fixed file search not working in VS Code extension on Windows
  * Improved read/search progress indicators to show "Reading…" while in progress and "Read" when complete
  * Improved Claude to prefer file operation tools (Read, Edit, Write) over bash equivalents (cat, sed, awk)
  * \[VSCode] Added automatic Python virtual environment activation, ensuring `python` and `pip` commands use the correct interpreter (configurable via `claudeCode.usePythonEnvironment` setting)
  * \[VSCode] Fixed message action buttons having incorrect background colors
</Update>

<Update label="2.1.20" description="January 27, 2026">
  * Added arrow key history navigation in vim normal mode when cursor cannot move further
  * Added external editor shortcut (Ctrl+G) to the help menu for better discoverability
  * Added PR review status indicator to the prompt footer, showing the current branch's PR state (approved, changes requested, pending, or draft) as a colored dot with a clickable link
  * Added support for loading `CLAUDE.md` files from additional directories specified via `--add-dir` flag (requires setting `CLAUDE_CODE_ADDITIONAL_DIRECTORIES_CLAUDE_MD=1`)
  * Added ability to delete tasks via the `TaskUpdate` tool
  * Fixed session compaction issues that could cause resume to load full history instead of the compact summary
  * Fixed agents sometimes ignoring user messages sent while actively working on a task
  * Fixed wide character (emoji, CJK) rendering artifacts where trailing columns were not cleared when replaced by narrower characters
  * Fixed JSON parsing errors when MCP tool responses contain special Unicode characters
  * Fixed up/down arrow keys in multi-line and wrapped text input to prioritize cursor movement over history navigation
  * Fixed draft prompt being lost when pressing UP arrow to navigate command history
  * Fixed ghost text flickering when typing slash commands mid-input
  * Fixed marketplace source removal not properly deleting settings
  * Fixed duplicate output in some commands like `/context`
  * Fixed task list sometimes showing outside the main conversation view
  * Fixed syntax highlighting for diffs occurring within multiline constructs like Python docstrings
  * Fixed crashes when cancelling tool use
  * Improved `/sandbox` command UI to show dependency status with installation instructions when dependencies are missing
  * Improved thinking status text with a subtle shimmer animation
  * Improved task list to dynamically adjust visible items based on terminal height
  * Improved fork conversation hint to show how to resume the original session
  * Changed collapsed read/search groups to show present tense ("Reading", "Searching for") while in progress, and past tense ("Read", "Searched for") when complete
  * Changed `ToolSearch` results to appear as a brief notification instead of inline in the conversation
  * Changed the `/commit-push-pr` skill to automatically post PR URLs to Slack channels when configured via MCP tools
  * Changed the `/copy` command to be available to all users
  * Changed background agents to prompt for tool permissions before launching
  * Changed permission rules like `Bash(*)` to be accepted and treated as equivalent to `Bash`
  * Changed config backups to be timestamped and rotated (keeping 5 most recent) to prevent data loss
</Update>

<Update label="2.1.19" description="January 23, 2026">
  * Added env var `CLAUDE_CODE_ENABLE_TASKS`, set to `false` to keep the old system temporarily
  * Added shorthand `$0`, `$1`, etc. for accessing individual arguments in custom commands
  * Fixed crashes on processors without AVX instruction support
  * Fixed dangling Claude Code processes when terminal is closed by catching EIO errors from `process.exit()` and using SIGKILL as fallback
  * Fixed `/rename` and `/tag` not updating the correct session when resuming from a different directory (e.g., git worktrees)
  * Fixed resuming sessions by custom title not working when run from a different directory
  * Fixed pasted text content being lost when using prompt stash (Ctrl+S) and restore
  * Fixed agent list displaying "Sonnet (default)" instead of "Inherit (default)" for agents without an explicit model setting
  * Fixed backgrounded hook commands not returning early, potentially causing the session to wait on a process that was intentionally backgrounded
  * Fixed file write preview omitting empty lines
  * Changed skills without additional permissions or hooks to be allowed without requiring approval
  * Changed indexed argument syntax from `$ARGUMENTS.0` to `$ARGUMENTS[0]` (bracket syntax)
  * \[SDK] Added replay of `queued_command` attachment messages as `SDKUserMessageReplay` events when `replayUserMessages` is enabled
  * \[VSCode] Enabled session forking and rewind functionality for all users
</Update>

<Update label="2.1.18" description="January 23, 2026">
  * Added customizable keyboard shortcuts. Configure keybindings per context, create chord sequences, and personalize your workflow. Run `/keybindings` to get started. Learn more at [https://code.claude.com/docs/en/keybindings](customize-keyboard-shortcuts-claude-code-docs-f23eb8cfd3.md)
</Update>

<Update label="2.1.17" description="January 22, 2026">
  * Fixed crashes on processors without AVX instruction support
</Update>

<Update label="2.1.16" description="January 22, 2026">
  * Added new task management system, including new capabilities like dependency tracking
  * \[VSCode] Added native plugin management support
  * \[VSCode] Added ability for OAuth users to browse and resume remote Claude sessions from the Sessions dialog
  * Fixed out-of-memory crashes when resuming sessions with heavy subagent usage
  * Fixed an issue where the "context remaining" warning was not hidden after running `/compact`
  * Fixed session titles on the resume screen not respecting the user's language setting
  * \[IDE] Fixed a race condition on Windows where the Claude Code sidebar view container would not appear on start
</Update>

<Update label="2.1.15" description="January 21, 2026">
  * Added deprecation notification for npm installations - run `claude install` or see [https://docs.anthropic.com/en/docs/claude-code/getting-started](https://docs.anthropic.com/en/docs/claude-code/getting-started) for more options
  * Improved UI rendering performance with React Compiler
  * Fixed the "Context left until auto-compact" warning not disappearing after running `/compact`
  * Fixed MCP stdio server timeout not killing child process, which could cause UI freezes
</Update>

<Update label="2.1.14" description="January 20, 2026">
  * Added history-based autocomplete in bash mode (`!`) - type a partial command and press Tab to complete from your bash command history
  * Added search to installed plugins list - type to filter by name or description
  * Added support for pinning plugins to specific git commit SHAs, allowing marketplace entries to install exact versions
  * Fixed a regression where the context window blocking limit was calculated too aggressively, blocking users at \~65% context usage instead of the intended \~98%
  * Fixed memory issues that could cause crashes when running parallel subagents
  * Fixed memory leak in long-running sessions where stream resources were not cleaned up after shell commands completed
  * Fixed `@` symbol incorrectly triggering file autocomplete suggestions in bash mode
  * Fixed `@`-mention menu folder click behavior to navigate into directories instead of selecting them
  * Fixed `/feedback` command generating invalid GitHub issue URLs when description is very long
  * Fixed `/context` command to show the same token count and percentage as the status line in verbose mode
  * Fixed an issue where `/config`, `/context`, `/model`, and `/todos` command overlays could close unexpectedly
  * Fixed slash command autocomplete selecting wrong command when typing similar commands (e.g., `/context` vs `/compact`)
  * Fixed inconsistent back navigation in plugin marketplace when only one marketplace is configured
  * Fixed iTerm2 progress bar not clearing properly on exit, preventing lingering indicators and bell sounds
  * Improved backspace to delete pasted text as a single token instead of one character at a time
  * \[VSCode] Added `/usage` command to display current plan usage
</Update>

<Update label="2.1.12" description="January 17, 2026">
  * Fixed message rendering bug
</Update>

<Update label="2.1.11" description="January 17, 2026">
  * Fixed excessive MCP connection requests for HTTP/SSE transports
</Update>

<Update label="2.1.10" description="January 17, 2026">
  * Added new `Setup` hook event that can be triggered via `--init`, `--init-only`, or `--maintenance` CLI flags for repository setup and maintenance operations
  * Added keyboard shortcut 'c' to copy OAuth URL when browser doesn't open automatically during login
  * Fixed a crash when running bash commands containing heredocs with JavaScript template literals like `${index + 1}`
  * Improved startup to capture keystrokes typed before the REPL is fully ready
  * Improved file suggestions to show as removable attachments instead of inserting text when accepted
  * \[VSCode] Added install count display to plugin listings
  * \[VSCode] Added trust warning when installing plugins
</Update>

<Update label="2.1.9" description="January 16, 2026">
  * Added `auto:N` syntax for configuring the MCP tool search auto-enable threshold, where N is the context window percentage (0-100)
  * Added `plansDirectory` setting to customize where plan files are stored
  * Added external editor support (Ctrl+G) in AskUserQuestion "Other" input field
  * Added session URL attribution to commits and PRs created from web sessions
  * Added support for `PreToolUse` hooks to return `additionalContext` to the model
  * Added `${CLAUDE_SESSION_ID}` string substitution for skills to access the current session ID
  * Fixed long sessions with parallel tool calls failing with an API error about orphan tool\_result blocks
  * Fixed MCP server reconnection hanging when cached connection promise never resolves
  * Fixed Ctrl+Z suspend not working in terminals using Kitty keyboard protocol (Ghostty, iTerm2, kitty, WezTerm)
</Update>

<Update label="2.1.7" description="January 14, 2026">
  * Added `showTurnDuration` setting to hide turn duration messages (e.g., "Cooked for 1m 6s")
  * Added ability to provide feedback when accepting permission prompts
  * Added inline display of agent's final response in task notifications, making it easier to see results without reading the full transcript file
  * Fixed security vulnerability where wildcard permission rules could match compound commands containing shell operators
  * Fixed false "file modified" errors on Windows when cloud sync tools, antivirus scanners, or Git touch file timestamps without changing content
  * Fixed orphaned tool\_result errors when sibling tools fail during streaming execution
  * Fixed context window blocking limit being calculated using the full context window instead of the effective context window (which reserves space for max output tokens)
  * Fixed spinner briefly flashing when running local slash commands like `/model` or `/theme`
  * Fixed terminal title animation jitter by using fixed-width braille characters
  * Fixed plugins with git submodules not being fully initialized when installed
  * Fixed bash commands failing on Windows when temp directory paths contained characters like `t` or `n` that were misinterpreted as escape sequences
  * Improved typing responsiveness by reducing memory allocation overhead in terminal rendering
  * Enabled MCP tool search auto mode by default for all users. When MCP tool descriptions exceed 10% of the context window, they are automatically deferred and discovered via the MCPSearch tool instead of being loaded upfront. This reduces context usage for users with many MCP tools configured. Users can disable this by adding `MCPSearch` to `disallowedTools` in their settings.
  * Changed OAuth and API Console URLs from console.anthropic.com to platform.claude.com
  * \[VSCode] Fixed `claudeProcessWrapper` setting passing the wrapper path instead of the Claude binary path
</Update>

<Update label="2.1.6" description="January 13, 2026">
  * Added search functionality to `/config` command for quickly filtering settings
  * Added Updates section to `/doctor` showing auto-update channel and available npm versions (stable/latest)
  * Added date range filtering to `/stats` command - press `r` to cycle between Last 7 days, Last 30 days, and All time
  * Added automatic discovery of skills from nested `.claude/skills` directories when working with files in subdirectories
  * Added `context_window.used_percentage` and `context_window.remaining_percentage` fields to status line input for easier context window display
  * Added an error display when the editor fails during Ctrl+G
  * Fixed permission bypass via shell line continuation that could allow blocked commands to execute
  * Fixed false "File has been unexpectedly modified" errors when file watchers touch files without changing content
  * Fixed text styling (bold, colors) getting progressively misaligned in multi-line responses
  * Fixed the feedback panel closing unexpectedly when typing 'n' in the description field
  * Fixed rate limit warning appearing at low usage after weekly reset (now requires 70% usage)
  * Fixed rate limit options menu incorrectly auto-opening when resuming a previous session
  * Fixed numpad keys outputting escape sequences instead of characters in Kitty keyboard protocol terminals
  * Fixed Option+Return not inserting newlines in Kitty keyboard protocol terminals
  * Fixed corrupted config backup files accumulating in the home directory (now only one backup is created per config file)
  * Fixed `mcp list` and `mcp get` commands leaving orphaned MCP server processes
  * Fixed visual artifacts in ink2 mode when nodes become hidden via `display:none`
  * Improved the external CLAUDE.md imports approval dialog to show which files are being imported and from where
  * Improved the `/tasks` dialog to go directly to task details when there's only one background task running
  * Improved @ autocomplete with icons for different suggestion types and single-line formatting
  * Updated "Help improve Claude" setting fetch to refresh OAuth and retry when it fails due to a stale OAuth token
  * Changed task notification display to cap at 3 lines with overflow summary when multiple background tasks complete simultaneously
  * Changed terminal title to "Claude Code" on startup for better window identification
  * Removed ability to @-mention MCP servers to enable/disable - use `/mcp enable <name>` instead
  * \[VSCode] Fixed usage indicator not updating after manual compact
</Update>

<Update label="2.1.5" description="January 12, 2026">
  * Added `CLAUDE_CODE_TMPDIR` environment variable to override the temp directory used for internal temp files, useful for environments with custom temp directory requirements
</Update>

<Update label="2.1.4" description="January 11, 2026">
  * Added `CLAUDE_CODE_DISABLE_BACKGROUND_TASKS` environment variable to disable all background task functionality including auto-backgrounding and the Ctrl+B shortcut
  * Fixed "Help improve Claude" setting fetch to refresh OAuth and retry when it fails due to a stale OAuth token
</Update>

<Update label="2.1.3" description="January 9, 2026">
  * Merged slash commands and skills, simplifying the mental model with no change in behavior
  * Added release channel (`stable` or `latest`) toggle to `/config`
  * Added detection and warnings for unreachable permission rules, with warnings in `/doctor` and after saving rules that include the source of each rule and actionable fix guidance
  * Fixed plan files persisting across `/clear` commands, now ensuring a fresh plan file is used after clearing a conversation
  * Fixed false skill duplicate detection on filesystems with large inodes (e.g., ExFAT) by using 64-bit precision for inode values
  * Fixed mismatch between background task count in status bar and items shown in tasks dialog
  * Fixed sub-agents using the wrong model during conversation compaction
  * Fixed web search in sub-agents using incorrect model
  * Fixed trust dialog acceptance when running from the home directory not enabling trust-requiring features like hooks during the session
  * Improved terminal rendering stability by preventing uncontrolled writes from corrupting cursor state
  * Improved slash command suggestion readability by truncating long descriptions to 2 lines
  * Changed tool hook execution timeout from 60 seconds to 10 minutes
  * \[VSCode] Added clickable destination selector for permission requests, allowing you to choose where settings are saved (this project, all projects, shared with team, or session only)
</Update>

<Update label="2.1.2" description="January 9, 2026">
  * Added source path metadata to images dragged onto the terminal, helping Claude understand where images originated
  * Added clickable hyperlinks for file paths in tool output in terminals that support OSC 8 (like iTerm)
  * Added support for Windows Package Manager (winget) installations with automatic detection and update instructions
  * Added Shift+Tab keyboard shortcut in plan mode to quickly select "auto-accept edits" option
  * Added `FORCE_AUTOUPDATE_PLUGINS` environment variable to allow plugin autoupdate even when the main auto-updater is disabled
  * Added `agent_type` to SessionStart hook input, populated if `--agent` is specified
  * Fixed a command injection vulnerability in bash command processing where malformed input could execute arbitrary commands
  * Fixed a memory leak where tree-sitter parse trees were not being freed, causing WASM memory to grow unbounded over long sessions
  * Fixed binary files (images, PDFs, etc.) being accidentally included in memory when using `@include` directives in CLAUDE.md files
  * Fixed updates incorrectly claiming another installation is in progress
  * Fixed crash when socket files exist in watched directories (defense-in-depth for EOPNOTSUPP errors)
  * Fixed remote session URL and teleport being broken when using `/tasks` command
  * Fixed MCP tool names being exposed in analytics events by sanitizing user-specific server configurations
  * Improved Option-as-Meta hint on macOS to show terminal-specific instructions for native CSIu terminals like iTerm2, Kitty, and WezTerm
  * Improved error message when pasting images over SSH to suggest using `scp` instead of the unhelpful clipboard shortcut hint
  * Improved permission explainer to not flag routine dev workflows (git fetch/rebase, npm install, tests, PRs) as medium risk
  * Changed large bash command outputs to be saved to disk instead of truncated, allowing Claude to read the full content
  * Changed large tool outputs to be persisted to disk instead of truncated, providing full output access via file references
  * Changed `/plugins` installed tab to unify plugins and MCPs with scope-based grouping
  * Deprecated Windows managed settings path `C:\ProgramData\ClaudeCode\managed-settings.json` - administrators should migrate to `C:\Program Files\ClaudeCode\managed-settings.json`
  * \[SDK] Changed minimum zod peer dependency to ^4.0.0
  * \[VSCode] Fixed usage display not updating after manual compact
</Update>

<Update label="2.1.0" description="January 7, 2026">
  * Added automatic skill hot-reload - skills created or modified in `~/.claude/skills` or `.claude/skills` are now immediately available without restarting the session
  * Added support for running skills and slash commands in a forked sub-agent context using `context: fork` in skill frontmatter
  * Added support for `agent` field in skills to specify agent type for execution
  * Added `language` setting to configure Claude's response language (e.g., language: "japanese")
  * Changed Shift+Enter to work out of the box in iTerm2, WezTerm, Ghostty, and Kitty without modifying terminal configs
  * Added `respectGitignore` support in `settings.json` for per-project control over @-mention file picker behavior
  * Added `IS_DEMO` environment variable to hide email and organization from the UI, useful for streaming or recording sessions
  * Fixed security issue where sensitive data (OAuth tokens, API keys, passwords) could be exposed in debug logs
  * Fixed files and skills not being properly discovered when resuming sessions with `-c` or `--resume`
  * Fixed pasted content being lost when replaying prompts from history using up arrow or Ctrl+R search
  * Fixed Esc key with queued prompts to only move them to input without canceling the running task
  * Reduced permission prompts for complex bash commands
  * Fixed command search to prioritize exact and prefix matches on command names over fuzzy matches in descriptions
  * Fixed PreToolUse hooks to allow `updatedInput` when returning `ask` permission decision, enabling hooks to act as middleware while still requesting user consent
  * Fixed plugin path resolution for file-based marketplace sources
  * Fixed LSP tool being incorrectly enabled when no LSP servers were configured
  * Fixed background tasks failing with "git repository not found" error for repositories with dots in their names
  * Fixed Claude in Chrome support for WSL environments
  * Fixed Windows native installer silently failing when executable creation fails
  * Improved CLI help output to display options and subcommands in alphabetical order for easier navigation
  * Added wildcard pattern matching for Bash tool permissions using `*` at any position in rules (e.g., `Bash(npm *)`, `Bash(* install)`, `Bash(git * main)`)
  * Added unified Ctrl+B backgrounding for both bash commands and agents - pressing Ctrl+B now backgrounds all running foreground tasks simultaneously
  * Added support for MCP `list_changed` notifications, allowing MCP servers to dynamically update their available tools, prompts, and resources without requiring reconnection
  * Added `/teleport` and `/remote-env` slash commands for claude.ai subscribers, allowing them to resume and configure remote sessions
  * Added support for disabling specific agents using `Task(AgentName)` syntax in settings.json permissions or the `--disallowedTools` CLI flag
  * Added hooks support to agent frontmatter, allowing agents to define PreToolUse, PostToolUse, and Stop hooks scoped to the agent's lifecycle
  * Added hooks support for skill and slash command frontmatter
  * Added new Vim motions: `;` and `,` to repeat f/F/t/T motions, `y` operator for yank with `yy`/`Y`, `p`/`P` for paste, text objects (`iw`, `aw`, `iW`, `aW`, `i"`, `a"`, `i'`, `a'`, `i(`, `a(`, `i[`, `a[`, `i{`, `a{`), `>>` and `<<` for indent/dedent, and `J` to join lines
  * Added `/plan` command shortcut to enable plan mode directly from the prompt
  * Added slash command autocomplete support when `/` appears anywhere in input, not just at the beginning
  * Added `--tools` flag support in interactive mode to restrict which built-in tools Claude can use during interactive sessions
  * Added `CLAUDE_CODE_FILE_READ_MAX_OUTPUT_TOKENS` environment variable to override the default file read token limit
  * Added support for `once: true` config for hooks
  * Added support for YAML-style lists in frontmatter `allowed-tools` field for cleaner skill declarations
  * Added support for prompt and agent hook types from plugins (previously only command hooks were supported)
  * Added Cmd+V support for image paste in iTerm2 (maps to Ctrl+V)
  * Added left/right arrow key navigation for cycling through tabs in dialogs
  * Added real-time thinking block display in Ctrl+O transcript mode
  * Added filepath to full output in background bash task details dialog
  * Added Skills as a separate category in the context visualization
  * Fixed OAuth token refresh not triggering when server reports token expired but local expiration check disagrees
  * Fixed session persistence getting stuck after transient server errors by recovering from 409 conflicts when the entry was actually stored
  * Fixed session resume failures caused by orphaned tool results during concurrent tool execution
  * Fixed a race condition where stale OAuth tokens could be read from the keychain cache during concurrent token refresh attempts
  * Fixed AWS Bedrock subagents not inheriting EU/APAC cross-region inference model configuration, causing 403 errors when IAM permissions are scoped to specific regions
  * Fixed API context overflow when background tasks produce large output by truncating to 30K chars with file path reference
  * Fixed a hang when reading FIFO files by skipping symlink resolution for special file types
  * Fixed terminal keyboard mode not being reset on exit in Ghostty, iTerm2, Kitty, and WezTerm
  * Fixed Alt+B and Alt+F (word navigation) not working in iTerm2, Ghostty, Kitty, and WezTerm
  * Fixed `${CLAUDE_PLUGIN_ROOT}` not being substituted in plugin `allowed-tools` frontmatter, which caused tools to incorrectly require approval
  * Fixed files created by the Write tool using hardcoded 0o600 permissions instead of respecting the system umask
  * Fixed commands with `$()` command substitution failing with parse errors
  * Fixed multi-line bash commands with backslash continuations being incorrectly split and flagged for permissions
  * Fixed bash command prefix extraction to correctly identify subcommands after global options (e.g., `git -C /path log` now correctly matches `Bash(git log:*)` rules)
  * Fixed slash commands passed as CLI arguments (e.g., `claude /context`) not being executed properly
  * Fixed pressing Enter after Tab-completing a slash command selecting a different command instead of submitting the completed one
  * Fixed slash command argument hint flickering and inconsistent display when typing commands with arguments
  * Fixed Claude sometimes redundantly invoking the Skill tool when running slash commands directly
  * Fixed skill token estimates in `/context` to accurately reflect frontmatter-only loading
  * Fixed subagents sometimes not inheriting the parent's model by default
  * Fixed model picker showing incorrect selection for Bedrock/Vertex users using `--model haiku`
  * Fixed duplicate Bash commands appearing in permission request option labels
  * Fixed noisy output when background tasks complete - now shows clean completion message instead of raw output
  * Fixed background task completion notifications to appear proactively with bullet point
  * Fixed forked slash commands showing "AbortError" instead of "Interrupted" message when cancelled
  * Fixed cursor disappearing after dismissing permission dialogs
  * Fixed `/hooks` menu selecting wrong hook type when scrolling to a different option
  * Fixed images in queued prompts showing as "\[object Object]" when pressing Esc to cancel
  * Fixed images being silently dropped when queueing messages while backgrounding a task
  * Fixed large pasted images failing with "Image was too large" error
  * Fixed extra blank lines in multiline prompts containing CJK characters (Japanese, Chinese, Korean)
  * Fixed ultrathink keyword highlighting being applied to wrong characters when user prompt text wraps to multiple lines
  * Fixed collapsed "Reading X files…" indicator incorrectly switching to past tense when thinking blocks appear mid-stream
  * Fixed Bash read commands (like `ls` and `cat`) not being counted in collapsed read/search groups, causing groups to incorrectly show "Read 0 files"
  * Fixed spinner token counter to properly accumulate tokens from subagents during execution
  * Fixed memory leak in git diff parsing where sliced strings retained large parent strings
  * Fixed race condition where LSP tool could return "no server available" during startup
  * Fixed feedback submission hanging indefinitely when network requests timeout
  * Fixed search mode in plugin discovery and log selector views exiting when pressing up arrow
  * Fixed hook success message showing trailing colon when hook has no output
  * Multiple optimizations to improve startup performance
  * Improved terminal rendering performance when using native installer or Bun, especially for text with emoji, ANSI codes, and Unicode characters
  * Improved performance when reading Jupyter notebooks with many cells
  * Improved reliability for piped input like `cat refactor.md | claude`
  * Improved reliability for AskQuestion tool
  * Improved sed in-place edit commands to render as file edits with diff preview
  * Improved Claude to automatically continue when response is cut off due to output token limit, instead of showing an error message
  * Improved compaction reliability
  * Improved subagents (Task tool) to continue working after permission denial, allowing them to try alternative approaches
  * Improved skills to show progress while executing, displaying tool uses as they happen
  * Improved skills from `/skills/` directories to be visible in the slash command menu by default (opt-out with `user-invocable: false` in frontmatter)
  * Improved skill suggestions to prioritize recently and frequently used skills
  * Improved spinner feedback when waiting for the first response token
  * Improved token count display in spinner to include tokens from background agents
  * Improved incremental output for async agents to give the main thread more control and visibility
  * Improved permission prompt UX with Tab hint moved to footer, cleaner Yes/No input labels with contextual placeholders
  * Improved the Claude in Chrome notification with shortened help text and persistent display until dismissed
  * Improved macOS screenshot paste reliability with TIFF format support
  * Improved `/stats` output
  * Updated Atlassian MCP integration to use a more reliable default configuration (streamable HTTP)
  * Changed "Interrupted" message color from red to grey for a less alarming appearance
  * Removed permission prompt when entering plan mode - users can now enter plan mode without approval
  * Removed underline styling from image reference links
  * \[SDK] Changed minimum zod peer dependency to ^4.0.0
  * \[VSCode] Added currently selected model name to the context menu
  * \[VSCode] Added descriptive labels on auto-accept permission button (e.g., "Yes, allow npm for this project" instead of "Yes, and don't ask again")
  * \[VSCode] Fixed paragraph breaks not rendering in markdown content
  * \[VSCode] Fixed scrolling in the extension inadvertently scrolling the parent iframe
  * \[Windows] Fixed issue with improper rendering
</Update>

<Update label="2.0.76" description="January 7, 2026">
  * Fixed issue with macOS code-sign warning when using Claude in Chrome integration
</Update>

<Update label="2.0.75" description="January 7, 2026">
  * Minor bugfixes
</Update>

<Update label="2.0.74" description="December 19, 2025">
  * Added LSP (Language Server Protocol) tool for code intelligence features like go-to-definition, find references, and hover documentation
  * Added `/terminal-setup` support for Kitty, Alacritty, Zed, and Warp terminals
  * Added ctrl+t shortcut in `/theme` to toggle syntax highlighting on/off
  * Added syntax highlighting info to theme picker
  * Added guidance for macOS users when Alt shortcuts fail due to terminal configuration
  * Fixed skill `allowed-tools` not being applied to tools invoked by the skill
  * Fixed Opus 4.5 tip incorrectly showing when user was already using Opus
  * Fixed a potential crash when syntax highlighting isn't initialized correctly
  * Fixed visual bug in `/plugins discover` where list selection indicator showed while search box was focused
  * Fixed macOS keyboard shortcuts to display 'opt' instead of 'alt'
  * Improved `/context` command visualization with grouped skills and agents by source, slash commands, and sorted token count
  * \[Windows] Fixed issue with improper rendering
  * \[VSCode] Added gift tag pictogram for year-end promotion message
</Update>

<Update label="2.0.73" description="December 19, 2025">
  * Added clickable `[Image #N]` links that open attached images in the default viewer
  * Added alt-y yank-pop to cycle through kill ring history after ctrl-y yank
  * Added search filtering to the plugin discover screen (type to filter by name, description, or marketplace)
  * Added support for custom session IDs when forking sessions with `--session-id` combined with `--resume` or `--continue` and `--fork-session`
  * Fixed slow input history cycling and race condition that could overwrite text after message submission
  * Improved `/theme` command to open theme picker directly
  * Improved theme picker UI
  * Improved search UX across resume session, permissions, and plugins screens with a unified SearchBox component
  * \[VSCode] Added tab icon badges showing pending permissions (blue) and unread completions (orange)
</Update>

<Update label="2.0.72" description="December 17, 2025">
  * Added Claude in Chrome (Beta) feature that works with the Chrome extension ([https://claude.ai/chrome](https://claude.ai/chrome)) to let you control your browser directly from Claude Code
  * Reduced terminal flickering
  * Added scannable QR code to mobile app tip for quick app downloads
  * Added loading indicator when resuming conversations for better feedback
  * Fixed `/context` command not respecting custom system prompts in non-interactive mode
  * Fixed order of consecutive Ctrl+K lines when pasting with Ctrl+Y
  * Improved @ mention file suggestion speed (\~3× faster in git repositories)
  * Improved file suggestion performance in repos with `.ignore` or `.rgignore` files
  * Improved settings validation errors to be more prominent
  * Changed thinking toggle from Tab to Alt+T to avoid accidental triggers
</Update>

<Update label="2.0.71" description="December 16, 2025">
  * Added /config toggle to enable/disable prompt suggestions
  * Added `/settings` as an alias for the `/config` command
  * Fixed @ file reference suggestions incorrectly triggering when cursor is in the middle of a path
  * Fixed MCP servers from `.mcp.json` not loading when using `--dangerously-skip-permissions`
  * Fixed permission rules incorrectly rejecting valid bash commands containing shell glob patterns (e.g., `ls *.txt`, `for f in *.png`)
  * Bedrock: Environment variable `ANTHROPIC_BEDROCK_BASE_URL` is now respected for token counting and inference profile listing
  * New syntax highlighting engine for native build
</Update>

<Update label="2.0.70" description="December 15, 2025">
  * Added Enter key to accept and submit prompt suggestions immediately (tab still accepts for editing)
  * Added wildcard syntax `mcp__server__*` for MCP tool permissions to allow or deny all tools from a server
  * Added auto-update toggle for plugin marketplaces, allowing per-marketplace control over automatic updates
  * Added `current_usage` field to status line input, enabling accurate context window percentage calculations
  * Fixed input being cleared when processing queued commands while the user was typing
  * Fixed prompt suggestions replacing typed input when pressing Tab
  * Fixed diff view not updating when terminal is resized
  * Improved memory usage by 3x for large conversations
  * Improved resolution of stats screenshots copied to clipboard (Ctrl+S) for crisper images
  * Removed # shortcut for quick memory entry (tell Claude to edit your CLAUDE.md instead)
  * Fix thinking mode toggle in /config not persisting correctly
  * Improve UI for file creation permission dialog
</Update>

<Update label="2.0.69" description="December 13, 2025">
  * Minor bugfixes
</Update>

<Update label="2.0.68" description="December 12, 2025">
  * Fixed IME (Input Method Editor) support for languages like Chinese, Japanese, and Korean by correctly positioning the composition window at the cursor
  * Fixed a bug where disallowed MCP tools were visible to the model
  * Fixed an issue where steering messages could be lost while a subagent is working
  * Fixed Option+Arrow word navigation treating entire CJK (Chinese, Japanese, Korean) text sequences as a single word instead of navigating by word boundaries
  * Improved plan mode exit UX: show simplified yes/no dialog when exiting with empty or missing plan instead of throwing an error
  * Add support for enterprise managed settings. Contact your Anthropic account team to enable this feature.
</Update>

<Update label="2.0.67" description="December 12, 2025">
  * Thinking mode is now enabled by default for Opus 4.5
  * Thinking mode configuration has moved to /config
  * Added search functionality to `/permissions` command with `/` keyboard shortcut for filtering rules by tool name
  * Show reason why autoupdater is disabled in `/doctor`
  * Fixed false "Another process is currently updating Claude" error when running `claude update` while another instance is already on the latest version
  * Fixed MCP servers from `.mcp.json` being stuck in pending state when running in non-interactive mode (`-p` flag or piped input)
  * Fixed scroll position resetting after deleting a permission rule in `/permissions`
  * Fixed word deletion (opt+delete) and word navigation (opt+arrow) not working correctly with non-Latin text such as Cyrillic, Greek, Arabic, Hebrew, Thai, and Chinese
  * Fixed `claude install --force` not bypassing stale lock files
  * Fixed consecutive @\~/ file references in CLAUDE.md being incorrectly parsed due to markdown strikethrough interference
  * Windows: Fixed plugin MCP servers failing due to colons in log directory paths
</Update>

<Update label="2.0.65" description="December 11, 2025">
  * Added ability to switch models while writing a prompt using alt+p (linux, windows), option+p (macos).
  * Added context window information to status line input
  * Added `fileSuggestion` setting for custom `@` file search commands
  * Added `CLAUDE_CODE_SHELL` environment variable to override automatic shell detection (useful when login shell differs from actual working shell)
  * Fixed prompt not being saved to history when aborting a query with Escape
  * Fixed Read tool image handling to identify format from bytes instead of file extension
</Update>

<Update label="2.0.64" description="December 10, 2025">
  * Made auto-compacting instant
  * Agents and bash commands can run asynchronously and send messages to wake up the main agent
  * /stats now provides users with interesting CC stats, such as favorite model, usage graph, usage streak
  * Added named session support: use `/rename` to name sessions, `/resume <name>` in REPL or `claude --resume <name>` from the terminal to resume them
  * Added support for .claude/rules/\`.  See [https://code.claude.com/docs/en/memory](how-claude-remembers-your-project-claude-code-docs-f1c064262d.md) for details.
  * Added image dimension metadata when images are resized, enabling accurate coordinate mappings for large images
  * Fixed auto-loading .env when using native installer
  * Fixed `--system-prompt` being ignored when using `--continue` or `--resume` flags
  * Improved `/resume` screen with grouped forked sessions and keyboard shortcuts for preview (P) and rename (R)
  * VSCode: Added copy-to-clipboard button on code blocks and bash tool inputs
  * VSCode: Fixed extension not working on Windows ARM64 by falling back to x64 binary via emulation
  * Bedrock: Improve efficiency of token counting
  * Bedrock: Add support for `aws login` AWS Management Console credentials
  * Unshipped AgentOutputTool and BashOutputTool, in favor of a new unified TaskOutputTool
</Update>

<Update label="2.0.62" description="December 9, 2025">
  * Added "(Recommended)" indicator for multiple-choice questions, with the recommended option moved to the top of the list
  * Added `attribution` setting to customize commit and PR bylines (deprecates `includeCoAuthoredBy`)
  * Fixed duplicate slash commands appearing when \~/.claude is symlinked to a project directory
  * Fixed slash command selection not working when multiple commands share the same name
  * Fixed an issue where skill files inside symlinked skill directories could become circular symlinks
  * Fixed running versions getting removed because lock file incorrectly going stale
  * Fixed IDE diff tab not closing when rejecting file changes
</Update>

<Update label="2.0.61" description="December 7, 2025">
  * Reverted VSCode support for multiple terminal clients due to responsiveness issues.
</Update>

<Update label="2.0.60" description="December 6, 2025">
  * Added background agent support. Agents run in the background while you work
  * Added --disable-slash-commands CLI flag to disable all slash commands
  * Added model name to "Co-Authored-By" commit messages
  * Enabled "/mcp enable \[server-name]" or "/mcp disable \[server-name]" to quickly toggle all servers
  * Updated Fetch to skip summarization for pre-approved websites
  * VSCode: Added support for multiple terminal clients connecting to the IDE server simultaneously
</Update>

<Update label="2.0.59" description="December 4, 2025">
  * Added --agent CLI flag to override the agent setting for the current session
  * Added `agent` setting to configure main thread with a specific agent's system prompt, tool restrictions, and model
  * VS Code: Fixed .claude.json config file being read from incorrect location
</Update>

<Update label="2.0.58" description="December 3, 2025">
  * Pro users now have access to Opus 4.5 as part of their subscription!
  * Fixed timer duration showing "11m 60s" instead of "12m 0s"
  * Windows: Managed settings now prefer `C:\Program Files\ClaudeCode` if it exists. Support for `C:\ProgramData\ClaudeCode` will be removed in a future version.
</Update>

<Update label="2.0.57" description="December 3, 2025">
  * Added feedback input when rejecting plans, allowing users to tell Claude what to change
  * VSCode: Added streaming message support for real-time response display
</Update>

<Update label="2.0.56" description="December 2, 2025">
  * Added setting to enable/disable terminal progress bar (OSC 9;4)
  * VSCode Extension: Added support for VS Code's secondary sidebar (VS Code 1.97+), allowing Claude Code to be displayed in the right sidebar while keeping the file explorer on the left. Requires setting sidebar as Preferred Location in the config.
</Update>

<Update label="2.0.55" description="November 26, 2025">
  * Fixed proxy DNS resolution being forced on by default. Now opt-in via `CLAUDE_CODE_PROXY_RESOLVES_HOSTS=true` environment variable
  * Fixed keyboard navigation becoming unresponsive when holding down arrow keys in memory location selector
  * Improved AskUserQuestion tool to auto-submit single-select questions on the last question, eliminating the extra review screen for simple question flows
  * Improved fuzzy matching for `@` file suggestions with faster, more accurate results
</Update>

<Update label="2.0.54" description="November 26, 2025">
  * Hooks: Enable PermissionRequest hooks to process 'always allow' suggestions and apply permission updates
  * Fix issue with excessive iTerm notifications
</Update>

<Update label="2.0.52" description="November 24, 2025">
  * Fixed duplicate message display when starting Claude with a command line argument
  * Fixed `/usage` command progress bars to fill up as usage increases (instead of showing remaining percentage)
  * Fixed image pasting not working on Linux systems running Wayland (now falls back to wl-paste when xclip is unavailable)
  * Permit some uses of `$!` in bash commands
</Update>

<Update label="2.0.51" description="November 24, 2025">
  * Added Opus 4.5! [https://www.anthropic.com/news/claude-opus-4-5](https://www.anthropic.com/news/claude-opus-4-5)
  * Introducing Claude Code for Desktop: [https://claude.com/download](https://claude.com/download)
  * To give you room to try out our new model, we've updated usage limits for Claude Code users. See the Claude Opus 4.5 blog for full details
  * Pro users can now purchase extra usage for access to Opus 4.5 in Claude Code
  * Plan Mode now builds more precise plans and executes more thoroughly
  * Usage limit notifications now easier to understand
  * Switched `/usage` back to "% used"
  * Fixed handling of thinking errors
  * Fixed performance regression
</Update>

<Update label="2.0.50" description="November 21, 2025">
  * Fixed bug preventing calling MCP tools that have nested references in their input schemas
  * Silenced a noisy but harmless error during upgrades
  * Improved ultrathink text display
  * Improved clarity of 5-hour session limit warning message
</Update>

<Update label="2.0.49" description="November 21, 2025">
  * Added readline-style ctrl-y for pasting deleted text
  * Improved clarity of usage limit warning message
  * Fixed handling of subagent permissions
</Update>

<Update label="2.0.47" description="November 19, 2025">
  * Improved error messages and validation for `claude --teleport`
  * Improved error handling in `/usage`
  * Fixed race condition with history entry not getting logged at exit
  * Fixed Vertex AI configuration not being applied from `settings.json`
</Update>

<Update label="2.0.46" description="November 19, 2025">
  * Fixed image files being reported with incorrect media type when format cannot be detected from metadata
</Update>

<Update label="2.0.45" description="November 18, 2025">
  * Added support for Microsoft Foundry! See [https://code.claude.com/docs/en/azure-ai-foundry](https://code.claude.com/docs/en/azure-ai-foundry)
  * Added `PermissionRequest` hook to automatically approve or deny tool permission requests with custom logic
  * Send background tasks to Claude Code on the web by starting a message with `&`
</Update>

<Update label="2.0.43" description="November 18, 2025">
  * Added `permissionMode` field for custom agents
  * Added `tool_use_id` field to `PreToolUseHookInput` and `PostToolUseHookInput` types
  * Added skills frontmatter field to declare skills to auto-load for subagents
  * Added the `SubagentStart` hook event
  * Fixed nested `CLAUDE.md` files not loading when @-mentioning files
  * Fixed duplicate rendering of some messages in the UI
  * Fixed some visual flickers
  * Fixed NotebookEdit tool inserting cells at incorrect positions when cell IDs matched the pattern `cell-N`
</Update>

<Update label="2.0.42" description="November 15, 2025">
  * Added `agent_id` and `agent_transcript_path` fields to `SubagentStop` hooks.
</Update>

<Update label="2.0.41" description="November 14, 2025">
  * Added `model` parameter to prompt-based stop hooks, allowing users to specify a custom model for hook evaluation
  * Fixed slash commands from user settings being loaded twice, which could cause rendering issues
  * Fixed incorrect labeling of user settings vs project settings in command descriptions
  * Fixed crash when plugin command hooks timeout during execution
  * Fixed: Bedrock users no longer see duplicate Opus entries in the /model picker when using `--model haiku`
  * Fixed broken security documentation links in trust dialogs and onboarding
  * Fixed issue where pressing ESC to close the diff modal would also interrupt the model
  * ctrl-r history search landing on a slash command no longer cancels the search
  * SDK: Support custom timeouts for hooks
  * Allow more safe git commands to run without approval
  * Plugins: Added support for sharing and installing output styles
  * Teleporting a session from web will automatically set the upstream branch
</Update>

<Update label="2.0.37" description="November 11, 2025">
  * Fixed how idleness is computed for notifications
  * Hooks: Added matcher values for Notification hook events
  * Output Styles: Added `keep-coding-instructions` option to frontmatter
</Update>

<Update label="2.0.36" description="November 7, 2025">
  * Fixed: DISABLE\_AUTOUPDATER environment variable now properly disables package manager update notifications
  * Fixed queued messages being incorrectly executed as bash commands
  * Fixed input being lost when typing while a queued message is processed
</Update>

<Update label="2.0.35" description="November 6, 2025">
  * Improve fuzzy search results when searching commands
  * Improved VS Code extension to respect `chat.fontSize` and `chat.fontFamily` settings throughout the entire UI, and apply font changes immediately without requiring reload
  * Added `CLAUDE_CODE_EXIT_AFTER_STOP_DELAY` environment variable to automatically exit SDK mode after a specified idle duration, useful for automated workflows and scripts
  * Migrated `ignorePatterns` from project config to deny permissions in the localSettings.
  * Fixed menu navigation getting stuck on items with empty string or other falsy values (e.g., in the `/hooks` menu)
</Update>

<Update label="2.0.34" description="November 5, 2025">
  * VSCode Extension: Added setting to configure the initial permission mode for new conversations
  * Improved file path suggestion performance with native Rust-based fuzzy finder
  * Fixed infinite token refresh loop that caused MCP servers with OAuth (e.g., Slack) to hang during connection
  * Fixed memory crash when reading or writing large files (especially base64-encoded images)
</Update>

<Update label="2.0.33" description="November 4, 2025">
  * Native binary installs now launch quicker.
  * Fixed `claude doctor` incorrectly detecting Homebrew vs npm-global installations by properly resolving symlinks
  * Fixed `claude mcp serve` exposing tools with incompatible outputSchemas
</Update>

<Update label="2.0.32" description="November 4, 2025">
  * Un-deprecate output styles based on community feedback
  * Added `companyAnnouncements` setting for displaying announcements on startup
  * Fixed hook progress messages not updating correctly during PostToolUse hook execution
</Update>

<Update label="2.0.31" description="October 31, 2025">
  * Windows: native installation uses shift+tab as shortcut for mode switching, instead of alt+m
  * Vertex: add support for Web Search on supported models
  * VSCode: Adding the respectGitIgnore configuration to include .gitignored files in file searches (defaults to true)
  * Fixed a bug with subagents and MCP servers related to "Tool names must be unique" error
  * Fixed issue causing `/compact` to fail with `prompt_too_long` by making it respect existing compact boundaries
  * Fixed plugin uninstall not removing plugins
</Update>

<Update label="2.0.30" description="October 30, 2025">
  * Added helpful hint to run `security unlock-keychain` when encountering API key errors on macOS with locked keychain
  * Added `allowUnsandboxedCommands` sandbox setting to disable the dangerouslyDisableSandbox escape hatch at policy level
  * Added `disallowedTools` field to custom agent definitions for explicit tool blocking
  * Added prompt-based stop hooks
  * VSCode: Added respectGitIgnore configuration to include .gitignored files in file searches (defaults to true)
  * Enabled SSE MCP servers on native build
  * Deprecated output styles. Review options in `/output-style` and use --system-prompt-file, --system-prompt, --append-system-prompt, CLAUDE.md, or plugins instead
  * Removed support for custom ripgrep configuration, resolving an issue where Search returns no results and config discovery fails
  * Fixed Explore agent creating unwanted .md investigation files during codebase exploration
  * Fixed a bug where `/context` would sometimes fail with "max\_tokens must be greater than thinking.budget\_tokens" error message
  * Fixed `--mcp-config` flag to correctly override file-based MCP configurations
  * Fixed bug that saved session permissions to local settings
  * Fixed MCP tools not being available to sub-agents
  * Fixed hooks and plugins not executing when using --dangerously-skip-permissions flag
  * Fixed delay when navigating through typeahead suggestions with arrow keys
  * VSCode: Restored selection indicator in input footer showing current file or code selection status
</Update>

<Update label="2.0.28" description="October 27, 2025">
  * Plan mode: introduced new Plan subagent
  * Subagents: claude can now choose to resume subagents
  * Subagents: claude can dynamically choose the model used by its subagents
  * SDK: added --max-budget-usd flag
  * Discovery of custom slash commands, subagents, and output styles no longer respects .gitignore
  * Stop `/terminal-setup` from adding backslash to `Shift + Enter` in VS Code
  * Add branch and tag support for git-based plugins and marketplaces using fragment syntax (e.g., `owner/repo#branch`)
  * Fixed a bug where macOS permission prompts would show up upon initial launch when launching from home directory
  * Various other bug fixes
</Update>

<Update label="2.0.27" description="October 24, 2025">
  * New UI for permission prompts
  * Added current branch filtering and search to session resume screen for easier navigation
  * Fixed directory @-mention causing "No assistant message found" error
  * VSCode Extension: Add config setting to include .gitignored files in file searches
  * VSCode Extension: Bug fixes for unrelated 'Warmup' conversations, and configuration/settings occasionally being reset to defaults
</Update>

<Update label="2.0.25" description="October 21, 2025">
  * Removed legacy SDK entrypoint. Please migrate to @anthropic-ai/claude-agent-sdk for future SDK updates: [https://platform.claude.com/docs/en/agent-sdk/migration-guide](../05-Agent-SDK/agent-sdk-migration-guide.md)
</Update>

<Update label="2.0.24" description="October 20, 2025">
  * Fixed a bug where project-level skills were not loading when --setting-sources 'project' was specified
  * Claude Code Web: Support for Web -> CLI teleport
  * Sandbox: Releasing a sandbox mode for the BashTool on Linux & Mac
  * Bedrock: Display awsAuthRefresh output when auth is required
</Update>

<Update label="2.0.22" description="October 17, 2025">
  * Fixed content layout shift when scrolling through slash commands
  * IDE: Add toggle to enable/disable thinking.
  * Fix bug causing duplicate permission prompts with parallel tool calls
  * Add support for enterprise managed MCP allowlist and denylist
</Update>

<Update label="2.0.21" description="October 18, 2025">
  * Support MCP `structuredContent` field in tool responses
  * Added an interactive question tool
  * Claude will now ask you questions more often in plan mode
  * Added Haiku 4.5 as a model option for Pro users
  * Fixed an issue where queued commands don't have access to previous messages' output
</Update>

<Update label="2.0.20" description="October 16, 2025">
  * Added support for Claude Skills
</Update>

<Update label="2.0.19" description="October 15, 2025">
  * Auto-background long-running bash commands instead of killing them. Customize with BASH\_DEFAULT\_TIMEOUT\_MS
  * Fixed a bug where Haiku was unnecessarily called in print mode
</Update>

<Update label="2.0.17" description="October 15, 2025">
  * Added Haiku 4.5 to model selector!
  * Haiku 4.5 automatically uses Sonnet in plan mode, and Haiku for execution (i.e. SonnetPlan by default)
  * 3P (Bedrock and Vertex) are not automatically upgraded yet. Manual upgrading can be done through setting `ANTHROPIC_DEFAULT_HAIKU_MODEL`
  * Introducing the Explore subagent. Powered by Haiku it'll search through your codebase efficiently to save context!
  * OTEL: support HTTP\_PROXY and HTTPS\_PROXY
  * `CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC` now disables release notes fetching
</Update>

<Update label="2.0.15" description="October 14, 2025">
  * Fixed bug with resuming where previously created files needed to be read again before writing
  * Fixed bug with `-p` mode where @-mentioned files needed to be read again before writing
</Update>

<Update label="2.0.14" description="October 10, 2025">
  * Fix @-mentioning MCP servers to toggle them on/off
  * Improve permission checks for bash with inline env vars
  * Fix ultrathink + thinking toggle
  * Reduce unnecessary logins
  * Document --system-prompt
  * Several improvements to rendering
  * Plugins UI polish
</Update>

<Update label="2.0.13" description="October 9, 2025">
  * Fixed `/plugin` not working on native build
</Update>

<Update label="2.0.12" description="October 9, 2025">
  * **Plugin System Released**: Extend Claude Code with custom commands, agents, hooks, and MCP servers from marketplaces
  * `/plugin install`, `/plugin enable/disable`, `/plugin marketplace` commands for plugin management
  * Repository-level plugin configuration via `extraKnownMarketplaces` for team collaboration
  * `/plugin validate` command for validating plugin structure and configuration
  * Plugin announcement blog post at [https://www.anthropic.com/news/claude-code-plugins](https://www.anthropic.com/news/claude-code-plugins)
  * Plugin documentation available at [https://code.claude.com/docs/en/plugins](../08-Plugins-Skills/create-plugins-claude-code-docs.md)
  * Comprehensive error messages and diagnostics via `/doctor` command
  * Avoid flickering in `/model` selector
  * Improvements to `/help`
  * Avoid mentioning hooks in `/resume` summaries
  * Changes to the "verbose" setting in `/config` now persist across sessions
</Update>

<Update label="2.0.11" description="October 8, 2025">
  * Reduced system prompt size by 1.4k tokens
  * IDE: Fixed keyboard shortcuts and focus issues for smoother interaction
  * Fixed Opus fallback rate limit errors appearing incorrectly
  * Fixed /add-dir command selecting wrong default tab
</Update>

<Update label="2.0.10" description="October 8, 2025">
  * Rewrote terminal renderer for buttery smooth UI
  * Enable/disable MCP servers by @mentioning, or in /mcp
  * Added tab completion for shell commands in bash mode
  * PreToolUse hooks can now modify tool inputs
  * Press Ctrl-G to edit your prompt in your system's configured text editor
  * Fixes for bash permission checks with environment variables in the command
</Update>

<Update label="2.0.9" description="October 6, 2025">
  * Fix regression where bash backgrounding stopped working
</Update>

<Update label="2.0.8" description="October 4, 2025">
  * Update Bedrock default Sonnet model to `global.anthropic.claude-sonnet-4-5-20250929-v1:0`
  * IDE: Add drag-and-drop support for files and folders in chat
  * /context: Fix counting for thinking blocks
  * Improve message rendering for users with light themes on dark terminals
  * Remove deprecated .claude.json allowedTools, ignorePatterns, env, and todoFeatureEnabled config options (instead, configure these in your settings.json)
</Update>

<Update label="2.0.5" description="October 4, 2025">
  * IDE: Fix IME unintended message submission with Enter and Tab
  * IDE: Add "Open in Terminal" link in login screen
  * Fix unhandled OAuth expiration 401 API errors
  * SDK: Added SDKUserMessageReplay.isReplay to prevent duplicate messages
</Update>

<Update label="2.0.1" description="September 30, 2025">
  * Skip Sonnet 4.5 default model setting change for Bedrock and Vertex
  * Various bug fixes and presentation improvements
</Update>

<Update label="2.0.0" description="September 29, 2025">
  * New native VS Code extension
  * Fresh coat of paint throughout the whole app
  * /rewind a conversation to undo code changes
  * /usage command to see plan limits
  * Tab to toggle thinking (sticky across sessions)
  * Ctrl-R to search history
  * Unshipped claude config command
  * Hooks: Reduced PostToolUse 'tool\_use' ids were found without 'tool\_result' blocks errors
  * SDK: The Claude Code SDK is now the Claude Agent SDK
  * Add subagents dynamically with `--agents` flag
</Update>

<Update label="1.0.126" description="September 26, 2025">
  * Enable /context command for Bedrock and Vertex
  * Add mTLS support for HTTP-based OpenTelemetry exporters
</Update>

<Update label="1.0.124" description="September 25, 2025">
  * Set `CLAUDE_BASH_NO_LOGIN` environment variable to 1 or true to to skip login shell for BashTool
  * Fix Bedrock and Vertex environment variables evaluating all strings as truthy
  * No longer inform Claude of the list of allowed tools when permission is denied
  * Fixed security vulnerability in Bash tool permission checks
  * Improved VSCode extension performance for large files
</Update>

<Update label="1.0.123" description="September 23, 2025">
  * Bash permission rules now support output redirections when matching (e.g., `Bash(python:*)` matches `python script.py > output.txt`)
  * Fixed thinking mode triggering on negation phrases like "don't think"
  * Fixed rendering performance degradation during token streaming
  * Added SlashCommand tool, which enables Claude to invoke your slash commands. [https://code.claude.com/docs/en/slash-commands#SlashCommand-tool](https://code.claude.com/docs/en/slash-commands#SlashCommand-tool)
  * Enhanced BashTool environment snapshot logging
  * Fixed a bug where resuming a conversation in headless mode would sometimes enable thinking unnecessarily
  * Migrated --debug logging to a file, to enable easy tailing & filtering
</Update>

<Update label="1.0.120" description="September 19, 2025">
  * Fix input lag during typing, especially noticeable with large prompts
  * Improved VSCode extension command registry and sessions dialog user experience
  * Enhanced sessions dialog responsiveness and visual feedback
  * Fixed IDE compatibility issue by removing worktree support check
  * Fixed security vulnerability where Bash tool permission checks could be bypassed using prefix matching
</Update>

<Update label="1.0.119" description="September 19, 2025">
  * Fix Windows issue where process visually freezes on entering interactive mode
  * Support dynamic headers for MCP servers via headersHelper configuration
  * Fix thinking mode not working in headless sessions
  * Fix slash commands now properly update allowed tools instead of replacing them
</Update>

<Update label="1.0.117" description="September 19, 2025">
  * Add Ctrl-R history search to recall previous commands like bash/zsh
  * Fix input lag while typing, especially on Windows
  * Add sed command to auto-allowed commands in acceptEdits mode
  * Fix Windows PATH comparison to be case-insensitive for drive letters
  * Add permissions management hint to /add-dir output
</Update>

<Update label="1.0.115" description="September 16, 2025">
  * Improve thinking mode display with enhanced visual effects
  * Type /t to temporarily disable thinking mode in your prompt
  * Improve path validation for glob and grep tools
  * Show condensed output for post-tool hooks to reduce visual clutter
  * Fix visual feedback when loading state completes
  * Improve UI consistency for permission request dialogs
</Update>

<Update label="1.0.113" description="September 13, 2025">
  * Deprecated piped input in interactive mode
  * Move Ctrl+R keybinding for toggling transcript to Ctrl+O
</Update>

<Update label="1.0.112" description="September 12, 2025">
  * Transcript mode (Ctrl+R): Added the model used to generate each assistant message
  * Addressed issue where some Claude Max users were incorrectly recognized as Claude Pro users
  * Hooks: Added systemMessage support for SessionEnd hooks
  * Added `spinnerTipsEnabled` setting to disable spinner tips
  * IDE: Various improvements and bug fixes
</Update>

<Update label="1.0.111" description="September 10, 2025">
  * /model now validates provided model names
  * Fixed Bash tool crashes caused by malformed shell syntax parsing
</Update>

<Update label="1.0.110" description="September 10, 2025">
  * /terminal-setup command now supports WezTerm
  * MCP: OAuth tokens now proactively refresh before expiration
  * Fixed reliability issues with background Bash processes
</Update>

<Update label="1.0.109" description="September 9, 2025">
  * SDK: Added partial message streaming support via `--include-partial-messages` CLI flag
</Update>

<Update label="1.0.106" description="September 5, 2025">
  * Windows: Fixed path permission matching to consistently use POSIX format (e.g., `Read(//c/Users/...)`)
</Update>

<Update label="1.0.97" description="August 29, 2025">
  * Settings: /doctor now validates permission rule syntax and suggests corrections
</Update>

<Update label="1.0.94" description="August 27, 2025">
  * Vertex: add support for global endpoints for supported models
  * /memory command now allows direct editing of all imported memory files
  * SDK: Add custom tools as callbacks
  * Added /todos command to list current todo items
</Update>

<Update label="1.0.93" description="August 26, 2025">
  * Windows: Add alt + v shortcut for pasting images from clipboard
  * Support NO\_PROXY environment variable to bypass proxy for specified hostnames and IPs
</Update>

<Update label="1.0.90" description="August 25, 2025">
  * Settings file changes take effect immediately - no restart required
</Update>

<Update label="1.0.88" description="August 22, 2025">
  * Fixed issue causing "OAuth authentication is currently not supported"
  * Status line input now includes `exceeds_200k_tokens`
  * Fixed incorrect usage tracking in /cost.
  * Introduced `ANTHROPIC_DEFAULT_SONNET_MODEL` and `ANTHROPIC_DEFAULT_OPUS_MODEL` for controlling model aliases opusplan, opus, and sonnet.
  * Bedrock: Updated default Sonnet model to Sonnet 4
</Update>

<Update label="1.0.86" description="August 22, 2025">
  * Added /context to help users self-serve debug context issues
  * SDK: Added UUID support for all SDK messages
  * SDK: Added `--replay-user-messages` to replay user messages back to stdout
</Update>

<Update label="1.0.85" description="August 19, 2025">
  * Status line input now includes session cost info
  * Hooks: Introduced SessionEnd hook
</Update>

<Update label="1.0.84" description="August 18, 2025">
  * Fix tool\_use/tool\_result id mismatch error when network is unstable
  * Fix Claude sometimes ignoring real-time steering when wrapping up a task
  * @-mention: Add \~/.claude/\* files to suggestions for easier agent, output style, and slash command editing
  * Use built-in ripgrep by default; to opt out of this behavior, set USE\_BUILTIN\_RIPGREP=0
</Update>

<Update label="1.0.83" description="August 18, 2025">
  * @-mention: Support files with spaces in path
  * New shimmering spinner
</Update>

<Update label="1.0.82" description="August 16, 2025">
  * SDK: Add request cancellation support
  * SDK: New additionalDirectories option to search custom paths, improved slash command processing
  * Settings: Validation prevents invalid fields in .claude/settings.json files
  * MCP: Improve tool name consistency
  * Bash: Fix crash when Claude tries to automatically read large files
</Update>

<Update label="1.0.81" description="August 14, 2025">
  * Released output styles, including new built-in educational output styles "Explanatory" and "Learning". Docs: [https://code.claude.com/docs/en/output-styles](output-styles-claude-code-docs-c315eeed32.md)
  * Agents: Fix custom agent loading when agent files are unparsable
</Update>

<Update label="1.0.80" description="August 14, 2025">
  * UI improvements: Fix text contrast for custom subagent colors and spinner rendering issues
</Update>

<Update label="1.0.77" description="August 14, 2025">
  * Bash tool: Fix heredoc and multiline string escaping, improve stderr redirection handling
  * SDK: Add session support and permission denial tracking
  * Fix token limit errors in conversation summarization
  * Opus Plan Mode: New setting in `/model` to run Opus only in plan mode, Sonnet otherwise
</Update>

<Update label="1.0.73" description="August 11, 2025">
  * MCP: Support multiple config files with `--mcp-config file1.json file2.json`
  * MCP: Press Esc to cancel OAuth authentication flows
  * Bash: Improved command validation and reduced false security warnings
  * UI: Enhanced spinner animations and status line visual hierarchy
  * Linux: Added support for Alpine and musl-based distributions (requires separate ripgrep installation)
</Update>

<Update label="1.0.72" description="August 11, 2025">
  * Ask permissions: have Claude Code always ask for confirmation to use specific tools with /permissions
</Update>

<Update label="1.0.71" description="August 7, 2025">
  * Background commands: (Ctrl-b) to run any Bash command in the background so Claude can keep working (great for dev servers, tailing logs, etc.)
  * Customizable status line: add your terminal prompt to Claude Code with /statusline
</Update>

<Update label="1.0.70" description="August 7, 2025">
  * Performance: Optimized message rendering for better performance with large contexts
  * Windows: Fixed native file search, ripgrep, and subagent functionality
  * Added support for @-mentions in slash command arguments
</Update>

<Update label="1.0.69" description="August 5, 2025">
  * Upgraded Opus to version 4.1
</Update>

<Update label="1.0.68" description="August 4, 2025">
  * Fix incorrect model names being used for certain commands like `/pr-comments`
  * Windows: improve permissions checks for allow / deny tools and project trust. This may create a new project entry in `.claude.json` - manually merge the history field if desired.
  * Windows: improve sub-process spawning to eliminate "No such file or directory" when running commands like pnpm
  * Enhanced /doctor command with CLAUDE.md and MCP tool context for self-serve debugging
  * SDK: Added canUseTool callback support for tool confirmation
  * Added `disableAllHooks` setting
  * Improved file suggestions performance in large repos
</Update>

<Update label="1.0.65" description="July 31, 2025">
  * IDE: Fixed connection stability issues and error handling for diagnostics
  * Windows: Fixed shell environment setup for users without .bashrc files
</Update>

<Update label="1.0.64" description="July 30, 2025">
  * Agents: Added model customization support - you can now specify which model an agent should use
  * Agents: Fixed unintended access to the recursive agent tool
  * Hooks: Added systemMessage field to hook JSON output for displaying warnings and context
  * SDK: Fixed user input tracking across multi-turn conversations
  * Added hidden files to file search and @-mention suggestions
</Update>

<Update label="1.0.63" description="July 29, 2025">
  * Windows: Fixed file search, @agent mentions, and custom slash commands functionality
</Update>

<Update label="1.0.62" description="July 28, 2025">
  * Added @-mention support with typeahead for custom agents. @`<your-custom-agent>` to invoke it
  * Hooks: Added SessionStart hook for new session initialization
  * /add-dir command now supports typeahead for directory paths
  * Improved network connectivity check reliability
</Update>

<Update label="1.0.61" description="July 25, 2025">
  * Transcript mode (Ctrl+R): Changed Esc to exit transcript mode rather than interrupt
  * Settings: Added `--settings` flag to load settings from a JSON file
  * Settings: Fixed resolution of settings files paths that are symlinks
  * OTEL: Fixed reporting of wrong organization after authentication changes
  * Slash commands: Fixed permissions checking for allowed-tools with Bash
  * IDE: Added support for pasting images in VSCode MacOS using ⌘+V
  * IDE: Added `CLAUDE_CODE_AUTO_CONNECT_IDE=false` for disabling IDE auto-connection
  * Added `CLAUDE_CODE_SHELL_PREFIX` for wrapping Claude and user-provided shell commands run by Claude Code
</Update>

<Update label="1.0.60" description="July 24, 2025">
  * You can now create custom subagents for specialized tasks! Run /agents to get started
</Update>

<Update label="1.0.59" description="July 23, 2025">
  * SDK: Added tool confirmation support with canUseTool callback
  * SDK: Allow specifying env for spawned process
  * Hooks: Exposed PermissionDecision to hooks (including "ask")
  * Hooks: UserPromptSubmit now supports additionalContext in advanced JSON output
  * Fixed issue where some Max users that specified Opus would still see fallback to Sonnet
</Update>

<Update label="1.0.58" description="July 23, 2025">
  * Added support for reading PDFs
  * MCP: Improved server health status display in 'claude mcp list'
  * Hooks: Added CLAUDE\_PROJECT\_DIR env var for hook commands
</Update>

<Update label="1.0.57" description="July 23, 2025">
  * Added support for specifying a model in slash commands
  * Improved permission messages to help Claude understand allowed tools
  * Fix: Remove trailing newlines from bash output in terminal wrapping
</Update>

<Update label="1.0.56" description="July 23, 2025">
  * Windows: Enabled shift+tab for mode switching on versions of Node.js that support terminal VT mode
  * Fixes for WSL IDE detection
  * Fix an issue causing awsRefreshHelper changes to .aws directory not to be picked up
</Update>

<Update label="1.0.55" description="July 23, 2025">
  * Clarified knowledge cutoff for Opus 4 and Sonnet 4 models
  * Windows: fixed Ctrl+Z crash
  * SDK: Added ability to capture error logging
  * Add --system-prompt-file option to override system prompt in print mode
</Update>

<Update label="1.0.54" description="July 19, 2025">
  * Hooks: Added UserPromptSubmit hook and the current working directory to hook inputs
  * Custom slash commands: Added argument-hint to frontmatter
  * Windows: OAuth uses port 45454 and properly constructs browser URL
  * Windows: mode switching now uses alt + m, and plan mode renders properly
  * Shell: Switch to in-memory shell snapshot to fix file-related errors
</Update>

<Update label="1.0.53" description="July 18, 2025">
  * Updated @-mention file truncation from 100 lines to 2000 lines
  * Add helper script settings for AWS token refresh: awsAuthRefresh (for foreground operations like aws sso login) and awsCredentialExport (for background operation with STS-like response).
</Update>

<Update label="1.0.52" description="July 18, 2025">
  * Added support for MCP server instructions
</Update>

<Update label="1.0.51" description="July 11, 2025">
  * Added support for native Windows (requires Git for Windows)
  * Added support for Bedrock API keys through environment variable AWS\_BEARER\_TOKEN\_BEDROCK
  * Settings: /doctor can now help you identify and fix invalid setting files
  * `--append-system-prompt` can now be used in interactive mode, not just --print/-p.
  * Increased auto-compact warning threshold from 60% to 80%
  * Fixed an issue with handling user directories with spaces for shell snapshots
  * OTEL resource now includes os.type, os.version, host.arch, and wsl.version (if running on Windows Subsystem for Linux)
  * Custom slash commands: Fixed user-level commands in subdirectories
  * Plan mode: Fixed issue where rejected plan from sub-task would get discarded
</Update>

<Update label="1.0.48" description="July 10, 2025">
  * Fixed a bug in v1.0.45 where the app would sometimes freeze on launch
  * Added progress messages to Bash tool based on the last 5 lines of command output
  * Added expanding variables support for MCP server configuration
  * Moved shell snapshots from /tmp to \~/.claude for more reliable Bash tool calls
  * Improved IDE extension path handling when Claude Code runs in WSL
  * Hooks: Added a PreCompact hook
  * Vim mode: Added c, f/F, t/T
</Update>

<Update label="1.0.45" description="July 10, 2025">
  * Redesigned Search (Grep) tool with new tool input parameters and features
  * Disabled IDE diffs for notebook files, fixing "Timeout waiting after 1000ms" error
  * Fixed config file corruption issue by enforcing atomic writes
  * Updated prompt input undo to Ctrl+\_ to avoid breaking existing Ctrl+U behavior, matching zsh's undo shortcut
  * Stop Hooks: Fixed transcript path after /clear and fixed triggering when loop ends with tool call
  * Custom slash commands: Restored namespacing in command names based on subdirectories. For example, .claude/commands/frontend/component.md is now /frontend:component, not /component.
</Update>

<Update label="1.0.44" description="July 7, 2025">
  * New /export command lets you quickly export a conversation for sharing
  * MCP: resource\_link tool results are now supported
  * MCP: tool annotations and tool titles now display in /mcp view
  * Changed Ctrl+Z to suspend Claude Code. Resume by running `fg`. Prompt input undo is now Ctrl+U.
</Update>

<Update label="1.0.43" description="July 3, 2025">
  * Fixed a bug where the theme selector was saving excessively
  * Hooks: Added EPIPE system error handling
</Update>

<Update label="1.0.42" description="July 3, 2025">
  * Added tilde (`~`) expansion support to `/add-dir` command
</Update>

<Update label="1.0.41" description="July 3, 2025">
  * Hooks: Split Stop hook triggering into Stop and SubagentStop
  * Hooks: Enabled optional timeout configuration for each command
  * Hooks: Added "hook\_event\_name" to hook input
  * Fixed a bug where MCP tools would display twice in tool list
  * New tool parameters JSON for Bash tool in `tool_decision` event
</Update>

<Update label="1.0.40" description="July 3, 2025">
  * Fixed a bug causing API connection errors with UNABLE\_TO\_GET\_ISSUER\_CERT\_LOCALLY if `NODE_EXTRA_CA_CERTS` was set
</Update>

<Update label="1.0.39" description="July 3, 2025">
  * New Active Time metric in OpenTelemetry logging
</Update>

<Update label="1.0.38" description="June 30, 2025">
  * Released hooks. Special thanks to community input in [https://github.com/anthropics/claude-code/issues/712](https://github.com/anthropics/claude-code/issues/712). Docs: [https://code.claude.com/docs/en/hooks](../07-Hooks/hooks-9168ed62a4.md)
</Update>

<Update label="1.0.37" description="June 30, 2025">
  * Remove ability to set `Proxy-Authorization` header via ANTHROPIC\_AUTH\_TOKEN or apiKeyHelper
</Update>

<Update label="1.0.36" description="June 30, 2025">
  * Web search now takes today's date into context
  * Fixed a bug where stdio MCP servers were not terminating properly on exit
</Update>

<Update label="1.0.35" description="June 25, 2025">
  * Added support for MCP OAuth Authorization Server discovery
</Update>

<Update label="1.0.34" description="June 24, 2025">
  * Fixed a memory leak causing a MaxListenersExceededWarning message to appear
</Update>

<Update label="1.0.33" description="June 24, 2025">
  * Improved logging functionality with session ID support
  * Added prompt input undo functionality (Ctrl+Z and vim 'u' command)
  * Improvements to plan mode
</Update>

<Update label="1.0.32" description="June 24, 2025">
  * Updated loopback config for litellm
  * Added forceLoginMethod setting to bypass login selection screen
</Update>

<Update label="1.0.31" description="June 24, 2025">
  * Fixed a bug where \~/.claude.json would get reset when file contained invalid JSON
</Update>

<Update label="1.0.30" description="June 24, 2025">
  * Custom slash commands: Run bash output, @-mention files, enable thinking with thinking keywords
  * Improved file path autocomplete with filename matching
  * Added timestamps in Ctrl-r mode and fixed Ctrl-c handling
  * Enhanced jq regex support for complex filters with pipes and select
</Update>

<Update label="1.0.29" description="June 24, 2025">
  * Improved CJK character support in cursor navigation and rendering
</Update>

<Update label="1.0.28" description="June 24, 2025">
  * Slash commands: Fix selector display during history navigation
  * Resizes images before upload to prevent API size limit errors
  * Added XDG\_CONFIG\_HOME support to configuration directory
  * Performance optimizations for memory usage
  * New attributes (terminal.type, language) in OpenTelemetry logging
</Update>

<Update label="1.0.27" description="June 18, 2025">
  * Streamable HTTP MCP servers are now supported
  * Remote MCP servers (SSE and HTTP) now support OAuth
  * MCP resources can now be @-mentioned
  * /resume slash command to switch conversations within Claude Code
</Update>

<Update label="1.0.25" description="June 16, 2025">
  * Slash commands: moved "project" and "user" prefixes to descriptions
  * Slash commands: improved reliability for command discovery
  * Improved support for Ghostty
  * Improved web search reliability
</Update>

<Update label="1.0.24" description="June 16, 2025">
  * Improved /mcp output
  * Fixed a bug where settings arrays got overwritten instead of merged
</Update>

<Update label="1.0.23" description="June 16, 2025">
  * Released TypeScript SDK: import @anthropic-ai/claude-code to get started
  * Released Python SDK: pip install claude-code-sdk to get started
</Update>

<Update label="1.0.22" description="June 12, 2025">
  * SDK: Renamed `total_cost` to `total_cost_usd`
</Update>

<Update label="1.0.21" description="June 12, 2025">
  * Improved editing of files with tab-based indentation
  * Fix for tool\_use without matching tool\_result errors
  * Fixed a bug where stdio MCP server processes would linger after quitting Claude Code
</Update>

<Update label="1.0.18" description="June 9, 2025">
  * Added --add-dir CLI argument for specifying additional working directories
  * Added streaming input support without require -p flag
  * Improved startup performance and session storage performance
  * Added CLAUDE\_BASH\_MAINTAIN\_PROJECT\_WORKING\_DIR environment variable to freeze working directory for bash commands
  * Added detailed MCP server tools display (/mcp)
  * MCP authentication and permission improvements
  * Added auto-reconnection for MCP SSE connections on disconnect
  * Fixed issue where pasted content was lost when dialogs appeared
</Update>

<Update label="1.0.17" description="June 9, 2025">
  * We now emit messages from sub-tasks in -p mode (look for the parent\_tool\_use\_id property)
  * Fixed crashes when the VS Code diff tool is invoked multiple times quickly
  * MCP server list UI improvements
  * Update Claude Code process title to display "claude" instead of "node"
</Update>

<Update label="1.0.11" description="June 4, 2025">
  * Claude Code can now also be used with a Claude Pro subscription
  * Added /upgrade for smoother switching to Claude Max plans
  * Improved UI for authentication from API keys and Bedrock/Vertex/external auth tokens
  * Improved shell configuration error handling
  * Improved todo list handling during compaction
</Update>

<Update label="1.0.10" description="June 4, 2025">
  * Added markdown table support
  * Improved streaming performance
</Update>

<Update label="1.0.8" description="June 2, 2025">
  * Fixed Vertex AI region fallback when using CLOUD\_ML\_REGION
  * Increased default otel interval from 1s -> 5s
  * Fixed edge cases where MCP\_TIMEOUT and MCP\_TOOL\_TIMEOUT weren't being respected
  * Fixed a regression where search tools unnecessarily asked for permissions
  * Added support for triggering thinking non-English languages
  * Improved compacting UI
</Update>

<Update label="1.0.7" description="June 2, 2025">
  * Renamed /allowed-tools -> /permissions
  * Migrated allowedTools and ignorePatterns from .claude.json -> settings.json
  * Deprecated claude config commands in favor of editing settings.json
  * Fixed a bug where --dangerously-skip-permissions sometimes didn't work in --print mode
  * Improved error handling for /install-github-app
  * Bugfixes, UI polish, and tool reliability improvements
</Update>

<Update label="1.0.6" description="June 2, 2025">
  * Improved edit reliability for tab-indented files
  * Respect CLAUDE\_CONFIG\_DIR everywhere
  * Reduced unnecessary tool permission prompts
  * Added support for symlinks in @file typeahead
  * Bugfixes, UI polish, and tool reliability improvements
</Update>

<Update label="1.0.4" description="May 28, 2025">
  * Fixed a bug where MCP tool errors weren't being parsed correctly
</Update>

<Update label="1.0.1" description="May 22, 2025">
  * Added `DISABLE_INTERLEAVED_THINKING` to give users the option to opt out of interleaved thinking.
  * Improved model references to show provider-specific names (Sonnet 3.7 for Bedrock, Sonnet 4 for Console)
  * Updated documentation links and OAuth process descriptions
</Update>

<Update label="1.0.0" description="May 22, 2025">
  * Claude Code is now generally available
  * Introducing Sonnet 4 and Opus 4 models
</Update>

<Update label="0.2.125" description="May 21, 2025">
  * Breaking change: Bedrock ARN passed to `ANTHROPIC_MODEL` or `ANTHROPIC_SMALL_FAST_MODEL` should no longer contain an escaped slash (specify `/` instead of `%2F`)
  * Removed `DEBUG=true` in favor of `ANTHROPIC_LOG=debug`, to log all requests
</Update>

<Update label="0.2.117" description="May 17, 2025">
  * Breaking change: --print JSON output now returns nested message objects, for forwards-compatibility as we introduce new metadata fields
  * Introduced settings.cleanupPeriodDays
  * Introduced CLAUDE\_CODE\_API\_KEY\_HELPER\_TTL\_MS env var
  * Introduced --debug mode
</Update>

<Update label="0.2.108" description="May 13, 2025">
  * You can now send messages to Claude while it works to steer Claude in real-time
  * Introduced BASH\_DEFAULT\_TIMEOUT\_MS and BASH\_MAX\_TIMEOUT\_MS env vars
  * Fixed a bug where thinking was not working in -p mode
  * Fixed a regression in /cost reporting
  * Deprecated MCP wizard interface in favor of other MCP commands
  * Lots of other bugfixes and improvements
</Update>

<Update label="0.2.107" description="May 9, 2025">
  * CLAUDE.md files can now import other files. Add @path/to/file.md to ./CLAUDE.md to load additional files on launch
</Update>

<Update label="0.2.106" description="May 9, 2025">
  * MCP SSE server configs can now specify custom headers
  * Fixed a bug where MCP permission prompt didn't always show correctly
</Update>

<Update label="0.2.105" description="May 8, 2025">
  * Claude can now search the web
  * Moved system & account status to /status
  * Added word movement keybindings for Vim
  * Improved latency for startup, todo tool, and file edits
</Update>

<Update label="0.2.102" description="May 5, 2025">
  * Improved thinking triggering reliability
  * Improved @mention reliability for images and folders
  * You can now paste multiple large chunks into one prompt
</Update>

<Update label="0.2.100" description="May 2, 2025">
  * Fixed a crash caused by a stack overflow error
  * Made db storage optional; missing db support disables --continue and --resume
</Update>

<Update label="0.2.98" description="May 1, 2025">
  * Fixed an issue where auto-compact was running twice
</Update>

<Update label="0.2.96" description="May 1, 2025">
  * Claude Code can now also be used with a Claude Max subscription ([https://claude.ai/upgrade](https://claude.ai/upgrade))
</Update>

<Update label="0.2.93" description="April 30, 2025">
  * Resume conversations from where you left off from with "claude --continue" and "claude --resume"
  * Claude now has access to a Todo list that helps it stay on track and be more organized
</Update>

<Update label="0.2.82" description="April 25, 2025">
  * Added support for --disallowedTools
  * Renamed tools for consistency: LSTool -> LS, View -> Read, etc.
</Update>

<Update label="0.2.75" description="April 21, 2025">
  * Hit Enter to queue up additional messages while Claude is working
  * Drag in or copy/paste image files directly into the prompt
  * @-mention files to directly add them to context
  * Run one-off MCP servers with `claude --mcp-config <path-to-file>`
  * Improved performance for filename auto-complete
</Update>

<Update label="0.2.74" description="April 18, 2025">
  * Added support for refreshing dynamically generated API keys (via apiKeyHelper), with a 5 minute TTL
  * Task tool can now perform writes and run bash commands
</Update>

<Update label="0.2.72" description="April 18, 2025">
  * Updated spinner to indicate tokens loaded and tool usage
</Update>

<Update label="0.2.70" description="April 17, 2025">
  * Network commands like curl are now available for Claude to use
  * Claude can now run multiple web queries in parallel
  * Pressing ESC once immediately interrupts Claude in Auto-accept mode
</Update>

<Update label="0.2.69" description="April 17, 2025">
  * Fixed UI glitches with improved Select component behavior
  * Enhanced terminal output display with better text truncation logic
</Update>

<Update label="0.2.67" description="April 17, 2025">
  * Shared project permission rules can be saved in .claude/settings.json
</Update>

<Update label="0.2.66" description="April 17, 2025">
  * Print mode (-p) now supports streaming output via --output-format=stream-json
  * Fixed issue where pasting could trigger memory or bash mode unexpectedly
</Update>

<Update label="0.2.63" description="April 17, 2025">
  * Fixed an issue where MCP tools were loaded twice, which caused tool call errors
</Update>

<Update label="0.2.61" description="April 2, 2025">
  * Navigate menus with vim-style keys (j/k) or bash/emacs shortcuts (Ctrl+n/p) for faster interaction
  * Enhanced image detection for more reliable clipboard paste functionality
  * Fixed an issue where ESC key could crash the conversation history selector
</Update>

<Update label="0.2.59" description="April 2, 2025">
  * Copy+paste images directly into your prompt
  * Improved progress indicators for bash and fetch tools
  * Bugfixes for non-interactive mode (-p)
</Update>

<Update label="0.2.54" description="April 2, 2025">
  * Quickly add to Memory by starting your message with '#'
  * Press ctrl+r to see full output for long tool results
  * Added support for MCP SSE transport
</Update>

<Update label="0.2.53" description="April 2, 2025">
  * New web fetch tool lets Claude view URLs that you paste in
  * Fixed a bug with JPEG detection
</Update>

<Update label="0.2.50" description="April 2, 2025">
  * New MCP "project" scope now allows you to add MCP servers to .mcp.json files and commit them to your repository
</Update>

<Update label="0.2.49" description="April 2, 2025">
  * Previous MCP server scopes have been renamed: previous "project" scope is now "local" and "global" scope is now "user"
</Update>

<Update label="0.2.47" description="April 2, 2025">
  * Press Tab to auto-complete file and folder names
  * Press Shift + Tab to toggle auto-accept for file edits
  * Automatic conversation compaction for infinite conversation length (toggle with /config)
</Update>

<Update label="0.2.44" description="April 2, 2025">
  * Ask Claude to make a plan with thinking mode: just say 'think' or 'think harder' or even 'ultrathink'
</Update>

<Update label="0.2.41" description="April 2, 2025">
  * MCP server startup timeout can now be configured via MCP\_TIMEOUT environment variable
  * MCP server startup no longer blocks the app from starting up
</Update>

<Update label="0.2.37" description="April 2, 2025">
  * New /release-notes command lets you view release notes at any time
  * `claude config add/remove` commands now accept multiple values separated by commas or spaces
</Update>

<Update label="0.2.36" description="April 2, 2025">
  * Import MCP servers from Claude Desktop with `claude mcp add-from-claude-desktop`
  * Add MCP servers as JSON strings with `claude mcp add-json <n> <json>`
</Update>

<Update label="0.2.34" description="April 2, 2025">
  * Vim bindings for text input - enable with /vim or /config
</Update>

<Update label="0.2.32" description="April 2, 2025">
  * Interactive MCP setup wizard: Run "claude mcp add" to add MCP servers with a step-by-step interface
  * Fix for some PersistentShell issues
</Update>

<Update label="0.2.31" description="April 2, 2025">
  * Custom slash commands: Markdown files in .claude/commands/ directories now appear as custom slash commands to insert prompts into your conversation
  * MCP debug mode: Run with --mcp-debug flag to get more information about MCP server errors
</Update>

<Update label="0.2.30" description="April 2, 2025">
  * Added ANSI color theme for better terminal compatibility
  * Fixed issue where slash command arguments weren't being sent properly
  * (Mac-only) API keys are now stored in macOS Keychain
</Update>

<Update label="0.2.26" description="April 2, 2025">
  * New /approved-tools command for managing tool permissions
  * Word-level diff display for improved code readability
  * Fuzzy matching for slash commands
</Update>

<Update label="0.2.21" description="April 2, 2025">
  * Fuzzy matching for /commands
</Update>


---
## channels-00374ea787.md

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

# 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) and require Claude Code v2.1.80 or later. They require claude.ai login. Console and API key authentication is not supported. 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.

This page covers:

* [Supported channels](#supported-channels): Telegram, Discord, and iMessage setup
* [Install and run a channel](#quickstart) with fakechat, a localhost demo
* [Who can push messages](#security): sender allowlists and how you pair
* [Enable channels for your organization](#enterprise-controls) on Team and Enterprise
* [How channels compare](#how-channels-compare) to web sessions, Slack, MCP, and Remote Control

To build your own channel, see the [Channels reference](channels-reference-claude-code-docs-67a0bbe41f.md).

## 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 Claude Code reports that the plugin is not found in any marketplace, your marketplace is either missing or outdated. Run `/plugin marketplace update claude-plugins-official` to refresh it, or `/plugin marketplace add anthropics/claude-plugins-official` if you haven't added it before. Then retry the install.

        After installing, run `/reload-plugins` 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 Claude Code reports that the plugin is not found in any marketplace, your marketplace is either missing or outdated. Run `/plugin marketplace update claude-plugins-official` to refresh it, or `/plugin marketplace add anthropics/claude-plugins-official` if you haven't added it before. Then retry the install.

        After installing, run `/reload-plugins` 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 Claude Code reports that the plugin is not found in any marketplace, your marketplace is either missing or outdated. Run `/plugin marketplace update claude-plugins-official` to refresh it, or `/plugin marketplace add anthropics/claude-plugins-official` if you haven't added it before. Then retry the install.
      </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 `user@example.com`.
      </Step>
    </Steps>
  </Tab>
</Tabs>

You can also [build your own channel](channels-reference-claude-code-docs-67a0bbe41f.md) for systems that don't have a plugin yet.

## 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](/en/quickstart#step-1-install-claude-code) with a claude.ai account
* [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 users**: your organization 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 Claude Code reports that the plugin is not found in any marketplace, your marketplace is either missing or outdated. Run `/plugin marketplace update claude-plugins-official` to refresh it, or `/plugin marketplace add anthropics/claude-plugins-official` if you haven't added it before. Then retry the install.
  </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.

    <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}
    hey, what's in my working directory?
    ```

    The message arrives in your Claude Code session as a `<channel source="fakechat">` event. Claude reads it, does the work, and calls fakechat's `reply` tool. 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](/en/channels-reference#relay-permission-prompts) can forward these prompts to you so you can approve or deny remotely. For unattended use, the admin-gated unattended-mode option bypasses prompts entirely; only enable it in environments you trust.

## 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 on Team and Enterprise plans your organization controls availability with [`channelsEnabled`](#enterprise-controls).

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](/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

On Team and Enterprise plans, channels are off by default. Admins control availability through two [managed settings](claude-code-settings-claude-code-docs-d4420b4b52.md) that users cannot override:

| Setting                 | Purpose                                                                                                                                                                                                                                                     | When not configured            |
| :---------------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------------------------- |
| `channelsEnabled`       | Master switch. Must be `true` for any channel to deliver messages. Set via the [claude.ai Admin console](https://claude.ai/admin-settings/claude-code) toggle or directly in managed settings. Blocks all channels including the development flag when off. | Channels blocked               |
| `allowedChannelPlugins` | Which plugins can register once channels are enabled. Replaces the Anthropic-maintained list when set. Only applies when `channelsEnabled` is `true`.                                                                                                       | Anthropic default list applies |

Pro and Max users without an organization skip these checks entirely: channels are available and users opt in per session with `--channels`.

### Enable channels for your organization

Admins can enable channels from [**claude.ai → Admin settings → Claude Code → Channels**](https://claude.ai/admin-settings/claude-code), or by setting `channelsEnabled` to `true` in managed settings.

Once enabled, users in your organization can use `--channels` to opt channel servers into individual sessions. If the setting is disabled or unset, the MCP server still connects and its tools work, but channel messages won't arrive. A startup warning tells the user to have an admin enable the setting.

### Restrict which channel plugins can run

By default, any plugin on the Anthropic-maintained allowlist can register as a channel. Admins on Team and Enterprise plans can replace that allowlist with their own by setting `allowedChannelPlugins` in managed settings. Use this to restrict which official plugins are allowed, approve channels from your own internal marketplace, or both. Each entry names a plugin and the marketplace it comes from:

```json theme={null}
{
  "channelsEnabled": true,
  "allowedChannelPlugins": [
    { "marketplace": "claude-plugins-official", "plugin": "telegram" },
    { "marketplace": "claude-plugins-official", "plugin": "discord" },
    { "marketplace": "acme-corp-plugins", "plugin": "internal-alerts" }
  ]
}
```

When `allowedChannelPlugins` is set, it replaces the Anthropic allowlist entirely: only the listed plugins can register. Leave it unset to fall back to the default Anthropic allowlist. An empty array blocks all channel plugins from the allowlist, but `--dangerously-load-development-channels` can still bypass it for local testing. To block channels entirely including the development flag, leave `channelsEnabled` unset instead.

This setting requires `channelsEnabled: true`. If a user passes a plugin to `--channels` that isn't on your list, Claude Code starts normally but the channel doesn't register, and the startup notice explains that the plugin isn't on the organization's approved list.

## Research preview

Channels are a research preview feature. Availability is rolling out gradually, and the `--channels` flag syntax and protocol contract may change based on feedback.

During the preview, `--channels` only accepts plugins from an Anthropic-maintained allowlist, or from your organization's allowlist if an admin has set [`allowedChannelPlugins`](#restrict-which-channel-plugins-can-run). The channel plugins in [claude-plugins-official](https://github.com/anthropics/claude-plugins-official/tree/main/external_plugins) are the default approved set. If you pass something that isn't on the effective allowlist, Claude Code starts normally but the channel doesn't register, and the startup notice tells you why.

To test a channel you're building, use `--dangerously-load-development-channels`. See [Test during the research preview](/en/channels-reference#test-during-the-research-preview) for information about testing custom channels that you build.

Report issues or feedback on the [Claude Code GitHub repository](https://github.com/anthropics/claude-code/issues).

## How channels compare

Several Claude Code features connect to systems outside the terminal, each suited to a different kind of work:

| Feature                                              | What it does                                                          | Good for                                                  |
| ---------------------------------------------------- | --------------------------------------------------------------------- | --------------------------------------------------------- |
| [Claude Code on the web](claude-code-on-the-web-69d53821d4.md) | Runs tasks in a fresh cloud sandbox, cloned from GitHub               | Delegating self-contained async work you check on later   |
| [Claude in Slack](../14-Connectors/claude-code-in-slack-claude-code-docs-c62a093e92.md)                         | Spawns a web session from an `@Claude` mention in a channel or thread | Starting tasks directly from team conversation context    |
| Standard [MCP server](../06-MCP-Tools/mcp-208e742686.md)                       | Claude queries it during a task; nothing is pushed to the session     | Giving Claude on-demand access to read or query a system  |
| [Remote Control](continue-local-sessions-from-any-device-with-remote-control-claude-code-docs-c1c03fd914.md)                 | You drive your local session from claude.ai or the Claude mobile app  | Steering an in-progress session while away from your desk |

Channels fill the gap in that list by pushing events from non-Claude sources into your already-running local session.

* **Chat bridge**: ask Claude something from your phone via Telegram, Discord, or iMessage, and the answer comes back in the same chat while the work runs on your machine against your real files.
* **[Webhook receiver](/en/channels-reference#example-build-a-webhook-receiver)**: a webhook from CI, your error tracker, a deploy pipeline, or other external service arrives where Claude already has your files open and remembers what you were debugging.

## Next steps

Once you have a channel running, explore these related features:

* [Build your own channel](channels-reference-claude-code-docs-67a0bbe41f.md) for systems that don't have plugins yet
* [Remote Control](continue-local-sessions-from-any-device-with-remote-control-claude-code-docs-c1c03fd914.md) to drive a local session from your phone instead of forwarding events into it
* [Scheduled tasks](run-prompts-on-a-schedule-claude-code-docs.md) to poll on a timer instead of reacting to pushed events


---
## channels-reference-claude-code-docs-67a0bbe41f.md

## On this page

- [Overview](#overview)
- [What you need](#what-you-need)
- [Example: build a webhook receiver](#example-build-a-webhook-receiver)
- [Test during the research preview](#test-during-the-research-preview)
- [Server options](#server-options)
- [Notification format](#notification-format)
- [Expose a reply tool](#expose-a-reply-tool)
- [Gate inbound messages](#gate-inbound-messages)
- [Relay permission prompts](#relay-permission-prompts)
  - [How relay works](#how-relay-works)
  - [Permission request fields](#permission-request-fields)
  - [Add relay to a chat bridge](#add-relay-to-a-chat-bridge)
  - [Full example](#full-example)
- [Package as a plugin](#package-as-a-plugin)
- [See also](#see-also)

Reference

# Channels reference

Copy pageCopy page

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.

Copy pageCopy page

Channels are in [research preview](/docs/en/channels#research-preview). Team and Enterprise organizations must [explicitly enable them](/docs/en/channels#enterprise-controls).

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)

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.


[​](#what-you-need)

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)

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).

1

Create the project

The [permission relay](#relay-permission-prompts) examples import `zod` directly, so it installs alongside the MCP SDK. Create a new directory and install both:

```python
mkdir webhook-channel && cd webhook-channel
bun add @modelcontextprotocol/sdk zod
```

2

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.

webhook.ts

```python
#!/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': {} } },
    // Claude Code delivers this to Claude as context when the server connects, 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 configures the server, connects over stdio, and starts an HTTP listener, in that order:

- **Server configuration**: creates the MCP server with `claude/channel` in its capabilities, which is what tells Claude Code this is a channel. Claude Code delivers the [`instructions`](#server-options) string to Claude as context when the server connects: 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.

3

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:

.mcp.json

```python
{
  "mcpServers": {
    "webhook": { "command": "bun", "args": ["./webhook.ts"] }
  }
}
```

Claude Code reads your MCP config at startup and spawns each server as a subprocess.

4

Test it

During the research preview, custom channels aren’t on the allowlist, so start Claude Code with the development flag:

```python
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):

```python
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:

```python
<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. A `failed` status usually means a dependency or import error in your server file. To see the stderr trace, restart with `claude --debug --dangerously-load-development-channels server:webhook` and check the debug log at `~/.claude/debug/<session-id>.txt`.
- **`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.

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)

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:

```python
# 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, your channel isn’t on the approved allowlist, so it stays on the development flag while you build and test.

This flag skips the allowlist only. The `channelsEnabled` organization policy still applies. Don’t use it to run channels from untrusted sources.


[​](#server-options)

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` or `false` | Optional. Set it to `{}` to declare 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. To opt out, omit the key or set it to `false`. Before v2.1.234, Claude Code treated `false` as declared. 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. Claude Code delivers it to Claude as context when the server connects. 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:

```python
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
    },
    // Claude Code delivers this to Claude as context when the server connects, so it knows how to handle your events
    instructions: 'Messages arrive as <channel source="your-channel" ...>. Reply with the reply tool.',
  },
)
```


[​](#notification-format)

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:

```python
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:

```python
<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)

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):

1

Enable tool discovery

In your `Server` constructor in `webhook.ts`, add `tools: {}` to the capabilities so Claude Code knows your server offers tools:

```python
capabilities: {
  experimental: { 'claude/channel': {} },
  tools: {},  // enables tool discovery
},
```

2

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`:

```python
// 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') {
    const { chat_id, text } = req.params.arguments as { chat_id: string; text: string }
    // send() is your outbound: POST to your chat platform, or for local
    // testing the SSE broadcast shown in the full example below.
    send(`Reply to ${chat_id}: ${text}`)
    return { content: [{ type: 'text', text: 'sent' }] }
  }
  throw new Error(`unknown tool: ${req.params.name}`)
})
```

3

Update the instructions

Update the `instructions` string in your `Server` constructor so Claude knows to route replies back through the tool. This example tells Claude to pass `chat_id` from the inbound tag:

```python
instructions: 'Messages arrive as <channel source="webhook" chat_id="...">. Reply with the reply tool, passing the chat_id from the tag.'
```

Here’s the complete `webhook.ts` with two-way support. Outbound replies stream over `GET /events` using [Server-Sent Events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events) (SSE), so `curl -N localhost:8788/events` can watch them live; inbound chat arrives on `POST /`:

Full webhook.ts with reply tool

```python
#!/usr/bin/env bun
import { Server } from '@modelcontextprotocol/sdk/server/index.js'
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'
import { ListToolsRequestSchema, CallToolRequestSchema } from '@modelcontextprotocol/sdk/types.js'

// --- Outbound: write to any curl -N listeners on /events --------------------
// A real bridge would POST to your chat platform instead.
const listeners = new Set<(chunk: string) => void>()
function send(text: string) {
  const chunk = text.split('\n').map(l => `data: ${l}\n`).join('') + '\n'
  for (const emit of listeners) emit(chunk)
}

const mcp = new Server(
  { name: 'webhook', version: '0.0.1' },
  {
    capabilities: {
      experimental: { 'claude/channel': {} },
      tools: {},
    },
    instructions: 'Messages arrive as <channel source="webhook" chat_id="...">. Reply with the reply tool, passing the chat_id from the tag.',
  },
)

mcp.setRequestHandler(ListToolsRequestSchema, async () => ({
  tools: [{
    name: 'reply',
    description: 'Send a message back over this channel',
    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'],
    },
  }],
}))

mcp.setRequestHandler(CallToolRequestSchema, async req => {
  if (req.params.name === 'reply') {
    const { chat_id, text } = req.params.arguments as { chat_id: string; text: string }
    send(`Reply to ${chat_id}: ${text}`)
    return { content: [{ type: 'text', text: 'sent' }] }
  }
  throw new Error(`unknown tool: ${req.params.name}`)
})

await mcp.connect(new StdioServerTransport())

let nextId = 1
Bun.serve({
  port: 8788,
  hostname: '127.0.0.1',
  idleTimeout: 0,  // don't close idle SSE streams
  async fetch(req) {
    const url = new URL(req.url)

    // GET /events: SSE stream so curl -N can watch Claude's replies live
    if (req.method === 'GET' && url.pathname === '/events') {
      const stream = new ReadableStream({
        start(ctrl) {
          ctrl.enqueue(': connected\n\n')  // so curl shows something immediately
          const emit = (chunk: string) => ctrl.enqueue(chunk)
          listeners.add(emit)
          req.signal.addEventListener('abort', () => listeners.delete(emit))
        },
      })
      return new Response(stream, {
        headers: { 'Content-Type': 'text/event-stream', 'Cache-Control': 'no-cache' },
      })
    }

    // POST: forward to Claude as a channel event
    const body = await req.text()
    const chat_id = String(nextId++)
    await mcp.notification({
      method: 'notifications/claude/channel',
      params: {
        content: body,
        meta: { chat_id, path: url.pathname, method: req.method },
      },
    })
    return new Response('ok')
  },
})
```

See all 86 lines

The [fakechat server](https://github.com/anthropics/claude-plugins-official/tree/main/external_plugins/fakechat) shows a more complete example with file attachments and message editing.


[​](#gate-inbound-messages)

Gate inbound messages

An ungated channel is a prompt injection vector. Anyone who can reach your endpoint can put text in front of Claude. A channel listening to a chat platform or a public endpoint needs a real sender check before it emits anything. Check the sender against an allowlist before calling `mcp.notification()`. This example drops any message from a sender not in the set:

```python
const allowed = new Set(loadAllowlist())  // from your access.json or equivalent

// inside your message handler, before emitting:
if (!allowed.has(message.from.id)) {  // sender, not room
  return  // drop silently
}
await mcp.notification({ ... })
```

Gate on the sender’s identity, not the chat or room identity: `message.from.id` in the example, not `message.chat.id`. In group chats, these differ, and gating on the room would let anyone in an allowlisted group inject messages into the session. The [Telegram](https://github.com/anthropics/claude-plugins-official/tree/main/external_plugins/telegram) and [Discord](https://github.com/anthropics/claude-plugins-official/tree/main/external_plugins/discord) channels gate on a sender allowlist the same way. They bootstrap the list by [pairing](/docs/en/channels#security). See either implementation for the full pairing flow. The [iMessage](https://github.com/anthropics/claude-plugins-official/tree/main/external_plugins/imessage) channel takes a different approach: it detects the user’s own addresses from the Messages database at startup and lets them through automatically, with other senders added by handle.


[​](#relay-permission-prompts)

Relay permission prompts

When Claude calls a tool that needs approval, the local terminal dialog opens and the session waits. A two-way channel can opt in to receive the same prompt in parallel and relay it to you on another device. Both stay live: you can answer in the terminal or on your phone, and Claude Code applies whichever answer arrives first and closes the other. Relay covers tool-use approvals like `Bash`, `Write`, and `Edit`. Project trust and MCP server consent dialogs don’t relay; those only appear in the local terminal. Claude Code v2.1.234 and later sends permission requests only to servers it registered as channels for the session, so relay sits behind the same [session opt-in and organization controls](/docs/en/channels#security) as message delivery. Relay also requires you to opt the server in with `--channels` or the development flag, and requires the server to declare the permission capability.


[​](#how-relay-works)

How relay works

When a permission prompt opens, the relay loop has four steps:

1.  Claude Code generates a short request ID and notifies your server
2.  Your server forwards the prompt and ID to your chat app
3.  The remote user replies with a yes or no and that ID
4.  Your inbound handler parses the reply into a verdict, and Claude Code applies it only if the ID matches an open request

The local terminal dialog stays open through all of this. If someone at the terminal answers before the remote verdict arrives, that answer is applied instead and the pending remote request is dropped.


[​](#permission-request-fields)

Permission request fields

The outbound notification from Claude Code is `notifications/claude/channel/permission_request`. Like the [channel notification](#notification-format), the transport is standard MCP but the method and schema are Claude Code extensions. The `params` object has four string fields your server formats into the outgoing prompt:

| Field           | Description                                                                                                                                                                                                                                                                                                                                                    |
|-----------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `request_id`    | Five lowercase letters drawn from `a`-`z` without `l`, so it never reads as a `1` or `I` when typed on a phone. Include it in your outgoing prompt so it can be echoed in the reply. Claude Code only accepts a verdict that carries an ID it issued. The local terminal dialog doesn’t display this ID, so your outbound handler is the only way to learn it. |
| `tool_name`     | Name of the tool Claude wants to use, for example `Bash` or `Write`.                                                                                                                                                                                                                                                                                           |
| `description`   | Human-readable summary of what this specific tool call does, never the command itself. For a Bash call this is Claude’s description of the command; when the model gives no description, the field is the constant `Run shell command` and carries zero command detail. Render `input_preview` when you have room.                                             |
| `input_preview` | The tool’s arguments as JSON-shaped display text, keyed per top-level field. For Bash this is the command; for Write, the file path and the content. Omit it from your prompt if you only have room for a one-line message. Your server decides what to show.                                                                                                  |

Clients on Claude Code v2.1.211 or later sanitize `description` and `input_preview` before relaying them. Expect three changes in the text you receive:

- Claude Code neutralizes direction-override characters, invisible characters, and quote and angle-bracket lookalikes.
- Claude Code folds each run of whitespace to a single space.
- Claude Code relays text whole up to 3,500 code points. For a longer value, you receive its start and its end around a counted `⋯ N code points elided ⋯` marker. The end of a long command still reaches the approver.

For `input_preview`, Claude Code applies the 3,500 limit to each top-level field of the arguments separately and keeps the JSON’s own structural quotes. Clients before v2.1.211 relay `description` raw and cut `input_preview` to 200 UTF-16 units with a trailing ellipsis. Clients on Claude Code v2.1.234 or later relay the marker `(value unserializable)` in place of an `input_preview` field value they can’t serialize safely, such as a circular structure or an extremely large array. You still receive the field’s key, and the preview’s other fields are unchanged. Clients on Claude Code v2.1.234 or later also mask credentials in `description` and `input_preview`. You receive `[REDACTED]` in place of a recognizable provider credential token, such as an API key or a personal access token. Expect three effects of the masking when you render the fields:

- Claude Code masks key names inside `input_preview` as well as their values. A key name you display may not match the key name in the input.
- Claude Code never masks a span that contains shell syntax, path characters, or URL characters. A mask can’t hide the command, file path, or destination being approved.
- Claude Code doesn’t mask a secret that lacks a recognizable prefix, or a secret that spans whitespace, such as a private-key block. Both reach your server unmasked.

Masking doesn’t change who receives the fields. Whatever stays unmasked goes only to servers you opted in with `--channels` or the development flag. Treat both fields as untrusted unless you control the client fleet. The verdict your server sends back is `notifications/claude/channel/permission` with two fields: `request_id` echoing the ID above, and `behavior` set to `'allow'` or `'deny'`. Allow lets the tool call proceed; deny rejects it. Neither verdict affects future calls.


[​](#add-relay-to-a-chat-bridge)

Add relay to a chat bridge

Adding permission relay to a two-way channel takes three components:

1.  A `claude/channel/permission: {}` entry under `experimental` capabilities in your `Server` constructor so Claude Code knows to forward prompts
2.  A notification handler for `notifications/claude/channel/permission_request` that formats the prompt and sends it out through your platform API
3.  A check in your inbound message handler that recognizes `yes <id>` or `no <id>` and emits a `notifications/claude/channel/permission` verdict instead of forwarding the text to Claude

Only declare the capability if your channel [authenticates the sender](#gate-inbound-messages), because anyone who can reply through your channel can approve or deny tool use in your session. To add these to a two-way chat bridge like the one assembled in [Expose a reply tool](#expose-a-reply-tool):

1

Declare the permission capability

In your `Server` constructor, add `claude/channel/permission: {}` alongside `claude/channel` under `experimental`:

```python
capabilities: {
  experimental: {
    'claude/channel': {},
    'claude/channel/permission': {},  // opt in to permission relay
  },
  tools: {},
},
```

2

Handle the incoming request

Register a notification handler between your `Server` constructor and `mcp.connect()`. Claude Code calls it with the [four request fields](#permission-request-fields) when a permission dialog opens. Your handler formats the prompt for your platform and includes instructions for replying with the ID:

```python
import { z } from 'zod'

// setNotificationHandler routes by z.literal on the method field,
// so this schema is both the validator and the dispatch key
const PermissionRequestSchema = z.object({
  method: z.literal('notifications/claude/channel/permission_request'),
  params: z.object({
    request_id: z.string(),     // five lowercase letters, include verbatim in your prompt
    tool_name: z.string(),      // e.g. "Bash", "Write"
    description: z.string(),    // summary of this call. Treat as untrusted.
    input_preview: z.string(),  // tool args as JSON-shaped text. Treat as untrusted.
  }),
})

mcp.setNotificationHandler(PermissionRequestSchema, async ({ params }) => {
  // send() is your outbound: POST to your chat platform, or for local
  // testing the SSE broadcast shown in the full example below.
  send(
    `Claude wants to run ${params.tool_name}: ${params.description}\n` +
    // input_preview carries the actual arguments; render it when you
    // have room: for Bash the description alone may be just
    // "Run shell command" with zero command detail
    `${params.input_preview}\n\n` +
    // the ID in the instruction is what your inbound handler parses in Step 3
    `Reply "yes ${params.request_id}" or "no ${params.request_id}"`,
  )
})
```

3

Intercept the verdict in your inbound handler

Your inbound handler is the loop or callback that receives messages from your platform: the same place you [gate on sender](#gate-inbound-messages) and emit `notifications/claude/channel` to forward chat to Claude. Add a check before the chat-forwarding call that recognizes the verdict format and emits the permission notification instead.The regex matches the ID format Claude Code generates: five letters, never `l`. The `/i` flag tolerates phone autocorrect capitalizing the reply; lowercase the captured ID before sending it back.

```python
// matches "y abcde", "yes abcde", "n abcde", "no abcde"
// [a-km-z] is the ID alphabet Claude Code uses (lowercase, skips 'l')
// /i tolerates phone autocorrect; lowercase the capture before sending
const PERMISSION_REPLY_RE = /^\s*(y|yes|n|no)\s+([a-km-z]{5})\s*$/i

async function onInbound(message: PlatformMessage) {
  if (!allowed.has(message.from.id)) return  // gate on sender first

  const m = PERMISSION_REPLY_RE.exec(message.text)
  if (m) {
    // m[1] is the verdict word, m[2] is the request ID
    // emit the verdict notification back to Claude Code instead of chat
    await mcp.notification({
      method: 'notifications/claude/channel/permission',
      params: {
        request_id: m[2].toLowerCase(),  // normalize in case of autocorrect caps
        behavior: m[1].toLowerCase().startsWith('y') ? 'allow' : 'deny',
      },
    })
    return  // handled as verdict, don't also forward as chat
  }

  // didn't match verdict format: fall through to the normal chat path
  await mcp.notification({
    method: 'notifications/claude/channel',
    params: { content: message.text, meta: { chat_id: String(message.chat.id) } },
  })
}
```

A remote reply that doesn’t exactly match the expected format fails in one of two ways, and in both cases the local terminal dialog stays open:

- **Different format**: your inbound handler’s regex fails to match, so text like `approve it` or `yes` without an ID falls through as a normal message to Claude.
- **Right format, wrong ID**: your server emits a verdict, but Claude Code finds no open request with that ID and drops it silently.


[​](#full-example)

Full example

The assembled `webhook.ts` below combines all three extensions from this page: the reply tool, sender gating, and permission relay. If you’re starting here, you’ll also need the [project setup and `.mcp.json` entry](#example-build-a-webhook-receiver) from the initial walkthrough. To make both directions testable from curl, the HTTP listener serves two paths:

- **`GET /events`**: holds an SSE stream open and pushes each outbound message as a `data:` line, so `curl -N` can watch Claude’s replies and permission prompts arrive live.
- **`POST /`**: the inbound side, the same handler as earlier, now with the verdict-format check inserted before the chat-forward branch.

Full webhook.ts with permission relay

```python
#!/usr/bin/env bun
import { Server } from '@modelcontextprotocol/sdk/server/index.js'
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'
import { ListToolsRequestSchema, CallToolRequestSchema } from '@modelcontextprotocol/sdk/types.js'
import { z } from 'zod'

// --- Outbound: write to any curl -N listeners on /events --------------------
// A real bridge would POST to your chat platform instead.
const listeners = new Set<(chunk: string) => void>()
function send(text: string) {
  const chunk = text.split('\n').map(l => `data: ${l}\n`).join('') + '\n'
  for (const emit of listeners) emit(chunk)
}

// Sender allowlist. For the local walkthrough we trust the single X-Sender
// header value "dev"; a real bridge would check the platform's user ID.
const allowed = new Set(['dev'])

const mcp = new Server(
  { name: 'webhook', version: '0.0.1' },
  {
    capabilities: {
      experimental: {
        'claude/channel': {},
        'claude/channel/permission': {},  // opt in to permission relay
      },
      tools: {},
    },
    instructions:
      'Messages arrive as <channel source="webhook" chat_id="...">. ' +
      'Reply with the reply tool, passing the chat_id from the tag.',
  },
)

// --- reply tool: Claude calls this to send a message back -------------------
mcp.setRequestHandler(ListToolsRequestSchema, async () => ({
  tools: [{
    name: 'reply',
    description: 'Send a message back over this channel',
    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'],
    },
  }],
}))

mcp.setRequestHandler(CallToolRequestSchema, async req => {
  if (req.params.name === 'reply') {
    const { chat_id, text } = req.params.arguments as { chat_id: string; text: string }
    send(`Reply to ${chat_id}: ${text}`)
    return { content: [{ type: 'text', text: 'sent' }] }
  }
  throw new Error(`unknown tool: ${req.params.name}`)
})

// --- permission relay: Claude Code (not Claude) calls this when a dialog opens
const PermissionRequestSchema = z.object({
  method: z.literal('notifications/claude/channel/permission_request'),
  params: z.object({
    request_id: z.string(),
    tool_name: z.string(),
    description: z.string(),
    input_preview: z.string(),
  }),
})

mcp.setNotificationHandler(PermissionRequestSchema, async ({ params }) => {
  send(
    `Claude wants to run ${params.tool_name}: ${params.description}\n` +
    `${params.input_preview}\n\n` +
    `Reply "yes ${params.request_id}" or "no ${params.request_id}"`,
  )
})

await mcp.connect(new StdioServerTransport())

// --- HTTP on :8788: GET /events streams outbound, POST routes inbound -------
const PERMISSION_REPLY_RE = /^\s*(y|yes|n|no)\s+([a-km-z]{5})\s*$/i
let nextId = 1

Bun.serve({
  port: 8788,
  hostname: '127.0.0.1',
  idleTimeout: 0,  // don't close idle SSE streams
  async fetch(req) {
    const url = new URL(req.url)

    // GET /events: SSE stream so curl -N can watch replies and prompts live
    if (req.method === 'GET' && url.pathname === '/events') {
      const stream = new ReadableStream({
        start(ctrl) {
          ctrl.enqueue(': connected\n\n')  // so curl shows something immediately
          const emit = (chunk: string) => ctrl.enqueue(chunk)
          listeners.add(emit)
          req.signal.addEventListener('abort', () => listeners.delete(emit))
        },
      })
      return new Response(stream, {
        headers: { 'Content-Type': 'text/event-stream', 'Cache-Control': 'no-cache' },
      })
    }

    // everything else is inbound: gate on sender first
    const body = await req.text()
    const sender = req.headers.get('X-Sender') ?? ''
    if (!allowed.has(sender)) return new Response('forbidden', { status: 403 })

    // check for verdict format before treating as chat
    const m = PERMISSION_REPLY_RE.exec(body)
    if (m) {
      await mcp.notification({
        method: 'notifications/claude/channel/permission',
        params: {
          request_id: m[2].toLowerCase(),
          behavior: m[1].toLowerCase().startsWith('y') ? 'allow' : 'deny',
        },
      })
      return new Response('verdict recorded')
    }

    // normal chat: forward to Claude as a channel event
    const chat_id = String(nextId++)
    await mcp.notification({
      method: 'notifications/claude/channel',
      params: { content: body, meta: { chat_id, path: url.pathname } },
    })
    return new Response('ok')
  },
})
```

See all 133 lines

Test the verdict path in three terminals. The first is your Claude Code session, started with the [development flag](#test-during-the-research-preview) so it spawns `webhook.ts`:

```python
claude --dangerously-load-development-channels server:webhook
```

This walkthrough tests the permission dialog itself, so once the session is open, press `Shift+Tab` until the status bar shows `⏸ manual mode on`. In auto mode the classifier would decide the `reply` call instead of you, and no dialog would open for the remote side to answer. In the second, stream the outbound side so you can see Claude’s replies and any permission prompts as they fire:

```python
curl -N localhost:8788/events
```

In the third, send a message that will make Claude try to run a command:

```python
curl -d "list the files in this directory" -H "X-Sender: dev" localhost:8788
```

Listing files is read-only, so Claude runs it without approval. The permission dialog opens when Claude calls the `reply` tool to send its answer back. The local dialog opens in your Claude Code terminal, and a moment later the prompt for `mcp__webhook__reply` appears in the `/events` stream, including the five-letter ID. Approve it from the remote side:

```python
curl -d "yes <id>" -H "X-Sender: dev" localhost:8788
```

The local dialog closes, the `reply` tool runs, and Claude’s reply appears in the stream. The three channel-specific pieces in this file:

- **Capabilities** in the `Server` constructor: `claude/channel` registers the notification listener, `claude/channel/permission` opts in to permission relay, `tools` lets Claude discover the reply tool.
- **Outbound paths**: the `reply` tool handler is what Claude calls for conversational responses; the `PermissionRequestSchema` notification handler is what Claude Code calls when a permission dialog opens. Both call `send()` to broadcast over `/events`, but they’re triggered by different parts of the system.
- **HTTP handler**: `GET /events` holds an SSE stream open so curl can watch outbound live; `POST` is inbound, gated on the `X-Sender` header. A `yes <id>` or `no <id>` body goes to Claude Code as a verdict notification and never reaches Claude; anything else is forwarded to Claude as a channel event.


[​](#package-as-a-plugin)

Package as a plugin

To make your channel installable and shareable, wrap it in a [plugin](/docs/en/plugins/overview) and publish it to a [marketplace](/docs/en/plugins/overview). Users install it with `/plugin install`, then enable it per session with `--channels plugin:<name>@<marketplace>`. A channel published to your own marketplace still needs `--dangerously-load-development-channels` to run, since it isn’t on the [approved allowlist](/docs/en/channels#supported-channels). The default allowlist is the channel plugins in `claude-plugins-official`. The community marketplace is not on the channel allowlist. If you are working with an Anthropic partner contact, reach out to them to coordinate an official-marketplace listing. On Team and Enterprise plans, an admin can instead include your plugin in the organization’s own [`allowedChannelPlugins`](/docs/en/channels#restrict-which-channel-plugins-can-run) list, which replaces the default Anthropic allowlist.


[​](#see-also)

See also

- [Channels](/docs/en/channels) to install and use Telegram, Discord, iMessage, or the fakechat demo, and to enable channels for a Team or Enterprise org
- [Working channel implementations](https://github.com/anthropics/claude-plugins-official/tree/main/external_plugins) for complete server code with pairing flows, reply tools, and file attachments
- [MCP](/docs/en/mcp) for the underlying protocol that channel servers implement
- [Plugins](/docs/en/plugins/overview) to package your channel so users can install it with `/plugin install`


---
## channels-reference.md

# 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.

> Channels are in research preview and require Claude Code v2.1.80 or later. They require claude.ai login. Console and API key authentication is not supported. Team and Enterprise organizations must explicitly enable them.

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 also 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 so you can approve or deny tool use remotely.

## Overview

A channel is an MCP server that runs on the same machine as Claude Code. Claude Code spawns it as a subprocess and communicates over stdio.

- **Chat platforms** (Telegram, Discord): your plugin runs locally and polls the platform's API for new messages
- **Webhooks** (CI, monitoring): your server listens on a local HTTP port. External systems POST to that port

## What you need

The only hard requirement is the `@modelcontextprotocol/sdk` package and a Node.js-compatible runtime (Bun, Node, or Deno).

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

## Example: build a webhook receiver

```ts
#!/usr/bin/env bun
import { Server } from '@modelcontextprotocol/sdk/server/index.js'
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'

const mcp = new Server(
  { name: 'webhook', version: '0.0.1' },
  {
    capabilities: { experimental: { 'claude/channel': {} } },
    instructions: 'Events from the webhook channel arrive as <channel source="webhook" ...>. Read them and act, no reply expected.',
  },
)

await mcp.connect(new StdioServerTransport())

Bun.serve({
  port: 8788,
  hostname: '127.0.0.1',
  async fetch(req) {
    const body = await req.text()
    await mcp.notification({
      method: 'notifications/claude/channel',
      params: {
        content: body,
        meta: { path: new URL(req.url).pathname, method: req.method },
      },
    })
    return new Response('ok')
  },
})
```

Register in `.mcp.json`:

```json
{
  "mcpServers": {
    "webhook": { "command": "bun", "args": ["./webhook.ts"] }
  }
}
```

Test with the development flag:

```bash
claude --dangerously-load-development-channels server:webhook
```

Then from another terminal:

```bash
curl -X POST localhost:8788 -d "build failed on main: https://ci.example.com/run/1234"
```

## Server options

| Field                                                    | Type     | Description                                                     |
| :------------------------------------------------------- | :------- | :-------------------------------------------------------------- |
| `capabilities.experimental['claude/channel']`            | `object` | Required. Always `{}`. Registers the notification listener.     |
| `capabilities.experimental['claude/channel/permission']` | `object` | Optional. Declares channel can receive permission relay requests.|
| `capabilities.tools`                                     | `object` | Two-way only. Standard MCP tool capability.                     |
| `instructions`                                           | `string` | Added to Claude's system prompt.                                |

## Notification format

| Field     | Type                     | Description                                            |
| :-------- | :----------------------- | :----------------------------------------------------- |
| `content` | `string`                 | The event body, delivered as body of `<channel>` tag.  |
| `meta`    | `Record<string, string>` | Optional. Each entry becomes a tag attribute.          |

## Expose a reply tool

For two-way channels, expose a standard MCP tool:

1. Add `tools: {}` to capabilities
2. Register `ListToolsRequestSchema` and `CallToolRequestSchema` handlers
3. Update `instructions` to tell Claude when to call the tool

## Gate inbound messages

Check the sender against an allowlist before calling `mcp.notification()`:

```ts
const allowed = new Set(loadAllowlist())
if (!allowed.has(message.from.id)) {
  return  // drop silently
}
await mcp.notification({ ... })
```

Gate on the **sender's identity**, not the chat or room identity.

## Relay permission prompts

> Permission relay requires Claude Code v2.1.81 or later.

When Claude calls a tool that needs approval, a two-way channel can receive the same prompt and relay it remotely.

### Permission request fields

| Field           | Description                                              |
| --------------- | -------------------------------------------------------- |
| `request_id`    | Five lowercase letters (a-z without l)                   |
| `tool_name`     | Name of the tool Claude wants to use                     |
| `description`   | Human-readable summary of what the tool call does        |
| `input_preview` | Tool arguments as JSON, truncated to 200 characters      |

The verdict sends `notifications/claude/channel/permission` with `request_id` and `behavior` set to `'allow'` or `'deny'`.

### Add relay to a chat bridge

1. Declare `claude/channel/permission: {}` capability
2. Handle `notifications/claude/channel/permission_request` notifications
3. Check inbound for `yes <id>` / `no <id>` verdict pattern

## Package as a plugin

Wrap in a plugin and publish to a marketplace. Users install with `/plugin install`, then enable per session with `--channels plugin:<name>@<marketplace>`.

## See Also

- [Channels](channels.md) — Install and use Telegram, Discord, iMessage, or fakechat
- [Working channel implementations](https://github.com/anthropics/claude-plugins-official/tree/main/external_plugins)
- [MCP](../06-MCP-Tools/General/connect-claude-code-to-tools-via-mcp-claude-code-docs.md) — The underlying protocol
- [Plugins](../08-Plugins-Skills/create-plugins-claude-code-docs.md) — Package your channel for installation


---
## channels.md

# 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.

> Channels are in research preview and require Claude Code v2.1.80 or later. They require claude.ai login. Console and API key authentication is not supported. Team and Enterprise organizations must explicitly enable them.

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.

You install a channel as a plugin and configure it with your own credentials. Telegram, Discord, and iMessage are included in the research preview.

## Supported channels

Each supported channel is a plugin that requires [Bun](https://bun.sh).

### Telegram

1. **Create a Telegram bot**: Open BotFather in Telegram and send `/newbot`. Copy the token.
2. **Install the plugin**: `/plugin install telegram@claude-plugins-official`
3. **Configure your token**: `/telegram:configure <token>`
4. **Restart with channels enabled**: `claude --channels plugin:telegram@claude-plugins-official`
5. **Pair your account**: Send any message to your bot, get the pairing code, run `/telegram:access pair <code>`, then `/telegram:access policy allowlist`

### Discord

1. **Create a Discord bot**: Go to Discord Developer Portal, create application, enable Message Content Intent
2. **Invite the bot**: OAuth2 > URL Generator, select `bot` scope with View Channels, Send Messages, Read Message History permissions
3. **Install the plugin**: `/plugin install discord@claude-plugins-official`
4. **Configure your token**: `/discord:configure <token>`
5. **Restart with channels enabled**: `claude --channels plugin:discord@claude-plugins-official`
6. **Pair your account**: DM your bot, get pairing code, run `/discord:access pair <code>`

### iMessage

The iMessage channel reads your Messages database directly and sends replies through AppleScript. Requires macOS.

1. **Grant Full Disk Access** for your terminal
2. **Install the plugin**: `/plugin install imessage@claude-plugins-official`
3. **Restart with channels enabled**: `claude --channels plugin:imessage@claude-plugins-official`
4. **Text yourself**: Self-chat bypasses access control
5. **Allow other senders**: `/imessage:access allow +15551234567`

You can also build your own channel — see the Channels reference.

## Quickstart with fakechat

Fakechat is a demo channel that runs a chat UI on localhost:

1. Install: `/plugin install fakechat@claude-plugins-official`
2. Restart: `claude --channels plugin:fakechat@claude-plugins-official`
3. Open http://localhost:8787 and type a message

## 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. iMessage detects the user's own addresses automatically.

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 if the channel declares it. Anyone who can reply through the channel can approve or deny tool use in your session.

## Enterprise controls

On Team and Enterprise plans, channels are off by default. Admins control availability through two managed settings:

| Setting                 | Purpose                                                    | When not configured            |
| :---------------------- | :--------------------------------------------------------- | :----------------------------- |
| `channelsEnabled`       | Master switch. Must be `true` for any channel to deliver.  | Channels blocked               |
| `allowedChannelPlugins` | Which plugins can register once channels are enabled.      | Anthropic default list applies |

Pro and Max users without an organization skip these checks entirely.

### Enable channels for your organization

Admins can enable channels from **claude.ai → Admin settings → Claude Code → Channels**, or by setting `channelsEnabled` to `true` in managed settings.

### Restrict which channel plugins can run

```json
{
  "channelsEnabled": true,
  "allowedChannelPlugins": [
    { "marketplace": "claude-plugins-official", "plugin": "telegram" },
    { "marketplace": "claude-plugins-official", "plugin": "discord" },
    { "marketplace": "acme-corp-plugins", "plugin": "internal-alerts" }
  ]
}
```

## How channels compare

| Feature                | What it does                                                    | Good for                                              |
| :--------------------- | :-------------------------------------------------------------- | :---------------------------------------------------- |
| Claude Code on the web | Runs tasks in a fresh cloud sandbox                             | Delegating self-contained async work                  |
| Claude in Slack        | Spawns a web session from an `@Claude` mention                  | Starting tasks from team conversation                 |
| Standard MCP server    | Claude queries it during a task; nothing is pushed              | Giving Claude on-demand access to a system            |
| Remote Control         | You drive your local session from claude.ai or mobile           | Steering in-progress work from another device         |
| **Channels**           | Push events from non-Claude sources into your running session   | Reacting to CI failures, chat messages, webhooks      |

## See Also

- [Channels reference](channels-reference.md) — Build your own channel
- [Remote Control](remote-control.md)
- [Scheduled tasks](run-prompts-on-a-schedule-claude-code-docs.md)


---
## checkpointing-94f7cd24eb.md

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

# 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 edit. This safety net lets you pursue ambitious, wide-scale tasks knowing you can always return to a prior code state.

### Automatic tracking

Claude Code tracks all changes made by its file editing tools:

* Every user prompt creates a new checkpoint
* Checkpoints persist across sessions, so you can access them in resumed conversations
* Automatically cleaned up along with sessions after 30 days (configurable)

### Rewind and summarize

Press `Esc` twice (`Esc` + `Esc`) or use the `/rewind` command to open the rewind menu. A scrollable list shows each of your prompts from 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
* **Never mind**: return to the message list without making changes

After restoring the conversation or summarizing, the original prompt from the selected message is restored into the input field so you can re-send or edit it.

#### Restore vs. summarize

The three restore options revert state: they undo code changes, conversation history, or both. "Summarize from here" works differently:

* Messages before the selected message stay intact
* The selected message and all subsequent messages get replaced with a compact AI-generated summary
* No files on disk are changed
* The original messages are preserved in the session transcript, so Claude can reference the details if needed

This is similar to `/compact`, but targeted: instead of summarizing the entire conversation, you keep early context in full detail and only compress the parts that are using up space. You can type optional instructions to guide what the summary focuses on.

<Note>
  Summarize keeps you in the same session and compresses context. If you want to branch off and try a different approach while preserving the original session intact, use [fork](/en/how-claude-code-works#resume-or-fork-sessions) instead (`claude --continue --fork-session`).
</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.

### 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.

### Not a replacement for version control

Checkpoints are designed for quick, session-level recovery. For permanent version history and collaboration:

* Continue using version control (ex. Git) for commits, branches, and long-term history
* Checkpoints complement but don't replace proper version control
* Think of checkpoints as "local undo" and Git as "permanent history"

## See also

* [Interactive mode](interactive-mode-c5c4efb32c.md) - Keyboard shortcuts and session controls
* [Commands](built-in-commands-claude-code-docs.md) - Accessing checkpoints using `/rewind`
* [CLI reference](cli-reference-7bb98e3312.md) - Command-line options


---
## checkpointing-claude-code-docs-49f31e05d8.md

## On this page

- [How checkpoints work](#how-checkpoints-work)
  - [Automatic tracking](#automatic-tracking)
  - [Rewind and summarize](#rewind-and-summarize)
  - [Rewind past a cleared conversation](#rewind-past-a-cleared-conversation)
  - [Guide a summary](#guide-a-summary)
- [Common use cases](#common-use-cases)
- [Limitations](#limitations)
  - [Bash command changes not tracked](#bash-command-changes-not-tracked)
  - [Subagent edits not restored](#subagent-edits-not-restored)
  - [External changes not tracked](#external-changes-not-tracked)
  - [Messages sent mid-turn not checkpointed](#messages-sent-mid-turn-not-checkpointed)
  - [Symlinked and hard-linked paths not restored](#symlinked-and-hard-linked-paths-not-restored)
  - [Not a replacement for version control](#not-a-replacement-for-version-control)
- [See also](#see-also)

Reference

# Checkpointing

Copy pageCopy page

Track, rewind, and summarize Claude’s edits and conversation to manage session state.

Copy pageCopy page

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)

How checkpoints work

As you work with Claude, checkpointing automatically captures the state of your code before each prompt you send that starts a turn.


[​](#automatic-tracking)

Automatic tracking

Claude Code tracks all changes made by its file editing tools:

- Every prompt you send that starts a turn 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 a session’s file snapshots in the [retention sweep](/docs/en/claude-directory#cleaned-up-automatically), by default about 30 days after the session last saved one. Rewinding to a checkpoint whose snapshots are gone can fail with [`No files were restored`](/docs/en/errors#no-files-were-restored). To keep snapshots longer, set [`cleanupPeriodDays`](/docs/en/settings-reference#cleanupperioddays).


[​](#rewind-and-summarize)

Rewind and summarize

Run `/rewind`, or press `Esc` twice when the prompt input is empty, to open the rewind menu.

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.

The rewind menu lists each prompt you sent during the session, except [messages that joined a running turn](#messages-sent-mid-turn-not-checkpointed). 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)

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.


[​](#guide-a-summary)

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.

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.


[​](#common-use-cases)

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)

Limitations


[​](#bash-command-changes-not-tracked)

Bash command changes not tracked

Checkpointing does not track files modified by Bash commands. For example, if Claude Code runs:

```python
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)

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)

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.


[​](#messages-sent-mid-turn-not-checkpointed)

Messages sent mid-turn not checkpointed

When a message you [queue while Claude works](/docs/en/interactive-mode#queue-messages-while-claude-works) reaches Claude within the running turn, it joins that turn instead of starting a new one. The message appears in the conversation, but Claude Code doesn’t create a checkpoint for it, and the rewind menu doesn’t list it. A queued message that Claude Code sends as part of a new turn gets a checkpoint as usual, including when several queued messages [share that turn](/docs/en/interactive-mode#when-claude-code-sends-what-you-queued). To remove such a message, or undo the edits Claude made after it, rewind to the prompt that started the turn. That rewinds the whole turn, including the work Claude did before your message arrived.


[​](#symlinked-and-hard-linked-paths-not-restored)

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)

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)

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


---
## checkpointing-claude-code-docs.md

# 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 edit. This safety net lets you pursue ambitious, wide-scale tasks knowing you can always return to a prior code state.


Automatic tracking

Claude Code tracks all changes made by its file editing tools:

- Every user prompt creates a new checkpoint
- Checkpoints persist across sessions, so you can access them in resumed conversations
- Automatically cleaned up along with sessions after 30 days (configurable)


Rewind and summarize

Press `Esc` twice (`Esc` + `Esc`) or use the `/rewind` command to open the rewind menu. A scrollable list shows each of your prompts from 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
- **Never mind**: return to the message list without making changes

After restoring the conversation or summarizing, the original prompt from the selected message is restored into the input field so you can re-send or edit it.


Restore vs. summarize

The three restore options revert state: they undo code changes, conversation history, or both. “Summarize from here” works differently:

- Messages before the selected message stay intact
- The selected message and all subsequent messages get replaced with a compact AI-generated summary
- No files on disk are changed
- The original messages are preserved in the session transcript, so Claude can reference the details if needed

This is similar to `/compact`, but targeted: instead of summarizing the entire conversation, you keep early context in full detail and only compress the parts that are using up space. You can type optional instructions to guide what the summary focuses on.

Summarize keeps you in the same session and compresses context. If you want to branch off and try a different approach while preserving the original session intact, use [fork](/docs/en/how-claude-code-works#resume-or-fork-sessions) instead (`claude --continue --fork-session`).


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:

```python
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.


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.


Not a replacement for version control

Checkpoints are designed for quick, session-level recovery. For permanent version history and collaboration:

- Continue using version control (ex. Git) for commits, branches, and long-term history
- Checkpoints complement but don’t replace proper version control
- Think of checkpoints as “local undo” and Git as “permanent 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


---
## checkpointing.md

> Fetch the complete documentation index at: https://code.claude.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Checkpointing

> Automatically track and rewind Claude's edits to quickly recover from unwanted changes.

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 edit. This safety net lets you pursue ambitious, wide-scale tasks knowing you can always return to a prior code state.

### Automatic tracking

Claude Code tracks all changes made by its file editing tools:

* Every user prompt creates a new checkpoint
* Checkpoints persist across sessions, so you can access them in resumed conversations
* Automatically cleaned up along with sessions after 30 days (configurable)

### Rewinding changes

Press `Esc` twice (`Esc` + `Esc`) or use the `/rewind` command to open up the rewind menu. You can choose to restore:

* **Conversation only**: Rewind to a user message while keeping code changes
* **Code only**: Revert file changes while keeping the conversation
* **Both code and conversation**: Restore both to a prior point in the session

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

## 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.

### 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.

### Not a replacement for version control

Checkpoints are designed for quick, session-level recovery. For permanent version history and collaboration:

* Continue using version control (ex. Git) for commits, branches, and long-term history
* Checkpoints complement but don't replace proper version control
* Think of checkpoints as "local undo" and Git as "permanent history"

## See also

* [Interactive mode](interactive-mode-claude-code-docs.md) - Keyboard shortcuts and session controls
* [Built-in commands](/en/interactive-mode#built-in-commands) - Accessing checkpoints using `/rewind`
* [CLI reference](cli-reference-7bb98e3312.md) - Command-line options


---
## choose-a-permission-mode-claude-code-docs-c8a9703cc1.md

## On this page

- [Available modes](#available-modes)
  - [Actions no mode auto-approves](#actions-no-mode-auto-approves)
- [Common setups](#common-setups)
- [Which mode a session starts in](#which-mode-a-session-starts-in)
  - [Start in a different permission mode](#start-in-a-different-mode)
- [Switch permission modes](#switch-permission-modes)
- [Auto-approve file edits with acceptEdits mode](#auto-approve-file-edits-with-acceptedits-mode)
- [Analyze before you edit with plan mode](#analyze-before-you-edit-with-plan-mode)
  - [Review and approve a plan](#review-and-approve-a-plan)
  - [Set plan mode as the default](#set-plan-mode-as-the-default)
- [Eliminate permission prompts with auto mode](#eliminate-prompts-with-auto-mode)
  - [Auto mode on Bedrock, Agent Platform, or Foundry](#enable-auto-mode-on-bedrock-agent-platform-or-foundry)
  - [Server-side classifier review](#server-side-classifier-review)
  - [What the classifier blocks by default](#what-the-classifier-blocks-by-default)
  - [The first read outside the working directories](#first-read-outside-the-working-directories)
  - [Boundaries you state in conversation](#boundaries-you-state-in-conversation)
  - [Approvals you state in conversation](#approvals-you-state-in-conversation)
  - [When auto mode falls back](#when-auto-mode-falls-back)
- [Allow only pre-approved tools with dontAsk mode](#allow-only-pre-approved-tools-with-dontask-mode)
- [Skip all checks with bypassPermissions mode](#skip-all-checks-with-bypasspermissions-mode)
- [Protected paths](#protected-paths)
- [Critical paths](#critical-paths)
  - [Remove-Item in PowerShell](#remove-item-in-powershell)
- [See also](#see-also)

Permissions and sandboxing

# Choose a permission mode

Copy pageCopy page

Control whether Claude asks before acting. Switch permission modes with Shift+Tab in the CLI, the mode indicator in VS Code, or the mode selector in Desktop.

Copy pageCopy page

A permission mode sets which actions Claude can take in a session without asking you first. In Manual mode, Claude Code stops and asks you before most actions that edit files, run shell commands, or reach the network. In [auto mode](#eliminate-prompts-with-auto-mode), a second model, the classifier, reviews actions instead of you; [how the classifier evaluates actions](#how-the-classifier-evaluates-actions) lists which actions it reviews and which skip it. With Claude Code v2.1.283 or later, auto mode is the built-in starting permission mode for interactive terminal and VS Code sessions. On earlier versions, it’s the built-in starting permission mode only on Pro, Max, and Team plans. [Which mode a session starts in](#which-mode-a-session-starts-in) covers the surfaces and settings that change the starting permission mode. You can also change a running session’s permission mode at any time.


[​](#available-modes)

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. Manual mode appears under its config value, `default`.

| Mode                                                                | What runs without asking                                                                                  | Best for                                        |
|:--------------------------------------------------------------------|:----------------------------------------------------------------------------------------------------------|:------------------------------------------------|
| `default`                                                           | Reads only                                                                                                | Reviewing every action yourself, 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)       | Reads and pre-approved tools; anything that would prompt is denied                                        | 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 plan-mode sessions where bypass permissions are available, meaning interactive terminal sessions started in a way that [puts `bypassPermissions` in the mode cycle](#switch-permission-modes). Modes set the baseline. Layer [permission rules](/docs/en/permissions#manage-permissions) on top to pre-approve or block specific tools. Deny rules block in every mode, including `bypassPermissions`. Deny and ask rules don’t apply to [`EndConversation`](/docs/en/tools-reference#endconversation-tool-behavior) as long as Claude still has at least one other tool it can call. Allow rules have no effect in `bypassPermissions`.


[​](#actions-no-mode-auto-approves)

Actions no mode auto-approves

Claude Code doesn’t auto-approve the following in any mode, including `bypassPermissions`. Each bullet links to the section that says what happens instead in each mode:

- Tools matched by an explicit [ask rule](/docs/en/permissions#manage-permissions)
- Connector tools your organization [set to `ask`](/docs/en/mcp#organization-controls-on-connector-tools), in sessions where that setting reaches Claude Code
- Tools that require user interaction: the built-in `AskUserQuestion` tool and MCP tools marked [`requiresUserInteraction`](/docs/en/mcp#require-approval-for-a-specific-tool)
- `rm` and `rmdir` removals targeting a [critical path](#critical-paths), which no allow rule or `PreToolUse` hook `"allow"` approves
- The [cross-session messaging safeguards](#skip-all-checks-with-bypasspermissions-mode)
- Reads outside the working directories while [`permissions.blockReadsOutsideWorkingDirectories`](/docs/en/settings-reference#permissions-blockreadsoutsideworkingdirectories) is on: recognized file-reading Bash commands prompt even in auto mode and `bypassPermissions` mode, and so does any [unsandboxed retry](/docs/en/sandboxing#the-unsandboxed-retry-escape-hatch) that needs approval to run outside the sandbox. Requires Claude Code v2.1.257 or later. A command the shell parser can’t trace, such as one that changes directory more than once or runs a subshell, prompts the same way even when it names no outside path. This prompt doesn’t apply when the command runs in the [sandbox](/docs/en/sandboxing) and the sandbox enforces the block.


[​](#common-setups)

Common setups

Permission modes decide whether Claude asks before an action, and the [Bash sandbox](/docs/en/sandboxing) and outer [isolation boundaries](/docs/en/sandbox-environments) decide what an action can reach once it runs. Each row below pairs a goal with the flags or settings that get you there and the isolation it needs, as a starting point. [Available modes](#available-modes) lists what runs without a prompt in each mode.

| You want to                                              | Start with                                                                                                                                                               | Isolation needed                                                                                                                                                                                  | Notes                                                                                                                                                                                                                                    |
|:---------------------------------------------------------|:-------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| Review every action yourself                             | Manual mode: `claude --permission-mode default`                                                                                                                          | None                                                                                                                                                                                              | Sensitive work, unfamiliar code                                                                                                                                                                                                          |
| Iterate locally with fewer prompts, without a classifier | Manual mode plus the Bash sandbox in [auto-allow mode](/docs/en/sandboxing#sandbox-modes): `claude --permission-mode default`, then run `/sandbox` and select auto-allow | The built-in Bash sandbox, on macOS, Linux, and WSL2                                                                                                                                              | Deny rules still apply, and ask rules that name a command, such as `Bash(git push *)`, still prompt. To turn the sandbox on from a settings file instead, set [`sandbox.enabled`](/docs/en/settings-reference#sandbox-enabled) to `true` |
| Explore before changing anything                         | `claude --permission-mode plan`                                                                                                                                          | None                                                                                                                                                                                              | Claude Code blocks edits until you [approve a plan](#review-and-approve-a-plan)                                                                                                                                                          |
| Work hands-off in auto mode                              | `claude --permission-mode auto`, the [built-in starting permission mode](#which-mode-a-session-starts-in) with v2.1.283 or later                                         | None; a sandbox or container adds defense in depth                                                                                                                                                | Requires a [supported model](#eliminate-prompts-with-auto-mode), and your organization can [turn auto mode off](#eliminate-prompts-with-auto-mode)                                                                                       |
| Run in CI with an exact allowlist                        | `claude -p "run the test suite" --permission-mode dontAsk --allowedTools "Bash(npm test)" "Read"`                                                                        | None beyond what your CI runner provides                                                                                                                                                          | [Cloud sessions](/docs/en/claude-code-on-the-web) ignore `dontAsk` from settings files                                                                                                                                                   |
| Run fully unattended inside a container                  | `claude -p "<prompt>" --dangerously-skip-permissions`                                                                                                                    | Required: a container, VM, or the [sandbox runtime](/docs/en/sandbox-environments#sandbox-runtime); on Linux and macOS, run it as a [non-root user](#skip-all-checks-with-bypasspermissions-mode) | Cloud sessions ignore this mode from settings files. In this `-p` run, the [few calls that would still prompt](#skip-all-checks-with-bypasspermissions-mode) are denied instead                                                          |

The Bash sandbox and auto mode work independently and combine, with the exceptions listed under [Sandbox modes](/docs/en/sandboxing#sandbox-modes). For the full interaction, see [How sandboxing relates to permissions and permission modes](/docs/en/sandboxing#how-sandboxing-relates-to-permissions-and-permission-modes) and [How isolation relates to permission modes](/docs/en/sandbox-environments#how-isolation-relates-to-permission-modes).


[​](#which-mode-a-session-starts-in)

Which mode a session starts in

When you start a new session in a terminal, Claude Code takes the permission mode from the first of these that applies:

1.  The `--permission-mode` flag, or `--dangerously-skip-permissions`
2.  `permissions.defaultMode` in a [settings file](/docs/en/settings#where-settings-live) If you set `"auto"` in `.claude/settings.json` or `.claude/settings.local.json`, the value doesn’t take effect, and Claude Code then uses the built-in default rather than a `defaultMode` from `~/.claude/settings.json`. If you set `"bypassPermissions"` in those two files, it doesn’t take effect either, and the session starts in Manual mode. The other values apply from any settings file.
3.  The built-in default

Conversations the VS Code extension starts follow the extension’s own list in [Switch permission modes](#switch-permission-modes). For the permission mode Claude Code starts a resumed session in, see [permission mode on resume](/docs/en/sessions#permission-mode-on-resume). The built-in `auto` default requires Claude Code v2.1.228 or later on macOS, Linux, and WSL, and v2.1.233 or later on native Windows. On earlier versions, the built-in default is Manual. The built-in default depends on how you run Claude Code. The first row that matches your session applies. The table covers sessions you start in a terminal or through the VS Code extension; for the desktop app and claude.ai, see the Desktop and Web tabs in [Switch permission modes](#switch-permission-modes).

| How you run Claude Code                                            | Built-in starting permission mode                                                                                                                                                                                             |
|:-------------------------------------------------------------------|:------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| Any settings file sets `disableAutoMode` to `"disable"`            | `default`                                                                                                                                                                                                                     |
| `claude -p` or the [Agent SDK](/docs/en/agent-sdk/permissions)     | `default`                                                                                                                                                                                                                     |
| In a terminal or through the [VS Code extension](/docs/en/vs-code) | `auto` with Claude Code v2.1.283 or later; on earlier versions, `auto` on Pro, Max, or Team plans in sessions that [fetch feature flags](/docs/en/env-vars#features-that-need-feature-flag-fetching), and `default` otherwise |

In your [first session after an install or upgrade](/docs/en/env-vars#first-session-after-an-install-or-upgrade), Claude Code can choose the starting permission mode before its feature flags arrive. That session can start in a different permission mode than the table gives, and your next session matches the table. When the flag, a settings file, or the built-in default selects `auto` but auto mode isn’t available to the session, Claude Code starts the session in Manual instead. Auto mode is unavailable when the session doesn’t meet the [availability requirements](#eliminate-prompts-with-auto-mode), such as a settings file turning it off or a model that doesn’t support it, or when Anthropic has temporarily turned it off server-side. The first time the built-in default starts one of your sessions in auto mode, Claude Code shows a notice that links to this page:

- In a terminal, once, at the top of the session
- In the VS Code extension, as a card on the new-conversation screen that stays until you dismiss it

On Pro, Max, and Team plans, if your `~/.claude/settings.json` sets a `defaultMode` other than `auto` and no other settings file sets one, your sessions keep starting in that mode. Claude Code asks once, in the terminal or in the VS Code extension, whether to change the setting to auto mode. If you decline, your setting stays as it is.


[​](#start-in-a-different-mode)

Start in a different permission mode

You can set the starting permission mode for one session, or as a default for every session on a machine, in a project, or in an organization. When more than one settings file sets `permissions.defaultMode`, [settings precedence](/docs/en/settings#settings-precedence) decides, so a project or managed value outranks `~/.claude/settings.json`. To change the permission mode of a session that’s already running, see [Switch permission modes](#switch-permission-modes).

| To set the starting permission mode for          | Do this                                                                                                                                                                                                                                                                                                                                                             |
|:-------------------------------------------------|:--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| One session you’re about to start                | Pass the permission mode as a flag, for example `claude --permission-mode default`                                                                                                                                                                                                                                                                                  |
| Every terminal session you start on this machine | Set `permissions.defaultMode` in `~/.claude/settings.json`. For what the VS Code extension reads, see [Switch permission modes](#switch-permission-modes)                                                                                                                                                                                                           |
| Every terminal session you start in one project  | Set `permissions.defaultMode` in the project’s `.claude/settings.json`. Sessions you start in a terminal honor every value except `auto` and `bypassPermissions`; sessions the VS Code extension starts don’t read project settings for the starting permission mode                                                                                                |
| Every terminal session in your organization      | Set `permissions.defaultMode` in [managed settings](/docs/en/managed-settings). Terminal sessions start in that mode and people can still switch to auto mode; for what the VS Code extension reads, see [Switch permission modes](#switch-permission-modes). To remove auto mode so nobody can select it, set `permissions.disableAutoMode` to `"disable"` instead |

This example makes every terminal session on your machine start in Manual mode, whose config value is `default`. Save it in `~/.claude/settings.json`:

```python
{
  "permissions": {
    "defaultMode": "default"
  }
}
```

The next session you start shows `⏸ manual mode on` in the status bar.


[​](#switch-permission-modes)

Switch permission modes

Each interface has its own control for switching permission modes during a session and its own way of choosing the permission mode new sessions start in. Select your interface to see its controls.

- CLI

- VS Code

- JetBrains

- Desktop

- Web and mobile

**During a session**: press `Shift+Tab` to cycle permission modes. From `auto`, the first press switches to `default`, and the cycle then runs `default` → `acceptEdits` → `plan` → back to `default`. Optional modes, described below, slot in after `plan`. The status bar shows the active mode as a gray `⏸ manual mode on` for `default`, or as `⏵⏵ accept edits on`, `⏸ plan mode on`, `⏵⏵ auto mode on`, `⏵⏵ don't ask on`, or `⏵⏵ bypass permissions on`.Not every mode is in the default cycle:

- `auto`: appears when [auto mode is available](#eliminate-prompts-with-auto-mode); cycling to it switches permission 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 [user, `--settings`, or managed settings](/docs/en/settings-reference#permissions-defaultmode). The `--allow-` variant adds the permission 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`.**From a Bash permission prompt**: in the Manual and `acceptEdits` permission modes, when [auto mode](#eliminate-prompts-with-auto-mode) is available, Claude Code adds **Yes, and switch to auto mode** to a Bash command’s permission prompt. Select it to approve the command and switch the session to auto mode. [PowerShell tool](/docs/en/tools-reference#powershell-tool) prompts don’t offer the option. Requires Claude Code v2.1.247 or later.Claude Code doesn’t add the option to prompts forced by one of your [`ask` rules](/docs/en/permissions#manage-permissions) or by a [hook](/docs/en/hooks#pretooluse-decision-control), because auto mode still shows you those prompts, so switching wouldn’t remove them.**At startup**: pass the permission mode as a flag.

```python
claude --permission-mode plan
```

**As a default**: set `permissions.defaultMode` at the scope you want, as described in [Start in a different permission mode](#start-in-a-different-mode).The same `--permission-mode` flag works with `-p` for [non-interactive runs](/docs/en/headless).

**During a session**: click the mode indicator at the bottom of the prompt box. It uses these labels for the modes on this page:

| UI label           | Mode                |
|:-------------------|:--------------------|
| Manual             | `default`           |
| Edit automatically | `acceptEdits`       |
| Plan               | `plan`              |
| Auto               | `auto`              |
| Bypass permissions | `bypassPermissions` |

**As a default**: to pin the permission mode conversations start in, set `claudeCode.initialPermissionMode` in your VS Code user settings to `default`, `manual`, `acceptEdits`, `plan`, or `bypassPermissions`. The setting doesn’t accept `auto`; to start in Auto, leave it unset and pick **Auto** from the mode indicator once, as item 2 below describes. The extension starts each new conversation in the first of these that applies:

1.  `claudeCode.initialPermissionMode`
2.  The mode you last picked from the mode indicator, if it was Manual, Edit automatically, or Auto. Picking Plan or Bypass permissions applies to that conversation only
3.  `permissions.defaultMode` from [managed settings](/docs/en/managed-settings) or `~/.claude/settings.json`
4.  The [built-in default](#which-mode-a-session-starts-in) for your plan, provider, and organization settings

The extension never reads a project’s `.claude/settings.json` or `.claude/settings.local.json` for the starting permission mode. When `claudeCode.claudeProcessWrapper` is set, items 3 and 4 don’t apply: those conversations start in Manual unless item 1 or item 2 sets a permission mode.Before v2.1.283, item 3 applied only on Pro, Max, and Team plans in sessions that [fetch feature flags](/docs/en/env-vars#features-that-need-feature-flag-fetching).Auto appears in the mode indicator when [auto mode is available](#eliminate-prompts-with-auto-mode).Bypass permissions requires the **Allow dangerously skip permissions** toggle in the extension settings. Without it, the permission mode doesn’t appear in the indicator, and a `bypassPermissions` value from item 1 or item 3 starts the conversation in Manual instead. Auto from any item likewise starts the conversation in Manual when auto mode isn’t available.See the [VS Code guide](/docs/en/vs-code) for extension-specific details.

The JetBrains plugin runs Claude Code in the IDE terminal, so switching permission modes works the same as in the CLI: press `Shift+Tab` to cycle, or pass `--permission-mode` when launching.

**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 [auto mode is available](#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#where-settings-live). The desktop app reads the same settings files as the CLI and applies the permission 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.For where `defaultMode` goes in a settings file, see the example under [Start in a different permission mode](#start-in-a-different-mode).

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](/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 for a session you started yourself, and you can’t select Auto or Bypass permissions from the app. For a project thread running on your computer, see [Run a thread on your own computer](/docs/en/claude-projects#run-a-thread-on-your-own-computer).
  - Except for Bypass permissions, the dropdown shows the permission mode the local session is in, including one set from the terminal. It updates when the permission mode changes in the app or in the terminal. The session never reports Bypass permissions to claude.ai, so switching into it from the terminal doesn’t change what the dropdown shows.
  - Sessions hosted by the [desktop app](/docs/en/desktop) or the [VS Code extension](/docs/en/vs-code) report permission mode changes to claude.ai as they happen, the same as sessions hosted in a terminal.
  - Before v2.1.202, sessions connected with `/remote-control` or `claude --remote-control` didn’t report their permission mode at all, so claude.ai and the mobile app could show a permission mode the session wasn’t in. The mismatch affected only the label. Claude Code generated permission prompts from the session’s actual permission mode, and they still appeared in the app for approval.

For Remote Control, the local machine running the session must be signed in with your claude.ai account; API keys aren’t supported. You can also set the starting permission mode when launching that local session:

```python
claude remote-control --permission-mode acceptEdits
```


[​](#auto-approve-file-edits-with-acceptedits-mode)

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`. Each path also goes through the [symlink check](/docs/en/permissions#symlinks), so a write that resolves outside that scope isn’t auto-approved either. Paths outside that scope, writes to [protected paths](#protected-paths), `rm` and `rmdir` removals targeting a [critical path](#critical-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, and `Remove-Item` gets [its own check](#remove-item-in-powershell). 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:

```python
claude --permission-mode acceptEdits
```


[​](#analyze-before-you-edit-with-plan-mode)

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 interactive terminal sessions with [bypass permissions available](#skip-all-checks-with-bypasspermissions-mode), edits stay blocked until you approve the plan. What happens to a shell command during planning depends on the session, and the first of these cases that matches applies:

- **Interactive terminal sessions with bypass permissions available**: neither the classifier nor a prompt applies to planning commands. [Skip all checks with bypassPermissions mode](#skip-all-checks-with-bypasspermissions-mode) covers the few things that still prompt there.
- **[Auto mode](/docs/en/auto-mode-config) available and the `useAutoModeDuringPlan` setting on**, which it is by default: the classifier reviews shell commands other than [critical-path removals](#critical-paths) instead of prompting you. Approved commands run, and rejected ones are blocked.
- **Auto mode not available, or `useAutoModeDuringPlan` off**: 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.

Enter plan mode by pressing `Shift+Tab` or prefixing a single prompt with `/plan`. You can also start in plan mode from the CLI:

```python
claude --permission-mode plan
```

Press `Shift+Tab` again to leave plan mode without approving a plan.


[​](#review-and-approve-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). If auto mode isn’t [available to your session](#eliminate-prompts-with-auto-mode), for example because your organization turned it off, this option reads **Yes, auto-accept edits**. If you started the session with bypass permissions enabled, the option reads **Yes, and switch to BYPASS PERMISSIONS (no further prompts) for this session** 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-reference#showclearcontextonplanaccept) is enabled, the list gains a first option that approves the plan and clears the planning context. Accepting a plan also gives the session a [generated title](/docs/en/sessions#name-your-sessions) based on the plan, unless you’ve already named the session.


[​](#set-plan-mode-as-the-default)

Set plan mode as the default

To make plan mode the default for a project’s terminal sessions, set `defaultMode` to `plan` in `.claude/settings.json`, placed as the example under [Start in a different permission mode](#start-in-a-different-mode) shows. Conversations the [VS Code extension](/docs/en/vs-code) starts don’t read project settings for the starting permission mode. There, set `claudeCode.initialPermissionMode` to `plan` in your VS Code user settings instead.


[​](#eliminate-prompts-with-auto-mode)

Eliminate permission prompts with auto mode

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. With Claude Code v2.1.283 or later, auto mode is the [built-in starting permission mode](#which-mode-a-session-starts-in) for interactive terminal and VS Code sessions on every plan and provider. On earlier versions, it’s the built-in starting permission mode only on Pro, Max, and Team plans. The classifier also reviews each message Claude sends to another agent with [`SendMessage`](/docs/en/tools-reference), whether plain text or a structured [agent team](/docs/en/agent-teams) message, 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. By default, the classifier doesn’t review `rm` and `rmdir` removals targeting a critical path, such as `rm -rf /` or `rm -rf ~`. [Critical paths](#critical-paths) covers what happens to them in each permission mode. 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 in a mode that still prompts you, set the [Proactive output style](/docs/en/output-styles) instead.

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.

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/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 a [Fable model](/docs/en/model-config#work-with-fable). 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 or later, Opus 4.7 or later, and the Fable models. 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, first check these requirements and whether any settings file sets [`disableAutoMode`](/docs/en/settings-reference#disableautomode). Anthropic may also have turned auto mode off server-side, or the server may have rejected auto mode for your account. A session that received either answer keeps auto mode off until the session ends, so start a new session later. 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-reference#all-settings) and a terminal session starts in Manual mode with no error, the setting is likely in `.claude/settings.json` or `.claude/settings.local.json`. `auto` doesn’t take effect from those files. Move it to `~/.claude/settings.json`. For a conversation the VS Code extension started, check the extension’s own list in [Switch permission modes](#switch-permission-modes) instead.


[​](#enable-auto-mode-on-bedrock-agent-platform-or-foundry)

Auto mode on Bedrock, Agent Platform, or Foundry

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 is available by default. With Claude Code v2.1.283 or later, it’s also the [built-in starting permission mode](#which-mode-a-session-starts-in) for interactive terminal and [VS Code](/docs/en/vs-code) sessions. To choose the starting permission mode yourself, set `permissions.defaultMode` as [Start in a different permission mode](#start-in-a-different-mode) describes, or pick a permission mode from the VS Code extension’s mode indicator. Only Claude Sonnet 5 or later, Opus 4.7 or later, and the Fable models are supported on these providers. On any other model, the session starts in Manual instead. To prevent developers from using auto mode, set `disableAutoMode` to `"disable"` in [managed settings](/docs/en/managed-settings). This removes `auto` from the `Shift+Tab` cycle, and a session started with `--permission-mode auto` starts in Manual instead. A session already running in auto mode leaves it when the setting reaches that session from an [admin-deployed source](/docs/en/managed-settings#which-managed-source-claude-code-uses), and shows `auto mode disabled by settings`. Before v2.1.251, a running session kept auto mode until it ended. 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.


[​](#server-side-classifier-review)

Server-side classifier review

In auto mode, Claude Code can ask the server to check the actions that [the decision order](#how-the-classifier-evaluates-actions) sends for review, as part of the session’s model requests, in place of sending its own classifier requests. These sessions ask:

- **A direct connection to the Anthropic API**: in an interactive terminal session, on every claude.ai plan and on accounts that use the Claude API, as Anthropic rolls it out. Requires Claude Code v2.1.271 or later on Pro, Max, and Team plans, and v2.1.278 or later on Enterprise plans and Claude API accounts. From v2.1.282, a session that [doesn’t fetch feature flags](/docs/en/env-vars#features-that-need-feature-flag-fetching), for example because you turned telemetry off, asks the server by default in any kind of session.
- **A cloud provider, or an LLM gateway or proxy**: on [Claude Platform on AWS](/docs/en/claude-platform-on-aws), Amazon Bedrock, Google Cloud’s Agent Platform, and Microsoft Foundry, and whenever you point `ANTHROPIC_BASE_URL` at an [LLM gateway or proxy](/docs/en/llm-gateway), whatever your plan. Asking by default requires Claude Code v2.1.278 or later.
- **A signed-in [Claude apps gateway](/docs/en/claude-apps-gateway) session**: requires Claude Code v2.1.280 or later

Where the server reviews the actions, its verdicts decide them. Two other outcomes are possible:

- **The server doesn’t review the session**: a response completes with no review results, or the server answers that it doesn’t review this session. The most common causes are an LLM gateway or proxy that drops the request for review or the results, and a platform, region, or credential that doesn’t have server-side checks yet. Claude Code falls back to its own classifier requests. Once that fallback holds for the rest of the session, it shows a [notice about classifier request charges](/docs/en/auto-mode-classifier-billing) on accounts where those requests are billed.
- **The server gives no verdict for an action**: Claude Code denies the action rather than run it unreviewed. On any connection, this happens when the response ends before the review results arrive or the results arrive in a form Claude Code can’t read. An LLM gateway or proxy that cuts responses short or rewrites the results can cause either. On a direct connection to the Anthropic API, it also happens when the server’s check fails for the action, for example by timing out. [The server returned no safety verdict](/docs/en/errors#the-server-returned-no-safety-verdict) covers the denial message, what happens when denials repeat, and what to do.

To skip asking the server and always use Claude Code’s own classifier requests, set [`CLAUDE_CODE_AUTO_MODE_SERVER=0`](/docs/en/env-vars). On a direct connection to the Anthropic API, the variable requires Claude Code v2.1.281 or later. Setting it to `1` there turns server review on in a session that doesn’t have it yet, such as a `-p` or Agent SDK session, unless you’ve also set `CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS=1`. If you set `CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS=1` and leave `CLAUDE_CODE_AUTO_MODE_SERVER` unset, Claude Code also stops asking the server, except as [Disable pre-release capabilities](/docs/en/llm-gateway-protocol#disable-pre-release-capabilities) describes.


[​](#what-the-classifier-blocks-by-default)

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 passes 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 is committed or pushed, whether or not that commit or push 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. 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 that isn’t assigned anywhere in the conversation the classifier sees, or a glob rooted at such a variable. 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 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. A glob directly under the variable, as in `rm -rf "$VAR"/*`, is a [critical path](#critical-paths) instead. `Remove-Item` targets that are a bare `*` or end in `/*` or `\*` never reach the classifier: Claude Code [denies them outright](#remove-item-in-powershell).

Claude Code v2.1.257 and later also block these by default:

- Requesting credentials from the cloud instance-metadata endpoint, such as `169.254.169.254`, or explicitly authenticating a cloud, cluster, or registry call with the machine’s own service-account or node identity
- Reaching a public host by a route other than a direct request, such as a tunnel, a reverse shell, or a resolver or proxy configuration rewritten to point outside
- Reading credentials that belong to the host rather than to your task, such as node certificates or the node’s container-registry auth
- Connecting to or scanning sibling containers, pods, or VMs that Claude didn’t start, or the node beneath the container

If Claude Code runs somewhere that is meant to allow one of these, describe that setup in a [Host containment entry](/docs/en/auto-mode-config#define-trusted-infrastructure) in `autoMode.environment`. Claude Code v2.1.261 and later also block these by default:

- Posting or writing a link to a public paste, diagram, or data-sharing service in a message, PR or issue text, a document, or anywhere else the link will be opened or fetched, when the URL itself carries the content being shared, unless you named that service

**Allowed by default**:

- Local file operations in your working directory
- Installing dependencies declared in your lock files or manifests
- Forwarding request-scoped credentials to the matching first-party API endpoint
- Read-only HTTP requests
- Pushing to any branch of the repository you’re working in, including the default branch. A non-default branch whose name marks it as a deploy or publication target, such as `production` or `gh-pages`, isn’t covered: the classifier judges a push there on its own terms. The push’s content is still checked against the other rules, [`permissions.deny` rules](/docs/en/permissions#manage-permissions) can still block push commands [as written](/docs/en/permissions#bash-rule-limits) in every mode, and the remote’s own branch protection still applies. Before v2.1.211, only pushes to the branch you started on, branches Claude created, and routine pushes to the default branch were allowed by default, and before v2.1.203 any direct push to the default branch was blocked

Claude Code v2.1.195 and later also allow these by default:

- Deleting the exact jobs Claude created earlier in the same session
- Reading, reviewing, or writing security-related code, configs, and threat models as part of your task
- Messages between agents working together in the same multi-agent session
- Sending data to the trusted domains, buckets, and services you list in [`environment`](/docs/en/auto-mode-config#define-trusted-infrastructure). This covers data flow only, not destructive or credential operations on the same infrastructure
- [Claude in Chrome](/docs/en/chrome) navigation to a trusted internal domain, localhost, or a URL you named

Sandboxed commands don’t get network access by default. Claude names the hosts a command needs on the command itself, the classifier reviews them with the command, and an approved list opens those hosts for that one command alone. [Per-command allowed domains](/docs/en/sandboxing#per-command-allowed-domains-in-auto-mode) covers what a list can and can’t open and what happens when a command reaches for an unlisted host. Run `claude auto-mode defaults` to print the full rule lists as JSON. If routine actions get blocked, an administrator can add trusted repos, buckets, and services via the `autoMode.environment` setting: see [Configure auto mode](/docs/en/auto-mode-config). Pushing to any branch of the repository you’re working in and creating a pull request that matches your request run without a prompt, unless the push or pull request falls under the [blocked list](#what-the-classifier-blocks-by-default), such as secrets or sensitive data leaving the repository, or a pull request that targets a different repository or organization. To require a human checkpoint before these commands while staying in auto mode, add `permissions.ask` rules, which match the command [as written](/docs/en/permissions#bash-rule-limits): see [Common boundaries](/docs/en/auto-mode-config#common-boundaries).


[​](#first-read-outside-the-working-directories)

The first read outside the working directories

While [`permissions.blockReadsOutsideWorkingDirectories`](/docs/en/settings-reference#permissions-blockreadsoutsideworkingdirectories) is off, file reads run without a prompt in auto mode, including reads outside the [working directories](/docs/en/permissions#working-directories). The first time Claude uses the Read, Grep, or Glob tool on a path outside them, Claude Code asks whether to allow that read. The prompt doesn’t appear in non-interactive `-p` runs or background sessions; reads there run as before. Whatever you answer, Claude keeps working:

- **Yes, and keep allowing any reads outside the working directories**: the read runs, later reads outside the working directories run as before, and Claude Code records your answer so the prompt doesn’t appear again
- **No, and block reads outside the working directories from now on**: the read is refused, and Claude Code sets [`permissions.blockReadsOutsideWorkingDirectories`](/docs/en/settings-reference#permissions-blockreadsoutsideworkingdirectories) to `true` in your user settings, which makes the file tools refuse such reads in every later session and every permission mode. To let Claude read such a path later, add its directory with `/add-dir` or remove the setting.
- **No, and ask again next time**: the read is refused, and the next read outside the working directories prompts again
- **Yes, but ask again next time**: the read runs, nothing is saved, and the next read outside the working directories prompts again


[​](#boundaries-you-state-in-conversation)

Boundaries you state in conversation

The classifier treats boundaries you state in the conversation as a block signal. If you tell Claude “don’t push” or “wait until I review before deploying”, the classifier blocks matching actions even when the default rules would allow them. A boundary stays in force until you lift it in a later message. Claude’s own judgment that a condition was met does not lift it. Boundaries are not stored as rules. The classifier re-reads them from the transcript on each check, so a boundary can be lost if [context compaction](/docs/en/costs#reduce-token-usage) removes the message that stated it. For a hard guarantee, add a [deny rule](/docs/en/permissions#permission-rule-syntax) instead.


[​](#approvals-you-state-in-conversation)

Approvals you state in conversation

If you tell Claude that a blocked action is allowed, the classifier reads that as your approval and can clear the block. How you worded it decides whether the action runs, and how far the approval reaches:

- **Name the action and its specifics**: your message has to name the action and the specific thing that makes it dangerous, such as the branch of a force push. Naming the verb alone clears nothing, so “you can force-push” leaves the block in place.
- **Expect it to cover one action**: an approval covers the destructive action you named, so a later action is blocked again unless you granted the approval as standing. To stop approving a routine pattern one action at a time, add it to [`autoMode.allow`](/docs/en/auto-mode-config#override-the-block-and-allow-rules).
- **Some blocks stay in place**: [the classifier’s precedence order](/docs/en/auto-mode-config#override-the-block-and-allow-rules) sets out which blocks your approval can reach. To run a step it won’t clear, [leave auto mode](#switch-permission-modes) and answer the permission prompt.


[​](#when-auto-mode-falls-back)

When auto mode falls back

When auto mode can’t approve your session’s actions, what happens depends on the case:

- **A blocked action**: Claude Code shows a notification and lists the action in `/permissions` under the **Recently denied** tab, where you can press `r` to retry it with a manual approval. When the classifier produces [no verdict on the action](/docs/en/errors#auto-mode-cannot-determine-the-safety-of-an-action), because a safety check separate from auto mode refused the classifier’s own request or its response didn’t parse, Claude Code denies the action without the notification or the **Recently denied** entry.
- **Repeated blocks**: if the classifier blocks an action 3 times in a row or 20 times total, auto mode pauses and Claude Code resumes prompting. Approving the prompted action resumes auto mode. These thresholds are not configurable. Any allowed action resets the consecutive counter, while the total counter persists for the session and resets only when its own limit triggers a fallback. Claude Code doesn’t count a denial toward either threshold when [a safety check separate from auto mode refuses the classifier’s own request](/docs/en/errors#auto-mode-cannot-determine-the-safety-of-an-action); the linked entry covers how Claude Code handles those denials.
- **Sessions that can’t prompt**: a [non-interactive](/docs/en/headless) `-p` run without a [`--permission-prompt-tool`](/docs/en/cli-reference#cli-flags) has no prompt to fall back to. When repeated blocks reach a threshold, the action doesn’t run and Claude keeps working. The same applies when [a safety check separate from auto mode refuses the classifier’s request](/docs/en/errors#auto-mode-cannot-determine-the-safety-of-an-action). Claude Code doesn’t stop the run in either case.
- **No verdict from the server**: under [server-side classifier review](#server-side-classifier-review), Claude Code denies an action the server gives no verdict for, and stops the turn after ten responses in a row with no verdict. See [The server returned no safety verdict](/docs/en/errors#the-server-returned-no-safety-verdict).
- **A mode switch during a check**: if you switch permission modes while a classifier check is pending, Claude Code discards a verdict the new mode wouldn’t have requested rather than applying it: you’re prompted for approval instead, or the action is auto-denied in [`dontAsk` mode](#allow-only-pre-approved-tools-with-dontask-mode).

Repeated blocks usually mean the classifier is missing context about your infrastructure. Use `/feedback` to report false positives, or have an administrator [configure trusted infrastructure](/docs/en/auto-mode-config).

How the classifier evaluates actions

Each action goes through a fixed decision order. The first matching step wins:

1.  Actions matching your [allow, ask, or deny rules](/docs/en/permissions#manage-permissions) resolve immediately, with these exceptions:
    - Writes to [protected paths](#protected-paths) route to the classifier even when an allow rule matches
    - No allow rule approves `rm` and `rmdir` removals targeting a [critical path](#critical-paths)
    - MCP tools marked [`requiresUserInteraction`](/docs/en/mcp#require-approval-for-a-specific-tool) prompt you directly even when an allow rule matches, and so do connector tools [your organization set to `ask`](/docs/en/mcp#organization-controls-on-connector-tools) in sessions where that setting reaches Claude Code
    - A shell command that carries [per-command allowed domains](/docs/en/sandboxing#per-command-allowed-domains-in-auto-mode) also routes to the classifier even when an allow rule matches, because a rule approves the command, not its hosts
    - Ask rules that match on a command’s content, such as `Bash(git push *)`, fall back to a permission prompt
    - A write that the [symlink check](/docs/en/permissions#symlinks) resolves to a protected path prompts you when the path Claude requested isn’t itself protected
2.  Read-only actions and file edits in your working directory are auto-approved, except writes to [protected paths](#protected-paths) and [the first read outside the working directories](#first-read-outside-the-working-directories), which prompts you
    - In a session with [server-side classifier review](#server-side-classifier-review), read-only and [sandboxed](/docs/en/sandboxing#sandbox-modes) shell commands wait for that review and are blocked if it flags them
    - A write inside your working directory that the [symlink check](/docs/en/permissions#symlinks) resolves to a location outside it prompts you
3.  Everything else goes to the classifier, apart from [critical-path removals](#critical-paths) under their default handling. The connector tools and `requiresUserInteraction` MCP tools that prompt you directly in step 1 never reach the classifier either, so neither an org-required approval nor a consent step is auto-approved
4.  If the classifier blocks, Claude receives the reason and tries an alternative. In most sessions the reason names the rule the classifier matched, such as `[Data Exfiltration]`, rather than giving a written explanation; see [Review denials](/docs/en/auto-mode-config#review-denials)

On entering auto mode, broad allow rules that grant arbitrary code execution are dropped:

- Blanket `Bash(*)` or `PowerShell(*)`
- Wildcarded interpreters like `Bash(python*)`
- Package-manager run commands
- `Agent` allow rules
- [`Monitor`](/docs/en/tools-reference#monitor-tool) allow rules, because Claude Code runs Monitor commands through the shell

Narrow rules like `Bash(npm test)` stay in effect. Claude Code restores the dropped rules when you leave auto mode. Before v2.1.236, Claude Code left `Monitor` allow rules in effect in auto mode, so a rule that matched the whole tool approved Monitor commands without classifier review.Claude Code also runs `git status` itself before a command that would discard uncommitted work, such as `git reset --hard` or `rm -rf`, and shows the classifier whether staged, modified, or untracked work is present. Claude Code reports untracked files in that check even when the repository’s git configuration sets `status.showUntrackedFiles=no`.In the classifier requests sent by Claude Code itself, the classifier sees user messages, tool calls other than read-only lookups such as file reads and searches, and your CLAUDE.md content. Tool results are stripped from those requests, so hostile content in a file or web page can’t manipulate the classifier directly.You can annotate a call’s result with a [PostToolUse hook’s `classifierContext` field](/docs/en/hooks#annotate-a-result-for-the-auto-mode-classifier), which the classifier reads as application-provided context. The field requires Claude Code v2.1.236 or later.A separate server-side probe scans incoming tool results and flags suspicious content before Claude reads it. For more on how these layers work together, see the [auto mode announcement](https://claude.com/blog/auto-mode) and the [engineering deep dive](https://www.anthropic.com/engineering/claude-code-auto-mode).

How auto mode handles subagents

The classifier checks [subagent](/docs/en/sub-agents) work at three points:

1.  Before a subagent starts, the delegated task description is evaluated, so a dangerous-looking task is blocked at spawn time.
2.  While the subagent runs, each of its actions goes through the classifier with the same rules as the parent session, and any `permissionMode` in the subagent’s frontmatter is ignored.
3.  When the subagent finishes, the classifier reviews its work and its final report before the parent reads the report. When the classifier flags the subagent’s work or report, or a separate API safety check refuses the review, the report is still delivered, prepended with a security warning. When the classifier is unavailable for the review, the report arrives with a note to verify the subagent’s work before acting on it.

Cost and latency

The classifier runs on Claude Sonnet 5 by default rather than on your `/model` selection. A classifier model that Anthropic configures server-side takes precedence over that default. When your session’s model is Claude Sonnet 4.6, or when [`availableModels`](/docs/en/model-config#restrict-model-selection) excludes Sonnet 5, the classifier runs on the session’s model instead, or on an Opus model when the session runs on a [Fable model](/docs/en/model-config#work-with-fable); on providers other than the Anthropic API, that Opus fallback is the provider’s default Opus model.The session’s first auto-mode request validates the Sonnet 5 default: if the request succeeds, Sonnet 5 stays the session’s classifier model, and if it fails because the model isn’t available, the session uses the fallback instead. After that validation settles, the classifier’s model doesn’t change for the session.On Enterprise plans and on accounts that use the Claude API, [Claude Platform on AWS](/docs/en/claude-platform-on-aws), Amazon Bedrock, Google Cloud’s Agent Platform, or Microsoft Foundry, classifier calls count toward your token usage. Each check sends a portion of the transcript plus the pending action, adding a round-trip before execution. Reads and working-directory edits outside protected paths skip the classifier, so the overhead comes mainly from shell commands and network operations. Where the server reviews the actions as part of the session’s model requests, there are no separate classifier calls to count; see [Server-side classifier review](#server-side-classifier-review).Sandboxed network access adds no per-connection classifier requests. The classifier judges [the hosts a command names](/docs/en/sandboxing#per-command-allowed-domains-in-auto-mode) together with the command in one review, and Claude Code checks each connection against the approved list without calling the classifier again.


[​](#allow-only-pre-approved-tools-with-dontask-mode)

Allow only pre-approved tools with dontAsk mode

If you set `dontAsk` mode, Claude Code auto-denies every tool call that would otherwise prompt you. Claude still runs actions that need no approval in Manual mode, such as file reads inside your working directories and [read-only Bash commands](/docs/en/permissions#read-only-commands), plus actions matching your `permissions.allow` rules and calls approved by a [PreToolUse hook](/docs/en/permissions#extend-permissions-with-hooks). Use this mode for CI pipelines or restricted environments where you pre-define what Claude may do; the session never waits for input. The status bar shows `⏵⏵ don't ask on` while this mode is active. Claude Code denies calls matching your explicit [`ask` rules](/docs/en/permissions#manage-permissions) rather than prompting. It also denies the built-in `AskUserQuestion` tool even if your allow rules match it, and does the same to connector tools [your organization set to `ask`](/docs/en/mcp#organization-controls-on-connector-tools) in sessions where that setting reaches Claude Code. It denies MCP tools marked [`_meta["anthropic/requiresUserInteraction"]`](/docs/en/mcp#require-approval-for-a-specific-tool) the same way, because their approval card needs an answer this mode never collects; this requires Claude Code v2.1.199 or later. `rm` and `rmdir` removals targeting a [critical path](#critical-paths), such as `rm -rf /` and `rm -rf ~`, are denied even when an allow rule matches them or a `PreToolUse` hook allows them. [Cloud sessions](/docs/en/claude-code-on-the-web) ignore `defaultMode: "dontAsk"`; see [bypassPermissions](#skip-all-checks-with-bypasspermissions-mode) for details. Set it at startup with the flag:

```python
claude --permission-mode dontAsk
```


[​](#skip-all-checks-with-bypasspermissions-mode)

Skip all checks with bypassPermissions mode

`bypassPermissions` mode disables permission prompts and safety checks so tool calls execute immediately, including writes to [protected paths](#protected-paths). The [actions no mode auto-approves](#actions-no-mode-auto-approves) still prompt in this mode. The [Remove-Item in PowerShell](#remove-item-in-powershell) denies also apply in this mode. Two [cross-session messaging](/docs/en/cross-session-messaging) safeguards still apply in this mode, and in interactive terminal plan-mode sessions where bypass permissions are available:

- The [`isolatePeerMachines`](/docs/en/settings-reference#isolatepeermachines) approval prompt for messages to your sessions beyond this machine still appears.
- When no [`crossSessionInbound`](/docs/en/cross-session-messaging#control-inbound-messages) value applies, Claude Code holds an inbound message from another of your sessions for your approval, and delivers without asking only when the sending session identifies itself as also bypassing permission prompts. If you leave the permission mode while messages are held, Claude Code re-applies the inbound rules and delivers any held message they now accept.

In interactive terminal sessions with bypass permissions available, Claude Code also doesn’t enforce [plan mode’s](#analyze-before-you-edit-with-plan-mode) blocks. Claude is still instructed to plan without editing, but a file edit or shell command it attempts during planning runs without prompting. Explicit [ask rules](/docs/en/permissions#manage-permissions) and `rm` and `rmdir` removals targeting a [critical path](#critical-paths) still prompt. Plan mode keeps its blocks wherever Claude Code runs without an interactive terminal, including [non-interactive runs](/docs/en/headless) with `-p`, [Agent SDK](/docs/en/agent-sdk/permissions#plan-mode-plan) sessions, and conversations in the [VS Code extension](/docs/en/vs-code)’s chat panel. There, `--allow-dangerously-skip-permissions` makes `bypassPermissions` selectable later.

Only use this mode in isolated environments like containers, VMs, or dev containers without internet access, where Claude Code cannot damage your host system.

You can’t enter `bypassPermissions` from a session you started without it enabled. Enable it at launch with [`permissions.defaultMode: "bypassPermissions"`](/docs/en/settings-reference#permissions-defaultmode) or with an enabling flag:

```python
claude --permission-mode bypassPermissions
```

The `--dangerously-skip-permissions` flag is equivalent. Claude Code refuses `bypassPermissions` in a session you start with [`--restricted`](/docs/en/cli-reference#cli-flags). `--restricted` requires Claude Code v2.1.248 or later. The first time you start an interactive session with this mode enabled, Claude Code shows a warning dialog asking you to accept responsibility for actions taken without permission checks. Claude Code saves your acceptance to user settings, so the dialog appears only once. If you decline, Claude Code exits. In [non-interactive mode](/docs/en/headless) no dialog is shown, and a [background session](/docs/en/agent-view) started with `--bg` is refused until you’ve accepted the dialog in an interactive session. On Linux and macOS, Claude Code refuses to start in this mode when running as root or under `sudo`:

```python
--dangerously-skip-permissions cannot be used with root/sudo privileges for security reasons
```

The check is skipped automatically inside a recognized sandbox. To run autonomously in a container, use the [dev container](/docs/en/devcontainer) configuration, which runs Claude Code as a non-root user. [Cloud sessions](/docs/en/claude-code-on-the-web) don’t honor `defaultMode: "bypassPermissions"` or `"dontAsk"` from your settings files, so a repository’s checked-in settings can’t start a cloud session in bypass-permissions mode. The setting is ignored silently and the session starts in the permission mode shown in the mode dropdown instead. See [Switch permission modes](#switch-permission-modes) for which modes cloud sessions offer.

`bypassPermissions` offers no protection against prompt injection or unintended actions. For background safety checks with far fewer permission prompts, use [auto mode](#eliminate-prompts-with-auto-mode) instead. Administrators can block this mode by setting `permissions.disableBypassPermissionsMode` to `"disable"` in [managed settings](/docs/en/managed-settings).


[​](#protected-paths)

Protected paths

Writes to a small set of paths are never auto-approved, except in `bypassPermissions` mode and in interactive terminal sessions in plan mode with [bypass permissions](#skip-all-checks-with-bypasspermissions-mode) available. This prevents accidental corruption of repository state and Claude’s own configuration.

| Mode                     | Protected-path writes                                                                                                                                                                                                                                                        |
|:-------------------------|:-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `default`, `acceptEdits` | Prompted                                                                                                                                                                                                                                                                     |
| `plan`                   | Allowed in interactive terminal sessions with [bypass permissions](#skip-all-checks-with-bypasspermissions-mode) available. Otherwise, routed to the classifier when [auto mode](#eliminate-prompts-with-auto-mode) is available during planning, and prompted when it isn’t |
| `auto`                   | Routed to the classifier                                                                                                                                                                                                                                                     |
| `dontAsk`                | Denied                                                                                                                                                                                                                                                                       |
| `bypassPermissions`      | Allowed                                                                                                                                                                                                                                                                      |

In a session started with [`--restricted`](/docs/en/cli-reference#cli-flags), which requires Claude Code v2.1.248 or later, the classifier can’t approve protected-path writes. In the modes that route protected-path writes to the classifier, a write that the [symlink check](/docs/en/permissions#symlinks) resolves to a protected path prompts you instead when the path Claude requested isn’t itself protected. [`permissions.allow`](/docs/en/permissions#manage-permissions) rules in settings files do not pre-approve protected-path writes. The safety check runs before Claude Code evaluates allow rules from settings, so an entry such as `Edit(.claude/**)` in `~/.claude/settings.json` or `.claude/settings.json` does not change the per-mode outcome in the table above. In permission modes that prompt, the prompt for a write to the project’s `.claude/` folder or to `~/.claude/` can offer one of these session-scoped options:

- For the project’s `.claude/` folder: **Yes, and allow Claude to edit files in this project’s .claude folder for this session**
- For `~/.claude/`: **Yes, and allow Claude to edit files in its ~/.claude folder for this session**

Protected directories:

- `.git`
- `.config/git`
- `.vscode`
- `.idea`
- `.husky`
- `.cargo`
- `.devcontainer`
- `.yarn`
- `.mvn`
- `.claude`, except for `.claude/worktrees` where Claude stores its own git worktrees

Protected files:

- `.gitconfig`, `.gitmodules`
- `.bashrc`, `.bash_profile`, `.bash_login`, `.bash_aliases`, `.bash_logout`, `.zshrc`, `.zprofile`, `.zshenv`, `.zlogin`, `.zlogout`, `.profile`, `.envrc`
- `.npmrc`, `.yarnrc`, `.yarnrc.yml`, `.pnp.cjs`, `.pnp.loader.mjs`, `.pnpmfile.cjs`, `bunfig.toml`, `.bunfig.toml`
- `.bazelrc`, `.bazelversion`, `.bazeliskrc`
- `.pre-commit-config.yaml`, `lefthook.yml`, `lefthook.yaml`, `.lefthook.yml`, `.lefthook.yaml`
- `gradle-wrapper.properties`, `maven-wrapper.properties`
- `.devcontainer.json`
- `.ripgreprc`, `pyrightconfig.json`
- `.mcp.json`, `.claude.json`


[​](#critical-paths)

Critical paths

Claude Code never lets a [`permissions.allow`](/docs/en/permissions#manage-permissions) rule or a [`PreToolUse` hook](/docs/en/permissions#extend-permissions-with-hooks) that returns `"allow"` approve an `rm` or `rmdir` command that targets a critical path, even in modes that skip other prompts. This circuit breaker guards against model error. A matching deny rule still blocks the command outright. What happens instead depends on your permission mode:

| Mode                     | What Claude Code does with a critical-path removal                                                                                                                                            |
|:-------------------------|:----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `default`, `acceptEdits` | Asks you to approve it                                                                                                                                                                        |
| `plan`                   | Asks you to approve it. When [the classifier reviews commands during planning](#analyze-before-you-edit-with-plan-mode) and no bypass permissions are available, handles it as in `auto` mode |
| `auto`                   | Asks you to approve it in the terminal, with a time limit. Elsewhere, denies it                                                                                                               |
| `dontAsk`                | Denies it                                                                                                                                                                                     |
| `bypassPermissions`      | Asks you to approve it, with a time limit in the terminal                                                                                                                                     |

If an explicit [ask rule](/docs/en/permissions#manage-permissions) matches the command, Claude Code asks you instead, even in `auto` mode and without a time limit. In modes that ask, a [`PermissionRequest` hook](/docs/en/hooks#permissionrequest) can answer the prompt. The `auto` and `bypassPermissions` handling requires Claude Code v2.1.281 or later. To turn it off, set [`CLAUDE_CODE_DISABLE_DANGEROUS_RM_TIMEOUT=1`](/docs/en/env-vars#variables) in the environment that launches Claude Code. In `auto` mode, critical-path removals then go to the classifier instead, and in `bypassPermissions` mode the prompt has no time limit. In `auto` and `bypassPermissions` modes, the terminal prompt shows a two-minute countdown:

- If the countdown runs out before you answer, Claude Code denies the command and tells Claude what to do instead, so an unattended session keeps working.
- Press any key while the prompt is open to stop the countdown and keep the prompt waiting for your answer.
- After three of these prompts run out unanswered in a session, Claude Code stops showing them and denies further critical-path removals immediately. Sending a new message starts the count over.

In `auto` mode, wherever Claude Code can’t show you a terminal prompt, it denies the command immediately, for example in [non-interactive runs](/docs/en/headless) with `-p`, in [Agent SDK](/docs/en/agent-sdk/permissions) sessions, and in the VS Code extension’s chat panel and the Desktop app. The denial tells Claude to report what it wanted to delete and leave the removal to you. Claude Code treats an `rm` or `rmdir` target as a critical path when it is any of the following:

- The filesystem root
- Top-level directories, meaning any direct child of the root, such as `/usr`, `/etc`, or `/data`
- Your home directory
- Windows drive roots and their top-level directories, such as `C:\` and `C:\Windows`
- Your working directory and its parents
- Your additional working directories and their parents, but only when the removal is a glob under one of them, such as `rm -rf <dir>/*`. `rm -rf <dir>` on the directory itself doesn’t trigger this check

Claude Code also treats a glob or trailing slash directly under a shell variable, such as `rm -rf "$DIR"/*`, as a critical-path removal, because the command becomes a removal from the filesystem root when the variable is empty. The prompt for this variable case names the flagged `rm` and says how to rewrite it so the check passes:

- For a variable such as `$DIR`, guard each expansion so the shell stops with an error when the variable is unset or empty, as in `rm -rf "${DIR:?}"/*`, or use a literal path
- For a variable that is normally set, such as `$HOME`, use a literal path

A removal whose expansions are all guarded that way passes this check, so in `bypassPermissions` mode it runs without a prompt unless another check in this section flags it. Claude Code also treats these targets as critical paths:

- **A shell variable followed by one top-level directory name**, such as `rm -rf "$TMPDIR/mnt"`: when the variable expands empty, the command removes `/mnt`. This covers common top-level names such as `mnt`, `tmp`, `usr`, and `Users`.
- **A variable that the same command assigns from a directory-printing substitution**, such as `D=$(pwd); rm -rf "$D"` or an assignment from `$(git rev-parse --show-toplevel)`: the value can name your working directory or repository root. A `"${D:?}"` guard doesn’t clear this check, because the variable isn’t empty; use a literal path instead.
- **A backslash-only target**, such as `rm -rf "\\"`: Git Bash on Windows reads a lone backslash as the current drive’s root, so the check applies on every platform.
- **Only the output of a command substitution**, such as `rm -rf "$(pwd)"`, when the `rm` is recursive: Claude Code can’t check the target before the command runs, so the prompt tells Claude to run the substitution on its own first and then remove the literal paths it prints. To turn off this one check, set [`CLAUDE_CODE_DISABLE_SUBSTITUTION_RM_PROMPT=1`](/docs/en/env-vars#variables) in the environment that launches Claude Code.

When a trailing command substitution can expand empty, as in `rm -rf ~/$(cmd)`, Claude Code checks the path that would remain, your home directory in this example. Hiding the removal inside a subshell with `(...)`, a brace group with `{ ...; }`, command substitution with `$(...)` or backticks, or process substitution with `<(...)`, doesn’t skip the check. Claude Code finds a critical-path removal whether it sits inside the nested form, as in `(rm -rf ~)` or `echo "$(rm -rf ~)"`, or elsewhere in the same command.


[​](#remove-item-in-powershell)

Remove-Item in PowerShell

When you enable the [PowerShell tool](/docs/en/tools-reference#powershell-tool), Claude Code gives `Remove-Item` and the `cmd` built-ins `rd`, `rmdir`, `del`, and `erase` their own checks, separate from the `rm` critical-path list. For `Remove-Item`, the outcome depends on the target, and the first matching case applies:

- **System paths**: the filesystem root and its top-level directories, drive roots and their top-level directories, and your home directory. Claude Code denies the command in every mode, without asking you.
- **Wildcards**: a bare `*`, or any target ending in `/*` or `\*`, including a glob under a shell variable such as `$dir/*`. Claude Code denies the command in every mode, without asking you, before the [classifier](#eliminate-prompts-with-auto-mode) sees it.
- **Your working directory or one of its parents, with `-Recurse`**: Claude Code treats the command like any other that needs approval in your permission mode, so it asks you in modes that ask, sends it to the classifier in `auto` mode, and denies it in `dontAsk` mode. `bypassPermissions` mode skips this check.

The system-paths case also applies to `rd`, `rmdir`, `del`, and `erase` when Claude runs them through `cmd`, as in `cmd /c rd /s /q C:\Users`. By default, Claude Code denies such a command in every mode, without asking you. This `cmd` check requires Claude Code v2.1.283 or later. When judging a `cmd` target, Claude Code treats a PowerShell variable that follows literal text as empty. That makes `cmd /c rd /s /q "C:\$name"` a removal of `C:\`, so it is denied too. A trailing wildcard counts as the folder it empties, so `cmd /c del /q C:\*` is denied and `cmd /c del /q dist\*` in your project is not. To turn the `cmd` check off, set [`CLAUDE_CODE_DISABLE_POWERSHELL_CMD_RM_DENY=1`](/docs/en/env-vars#variables) in the environment that launches Claude Code. Claude Code ignores this variable in a settings file’s `env` block. `Remove-Item` on a system path stays denied either way.


[​](#see-also)

See also

- [Permissions](/docs/en/permissions): allow, ask, and deny rules; managed policies
- [Configure auto mode](/docs/en/auto-mode-config): tell the classifier which infrastructure your organization trusts
- [Hooks](/docs/en/hooks): custom permission logic via `PreToolUse` and `PermissionRequest` hooks
- [Security](/docs/en/security): safeguards and best practices
- [Sandboxing](/docs/en/sandboxing): filesystem and network isolation for Bash commands
- [Non-interactive mode](/docs/en/headless): run Claude Code with the `-p` flag


---
## choose-a-sandbox-environment-claude-code-docs-3f4c15772e.md

## On this page

- [Compare sandboxing approaches](#compare-sandboxing-approaches)
- [Choose an approach](#choose-an-approach)
  - [How isolation relates to permission modes](#how-isolation-relates-to-permission-modes)
- [Sandboxed Bash tool](#sandboxed-bash-tool)
- [Sandbox runtime](#sandbox-runtime)
  - [Set up and launch the runtime](#set-up-and-launch-the-runtime)
  - [What the runtime blocks on its own](#what-the-runtime-blocks-on-its-own)
  - [After unattended runs](#after-unattended-runs)
- [Dev containers](#dev-containers)
- [Custom container](#custom-container)
- [Virtual machine](#virtual-machine)
- [Cloud sessions](#cloud-sessions)
- [Enforce isolation across an organization](#enforce-isolation-across-an-organization)
- [See also](#see-also)

Permissions and sandboxing

# Choose a sandbox environment

Copy pageCopy page

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.

Copy pageCopy page

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.

For the broader security model, see [Security](/docs/en/security). For Agent SDK deployments, see [Secure deployment](/docs/en/agent-sdk/secure-deployment).


[​](#compare-sandboxing-approaches)

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, PowerShell, and Monitor 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                                                                                                         |
| [Cloud sessions](#cloud-sessions)           | Full operating system, hosted by Anthropic                                  | No              | None; requires a Claude subscription, and a connected GitHub account unless you launch with `claude --cloud` |

The [sandboxed Bash tool](/docs/en/sandboxing) is built into Claude Code and restricts 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.

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.


[​](#choose-an-approach)

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 a [cloud session](/docs/en/claude-code-on-the-web) if you have a Claude subscription; GitHub is not required when you launch with `claude --cloud` |
| 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                             | A [cloud session](/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)

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. The [actions no mode auto-approves](/docs/en/permission-modes#actions-no-mode-auto-approves) still apply. 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 shell commands, 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)

Sandboxed Bash tool

This option does not support native Windows. On Windows hosts, use WSL2 or one of the container or VM approaches below.

The sandboxed Bash tool is built into Claude Code. It uses operating system primitives to restrict the filesystem and network access of every Bash, PowerShell, or Monitor 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 [command hooks](/docs/en/hooks#command-hook-fields) 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)

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 shell commands. 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)

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:

```python
mkdir -p ~/.claude && { [ -f ~/.claude.json ] || echo '{}' > ~/.claude.json; }
```

Once the settings file is in place, launch Claude Code with `npx` and pass `claude` as the command to wrap:

```python
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)

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)

After unattended runs

Review the paths you kept writable. On Linux and WSL2, also review anything the session created.


[​](#dev-containers)

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)

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)

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.


[​](#cloud-sessions)

Cloud sessions

A [cloud session](/docs/en/claude-code-on-the-web) runs 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. Unless you launch from the CLI, 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. See [Use Claude Code in the cloud](/docs/en/claude-code-on-the-web) for plan availability and GitHub authentication options.


[​](#enforce-isolation-across-an-organization)

Enforce isolation across an organization

Individual developers can opt into any of the sandboxing approaches on this page. 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/managed-settings#delivery-mechanisms), 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)

See also

These pages cover configuration and policy details for the sandboxing approaches on this page.

- [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-reference#sandbox-settings): all sandbox configuration keys, including managed settings delivery


---
## chrome-8f9d762cc1.md

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

# Use Claude Code with Chrome (beta)

> 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](/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 is in beta and currently works with Google Chrome and Microsoft Edge. It is not yet supported on Brave, Arc, or other Chromium-based browsers. WSL (Windows Subsystem for Linux) is also not supported.
</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
* **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/) or [Microsoft Edge](https://www.microsoft.com/edge) browser
* [Claude in Chrome extension](https://chromewebstore.google.com/detail/claude/fcoeoabgfenejglbffodgkkbkcdhcgfn) version 1.0.36 or higher, available in the Chrome Web Store for both browsers
* [Claude Code](/en/quickstart#step-1-install-claude-code) version 2.0.73 or higher
* A direct Anthropic plan (Pro, Max, Team, or Enterprise)

<Note>
  Chrome integration is not available through third-party providers like Amazon Bedrock, Google Cloud Vertex AI, 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
    ```

    You can also enable Chrome from within an existing session by running `/chrome`.
  </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 theme={null}
    Go to code.claude.com/docs, click on the search box,
    type "hooks", and tell me what results appear
    ```
  </Step>
</Steps>

Run `/chrome` at any time to check the connection status, manage permissions, or reconnect the extension.

For VS Code, see [browser automation in VS Code](/en/vs-code#automate-browser-tasks-with-chrome).

### Enable Chrome by default

To avoid passing `--chrome` each session, run `/chrome` and select "Enabled by default".

In the [VS Code extension](/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.

## Example workflows

These examples show common ways to combine browser actions with coding tasks. Run `/mcp` and select `claude-in-chrome` 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 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 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 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.

### Draft content in Google Docs

Use Claude to write directly in your documents without API setup:

```text 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 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 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 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.

## Troubleshooting

### Extension not detected

If Claude Code shows "Chrome extension not detected":

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.

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

### 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.

### 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 not detected"             | 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](computer-use-427032da98.md): control native macOS apps when a task can't be done in a browser
* [Use Claude Code in VS Code](/en/vs-code#automate-browser-tasks-with-chrome): browser automation in the VS Code extension
* [CLI reference](cli-reference-7bb98e3312.md): command-line flags including `--chrome`
* [Common workflows](common-workflows-c909406123.md): more ways to use Claude Code
* [Data and privacy](../22-Safety-Policy/data-usage-claude-code-docs-06b2864aaa.md): how Claude Code handles your data
* [Getting started with Claude in Chrome](../15-Claude-AI-Features/getting-started-with-claude-in-chrome.md): full documentation for the Chrome extension, including shortcuts, scheduling, and permissions


---
## chrome.md

# Use Claude Code with Chrome (beta)

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 to give you browser automation capabilities from the CLI or the VS Code extension. 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.

> Chrome integration is in beta and currently works with Google Chrome and Microsoft Edge. WSL is not supported.

## Capabilities

- **Live debugging**: read console errors and DOM state, then fix the code
- **Design verification**: build a UI from a Figma mock, open it in browser to verify
- **Web app testing**: test form validation, check for visual regressions, verify user flows
- **Authenticated web apps**: interact with Google Docs, Gmail, Notion, or any app you're logged into
- **Data extraction**: pull structured information from web pages and save locally
- **Task automation**: automate repetitive browser tasks like data entry, form filling
- **Session recording**: record browser interactions as GIFs

## Prerequisites

- Google Chrome or Microsoft Edge
- Claude in Chrome extension v1.0.36+
- Claude Code v2.0.73+
- Direct Anthropic plan (Pro, Max, Team, or Enterprise)

> Not available through third-party providers (Bedrock, Vertex, Foundry).

## Get started

```bash
claude --chrome
```

Or enable from within a session with `/chrome`.

### Enable Chrome by default

Run `/chrome` and select "Enabled by default".

### Manage site permissions

Site-level permissions are inherited from the Chrome extension settings.

## Example workflows

### Test a local web application

```
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?
```

### Debug with console logs

```
Open the dashboard page and check the console for any errors when
the page loads.
```

### Automate form filling

```
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.
```

### Draft content in Google Docs

```
Draft a project update based on the recent commits and add it to my
Google Doc at docs.google.com/document/d/abc123
```

### Record a demo GIF

```
Record a GIF showing how to complete the checkout flow, from adding
an item to the cart through to the confirmation page.
```

## Troubleshooting

| 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 not detected"             | Chrome extension not installed or disabled        | Install or enable 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](computer-use.md)
- [VS Code browser automation](/en/vs-code#automate-browser-tasks-with-chrome)
- [CLI reference](cli-reference-7bb98e3312.md)


---
## claude-apps-gateway-configuration-claude-code-docs-8fd92cde14.md

## On this page

- [File structure](#file-structure)
- [Secret expansion](#secret-expansion)
- [Required sections](#required-sections)
  - [listen](#listen)
  - [oidc](#oidc)
  - [IdP requests through a forward proxy](#idp-requests-through-a-forward-proxy)
  - [Proxy-only egress](#proxy-only-egress)
  - [session](#session)
  - [store](#store)
  - [upstreams](#upstreams)
  - [Upstream error messages](#upstream-error-messages)
  - [Anthropic API](#anthropic-api)
  - [Amazon Bedrock](#amazon-bedrock)
  - [Claude Platform on AWS](#claude-platform-on-aws)
  - [Google Cloud Agent Platform](#google-cloud-agent-platform)
  - [Microsoft Foundry](#microsoft-foundry)
  - [Static headers on upstream requests](#static-headers-on-upstream-requests)
  - [Multiple upstreams](#multiple-upstreams)
- [Optional sections](#optional-sections)
  - [admin](#admin)
  - [enforcement](#enforcement)
  - [pricing](#pricing)
  - [Mark prices up](#mark-prices-up)
  - [Send the rates to signed-in clients](#send-the-rates-to-signed-in-clients)
  - [models](#models)
  - [managed](#managed)
  - [Matcher values that stop the gateway at boot](#matcher-values-that-stop-the-gateway-at-boot)
  - [What goes in cli](#what-goes-in-cli)
  - [MCP servers in a policy](#mcp-servers-in-a-policy)
  - [Claude Desktop overlay](#claude-desktop-overlay)
  - [Precedence with other managed sources](#precedence-with-other-managed-sources)
  - [telemetry](#telemetry)
  - [Add your own labels](#add-your-own-labels)
  - [Export directly to your collector](#export-directly-to-your-collector)
  - [When a destination fails](#when-a-destination-fails)
  - [HTTP tuning](#http-tuning)
  - [load_test_mode](#load_test_mode)
- [Complete example](#complete-example)
- [Client-side managed settings](#client-side-managed-settings)
- [Related](#related)

Claude apps gateway

# Claude apps gateway configuration

Copy pageCopy page

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.

Copy pageCopy page

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)

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 multiplier for the spend meter and for the cost figures developers see
- [`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
- [`load_test_mode`](#load_test_mode): load testing the gateway without calling a model provider


[​](#secret-expansion)

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)

Required sections


[​](#listen)

`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`. `X-Forwarded-For` entries written as `ipv4:port` or `[ipv6]:port`, as some load balancers do, are read with the port dropped. An IPv6 address with a port appended and no brackets may be read as a different address or not read at all, so turn off the port option on any proxy that writes that form.                                                       |


[​](#oidc)

`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.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 |
| `scope_on_refresh`              | No       | Also send `scope`, with the same list as the sign-in request, when the gateway exchanges a refresh token. Default `false`: the refresh request omits `scope`. Most IdPs return an id_token on every refresh and don’t need this. Set `true` when your IdP returns an id_token on refresh only if asked for `openid` again, which Okta documents for its refresh grant. Without an id_token, every refresh depends on the IdP’s userinfo endpoint accepting the refreshed access token. If you gate sign-in or match policies on groups and your IdP’s refresh-time id_token omits them, also set `userinfo_fallback: true` so the gateway fills them from the userinfo endpoint. An IdP that granted fewer scopes than requested can reject the refresh with `invalid_scope`, including for existing sessions if you add entries to `scopes` while this is on. Unset the key if refreshes start failing at `token_endpoint` after you set it. Requires Claude Code v2.1.260 or later on the gateway server. |
| `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`. `false` keeps those requests 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)

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. [Proxy-only egress](#proxy-only-egress) changes both of these: while it’s active, IdP requests follow the proxy unless you set `use_proxy: false`, and the gateway hands the proxy each IdP hostname without resolving it first.


[​](#proxy-only-egress)

Proxy-only egress

Set `CLAUDE_GATEWAY_PROXY_IS_EGRESS_BOUNDARY=1` in the gateway’s environment, next to `HTTPS_PROXY`, when the pod reaches other hosts only through that forward proxy and can’t resolve public DNS names itself, or when the proxy refuses `CONNECT` to an IP address. Requires v2.1.277 or later. It’s an environment variable rather than a `gateway.yaml` key so that nothing in the config file can relax the gateway’s address check.

```python
export HTTPS_PROXY=http://proxy.corp.example.com:3128
export NO_PROXY=
export no_proxy=
export CLAUDE_GATEWAY_PROXY_IS_EGRESS_BOUNDARY=1
```

The gateway logs one `network:` line at boot while proxy-only egress is active. Each row below is one class of outbound request on a gateway with `HTTPS_PROXY` set, by default and while proxy-only egress is active.

| Outbound request                                                                                                             | Default                                                                                                                                                          | Proxy-only egress active                                                                  |
|------------------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------|
| `provider: anthropic` upstreams, Workload Identity Federation token exchange, `telemetry.forward_to` exports                 | Resolved and checked locally, then `CONNECT` to the checked IP address through the proxy. A telemetry collector listed in `NO_PROXY` is reached directly instead | Hostname handed to the proxy                                                              |
| IdP discovery, JWKS, token, and userinfo                                                                                     | Direct unless [`oidc.use_proxy: true`](#idp-requests-through-a-forward-proxy), then `CONNECT` to the checked IP address                                          | Hostname handed to the proxy, unless `oidc.use_proxy: false` keeps an internal IdP direct |
| Amazon Bedrock, Claude Platform on AWS, Google Cloud’s Agent Platform, and Microsoft Foundry upstreams; Google group lookups | Hostname handed to the proxy                                                                                                                                     | Unchanged                                                                                 |

Proxy-only egress stays off unless the gateway’s environment meets all three of these conditions:

- `HTTPS_PROXY` or `HTTP_PROXY` is set.
- `NO_PROXY` and `no_proxy` are empty. If your platform injects either into pods, set both to an empty value on the gateway container. Listing a telemetry collector in `NO_PROXY` keeps proxy-only egress off.
- `CLAUDE_GATEWAY_ALLOW_LOOPBACK` isn’t turned on. A collector or IdP on the pod’s own loopback can’t be combined with proxy-only egress, because a loopback address handed to the proxy would be the proxy host’s own, so give those services an address the proxy can reach instead. For the same reason the gateway refuses `localhost`-style names outright while proxy-only egress is active.

When one of those conditions isn’t met, the gateway logs a warning at boot naming the variable that stopped it and keeps the default behavior. Once proxy-only egress is active, allow every destination in the proxy, including an internal collector and any host configured by IP address. You can still keep an internal IdP direct with [`oidc.use_proxy: false`](#idp-requests-through-a-forward-proxy).

Turn this on only when the proxy’s allowlist is at least as strict as the gateway’s own check. The proxy must refuse cloud metadata endpoints such as `169.254.169.254` and `metadata.google.internal`, link-local addresses, and the proxy host’s own loopback, and it must refuse them by the address a name resolves to, not only by name, because the gateway no longer catches a hostname that resolves to one of them. A proxy that connects anywhere it’s asked removes the gateway’s [SSRF guard](/docs/en/claude-apps-gateway-deploy#threat-model-summary) for these requests.


[​](#session)

`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)

`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`.                                                                                                             |
| `connect_timeout_seconds` | No       | Seconds the gateway waits when it opens a Postgres connection. A whole number from `1` to `60`, default `5`. Raise it if connection attempts time out when a new gateway instance starts. Requires Claude Code v2.1.274 or later on the gateway server. Earlier versions refuse to start when the key is set.                                                                                                                                |
| `readiness_grace_seconds` | No       | How many seconds `/readyz` keeps reporting ready after Postgres stops answering. A whole number from `0` to `3600`, default `0`. See [Outage behavior](/docs/en/claude-apps-gateway-deploy#outage-behavior) for how to pick a value. Requires Claude Code v2.1.282 or later on the gateway server. Earlier versions refuse to start when the key is set.                                                                                     |

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`

`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 the gateway fails over to the next upstream; 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. A `404` means that upstream doesn’t serve the requested model, so a later upstream in the list still can. If you set `forward_user_identity: true` on an upstream, a `429` it returns to a request that carried the developer’s email doesn’t fail over. See [how a per-user limit denial reaches the developer](#per-user-identity-headers-for-a-proxy-you-run). 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).


[​](#upstream-error-messages)

Upstream error messages

The gateway returns one upstream’s error response, or its own `502`, depending on how the upstreams answered:

- **An upstream returned a status the gateway doesn’t [fail over](#multiple-upstreams) on**: that upstream’s response. The gateway tries no further upstreams.
- **Every upstream the gateway tried failed in a way it [fails over on](#multiple-upstreams)**: the last `429`. When none returned a `429`, the gateway prefers, in order, the last `401` or `403`, the last `404`, and the last `501`. When none returned any of those, the gateway’s own `502`, `all upstreams failed (N attempted)`, where N counts every entry in [`upstreams`](#upstreams), including entries the gateway skipped because they don’t serve the requested model.

When the gateway returns an upstream’s response, it keeps the upstream’s status code. Whether it keeps the upstream’s message depends on the provider. An Anthropic API upstream’s error body reaches the developer unchanged. The Amazon Bedrock, Claude Platform on AWS, Google Cloud’s Agent Platform, and Microsoft Foundry upstreams can name your account IDs, role ARNs, and project IDs in their error text. The gateway records that full text in the [operational log](/docs/en/claude-apps-gateway-deploy#logs). What the developer sees from those upstreams depends on the rejection:

- `400` or `413` in Anthropic’s standard error envelope: the upstream’s own message, such as `prompt is too long`. Claude Platform on AWS, Agent Platform, and Microsoft Foundry return this envelope for model API rejections.
- `400` or `413` in the provider’s own shape: a `capability_rejected:` token. When the gateway can’t classify the rejection, `upstream rejected the request` on a `400` or `request too large for this upstream` on a `413`.
- Any other status: generic per-status copy, such as `upstream rate limit exceeded` on a `429`.

For example, the gateway replaces Amazon Bedrock’s `Input is too long for requested model.` with `capability_rejected: prompt_too_long`. Claude Code [compacts automatically](/docs/en/errors#prompt-is-too-long) on that token, as it does on `prompt is too long`. Keeping a cloud upstream’s `400` or `413` message, or replacing it with a `capability_rejected:` token, requires gateway v2.1.233 or later.


[​](#anthropic-api)

Anthropic API

The minimal Anthropic upstream is an API key from the [Claude Console](https://platform.claude.com):

```python
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.

```python
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
```

##### Per-user identity headers for a proxy you run

You can point a `provider: anthropic` upstream’s `base_url` at a proxy you run instead of at the Anthropic API. To tell that proxy which developer sent each request, set `forward_user_identity: true` on that upstream. The proxy can then attribute spend per developer. Requires a gateway running Claude Code v2.1.233 or later. For example, for a proxy at `upstream-gateway.internal.example.com`:

```python
upstreams:
  - provider: anthropic
    base_url: https://upstream-gateway.internal.example.com
    auth:
      api_key: ${PROXY_KEY}
    forward_user_identity: true        # default false
```

The gateway adds these headers to every request it forwards to that upstream.

| Header                        | Value                                                      |
|-------------------------------|------------------------------------------------------------|
| `x-litellm-end-user-id`       | The developer’s email, when the IdP supplied one.          |
| `x-claude-gateway-user-id`    | The developer’s IdP subject, from the token’s `sub` claim. |
| `x-claude-gateway-user-email` | The developer’s email, when the IdP supplied one.          |

When the IdP token carries no email, the gateway sends only `x-claude-gateway-user-id` and omits the two email headers. If your IdP puts the email in a different claim, set [`oidc.email_claim`](#oidc) to that claim. When your proxy answers `429` to a request that carried the developer’s email, the gateway returns that response to the developer as-is instead of failing over to the next upstream, so your proxy’s per-user budget or rate limit holds. The proxy’s other responses follow the ordinary [failover rules](#upstreams). If a developer’s IdP token carries no email, the gateway forwards their requests without the email headers, so a `429` to one of those requests counts as upstream capacity and fails over. Before v2.1.267 on the gateway server, every `429` failed over. Set `forward_user_identity` only on an upstream whose `base_url` is a proxy you operate. The gateway sends developer emails to whatever server that `base_url` names. If the `base_url` is the Anthropic API, which is the default, the gateway refuses to start.


[​](#amazon-bedrock)

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:

```python
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.*`. Also grant `bedrock:CountTokens` on the foundation-model ARNs. The gateway uses it, at no charge, to count the input tokens of a request the client abandoned, so [spend limits](#admin) stay accurate. Without it the gateway falls back to a one-token Bedrock request for that count. |
| 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

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:

```python
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)

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:

```python
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)

Microsoft Foundry

For the client-side Microsoft Foundry deployment, see [Claude Code on Microsoft Foundry](/docs/en/microsoft-foundry). The gateway-side upstream:

```python
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 `{ }`.                                                                                                                     |


[​](#static-headers-on-upstream-requests)

Static headers on upstream requests

To add fixed headers to the requests the gateway sends to one upstream, set `headers:` on that upstream. Use it when a proxy you run in front of the provider routes or attributes traffic by a header. `headers:` requires Claude Code v2.1.277 or later on the gateway server. An earlier gateway refuses to start when it finds the key. Upgrade every replica before you add the key, and remove the key before you roll back to an earlier version. The headers go to the server that `base_url` names, or to the provider’s own endpoint when `base_url` is unset. The provider receives them too unless your proxy removes them. This example reaches a `provider: vertex` upstream through a proxy at `upstream-proxy.internal.example.com`. It sets the `x-source` header the proxy reads, and sends a token from the `PROXY_TOKEN` environment variable as `x-proxy-token`:

```python
upstreams:
  - provider: vertex
    region: us-east5
    project_id: example-prod
    base_url: https://upstream-proxy.internal.example.com
    auth: {}
    headers:
      x-source: claude-apps-gateway
      x-proxy-token: ${PROXY_TOKEN}
```

Values are printable ASCII text with no space at either end. Quote a number, `true`, or `false` so YAML reads it as text. To keep a secret out of the config file, use [secret expansion](#secret-expansion) to load the value from an environment variable with `${VAR}` or from a file with `${file:/path}`. A `${VAR}` that resolves to an empty value stops the gateway from starting. `headers:` works on every provider, and each upstream sends only its own. Not every request that the gateway sends to an upstream carries them:

| Request the gateway sends to this upstream                             | Carries `headers:`                   |
|------------------------------------------------------------------------|--------------------------------------|
| `/v1/messages`, streaming or not, and `/v1/messages/count_tokens`      | Yes                                  |
| A request that failed over from another upstream                       | Yes, this upstream’s `headers:` only |
| Amazon Bedrock’s `CountTokens` call for a request the client abandoned | No                                   |
| The Workload Identity Federation token exchange                        | No                                   |

On an Amazon Bedrock or Claude Platform on AWS upstream that signs requests with AWS SigV4, these headers are part of the signature, so your proxy must pass them through unchanged. If you use a name the gateway reserves, it refuses to start, and the startup error names the header. Reserved names include:

- `authorization` and `x-api-key`
- `host`, `content-type`, and `user-agent`
- Any name starting with `anthropic-`, `x-goog-`, `x-amz-`, or `x-amzn-`


[​](#multiple-upstreams)

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. If you set [`forward_user_identity: true`](#per-user-identity-headers-for-a-proxy-you-run) on an upstream, a `429` to a request that carried the developer’s email is a per-user denial instead and doesn’t fail over. Every request starts at the first upstream. A request reaches a later upstream only when every upstream ahead of it has failed or doesn’t serve the requested model. The gateway keeps no record of failed upstreams, so while an upstream is down, every request that reaches it still tries it and waits for it to fail before moving on. For an Anthropic API upstream, [`timeouts.upstream_ttfb_ms`](#http-tuning) bounds the wait on a down upstream. That setting doesn’t apply to the other providers, where the gateway waits up to one hour for an upstream to start responding. `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:

```python
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
    provider: bedrock
    region: us-east-1
    auth:
      aws_access_key_id: ${ACCT2_AKID}
      aws_secret_access_key: ${ACCT2_SK}
  # Last resort: direct Anthropic API.
  - name: anthropic-fallback
    provider: anthropic
    auth:
      api_key: ${ANTHROPIC_API_KEY}

# Per-upstream model IDs are keyed on the upstream's `name:`.
models:
  - id: claude-opus-4-8
    label: Claude Opus 4.8
    upstream_model:
      bedrock-pt: arn:aws:bedrock:us-east-1:111111111111:provisioned-model/abcdef
      bedrock-od: us.anthropic.claude-opus-4-8
      bedrock-acct2: us.anthropic.claude-opus-4-8
      anthropic-fallback: claude-opus-4-8
```

| Lever                  | How                                                                                                                                                                                                                                                                                                                                                                                                                                                                       |
|------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| Different regions      | One Amazon Bedrock upstream per region, each with its own `region:`. With [`auto_include_builtin_models: true`](#models) the cross-region inference profiles route automatically; for region-pinned deployments use a `models:` block.                                                                                                                                                                                                                                    |
| Different accounts     | One Amazon Bedrock upstream per account, each with its own credentials in `auth:`. The default chain (`auth: {}`) uses the pod’s identity; for a second account, set explicit credentials or a bearer token.                                                                                                                                                                                                                                                              |
| Provisioned throughput | Map the model to the provisioned-throughput ARN in `models:` for that upstream’s name. Other upstreams keep the on-demand ID, so PT capacity is exhausted before failing over.                                                                                                                                                                                                                                                                                            |
| VPC / FIPS endpoints   | Set `base_url:` on the upstream to your VPC endpoint or FIPS endpoint URL                                                                                                                                                                                                                                                                                                                                                                                                 |
| Model-scoped routing   | Only a custom model `id`, one that isn’t a built-in Claude model, skips the upstreams absent from its `upstream_model:` map. The gateway tries built-in models on every upstream in order and uses the provider’s default ID where the map has no entry, so for built-in models the map changes which ID an upstream receives rather than whether it is tried; an upstream that rejects the ID follows the same [failover rules](#upstreams) as any other upstream error. |

Failing over between cloud providers, or to the direct Anthropic API, changes which agreement, geography, and other terms govern the request. The CLI applies the same feature gating to gateways regardless of which upstream serves a given request, so failover doesn’t send a body field an upstream would reject.


[​](#optional-sections)

Optional sections


[​](#admin)

`admin`

Optional. Enables `/v1/organizations/spend_limits`, which mirrors Anthropic’s public Admin API, and per-developer spend enforcement on `/v1/messages`. See [Spend limits](/docs/en/claude-apps-gateway-spend-limits) for how caps are set and enforced; this section covers the `gateway.yaml` keys that turn the feature on and tune it.

```python
admin:
  # Named static API keys for the admin endpoints, sent as x-api-key.
  # The id appears in the audit log as admin-key:<id> so each key is
  # attributable. Array for rotation: add the new key, roll clients,
  # remove the old.
  write_keys:
    - { id: terraform, key: "${GATEWAY_ADMIN_WRITE_KEY_TF}" }
    - { id: ci,        key: "${GATEWAY_ADMIN_WRITE_KEY_CI}" }
  read_keys:
    - { id: reporting, key: "${GATEWAY_ADMIN_READ_KEY}" }
  # IdP groups granted full admin via the normal gateway JWT (no API key).
  admin_groups: [platform-finops]
  blocked_message: request an increase at https://go.example.com/claude-limits
```

| Field                     | Required | Description                                                                                                                                                                                                                                                                                                                                                  |
|---------------------------|----------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `write_keys`              | No       | Array of `{id, key}`. An `x-api-key` matching one of these can list, set, and delete spend limits. Key values must be at least 32 characters; `id`s must be unique across `read_keys` and `write_keys`.                                                                                                                                                      |
| `read_keys`               | No       | Array of `{id, key}`. Read-only: every `GET` endpoint, including listing caps, fetching one by ID, and reading [`/effective`](/docs/en/claude-apps-gateway-spend-limits#%2Feffective) and [`/audit`](/docs/en/claude-apps-gateway-spend-limits#%2Faudit).                                                                                                    |
| `admin_groups`            | No       | IdP group names. A gateway JWT whose `groups` claim includes one of these has full admin access, read and write, and audits as `oidc:<sub>`. Use this for human admins; use API keys for machines. An empty entry in this list stops the gateway at boot. See [Matcher values that stop the gateway at boot](#matcher-values-that-stop-the-gateway-at-boot). |
| `blocked_message`         | No       | Appended verbatim to the `429 billing_error` a blocked developer sees. Write the whole instruction, such as a URL or a Slack channel. When unset, the gateway sends only the default message. See [How enforcement works](/docs/en/claude-apps-gateway-spend-limits#how-enforcement-works).                                                                  |
| `audit_retention_days`    | No       | Default `365`. Older `admin_audit` rows are swept.                                                                                                                                                                                                                                                                                                           |
| `spend_retention_months`  | No       | Default `13`. `spend` counter rows older than this are swept. The default keeps a full year plus the current partial month for year-over-year reporting.                                                                                                                                                                                                     |
| `identity_retention_days` | No       | Default `90`. Last-seen TTL for `principal_emails` rows, which hold each developer’s email, display name, and groups (PII). Deliberately shorter than spend retention so a deprovisioned identity ages out while its anonymous spend counters remain.                                                                                                        |
| `group_limit_mode`        | No       | `min` (default) or `max`. When a developer is in several groups with caps, `min` enforces the most restrictive and `max` the least. Used by both enforcement and `/effective`.                                                                                                                                                                               |


[​](#enforcement)

`enforcement`

The `enforcement` block controls how spend-limit checks behave when the store is unavailable.

| Field                  | Required | Description                                                                                                                                                                                                                                                                                                                                                                    |
|------------------------|----------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `fail_closed_on_error` | No       | Default `false`. Spend enforcement fails open on a Postgres outage, so inference stays up. Set `true` to fail closed: over-cap developers are blocked, but so is everyone else if the store is unreachable. Requires an [`admin:`](#admin) block: spend enforcement only runs when `admin` is configured, and the gateway refuses to start if you set this `true` without one. |


[​](#pricing)

`pricing`

The `pricing` block tells the spend meter what to charge instead of USD list price, so caps and [`/effective`](/docs/en/claude-apps-gateway-spend-limits#%2Feffective) reflect your contracted rates. Amounts stay in USD and remain an estimate, not an invoice. Two prerequisites:

- Claude Code v2.1.227 or later on the gateway server. Earlier versions reject the unknown key at boot.
- An [`admin:`](#admin) block or, in v2.1.268 or later, a [`managed:`](#managed) block with at least one policy. The gateway refuses to start with `pricing` set and neither block, because nothing would read it.

```python
pricing:
  multiplier: 0.85
  overrides:
    - upstream: bedrock-eu
      model: claude-sonnet-4-6
      input: 3.30
      output: 16.50
      cache_read: 0.33
      cache_write: 4.125
```

| Field        | Required | Description                                                                                                                                                                                                                     |
|--------------|----------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `multiplier` | No       | Default `1`. The meter multiplies every metered amount by this, whether list-priced or overridden, so `0.85` bills 85% of the price. Must be greater than 0 and at most 10, and a value above 1 is a [markup](#mark-prices-up). |
| `overrides`  | No       | Rows of `{upstream, model, input, output, cache_read, cache_write}` in USD per million tokens. All four rates are required. Each must be greater than 0 and at most 10000.                                                      |

How the meter matches an override row:

- A row replaces list price for requests that `upstream`, an [`upstreams[].name`](#upstreams), serves for `model`. That includes the higher [fast mode](/docs/en/fast-mode#understand-the-cost-tradeoff) rate, so fast and standard requests meter at the same four rates.
- A built-in ID such as `claude-sonnet-4-6`, matched like [`models[].id`](#models), covers every dated form, regional Amazon Bedrock form, or Google Cloud’s Agent Platform form the meter prices as that model. Any other string, such as an alias or an inference-profile ARN, matches the ID the client sent or the string sent upstream, case-insensitively.
- Where rows overlap, the meter picks the most specific row rather than the first row: a row whose `model` is the exact model string sent upstream, then a row matching the exact ID the client sent, then a row naming the built-in model.
- An unknown upstream name fails boot, and so do two rows for one upstream that name the same model, including two spellings of one built-in model. The gateway warns at boot about a row no requestable model can use.
- Web-search requests stay at the \$0.01 list price; the multiplier still applies to them.

For per-region rates, give each region its own named upstream and one row per upstream.


[​](#mark-prices-up)

Mark prices up

With v2.1.271 or later on the gateway server, you can set `multiplier` above 1, up to 10, to meter more than the provider charges, for example an internal chargeback rate. This example meters every request at 120% of the price:

```python
pricing:
  multiplier: 1.2
```

With an [`admin:`](#admin) block, the markup also applies to spend limits. The meter counts 120% of the price, so developers reach their caps sooner. The gateway logs a warning at boot that says so. The multiplier doesn’t change what the upstream provider charges for the requests. If the gateway also [sends the rates to signed-in clients](#send-the-rates-to-signed-in-clients), developers need Claude Code v2.1.271 or later to see the markup. Earlier clients ignore a `multiplier` above 1 and show costs without it. A gateway server earlier than v2.1.271 refuses to start if you set a `multiplier` above 1.


[​](#send-the-rates-to-signed-in-clients)

Send the rates to signed-in clients

With v2.1.268 or later on the gateway server, the gateway also puts the rates from `pricing` into the [`managed`](#managed) policies it serves, as the [`modelPricing`](/docs/en/settings-reference#modelpricing) managed setting. Developers matched by a policy then see the `pricing` rates for the first upstream that serves each model ID in `/usage`, the status line, and OpenTelemetry. A developer who matches no policy receives no managed settings, so their figures stay at list price. Clients apply the setting in Claude Code v2.1.242 or later.

- What the gateway adds: unless a policy’s `cli` block already sets `modelPricing`, the gateway adds the `multiplier` and, for every model ID a client can request, the override row of the first upstream that serves that ID. A rate that only a failover upstream charges stays on the gateway.
- Opt one policy out: set `modelPricing` to `{}` in that policy’s `cli` block, and its developers stay at list price.
- Keep a policy’s own rates: a policy whose `cli` block sets `modelPricing` with its own `multiplier` or `overrides` keeps that `modelPricing` whole, and the gateway adds no rates of its own to it.


[​](#models)

`models`

The `models` block is an optional admin-curated model list, served at `/v1/models` and used to translate model IDs per upstream. It is required for non-US Amazon Bedrock regions, Amazon Bedrock provisioned-throughput ARNs, and Microsoft Foundry deployment names.

```python
auto_include_builtin_models: true   # false: expose only the list below
models:
  - id: claude-opus-4-8
    label: Claude Opus 4.8
    # description: optional text shown in clients that surface it
    upstream_model:
      anthropic: claude-opus-4-8
      bedrock: us.anthropic.claude-opus-4-8   # or an inference-profile ARN
      foundry: your-opus-deployment-name
```

Each key under `upstream_model` must match the `name` of a configured upstream, which defaults to the provider name. A key that matches no upstream fails boot, so omit the lines for providers you don’t use.


[​](#managed)

`managed`

The `managed` block defines role-based access policies keyed on IdP groups or email domain. Policies are evaluated in order; the first match is selected, then merged onto the `match: {}` catch-all base. They are served per-user at `GET /managed/settings` with ETag/304 caching.

```python
managed:
  policies:
    # Specific groups first.
    - match: { groups: [eng-contractors] }
      cli:
        availableModels: [claude-sonnet-4-6]
        permissions: { deny: ["WebFetch", "WebSearch"] }
    # Default catch-all last: matches everyone who authenticated.
    - match: {}
      cli:
        availableModels: [claude-opus-4-8, claude-sonnet-4-6, claude-haiku-4-5]
```

A `match: {}` catch-all, conventionally listed last, is treated as a base layer. Every other policy inherits any key it doesn’t set from the catch-all, so per-role entries only need to list what differs from the org default. The merge rules depend on the key type:

- **Allow-lists**: `availableModels` and `permissions.allow`. A specific policy’s list fully replaces the base’s.
- **Deny-lists and hook arrays**: `permissions.deny`, `permissions.ask`, `disabledMcpjsonServers`, `deniedMcpServers`, `blockedMarketplaces`, and every `hooks` event-type array. These take the union of base and policy, so an org-wide deny or audit hook can’t be accidentally dropped by a per-role override.
- **Record-typed keys**: `env`, `modelOverrides`, and `skillOverrides`. These shallow-merge, so a per-role `env` block overrides keys it sets and inherits the rest from the base.

`availableModels` is also enforced server-side at `/v1/messages`, so a denied model returns `400` regardless of what the client sends. The gateway validates the `model` value itself before it relays a request, so a malformed value never reaches an upstream. It rejects the request with a `400` in two cases:

- When the value is missing or empty, the gateway rejects the request with the message `model is required`. That check requires a gateway running Claude Code v2.1.228 or later.
- When the value is present but isn’t a string, the gateway rejects the request with the message `model must be a string`. Requires a gateway running Claude Code v2.1.221 or later.

| Matcher                                             | Behavior                                                                                                                         |
|-----------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------|
| `match: {}`                                         | Matches every authenticated user. Start with one of these and add group-scoped policies above it later.                          |
| `match: { groups: [a, b] }`                         | Matches if the JWT’s `groups` claim contains any of the listed groups. Case-sensitive: groups must match the IdP’s exact casing. |
| `match: { email_domain: example.com }`              | Matches the part after the last `@` in the JWT’s `email` claim, case-insensitive. Accepts one domain per policy.                 |
| `match: { groups: [a], email_domain: example.com }` | Both conditions must match                                                                                                       |

An authenticated user who matches no policy gets the gateway’s defaults, which means every model in the catalog and no managed settings. Add a `match: {}` catch-all last if you want a guaranteed default policy.

The gateway keeps no user directory of its own. It authorizes each request from the user’s IdP token, reading group membership from the token’s `groups` claim and evaluating policies against it. There is no roster to enumerate and no accounts to pre-create, and therefore no SCIM endpoint, because there is nothing for SCIM to sync into.Run user and group lifecycle management at the source of truth, which is your IdP’s native SCIM provisioning or a dedicated identity-governance platform. Membership and deprovisioning governed there flow into the gateway automatically through the token. If you want SCIM provisioning of Claude accounts themselves, that is a [Claude for Enterprise](/docs/en/admin-setup) capability.Two propagation clocks apply:

- **Policy contents**: editing a policy and redeploying reaches connected clients on their next managed-settings poll, within an hour, apart from the [changes that apply only at the next launch](/docs/en/server-managed-settings#fetch-and-caching-behavior)
- **Group membership**: changing a user’s group membership changes which policy matches them. This takes effect on the next session re-mint, meaning the next silent refresh, bounded by `session.ttl_hours`.


[​](#matcher-values-that-stop-the-gateway-at-boot)

Matcher values that stop the gateway at boot

At boot, the gateway checks the `match` block of every policy and the [`admin_groups`](#admin) list. Any of these values stops the gateway with an error that names the field:

- An empty `groups` list
- An empty entry in `groups` or in `admin_groups`
- An empty `email_domain`
- An `email_domain` that contains `@`, whitespace, or a comma. The gateway trims the value and strips one leading `@` before this check. Write one bare domain, such as `example.com`.

Before v2.1.232, the gateway started with these values. Each value had this effect:

- An empty `email_domain`: the gateway skipped the domain check, so a policy with an empty `email_domain` and no `groups` list matched every authenticated user
- An empty `groups` list: the policy matched no one
- An `email_domain` containing `@`, whitespace, or a comma: the policy matched no one
- An empty entry in `groups` or in `admin_groups`: the entry matched a user only when that user’s IdP `groups` claim also contained an empty entry. In `admin_groups`, that match granted admin access. If your `admin_groups` list never contained an empty entry, no one gained admin access this way.


[​](#what-goes-in-cli)

What goes in `cli`

Each `cli` value is a complete Claude Code `managed-settings.json` document, the same schema you would deploy via MDM or `/etc/claude-code/managed-settings.json`, expressed here as YAML. The CLI applies the delivered document at the managed tier, above user and project settings, in place of server-managed settings. It therefore ignores the settings [restricted to OS-level policy sources](/docs/en/server-managed-settings#current-limitations), such as `policyHelper` and `wslInheritsWindowsSettings`. The gateway validates each document against the CLI’s settings schema at boot, so an unrecognized top-level key fails boot with an error naming every offending key. Deliberately open parts of the schema still accept arbitrary values, because newer clients may recognize entries the gateway’s schema doesn’t. These open keys include `env`, `pluginConfigs`, and keys nested under `permissions`. Because validation uses the schema bundled with the gateway’s installed version, putting a top-level settings key introduced by a newer Claude Code release into managed config requires upgrading the gateway first. Smoke-test a new policy on one client before rolling it out. The full key reference is in [Claude Code settings](/docs/en/settings-reference#all-settings). The keys most operators reach for first:

```python
managed:
  policies:
    - match: {}
      cli:
        # Model access (also enforced server-side at /v1/messages)
        availableModels: [claude-opus-4-8, claude-sonnet-4-6, claude-haiku-4-5]

        # Permission policy
        permissions:
          deny:
            - "WebFetch"
            - "Read(./.env)"
            - "Read(./secrets/**)"
          disableBypassPermissionsMode: disable   # blocks --dangerously-skip-permissions
        allowManagedPermissionRulesOnly: true     # ignore user/project permission rules

        # Environment pushed into the CLI process. DISABLE_UPDATES blocks
        # background and manual updates; DISABLE_AUTOUPDATER stops only
        # background updates.
        env:
          DISABLE_UPDATES: "1"                    # pin versions via your own distribution

        # Org-wide hooks. Hook commands run on developer machines, not the
        # gateway, so the path must exist on every client OS in the policy.
        hooks:
          PostToolUse:
            - matcher: "Edit|Write"
              hooks:
                - { type: command, command: /usr/local/bin/audit-edit.sh }
```

| Key                                        | Enforced by   | Effect                                                                                                                                                                                                                                                                                                                                                           |
|--------------------------------------------|---------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `availableModels`                          | Gateway + CLI | Model allowlist. Also checked at `/v1/messages`, so a patched client can’t bypass it.                                                                                                                                                                                                                                                                            |
| `permissions.allow` / `.deny`              | CLI           | Tool and command rules. See [Permissions](/docs/en/permissions).                                                                                                                                                                                                                                                                                                 |
| `permissions.disableBypassPermissionsMode` | CLI           | Set to `disable` to block [`bypassPermissions`](/docs/en/permission-modes#skip-all-checks-with-bypasspermissions-mode), the mode that skips permission prompts, and the `--dangerously-skip-permissions` flag                                                                                                                                                    |
| `allowManagedPermissionRulesOnly`          | CLI           | When `true`, managed settings become the only settings source of permission rules. The [`allowManagedPermissionRulesOnly`](/docs/en/settings-reference#allowmanagedpermissionrulesonly) entry lists every source Claude Code then ignores.                                                                                                                       |
| `env`                                      | CLI           | Environment variables merged into the CLI process. Use for telemetry, auto-update, and model-name overrides.                                                                                                                                                                                                                                                     |
| `hooks`                                    | CLI           | Org-wide [hooks](/docs/en/hooks)                                                                                                                                                                                                                                                                                                                                 |
| `managedMcpServers`                        | CLI           | Remote MCP servers [provided to every matching developer](/docs/en/managed-mcp#provide-servers-through-managed-settings) alongside the servers they add themselves, `http` and `sse` only. See [MCP servers in a policy](#mcp-servers-in-a-policy). Requires Claude Code v2.1.259 or later on the gateway server and on clients. Earlier clients ignore the key. |

Because these settings arrive over the network, the CLI shows each developer a security approval dialog before applying the settings listed below:

- `hooks`
- `env` variables that require the developer’s approval, such as proxy and base-URL variables
- shell-execution settings such as `apiKeyHelper` and `statusLine`
- the sandbox binary settings `sandbox.bwrapPath`, `sandbox.socatPath`, and `sandbox.ripgrep`
- Sandbox settings that intercept traffic, inject credentials, or weaken isolation, such as `sandbox.network.tlsTerminate` and the proxy port settings. [Security approval dialogs](/docs/en/server-managed-settings#security-approval-dialogs) lists them all.

[Approval memory](/docs/en/server-managed-settings#approval-memory) covers how long an approval lasts and when the dialog appears again. Claude Code applies some delivered `env` variables without showing the developer the approval dialog, such as model selection settings and numeric limits. Other delivered variables can require the developer’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. [Environment variables and the approval dialog](/docs/en/server-managed-settings#environment-variables-and-the-approval-dialog) has the details, including four privacy toggles whose delivered value decides whether they need approval. Before v2.1.218, Claude Code applied fewer variables without asking the developer, so more delivered variables triggered the dialog. The gateway’s [telemetry](#telemetry) configuration pushes `OTEL_EXPORTER_OTLP_ENDPOINT`, so setting `telemetry.forward_to` triggers the dialog on each interactive client. The dialog protects the developer’s machine from a compromised or hostile gateway, not the organization from the developer. A non-interactive run with the `-p` flag can’t show the dialog. It applies the pushed settings for that run only and doesn’t record them as approved, so the developer’s next interactive session still shows the dialog. Before v2.1.207, a non-interactive run saved the settings as approved and no later interactive session showed the dialog for them. If a developer declines, Claude Code exits that session rather than applying the policy. When you push a new hook, or any env var that triggers the dialog, to a broad policy, Claude Code therefore shows the dialog to every matching developer. It shows the dialog in a running session on the next hourly poll, and otherwise at the developer’s next startup. The `cli` key was named `settings` in earlier releases. That spelling is still accepted as an alias, but new deployments should use `cli`.


[​](#mcp-servers-in-a-policy)

MCP servers in a policy

To provide MCP servers to the Claude Code clients a policy matches, set [`managedMcpServers`](/docs/en/managed-mcp#provide-servers-through-managed-settings) in that policy’s `cli` block. You need Claude Code v2.1.259 or later on the gateway server and on clients. The gateway checks each entry at boot with [the same rules Claude Code applies on the client](/docs/en/managed-mcp#what-an-entry-can-contain), and if an entry fails a check, the gateway refuses to start and names the entry. If you write a `${VAR}` reference in `gateway.yaml`, the gateway resolves it from its environment at boot through [secret expansion](#secret-expansion) before it runs the entry checks, so every matching client receives the literal value and can read it. The [header guidance for provided servers](/docs/en/managed-mcp#provide-servers-through-managed-settings) applies to the expanded value. The gateway rejects the `.mcp.json` spelling `mcpServers` in a `cli` block, and its boot error names `managedMcpServers` as the key to use. Before v2.1.259, the gateway rejected any MCP server definition in a `cli` block.


[​](#claude-desktop-overlay)

Claude Desktop overlay

If your organization also deploys [Claude Desktop](/docs/en/desktop), the same gateway serves both clients. Point `bootstrapUrl`, in Claude Desktop’s [managed configuration](https://claude.com/docs/third-party/claude-desktop/configuration), at `<listen.public_url>/user/bootstrap`. Claude Desktop derives the OAuth issuer from that URL, runs the same device-code sign-in against this gateway, and fetches its configuration from the response.

Requires Claude Code v2.1.203 or later on the gateway server, and an explicit opt-in: `/user/bootstrap` returns 404 unless the policy matching the user carries a `desktop` key. An empty `desktop: {}` opts a policy in, and a `desktop` key on the `match: {}` base layer opts in every policy that inherits it. The audit log records each request as `desktop_bootstrap.serve` or `desktop_bootstrap.denied`.

The gateway derives much of the response from the matched policy’s `cli` block and from top-level gateway config:

- The model list, from `availableModels`
- Disabled tools, from bare tool-name `permissions.deny` entries. If you set `disabledBuiltinTools` in the policy’s `desktop` block, the gateway serves the union of your value and the derived list, so you can disable more tools this way but can’t re-enable one you disabled through `permissions.deny`
- The egress allowlist, from `sandbox.network.allowedDomains`. If you set `coworkEgressAllowedHosts` in the policy’s `desktop` block, the gateway uses that value instead of the derived list
- An OTLP endpoint that points at the gateway itself, and the signed-in user’s identity attributes. The gateway relays the exports it receives at that endpoint to your `forward_to` destinations. It includes the endpoint and the attributes when you set both [`telemetry.forward_to`](#telemetry) and `listen.public_url`. Claude Desktop exports every signal with one encoding: `http/protobuf`, or `http/json` when you set `OTEL_EXPORTER_OTLP_PROTOCOL` or one of its per-signal variants to `http/json` in the policy’s `env`. Before Claude Code v2.1.261 on the gateway server, the response set `http/json` regardless, so a collector that accepts only protobuf rejected Claude Desktop’s exports

To set `disabledBuiltinTools`, `coworkEgressAllowedHosts`, or Claude Desktop’s own `managedMcpServers` setting in a policy’s `desktop` block, you need Claude Code v2.1.232 or later on the gateway server. Claude Desktop’s `managedMcpServers` takes an array value rather than an object. The gateway omits keys with no Claude Desktop equivalent, such as `hooks` and scoped permission rules like `Bash(npm *)`, from the bootstrap response. Add the optional `desktop` block alongside `cli` to set Claude Desktop settings directly. Write settings from Claude Desktop’s [managed configuration reference](https://claude.com/docs/third-party/claude-desktop/configuration) as flat key names. Leave out keys Claude Desktop reads only from MDM or local files, such as `bootstrapUrl`; the gateway rejects them at boot. Before v2.1.232, the gateway accepted a fixed list of 11 feature-gate keys, such as `chatTabEnabled` and `disableAutoUpdates`, and rejected every other key at boot. Before v2.1.227, the gateway also rejected `chatTabEnabled` and `chatAdvancedFileAnalysisEnabled` at boot.

```python
managed:
  policies:
    - match: { groups: [eng-contractors] }
      cli:
        availableModels: [claude-sonnet-4-6]
      desktop:
        isLocalDevMcpEnabled: false
        disableAutoUpdates: true
        banner: { text: "Contractor build: internal use only" }
```

Every key is optional; Claude Desktop applies its own default for any key you omit. The gateway validates each `desktop` block at boot against the configuration schema Claude Desktop itself uses, so a mistake surfaces at gateway start as an error naming the key rather than reaching every connected desktop. The gateway fails at boot when a block contains:

- An unknown key
- A recognized key whose value Claude Desktop would reject or silently drop, such as an empty value or a misspelled sub-key inside a nested entry. Before v2.1.260, the gateway silently dropped a misspelled field inside a nested object of a `managedMcpServers` or `orgPluginSettings` entry instead of failing at boot.
- A key the gateway computes itself: the inference connection, the model list, and the OTLP relay. Configure those through [`upstreams`](#upstreams), [`models`](#models), and the [`telemetry`](#telemetry) section’s `forward_to`.
- A legacy alias of a current key. In the boot error, the gateway names the canonical key to write.

If you use a deprecated value or entry shape, such as a `managedMcpServers` entry without `transport`, the gateway starts and logs a warning that names the replacement. The gateway validates a `desktop` block against the schema bundled with its installed version, as it does the `cli` block. To deliver a setting introduced by a newer Claude Desktop release, upgrade the gateway first. For example, `userPluginMarketplacesEnabled` and `userPluginUploadsEnabled` need Claude Code v2.1.260 or later on the gateway server and Claude Desktop 1.37937.0 or later on members’ machines. If you set `orgPluginSettings` in a policy’s `desktop` block, the gateway serves it in the array form that Claude Desktop 1.15200.0 and later reads. Older desktops ignore the array and enforce no plugin tool policy, so update members to 1.15200.0 or later before you rely on it. The gateway fills in keys a policy’s `desktop` block doesn’t set from the `match: {}` catch-all’s `desktop` block, the same way it fills in a policy’s `cli` block from the base. If you set `disabledBuiltinTools` or `builtinToolPolicy` in both the base and a role policy, the gateway keeps the base’s restriction:

- `disabledBuiltinTools`: the gateway uses the union of the base’s list and the policy’s list
- `builtinToolPolicy`: if you set a tool to a value other than `allow` in the base, the gateway keeps that value even if you set `allow` for the same tool in a role policy

For every other key, if you set it in the role policy, the gateway uses the role policy’s value. The gateway replaces an array or a nested object such as `banner` whole, so if you set `banner.text` in a role policy, the gateway drops the base’s `banner.backgroundColor`. If you don’t deploy Claude Desktop, leave `desktop` out of your policies entirely; the gateway then returns 404 from `/user/bootstrap` for every user.


[​](#precedence-with-other-managed-sources)

Precedence with other managed sources

If a device also has an MDM-delivered policy or a local `managed-settings.json`, gateway-delivered settings rank first. [Precedence within the managed tier](/docs/en/managed-settings#precedence-within-the-managed-tier) on the managed settings page says when the local sources apply, and has the [keys Claude Code reads from every admin source](/docs/en/managed-settings#keys-read-from-every-admin-source) regardless of which source it selected, such as the sandbox lock keys, `forceRemoteSettingsRefresh`, and the per-variable `env` merge. A [`policyHelper`](/docs/en/settings-reference#policyhelper) configured in an MDM profile or the managed settings file runs only when the gateway delivers no settings; the entry says what its output replaces. Embedding hosts such as [Claude Desktop](/docs/en/desktop) can supply policy through the SDK `managedSettings` option. [Parent settings from embedding hosts](/docs/en/managed-settings#parent-settings-from-embedding-hosts) says when Claude Code applies it, and [Restrict parent settings](/docs/en/claude-apps-gateway#restrict-parent-settings) lists which allow-direction settings still apply without the `allowManaged*Only` locks. Gateway policies apply to every Claude Code invocation on the machine, including non-interactive `claude -p` runs and sessions spawned by the Agent SDK. If the gateway is unreachable at startup, signed-in sessions exit with an error rather than running without their policy.


[​](#telemetry)

`telemetry`

The CLI sends metrics, logs, and, when enabled, traces to the gateway, which relays them verbatim to each configured destination. The exports use OpenTelemetry Protocol (OTLP) over HTTP. To skip the relay and have sessions export straight to your collector, [name the collector in a policy](#export-directly-to-your-collector). See [Monitoring usage](/docs/en/monitoring-usage) for the metrics and events the CLI emits. The CLI stamps each export with the authenticated user’s identity, read from the gateway-issued JWT: the `user.id`, `user.email`, and `user.groups` attributes. Per-developer cost and usage attribution therefore works with no developer-side configuration. [Claude Desktop](#claude-desktop-overlay) and Cowork sessions signed in through the gateway stamp their telemetry with `user.email` and `user.groups` alongside `enduser.id`, so you can cover terminal, Desktop, and Cowork usage with one query on `user.email` or `user.groups`. `user.groups` is the comma-separated IdP group list. Desktop and Cowork telemetry also carries `enduser.sub`, the `sub` claim your identity provider issues for the user, which stays the same when a user’s email changes. Terminal sessions stamp the same value under `user.id`, so a query that matches `enduser.sub` against terminal `user.id` covers one user’s terminal, Desktop, and Cowork usage together. On Desktop and Cowork exports, `user.id` is an anonymous identifier, not the subject. Like all OpenTelemetry data from Claude Code, these attributes go only to destinations your organization configures, never to Anthropic. If a user’s group list is longer than 255 characters once percent-encoded, or a group name contains a comma or equals sign, the gateway leaves `user.groups` off that user’s Desktop and Cowork telemetry rather than truncating it. That user’s terminal sessions still carry the full list. The gateway leaves `enduser.sub` off when the subject is longer than 255 characters once percent-encoded, or contains a space, a character outside printable ASCII, or one of `,` `;` `=` `\` `"` `%`. That user’s Desktop and Cowork telemetry keeps its other attributes. You need Claude Code v2.1.265 or later on the gateway server for `user.email` and `user.groups` on Desktop and Cowork telemetry, and Claude Desktop 1.24012 or later on each developer’s machine for `user.groups`. You need Claude Code v2.1.274 or later on the gateway server for `enduser.sub`.

```python
telemetry:
  forward_to:
    - url: https://otel-collector.internal.example.com
      headers:
        Authorization: ${OTLP_TOKEN}
      # Per-signal opt-in. Default: metrics only.
      metrics: true
      logs: false
      traces: false
    - url: https://api.datadoghq.com/api/v2/otlp
      headers:
        DD-API-KEY: ${DD_API_KEY}
```

Each destination opts into `metrics`, `logs`, and `traces` independently, and the default is metrics only. The signals differ in sensitivity:

- **Metrics**: aggregate counters such as token counts, request counts, and latency
- **Logs and traces**: can carry full Bash commands, tool inputs, and file paths, covering anything Claude Code does on a developer’s machine

Enable logs and traces only on destinations with the access controls and retention policy that data warrants.

Each `forward_to` URL must use `https://`, with one exception for a collector on the gateway’s own loopback interface:

- `http://localhost:<port>` passes config validation, but the [SSRF guard](/docs/en/claude-apps-gateway-deploy#threat-model-summary) blocks every export with `ECONNREFUSED_SSRF` unless you set `CLAUDE_GATEWAY_ALLOW_LOOPBACK=1` in the gateway’s environment
- `http://127.0.0.1:<port>` or `http://[::1]:<port>` fails boot unless that variable is set

For an in-cluster collector, expose it over HTTPS at its own internal address, or run it as a sidecar with the variable set. When `HTTPS_PROXY` is set, the gateway sends exports through that proxy. To reach an internal collector directly, add it to `NO_PROXY` by hostname or by a domain with a leading dot such as `.internal.example.com`, which requires Claude Code v2.1.277 or later on the gateway server. Make sure the gateway can reach the collector without the proxy. An entry without a leading dot matches only that exact name, not names under it. CIDR ranges don’t match. With [proxy-only egress](#proxy-only-egress) turned on, allow the collector in the proxy instead, since any `NO_PROXY` entry keeps proxy-only egress off. Telemetry is off in the CLI by default. When you set both `telemetry.forward_to` and `listen.public_url`, the gateway turns it on for connected clients by pushing six environment variables through `/managed/settings`:

- `CLAUDE_CODE_ENABLE_TELEMETRY=1`
- `OTEL_METRICS_EXPORTER`, `OTEL_LOGS_EXPORTER`, and `OTEL_TRACES_EXPORTER`, each set to `otlp` if at least one `forward_to` destination enables that signal and to `none` otherwise
- `OTEL_EXPORTER_OTLP_ENDPOINT=<public_url>`
- `OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf`

When you [add your own labels](#add-your-own-labels), the gateway also pushes `OTEL_RESOURCE_ATTRIBUTES`. Before Claude Code v2.1.265 on the gateway server, the gateway pushed all three exporter selectors as `otlp`, including for signals no destination opted into. The pushed endpoint is built from the public URL, so metrics and logs need no OTEL configuration from developers or policies. Developers signed in through `/login` can’t redirect exports with their own OTEL configuration:

- **Locally set variables**: Claude Code applies the pushed variables at the managed tier, so each one overrides the value a developer sets for it locally.
- **Locally configured endpoints**: with OTLP/HTTP export enabled, the CLI ignores any locally configured endpoint, whether or not the gateway pushed the telemetry variables. Its exports go to the gateway unless a policy [names your collector as the endpoint](#export-directly-to-your-collector).

Without a `forward_to` destination for a signal, the gateway accepts and discards it. If developers already export Claude Code telemetry to one of your collectors, add it as a `forward_to` destination, with logs or traces enabled if they export those, so it keeps receiving their data after they sign in. To skip the relay instead, [name the collector in a policy](#export-directly-to-your-collector). [Traces](/docs/en/monitoring-usage#traces-beta) also require `CLAUDE_CODE_ENHANCED_TELEMETRY_BETA=1` on each client. Set it in a managed policy’s `env` block, since the gateway doesn’t push it. Developers approve it in the same [security approval dialog](#managed) that the pushed endpoint already triggers. Set it to `1` only in the policies whose groups you want traced. A policy that doesn’t set it inherits the value from your `match: {}` catch-all policy if that policy sets one, per the [merge rules](#managed). To keep a group’s clients from sending traces even when a developer sets the variable locally, set it to `0` in that group’s policy. Both protobuf and JSON OTLP encodings are relayed, and any OpenTelemetry-compatible backend works as a destination.


[​](#add-your-own-labels)

Add your own labels

To put fixed labels such as `service.namespace` or `deployment.environment.name` on the telemetry of sessions signed in through the gateway, set `telemetry.resource_attributes`. Each label is an OpenTelemetry resource attribute, and every destination receives the same labels. Sessions get the labels only when you also set `telemetry.forward_to` and `listen.public_url`. This example adds two labels:

```python
telemetry:
  forward_to:
    - url: https://otel-collector.internal.example.com
  resource_attributes:
    service.namespace: claude
    deployment.environment.name: prod
```

The gateway refuses to start when a label breaks one of these rules, and the startup error names the label:

- Names use only letters, digits, `.`, `_`, and `-`
- Names aren’t reserved. Compared in any letter case, the reserved names are everything that starts with `user.`, `enduser.`, or `identity.`, plus `service.name`, `service.version`, `claude.deployment_mode`, `host.arch`, `os.type`, `os.version`, and `wsl.version`
- Values are non-empty printable ASCII with no space and none of `, ; = \ " %`
- Values are at most 255 characters as the gateway counts them after percent-encoding, so `/`, `:`, and `@` each count as three
- Values are text, so quote a number, `true`, or `false`

You need Claude Code v2.1.281 or later on the gateway server to set `telemetry.resource_attributes`. An earlier gateway refuses to start when it finds the key. Upgrade every replica before you add the key, and remove the key before you roll back to an earlier version. Terminal sessions signed in through `/login` receive the labels as `OTEL_RESOURCE_ATTRIBUTES`, pushed with the other [telemetry variables](#telemetry). If you set `OTEL_RESOURCE_ATTRIBUTES` in a policy’s `env` block, terminal sessions that policy matches get that value instead of the labels. Claude Desktop receives the labels from the gateway alongside `user.email` and the other identity attributes. Claude Code also copies each label onto every metric data point, so you can filter metrics by it in a backend that doesn’t index resource attributes. To turn that copy off, see [Metrics cardinality control](/docs/en/monitoring-usage#metrics-cardinality-control).


[​](#export-directly-to-your-collector)

Export directly to your collector

To have sessions signed in through `/login` send telemetry straight to your collector instead of through the relay, set `OTEL_EXPORTER_OTLP_ENDPOINT` to the collector’s `https://` base URL in the `env` block of a [managed policy](#managed). Claude Code appends `/v1/metrics`, `/v1/logs`, or `/v1/traces` to the URL you set, such as `https://otel-collector.example.com:4318`, and exports each signal there over OTLP/HTTP. Requires Claude Code v2.1.265 or later on each developer’s machine. Earlier clients export through the relay. To authenticate to the collector, set `OTEL_EXPORTER_OTLP_HEADERS` in the same `env` block. Sessions never send the developer’s gateway session token to a collector named this way. When you add or change this endpoint in a policy, Claude Code asks each developer to approve it in the [security approval dialog](#managed) before applying it in an interactive session. Claude Code checks the endpoint before it exports a signal directly, and keeps that signal on the relay when a check fails. The checks include:

- The endpoint comes from the gateway itself. If you set the same variable in an MDM profile or a local `managed-settings.json`, exports stay on the relay.
- The URL uses `https://`, or `http://` to a loopback address
- The URL resolves to a path ending in `/v1/<signal>`, with no query or fragment. Claude Code builds that path itself from the generic variable. It uses a per-signal variable such as `OTEL_EXPORTER_OTLP_METRICS_ENDPOINT` as written, so include the full path there.
- The URL isn’t the gateway’s own host. An endpoint addressed to the gateway keeps the relay path and its session token.
- Neither you nor the developer has configured [`otelHeadersHelper`](/docs/en/settings-reference#otelheadershelper) in any settings source. With a helper configured, every signal stays on the relay.

The endpoint you name changes only where exports go. You still choose which signals export at all with the `OTEL_*_EXPORTER` selectors. The endpoint alone doesn’t turn export on, so also set the variables that do, unless the gateway already pushes them:

- If the gateway already [pushes the telemetry variables](#telemetry), they cover enablement, selectors, and protocol, and your explicit endpoint overrides the pushed `<public_url>` value. Set an `OTEL_*_EXPORTER` selector to `otlp` yourself only for a signal that no `forward_to` destination enables.
- If it doesn’t, also set `CLAUDE_CODE_ENABLE_TELEMETRY=1`, the `OTEL_*_EXPORTER` selectors, and `OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf`.

When the developer signs out, or signs in to a different gateway, exports to the collector stop and Claude Code drops each remaining batch rather than sending it.


[​](#when-a-destination-fails)

When a destination fails

The gateway doesn’t buffer, retry, or store telemetry, so it drops an export that doesn’t reach a destination rather than delivering it late. Each destination succeeds or fails on its own, and the exporting client receives a success response either way, so a failed delivery appears only in the gateway’s log. After five consecutive failed deliveries to a destination, the gateway pauses forwarding to it in 30-second stretches, logging each pause, until a delivery succeeds. Any error response, timeout, or connection error counts as a failed delivery, except `400`, `413`, `415`, `422`, and `431`, which mean the collector refused that export’s payload as malformed or too large. A refused payload neither advances nor resets the failure count: the gateway keeps forwarding to the destination and logs a warning naming it and the status, on the destination’s first refusal and every hundredth after.


[​](#http-tuning)

HTTP tuning

Four optional top-level blocks, `access_control`, `limits`, `timeouts`, and `rate_limits`, tune the HTTP surface. The defaults suit most deployments.

| Block            | Key                                            | Default  | Description                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  |
|------------------|------------------------------------------------|----------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `access_control` | `allow_cidrs` / `deny_cidrs`                   | empty    | Inbound IP allow/deny by client address, after `trusted_proxies` resolution. `deny_cidrs` is checked first; a client it matches is rejected even if `allow_cidrs` also matches. If `allow_cidrs` is non-empty the gateway is default-deny. `/healthz` and `/readyz` are exempt from `allow_cidrs`. When a trusted proxy sends an `X-Forwarded-For` entry that isn’t an IP address, the real client is unknown and the gateway logs a warning once naming what to check. Where either list applies to the request, it refuses it with `403` and audit reason `xff_unparseable`. Where neither does, it serves the request and uses the proxy’s own address as the client IP for per-IP rate limits and audit. |
| `limits`         | `max_request_bytes`                            | 32 MiB   | Max inbound request body; oversize requests get `413` before the body is buffered. Raise for large file or image requests.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   |
| `limits`         | `max_request_header_bytes`                     | unset    | When set, oversize headers return `431`                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      |
| `limits`         | `max_url_length`                               | unset    | When set, an over-long URL returns `414`                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     |
| `timeouts`       | `upstream_ttfb_ms`                             | 120000   | Max wait for the upstream’s response headers (time to first byte). The response body then streams with no wall-clock cap. Applies to the direct Anthropic upstream path; on every other provider the gateway waits up to one hour for the response to start.                                                                                                                                                                                                                                                                                                                                                                                                                                                 |
| `rate_limits`    | `device_authorization.max` / `.window_seconds` | 30 / 600 | Per-IP rate limit on the unauthenticated device-authorization endpoint. Raise for a large org behind a shared egress IP or NAT. [Large rollouts](/docs/en/claude-apps-gateway-deploy#large-rollouts) shows how to size it. These limits apply only to the device-grant sign-in flow, not to `/v1/messages` inference. See [User-code brute-force resistance](/docs/en/claude-apps-gateway-deploy#user-code-brute-force-resistance).                                                                                                                                                                                                                                                                          |
| `rate_limits`    | `device_verify.max` / `.window_seconds`        | 10 / 600 | Per-IP rate limit on `user_code` submissions at `/device`. It is what stops someone from guessing another developer’s code. [Large rollouts](/docs/en/claude-apps-gateway-deploy#large-rollouts) shows how far to raise it.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  |

If you leave both `access_control` lists empty, which is the default, the gateway serves any client address, so only your network restricts who can reach it. That matters because a gateway can push [managed settings](#managed) that run commands on developer machines. While `allow_cidrs` is empty, the gateway warns in two places, without changing how it answers any request:

- **At boot**: a warning in the operational log recommends allowing only the private ranges `10.0.0.0/8`, `172.16.0.0/12`, `192.168.0.0/16`, `100.64.0.0/10`, `127.0.0.0/8`, `::1/128`, and `fc00::/7`, plus any other internal ranges your developers connect from. If you bind the gateway to a loopback address and set neither `trusted_proxies` nor `public_url`, as in local development, the warning doesn’t appear.
- **At runtime**: the first time a request arrives from an address outside those private ranges, the gateway logs a warning and emits an [`access.public_client` audit event](/docs/en/claude-apps-gateway-deploy#logs) carrying the client IP. Both fire once per process. Link-local addresses, `169.254.0.0/16` and `fe80::/10`, don’t count as public. The gateway answers `/healthz` and `/readyz` before this check runs, so health probes from public ranges don’t trigger it.

Both signals use the client address as the gateway resolves it. If a load balancer, port-forward, or tunnel relays traffic and isn’t listed in `listen.trusted_proxies`, the gateway sees the relay’s address, which is usually private, so neither the runtime warning nor a private allow list catches traffic relayed through it. Behind such a front end, set [`listen.trusted_proxies`](#listen) first so the gateway sees real client addresses, and keep the gateway and everything in front of it unreachable from the public internet regardless.


[​](#load_test_mode)

`load_test_mode`

The `load_test_mode` block lets you load test a gateway without calling a model provider. While it’s on, the gateway builds and signs each provider request as usual, discards it instead of sending it, and streams a canned reply back through its normal response path. The reply is filler text that begins with a sentence saying it is canned. Requires Claude Code v2.1.282 or later on the gateway server. An earlier gateway refuses to start when it finds the key. Upgrade every replica before you add the block, and remove the block before you roll back. The example below turns the mode on with the defaults, a reply of roughly 750 tokens of text streamed over about 10 seconds:

```python
load_test_mode:
  enabled: true
  reply_tokens: 750     # roughly how many tokens of text each canned reply carries
  reply_seconds: 9.5    # how long a streamed reply takes
```

| Field           | Required | Description                                                                                                                                                     |
|-----------------|----------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `enabled`       | Yes      | `true` turns the mode on. `false` keeps your numbers in the file with the mode off. The gateway refuses to start if the block is present without it.            |
| `reply_tokens`  | No       | Default `750`. Roughly how many tokens of text each canned reply carries, a whole number from 1 to 100000.                                                      |
| `reply_seconds` | No       | Default `9.5`. How long a streamed reply takes, from 0 to 600. `0` sends the whole reply at once. A reply to a non-streaming request always comes back at once. |

A load test in this mode covers the gateway, your Postgres, and everything in front of the gateway. It doesn’t cover the provider’s limits, speed, or network path. No model request is sent to the provider, so a replica’s CPU per request is an estimate and reads lower than production, which also encrypts its traffic to the provider. Confirm a replica count with a small pilot against the real provider. Before v2.1.283, the estimate reads much lower. While the mode is on, a request can carry an `x-load-test-user` header holding a whole number of up to seven digits. The gateway counts each number as a separate developer, with the email and groups of the developer whose token came with the request. Give the load-test deployment its own empty database, because the gateway refuses to start with the mode on against a database in which any developer has already spent anything.

Never turn this on for a gateway that developers use. Every request gets the canned reply and no model is called. The gateway logs a `load_test_mode is on` warning at boot and marks each `inference` [audit event](/docs/en/claude-apps-gateway-deploy#logs) with `load_test: true` while the mode is on.


[​](#complete-example)

Complete example

This full reference config exercises every core section; the [HTTP tuning blocks](#http-tuning) keep their defaults. Copy it, delete what you don’t need, and fill in your values. The config in the [Quickstart](/docs/en/claude-apps-gateway#quickstart) is a minimal version of this.

gateway.yaml

```python
# Run with:
#   claude gateway --config gateway.yaml
# Operational log verbosity is controlled by the CLAUDE_GATEWAY_LOG_LEVEL
# environment variable (debug | info | warn | error; default info). debug
# also logs the claim names in each id_token, for groups_claim diagnosis.
# It does not affect audit events, which are always emitted.

listen:
  host: 0.0.0.0
  port: 8080
  public_url: https://claude-gateway.internal.example.com
  # Omit the tls block when running behind a TLS-terminating ingress.
  # tls:
  #   cert: /certs/gateway.crt
  #   key: /certs/gateway.key
  # trusted_proxies:
  #   - 10.0.0.0/8

oidc:
  issuer: https://example.okta.com
  client_id: 0oa1example2
  client_secret: ${OIDC_CLIENT_SECRET}
  allowed_email_domains:
    - example.com
  # Required when the issuer is the Okta org server, whose id_tokens
  # can omit email and groups; the gateway fills them from /userinfo.
  userinfo_fallback: true
  # allowed_groups: [claude-code-users]
  # Okta emits groups only when the `groups` scope is requested and the
  # app's groups claim filter allows them. The contractors policy below
  # matches on groups, so the scope is requested here.
  scopes: [openid, profile, email, offline_access, groups]
  # extra_auth_params: { access_type: offline, prompt: consent }  # Google
  # groups_claim: groups          # Entra app roles: use `roles`
  # email_claim: email

session:
  jwt_secret: ${GATEWAY_JWT_SECRET}   # openssl rand -base64 32
  # ttl_hours: 1

store:
  postgres_url: ${GATEWAY_POSTGRES_URL}
  # max_connections: 5
  # connect_timeout_seconds: 5
  # readiness_grace_seconds: 300   # keep passing the readiness check through a database failover

# Enables /v1/organizations/spend_limits (mirrors the Anthropic Admin API)
# and per-developer spend enforcement on /v1/messages. Omit to disable.
# Caps themselves are set via the admin API, not here.
# admin:
#   write_keys:
#     - { id: terraform, key: "${GATEWAY_ADMIN_WRITE_KEY_TF}" }
#   read_keys:
#     - { id: reporting, key: "${GATEWAY_ADMIN_READ_KEY}" }
#   admin_groups: [platform-finops]
#   blocked_message: request an increase at https://go.example.com/claude-limits
#   # audit_retention_days: 365
#   # spend_retention_months: 13
#   # identity_retention_days: 90
#   # group_limit_mode: min

# enforcement:
#   fail_closed_on_error: false

# Load test this deployment without calling a model provider. Never on a
# gateway that developers use: every request gets a canned reply.
# load_test_mode:
#   enabled: true
#   # reply_tokens: 750
#   # reply_seconds: 9.5

# Meter at contracted rates instead of USD list price. Requires admin: or a
# managed: policy. With managed:, the same rates also go to signed-in clients.
# Rates below are placeholders, not real contract prices.
# pricing:
#   multiplier: 0.85
#   overrides:
#     - { upstream: anthropic, model: claude-sonnet-4-6, input: 3.30, output: 16.50, cache_read: 0.33, cache_write: 4.125 }

upstreams:
  - provider: anthropic
    auth:
      api_key: ${ANTHROPIC_API_KEY}

  # - provider: bedrock
  #   region: us-east-1
  #   auth: {}

  # - provider: anthropicAws
  #   region: us-east-1
  #   workspace_id: wrkspc_...
  #   auth:
  #     api_key: ${ANTHROPIC_AWS_API_KEY}

  # - provider: vertex
  #   region: us-east5
  #   project_id: example-prod
  #   auth: {}

  # - provider: foundry
  #   resource: example-foundry
  #   auth: { use_azure_ad: true }

auto_include_builtin_models: true
models:
  - id: claude-opus-4-8
    label: Claude Opus 4.8
    upstream_model:
      anthropic: claude-opus-4-8
      # bedrock: us.anthropic.claude-opus-4-8
      # anthropicAws: claude-opus-4-8
      # vertex: claude-opus-4-8
      # foundry: <your-opus-deployment-name>
  - id: claude-sonnet-4-6
    label: Claude Sonnet 4.6
    upstream_model:
      anthropic: claude-sonnet-4-6
  - id: claude-haiku-4-5
    label: Claude Haiku 4.5
    upstream_model:
      anthropic: claude-haiku-4-5

managed:
  policies:
    - match: { groups: [contractors] }
      cli:
        availableModels: [claude-haiku-4-5]
        # Constrain the Default picker option to availableModels instead of
        # the tier default, so contractors don't get a 400 on the default.
        enforceAvailableModels: true
        # allow auto-approves these tools; it does not block the rest.
        # Add deny rules to restrict tools.
        permissions: { allow: [Read, Grep] }
    - match: {}
      cli:
        availableModels: [claude-opus-4-8, claude-sonnet-4-6, claude-haiku-4-5]
        permissions:
          allow: [Read, Grep, Bash, Edit]
          deny: ["WebFetch"]
        env: { HTTP_PROXY: http://proxy.example.com:8080 }

telemetry:
  forward_to:
    - url: https://otel.internal.example.com:4318
      headers:
        Authorization: Bearer ${OTEL_TOKEN}
```


[​](#client-side-managed-settings)

Client-side managed settings

Everything above configures the gateway server. You point developer machines at the gateway separately, on each device, through Claude Code’s [managed settings](/docs/en/managed-settings). The gateway can’t push the login keys itself, because they’re what tell the client where the gateway is. For the CLI, set these keys in the per-OS `managed-settings.json`. The two login keys route each developer’s `/login` to your gateway:

```python
{
  "forceLoginMethod": "gateway",
  "forceLoginGatewayUrl": "https://claude-gateway.internal.example.com",
  "parentSettingsBehavior": "merge"
}
```

`parentSettingsBehavior: "merge"` keeps Claude Desktop’s delivery of the egress allowlist to its embedded Claude Code sessions working; [Deliver policy to Claude Desktop sessions](/docs/en/claude-apps-gateway#deliver-policy-to-claude-desktop-sessions) explains the mechanism and where the opt-in must sit. Deploy the `managed-settings.json` file to each device, typically via your MDM platform. The file path differs by platform. See [where each mechanism stores the policy](/docs/en/managed-settings#where-each-mechanism-stores-the-policy). By default, a registry policy on Windows or a managed-preferences plist on macOS replaces the `managed-settings.json` file rather than merging with it, apart from the [exception keys and cross-source checks above](#precedence-with-other-managed-sources). All three keys in this snippet follow the highest-priority-source rule, so fleets that deliver policy through Group Policy or configuration profiles must put all three in that mechanism instead. For Claude Desktop, set the `bootstrapUrl` key in Claude Desktop’s own [managed configuration](https://claude.com/docs/third-party/claude-desktop/configuration) to `<listen.public_url>/user/bootstrap`. The sign-in flow and per-group policy then match the CLI’s once a policy opts in server-side with a `desktop` key; without the opt-in, `/user/bootstrap` returns 404. See [Claude Desktop overlay](#claude-desktop-overlay) for the server-side half. Claude Code honors [`forceLoginGatewayUrl`](/docs/en/settings-reference#forcelogingatewayurl), [`gatewayInternalNetworks`](/docs/en/settings-reference#gatewayinternalnetworks), and the `"gateway"` value of [`forceLoginMethod`](/docs/en/settings-reference#forceloginmethod) only from a managed source on the machine: `managed-settings.json`, the macOS plist or Windows HKLM registry, or a policy helper. Setting them in a developer’s own `~/.claude/settings.json` or in the gateway payload doesn’t configure the gateway sign-in. Leave `forceLoginMethod` and `forceLoginOrgUUID` out of the payload. Claude Code still reads both keys from the payload for its startup credential check, so a developer who keeps an Anthropic-issued credential on the machine gets the startup exit described under [Administrator policy requires a Cloud gateway sign-in](/docs/en/errors#administrator-policy-requires-a-cloud-gateway-sign-in) even after they sign in.


[​](#related)

Related

- [Claude apps gateway overview](/docs/en/claude-apps-gateway): quickstart and developer connection
- [Deployment guide](/docs/en/claude-apps-gateway-deploy): IdP setup, container image, Kubernetes and Cloud Run, and operations
- [Spend limits](/docs/en/claude-apps-gateway-spend-limits): per-developer caps and the Admin API


---
## claude-apps-gateway-deployment-and-operations-claude-code-docs-8daea5e8ae.md

## On this page

- [Identity provider setup](#identity-provider-setup)
- [Deployment](#deployment)
  - [Container image](#container-image)
  - [Kubernetes](#kubernetes)
  - [Cloud Run](#cloud-run)
  - [Push the gateway URL to developer machines](#push-the-gateway-url-to-developer-machines)
  - [Large rollouts](#large-rollouts)
- [Operations](#operations)
  - [Logs](#logs)
  - [Health](#health)
  - [Concurrent upstream requests](#concurrent-upstream-requests)
  - [Outage behavior](#outage-behavior)
  - [Readiness grace period](#readiness-grace-period)
  - [JWT secret rotation](#jwt-secret-rotation)
  - [Postgres](#postgres)
  - [Upgrades](#upgrades)
- [Security](#security)
  - [Data flow](#data-flow)
  - [Threat model summary](#threat-model-summary)
  - [User-code brute-force resistance](#user-code-brute-force-resistance)
  - [Compliance posture](#compliance-posture)
- [Troubleshooting](#troubleshooting)
- [Related](#related)

Claude apps gateway

# Claude apps gateway deployment and operations

Copy pageCopy page

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.

Copy pageCopy page

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.

**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. If your internal network is numbered from public IPv4 space your organization owns, see [Allow a gateway on public address space you own](/docs/en/claude-apps-gateway#allow-a-gateway-on-public-address-space-you-own).


[​](#identity-provider-setup)

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 }`.

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.


[​](#deployment)

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. Don’t redirect requests to the gateway’s device-authorization and token endpoints, for example with an HTTP-to-HTTPS or host-canonicalization rewrite at the ingress. Claude Code doesn’t follow redirects on those requests, so an ingress rule that redirects them breaks sign-in and token refresh. 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)

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)

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). Prefer the platform’s workload identity over static keys; the [`upstreams` reference](/docs/en/claude-apps-gateway-config#upstreams) has per-platform setup details. For a cross-cloud pairing, such as an Amazon Bedrock upstream on GKE, set explicit credentials in the upstream’s `auth` block instead.


[​](#cloud-run)

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

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).


[​](#push-the-gateway-url-to-developer-machines)

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. Once you deploy the keys, Claude Code stops using a leftover API key or claude.ai login on the machine, so plan the push together with your sign-in instructions. [Administrator policy requires a Cloud gateway sign-in](/docs/en/errors#administrator-policy-requires-a-cloud-gateway-sign-in) describes the messages developers see. See [where each mechanism stores the policy](/docs/en/managed-settings#where-each-mechanism-stores-the-policy) for the file paths, and [Client-side managed settings](/docs/en/claude-apps-gateway-config#client-side-managed-settings) for the Claude Desktop `bootstrapUrl` equivalent.


[​](#large-rollouts)

Large rollouts

Sign-in is rate limited per client IP address, and the defaults suit a small team. Each address gets 30 sign-in starts and 10 code submissions every 10 minutes. A rollout to thousands of developers can reach those limits on the first morning, for one of two reasons:

- **The gateway can’t see past your load balancer.** Without [`listen.trusted_proxies`](/docs/en/claude-apps-gateway-config#listen), every developer appears to come from the load balancer’s address and shares one limit. Set it before anything else. The gateway logs a warning the first time it ignores an `X-Forwarded-For` header.
- **Many developers share a few NAT or VPN egress addresses.** They share those addresses’ limits even when `trusted_proxies` is right. Raise [`rate_limits`](/docs/en/claude-apps-gateway-config#http-tuning) to fit.

To size `max`, divide the developers by the egress addresses they share. Estimate how many of those sign in within one `window_seconds` period, which is 10 minutes by default. Then double it to cover retries and developers who sign in to both Claude Code and Claude Desktop. For example, 10,000 developers behind 4 egress addresses sign in evenly over an hour. That is 2,500 developers per address and about 420 of them in each 10 minutes, which you double and round up to 1,000. The example below sets both limits to 1,000:

```python
rate_limits:
  device_authorization: { max: 1000, window_seconds: 600 }
  device_verify: { max: 1000, window_seconds: 600 }
```

`device_verify` is what stops someone from guessing another developer’s sign-in code, so raise it only as far as your estimate needs. Even at these limits, a code is 8 characters from a 20-character alphabet and expires after 10 minutes, so guessing stays impractical; see [User-code brute-force resistance](#user-code-brute-force-resistance). When your IdP issues refresh tokens, Claude Code renews sessions silently, so you can put the limit back after the rollout. Without refresh tokens, developers sign in again every [`session.ttl_hours`](/docs/en/claude-apps-gateway-config#session). Size both limits for that steady rate too and leave them raised. When a limit is reached, Claude Code v2.1.274 or later shows `The gateway is limiting sign-in attempts right now`. A gateway on v2.1.274 or later shows `Too many attempts came from your network address` on the verification page, with the settings to check. It also writes a `sign-in refused` log line that names the setting to change.


[​](#operations)

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)

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`, `access.public_client`, `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. Two `access.denied` reasons change what the event carries:
    - `xff_unparseable`: the event also carries the `X-Forwarded-For` entry that couldn’t be read
    - `client_ip_unknown`: the event carries no client IP, because the connection had no peer address while an `access_control` list was set
  - `access.public_client` carries the client IP of the first request per process to arrive from a public address while `access_control.allow_cidrs` is empty. The gateway serves the request as usual; the event signals that the gateway may be reachable from the public internet. See the [`access_control` reference](/docs/en/claude-apps-gateway-config#http-tuning) for what counts as public and for the recommended allow list.
  - `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)

Health

The gateway serves `GET /healthz` as a liveness probe and `GET /readyz` as a readiness probe. `/readyz` verifies the store is reachable. If you set [`store.readiness_grace_seconds`](/docs/en/claude-apps-gateway-config#store), `/readyz` keeps reporting ready for up to that many seconds after the store stops answering. Both endpoints 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.


[​](#concurrent-upstream-requests)

Concurrent upstream requests

By default, each gateway replica sends at most 256 requests upstream at the same time. A streaming response counts against the limit until the stream ends. A request that arrives while a replica is at the limit waits inside the gateway for a free slot. The developer sees a response that is slow to start or appears to hang. On a `provider: anthropic` upstream, a request that waits longer than [`timeouts.upstream_ttfb_ms`](/docs/en/claude-apps-gateway-config#http-tuning) gives up on that upstream, and fails with a 502 when no later upstream serves it. The startup log line that contains `upstream requests:` shows the limit in effect. While a replica has more requests open than the limit, it also logs a warning that contains `client requests are open`, at most once a minute. To serve more requests at once, you have two options:

- Add replicas.
- Raise the limit on each replica. Set the `BUN_CONFIG_MAX_HTTP_REQUESTS` environment variable on the gateway container to a whole number from 1 to 65535, then restart the container.

A replica fills its limit at a request rate of about the limit divided by the average number of seconds a request stays open. For example, if requests stay open for 10 seconds on average, a replica at the default limit of 256 fills it at about 26 requests a second. If you autoscale on CPU, a replica at the limit queues requests without triggering a scale-out, so set the target below the CPU level your replicas show when they log the `client requests are open` warning.

Every open request holds memory in the gateway process while it streams and while it waits for a slot. If you keep the limit at 256, memory on an overloaded replica still grows, because waiting requests keep their request bodies. Size the container’s memory for the number of requests open at peak, and watch memory when you change the limit. A replica that runs out of memory is killed and drops every stream it holds.


[​](#outage-behavior)

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**: by default `/readyz` reports not-ready as soon as Postgres is unreachable, so every replica fails its readiness check at once. Where traffic only reaches replicas that pass the check, all traffic, including inference the gateway could still serve, fails until Postgres recovers. The liveness probe on `/healthz` keeps passing throughout.

If your IdP goes down, existing sessions work until `ttl_hours` and new logins fail. A session refresh gets a try-again answer and succeeds once the IdP is back. Set a longer `ttl_hours` if your IdP has frequent maintenance windows.


[​](#readiness-grace-period)

Readiness grace period

To keep signed-in developers working through a short Postgres outage such as a database failover, set [`store.readiness_grace_seconds`](/docs/en/claude-apps-gateway-config#store) to longer than the failover takes, for example `300`. With spend limits on and the default fail-open behavior, requests through a replica that stays ready are unmetered until Postgres recovers, so keep the value as low as covers your failover. If you set [`enforcement.fail_closed_on_error: true`](/docs/en/claude-apps-gateway-config#enforcement), the gateway refuses signed-in developers’ inference with the `429` `spend limit unavailable` message until Postgres recovers, even while replicas still pass their readiness check. The setting requires Claude Code v2.1.282 or later on the gateway server. An earlier gateway refuses to start when it finds the key, so upgrade every replica before you add it. [Upgrades](#upgrades) covers rolling back. If you point the readiness probe at `/healthz` instead, replicas also keep passing it through an outage, but `/healthz` never reports not-ready, so a replica whose Postgres connection doesn’t recover keeps passing too.


[​](#jwt-secret-rotation)

JWT secret rotation

Rotate the signing secret in stages 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)

Postgres

The gateway holds five data tables plus a `_migrations` table, 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)

Upgrades

Replicas are stateless, so a rolling restart loses no gateway state. 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. When your orchestrator stops a replica with `SIGTERM`, as in a rolling restart or a scale-in, the gateway stops accepting new connections and lets requests and streams already in flight finish before it exits. It waits up to 25 seconds, called the drain window, then closes whatever is still open. A `SIGINT`, such as Ctrl+C in a terminal, starts the same drain, and a second signal during the drain closes the open requests and exits right away. Draining requires gateway v2.1.274 or later. Long generations can stream for minutes. On Kubernetes and Amazon ECS, raise both of these together to give those streams more time:

- **The drain window**: set the `CLAUDE_GATEWAY_DRAIN_TIMEOUT_MS` environment variable on the gateway container to a positive whole number of milliseconds, such as `120000`. The gateway ignores a value in any other form, such as `120s`, and keeps the 25-second default
- **Your orchestrator’s grace period**: `terminationGracePeriodSeconds` on Kubernetes, or `stopTimeout` on Amazon ECS

The grace period defaults to 30 seconds on both platforms. Keep it at least 5 seconds longer than the drain window, or the orchestrator kills the gateway before the drain finishes. On Kubernetes, add the duration of any `preStop` hook as well, because the grace period starts counting before the hook runs rather than when the gateway receives `SIGTERM`. Your platform may also cap how long the drain can run:

- **Amazon ECS on Fargate**: `stopTimeout` allows at most 120 seconds
- **Cloud Run**: stops an instance 10 seconds after `SIGTERM`, so open streams get at most 10 seconds there, whatever the drain window is

When the drain window ends with requests still open, the gateway logs a warning that contains `drain window over after`, counts the requests it cut, and names both settings to raise. 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)

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 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. If you turn on [`forward_user_identity`](/docs/en/claude-apps-gateway-config#per-user-identity-headers-for-a-proxy-you-run), the gateway also sends the developer’s email and IdP subject as headers to your proxy | Never                                              |
| Managed settings                                                                                       | Your gateway YAML → CLI                                                                                                                                                                                                                                                          | Never                                              |
| Audit log                                                                                              | Gateway stderr → your aggregator                                                                                                                                                                                                                                                 | Never                                              |


[​](#threat-model-summary)

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).
- The gateway’s requests to your IdP, your OTLP collectors, and `provider: anthropic` upstreams 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 those operator-influenced URLs 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. For the other providers, the gateway refuses a `base_url` that names one of those addresses or a metadata hostname when it loads the config, and the provider’s SDK then connects without the DNS check. If you turn on [proxy-only egress](/docs/en/claude-apps-gateway-config#proxy-only-egress), that address check moves to your forward proxy: the gateway hands it hostnames and the proxy’s allowlist must refuse those destinations. 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)

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. [Large rollouts](#large-rollouts) shows how to size them. The limits apply only to the sign-in flow, not to inference.


[​](#compliance-posture)

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 and error reporting while signed in to a gateway. Before the first sign-in, the CLI still sends startup events to Anthropic, including on machines whose managed settings force gateway sign-in. To keep those off too, deliver [`DISABLE_TELEMETRY`](/docs/en/managed-settings#turn-telemetry-off-for-your-organization) in the same [client-side managed settings](/docs/en/claude-apps-gateway-config#client-side-managed-settings) that force gateway sign-in.
- **Error reporting**: the CLI turns error reporting off whenever its model requests go to any endpoint other than Anthropic’s first-party API, such as Amazon Bedrock or a custom `ANTHROPIC_BASE_URL`.
- **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**: while signed in to a gateway, the CLI disables the Anthropic-bound rating upload together with the analytics streams, so it doesn’t send ratings 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)

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                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               |
| A developer’s requests fail with `Not signed in to the Cloud gateway — run /login.`                                                                                                                                    | The machine’s managed settings set `forceLoginMethod: "gateway"` or `forceLoginGatewayUrl`, and the session has no gateway sign-in. A leftover claude.ai login doesn’t satisfy the requirement.                                                                                                                                                                                                                      | Have the developer run `/login` and complete the gateway sign-in. See also [Administrator policy requires a Cloud gateway sign-in](/docs/en/errors#administrator-policy-requires-a-cloud-gateway-sign-in).                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  |
| 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                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      |
| Startup exits with `Administrator policy requires a Cloud gateway sign-in on this machine`                                                                                                                             | The developer’s environment sets `ANTHROPIC_API_KEY` or `ANTHROPIC_AUTH_TOKEN`, their settings configure an [`apiKeyHelper`](/docs/en/settings-reference#apikeyhelper), or an API key from an earlier Claude Console login is still saved                                                                                                                                                                            | Have the developer clear each that applies: unset the variable, remove the `apiKeyHelper` entry, or run `claude auth logout` to remove the saved key. A session that selects a cloud provider with `CLAUDE_CODE_USE_*` then starts with no sign-in; for every other session, have them start `claude` and sign in with `/login`. See also [Administrator policy requires a Cloud gateway sign-in](/docs/en/errors#administrator-policy-requires-a-cloud-gateway-sign-in).                                                                                                                                                                                                                                                                   |
| Startup or `/login` reports `Claude Code may not be enabled for your organization` after a 403 on the managed settings load                                                                                            | The gateway, or something in front of it, answered the `/managed/settings` request with 403. The gateway’s own settings route never answers 403. The status comes from the [`access_control`](/docs/en/claude-apps-gateway-config#http-tuning) IP checks or from a proxy or WAF in front of the gateway. The audit log records an IP-check denial as `access.denied` with the reason. The developer stays signed in. | Check the audit log for `access.denied` at the time of the failure and fix the `access_control` lists or the front end, then have the developer start `claude` again                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        |
| CLI `/login`: `The gateway is limiting sign-in attempts right now`, or `Request failed with status code 429` on older versions. The `/device` page may show `Too many attempts` to developers who haven’t tried before | The per-IP sign-in rate limit was reached. Either `listen.trusted_proxies` doesn’t cover the load balancer, so every developer shares its address, or many developers share a NAT or VPN egress address. Audit events with `result: rate_limited` show the same one or few `client_ip` values.                                                                                                                       | Set `listen.trusted_proxies` to the load balancer’s source ranges first, then raise `rate_limits` if developers still share addresses. See [Large rollouts](#large-rollouts).                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               |
| 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). If the address is public space your organization owns and uses internally, [declare that block](/docs/en/claude-apps-gateway#allow-a-gateway-on-public-address-space-you-own) instead.                                                                                                                                                                                                                                                                                   |
| 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`: `Claude Code only signs in to <host> from inside its declared network <block> (managed settings), and this machine is connecting from <ip>, outside it`                                                  | The gateway is on a block declared in [`gatewayInternalNetworks`](/docs/en/claude-apps-gateway#allow-a-gateway-on-public-address-space-you-own), and the developer’s machine reached it from an address outside that block: a VPN address pool, a container or WSL2 NAT segment, or a network that isn’t yours                                                                                                       | Have the developer run `/login` from the host OS on your network. If the address shown is also your organization’s own public space, replace the gateway’s entry with a block that covers both, up to `/8`; a second, overlapping entry is refused                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          |
| CLI `/login`: `Every address for gateway host <host> must be inside its declared network <block>, and it also resolves to <ip>`                                                                                        | The gateway’s name resolves to an address outside the block declared in [`gatewayInternalNetworks`](/docs/en/claude-apps-gateway#allow-a-gateway-on-public-address-space-you-own): a second site, or an IPv6 record on a dual-stack name. Under a declared block every record must be inside that one IPv4 block, private and IPv6 addresses included                                                                | Publish only records inside the block for the gateway name on developer machines, or serve a separate internal-only name                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    |
| CLI `/login`: `<host> is on the declared network <block>, which Claude Code checks over a direct connection, not through an HTTP proxy`                                                                                | An `HTTPS_PROXY` or `HTTP_PROXY` applies to a gateway on a declared block                                                                                                                                                                                                                                                                                                                                            | On the developer’s machine, add the `NO_PROXY` entry the message names                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      |
| CLI `/login`: a message starting `gatewayInternalNetworks in managed settings`                                                                                                                                         | The value breaks one of the [validation rules](/docs/en/claude-apps-gateway#allow-a-gateway-on-public-address-space-you-own), and the message names which. Until you fix it, Claude Code refuses every new gateway `/login` on the machine, gateways on private addresses included; existing sign-ins keep working                                                                                                   | In the managed settings source you deploy, correct the entry the message names, then rerun `/login`                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         |
| 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. If the pod also can’t resolve the IdP’s hostname, or the proxy refuses `CONNECT` to an IP address, see [Proxy-only egress](/docs/en/claude-apps-gateway-config#proxy-only-egress), which requires v2.1.277 or later.                                                                                                                        |
| 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                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               |
| Log: `could not connect to Postgres at boot, attempt 1 of 3`                                                                                                                                                           | The database wasn’t reachable yet when the gateway started, for example on a cold instance whose network is still coming up                                                                                                                                                                                                                                                                                          | If the gateway then finishes booting, no action is needed. When the database isn’t reachable, the gateway tries the connection three times, two seconds apart, before it exits. If it exits with `could not connect to Postgres`, check `store.postgres_url` and the network path to the database. If the attempts time out rather than being refused, raise [`store.connect_timeout_seconds`](/docs/en/claude-apps-gateway-config#store) to give each one longer.                                                                                                                                                                                                                                                                          |
| `/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 request_id=<id>: 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`.                                                                                                                                                                                                                                                                                                       |
| Log: `refresh failed request_id=<id>: invalid_token (…) (at userinfo_no_id_token, …)`, and developers see `Cloud gateway session expired` every `session.ttl_hours`                                                    | The IdP accepted the refresh token but returned no id_token with it, so the gateway asked the IdP’s userinfo endpoint for the user’s claims. The IdP rejected the refreshed access token there. The gateway answers `temporarily_unavailable`, so Claude Code keeps the refresh token but can’t renew the session. Gateway versions before v2.1.260 log the same line without the `(at …)` detail.                   | Set [`oidc.scope_on_refresh: true`](/docs/en/claude-apps-gateway-config#oidc), available in gateway v2.1.260 or later, so the refresh request asks for `openid` again. Some IdPs, such as Okta, return an id_token on refresh only when asked. On PingFederate, enable **Return ID Token On Refresh Grant** under **Applications \> OAuth \> OpenID Connect Policy Management** instead. The key doesn’t change PingFederate’s behavior. For other IdPs that still omit it, check whether the userinfo endpoint accepts access tokens issued by a refresh. As a stopgap, raise [`session.ttl_hours`](/docs/en/claude-apps-gateway-config#session). See [Identity provider setup](#identity-provider-setup) for the deprovisioning tradeoff. |
| 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.                                                                                                                                                                                                                                                                                                        |
| At peak load, responses are slow to start or appear to hang, or fail with a 502 `all upstreams failed` while the upstream is healthy                                                                                   | A replica has more requests open than it sends upstream at once, so the extra requests wait inside the gateway. On a `provider: anthropic` upstream, a request that waits longer than `timeouts.upstream_ttfb_ms` gives up on that upstream, which produces the 502 when no later upstream serves it. The log shows a warning that contains `client requests are open`.                                              | Add replicas, or raise the limit on each replica. See [Concurrent upstream requests](#concurrent-upstream-requests).                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        |
| 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.                                                                                                                                                                                                                                                                                                             |
| CLI `/login` completes the browser sign-in, then the session ends with `Cloud gateway sign-in was not completed` and a TLS certificate mismatch                                                                        | On the first request after sign-in, the gateway presented a certificate that doesn’t match the fingerprint Claude Code pinned, so Claude Code kept no gateway credential. The usual causes are replicas behind one address that serve different certificates, or something on the network path that intercepts TLS.                                                                                                  | Serve one certificate for the hostname, for example by terminating TLS once at the ingress, then have the developer run `/login` again. If that certificate differs from the pinned one, Claude Code shows the [trust prompt](/docs/en/claude-apps-gateway#connect-developers) again with a warning that the certificate changed.                                                                                                                                                                                                                                                                                                                                                                                                           |
| CLI `/login` stops with `The gateway's TLS certificate changed during sign-in: it no longer matches the one you trusted`                                                                                               | A sign-in request reached a server whose certificate doesn’t match the one the developer accepted when `/login` started: replicas behind one address serving different certificates, TLS interception on the path, or a certificate rotation while the sign-in was in progress.                                                                                                                                      | Serve one certificate for the hostname, then have the developer start the sign-in again and review the new certificate at the [trust prompt](/docs/en/claude-apps-gateway#connect-developers).                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              |

The `Cloud gateway sign-in was not completed` message names the gateway hostname. When Claude Code has both the pinned fingerprint and the presented one, the message also shows the first 16 characters of each. If Claude Code reports `couldn't load your organization's managed settings` after a gateway sign-in, Claude Code names the reason, restarts in place, and resumes the conversation. If Claude Code can’t restart, for example in a background session, Claude Code ends the session and keeps the sign-in.


[​](#related)

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-for-amazon-bedrock-claude-platform-on-aws-google-cloud-and-m-092e6df913.md

## On this page

- [Why Claude apps gateway](#why-claude-apps-gateway)
  - [Other gateway implementations](#other-gateway-implementations)
- [Quickstart](#quickstart)
  - [Prerequisites](#prerequisites)
  - [Steps](#steps)
- [Connect developers](#connect-developers)
  - [Set the gateway URL](#set-the-gateway-url)
  - [Allow a gateway on public address space you own](#allow-a-gateway-on-public-address-space-you-own)
  - [Deliver policy to Claude Desktop sessions](#deliver-policy-to-claude-desktop-sessions)
  - [Which machines need the opt-in](#which-machines-need-the-opt-in)
  - [Set the opt-in](#set-the-opt-in)
  - [Restrict parent settings](#restrict-parent-settings)
  - [Deploy the locks](#deploy-the-locks)
  - [Lock behavior across sources](#lock-behavior-across-sources)
  - [Settings the locks don’t cover](#settings-the-locks-don%E2%80%99t-cover)
  - [Connect Claude Desktop](#connect-claude-desktop)
  - [CI pipelines and remote machines](#ci-pipelines-and-remote-machines)
  - [What’s enforced on developers](#whats-enforced-on-developers)
  - [What the organization can see](#what-the-organization-can-see)
- [Availability and limitations](#availability-and-limitations)
- [Next steps](#next-steps)

Claude apps gateway

# Claude apps gateway for Amazon Bedrock, Claude Platform on AWS, Google Cloud, and Microsoft Foundry

Copy pageCopy page

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.

Copy pageCopy page

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.

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/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)

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/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 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.

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).

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)

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 compatibility guide](/docs/en/llm-gateway-protocol) documents what 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 also serves its own protocol reference at `GET /protocol`, which describes the endpoints it exposes to Claude Code clients: SSO sign-in, inference, managed settings delivery, model discovery, 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)

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.

**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. If your internal network is numbered from public IPv4 space your organization owns, see [Allow a gateway on public address space you own](#allow-a-gateway-on-public-address-space-you-own).


[​](#prerequisites)

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. At `/login`, Claude Code accepts a plain `http://` origin 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 outside a block you declare is rejected; see the [threat model](/docs/en/claude-apps-gateway-deploy#threat-model-summary) in the deployment guide. 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. If your internal network is numbered from public IPv4 space your organization owns, [declare those blocks](#allow-a-gateway-on-public-address-space-you-own) so `/login` accepts a gateway there. |
| 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.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      |


[​](#steps)

Steps

1

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).

2

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).

3

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:

gateway.yaml

```python
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).

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. It also needs 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.

4

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:

docker-compose.yaml

```python
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, 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:

```python
{"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
```

The gateway also logs a warning that `access_control.allow_cidrs` is empty. That’s expected here, because nothing limits which client addresses the gateway serves until you set an allow list. The [`access_control` reference](/docs/en/claude-apps-gateway-config#http-tuning) has the recommended ranges.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.

5

Verify the auth surface

Three checks confirm the gateway can authenticate a real user before you share it with 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:

```python
curl -s https://claude-gateway.internal.example.com/.well-known/oauth-authorization-server | jq
```

```python
{
  "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:

```python
curl -s -X POST https://claude-gateway.internal.example.com/oauth/device_authorization | jq
```

```python
{
  "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`

6

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/managed-settings#delivery-mechanisms), 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 to every developer machine.


[​](#connect-developers)

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. It checks that pin again during sign-in, on silent session refreshes, and on managed-settings fetches, while inference requests use standard TLS validation without the pin. Requests routed through an HTTPS proxy skip the pin check, so add the gateway host to `NO_PROXY` to keep them direct. Publish the expected SHA-256 fingerprint alongside the gateway URL so developers have something to compare against. The `/login` prompt shows the first 16 characters of the fingerprint as lowercase hexadecimal with no colons. To print the full fingerprint in that form from the certificate file, run:

```python
openssl x509 -noout -fingerprint -sha256 -in cert.pem | cut -d= -f2 | tr -d : | tr 'A-F' 'a-f'
```

When the certificate rotates, every developer sees the trust prompt again, so treat rotations as a planned event and republish the fingerprint. If your gateway policy includes [settings that need approval](/docs/en/server-managed-settings#security-approval-dialogs), the developer also sees that approval dialog again after accepting the new certificate, because Claude Code keys [approval memory](/docs/en/server-managed-settings#approval-memory) to the pinned certificate. A gateway can return the optional `email` field in its token response to name the account that a sign-in used. When it does, the developer confirms the account before Claude Code saves the credential. After a confirmed sign-in, `/status` shows the account. The confirmation requires Claude Code v2.1.275 or later on the developer machine; a client below that version ignores the field. The gateway server in the `claude` binary doesn’t return the field, so its sign-ins complete without the confirmation. Once the developer signs in, the [model picker](/docs/en/model-config) shows the models in their `availableModels` allowlist. Managed settings apply at startup and refresh hourly, and telemetry routes to your collector. Sessions refresh silently before `ttl_hours` expiry. When a refresh fails after IdP deprovisioning, Claude Code prompts the developer to log in again.


[​](#set-the-gateway-url)

Set the gateway URL

Three keys go in the per-OS [managed settings file](/docs/en/managed-settings#delivery-mechanisms) 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):

```python
{
  "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. Once the file is on a machine, a developer who hasn’t completed the gateway sign-in sees one of the messages described under [Administrator policy requires a Cloud gateway sign-in](/docs/en/errors#administrator-policy-requires-a-cloud-gateway-sign-in). Developers who select a cloud provider through an environment variable such as `CLAUDE_CODE_USE_BEDROCK` don’t need the gateway sign-in. 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.


[​](#allow-a-gateway-on-public-address-space-you-own)

Allow a gateway on public address space you own

Some organizations number their internal network from a public IPv4 block they own, such as a carrier’s own address space or a legacy `/8`, so their gateway can’t have a private address. List those blocks in the `gatewayInternalNetworks` managed setting. `/login` then accepts a gateway inside a listed block when the developer’s machine connects to it from an address inside the same block. This requires Claude Code v2.1.268 or later on the developer machine; earlier versions ignore the key and apply the private-address rule.

`gatewayInternalNetworks` is for internal networks that happen to be numbered from public address space. It doesn’t make it safe to expose a gateway to the internet: a trusted gateway can push settings that run commands on developer machines.Keep the gateway unreachable from outside your network with your firewall or load balancer rules. Set the gateway’s [`access_control.allow_cidrs`](/docs/en/claude-apps-gateway-config#http-tuning) to the same blocks you declare here, so the gateway itself refuses clients from anywhere else. Behind a load balancer or ingress, set `listen.trusted_proxies` to that front end as well, because the gateway otherwise matches `allow_cidrs` against the front end’s own address rather than the developer’s.

Add the key to the same managed settings source as the login keys: the managed settings file, MDM profile, or registry policy. Claude Code ignores it in user, project, and server-managed settings. This example declares one block. Replace `203.0.113.0/24` with your own block. It is a documentation range, and Claude Code refuses those.

```python
{
  "gatewayInternalNetworks": ["203.0.113.0/24"]
}
```

Claude Code validates the list at `/login` before it contacts any gateway:

- Each entry is an IPv4 block written as its first address and a prefix from `/8` to `/32`.
- The list holds at most four blocks, and no two overlap.
- No block overlaps private address space: `10.0.0.0/8`, `172.16.0.0/12`, `192.168.0.0/16`, `127.0.0.0/8`, `169.254.0.0/16`, and `100.64.0.0/10`. `/login` already accepts a gateway there without this key.
- No block overlaps space that is never an organization’s network: `198.18.0.0/15` and `192.0.0.0/24`, which VPN and NAT64 clients hold as local addresses; the documentation ranges `192.0.2.0/24`, `198.51.100.0/24`, and `203.0.113.0/24`; and the reserved ranges `0.0.0.0/8`, `192.88.99.0/24`, and multicast `224.0.0.0/4`. You can declare blocks inside `240.0.0.0/4`, which some large networks use as internal unicast space.

Blocks from `managed-settings.json` and its `managed-settings.d/` drop-in files combine into one list, and these limits apply to the combined list. To narrow a block, replace its entry rather than add a second, overlapping one in a drop-in; `/login` refuses the overlap. If an entry breaks a rule, or the value isn’t a list of strings, Claude Code refuses every new gateway sign-in on that machine and names the problem in the message. Sign-in to a gateway on a private address fails too, and existing sign-ins keep working. Try the value on one machine before you deploy it. Claude Code also lists a wrongly typed value among the [invalid managed settings it reports](/docs/en/managed-settings#keys-that-fail-closed). With a valid list, `/login` applies three checks to a gateway whose address is inside a listed block:

- Every address the gateway’s hostname resolves to is inside that one block. Claude Code refuses a name that also has records outside it, private and IPv6 addresses included.
- The developer’s machine connects from inside the same block. Claude Code refuses a machine behind NAT, inside a container or WSL2, or on a VPN whose address pool sits outside the block, and names the address the machine connected from.
- The connection is direct. If `HTTPS_PROXY` applies to the gateway host, `/login` refuses and names the `NO_PROXY` entry to add.

When all three pass, the [trust prompt](#connect-developers) adds a line naming the machine’s address, the gateway’s address, and the declared block that contains both. The key changes nothing for other gateways: sign-in to one on a private address works as before, and sign-in to one on a public address outside every listed block is refused as before. A declared block narrows who can sign in but doesn’t prove where a machine is, so declare only address space your organization controls. A block shared with other tenants, such as a cloud provider’s public range, lets anyone in it pass the same check.


[​](#deliver-policy-to-claude-desktop-sessions)

Deliver policy to Claude Desktop sessions

Claude Desktop runs its Cowork and Code tabs, plus the Chat tab when you enable it, on embedded Claude Code sessions and sends their model requests through the gateway. It passes policy to each of those sessions, built 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 reads the gateway URL from 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 [source that delivers the policy](/docs/en/managed-settings#which-managed-source-claude-code-uses) sets `parentSettingsBehavior: "merge"`.


[​](#which-machines-need-the-opt-in)

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; each Claude Code session fetches its policy from the gateway. Fleets whose [`policyHelper`](/docs/en/settings-reference#policyhelper) supplies managed settings can’t use it: Claude Code never merges parent settings on those fleets, because it reads managed settings from the helper’s output alone.


[​](#set-the-opt-in)

Set the opt-in

Deploy the managed settings snippet from [Set the gateway URL](#set-the-gateway-url), mirror it to any client-side source that outranks the file, then verify.

1

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.

2

Mirror the snippet to any source that outranks the file

Claude Code reads `parentSettingsBehavior` only from the [selected source](/docs/en/managed-settings#which-managed-source-claude-code-uses). Adding any policy key to a source can make that source the selected one, so in a client-side source, mirror the whole snippet rather than `parentSettingsBehavior` alone. [Client-side managed settings](/docs/en/claude-apps-gateway-config#client-side-managed-settings) covers fleets that deliver policy through Group Policy or configuration profiles. 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 `parentSettingsBehavior` in the gateway policy’s [`cli` block](/docs/en/claude-apps-gateway-config#managed).

3

Check which source is selected

On a machine that only runs Claude Desktop, call the Agent SDK’s [`resolveSettings()`](/docs/en/agent-sdk/typescript#resolvesettings) and read `policyOrigin` on the `managed` entry in its `sources` list. The value names the selected client-side source, `plist`, `hklm`, or `file`, which is the source that must carry the snippet. Claude Desktop’s embedded sessions don’t fetch the gateway policy, so the gateway’s `cli` block never counts as the selected source for them.


[​](#restrict-parent-settings)

Restrict parent settings

Once you deploy `parentSettingsBehavior: "merge"`, any host process that launches Claude Code can supply parent settings, not only Claude Desktop but also an Agent SDK application or an IDE extension. Claude Code filters parent settings against an allowlist of restrictive keys, but some allowed keys can grant access rather than restrict it. Unless you set the `allowManaged*Only` locks, permission allow rules and sandbox allowlists supplied by the host still apply. Your policy’s deny and ask rules stay in force either way; [they’re evaluated before any allow rule](/docs/en/permissions#manage-permissions). Claude Code forwards parent-supplied [`sandbox.credentials`](/docs/en/settings-reference#sandbox-credentials) entries in stripped form:

- **`deny` entries**: forwarded with only their `path` or `name` and the mode.
- **File entries with [`mode: mask`](/docs/en/sandboxing#mask-credential-files)**: forwarded sentinel-only, as a whole-file mask whose `injectHosts` is the empty list, so the proxy never substitutes the real value for a parent-supplied entry on any platform. All structured-masking fields are dropped too, so a parent-supplied extract pattern can’t displace a stricter mask another source sets for the same path.
- **`envVars` entries with `mode: mask`**: not forwarded. `deny` is the only restriction the parent channel can express through `envVars` entries.
- **[`awsPairs` and `sigv4`](/docs/en/sandboxing#re-sign-aws-requests)**: forwarded restriction-only. From `sigv4`, only `deny` values are kept, and a parent that defines a `sigv4` block at all pins all three request forms, `streaming`, `presigned`, and `sigv4a`, to `deny`. An `awsPairs` pair is never forwarded in a form that can re-sign; a pair that names one of the conventional AWS variables is replaced by an inert entry that keeps the automatic pairing of `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, and `AWS_SESSION_TOKEN` suppressed.


[​](#deploy-the-locks)

Deploy the locks

To keep parent settings as close to restriction-only as the filter supports, add all five `allowManaged*Only` locks, and the allowlists they govern, to the same sources as the merge opt-in:

```python
{
  "forceLoginMethod": "gateway",
  "forceLoginGatewayUrl": "https://claude-gateway.internal.example.com",
  "parentSettingsBehavior": "merge",
  "allowManagedPermissionRulesOnly": true,
  "allowManagedMcpServersOnly": true,
  "allowManagedHooksOnly": true,
  "allowedMcpServers": [{ "serverUrl": "https://mcp.internal.example.com/*" }],
  "sandbox": {
    "network": {
      "allowManagedDomainsOnly": true,
      "allowedDomains": ["github.com", "*.npmjs.org"]
    },
    "filesystem": {
      "allowManagedReadPathsOnly": true,
      "denyRead": ["~/"],
      "allowRead": ["~/projects"]
    }
  }
}
```

An OS policy, such as an HKLM registry policy or a managed-preferences plist, outranks this file, so deliver the whole snippet through it instead of the file. The gateway’s remote managed settings outrank the OS policy and file sources but reach only connected clients. Mirror the locks, the allowlists, and the merge opt-in into the policy’s [`cli` block](/docs/en/claude-apps-gateway-config#managed) and keep this file deployed, because machines that never connect, including ones that only run Claude Desktop, get their policy from the file alone.


[​](#lock-behavior-across-sources)

Lock behavior across sources

Setting one lock doesn’t restrict the others; each key is documented in the [settings reference](/docs/en/settings-reference#all-settings). From an admin source below the winner, the two sandbox locks still apply, and `allowManagedPermissionRulesOnly` still blocks parent-supplied allow rules and `additionalDirectories`. On Claude Code v2.1.273 or later, the MCP server lock also applies from a source below the winner, and while it is on, the managed `allowedMcpServers` list comes from the highest-priority admin source that sets one. The hooks lock and `allowManagedPermissionRulesOnly`’s effect on the developer’s own rules need the winning source by default; under the `managedSourcesBehavior` merge opt-in in [how Claude Code combines managed sources](/docs/en/managed-settings#how-claude-code-combines-managed-sources), Claude Code applies the strictest value any source sets for every lock. On [`policyHelper`](/docs/en/settings-reference#policyhelper) fleets, Claude Code reads the locks from the helper’s output alone. Each lock makes Claude Code ignore the developer’s own entries for that setting, so include your organization’s allowlists next to the locks:

- **Network domains**: locking with an empty managed domain list blocks all sandboxed outbound traffic.
- **MCP servers**: locking with no `allowedMcpServers` in any admin source or in the parent-supplied settings loads every server that `deniedMcpServers` doesn’t block.
- **Read paths**: `allowRead` entries only re-allow paths inside `denyRead` regions, so pair them with a managed `denyRead`.


[​](#settings-the-locks-don’t-cover)

Settings the locks don’t cover

Six parent-supplied settings pass the filter even with all five locks set. Under the default first-wins setting, an admin value blocks the parent’s only when it sits in the highest-priority admin source, except for `allowedMcpServers` while the [MCP server lock](#lock-behavior-across-sources) is on. Under the `managedSourcesBehavior` merge opt-in, [how Claude Code combines managed sources](/docs/en/managed-settings#how-claude-code-combines-managed-sources) says which source’s value applies instead.

- **`forceLoginOrgUUID`**: Claude Code honors a parent-supplied value when the highest-priority admin source doesn’t set an org UUID. Gateway sign-in doesn’t check this key. An org UUID in the highest-priority admin source blocks the parent’s value and is the one Claude Code enforces.
- **`allowedMcpServers`**: Claude Code honors a parent-supplied allowlist when no admin list is in force. `allowManagedMcpServersOnly` doesn’t block it, because the lock enforces whichever list wins as the managed value, including a parent-supplied one when no admin source supplies a list. A list in the highest-priority admin source blocks the parent’s and is the list Claude Code enforces, so set `allowedMcpServers` there, next to the lock. Before v2.1.223, a value for either key in any admin source blocked the parent’s.
- **`availableModels`**: Claude Code honors a parent-supplied model list when the winning managed source doesn’t set one. If your fleet restricts models, set `availableModels` in the winning source.
- **`strictKnownMarketplaces`**: Claude Code honors a parent-supplied plugin marketplace allowlist when the winning managed source doesn’t set one. If your fleet restricts marketplaces, set `strictKnownMarketplaces` in the winning source. Requires Claude Code v2.1.282 or later.
- **`blockedMarketplaces`**: a parent-supplied marketplace blocklist passes and adds to any blocklist that a managed source sets, since a blocklist can only restrict further. Requires Claude Code v2.1.282 or later.
- **`strictPluginOnlyCustomization`**: this key passes the filter regardless of any lock, and it makes Claude Code ignore the developer’s own customization, including protective hooks. No lock blocks it.


[​](#connect-claude-desktop)

Connect Claude Desktop

[Claude Desktop](/docs/en/desktop) connects to the same gateway through a different MDM key: set `bootstrapUrl` in Claude Desktop’s [managed configuration](https://claude.com/docs/third-party/claude-desktop/configuration) to `<listen.public_url>/user/bootstrap`, and opt the user’s policy in with a `desktop` key. [Claude Desktop overlay](/docs/en/claude-apps-gateway-config#claude-desktop-overlay) covers both halves. Requires Claude Code v2.1.203 or later on the gateway server. Claude Desktop signs the developer in through the gateway’s identity provider with the same browser SSO step, then fetches its configuration from the gateway instead of from Anthropic. Model access and policy follow the same per-group rules as the CLI. A developer who uses both the CLI and Claude Desktop signs in to each separately; the gateway session isn’t shared between them. Once connected, Claude Desktop sends model requests from every enabled tab through the gateway. It shows the Cowork and Code tabs by default. To turn on the Chat tab as well, set `chatTabEnabled` to `true` in Claude Desktop’s [managed configuration](https://claude.com/docs/third-party/claude-desktop/configuration), or in the policy’s [`desktop` block](/docs/en/claude-apps-gateway-config#claude-desktop-overlay) on a gateway running Claude Code v2.1.227 or later.


[​](#ci-pipelines-and-remote-machines)

CI pipelines and remote machines

There is no service-token flow for unattended pipelines. Gateway sign-in always runs the browser device flow, so a CI job with no developer to approve the sign-in can’t authenticate; configure those against your provider directly. Once a developer has signed in, each Claude Code session on that machine uses the gateway session, including non-interactive `claude -p` runs and sessions started by the Agent SDK. Claude Code applies the [gateway policy](/docs/en/claude-apps-gateway-config#managed) to each of them. The device flow separates the polling CLI from the approving browser, so a remote development box with no display still works: the developer runs `/login` over SSH on the remote machine and opens the verification link in the browser on their laptop.


[​](#whats-enforced-on-developers)

What’s enforced on developers

These guarantees apply to every session signed in through `/login`. The embedded sessions Claude Desktop launches get their policy as described in [Deliver policy to Claude Desktop sessions](#deliver-policy-to-claude-desktop-sessions), and the telemetry bullet says where their exports go.

- **Model access**: requests for models the policy doesn’t grant return 400, and the `/model` picker is filtered to the policy’s `availableModels` allowlist. Set [`enforceAvailableModels: true`](/docs/en/model-config#default-model-behavior) in the policy so the Default option resolves to a model inside `availableModels` instead of to Claude Code’s built-in default; without it, Default stays selectable and is rejected at request time if that model isn’t granted.
- **Telemetry destination**: in sessions signed in through `/login`, the CLI sends its OTLP/HTTP exports to the gateway rather than to a locally set `OTEL_EXPORTER_OTLP_ENDPOINT`, unless a policy [names your collector as the endpoint](/docs/en/claude-apps-gateway-config#export-directly-to-your-collector). The gateway relays the exports it receives to the destinations in [`telemetry.forward_to`](/docs/en/claude-apps-gateway-config#telemetry).
  - In the embedded sessions [Claude Desktop launches](#connect-claude-desktop), the CLI sends its exports to the configured `OTEL_EXPORTER_OTLP_ENDPOINT`. The CLI attaches the gateway session token to those exports only when that endpoint points at the gateway itself.
  - With no destination configured for a signal, the gateway accepts and discards it.
  - If you already collect Claude Code telemetry directly, add your collector as a `forward_to` destination, or name it in a policy to skip the relay.
- **Credentials**: the gateway token is the session’s only credential. [Anthropic profiles](/docs/en/authentication#anthropic-profiles-and-federation-credentials) and any earlier claude.ai login are ignored while signed in, so developers don’t need to log out of claude.ai first. For a configured `ANTHROPIC_API_KEY`, `ANTHROPIC_AUTH_TOKEN`, or `apiKeyHelper` credential, or an API key saved by an earlier Claude Console login, see [Administrator policy requires a Cloud gateway sign-in](/docs/en/errors#administrator-policy-requires-a-cloud-gateway-sign-in).
- **Managed settings**: locked keys can’t be overridden locally. The CLI applies the policy at startup and applies changes on each hourly poll, apart from the [changes that apply only at the next launch](/docs/en/server-managed-settings#fetch-and-caching-behavior).
- **Startup with the gateway unreachable**: signed-in sessions exit at startup with an error after about 10 seconds rather than starting without their settings.
- **Startup after the gateway ends the session**: see [Enforce fail-closed startup](/docs/en/server-managed-settings#enforce-fail-closed-startup) for the launches that open signed out of the gateway and the ones that exit when the gateway answers with a `401`.
- **Deprovisioning**: a session whose user is disabled in the IdP expires within `ttl_hours` when the next refresh fails.
- **Sign-out**: `/logout` deletes the gateway credential from the developer’s machine.
  - When the gateway’s discovery document advertises a `revocation_endpoint` on the gateway URL’s own scheme, host, and port, `/logout` also sends the stored tokens to that endpoint so the gateway can end the session on its side. The request is best effort, so sign-out completes on the developer’s machine whether or not the endpoint answers. The revocation requires Claude Code v2.1.275 or later on the developer’s machine.
  - The gateway server in the `claude` binary advertises none, so a sign-out from it ends the session on the developer’s machine only. To force sessions out server-side, see [JWT secret rotation](/docs/en/claude-apps-gateway-deploy#jwt-secret-rotation).


[​](#what-the-organization-can-see)

What the organization can see

Usage telemetry carries the developer’s identity, token counts, model, and latency to the organization’s collector. The gateway doesn’t log or store prompt or completion content. Whether richer telemetry such as logs and traces is collected, which can include commands and file paths, is the organization’s [per-destination choice](/docs/en/claude-apps-gateway-config#telemetry).


[​](#availability-and-limitations)

Availability and limitations

The table covers which Claude Code features work when developers connect through the gateway, and what the gateway server itself supports. Where something isn’t supported, the Notes column gives the alternative. The gateway delivers the [`anthropic-beta`](https://platform.claude.com/docs/en/api/beta-headers) values the CLI sends to every upstream, so operators don’t maintain a beta allowlist. For Amazon Bedrock, which ignores the header, the gateway moves the values into the request body’s `anthropic_beta` field; the other upstreams receive the header as sent.

| Feature                                                                                                                    | Status                | Notes                                                                                                                                                                                                                                                                                                                                                                                                                                                                |
|----------------------------------------------------------------------------------------------------------------------------|-----------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| Inference forwarding (Amazon Bedrock, Claude Platform on AWS, Google Cloud’s Agent Platform, Microsoft Foundry, Anthropic) | Available             | With per-upstream model translation and failover. The Amazon Bedrock upstream uses the `bedrock-runtime` endpoint and the AWS default credential chain; the Amazon Bedrock [Mantle endpoint](/docs/en/amazon-bedrock#use-the-mantle-endpoint) is not a supported upstream. 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.                           |
| Model access and managed settings by IdP group                                                                             | Available             | Model access is enforced server-side; managed settings are delivered per IdP group and applied by the CLI at the [managed settings tier](/docs/en/settings#settings-precedence)                                                                                                                                                                                                                                                                                      |
| Claude Desktop                                                                                                             | Available with opt-in | The gateway serves Claude Desktop’s configuration at `/user/bootstrap` once a policy [opts in with a `desktop` key](/docs/en/claude-apps-gateway-config#claude-desktop-overlay), and Claude Desktop sends model requests from its Cowork and Code tabs, and from the Chat tab when you enable it, through the gateway. To turn on the Chat tab, see [Connect Claude Desktop](#connect-claude-desktop). Requires Claude Code v2.1.203 or later on the gateway server. |
| Telemetry fan-out (OTLP/HTTP)                                                                                              | Available             | Identity-stamped per export; both protobuf and JSON encodings                                                                                                                                                                                                                                                                                                                                                                                                        |
| OIDC identity providers                                                                                                    | Available             | Any OIDC-compliant IdP; the gateway runs standard OIDC discovery and the authorization-code flow. See [Identity provider setup](/docs/en/claude-apps-gateway-deploy#identity-provider-setup) for per-IdP configuration                                                                                                                                                                                                                                               |
| Per-user and per-group spend limits                                                                                        | Available             | See [Spend limits](/docs/en/claude-apps-gateway-spend-limits)                                                                                                                                                                                                                                                                                                                                                                                                        |
| Server-side web search                                                                                                     | Not available         | The CLI can’t see which upstream provider the gateway routes to, so it can’t verify web search support and disables WebSearch on gateway sessions                                                                                                                                                                                                                                                                                                                    |
| [Remote Control](/docs/en/remote-control)                                                                                  | Not available         | The CLI shows [an error naming the gateway](/docs/en/errors#remote-control-requires-the-anthropic-api)                                                                                                                                                                                                                                                                                                                                                               |
| [`/design-sync`](/docs/en/commands#all-commands) and `/design-login`                                                       | Not available         | Both need claude.ai, which the CLI doesn’t contact on gateway sessions, so neither command appears there                                                                                                                                                                                                                                                                                                                                                             |
| Features that need feature-flag fetching, such as `/import` and `claude import`                                            | Not available         | The CLI skips the flag fetch on gateway sessions. [Features that need feature-flag fetching](/docs/en/env-vars#features-that-need-feature-flag-fetching) lists what that turns off                                                                                                                                                                                                                                                                                   |
| Standard prompt caching                                                                                                    | Available             | The gateway forwards `cache_control` breakpoints to every upstream. [Where the cache lives](/docs/en/prompt-caching#where-the-cache-lives) covers which blocks the CLI marks, including the system context it appends mid-conversation                                                                                                                                                                                                                               |
| 1-hour cache TTL                                                                                                           | Not available         | The CLI omits the extended-cache-ttl beta on gateway sessions, because not every upstream the gateway can route to supports the 1-hour TTL, so prompt caching through the gateway uses the 5-minute TTL; see the beta-header note above                                                                                                                                                                                                                              |
| Auto mode                                                                                                                  | Available             | Follows the [third-party provider rules](/docs/en/permission-modes#enable-auto-mode-on-bedrock-agent-platform-or-foundry): only the models eligible on third-party providers can use it. Before v2.1.207, auto mode on gateway sessions required setting `CLAUDE_CODE_ENABLE_AUTO_MODE=1`, deliverable through the managed policy `env` block                                                                                                                        |
| First-party-only optimizations such as global cache scope and token-efficient tools                                        | Not available         | The CLI doesn’t enable them on gateway sessions; see the beta-header note above                                                                                                                                                                                                                                                                                                                                                                                      |
| OTLP/gRPC                                                                                                                  | Not supported         | OTLP over HTTP only                                                                                                                                                                                                                                                                                                                                                                                                                                                  |
| SAML, LDAP, and other non-OIDC auth                                                                                        | Not supported         | OIDC only. Front with an OIDC bridge if needed                                                                                                                                                                                                                                                                                                                                                                                                                       |
| Multi-tenant (multiple OIDC issuers)                                                                                       | Not supported         | One issuer per gateway. Run separate instances                                                                                                                                                                                                                                                                                                                                                                                                                       |
| Windows server                                                                                                             | Not supported         | Deploy on Linux. macOS for local development only                                                                                                                                                                                                                                                                                                                                                                                                                    |
| Helm chart                                                                                                                 | Not available         | The gateway runs as a standard stateless Deployment; see the [deployment guide](/docs/en/claude-apps-gateway-deploy#kubernetes)                                                                                                                                                                                                                                                                                                                                      |
| Admin UI                                                                                                                   | Not available         | Configuration is the YAML file; redeploy to change it                                                                                                                                                                                                                                                                                                                                                                                                                |


[​](#next-steps)

Next steps

The quickstart leaves you with a minimal config running under Docker Compose. To take it further:

- Expand `gateway.yaml` beyond the minimal config, for example to add per-group RBAC, multi-upstream failover, or telemetry destinations. The [configuration reference](/docs/en/claude-apps-gateway-config) covers every option.
- Move from Compose to a production deployment on Kubernetes or Cloud Run, set up your IdP properly, and review the security model. The [deployment and operations guide](/docs/en/claude-apps-gateway-deploy) covers per-IdP setup, container image requirements, health probes, and troubleshooting.
- Put spend caps on individual developers or groups so a runaway workload can’t consume your whole commitment. [Spend limits](/docs/en/claude-apps-gateway-spend-limits) covers the admin API and how enforcement works.
- For a complete worked example on AWS, with ECS Fargate or EKS, Amazon RDS, and Secrets Manager, see [Deploy on AWS](/docs/en/claude-apps-gateway-on-aws).
- For a complete worked example on Google Cloud, with Cloud Run, Cloud SQL, and Secret Manager, see [Deploy on Google Cloud](/docs/en/claude-apps-gateway-on-gcp).


---
## claude-apps-gateway-spend-limits-claude-code-docs-d10936451f.md

## On this page

- [Set a cap](#set-a-cap)
  - [Authenticate to the admin API](#authenticate-to-the-admin-api)
- [How enforcement works](#how-enforcement-works)
  - [How requests are priced](#how-requests-are-priced)
  - [Postgres availability](#postgres-availability)
  - [Usage warnings in Claude Code](#usage-warnings-in-claude-code)
- [Admin API reference](#admin-api-reference)
  - [/effective](#%2Feffective)
  - [/audit](#%2Faudit)
  - [Pagination](#pagination)
- [Data lifecycle](#data-lifecycle)
- [Related](#related)

Claude apps gateway

# Claude apps gateway spend limits

Copy pageCopy page

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.

Copy pageCopy page

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)

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:

```python
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:

```python
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)

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)

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)

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)

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. Fail-open only helps while your load balancer or orchestrator still routes traffic to the gateway. See [Outage behavior](/docs/en/claude-apps-gateway-deploy#outage-behavior) for `store.readiness_grace_seconds`, which keeps replicas passing their readiness check through a short outage.


[​](#usage-warnings-in-claude-code)

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 most-consumed 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. With v2.1.251 or later on the developer’s machine, Claude Code also reads the same headers to show a **Spend limit** bar in `/usage`, with the percentage of their cap used and when it resets, and to add a `rate_limits.spend_limit` object to the [status line](/docs/en/statusline#rate-limit-usage) input. Claude Code shows both as a percentage rather than a dollar amount, and needs nothing newer than v2.1.225 on the gateway server.


[​](#admin-api-reference)

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)

`/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`. |

`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.


[​](#/audit)

`/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)

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)

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 |

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)

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-architecture.md

# Claude Code Architecture for Zack's Setup

> Arch Linux (omarchy) · Ryzen 9800X3D · RTX 4090 · 10Gbps fiber
> Projects: SpecHO, definitelynot.ai, llmx, iOS RE toolkits, Iconics

## Design Principles

1. **Tool Search handles main context** - Don't fight it, leverage it
2. **MCP isolation for subagents** - Purpose-built agents get specific tools
3. **llmx as primary code search** - Replace/augment built-in Explore
4. **No plugin wrappers** - That strategy is obsolete
5. **Explicit over implicit** - Clear tool boundaries, no inheritance surprises

---

## Directory Structure

```
~/.claude/
├── settings.json              # Global settings, disabled built-ins
├── settings.local.json        # Machine-specific overrides (not synced)
├── mcp.json                   # User-scope MCP servers (always available)
├── agents/
│   ├── llmx-explorer.md       # Replaces Explore, uses llmx
│   ├── web-automation.md      # Playwright isolation
│   ├── github-ops.md          # GitHub MCP isolation  
│   ├── re-analyst.md          # Reverse engineering workflow
│   ├── specwriter.md          # Documentation/spec generation
│   └── clean-task.md          # MCP-free general purpose
├── commands/
│   ├── search.md              # /search - llmx semantic search
│   ├── re.md                  # /re - reverse engineering workflow
│   ├── detect.md              # /detect - AI detection pipeline
│   └── index.md               # /index - reindex with llmx
├── skills/
│   ├── specho-analysis/
│   │   └── SKILL.md           # SpecHO fingerprinting workflow
│   ├── re-patterns/
│   │   └── SKILL.md           # iOS/binary RE patterns
│   └── api-conventions/
│       └── SKILL.md           # Your API style guide
├── hooks/
│   └── mcp-guard.py           # PreToolUse hook for MCP auditing
└── plugins/
    └── (empty - not using plugin wrappers)

# Project-level (per repo)
.claude/
├── settings.json              # Project permissions
├── agents/                    # Project-specific agents
├── skills/                    # Project-specific skills
└── CLAUDE.md                  # Project context
```

---

## MCP Server Tiers

### Tier 1: Always-On (User Scope)
Low token cost, high utility, used across all projects.

```json
// ~/.claude/mcp.json
{
  "mcpServers": {
    "llmx": {
      "command": "/home/zack/dev/llmx/target/release/llmx-mcp",
      "env": {
        "LLMX_STORAGE_DIR": "/home/zack/.llmx/indexes",
        "LLMX_MODEL_PATH": "/home/zack/.llmx/models/bge-small-en-v1.5.onnx"
      }
    }
  }
}
```

**Why llmx only?** It's your local semantic search - lightweight, always useful, no external API calls. Everything else is situational.

### Tier 2: Project-Scope (Shared with team)
Tools specific to a project, checked into repo.

```json
// ~/dev/specho/.mcp.json
{
  "mcpServers": {
    "huggingface": {
      "command": "npx",
      "args": ["-y", "@huggingface/mcp-server"],
      "env": {
        "HF_TOKEN": "${HF_TOKEN}"
      }
    }
  }
}

// ~/dev/definitelynot.ai/.mcp.json  
{
  "mcpServers": {
    "playwright": {
      "command": "npx",
      "args": ["@anthropic-ai/mcp-server-playwright"]
    }
  }
}
```

### Tier 3: On-Demand via Subagent
Heavy/specialized tools that should NEVER pollute main context.

These aren't configured globally - they're started by subagents or enabled per-session:

- `playwright` - Only via `web-automation` subagent
- `github` - Only via `github-ops` subagent  
- Database servers - Only via project-specific agents
- Heavy API servers (Jira, Linear, etc.)

---

## Settings Configuration

### Global Settings
```json
// ~/.claude/settings.json
{
  "permissions": {
    "deny": [
      "Task(Explore)",
      "mcp__playwright__*",
      "mcp__github__*"
    ],
    "allow": [
      "mcp__llmx__*",
      "Read",
      "Grep", 
      "Glob"
    ]
  },
  "env": {
    "ENABLE_TOOL_SEARCH": "auto:5",
    "MAX_MCP_OUTPUT_TOKENS": "50000",
    "CLAUDE_AUTOCOMPACT_PCT_OVERRIDE": "85"
  },
  "model": "claude-sonnet-4-5-20250929",
  "cleanupPeriodDays": 14
}
```

**Key decisions:**
- `Task(Explore)` denied - replaced by `llmx-explorer`
- `mcp__playwright__*` denied in main context - only via subagent
- `mcp__github__*` denied in main context - only via subagent
- `ENABLE_TOOL_SEARCH=auto:5` - More aggressive lazy loading (5% threshold)
- `CLAUDE_AUTOCOMPACT_PCT_OVERRIDE=85` - Compact earlier to preserve context

### Project Settings Example
```json
// ~/dev/specho/.claude/settings.json
{
  "permissions": {
    "allow": [
      "mcp__huggingface__*",
      "Bash(python *)",
      "Bash(cargo *)"
    ]
  }
}
```

---

## Subagent Definitions

### 1. llmx-explorer.md (Replaces Explore)
```yaml
---
name: llmx-explorer
description: |
  Fast semantic code search and codebase exploration. Use for:
  - Finding relevant code by meaning, not just text
  - Understanding codebase structure and patterns
  - Locating implementations, definitions, usages
  - Architectural analysis
  Replaces built-in Explore with semantic search via llmx.
model: haiku
tools: Read, Grep, Glob, mcp__llmx__search, mcp__llmx__index_status
disallowedTools: Write, Edit, Bash, mcp__playwright__*, mcp__github__*
permissionMode: plan
---

You are a semantic code explorer powered by llmx.

## Primary Tool: mcp__llmx__search
Use semantic search FIRST for any code discovery task. Query with natural language descriptions of what you're looking for.

## Fallback Tools
- `Grep` - For exact string/regex matches after semantic search narrows scope
- `Glob` - For file pattern discovery
- `Read` - To examine files found via search

## Response Format
Return findings as:
1. **Relevant Files** - Ranked by semantic relevance
2. **Key Patterns** - Architectural observations
3. **Entry Points** - Where to start for the task

Never modify files. Summarize concisely for the main agent.
```

### 2. web-automation.md (Playwright Isolation)
```yaml
---
name: web-automation
description: |
  Browser automation and web scraping tasks. Use for:
  - Automated testing with Playwright
  - Web scraping and data extraction
  - Screenshot capture and visual testing
  - Form filling and interaction recording
  MUST run in foreground (MCP required).
model: inherit
tools: Read, Write, Bash, mcp__playwright__*
disallowedTools: mcp__github__*, mcp__llmx__*
permissionMode: acceptEdits
---

You are a browser automation specialist using Playwright MCP.

## Available Playwright Tools
- Navigate, click, fill, screenshot
- Wait for selectors, extract text
- Handle multiple pages/contexts

## Constraints
- Always close browser contexts when done
- Return screenshots as base64 when visual verification needed
- Summarize extracted data, don't dump raw HTML

## Response Format
Return:
1. **Actions Taken** - What you did
2. **Results** - Data extracted or verification status
3. **Screenshots** - If visual confirmation needed
```

### 3. github-ops.md (GitHub Isolation)
```yaml
---
name: github-ops
description: |
  GitHub operations and repository management. Use for:
  - Creating/reviewing pull requests
  - Issue management and triage
  - Repository search and analysis
  - Release management
  MUST run in foreground (MCP required).
model: inherit
tools: Read, Bash, mcp__github__*
disallowedTools: mcp__playwright__*, Write, Edit
permissionMode: default
---

You are a GitHub operations specialist.

## Available GitHub Tools
- Search repositories, issues, PRs
- Create/update issues and PRs
- Manage labels, milestones, releases
- Review and comment on code

## Constraints
- Confirm destructive operations before executing
- Summarize PR/issue content, don't dump full text
- Use rate limiting awareness

## Response Format
Return:
1. **Operation** - What was done
2. **Result** - Links to created/modified resources
3. **Summary** - Key information extracted
```

### 4. re-analyst.md (Reverse Engineering)
```yaml
---
name: re-analyst
description: |
  Binary analysis and reverse engineering tasks. Use for:
  - iOS/macOS binary analysis
  - Disassembly and decompilation review
  - Protocol reverse engineering
  - Security vulnerability analysis
  Specialized for RE workflows.
model: inherit
tools: Read, Bash, Grep, Glob
disallowedTools: mcp__*, Write, Edit
permissionMode: plan
skills:
  - re-patterns
---

You are a reverse engineering analyst.

## Methodology
1. Static analysis first (strings, headers, imports)
2. Identify interesting functions via symbols/patterns
3. Trace data flow and control flow
4. Document findings systematically

## Tools Available
- `Bash` for running analysis tools (otool, nm, strings, class-dump, frida)
- `Read` for examining output files
- `Grep/Glob` for pattern matching

## Response Format
Return:
1. **Target Info** - Binary metadata, architecture
2. **Interesting Findings** - Functions, strings, patterns
3. **Recommendations** - Next analysis steps
```

### 5. clean-task.md (MCP-Free General Purpose)
```yaml
---
name: clean-task
description: |
  General purpose task execution WITHOUT external tool access. Use for:
  - Code modifications that don't need external APIs
  - File operations and refactoring
  - Local testing and validation
  - Any task where MCP would be overhead
  Lightweight alternative to general-purpose.
model: inherit
tools: Read, Write, Edit, Bash, Grep, Glob
disallowedTools: mcp__*
permissionMode: acceptEdits
---

You are a focused task executor. No external API access.

Work efficiently with local tools only. If you need external data,
tell the main agent to use a specialized subagent instead.
```

### 6. specwriter.md (Documentation)
```yaml
---
name: specwriter
description: |
  Technical documentation and specification writing. Use for:
  - API documentation generation
  - Architecture decision records
  - README and guide creation
  - Code comment generation
  Read-heavy, write-focused.
model: inherit  
tools: Read, Write, Grep, Glob, mcp__llmx__search
disallowedTools: Bash, Edit, mcp__playwright__*, mcp__github__*
permissionMode: acceptEdits
---

You are a technical writer. Read code, write documentation.

## Process
1. Use llmx to find relevant code
2. Read and understand the implementation
3. Write clear, accurate documentation
4. Use consistent formatting (your conventions)

## Output Formats
- Markdown for docs
- JSDoc/docstrings for inline
- OpenAPI for APIs
```

---

## Slash Commands

### /search - Semantic Code Search
```yaml
# ~/.claude/commands/search.md
---
description: Semantic code search using llmx
argument-hint: "<natural language query>"
---

Use the llmx-explorer subagent to search for: $ARGUMENTS

Return the top 10 most relevant results with file paths and brief context.
```

### /index - Reindex Codebase
```yaml
# ~/.claude/commands/index.md
---
description: Reindex current project with llmx
---

Run the llmx indexer on the current project:

```bash
llmx index --path . --storage ~/.llmx/indexes/$(basename $(pwd))
```

Report indexing status when complete.
```

### /re - Reverse Engineering Session
```yaml
# ~/.claude/commands/re.md
---
description: Start reverse engineering analysis
argument-hint: "<binary path or description>"
---

Use the re-analyst subagent to analyze: $ARGUMENTS

Begin with static analysis, then report findings and recommended next steps.
```

### /detect - AI Detection Pipeline
```yaml
# ~/.claude/commands/detect.md
---
description: Run SpecHO AI text detection
argument-hint: "<text or file path>"
---

Analyze the provided content using SpecHO fingerprinting:

1. Extract linguistic features (148-dimensional)
2. Compare against known model signatures
3. Report confidence scores and model attribution

Input: $ARGUMENTS
```

---

## Skills

### specho-analysis/SKILL.md
```yaml
---
name: specho-analysis
description: |
  SpecHO AI text detection and model fingerprinting workflow.
  148-dimensional linguistic analysis for identifying AI-generated text
  and attributing specific models.
disable-model-invocation: false
---

# SpecHO Analysis Workflow

## Feature Extraction Pipeline
1. **Tokenization** - Split into sentences and tokens
2. **Lexical Features** - Vocabulary richness, word length distribution
3. **Syntactic Features** - POS patterns, dependency structures  
4. **Semantic Features** - Embedding-based coherence metrics
5. **Stylometric Features** - Punctuation patterns, sentence rhythm

## Model Signatures
Known fingerprints for: GPT-4, GPT-3.5, Claude 3, Claude 2, Llama, Mistral

## Usage
```python
from specho import SpecHODetector

detector = SpecHODetector()
result = detector.analyze(text)
print(f"AI Probability: {result.ai_score:.2%}")
print(f"Likely Model: {result.attribution}")
```

## Confidence Thresholds
- >0.95: High confidence AI
- 0.70-0.95: Likely AI, needs review
- 0.30-0.70: Uncertain
- <0.30: Likely human
```

### re-patterns/SKILL.md
```yaml
---
name: re-patterns
description: |
  iOS and macOS reverse engineering patterns and techniques.
  Common analysis workflows for binary analysis.
disable-model-invocation: true
---

# iOS/macOS RE Patterns

## Initial Triage
```bash
# Binary info
file <binary>
otool -h <binary>
otool -l <binary> | grep -A2 LC_ENCRYPTION

# Symbols
nm -u <binary>  # Undefined (imports)
nm -g <binary>  # Global (exports)

# Strings
strings -a <binary> | grep -i "api\|key\|secret\|token"

# Objective-C
class-dump <binary> > headers.h
```

## Frida Patterns
```javascript
// Hook method
Interceptor.attach(ObjC.classes.ClassName["- methodName:"].implementation, {
  onEnter: function(args) {
    console.log("Called with:", ObjC.Object(args[2]));
  }
});

// Trace class
ObjC.classes.ClassName.$methods.forEach(function(m) {
  console.log(m);
});
```

## Common Targets
- `SecItemCopyMatching` - Keychain access
- `CC_SHA256` / `CCCrypt` - Crypto operations
- `NSURLSession` - Network requests
- `UIApplication openURL` - URL schemes
```

---

## Hooks

### mcp-guard.py (Audit MCP Usage)
```python
#!/usr/bin/env python3
# ~/.claude/hooks/mcp-guard.py
"""
PreToolUse hook to audit and optionally block MCP tool usage.
Logs all MCP calls for review.
"""

import json
import sys
from datetime import datetime
from pathlib import Path

LOG_FILE = Path.home() / ".claude" / "mcp-audit.log"

def main():
    input_data = json.load(sys.stdin)
    tool_name = input_data.get("tool_name", "")
    
    # Only process MCP tools
    if not tool_name.startswith("mcp__"):
        sys.exit(0)
    
    # Log the call
    log_entry = {
        "timestamp": datetime.now().isoformat(),
        "tool": tool_name,
        "session": input_data.get("session_id", "unknown"),
        "input": input_data.get("tool_input", {})
    }
    
    with open(LOG_FILE, "a") as f:
        f.write(json.dumps(log_entry) + "\n")
    
    # Allow the call (exit 0)
    # To block: exit 2 with error message
    sys.exit(0)

if __name__ == "__main__":
    main()
```

### Hook Configuration
```json
// In ~/.claude/settings.json
{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "mcp__.*",
        "hooks": [
          {
            "type": "command",
            "command": "python3 ~/.claude/hooks/mcp-guard.py"
          }
        ]
      }
    ]
  }
}
```

---

## CLAUDE.md Template

```markdown
# Project: [Name]

## Quick Context
[2-3 sentences about what this project does]

## Tech Stack
- Language: [Rust/Python/TypeScript/etc]
- Framework: [if applicable]
- Key deps: [important libraries]

## Directory Structure
```
src/           - Main source
tests/         - Test files
docs/          - Documentation
```

## Development Commands
```bash
# Build
cargo build --release

# Test
cargo test

# Run
./target/release/[binary]
```

## Code Conventions
- [Your style preferences]
- [Naming conventions]
- [Error handling patterns]

## Subagent Guidelines
When delegating tasks:
- **Code search**: Use llmx-explorer (semantic search)
- **Web automation**: Use web-automation (foreground only)
- **GitHub ops**: Use github-ops (foreground only)
- **General tasks**: Use clean-task (no MCP overhead)
- **NEVER** give subagents MCP tools they don't explicitly need

## Current Focus
[What you're working on - update this]
```

---

## Workflow Integration

### Daily Workflow
```
1. Start session: `claude` (llmx always available)
2. Code search: `/search <query>` or natural language
3. Tasks needing GitHub: Claude auto-delegates to github-ops
4. Tasks needing browser: Claude auto-delegates to web-automation
5. Everything else: clean-task or inline

Tool Search handles main context. Subagents handle isolation.
```

### Project Onboarding
```bash
# Index new project
cd ~/dev/new-project
llmx index --path . --storage ~/.llmx/indexes/new-project

# Create project config if needed
mkdir -p .claude
cat > .claude/settings.json << 'EOF'
{
  "permissions": {
    "allow": ["Bash(cargo *)"]
  }
}
EOF

# Add project-specific MCP if needed
cat > .mcp.json << 'EOF'
{
  "mcpServers": {}
}
EOF
```

### Monitoring
```bash
# Check context usage
# In Claude Code: /context or /doctor

# Review MCP audit log
tail -f ~/.claude/mcp-audit.log | jq .

# Check llmx index status
llmx status
```

---

## Migration Checklist

- [ ] Backup existing `~/.claude/` directory
- [ ] Create directory structure above
- [ ] Move MCP servers from mcp.json to appropriate tiers
- [ ] Create subagent definitions
- [ ] Create slash commands
- [ ] Add hooks for MCP auditing
- [ ] Update CLAUDE.md in active projects
- [ ] Test subagent delegation with explicit prompts
- [ ] Verify Tool Search is working: `/doctor`

---

## Known Limitations

1. **Background subagents can't use MCP** - Always use foreground for MCP-heavy tasks
2. **Custom plugin subagents may not inherit MCP** - Known bug (#13605)
3. **No scoped MCP per-subagent** - Feature requested (#6915, #7289)
4. **Built-in agents can't be overridden by name** - Must disable + replace
5. **Tool Search threshold is global** - Can't set per-server

---

## Future Improvements (Watch These Issues)

- [ ] #6915 - MCP tools available only to subagent
- [ ] #7289 - Disable MCP in default, enable for subagents
- [ ] #13605 - Plugin subagents MCP access
- [ ] #13254 - Background subagents MCP access


---
## claude-code-built-in-tools-reference.md

This is a guide I created while exploring the built-in tools Claude Code ships with and how it’s trained/prompted to use them. Included below is every built-in tool, their parameters, usage patterns, and examples. This was basically all written by prompting Claude Code itself.

___

## Table of Contents[](https://www.vtrivedy.com/posts/claudecode-tools-reference#table-of-contents)

Open Table of Contents

-   [1\. Task](https://www.vtrivedy.com/posts/claudecode-tools-reference#1-task)
-   [2\. Bash](https://www.vtrivedy.com/posts/claudecode-tools-reference#2-bash)
-   [3\. Glob](https://www.vtrivedy.com/posts/claudecode-tools-reference#3-glob)
-   [4\. Grep](https://www.vtrivedy.com/posts/claudecode-tools-reference#4-grep)
-   [5\. Read, 6. Edit, 7. Write](https://www.vtrivedy.com/posts/claudecode-tools-reference#5-read-6-edit-7-write)
    -   [Read](https://www.vtrivedy.com/posts/claudecode-tools-reference#read)
    -   [Edit](https://www.vtrivedy.com/posts/claudecode-tools-reference#edit)
    -   [Write](https://www.vtrivedy.com/posts/claudecode-tools-reference#write)
-   [8\. NotebookEdit](https://www.vtrivedy.com/posts/claudecode-tools-reference#8-notebookedit)
-   [9\. WebFetch, 10. WebSearch](https://www.vtrivedy.com/posts/claudecode-tools-reference#9-webfetch-10-websearch)
    -   [WebFetch](https://www.vtrivedy.com/posts/claudecode-tools-reference#webfetch)
    -   [WebSearch](https://www.vtrivedy.com/posts/claudecode-tools-reference#websearch)
-   [11\. TodoWrite](https://www.vtrivedy.com/posts/claudecode-tools-reference#11-todowrite)
-   [12\. ExitPlanMode](https://www.vtrivedy.com/posts/claudecode-tools-reference#12-exitplanmode)
-   [13\. BashOutput, 14. KillShell](https://www.vtrivedy.com/posts/claudecode-tools-reference#13-bashoutput-14-killshell)
    -   [BashOutput](https://www.vtrivedy.com/posts/claudecode-tools-reference#bashoutput)
    -   [KillShell](https://www.vtrivedy.com/posts/claudecode-tools-reference#killshell)
-   [15\. SlashCommand](https://www.vtrivedy.com/posts/claudecode-tools-reference#15-slashcommand)
-   [Tool Usage Patterns & Best Practices](https://www.vtrivedy.com/posts/claudecode-tools-reference#tool-usage-patterns--best-practices)
    -   [Batch Operations](https://www.vtrivedy.com/posts/claudecode-tools-reference#batch-operations)
    -   [File Operations Workflow](https://www.vtrivedy.com/posts/claudecode-tools-reference#file-operations-workflow)
    -   [Complex Task Workflow](https://www.vtrivedy.com/posts/claudecode-tools-reference#complex-task-workflow)
    -   [Background Process Management](https://www.vtrivedy.com/posts/claudecode-tools-reference#background-process-management)
    -   [Searching Strategy](https://www.vtrivedy.com/posts/claudecode-tools-reference#searching-strategy)
    -   [Communication](https://www.vtrivedy.com/posts/claudecode-tools-reference#communication)
-   [Examples: Complete Workflows](https://www.vtrivedy.com/posts/claudecode-tools-reference#examples-complete-workflows)
    -   [Workflow 1: Add New Feature](https://www.vtrivedy.com/posts/claudecode-tools-reference#workflow-1-add-new-feature)
    -   [Workflow 2: Debug Issue](https://www.vtrivedy.com/posts/claudecode-tools-reference#workflow-2-debug-issue)
    -   [Workflow 3: Research Codebase](https://www.vtrivedy.com/posts/claudecode-tools-reference#workflow-3-research-codebase)
    -   [Workflow 4: Refactor Code](https://www.vtrivedy.com/posts/claudecode-tools-reference#workflow-4-refactor-code)
-   [Tool Selection Quick Reference](https://www.vtrivedy.com/posts/claudecode-tools-reference#tool-selection-quick-reference)
-   [Common Antipatterns to Avoid](https://www.vtrivedy.com/posts/claudecode-tools-reference#common-antipatterns-to-avoid)

## 1\. Task[](https://www.vtrivedy.com/posts/claudecode-tools-reference#1-task)

**Purpose**: Launch specialized sub-agents for complex, multi-step tasks that require autonomous work.

**Available Agent Types**:

-   `general-purpose`: Research, code search, multi-step tasks (has access to all tools)
-   `statusline-setup`: Configure status line settings (Read, Edit)
-   `output-style-setup`: Create output styles (Read, Write, Edit, Glob, Grep)

**Parameters**:

-   `subagent_type` (required): Which agent type to use
-   `prompt` (required): Detailed task description for the agent
-   `description` (required): Short 3-5 word description of the task

**When to Use**:

-   Complex tasks requiring multiple rounds of searching/reading
-   When you need an agent to work autonomously
-   Open-ended research tasks

**When NOT to Use**:

-   Reading a specific known file path (use Read instead)
-   Searching within 2-3 specific files (use Read instead)
-   Searching for specific class definitions (use Glob instead)

**Example**:

```
{
  "subagent_type": "general-purpose",
  "description": "Find authentication implementation",
  "prompt": "Search the codebase to find where user authentication is implemented. Look for login functions, JWT handling, and session management. Return the file paths and a summary of how authentication works."
}
```

**Key Notes**:

-   Launch multiple agents concurrently when possible (single message, multiple tool calls)
-   Agent returns one final message - cannot communicate back and forth
-   Agents are stateless - provide complete instructions upfront
-   Specify whether agent should write code or just research

___

## 2\. Bash[](https://www.vtrivedy.com/posts/claudecode-tools-reference#2-bash)

**Purpose**: Execute shell commands in a persistent bash session.

**Parameters**:

-   `command` (required): The shell command to execute
-   `description` (optional but recommended): Clear 5-10 word description
-   `timeout` (optional): Milliseconds before timeout (default: 120000ms, max: 600000ms)
-   `run_in_background` (optional): Boolean to run command in background

**When to Use**:

-   Terminal operations: git, npm, docker, pytest, etc.
-   Commands that modify system state
-   Running builds, tests, servers

**When NOT to Use**:

-   File reading (use Read instead of cat/head/tail)
-   File editing (use Edit instead of sed/awk)
-   File writing (use Write instead of echo >)
-   File searching (use Glob instead of find/ls)
-   Content searching (use Grep instead of grep/rg)

**Examples**:

Basic command:

```
{
  "command": "git status",
  "description": "Check git repository status"
}
```

With timeout:

```
{
  "command": "npm run build",
  "description": "Build production bundle",
  "timeout": 300000
}
```

Background process:

```
{
  "command": "npm run dev",
  "description": "Start development server",
  "run_in_background": true
}
```

Chained commands (sequential with &&):

```
{
  "command": "git add . && git commit -m \"Update features\" && git push",
  "description": "Stage, commit, and push changes"
}
```

Command with paths containing spaces:

```
{
  "command": "cd \"/path/with spaces/\" && ls",
  "description": "List files in directory with spaces"
}
```

**Git Commit Best Practices**:

1.  Run `git status`, `git diff`, and `git log` in parallel
2.  Draft commit message following repo style
3.  Add files and commit with co-authorship footer:

```
git commit -m "$(cat <<'EOF'
Your commit message here.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
EOF
)"
```

**Pull Request Creation**:

1.  Run `git status`, `git diff`, and `git log [base-branch]...HEAD` in parallel
2.  Draft PR summary from ALL commits (not just latest)
3.  Push and create PR:

```
gh pr create --title "PR title" --body "$(cat <<'EOF'
## Summary
- Bullet point 1
- Bullet point 2

## Test plan
- [ ] Test item 1
- [ ] Test item 2

🤖 Generated with [Claude Code](https://claude.com/claude-code)
EOF
)"
```

___

## 3\. Glob[](https://www.vtrivedy.com/posts/claudecode-tools-reference#3-glob)

**Purpose**: Fast file pattern matching for finding files by name patterns.

**Parameters**:

-   `pattern` (required): Glob pattern to match files
-   `path` (optional): Directory to search in (defaults to current working directory)

**Pattern Examples**:

-   `**/*.js` - All JavaScript files recursively
-   `src/**/*.ts` - All TypeScript files in src/
-   `*.{json,yaml}` - All JSON and YAML files in current directory
-   `test/**/*test.js` - All test files

**Examples**:

Find all TypeScript files:

Find config files in specific directory:

```
{
  "pattern": "*.config.{js,ts}",
  "path": "/Users/project/root"
}
```

Find React components:

```
{
  "pattern": "src/components/**/*.tsx"
}
```

**Key Notes**:

-   Returns files sorted by modification time
-   Works with any codebase size
-   Faster than bash find command
-   Use for open-ended searches with Agent tool

___

## 4\. Grep[](https://www.vtrivedy.com/posts/claudecode-tools-reference#4-grep)

**Purpose**: Powerful content search built on ripgrep for finding text/code patterns in files.

**Parameters**:

-   `pattern` (required): Regular expression to search for
-   `path` (optional): File or directory to search (defaults to current directory)
-   `output_mode` (optional): “content” (lines), “files\_with\_matches” (paths), “count” (counts)
-   `glob` (optional): Filter files by glob pattern (e.g., “\*.js”)
-   `type` (optional): Filter by file type (e.g., “js”, “py”, “rust”)
-   `-A` (optional): Lines to show after match (requires output\_mode: “content”)
-   `-B` (optional): Lines to show before match (requires output\_mode: “content”)
-   `-C` (optional): Lines to show before and after match (requires output\_mode: “content”)
-   `-i` (optional): Case insensitive search (boolean)
-   `-n` (optional): Show line numbers (requires output\_mode: “content”)
-   `multiline` (optional): Enable multiline matching where . matches newlines (boolean)
-   `head_limit` (optional): Limit output to first N results

**Pattern Syntax**:

-   Uses ripgrep (not grep)
-   Literal braces need escaping: `interface\\{\\}` to find `interface{}`
-   Standard regex: `log.*Error`, `function\\s+\\w+`

**Examples**:

Find files containing pattern:

```
{
  "pattern": "function authenticate",
  "output_mode": "files_with_matches"
}
```

Show matching lines with context:

```
{
  "pattern": "TODO",
  "output_mode": "content",
  "-n": true,
  "-C": 2
}
```

Case-insensitive search in TypeScript files:

```
{
  "pattern": "errorhandler",
  "-i": true,
  "type": "ts",
  "output_mode": "content"
}
```

Count occurrences:

```
{
  "pattern": "console\\.log",
  "output_mode": "count"
}
```

Search with glob filter:

```
{
  "pattern": "import.*React",
  "glob": "*.tsx",
  "output_mode": "content"
}
```

Multiline pattern matching:

```
{
  "pattern": "struct \\{[\\s\\S]*?field",
  "multiline": true,
  "output_mode": "content"
}
```

Limited results:

```
{
  "pattern": "export function",
  "output_mode": "content",
  "head_limit": 10
}
```

___

## 5\. Read, 6. Edit, 7. Write[](https://www.vtrivedy.com/posts/claudecode-tools-reference#5-read-6-edit-7-write)

### Read[](https://www.vtrivedy.com/posts/claudecode-tools-reference#read)

**Purpose**: Read files from the filesystem.

**Parameters**:

-   `file_path` (required): Absolute path to file
-   `offset` (optional): Line number to start reading from
-   `limit` (optional): Number of lines to read

**Supported File Types**:

-   Text files
-   Images (PNG, JPG) - displayed visually
-   PDFs - processed page by page
-   Jupyter notebooks (.ipynb) - all cells with outputs

**Examples**:

Read entire file:

```
{
  "file_path": "/Users/vivektrivedy/Desktop/personal_website/vtrivedy_website/src/config.ts"
}
```

Read specific line range:

```
{
  "file_path": "/path/to/large/file.log",
  "offset": 1000,
  "limit": 100
}
```

Read image:

```
{
  "file_path": "/var/folders/tmp/Screenshot.png"
}
```

**Key Notes**:

-   Returns content with line numbers (cat -n format)
-   Lines longer than 2000 characters are truncated
-   Default reads up to 2000 lines from start
-   Batch multiple Read calls in one response for performance

___

### Edit[](https://www.vtrivedy.com/posts/claudecode-tools-reference#edit)

**Purpose**: Perform exact string replacements in files.

**Parameters**:

-   `file_path` (required): Absolute path to file
-   `old_string` (required): Exact text to replace
-   `new_string` (required): Replacement text (must differ from old\_string)
-   `replace_all` (optional): Replace all occurrences (default: false)

**Requirements**:

-   MUST read file with Read tool first
-   `old_string` must be unique in file (unless using `replace_all`)
-   Preserve exact indentation from Read output (exclude line number prefix)

**Line Number Prefix Format**:

```
[spaces][line_number][tab][actual_content]
```

Only include `[actual_content]` in old\_string/new\_string.

**Examples**:

Simple replacement:

```
{
  "file_path": "/path/to/file.ts",
  "old_string": "const port = 3000;",
  "new_string": "const port = 4321;"
}
```

Multi-line replacement:

```
{
  "file_path": "/path/to/component.tsx",
  "old_string": "export default function Header() {\n  return <h1>Old Title</h1>;\n}",
  "new_string": "export default function Header() {\n  return <h1>New Title</h1>;\n}"
}
```

Replace all occurrences (renaming):

```
{
  "file_path": "/path/to/file.ts",
  "old_string": "oldFunctionName",
  "new_string": "newFunctionName",
  "replace_all": true
}
```

**Common Mistakes to Avoid**:

-   ❌ Including line numbers in old\_string: `"25\tconst x = 5;"`
-   ✅ Correct: `"const x = 5;"`
-   ❌ Wrong indentation: `" function foo()"` when file has tabs
-   ✅ Match exact whitespace from Read output

___

### Write[](https://www.vtrivedy.com/posts/claudecode-tools-reference#write)

**Purpose**: Write or overwrite files.

**Parameters**:

-   `file_path` (required): Absolute path to file
-   `content` (required): Complete file content

**Requirements**:

-   For existing files: MUST read with Read tool first
-   Prefer Edit over Write for existing files
-   Only create new files when absolutely necessary

**Examples**:

Create new file:

```
{
  "file_path": "/path/to/new/file.ts",
  "content": "export const API_URL = 'https://api.example.com';\n"
}
```

Overwrite existing file:

```
{
  "file_path": "/path/to/existing/config.json",
  "content": "{\n  \"version\": \"2.0\",\n  \"enabled\": true\n}\n"
}
```

___

## 8\. NotebookEdit[](https://www.vtrivedy.com/posts/claudecode-tools-reference#8-notebookedit)

**Purpose**: Edit Jupyter notebook (.ipynb) cells.

**Parameters**:

-   `notebook_path` (required): Absolute path to notebook
-   `new_source` (required): New cell content
-   `cell_id` (optional): ID of cell to edit (for insert: new cell inserted after this ID)
-   `cell_type` (optional): “code” or “markdown” (required for insert mode)
-   `edit_mode` (optional): “replace” (default), “insert”, “delete”

**Examples**:

Replace cell content:

```
{
  "notebook_path": "/path/to/notebook.ipynb",
  "cell_id": "abc123",
  "new_source": "import pandas as pd\ndf = pd.read_csv('data.csv')\ndf.head()"
}
```

Insert new code cell:

```
{
  "notebook_path": "/path/to/notebook.ipynb",
  "cell_id": "xyz789",
  "cell_type": "code",
  "edit_mode": "insert",
  "new_source": "# New analysis step\nresult = df.groupby('category').sum()"
}
```

Delete cell:

```
{
  "notebook_path": "/path/to/notebook.ipynb",
  "cell_id": "def456",
  "edit_mode": "delete",
  "new_source": ""
}
```

___

## 9\. WebFetch, 10. WebSearch[](https://www.vtrivedy.com/posts/claudecode-tools-reference#9-webfetch-10-websearch)

### WebFetch[](https://www.vtrivedy.com/posts/claudecode-tools-reference#webfetch)

**Purpose**: Fetch and process web content with AI analysis.

**Parameters**:

-   `url` (required): Fully-formed valid URL
-   `prompt` (required): What information to extract from the page

**Features**:

-   Converts HTML to markdown
-   Auto-upgrades HTTP to HTTPS
-   15-minute cache for repeated requests
-   Handles redirects (requires new request with redirect URL)

**Examples**:

Fetch documentation:

```
{
  "url": "https://docs.astro.build/en/getting-started/",
  "prompt": "Extract the commands needed to create a new Astro project and start the dev server"
}
```

Analyze article:

```
{
  "url": "https://example.com/blog/best-practices",
  "prompt": "Summarize the key best practices mentioned in this article"
}
```

Check API reference:

```
{
  "url": "https://api-docs.example.com/v2/authentication",
  "prompt": "What are the authentication methods supported and their required parameters?"
}
```

**Key Notes**:

-   Prefer MCP-provided web fetch tools if available (start with “mcp\_\_”)
-   Read-only operation
-   Results may be summarized for large content
-   When redirected to different host, make new request with redirect URL

___

### WebSearch[](https://www.vtrivedy.com/posts/claudecode-tools-reference#websearch)

**Purpose**: Search the web for current information beyond Claude’s knowledge cutoff.

**Parameters**:

-   `query` (required): Search query string (min 2 characters)
-   `allowed_domains` (optional): Array of domains to include
-   `blocked_domains` (optional): Array of domains to exclude

**Availability**: US only

**Examples**:

Basic search:

```
{
  "query": "Astro 4.0 new features 2025"
}
```

Search specific domains:

```
{
  "query": "TypeScript best practices",
  "allowed_domains": ["typescript-lang.org", "github.com"]
}
```

Exclude domains:

```
{
  "query": "React hooks tutorial",
  "blocked_domains": ["pinterest.com", "youtube.com"]
}
```

**Key Notes**:

-   Consider current date when forming queries
-   Returns search result blocks
-   Use for information beyond January 2025 knowledge cutoff

___

## 11\. TodoWrite[](https://www.vtrivedy.com/posts/claudecode-tools-reference#11-todowrite)

**Purpose**: Create and manage structured task lists for tracking progress.

**Parameters**:

-   `todos` (required): Array of todo objects with:
    -   `content` (required): Imperative form (e.g., “Run tests”)
    -   `activeForm` (required): Present continuous form (e.g., “Running tests”)
    -   `status` (required): “pending”, “in\_progress”, or “completed”

**When to Use**:

-   Complex multi-step tasks (3+ steps)
-   Non-trivial complex tasks requiring planning
-   User explicitly requests todo list
-   User provides multiple tasks
-   After receiving new instructions (capture requirements)
-   When starting work (mark as in\_progress BEFORE beginning)
-   After completing tasks (mark completed, add follow-ups)

**When NOT to Use**:

-   Single straightforward task
-   Trivial tasks providing no organizational benefit
-   Tasks completable in <3 trivial steps
-   Purely conversational/informational requests

**Critical Rules**:

-   EXACTLY ONE task must be “in\_progress” at any time
-   Mark tasks completed IMMEDIATELY after finishing (no batching)
-   Only mark completed when FULLY accomplished
-   Keep task in\_progress if: tests failing, implementation partial, unresolved errors, missing dependencies
-   Remove irrelevant tasks entirely

**Examples**:

Create initial todo list:

```
{
  "todos": [
    {
      "content": "Search codebase for authentication logic",
      "activeForm": "Searching codebase for authentication logic",
      "status": "in_progress"
    },
    {
      "content": "Implement OAuth2 flow",
      "activeForm": "Implementing OAuth2 flow",
      "status": "pending"
    },
    {
      "content": "Add authentication tests",
      "activeForm": "Adding authentication tests",
      "status": "pending"
    }
  ]
}
```

Update progress:

```
{
  "todos": [
    {
      "content": "Search codebase for authentication logic",
      "activeForm": "Searching codebase for authentication logic",
      "status": "completed"
    },
    {
      "content": "Implement OAuth2 flow",
      "activeForm": "Implementing OAuth2 flow",
      "status": "in_progress"
    },
    {
      "content": "Add authentication tests",
      "activeForm": "Adding authentication tests",
      "status": "pending"
    }
  ]
}
```

___

## 12\. ExitPlanMode[](https://www.vtrivedy.com/posts/claudecode-tools-reference#12-exitplanmode)

**Purpose**: Exit plan mode after presenting an implementation plan.

**Parameters**:

-   `plan` (required): The implementation plan (supports markdown)

**When to Use**:

-   ONLY for tasks requiring code implementation planning
-   After finishing planning implementation steps

**When NOT to Use**:

-   Research tasks (gathering information, searching, reading, understanding codebase)

**Example**:

```
{
  "plan": "## Implementation Plan\n\n1. Create authentication middleware in `src/middleware/auth.ts`\n2. Add JWT verification logic\n3. Protect API routes with middleware\n4. Add tests for authentication flow\n\nShall I proceed with this plan?"
}
```

___

## 13\. BashOutput, 14. KillShell[](https://www.vtrivedy.com/posts/claudecode-tools-reference#13-bashoutput-14-killshell)

### BashOutput[](https://www.vtrivedy.com/posts/claudecode-tools-reference#bashoutput)

**Purpose**: Retrieve output from running or completed background bash shells.

**Parameters**:

-   `bash_id` (required): ID of the background shell
-   `filter` (optional): Regular expression to filter output lines

**Returns**:

-   Only new output since last check
-   stdout and stderr
-   Shell status

**Example**:

```
{
  "bash_id": "shell_12345",
  "filter": "ERROR|WARN"
}
```

**Key Notes**:

-   Find shell IDs with `/bashes` command
-   Filtered lines are consumed (no longer available to read)

___

### KillShell[](https://www.vtrivedy.com/posts/claudecode-tools-reference#killshell)

**Purpose**: Terminate a running background bash shell.

**Parameters**:

-   `shell_id` (required): ID of shell to terminate

**Example**:

```
{
  "shell_id": "shell_12345"
}
```

___

## 15\. SlashCommand[](https://www.vtrivedy.com/posts/claudecode-tools-reference#15-slashcommand)

**Purpose**: Execute slash commands within the conversation.

**Parameters**:

-   `command` (required): Slash command with arguments (e.g., “/review-pr 123”)

**Restrictions**:

-   Only available commands can be executed
-   Don’t use if command with same name is already running
-   Check command-message tags for running status

**Example**:

___

### Batch Operations[](https://www.vtrivedy.com/posts/claudecode-tools-reference#batch-operations)

**Always batch independent tool calls in a single response for optimal performance.**

Example - Multiple file reads:

```
Call Read on file1.ts, file2.ts, file3.ts in single message
```

Example - Parallel git commands:

```
Call Bash("git status") and Bash("git diff") in single message
```

### File Operations Workflow[](https://www.vtrivedy.com/posts/claudecode-tools-reference#file-operations-workflow)

1.  **Search for files**: Use Glob (not find/ls)
2.  **Search content**: Use Grep (not bash grep/rg)
3.  **Read files**: Use Read (not cat/head/tail)
4.  **Edit files**: Use Edit (not sed/awk)
5.  **Write files**: Use Write (not echo >/heredoc)

### Complex Task Workflow[](https://www.vtrivedy.com/posts/claudecode-tools-reference#complex-task-workflow)

1.  Use TodoWrite to plan and track tasks
2.  Use Task agent for multi-step research
3.  Use Bash for terminal operations only
4.  Batch independent operations together
5.  Mark todos completed immediately after finishing

### Background Process Management[](https://www.vtrivedy.com/posts/claudecode-tools-reference#background-process-management)

1.  Start process: `Bash` with `run_in_background: true`
2.  Monitor output: `BashOutput` with bash\_id
3.  Filter if needed: Use `filter` parameter with regex
4.  Terminate when done: `KillShell` with shell\_id

### Searching Strategy[](https://www.vtrivedy.com/posts/claudecode-tools-reference#searching-strategy)

-   **Known file paths**: Use Read directly
-   **File name patterns**: Use Glob
-   **Content in specific files**: Read then search locally
-   **Content across codebase**: Use Grep
-   **Complex multi-round search**: Use Task with general-purpose agent

### Communication[](https://www.vtrivedy.com/posts/claudecode-tools-reference#communication)

-   Output text directly to user (not bash echo)
-   Use markdown formatting for readability
-   Include file references as links: `[filename.ts](src/filename.ts)` or `[filename.ts:42](src/filename.ts#L42)`
-   Be concise and direct (avoid preamble/postamble)

___

## Examples: Complete Workflows[](https://www.vtrivedy.com/posts/claudecode-tools-reference#examples-complete-workflows)

### Workflow 1: Add New Feature[](https://www.vtrivedy.com/posts/claudecode-tools-reference#workflow-1-add-new-feature)

```
1. TodoWrite: Plan the feature tasks
2. Grep: Find related existing implementations
3. Read: Read relevant files (batch multiple)
4. Edit: Modify existing files (or Write for new files)
5. Bash: Run tests
6. TodoWrite: Mark tasks completed
7. Bash: Commit changes (if requested)
```

### Workflow 2: Debug Issue[](https://www.vtrivedy.com/posts/claudecode-tools-reference#workflow-2-debug-issue)

```
1. TodoWrite: Create debugging task list
2. Grep: Search for error messages/patterns
3. Read: Read files with issues (batch multiple)
4. Bash: Run failing tests/reproduce issue
5. Edit: Apply fixes
6. Bash: Verify fix with tests
7. TodoWrite: Mark completed
```

### Workflow 3: Research Codebase[](https://www.vtrivedy.com/posts/claudecode-tools-reference#workflow-3-research-codebase)

```
1. Task: Launch general-purpose agent with research prompt
   (Agent will autonomously use Grep, Glob, Read to explore)
2. Agent returns findings
3. Present summary to user
```

### Workflow 4: Refactor Code[](https://www.vtrivedy.com/posts/claudecode-tools-reference#workflow-4-refactor-code)

```
1. TodoWrite: Break down refactoring tasks
2. Grep: Find all occurrences of code to refactor
3. Read: Read affected files (batch multiple)
4. Edit: Apply refactoring with replace_all for renames
5. Bash: Run tests and type checking
6. TodoWrite: Mark tasks completed
```

___

| Task | Use This Tool | NOT This |
| --- | --- | --- |
| Find files by name | Glob | Bash(find/ls) |
| Search content | Grep | Bash(grep/rg) |
| Read file | Read | Bash(cat/head/tail) |
| Edit file | Edit | Bash(sed/awk) |
| Create file | Write | Bash(echo >) |
| Run tests/builds | Bash | N/A |
| Multi-step research | Task | Multiple manual searches |
| Track complex tasks | TodoWrite | Comments/memory |
| Fetch web content | WebFetch | Bash(curl) |
| Search web | WebSearch | WebFetch with search engine |
| Background process | Bash(run\_in\_background) + BashOutput | Bash with & |

___

## Common Antipatterns to Avoid[](https://www.vtrivedy.com/posts/claudecode-tools-reference#common-antipatterns-to-avoid)

❌ **Don’t**: Use bash for file operations

✅ **Do**: Use Read tool

```
{"file_path": "src/config.ts"}
```

❌ **Don’t**: Use echo for communication

```
echo "Now processing files..."
```

✅ **Do**: Output text directly to user

❌ **Don’t**: Make sequential tool calls when independent

```
Call Read on file1, wait, then Read on file2, wait, then Read on file3
```

✅ **Do**: Batch independent calls

```
Call Read on file1, file2, file3 in single message
```

❌ **Don’t**: Forget to use TodoWrite for complex tasks

```
Start working without planning
```

✅ **Do**: Plan first with TodoWrite

❌ **Don’t**: Mark todos completed before actually finishing

```
{"status": "completed"} // but tests are failing
```

✅ **Do**: Only mark completed when fully done

```
{"status": "in_progress"} // keep working until tests pass
```

___

That’s all for now, I’ll try to update this guide with more tools or useful patterns as they come out. But really this is mostly just a reference to look back on when I need to.


---
## claude-code-by-anthropic-ai-coding-agent-terminal-ide-550631f98b.md

# Claude Code

Hand Claude a bug fix, test, or multi-day migration. Steer and review from your terminal, IDE, Slack, or web.

Claude Code is included in Claude Pro and Max plans. [See plans](#pricing)

[Download for macOS](https://claude.ai/api/desktop/darwin/universal/dmg/latest/redirect)[Read documentation](https://code.claude.com/docs/en/overview)

(opens in new tab)

Available for macOS, Linux, and Windows.

Other ways to use Claude Code:

- [Terminal (opens in new tab)](https://code.claude.com/docs/en/quickstart#step-1-install-claude-code)
- [Web (opens in new tab)](https://claude.ai/code)
- [iOS (opens in new tab)](https://apps.apple.com/us/app/claude-by-anthropic/id6473753684)
- [Android (opens in new tab)](https://play.google.com/store/apps/details?id=com.anthropic.claude)
- [GitHub (opens in new tab)](https://code.claude.com/docs/en/github-actions)
- [VS Code (opens in new tab)](https://marketplace.visualstudio.com/items?itemName=anthropic.claude-code)
- [JetBrains (opens in new tab)](https://plugins.jetbrains.com/plugin/27310-claude-code-beta-)
- [Slack (opens in new tab)](https://slack.com/oauth/v2/authorize?client_id=1601185624273.8899143856786&scope=app_mentions:read,assistant:write,channels:history,channels:read,chat:write,files:read,files:write,groups:history,groups:read,im:history,im:read,im:write,mpim:history,reactions:write,users:read,users:read.email,commands,search:read.public&user_scope=bookmarks:read,channels:history,channels:read,chat:write,emoji:read,files:read,groups:history,groups:read,groups:write,im:history,im:read,im:write,links:read,mpim:history,mpim:read,mpim:write,mpim:write.topic,pins:read,reactions:read,reactions:write,remote_files:read,team:read,users:read,users:read.email,search:read.public,search:read.private,search:read.im,search:read.mpim,search:read.files,search:read.users,canvases:read,canvases:write)

Home

Code

New session

Routines

Customize

More

Pinned

Add a dark mode toggle to settings

Scheduled

Weekly dependency audit

Recents

Fix the double-charge bug in checkout

Write tests for the payments module

Explain what this repo does

Layout's broken on mobile

Debug flaky CI tests

Susan Sample

acme-storefront/Fix the double-charge bug in checkout

We're seeing duplicate charges when customers double-click the pay button. Can you find and fix it?

I can reproduce it: the checkout form fires two `POST /charges` requests when the button is clicked twice before the first response lands. Tracing where the idempotency key should be.

just now

Read 3 files, searched the checkout flow

Ran agent · Reproduce the double-submit against a test charge

Root cause: `createCharge()` generates a new idempotency key per call instead of per checkout session, so the gateway treats the second click as a fresh charge. Fixing that and disabling the button while a charge is in flight.

just now

Edited **charges.ts** +9 -3

Auto

Opus

Extra high

Home

Code

New session

Routines

Customize

More

Pinned

Add a dark mode toggle to settings

Scheduled

Weekly dependency audit

Recents

Fix the double-charge bug in checkout

Write tests for the payments module

Explain what this repo does

Layout's broken on mobile

Debug flaky CI tests

Susan Sample

acme-dashboard/Add a dark mode toggle to settings

Add a dark mode toggle to the settings page. Should remember the choice and follow the system default until they pick one.

There's already a `ThemeProvider` wrapping the app but it's hard-wired to light. I'll lift the mode into context, persist it to `localStorage`, and add a Light / Dark segmented control under **Settings → Appearance**.

just now

Read 4 files, searched the codebase

Edited **ThemeProvider.tsx** +18 -2

src/theme/ThemeProvider.tsx

10`export`` ``function`` ``ThemeProvider``({``children``}:`` ``{``children``:`` ``ReactNode``})`` ``{`

11+` ``const`` ``prefersDark`` ``=`` ``useMediaQuery``('(``prefers``-``color``-``scheme``:`` ``dark``)')`

12` ``const`` ``stored`` ``=`` ``localStorage``.``getItem``('``theme``')`

13+` ``const`` ``[``mode``,`` ``setMode``]`` ``=`` ``useState``(``stored`` ``??`` ``(``prefersDark`` ``?`` ``'dark'`` ``:`` ``'light'``))`

14` `

15+` ``useEffect``(()`` ``=>`` ``{`

16+` ``localStorage``.``setItem``('``theme``',`` ``mode``)`

17+` ``},`` ``[``mode``])`

18` `

19` ``return`` ``(`

20` ``<``ThemeContext``.``Provider`` ``value``={{``mode``,`` ``setMode``}}>`

21` ``{``children``}`

Edited **settings.tsx** +21 -0

Edited **tokens.css** +24 -0

Ran 2 commands

Done — the **Appearance** section now has the toggle. Preview's on the right; flip it and the whole app should follow. Reduce-motion and density rows are stubbed so the page doesn't look bare.

just now

\#112acme-dashboardclaude/settings-dark-mode-Xk4fq CI

Auto

Opus

Extra high

localhost:5173/settings

Appearance

Theme

Choose how the dashboard looks.

Light

Dark

Density

Compact rows for power users.

Default

Compact

Reduce motion

Disable animations and transitions.

- 

- 

- 

- 

- 

- 

- 

- 

- 

- 

- 

- 

- 

- 

- 

- 

- 

- 

## Get started with Claude Code

Individual

Team & Enterprise

### Pro

Claude Code is included in your Pro plan. Perfect for short coding sprints in small codebases.

\$17

Per month with annual subscription discount (\$200 billed up front). \$20 if billed monthly.

[Try Claude (opens in new tab)](https://claude.ai/login?plan=pro)

### Max 5x

Claude Code is included in your Max plan. Great value for everyday use in larger codebases.

\$100

Per month

[Try Claude (opens in new tab)](https://claude.ai/login?plan=max)

### Max 20x

Even more Claude Code included in your Max plan. Great value for power users with the most access to Claude models.

\$200

Per month

[Try Claude (opens in new tab)](https://claude.ai/login?plan=max)

[Usage limits apply](https://support.anthropic.com/en/articles/9797557-usage-limit-best-practices). Prices shown don't include applicable tax. Price and plans are subject to change at Anthropic's discretion.

[Usage limits apply](https://support.anthropic.com/en/articles/9797557-usage-limit-best-practices). Price and plans are subject to change at Anthropic's discretion.

## Latest feature announcements


### **Projects:** Group related coding sessions so you can run and easily supervise multiple Claude agents at once. Available on Claude Code Desktop.

BlogSep 17, 2026


### **Auto mode by default:** Claude Code now runs in auto mode by default on Pro, Max, and Team plans, so it can work longer while still catching risky commands.

BlogAug 7, 2026


### **Self-hosted environments:** Run Claude Code sessions on your own infrastructure, inside your network and next to your internal services. Now in public beta.

BlogAug 6, 2026


### **Artifacts:** Preview in-progress work as a live, interactive artifact built from your session context, and share it with your team.

BlogJun 18, 2026

[View changelog](https://code.claude.com/docs/en/changelog)

(opens in new tab)

## What Claude Code can take on

Claude Code builds the plan, asks clarifying questions, and handles work that runs for hours or days.

Get Claude Code

- [Desktop](/download)
- [VS Code](https://marketplace.visualstudio.com/items?itemName=anthropic.claude-code)
- [JetBrains](https://plugins.jetbrains.com/plugin/27310-claude-code-beta-)
- [On the web](https://claude.ai/code)
- [Slack](https://slack.com/oauth/v2/authorize?client_id=1601185624273.8899143856786&scope=app_mentions:read,assistant:write,channels:history,channels:read,chat:write,files:read,files:write,groups:history,groups:read,im:history,im:read,im:write,mpim:history,reactions:write,users:read,users:read.email,commands,search:read.public&user_scope=bookmarks:read,channels:history,channels:read,chat:write,emoji:read,files:read,groups:history,groups:read,groups:write,im:history,im:read,im:write,links:read,mpim:history,mpim:read,mpim:write,mpim:write.topic,pins:read,reactions:read,reactions:write,remote_files:read,team:read,users:read,users:read.email,search:read.public,search:read.private,search:read.im,search:read.mpim,search:read.files,search:read.users,canvases:read,canvases:write)

curl -fsSL https://claude.ai/install.sh \| bash

Or read the [documentation](https://code.claude.com/docs/en/overview)

[Try Claude Code (opens in new tab)](https://claude.ai/code)[Developer docs (opens in new tab)](https://code.claude.com/docs/en/overview)

Onboarding

Triage issues

Migrate code

``` Terminal-module-scss-module__gNncDq__pre
I'm new to this codebase. Can you explain it to me?

⏺︎ I'll analyze the Excalidraw codebase structure and provide a high-level overview.

⏺︎ Analyze(what: "Excalidraw codebase structure and provide a high-level overview of what it is, its main components, and how they're organized. Focus on understanding the project's purpose, architecture, and key features.")

Excalidraw is an open-source virtual whiteboard application with a hand-drawn style. It's designed for creating diagrams, wireframes, and collaborative drawings.

## Purpose
- Web-based drawing tool with infinite canvas
- Hand-drawn aesthetic with support for shapes, arrows, text
- End-to-end encrypted collaboration
- Works offline as a PWA (Progressive Web App)

## Architecture
The project is organized as a monorepo with these main components:

1. Core Package (packages/excalidraw)
- React component library for embedding Excalidraw in any application
- Handles drawing, rendering, and core functionality
- Uses roughjs for the hand-drawn style
- Includes export capabilities (PNG, SVG)

2. Web Application (excalidraw-app/)
- Full-featured web app at excalidraw.com
- Built on top of the core package
- Adds collaboration features, local storage, sharing

3. Support Packages
- common - Shared utilities and constants
- element - Element manipulation and type definitions
- math - Mathematical functions for drawing
- utils - General utility functions

4. Examples
- Integration examples (Next.js, browser script)
- Shows how to embed Excalidraw in different environments

5. Documentation (dev-docs/)
- API documentation
- Integration guides
- Component documentation

## Key Features
- Real-time collaboration with end-to-end encryption
- Export to various formats (PNG, SVG, JSON)
- Customizable drawing tools (rectangle, circle, arrows, freehand)
- Dark mode support
- Localization
- Library of shapes and components
- Arrow bindings and labels
- Image support

## Technology Stack
- React for UI
- TypeScript for type safety
- Firebase for collaboration backend
- Jotai for state management
- Vite for development and building
- IndexedDB/LocalStorage for offline storage

The architecture allows for both using Excalidraw as a component in other applications and as a standalone web application with additional features.
```

- ### Code onboarding

  Claude Code maps and explains entire codebases in a few seconds. It uses agentic search to understand project structure and dependencies without you having to manually select context files.

- ### Turn issues into PRs

  Stop bouncing between tools. Claude Code works with GitHub, GitLab, and your command line tools to read issues, write code, run tests, and open pull requests.

- ### Run multi-hour refactors and migrations

  Claude Code follows imports across the repo, runs your tests, and keeps going when something breaks.

## Meets you where you code

### Start in your terminal

Super powerful terminal integration. Works with all your CLI tools alongside any IDE.

curl -fsSL https://claude.ai/install.sh \| bash

Or read the [documentation](https://code.claude.com/docs/en/overview)

[Try Claude Code (opens in new tab)](https://claude.ai/code)[Developer docs (opens in new tab)](https://code.claude.com/docs/en/overview)

### Integrate with your editor

Native extensions for VS Code (+ Cursor, Devin Desktop) and JetBrains IDEs.

[VS Code (opens in new tab)](https://marketplace.visualstudio.com/items?itemName=anthropic.claude-code)[JetBrains (opens in new tab)](https://plugins.jetbrains.com/plugin/27310-claude-code-beta-)

### Access anywhere

Quick access from browser, mobile app, or Claude on desktop. Great for parallel work or on-the-go coding.

[Open in browser (opens in new tab)](https://claude.ai/code)[Download app](/download)

## Kick off coding tasks in Slack

[Add to Slack (opens in new tab)](https://slack.com/oauth/v2/authorize?client_id=1601185624273.8899143856786&scope=app_mentions:read,assistant:write,channels:history,channels:read,chat:write,files:read,files:write,groups:history,groups:read,im:history,im:read,im:write,mpim:history,reactions:write,users:read,users:read.email,commands,search:read.public&user_scope=bookmarks:read,channels:history,channels:read,chat:write,emoji:read,files:read,groups:history,groups:read,groups:write,im:history,im:read,im:write,links:read,mpim:history,mpim:read,mpim:write,mpim:write.topic,pins:read,reactions:read,reactions:write,remote_files:read,team:read,users:read,users:read.email,search:read.public,search:read.private,search:read.im,search:read.mpim,search:read.files,search:read.users,canvases:read,canvases:write)[Learn more](/claude-for-slack)

## What developers are saying

- 

  > “Claude Code has dramatically accelerated our team's coding efficiency. I can now write EDA code in a notebook—pulling data, training a model, and evaluating it with basic metrics—and then ask Claude to convert that into a Metaflow pipeline. This process saves 1-2 days of routine (and often boring!) work per model.”

  Anton Biryukov, Staff Software Engineer

  [Read story](/customers/ramp)

- 

  > “With Claude, we're not just automating customer service—we're elevating it to truly human quality. This lets support teams think more strategically about customer experience and what makes interactions genuinely valuable.”

  Fergal Reid, VP of AI

  [Read story](/customers/intercom)

- 

  > “Claude Code is moving our team up a level: we decide what needs to happen, and smooth the process so it can build and verify end-to-end. A big part of my job now is to keep as many instances of Claude Code busy as possible.”

  Simon Last, Co-founder

  [Read story](/customers/notion)

- 

- 

- 

- 

- 

- 

- 

- 

- 

- 

- 

- 

- 

- 

- 

## Connects with your favorite command line tools

Your terminal is where real work happens. Claude Code connects with the tools that power development—deployment, databases, monitoring, version control. Rather than adding another interface to juggle, it enhances your existing stack.

## FAQ

### How do I get started with Claude?

You can access Claude Code with a Claude Pro or Max plan, a Team or Enterprise plan, or a Claude Console account. [Download Claude Code](https://code.claude.com/docs/en/overview) and sign in with your respective Claude or Console credentials.

### What kind of tasks can Claude Code handle?

Claude Code can handle routine work like bug fixes and testing, and larger jobs like refactors and new features that are long-running and asynchronous.

You set the direction as the architect and orchestrator, and Claude Code does the work. Describe what you want and it plans, writes code, runs tests, and opens pull requests. It can work on several tasks at once, and surface decisions you need to make for it to keep going.

### How does Claude Code work with my existing tools?

Claude Code runs in your terminal and works alongside your preferred IDE and development tools without requiring you to change your workflow. Claude Code can also use command line tools (like Git) and MCP servers (like GitHub) to extend its own capabilities using your tools.

### Is Claude Code secure?

Yes. Claude Code runs locally in your terminal and talks directly to model APIs without requiring a backend server or remote code index. It also asks for permission before making changes to your files or running commands.

### What are the system requirements to run Claude Code?

Claude Code works on macOS, Linux, and Windows. [See full system requirements](https://code.claude.com/docs/en/setup#system-requirements).

### How much does Claude Code cost?

When used with a Claude Console account, Claude Code consumes API tokens at [standard API pricing](https://claude.com/pricing#api).

### Does Claude Code work with the Claude desktop app?

Yes. Max, Pro, Team, and Enterprise users can access Claude Code on the [Claude desktop app](https://claude.com/download).

### What is fast mode on Claude Code?

Fast mode is a high-speed configuration for Opus 5.5, making the model 2.5x faster at a higher cost per token. Fast mode is available:

- In research preview on Claude Code, and is priced at \$8/\$40 per million tokens.
- On consumption-based plans.
- Via usage credits for users on subscription plans.

## Get the technical rundown


### Claude Code documentation

Developer docs


### Common workflows

Developer docs


### Using CLAUDE.md files

Blog


### Introduction to agentic coding

Blog


### How Anthropic teams use Claude Code

Blog


### Fix software bugs faster with Claude

Blog

## Create what’s exciting. Maintain what’s essential.

Use Claude Code where you work

Get Claude Code

- [Desktop](/download)
- [VS Code](https://marketplace.visualstudio.com/items?itemName=anthropic.claude-code)
- [JetBrains](https://plugins.jetbrains.com/plugin/27310-claude-code-beta-)
- [On the web](https://claude.ai/code)
- [Slack](https://slack.com/oauth/v2/authorize?client_id=1601185624273.8899143856786&scope=app_mentions:read,assistant:write,channels:history,channels:read,chat:write,files:read,files:write,groups:history,groups:read,im:history,im:read,im:write,mpim:history,reactions:write,users:read,users:read.email,commands,search:read.public&user_scope=bookmarks:read,channels:history,channels:read,chat:write,emoji:read,files:read,groups:history,groups:read,groups:write,im:history,im:read,im:write,links:read,mpim:history,mpim:read,mpim:write,mpim:write.topic,pins:read,reactions:read,reactions:write,remote_files:read,team:read,users:read,users:read.email,search:read.public,search:read.private,search:read.im,search:read.mpim,search:read.files,search:read.users,canvases:read,canvases:write)

curl -fsSL https://claude.ai/install.sh \| bash

Or read the [documentation](https://code.claude.com/docs/en/overview)

[Try Claude Code (opens in new tab)](https://claude.ai/code)[Developer docs (opens in new tab)](https://code.claude.com/docs/en/overview)


---
## claude-code-champion-kit-4934ff2d44.md

# Claude Code champion kit

A guide for internal champions driving adoption

April 15, 2026

Copy for LLM

Adoption of a new developer tool rarely happens just 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. This kit is designed to support that effort without turning it into a second job. It gives shape to things you are likely already doing and provides material you can hand directly to colleagues.

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. You are acting as a multiplier for your team, not as a help desk, and this guide is structured to keep the role sustainable on those terms.

## How to use this guide

The champion role consists of three behaviors that reinforce one another: sharing what you discover, being the person people ask, and growing the circle of active users. The sections below cover each in turn, followed by a thirty-day playbook, guidance for responding to common concerns, and a quick-reference sheet you can hand to anyone.

Use whatever fits your team and set aside whatever does not. Nothing here is a checklist you are expected to complete; it is a set of patterns that have worked across many engineering organizations.

------------------------------------------------------------------------

## Phase 1: The champion role

### What a champion does

[TABLE]

Most of this fits naturally inside the work you are already doing. The difference is a small amount of additional intention about where your discoveries are posted and how your answers travel.

### Why this matters

Tools spread inside an organization when someone trusted demonstrates that they are worth the effort. Documentation can describe what is possible, but a colleague’s example—drawn from the same codebase, the same workflows, and the same constraints—is what moves people from curiosity to a first attempt. By making your own experience visible, you remove the most common reason adoption stalls: not knowing where to start.

### What this should cost you

It is worth setting 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.

[TABLE]

------------------------------------------------------------------------

## Phase 2: 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.

[TABLE]

### 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 new destination.

[TABLE]

### 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.

### Example posts

The following are illustrations of tone and length rather than templates to copy verbatim.

*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.*

*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.*

*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.*

------------------------------------------------------------------------

## Phase 3: 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.

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

The table below covers the questions champions are asked most frequently, along with a suggested response and the resource to offer when the person is ready for more depth.

[TABLE]

------------------------------------------------------------------------

## Phase 4: 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

[TABLE]

### A 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.

[TABLE]

### 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 official **[Quickstart](https://code.claude.com/docs/en/quickstart)** and **[Common workflows](https://code.claude.com/docs/en/common-workflows)** pages. They contain short sections covering the features that are genuinely useful but difficult to discover on your own.

------------------------------------------------------------------------

## Phase 5: Responding to common concerns

Healthy skepticism is to be expected; engineers should be cautious about new tools. 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.

[TABLE]

------------------------------------------------------------------------

## Appendix: Quick-reference sheet

The techniques below are the ones that most reliably move someone from a first trial to daily use. This table is intended to be pinned in a channel or shared on its own.

[TABLE]

------------------------------------------------------------------------

## Appendix: Resource directory

[TABLE]

Thank you for taking on this role. People adopt new tools because someone they trust showed them it was worth the effort, and that is the contribution you are making. Claude Code is updated frequently; please verify version-specific details against **[code.claude.com/docs](https://code.claude.com/docs)** before distributing this material internally.


---
*Truncated: 291 more documents not shown (size limit)*
