SpyBara
Go Premium

Documentation 2026-09-30 23:00 UTC to 2026-10-01 21:59 UTC

74 files changed +4,069 −256. View all changes and history on the product overview
2026
Thu 1 21:59

admin-setup.md +1 −0

Details

98| [Disable claude.ai sync](/docs/en/settings-reference#syncclaudeaiskills) | Stop Claude Code from loading the [skills](/docs/en/skills#how-synced-skills-behave) and [plugins](/docs/en/plugins/loading#synced-plugins) your developers enable on claude.ai. If you turn off Skills for your organization on claude.ai, Claude Code stops syncing both, and on v2.1.273 or later it also removes the ones it already synced. To stop either one without turning Skills off, set its key to `false` in managed settings | `syncClaudeAiSkills`, `syncClaudeAiPlugins` |98| [Disable claude.ai sync](/docs/en/settings-reference#syncclaudeaiskills) | Stop Claude Code from loading the [skills](/docs/en/skills#how-synced-skills-behave) and [plugins](/docs/en/plugins/loading#synced-plugins) your developers enable on claude.ai. If you turn off Skills for your organization on claude.ai, Claude Code stops syncing both, and on v2.1.273 or later it also removes the ones it already synced. To stop either one without turning Skills off, set its key to `false` in managed settings | `syncClaudeAiSkills`, `syncClaudeAiPlugins` |

99| [Hook restrictions](/docs/en/settings-reference#allowmanagedhooksonly) | Restrict which hooks run and restrict HTTP hook URLs; see [what runs under `allowManagedHooksOnly`](/docs/en/settings-reference#what-runs-under-allowmanagedhooksonly) for the full effect list | `allowManagedHooksOnly`, `allowedHttpHookUrls` |99| [Hook restrictions](/docs/en/settings-reference#allowmanagedhooksonly) | Restrict which hooks run and restrict HTTP hook URLs; see [what runs under `allowManagedHooksOnly`](/docs/en/settings-reference#what-runs-under-allowmanagedhooksonly) for the full effect list | `allowManagedHooksOnly`, `allowedHttpHookUrls` |

100| [Login enforcement](/docs/en/settings-reference#forceloginmethod) | Restrict login to a specific method or Anthropic organization. The method restriction applies across the VS Code extension, Agent SDK, `claude setup-token`, and `/install-github-app`, and the terminal's interactive login screen, reached by `/login` or first-run onboarding, pre-selects the method without enforcing it; Claude Code verifies the organization for claude.ai account logins in the terminal, VS Code extension, and Agent SDK, and doesn't check it for Claude Console logins or for [gateway](/docs/en/claude-apps-gateway) sign-in. Before v2.1.212, only terminal logins applied either key. When set, sessions authenticated by `ANTHROPIC_API_KEY`, `ANTHROPIC_AUTH_TOKEN`, or `apiKeyHelper` are blocked at startup; cloud provider sessions aren't affected unless one of those credentials, or an API key saved by an earlier Claude Console login, is also present | `forceLoginMethod`, `forceLoginOrgUUID` |100| [Login enforcement](/docs/en/settings-reference#forceloginmethod) | Restrict login to a specific method or Anthropic organization. The method restriction applies across the VS Code extension, Agent SDK, `claude setup-token`, and `/install-github-app`, and the terminal's interactive login screen, reached by `/login` or first-run onboarding, pre-selects the method without enforcing it; Claude Code verifies the organization for claude.ai account logins in the terminal, VS Code extension, and Agent SDK, and doesn't check it for Claude Console logins or for [gateway](/docs/en/claude-apps-gateway) sign-in. Before v2.1.212, only terminal logins applied either key. When set, sessions authenticated by `ANTHROPIC_API_KEY`, `ANTHROPIC_AUTH_TOKEN`, or `apiKeyHelper` are blocked at startup; cloud provider sessions aren't affected unless one of those credentials, or an API key saved by an earlier Claude Console login, is also present | `forceLoginMethod`, `forceLoginOrgUUID` |

101| [Provider restrictions](/docs/en/settings-reference#allowedproviders) | Limit which API providers a machine may use. A session on a provider that isn't listed is refused at startup, at login, and when it next contacts the API. Requires Claude Code v2.1.285 or later | `allowedProviders` |

101| [Disable agent view](/docs/en/agent-view#how-background-sessions-are-hosted) | Turn off `claude agents`, `--bg`, `/background`, and the on-demand supervisor | `disableAgentView` |102| [Disable agent view](/docs/en/agent-view#how-background-sessions-are-hosted) | Turn off `claude agents`, `--bg`, `/background`, and the on-demand supervisor | `disableAgentView` |

102| [Configure the corporate launcher](/docs/en/corporate-launcher) | Prefix the [background-agent supervisor](/docs/en/agent-view#how-background-sessions-are-hosted), its workers, and the [other covered background processes](/docs/en/corporate-launcher#what-the-launcher-covers) with a required corporate launcher instead of turning agent view off | `processWrapper` |103| [Configure the corporate launcher](/docs/en/corporate-launcher) | Prefix the [background-agent supervisor](/docs/en/agent-view#how-background-sessions-are-hosted), its workers, and the [other covered background processes](/docs/en/corporate-launcher#what-the-launcher-covers) with a required corporate launcher instead of turning agent view off | `processWrapper` |

103| [Model restrictions](/docs/en/model-config#restrict-model-selection) | `availableModels` filters which models appear in the picker. Adding `enforceAvailableModels` also constrains the auto-selected default model. See [surface coverage](/docs/en/model-config#surface-coverage) for how this setting reaches the CLI, web, and IDE | `availableModels`, `enforceAvailableModels` |104| [Model restrictions](/docs/en/model-config#restrict-model-selection) | `availableModels` filters which models appear in the picker. Adding `enforceAvailableModels` also constrains the auto-selected default model. See [surface coverage](/docs/en/model-config#surface-coverage) for how this setting reaches the CLI, web, and IDE | `availableModels`, `enforceAvailableModels` |

Details

291 291 

292A few strategies for long-running agents:292A few strategies for long-running agents:

293 293 

294* **Use subagents for subtasks.** Each subagent starts with a fresh conversation (no prior message history, though it does load its own system prompt and project-level context like CLAUDE.md). It does not see the parent's turns, and only its final response returns to the parent as a tool result. The main agent's context grows by that summary, not by the full subtask transcript. See [What subagents inherit](/docs/en/agent-sdk/subagents#what-subagents-inherit) for details.294* **Use subagents for subtasks.** Each subagent starts with a fresh conversation (no prior message history, though it does load its own system prompt and project-level context like CLAUDE.md). It does not see the parent's turns, and only its final response returns to the parent. The main agent's context grows by that summary, not by the full subtask transcript. See [What subagents inherit](/docs/en/agent-sdk/subagents#what-subagents-inherit) for details.

295* **Be selective with tools.** Every tool definition takes context space. Use the `tools` field on [`AgentDefinition`](/docs/en/agent-sdk/subagents#agentdefinition-configuration) to scope subagents to the minimum set they need.295* **Be selective with tools.** Every tool definition takes context space. Use the `tools` field on [`AgentDefinition`](/docs/en/agent-sdk/subagents#agentdefinition-configuration) to scope subagents to the minimum set they need.

296* **Watch MCP server costs.** [MCP tool search](/docs/en/agent-sdk/mcp#mcp-tool-search) defers MCP tool schemas by default and loads them on demand. When tool search is off or has fallen back to upfront loading, each MCP server adds all its tool schemas to every request, so a few servers with many tools can consume significant context before the agent does any work. See [Configure tool search](/docs/en/agent-sdk/tool-search#configure-tool-search) for the configurations where the fallback applies.296* **Watch MCP server costs.** [MCP tool search](/docs/en/agent-sdk/mcp#mcp-tool-search) defers MCP tool schemas by default and loads them on demand. When tool search is off or has fallen back to upfront loading, each MCP server adds all its tool schemas to every request, so a few servers with many tools can consume significant context before the agent does any work. See [Configure tool search](/docs/en/agent-sdk/tool-search#configure-tool-search) for the configurations where the fallback applies.

297* **Use lower effort for routine tasks.** Set [effort](#effort-level) to `"low"` for agents that only need to read files or list directories. This reduces token usage and cost.297* **Use lower effort for routine tasks.** Set [effort](#effort-level) to `"low"` for agents that only need to read files or list directories. This reduces token usage and cost.

Details

396 ```396 ```

397</CodeGroup>397</CodeGroup>

398 398 

399To confirm the block, register the callback under `PreToolUse` with a `Write|Edit` matcher and ask the agent to create a file under `/etc`: the Write tool's result in the message stream contains `Writing to /etc is not allowed`, and no file is created.

400 

399### Auto-approve specific tools401### Auto-approve specific tools

400 402 

401By default, the agent may prompt for permission before using certain tools. This example auto-approves read-only filesystem tools (Read, Glob, Grep) by returning `permissionDecision: 'allow'`, letting them run without user confirmation while leaving all other tools subject to normal permission checks:403By default, the agent may prompt for permission before using certain tools. This example auto-approves read-only filesystem tools (Read, Glob, Grep) by returning `permissionDecision: 'allow'`, letting them run without user confirmation while leaving all other tools subject to normal permission checks:


442 444 

443When an event fires, all matching hooks run in parallel. For permission decisions, the most restrictive result applies: a single `deny` blocks the tool call regardless of what the other hooks return. Because completion order is non-deterministic, write each hook to act independently rather than relying on another hook having run first.445When an event fires, all matching hooks run in parallel. For permission decisions, the most restrictive result applies: a single `deny` blocks the tool call regardless of what the other hooks return. Because completion order is non-deterministic, write each hook to act independently rather than relying on another hook having run first.

444 446 

445The example below registers three independent checks for every tool call:447The example below registers three independent checks for every tool call. The hook names in it, such as `audit_logger` in Python or `auditLogger` in TypeScript, stand in for callbacks you define:

446 448 

447<CodeGroup>449<CodeGroup>

448 ```python Python theme={null}450 ```python Python theme={null}


472 474 

473### Filter with multi-tool matchers475### Filter with multi-tool matchers

474 476 

475Use multi-tool matchers to share one callback across related tools. This example registers three matchers with different scopes:477Use multi-tool matchers to share one callback across related tools. This example registers three matchers with different scopes, and each hook it names stands in for a callback you define:

476 478 

477* A pipe-separated exact list (`Write|Edit|NotebookEdit`) triggers `file_security_hook` only for file modification tools.479* A pipe-separated exact list (`Write|Edit|NotebookEdit`) triggers `file_security_hook` only for file modification tools.

478* A regex (`^mcp__`) triggers `mcp_audit_hook` for any MCP tool whose name starts with `mcp__`.480* A regex (`^mcp__`) triggers `mcp_audit_hook` for any MCP tool whose name starts with `mcp__`.


555 ```557 ```

556</CodeGroup>558</CodeGroup>

557 559 

560To confirm the hook fires, register the callback and ask the agent to delegate a small task to a subagent, such as listing the files in the current directory: when the subagent finishes, the callback prints the `[SUBAGENT] Completed:` lines with the subagent's ID and transcript path.

561 

558### Make HTTP requests from hooks562### Make HTTP requests from hooks

559 563 

560Hooks can perform asynchronous operations like HTTP requests. Catch errors inside your hook instead of letting them propagate.564Hooks can perform asynchronous operations like HTTP requests. Catch errors inside your hook instead of letting them propagate.

Details

115 115 

116Permission modes provide global control over how Claude uses tools. You can set the permission mode when calling `query()` or change it dynamically during streaming sessions.116Permission modes provide global control over how Claude uses tools. You can set the permission mode when calling `query()` or change it dynamically during streaming sessions.

117 117 

118If you don't set one, Claude Code picks the starting permission mode by the rules in [Which mode a session starts in](/docs/en/permission-modes#which-mode-a-session-starts-in):

119 

120* A `permissions.defaultMode` from the session's [settings files](/docs/en/settings#where-settings-live) when one applies

121* Otherwise the built-in default, which can be [auto mode](/docs/en/permission-modes#eliminate-prompts-with-auto-mode)

122 

123A session that starts in auto mode drops broad allow rules such as a bare `Bash` entry, as [How auto mode evaluates actions](/docs/en/permission-modes#how-auto-mode-evaluates-actions) describes. If your application relies on the `default` mode or on such a rule, pass `default` explicitly.

124 

125Before TypeScript Agent SDK v0.3.286, omitting `permissionMode` was the same as passing `default`.

126 

118### Available modes127### Available modes

119 128 

120The SDK supports these permission modes:129The SDK supports these permission modes:

agent-sdk/python.md +241 −69

Details

445 async def set_model(self, model: str | None = None) -> None445 async def set_model(self, model: str | None = None) -> None

446 async def rewind_files(self, user_message_id: str) -> None446 async def rewind_files(self, user_message_id: str) -> None

447 async def get_mcp_status(self) -> McpStatusResponse447 async def get_mcp_status(self) -> McpStatusResponse

448 async def get_context_usage(self) -> ContextUsageResponse

448 async def reconnect_mcp_server(self, server_name: str) -> None449 async def reconnect_mcp_server(self, server_name: str) -> None

449 async def toggle_mcp_server(self, server_name: str, enabled: bool) -> None450 async def toggle_mcp_server(self, server_name: str, enabled: bool) -> None

450 async def stop_task(self, task_id: str) -> None451 async def stop_task(self, task_id: str) -> None


466| `set_model(model)` | Change the model for the current session. Pass `None` to reset to [Claude Code's default model](/docs/en/model-config) |467| `set_model(model)` | Change the model for the current session. Pass `None` to reset to [Claude Code's default model](/docs/en/model-config) |

467| `rewind_files(user_message_id)` | Restore files to their state at the specified user message. Requires `enable_file_checkpointing=True`. See [File checkpointing](/docs/en/agent-sdk/file-checkpointing) |468| `rewind_files(user_message_id)` | Restore files to their state at the specified user message. Requires `enable_file_checkpointing=True`. See [File checkpointing](/docs/en/agent-sdk/file-checkpointing) |

468| `get_mcp_status()` | Get the status of all configured MCP servers. Returns [`McpStatusResponse`](#mcpstatusresponse) |469| `get_mcp_status()` | Get the status of all configured MCP servers. Returns [`McpStatusResponse`](#mcpstatusresponse) |

470| `get_context_usage()` | Get a breakdown of context window usage by category, skill, and tool. The same data `/context` shows in an interactive session. Returns [`ContextUsageResponse`](#contextusageresponse). To compute the breakdown, Claude Code makes several token-counting API requests that don't appear in the message stream; see [how these requests are handled](#contextusageresponse) |

469| `reconnect_mcp_server(server_name)` | Retry connecting to an MCP server that failed or was disconnected |471| `reconnect_mcp_server(server_name)` | Retry connecting to an MCP server that failed or was disconnected |

470| `toggle_mcp_server(server_name, enabled)` | Enable or disable an MCP server mid-session. Disabling removes its tools |472| `toggle_mcp_server(server_name, enabled)` | Enable or disable an MCP server mid-session. Disabling a stdio, SSE, or HTTP server removes its tools |

471| `stop_task(task_id)` | Stop a running background task. A [`TaskNotificationMessage`](#tasknotificationmessage) with status `"stopped"` follows in the message stream |473| `stop_task(task_id)` | Stop a running background task. A [`TaskNotificationMessage`](#tasknotificationmessage) with status `"stopped"` follows in the message stream |

472| `get_server_info()` | Get the server's initialization info, including available commands and output styles |474| `get_server_info()` | Get the server's initialization info, including available commands and output styles |

473| `disconnect()` | Disconnect from Claude |475| `disconnect()` | Disconnect from Claude |


536 538 

537#### Example - Streaming input with ClaudeSDKClient539#### Example - Streaming input with ClaudeSDKClient

538 540 

541`query()` also accepts an async iterable of user message dicts, so you can assemble the prompt at send time or include content blocks such as images. Claude Code starts responding to the first yielded message as soon as it arrives, without waiting for the iterable to finish, and `receive_response()` stops at the `ResultMessage` that ends that response. Put everything Claude should read before answering into one message, as this generator does, and pair each `query()` call with its own `receive_response()` loop.

542 

539```python theme={null}543```python theme={null}

540import asyncio544import asyncio

541from claude_agent_sdk import ClaudeSDKClient545from claude_agent_sdk import ClaudeSDKClient

542 546 

543 547 

544async def message_stream():548async def message_stream():

545 """Generate messages dynamically."""549 """Assemble the prompt at send time and yield it as one user message."""

546 yield {550 readings = {"Temperature": "25°C", "Humidity": "60%"}

547 "type": "user",551 data = ", ".join(f"{name}: {value}" for name, value in readings.items())

548 "message": {"role": "user", "content": "Analyze the following data:"},

549 }

550 await asyncio.sleep(0.5)

551 yield {

552 "type": "user",

553 "message": {"role": "user", "content": "Temperature: 25°C, Humidity: 60%"},

554 }

555 await asyncio.sleep(0.5)

556 yield {552 yield {

557 "type": "user",553 "type": "user",

558 "message": {"role": "user", "content": "What patterns do you see?"},554 "message": {

555 "role": "user",

556 "content": f"Analyze the following sensor data and describe any patterns you see: {data}",

557 },

559 }558 }

560 559 

561 560 


1453| `scope` | `str` (optional) | Configuration scope |1452| `scope` | `str` (optional) | Configuration scope |

1454| `tools` | `list` (optional) | Tools provided by this server, each with `name`, `description`, and `annotations` fields |1453| `tools` | `list` (optional) | Tools provided by this server, each with `name`, `description`, and `annotations` fields |

1455 1454 

1455### `ContextUsageResponse`

1456 

1457Response from [`ClaudeSDKClient.get_context_usage()`](#methods). This is the same payload Claude Code renders for the `/context` command in an interactive session, so alongside the token counts it carries display fields such as `color` and `gridRows` that Claude Code uses to draw the `/context` usage grid.

1458 

1459Claude Code builds this payload by sending several requests to the [token-counting](https://platform.claude.com/docs/en/build-with-claude/token-counting) API. These requests don't appear in the message stream, so cost tracking that reads the stream won't see them. On the Anthropic API, token counting isn't billed.

1460 

1461```python theme={null}

1462class ContextUsageResponse(TypedDict):

1463 categories: list[ContextUsageCategory]

1464 totalTokens: int

1465 maxTokens: int

1466 rawMaxTokens: int

1467 percentage: float

1468 model: str

1469 isAutoCompactEnabled: bool

1470 memoryFiles: list[dict[str, Any]]

1471 mcpTools: list[dict[str, Any]]

1472 agents: list[dict[str, Any]]

1473 gridRows: list[list[dict[str, Any]]]

1474 autoCompactThreshold: NotRequired[int]

1475 deferredBuiltinTools: NotRequired[list[dict[str, Any]]]

1476 systemTools: NotRequired[list[dict[str, Any]]]

1477 systemPromptSections: NotRequired[list[dict[str, Any]]]

1478 slashCommands: NotRequired[dict[str, Any]]

1479 skills: NotRequired[dict[str, Any]] # skill usage with frontmatter breakdown

1480 messageBreakdown: NotRequired[dict[str, Any]] # message tokens by type

1481 apiUsage: NotRequired[dict[str, Any] | None]

1482```

1483 

1484Each `ContextUsageCategory` entry carries `name`, `tokens`, `color`, and an optional `isDeferred` flag. `totalTokens` is the session's current context usage, and `maxTokens` is the window that usage is measured against. That window is the model's context window, or the lower auto-compaction window when one applies, and `rawMaxTokens` carries the same value as `maxTokens`. `apiUsage` holds the usage from the latest API response, not a running total for the session. Claude Code leaves the optional `deferredBuiltinTools`, `systemTools`, and `systemPromptSections` keys unset, so expect them to be absent even though the type declares them.

1485 

1456### `SdkPluginConfig`1486### `SdkPluginConfig`

1457 1487 

1458Configuration for loading plugins in the SDK.1488Configuration for loading plugins in the SDK.


2454 2484 

2455Documentation of input/output schemas for all built-in Claude Code tools. While the Python SDK doesn't export these as types, they represent the structure of tool inputs and outputs in messages.2485Documentation of input/output schemas for all built-in Claude Code tools. While the Python SDK doesn't export these as types, they represent the structure of tool inputs and outputs in messages.

2456 2486 

2487Each output shown is the value you read from [`UserMessage.tool_use_result`](#usermessage) for that tool. Key names appear exactly as Claude Code emits them. A key annotated `| None` with a "present when" or "optional" comment is omitted when it doesn't apply.

2488 

2457### Agent2489### Agent

2458 2490 

2459**Tool name:** `Agent`. The previous name `Task` is still accepted as an alias, and the `tools` list in the init [`SystemMessage`](#systemmessage) reports this tool as `Task` for backward compatibility.2491**Tool name:** `Agent`. The previous name `Task` is still accepted as an alias, and the `tools` list in the init [`SystemMessage`](#systemmessage) reports this tool as `Task` for backward compatibility.


2621 2653 

2622**Tool name:** `Bash`2654**Tool name:** `Bash`

2623 2655 

2624For what sets the foreground ceiling, see [Timeout and output limits](/docs/en/tools-reference#timeout-and-output-limits). For the background time limit, see [Background commands](/docs/en/tools-reference#background-commands).2656For what sets the foreground ceiling, see [Timeout and output limits](/docs/en/tools-reference#timeout-and-output-limits). For the background time limit, see [Time limit for background commands](/docs/en/tools-reference#time-limit-for-background-commands).

2625 2657 

2626**Input:**2658**Input:**

2627 2659 


2694 2726 

2695```python theme={null}2727```python theme={null}

2696{2728{

2697 "message": str, # Confirmation message2729 "filePath": str, # The file that was edited

2698 "replacements": int, # Number of replacements made2730 "oldString": str, # The text that was replaced

2699 "file_path": str, # File path that was edited2731 "newString": str, # The text that replaced it

2732 "originalFile": str | None, # File contents before the edit

2733 "structuredPatch": [ # Diff hunks for the change

2734 {

2735 "oldStart": int,

2736 "oldLines": int,

2737 "newStart": int,

2738 "newLines": int,

2739 "lines": list[str],

2740 }

2741 ],

2742 "userModified": bool, # Whether the user changed the proposed edit before accepting it

2743 "replaceAll": bool, # Whether all occurrences were replaced

2744 "gitDiff": { # Optional git diff summary for the file

2745 "filename": str,

2746 "status": "modified" | "added",

2747 "additions": int,

2748 "deletions": int,

2749 "changes": int,

2750 "patch": str,

2751 "repository": str | None, # GitHub owner/repo when available

2752 } | None,

2700}2753}

2701```2754```

2702 2755 


2714}2767}

2715```2768```

2716 2769 

2717**Output (Text files):**2770The output takes one of the following shapes depending on what Claude read. Check the `type` key to tell them apart.

2771 

2772**Output (type: `"text"`):**

2773 

2774```python theme={null}

2775{

2776 "type": "text",

2777 "file": {

2778 "filePath": str, # The file that was read

2779 "content": str, # The returned content

2780 "numLines": int, # Number of lines in the returned content

2781 "startLine": int, # Line number the content starts at

2782 "totalLines": int, # Total number of lines in the file

2783 "truncatedByTokenCap": bool | None, # Present and True when a whole-file read exceeded the token cap and content is the first page

2784 },

2785}

2786```

2787 

2788**Output (type: `"image"`):**

2789 

2790```python theme={null}

2791{

2792 "type": "image",

2793 "file": {

2794 "base64": str, # Base64-encoded image data

2795 "type": "image/jpeg" | "image/png" | "image/gif" | "image/webp", # Image MIME type

2796 "originalSize": int, # Original file size in bytes

2797 "dimensions": { # Optional sizing info for coordinate mapping

2798 "originalWidth": int | None, # Optional; original width in pixels

2799 "originalHeight": int | None, # Optional; original height in pixels

2800 "displayWidth": int | None, # Optional; width after resizing

2801 "displayHeight": int | None, # Optional; height after resizing

2802 } | None,

2803 },

2804}

2805```

2806 

2807**Output (type: `"notebook"`):**

2718 2808 

2719```python theme={null}2809```python theme={null}

2720{2810{

2721 "content": str, # File contents with line numbers2811 "type": "notebook",

2722 "total_lines": int, # Total number of lines in file2812 "file": {

2723 "lines_returned": int, # Lines actually returned2813 "filePath": str, # The notebook that was read

2814 "cells": list, # Notebook cells

2815 },

2724}2816}

2725```2817```

2726 2818 

2727**Output (Images):**2819**Output (type: `"pdf"`):**

2728 2820 

2729```python theme={null}2821```python theme={null}

2730{2822{

2731 "image": str, # Base64 encoded image data2823 "type": "pdf",

2732 "mime_type": str, # Image MIME type2824 "file": {

2733 "file_size": int, # File size in bytes2825 "filePath": str, # The PDF that was read

2826 "base64": str, # Base64-encoded PDF data

2827 "originalSize": int, # File size in bytes

2828 },

2829}

2830```

2831 

2832**Output (type: `"parts"`):**

2833 

2834```python theme={null}

2835{

2836 "type": "parts",

2837 "file": {

2838 "filePath": str, # The PDF that was read

2839 "originalSize": int, # File size in bytes

2840 "count": int, # Number of pages extracted as images

2841 "outputDir": str, # Directory containing the extracted page images

2842 },

2843 "firstPage": int | None, # Optional document page number of the first extracted page

2844}

2845```

2846 

2847**Output (type: `"file_unchanged"`):**

2848 

2849```python theme={null}

2850{

2851 "type": "file_unchanged", # The file is unchanged since Claude last read it in this session, so the content isn't repeated

2852 "file": {

2853 "filePath": str,

2854 },

2855 "source": "seeded" | None, # Present when the earlier copy came from a CLAUDE.md or memory file loaded at startup rather than a Read call

2734}2856}

2735```2857```

2736 2858 


2751 2873 

2752```python theme={null}2874```python theme={null}

2753{2875{

2754 "message": str, # Success message2876 "type": "create" | "update", # Whether the write created a new file or overwrote an existing one

2755 "bytes_written": int, # Number of bytes written2877 "filePath": str, # The file that was written

2756 "file_path": str, # File path that was written2878 "content": str, # The content that was written

2879 "structuredPatch": [ # Diff hunks; empty for a new file, when nothing changed, or when Claude Code skipped the diff

2880 {

2881 "oldStart": int,

2882 "oldLines": int,

2883 "newStart": int,

2884 "newLines": int,

2885 "lines": list[str],

2886 }

2887 ],

2888 "originalFile": str | None, # Previous content; None for a new file or when the previous content was too large to include

2889 "gitDiff": { # Optional git diff summary for the file

2890 "filename": str,

2891 "status": "modified" | "added",

2892 "additions": int,

2893 "deletions": int,

2894 "changes": int,

2895 "patch": str,

2896 "repository": str | None, # GitHub owner/repo when available

2897 } | None,

2898 "userModified": bool | None, # Optional; whether the user edited the proposed content before accepting it

2757}2899}

2758```2900```

2759 2901 


2774 2916 

2775```python theme={null}2917```python theme={null}

2776{2918{

2777 "matches": list[str], # Array of matching file paths2919 "durationMs": int, # Time taken to run the search, in milliseconds

2778 "count": int, # Number of matches found2920 "numFiles": int, # Number of paths returned, after any truncation

2779 "search_path": str, # Search directory used2921 "filenames": list[str], # Matching file paths

2922 "truncated": bool, # Whether the results were truncated at the 100-file limit

2923 "totalMatches": int | None, # Optional total number of matching files before truncation; a lower bound when countIsComplete is False

2924 "countIsComplete": bool | None, # Optional; whether totalMatches is exact

2780}2925}

2781```2926```

2782 2927 

2928`totalMatches` and `countIsComplete` require Claude Code v2.1.191 or later.

2929 

2783### Grep2930### Grep

2784 2931 

2785**Tool name:** `Grep`2932**Tool name:** `Grep`


2798 "-B": int | None, # Lines to show before each match2945 "-B": int | None, # Lines to show before each match

2799 "-A": int | None, # Lines to show after each match2946 "-A": int | None, # Lines to show after each match

2800 "-C": int | None, # Lines to show before and after2947 "-C": int | None, # Lines to show before and after

2948 "context": int | None, # Lines to show before and after; -C is an alias

2949 "-o": bool | None, # Print only the matched parts of each line

2801 "head_limit": int | None, # Limit output to first N lines/entries2950 "head_limit": int | None, # Limit output to first N lines/entries

2951 "offset": int | None, # Skip first N lines/entries before applying head_limit

2802 "multiline": bool | None, # Enable multiline mode2952 "multiline": bool | None, # Enable multiline mode

2803}2953}

2804```2954```

2805 2955 

2806**Output (content mode):**2956**Output:**

2807 2957 

2808```python theme={null}2958```python theme={null}

2809{2959{

2810 "matches": [2960 "mode": "content" | "files_with_matches" | "count" | None, # The output mode that was used

2811 {2961 "numFiles": int, # Number of files in the result; always 0 in content mode

2812 "file": str,2962 "filenames": list[str], # Matching files in files_with_matches mode; empty in the other modes

2813 "line_number": int | None,2963 "content": str | None, # Matching lines in content mode, or per-file counts in count mode

2814 "line": str,2964 "numLines": int | None, # Number of lines in content, present in content mode

2815 "before_context": list[str] | None,2965 "numMatches": int | None, # Total match count, present in count mode

2816 "after_context": list[str] | None,2966 "totalFiles": int | None, # Optional total before head_limit and offset, in files_with_matches mode

2817 }2967 "totalLines": int | None, # Optional total before head_limit and offset, in content mode

2818 ],2968 "appliedLimit": int | None, # Present when head_limit truncated the result

2819 "total_matches": int,2969 "appliedOffset": int | None, # Present when an offset was applied

2820}2970}

2821```2971```

2822 2972 

2823**Output (files\_with\_matches mode):**2973Grep returns this dict shape in each output mode. Which optional keys are present depends on `output_mode`.

2824 2974 

2825```python theme={null}2975`totalFiles` requires Claude Code v2.1.208 or later. `totalLines` requires Claude Code v2.1.210 or later.

2826{

2827 "files": list[str], # Files containing matches

2828 "count": int, # Number of files with matches

2829}

2830```

2831 2976 

2832### NotebookEdit2977### NotebookEdit

2833 2978 


2849 2994 

2850```python theme={null}2995```python theme={null}

2851{2996{

2852 "message": str, # Success message2997 "new_source": str, # The source written to the cell

2853 "edit_type": "replaced" | "inserted" | "deleted", # Type of edit performed2998 "old_source": str | None, # Previous cell source, present for replace and delete

2854 "cell_id": str | None, # Cell ID that was affected2999 "cell_id": str | None, # ID of the edited cell, when available

2855 "total_cells": int, # Total cells in notebook after edit3000 "cell_type": "code" | "markdown", # The cell type

3001 "language": str, # The notebook's programming language

3002 "edit_mode": str, # The edit mode that was used

3003 "error": str | None, # Error message when the operation failed

3004 "notebook_path": str, # The notebook file

3005 "original_file": str, # Notebook content before the edit

3006 "updated_file": str, # Notebook content after the edit

2856}3007}

2857```3008```

2858 3009 


2944 3095 

2945```python theme={null}3096```python theme={null}

2946{3097{

2947 "message": str, # Success message3098 "oldTodos": [ # The todo list before the update

2948 "stats": {"total": int, "pending": int, "in_progress": int, "completed": int},3099 {

3100 "content": str,

3101 "status": "pending" | "in_progress" | "completed",

3102 "activeForm": str,

3103 }

3104 ],

3105 "newTodos": [ # The todo list after the update

3106 {

3107 "content": str,

3108 "status": "pending" | "in_progress" | "completed",

3109 "activeForm": str,

3110 }

3111 ],

2949}3112}

2950```3113```

2951 3114 


3103 3266 

3104```python theme={null}3267```python theme={null}

3105{3268{

3106 "message": str, # Confirmation message3269 "plan": str | None, # The plan that was presented to the user

3107 "approved": bool | None, # Whether user approved the plan3270 "isAgent": bool, # True when a subagent called the tool

3271 "filePath": str | None, # Present when the plan was saved to a file

3272 "hasTaskTool": bool | None, # Optional; whether the Agent tool is available in the current context

3273 "planWasEdited": bool | None, # Present and True when the user edited the plan before approving

3274 "awaitingLeaderApproval": bool | None, # Present and True when a teammate sent the plan to the team lead for approval

3275 "requestId": str | None, # Optional ID of that approval request

3108}3276}

3109```3277```

3110 3278 


3120}3288}

3121```3289```

3122 3290 

3291The result is a list rather than a dict, so `tool_use_result` holds a `list` for this tool.

3292 

3123**Output:**3293**Output:**

3124 3294 

3125```python theme={null}3295```python theme={null}

3126{3296[ # One entry per resource

3127 "resources": [

3128 {3297 {

3129 "uri": str,3298 "uri": str, # Resource URI

3130 "name": str,3299 "name": str, # Resource name

3131 "description": str | None,3300 "mimeType": str | None, # Optional MIME type

3132 "mimeType": str | None,3301 "description": str | None, # Optional description

3133 "server": str,3302 "server": str, # Server that provides this resource

3134 }3303 }

3135 ],3304]

3136 "total": int,

3137}

3138```3305```

3139 3306 

3140### ReadMcpResource3307### ReadMcpResource


3155```python theme={null}3322```python theme={null}

3156{3323{

3157 "contents": [3324 "contents": [

3158 {"uri": str, "mimeType": str | None, "text": str | None, "blob": str | None}3325 {

3326 "uri": str, # Resource URI

3327 "mimeType": str | None, # Optional MIME type

3328 "text": str | None, # Text content, or a note about the binary content

3329 "blobSavedTo": str | None, # Present when Claude Code saved binary content to disk; path of the saved file

3330 }

3159 ],3331 ],

3160 "server": str,3332 "error": str | None, # Present when the server couldn't read the resource

3161}3333}

3162```3334```

3163 3335 

Details

253</CodeGroup>253</CodeGroup>

254 254 

255<Note>255<Note>

256 A `compact_boundary` message only arrives when compaction ran. With nothing to summarize, `/compact` reports the reason instead of raising. The run still ends with a `success` result and no `compact_boundary` message, and the result text carries the reason, for example `Not enough messages to compact.` after a single short exchange. A fresh one-shot `query()` call starts with empty context, so use this pattern in a session with prior turns, for example in [streaming input mode](/docs/en/agent-sdk/streaming-vs-single-mode) or when resuming a session.256 A `compact_boundary` message only arrives when compaction ran. When a continued session has messages but nothing `/compact` can summarize, the run still ends with a `success` result rather than an error, and no `compact_boundary` message arrives. The result text then carries the reason, for example `Not enough messages to compact.` when the session holds a prompt but no reply from Claude yet. A fresh one-shot `query()` call starts with empty context, so use this pattern in a session with prior turns, for example in [streaming input mode](/docs/en/agent-sdk/streaming-vs-single-mode) or when resuming a session.

257</Note>257</Note>

258 258 

259### Reset context with `/clear`259### Reset context with `/clear`

Details

190| Tool definitions (inherited from parent or the subset in `tools`, [filtered for background runs](/docs/en/sub-agents#available-tools)) | The parent's system prompt |190| Tool definitions (inherited from parent or the subset in `tools`, [filtered for background runs](/docs/en/sub-agents#available-tools)) | The parent's system prompt |

191 191 

192<Note>192<Note>

193 The parent receives the subagent's final message as the Agent tool result, but may summarize it in its own response. To preserve subagent output verbatim in the user-facing response, include an instruction to do so in the prompt or `systemPrompt` option you pass to the main `query()` call.193 The parent receives the subagent's final report, but may summarize it in its own response. To preserve subagent output verbatim in the user-facing response, include an instruction to do so in the prompt or `systemPrompt` option you pass to the main `query()` call.

194 194 

195 In v2.1.210 and later, Claude Code [scans the final message for instruction-shaped patterns](/docs/en/sub-agents#subagent-output-scanning) before the parent reads it. The scan treats three kinds of pattern differently:195 In v2.1.210 and later, Claude Code [scans the final message for instruction-shaped patterns](/docs/en/sub-agents#subagent-output-scanning) before the parent reads it. The scan treats three kinds of pattern differently:

196 196 

Details

499| `outputFormat` | `{ type: 'json_schema', schema: JSONSchema }` | `undefined` | Define output format for agent results. See [Structured outputs](/docs/en/agent-sdk/structured-outputs) for details |499| `outputFormat` | `{ type: 'json_schema', schema: JSONSchema }` | `undefined` | Define output format for agent results. See [Structured outputs](/docs/en/agent-sdk/structured-outputs) for details |

500| `outputStyle` | `string` | `undefined` | Not an `Options` field. Set `outputStyle` in the inline [`settings`](/docs/en/settings) object or a settings file instead. See [Activate an output style](/docs/en/agent-sdk/modifying-system-prompts#activate-an-output-style) |500| `outputStyle` | `string` | `undefined` | Not an `Options` field. Set `outputStyle` in the inline [`settings`](/docs/en/settings) object or a settings file instead. See [Activate an output style](/docs/en/agent-sdk/modifying-system-prompts#activate-an-output-style) |

501| `pathToClaudeCodeExecutable` | `string` | Auto-resolved from bundled native binary | Path to Claude Code executable. Only needed if optional dependencies were skipped during install or your platform isn't in the supported set |501| `pathToClaudeCodeExecutable` | `string` | Auto-resolved from bundled native binary | Path to Claude Code executable. Only needed if optional dependencies were skipped during install or your platform isn't in the supported set |

502| `permissionMode` | [`PermissionMode`](#permissionmode) | `'default'` | Permission mode for the session |502| `permissionMode` | [`PermissionMode`](#permissionmode) | `undefined` | Permission mode for the session. If you omit it, the session can start in auto mode. See [Permission modes](/docs/en/agent-sdk/permissions#permission-modes) for how Claude Code picks the starting permission mode |

503| `permissionPromptToolName` | `string` | `undefined` | MCP tool name for permission prompts |503| `permissionPromptToolName` | `string` | `undefined` | MCP tool name for permission prompts |

504| `permissionPrompts` | `'host' \| 'none'` | `'host'` | Who answers permission prompts: `'host'` routes them to your [`canUseTool`](#canusetool) callback or the `permissionPromptToolName` tool, and `'none'` [denies the calls that would have prompted](/docs/en/agent-sdk/permissions#how-permissions-are-evaluated). Requires Claude Code v2.1.259 or later |504| `permissionPrompts` | `'host' \| 'none'` | `'host'` | Who answers permission prompts: `'host'` routes them to your [`canUseTool`](#canusetool) callback or the `permissionPromptToolName` tool, and `'none'` [denies the calls that would have prompted](/docs/en/agent-sdk/permissions#how-permissions-are-evaluated). Requires Claude Code v2.1.259 or later |

505| `persistSession` | `boolean` | `true` | When `false`, disables session persistence to disk. Sessions cannot be resumed later |505| `persistSession` | `boolean` | `true` | When `false`, disables session persistence to disk. Sessions cannot be resumed later |


627| `supportedModels()` | Returns available models with display info |627| `supportedModels()` | Returns available models with display info |

628| `supportedAgents()` | Returns available subagents as [`AgentInfo`](#agentinfo)`[]` |628| `supportedAgents()` | Returns available subagents as [`AgentInfo`](#agentinfo)`[]` |

629| `mcpServerStatus()` | Returns the status of connected MCP servers as [`McpServerStatus`](#mcpserverstatus)`[]` |629| `mcpServerStatus()` | Returns the status of connected MCP servers as [`McpServerStatus`](#mcpserverstatus)`[]` |

630| `getContextUsage(opts?)` | Returns an [`SDKControlGetContextUsageResponse`](#sdkcontrolgetcontextusageresponse) breaking down the session's context window usage by category, skill, and tool. With the default `detail`, it is the same data `/context` shows in an interactive session. The [`detail` option](#sdkcontrolgetcontextusageresponse) requires Agent SDK v0.3.257 or later |630| `getContextUsage(opts?)` | Returns an [`SDKControlGetContextUsageResponse`](#sdkcontrolgetcontextusageresponse) breaking down the session's context window usage by category, skill, and tool. With the default `detail`, it is the same data `/context` shows in an interactive session, computed with token-counting API requests that don't appear in the message stream; see [how these requests are handled](#sdkcontrolgetcontextusageresponse). The [`detail` option](#sdkcontrolgetcontextusageresponse) requires Agent SDK v0.3.257 or later |

631| `readFile(path, options?)` | Reads a file from the session's filesystem. Claude Code resolves the path against `cwd`; [What `readFile()` can read](#what-readfile-can-read) lists the files it serves. Pass `{ maxBytes }` to change the read cap (default 1 MB, ceiling 10 MB) and `{ encoding: 'base64' }` for binary files such as images. Resolves with an [`SDKControlReadFileResponse`](#sdkcontrolreadfileresponse), or `null` on permission denial, a missing file, or a transport error. Requires TypeScript SDK v0.2.121 or later |631| `readFile(path, options?)` | Reads a file from the session's filesystem. Claude Code resolves the path against `cwd`; [What `readFile()` can read](#what-readfile-can-read) lists the files it serves. Pass `{ maxBytes }` to change the read cap (default 1 MB, ceiling 10 MB) and `{ encoding: 'base64' }` for binary files such as images. Resolves with an [`SDKControlReadFileResponse`](#sdkcontrolreadfileresponse), or `null` on permission denial, a missing file, or a transport error. Requires TypeScript SDK v0.2.121 or later |

632| `reloadPlugins(options?)` | Reloads plugins from disk, so plugins you install or edit mid-session reach the running session. Resolves with an [`SDKControlReloadPluginsResponse`](#sdkcontrolreloadpluginsresponse) listing the session's commands, subagents, plugins, and MCP server status. Requires Agent SDK v0.2.85 or later. The [`holdOnCacheImpact` option](#sdkcontrolreloadpluginsresponse) requires Agent SDK v0.3.268 or later |632| `reloadPlugins(options?)` | Reloads plugins from disk, so plugins you install or edit mid-session reach the running session. Resolves with an [`SDKControlReloadPluginsResponse`](#sdkcontrolreloadpluginsresponse) listing the session's commands, subagents, plugins, and MCP server status. Requires Agent SDK v0.2.85 or later. The [`holdOnCacheImpact` option](#sdkcontrolreloadpluginsresponse) requires Agent SDK v0.3.268 or later |

633| `reloadSkills()` | Reloads skills from disk, so skills you add or edit mid-session become available to the running session. Resolves with an [`SDKControlReloadSkillsResponse`](#sdkcontrolreloadskillsresponse) listing the skills available after the reload. Requires Agent SDK v0.3.163 or later |633| `reloadSkills()` | Reloads skills from disk, so skills you add or edit mid-session become available to the running session. Resolves with an [`SDKControlReloadSkillsResponse`](#sdkcontrolreloadskillsresponse) listing the skills available after the reload. Requires Agent SDK v0.3.163 or later |

634| `reloadOutputStyles()` | Re-reads [output styles](/docs/en/output-styles) from disk, so a style file you add or edit mid-session becomes available to the running session. Resolves with an [`SDKControlReloadOutputStylesResponse`](#sdkcontrolreloadoutputstylesresponse) listing the style names available after the reload. Requires Agent SDK v0.3.261 or later |634| `reloadOutputStyles()` | Re-reads [output styles](/docs/en/output-styles) from disk, so a style file you add or edit mid-session becomes available to the running session. Resolves with an [`SDKControlReloadOutputStylesResponse`](#sdkcontrolreloadoutputstylesresponse) listing the style names available after the reload. Requires Agent SDK v0.3.261 or later |

635| `accountInfo()` | Returns account information |635| `accountInfo()` | Returns account information |

636| `reconnectMcpServer(serverName)` | Reconnect an MCP server by name. If the name also matches an entry in a settings file such as `.mcp.json` or `~/.claude.json`, Claude Code reconnects the server you configured through [`mcpServers`](#options) or `setMcpServers()`, not the settings-file entry. That resolution order requires Claude Code v2.1.257 or later |636| `reconnectMcpServer(serverName)` | Reconnect an MCP server by name. If the name also matches an entry in a settings file such as `.mcp.json` or `~/.claude.json`, Claude Code reconnects the server you configured through [`mcpServers`](#options) or `setMcpServers()`, not the settings-file entry. That resolution order requires Claude Code v2.1.257 or later |

637| `toggleMcpServer(serverName, enabled)` | Enable or disable an MCP server by name, with the same name resolution as `reconnectMcpServer()`. Disabling disconnects the server |637| `toggleMcpServer(serverName, enabled)` | Enable or disable an MCP server by name, with the same name resolution as `reconnectMcpServer()`. Disabling a stdio, SSE, or HTTP server disconnects it and removes its tools; for a server you added mid-session with `setMcpServers()`, tool removal requires Claude Code v2.1.285 or later |

638| `setMcpServers(servers)` | Dynamically replace the set of MCP servers for this session. Resolves with an [`McpSetServersResult`](#mcpsetserversresult) naming which servers were added and removed, and any errors |638| `setMcpServers(servers)` | Dynamically replace the set of MCP servers for this session. Resolves with an [`McpSetServersResult`](#mcpsetserversresult) naming which servers were added and removed, and any errors |

639| `readMcpResource(serverName, uri)` | *Alpha.* Reads one MCP Apps `ui://` resource from a connected MCP server so your application can render a tool's widget. Resolves with an [`SDKControlMcpReadResourceResponse`](#sdkcontrolmcpreadresourceresponse). Requires TypeScript Agent SDK v0.3.280 or later |639| `readMcpResource(serverName, uri)` | *Alpha.* Reads one MCP Apps `ui://` resource from a connected MCP server so your application can render a tool's widget. Resolves with an [`SDKControlMcpReadResourceResponse`](#sdkcontrolmcpreadresourceresponse). Requires TypeScript Agent SDK v0.3.280 or later |

640| `streamInput(stream)` | Stream input messages to the query for multi-turn conversations |640| `streamInput(stream)` | Stream input messages to the query for multi-turn conversations |


809 809 

810Return type of [`getContextUsage()`](#query-object). With the default `detail`, this is the same payload Claude Code renders for the `/context` command in an interactive session, so alongside the token counts it carries display fields such as `color` and `gridRows` that Claude Code uses to draw the `/context` usage grid.810Return type of [`getContextUsage()`](#query-object). With the default `detail`, this is the same payload Claude Code renders for the `/context` command in an interactive session, so alongside the token counts it carries display fields such as `color` and `gridRows` that Claude Code uses to draw the `/context` usage grid.

811 811 

812The method's optional `detail` argument chooses how Claude Code counts each category. With the default, `'full'`, Claude Code counts each category with token-counting API requests. Pass `{ detail: 'summary' }` to get an answer from the last response's usage and local estimates instead. No token-count requests go out, and the per-category numbers are approximate. The `detail` argument requires Agent SDK v0.3.257 or later.812The method's optional `detail` argument chooses how Claude Code counts each category. The `detail` argument requires Agent SDK v0.3.257 or later.

813 

814* **`'full'`**: the default. Claude Code counts each category with [token-counting](https://platform.claude.com/docs/en/build-with-claude/token-counting) API requests. These requests don't appear in the message stream, so cost tracking that reads the stream won't see them. On the Anthropic API, token counting isn't billed.

815* **`'summary'`**: pass `{ detail: 'summary' }` to get an answer from the last response's usage and local estimates instead. No token-count requests go out, and the per-category numbers are approximate.

813 816 

814When you send `/context` as a prompt instead of calling the method, Claude Code attaches an [`SDKContextUsage`](#sdkcontextusage) payload to the `context_usage` field of the assistant message that delivers the result. That field requires Agent SDK v0.3.232 or later.817When you send `/context` as a prompt instead of calling the method, Claude Code attaches an [`SDKContextUsage`](#sdkcontextusage) payload to the `context_usage` field of the assistant message that delivers the result. That field requires Agent SDK v0.3.232 or later.

815 818 


915* `memoryFiles` lists each loaded memory file with its cost.918* `memoryFiles` lists each loaded memory file with its cost.

916* `skills.skillFrontmatter` attributes the skill listing's tokens to each included skill. The per-skill counts measure each skill's listing entry as Claude Code actually sends it, which can be shorter than the skill's full frontmatter. Compare `skills.totalSkills` with `skills.includedSkills` to see whether every discovered skill made it into the listing.919* `skills.skillFrontmatter` attributes the skill listing's tokens to each included skill. The per-skill counts measure each skill's listing entry as Claude Code actually sends it, which can be shorter than the skill's full frontmatter. Compare `skills.totalSkills` with `skills.includedSkills` to see whether every discovered skill made it into the listing.

917 920 

918`totalTokens` is the session's current context usage, and `maxTokens` is the window that usage is measured against. That window is the model's context window, or the lower auto-compaction window when one applies. `rawMaxTokens` carries the same value as `maxTokens`, and `percentage` is `totalTokens` as a rounded percentage of that window.921`totalTokens` is the session's current context usage, and `maxTokens` is the window that usage is measured against. That window is the model's context window, or the lower auto-compaction window when one applies. `rawMaxTokens` carries the same value as `maxTokens`, and `percentage` is `totalTokens` as a rounded percentage of that window. `apiUsage` holds the usage from the latest API response, not a running total for the session.

919 922 

920Claude Code leaves the optional `deferredBuiltinTools`, `systemTools`, and `systemPromptSections` diagnostics unset, so expect them to be absent even though the type declares them.923Claude Code leaves the optional `deferredBuiltinTools`, `systemTools`, and `systemPromptSections` diagnostics unset, so expect them to be absent even though the type declares them.

921 924 


1441 1444 

1442On a message that carries a `tool_result` block, `tool_use_result` is the tool's structured output object rather than the text sent to the model. Its shape depends on the tool named by the matching `tool_use` block, so the field is typed `unknown`; the built-in shapes are listed under [Tool Output Types](#tool-output-types).1445On a message that carries a `tool_result` block, `tool_use_result` is the tool's structured output object rather than the text sent to the model. Its shape depends on the tool named by the matching `tool_use` block, so the field is typed `unknown`; the built-in shapes are listed under [Tool Output Types](#tool-output-types).

1443 1446 

1444For the `Agent` tool, `tool_use_result` is [`AgentOutput`](#agent-2). On a `completed` result, `content` holds the subagent's report without the agent ID and usage trailer that Claude Code appends to the `tool_result` text, so render from `tool_use_result` instead of parsing that text.1447For the `Agent` tool, `tool_use_result` is [`AgentOutput`](#agent-2). Render from it rather than parsing the `tool_result` text. A `completed` result's `content` holds the subagent's report, or, for a subagent whose report goes through a `SubagentHandback` tool call, a short note about that hand-back in place of the report. In [auto mode](/docs/en/permission-modes#eliminate-prompts-with-auto-mode) on Claude Code v2.1.271 or later, every subagent that produces a `completed` result reports that way unless it is a [fork](/docs/en/sub-agents#fork-the-current-conversation), and Claude receives the report as a separate message from the subagent.

1445 1448 

1446For an MCP tool whose result contains `resource_link` blocks, `tool_use_result` is an object with a `resourceLinks` array of [`SDKMcpResourceLink`](#sdkmcpresourcelink) entries. Claude receives each link as a line of text in the `tool_result` block, so read `resourceLinks` to render the files the server returned instead of parsing that text. Claude Code omits `resourceLinks` when the result has no links and on results from subagents, keeps at most 50 links per result, and stops adding links once the array reaches 64 KiB of serialized JSON. `resourceLinks` requires Agent SDK v0.3.257 or later.1449For an MCP tool whose result contains `resource_link` blocks, `tool_use_result` is an object with a `resourceLinks` array of [`SDKMcpResourceLink`](#sdkmcpresourcelink) entries. Claude receives each link as a line of text in the `tool_result` block, so read `resourceLinks` to render the files the server returned instead of parsing that text. Claude Code omits `resourceLinks` when the result has no links and on results from subagents, keeps at most 50 links per result, and stops adding links once the array reaches 64 KiB of serialized JSON. `resourceLinks` requires Agent SDK v0.3.257 or later.

1447 1450 


1655```typescript theme={null}1658```typescript theme={null}

1656type SDKStartupFailureReason =1659type SDKStartupFailureReason =

1657 | "org_pin_api_key_conflict"1660 | "org_pin_api_key_conflict"

1661 | "provider_not_allowed"

1658 | "org_verify_failed"1662 | "org_verify_failed"

1659 | "org_pin_mismatch"1663 | "org_pin_mismatch"

1660 | "managed_settings_invalid"1664 | "managed_settings_invalid"


1677| Value | What stopped the session |1681| Value | What stopped the session |

1678| :- | :- |1682| :- | :- |

1679| `org_pin_api_key_conflict` | Managed settings [require a first-party or Cloud gateway sign-in](/docs/en/authentication#restrict-login-to-your-organization), and an Anthropic API key, auth token, or `apiKeyHelper` is configured instead |1683| `org_pin_api_key_conflict` | Managed settings [require a first-party or Cloud gateway sign-in](/docs/en/authentication#restrict-login-to-your-organization), and an Anthropic API key, auth token, or `apiKeyHelper` is configured instead |

1684| `provider_not_allowed` | Managed settings [list the API providers this machine may use](/docs/en/settings-reference#allowedproviders), and the session is set up for a provider that isn't listed, or for an endpoint the settings don't pin. Requires Claude Code v2.1.285 or later |

1680| `org_verify_failed` | The sign-in's organization couldn't be verified against the pin, for example because of a network failure or a revoked token |1685| `org_verify_failed` | The sign-in's organization couldn't be verified against the pin, for example because of a network failure or a revoked token |

1681| `org_pin_mismatch` | The sign-in belongs to an organization the pin doesn't allow |1686| `org_pin_mismatch` | The sign-in belongs to an organization the pin doesn't allow |

1682| `managed_settings_invalid` | Managed policy settings couldn't be read, the pin names no organization, or [managed model restrictions](/docs/en/errors#managed-settings-block-the-default-model) leave no permitted model for the Default option |1687| `managed_settings_invalid` | Managed policy settings couldn't be read, the pin names no organization, or [managed model restrictions](/docs/en/errors#managed-settings-block-the-default-model) leave no permitted model for the Default option |


1898 1903 

1899### `SDKContextUsage`1904### `SDKContextUsage`

1900 1905 

1901Structured form of the `/context` report, carried as `context_usage` on the [`SDKAssistantMessage`](#sdkassistantmessage) that delivers a `/context` result. Agent SDK v0.3.232 and later export the type. Unlike [`SDKControlGetContextUsageResponse`](#sdkcontrolgetcontextusageresponse), it carries only the data needed to render the usage breakdown, without display fields such as `color` and `gridRows`.1906Structured form of the `/context` report, carried as `context_usage` on the [`SDKAssistantMessage`](#sdkassistantmessage) that delivers a `/context` result. Agent SDK v0.3.232 and later export the type. Unlike [`SDKControlGetContextUsageResponse`](#sdkcontrolgetcontextusageresponse), it carries only the data needed to render the usage breakdown, without display fields such as `color` and `gridRows`. Claude Code computes the report with token-counting API requests that don't appear in the message stream; see [how these requests are handled](#sdkcontrolgetcontextusageresponse).

1902 1907 

1903```typescript theme={null}1908```typescript theme={null}

1904type SDKContextUsage = {1909type SDKContextUsage = {


2897};2902};

2898```2903```

2899 2904 

2900Executes Bash commands with optional timeout and background execution. The working directory persists between commands, including commands run in later turns of a multi-turn session; shell state such as exported environment variables doesn't. For the limits on which directory changes carry over, see [What persists between commands](/docs/en/tools-reference#what-persists-between-commands). For what sets the foreground ceiling, see [Timeout and output limits](/docs/en/tools-reference#timeout-and-output-limits). For the background time limit, see [Background commands](/docs/en/tools-reference#background-commands).2905Executes Bash commands with optional timeout and background execution. The working directory persists between commands, including commands run in later turns of a multi-turn session; shell state such as exported environment variables doesn't. For the limits on which directory changes carry over, see [What persists between commands](/docs/en/tools-reference#what-persists-between-commands). For what sets the foreground ceiling, see [Timeout and output limits](/docs/en/tools-reference#timeout-and-output-limits). For the background time limit, see [Time limit for background commands](/docs/en/tools-reference#time-limit-for-background-commands).

2901 2906 

2902### Monitor2907### Monitor

2903 2908 


3717 3722 

3718`timedOutAfterMs` is the timeout in milliseconds, set when the command reached its timeout and moved to the background rather than starting there explicitly. `backgroundCwdHint` is set when the backgrounded command contained a directory-change builtin such as `cd`, `pushd`, `popd`, or `chdir`, and notes that the session working directory didn't change. Both fields require Claude Code v2.1.210 or later.3723`timedOutAfterMs` is the timeout in milliseconds, set when the command reached its timeout and moved to the background rather than starting there explicitly. `backgroundCwdHint` is set when the backgrounded command contained a directory-change builtin such as `cd`, `pushd`, `popd`, or `chdir`, and notes that the session working directory didn't change. Both fields require Claude Code v2.1.210 or later.

3719 3724 

3720When a subagent running in the foreground owns a backgrounded command, the command [ends when that subagent's run ends](/docs/en/tools-reference#background-commands). Claude Code sets `backgroundEndsWithFinalResponse` to `true` on such commands, and omits the field when the command survives the turn, as commands started by the main conversation or by background subagents do. The field requires Claude Code v2.1.227 or later.3725When a subagent running in the foreground owns a backgrounded command, the command [ends when that subagent's run ends](/docs/en/tools-reference#when-a-background-command-stops). Claude Code sets `backgroundEndsWithFinalResponse` to `true` on such commands, and omits the field when the command survives the turn, as commands started by the main conversation or by background subagents do. The field requires Claude Code v2.1.227 or later.

3721 3726 

3722Claude Code sets `gitOperation.commit.branch` to the branch named in git's commit summary line, and omits it for a commit made on a detached HEAD. The field requires Agent SDK v0.3.227 or later. Claude Code reports a `gh pr reopen` command as the `reopened` PR action, which requires Agent SDK v0.3.234 or later.3727Claude Code sets `gitOperation.commit.branch` to the branch named in git's commit summary line, and omits it for a commit made on a detached HEAD. The field requires Agent SDK v0.3.227 or later. Claude Code reports a `gh pr reopen` command as the `reopened` PR action, which requires Agent SDK v0.3.234 or later.

3723 3728 

agent-view.md +2 −16

Details

8 8 

9Agent view, opened with `claude agents`, is one screen for all your background sessions: what's running, what needs your input, and what's done. Dispatch new sessions, watch their state at a glance instead of scrolling through transcripts, and step in only when one needs you. Each background session is a full Claude Code conversation that keeps running without a terminal attached, so you can open it, reply, and leave whenever you want.9Agent view, opened with `claude agents`, is one screen for all your background sessions: what's running, what needs your input, and what's done. Dispatch new sessions, watch their state at a glance instead of scrolling through transcripts, and step in only when one needs you. Each background session is a full Claude Code conversation that keeps running without a terminal attached, so you can open it, reply, and leave whenever you want.

10 10 

11<img src="https://mintcdn.com/claude-code/1B48Qz2Z9hac4SLG/images/agent-view-light.png?fit=max&auto=format&n=1B48Qz2Z9hac4SLG&q=85&s=7a186c96ed47d6700d084d77e786be65" className="dark:hidden" alt="Agent view in a terminal: the header shows Claude Code v2.1.140, the model, the working directory, and a summary count. Sessions are grouped under Needs input, Working, and Completed, with a dispatch input at the bottom and a footer of keyboard hints." width="1772" height="780" data-path="images/agent-view-light.png" />11<img src="https://mintcdn.com/claude-code/HDAmBwgbrZVk0pOt/images/agent-view-light.png?fit=max&auto=format&n=HDAmBwgbrZVk0pOt&q=85&s=d6905012bee31f3e6b3920b09c05dd02" className="dark:hidden" alt="Agent view in a terminal. A line at the top counts the sessions awaiting input, working, and completed. Four sessions are grouped under Needs input, Working, and Completed. Each row shows the session's name, its latest status or question, and a time. At the bottom are an input for describing a new task and a row of keyboard hints." width="1872" height="680" data-path="images/agent-view-light.png" />

12 12 

13<img src="https://mintcdn.com/claude-code/1B48Qz2Z9hac4SLG/images/agent-view-dark.png?fit=max&auto=format&n=1B48Qz2Z9hac4SLG&q=85&s=a5bed7434bae368faea3a8f023b52aa2" className="hidden dark:block" alt="Agent view in a terminal: the header shows Claude Code v2.1.140, the model, the working directory, and a summary count. Sessions are grouped under Needs input, Working, and Completed, with a dispatch input at the bottom and a footer of keyboard hints." width="1772" height="780" data-path="images/agent-view-dark.png" />13<img src="https://mintcdn.com/claude-code/HDAmBwgbrZVk0pOt/images/agent-view-dark.png?fit=max&auto=format&n=HDAmBwgbrZVk0pOt&q=85&s=fc3c195bfc57e313ced1f1beb36cee93" className="hidden dark:block" alt="Agent view in a terminal. A line at the top counts the sessions awaiting input, working, and completed. Four sessions are grouped under Needs input, Working, and Completed. Each row shows the session's name, its latest status or question, and a time. At the bottom are an input for describing a new task and a row of keyboard hints." width="1872" height="680" data-path="images/agent-view-dark.png" />

14 14 

15Use agent view when you have several independent tasks Claude can work on without you watching every step. Dispatch a bug fix, a pull request review, and a flaky-test investigation as three rows, keep working in another window, and check back when a row shows it needs you or has a result.15Use agent view when you have several independent tasks Claude can work on without you watching every step. Dispatch a bug fix, a pull request review, and a flaky-test investigation as three rows, keep working in another window, and check back when a row shows it needs you or has a result.

16 16 


874 874 

875Claude Code never restarts a row running a [shell command](#run-a-shell-command), from `Enter` or from `claude attach`, because that would run the command again; the row's message and `claude attach` both say the command isn't run again.875Claude Code never restarts a row running a [shell command](#run-a-shell-command), from `Enter` or from `claude attach`, because that would run the command again; the row's message and `claude attach` both say the command isn't run again.

876 876 

877#### Terminal host died

878 

879On Linux and WSL, the supervisor checks each host process every few seconds, whether or not you open the session, and marks the session failed when the process has exited but its connection to the supervisor never closed.

880 

881* In agent view, the row shows `terminal host process died — press Enter to restart`. Press `Enter` on it and Claude Code restarts the session on a fresh host process.

882* From the shell, `claude attach <id>` restarts a session already marked failed. Otherwise it reports the cause and exits, telling you to run `claude attach <id>` again.

883 

884#### Session isn't responding

885 

886When the supervisor accepts an open but no output arrives for about ten seconds, Claude Code ends the attempt and offers a restart. A session that merely stalled, for example across machine sleep, doesn't reach this offer: the supervisor [restarts it on open](#read-session-state) itself.

887 

888* In agent view, the footer shows `Press enter again to restart this session — it isn't responding (its conversation is saved and resumes).` Press `Enter` on the same row again and Claude Code stops the unresponsive process and restarts the session; it stops nothing without that second press.

889* From the shell, `claude attach <id>` reports the cause and exits, telling you to run `claude stop <id>`, then `claude attach <id>`.

890 

891### A session fails before starting with a `possibly low memory` note877### A session fails before starting with a `possibly low memory` note

892 878 

893When a background session's process exits before it finishes starting and the host is low on memory, the row's status names the exit and adds `possibly low memory — free some up and retry`.879When a background session's process exits before it finishes starting and the host is low on memory, the row's status names the exit and adds `possibly low memory — free some up and retry`.

Details

358 358 

359Model aliases such as `opus` don't act as pins, and neither does a model ID Claude Code doesn't recognize, such as an application inference profile ARN.359Model aliases such as `opus` don't act as pins, and neither does a model ID Claude Code doesn't recognize, such as an application inference profile ARN.

360 360 

361When these checks find a model your account can't invoke, Claude Code remembers the refusal on this machine for up to a day, and launches during that time skip the remembered model without asking Amazon Bedrock again. Claude Code checks a remembered refusal of a current default model again at launch once ten minutes have passed since the last check, so a default your administrator re-enables comes back. To turn the memory off, set [`CLAUDE_CODE_SKIP_MODEL_ACCESS_MEMORY=1`](/docs/en/env-vars).

362 

363### When a model is disabled mid-session

364 

365If your account loses access to the model your session is running on, for example because an administrator disables it in your Amazon Bedrock account, Claude Code switches the session to another model instead of failing each request, and shows `Switched to <fallback> because <model> is not available`. It tries the same models as the startup fallback: earlier versions of the same tier first and, for an Opus session with no Opus version available, the default Sonnet model.

366 

367The switch applies only to a tier you haven't pinned, the same condition as the startup fallback. A session on a specific version you picked, or on an [application inference profile ARN](#map-each-model-version-to-an-inference-profile), keeps its model and, without a fallback model chain, the request fails instead. In [auto mode](/docs/en/permission-modes#enable-auto-mode-on-bedrock-agent-platform-or-foundry), Claude Code switches only to a model auto mode supports on Amazon Bedrock. If none of those models is available either, the request fails with [AWS authentication failed](/docs/en/errors#aws-authentication-failed) and a hint to enable the model.

368 

369A [fallback model chain](/docs/en/model-config#fallback-model-chains) you configure replaces the tier switch: on these refusals Claude Code switches to your configured fallback instead. To have refused requests fail rather than switch, set [`CLAUDE_CODE_DISABLE_MODEL_ACCESS_FALLBACK=1`](/docs/en/env-vars). A fallback chain you configured still switches on these refusals; remove the chain as well if you want every refused request to fail.

370 

361## Cross-region inference profile prefixes371## Cross-region inference profile prefixes

362 372 

363On the Amazon Bedrock [Invoke API](https://docs.aws.amazon.com/bedrock/latest/APIReference/API_runtime_InvokeModelWithResponseStream.html), Claude Code resolves its built-in default models to [cross-region inference profile](https://docs.aws.amazon.com/bedrock/latest/userguide/inference-profiles-support.html) IDs; to route model versions through your own inference profiles instead, see [Map each model version to an inference profile](#map-each-model-version-to-an-inference-profile). This table shows the prefix Claude Code prefers for each resolved AWS region:373On the Amazon Bedrock [Invoke API](https://docs.aws.amazon.com/bedrock/latest/APIReference/API_runtime_InvokeModelWithResponseStream.html), Claude Code resolves its built-in default models to [cross-region inference profile](https://docs.aws.amazon.com/bedrock/latest/userguide/inference-profiles-support.html) IDs; to route model versions through your own inference profiles instead, see [Map each model version to an inference profile](#map-each-model-version-to-an-inference-profile). This table shows the prefix Claude Code prefers for each resolved AWS region:

artifacts.md +1 −1

Details

342| [Environment variable](/docs/en/env-vars) | Set `CLAUDE_CODE_DISABLE_ARTIFACT=1` |342| [Environment variable](/docs/en/env-vars) | Set `CLAUDE_CODE_DISABLE_ARTIFACT=1` |

343| [Permission rule](/docs/en/permissions) | Add `Artifact` to `permissions.deny` |343| [Permission rule](/docs/en/permissions) | Add `Artifact` to `permissions.deny` |

344 344 

345Once you turn artifacts off in a [`--settings`](/docs/en/cli-reference#cli-flags) file or with `CLAUDE_CODE_DISABLE_ARTIFACT`, or your administrator turns them off in [managed settings](/docs/en/server-managed-settings), no settings file turns them back on. Before v2.1.242, a file higher in the [precedence stack](/docs/en/settings#settings-precedence) could turn artifacts back on even when a lower-precedence file set `"enableArtifact": false`.345Once you turn artifacts off in a [`--settings`](/docs/en/cli-reference#cli-flags) file or with `CLAUDE_CODE_DISABLE_ARTIFACT`, or your administrator turns them off in [managed settings](/docs/en/server-managed-settings), no settings file turns them back on.

346 346 

347You can also set `"enableArtifact": false` in a project's `.claude/settings.json` or `.claude/settings.local.json` to turn artifacts off for sessions in that project. An `"enableArtifact": true` in either file doesn't turn them back on. Honoring the key in project and local settings requires Claude Code v2.1.242 or later.347You can also set `"enableArtifact": false` in a project's `.claude/settings.json` or `.claude/settings.local.json` to turn artifacts off for sessions in that project. An `"enableArtifact": true` in either file doesn't turn them back on. Honoring the key in project and local settings requires Claude Code v2.1.242 or later.

348 348 

Details

178* **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 them178* **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

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

180 180 

181### Restrict which API providers a machine may use

182 

183[`allowedProviders`](/docs/en/settings-reference#allowedproviders) in [managed settings](/docs/en/managed-settings) lists which services a managed machine may reach Claude through, such as the Anthropic API, Amazon Bedrock, or an LLM gateway. It complements `forceLoginMethod` and `forceLoginOrgUUID`, which govern which account a session uses when it talks to Anthropic. Requires Claude Code v2.1.285 or later.

184 

185```json managed-settings.json theme={null}

186{

187 "forceLoginMethod": "claudeai",

188 "forceLoginOrgUUID": ["xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"],

189 "allowedProviders": ["anthropic", "bedrock"]

190}

191```

192 

193With this file, a developer signed in to your claude.ai organization or configured for Amazon Bedrock starts normally. A session set up for any other provider is refused at startup, and a running session that switches to one is refused on its next request. [Managed settings don't allow this API provider](/docs/en/errors#managed-settings-dont-allow-this-api-provider) shows each message.

194 

195* **Allow an LLM gateway or proxy**: list `"customEndpoint"` and set the gateway's URL in the managed `env` block of the same source. The [settings reference](/docs/en/settings-reference#allowedproviders) lists every value and says which endpoint variables need a managed `env` pin.

196* **Deploy on managed machines**: put the list in the managed source that carries the rest of your policy. The entry's [Scope note](/docs/en/settings-reference#allowedproviders) says how a server-managed list combines with it.

197* **Server-managed settings only**: a list you set only in [server-managed settings](/docs/en/server-managed-settings) reaches only sessions that fetch your organization's settings, so treat it as a convenience for machines you can't reach with device management, not as enforcement. [Platform availability](/docs/en/server-managed-settings#platform-availability) lists which sessions fetch them.

198 

181## Credential management199## Credential management

182 200 

183Claude Code securely manages your authentication credentials:201Claude Code securely manages your authentication credentials:

Details

273 Edit rules from `/permissions`273 Edit rules from `/permissions`

274</h2>274</h2>

275 275 

276To view and edit classifier rules without opening a settings file, run [`/permissions`](/docs/en/permissions#manage-permissions) and select the **Auto mode** tab. The tab requires Claude Code v2.1.246 or later, and it appears only when [auto mode is available](/docs/en/permission-modes#eliminate-prompts-with-auto-mode) to your session.276To view and edit classifier rules and `environment` entries without opening a settings file, run [`/permissions`](/docs/en/permissions#manage-permissions) and select the **Auto mode** tab. The tab requires Claude Code v2.1.246 or later, and it appears only when [auto mode is available](/docs/en/permission-modes#eliminate-prompts-with-auto-mode) to your session.

277 277 

278The tab lists the `allow`, `soft_deny`, `hard_deny`, and `environment` entries from each of the [scopes the classifier reads](#where-the-classifier-reads-configuration), and shows whether the built-in rules are in effect for each section. Claude Code shows entries from [managed settings](/docs/en/server-managed-settings) or the `--settings` flag as read-only, and saves every change you make on the tab to `~/.claude/settings.json`. From the tab you can:278Claude Code shows entries from [managed settings](/docs/en/server-managed-settings) or the `--settings` flag as read-only, and saves every change you make on the tab to `~/.claude/settings.json`.

279 

280* Add, edit, or delete rules in the `allow`, `soft_deny`, and `hard_deny` sections. When you add the first rule to a section, Claude Code also inserts `"$defaults"` so the [built-in rules](#override-the-block-and-allow-rules) stay in effect.

281* Turn the built-in rules for `allow`, `soft_deny`, or `hard_deny` off or back on. Claude Code records the choice by adding or removing `"$defaults"` in your list for that section, so a section needs at least one rule of your own before you can turn its built-in rules off.

282* Edit the `environment` entries as one document in your editor. If you haven't configured any `environment` entries yet, Claude Code first asks whether to replace the built-in environment, then opens the editor on the full built-in text. When you save, Claude Code replaces your `autoMode.environment` array with the document. Include the `"$defaults"` line to [keep the built-in entries](#define-trusted-infrastructure).

283 279 

284## Route all shell commands through the classifier280## Route all shell commands through the classifier

285 281 

Details

1301 1301 

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

1303 1303 

1304To stop developers from bypassing the gateway with a cloud provider variable or an `ANTHROPIC_BASE_URL` of their own, add `"allowedProviders": ["gateway"]` to the same file. Claude Code then refuses every session on the machine that isn't set up for a Cloud gateway, and admits a gateway only when it is the one `forceLoginGatewayUrl` names or one whose URL the file's `env` block sets as `ANTHROPIC_BASE_URL`. `claude gateway` refuses to run on a machine that sets the list, so keep the key off the gateway host. See the [`allowedProviders`](/docs/en/settings-reference#allowedproviders) entry in the settings reference. Requires Claude Code v2.1.285 or later.

1305 

1304Deploy 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).1306Deploy 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).

1305 1307 

1306By 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.1308By 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.

Details

1545| `paste-cache/` | Contents of large pastes |1545| `paste-cache/` | Contents of large pastes |

1546| `image-cache/<session>/` | Attached images saved by Claude Code v2.1.274 and earlier. Later versions save pasted and attached images outside `~/.claude`, in an `images/` directory for each session under the temp directory that [`CLAUDE_CODE_TMPDIR`](/docs/en/env-vars) controls. The sweep removes other sessions' leftover directories here, whatever their age. |1546| `image-cache/<session>/` | Attached images saved by Claude Code v2.1.274 and earlier. Later versions save pasted and attached images outside `~/.claude`, in an `images/` directory for each session under the temp directory that [`CLAUDE_CODE_TMPDIR`](/docs/en/env-vars) controls. The sweep removes other sessions' leftover directories here, whatever their age. |

1547| `uploads/<session>/` | Files you attach from the web or mobile app, and photos you attach from the mobile app, when messaging a [Remote Control](/docs/en/remote-control) session. An attachment to a [cloud session](/docs/en/claude-code-on-the-web) is saved in that session's own cloud environment instead, not on your machine. |1547| `uploads/<session>/` | Files you attach from the web or mobile app, and photos you attach from the mobile app, when messaging a [Remote Control](/docs/en/remote-control) session. An attachment to a [cloud session](/docs/en/claude-code-on-the-web) is saved in that session's own cloud environment instead, not on your machine. |

1548| `dev-mods/<session>/` | [Mods that Claude wrote](/docs/en/plugins/mods/create#ask-claude-for-a-mod) during the session |

1548| `session-env/` | Per-session environment metadata |1549| `session-env/` | Per-session environment metadata |

1549| `tasks/` | Task lists written by the task tools, one directory per list |1550| `tasks/` | Task lists written by the task tools, one directory per list |

1550| `shell-snapshots/` | Aliases, functions, and shell options captured at startup and applied by the [Bash tool](/docs/en/tools-reference#bash-tool-behavior) to each command. Removed on clean exit. The sweep clears any left after a crash. |1551| `shell-snapshots/` | Aliases, functions, and shell options captured at startup and applied by the [Bash tool](/docs/en/tools-reference#bash-tool-behavior) to each command. Removed on clean exit. The sweep clears any left after a crash. |

cli-reference.md +13 −4

Details

80| `--dangerously-skip-permissions` | Skip permission prompts. Equivalent to `--permission-mode bypassPermissions`. See [permission modes](/docs/en/permission-modes#skip-all-checks-with-bypasspermissions-mode) for what this does and does not skip. For sessions started with `--bg`, the mode [persists when the supervisor restarts the session](/docs/en/agent-view#permission-mode-model-and-effort) | `claude --dangerously-skip-permissions` |80| `--dangerously-skip-permissions` | Skip permission prompts. Equivalent to `--permission-mode bypassPermissions`. See [permission modes](/docs/en/permission-modes#skip-all-checks-with-bypasspermissions-mode) for what this does and does not skip. For sessions started with `--bg`, the mode [persists when the supervisor restarts the session](/docs/en/agent-view#permission-mode-model-and-effort) | `claude --dangerously-skip-permissions` |

81| `--debug` | Enable debug mode with optional category filtering, such as `--debug='mcp,startup'` or `--debug='!1p'`. The filter binds only in the `=` form; a space-separated filter enables debug mode without filtering | `claude --debug='mcp,startup'` |81| `--debug` | Enable debug mode with optional category filtering, such as `--debug='mcp,startup'` or `--debug='!1p'`. The filter binds only in the `=` form; a space-separated filter enables debug mode without filtering | `claude --debug='mcp,startup'` |

82| `--debug-file <path>` | Write debug logs to a specific file path. Implicitly enables debug mode. Takes precedence over `CLAUDE_CODE_DEBUG_LOGS_DIR` | `claude --debug-file /tmp/claude-debug.log` |82| `--debug-file <path>` | Write debug logs to a specific file path. Implicitly enables debug mode. Takes precedence over `CLAUDE_CODE_DEBUG_LOGS_DIR` | `claude --debug-file /tmp/claude-debug.log` |

83| `--desktop` | Open the [Claude Desktop app](/docs/en/desktop) on the current directory and exit without starting a session in the terminal. Add `--continue`, or `--resume` with a session ID, to [open that session in Desktop](/docs/en/desktop#coming-from-the-cli) instead. `--resume` here takes only a session ID, not a name or transcript path. Takes no prompt and no other flags except `--verbose` and the `--debug` flags, since the app starts the session itself. Available on macOS and x64 Windows when you're signed in with a Claude subscription. Requires Claude Code v2.1.285 or later | `claude --desktop` |

83| `--disable-slash-commands` | Disable all skills and commands for this session | `claude --disable-slash-commands` |84| `--disable-slash-commands` | Disable all skills and commands for this session | `claude --disable-slash-commands` |

84| `--disallowedTools`, `--disallowed-tools` | Deny rules. A bare tool name removes the matching tools from Claude's context: `"Edit"` removes Edit, `"*"` removes every tool, and `"mcp__*"` removes every MCP tool. A scoped rule such as `Bash(rm *)` leaves the tool available and denies only calls that match [as written](/docs/en/permissions#bash-rule-limits). A rule naming [`EndConversation`](/docs/en/tools-reference#endconversation-tool-behavior) can't remove it while any other tool remains | `"Bash(git log *)" "Bash(git diff *)" "Edit"` |85| `--disallowedTools`, `--disallowed-tools` | Deny rules. A bare tool name removes the matching tools from Claude's context: `"Edit"` removes Edit, `"*"` removes every tool, and `"mcp__*"` removes every MCP tool. A scoped rule such as `Bash(rm *)` leaves the tool available and denies only calls that match [as written](/docs/en/permissions#bash-rule-limits). A rule naming [`EndConversation`](/docs/en/tools-reference#endconversation-tool-behavior) can't remove it while any other tool remains | `"Bash(git log *)" "Bash(git diff *)" "Edit"` |

85| `--effort` | Set the [effort level](/docs/en/model-config#adjust-effort-level) for the current session. Options: `low`, `medium`, `high`, `xhigh`, `max`, or `ultracode`. Available levels depend on the model. `ultracode` requests `xhigh` effort with [ultracode](/docs/en/workflows#let-claude-decide-with-ultracode) turned on, and requires Claude Code v2.1.203 or later. Overrides the [`modelSettings`](/docs/en/settings-reference#modelsettings) and [`effortLevel`](/docs/en/settings-reference#effortlevel) settings for this session and does not persist | `claude --effort high` |86| `--effort` | Set the [effort level](/docs/en/model-config#adjust-effort-level) for the current session. Options: `low`, `medium`, `high`, `xhigh`, `max`, or `ultracode`. Available levels depend on the model. `ultracode` requests `xhigh` effort with [ultracode](/docs/en/workflows#let-claude-decide-with-ultracode) turned on, and requires Claude Code v2.1.203 or later. Overrides the [`modelSettings`](/docs/en/settings-reference#modelsettings) and [`effortLevel`](/docs/en/settings-reference#effortlevel) settings for this session and does not persist | `claude --effort high` |


107| `--no-chrome` | Disable [Chrome browser integration](/docs/en/chrome) for this session | `claude --no-chrome` |108| `--no-chrome` | Disable [Chrome browser integration](/docs/en/chrome) for this session | `claude --no-chrome` |

108| `--no-session-persistence` | Disable session persistence so sessions are not saved to disk and cannot be resumed. Print mode only. The [`CLAUDE_CODE_SKIP_PROMPT_HISTORY`](/docs/en/env-vars) environment variable does the same in any mode | `claude -p --no-session-persistence "query"` |109| `--no-session-persistence` | Disable session persistence so sessions are not saved to disk and cannot be resumed. Print mode only. The [`CLAUDE_CODE_SKIP_PROMPT_HISTORY`](/docs/en/env-vars) environment variable does the same in any mode | `claude -p --no-session-persistence "query"` |

109| `--output-format` | Specify output format for print mode (options: `text`, `json`, `stream-json`) | `claude -p "query" --output-format json` |110| `--output-format` | Specify output format for print mode (options: `text`, `json`, `stream-json`) | `claude -p "query" --output-format json` |

110| `--permission-mode` | Begin in a specified [permission mode](/docs/en/permission-modes). Accepts `default`, `acceptEdits`, `plan`, `auto`, `dontAsk`, `bypassPermissions`, or `manual` as an alias for `default`. The `manual` alias selects the permission mode the UI labels Manual and requires Claude Code v2.1.200 or later; `claude --help` lists it in place of `default`, and both values work. Overrides `defaultMode` from settings files. Without this flag or `--dangerously-skip-permissions`, a new session starts in the permission mode described in [which permission mode a session starts in](/docs/en/permission-modes#which-mode-a-session-starts-in). For `-p`, that's `default` when nothing is configured | `claude --permission-mode plan` |111| `--permission-mode` | Begin in a specified [permission mode](/docs/en/permission-modes). Accepts `default`, `acceptEdits`, `plan`, `auto`, `dontAsk`, `bypassPermissions`, or `manual` as an alias for `default`. The `manual` alias selects the permission mode the UI labels Manual and requires Claude Code v2.1.200 or later; `claude --help` lists it in place of `default`, and both values work. Overrides `defaultMode` from settings files. Without this flag or `--dangerously-skip-permissions`, a new session starts in the permission mode described in [which permission mode a session starts in](/docs/en/permission-modes#which-mode-a-session-starts-in), which also covers what a `-p` run starts in | `claude --permission-mode plan` |

111| `--permission-prompt-tool` | Specify an MCP tool to handle permission prompts in non-interactive mode. Claude Code waits for that tool's MCP server to connect before running the first turn, up to the [`MCP_TIMEOUT`](/docs/en/env-vars) startup timeout, 30 seconds by default. <br /><br />The prompt tool can't approve an MCP tool marked as [requiring user interaction](/docs/en/mcp#require-approval-for-a-specific-tool): Claude Code converts an `allow` result for one to a deny. This restriction requires Claude Code v2.1.199 or later | `claude -p --permission-prompt-tool mcp_auth_tool "query"` |112| `--permission-prompt-tool` | Specify an MCP tool to handle permission prompts in non-interactive mode. Claude Code waits for that tool's MCP server to connect before running the first turn, up to the [`MCP_TIMEOUT`](/docs/en/env-vars) startup timeout, 30 seconds by default. <br /><br />The prompt tool can't approve an MCP tool marked as [requiring user interaction](/docs/en/mcp#require-approval-for-a-specific-tool): Claude Code converts an `allow` result for one to a deny. This restriction requires Claude Code v2.1.199 or later | `claude -p --permission-prompt-tool mcp_auth_tool "query"` |

112| `--permission-prompts` | Set who answers permission prompts in print mode. With the default `host`, Claude Code sends them to the Agent SDK host or the `--permission-prompt-tool` tool. Pass `none` when nobody can answer, and Claude Code denies them instead. See [Turn off permission prompts in unattended runs](/docs/en/headless#turn-off-permission-prompts-in-unattended-runs). Requires Claude Code v2.1.259 or later | `claude -p --permission-prompts none "query"` |113| `--permission-prompts` | Set who answers permission prompts in print mode. With the default `host`, Claude Code sends them to the Agent SDK host or the `--permission-prompt-tool` tool. Pass `none` when nobody can answer, and Claude Code denies them instead. See [Turn off permission prompts in unattended runs](/docs/en/headless#turn-off-permission-prompts-in-unattended-runs). Requires Claude Code v2.1.259 or later | `claude -p --permission-prompts none "query"` |

113| `--plugin-dir` | Load a plugin from a directory or `.zip` archive, or several from a [folder of plugins](/docs/en/plugins/create#load-a-directory-or-archive-for-one-session), for this session only. Each flag takes one path. Repeat the flag for more paths: `--plugin-dir A --plugin-dir B.zip`. Passing a folder of plugins requires Claude Code v2.1.265 or later | `claude --plugin-dir ./my-plugin` |114| `--plugin-dir` | Load a plugin from a directory or `.zip` archive, or several from a [folder of plugins](/docs/en/plugins/create#load-a-directory-or-archive-for-one-session), for this session only. Each flag takes one path. Repeat the flag for more paths: `--plugin-dir A --plugin-dir B.zip`. Passing a folder of plugins requires Claude Code v2.1.265 or later | `claude --plugin-dir ./my-plugin` |

114| `--plugin-url` | Fetch a plugin `.zip` archive from a URL for this session only. Repeat the flag for multiple plugins, or pass space-separated URLs in a single quoted value | `claude --plugin-url https://example.com/plugin.zip` |115| `--plugin-url` | Fetch a plugin `.zip` archive from a URL for this session only. Repeat the flag for multiple plugins, or pass space-separated URLs in a single quoted value | `claude --plugin-url https://example.com/plugin.zip` |

115| `--print`, `-p` | Print response without interactive mode (see [Agent SDK documentation](/docs/en/agent-sdk/overview) for programmatic usage details) | `claude -p "query"` |116| `--print`, `-p` | Print response without interactive mode (see [Agent SDK documentation](/docs/en/agent-sdk/overview) for programmatic usage details). For `--resume` on a background session that is still running, see [Resume a session](/docs/en/sessions#resume-a-running-background-session) | `claude -p "query"` |

116| `--prompt-suggestions` | Emit a `prompt_suggestion` message with a predicted next user prompt after each turn that generates one; very short conversations can produce none. Requires `--print`, `--output-format stream-json`, and `--verbose`. See [Prompt suggestions](/docs/en/interactive-mode#prompt-suggestions) | `claude -p --prompt-suggestions --output-format stream-json --verbose "query"` |117| `--prompt-suggestions` | Emit a `prompt_suggestion` message with a predicted next user prompt after each turn that generates one; very short conversations can produce none. Requires `--print`, `--output-format stream-json`, and `--verbose`. See [Prompt suggestions](/docs/en/interactive-mode#prompt-suggestions) | `claude -p --prompt-suggestions --output-format stream-json --verbose "query"` |

117| `--ref <branch>` | With `--environment`, base the new session's checkout on a named ref instead of local `HEAD` | `claude -p "Run the smoke test" --environment ccpool_abc123 --ref main` |118| `--ref <branch>` | With `--environment`, base the new session's checkout on a named ref instead of local `HEAD` | `claude -p "Run the smoke test" --environment ccpool_abc123 --ref main` |

118| `--remote` | Deprecated alias for `--cloud`, including the existing-session form | `claude --remote "Fix the login bug"` |119| `--remote` | Deprecated alias for `--cloud`, including the existing-session form | `claude --remote "Fix the login bug"` |


120| `--remote-control-session-name-prefix <prefix>` | Prefix for auto-generated [Remote Control](/docs/en/remote-control) session names when no explicit name is set. Defaults to your machine's hostname, producing names like `myhost-graceful-unicorn`. Set `CLAUDE_REMOTE_CONTROL_SESSION_NAME_PREFIX` for the same effect | `claude remote-control --remote-control-session-name-prefix dev-box` |121| `--remote-control-session-name-prefix <prefix>` | Prefix for auto-generated [Remote Control](/docs/en/remote-control) session names when no explicit name is set. Defaults to your machine's hostname, producing names like `myhost-graceful-unicorn`. Set `CLAUDE_REMOTE_CONTROL_SESSION_NAME_PREFIX` for the same effect | `claude remote-control --remote-control-session-name-prefix dev-box` |

121| `--replay-user-messages` | Re-emit user messages from stdin back on stdout for acknowledgment. Requires `--input-format stream-json` and `--output-format stream-json` | `claude -p --input-format stream-json --output-format stream-json --verbose --replay-user-messages` |122| `--replay-user-messages` | Re-emit user messages from stdin back on stdout for acknowledgment. Requires `--input-format stream-json` and `--output-format stream-json` | `claude -p --input-format stream-json --output-format stream-json --verbose --replay-user-messages` |

122| `--restricted` | Start in restricted mode. Use it when an evaluation harness drives `claude` on a shared machine and Claude Code must not run commands or read that machine's user and project settings. Claude Code removes the built-in tools that run commands or code, and WebFetch, unless you name them individually in `--tools`, not through the `default` preset. It also confines the built-in file tools to the [working directories](/docs/en/permissions#working-directories), loads only [managed settings](/docs/en/managed-settings) and `--settings`, refuses [`bypassPermissions`](/docs/en/permission-modes#skip-all-checks-with-bypasspermissions-mode), and [refuses to create cloud sessions](/docs/en/errors#cloud-sessions-cannot-be-created-from-a-restricted-session). Requires Claude Code v2.1.248 or later | `claude --restricted -p "query"` |123| `--restricted` | Start in restricted mode. Use it when an evaluation harness drives `claude` on a shared machine and Claude Code must not run commands or read that machine's user and project settings. Claude Code removes the built-in tools that run commands or code, and WebFetch, unless you name them individually in `--tools`, not through the `default` preset. It also confines the built-in file tools to the [working directories](/docs/en/permissions#working-directories), loads only [managed settings](/docs/en/managed-settings) and `--settings`, refuses [`bypassPermissions`](/docs/en/permission-modes#skip-all-checks-with-bypasspermissions-mode), and [refuses to create cloud sessions](/docs/en/errors#cloud-sessions-cannot-be-created-from-a-restricted-session). Requires Claude Code v2.1.248 or later | `claude --restricted -p "query"` |

123| `--resume`, `-r` | Resume a specific session by ID or name, or show an interactive picker to choose a session. In place of an ID, you can pass the absolute path to a session's `.jsonl` [transcript file](/docs/en/sessions#where-transcripts-are-stored). The picker and name search include sessions that added this directory with `/add-dir`. When you pass a session ID, Claude Code searches the current project directory and its git worktrees, then every other project on this machine. Before v2.1.223, the ID search covered only the current project directory and its git worktrees. [Background sessions](/docs/en/agent-view) appear in the picker marked with `bg` | `claude --resume auth-refactor` |124| `--resume`, `-r` | Resume a specific session by ID or name, or show an interactive picker to choose a session. In place of an ID, you can pass the absolute path to a session's `.jsonl` [transcript file](/docs/en/sessions#where-transcripts-are-stored). The picker and name search include sessions that added this directory with `/add-dir`. When you pass a session ID, Claude Code searches the current project directory and its git worktrees, then every other project on this machine. Before v2.1.223, the ID search covered only the current project directory and its git worktrees. [Background sessions](/docs/en/agent-view) appear in the picker marked with `bg`. Resuming one that is still running [opens that session](/docs/en/sessions#resume-a-running-background-session) in this terminal through `claude attach`, and a prompt you pass on the command line goes to it as its next turn. Before v2.1.285, Claude Code refused and printed the `claude attach` command to run instead | `claude --resume auth-refactor` |

124| `--safe-mode` | Start with all customizations disabled to troubleshoot a broken configuration: CLAUDE.md, skills, plugins, hooks, MCP servers, custom commands and agents, output styles, workflows, custom themes, custom keybindings, status line and file-suggestion commands, LSP servers, and auto memory do not load. Authentication, model selection, built-in tools, and permissions work normally, which differs from [`--bare`](/docs/en/headless#start-faster-with-bare-mode). Managed settings policy still applies, including policy-configured hooks, status line, and file-suggestion commands; managed plugins, managed skills, managed CLAUDE.md, and policy-configured MCP servers do not. Useful for checking whether a customization is what triggers [automatic model fallback](/docs/en/model-config#automatic-model-fallback). Sets [`CLAUDE_CODE_SAFE_MODE`](/docs/en/env-vars) | `claude --safe-mode` |125| `--safe-mode` | Start with all customizations disabled to troubleshoot a broken configuration: CLAUDE.md, skills, plugins, hooks, MCP servers, custom commands and agents, output styles, workflows, custom themes, custom keybindings, status line and file-suggestion commands, LSP servers, and auto memory do not load. Authentication, model selection, built-in tools, and permissions work normally, which differs from [`--bare`](/docs/en/headless#start-faster-with-bare-mode). Managed settings policy still applies, including policy-configured hooks, status line, and file-suggestion commands; managed plugins, managed skills, managed CLAUDE.md, and policy-configured MCP servers do not. Useful for checking whether a customization is what triggers [automatic model fallback](/docs/en/model-config#automatic-model-fallback). Sets [`CLAUDE_CODE_SAFE_MODE`](/docs/en/env-vars) | `claude --safe-mode` |

125| `--session-id` | Use a specific session ID for the conversation (must be a valid UUID) | `claude --session-id "550e8400-e29b-41d4-a716-446655440000"` |126| `--session-id` | Use a specific session ID for the conversation (must be a valid UUID) | `claude --session-id "550e8400-e29b-41d4-a716-446655440000"` |

126| `--setting-sources` | Comma-separated list of setting sources to load (`user`, `project`, `local`). See [agent view](/docs/en/agent-view#what-carries-over-when-you-background) and [agent teams](/docs/en/agent-teams#context-and-communication) for the sessions you start from this one that inherit the list | `claude --setting-sources user,project` |127| `--setting-sources` | Comma-separated list of setting sources to load (`user`, `project`, `local`). See [agent view](/docs/en/agent-view#what-carries-over-when-you-background) and [agent teams](/docs/en/agent-teams#context-and-communication) for the sessions you start from this one that inherit the list | `claude --setting-sources user,project` |


149| `--append-system-prompt-file` | Appends file contents to the default prompt | `claude --append-system-prompt-file ./style-rules.txt` |150| `--append-system-prompt-file` | Appends file contents to the default prompt | `claude --append-system-prompt-file ./style-rules.txt` |

150| `--system-prompt-snapshot` | With `off`, rebuilds the prompt on every request. With `on`, the default, reuses a recorded prompt where [recording applies](#system-prompt-flags-in-resumed-conversations) | `claude --append-system-prompt "Draft rules" --system-prompt-snapshot off` |151| `--system-prompt-snapshot` | With `off`, rebuilds the prompt on every request. With `on`, the default, reuses a recorded prompt where [recording applies](#system-prompt-flags-in-resumed-conversations) | `claude --append-system-prompt "Draft rules" --system-prompt-snapshot off` |

151 152 

152`--system-prompt` and `--system-prompt-file` are mutually exclusive. The append flags can be combined with either replacement flag.153You can combine these flags. To replace the default prompt and still append your own text, pass `--append-system-prompt` or `--append-system-prompt-file` together with `--system-prompt` or `--system-prompt-file`. With Claude Code v2.1.283 or later, you can also pass a flag together with its own file form, such as `--append-system-prompt` with `--append-system-prompt-file`, and Claude Code uses both.

154 

155For example, run the following in your shell to append both a style guide from a file and one extra instruction:

156 

157```bash theme={null}

158claude -p --append-system-prompt-file ./style.md --append-system-prompt "Always reply in French" "Summarize README.md"

159```

160 

161Claude receives the default system prompt followed by the contents of `style.md`, a blank line, and then `Always reply in French`. The file's contents come first even if you pass `--append-system-prompt` before `--append-system-prompt-file`.

153 162 

154When the replacement text combines instructions that are the same on every run with context that changes per run, add a line containing only `__SYSTEM_PROMPT_DYNAMIC_BOUNDARY__` between the instructions and the context. Claude Code splits the prompt at the first such line and removes that line, so the part above it stays cached while the part below changes. Requires Claude Code v2.1.275 or later. [Cache the static part of a custom prompt](/docs/en/agent-sdk/modifying-system-prompts#cache-the-static-part-of-a-custom-prompt) lists the configurations where the split applies.163When the replacement text combines instructions that are the same on every run with context that changes per run, add a line containing only `__SYSTEM_PROMPT_DYNAMIC_BOUNDARY__` between the instructions and the context. Claude Code splits the prompt at the first such line and removes that line, so the part above it stays cached while the part below changes. Requires Claude Code v2.1.275 or later. [Cache the static part of a custom prompt](/docs/en/agent-sdk/modifying-system-prompts#cache-the-static-part-of-a-custom-prompt) lists the configurations where the split applies.

155 164 

Details

52 </Step>52 </Step>

53 53 

54 <Step title="Add or edit an environment">54 <Step title="Add or edit an environment">

55 Select **Add cloud environment**, or hover over an existing environment and select the settings icon that appears on the right. The dialog includes the name, network access level, environment variables, and setup script. When you edit an existing cloud environment on a Pro or Max plan, the dialog also includes [API credentials](#add-api-credentials).55 Select **Cloud** to list your environments. Then select **Add cloud environment**, or hover over an existing environment and select the settings icon that appears on the right.

56 

57 The dialog includes the name, network access level, environment variables, and setup script. When you edit an existing cloud environment on a Pro or Max plan, the dialog also includes [API credentials](#add-api-credentials).

56 58 

57 <Frame>59 <Frame>

58 <img src="https://mintcdn.com/claude-code/ZFId6l95856c5LSw/images/cloud-environment-dialog.png?fit=max&auto=format&n=ZFId6l95856c5LSw&q=85&s=30d4478b31d1f879f7ee287ddab32505" alt="The New cloud environment dialog. A Name field with the placeholder Default, a Network access selector set to Trusted with links to the network policy and access levels, an Environment variables box showing .env-format placeholder text with a note that values are visible to anyone using the environment, a Setup script box described as a Bash script that runs when a new session starts before Claude Code launches, and Cancel and Create environment buttons." width="874" height="1372" data-path="images/cloud-environment-dialog.png" />60 <img src="https://mintcdn.com/claude-code/ZFId6l95856c5LSw/images/cloud-environment-dialog.png?fit=max&auto=format&n=ZFId6l95856c5LSw&q=85&s=30d4478b31d1f879f7ee287ddab32505" alt="The New cloud environment dialog. A Name field with the placeholder Default, a Network access selector set to Trusted with links to the network policy and access levels, an Environment variables box showing .env-format placeholder text with a note that values are visible to anyone using the environment, a Setup script box described as a Bash script that runs when a new session starts before Claude Code launches, and Cancel and Create environment buttons." width="874" height="1372" data-path="images/cloud-environment-dialog.png" />


98* **Role**: an organization admin role in your claude.ai organization100* **Role**: an organization admin role in your claude.ai organization

99 * On Team and Enterprise, Owners hold it and Admins don't101 * On Team and Enterprise, Owners hold it and Admins don't

100 * On Pro and Max, you hold it in your own organization102 * On Pro and Max, you hold it in your own organization

101 * Without it, you see a note instead of the credential list, on your own environments too. Ask an Owner to add the credential to a shared environment and run your sessions there

102* **Environment type**: an Anthropic-hosted cloud environment that already exists. A [self-hosted environment](/docs/en/self-hosted-environments) doesn't have API credentials103* **Environment type**: an Anthropic-hosted cloud environment that already exists. A [self-hosted environment](/docs/en/self-hosted-environments) doesn't have API credentials

103* **API reachability**: the API accepts connections from the internet, because requests leave from Anthropic's network104* **API reachability**: the API accepts connections from the internet, because requests leave from Anthropic's network

104* **Encryption keys**: if your organization uses customer-managed encryption keys, you can't save credentials105* **Encryption keys**: if your organization uses customer-managed encryption keys, you can't save credentials


109 110 

110<Steps>111<Steps>

111 <Step title="Open the environment's API credentials">112 <Step title="Open the environment's API credentials">

112 [Open the environment for editing](#configure-your-environment) at [claude.ai/code](https://claude.ai/code). In the **Update cloud environment** dialog, find **API credentials** below **Environment variables**. You see the credentials already on the environment, each with the hosts it applies to.113 [Open the environment for editing](#configure-your-environment) at [claude.ai/code](https://claude.ai/code). In the **Edit cloud environment** dialog, find **API credentials** below **Environment variables**. You see the credentials already on the environment, each with the hosts it applies to.

113 </Step>114 </Step>

114 115 

115 <Step title="Add the credential">116 <Step title="Add the credential">


383 384 

384* **Commands Claude runs**: a cloud environment doesn't set its own command timeout, so the Bash tool's defaults apply. Claude waits 2 minutes for a foreground command by default and can ask for up to 10 minutes.385* **Commands Claude runs**: a cloud environment doesn't set its own command timeout, so the Bash tool's defaults apply. Claude waits 2 minutes for a foreground command by default and can ask for up to 10 minutes.

385 386 

386 When a command reaches its [timeout](/docs/en/tools-reference#timeout-and-output-limits), Claude Code [moves it to the background](/docs/en/tools-reference#background-commands) instead of stopping it, unless the command starts with `sleep`. A command moved this way can keep running for up to 30 more minutes before Claude Code stops it at its [background time limit](/docs/en/tools-reference#background-commands). Setting `BASH_DEFAULT_TIMEOUT_MS` above `1800000` milliseconds lengthens that limit as well as the foreground default.387 When a command reaches its [timeout](/docs/en/tools-reference#timeout-and-output-limits), Claude Code [moves it to the background](/docs/en/tools-reference#foreground-commands-that-move-to-the-background) instead of stopping it, unless the command starts with `sleep`. A command moved this way can keep running for up to 30 more minutes before Claude Code stops it at its [background time limit](/docs/en/tools-reference#time-limit-for-background-commands). Setting `BASH_DEFAULT_TIMEOUT_MS` above `1800000` milliseconds lengthens that limit as well as the foreground default.

387* **SessionStart hooks**: Claude Code cancels a `command` hook after 600 seconds unless you set [`timeout`](/docs/en/hooks#common-fields), in seconds, on the hook entry. Claude Code doesn't enforce the timeout on a hook you run with [`async: true`](/docs/en/hooks#run-hooks-in-the-background).388* **SessionStart hooks**: Claude Code cancels a `command` hook after 600 seconds unless you set [`timeout`](/docs/en/hooks#common-fields), in seconds, on the hook entry. Claude Code doesn't enforce the timeout on a hook you run with [`async: true`](/docs/en/hooks#run-hooks-in-the-background).

388* **Setup script**: a script that takes longer than roughly five minutes isn't cached. [Script requirements](#script-requirements) covers how to stay under that.389* **Setup script**: a script that takes longer than roughly five minutes isn't cached. [Script requirements](#script-requirements) covers how to stay under that.

389* **Idle sessions**: after a few minutes without activity, a session's VM pauses with its files saved, and a paused VM can later be reclaimed. [Set environment variables](#set-environment-variables) describes what a session picks up in each case, and [Environment expired](/docs/en/claude-code-on-the-web#environment-expired) covers how to reopen a session whose VM was reclaimed.390* **Idle sessions**: after a few minutes without activity, a session's VM pauses with its files saved, and a paused VM can later be reclaimed. [Set environment variables](#set-environment-variables) describes what a session picks up in each case, and [Environment expired](/docs/en/claude-code-on-the-web#environment-expired) covers how to reopen a session whose VM was reclaimed.

commands.md +1 −1

Details

128| `/remote-control` | Make this session available for [Remote Control](/docs/en/remote-control) from claude.ai. Running it while signed out prints that Remote Control requires a claude.ai subscription and tells you how to sign in; before v2.1.206 it reported `Unknown command: /remote-control`. Alias: `/rc` |128| `/remote-control` | Make this session available for [Remote Control](/docs/en/remote-control) from claude.ai. Running it while signed out prints that Remote Control requires a claude.ai subscription and tells you how to sign in; before v2.1.206 it reported `Unknown command: /remote-control`. Alias: `/rc` |

129| `/remote-env` | Choose the default [cloud environment](/docs/en/cloud-environments#select-an-environment-from-the-cli) for cloud sessions you start from the CLI |129| `/remote-env` | Choose the default [cloud environment](/docs/en/cloud-environments#select-an-environment-from-the-cli) for cloud sessions you start from the CLI |

130| `/rename [name]` | Rename the current session and show the name on the prompt bar. Without a name, auto-generates one from conversation history. Also available in non-interactive mode (`-p`); requires Claude Code v2.1.205 or later. From every rename surface, including claude.ai and the desktop app, Claude Code replaces control and invisible characters in the new name with spaces and caps the name at 200 characters. If the name is empty once invisible characters are removed, Claude Code rejects it and shows `That name is empty once invisible characters are removed. Usage: /rename <name>`. The character replacement and length cap require Claude Code v2.1.221 or later. If another live session on this machine already uses a name you pass, Claude Code applies [a variant of it](/docs/en/sessions#name-your-sessions) instead |130| `/rename [name]` | Rename the current session and show the name on the prompt bar. Without a name, auto-generates one from conversation history. Also available in non-interactive mode (`-p`); requires Claude Code v2.1.205 or later. From every rename surface, including claude.ai and the desktop app, Claude Code replaces control and invisible characters in the new name with spaces and caps the name at 200 characters. If the name is empty once invisible characters are removed, Claude Code rejects it and shows `That name is empty once invisible characters are removed. Usage: /rename <name>`. The character replacement and length cap require Claude Code v2.1.221 or later. If another live session on this machine already uses a name you pass, Claude Code applies [a variant of it](/docs/en/sessions#name-your-sessions) instead |

131| `/resume [session]` | Resume a conversation by ID or name, or open the session picker. [Background sessions](/docs/en/agent-view) appear in the picker marked with `bg`; one that is still running can't be resumed here, so attach to it from `claude agents` or stop it there first. Alias: `/continue` |131| `/resume [session]` | Resume a conversation by ID or name, or open the session picker. [Background sessions](/docs/en/agent-view) appear in the picker marked with `bg`. Resuming one that is still running, from the picker or by ID or name, [opens that session](/docs/en/sessions#resume-a-running-background-session): your current conversation moves to the background and this terminal attaches to the running one. Press `←` on an empty prompt to return to agent view, which also lists the conversation you left. Before v2.1.285, Claude Code refused and told you to open the session with `claude attach` or stop it first. Alias: `/continue` |

132| `/review [low\|medium\|high\|xhigh\|max\|ultra] [--fix] [--comment] [pr#\|branch\|path]` | Alias of [`/code-review`](/docs/en/code-review#review-a-diff-locally): reviews the current diff, or a PR number, branch, or path you pass, such as `/review 1234`, and takes the same effort levels and flags. With no level given, the review reuses the last `low` through `max` level you typed; see [Review a diff locally](/docs/en/code-review#review-a-diff-locally) for the exact rules. For a deep cloud review, use [`/code-review ultra`](/docs/en/ultrareview). Before v2.1.223, `/review` was a separate command that ran a single-pass, read-only review of a GitHub pull request by number, listing open PRs to pick from when run with no argument; from v2.1.186 through v2.1.201, it ran the same multi-agent engine as `/code-review medium` |132| `/review [low\|medium\|high\|xhigh\|max\|ultra] [--fix] [--comment] [pr#\|branch\|path]` | Alias of [`/code-review`](/docs/en/code-review#review-a-diff-locally): reviews the current diff, or a PR number, branch, or path you pass, such as `/review 1234`, and takes the same effort levels and flags. With no level given, the review reuses the last `low` through `max` level you typed; see [Review a diff locally](/docs/en/code-review#review-a-diff-locally) for the exact rules. For a deep cloud review, use [`/code-review ultra`](/docs/en/ultrareview). Before v2.1.223, `/review` was a separate command that ran a single-pass, read-only review of a GitHub pull request by number, listing open PRs to pick from when run with no argument; from v2.1.186 through v2.1.201, it ran the same multi-agent engine as `/code-review medium` |

133| `/rewind` | Rewind the conversation and/or code to a previous point, or summarize from a selected message. See [checkpointing](/docs/en/checkpointing). Aliases: `/checkpoint`, `/undo` |133| `/rewind` | Rewind the conversation and/or code to a previous point, or summarize from a selected message. See [checkpointing](/docs/en/checkpointing). Aliases: `/checkpoint`, `/undo` |

134| `/run` | **[Skill](/docs/en/skills#bundled-skills).** Launch and drive your project's app to see a change working, not only passing tests. See [Run and verify your app](/docs/en/skills#run-and-verify-your-app) |134| `/run` | **[Skill](/docs/en/skills#bundled-skills).** Launch and drive your project's app to see a change working, not only passing tests. See [Run and verify your app](/docs/en/skills#run-and-verify-your-app) |

Details

1628 1628 

1629If you need a larger window rather than a smaller conversation, Fable models, Sonnet 5 and later, Opus 4.6 and later, and Sonnet 4.6 support a 1 million token context window. See [Extended context](/docs/en/model-config#extended-context) for availability by plan and how to select a `[1m]` model variant. Compaction works the same way at the larger limit.1629If you need a larger window rather than a smaller conversation, Fable models, Sonnet 5 and later, Opus 4.6 and later, and Sonnet 4.6 support a 1 million token context window. See [Extended context](/docs/en/model-config#extended-context) for availability by plan and how to select a `[1m]` model variant. Compaction works the same way at the larger limit.

1630 1630 

1631Sonnet 5.5 and Sonnet 5 run with the 1M context window and have no `[1m]` variant to select. See [Sonnet 5.5 and Sonnet 5 context window](/docs/en/model-config#sonnet-5-5-and-sonnet-5-context-window) for their auto-compaction thresholds and the LLM gateway exception.1631Sonnet 5.5 and Sonnet 5 run with the 1M context window and have no `[1m]` variant to select. See [Sonnet 5.5 and Sonnet 5 context window](/docs/en/model-config#sonnet-5-5-and-sonnet-5-context-window) for their auto-compaction thresholds, and [the context window behind a gateway](/docs/en/model-config#context-window-behind-a-gateway) for how Claude Code sizes the window when you set `ANTHROPIC_BASE_URL` to an [LLM gateway](/docs/en/llm-gateway).

1632 1632 

1633The point where automatic compaction runs depends on your model and configuration. See [Default auto-compact thresholds](/docs/en/model-config#default-auto-compact-thresholds) for the boundaries per model, and [Correct the window for a gateway or custom model ID](/docs/en/model-config#correct-the-window-for-a-gateway-or-custom-model-id) if Claude Code assumes the wrong window for your model ID, such as an [LLM gateway](/docs/en/llm-gateway) alias.1633The point where automatic compaction runs depends on your model and configuration. See [Default auto-compact thresholds](/docs/en/model-config#default-auto-compact-thresholds) for the boundaries per model, and [Correct the window for a gateway or custom model ID](/docs/en/model-config#correct-the-window-for-a-gateway-or-custom-model-id) if Claude Code assumes the wrong window for your model ID, such as an [LLM gateway](/docs/en/llm-gateway) alias.

1634 1634 

costs.md +2 −2

Details

45 45 

46The misses, expected rebuilds, and warm or cold parts of the line mean the following:46The misses, expected rebuilds, and warm or cold parts of the line mean the following:

47 47 

48* **Misses**: requests that re-processed content the cache already held, with the time of the last miss and how many tokens those requests wrote back to the cache. Claude Code counts a request as a miss when the request re-processed more than 5% and at least 2,000 tokens of what it could have read from cache. [Actions that invalidate the cache](/docs/en/prompt-caching#actions-that-invalidate-the-cache) lists the usual causes. When Claude Code can identify a likely cause for the last miss, the line names it too, for example `likely cause: tool definitions changed`. The likely-cause text requires Claude Code v2.1.260 or later.48* **Misses**: requests that re-processed content the cache already held, with the time of the last miss and how many tokens those requests wrote back to the cache. [Actions that invalidate the cache](/docs/en/prompt-caching#actions-that-invalidate-the-cache) lists the usual causes. When Claude Code can identify a likely cause for the last miss, the line names it too, for example `likely cause: tool definitions changed`. The likely-cause text requires Claude Code v2.1.260 or later.

49* **Expected rebuilds**: when Claude Code has itself just rewritten the conversation, by [compaction](/docs/en/prompt-caching#compacting-the-conversation) or by clearing old tool results from context, it counts the same kind of miss as an expected rebuild instead. This part appears only after at least one expected rebuild has happened.49* **Expected rebuilds**: when Claude Code has itself just rewritten the conversation, by [compaction](/docs/en/prompt-caching#compacting-the-conversation) or by clearing old tool results from context, it counts the same kind of miss as an expected rebuild instead. This part appears only after at least one expected rebuild has happened.

50* **Warm or cold**: whether the cached prefix is still within its [cache lifetime](/docs/en/prompt-caching#cache-lifetime), with the TTL in effect. When the cache is cold, the line shows how long the session has been idle. When no response has reported cache tokens, the line ends with `no prompt caching reported by the API` instead.50* **Warm or cold**: whether the cached prefix is still within its [cache lifetime](/docs/en/prompt-caching#cache-lifetime), with the TTL in effect. When the cache is cold, the line shows how long the session has been idle. When no response has reported cache tokens, the line ends with `no prompt caching reported by the API` instead.

51 51 


227Use `/usage` to check your current token usage, or [configure your status line](/docs/en/statusline#context-window-usage) to display it continuously.227Use `/usage` to check your current token usage, or [configure your status line](/docs/en/statusline#context-window-usage) to display it continuously.

228 228 

229* **Clear between tasks**: Use `/clear` to start fresh when switching to unrelated work. Stale context wastes tokens on every subsequent message. Use `/rename` before clearing so you can easily find the session later, then `/resume` to return to it.229* **Clear between tasks**: Use `/clear` to start fresh when switching to unrelated work. Stale context wastes tokens on every subsequent message. Use `/rename` before clearing so you can easily find the session later, then `/resume` to return to it.

230* **Add custom compaction instructions**: `/compact Focus on code samples and API usage` tells Claude what to preserve during summarization. In a fresh session, `/compact` prints `Not enough messages to compact.` because there's no conversation history to summarize yet.230* **Add custom compaction instructions**: `/compact Focus on code samples and API usage` tells Claude what to preserve during summarization.

231 231 

232You can also customize compaction behavior in your CLAUDE.md file at the root of your project:232You can also customize compaction behavior in your CLAUDE.md file at the root of your project:

233 233 

Details

57 57 

58## Check hooks58## Check hooks

59 59 

60Run `/hooks` to list every hook registered for the current session, grouped by event. If a hook you defined doesn't appear, it isn't being read: hooks go under the `"hooks"` key in a settings file, not in a standalone file.60Run `/hooks` to list every hook registered for the current session, grouped by event. If a hook you defined doesn't appear, Claude Code didn't load it. Check for these causes:

61 

62* The hook is defined in a standalone file. Hooks go under the `"hooks"` key in a [settings file](/docs/en/settings#settings-files).

63* The `matcher` value is an array instead of a single string. Claude Code lists the entry as an invalid setting when you start an interactive session and in `claude doctor`. If the array is under `PreToolUse` or `PermissionRequest`, none of that file's other hooks load either.

61 64 

62If the hook appears but doesn't fire, the matcher is the usual cause. Check it for these mistakes:65If the hook appears but doesn't fire, the matcher is the usual cause. Check it for these mistakes:

63 66 

64* The `matcher` field is a single string that uses `|` to match multiple tool names, for example `"Edit|Write"`. A `,` separator is equivalent, so `"Edit,Write"` matches the same tools. Before v2.1.191, a comma fell through to regex evaluation and the matcher never matched, so use `|` if you aren't on v2.1.191 yet.67* The `matcher` field is a single string that uses `|` to match multiple tool names, for example `"Edit|Write"`. A `,` separator is equivalent, so `"Edit,Write"` matches the same tools. Before v2.1.191, a comma fell through to regex evaluation and the matcher never matched, so use `|` if you aren't on v2.1.191 yet.

65* A misspelled tool name produces a matcher that matches nothing, so the hook fails silently.68* A misspelled tool name produces a matcher that matches nothing, so the hook fails silently.

66* An array value is a schema error: Claude Code shows a settings error notice and rejects the whole user, project, or local settings file, `claude doctor` reports the validation failure, and no hook from that file appears in `/hooks`. In [managed settings](/docs/en/managed-settings), Claude Code drops the whole `hooks` key from the file that contains the array, so none of that file's hooks apply. The file's other settings still apply, and `claude doctor` lists the dropped key.

67 69 

68When you edit `settings.json`, the change takes effect in the running session after a brief file-stability delay, even if you create the file or the project's `.claude/` folder itself after the session started. You don't need to restart. Before v2.1.257, Claude Code didn't detect edits in a `.claude/` folder created after the session started.70When you edit `settings.json`, the change takes effect in the running session after a brief file-stability delay, even if you create the file or the project's `.claude/` folder itself after the session started. You don't need to restart. Before v2.1.257, Claude Code didn't detect edits in a `.claude/` folder created after the session started.

69 71 

desktop.md +9 −1

Details

657 657 

658Cloud sessions continue in the background even if you close the app. Usage counts toward your [subscription plan limits](/docs/en/costs) with no separate compute charges.658Cloud sessions continue in the background even if you close the app. Usage counts toward your [subscription plan limits](/docs/en/costs) with no separate compute charges.

659 659 

660You can create custom cloud environments with different network access levels and environment variables. When you start a cloud session, open the environment dropdown in the prompt box to manage them:660You can create custom cloud environments with different network access levels and environment variables. To manage them, open the environment dropdown in the prompt box and select **Cloud**:

661 661 

662* **Add an environment**: select **Add cloud environment**662* **Add an environment**: select **Add cloud environment**

663* **Edit or archive one of your own environments**: hover over it and click the gear icon663* **Edit or archive one of your own environments**: hover over it and click the gear icon


849 849 

850To move a CLI session into Desktop, run `/desktop` in the terminal. Claude saves your session and opens it in the desktop app, then exits the CLI. This command is available on macOS and x64 Windows when you are signed in with a Claude subscription. It is not available with API key authentication or on Amazon Bedrock, Google Cloud's Agent Platform, or Microsoft Foundry.850To move a CLI session into Desktop, run `/desktop` in the terminal. Claude saves your session and opens it in the desktop app, then exits the CLI. This command is available on macOS and x64 Windows when you are signed in with a Claude subscription. It is not available with API key authentication or on Amazon Bedrock, Google Cloud's Agent Platform, or Microsoft Foundry.

851 851 

852From your shell, [`claude --desktop`](/docs/en/cli-reference#cli-flags) opens Desktop directly without starting a terminal session. It requires Claude Code v2.1.285 or later and has the same platform and sign-in requirements as `/desktop`. With no other arguments, it opens Desktop on the current directory. To open an existing CLI session in Desktop, add `--continue` for the most recent conversation in this directory, or `--resume` with the session ID that `/status` shows:

853 

854```bash theme={null}

855claude --desktop --resume <session-id>

856```

857 

858Claude Code prints `Opening session <session-id> in Claude Desktop`, the session opens in the app, and the command exits. A session name doesn't work in place of the ID. Claude Code doesn't move a session that is open in another terminal or still running in the background. If Claude Desktop isn't installed, the command prints a download link and exits.

859 

852You can also pick up a CLI session from inside Desktop with `/resume`. The command is available in local sessions, not in SSH, WSL, or cloud sessions.860You can also pick up a CLI session from inside Desktop with `/resume`. The command is available in local sessions, not in SSH, WSL, or cloud sessions.

853 861 

854To continue a terminal session in Desktop:862To continue a terminal session in Desktop:

desktop-linux.md +10 −0

Details

145 145 

146If `claude-desktop` exits with this message, you launched it as root. Log in as a regular user and launch it from there.146If `claude-desktop` exits with this message, you launched it as root. Log in as a regular user and launch it from there.

147 147 

148### Your sign-in won't be saved on this device

149 

150Claude Desktop saves your sign-in in your desktop's keyring, such as GNOME Keyring or KDE Wallet. If it can't reach an unlocked keyring, your sign-in isn't saved and you sign in again each time you launch the app. Pick the case that matches your system:

151 

152* **No keyring installed, on a desktop other than KDE Plasma**: if you installed with `--no-install-recommends`, or on a minimal image that skips recommended packages, apt didn't install a keyring. Install GNOME Keyring with `sudo apt install gnome-keyring`.

153* **KDE Plasma with GNOME Keyring also installed**: KDE Wallet comes with the Plasma desktop. The two keyrings conflict, and Claude Desktop can show this notice even though KDE Wallet works. Remove the extra one with `sudo apt remove gnome-keyring`, then restart your computer.

154* **Keyring installed but locked**: unlock it.

155 

156After the fix, restart the app and sign in. Then quit and launch it again to confirm the app opens with you still signed in.

157 

148### Cowork isn't available158### Cowork isn't available

149 159 

150If the Cowork tab shows one of these messages, fix the requirement it names, then restart the app:160If the Cowork tab shows one of these messages, fix the requirement it names, then restart the app:

env-vars.md +7 −4

Details

52 </Tab>52 </Tab>

53</Tabs>53</Tabs>

54 54 

55The assignment line prints nothing on success, so confirm the variable is set by printing it in the same shell before you run `claude`:55The assignment line prints nothing on success. To confirm the variable is set, print it in the same shell:

56 56 

57<Tabs>57<Tabs>

58 <Tab title="macOS, Linux, WSL">58 <Tab title="macOS, Linux, WSL">


117Numeric variables such as timeouts, token budgets, and retry counts accept scientific notation and digit-separator spellings in addition to plain digits, except where a variable's row notes it takes plain digits only. For example, Claude Code reads `2e3` as 2000 and `64_000` as 64000. Before v2.1.211, these spellings could silently set a much smaller value, such as `1e6` setting a timeout to 1.117Numeric variables such as timeouts, token budgets, and retry counts accept scientific notation and digit-separator spellings in addition to plain digits, except where a variable's row notes it takes plain digits only. For example, Claude Code reads `2e3` as 2000 and `64_000` as 64000. Before v2.1.211, these spellings could silently set a much smaller value, such as `1e6` setting a timeout to 1.

118 118 

119<Note>119<Note>

120 For variables that turn a behavior on or off, set `1` or `true` to turn it on and `0` or `false` to turn it off, in any casing.120 For variables that turn a behavior on or off, set `1`, `true`, `yes`, or `on` to turn it on and `0`, `false`, `no`, or `off` to turn it off, in any casing.

121 121 

122 Some variables read only whether you set them at all, so any non-empty value including `0` turns the behavior on, and you turn the behavior off by unsetting the variable or setting it to an empty value. These variables work that way:122 Some variables read only whether you set them at all, so any non-empty value including `0` turns the behavior on, and you turn the behavior off by unsetting the variable or setting it to an empty value. These variables work that way:

123 123 


182| `API_FORCE_IDLE_TIMEOUT` | Override the 5-minute body idle timeout that aborts a streaming model response when no bytes arrive. Set to `0` to turn the timeout off, for example when a slow [gateway](/docs/en/llm-gateway) or local model pauses longer than 5 minutes between chunks, or `1` to keep it on for every provider. When unset, the timeout is active on providers other than the direct Anthropic API, [Claude Platform on AWS](/docs/en/claude-platform-on-aws), and Amazon Bedrock with `CLAUDE_ENABLE_BYTE_WATCHDOG_BEDROCK=1` set. The [stream watchdogs](/docs/en/network-config#streaming-idle-watchdogs) run independently of it and abort a long silent pause even when you set `0` here |182| `API_FORCE_IDLE_TIMEOUT` | Override the 5-minute body idle timeout that aborts a streaming model response when no bytes arrive. Set to `0` to turn the timeout off, for example when a slow [gateway](/docs/en/llm-gateway) or local model pauses longer than 5 minutes between chunks, or `1` to keep it on for every provider. When unset, the timeout is active on providers other than the direct Anthropic API, [Claude Platform on AWS](/docs/en/claude-platform-on-aws), and Amazon Bedrock with `CLAUDE_ENABLE_BYTE_WATCHDOG_BEDROCK=1` set. The [stream watchdogs](/docs/en/network-config#streaming-idle-watchdogs) run independently of it and abort a long silent pause even when you set `0` here |

183| `API_TIMEOUT_MS` | Timeout for API requests in milliseconds (default: 600000, or 10 minutes; maximum: 2147483647). Increase this when requests time out on slow networks or when routing through a proxy. Values above the maximum overflow the underlying timer and cause requests to fail immediately |183| `API_TIMEOUT_MS` | Timeout for API requests in milliseconds (default: 600000, or 10 minutes; maximum: 2147483647). Increase this when requests time out on slow networks or when routing through a proxy. Values above the maximum overflow the underlying timer and cause requests to fail immediately |

184| `AWS_BEARER_TOKEN_BEDROCK` | Amazon Bedrock API key for authentication (see [Amazon Bedrock API keys](https://aws.amazon.com/blogs/machine-learning/accelerate-ai-development-with-amazon-bedrock-api-keys/)) |184| `AWS_BEARER_TOKEN_BEDROCK` | Amazon Bedrock API key for authentication (see [Amazon Bedrock API keys](https://aws.amazon.com/blogs/machine-learning/accelerate-ai-development-with-amazon-bedrock-api-keys/)) |

185| `BASH_DEFAULT_TIMEOUT_MS` | Default timeout for a foreground Bash or PowerShell tool command, in milliseconds (default: 120000, or 2 minutes). A default longer than 30 minutes also becomes the default [time limit for background commands](/docs/en/tools-reference#background-commands). The background time limit requires Claude Code v2.1.285 or later |185| `BASH_DEFAULT_TIMEOUT_MS` | Default timeout for a foreground Bash or PowerShell tool command, in milliseconds (default: 120000, or 2 minutes). A default longer than 30 minutes also becomes the default [time limit for background commands](/docs/en/tools-reference#time-limit-for-background-commands). The background time limit requires Claude Code v2.1.285 or later |

186| `BASH_MAX_OUTPUT_LENGTH` | Maximum number of characters of bash output that Claude Code reads back into a command's result (default: 30000; maximum: 150000). If you set the [`bashOutputMaxChars`](/docs/en/settings-reference#bashoutputmaxchars) setting, Claude Code ignores this variable. See [Output limits](/docs/en/tools-reference#output-limits) |186| `BASH_MAX_OUTPUT_LENGTH` | Maximum number of characters of bash output that Claude Code reads back into a command's result (default: 30000; maximum: 150000). If you set the [`bashOutputMaxChars`](/docs/en/settings-reference#bashoutputmaxchars) setting, Claude Code ignores this variable. See [Output limits](/docs/en/tools-reference#output-limits) |

187| `BASH_MAX_TIMEOUT_MS` | Maximum timeout the model can set for a foreground Bash or PowerShell tool command, in milliseconds (default: 600000, or 10 minutes). The effective ceiling is the larger of this and `BASH_DEFAULT_TIMEOUT_MS`. An effective ceiling longer than 2 hours also becomes the maximum [time limit for background commands](/docs/en/tools-reference#background-commands). The background time limit requires Claude Code v2.1.285 or later |187| `BASH_MAX_TIMEOUT_MS` | Maximum timeout the model can set for a foreground Bash or PowerShell tool command, in milliseconds (default: 600000, or 10 minutes). The effective ceiling is the larger of this and `BASH_DEFAULT_TIMEOUT_MS`. An effective ceiling longer than 2 hours also becomes the maximum [time limit for background commands](/docs/en/tools-reference#time-limit-for-background-commands). The background time limit requires Claude Code v2.1.285 or later |

188| `BETA_TRACING_ENDPOINT` | OTLP endpoint for [detailed beta tracing](/docs/en/monitoring-usage#traces-beta): with `ENABLE_BETA_TRACING_DETAILED=1`, logs and traces go there instead of to the configured exporters. Set it in your shell, user settings, or managed settings. Ignored in [project and local settings](/docs/en/settings-reference#variables-claude-code-ignores-in-env) |188| `BETA_TRACING_ENDPOINT` | OTLP endpoint for [detailed beta tracing](/docs/en/monitoring-usage#traces-beta): with `ENABLE_BETA_TRACING_DETAILED=1`, logs and traces go there instead of to the configured exporters. Set it in your shell, user settings, or managed settings. Ignored in [project and local settings](/docs/en/settings-reference#variables-claude-code-ignores-in-env) |

189| `CCR_FORCE_BUNDLE` | Set to `1` to force [`claude --cloud`](/docs/en/claude-code-on-the-web#send-local-repositories-without-github) to bundle and upload your local repository instead of cloning from its remote |189| `CCR_FORCE_BUNDLE` | Set to `1` to force [`claude --cloud`](/docs/en/claude-code-on-the-web#send-local-repositories-without-github) to bundle and upload your local repository instead of cloning from its remote |

190| `CLAUDECODE` | Set to `1` in subprocesses Claude Code spawns (Bash and PowerShell tools, tmux sessions, [hook](/docs/en/hooks) commands, [status line](/docs/en/statusline) commands, stdio [MCP server](/docs/en/mcp) subprocesses). IDE extensions also set this in their integrated terminals. Use to detect when a script is running inside a subprocess spawned by Claude Code. To check whether the current process was spawned directly by a tool call or hook, rather than inside a stdio MCP server that Claude Code started, use `CLAUDE_CODE_CHILD_SESSION` instead |190| `CLAUDECODE` | Set to `1` in subprocesses Claude Code spawns (Bash and PowerShell tools, tmux sessions, [hook](/docs/en/hooks) commands, [status line](/docs/en/statusline) commands, stdio [MCP server](/docs/en/mcp) subprocesses). IDE extensions also set this in their integrated terminals. Use to detect when a script is running inside a subprocess spawned by Claude Code. To check whether the current process was spawned directly by a tool call or hook, rather than inside a stdio MCP server that Claude Code started, use `CLAUDE_CODE_CHILD_SESSION` instead |


253| `CLAUDE_CODE_DISABLE_FILE_CHECKPOINTING` | Set to `1` to disable file [checkpointing](/docs/en/checkpointing). The `/rewind` command will not be able to restore code changes. Overrides the [`fileCheckpointingEnabled`](/docs/en/settings-reference#filecheckpointingenabled) setting |253| `CLAUDE_CODE_DISABLE_FILE_CHECKPOINTING` | Set to `1` to disable file [checkpointing](/docs/en/checkpointing). The `/rewind` command will not be able to restore code changes. Overrides the [`fileCheckpointingEnabled`](/docs/en/settings-reference#filecheckpointingenabled) setting |

254| `CLAUDE_CODE_DISABLE_GIT_INSTRUCTIONS` | Set to `1` to remove built-in commit and PR workflow instructions and the git status snapshot from Claude's context. Useful when using your own git workflow skills. Takes precedence over the [`includeGitInstructions`](/docs/en/settings-reference#includegitinstructions) setting when set |254| `CLAUDE_CODE_DISABLE_GIT_INSTRUCTIONS` | Set to `1` to remove built-in commit and PR workflow instructions and the git status snapshot from Claude's context. Useful when using your own git workflow skills. Takes precedence over the [`includeGitInstructions`](/docs/en/settings-reference#includegitinstructions) setting when set |

255| `CLAUDE_CODE_DISABLE_LEGACY_MODEL_REMAP` | Set to `1` to prevent automatic remapping of Opus 4.0 and 4.1 to the current Opus version on the Anthropic API. Use when you intentionally want to pin an older model. The remap does not run on Amazon Bedrock, Google Cloud's Agent Platform, or Microsoft Foundry |255| `CLAUDE_CODE_DISABLE_LEGACY_MODEL_REMAP` | Set to `1` to prevent automatic remapping of Opus 4.0 and 4.1 to the current Opus version on the Anthropic API. Use when you intentionally want to pin an older model. The remap does not run on Amazon Bedrock, Google Cloud's Agent Platform, or Microsoft Foundry |

256| `CLAUDE_CODE_DISABLE_MODEL_ACCESS_FALLBACK` | Set to `1` to stop Claude Code on [Amazon Bedrock](/docs/en/amazon-bedrock#when-a-model-is-disabled-mid-session) and [Google Cloud's Agent Platform](/docs/en/google-vertex-ai#when-a-model-is-disabled-mid-session) from switching to an older model when your account loses access to a session's model mid-session; the refused request fails at once instead. A [fallback model chain](/docs/en/model-config#fallback-model-chains) you configure still switches on that refusal, and the [startup model checks](/docs/en/amazon-bedrock#startup-model-checks) still fall back at launch. Requires Claude Code v2.1.285 or later |

256| `CLAUDE_CODE_DISABLE_MOUSE` | Set to `1` to disable mouse tracking in [fullscreen rendering](/docs/en/fullscreen). Keyboard scrolling with `PgUp` and `PgDn` still works. Use this to keep your terminal's native copy-on-select behavior |257| `CLAUDE_CODE_DISABLE_MOUSE` | Set to `1` to disable mouse tracking in [fullscreen rendering](/docs/en/fullscreen). Keyboard scrolling with `PgUp` and `PgDn` still works. Use this to keep your terminal's native copy-on-select behavior |

257| `CLAUDE_CODE_DISABLE_MOUSE_CLICKS` | Set to `1` to disable click, drag, and hover handling in [fullscreen rendering](/docs/en/fullscreen) while keeping mouse-wheel scrolling. Use this when you want wheel scroll to work inside Claude Code but don't want clicks to position the cursor, expand tool output, or open links. `CLAUDE_CODE_DISABLE_MOUSE` takes precedence when both are set. Requires Claude Code v2.1.195 or later |258| `CLAUDE_CODE_DISABLE_MOUSE_CLICKS` | Set to `1` to disable click, drag, and hover handling in [fullscreen rendering](/docs/en/fullscreen) while keeping mouse-wheel scrolling. Use this when you want wheel scroll to work inside Claude Code but don't want clicks to position the cursor, expand tool output, or open links. `CLAUDE_CODE_DISABLE_MOUSE` takes precedence when both are set. Requires Claude Code v2.1.195 or later |

258| `CLAUDE_CODE_DISABLE_MTLS_RELOAD_ON_STALE_CONNECTION` | Set to `1` to stop Claude Code from re-reading the [mTLS client certificate and key](/docs/en/network-config#mtls-authentication) when an API request fails with a connection-level error, such as a connection reset or a TLS handshake error. With the reload disabled, Claude Code loads rotated files only when it next applies settings or at the next startup. Requires Claude Code v2.1.232 or later |259| `CLAUDE_CODE_DISABLE_MTLS_RELOAD_ON_STALE_CONNECTION` | Set to `1` to stop Claude Code from re-reading the [mTLS client certificate and key](/docs/en/network-config#mtls-authentication) when an API request fails with a connection-level error, such as a connection reset or a TLS handshake error. With the reload disabled, Claude Code loads rotated files only when it next applies settings or at the next startup. Requires Claude Code v2.1.232 or later |


268| `CLAUDE_CODE_DISABLE_THINKING` | Set to `1` to omit the `thinking` parameter from API requests entirely. This is a compatibility option for proxies and gateways that reject the parameter. On models that think by default, omitting the parameter means the model may still think. To explicitly disable [extended thinking](https://platform.claude.com/docs/en/build-with-claude/extended-thinking) on the Anthropic API, use `MAX_THINKING_TOKENS=0` instead. Neither variable turns thinking off on Opus 5.5, Sonnet 5.5, or the Fable models, which can't have thinking turned off. On [third-party providers](/docs/en/third-party-integrations), `MAX_THINKING_TOKENS=0` likewise omits the parameter, so the two variables behave the same there |269| `CLAUDE_CODE_DISABLE_THINKING` | Set to `1` to omit the `thinking` parameter from API requests entirely. This is a compatibility option for proxies and gateways that reject the parameter. On models that think by default, omitting the parameter means the model may still think. To explicitly disable [extended thinking](https://platform.claude.com/docs/en/build-with-claude/extended-thinking) on the Anthropic API, use `MAX_THINKING_TOKENS=0` instead. Neither variable turns thinking off on Opus 5.5, Sonnet 5.5, or the Fable models, which can't have thinking turned off. On [third-party providers](/docs/en/third-party-integrations), `MAX_THINKING_TOKENS=0` likewise omits the parameter, so the two variables behave the same there |

269| `CLAUDE_CODE_DISABLE_UNKNOWN_MODEL_WINDOW_ENFORCEMENT` | Set to `1` to skip proactive [auto-compaction](/docs/en/costs#reduce-token-usage) when Claude Code doesn't recognize the model ID, such as an [LLM gateway](/docs/en/llm-gateway) alias. Without this variable, Claude Code compacts at the context window it assumes for the ID. `CLAUDE_CODE_MAX_CONTEXT_TOKENS` can correct the assumed window instead; see [Correct the window for a gateway or custom model ID](/docs/en/model-config#correct-the-window-for-a-gateway-or-custom-model-id) for when each variable applies. Requires Claude Code v2.1.223 or later |270| `CLAUDE_CODE_DISABLE_UNKNOWN_MODEL_WINDOW_ENFORCEMENT` | Set to `1` to skip proactive [auto-compaction](/docs/en/costs#reduce-token-usage) when Claude Code doesn't recognize the model ID, such as an [LLM gateway](/docs/en/llm-gateway) alias. Without this variable, Claude Code compacts at the context window it assumes for the ID. `CLAUDE_CODE_MAX_CONTEXT_TOKENS` can correct the assumed window instead; see [Correct the window for a gateway or custom model ID](/docs/en/model-config#correct-the-window-for-a-gateway-or-custom-model-id) for when each variable applies. Requires Claude Code v2.1.223 or later |

270| `CLAUDE_CODE_DISABLE_VIRTUAL_SCROLL` | Set to `1` to disable virtual scrolling in [fullscreen rendering](/docs/en/fullscreen) and render every message in the transcript. Use this if scrolling in fullscreen mode shows blank regions where messages should appear |271| `CLAUDE_CODE_DISABLE_VIRTUAL_SCROLL` | Set to `1` to disable virtual scrolling in [fullscreen rendering](/docs/en/fullscreen) and render every message in the transcript. Use this if scrolling in fullscreen mode shows blank regions where messages should appear |

272| `CLAUDE_CODE_DISABLE_WEB_FETCH` | Set to `1` to turn off the [WebFetch](/docs/en/tools-reference#webfetch-tool-behavior) tool. The [WebSearch](/docs/en/tools-reference#websearch-tool-behavior) tool stays available. Requires Claude Code v2.1.285 or later |

271| `CLAUDE_CODE_DISABLE_WINDOWS_SHELL_LAUNCHER` | Set to `1` to start [PowerShell tool](/docs/en/tools-reference#powershell-tool) commands on Windows directly instead of through the `cmd.exe` launcher. By default, the launcher lets a PowerShell command [running in the background](/docs/en/tools-reference#background-commands) [carry over to the session's next process](/docs/en/agent-view#the-supervisor-process), such as when you [background the session](/docs/en/agent-view#from-inside-a-session). If you set the variable, a backgrounded PowerShell command stops when the session's process exits. Bash commands aren't affected. Requires Claude Code v2.1.269 or later |273| `CLAUDE_CODE_DISABLE_WINDOWS_SHELL_LAUNCHER` | Set to `1` to start [PowerShell tool](/docs/en/tools-reference#powershell-tool) commands on Windows directly instead of through the `cmd.exe` launcher. By default, the launcher lets a PowerShell command [running in the background](/docs/en/tools-reference#background-commands) [carry over to the session's next process](/docs/en/agent-view#the-supervisor-process), such as when you [background the session](/docs/en/agent-view#from-inside-a-session). If you set the variable, a backgrounded PowerShell command stops when the session's process exits. Bash commands aren't affected. Requires Claude Code v2.1.269 or later |

272| `CLAUDE_CODE_DISABLE_WORKFLOWS` | Set to `1` to disable [workflows](/docs/en/workflows#turn-workflows-off). Equivalent to the [`disableWorkflows`](/docs/en/settings-reference#disableworkflows) setting |274| `CLAUDE_CODE_DISABLE_WORKFLOWS` | Set to `1` to disable [workflows](/docs/en/workflows#turn-workflows-off). Equivalent to the [`disableWorkflows`](/docs/en/settings-reference#disableworkflows) setting |

273| `CLAUDE_CODE_EFFORT_LEVEL` | Set the effort level for supported models. Values: `low`, `medium`, `high`, `xhigh`, `max`, or `auto` to use the model default. Available levels depend on the model. Takes precedence over `--effort`, `/effort`, and the `modelSettings` and `effortLevel` settings. A [`maxEffortLevel`](/docs/en/settings-reference#maxeffortlevel) cap still applies. See [Adjust effort level](/docs/en/model-config#adjust-effort-level) |275| `CLAUDE_CODE_EFFORT_LEVEL` | Set the effort level for supported models. Values: `low`, `medium`, `high`, `xhigh`, `max`, or `auto` to use the model default. Available levels depend on the model. Takes precedence over `--effort`, `/effort`, and the `modelSettings` and `effortLevel` settings. A [`maxEffortLevel`](/docs/en/settings-reference#maxeffortlevel) cap still applies. See [Adjust effort level](/docs/en/model-config#adjust-effort-level) |


371| `CLAUDE_CODE_SKIP_FAST_MODE_ORG_CHECK` | Set to `1` to skip the client-side [fast mode](/docs/en/fast-mode#use-fast-mode-behind-proxies-and-llm-gateways) availability check, for proxies that intercept the check's request rather than refuse it. The API still rejects fast mode requests when your organization has fast mode disabled |373| `CLAUDE_CODE_SKIP_FAST_MODE_ORG_CHECK` | Set to `1` to skip the client-side [fast mode](/docs/en/fast-mode#use-fast-mode-behind-proxies-and-llm-gateways) availability check, for proxies that intercept the check's request rather than refuse it. The API still rejects fast mode requests when your organization has fast mode disabled |

372| `CLAUDE_CODE_SKIP_FOUNDRY_AUTH` | Skip Azure authentication for Microsoft Foundry, for a proxy or gateway that injects its own `Authorization` header. Claude Code sends requests without an Azure credential and preserves the `Authorization` header you supply, for example through `ANTHROPIC_CUSTOM_HEADERS`. Ignored when `ANTHROPIC_FOUNDRY_API_KEY` or `ANTHROPIC_FOUNDRY_AUTH_TOKEN` is set. Before v2.1.203, this variable left the Microsoft Foundry client unable to send requests unless an API key was also set |374| `CLAUDE_CODE_SKIP_FOUNDRY_AUTH` | Skip Azure authentication for Microsoft Foundry, for a proxy or gateway that injects its own `Authorization` header. Claude Code sends requests without an Azure credential and preserves the `Authorization` header you supply, for example through `ANTHROPIC_CUSTOM_HEADERS`. Ignored when `ANTHROPIC_FOUNDRY_API_KEY` or `ANTHROPIC_FOUNDRY_AUTH_TOKEN` is set. Before v2.1.203, this variable left the Microsoft Foundry client unable to send requests unless an API key was also set |

373| `CLAUDE_CODE_SKIP_MANTLE_AUTH` | Skip AWS authentication for Amazon Bedrock Mantle (for example, when using an LLM gateway) |375| `CLAUDE_CODE_SKIP_MANTLE_AUTH` | Skip AWS authentication for Amazon Bedrock Mantle (for example, when using an LLM gateway) |

376| `CLAUDE_CODE_SKIP_MODEL_ACCESS_MEMORY` | The [startup model checks](/docs/en/amazon-bedrock#startup-model-checks) on [Amazon Bedrock](/docs/en/amazon-bedrock) and [Google Cloud's Agent Platform](/docs/en/google-vertex-ai) remember on this machine which models they found your account can't invoke, for up to a day. Set to `1` to turn that memory off. Requires Claude Code v2.1.285 or later |

374| `CLAUDE_CODE_SKIP_PROMPT_HISTORY` | Set to `1` to skip writing prompt history and session transcripts to disk. Sessions started with this variable set do not appear in `--resume`, `--continue`, or up-arrow history. Useful for ephemeral scripted sessions |377| `CLAUDE_CODE_SKIP_PROMPT_HISTORY` | Set to `1` to skip writing prompt history and session transcripts to disk. Sessions started with this variable set do not appear in `--resume`, `--continue`, or up-arrow history. Useful for ephemeral scripted sessions |

375| `CLAUDE_CODE_SKIP_VERTEX_AUTH` | Skip Google authentication for Google Cloud's Agent Platform (for example, when using an LLM gateway) |378| `CLAUDE_CODE_SKIP_VERTEX_AUTH` | Skip Google authentication for Google Cloud's Agent Platform (for example, when using an LLM gateway) |

376| `CLAUDE_CODE_STARTUP_FAILURE_RESULTS` | Set to `1` to have a session started with `--output-format stream-json` write a [result message naming why Claude Code refused to start](/docs/en/agent-sdk/typescript#startup_failure_reason) for startup failures that otherwise end with stderr alone. Requires Claude Code v2.1.274 or later |379| `CLAUDE_CODE_STARTUP_FAILURE_RESULTS` | Set to `1` to have a session started with `--output-format stream-json` write a [result message naming why Claude Code refused to start](/docs/en/agent-sdk/typescript#startup_failure_reason) for startup failures that otherwise end with stderr alone. Requires Claude Code v2.1.274 or later |

errors.md +103 −39

Details

184| `The connection dropped while downloading the update` | [Installation errors](#the-connection-dropped-while-downloading-the-update) |184| `The connection dropped while downloading the update` | [Installation errors](#the-connection-dropped-while-downloading-the-update) |

185| `Download timed out: exceeded the total deadline` | [Installation errors](#the-connection-dropped-while-downloading-the-update) |185| `Download timed out: exceeded the total deadline` | [Installation errors](#the-connection-dropped-while-downloading-the-update) |

186| `--bg and --print conflict` | [Command-line errors](#conflict-between-bg-and-print) |186| `--bg and --print conflict` | [Command-line errors](#conflict-between-bg-and-print) |

187| `Error: Cannot use both --append-subagent-system-prompt and --append-subagent-system-prompt-file. Please use only one.` | [Command-line errors](#conflict-between-a-system-prompt-flag-and-its-file-form) |

187| `Cloud sessions cannot be created from a --restricted session` | [Command-line errors](#cloud-sessions-cannot-be-created-from-a-restricted-session) |188| `Cloud sessions cannot be created from a --restricted session` | [Command-line errors](#cloud-sessions-cannot-be-created-from-a-restricted-session) |

188| `Cloud sessions are disabled by your organization's policy` | [Command-line errors](#cloud-sessions-are-disabled-by-your-organizations-policy) |189| `Cloud sessions are disabled by your organization's policy` | [Command-line errors](#cloud-sessions-are-disabled-by-your-organizations-policy) |

189| `Couldn't verify your organization's policy for cloud sessions` | [Command-line errors](#cloud-sessions-are-disabled-by-your-organizations-policy) |190| `Couldn't verify your organization's policy for cloud sessions` | [Command-line errors](#cloud-sessions-are-disabled-by-your-organizations-policy) |


288| `Reading a local file from outside this session's connected folders, or through a link, needs the approval card` | [Tool errors](#reading-a-local-file-from-outside-the-connected-folders) |289| `Reading a local file from outside this session's connected folders, or through a link, needs the approval card` | [Tool errors](#reading-a-local-file-from-outside-the-connected-folders) |

289| `cannot read file_path (...) — the file could not be examined, and no one can answer the approval card` | [Tool errors](#reading-a-local-file-from-outside-the-connected-folders) |290| `cannot read file_path (...) — the file could not be examined, and no one can answer the approval card` | [Tool errors](#reading-a-local-file-from-outside-the-connected-folders) |

290| `WebFetch cannot fetch localhost or other hostnames without a dot` | [Tool errors](#webfetch-cannot-fetch-localhost) |291| `WebFetch cannot fetch localhost or other hostnames without a dot` | [Tool errors](#webfetch-cannot-fetch-localhost) |

292| `The safety check for domain ... is rate-limited` | [Tool errors](#webfetch-domain-safety-check-failed) |

293| `The safety check for domain ... is temporarily rate-limited` | [Tool errors](#webfetch-domain-safety-check-failed) |

294| `Unable to verify if domain ... is safe to fetch` | [Tool errors](#webfetch-domain-safety-check-failed) |

291| `Can't open MCP settings while no terminal is attached to this background session` | [Background session errors](#commands-refused-in-a-background-session) |295| `Can't open MCP settings while no terminal is attached to this background session` | [Background session errors](#commands-refused-in-a-background-session) |

292| `Can't open MCP settings in a background session` | [Background session errors](#commands-refused-in-a-background-session) |296| `Can't open MCP settings in a background session` | [Background session errors](#commands-refused-in-a-background-session) |

293| `blocked because the path is spelled in a form that cannot be safely resolved` | [Background session errors](#write-or-command-blocked-because-the-path-cannot-be-safely-resolved) |297| `blocked because the path is spelled in a form that cannot be safely resolved` | [Background session errors](#write-or-command-blocked-because-the-path-cannot-be-safely-resolved) |


320| `Transcript writes are failing (...)` | [Session saving warnings](#transcript-writes-are-failing) |324| `Transcript writes are failing (...)` | [Session saving warnings](#transcript-writes-are-failing) |

321| `Transcript saving is off — CLAUDE_CODE_SKIP_PROMPT_HISTORY is set` | [Session saving warnings](#transcript-saving-is-off-skip-prompt-history) |325| `Transcript saving is off — CLAUDE_CODE_SKIP_PROMPT_HISTORY is set` | [Session saving warnings](#transcript-saving-is-off-skip-prompt-history) |

322| `Transcript saving is off — inherited CLAUDE_CODE_CHILD_SESSION marker` | [Session saving warnings](#transcript-saving-is-off-child-session-marker) |326| `Transcript saving is off — inherited CLAUDE_CODE_CHILD_SESSION marker` | [Session saving warnings](#transcript-saving-is-off-child-session-marker) |

323| `Claude Code's fullscreen renderer didn't finish starting last time on this machine` / `Claude Code's fullscreen renderer has repeatedly failed to start on this machine` | [Configuration warnings](#fullscreen-failed-start-notice) |327| `Claude Code's fullscreen renderer didn't finish starting last time on this machine` / `Claude Code's fullscreen renderer has repeatedly failed to start on this machine` | [Fullscreen rendering](/docs/en/fullscreen#fullscreen-renderer-didnt-finish-starting) |

324| `Claude Code exited after an unrecoverable interface error (...)` | [Configuration warnings](#exited-after-an-unrecoverable-interface-error) |328| `Claude Code exited after an unrecoverable interface error (...)` | [Configuration warnings](#exited-after-an-unrecoverable-interface-error) |

325| `Agent descriptions are over the 15.0k-token limit` | [Configuration warnings](#agent-descriptions-are-over-the-15000-token-limit) |329| `Agent descriptions are over the 15.0k-token limit` | [Configuration warnings](#agent-descriptions-are-over-the-15000-token-limit) |

326| `Not loaded: rename <path>, then restart — its name uses "<name>", a name reserved for the skills synced from your claude.ai account` | [Configuration warnings](#a-skill-command-or-workflow-wasnt-loaded-because-its-name-is-reserved) |330| `Not loaded: rename <path>, then restart — its name uses "<name>", a name reserved for the skills synced from your claude.ai account` | [Configuration warnings](#a-skill-command-or-workflow-wasnt-loaded-because-its-name-is-reserved) |


329| `Remote managed settings failed to load (<cause>)` | [Configuration warnings](#remote-managed-settings-failed-to-load) |333| `Remote managed settings failed to load (<cause>)` | [Configuration warnings](#remote-managed-settings-failed-to-load) |

330| `Managed settings were not approved; exiting without applying them.` | [Configuration warnings](#managed-settings-were-not-approved) |334| `Managed settings were not approved; exiting without applying them.` | [Configuration warnings](#managed-settings-were-not-approved) |

331| `Claude Code can't start: your organization's managed settings block the default model` / `Claude Code can't start: your organization allows only the models listed in "availableModels"` | [Configuration warnings](#managed-settings-block-the-default-model) |335| `Claude Code can't start: your organization's managed settings block the default model` / `Claude Code can't start: your organization allows only the models listed in "availableModels"` | [Configuration warnings](#managed-settings-block-the-default-model) |

336| `Your organization's managed settings allow Claude Code to use: <providers>` | [Configuration warnings](#managed-settings-dont-allow-this-api-provider) |

337| `Your organization's managed settings allow Claude Code to use no API provider at all` | [Configuration warnings](#managed-settings-dont-allow-this-api-provider) |

332| `MCP server <name> is blocked by enterprise managed policy` | [Configuration warnings](#mcp-server-is-blocked-by-enterprise-managed-policy) |338| `MCP server <name> is blocked by enterprise managed policy` | [Configuration warnings](#mcp-server-is-blocked-by-enterprise-managed-policy) |

333| `Managed settings document could not be parsed as a JSON object; none of its settings are in effect. Fix or remove it.` | [Configuration warnings](#managed-settings-document-could-not-be-parsed) |339| `Managed settings document could not be parsed as a JSON object; none of its settings are in effect. Fix or remove it.` | [Configuration warnings](#managed-settings-document-could-not-be-parsed) |

334| `Managed settings drop-in directory could not be read` | [Configuration warnings](#managed-settings-document-could-not-be-parsed) |340| `Managed settings drop-in directory could not be read` | [Configuration warnings](#managed-settings-document-could-not-be-parsed) |

341| `Unable to read managed policy settings` | [Configuration warnings](#unable-to-read-managed-policy-settings) |

335| `otelHeadersHelper failed; telemetry is not being exported. See /status: ...` | [Configuration warnings](#otelheadershelper-failed) |342| `otelHeadersHelper failed; telemetry is not being exported. See /status: ...` | [Configuration warnings](#otelheadershelper-failed) |

336| `"crossSessionInbound" must be one of "accept", "hold", "refuse"` | [Configuration warnings](#crosssessioninbound-must-be-one-of-accept-hold-refuse) |343| `"crossSessionInbound" must be one of "accept", "hold", "refuse"` | [Configuration warnings](#crosssessioninbound-must-be-one-of-accept-hold-refuse) |

337| `headersHelper not run — this workspace has no persisted trust` | [Configuration warnings](#headershelper-not-run) |344| `headersHelper not run — this workspace has no persisted trust` | [Configuration warnings](#headershelper-not-run) |


1850 1857 

1851These steps change one of your own environments. An [organization-shared environment](/docs/en/cloud-environments#organization-shared-environments) opens read-only in the selector, so ask an Owner to change its network access from the **Cloud environments** page in [admin settings](https://claude.ai/admin-settings).1858These steps change one of your own environments. An [organization-shared environment](/docs/en/cloud-environments#organization-shared-environments) opens read-only in the selector, so ask an Owner to change its network access from the **Cloud environments** page in [admin settings](https://claude.ai/admin-settings).

1852 1859 

1853* Open the routine for editing, or start a cloud session. Select the cloud icon showing your environment's name, such as **Default**, to open the selector. Hover over your environment and click the settings icon.1860* Open your environment for editing, either from the [routine's form](/docs/en/routines#environments-and-network-access) or from the [environment selector](/docs/en/cloud-environments#configure-your-environment) where you start cloud sessions.

1854* In the **Update cloud environment** dialog, change **Network access** from **Trusted** to **Custom**, then add the blocked domain to **Allowed domains**. Enter one domain per line. 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 if you want unrestricted access.1861* In the **Edit cloud environment** dialog, change **Network access** from **Trusted** to **Custom**, then add the blocked domain to **Allowed domains**. Enter one domain per line. 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 if you want unrestricted access.

1855* Click **Save changes**. The next run uses the updated allowlist. For a cloud session that's already open, see [when a network access change reaches existing sessions](/docs/en/cloud-environments#network-access).1862* Click **Save changes**. The next run uses the updated allowlist. For a cloud session that's already open, see [when a network access change reaches existing sessions](/docs/en/cloud-environments#network-access).

1856 1863 

1857See [Network access](/docs/en/cloud-environments#network-access) for access levels and the default allowlist. Local CLI sessions are not affected by this policy.1864See [Network access](/docs/en/cloud-environments#network-access) for access levels and the default allowlist. Local CLI sessions are not affected by this policy.


2689* Drop `-p` or `--print`. `--bg` takes the prompt as its positional argument, so `claude --bg "<task>"` is the complete command. See [Dispatch new agents from your shell](/docs/en/agent-view#from-your-shell).2696* Drop `-p` or `--print`. `--bg` takes the prompt as its positional argument, so `claude --bg "<task>"` is the complete command. See [Dispatch new agents from your shell](/docs/en/agent-view#from-your-shell).

2690* To run the prompt non-interactively and print the result instead of creating a background session, drop `--bg` and run `claude -p "<task>"`2697* To run the prompt non-interactively and print the result instead of creating a background session, drop `--bg` and run `claude -p "<task>"`

2691 2698 

2699<h3 id="conflict-between-a-system-prompt-flag-and-its-file-form">

2700 Conflict between a system prompt flag and its file form

2701</h3>

2702 

2703You passed [`--append-subagent-system-prompt`](/docs/en/cli-reference#cli-flags) together with `--append-subagent-system-prompt-file` in one `claude` invocation, so `claude` exits with code 1 instead of starting the session:

2704 

2705```text theme={null}

2706Error: Cannot use both --append-subagent-system-prompt and --append-subagent-system-prompt-file. Please use only one.

2707```

2708 

2709Before v2.1.283, `claude` exited the same way when you passed `--system-prompt` with `--system-prompt-file`, or `--append-system-prompt` with `--append-system-prompt-file`, because those pairs conflicted instead of [combining](/docs/en/cli-reference#system-prompt-flags). On those versions the message names the pair you combined.

2710 

2711**What to do:**

2712 

2713* Keep one form of the flag and drop the other. To combine a fixed prompt file with per-run text, merge the text into the file before launching instead of passing both flags

2714 

2692<h3 id="invalid-agents-configuration">2715<h3 id="invalid-agents-configuration">

2693 Invalid `--agents` configuration2716 Invalid `--agents` configuration

2694</h3>2717</h3>


3020"gmail" is Anthropic-hosted and doesn't support local OAuth. Connect it via Settings → Connectors on claude.ai (requires `claude login`), then it'll be available here automatically.3043"gmail" is Anthropic-hosted and doesn't support local OAuth. Connect it via Settings → Connectors on claude.ai (requires `claude login`), then it'll be available here automatically.

3021```3044```

3022 3045 

3023Claude Code matches these hosts by URL, so the message appears when a server you added with `claude mcp add` or in `.mcp.json` points at one of them.

3024 

3025**What to do:**3046**What to do:**

3026 3047 

3027* Remove your entry with `claude mcp remove <name>`, so it can't hide the claude.ai connector at the same URL3048* Remove your entry with `claude mcp remove <name>`, so it can't hide the claude.ai connector at the same URL


3447 Couldn't open Claude Desktop3468 Couldn't open Claude Desktop

3448</h3>3469</h3>

3449 3470 

3450You ran [`/desktop`](/docs/en/desktop#coming-from-the-cli), or its alias `/app`, and the system command Claude Code uses to open Claude Desktop failed. The session stays in the terminal.3471You ran [`/desktop`](/docs/en/desktop#coming-from-the-cli) or its alias `/app` in a session, or [`claude --desktop`](/docs/en/cli-reference#cli-flags) in your shell, and the system command Claude Code uses to open Claude Desktop failed. After `/desktop`, the session stays in the terminal; `claude --desktop` prints the message without the `Error:` prefix and exits with status 1.

3472 

3473The text in parentheses names the command that failed, with its exit status and the first line of its error output when it produced them. On macOS that command is `open`, as in this example; on Windows it is `rundll32`:

3451 3474 

3452```text theme={null}3475```text theme={null}

3453Error: Couldn't open Claude Desktop (`open` exited 1: LSOpenURLsWithRole() failed for the URL claude://resume?session=<session-id> with error -10814). Open Claude Desktop and run /desktop again.3476Error: Couldn't open Claude Desktop (`open` exited 1: LSOpenURLsWithRole() failed for the URL claude://resume?session=<session-id> with error -10814). Open Claude Desktop and try again.

3454```3477```

3455 3478 

3456**What to do:**3479**What to do:**

3457 3480 

3458* Open Claude Desktop yourself, then run `/desktop` again3481* Open Claude Desktop yourself, then run `/desktop` or `claude --desktop` again

3459* To read that command's full error output, turn on debug logging with `/debug`, run `/desktop` again, and check the debug log3482* To read the failed command's full error output, turn on debug logging with `/debug` and run `/desktop` again, or run `claude --desktop --debug-file <path>`, then check the debug log

3460 3483 

3461Before v2.1.275, the message was `Failed to open Claude Desktop. Please try opening it manually.` and didn't say what failed.3484Before v2.1.285, the message ended `Open Claude Desktop and run /desktop again.` Before v2.1.275, it was `Failed to open Claude Desktop. Please try opening it manually.` and didn't say what failed.

3462 3485 

3463<h3 id="terminal-setup-left-your-zed-keymap-unchanged">3486<h3 id="terminal-setup-left-your-zed-keymap-unchanged">

3464 /terminal-setup left your Zed keymap unchanged3487 /terminal-setup left your Zed keymap unchanged


3699commands path escapes plugin directory: ./commands\deploy.md — its path contains a backslash, which is not resolved reliably on this platform3722commands path escapes plugin directory: ./commands\deploy.md — its path contains a backslash, which is not resolved reliably on this platform

3700```3723```

3701 3724 

3702Before v2.1.251, Claude Code loaded a `commands` path declared in a marketplace entry even when it pointed outside the plugin directory. Claude Code already rejected paths declared in `plugin.json` and the other component paths in a marketplace entry.3725Before v2.1.251, Claude Code loaded a `commands` path declared in a marketplace entry even when it pointed outside the plugin directory.

3703 3726 

3704Before v2.1.257, the check looked only at the path's spelling, not at where a symlink leads.3727Before v2.1.257, the check looked only at the path's spelling, not at where a symlink leads.

3705 3728 


3770 3793 

3771Claude Code keeps the plugin marketplaces you've added in a registry file at `~/.claude/plugins/known_marketplaces.json`. A plugin command that needs the registry, such as `claude plugin install`, fails with one of two messages when Claude Code can't use the file:3794Claude Code keeps the plugin marketplaces you've added in a registry file at `~/.claude/plugins/known_marketplaces.json`. A plugin command that needs the registry, such as `claude plugin install`, fails with one of two messages when Claude Code can't use the file:

3772 3795 

3773* `Failed to load marketplace configuration`: the file isn't valid JSON, or can't be read. An empty file fails this way too.3796* `Failed to load marketplace configuration`: the file exists but isn't valid JSON or can't be read. An empty file fails this way too.

3774* `Marketplace configuration file is corrupted`: the file is valid JSON but its contents don't match the registry schema.3797* `Marketplace configuration file is corrupted`: the file is valid JSON but its contents don't match the registry schema.

3775 3798 

3776A missing file isn't a failure: Claude Code treats it as a registry with no marketplaces.

3777 

3778With an empty file, `claude plugin install` reports:3799With an empty file, `claude plugin install` reports:

3779 3800 

3780```text theme={null}3801```text theme={null}


4204 4225 

4205Before v2.1.268, WebFetch reported these URLs with a generic `Invalid URL` error.4226Before v2.1.268, WebFetch reported these URLs with a generic `Invalid URL` error.

4206 4227 

4228<h3 id="webfetch-domain-safety-check-failed">

4229 WebFetch domain safety check failed

4230</h3>

4231 

4232Before fetching a URL, WebFetch sends the URL's hostname to `api.anthropic.com` to check it against Anthropic's [domain safety blocklist](/docs/en/data-usage#webfetch-domain-safety-check). If the check can't complete, WebFetch can't confirm that the domain is safe, so it doesn't fetch the page and the tool result carries one of these messages instead:

4233 

4234```text wrap theme={null}

4235The safety check for domain example.com is rate-limited (too many domain checks from this network; the limit is shared and can stay exhausted for minutes). Do not retry WebFetch in a loop or sleep to wait it out; continue without this page and report that its safety check was rate-limited. A single later attempt is fine; if that is rate-limited too, stop.

4236 

4237Unable to verify if domain example.com is safe to fetch. This may be due to network restrictions or enterprise security policies blocking claude.ai.

4238```

4239 

4240* `rate-limited`: the check endpoint answered with HTTP `429`. The message tells Claude to continue without the page and to try again at most once later. Claude Code doesn't cache a failed check, so a later fetch of that domain runs the check again. If sessions on your network hit this often, you can skip the check with [`skipWebFetchPreflight: true`](/docs/en/settings-reference#skipwebfetchpreflight) in settings.

4241* `Unable to verify`: the check request failed, timed out, or got another error status. If your network blocks `api.anthropic.com`, allowlist that domain, or skip the check with [`skipWebFetchPreflight: true`](/docs/en/settings-reference#skipwebfetchpreflight) in settings.

4242 

4243Before v2.1.286, the rate-limited message read `The safety check for domain example.com is temporarily rate-limited (too many domain checks from this network). Retry after about a minute; retrying sooner will fail the same way.`.

4244Before v2.1.285, a rate-limited check was reported with the `Unable to verify` message instead.

4245 

4207## Background session errors4246## Background session errors

4208 4247 

4209[Background sessions](/docs/en/agent-view) run without an interactive terminal of their own, so commands that need one behave differently there. These messages appear in the transcript of a background session, in the terminal that attaches to one, in the session or shell you dispatch from, or, for the [worktree-guard entries](#write-or-command-blocked-because-the-path-cannot-be-safely-resolved) below, in any session isolated in a worktree or running a worktree-isolated subagent; where a message is specific to one surface, its entry says so.4248[Background sessions](/docs/en/agent-view) run without an interactive terminal of their own, so commands that need one behave differently there. These messages appear in the transcript of a background session, in the terminal that attaches to one, in the session or shell you dispatch from, or, for the [worktree-guard entries](#write-or-command-blocked-because-the-path-cannot-be-safely-resolved) below, in any session isolated in a worktree or running a worktree-isolated subagent; where a message is specific to one surface, its entry says so.


4368terminal host process died — press Enter to restart4407terminal host process died — press Enter to restart

4369```4408```

4370 4409 

4371If you open the row before the check runs, the footer shows `This session's terminal host process died (the conversation is saved) — press Enter to restart it` and the row turns failed.

4372 

4373From the shell, `claude attach <id>` restarts a session already marked failed for a dead host, and otherwise prints the cause and exits:4410From the shell, `claude attach <id>` restarts a session already marked failed for a dead host, and otherwise prints the cause and exits:

4374 4411 

4375```text theme={null}4412```text theme={null}


4739 4776 

4740Claude Code writes most of these messages to stderr, not into the conversation, and writes most of them at startup. An entry says so when its message appears somewhere else, such as in the debug log or as a startup notice in the conversation view, or at another time, such as the [unrecognized-model diagnostic line](#unrecognized-model-id-on-a-request) at request time.4777Claude Code writes most of these messages to stderr, not into the conversation, and writes most of them at startup. An entry says so when its message appears somewhere else, such as in the debug log or as a startup notice in the conversation view, or at another time, such as the [unrecognized-model diagnostic line](#unrecognized-model-id-on-a-request) at request time.

4741 4778 

4742<h3 id="fullscreen-failed-start-notice">

4743 Fullscreen renderer didn't finish starting

4744</h3>

4745 

4746A previous [fullscreen](/docs/en/fullscreen) session on this machine exited before it finished starting, so Claude Code starts this session on the classic renderer and prints one of these notices:

4747 

4748```text theme={null}

4749Claude Code's fullscreen renderer didn't finish starting last time on this machine, so this launch is using the classic renderer. It will try fullscreen again next launch; /tui default keeps the classic renderer.

4750 

4751Claude Code's fullscreen renderer has repeatedly failed to start on this machine, so it has been turned off here. Run /tui fullscreen to try it again (this also resets after an update).

4752```

4753 

4754**What to do:**

4755 

4756* Follow [Fullscreen rendering](/docs/en/fullscreen#fullscreen-renderer-didnt-finish-starting). It says which notice you get, what Claude Code does in later sessions, and how to try fullscreen again or keep the classic renderer.

4757* If the session that died printed an exit message, see [Claude Code exited after an unrecoverable interface error](#exited-after-an-unrecoverable-interface-error) for what it names.

4758 

4759Before v2.1.236, Claude Code printed no notice and kept starting sessions in fullscreen rendering after a failed start.

4760 

4761<h3 id="exited-after-an-unrecoverable-interface-error">4779<h3 id="exited-after-an-unrecoverable-interface-error">

4762 Claude Code exited after an unrecoverable interface error4780 Claude Code exited after an unrecoverable interface error

4763</h3>4781</h3>


4905* If you administer the settings, add a model your users can run to `availableModels`, or narrow the `deniedModels` entries that block every fallback. [Block specific models or versions](/docs/en/model-config#block-specific-models-or-versions) describes how the Default option steps down4923* If you administer the settings, add a model your users can run to `availableModels`, or narrow the `deniedModels` entries that block every fallback. [Block specific models or versions](/docs/en/model-config#block-specific-models-or-versions) describes how the Default option steps down

4906* If you don't administer them, send the message to your administrator. Your own settings files can't widen a managed `availableModels` or `deniedModels` list4924* If you don't administer them, send the message to your administrator. Your own settings files can't widen a managed `availableModels` or `deniedModels` list

4907 4925 

4926<h3 id="managed-settings-dont-allow-this-api-provider">

4927 Managed settings don't allow this API provider

4928</h3>

4929 

4930Your organization's [managed settings](/docs/en/managed-settings) set an [`allowedProviders`](/docs/en/settings-reference#allowedproviders) list, and the session's API provider isn't on it or the session uses an endpoint that isn't pinned the way that entry requires. Claude Code refuses at startup, before a login, or when the session next contacts the API. The message begins with the permitted providers:

4931 

4932```text theme={null}

4933Your organization's managed settings allow Claude Code to use: Anthropic API, Amazon Bedrock.

4934```

4935 

4936When the list is empty, the message reads instead:

4937 

4938```text theme={null}

4939Your organization's managed settings allow Claude Code to use no API provider at all (allowedProviders is an empty list), so it cannot start on this machine.

4940```

4941 

4942When every entry is unrecognized, the parenthetical reads `(allowedProviders lists only unrecognized entries)` instead.

4943 

4944**What to do:**

4945 

4946* Follow the message's `To continue:` steps

4947* If you administer the settings, the message's lines starting `Admins:` name the entry to add or the value to pin, and the [`allowedProviders`](/docs/en/settings-reference#allowedproviders) entry says which source's `env` block can pin it

4948 

4908<h3 id="mcp-server-is-blocked-by-enterprise-managed-policy">4949<h3 id="mcp-server-is-blocked-by-enterprise-managed-policy">

4909 MCP server is blocked by enterprise managed policy4950 MCP server is blocked by enterprise managed policy

4910</h3>4951</h3>


4958* If you administer the machine, fix the named document so it parses as a JSON object, or remove the file, profile, or registry value. An empty `managed-settings.json` counts as `{}` and doesn't block launch.4999* If you administer the machine, fix the named document so it parses as a JSON object, or remove the file, profile, or registry value. An empty `managed-settings.json` counts as `{}` and doesn't block launch.

4959* If you don't, ask your administrator to fix the deployed document. Nothing in your own settings files causes or clears this error.5000* If you don't, ask your administrator to fix the deployed document. Nothing in your own settings files causes or clears this error.

4960 5001 

5002<h3 id="unable-to-read-managed-policy-settings">

5003 Unable to read managed policy settings

5004</h3>

5005 

5006Your organization deploys [managed settings](/docs/en/managed-settings), and one of the deployed sources exists but couldn't be read, for a reason such as an I/O error rather than the operating system denying the read. With no other admin source supplying a policy, Claude Code exits at startup rather than run without the policy the source may carry:

5007 

5008```text theme={null}

5009Unable to read managed policy settings.

5010This machine may require organization login enforcement, but the policy file failed to load.

5011Contact your administrator.

5012 

5013Detail: <source>: <reason>

5014```

5015 

5016In the same state, sign-in flows, API requests from a session that is already running, and the [`claude gateway`](/docs/en/claude-apps-gateway) server are refused with a variant of the first line that names [`allowedProviders`](/docs/en/settings-reference#allowedproviders).

5017 

5018A read that the operating system denied, such as on a root-only file, doesn't produce this exit: [the session starts without that source's policies](/docs/en/managed-settings#find-entries-claude-code-dropped). For a source that can't be parsed, Claude Code exits with [a different message naming the source](#managed-settings-document-could-not-be-parsed).

5019 

5020**What to do:**

5021 

5022* If you administer the machine, fix the problem the `Detail:` line names so the deployed source can be read, or remove the source

5023* If you don't, send the message to your administrator. Nothing in your own settings files causes or clears this error

5024 

5025Before v2.1.285, only sessions signed in with claude.ai or Claude Console credentials exited with this message, and a read that the operating system denied produced it too.

5026 

4961<h3 id="otelheadershelper-failed">5027<h3 id="otelheadershelper-failed">

4962 otelHeadersHelper failed5028 otelHeadersHelper failed

4963</h3>5029</h3>


5050* Fix the rule at the source the warning names in parentheses: a settings file path, or the `--allowed-tools` flag itself. A `claude-settings-<hash>.json` path that doesn't exist on disk stands for an inline `--settings` value. Fix the JSON you pass to that flag.5116* Fix the rule at the source the warning names in parentheses: a settings file path, or the `--allowed-tools` flag itself. A `claude-settings-<hash>.json` path that doesn't exist on disk stands for an inline `--settings` value. Fix the JSON you pass to that flag.

5051* If the source reads `managed policy settings`, forward the warning to whoever maintains your managed settings, since you can't clear it yourself.5117* If the source reads `managed policy settings`, forward the warning to whoever maintains your managed settings, since you can't clear it yourself.

5052 5118 

5053Claude Code doesn't warn about deny and ask rules with the same shape: it refuses or prompts for the extra commands they match rather than approving them. It also doesn't warn about rules whose subcommand comes before the first `*`, such as `Bash(git commit *)`, or rules in which no word other than an option follows the `*`, such as `Bash(git *)`, or about `:*` prefix rules such as `Bash(git:*)`.

5054 

5055In a [background session](/docs/en/agent-view) or with `--output-format json` or `stream-json`, Claude Code writes the warning to the debug log instead of stderr, so machine-read output stays clean. Run with `--debug` to capture it at `~/.claude/debug/<session-id>.txt`. Before v2.1.246, Claude Code accepted these rules without a warning.5119In a [background session](/docs/en/agent-view) or with `--output-format json` or `stream-json`, Claude Code writes the warning to the debug log instead of stderr, so machine-read output stays clean. Run with `--debug` to capture it at `~/.claude/debug/<session-id>.txt`. Before v2.1.246, Claude Code accepted these rules without a warning.

5056 5120 

5057<h3 id="crosssessioninbound-must-be-one-of-accept-hold-refuse">5121<h3 id="crosssessioninbound-must-be-one-of-accept-hold-refuse">


5160 5224 

5161## Responses seem lower quality than usual5225## Responses seem lower quality than usual

5162 5226 

5163If Claude's answers seem less capable than you expect but no error is shown, the cause is usually conversation state rather than the model itself. Claude Code doesn't silently change model versions. It can switch to a fallback model in three specific cases:5227If Claude's answers seem less capable than you expect but no error is shown, the cause is usually conversation state rather than the model itself. Claude Code doesn't silently change model versions. It can switch to a fallback model in these cases:

5164 5228 

5165* A configured [`--fallback-model`](/docs/en/cli-reference#cli-flags) takes over after an availability error, for that turn only, with a notice in the transcript5229* A configured [`--fallback-model`](/docs/en/cli-reference#cli-flags) takes over after an availability error, for that turn only, with a notice in the transcript

5166* An Amazon Bedrock or Google Cloud's Agent Platform startup check finds your default model unavailable5230* An Amazon Bedrock or Google Cloud's Agent Platform startup check finds your default model unavailable, or your account [loses access to it mid-session](/docs/en/amazon-bedrock#when-a-model-is-disabled-mid-session)

5167* [Automatic model fallback](/docs/en/model-config#automatic-model-fallback) on Fable 5.1, Fable 5, Opus 5.5, Sonnet 5.5, and Opus 5 moves the session to the flagged category's fallback model, when that category has one, and shows a notice in the transcript5231* [Automatic model fallback](/docs/en/model-config#automatic-model-fallback) on Fable 5.1, Fable 5, Opus 5.5, Sonnet 5.5, and Opus 5 moves the session to the flagged category's fallback model, when that category has one, and shows a notice in the transcript

5168 5232 

5169The Model selection check below catches the second and third cases; the first appears as a transcript notice rather than a `/model` change. [Model configuration](/docs/en/model-config) explains when each fallback applies.5233The Model selection check below catches the second and third cases; the first appears as a transcript notice rather than a `/model` change. [Model configuration](/docs/en/model-config) explains when each fallback applies.

Details

269 269 

270Model aliases such as `opus` don't act as pins, and neither does a model ID Claude Code doesn't recognize.270Model aliases such as `opus` don't act as pins, and neither does a model ID Claude Code doesn't recognize.

271 271 

272When these checks find a model your project can't invoke, Claude Code remembers the refusal on this machine for up to a day, and launches during that time skip the remembered model without asking Agent Platform again. Claude Code checks a remembered refusal of a current default model again at launch once ten minutes have passed since the last check, so a default your administrator re-enables comes back. To turn the memory off, set [`CLAUDE_CODE_SKIP_MODEL_ACCESS_MEMORY=1`](/docs/en/env-vars).

273 

274### When a model is disabled mid-session

275 

276If your project loses access to the model your session is running on, for example because an administrator disables it in [Model Garden](https://console.cloud.google.com/vertex-ai/model-garden), Claude Code switches the session to another model instead of failing each request, and shows `Switched to <fallback> because <model> is not available`. It tries the same models as the startup fallback: earlier versions of the same tier first and, for an Opus session with no Opus version available, the default Sonnet model.

277 

278The switch applies only to a tier you haven't pinned, the same condition as the startup fallback. A session on a specific version you picked keeps its model and, without a fallback model chain, the request fails instead. In [auto mode](/docs/en/permission-modes#enable-auto-mode-on-bedrock-agent-platform-or-foundry), Claude Code switches only to a model auto mode supports on Agent Platform. If none of those models is available either, the request fails.

279 

280A [fallback model chain](/docs/en/model-config#fallback-model-chains) you configure replaces the tier switch: on these refusals Claude Code switches to your configured fallback instead. To have refused requests fail rather than switch, set [`CLAUDE_CODE_DISABLE_MODEL_ACCESS_FALLBACK=1`](/docs/en/env-vars). A fallback chain you configured still switches on these refusals; remove the chain as well if you want every refused request to fail.

281 

272## IAM configuration282## IAM configuration

273 283 

274Assign the `roles/aiplatform.user` role, which includes the required permissions:284Assign the `roles/aiplatform.user` role, which includes the required permissions:

headless.md +2 −2

Details

264 264 

265### Auto-approve tools265### Auto-approve tools

266 266 

267Use `--allowedTools` to let Claude use certain tools without prompting. This example runs a test suite and fixes failures, allowing Claude to execute Bash commands and read/edit files without asking for permission:267Use `--allowedTools` to let Claude use certain tools without prompting. Listing `Read` and `Edit` lets Claude read and edit files without asking for permission. Listing `Bash` does the same for shell commands, except in a run that starts in [auto mode](/docs/en/permission-modes#how-auto-mode-evaluates-actions), where Claude Code drops a bare `Bash` entry as a broad allow rule and auto mode evaluates each command instead. This example runs a test suite and fixes failures with those three tools listed:

268 268 

269```bash theme={null}269```bash theme={null}

270claude -p "Run the test suite and fix any failures" \270claude -p "Run the test suite and fix any failures" \

271 --allowedTools "Bash,Read,Edit"271 --allowedTools "Bash,Read,Edit"

272```272```

273 273 

274To set a baseline for the whole session instead of listing individual tools, pass a [permission mode](/docs/en/permission-modes). For `-p`, the [built-in starting permission mode](/docs/en/permission-modes#which-mode-a-session-starts-in) is Manual on every plan, so pass the permission mode you want:274To set a baseline for the whole session instead of listing individual tools, pass a [permission mode](/docs/en/permission-modes). A run where nothing sets a permission mode takes the [built-in starting permission mode](/docs/en/permission-modes#which-mode-a-session-starts-in), which can be `auto`, so pass the one you want:

275 275 

276* **`auto`**: pass `--permission-mode auto` to have a classifier review most actions instead of you276* **`auto`**: pass `--permission-mode auto` to have a classifier review most actions instead of you

277* **`dontAsk`**: Claude Code denies every call that would otherwise prompt, which is useful for locked-down CI runs. Actions that need no approval in Manual mode still run, such as file reads in your working directories and the [read-only command set](/docs/en/permissions#read-only-commands), and so do actions your `--allowedTools` entries or `permissions.allow` rules cover. `AskUserQuestion`, connector tools [your organization set to `ask`](/docs/en/mcp#organization-controls-on-connector-tools), and MCP tools marked [`requiresUserInteraction`](/docs/en/mcp#require-approval-for-a-specific-tool) are denied even when an allow rule matches277* **`dontAsk`**: Claude Code denies every call that would otherwise prompt, which is useful for locked-down CI runs. Actions that need no approval in Manual mode still run, such as file reads in your working directories and the [read-only command set](/docs/en/permissions#read-only-commands), and so do actions your `--allowedTools` entries or `permissions.allow` rules cover. `AskUserQuestion`, connector tools [your organization set to `ask`](/docs/en/mcp#organization-controls-on-connector-tools), and MCP tools marked [`requiresUserInteraction`](/docs/en/mcp#require-approval-for-a-specific-tool) are denied even when an allow rule matches

hooks.md +3 −3

Details

12 12 

13Hooks are user-defined shell commands, HTTP endpoints, MCP tool calls, LLM prompts, or subagents that execute automatically at specific points in Claude Code's lifecycle. Claude Code fires the same hook events wherever it runs: sessions in the terminal, IDE extensions, the [Desktop app](/docs/en/desktop-quickstart), and [cloud sessions](/docs/en/claude-code-on-the-web). Use this reference to look up event schemas, configuration options, JSON input/output formats, and advanced features like async hooks, HTTP hooks, and MCP tool hooks.13Hooks are user-defined shell commands, HTTP endpoints, MCP tool calls, LLM prompts, or subagents that execute automatically at specific points in Claude Code's lifecycle. Claude Code fires the same hook events wherever it runs: sessions in the terminal, IDE extensions, the [Desktop app](/docs/en/desktop-quickstart), and [cloud sessions](/docs/en/claude-code-on-the-web). Use this reference to look up event schemas, configuration options, JSON input/output formats, and advanced features like async hooks, HTTP hooks, and MCP tool hooks.

14 14 

15A plugin can also register hooks as JavaScript functions that Claude Code calls in its own process, which can draw in the interface as well as act on events. A plugin that does is a [mod](/docs/en/plugins/mods/overview), and those function hooks are covered in [React to events](/docs/en/plugins/mods/events) rather than here. The hooks on this page keep working alongside mods.

16 

15## Hook lifecycle17## Hook lifecycle

16 18 

17Claude Code runs hooks at specific points during a session. When an event fires and a matcher matches, Claude Code passes JSON context about the event to your hook handler. For command hooks, input arrives on stdin. For HTTP hooks, it arrives as the POST request body. Your handler can then inspect the input, take action, and optionally return a decision.19Claude Code runs hooks at specific points during a session. When an event fires and a matcher matches, Claude Code passes JSON context about the event to your hook handler. For command hooks, input arrives on stdin. For HTTP hooks, it arrives as the POST request body. Your handler can then inspect the input, take action, and optionally return a decision.


437| `Bash(git *)` | `npm test && git push` | yes | each subcommand is checked; `git push` matches |439| `Bash(git *)` | `npm test && git push` | yes | each subcommand is checked; `git push` matches |

438| `Bash(rm *)` | `echo $(rm -rf /)` | yes | commands inside `$()` and backticks are checked; `rm -rf /` matches |440| `Bash(rm *)` | `echo $(rm -rf /)` | yes | commands inside `$()` and backticks are checked; `rm -rf /` matches |

439| `Bash(rm *)` | `echo $(date)` | no | no subcommand matches `rm *` |441| `Bash(rm *)` | `echo $(date)` | no | no subcommand matches `rm *` |

440| `Bash(cat *)` | `echo before $(date) after` | no | a substitution can sit at any argument position, so the full command and `date` are both checked; neither matches `cat *` |

441| `Bash(git *)` | `$TOOL git push` | yes | Claude Code can't tell what the command name expands to, so it runs the hook |

442| `Bash(git push *)` | `echo $(date)` | yes | patterns that specify more than the command name run the hook anyway on `$()`, backticks, or `$VAR` |442| `Bash(git push *)` | `echo $(date)` | yes | patterns that specify more than the command name run the hook anyway on `$()`, backticks, or `$VAR` |

443 443 

444When Claude Code can't determine which commands the Bash input runs, it runs your hook regardless of the pattern. Because the `if` filter is best-effort, use the [permission system](/docs/en/permissions) rather than a hook to enforce a hard allow or deny.444When Claude Code can't determine which commands the Bash input runs, it runs your hook regardless of the pattern. Because the `if` filter is best-effort, use the [permission system](/docs/en/permissions) rather than a hook to enforce a hard allow or deny.


1802| :- | :- |1802| :- | :- |

1803| `permissionDecision` | `"allow"` skips the permission prompt, except for the [actions no mode auto-approves](/docs/en/permission-modes#actions-no-mode-auto-approves) and for `AskUserQuestion` and `ExitPlanMode`, which need [`updatedInput` paired with it](#allow-with-updatedinput). `"deny"` prevents the tool call. `"ask"` prompts the user to confirm. `"defer"` exits gracefully so the tool can be resumed later. [Deny and ask rules](/docs/en/permissions#manage-permissions) are still evaluated regardless of what the hook returns |1803| `permissionDecision` | `"allow"` skips the permission prompt, except for the [actions no mode auto-approves](/docs/en/permission-modes#actions-no-mode-auto-approves) and for `AskUserQuestion` and `ExitPlanMode`, which need [`updatedInput` paired with it](#allow-with-updatedinput). `"deny"` prevents the tool call. `"ask"` prompts the user to confirm. `"defer"` exits gracefully so the tool can be resumed later. [Deny and ask rules](/docs/en/permissions#manage-permissions) are still evaluated regardless of what the hook returns |

1804| `permissionDecisionReason` | For `"ask"`, shown to the user but not Claude. For `"deny"`, shown to Claude. For `"allow"` and `"defer"`, written to the [debug log](#debug-hooks) only |1804| `permissionDecisionReason` | For `"ask"`, shown to the user but not Claude. For `"deny"`, shown to Claude. For `"allow"` and `"defer"`, written to the [debug log](#debug-hooks) only |

1805| `updatedInput` | Modifies the tool's input parameters before execution. Replaces the entire input object, so include unchanged fields alongside modified ones. Claude Code evaluates permission rules and a Bash command's [auto-background eligibility](/docs/en/tools-reference#background-commands) against the input your hook returns, not the input Claude sent. Combine with `"allow"` to auto-approve, or `"ask"` to show the modified input to the user. For `"defer"`, ignored |1805| `updatedInput` | Modifies the tool's input parameters before execution. Replaces the entire input object, so include unchanged fields alongside modified ones. Claude Code evaluates permission rules and a Bash command's [auto-background eligibility](/docs/en/tools-reference#foreground-commands-that-move-to-the-background) against the input your hook returns, not the input Claude sent. Combine with `"allow"` to auto-approve, or `"ask"` to show the modified input to the user. For `"defer"`, ignored |

1806| `additionalContext` | String added to Claude's context alongside the tool result. Ignored when `permissionDecision` is `"defer"`. See [Add context for Claude](#add-context-for-claude) |1806| `additionalContext` | String added to Claude's context alongside the tool result. Ignored when `permissionDecision` is `"defer"`. See [Add context for Claude](#add-context-for-claude) |

1807 1807 

1808When multiple PreToolUse hooks return different decisions, precedence is `deny` > `defer` > `ask` > `allow`.1808When multiple PreToolUse hooks return different decisions, precedence is `deny` > `defer` > `ask` > `allow`.

hooks-guide.md +3 −1

Details

966 966 

967`PreToolUse` hooks fire before any permission-mode check, in every [permission mode](/docs/en/permission-modes), including `dontAsk`. A hook that returns `permissionDecision: "deny"` blocks the tool even in `bypassPermissions` mode or with `--dangerously-skip-permissions`. This lets you enforce policy that users can't bypass by changing their permission mode.967`PreToolUse` hooks fire before any permission-mode check, in every [permission mode](/docs/en/permission-modes), including `dontAsk`. A hook that returns `permissionDecision: "deny"` blocks the tool even in `bypassPermissions` mode or with `--dangerously-skip-permissions`. This lets you enforce policy that users can't bypass by changing their permission mode.

968 968 

969The reverse is not true: a hook returning `"allow"` doesn't bypass deny rules from settings, and it can't suppress the prompt for MCP tools marked [`requiresUserInteraction`](/docs/en/mcp#require-approval-for-a-specific-tool) or for connector tools [your organization set to `ask`](/docs/en/mcp#organization-controls-on-connector-tools) in sessions where that setting reaches Claude Code. Hooks can tighten restrictions but not loosen them past what permission rules allow.969The reverse is not true: a hook returning `"allow"` doesn't bypass deny rules from settings, and it can't suppress the prompt for MCP tools marked [`requiresUserInteraction`](/docs/en/mcp#require-approval-for-a-specific-tool) or for connector tools [your organization set to `ask`](/docs/en/mcp#organization-controls-on-connector-tools) in sessions where that setting reaches Claude Code. Hooks in settings files and in a plugin's `hooks/hooks.json` can tighten restrictions but not loosen them past what permission rules allow.

970 

971A [mod](/docs/en/plugins/mods/overview) you install that hooks `tool.check` can approve a call that your `PreToolUse` hook blocked, unless the hook is in managed settings. [Extend permissions with hooks](/docs/en/permissions#extend-permissions-with-hooks) lists which rules hold over a mod.

970 972 

971### Hook not firing973### Hook not firing

972 974 

Details

304* Prompt Claude Code to run a command in the background304* Prompt Claude Code to run a command in the background

305* Press `Ctrl+B` to move a regular Bash tool invocation to the background. Tmux users must press `Ctrl+B` twice due to tmux's prefix key.305* Press `Ctrl+B` to move a regular Bash tool invocation to the background. Tmux users must press `Ctrl+B` twice due to tmux's prefix key.

306 306 

307When a command reaches its timeout before it finishes, Claude Code automatically [moves it to the background](/docs/en/tools-reference#background-commands) instead of stopping it, unless the command starts with `sleep`. To change how long commands run before that happens, set the [Bash timeout environment variables](/docs/en/tools-reference#timeout-and-output-limits).307When a command reaches its timeout before it finishes, Claude Code automatically [moves it to the background](/docs/en/tools-reference#foreground-commands-that-move-to-the-background) instead of stopping it, unless the command starts with `sleep`. If you've turned background tasks off with [`CLAUDE_CODE_DISABLE_BACKGROUND_TASKS`](/docs/en/env-vars#variables) or by starting in [bare mode](/docs/en/headless#start-faster-with-bare-mode), the command stops at its timeout. To change the timeout, set the [Bash timeout environment variables](/docs/en/tools-reference#timeout-and-output-limits).

308 308 

309**Key features:**309**Key features:**

310 310 


316* On macOS and Linux, Claude Code stops your running background tasks when the operating system reports critical memory pressure, provided the session has been idle for at least 30 minutes and no turn or subagent is running. Requires Claude Code v2.1.193 or later316* On macOS and Linux, Claude Code stops your running background tasks when the operating system reports critical memory pressure, provided the session has been idle for at least 30 minutes and no turn or subagent is running. Requires Claude Code v2.1.193 or later

317 * The [debug log](/docs/en/debug-your-config) says why tasks were stopped, or why a pressure event left them running317 * The [debug log](/docs/en/debug-your-config) says why tasks were stopped, or why a pressure event left them running

318 * Set [`CLAUDE_CODE_DISABLE_BG_SHELL_PRESSURE_REAP`](/docs/en/env-vars) to `1` to turn off memory-pressure stops318 * Set [`CLAUDE_CODE_DISABLE_BG_SHELL_PRESSURE_REAP`](/docs/en/env-vars) to `1` to turn off memory-pressure stops

319* Background Bash and PowerShell commands have a time limit, counted from the moment the command enters the background: 30 minutes, or the `timeout` Claude asks for when it starts a command in the background, up to a maximum of 2 hours. A command that moves to the background while it runs, for example with `Ctrl+B`, gets 30 minutes from the move. When a command reaches its limit, Claude Code stops it and tells Claude why, and Claude can start it again with a longer `timeout` if the work still needs it. Two environment variables raise the limits, in milliseconds, and neither can shorten them:319* Background Bash and PowerShell commands have a time limit, counted from the moment the command enters the background: 30 minutes, or the `timeout` Claude asks for when it starts a command in the background, up to a maximum of 2 hours. A command that moves to the background while it runs, for example with `Ctrl+B`, gets 30 minutes from the move. When a command reaches its limit, Claude Code stops it and tells Claude why, and Claude can start it again with a longer `timeout` if the work still needs it. To lengthen the limits, see [Raise the time limit for background commands](/docs/en/tools-reference#raise-the-time-limit-for-background-commands) in the tools reference

320 * Set [`BASH_DEFAULT_TIMEOUT_MS`](/docs/en/env-vars) above `1800000` to replace the 30-minute default with that value, for moved commands as well320* A background command that a foreground [subagent](/docs/en/sub-agents#run-subagents-in-foreground-or-background) started ends when that subagent's run ends, whether it finished, failed, or was interrupted; see [When a background command stops](/docs/en/tools-reference#when-a-background-command-stops) in the tools reference

321 * Set [`BASH_MAX_TIMEOUT_MS`](/docs/en/env-vars) above `7200000` to raise the 2-hour maximum. Setting `BASH_DEFAULT_TIMEOUT_MS` above `7200000` raises it the same way

322* A background command that a foreground [subagent](/docs/en/sub-agents#run-subagents-in-foreground-or-background) started ends when that subagent's run ends, whether it finished, failed, or was interrupted; see [Background commands](/docs/en/tools-reference#background-commands) in the tools reference

323 321 

324To disable all background task functionality, set the `CLAUDE_CODE_DISABLE_BACKGROUND_TASKS` environment variable to `1`. See [Environment variables](/docs/en/env-vars) for details.322To disable all background task functionality, set the [`CLAUDE_CODE_DISABLE_BACKGROUND_TASKS`](/docs/en/env-vars#variables) environment variable to `1`. Starting in [bare mode](/docs/en/headless#start-faster-with-bare-mode) turns it off as well.

325 323 

326**Common backgrounded commands:**324**Common backgrounded commands:**

327 325 

keybindings.md +1 −3

Details

481* Under a non-Latin layout such as Cyrillic, Claude Code matches Ctrl shortcuts by the key's US-layout position when the terminal uses the Kitty keyboard protocol and reports that position. In such a terminal, with a Russian layout active, pressing Ctrl and the physical W key triggers `ctrl+w`. In a terminal that doesn't report the position, Claude Code matches whatever the terminal sends for the keypress: an ASCII control code triggers the Latin shortcut, and a keypress that arrives as the Cyrillic character matches no binding481* Under a non-Latin layout such as Cyrillic, Claude Code matches Ctrl shortcuts by the key's US-layout position when the terminal uses the Kitty keyboard protocol and reports that position. In such a terminal, with a Russian layout active, pressing Ctrl and the physical W key triggers `ctrl+w`. In a terminal that doesn't report the position, Claude Code matches whatever the terminal sends for the keypress: an ASCII control code triggers the Latin shortcut, and a keypress that arrives as the Cyrillic character matches no binding

482* Under layouts that rearrange Latin letters, such as AZERTY, Claude Code matches the letter that the key types, so pressing Ctrl and the key labeled A triggers `ctrl+a`482* Under layouts that rearrange Latin letters, such as AZERTY, Claude Code matches the letter that the key types, so pressing Ctrl and the key labeled A triggers `ctrl+a`

483 483 

484Before v2.1.247, pressing a Ctrl shortcut under a non-Latin layout didn't trigger its binding in terminals that use the Kitty keyboard protocol, such as Ghostty, Kitty, WezTerm, and iTerm2.

485 

486### Chords484### Chords

487 485 

488Chords are sequences of keystrokes separated by spaces:486Chords are sequences of keystrokes separated by spaces:


617* Misspelled modifiers, such as `ctl+k`. Claude Code drops the part it doesn't recognize and applies the binding to the keystroke that remains, `k` in this example.615* Misspelled modifiers, such as `ctl+k`. Claude Code drops the part it doesn't recognize and applies the binding to the keystroke that remains, `k` in this example.

618* Invalid context names616* Invalid context names

619* Invalid action values, such as an action that isn't a string or `null`617* Invalid action values, such as an action that isn't a string or `null`

620* Unknown action names, such as a typo of a registered action. Claude Code skips the binding and keeps any default binding for that key in effect. Before v2.1.246, a binding with an unknown action name silently disabled that key618* Unknown action names, such as a typo of a registered action. Claude Code skips the binding and keeps any default binding for that key in effect.

621* Reserved shortcut conflicts619* Reserved shortcut conflicts

622* Duplicate bindings in the same context620* Duplicate bindings in the same context

623 621 

llm-gateway.md +2 −0

Details

41 41 

42[Roll out an LLM gateway for your organization](/docs/en/llm-gateway-rollout) walks each step and shows the configuration files to distribute at each one. The gateway is one part of organization setup; for policy enforcement, usage visibility, and data handling decisions, see [Set up Claude Code for your organization](/docs/en/admin-setup).42[Roll out an LLM gateway for your organization](/docs/en/llm-gateway-rollout) walks each step and shows the configuration files to distribute at each one. The gateway is one part of organization setup; for policy enforcement, usage visibility, and data handling decisions, see [Set up Claude Code for your organization](/docs/en/admin-setup).

43 43 

44To make a gateway reached through `ANTHROPIC_BASE_URL` the only destination a managed machine may use, set [`allowedProviders`](/docs/en/settings-reference#allowedproviders) to `["customEndpoint"]` in the same managed settings file and put the gateway's `ANTHROPIC_BASE_URL` in that file's `env` block. Claude Code then refuses a session pointed anywhere else, including at Anthropic directly or at a developer's own proxy, and accepts `ANTHROPIC_BASE_URL` only with the value you set there. For a gateway reached through a provider-specific endpoint variable such as `ANTHROPIC_BEDROCK_BASE_URL`, the `allowedProviders` entry says which variable to pin. Requires Claude Code v2.1.285 or later.

45 

44## Subscriptions and gateways46## Subscriptions and gateways

45 47 

46While a [gateway credential variable](/docs/en/llm-gateway-connect#set-the-credential-variable) or `apiKeyHelper` is active, requests carry that credential in place of a developer's claude.ai subscription login, and the subscription's usage limits don't apply to them. Claude Code keeps a saved claude.ai login on the machine but doesn't send it with those requests. That traffic is billed per token to whoever owns the credential the gateway forwards, such as your organization's Anthropic Console account, or your Amazon Bedrock, Google Cloud's Agent Platform, or Microsoft Foundry account when the gateway routes there.48While a [gateway credential variable](/docs/en/llm-gateway-connect#set-the-credential-variable) or `apiKeyHelper` is active, requests carry that credential in place of a developer's claude.ai subscription login, and the subscription's usage limits don't apply to them. Claude Code keeps a saved claude.ai login on the machine but doesn't send it with those requests. That traffic is billed per token to whoever owns the credential the gateway forwards, such as your organization's Anthropic Console account, or your Amazon Bedrock, Google Cloud's Agent Platform, or Microsoft Foundry account when the gateway routes there.

Details

272 272 

273Claude Code sends the discovery request with both credential headers below and omits a header whose value doesn't resolve. Sending both headers requires Claude Code v2.1.248 or later. Earlier versions send only `Authorization` when `ANTHROPIC_AUTH_TOKEN` is set and only `x-api-key` otherwise.273Claude Code sends the discovery request with both credential headers below and omits a header whose value doesn't resolve. Sending both headers requires Claude Code v2.1.248 or later. Earlier versions send only `Authorization` when `ANTHROPIC_AUTH_TOKEN` is set and only `x-api-key` otherwise.

274 274 

275* `Authorization`: `ANTHROPIC_AUTH_TOKEN` as a bearer token, otherwise the [`apiKeyHelper`](/docs/en/llm-gateway-connect#rotate-credentials-with-apikeyhelper) value as a bearer token. In that case Claude Code waits for the helper to return before sending the request.275* `Authorization`: `ANTHROPIC_AUTH_TOKEN` as a bearer token, otherwise the [`apiKeyHelper`](/docs/en/llm-gateway-connect#rotate-credentials-with-apikeyhelper) value as a bearer token.

276* `x-api-key`: the API key Claude Code resolved, such as `ANTHROPIC_API_KEY`. When a helper value is the only credential, this header carries it too, so the value arrives in both headers.276* `x-api-key`: the API key Claude Code resolved, such as `ANTHROPIC_API_KEY`. When a helper value is the only credential, this header carries it too, so the value arrives in both headers.

277 277 

278Claude Code also sends any headers from `ANTHROPIC_CUSTOM_HEADERS`. When a custom header has a non-empty value, Claude Code sends it in place of a built-in header of the same name, matching names case-insensitively.278Claude Code also sends any headers from `ANTHROPIC_CUSTOM_HEADERS`. When a custom header has a non-empty value, Claude Code sends it in place of a built-in header of the same name, matching names case-insensitively.

Details

185 185 

186The [gateway login keys](#choose-a-delivery-mechanism) follow a separate rule. Claude Code never reads them from server-managed settings, so while server-managed settings are the selected source, the highest-ranked admin source on the machine that carries a policy key still supplies them. A value in an admin source ranked below that one, or in the HKCU registry, is ignored.186The [gateway login keys](#choose-a-delivery-mechanism) follow a separate rule. Claude Code never reads them from server-managed settings, so while server-managed settings are the selected source, the highest-ranked admin source on the machine that carries a policy key still supplies them. A value in an admin source ranked below that one, or in the HKCU registry, is ignored.

187 187 

188[`allowedProviders`](/docs/en/settings-reference#allowedproviders) has its own rule: its entry's Scope note says how a list set on the machine combines with a server-managed one. Requires Claude Code v2.1.285 or later.

189 

188When an admin source sets `allowManagedMcpServersOnly` or an `allowedMcpServers` list and that value isn't the one in force, `/status` and `claude doctor` name that source and key.190When an admin source sets `allowManagedMcpServersOnly` or an `allowedMcpServers` list and that value isn't the one in force, `/status` and `claude doctor` name that source and key.

189 191 

190### Compose every managed source192### Compose every managed source


323* An empty managed settings file counts as `{}`.325* An empty managed settings file counts as `{}`.

324* A malformed value in the user-writable HKCU registry key never blocks launch. Claude Code reports it as a notice in `/status` and `claude doctor` instead.326* A malformed value in the user-writable HKCU registry key never blocks launch. Claude Code reports it as a notice in `/status` and `claude doctor` instead.

325 327 

326If a managed settings file, drop-in file, or `managed-settings.d/` directory can't be read and no admin source supplies a policy, sessions signed in with claude.ai or Claude Console credentials exit at startup with a message to contact an administrator.328When a managed settings file, drop-in file, `managed-settings.d/` directory, MDM profile, or HKLM registry value exists but can't be read, and no admin source supplies a policy, what happens depends on why the read failed:

329 

330* If the operating system denied the read, for example on a root-only file, every session starts without that source's policies. `/status` and `claude doctor` record the failure, and a run with `-p` also prints it to stderr.

331* For any other read failure, such as an I/O error, every session exits at startup with [a message to contact an administrator](/docs/en/errors#unable-to-read-managed-policy-settings).

327 332 

328To find a dropped entry, look in one of three places:333To find a dropped entry, look in one of three places:

329 334 


355| Field | Behavior when present but invalid |360| Field | Behavior when present but invalid |

356| :- | :- |361| :- | :- |

357| `allowedMcpServers` | Enforced as an empty allowlist until the value is fixed, so no MCP servers that users add are admitted. Servers your organization delivers through [`managedMcpServers`](/docs/en/settings-reference#managedmcpservers) still load, and `managed-mcp.json` servers load per [How a server is evaluated](/docs/en/managed-mcp#how-a-server-is-evaluated). An individual invalid entry is stripped and the valid subset is enforced. |362| `allowedMcpServers` | Enforced as an empty allowlist until the value is fixed, so no MCP servers that users add are admitted. Servers your organization delivers through [`managedMcpServers`](/docs/en/settings-reference#managedmcpservers) still load, and `managed-mcp.json` servers load per [How a server is evaluated](/docs/en/managed-mcp#how-a-server-is-evaluated). An individual invalid entry is stripped and the valid subset is enforced. |

363| [`allowedProviders`](/docs/en/settings-reference#allowedproviders) | Enforced as an empty allowlist until the value is fixed, so every API provider is refused and Claude Code doesn't start on the machine. If only an individual entry isn't a known provider name, Claude Code drops and reports that entry and enforces the rest. |

358| `allowedHttpHookUrls` | Claude Code enforces an empty managed [allowlist](/docs/en/settings-reference#allowedhttphookurls) until you fix the value, so an HTTP hook runs only if another settings file lists its URL. If only an individual entry is invalid, Claude Code strips that entry and enforces the rest. |364| `allowedHttpHookUrls` | Claude Code enforces an empty managed [allowlist](/docs/en/settings-reference#allowedhttphookurls) until you fix the value, so an HTTP hook runs only if another settings file lists its URL. If only an individual entry is invalid, Claude Code strips that entry and enforces the rest. |

359| `httpHookAllowedEnvVars` | Claude Code enforces an empty managed [allowlist](/docs/en/settings-reference#httphookallowedenvvars) until you fix the value, so a header variable is interpolated only if another settings file names it. If only an individual entry is invalid, Claude Code strips that entry and enforces the rest. |365| `httpHookAllowedEnvVars` | Claude Code enforces an empty managed [allowlist](/docs/en/settings-reference#httphookallowedenvvars) until you fix the value, so a header variable is interpolated only if another settings file names it. If only an individual entry is invalid, Claude Code strips that entry and enforces the rest. |

360| `allowedChannelPlugins` | Claude Code enforces an empty allowlist until you fix the value, so no channel plugin passed to `--channels` is admitted. If only an individual entry is invalid, it strips that entry and enforces the rest. |366| `allowedChannelPlugins` | Claude Code enforces an empty allowlist until you fix the value, so no channel plugin passed to `--channels` is admitted. If only an individual entry is invalid, it strips that entry and enforces the rest. |


401 407 

402Most of them are locks: the value a lock governs, such as permission rules or `sandbox.network.allowedDomains`, is an ordinary key that any level can set, and the lock tells Claude Code to honor only the managed value.408Most of them are locks: the value a lock governs, such as permission rules or `sandbox.network.allowedDomains`, is an ordinary key that any level can set, and the lock tells Claude Code to honor only the managed value.

403 409 

404The table covers the permission, plugin, and delivery controls. For any key not listed here, the Scope column of the [settings reference](/docs/en/settings-reference#all-settings) index says whether it's managed-only; the remaining managed-only keys there include the gateway login URL, version, browser, mobile-simulator, SSH host, Desktop local-session, sandbox binary path, model pricing, model restriction, and CLAUDE.md controls.410The table covers the permission, plugin, and delivery controls. For any key not listed here, the Scope column of the [settings reference](/docs/en/settings-reference#all-settings) index says whether it's managed-only.

405 411 

406| Setting | Description |412| Setting | Description |

407| :- | :- |413| :- | :- |

mcp.md +5 −5

Details

247 247 

248* ``⏸ Pending approval (run `claude` to approve)``: a project-scoped server from `.mcp.json` that you haven't approved yet. Claude Code shows it in both `claude mcp list` and `claude mcp get <name>`. Run `claude` interactively to review and approve it.248* ``⏸ Pending approval (run `claude` to approve)``: a project-scoped server from `.mcp.json` that you haven't approved yet. Claude Code shows it in both `claude mcp list` and `claude mcp get <name>`. Run `claude` interactively to review and approve it.

249* `✘ Rejected (see disabledMcpjsonServers in settings)`: a `.mcp.json` server that a [`disabledMcpjsonServers`](/docs/en/settings-reference#disabledmcpjsonservers) entry rejects. Claude Code shows it only in `claude mcp get <name>`.249* `✘ Rejected (see disabledMcpjsonServers in settings)`: a `.mcp.json` server that a [`disabledMcpjsonServers`](/docs/en/settings-reference#disabledmcpjsonservers) entry rejects. Claude Code shows it only in `claude mcp get <name>`.

250* `⊘ Disabled for this project (re-enable via /mcp)`: a server that the project's [`disabledMcpServers`](#disable-a-server-without-removing-it) list names. Claude Code shows it in both `claude mcp list` and `claude mcp get <name>`. Turn the server back on from the `/mcp` panel. Before v2.1.238, both commands connected to a disabled server to health-check it and reported the connection result.250* `⊘ Disabled for this project (re-enable via /mcp)`: a server that the project's [`disabledMcpServers`](#disable-a-server-without-removing-it) list names. Claude Code shows it in both `claude mcp list` and `claude mcp get <name>`. Turn the server back on from the `/mcp` panel.

251 251 

252WebSocket servers don't appear in `claude mcp list` output. Use `claude mcp get <name>` or the `/mcp` panel to check them.252WebSocket servers don't appear in `claude mcp list` output. Use `claude mcp get <name>` or the `/mcp` panel to check them.

253 253 


289 289 

290* **Hidden whitespace**: Claude Code warns when an MCP config value carries hidden leading or trailing whitespace, which often comes from pasting a token with a trailing newline. Claude Code checks `command`, `url`, each `args` entry, and the values and key names under `env` and `headers`. Claude Code shows the warning in `claude mcp list` output and in `/mcp`, naming the affected fields without echoing their values, for example `Leading or trailing whitespace in: headers.Authorization`. Claude Code doesn't trim the whitespace and uses the values exactly as written, so edit the configuration to remove it.290* **Hidden whitespace**: Claude Code warns when an MCP config value carries hidden leading or trailing whitespace, which often comes from pasting a token with a trailing newline. Claude Code checks `command`, `url`, each `args` entry, and the values and key names under `env` and `headers`. Claude Code shows the warning in `claude mcp list` output and in `/mcp`, naming the affected fields without echoing their values, for example `Leading or trailing whitespace in: headers.Authorization`. Claude Code doesn't trim the whitespace and uses the values exactly as written, so edit the configuration to remove it.

291* **Same name in more than one scope**: if you define the same server name in more than one [scope](#mcp-installation-scopes) with different endpoints, Claude Code warns about the conflict in `claude mcp list` output and in `/mcp`. Claude Code stores OAuth sign-ins per endpoint, so when you authenticate the definition that loads in one project, you still need to sign in separately in a project where a different definition loads. Keep the endpoint you want and remove the others with `claude mcp remove <name> --scope <scope>`. In the warning, Claude Code quotes each scope's endpoint as written in your configuration, with [`${VAR}` references](#environment-variable-expansion-in-mcp-json) unexpanded, so it never shows a resolved value such as an API key.291* **Same name in more than one scope**: if you define the same server name in more than one [scope](#mcp-installation-scopes) with different endpoints, Claude Code warns about the conflict in `claude mcp list` output and in `/mcp`. Claude Code stores OAuth sign-ins per endpoint, so when you authenticate the definition that loads in one project, you still need to sign in separately in a project where a different definition loads. Keep the endpoint you want and remove the others with `claude mcp remove <name> --scope <scope>`. In the warning, Claude Code quotes each scope's endpoint as written in your configuration, with [`${VAR}` references](#environment-variable-expansion-in-mcp-json) unexpanded, so it never shows a resolved value such as an API key.

292* **Reserved names**: Claude Code reserves the names of its built-in servers, including `workspace`, `claude-in-chrome`, `computer-use`, `Claude Preview`, and `Claude Browser`. If your configuration defines a server with a reserved name, Claude Code skips it at load time and shows a warning asking you to rename it. `claude mcp add` rejects a reserved name with an error. `Claude Preview` and `Claude Browser` both name the built-in server that the [Claude Code desktop app's preview pane](/docs/en/desktop#preview-your-app) uses. Before v2.1.205, `Claude Browser` wasn't reserved, so a user-configured server could register under that name.292* **Reserved names**: Claude Code reserves the names of its built-in servers, including `workspace`, `claude-in-chrome`, `computer-use`, `Claude Preview`, and `Claude Browser`. If your configuration defines a server with a reserved name, Claude Code skips it at load time and shows a warning asking you to rename it. `claude mcp add` rejects a reserved name with an error. `Claude Preview` and `Claude Browser` both name the built-in server that the [Claude Code desktop app's preview pane](/docs/en/desktop#preview-your-app) uses.

293* **Missing environment variable**: if a [`${VAR}` reference](#environment-variable-expansion-in-mcp-json) in a server's configuration names a variable that isn't set and has no `:-default`, Claude Code warns in `claude mcp list` output and in `/mcp`, naming the variable, and still loads the server with the `${VAR}` text unexpanded. Set the variable or add a `${VAR:-default}` fallback. In a remote server's `url` and `headers`, some credential variables [read as empty](#credential-variables-that-read-as-empty) instead, with no warning.293* **Missing environment variable**: if a [`${VAR}` reference](#environment-variable-expansion-in-mcp-json) in a server's configuration names a variable that isn't set and has no `:-default`, Claude Code warns in `claude mcp list` output and in `/mcp`, naming the variable, and still loads the server with the `${VAR}` text unexpanded. Set the variable or add a `${VAR:-default}` fallback. In a remote server's `url` and `headers`, some credential variables [read as empty](#credential-variables-that-read-as-empty) instead, with no warning.

294 294 

295#### Tool availability295#### Tool availability


369 369 

370#### Failed first connections370#### Failed first connections

371 371 

372When an HTTP or SSE server's first connection fails with a transient error, such as a 5xx response, a connection refused, or a timeout, Claude Code retries up to three times. If the connection still fails, Claude Code marks the server as failed. Claude Code retries this way at startup and when a server is added mid-session. That includes a server Claude Code adds to a [cloud session](/docs/en/claude-code-on-the-web) from its configuration and a server you add with the Agent SDK's [`setMcpServers()`](/docs/en/agent-sdk/typescript).372When an HTTP or SSE server's first connection fails with a transient error, such as a 5xx response, a connection refused, or a timeout, Claude Code retries up to three times. If the connection still fails, Claude Code marks the server as failed.

373 373 

374Claude Code doesn't retry in these cases:374Claude Code doesn't retry in these cases:

375 375 


495 495 

496Plugin servers appear in `/mcp` with indicators showing they come from plugins.496Plugin servers appear in `/mcp` with indicators showing they come from plugins.

497 497 

498For a plugin's stdio server, `claude mcp get` prints `Command: stdio`, an empty `Args:` line, and each environment variable as `NAME=[REDACTED]`. The values are hidden because they can carry credentials.

499 

498**Plugin MCP tool names**:500**Plugin MCP tool names**:

499 501 

500Tools from a plugin-bundled MCP server include both the plugin name and the server key in their callable name. The full form is `mcp__plugin_<plugin-name>_<server-name>__<tool-name>`, where any character outside `A-Z`, `a-z`, `0-9`, `_`, and `-` is replaced with `_`. For the `database-tools` server bundled in a plugin named `my-plugin`, a `query` tool is callable as:502Tools from a plugin-bundled MCP server include both the plugin name and the server key in their callable name. The full form is `mcp__plugin_<plugin-name>_<server-name>__<tool-name>`, where any character outside `A-Z`, `a-z`, `0-9`, `_`, and `-` is replaced with `_`. For the `database-tools` server bundled in a plugin named `my-plugin`, a `query` tool is callable as:


1326* Top-level property names must be 1 to 64 characters long and use only ASCII letters and digits, `_`, `.`, and `-`1328* Top-level property names must be 1 to 64 characters long and use only ASCII letters and digits, `_`, `.`, and `-`

1327* The schema must be valid against the JSON Schema draft 2020-12 meta-schema. Claude Code applies this check to schemas that declare no `$schema` and schemas that declare draft 2020-12. A schema that declares any other dialect skips this check, though the property-name check above still applies1329* The schema must be valid against the JSON Schema draft 2020-12 meta-schema. Claude Code applies this check to schemas that declare no `$schema` and schemas that declare draft 2020-12. A schema that declares any other dialect skips this check, though the property-name check above still applies

1328 1330 

1329Claude Code runs the checks after the [root-level combinator rewrite](#tool-input-schemas-with-a-root-level-combinator), on the schema it would actually send.

1330 

1331When Claude Code excludes a tool, it records the reason in the server's log and tells Claude which tools it excluded and why, so you can ask Claude why a tool is missing. If you fix the schema on the server, the tool comes back the next time Claude Code loads the server's tools.1331When Claude Code excludes a tool, it records the reason in the server's log and tells Claude which tools it excluded and why, so you can ask Claude why a tool is missing. If you fix the schema on the server, the tool comes back the next time Claude Code loads the server's tools.

1332 1332 

1333Claude Code turns the exclusion on through a feature flag it fetches from Anthropic. On a [deployment where flag fetching is off](/docs/en/env-vars#features-that-need-feature-flag-fetching), or on a machine whose flags have never arrived, such as an air-gapped machine, Claude Code still runs the checks and records in the server's log which tool would be rejected, but sends the tool's schema to the API anyway. The API rejects a request that includes that schema with [a 400 error naming the tool by its position](/docs/en/errors#tool-input-schema-is-invalid). Before v2.1.216, no deployment ran these checks.1333Claude Code turns the exclusion on through a feature flag it fetches from Anthropic. On a [deployment where flag fetching is off](/docs/en/env-vars#features-that-need-feature-flag-fetching), or on a machine whose flags have never arrived, such as an air-gapped machine, Claude Code still runs the checks and records in the server's log which tool would be rejected, but sends the tool's schema to the API anyway. The API rejects a request that includes that schema with [a 400 error naming the tool by its position](/docs/en/errors#tool-input-schema-is-invalid). Before v2.1.216, no deployment ran these checks.

Details

334 334 

335 What happens next tells you where the problem is:335 What happens next tells you where the problem is:

336 336 

337 * The command starts and waits for input: the server itself works. Run `claude mcp get <name>` and confirm the command shown there matches what you just ran. If the command shown differs from what you typed, you likely omitted the `--` separator before the server command. Remove the server and re-add it with `--` in place. If you wrote `.mcp.json` by hand, check its syntax and location.337 * The command starts and waits for input: the server itself works.

338 

339 Run `claude mcp get <name>` and confirm the command shown there matches what you just ran. If the command shown differs from what you typed, you likely omitted the `--` separator before the server command. Remove the server and re-add it with `--` in place. If you wrote `.mcp.json` by hand, check its syntax and location. Before v2.1.285, `claude mcp get` printed no `Command` line for a stdio entry saved without a `type` field, such as a hand-written `.mcp.json` entry. On those versions, run `claude mcp list` instead, which prints the command line either way.

338 * The command errors: the message names what's missing, such as Node.js or a browser.340 * The command errors: the message names what's missing, such as Node.js or a browser.

339 </Accordion>341 </Accordion>

340 342 

model-config.md +10 −5

Details

35| **`sonnet`** | Uses the latest Sonnet model for daily coding tasks |35| **`sonnet`** | Uses the latest Sonnet model for daily coding tasks |

36| **`opus`** | Uses the latest Opus model for complex reasoning tasks |36| **`opus`** | Uses the latest Opus model for complex reasoning tasks |

37| **`haiku`** | Uses the fast and efficient Haiku model for simple tasks |37| **`haiku`** | Uses the fast and efficient Haiku model for simple tasks |

38| **`sonnet[1m]`** | Uses Sonnet with a [1 million token context window](https://platform.claude.com/docs/en/build-with-claude/context-windows#context-window-sizes-by-model) for long sessions. No effect when `sonnet` already resolves to Sonnet 5.5 or Sonnet 5 with their native 1M window; behind an [LLM gateway](/docs/en/llm-gateway), selects the 1M window for that model |38| **`sonnet[1m]`** | Uses Sonnet with a [1 million token context window](https://platform.claude.com/docs/en/build-with-claude/context-windows#context-window-sizes-by-model) for long sessions. No effect when `sonnet` already resolves to Sonnet 5.5 or Sonnet 5 with their native 1M window |

39| **`opus[1m]`** | Uses Opus with a [1 million token context window](https://platform.claude.com/docs/en/build-with-claude/context-windows#context-window-sizes-by-model) for long sessions |39| **`opus[1m]`** | Uses Opus with a [1 million token context window](https://platform.claude.com/docs/en/build-with-claude/context-windows#context-window-sizes-by-model) for long sessions |

40| **`opusplan`** | Special mode that uses `opus` during plan mode, then switches to `sonnet` for execution |40| **`opusplan`** | Special mode that uses `opus` during plan mode, then switches to `sonnet` for execution |

41 41 


477 477 

478### Fallback model chains478### Fallback model chains

479 479 

480When the primary model is overloaded, unavailable, or returns another non-retryable server error, Claude Code can switch to a fallback model instead of failing the request. Authentication, billing, rate-limit, request-size, and transport errors, and a [denial by your organization's policy check](/docs/en/errors#automatic-retries), never trigger a switch; those follow their normal retry and error handling.480When the primary model is overloaded, unavailable, or returns another non-retryable server error, Claude Code can switch to a fallback model instead of failing the request. Authentication, billing, rate-limit, request-size, and transport errors, and a [denial by your organization's policy check](/docs/en/errors#automatic-retries), never trigger a switch; those follow their normal retry and error handling. It does switch when [Amazon Bedrock](/docs/en/amazon-bedrock#when-a-model-is-disabled-mid-session) or [Google Cloud's Agent Platform](/docs/en/google-vertex-ai#when-a-model-is-disabled-mid-session) refuses a model your account can't invoke, which Claude Code treats as the model being unavailable rather than as an authentication error.

481 481 

482Configure one or more fallback models and Claude Code tries them in order, showing a notice when it switches. The switch lasts for the current turn only, so your next message tries the primary model first again. Claude Code caps chains at three models after duplicate removal and ignores extra entries.482Configure one or more fallback models and Claude Code tries them in order, showing a notice when it switches. The switch lasts for the current turn only, so your next message tries the primary model first again. Claude Code caps chains at three models after duplicate removal and ignores extra entries.

483 483 


707 707 

708Claude Code checks these plan requirements only when it connects to the Anthropic API directly. If you point `ANTHROPIC_BASE_URL` at an [LLM gateway](/docs/en/llm-gateway#subscriptions-and-gateways) and your saved claude.ai login stays the active credential, Claude Code doesn't check your plan's usage credits. The `[1m]` options stay available in `/model`, and the gateway decides whether the request succeeds. Before v2.1.229, Claude Code rejected `/model sonnet[1m]` in that configuration when it couldn't confirm usage credits on the account.708Claude Code checks these plan requirements only when it connects to the Anthropic API directly. If you point `ANTHROPIC_BASE_URL` at an [LLM gateway](/docs/en/llm-gateway#subscriptions-and-gateways) and your saved claude.ai login stays the active credential, Claude Code doesn't check your plan's usage credits. The `[1m]` options stay available in `/model`, and the gateway decides whether the request succeeds. Before v2.1.229, Claude Code rejected `/model sonnet[1m]` in that configuration when it couldn't confirm usage credits on the account.

709 709 

710<span id="context-window-behind-a-gateway" />

711 

712If you set `ANTHROPIC_BASE_URL` to an [LLM gateway](/docs/en/llm-gateway) or another proxy, Claude Code gives each model it recognizes the same context window the model has on the Anthropic API. Fable 5.1, Fable 5, Sonnet 5 and later, and Opus 4.7 and later get the 1M window with no `[1m]` variant to select, and a model that reaches 1M only through its `[1m]` variant, such as Opus 4.6, runs at 200K without it. Claude Code can't detect a lower limit that the gateway or the server behind it enforces. If your gateway rejects requests above 200K tokens, run [`/autocompact 200k`](#set-the-auto-compact-window) so sessions compact at that boundary.

713 

710To turn off 1M context, set `CLAUDE_CODE_DISABLE_1M_CONTEXT=1`. Claude Code removes 1M model variants from the model picker. On models with a native 1M window, such as Sonnet 5 and the Fable models, it also treats the model as having a 200K context window:714To turn off 1M context, set `CLAUDE_CODE_DISABLE_1M_CONTEXT=1`. Claude Code removes 1M model variants from the model picker. On models with a native 1M window, such as Sonnet 5 and the Fable models, it also treats the model as having a 200K context window:

711 715 

712* With auto-compaction on, sessions compact at the 200K boundary through [auto-compaction](#set-the-auto-compact-window). Setting the auto-compact window above 200K doesn't lift the hold, because Claude Code caps that window at the model's context window.716* With auto-compaction on, sessions compact at the 200K boundary through [auto-compaction](#set-the-auto-compact-window). Setting the auto-compact window above 200K doesn't lift the hold, because Claude Code caps that window at the model's context window.


733 737 

734On the Anthropic API, Sonnet 5.5 and Sonnet 5 always run with the 1M context window. There is no 200K variant, no `[1m]` suffix to select, and no usage credits required on any plan. Sessions auto-compact before the window fills, at about 967K tokens by default; set [`CLAUDE_CODE_AUTO_COMPACT_WINDOW`](/docs/en/env-vars) to choose a different threshold.738On the Anthropic API, Sonnet 5.5 and Sonnet 5 always run with the 1M context window. There is no 200K variant, no `[1m]` suffix to select, and no usage credits required on any plan. Sessions auto-compact before the window fills, at about 967K tokens by default; set [`CLAUDE_CODE_AUTO_COMPACT_WINDOW`](/docs/en/env-vars) to choose a different threshold.

735 739 

736Two configurations budget the window at 200K instead:740Claude Code gives Sonnet 5.5 and Sonnet 5 the same 1M window behind an [LLM gateway](/docs/en/llm-gateway) or another custom `ANTHROPIC_BASE_URL`. If your gateway enforces a lower limit, see [the context window behind a gateway](#context-window-behind-a-gateway).

741 

742This setting budgets the window at 200K instead:

737 743 

738* **LLM gateway**: when `ANTHROPIC_BASE_URL` points at a [gateway](/docs/en/llm-gateway), Claude Code can't verify 1M support. To use the full window, select Sonnet 5.5 (1M context) in the model picker, which maps to `sonnet[1m]`, or run `/model claude-sonnet-5[1m]` for Sonnet 5.

739* **`CLAUDE_CODE_DISABLE_1M_CONTEXT=1`**: holds sessions on every model with a native 1M window to a 200K window; see [Extended context](#extended-context) for how the hold is enforced. Useful for deployments that need to cap context.744* **`CLAUDE_CODE_DISABLE_1M_CONTEXT=1`**: holds sessions on every model with a native 1M window to a 200K window; see [Extended context](#extended-context) for how the hold is enforced. Useful for deployments that need to cap context.

740 745 

741## Context window and auto-compaction746## Context window and auto-compaction


765* [Cloud sessions](/docs/en/claude-code-on-the-web) compact as the conversation approaches the model's limit770* [Cloud sessions](/docs/en/claude-code-on-the-web) compact as the conversation approaches the model's limit

766* Sonnet 4.6 and Opus 4.6 without [extended context](#extended-context) compact at the 200K boundary, and so do Opus 4.8 and later when they run with a 200K context window, such as on Amazon Bedrock, Google Cloud's Agent Platform, and Microsoft Foundry771* Sonnet 4.6 and Opus 4.6 without [extended context](#extended-context) compact at the 200K boundary, and so do Opus 4.8 and later when they run with a 200K context window, such as on Amazon Bedrock, Google Cloud's Agent Platform, and Microsoft Foundry

767* When you set [`CLAUDE_CODE_DISABLE_1M_CONTEXT=1`](/docs/en/env-vars), models with a native 1M window, such as Sonnet 5 and the Fable models, compact at the 200K boundary772* When you set [`CLAUDE_CODE_DISABLE_1M_CONTEXT=1`](/docs/en/env-vars), models with a native 1M window, such as Sonnet 5 and the Fable models, compact at the 200K boundary

768* Models running with a native 1M window, such as Sonnet 5, the Fable models, and Opus 4.7 and later on the Anthropic API, compact before the window fills, at about 967K tokens by default. On Amazon Bedrock, Google Cloud's Agent Platform, and Microsoft Foundry, [Pin models for third-party deployments](#pin-models-for-third-party-deployments) says which models run with that window; for the configurations that budget Sonnet 5.5 and Sonnet 5 at 200K instead, see [Sonnet 5.5 and Sonnet 5 context window](#sonnet-5-5-and-sonnet-5-context-window)773* Models running with a native 1M window compact before the window fills, at about 967K tokens by default. On the Anthropic API, these include Sonnet 5, the Fable models, and Opus 4.7 and later. On Amazon Bedrock, Google Cloud's Agent Platform, and Microsoft Foundry, see [Pin models for third-party deployments](#pin-models-for-third-party-deployments) for which models run with that window. Behind a custom `ANTHROPIC_BASE_URL`, see [the context window behind a gateway](#context-window-behind-a-gateway)

769* Sessions on a model ID Claude Code doesn't recognize, such as an [LLM gateway](/docs/en/llm-gateway) alias, compact at the context window Claude Code assumes for the ID; see [Correct the window for a gateway or custom model ID](#correct-the-window-for-a-gateway-or-custom-model-id)774* Sessions on a model ID Claude Code doesn't recognize, such as an [LLM gateway](/docs/en/llm-gateway) alias, compact at the context window Claude Code assumes for the ID; see [Correct the window for a gateway or custom model ID](#correct-the-window-for-a-gateway-or-custom-model-id)

770 775 

771### Correct the window for a gateway or custom model ID776### Correct the window for a gateway or custom model ID

Details

1309* `managed_settings.trigger`: `"startup"` for the session-start event, `"change"` when the managed settings or the policy helper's state changed later in the session, or `"refused"` when a managed settings policy stopped the session. Claude Code sends a `change` event only when an attribute differs from the last event it sent, and a changed setting value counts even when `OTEL_LOG_MANAGED_SETTINGS` is off1309* `managed_settings.trigger`: `"startup"` for the session-start event, `"change"` when the managed settings or the policy helper's state changed later in the session, or `"refused"` when a managed settings policy stopped the session. Claude Code sends a `change` event only when an attribute differs from the last event it sent, and a changed setting value counts even when `OTEL_LOG_MANAGED_SETTINGS` is off

1310* `error.type`: why Claude Code stopped the session. Present only on `refused` events:1310* `error.type`: why Claude Code stopped the session. Present only on `refused` events:

1311 * `"helper_failed"`: a [policy helper run failed](/docs/en/settings-reference#helper-failures)1311 * `"helper_failed"`: a [policy helper run failed](/docs/en/settings-reference#helper-failures)

1312 * `"policy_invalid"`: the managed settings contain an error that stops Claude Code from starting, or an admin source failed to load, so Claude Code can't check organization login enforcement1312 * `"policy_invalid"`: the managed settings contain an error that stops Claude Code from starting, or an admin source failed to load for a reason other than a denied read, so Claude Code can't check organization login or provider enforcement

1313 * `"provider_not_allowed"`: the session would use an API provider, or send a provider's traffic to a host, that the managed [`allowedProviders`](/docs/en/settings-reference#allowedproviders) list doesn't allow. Requires Claude Code v2.1.285 or later

1313 * `"consent_rejected"`: the user rejected the [security approval dialog](/docs/en/server-managed-settings#security-approval-dialogs) for server-managed settings1314 * `"consent_rejected"`: the user rejected the [security approval dialog](/docs/en/server-managed-settings#security-approval-dialogs) for server-managed settings

1314 * `"force_refresh_failed"`: the settings fetch that [`forceRemoteSettingsRefresh`](/docs/en/settings-reference#forceremotesettingsrefresh) requires failed1315 * `"force_refresh_failed"`: the settings fetch that [`forceRemoteSettingsRefresh`](/docs/en/settings-reference#forceremotesettingsrefresh) requires failed

1315 * `"gateway_rejected"`: a [Claude apps gateway](/docs/en/claude-apps-gateway) answered the managed settings load with HTTP 4031316 * `"gateway_rejected"`: a [Claude apps gateway](/docs/en/claude-apps-gateway) answered the managed settings load with HTTP 403

Details

222| `storage.googleapis.com` | Native installer and native auto-updater on versions prior to 2.1.116 |222| `storage.googleapis.com` | Native installer and native auto-updater on versions prior to 2.1.116 |

223| `registry.npmjs.org` | Plugin installs (fetching npm-source plugin packages and installing plugins' Node.js package dependencies), `npx`-launched MCP servers, and the package registry for npm and bun installs of Claude Code itself |223| `registry.npmjs.org` | Plugin installs (fetching npm-source plugin packages and installing plugins' Node.js package dependencies), `npx`-launched MCP servers, and the package registry for npm and bun installs of Claude Code itself |

224| `bridge.claudeusercontent.com` | [Claude in Chrome](/docs/en/chrome) extension WebSocket bridge |224| `bridge.claudeusercontent.com` | [Claude in Chrome](/docs/en/chrome) extension WebSocket bridge |

225| `*.frame.claudeusercontent.com` | [Artifact](/docs/en/artifacts) content reads. The CLI fetches an artifact's files from this host when Claude opens one, and only when the Artifact tool is [available](/docs/en/artifacts#availability) for your account. To turn the tool off and drop this requirement, set [`"enableArtifact": false`](/docs/en/settings-reference#enableartifact) or [`CLAUDE_CODE_DISABLE_ARTIFACT=1`](/docs/en/env-vars); Claude Code also honors the deprecated [`disableArtifact`](/docs/en/settings-reference#disableartifact) setting. See [Disable artifacts](/docs/en/artifacts#disable-artifacts) for how these settings interact |225| `*.frame.claudeusercontent.com` | [Artifact](/docs/en/artifacts) content reads. The CLI fetches an artifact's files from this host when Claude opens one, and only when the Artifact tool is [available](/docs/en/artifacts#availability) for your account. To turn the tool off and drop this requirement, set [`"enableArtifact": false`](/docs/en/settings-reference#enableartifact) or [`CLAUDE_CODE_DISABLE_ARTIFACT=1`](/docs/en/env-vars) |

226| `github.com` | Cloning GitHub-hosted [plugin marketplaces](/docs/en/plugins/overview) and plugins, including the official Anthropic marketplace, over HTTPS or SSH. To clone GitHub `owner/repo` sources over HTTPS only, set [`CLAUDE_CODE_PLUGIN_PREFER_HTTPS=1`](/docs/en/env-vars) |226| `github.com` | Cloning GitHub-hosted [plugin marketplaces](/docs/en/plugins/overview) and plugins, including the official Anthropic marketplace, over HTTPS or SSH. To clone GitHub `owner/repo` sources over HTTPS only, set [`CLAUDE_CODE_PLUGIN_PREFER_HTTPS=1`](/docs/en/env-vars) |

227| `raw.githubusercontent.com` | Changelog feed for [`/release-notes`](/docs/en/commands). In interactive sessions, Claude Code also fetches it in the background at startup when its cached changelog doesn't yet cover the running version, such as the first start after an update; non-interactive and cloud sessions never fetch it |227| `raw.githubusercontent.com` | Changelog feed for [`/release-notes`](/docs/en/commands). In interactive sessions, Claude Code also fetches it in the background at startup when its cached changelog doesn't yet cover the running version, such as the first start after an update; non-interactive and cloud sessions never fetch it |

228| `*-review.googlesource.com` | Gerrit change lookup on `googlesource.com` checkouts. When a Claude Desktop Code tab session starts or resumes on a [trusted](/docs/en/permissions#project-allow-rules-and-workspace-trust) checkout whose `origin` is a `googlesource.com` host, Claude Code asks that host's `-review` server anonymously for the open change matching HEAD's `Change-Id`, once per start or resume. Other session types skip the lookup, and no other Gerrit host is contacted. Optional: disable with [`CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC`](/docs/en/env-vars) |228| `*-review.googlesource.com` | Gerrit change lookup on `googlesource.com` checkouts. When a Claude Desktop Code tab session starts or resumes on a [trusted](/docs/en/permissions#project-allow-rules-and-workspace-trust) checkout whose `origin` is a `googlesource.com` host, Claude Code asks that host's `-review` server anonymously for the open change matching HEAD's `Change-Id`, once per start or resume. Other session types skip the lookup, and no other Gerrit host is contacted. Optional: disable with [`CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC`](/docs/en/env-vars) |

Details

82| How you run Claude Code | Built-in starting permission mode |82| How you run Claude Code | Built-in starting permission mode |

83| :- | :- |83| :- | :- |

84| Any settings file sets `disableAutoMode` to `"disable"` | `default` |84| Any settings file sets `disableAutoMode` to `"disable"` | `default` |

85| `claude -p` or the [Agent SDK](/docs/en/agent-sdk/permissions) | `default` |85| `claude -p` or the [Agent SDK](/docs/en/agent-sdk/permissions#permission-modes) | `default` in sessions that [fetch feature flags](/docs/en/env-vars#features-that-need-feature-flag-fetching). In sessions that don't, such as on a third-party provider or with telemetry off, `auto` with Claude Code v2.1.285 or later and `default` on earlier versions. A session in an organization whose policy withholds the `auto` default starts in `default` instead |

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

87 87 

88In 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.88In 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.


94* In a terminal, once, at the top of the session94* In a terminal, once, at the top of the session

95* In the VS Code extension, as a card on the new-conversation screen that stays until you dismiss it95* In the VS Code extension, as a card on the new-conversation screen that stays until you dismiss it

96 96 

97On 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.97If your `~/.claude/settings.json` sets a `defaultMode` other than `auto` and no other settings file sets one, your sessions keep starting in that mode. On Pro, Max, and Team plans, and in sessions that [don't fetch feature flags](/docs/en/env-vars#features-that-need-feature-flag-fetching), 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.

98 98 

99<h3 id="start-in-a-different-mode">99<h3 id="start-in-a-different-mode">

100 Start in a different permission mode100 Start in a different permission mode


308 Auto mode on Bedrock, Agent Platform, or Foundry308 Auto mode on Bedrock, Agent Platform, or Foundry

309</h3>309</h3>

310 310 

311On [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.311On [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. When nothing else sets a permission mode, it's also the [built-in starting permission mode](#which-mode-a-session-starts-in), on the versions that section's table lists. 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.

312 312 

313Only 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.313Only 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.

314 314 


496 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-approved496 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

497 4. If the classifier blocks, Claude receives the reason. 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)497 4. If the classifier blocks, Claude receives the reason. 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)

498 498 

499 A [mod](/docs/en/plugins/mods/overview) you install that hooks `tool.check` can approve an action before step 3, and the classifier doesn't check an action the mod approves. See [Extend permissions with hooks](/docs/en/permissions#extend-permissions-with-hooks).

500 

499 On entering auto mode, broad allow rules that grant arbitrary code execution are dropped:501 On entering auto mode, broad allow rules that grant arbitrary code execution are dropped:

500 502 

501 * Blanket `Bash(*)` or `PowerShell(*)`503 * Blanket `Bash(*)` or `PowerShell(*)`


526 <Accordion title="Cost and latency">528 <Accordion title="Cost and latency">

527 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.529 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.

528 530 

529 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.531 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.

530 532 

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

532 534 


633* `.yarn`635* `.yarn`

634* `.mvn`636* `.mvn`

635* `.claude`, except for `.claude/worktrees` where Claude stores its own git worktrees637* `.claude`, except for `.claude/worktrees` where Claude stores its own git worktrees

638* A directory you loaded with [`--plugin-dir`](/docs/en/plugins/mods/create#change-a-mod-with-claude), because Claude Code reloads and runs a mod's code from it when a file changes

636 639 

637Protected files:640Protected files:

638 641 

permissions.md +14 −3

Details

547 547 

548[Claude Code hooks](/docs/en/hooks-guide) let you register custom shell commands that evaluate permissions at runtime. When Claude Code makes a tool call, PreToolUse hooks run before the permission prompt, for every tool except [`EndConversation`](/docs/en/tools-reference#endconversation-tool-behavior). The hook output can deny the tool call, force a prompt, or skip the prompt to let the call proceed.548[Claude Code hooks](/docs/en/hooks-guide) let you register custom shell commands that evaluate permissions at runtime. When Claude Code makes a tool call, PreToolUse hooks run before the permission prompt, for every tool except [`EndConversation`](/docs/en/tools-reference#endconversation-tool-behavior). The hook output can deny the tool call, force a prompt, or skip the prompt to let the call proceed.

549 549 

550Hook decisions don't bypass permission rules. Claude Code evaluates deny and ask rules regardless of what a PreToolUse hook returns: a matching deny rule blocks the call, and a matching ask rule still prompts even when the hook returned `"allow"` or `"ask"`. This preserves the deny-first precedence described in [Manage permissions](#manage-permissions), including deny rules set in managed settings.550PreToolUse hook decisions don't bypass permission rules. Claude Code evaluates deny and ask rules regardless of what a PreToolUse hook returns: a matching deny rule blocks the call, and a matching ask rule still prompts even when the hook returned `"allow"` or `"ask"`. This preserves the deny-first precedence described in [Manage permissions](#manage-permissions), including deny rules set in managed settings.

551 

552That precedence covers hooks in settings files and in a plugin's `hooks/hooks.json`. A [mod](/docs/en/plugins/mods/overview) you install that hooks `tool.check` answers after the rules and the `PreToolUse` hooks have decided, and its answer can replace theirs:

553 

554* **Ask rules**: the mod can approve a call that an ask rule would prompt for

555* **A block from a `PreToolUse` hook**: the mod can approve the call, unless the hook is in managed settings

556* **The auto mode classifier**: in [auto mode](/docs/en/permission-modes#eliminate-prompts-with-auto-mode), a call the mod approves runs without a classifier check

557* **Deny rules**: on a machine with managed settings, or when you're signed in with a Team or Enterprise plan, deny rules hold over the mod by default, and your organization can change that. Anywhere else, the mod can approve a call that a deny rule refuses.

558 

559See [Decide whether to trust a mod](/docs/en/plugins/mods/overview#decide-whether-to-trust-a-mod), or [Manage mods for your organization](/docs/en/plugins/mods/admin#know-what-happens-by-default) if you deploy managed settings.

551 560 

552MCP tools marked [`requiresUserInteraction`](/docs/en/mcp#require-approval-for-a-specific-tool) also still prompt when a hook returns `"allow"`, as do connector tools [your organization set to `ask`](/docs/en/mcp#organization-controls-on-connector-tools) in sessions where that setting reaches Claude Code.561MCP tools marked [`requiresUserInteraction`](/docs/en/mcp#require-approval-for-a-specific-tool) also still prompt when a hook returns `"allow"`, as do connector tools [your organization set to `ask`](/docs/en/mcp#organization-controls-on-connector-tools) in sessions where that setting reaches Claude Code.

553 562 


653 662 

654The same holds across settings scopes: if user settings allow a permission and project settings deny it, the deny rule blocks it. The reverse is also true: a user-level deny blocks a project-level allow, because deny rules from any scope are evaluated before allow rules.663The same holds across settings scopes: if user settings allow a permission and project settings deny it, the deny rule blocks it. The reverse is also true: a user-level deny blocks a project-level allow, because deny rules from any scope are evaluated before allow rules.

655 664 

665This precedence is between settings files and command line arguments. For whether a deny rule holds over a [mod](/docs/en/plugins/mods/overview) you install, see [Extend permissions with hooks](#extend-permissions-with-hooks).

666 

656Embedding hosts can supply additional managed policy via the SDK `managedSettings` option, including permission allow rules unless the admin sets the `allowManaged*Only` locks; [Deliver policy to Claude Desktop sessions](/docs/en/claude-apps-gateway#deliver-policy-to-claude-desktop-sessions) covers when embedder policy applies at all.667Embedding hosts can supply additional managed policy via the SDK `managedSettings` option, including permission allow rules unless the admin sets the `allowManaged*Only` locks; [Deliver policy to Claude Desktop sessions](/docs/en/claude-apps-gateway#deliver-policy-to-claude-desktop-sessions) covers when embedder policy applies at all.

657 668 

658## Project allow rules and workspace trust669## Project allow rules and workspace trust


693| [Hooks](/docs/en/hooks) in settings files, the [`env`](/docs/en/settings-reference#env) block and helper commands such as [`apiKeyHelper`](/docs/en/settings-reference#apikeyhelper), and a project skill's [hooks](/docs/en/hooks#hooks-in-skills-and-agents) and [`allowed-tools`](/docs/en/skills#pre-approve-tools-for-a-skill) | Used | Used. Workspace trust never gates a skill's `allowed-tools` in any session |704| [Hooks](/docs/en/hooks) in settings files, the [`env`](/docs/en/settings-reference#env) block and helper commands such as [`apiKeyHelper`](/docs/en/settings-reference#apikeyhelper), and a project skill's [hooks](/docs/en/hooks#hooks-in-skills-and-agents) and [`allowed-tools`](/docs/en/skills#pre-approve-tools-for-a-skill) | Used | Used. Workspace trust never gates a skill's `allowed-tools` in any session |

694| `permissions.allow` rules and `additionalDirectories` in `.claude/settings.json` | Not used until you accept the trust dialog, which appears again listing them | Not used. Claude Code prints a [`this workspace has not been trusted`](/docs/en/errors#workspace-has-not-been-trusted) warning to stderr |705| `permissions.allow` rules and `additionalDirectories` in `.claude/settings.json` | Not used until you accept the trust dialog, which appears again listing them | Not used. Claude Code prints a [`this workspace has not been trusted`](/docs/en/errors#workspace-has-not-been-trusted) warning to stderr |

695| Frontmatter hooks in a project [subagent](/docs/en/sub-agents#hooks-in-subagent-frontmatter), a project [`@skills-dir` plugin](/docs/en/plugins/loading#plugins-shared-through-a-repository), and [`extraKnownMarketplaces`](/docs/en/settings-reference#extraknownmarketplaces) entries from the repository or an `--add-dir` directory | Not used, and no dialog is offered | Not used |706| Frontmatter hooks in a project [subagent](/docs/en/sub-agents#hooks-in-subagent-frontmatter), a project [`@skills-dir` plugin](/docs/en/plugins/loading#plugins-shared-through-a-repository), and [`extraKnownMarketplaces`](/docs/en/settings-reference#extraknownmarketplaces) entries from the repository or an `--add-dir` directory | Not used, and no dialog is offered | Not used |

696| Inline [`mcpServers`](/docs/en/sub-agents#scope-mcp-servers-to-a-subagent) in the frontmatter of a subagent from the repository or an `--add-dir` directory. Before v2.1.238, Claude Code loaded these servers in both situations | Not used, and no dialog is offered | Not used |707| Inline [`mcpServers`](/docs/en/sub-agents#scope-mcp-servers-to-a-subagent) in the frontmatter of a subagent from the repository or an `--add-dir` directory | Not used, and no dialog is offered | Not used |

697| Servers in `.mcp.json`, including ones the repository [approves in its own settings](/docs/en/mcp#project-server-approvals-and-workspace-trust) | Claude Code asks you before connecting them. The repository's own approvals don't count | Connected without asking, approved or not. The SDK loads them only when `settingSources` includes project settings. `claude mcp list` in the same folder still reports such a server as pending |708| Servers in `.mcp.json`, including ones the repository [approves in its own settings](/docs/en/mcp#project-server-approvals-and-workspace-trust) | Claude Code asks you before connecting them. The repository's own approvals don't count | Connected without asking, approved or not. The SDK loads them only when `settingSources` includes project settings. `claude mcp list` in the same folder still reports such a server as pending |

698| A [`headersHelper`](/docs/en/mcp#trust-a-folder-before-its-headershelper-runs) on a server in `.mcp.json`. Before v2.1.238, Claude Code ran the helper in both situations | Not run until you accept the trust dialog, which appears again naming where the helper is declared. Claude Code connects the server with its static `headers` alone until then | Not run. Claude Code connects the server with its static `headers` alone and prints a [`headersHelper not run`](/docs/en/errors#headershelper-not-run) line per server to stderr |709| A [`headersHelper`](/docs/en/mcp#trust-a-folder-before-its-headershelper-runs) on a server in `.mcp.json` | Not run until you accept the trust dialog, which appears again naming where the helper is declared. Claude Code connects the server with its static `headers` alone until then | Not run. Claude Code connects the server with its static `headers` alone and prints a [`headersHelper not run`](/docs/en/errors#headershelper-not-run) line per server to stderr |

699 710 

700For the rows that need this exact folder trusted, trust it by hand: set `projects["<path>"].hasTrustDialogAccepted` to `true` in `~/.claude.json`, where `<path>` is the repository root, or the folder itself outside a repository. Claude Code prints the exact key in the debug log line for a skipped subagent hook or inline MCP server, in the stderr warning for skipped allow rules, and in the `headersHelper not run` line for a skipped helper.711For the rows that need this exact folder trusted, trust it by hand: set `projects["<path>"].hasTrustDialogAccepted` to `true` in `~/.claude.json`, where `<path>` is the repository root, or the folder itself outside a repository. Claude Code prints the exact key in the debug log line for a skipped subagent hook or inline MCP server, in the stderr warning for skipped allow rules, and in the `headersHelper not run` line for a skipped helper.

701 712 

Details

39 39 

40### The demo marketplace in `anthropics/claude-code`40### The demo marketplace in `anthropics/claude-code`

41 41 

42If a tutorial or an older set of instructions tells you to run `/plugin marketplace add anthropics/claude-code`, that adds the demo marketplace, named `claude-code-plugins`. It isn't the official marketplace, which Claude Code already added for you.42If a tutorial or an older set of instructions tells you to run `/plugin marketplace add anthropics/claude-code`, that adds the demo marketplace, named `claude-code-plugins`. It isn't the official marketplace.

43 43 

44Most of the demo marketplace's plugins are also in the official marketplace under the same names. For example, `code-review`, `feature-dev`, `commit-commands`, and `security-guidance` are in both. Install those from `claude-plugins-official` so you don't have two copies installed.44Most of the demo marketplace's plugins are also in the official marketplace under the same names. For example, `code-review`, `feature-dev`, `commit-commands`, and `security-guidance` are in both. Install those from `claude-plugins-official` so you don't have two copies installed.

45 45 

46## Find plugins in the official marketplace46## Find plugins in the official marketplace

47 47 

48The official marketplace, `claude-plugins-official`, is the one Claude Code adds for you. Most of what it lists comes from partners and other authors rather than from Anthropic: tool vendors publish plugins that connect Claude Code to their services, and Anthropic maintains a smaller set of its own, such as `commit-commands`, `code-review`, `feature-dev`, and the [language server plugins](/docs/en/plugins/code-intelligence). The catalog changes often, so this page doesn't list it.48By default, Claude Code adds the official marketplace, `claude-plugins-official`, for you. Most of what it lists comes from partners and other authors rather than from Anthropic: tool vendors publish plugins that connect Claude Code to their services, and Anthropic maintains a smaller set of its own, such as `commit-commands`, `code-review`, `feature-dev`, and the [language server plugins](/docs/en/plugins/code-intelligence). The catalog changes often, so this page doesn't list it.

49 49 

50To see what's in it, use the **Discover** tab of `/plugin` in a Claude Code session, which you can search, or browse [Claude Marketplace](https://claude.com/marketplace/plugins) on the web.50To see what's in it, use the **Discover** tab of `/plugin` in a Claude Code session, which you can search, or browse [Claude Marketplace](https://claude.com/marketplace/plugins) on the web.

51 51 


53 53 

54You can search Anthropic's marketplaces for a plugin in Claude Code, on the web, or on GitHub:54You can search Anthropic's marketplaces for a plugin in Claude Code, on the web, or on GitHub:

55 55 

56* **In Claude Code, by browsing**: run `/plugin` in an interactive session. Its **Discover** tab lists the plugins from the marketplaces you've added.56* **In Claude Code, by browsing**: run `/plugin` in an interactive session. Its **Discover** tab lists the plugins from your marketplaces.

57* **In Claude Code, by name**: run `/plugin install <name>` in a session, which looks the name up in the marketplaces you've added. If the plugin is in one of them, its details open in the `/plugin` panel, and nothing installs until you choose an [installation scope](/docs/en/plugins/install#install-a-plugin) and confirm there. If it isn't, you see `Plugin "<name>" not found in any marketplace`.57* **In Claude Code, by name**: run `/plugin install <name>` in a session, which looks the name up in your marketplaces. If the plugin is in one of them, its details open in the `/plugin` panel, and nothing installs until you choose an [installation scope](/docs/en/plugins/install#install-a-plugin) and confirm there. If it isn't, you see `Plugin "<name>" not found in any marketplace`.

58* **On the web**: search the full catalog on [Claude Marketplace](https://claude.com/marketplace/plugins), which shows install counts and marks some plugins **Anthropic verified**.58* **On the web**: search the full catalog on [Claude Marketplace](https://claude.com/marketplace/plugins), which shows install counts and marks some plugins **Anthropic verified**.

59* **On GitHub**: open `.claude-plugin/marketplace.json` in the marketplace's repository, such as [`anthropics/claude-plugins-official`](https://github.com/anthropics/claude-plugins-official). That file is the catalog itself.59* **On GitHub**: open `.claude-plugin/marketplace.json` in the marketplace's repository, such as [`anthropics/claude-plugins-official`](https://github.com/anthropics/claude-plugins-official). That file is the catalog itself.

60 60 

61Anthropic's directory is separate from these marketplaces. The directory is the catalog on claude.ai, and `/plugin` doesn't list it. A plugin you add from the directory on claude.ai reaches Claude Code through [account sync](/docs/en/plugins/loading#synced-plugins). To list your own plugin there, see [Submit to Anthropic's directory](/docs/en/plugins/publish#submit-to-anthropics-directory).61Anthropic's directory is separate from these marketplaces. The directory is the catalog on claude.ai. A plugin you add from the directory on claude.ai reaches Claude Code through [account sync](/docs/en/plugins/loading#synced-plugins). To list your own plugin there, see [Submit to Anthropic's directory](/docs/en/plugins/publish#submit-to-anthropics-directory).

62 62 

63To install from the desktop app or from a script, or to see what a cloud session loads, see [Install plugins](/docs/en/plugins/install).63To install from the desktop app or from a script, or to see what a cloud session loads, see [Install plugins](/docs/en/plugins/install).

64 64 

Details

27Every subcommand shares these exit codes, plugin arguments, and scope values:27Every subcommand shares these exit codes, plugin arguments, and scope values:

28 28 

29* **Exit codes**: `0` on success and `1` on failure. `validate` adds exit `2` for an unexpected error, and `eval` adds the codes listed in [its section](#plugin-eval).29* **Exit codes**: `0` on success and `1` on failure. `validate` adds exit `2` for an unexpected error, and `eval` adds the codes listed in [its section](#plugin-eval).

30* **Plugin arguments**: a `<plugin>` argument is a plugin `name` or `name@marketplace`. When two marketplaces offer the same name, use the qualified form.30* **Plugin arguments**: a `<plugin>` argument is a plugin `name` or `name@marketplace`. When two marketplaces offer the same name, use the qualified form. `configure` takes only the qualified form.

31* **Scopes**: `--scope` takes `user`, `project`, or `local`, and names the settings file the command writes to. `update` also takes `managed`.31* **Scopes**: `--scope` takes `user`, `project`, or `local`, and names the settings file the command writes to. `update` also takes `managed`.

32 32 

33### plugin init33### plugin init


81| Flag | Description |81| Flag | Description |

82| :- | :- |82| :- | :- |

83| `-s, --scope <scope>` | Installation scope: `user`, `project`, or `local`. Defaults to `user` |83| `-s, --scope <scope>` | Installation scope: `user`, `project`, or `local`. Defaults to `user` |

84| `--config <key=value>` | Set a [`userConfig`](/docs/en/plugins/manifest-reference) option the plugin's manifest declares. Repeat the flag for each option. Requires Claude Code v2.1.147 or later |84| `--config <key=value>` | Set a [`userConfig`](/docs/en/plugins/manifest-reference) option the plugin's manifest declares. Repeat the flag for each option. Requires Claude Code v2.1.147 or later. A key written `<server>.<key>` sets a setting that a [bundled MCP server](/docs/en/plugins/components#include-a-packaged-mcpb-server) declares in its own `user_config` instead, for a bundle file shipped inside the plugin. The `<server>.<key>` form requires Claude Code v2.1.285 or later |

85| `-y, --yes` | Accept the displayed install command without the `Run this command now?` prompt. Ignored when the command runs inside a Claude Code session, such as from the Bash tool or a hook. Requires Claude Code v2.1.229 or later |85| `-y, --yes` | Accept the displayed install command without the `Run this command now?` prompt. Ignored when the command runs inside a Claude Code session, such as from the Bash tool or a hook. Requires Claude Code v2.1.229 or later |

86| `--accept-command <sha256>` | Accept the displayed install command whose `sha256` a previous [`--json` run](#plugin-json-result) reported in `shownCommand`, in place of `-y`. Can't be combined with `-y`. See [Accept a displayed install command](#accept-a-displayed-install-command). Requires Claude Code v2.1.271 or later |86| `--accept-command <sha256>` | Accept the displayed install command whose `sha256` a previous [`--json` run](#plugin-json-result) reported in `shownCommand`, in place of `-y`. Can't be combined with `-y`. See [Accept a displayed install command](#accept-a-displayed-install-command). Requires Claude Code v2.1.271 or later |

87| `--json` | Print the result as one JSON object on the last line of stdout instead of the human-readable message, for use in scripts. See [JSON result format](#plugin-json-result). Requires Claude Code v2.1.268 or later |87| `--json` | Print the result as one JSON object on the last line of stdout instead of the human-readable message, for use in scripts. See [JSON result format](#plugin-json-result). Requires Claude Code v2.1.268 or later |


279| :- | :- |279| :- | :- |

280| `--json` | Print the list as JSON |280| `--json` | Print the list as JSON |

281| `--available` | Also list plugins your marketplaces offer that you haven't installed. Has no effect without `--json` |281| `--available` | Also list plugins your marketplaces offer that you haven't installed. Has no effect without `--json` |

282| `--data-size [plugin]` | Measure each installed plugin's [saved data directory](#what-an-uninstall-deletes-and-keeps), or only the named plugin's, given as `name@marketplace`. Has no effect without `--json`. If the name has no install record, the command prints `--data-size names a plugin that is not installed` and exits `1` instead of printing the list. Requires Claude Code v2.1.285 or later |

282 283 

283Claude Code groups the human-readable output by how each plugin loads:284Claude Code groups the human-readable output by how each plugin loads:

284 285 


308| `notes` | array of strings | Authoring warnings for a plugin that loaded and works |309| `notes` | array of strings | Authoring warnings for a plugin that loaded and works |

309| `errorDetails` | array of objects | One object per `errors` entry, giving its diagnostic `type` and the names it refers to, such as the plugin, marketplace, server, or file. Requires Claude Code v2.1.268 or later |310| `errorDetails` | array of objects | One object per `errors` entry, giving its diagnostic `type` and the names it refers to, such as the plugin, marketplace, server, or file. Requires Claude Code v2.1.268 or later |

310| `noteDetails` | array of objects | The same detail objects for each `notes` entry. Requires Claude Code v2.1.268 or later |311| `noteDetails` | array of objects | The same detail objects for each `notes` entry. Requires Claude Code v2.1.268 or later |

312| `hasUserConfig` | boolean | Present and `true` when the plugin loaded and its manifest declares [`userConfig` options](/docs/en/plugins/manifest-reference#user-configuration). Absent for a plugin that failed to load, whatever its manifest declares. Saved values are never included. Requires Claude Code v2.1.285 or later |

313| `projectEnabled` | boolean | Whether the project's shared `.claude/settings.json` turns the plugin on. Marketplace installs only. Requires Claude Code v2.1.285 or later |

314| `dataDirSize` | object | With `--data-size`, the size of the plugin's [saved data directory](#what-an-uninstall-deletes-and-keeps) as `bytes` and `human`; absent when the directory is missing or empty. Marketplace installs only. Requires Claude Code v2.1.285 or later |

315| `dataDirUnreadable` | boolean | With `--data-size`, `true` when the saved data directory exists but couldn't be measured. Marketplace installs only. Requires Claude Code v2.1.285 or later |

311 316 

312With `--json --available`, Claude Code prints one object instead of an array. Its `installed` field holds the array of installed-plugin objects, and its `available` field holds one object per uninstalled marketplace plugin with the fields below.317With `--json --available`, Claude Code prints one object instead of an array. Its `installed` field holds the array of installed-plugin objects, and its `available` field holds one object per uninstalled marketplace plugin with the fields below.

313 318 


349 354 

350For a plugin that isn't loaded, Claude Code prints ``Plugin "formatter" not found. Run `claude plugin list` to see installed plugins, or pass --plugin-dir <path> to load one from disk.`` and exits `1`.355For a plugin that isn't loaded, Claude Code prints ``Plugin "formatter" not found. Run `claude plugin list` to see installed plugins, or pass --plugin-dir <path> to load one from disk.`` and exits `1`.

351 356 

357### plugin configure

358 

359Show an installed plugin's [`userConfig`](/docs/en/plugins/manifest-reference#user-configuration) options and which are set, or save values piped in on stdin. Requires Claude Code v2.1.285 or later.

360 

361```bash theme={null}

362claude plugin configure <plugin>

363```

364 

365| Flag | Description |

366| :- | :- |

367| `--values-stdin` | Read option values from stdin as a JSON object of single-line strings and save them. Options you leave out keep their saved values |

368| `--json` | Print the result as one JSON object on stdout. Without `--values-stdin`, the object carries the options' `schema` and `choices`, their starting `inputs`, and the `configured` and `unconfigured` option names. With `--values-stdin`, it carries the `saved` option names and, when they could be read back, the `unconfigured` ones |

369 

370Without flags, the command lists each option with up to three labels: `required` or `optional`, then `sensitive` for an option the manifest declares sensitive, then `set` or `not set`. It prints no saved values. With `--json`, the output includes the saved values of options that aren't sensitive, and never the text of a sensitive one.

371 

372To save values, write them to a file as a JSON object that maps option keys to string values, then pass the file on stdin. Replace `formatter@my-marketplace` with your own plugin's id as `claude plugin list` shows it. This example sets one option named `api_url` from a file `values.json` that contains `{"api_url": "https://example.com"}`:

373 

374```bash theme={null}

375claude plugin configure formatter@my-marketplace --values-stdin < values.json

376```

377 

378Claude Code validates each value against the option's declared type and prints `Configuration saved. Restart Claude Code to apply it.` If you pass a key the manifest doesn't declare, or a value that fails validation, the command saves nothing, prints `Failed to save configuration:` with the reason, and exits `1`. With `--json`, a refused value also prints an object on stdout whose `refused` field carries the `message` and, when one option is at fault, its `option` key.

379 

380Pass the plugin's full `name@marketplace` id, as `claude plugin list` shows it. `configure` doesn't accept a bare `name`. When no loaded plugin has that id, the command prints `No installed plugin has the id "<plugin>".` and exits `1`.

381 

382For the settings of a bundled MCP server, see [`plugin install --config`](#plugin-install) or the **Configure** item in `/plugin`.

383 

352### plugin prune384### plugin prune

353 385 

354Remove auto-installed [dependencies](/docs/en/plugins/dependencies) that no installed plugin needs anymore. The command never removes a plugin you installed yourself. `autoremove` is an alias for `prune`.386Remove auto-installed [dependencies](/docs/en/plugins/dependencies) that no installed plugin needs anymore. The command never removes a plugin you installed yourself. `autoremove` is an alias for `prune`.

Details

773 773 

774Hooks in `hooks/hooks.json` and in the `hooks` manifest key both load. For every event and its payload, see [Hook events](/docs/en/hooks#hook-events).774Hooks in `hooks/hooks.json` and in the `hooks` manifest key both load. For every event and its payload, see [Hook events](/docs/en/hooks#hook-events).

775 775 

776To write hooks as JavaScript functions that run inside Claude Code and can draw in its interface, list a module file under a `modules` key in the same `hooks/hooks.json`. A plugin with one is a mod. See [Create a mod](/docs/en/plugins/mods/create).

777 

776#### When plugin hooks fire778#### When plugin hooks fire

777 779 

778A plugin's hooks don't wait for one of the plugin's skills or commands to be used. Claude Code registers them when a session loads the plugin, and they fire on their events from then on. To limit when a hook runs, narrow its `matcher`.780A plugin's hooks don't wait for one of the plugin's skills or commands to be used. Claude Code registers them when a session loads the plugin, and they fire on their events from then on. To limit when a hook runs, narrow its `matcher`.


838 840 

839The server takes its name from the `name` in the bundle's manifest.841The server takes its name from the `name` in the bundle's manifest.

840 842 

843A bundle's own manifest can declare settings the server needs from the user in a `user_config` block. A bundled server with a required setting that has no saved value doesn't start. The `/plugin` **Errors** tab shows `Bundled MCP server "<name>" was not started: it needs configuration`.

844 

845Users supply the values in one of two ways:

846 

847* **In `/plugin`**: select the plugin on the **Installed** tab and choose **Configure**

848* **At install, from the shell**: pass [`--config <server>.<key>=<value>`](/docs/en/plugins/cli-reference#plugin-install) to `claude plugin install`. Requires Claude Code v2.1.285 or later, and works only for a bundle packaged inside the plugin.

849 

841For transports and authentication, see [MCP](/docs/en/mcp#plugin-provided-mcp-servers).850For transports and authentication, see [MCP](/docs/en/mcp#plugin-provided-mcp-servers).

842 851 

843### LSP servers852### LSP servers


1027 1036 

1028### When the configuration dialog appears1037### When the configuration dialog appears

1029 1038 

1030The dialog appears only in the interactive `/plugin` interface. It opens for any option that isn't set yet when the user does any of the following:1039The dialog is part of the interactive `/plugin` interface. When the user does any of the following, it opens for any option that isn't set yet:

1031 1040 

1032* Installs the plugin in `/plugin`1041* Installs the plugin in `/plugin`

1033* Runs `/plugin install <plugin>@<marketplace>` inside a session1042* Runs `/plugin install <plugin>@<marketplace>` inside a session


1035 1044 

1036To open the same dialog at any time, the user runs `/plugin configure <plugin>@<marketplace>`.1045To open the same dialog at any time, the user runs `/plugin configure <plugin>@<marketplace>`.

1037 1046 

1038The `claude plugin install` shell command never prompts for `userConfig` values. To set values from the shell, pass each one as `--config KEY=VALUE`. When options remain unset, the command prints a `userConfig options not yet set` line that names both ways to set them. [The `userConfig` dialog never appears](/docs/en/plugins/troubleshooting#the-userconfig-dialog-never-appears) quotes the line.1047The VS Code extension's [Manage plugins dialog](/docs/en/vs-code#install-plugins) asks for unset options as a form after an install, and a gear icon on the plugin's row opens the form again with every option.

1048 

1049The `claude plugin install` shell command never prompts for `userConfig` values. To set values from the shell, pass each one as `--config KEY=VALUE` when you install, or pipe a JSON object to [`claude plugin configure --values-stdin`](/docs/en/plugins/cli-reference#plugin-configure) afterward.

1050 

1051When options remain unset, `claude plugin install` prints a `userConfig options not yet set` line. For the line's exact text, see [The `userConfig` dialog never appears](/docs/en/plugins/troubleshooting#the-userconfig-dialog-never-appears).

1039 1052 

1040For the option fields, where each value is stored, how a component references a saved value, and which fields reject `${user_config.*}`, see [User configuration](/docs/en/plugins/manifest-reference#user-configuration).1053For the option fields, where each value is stored, how a component references a saved value, and which fields reject `${user_config.*}`, see [User configuration](/docs/en/plugins/manifest-reference#user-configuration).

1041 1054 

Details

39 /plugin install commit-commands@claude-plugins-official39 /plugin install commit-commands@claude-plugins-official

40 ```40 ```

41 41 

42 To browse instead, run `/plugin` with no plugin name: the panel opens on the **Discover** tab, which lists plugins from every marketplace you've added, and you can type to search, then press **Enter** on a plugin to open its details.42 To browse instead, run `/plugin` with no plugin name: the panel opens on the **Discover** tab, which lists the plugins from your marketplaces, and you can type to search, then press **Enter** on a plugin to open its details.

43 </Step>43 </Step>

44 44 

45 <Step title="Review what the plugin adds">45 <Step title="Review what the plugin adds">


70 The last sentence of the summary tells you whether the plugin is usable in this session yet:70 The last sentence of the summary tells you whether the plugin is usable in this session yet:

71 71 

72 * **Active now**: `Plugin is now active.` No reload is needed.72 * **Active now**: `Plugin is now active.` No reload is needed.

73 * **Active, but a server needs setup**: `Plugin is now active.` is followed by `Its bundled MCP server needs configuration before it can start`. The plugin's [bundled MCP server](/docs/en/plugins/components#include-a-packaged-mcpb-server) can't start until you set its options. Select the plugin on the **Installed** tab in `/plugin` and choose **Configure** to set the server's options.

73 * **Reload needed**: `Run /reload-plugins to activate.` The panel closes and Claude Code runs that reload for you. If the reload would [invalidate the prompt cache](/docs/en/prompt-caching#enabling-or-disabling-a-plugin), it warns and leaves the plugin pending instead. Run `/reload-plugins --force` to activate it anyway, which costs one uncached request.74 * **Reload needed**: `Run /reload-plugins to activate.` The panel closes and Claude Code runs that reload for you. If the reload would [invalidate the prompt cache](/docs/en/prompt-caching#enabling-or-disabling-a-plugin), it warns and leaves the plugin pending instead. Run `/reload-plugins --force` to activate it anyway, which costs one uncached request.

74 * **Load failed**: `The plugin couldn't be loaded`. Open the **Errors** tab in `/plugin` for the reason, then see [After install: plugin not working](/docs/en/plugins/troubleshooting#plugin-installed-but-not-working).75 * **Load failed**: `The plugin couldn't be loaded`. Open the **Errors** tab in `/plugin` for the reason, then see [After install: plugin not working](/docs/en/plugins/troubleshooting#plugin-installed-but-not-working).

75 </Step>76 </Step>


236A private marketplace is one in a repository you need credentials to clone, on GitHub or any other git host. You add it with the same `/plugin marketplace add` or `claude plugin marketplace add` command as a public one. Claude Code clones it with the git credentials already on your machine and never prompts, so each way of connecting has a requirement:237A private marketplace is one in a repository you need credentials to clone, on GitHub or any other git host. You add it with the same `/plugin marketplace add` or `claude plugin marketplace add` command as a public one. Claude Code clones it with the git credentials already on your machine and never prompts, so each way of connecting has a requirement:

237 238 

238* **HTTPS**: your git credential helpers apply, so access you set up with `gh auth login`, the macOS Keychain, or `git-credential-store` works. Interactive prompts are suppressed, so a host you have never authenticated to fails instead of asking for a password.239* **HTTPS**: your git credential helpers apply, so access you set up with `gh auth login`, the macOS Keychain, or `git-credential-store` works. Interactive prompts are suppressed, so a host you have never authenticated to fails instead of asking for a password.

239* **SSH**: the host must already be in your `known_hosts` file and the key must work without a passphrase prompt, because the host-fingerprint and passphrase prompts are suppressed too.240* **SSH**: the host must already be in your `known_hosts` file and the key must work without a passphrase prompt. If your git setup names an SSH program in `GIT_SSH_COMMAND`, `GIT_SSH`, or your git config's `core.sshCommand`, Claude Code runs that program.

240* **GitHub `owner/repo` shorthand**: Claude Code checks whether your SSH key authenticates to `github.com`, then clones over SSH if it does and over HTTPS if it doesn't. Set [`CLAUDE_CODE_PLUGIN_PREFER_HTTPS=1`](/docs/en/env-vars#variables) to skip that check and always clone over HTTPS.241* **GitHub `owner/repo` shorthand**: Claude Code checks whether your SSH key authenticates to `github.com`, then clones over SSH if it does and over HTTPS if it doesn't. Set [`CLAUDE_CODE_PLUGIN_PREFER_HTTPS=1`](/docs/en/env-vars#variables) to skip that check and always clone over HTTPS.

241 242 

242The same credentials apply when you run `/plugin install`, `/plugin marketplace update`, and `claude plugin update`.243The same credentials apply when you run `/plugin install`, `/plugin marketplace update`, and `claude plugin update`.


274 275 

275* Type to filter by name or description.276* Type to filter by name or description.

276* Press **Space** to enable or disable the selected plugin, and **f** to favorite it.277* Press **Space** to enable or disable the selected plugin, and **f** to favorite it.

277* Press **Enter** to open a plugin's details. The menu there offers **Disable plugin** or **Enable plugin**, **Update now**, and **Uninstall**. Plugins that take settings also offer **Configure options**.278* Press **Enter** to open a plugin's details.

279 

280A plugin's details menu offers **Disable plugin** or **Enable plugin**, **Update now**, and **Uninstall**. Two more items appear for plugins that take settings, and a plugin can show both:

281 

282* **Configure options**: shown when the plugin's manifest declares [`userConfig` options](/docs/en/plugins/manifest-reference#user-configuration). Opens the dialog for those options

283* **Configure**: shown when the plugin includes a [bundled MCP server](/docs/en/plugins/components#include-a-packaged-mcpb-server). Sets that server's own `user_config` settings

278 284 

279The tab can also show plugins at **Managed** scope. Your organization installed those through [managed settings](/docs/en/settings#settings-files), and you can't enable, disable, or uninstall them here.285The tab can also show plugins at **Managed** scope. Your organization installed those through [managed settings](/docs/en/settings#settings-files), and you can't enable, disable, or uninstall them here.

280 286 

Details

138| [`dependencies`](#dependencies) | Array of strings or objects | Plugins that must be enabled for this one to work |138| [`dependencies`](#dependencies) | Array of strings or objects | Plugins that must be enabled for this one to work |

139| [`settings`](#settings) | Object | Settings Claude Code applies while the plugin is enabled. Only `agent` and `subagentStatusLine` take effect |139| [`settings`](#settings) | Object | Settings Claude Code applies while the plugin is enabled. Only `agent` and `subagentStatusLine` take effect |

140| [`userConfig`](#user-configuration) | Object | Values Claude Code prompts the user for when the plugin is enabled |140| [`userConfig`](#user-configuration) | Object | Values Claude Code prompts the user for when the plugin is enabled |

141| `types` | Path | A `.d.ts` file that declares the `$.state` values and `$` nouns of a [mod](/docs/en/plugins/mods/reference#files) |

141| [`channels`](#channels) | Array of objects | Message channels the plugin provides, each bound to one of its MCP servers |142| [`channels`](#channels) | Array of objects | Message channels the plugin provides, each bound to one of its MCP servers |

142| `skills` | Path, or array of paths | Directories to scan for skills, each a directory of `<name>/SKILL.md` folders or one folder holding `SKILL.md` directly. `"."` names the plugin root. Adds to the default `skills/` scan |143| `skills` | Path, or array of paths | Directories to scan for skills, each a directory of `<name>/SKILL.md` folders or one folder holding `SKILL.md` directly. `"."` names the plugin root. Adds to the default `skills/` scan |

143| [`commands`](#commands) | Path, array of paths, or object | Flat `.md` command files, directories of them, or an object map of command name to `source` or `content`. Replaces the default `commands/` scan |144| [`commands`](#commands) | Path, array of paths, or object | Flat `.md` command files, directories of them, or an object map of command name to `source` or `content`. Replaces the default `commands/` scan |


160 161 

161Claude Code namespaces every component under it, so an agent `reviewer` in plugin `deploy-tools` appears as `deploy-tools:reviewer`.162Claude Code namespaces every component under it, so an agent `reviewer` in plugin `deploy-tools` appears as `deploy-tools:reviewer`.

162 163 

164`claude plugin validate` also checks that the name doesn't pass as one of Anthropic's own plugins. The check ignores case and treats any run of separators as one:

165 

166| Name | Result |

167| :- | :- |

168| Starts with `claude-`, `anthropic-`, `anthropics-`, or `cc-plugin-` | Error |

169| Is `claude`, `anthropic`, `anthropics`, `claude-code`, or `claude-mods` | Error |

170| Puts `official` beside `claude` or `anthropic`, such as `official-claude-tools` | Error |

171| Has `claude`, `anthropic`, or `anthropics` as a whole word anywhere else, such as `mcp-for-claude` | Warning |

172 

173The error reads `Plugin name "<name>" is reserved: it passes as one of Anthropic's own`, and the warning reads `Plugin name "<name>" reads as one of Anthropic's own`. `claude plugin init` and `claude plugin tag` refuse a name that draws the error. Only these commands check the name. Claude Code still installs and loads a plugin whose name they refuse.

174 

163### `displayName`175### `displayName`

164 176 

165The name shown in UI in place of `name`. It may contain spaces and any casing, and it isn't used for namespacing or lookup.177The name shown in UI in place of `name`. It may contain spaces and any casing, and it isn't used for namespacing or lookup.

Details

436| `Author name cannot be empty` | Error | `owner.name` |436| `Author name cannot be empty` | Error | `owner.name` |

437| `Plugin name cannot contain spaces. Use kebab-case (e.g., "my-plugin")` | Error | `plugins[i].name` |437| `Plugin name cannot contain spaces. Use kebab-case (e.g., "my-plugin")` | Error | `plugins[i].name` |

438| `Plugin name cannot contain control or bidirectional-formatting characters` | Error | `plugins[i].name` |438| `Plugin name cannot contain control or bidirectional-formatting characters` | Error | `plugins[i].name` |

439| `Plugin name "x" is reserved: it passes as one of Anthropic's own` | Error | `plugins[i].name`. See the manifest's [`name`](/docs/en/plugins/manifest-reference#name) for the reserved names |

440| `Plugin name "x" reads as one of Anthropic's own` | Warning | `plugins[i].name` |

439| `Claude Code cannot install plugins from marketplace "x". Each part of a plugin id (plugin@marketplace) may use only the letters a-z and A-Z, digits, ".", "_" and "-", and must start with a letter or digit. Change the marketplace's "name".` | Error | `name` |441| `Claude Code cannot install plugins from marketplace "x". Each part of a plugin id (plugin@marketplace) may use only the letters a-z and A-Z, digits, ".", "_" and "-", and must start with a letter or digit. Change the marketplace's "name".` | Error | `name` |

440| `Claude Code cannot install plugin "x". Each part of a plugin id (plugin@marketplace) may use only the letters a-z and A-Z, digits, ".", "_" and "-", and must start with a letter or digit. Change this entry's "name".` | Error | `plugins[i].name` |442| `Claude Code cannot install plugin "x". Each part of a plugin id (plugin@marketplace) may use only the letters a-z and A-Z, digits, ".", "_" and "-", and must start with a letter or digit. Change this entry's "name".` | Error | `plugins[i].name` |

441| `Duplicate plugin name "x" found in marketplace` | Error | Two entries share a `name` |443| `Duplicate plugin name "x" found in marketplace` | Error | Two entries share a `name` |

plugins/mods/admin.md +353 −0 created

Details

1> ## Documentation Index

2> Fetch the complete documentation index at: https://code.claude.com/docs/llms.txt

3> Use this file to discover all available pages before exploring further.

4 

5# Manage mods for your organization

6 

7> Control Claude Code mods with managed settings: stop user-installed mods, allow only your own, review what a mod can do, and enforce policy with your own mod.

8 

9A [mod](/docs/en/plugins/mods/overview) is a plugin that runs code inside Claude Code with the permissions of the user who installed it. Mods aren't sandboxed. Through [managed settings](/docs/en/managed-settings), you decide whether mods run on your users' machines, which ones, and in what order. You can also install a mod of your own that watches or refuses what other mods do.

10 

11This page is for the person who deploys managed settings for Claude Code, whether as a file, through MDM, or from the claude.ai admin console. Mods are on by default in Claude Code v2.1.287 and later. Start with the section that matches what you came to do:

12 

13* **Keep users' own mods out, with or without mods of your own**: [Stop user-installed mods from loading](#stop-user-installed-mods-from-loading)

14* **See what your users get when you change nothing**: [Know what happens by default](#know-what-happens-by-default)

15* **Leave mods on with other limits**: [Choose how much to allow](#choose-how-much-to-allow)

16 

17<Note>

18 These cases are covered on other pages:

19 

20 * **You haven't deployed managed settings before**: start with [Deploy managed settings](/docs/en/managed-settings)

21 * **You want to control which plugins users can install**: see [Manage plugins for your organization](/docs/en/plugins/org)

22</Note>

23 

24## Stop user-installed mods from loading

25 

26To keep every mod your users bring from loading, set the `allowManagedModsOnly` option on the [built-in guard](#know-what-happens-by-default), a policy mod that Claude Code loads ahead of every mod a user installs. The option goes in managed settings under `pluginConfigs`, keyed by `cc-plugin-sec-default@builtin`:

27 

28```json managed-settings.json theme={null}

29{

30 "pluginConfigs": {

31 "cc-plugin-sec-default@builtin": {

32 "options": {

33 "allowManagedModsOnly": true

34 }

35 }

36 }

37}

38```

39 

40With the option set in managed settings:

41 

42* **No mod a user brings loads**: that covers a mod in a plugin the user installed, a mod loaded with `--plugin-dir`, and a mod [Claude wrote during a session](/docs/en/plugins/mods/create#ask-claude-for-a-mod)

43* **Your organization's mods still load**: a mod that [counts as your organization's](#install-your-organizations-mods) isn't checked. Every other mod counts as a user's and doesn't load. That includes a mod in a plugin you enable from a GitHub or other remote marketplace, and one your organization turns on for its members on claude.ai. If none counts as yours, no installed mod loads.

44* **Users can't undo it**: the guard reads the option from managed settings only, so the same entry in a user, project, or local settings file, or in a file passed with `--settings`, changes nothing

45* **A file or MDM policy covers every provider**: when you deliver the option as a file or through MDM, it works the same way on Amazon Bedrock, Google Cloud's Agent Platform, and Microsoft Foundry. For delivery from the claude.ai admin console, see [Platform availability](/docs/en/server-managed-settings#platform-availability)

46* **Users' other customizations keep working**: their [hooks in settings files](/docs/en/hooks), status lines, and `/goal` aren't affected

47* **Built-in mods keep running**: mods built into Claude Code, such as `AGENTS.md` support, each have [their own switch](/docs/en/plugins/mods/overview#mods-built-into-claude-code)

48 

49To confirm the option on a user's machine, start Claude Code there with `--plugin-dir` and the path of a directory that holds a mod, such as `claude --plugin-dir ./first-mod`. The mod's hooks don't run, and the transcript and the debug log have the [guard's message](/docs/en/plugins/mods/troubleshoot#messages-from-the-built-in-guard), which names the mod and `allowManagedModsOnly`. If the mod loads, see [Check that a policy is in force](/docs/en/managed-settings#check-that-a-policy-is-in-force) and the [rules that decide whether an option takes effect](#set-options-on-the-built-in-guard).

50 

51If you set `CLAUDE_CODE_ENABLE_FUNCTION_HOOKS` to `0` during early access, replace it with this option. Claude Code v2.1.287 and later ignores the variable at any value, so a `0` there leaves mods on.

52 

53## Know what happens by default

54 

55With no mod settings of your own, this is what your users get:

56 

57* **Mods are on.** A user can install a plugin that contains a mod from any marketplace your plugin settings allow, or load one from a directory with `--plugin-dir`.

58* **A built-in guard runs first.** Claude Code loads a built-in mod named `sec-default@builtin` ahead of every mod a user installs. Users can't turn it off. `/plugin` and the debug log list it as `cc-plugin-sec-default`. The guard loads when either of these is true:

59 

60 * The machine has managed settings

61 * The user is signed in to Claude Code with a Team or Enterprise plan

62 

63 A user who authenticates with an API key, or through Amazon Bedrock, Google Cloud's Agent Platform, or Microsoft Foundry, gets the guard only on a machine that has managed settings.

64* **The guard protects what you manage.** A user's mod can't change what your managed hooks receive or decide, the system prompt, your managed `CLAUDE.md` and other managed instructions, what any mod reads as settings, or the tools and descriptions of your managed MCP servers.

65* **Everything else is allowed.** The guard adds no other restrictions. A user's mod can still read and write files, start processes, make network requests, rewrite tool calls and prompts, deny a tool call, approve one that would otherwise prompt, and draw in the interface, all with that user's permissions.

66* **Deny rules and your managed hooks take precedence.** Where the guard loads, a user's mod can't approve a call that a `deny` rule refuses, whichever settings file holds the rule. A block from a `PreToolUse` hook in managed settings is final too. Both apply to Claude's tool calls. Neither applies to a mod's own [`$.fs` and `$.process` calls](/docs/en/plugins/mods/api#reach-files-processes-and-the-network): with `Read(.env)` denied, a mod can still read that file with `$.fs.read` or start a program that does. To limit those calls, keep the mod from loading or hook the call in a [policy mod](#enforce-a-policy-with-a-mod-of-your-own).

67* **Other permission checks can be overridden.** A user's mod that approves tool calls can approve a call that an `ask` rule would prompt for, or that a `PreToolUse` hook outside managed settings blocked. In auto mode, a call the mod approves runs without a classifier check.

68 

69The guard's source is public in the [`mods/sec-default` directory of the Claude Code repository](https://github.com/anthropics/claude-code/tree/main/mods/sec-default).

70 

71### Know which controls still apply

72 

73Mods don't replace the controls you already have:

74 

75* **Settings hooks keep working.** Command, HTTP, prompt, and agent hooks in settings files and in plugins' `hooks/hooks.json` run as before, alongside mods. Nothing about them is deprecated.

76* **Deny rules take precedence where the guard loads.** A user's mod can't approve a call that a `deny` rule refuses, unless you set [`allowModsToOverrideDenyRules`](#set-options-on-the-built-in-guard).

77* **Managed hooks run first.** A `PreToolUse` hook in managed settings runs before any mod sees the tool call, and its block is final. If a mod then rewrites the call, your managed hooks run again on the rewritten call, so a block still applies. `PreToolUse` hooks from other settings files and from plugins run after the last mod, so a mod that returns its own result in place of running the tool keeps those from running. See [The order mods run in](/docs/en/plugins/mods/events#the-order-mods-run-in).

78* **Network policy covers `$.http.fetch`.** If your organization turns off web fetching, or nonessential network traffic is turned off for the session, Claude Code refuses a network request that a mod makes with `$.http.fetch`. The policy doesn't cover a program the mod starts with `$.process.run`. That program reaches the network with the user's own access.

79* **Plugin controls cover mods.** A mod is a plugin, so the [settings that restrict what users can install](/docs/en/plugins/org#restrict-what-users-can-install), such as `strictKnownMarketplaces`, decide whether it can be installed at all.

80* **Mods can't change the permission prompt.** A mod can restyle much of Claude Code's interface, but not the permission prompt, so it can't change what a prompt shows. A mod can still approve or deny a tool call before the prompt appears, as [Know what happens by default](#know-what-happens-by-default) describes.

81* **Trust prompts come first.** In an interactive session in a directory the user hasn't trusted yet, no mod loads until they answer the trust prompt.

82* **`--safe-mode` turns installed mods off, yours included.** Start a session with `claude --safe-mode` to check whether a mod caused a problem.

83 

84None of these controls sandboxes a mod. A mod you allow runs as the user, with the user's access to files, processes, and the network.

85 

86## Decide whether to leave mods on

87 

88A mod can do more than the other parts of a plugin because it runs inside Claude Code. It sees every prompt and tool call, can change them, and can allow or deny a tool call before a permission prompt appears.

89 

90What a user can load as a mod depends on the plugin controls you already have:

91 

92| Your plugin controls today | What a user can load as a mod |

93| :- | :- |

94| None | A mod from any marketplace, from any directory with `--plugin-dir`, or that Claude writes during a session |

95| A marketplace allowlist | A mod from the marketplaces you allow, or from any directory with `--plugin-dir`. A mod Claude writes during a session loads only when the allowlist [includes `skills-dir`](/docs/en/plugins/org#keep-skills-directory-plugins-loading). |

96| A marketplace allowlist and `disableSideloadFlags` | A mod from the marketplaces you allow |

97 

98[Manage plugins for your organization](/docs/en/plugins/org) lists every way a plugin loads and the setting that controls each.

99 

100To check the mods in a marketplace before your users install them, see [Review what a mod can do](#review-what-a-mod-can-do). To keep users' mods out until you've done that, see [Stop user-installed mods from loading](#stop-user-installed-mods-from-loading).

101 

102### Review what a mod can do

103 

104You can see what a mod is able to do without running it. In your shell, run `claude plugin validate` on the plugin's directory:

105 

106```bash theme={null}

107claude plugin validate ./some-mod

108```

109 

110Two lines in the output describe the mod's code:

111 

112```text theme={null}

113 ❯ ./register.js hooks: session.start, tool.call, ui.render{component=Pane}

114 ❯ ./register.js calls: $.fs.read, $.http.fetch, $.store.set, $.ui.open

115```

116 

117The `hooks:` line lists the events the mod receives. The `calls:` line lists the mods API methods its code calls. The [mods API](/docs/en/plugins/mods/api), written `$` in a mod's code, is how a mod reaches files, processes, and the network. Claude Code refuses to load a mod that uses the mods API in a way this command can't read.

118 

119Look at the `calls:` line for these:

120 

121| Call | What it means |

122| :- | :- |

123| `$.fs.read`, `$.fs.write` | Reads or writes files anywhere the user can |

124| `$.process.run`, `$.process.spawn` | Starts programs as the user |

125| `$.http.fetch` | Makes network requests |

126| `$.env.get`, `$.settings.read` | Reads environment variables and settings, which can hold API keys. An `env reads:` line in the output names each variable. |

127| `$.env.set` | Sets an environment variable for Claude Code and for every command and MCP server it starts afterward, which can change what those programs run. An `env writes:` line names each variable. |

128| `$.mcp.call` | Calls a tool on a connected MCP server, under the session's permission rules |

129| `$.model.complete` | Uses the user's plan or API key for model calls |

130| `$.prompt.submit` | Submits a prompt, and can send it as the user's own words |

131| `$.session.send` | Sends a message that another session's or subagent's Claude reads |

132 

133In the `hooks:` line, [`tool.call`](/docs/en/plugins/mods/reference#tools) and [`prompt.submit`](/docs/en/plugins/mods/reference#prompts-and-what-claude-reads) mean the mod sees every tool call and every prompt, and can change them. [`session.append`](/docs/en/plugins/mods/reference#session) means the mod can rewrite each row of the conversation before it's stored. [`ui.render{component=AskUserQuestion}`](/docs/en/plugins/mods/interface#change-what-claude-code-already-draws) means the mod can redraw the dialog Claude uses to ask the user a question. `tool.check` means the mod can approve or deny a tool call before a permission prompt appears. [Know what happens by default](#know-what-happens-by-default) lists which of your rules and hooks take precedence over its answer.

134 

135## Choose how much to allow

136 

137Mod policies range from no installed mods at all to any mod a user chooses, with your own mod checking the others, and each one is a few managed settings. Find the policy you want in the first column and set what the second column names. [Deploy managed settings](/docs/en/managed-settings) covers where managed settings live.

138 

139| What you want | Settings |

140| :- | :- |

141| No installed mods, with hooks untouched | Set [`allowManagedModsOnly`](#set-options-on-the-built-in-guard) and deploy no mods of your own |

142| No installed mods and no hooks at all, your managed hooks included | Set `disableAllHooks` to `true` |

143| Only your organization's mods | Set the guard's [`allowManagedModsOnly` option](#stop-user-installed-mods-from-loading), and [install your mods](#install-your-organizations-mods) so that they count as yours |

144| Any mod from marketplaces you approve | Keep your [marketplace restrictions](/docs/en/plugins/org#restrict-what-users-can-install), and set `disableSideloadFlags` to `true` |

145| Any mod, with your own mod checking the others | [Install your mod](#install-your-organizations-mods), and list it with `sec-default@builtin` in `prependPlugins` |

146 

147What each setting does:

148 

149* **`allowManagedModsOnly`**: an option on the built-in guard. Users' own mods don't load, and their settings hooks, status lines, and `/goal` keep working. [Stop user-installed mods from loading](#stop-user-installed-mods-from-loading) lists what it covers.

150* **`allowManagedHooksOnly`**: a wider setting. Only [your organization's mods](#install-your-organizations-mods) and the mods built into Claude Code load. A mod a user installed themselves doesn't. The setting also blocks hooks in users' own settings files. Read [What runs under `allowManagedHooksOnly`](/docs/en/settings-reference#what-runs-under-allowmanagedhooksonly) before you set it.

151* **`disableAllHooks`**: the widest setting. In managed settings, it stops the mods in every installed plugin, yours included, and turns off every hook in settings files, so a `PreToolUse` hook in your managed settings no longer blocks anything. Custom status lines and `/goal` stop working too. Read [`disableAllHooks`](/docs/en/settings-reference#disableallhooks) before you set it.

152* **`disableSideloadFlags`**: rejects `--plugin-dir` and `--plugin-url` at startup, so nobody loads a mod from a directory, and keeps mods Claude writes during a session from loading. The setting also rejects `--agents` and `--mcp-config`. Read [`disableSideloadFlags`](/docs/en/settings-reference#disablesideloadflags) before you set it.

153 

154Mods built into Claude Code, such as `AGENTS.md` support, aren't affected by these settings. Each has [its own switch](/docs/en/plugins/mods/overview#mods-built-into-claude-code).

155 

156A user whose mod didn't load finds the reason in their debug log. [Refusal messages](/docs/en/plugins/mods/troubleshoot#refusal-messages) lists the lines for `allowManagedHooksOnly` and `disableAllHooks`, and [Messages from the built-in guard](/docs/en/plugins/mods/troubleshoot#messages-from-the-built-in-guard) has the line for `allowManagedModsOnly`.

157 

158### Set options on the built-in guard

159 

160The built-in guard takes two options. Set them in managed settings under `pluginConfigs`, keyed by `cc-plugin-sec-default@builtin`, as the example in [Stop user-installed mods from loading](#stop-user-installed-mods-from-loading) does.

161 

162The table gives what your users get with each option unset and with it set to `true`:

163 

164| Option | Unset | `true` |

165| :- | :- | :- |

166| `allowManagedModsOnly` | Users' own mods load | Only [your organization's mods](#install-your-organizations-mods), and mods built into Claude Code, load. Claude Code refuses every other mod, including one a user installed or named with `--plugin-dir`. |

167| `allowModsToOverrideDenyRules` | Deny rules take precedence over users' mods | A user's mod that approves tool calls can approve a call that a `deny` rule refuses |

168 

169These rules decide whether an option takes effect:

170 

171* **The id has one spelling here**: Claude Code reads the options only under `cc-plugin-sec-default@builtin`. `prependPlugins` accepts `sec-default@builtin` as well, and `pluginConfigs` doesn't.

172* **Only managed settings count**: the same entry in a user, project, or local settings file, or in a file passed with `--settings`, neither sets an option nor loosens one

173* **The guard has to load**: if you set `prependPlugins`, [name the guard in the list](#install-your-organizations-mods). Where the guard doesn't load, neither option applies.

174* **The guard fails closed**: if the guard can't read managed settings, it refuses every user's mod at load. If it can't check the deny rules for a call that a user's mod approved, it refuses the call.

175 

176The [messages from the built-in guard](/docs/en/plugins/mods/troubleshoot#messages-from-the-built-in-guard) are what your users see when either option applies.

177 

178## Run your organization's own mods

179 

180You can deploy mods of your own to every user, choose where they run relative to users' mods, and use one to enforce a policy.

181 

182<h3 id="install-your-organizations-mods">

183 Install your organization's mods and set the order

184</h3>

185 

186Your organization's mods load where users' mods don't and can run ahead of them, so Claude Code has to be able to tell that a mod came from you. It treats a mod as your organization's only when all of these are true:

187 

188* Managed `enabledPlugins` sets the mod's plugin to `true`

189* Managed settings name the plugin's [marketplace](/docs/en/plugins/create-marketplace) as a directory on the user's machine, by absolute path. An `extraKnownMarketplaces` entry does that and registers the marketplace for the user too.

190* The marketplace lists the plugin by a relative path, so Claude Code [loads it in place](/docs/en/plugins/loading#in-place-and-copied-plugins) from that directory

191 

192To meet them, have your device management copy the marketplace directory to the same path on every machine. Make the directory and every directory above it writable only by an administrator, as the managed settings file is. Anyone who can write there can rewrite your mod. Managed settings you deliver from the claude.ai admin console can carry the keys, but they can't put the directory on a machine.

193 

194The directory holds the marketplace's manifest and the plugin:

195 

196```text theme={null}

197/opt/acme/claude-plugins/

198├── .claude-plugin/

199│ └── marketplace.json

200└── plugins/

201 └── acme-guard/

202 ├── .claude-plugin/

203 │ └── plugin.json

204 └── hooks/

205 ├── hooks.json

206 └── register.js

207```

208 

209The manifest lists the plugin by its path relative to that directory:

210 

211```json /opt/acme/claude-plugins/.claude-plugin/marketplace.json theme={null}

212{

213 "name": "acme-tools",

214 "owner": { "name": "Acme" },

215 "plugins": [

216 { "name": "acme-guard", "source": "./plugins/acme-guard", "description": "Acme policy mod" }

217 ]

218}

219```

220 

221A plugin that Claude Code copies into its cache counts as a user's, even when managed `enabledPlugins` enables it. That covers every plugin from a GitHub, git, URL, or npm source. Its mod runs among users' mods, `prependPlugins` and `appendPlugins` skip it, and it doesn't load under `allowManagedModsOnly` or `allowManagedHooksOnly`. The user's debug log has a line that starts with the plugin's id and `is enabled by managed settings, but`.

222 

223Claude Code raises an event each time it's about to act, such as run a tool, and passes it to each mod in turn. A mod that counts as yours [runs before users' mods](/docs/en/plugins/mods/events#the-order-mods-run-in) even when you list it nowhere. To set its place, list its id in one of two settings. The id is the plugin's name, `@`, and the marketplace's name, such as `acme-guard@acme-tools`.

224 

225* **`prependPlugins`**: your mod sees every event before any user's mod and every result after. It can change the event, refuse it, or skip the users' mods.

226* **`appendPlugins`**: your mod runs after every user's mod, so it sees only the events those mods pass on, in the form they pass them

227 

228This example declares the `acme-tools` marketplace at `/opt/acme/claude-plugins`, enables `acme-guard` from it, and runs that mod first, with the built-in guard after it:

229 

230```json managed-settings.json theme={null}

231{

232 "extraKnownMarketplaces": {

233 "acme-tools": {

234 "source": { "source": "directory", "path": "/opt/acme/claude-plugins" }

235 }

236 },

237 "enabledPlugins": { "acme-guard@acme-tools": true },

238 "prependPlugins": ["acme-guard@acme-tools", "sec-default@builtin"]

239}

240```

241 

242Each key does one job:

243 

244* **`extraKnownMarketplaces`**: names the directory that holds the `acme-tools` marketplace. `path` is the absolute path of the directory that contains `.claude-plugin/marketplace.json`.

245* **`enabledPlugins`**: turns `acme-guard` on for every user who receives these managed settings

246* **`prependPlugins`**: puts `acme-guard` first and the built-in guard second, both ahead of any mod a user installs. Claude Code follows the order you list.

247 

248To confirm that a user's machine received the settings, see [Check that a policy is in force](/docs/en/managed-settings#check-that-a-policy-is-in-force).

249 

250To confirm where the mod runs, start a session on that machine with `claude --debug` and search the [debug log](/docs/en/plugins/mods/troubleshoot#read-the-debug-log) for the mod's id:

251 

252* **`hooks module acme-guard@acme-tools loaded`, with `tier prepend`**: the mod counts as your organization's and runs first

253* **The same line with `tier user`**: Claude Code treats it as a user's mod. A second line, `prependPlugins names acme-guard@acme-tools, which is not an enabled managed plugin with a hooks module; skipped`, says the list skipped it.

254 

255These rules decide which ids in the two lists take effect:

256 

257* **The list replaces the default**: when you set `prependPlugins` in managed settings, name `sec-default@builtin` in it to keep the built-in guard. The guard is built in and needs no `enabledPlugins` entry.

258* **Your own ids must count as yours**: in managed settings, Claude Code skips an id whose plugin doesn't meet the three conditions for an organization's mod

259* **Repositories can't set them**: Claude Code reads both settings from managed settings and never from a repository's settings file. A user can set them in `~/.claude/settings.json` to order their own mods only on a machine with no managed settings, and only when they aren't signed in with a Team or Enterprise plan. Anywhere else, Claude Code ignores both keys in user settings. A list there neither adds nor removes the built-in guard.

260 

261### Enforce a policy with a mod of your own

262 

263To keep every user's mod out, you don't need a mod of your own. Set [`allowManagedModsOnly`](#stop-user-installed-mods-from-loading). Write a policy mod when you want to admit some users' mods and refuse others, or to record what mods do.

264 

265Each time another mod is about to load, your mod receives the list that `claude plugin validate` prints, in an event named [`plugin.register`](/docs/en/plugins/mods/reference#other-mods). A mod in `prependPlugins` can read that list and refuse the mod. It can also [hook any mods API call by name](/docs/en/plugins/mods/api#reach-files-processes-and-the-network) to record or refuse that call for every other mod. The name is the method without the `$.`, so a hook on `fs.write` sees every `$.fs.write` call.

266 

267This policy mod refuses any user's mod whose own code calls `$.process.run` or `$.process.spawn`. It also keeps an audit log, writing each tool call and each file a mod writes to the debug log. Because it runs first, the log records what was requested, before any user's mod changes it. Save it as `acme-guard/hooks/register.js`:

268 

269```javascript acme-guard/hooks/register.js theme={null}

270// The methods no user's mod may call, each spelled namespace.method

271const BLOCKED_CALLS = ['process.run', 'process.spawn']

272 

273export function register(on) {

274 // Runs each time another mod is about to load

275 on('plugin.register', async ($, e, next) => {

276 // Keep the calls in that mod's code that are on the blocked list

277 const blocked = e.uses.calls.filter((call) => BLOCKED_CALLS.includes(call))

278 if (e.tier === 'user' && blocked.length > 0) {

279 // Returning refuse keeps the mod from loading, and the text is the reason

280 return { refuse: 'Acme policy: mods may not call ' + blocked.join(', ') }

281 }

282 // Let every other mod load

283 return next(e)

284 })

285 

286 // Record each tool call, then let it go ahead unchanged

287 on('tool.call', async ($, e, next) => {

288 $.ui.log('audit tool.call ' + e.tool, { to: 'debug' })

289 return next(e)

290 })

291 

292 // Record which mod wrote a file, then the path, quoted because the mod chose it

293 on('fs.write', async ($, e, next) => {

294 $.ui.log('audit fs.write by ' + next.origin.plugin + ' ' + JSON.stringify(e.path), { to: 'debug' })

295 return next(e)

296 })

297}

298```

299 

300The file registers three hooks:

301 

302* **`plugin.register`**: decides whether another mod loads. It refuses a user's mod that calls a blocked method and passes every other mod on.

303* **`tool.call`**: writes a line such as `audit tool.call Bash` to the debug log for each tool call, and changes nothing

304* **`fs.write`**: writes a line such as `audit fs.write by reader "/tmp/notes.md"` for each `$.fs.write` call another mod makes, and changes nothing. The mod's name comes first and the path is quoted, so a path that a mod picks can't pass for another field of the line.

305 

306The `plugin.register` hook reads two fields of the event:

307 

308* **`e.tier`**: where the mod would run, one of `prepend`, `user`, `append`, or `builtin`. Every mod a person installs is `user`.

309* **`e.uses.calls`**: the mods API methods the mod calls, each spelled `namespace.method` such as `process.run`, without the `$.` that `claude plugin validate` prints

310 

311When a user installs a mod that calls `$.process.run`, the mod doesn't load, and their debug log has a line that ends with `refused by acme-guard:` and your reason. The refusal also reaches the transcript in a [session that hot-reloads a plugin directory](/docs/en/plugins/mods/troubleshoot#find-out-why-a-mod-does-nothing). To block a call without refusing the whole mod, return `{ deny: 'your reason' }` from a hook on that call's name.

312 

313To send the audit lines somewhere other than the debug log, call `$.http.fetch` from the same hooks.

314 

315A session can run without your mod. If the worker thread that runs installed mods [crashes three times](/docs/en/plugins/mods/troubleshoot#mods-that-run-in-the-hooks-worker-are-off-for-this-session), Claude Code unloads every mod that isn't built in, including yours, until the user runs `/reload-plugins` or starts a new session. And a user who starts Claude Code with `--safe-mode` runs without installed mods, yours included.

316 

317[Create a mod](/docs/en/plugins/mods/create) covers the files a mod needs. [Test a mod that judges other mods](/docs/en/plugins/mods/test#test-a-mod-that-judges-other-mods) has a test file for this policy mod.

318 

319#### Refuse mods when your check fails

320 

321If your `plugin.register` hook throws or runs past its time limit, Claude Code skips the hook, so the check fails open and the mod it was checking loads. To fail closed and refuse users' mods, move the check into a named function and add a `.catch` handler that returns the refusal. This version of the file shows the `plugin.register` hook only, so keep the two audit hooks from the first version in `register`:

322 

323```javascript acme-guard/hooks/register.js theme={null}

324const BLOCKED_CALLS = ['process.run', 'process.spawn']

325 

326// The same check as before, moved into a function of its own

327async function checkMod($, e, next) {

328 const blocked = e.uses.calls.filter((call) => BLOCKED_CALLS.includes(call))

329 if (e.tier === 'user' && blocked.length > 0) {

330 return { refuse: 'Acme policy: mods may not call ' + blocked.join(', ') }

331 }

332 return next(e)

333}

334 

335export function register(on) {

336 // The handler runs only when checkMod throws or runs past its time limit

337 on('plugin.register', checkMod).catch(async ($, e, next) => {

338 // Let your organization's mods and built-in mods load

339 if (e.tier !== 'user') return next(e)

340 // Refuse the user's mod that couldn't be checked

341 return { refuse: 'Acme policy check failed, so this mod was not loaded' }

342 })

343}

344```

345 

346With the handler in place, a mod that was being checked when the check threw or timed out doesn't load, and the refusal line carries the second reason, as in `refused by acme-guard: Acme policy check failed, so this mod was not loaded`. The handler passes every mod outside the `user` tier to `next(e)`, so a failed check doesn't stop the mods your organization lists. [Handle a hook that fails](/docs/en/plugins/mods/events#handle-a-hook-that-fails) covers `.catch` for other events.

347 

348## Next steps

349 

350* [Plugin security](/docs/en/plugins/security): what any plugin can do on a user's machine, and how to review one before it's installed

351* [Mods overview](/docs/en/plugins/mods/overview): what a mod is and how it compares to hooks, skills, and MCP servers

352* [The order mods run in](/docs/en/plugins/mods/events#the-order-mods-run-in): how `prependPlugins` and `appendPlugins` fit with users' mods

353* [Settings and environment variables](/docs/en/plugins/mods/reference#settings-and-environment-variables): every setting named on this page in one table

plugins/mods/api.md +196 −0 created

Details

1> ## Documentation Index

2> Fetch the complete documentation index at: https://code.claude.com/docs/llms.txt

3> Use this file to discover all available pages before exploring further.

4 

5# Use the mods API

6 

7> Call the mods API from a Claude Code mod to add commands and tools, call a model, run work on a timer, message other sessions, and reach files and the network.

8 

9The mods API is the set of methods a mod calls to act: add commands and tools, call a model, run work between events, and reach the file system, processes, and the network. Every hook receives it as its first argument, `$`, with the methods grouped in namespaces such as `$.ui` and `$.fs`. [Events](/docs/en/plugins/mods/events) decide when a hook runs, and the mods API is what the hook calls once it does.

10 

11Build your [first mod](/docs/en/plugins/mods/create) before you start here. For every method, see [mods API methods](/docs/en/plugins/mods/reference#mods-api-methods) or read [the types for your build](/docs/en/plugins/mods/create#get-the-types-for-your-build).

12 

13## Add a command or a tool

14 

15A mod can add a command for the user to run and a tool for Claude to call. Register both in a [`session.start`](/docs/en/plugins/mods/reference#session) hook. Claude Code waits for that hook before the first prompt, so what you register is available from the first turn.

16 

17### Add a command

18 

19A command is for the user. Register it, then handle [`command.run`](/docs/en/plugins/mods/reference#commands-and-configuration) for its name. This example adds a `/standup` command that takes an optional number of days:

20 

21```javascript theme={null}

22on('session.start', async ($, e, next) => {

23 // Add /standup to the command list, with the description the user sees there

24 await $.command.register({ name: 'standup', description: 'Summarize what changed today', argumentHint: '[days]' })

25 return next(e)

26})

27 

28// The matcher limits the hook to /standup, so other commands don't reach it

29on('command.run', { command: 'standup' }, async ($, e) => {

30 // e.args is the text typed after the command name, or an empty string

31 return { text: 'Summary for the last ' + (e.args || '1') + ' day(s): ...' }

32})

33```

34 

35After the session starts, `/standup` appears with its description in the list you see when you type `/`. The `argumentHint` shows in the prompt after you type the command and a space, as in `/standup [days]`. When you run `/standup 3`, the second hook returns `Summary for the last 3 day(s): ...`, and the transcript shows that text after the plugin's name. The hook never calls `next`, because the command has no behavior other than yours.

36 

37The `text` you return prints in the transcript and Claude reads it. To print nothing, as a command that only opens a [pane](/docs/en/plugins/mods/interface#pick-where-to-draw) does, return `{}`. To let the command run while Claude is working, add `immediate: true` to the registration.

38 

39Pick a name that no built-in command uses. Type `/` in a session to see them. `$.command.register` throws for a taken name, with a message such as `"/focus" refused: it is the built-in /focus`. A hook that throws is skipped, so the rest of your `session.start` hook doesn't run either. Register commands last in that hook, or wrap the call in `try` and `catch`.

40 

41### Add a tool

42 

43A tool is for Claude. Register it with a name, a description Claude reads, and a JSON Schema for its input. Claude sees it under a longer name made of `mcp__`, your plugin's name, two underscores, and the name you registered. You handle its calls in a [`tool.call`](/docs/en/plugins/mods/events#guard-or-change-a-tool-call) hook filtered to that full name. This example, from a plugin named `my-mod`, registers `ticket`, so the full name is `mcp__my-mod__ticket`. It gives Claude a tool that looks up a ticket in an issue tracker:

44 

45```javascript theme={null}

46on('session.start', async ($, e, next) => {

47 await $.tool.register({

48 name: 'ticket',

49 // Claude decides when to call the tool from this description

50 description: 'Look up a ticket by its id and return its title and status',

51 // The arguments Claude has to send: one required string named id

52 inputSchema: { type: 'object', properties: { id: { type: 'string' } }, required: ['id'] },

53 })

54 return next(e)

55})

56 

57// The full tool name is mcp__, the plugin's name, and the registered name

58on('tool.call', { tool: 'mcp__my-mod__ticket' }, async ($, e) => {

59 // The tool's arguments are fields of e, so the id is e.id

60 const response = await $.http.fetch('https://tickets.example.com/api/' + encodeURIComponent(e.id))

61 // Return a result either way, so Claude learns when the lookup failed

62 return { result: response.ok ? response.text : 'Lookup failed with status ' + response.status }

63})

64```

65 

66When you ask about a ticket, Claude can call `mcp__my-mod__ticket` with its id. The second hook fetches the ticket and returns the response body, which Claude reads as the tool's result. When the server answers with an error status, Claude reads `Lookup failed with status` and the number.

67 

68## Call a model

69 

70A mod can ask a model a question of its own, outside the conversation, for a small job such as sorting or summarizing a piece of text. `$.model.complete` sends one prompt to a model with your session's credentials and resolves to the reply. It has no conversation history.

71 

72This hook answers a `/triage` command, [registered as a command](#add-a-command), by asking a small model to label the text typed after it:

73 

74```javascript theme={null}

75on('command.run', { command: 'triage' }, async ($, e) => {

76 const r = await $.model.complete({

77 model: 'haiku',

78 // The system prompt sets the job, and the prompt carries the text to label

79 system: 'Reply with one word: bug, feature, or question.',

80 prompt: e.args,

81 // One word needs few tokens, and the call gives up after 15 seconds

82 maxTokens: 20,

83 timeoutMs: 15000,

84 })

85 // r.text exists only when the model answered, so check r.isAnswered first

86 const label = r.isAnswered ? r.text.trim() : 'unknown'

87 return { text: 'Label: ' + label }

88})

89```

90 

91When you run `/triage the export button does nothing`, the mod sends that text to the model and prints its answer, such as `Label: bug`. Claude's conversation isn't part of the request. When the model doesn't answer, the label is `unknown`.

92 

93A Claude API failure doesn't reject the call, so check `r.isAnswered`, and read `r.reason` when it's `false`. The call rejects only for a request Claude Code won't send, such as a model your organization blocks. [The types for your build](/docs/en/plugins/mods/create#get-the-types-for-your-build) list the other options, such as `effort`, and the [limits](/docs/en/plugins/mods/reference#limits) give the `maxTokens` default.

94 

95`$.model.fork({ prompt })` asks one question over the current conversation instead, with the same model and system prompt, so the Claude API serves most of it from the prompt cache.

96 

97These calls use the user's plan or API key.

98 

99## Run work in the background

100 

101Work that outlives one event, such as checking on something once a minute, runs on a timer you start from `session.start`. A hook itself runs for one event and has a time limit of 10 seconds of its own running time. Time spent waiting on `next` or on a mods API call doesn't count, except a `$.clock.sleep`. `$.clock.every` and `$.clock.after` take the place of `setInterval` and `setTimeout`, with the delay in milliseconds first: `$.clock.after(5000, fn)` calls `fn` once, five seconds from now. Each returns a timer with a `cancel()` method, and `await $.clock.now()` gives the time in milliseconds.

102 

103This hook looks up a pull request's checks once a minute and shows the result under the prompt. `summarize` is a function of your own that turns the command's JSON output into a few words:

104 

105```javascript theme={null}

106on('session.start', async ($, e, next) => {

107 // Call the function every 60,000 milliseconds, starting one minute from now

108 $.clock.every(60_000, async () => {

109 const status = await $.process.run(['gh', 'pr', 'checks', '--json', 'state'])

110 // Replace the line under the prompt with the latest summary

111 $.ui.status('checks: ' + summarize(status.stdout))

112 })

113 // Return without waiting for the timer, so the session starts right away

114 return next(e)

115})

116```

117 

118The session starts as usual. A minute later, a line appears under the prompt with a `⚠`, the mod's name, and then `checks:` and your summary. It's replaced once a minute after that. The timer's callback runs outside any event, so it keeps running between turns and doesn't start one. If the callback throws, the error goes to the [debug log](/docs/en/plugins/mods/troubleshoot#read-the-debug-log) and the timer runs again at the next interval.

119 

120### Show something without starting a turn

121 

122A background job can show the user something without starting a turn. Each of these calls puts text in a different place:

123 

124| Call | What the user sees |

125| :- | :- |

126| `$.ui.status(text)` | One line under the prompt that stays until you change it. It starts with `⚠` and the mod's name, as in `⚠ my-mod: checks: 3 passing`. |

127| `$.ui.toast(text)` | A small box at the top right, with the mod's name above the text, that disappears after a few seconds |

128| `$.ui.log(text)` | A dim line in the transcript that Claude doesn't read. It starts with `●` and the mod's name, as in `● my-mod: build finished`. |

129 

130### Start a turn from a background job

131 

132When a background job finds something that needs Claude's attention, it can start a turn by submitting a prompt with `$.prompt.submit({ text })`. Claude reads the text after a sentence that names your mod as the sender. To send it as the user's own words, without that sentence, add `asUser: true`. The call waits until the session is idle and then starts a new turn. It resolves when that turn starts, so don't `await` it in a handler that runs while Claude is working.

133 

134### Stop background work

135 

136Background work stops in two ways. Timers stop when the module reloads. For long-running work inside a hook, [`next.signal`](/docs/en/plugins/mods/reference#the-hook-function) is an `AbortSignal` that aborts when the event your hook is handling is abandoned, for example when the user interrupts, so pass it to anything long-running.

137 

138## Send and receive messages between sessions

139 

140A mod can send a plain-text message to another of your sessions or to one of this session's subagents, and observe the messages that arrive and leave. `$.session.send({ to, text })` sends one, the same delivery the SendMessage tool makes. `to` is `{ sessionId }` for a session, `{ agentId }` for a subagent from `$.agent.list()`, or the string address a received message came from. The call resolves once the message is queued, with `{ isDelivered: true }`. When nothing was delivered it resolves with `{ isDelivered: false, reason }`, and `reason` says why.

141 

142This hook answers a `/ping` command, [registered as a command](#add-a-command), by asking the session whose id you type after it for a status:

143 

144```javascript theme={null}

145on('command.run', { command: 'ping' }, async ($, e) => {

146 // e.args is the session id typed after /ping

147 const sent = await $.session.send({ to: { sessionId: e.args }, text: 'Status? One line.' })

148 // The call resolves either way, so check isDelivered to learn what happened

149 if (!sent.isDelivered) $.ui.toast('Not delivered: ' + sent.reason)

150 // An empty result prints nothing in this session's transcript

151 return {}

152})

153```

154 

155When the message is queued, nothing appears in your session, and the other session's Claude reads `Status? One line.` When nothing was delivered, a small box at the top right gives the reason and disappears after a few seconds.

156 

157Two events let a mod observe the messages. Return `next(e)` from both to pass each message through unchanged:

158 

159| Event | Fires when | Useful fields |

160| :- | :- | :- |

161| `session.receive` | A message arrives for this session, before Claude reads it | `e.text`, and `e.origin.kind`, such as `peer` or `peer-send-message` for another session or agent, `task-notification`, or `scheduled-trigger`. Return `{ consumed: reason }` to keep it from Claude. |

162| `session.send` | A message is about to leave, from the SendMessage tool or a mod | `e.to`, `e.text`, and `e.origin.kind`, which is `model` or `plugin` |

163 

164A session set to [refuse inbound messages](/docs/en/cross-session-messaging#control-inbound-messages) refuses a message before `session.receive` fires, so a hook never sees it. A message that's held for your approval reaches the hook first, so a mod can read a message you haven't approved yet. The hook's `next(e)` rejects when the message isn't delivered.

165 

166The sender's name on a received message is whatever the sender wrote, so don't base a decision on it.

167 

168## Reach files, processes, and the network

169 

170A mod reaches the file system, processes, and the network through the mods API, with the same permissions as the user running Claude Code. The hooks module itself has no Node.js APIs, no timer globals such as `setTimeout`, and no network or file access of its own. Standard JavaScript and web APIs such as `URL`, `TextEncoder`, `AbortController`, and `crypto.subtle` are available. Each namespace below covers one kind of access:

171 

172| Namespace | What it does |

173| :- | :- |

174| `$.fs` | `read(path)`, `write(path, text)`, `exists(path)`, `stat(path)`, and `list(path)` work on files and directories |

175| `$.process` | `run(['git', 'status'])` starts a command and resolves when it exits. `spawn` streams a long-running command's output. |

176| `$.http` | `fetch(url, init)` over `http` or `https`. It resolves to `{ status, ok, headers, text }` once the body is read. |

177| `$.store` | A JSON key-value store of your plugin's own, kept between sessions |

178| `$.env` | `get` and `set` environment variables. Write the name as a literal string. |

179| `$.settings` | `read` what the settings files and managed policy hold |

180| `$.session` | `messages()` returns the transcript as a list of `{ role, text, toolUses }`. Also the working directory, model, and more. [`usage()`](/docs/en/plugins/mods/reference#mods-api-methods) returns context window use and plan limits. |

181| `$.mcp` | `call` a tool on a connected MCP server |

182 

183Files and processes have a few rules of their own:

184 

185* **Paths**: a relative path is under the session's working directory

186* **`$.fs.list`**: returns one directory's entries as `{ name, kind, size, isLink }` and doesn't descend into subdirectories

187* **`$.process.run`**: takes an argument list and uses no shell. It resolves to `{ exitCode, stdout, stderr }` whatever the exit code. It rejects if the program can't start or is still running at the timeout, which is 30 seconds by default, so wrap it in `try` and `catch`.

188 

189Every one of these calls is itself an event, named for its namespace and method without the `$.`, such as `fs.read` for `$.fs.read`. A mod [earlier in the chain](/docs/en/plugins/mods/events#the-order-mods-run-in) can observe, rewrite, or refuse your call, which is how an organization restricts what mods reach.

190 

191## Next steps

192 

193* [React to events](/docs/en/plugins/mods/events): hook tool calls, prompts, and turns

194* [Draw in the interface](/docs/en/plugins/mods/interface): show what your mod collects in a pane or above the prompt

195* [Test a mod](/docs/en/plugins/mods/test): stub any of these calls in a test

196* [Mods reference](/docs/en/plugins/mods/reference): every event, every mods API method, and the limits

plugins/mods/create.md +375 −0 created

Details

1> ## Documentation Index

2> Fetch the complete documentation index at: https://code.claude.com/docs/llms.txt

3> Use this file to discover all available pages before exploring further.

4 

5# Create a mod

6 

7> Have Claude write a Claude Code mod from a description, or write one yourself that counts tool calls and adds a command. Learn the reload and validate loop.

8 

9A mod is a Claude Code [plugin](/docs/en/plugins/overview) with an entry file, called the hooks module: a JavaScript or TypeScript file whose functions Claude Code calls when events happen. There are two ways to make one:

10 

11* **Ask Claude to write it**: [describe what you want](#ask-claude-for-a-mod) in a Claude Code session

12* **Write it yourself**: [follow the tutorial](#write-a-mod-yourself) to learn how a mod's code works. You don't need Node.js, a bundler, or a build step, because Claude Code loads `.js` and `.ts` files directly.

13 

14If you haven't decided whether a mod is the right tool, read the [comparison on the overview](/docs/en/plugins/mods/overview#compare-mods-settings-hooks-skills-and-mcp-servers) first.

15 

16<Note>

17 Mods require Claude Code v2.1.287 or later. In your shell, run `claude --version` to check. To see whether mods can load for you, see [Check whether mods can load](/docs/en/plugins/mods/troubleshoot#check-whether-mods-can-load).

18</Note>

19 

20## Ask Claude for a mod

21 

22Describe the mod you want in an interactive Claude Code session, and Claude writes it. Claude works from a built-in [skill](/docs/en/skills) named `plugin-authoring`, which tells it where to write the mod, which events and methods your version has, and how the mod gets loaded. Claude can load the skill when you ask for a mod, or you can load it yourself by running `/plugin-authoring` at the Claude Code prompt.

23 

24The mod runs once you approve it, except in [sessions where a mod Claude writes can't load](#sessions-that-skip-the-approval).

25 

26<Steps>

27 <Step title="Describe the mod">

28 Ask for the mod in your own words, for example `make a mod that shows the current git branch above the prompt`. Claude writes the mod in a directory of its own in the session's mods folder, which is `~/.claude/dev-mods/` followed by the session's ID. A mod's full path looks like `~/.claude/dev-mods/3f2a9c1e-5b7d-4e8a-9c21-6d0f4b8a7e13/git-branch/`.

29 

30 <Note>

31 In the `default` and `acceptEdits` [permission modes](/docs/en/permission-modes#protected-paths), Claude Code asks before Claude creates each of the mod's files, because `~/.claude` is a protected path. Approve each file as it comes up.

32 </Note>

33 </Step>

34 

35 <Step title="Approve the mod">

36 When Claude saves the first file, Claude Code asks whether to enable hot reloading for the session. Hot reloading runs the mods Claude writes in this session and picks up each later change.

37 

38 Choose one of these answers:

39 

40 * **Enable for this session**: the mods in the session's mods folder load when the turn ends, and reload at the end of each turn that changes them. Your answer lasts for the session, including after you resume it.

41 * **Not now**: nothing loads for now. The files stay where Claude wrote them, and the mods load the next time that session starts. To keep a mod from ever loading, delete its directory.

42 </Step>

43 

44 <Step title="Check that the mod loaded">

45 Run `/plugin` at the Claude Code prompt and press Tab until the **Installed** tab is selected. It lists the mod, and you can turn it off there.

46 </Step>

47 

48 <Step title="Try the mod">

49 Use what you asked for. For the example prompt, the current branch name appears above the prompt box. If the mod doesn't do what you wanted, tell Claude what to change. The mod reloads at the end of each turn that changes its files, so you can try the change as soon as Claude finishes.

50 </Step>

51</Steps>

52 

53### Use the mod in other sessions

54 

55A mod Claude wrote loads only in the session that made it, and Claude Code deletes that session's mods folder once it's older than [`cleanupPeriodDays`](/docs/en/settings-reference#cleanupperioddays). To keep the mod, copy its directory out of the mods folder to a place of your own, such as `~/mods/git-branch`. Then choose how to load it:

56 

57* **In a session you start**: in your shell, run `claude --plugin-dir ~/mods/git-branch`

58* **For other people**: [add it to a marketplace](#share-your-mod) so they can install it

59 

60<h3 id="sessions-that-skip-the-approval">

61 Sessions where a mod Claude writes can't load

62</h3>

63 

64A mod Claude writes loads only after you approve it, in a trusted workspace where mods are allowed to run. In these sessions it doesn't load:

65 

66* **Nobody is there to approve**: the session can't show you a prompt, as in a `claude -p` run or [`dontAsk` mode](/docs/en/permission-modes)

67* **The workspace isn't trusted**: you haven't accepted the trust prompt for the directory

68* **Mods are stopped**: you started with `--safe-mode` or `--bare`, you set `disableAllHooks`, or your organization's [managed settings block it](/docs/en/plugins/mods/admin#choose-how-much-to-allow)

69 

70## Write a mod yourself

71 

72In this tutorial you build a mod named `first-mod` that counts the tool calls Claude makes, shows the count beside the spinner while Claude works, and adds a `/tally` command that prints it. You then read the type declarations Claude Code writes beside your mod and run `claude plugin validate`. Together they show you the events and methods your version offers and what Claude Code reads from your code.

73 

74This recording shows the finished mod. The spinner counts tool calls, `/tally` prints the count, and an edit to the code takes effect while the session runs:

75 

76<Frame>

77 <video autoPlay muted loop playsInline controls className="w-full dark:hidden" src="https://mintcdn.com/claude-code/dgiVO_Od1X1faduV/images/mods-first-mod-light.mp4?fit=max&auto=format&n=dgiVO_Od1X1faduV&q=85&s=eb561134afa90375777408453ba51c77" aria-label="In a Claude Code session, the prompt 'list the files here and read the README' is typed and sent. The spinner reads 'Thinking · tool calls: 1' and the count rises as Claude works. The /tally command prints 'first-mod: Claude has made 3 tool calls since this mod loaded'. A line says first-mod reloaded and lists its four hooks. On the next prompt the spinner reads 'Thinking · tools used: 1'." data-path="images/mods-first-mod-light.mp4" />

78 

79 <video autoPlay muted loop playsInline controls className="w-full hidden dark:block" src="https://mintcdn.com/claude-code/dgiVO_Od1X1faduV/images/mods-first-mod-dark.mp4?fit=max&auto=format&n=dgiVO_Od1X1faduV&q=85&s=09779dadc7ef66c2b1e2da0c2e31ac72" aria-label="In a Claude Code session, the prompt 'list the files here and read the README' is typed and sent. The spinner reads 'Thinking · tool calls: 1' and the count rises as Claude works. The /tally command prints 'first-mod: Claude has made 3 tool calls since this mod loaded'. A line says first-mod reloaded and lists its four hooks. On the next prompt the spinner reads 'Thinking · tools used: 1'." data-path="images/mods-first-mod-dark.mp4" />

80</Frame>

81 

82You write three files:

83 

84```text theme={null}

85first-mod/

86├── .claude-plugin/

87│ └── plugin.json

88└── hooks/

89 ├── hooks.json

90 └── register.js

91```

92 

93* **`plugin.json`**: the plugin's [manifest](/docs/en/plugins/manifest-reference)

94* **`hooks.json`**: [points to your code file](/docs/en/plugins/mods/reference#files)

95* **`register.js`**: your code, called the hooks module

96 

97<Steps>

98 <Step title="Create the plugin directory">

99 Create the two directories that hold the files:

100 

101 <Tabs>

102 <Tab title="Bash or Zsh">

103 ```bash theme={null}

104 mkdir -p first-mod/.claude-plugin first-mod/hooks

105 ```

106 </Tab>

107 

108 <Tab title="PowerShell">

109 ```powershell theme={null}

110 New-Item -ItemType Directory -Force first-mod\.claude-plugin, first-mod\hooks

111 ```

112 </Tab>

113 </Tabs>

114 </Step>

115 

116 <Step title="Write the manifest">

117 A mod is a plugin, and a mod needs a [manifest](/docs/en/plugins/manifest-reference). This mod's manifest has no special fields. Save this as `first-mod/.claude-plugin/plugin.json`:

118 

119 ```json first-mod/.claude-plugin/plugin.json theme={null}

120 {

121 "name": "first-mod",

122 "version": "0.1.0",

123 "description": "Counts Claude's tool calls, shows the count beside the spinner, and adds a /tally command",

124 "author": { "name": "Your Name" }

125 }

126 ```

127 </Step>

128 

129 <Step title="Tell Claude Code where your code is">

130 When Claude Code loads a plugin, it reads the plugin's `hooks/hooks.json`. The `modules` key in that file gives the path to your code, and having it is what makes the plugin a mod. List one path, relative to `hooks.json`. Here it points to `register.js`, which you write in the next step.

131 

132 Save this as `first-mod/hooks/hooks.json`:

133 

134 ```json first-mod/hooks/hooks.json theme={null}

135 {

136 "description": "The first-mod hooks module",

137 "modules": ["./register.js"]

138 }

139 ```

140 </Step>

141 

142 <Step title="Write the code">

143 This file is the mod's code, called the hooks module. When the mod loads, Claude Code calls the `register` function the file exports and passes it a function named [`on`](/docs/en/plugins/mods/reference#the-hook-function). Each call to `on` registers an event handler, called a hook, for the event it names.

144 

145 Save this as `first-mod/hooks/register.js`:

146 

147 ```javascript first-mod/hooks/register.js theme={null}

148 // The count, shared by the hooks below

149 let calls = 0

150 

151 // Claude Code calls this once when the mod loads

152 export function register(on) {

153 // Runs when the session starts, before your first prompt

154 on('session.start', async ($, e, next) => {

155 // Add the /tally command

156 await $.command.register({

157 name: 'tally',

158 description: 'Show how many tool calls Claude has made',

159 })

160 // Let the session start as usual

161 return next(e)

162 })

163 

164 // Runs each time Claude is about to use a tool

165 on('tool.call', async ($, e, next) => {

166 calls += 1

167 // Ask Claude Code to draw the interface again, so the new count shows

168 $.ui.invalidate('ui.render')

169 // Let the tool run as usual

170 return next(e)

171 })

172 

173 // Runs when you type /tally, and only then, because of the matcher

174 on('command.run', { command: 'tally' }, async () => {

175 // The text to print in the transcript

176 return { text: 'Claude has made ' + calls + ' tool calls since this mod loaded' }

177 })

178 

179 // Runs each time Claude Code draws the spinner

180 on('ui.render', { component: 'Spinner' }, async ($, e, next) => {

181 // Keep Claude Code's spinner, with the count added after its word

182 return next({ ...e, props: { ...e.props, suffix: ' · tool calls: ' + calls + '…' } })

183 })

184 }

185 ```

186 

187 The file keeps a count in `calls` and registers four hooks:

188 

189 * **[`session.start`](/docs/en/plugins/mods/reference#session)** runs when the session starts, before your first prompt, and again each time the mod reloads. It adds the `/tally` command to Claude Code.

190 * **[`tool.call`](/docs/en/plugins/mods/reference#tools)** runs each time Claude is about to use a tool. It adds one to `calls` and asks Claude Code to draw the interface again.

191 * **[`command.run`](/docs/en/plugins/mods/reference#commands-and-configuration)** runs when you type `/tally`. It returns the text to print.

192 * **[`ui.render`](/docs/en/plugins/mods/reference#interface)** runs each time Claude Code draws the spinner. It adds the count after the spinner's word.

193 

194 [How the example mod works](#how-the-example-mod-works) explains the three arguments each hook takes and what each one returns.

195 </Step>

196 

197 <Step title="Load the mod">

198 Start Claude Code with the `--plugin-dir` flag, which loads a plugin directory for one session without installing it:

199 

200 ```bash theme={null}

201 claude --plugin-dir ./first-mod

202 ```

203 </Step>

204 

205 <Step title="Try the mod">

206 Ask Claude to do something that takes a few tool calls, such as `list the files here and read the README`. While Claude works, the spinner's word is followed by a count that rises, as in `Thinking · tool calls: 2…`. When Claude finishes, type `/tally` and press Enter. The transcript shows `first-mod: Claude has made 2 tool calls since this mod loaded`, with your own count. Claude Code puts the plugin's name in front of the command's text.

207 

208 To check the command without an interactive session, run it in non-interactive mode:

209 

210 ```bash theme={null}

211 claude -p "/tally" --plugin-dir ./first-mod

212 ```

213 

214 ```text theme={null}

215 first-mod: Claude has made 0 tool calls since this mod loaded

216 ```

217 

218 If `/tally` isn't in the command list, the module didn't load. See [Find out why a mod does nothing](/docs/en/plugins/mods/troubleshoot#find-out-why-a-mod-does-nothing).

219 </Step>

220 

221 <Step title="Change the code while the session runs">

222 Leave the session open. In `register.js`, change `' · tool calls: '` to `' · tools used: '` in the `ui.render` hook and save. The highlighted line is the one that changes:

223 

224 ```javascript first-mod/hooks/register.js {4} theme={null}

225 // Runs each time Claude Code draws the spinner

226 on('ui.render', { component: 'Spinner' }, async ($, e, next) => {

227 // Keep Claude Code's spinner, with the count added after its word

228 return next({ ...e, props: { ...e.props, suffix: ' · tools used: ' + calls + '…' } })

229 })

230 ```

231 

232 A line in the transcript says `first-mod` reloaded and lists its hooks, and the next spinner uses the new text, as in `Thinking · tools used: 1…`.

233 </Step>

234</Steps>

235 

236### How the example mod works

237 

238Each function you pass to `on` is a hook, which is an event handler. Claude Code passes every hook the same three arguments:

239 

240* **The mods API**, named `$`: every method a mod can call to reach outside itself, in [namespaces](/docs/en/plugins/mods/reference#mods-api-methods) such as `$.ui` and `$.command`

241* **The event**, named `e`: the [event's input](/docs/en/plugins/mods/reference#events) as plain data, such as a tool call's name and arguments

242* **The next handler**, named [`next`](/docs/en/plugins/mods/events#how-a-hook-handles-an-event): a function that passes the event on to the other mods and then to Claude Code's own behavior, and returns the result

243 

244The hooks in `first-mod` handle their events in the three ways a hook can:

245 

246* **Observe**: the `session.start` hook registers the command, and the `tool.call` hook counts the call and asks for a redraw. Both return `next(e)`, so the session starts and the tool runs as usual.

247* **Answer**: the `command.run` hook returns its own result and never calls `next`. The second argument to `on`, `{ command: 'tally' }`, is a filter, called a [matcher](/docs/en/plugins/mods/events#filter-which-events-a-hook-handles), so the hook runs only for `/tally`.

248* **Rewrite**: the `ui.render` hook calls `next` with a copy of `e` whose `suffix` holds the count, so Claude Code draws its usual spinner with your text after the word

249 

250Claude Code watches a directory loaded with `--plugin-dir` and hot-reloads the hooks module when a file in it changes. Each reload runs `register` again, so `calls` goes back to `0` and `/tally` starts counting again. To keep a value across reloads, see [Keep state](/docs/en/plugins/mods/interface#keep-state).

251 

252## Keep working on a mod

253 

254Once a mod loads, you can have Claude change it, check your code against the type definitions for your version, list the events and calls Claude Code finds in it, and test it.

255 

256### Change a mod with Claude

257 

258To change a mod you already have, start the session with `--plugin-dir` pointed at the mod's directory, so that what Claude writes loads in the same session:

259 

260```bash theme={null}

261claude --plugin-dir ./first-mod

262```

263 

264Then ask for the change, for example `add a /tally-reset command to this mod that sets the tally back to zero`. Claude edits the hooks module, runs `claude plugin validate`, and fixes what it reports. A directory you load with `--plugin-dir` is a [protected path](/docs/en/permission-modes#protected-paths), so in `default` and `acceptEdits` modes you're asked to approve each of Claude's edits to the mod. The protected paths table gives the result for the other permission modes.

265 

266Files Claude saves during its turn reload when the turn ends, so you can try `/tally-reset` as soon as Claude finishes.

267 

268<h3 id="get-the-types-for-your-build">

269 Get type definitions for your version

270</h3>

271 

272Each time Claude Code loads or reloads a mod from a directory you pass to `--plugin-dir`, or a mod [Claude wrote for you](#ask-claude-for-a-mod), it writes TypeScript declaration files, ending in `.d.ts`, into `.claude-plugin/types/` inside the mod's directory. They describe the exact events, mods API methods, and elements in the Claude Code version you're running, so your editor can autocomplete and type-check your hooks. To browse the declarations online, read [`mods/types/claude-code.d.ts`](https://github.com/anthropics/claude-code/blob/main/mods/types/claude-code.d.ts) in the Claude Code repository, whose first line names the version that wrote it. The directory holds these files:

273 

274| Path | What it declares |

275| :- | :- |

276| `claude-code/index.d.ts` | Every event and its input and result, every mods API namespace and method, and the elements each surface can draw |

277| `claude-code-tools/index.d.ts` | The built-in tools' inputs and results, so that checking `e.tool === 'Bash'` narrows `e` |

278| `claude-code-mcp/index.d.ts` | The inputs of the MCP tools that were connected the last time you saved a file in the mod |

279| `index.d.ts` in a directory named for a plugin | What that plugin adds to the mods API. There's one directory for each plugin your `plugin.json` lists under `dependencies`. |

280| `tsconfig.json` | Compiler options that fit a hooks module |

281 

282If your mod has no `tsconfig.json` of its own, Claude Code adds one at the mod's root that extends the generated one, so your editor and `tsc -p ./first-mod` type-check the mod without more setup.

283 

284The events and methods can change between releases, so trust these files over any page, this one included, when they disagree.

285 

286`claude-code/index.d.ts` is the fullest reference for your build, with a comment and an example for every mods API method. To look something up, search the file for its name, such as `'tool.call'`.

287 

288### Check what Claude Code reads from your mod

289 

290To see your mod the way Claude Code sees it, without running your code or starting a session, use `claude plugin validate`. It checks the manifest and runs the same static analysis on the hooks module's source that Claude Code runs when it loads a mod. In your shell, run it on the mod's directory:

291 

292```bash theme={null}

293claude plugin validate ./first-mod

294```

295 

296For `first-mod`, the output includes these lines.

297 

298```text theme={null}

299 ❯ ./register.js hooks: session.start, tool.call, command.run{command=tally}, ui.render{component=Spinner}

300 ❯ ./register.js calls: $.command.register, $.ui.invalidate

301 

302✔ Validation passed

303```

304 

305The `hooks:` line lists the events your module hooks, each with its filter in braces. The `calls:` line lists every mods API method it calls. A module that reads or sets environment variables also gets `env reads:` and `env writes:` lines, and one that uses [`$.state`](/docs/en/plugins/mods/interface#keep-state) gets `state reads:` and `state writes:`.

306 

307If an event you meant to hook is missing from the first line, Claude Code won't call that hook either. The usual cause is a misspelled event name, which the command reports as an error such as `"tool.calls" is not an event`.

308 

309Follow these rules so that static analysis can find every hook and call:

310 

311* Spell each mods API call in full: `$`, the namespace, then the method, as in `$.store.get('notes')`. You can pass `$` to a function declared at the top level of the same file, and for a function of yours named `loadNotes`, the `calls:` line then reads `$.store.get (via loadNotes)`. Passing `$` to a method, a function defined inside the hook, or a function you import from another of your files fails validation. The `read` and `update` functions that [`$.state`](/docs/en/plugins/mods/interface#keep-state) uses are the imports that can take it. Don't assign `$` or one of its namespaces to a variable, destructure it, or index it with a computed name. `const ui = $.ui` fails with `$.ui is used as a value`.

312* Write the event name in each `on` call as a string literal, such as `'tool.call'`. A variable, or a loop over a list of names, fails with `the event name passed to on() is not a string literal`.

313* Inside `register`, don't declare a second variable or parameter named `on`. Validation fails with `"on" is declared again (shadowed)`.

314* Import only from files inside the plugin directory, by relative path. The one bare import allowed is `claude-code`, for types and a few helpers.

315* Use `import` declarations at the top of the file, as in `import { name } from './file.js'`. A dynamic `import()` fails with `a dynamic import(); a hooks module imports its own files with an import declaration`.

316* Write every file as an ES module, with `import` and not `require`. The [reference](/docs/en/plugins/mods/reference#files) lists the file extensions Claude Code loads.

317 

318### Test the mod

319 

320You can write automated tests for a mod and run them from your shell with `claude plugin test`, with no session, sign-in, or network. A test raises the events your hooks handle and checks what the hooks did.

321 

322This test raises two tool calls, runs `/tally`, and checks that the reply counts both. Save it as `first-mod/tests/first-mod.test.ts`:

323 

324```typescript first-mod/tests/first-mod.test.ts theme={null}

325import { expect, test } from 'claude-code/testing'

326 

327test('/tally reports the tool calls the mod has seen', async ($, on) => {

328 // Answer each tool call in Claude Code's place, so no tool runs

329 on('tool.call', () => ({ result: 'ok' }))

330 

331 // Raise two tool calls, which the mod's tool.call hook counts

332 await $.tool.call({ tool: 'Bash', command: 'ls' })

333 await $.tool.call({ tool: 'Read', file_path: 'README.md' })

334 

335 // Run /tally and check the text its hook returns

336 const answer = await $.command.run({ command: 'tally', args: '' })

337 expect(answer.text).toBe('Claude has made 2 tool calls since this mod loaded')

338})

339```

340 

341In your shell, run the tests from the `first-mod` directory:

342 

343```bash theme={null}

344claude plugin test

345```

346 

347The output names each test and whether it passed, with timings that vary from run to run:

348 

349```text theme={null}

350tests/first-mod.test.ts:

351(pass) /tally reports the tool calls the mod has seen [22.87ms]

352 

353 1 pass

354 0 fail

355Ran 1 test across 1 file. [0.19s]

356```

357 

358[Test a mod](/docs/en/plugins/mods/test) covers stubbing a model call or the store, and testing timers and drawings.

359 

360## Share your mod

361 

362A mod is a plugin, so you version it in the manifest and people install and update it with the `/plugin` commands. To give it to other people, [add it to a marketplace](/docs/en/plugins/publish).

363 

364Before you do, check the plugin's `name`: `claude plugin validate` fails a name that [looks like one of Anthropic's own](/docs/en/plugins/manifest-reference#name), such as one that starts with `claude-`. The events and methods can change between releases, so your README is the place to say which Claude Code version you tested with.

365 

366Keep developing against the directory with `--plugin-dir`, not against an installed copy. Claude Code caches an installed plugin by version, so your edits don't reach the installed copy until you raise the version and install again.

367 

368## Next steps

369 

370* [Draw in the interface](/docs/en/plugins/mods/interface): open a pane, draw above the prompt, and add buttons and text fields

371* [React to events](/docs/en/plugins/mods/events): hook tool calls, prompts, and turns

372* [Use the mods API](/docs/en/plugins/mods/api): add commands and tools, call a model, and run work on a timer

373* [Test a mod](/docs/en/plugins/mods/test): stub what Claude Code would answer, and test timers and drawings

374* [Troubleshoot a mod](/docs/en/plugins/mods/troubleshoot): the reasons a mod does nothing, and the debug log

375* [Read the source of built-in mods](/docs/en/plugins/mods/overview#read-the-source-of-built-in-mods): complete plugins, each with its hooks module and tests

plugins/mods/events.md +304 −0 created

Details

1> ## Documentation Index

2> Fetch the complete documentation index at: https://code.claude.com/docs/llms.txt

3> Use this file to discover all available pages before exploring further.

4 

5# React to events with a mod

6 

7> Handle Claude Code events from a mod: observe, rewrite, or answer tool calls, prompts, and turns, filter which events a hook handles, and plan for other mods.

8 

9A hook is an event handler: a function that Claude Code runs when a named event happens. Claude Code fires an event at each point where it's about to act, such as when it runs a tool, submits a prompt, sends a request to the model, or starts or ends a session. Your hook runs before Claude Code acts, so it can observe the event, rewrite it, or answer it in Claude Code's place. You register a hook with [`on(eventName, handler)`](/docs/en/plugins/mods/reference#the-hook-function).

10 

11Build your [first mod](/docs/en/plugins/mods/create) before you start here. For every event and its exact fields, see the [reference](/docs/en/plugins/mods/reference#events) or read [the types for your build](/docs/en/plugins/mods/create#get-the-types-for-your-build).

12 

13## How a hook handles an event

14 

15A hook sits between an event and what Claude Code would do about it, so it can observe the event, rewrite it, or answer it itself. It receives three arguments: the [mods API](/docs/en/plugins/mods/api) as `$`, the event as `e`, and the next handler as `next`. The handlers for an event form a middleware chain. `next(e)` calls the next handler, which is another mod's hook or, at the end of the chain, Claude Code's own behavior, and it resolves to the result. What your hook does with `next` decides which of the three it does.

16 

17### Observe an event

18 

19To observe an event without changing it, do your work and return `next(e)`. This hook logs each tool Claude is about to use:

20 

21```javascript theme={null}

22on('tool.call', async ($, e, next) => {

23 // Runs before the tool does

24 $.ui.log('Claude is about to use ' + e.tool)

25 // Pass the event on unchanged

26 return next(e)

27})

28```

29 

30Before each tool runs, a dim line such as `● my-mod: Claude is about to use Bash` appears in the transcript, where `my-mod` is your plugin's name. The tool runs as it would without the mod.

31 

32To act after the event, `await next(e)`, do your work, and return the result. This hook logs each tool after it has run:

33 

34```javascript theme={null}

35on('tool.call', async ($, e, next) => {

36 // Let the tool run, and wait for its result

37 const result = await next(e)

38 // Runs after the tool does

39 $.ui.log(e.tool + ' finished')

40 // Give the result back unchanged

41 return result

42})

43```

44 

45The line now appears after each tool finishes. Claude reads the same result either way, because the hook returns what `next(e)` resolved to.

46 

47### Rewrite an event

48 

49To change what Claude Code acts on, such as the text of a prompt, call `next` with a modified copy of the event. The event itself is immutable: it's frozen at every depth, and assigning to a field throws. This hook trims each prompt before it's sent:

50 

51```javascript theme={null}

52on('prompt.submit', async ($, e, next) => {

53 // Pass on a copy of the event with its text changed

54 return next({ ...e, text: e.text.trim() })

55})

56```

57 

58Later handlers and Claude Code receive the trimmed prompt and never see the original. You can also change the result: `await next(e)`, then return a copy of the result with a field replaced.

59 

60### Answer an event

61 

62To handle an event yourself, return a result without calling `next`. That short-circuits the chain, so later mods and Claude Code's own behavior don't run. This hook refuses every Bash command:

63 

64```javascript theme={null}

65on('tool.call', { tool: 'Bash' }, async () => {

66 // No call to next, so the command never runs

67 return { deny: 'Bash is turned off in this project. Use the file tools.' }

68})

69```

70 

71When Claude tries a Bash command, the command doesn't run, and Claude reads the `deny` text as the tool's result. Each event has its own result shape, which the [events reference](/docs/en/plugins/mods/reference#events) lists.

72 

73### Filter which events a hook handles

74 

75To run a hook for some events only, pass a filter as the second argument to `on`. Claude Code calls the filter a matcher. It's an object whose fields are compared with the event's, and the hook runs only when every field matches. A field can be a value, an array of allowed values, or a regular expression.

76 

77Each line in this example registers the same function, `hook`, for a narrower set of tool calls:

78 

79```javascript theme={null}

80// A string matches one value: Bash calls only

81on('tool.call', { tool: 'Bash' }, hook)

82// An array matches any value in it: Edit calls and Write calls

83on('tool.call', { tool: ['Edit', 'Write'] }, hook)

84// A regular expression matches by pattern: every tool of one MCP server

85on('tool.call', { tool: /^mcp__github__/ }, hook)

86```

87 

88`hook` runs once for a Bash, Edit, or Write call, and once for a call to a tool whose name starts with `mcp__github__`. A call to any other tool, such as Read, matches none of the three, so `hook` doesn't run for it.

89 

90The event name can be a wildcard. `'classic.*'` matches every [settings hook event](#hook-the-settings-hook-events). `'*'` matches every event except the [telemetry events](/docs/en/plugins/mods/reference#telemetry), which you hook by name or as `'telemetry.*'`.

91 

92Register each event once per matcher. If you call `on` twice for `session.start` with no matcher, the module fails to load with `on("session.start") is registered twice without a matcher`. Put everything your mod does at session start in one hook.

93 

94## Hook what Claude is doing

95 

96Hook these events to see or change a tool call, a prompt, or a turn as it happens. For every event and what a hook can return, see the [events reference](/docs/en/plugins/mods/reference#events).

97 

98### Guard or change a tool call

99 

100A `tool.call` hook sees each tool Claude is about to use, so it can refuse the call, change its arguments, or let it through. `tool.call` fires when Claude Code is about to run a tool, including calls a subagent makes and calls to MCP tools. `e.tool` is the tool's name and the tool's arguments are fields of `e`, such as `e.command` for Bash. When you call `next(e)`, Claude Code runs the permission check and then the tool.

101 

102This hook refuses a Bash command that force-pushes, and tells Claude why:

103 

104```javascript theme={null}

105// The matcher limits the hook to Bash calls, so e.command is the shell command

106on('tool.call', { tool: 'Bash' }, async ($, e, next) => {

107 if (/git push .*--force/.test(e.command)) {

108 // Returning without calling next answers the event, so the command never runs

109 return { deny: 'Force pushes are not allowed in this repository. Push to a new branch instead.' }

110 }

111 // Every other command goes on to the permission check and then to Bash

112 return next(e)

113})

114```

115 

116When Claude tries `git push --force`, the command doesn't run and no permission prompt appears, because the hook never calls `next`. Claude reads the `deny` text as the tool's result, so write it as an instruction Claude can act on. Every other Bash command runs as it would without the mod.

117 

118To act after a tool has run, `await next(e)`, do your work, and return what `next` gave you. This hook logs each `.mdx` file Claude changes, with [`$.ui.log`](/docs/en/plugins/mods/api#show-something-without-starting-a-turn), which adds a dim line to the transcript that Claude doesn't read:

119 

120```javascript theme={null}

121on('tool.call', { tool: ['Edit', 'Write'] }, async ($, e, next) => {

122 // Wait for the permission check and the tool, and keep what they produced

123 const result = await next(e)

124 // A refused call comes back as { deny }, and a failed one has isError set

125 const changed = !result.deny && !result.isError

126 if (changed && e.file_path.endsWith('.mdx')) $.ui.log('Claude changed ' + e.file_path)

127 // Return the result as it came, so Claude reads what the tool returned

128 return result

129})

130```

131 

132After Claude edits or writes an `.mdx` file, a dim line in the transcript names the file. Nothing is logged for another kind of file, or for a call that was refused or failed. Claude's view of the call doesn't change, because the hook returns the result it received.

133 

134To change a call, pass changed arguments to `next`. To retry a call, call `next(e)` again: a hook that sees `isError` on the first result can run the tool a second time and return that result. To answer a call yourself, return an object with a `result` field, such as `{ result: 'Skipped by my-mod' }`, without calling `next`. When you do that, no permission prompt appears and the tool doesn't run, so the result you return is all Claude learns about what happened.

135 

136Hooks in your organization's [managed settings](/docs/en/server-managed-settings) run before any mod's `tool.call` hook, and a block from one of them is final.

137 

138#### Hold a tool call until the user decides

139 

140A hook can pause a tool call and ask the user what to do before it goes ahead. A `tool.call` hook can `await` before it calls `next` or returns, and the tool call stays pending until then. To put the question to the user, call `$.ui.ask`. It shows your question above a numbered list of your options, in the dialog Claude uses to ask you something, and resolves to the label the user picks. After your options, the dialog adds a row for typing a different answer and a **Chat about this** row.

141 

142The `RISKY` pattern in this example matches `rm -r`, `rm -rf`, `git reset --hard`, and `git push` with `--force`, and it misses other spellings such as `git push -f`. This module asks before it runs a Bash command that matches the pattern:

143 

144```javascript theme={null}

145const RISKY = /\brm\s+-rf?\b|\bgit\s+reset\s+--hard\b|\bgit\s+push\b.*--force/

146 

147export function register(on) {

148 on('tool.call', { tool: 'Bash' }, async ($, e, next) => {

149 // Let every other command through without a question

150 if (!RISKY.test(e.command)) return next(e)

151 // Start from the safe answer, so a question nobody answers refuses the command

152 let answer = 'Refuse'

153 try {

154 // The tool call waits here until the user picks one of the two labels

155 answer = await $.ui.ask('Run this command? ' + e.command, ['Run it', 'Refuse'])

156 } catch {

157 // The user dismissed the question, or this is a claude -p run with nobody to ask

158 }

159 if (answer !== 'Run it') {

160 // Answer without calling next, so the command doesn't run

161 return { deny: 'The user declined this command. Ask before trying a different approach.' }

162 }

163 return next(e)

164 })

165}

166```

167 

168When Claude tries a command such as `rm -rf build`, the question appears with the command in it, and the command waits for the answer:

169 

170* **The user picks Run it**: the hook calls `next(e)`, and the usual permission check still runs after it

171* **The user picks Refuse**: the command doesn't run, and Claude reads the `deny` text

172* **The user types an answer**: `$.ui.ask` resolves to the typed text. The hook compares it with `Run it`, so any other text refuses the command.

173* **Nobody answers**: `$.ui.ask` rejects when the user dismisses the question or picks **Chat about this**, and in a `claude -p` run, so the `catch` block leaves the answer at `Refuse`

174 

175Keep the wait inside a mods API call such as `$.ui.ask`, because that time doesn't count against the hook's [10-second time limit](/docs/en/plugins/mods/reference#limits). Time spent awaiting a promise of your own does count. Claude Code skips a hook that times out, so the held command would run.

176 

177### Rewrite or add to a prompt

178 

179A `prompt.submit` hook sees each prompt before the turn starts, so it can rewrite the text or add to it. `e.text` is what was typed.

180 

181| To do this | Return this |

182| :- | :- |

183| Rewrite the prompt. The message in the transcript shows the new text. | `next({ ...e, text: newText })` |

184| Add text only Claude reads, after the prompt | `next({ ...e, context: [...(e.context ?? []), extraText] })` |

185| Stop the prompt from being sent | `{ drop: 'the reason' }` |

186 

187This hook adds the current branch name for Claude whenever a prompt mentions a pull request:

188 

189```javascript theme={null}

190on('prompt.submit', async ($, e, next) => {

191 // Pass on a prompt that doesn't mention a pull request as it is

192 if (!/\bPR\b|pull request/i.test(e.text)) return next(e)

193 const git = await $.process.run(['git', 'branch', '--show-current'])

194 // Outside a git repository the command fails, so there's no branch to add

195 if (git.exitCode !== 0) return next(e)

196 // Keep any context an earlier hook added, and add one more line for Claude

197 return next({ ...e, context: [...(e.context ?? []), 'Current branch: ' + git.stdout.trim()] })

198})

199```

200 

201When you send a prompt such as `open a PR for this change`, your message looks the same in the transcript, and Claude also reads a line such as `Current branch: feature/auth` after it. A prompt that doesn't mention a pull request goes through unchanged, and `git` doesn't run.

202 

203[Other events](/docs/en/plugins/mods/reference#prompts-and-what-claude-reads) cover the rest of what Claude reads: `prompt.section` for each section of the system prompt, `prompt.context` for the context sent with the first message, and `skill.prompt` for a skill's text. Text from these hooks that changes between requests [invalidates the prompt cache](/docs/en/prompt-caching).

204 

205### Follow a turn

206 

207A turn is everything Claude does in answer to one prompt. Hook `turn.start`, `turn.step`, and `turn.complete` to follow one:

208 

209| Event | When it fires | What a hook can do |

210| :- | :- | :- |

211| `turn.start` | A turn begins | Observe. `e.turnId` identifies the turn in the other two events. |

212| `turn.step` | Claude Code is about to send one request to the model. A turn with tool calls has several. `e.agentId` is set for a subagent's request. | Read each request's token usage, send it to a different model with `next({ ...e, model })`, or answer without calling the model |

213| `turn.complete` | The turn ended, including a turn the user interrupted, where `e.isAborted` is `true`. `e.answer` is Claude's final text, `e.durationMs` how long it took, and `e.usage` the turn's token totals. A subagent's turn fires it with `e.agentId` set. | Observe, or return an object with a `text` field, such as `{ text: 'Done in 12 seconds' }`, to show a line under the answer |

214 

215Write a `turn.step` hook as an async generator, because the event streams. `yield* next(e)` forwards the response as it streams and evaluates to the finished result. This hook logs how much of each request the Claude API served from the [prompt cache](/docs/en/prompt-caching):

216 

217```javascript theme={null}

218// function* makes the hook a generator, which can pass the response on piece by piece

219on('turn.step', async function* ($, e, next) {

220 // Send the request, forward each piece as it arrives, and keep the finished result

221 const result = yield* next(e)

222 // Skip a result that reports no token counts

223 if (result.usage) {

224 $.ui.log('cache read ' + result.usage.cache_read_input_tokens + ' · wrote ' + result.usage.cache_creation_input_tokens)

225 }

226 // Return the result unchanged, so the turn continues as usual

227 return result

228})

229```

230 

231Claude's response streams to the screen as it does without the mod. After each request finishes, a dim line in the transcript gives the number of tokens read from the cache and the number written to it. A turn with tool calls has several requests, so it adds several lines.

232 

233`result.usage` holds the four token counts the Claude API reports for a request, plus the `model` that answered: `input_tokens`, `output_tokens`, `cache_read_input_tokens`, and `cache_creation_input_tokens`. The hook runs for subagents' requests too, so check `e.agentId` when you want only the main conversation.

234 

235### Hook the settings hook events

236 

237Settings hooks are the command, HTTP, prompt, and agent hooks you configure in settings files. Each [settings hook event](/docs/en/hooks#hook-events), such as `Stop`, `SessionEnd`, or `PostToolUse`, is also an event named `classic.` followed by the settings hook event's name, such as `classic.Stop`. `e` is the JSON a settings hook receives on stdin, including `transcript_path`.

238 

239This hook uses `Stop`, which fires when Claude finishes responding, to log where the session's transcript is saved:

240 

241```javascript theme={null}

242on('classic.Stop', async ($, e, next) => {

243 // e has the same fields a Stop hook in a settings file reads from stdin

244 $.ui.log('Transcript saved at ' + e.transcript_path)

245 // Pass the event on, so Stop hooks in your settings files still run

246 return next(e)

247})

248```

249 

250Each time Claude finishes responding, a dim line in the transcript gives the path of the transcript file. The hook returns `next(e)`, so it observes the event and changes nothing about how the turn ends.

251 

252## Run alongside other mods

253 

254Several mods can hook the same event, and any one of them can fail. If your mod blocks tool calls, check its position in the chain and what happens when its hook fails.

255 

256### The order mods run in

257 

258Hooks on the same event form one middleware chain. Each mod's `next` calls the following mod's hook, and the last `next` reaches Claude Code's own behavior. The first mod is outermost: it sees the event before the others and the result after them, and it decides whether the others run at all. A later mod can't stop an earlier one from seeing an event.

259 

260Claude Code orders the chain by where each mod comes from:

261 

2621. The built-in guard `sec-default@builtin`, a mod built into Claude Code that `/plugin` lists as `cc-plugin-sec-default`, where [it loads](/docs/en/plugins/mods/admin#know-what-happens-by-default), mods your organization lists in [`prependPlugins`](/docs/en/plugins/mods/admin#install-your-organizations-mods), and then any other mod that counts as your organization's and isn't in `appendPlugins`

2632. Mods you install

2643. Mods your organization lists in `appendPlugins`

2654. Other mods built into Claude Code

266 

267Among the mods you install, a mod runs before the mods it lists under `dependencies` in its manifest. Within one module, hooks run in the order `register` called `on`.

268 

269#### Where settings hooks run in the order

270 

271The `PreToolUse` hooks configured in settings files also run during a tool call, at fixed points in the chain of mods:

272 

273* **`PreToolUse` hooks from managed settings**: run before the first mod's `tool.call` hook, and a block from one of them is final, so no mod sees the call.

274* **`PreToolUse` hooks from every other settings file and from plugins' `hooks/hooks.json`**: run after the last mod calls `next`, as part of Claude Code's own behavior. A mod that answers `tool.call` without calling `next` keeps them from running, and a mod that calls `next` sees their decision in the result it returns.

275 

276[`tool.check`](/docs/en/plugins/mods/reference#tools) is the event where Claude Code decides whether a tool call may run. It fires after those hooks and the permission rules have decided, and `next(e)` resolves to their decision. A hook on `tool.check` can return a different decision, such as `{ decision: 'allow' }`, so it can approve a call that a hook in the second group blocked. [Extend permissions with hooks](/docs/en/permissions#extend-permissions-with-hooks) lists which decisions hold over a mod.

277 

278### Handle a hook that fails

279 

280A hook that fails doesn't break the session, and you can decide what happens instead. When a hook with no `.catch` handler throws, times out, or returns a result of the wrong shape, what happens next depends on whether it had called `next`:

281 

282* **It failed before calling `next`**: Claude Code skips it, and the next handler runs in its place

283* **It failed after `next` resolved**: that result stands, and nothing runs a second time

284 

285One line names the mod, the event, and the reason, such as `my-mod: tool.call hook skipped: threw Error: boom`. Where you read it depends on the session, as [Find out why a mod does nothing](/docs/en/plugins/mods/troubleshoot#find-out-why-a-mod-does-nothing) lists. A `ui.render` hook whose drawing doesn't validate is reported differently, as [Build a tree from elements](/docs/en/plugins/mods/interface#build-a-tree-from-elements) describes.

286 

287To make a hook that blocks calls fail closed, add a `.catch` error handler that answers in its place. Here, `guard` is your hook function:

288 

289```javascript theme={null}

290// on returns a registration, and .catch attaches a handler to that one hook

291on('tool.call', { tool: 'Bash' }, guard).catch(async ($, e, next) => {

292 // next.error.kind is 'throw' or 'timeout', which says how guard failed

293 return { deny: 'The command guard failed, so this command was not run: ' + next.error.kind }

294})

295```

296 

297While `guard` works, the handler never runs. When `guard` throws or times out on a Bash call, Claude Code calls the handler with the same event. The handler returns `{ deny }`, so the command doesn't run, and Claude reads the text with `throw` or `timeout` at the end. Without the handler, Claude Code would skip `guard` and run the command. The handler has [one second](/docs/en/plugins/mods/reference#limits) to answer.

298 

299## Next steps

300 

301* [Use the mods API](/docs/en/plugins/mods/api): add commands and tools, call a model, and run work on a timer

302* [Draw in the interface](/docs/en/plugins/mods/interface): show what your hooks collect in a pane or above the prompt

303* [Test a mod](/docs/en/plugins/mods/test): raise any of these events from a test

304* [Mods reference](/docs/en/plugins/mods/reference): every event, every mods API method, and the limits

plugins/mods/interface.md +826 −0 created

Details

1> ## Documentation Index

2> Fetch the complete documentation index at: https://code.claude.com/docs/llms.txt

3> Use this file to discover all available pages before exploring further.

4 

5# Draw in the interface with a mod

6 

7> Draw panes, a band above the prompt, buttons, and text fields from a Claude Code mod, handle presses and input, and keep state between redraws and sessions.

8 

9A mod can draw its own interface in Claude Code and change parts of the interface Claude Code already draws. Each place a mod can draw is called a [render site](/docs/en/plugins/mods/reference#render-sites), such as a pane, the band above the prompt, or the spinner. Claude Code raises the [`ui.render`](/docs/en/plugins/mods/reference#interface) event each time it's about to draw a render site, and your hook for that event returns what to draw there.

10 

11This map shows where a mod can draw in a terminal session:

12 

13<img src="https://mintcdn.com/claude-code/dgiVO_Od1X1faduV/images/mods-screen-map.svg?fit=max&auto=format&n=dgiVO_Od1X1faduV&q=85&s=5fda26b6609c62b68c6f9e528c1590ea" className="dark:hidden" alt="Map of a Claude Code terminal session. A mod can add a pane as a sidebar on the right, a toast at the top right of the transcript, a log line in the transcript, a band above the prompt, and a status line under the prompt. A mod can redraw messages, tool call rows, and the spinner. The prompt is Claude Code's own." width="600" height="336" data-path="images/mods-screen-map.svg" />

14 

15<img src="https://mintcdn.com/claude-code/dgiVO_Od1X1faduV/images/mods-screen-map-dark.svg?fit=max&auto=format&n=dgiVO_Od1X1faduV&q=85&s=5b4161581a1bd2c0450b0c8b57bc1225" className="hidden dark:block" alt="Map of a Claude Code terminal session. A mod can add a pane as a sidebar on the right, a toast at the top right of the transcript, a log line in the transcript, a band above the prompt, and a status line under the prompt. A mod can redraw messages, tool call rows, and the spinner. The prompt is Claude Code's own." width="600" height="336" data-path="images/mods-screen-map-dark.svg" />

16 

17In a narrower terminal, the pane sits above the prompt instead of beside the transcript.

18 

19Build your [first mod](/docs/en/plugins/mods/create) before you start here. Begin with the worked example, which builds a pane with two tabs and a counter, then read the section for each piece you want to change.

20 

21<Note>

22 To look up one prop or limit, see the [reference](/docs/en/plugins/mods/reference#render-sites).

23</Note>

24 

25## Build a pane with tabs

26 

27In this section you build a mod that adds a `/hello-tabs` command, and the command opens a pane. A pane is a sidebar beside the transcript in a wide fullscreen terminal, or a framed region above the prompt otherwise. This pane shows two tabs, and the second tab has a button that adds one to a counter. The count is still there after you restart Claude Code.

28 

29The finished mod looks like this. The recording opens the pane, switches to the second tab, presses the button a few times, and returns to the first tab:

30 

31<Frame>

32 <video autoPlay muted loop playsInline controls className="w-full dark:hidden" src="https://mintcdn.com/claude-code/dgiVO_Od1X1faduV/images/mods-hello-tabs-light.mp4?fit=max&auto=format&n=dgiVO_Od1X1faduV&q=85&s=49d520094d87b5b44bfe50fa49677f06" aria-label="The /hello-tabs command is typed at the Claude Code prompt and a framed pane opens above it, with '1: One' and '2: Two' across the top and the text 'This is the first tab.' The second tab shows an 'Add one' button beside 'Count: 1', and the count rises to 3. The pane then returns to the first tab." data-path="images/mods-hello-tabs-light.mp4" />

33 

34 <video autoPlay muted loop playsInline controls className="w-full hidden dark:block" src="https://mintcdn.com/claude-code/dgiVO_Od1X1faduV/images/mods-hello-tabs-dark.mp4?fit=max&auto=format&n=dgiVO_Od1X1faduV&q=85&s=ff7a14d713d6e5d3b0000efa8522ea4b" aria-label="The /hello-tabs command is typed at the Claude Code prompt and a framed pane opens above it, with '1: One' and '2: Two' across the top and the text 'This is the first tab.' The second tab shows an 'Add one' button beside 'Count: 1', and the count rises to 3. The pane then returns to the first tab." data-path="images/mods-hello-tabs-dark.mp4" />

35</Frame>

36 

37Claude Code has no built-in tabs element, so the tabs are two buttons in a row. The mod keeps track of which one is active and draws that tab's content under the row.

38 

39<Steps>

40 <Step title="Create the plugin">

41 A mod is a plugin with a manifest, a `hooks.json` that points to your code, and the code file. [Create a mod](/docs/en/plugins/mods/create#write-a-mod-yourself) explains each one. Create a directory named `hello-tabs` with `.claude-plugin` and `hooks` directories inside it, then save the first two files.

42 

43 Save the manifest as `hello-tabs/.claude-plugin/plugin.json`:

44 

45 ```json hello-tabs/.claude-plugin/plugin.json theme={null}

46 {

47 "name": "hello-tabs",

48 "version": "0.1.0",

49 "description": "Opens a pane with two tabs and a counter",

50 "author": { "name": "Your Name" }

51 }

52 ```

53 

54 Name your entry point in `hello-tabs/hooks/hooks.json`:

55 

56 ```json hello-tabs/hooks/hooks.json theme={null}

57 {

58 "modules": ["./register.js"]

59 }

60 ```

61 </Step>

62 

63 <Step title="Write the code">

64 The code does three jobs, one in each hook:

65 

66 * Adds the `/hello-tabs` command

67 * Opens the pane when you run that command

68 * Draws the pane's content: the row of tabs and the open tab's body

69 

70 Two module-level variables, `tab` and `count`, hold the pane's state.

71 

72 Save this as `hello-tabs/hooks/register.js`:

73 

74 ```javascript hello-tabs/hooks/register.js theme={null}

75 // The pane's id, used to open the pane and to recognize it when drawing

76 const PANE = 'hello-tabs'

77 

78 // What the pane shows: which tab is open, and the counter's value

79 let tab = 'one'

80 let count = 0

81 

82 export function register(on) {

83 // Runs before your first prompt, and again after a reload

84 on('session.start', async ($, e, next) => {

85 await $.command.register({ name: 'hello-tabs', description: 'Open the hello-tabs pane' })

86 // Load the count an earlier session saved, if there is one

87 const saved = await $.store.get('count')

88 if (typeof saved === 'number') count = saved

89 return next(e)

90 })

91 

92 // Runs when you type /hello-tabs

93 on('command.run', { command: 'hello-tabs' }, async ($) => {

94 // Open the pane, give it the keyboard, and let Esc close it

95 await $.ui.open({ id: PANE, title: 'Hello tabs', focus: true, closeOnEscape: true })

96 // Print nothing in the transcript

97 return {}

98 })

99 

100 // Runs each time Claude Code draws a pane

101 on('ui.render', { component: 'Pane' }, async ($, e, next) => {

102 // Leave other mods' panes alone

103 if (e.requestId !== PANE) return next(e)

104 // Get the elements this app can draw

105 const { Box, Text, Button } = $.ui.resolve(e)

106 // Ask Claude Code to run this hook again

107 const redraw = () => $.ui.invalidate('ui.render')

108 

109 // One tab: a button that switches to its tab when pressed

110 const tabButton = (name, label, hotkey) =>

111 Button({

112 key: 'tab-' + name,

113 label,

114 hotkey,

115 plain: true,

116 // Dim the tab that isn't open

117 dimColor: tab !== name,

118 onPress: () => {

119 tab = name

120 redraw()

121 },

122 })

123 

124 // What goes under the tabs, depending on which one is open

125 const body =

126 tab === 'one'

127 ? [Text({ children: ['This is the first tab.'] })]

128 : [

129 Box({

130 flexDirection: 'row',

131 columnGap: 2,

132 children: [

133 Button({

134 key: 'more',

135 label: 'Add one',

136 hotkey: 'a',

137 onPress: async () => {

138 count += 1

139 redraw()

140 // Save the count so it's there after a restart

141 await $.store.set('count', count)

142 },

143 }),

144 Text({ children: ['Count: ' + count] }),

145 ],

146 }),

147 ]

148 

149 // The whole pane: the row of tabs, a blank line, then the body

150 return Box({

151 flexDirection: 'column',

152 children: [

153 Box({

154 flexDirection: 'row',

155 columnGap: 3,

156 children: [tabButton('one', 'One', '1'), tabButton('two', 'Two', '2')],

157 }),

158 Text({ children: [' '] }),

159 ...body,

160 ],

161 })

162 })

163 }

164 ```

165 

166 Each hook also does something the code doesn't make plain:

167 

168 * **[`session.start`](/docs/en/plugins/mods/reference#session)** also reads the saved count from [`$.store`](#keep-state), a key-value store that persists between sessions.

169 * **[`command.run`](/docs/en/plugins/mods/api#add-a-command)** only tells Claude Code the pane exists. Opening a pane draws nothing by itself: Claude Code then raises `ui.render` to ask what goes in it.

170 * **`ui.render`** returns the element tree, a `Box` that holds other boxes, text, and buttons, and builds it again from `tab` and `count` each time it runs.

171 

172 Pressing a button runs its `onPress` callback, which changes a variable and calls `redraw`. Claude Code then runs the `ui.render` hook again, and the hook builds a new tree from the new values. Every interactive drawing uses that render cycle: a callback changes state, and the hook renders again from the new state.

173 </Step>

174 

175 <Step title="Open the pane">

176 In your shell, start Claude Code with `claude --plugin-dir ./hello-tabs`. At the Claude Code prompt, run `/hello-tabs`. A pane opens with `1: One` and `2: Two` across the top. Press `2`, then press `a`, the hotkey for **Add one**, a few times. The count rises.

177 </Step>

178 

179 <Step title="Check that the count was saved">

180 Press Esc to close the pane, then exit the session. In your shell, start Claude Code again with the same `claude --plugin-dir ./hello-tabs` command, and at the Claude Code prompt run `/hello-tabs`. The count is where you left it.

181 

182 To clear the count, have the mod call `$.store.delete('count')`. [Keep state](#keep-state) covers how long each kind of value lasts.

183 </Step>

184</Steps>

185 

186## Pick where to draw

187 

188A `ui.render` hook runs for every render site unless you narrow it to the one you want to draw in. To choose the render site, pass a filter, called a [matcher](/docs/en/plugins/mods/events#filter-which-events-a-hook-handles), as the second argument to `on`. `{ component: 'Pane' }` runs the hook only for panes. In the hook, `e.component` names the site, `e.surface` says which app is drawing, and `e.props` holds the site's own data. For a pane, `e.requestId` is the `id` you opened it with.

189 

190Two sites are empty until a mod fills them, the pane and the band. Select a tab to see what each one is and how to draw in it:

191 

192<Tabs>

193 <Tab title="Pane">

194 A pane is a sidebar beside the transcript in a wide fullscreen terminal, or a framed region above the prompt otherwise. With several panes open, each gets a tab that shows its title.

195 

196 A pane appears when your mod calls `$.ui.open` with an `id` you choose, as in `$.ui.open({ id: 'hello-tabs' })`. [Open a pane at the right time](#open-a-pane-at-the-right-time) covers the other fields and when a pane waits for a wider terminal.

197 

198 To draw in your pane, filter on `{ component: 'Pane' }` and check that `e.requestId` is your `id`.

199 </Tab>

200 

201 <Tab title="Band above the prompt">

202 The band is a strip directly above the prompt input. It's always there, and every mod shares it.

203 

204 Your hook returns a tree to show something in the band, or `next(e)` to show nothing. A tree replaces what the mods [after yours](/docs/en/plugins/mods/events#the-order-mods-run-in) draw there. To keep theirs, put the result of `await next(e)` among the children of a [`Box`](#build-a-tree-from-elements) in your tree.

205 

206 To draw in the band, filter on `{ component: 'AbovePrompt' }`.

207 </Tab>

208</Tabs>

209 

210### Change what Claude Code already draws

211 

212Claude Code draws most of its interface itself: messages, tool call rows, the spinner, and more. Each of those parts is a render site too, so a mod can restyle or replace it. To change one, filter your `ui.render` hook on its name from this table:

213 

214| Site | What it is |

215| :- | :- |

216| `UserMessage`, `AssistantMessage` | A message in the transcript |

217| `ToolUse`, `ToolResult`, `ToolGroup` | A tool call's row, its result, and a folded run of calls |

218| `CommandOutput` | The row a command printed |

219| `AskUserQuestion` | The dialog Claude opens to ask you a question |

220| `Spinner`, `ToolProgress`, `TurnDuration` | Status lines for a turn: the line that animates while Claude works, a running tool's live progress line, and the line that closes a turn |

221| `InfoNotice`, `SessionMode`, `PromptHint` | Status lines under the logo, the mode labels in the footer, and the hint line under the prompt |

222 

223At a site Claude Code already draws, your hook has three choices: change a detail, replace the drawing, or leave it alone. Select a tab to see each one applied to the spinner. The examples read a `calls` variable that another hook counts, as in the [tutorial mod](/docs/en/plugins/mods/create#write-a-mod-yourself).

224 

225<Tabs>

226 <Tab title="Change a detail">

227 To keep Claude Code's drawing and change one part of it, pass `next` a copy of the event with changed `props`. This hook changes the text after the spinner's word:

228 

229 ```javascript theme={null}

230 on('ui.render', { component: 'Spinner' }, async ($, e, next) => {

231 // Keep Claude Code's spinner, and change the text after its word

232 return next({ ...e, props: { ...e.props, suffix: ' · tool calls: ' + calls + '…' } })

233 })

234 ```

235 

236 The spinner keeps its animation and its word, and your text follows the word:

237 

238 ```text theme={null}

239 Thinking · tool calls: 2…

240 ```

241 </Tab>

242 

243 <Tab title="Replace the drawing">

244 To draw something of your own in the site's place, return a tree and don't call `next`. This hook draws one line of text where the spinner would be:

245 

246 ```javascript theme={null}

247 on('ui.render', { component: 'Spinner' }, async ($, e) => {

248 const { Text } = $.ui.resolve(e)

249 // No call to next, so this line is drawn in the spinner's place

250 return Text({ children: ['Claude has made ' + calls + ' tool calls'] })

251 })

252 ```

253 

254 While Claude works, your line shows and Claude Code's spinner doesn't:

255 

256 ```text theme={null}

257 Claude has made 2 tool calls

258 ```

259 </Tab>

260 

261 <Tab title="Leave it alone">

262 To leave the site as Claude Code draws it, return `next(e)`. A hook often does that for some events and not others. This hook leaves the spinner alone until there's a call to count:

263 

264 ```javascript theme={null}

265 on('ui.render', { component: 'Spinner' }, async ($, e, next) => {

266 // Nothing to show yet, so pass the event on unchanged

267 if (calls === 0) return next(e)

268 return next({ ...e, props: { ...e.props, suffix: ' · tool calls: ' + calls + '…' } })

269 })

270 ```

271 

272 Before the first tool call, the spinner looks the way it does without the mod:

273 

274 ```text theme={null}

275 Thinking…

276 ```

277 </Tab>

278</Tabs>

279 

280The permission prompt isn't a render site, so a mod can't change what it shows. The question dialog, `AskUserQuestion`, is one, so a mod can change that.

281 

282The terminal and the Desktop app don't raise all the same sites. `Pane`, `AbovePrompt`, `Spinner`, and the transcript sites work in both. A few other status lines are raised in the terminal only. The [render sites table](/docs/en/plugins/mods/reference#render-sites) lists where each one is raised.

283 

284### Open a pane at the right time

285 

286A pane appears only when your mod opens it. How and when you open it decides whether it takes keyboard focus, how much room it asks for, and whether it shows at all in a narrow terminal.

287 

288To open a pane, call [`$.ui.open`](/docs/en/plugins/mods/reference#mods-api-methods) with an `id` you choose. The `id` is the pane's name: your `ui.render` hook checks for it, and you pass it again to close the pane.

289 

290```javascript theme={null}

291await $.ui.open({ id: 'hello-tabs', title: 'Hello tabs', focus: true })

292```

293 

294To close the pane, call `$.ui.close` with the `id` you opened it with:

295 

296```javascript theme={null}

297await $.ui.close({ id: 'hello-tabs' })

298```

299 

300Besides `id`, `$.ui.open` takes these optional fields:

301 

302| Field | What it does |

303| :- | :- |

304| `title` | The pane's tab label when more than one pane is open |

305| `focus` | Requests [keyboard focus](#know-which-keys-your-mod-can-receive) |

306| `closeOnEscape` | Makes Esc close the pane |

307| `holdToasts` | Holds toasts, the small notices from [`$.ui.toast`](/docs/en/plugins/mods/api#show-something-without-starting-a-turn), until the pane closes |

308| `rows` | The height to ask for when the pane sits above the prompt. The default is a third of the space. |

309| `columns` | The width to ask for when the pane sits beside the transcript |

310 

311`focus`, `closeOnEscape`, and `holdToasts` are optional and accept only `true`. To leave one off, omit it. Passing `false` throws an error such as `ui.open: focus is true or left out`. To set one of them conditionally, add the field only when the condition holds. This call asks for keyboard focus only when `items` isn't empty:

312 

313```javascript theme={null}

314const pane = { id: 'hello-tabs', title: 'Hello tabs' }

315await $.ui.open(items.length > 0 ? { ...pane, focus: true } : pane)

316```

317 

318To let a command open the pane while Claude is working, add `immediate: true` when you [register the command](/docs/en/plugins/mods/api#add-a-command). Without it, a command typed during a turn waits for the turn to end.

319 

320#### When a pane waits for a wider terminal

321 

322A pane your mod opens without being asked doesn't appear in a narrow terminal, so it can't take over a small screen. Whether it appears depends on what opened it:

323 

324* **Opened by something the user did**, such as a command they ran or a button they pressed, the pane appears at any width

325* **Opened by your mod acting by itself**, such as from a timer or a [`turn.start`](/docs/en/plugins/mods/events#follow-a-turn) hook, the pane appears only in a terminal at least 144 columns wide. After the user has opened that pane once themselves, 110 columns is enough.

326 

327When the pane appears, `$.ui.open` resolves to `{ isPlaced: true }`. When the pane is waiting, `isPlaced` is `false` and `reason` is a string that says why. A waiting pane appears when the user opens it or widens the terminal. To say something is available without opening a pane, call `$.ui.toast('Your message')`, which shows a small notice that disappears after a few seconds.

328 

329## Build a tree from elements

330 

331What a `ui.render` hook returns is an element tree: a description of what to draw, made of boxes, text, and controls nested inside each other. You describe the drawing, and Claude Code renders it in the terminal or the Desktop app.

332 

333To get the elements, call `$.ui.resolve(e)` in your hook, as in `const { Box, Text, Button } = $.ui.resolve(e)`. Each element is a function. You pass it props, and you put the elements and strings that go inside it in `children`.

334 

335Most drawings use four elements. Select a tab to see each one and how the terminal draws it:

336 

337<Tabs>

338 <Tab title="Text">

339 `Text` draws a string, with optional styling such as `bold` and `color`:

340 

341 ```javascript theme={null}

342 Text({ children: ['This is the first tab.'] })

343 ```

344 

345 ```text theme={null}

346 This is the first tab.

347 ```

348 </Tab>

349 

350 <Tab title="Box">

351 `Box` arranges what's inside it, in a row or a column. This one puts a button and a line of text side by side, two columns apart:

352 

353 ```javascript theme={null}

354 Box({

355 flexDirection: 'row',

356 columnGap: 2,

357 children: [

358 Button({ key: 'more', label: 'Add one', onPress: addOne }),

359 Text({ children: ['Count: 0'] }),

360 ],

361 })

362 ```

363 

364 ```text theme={null}

365 [ Add one ] Count: 0

366 ```

367 </Tab>

368 

369 <Tab title="Button">

370 `Button` is a control the user can press. It runs your `onPress` callback. With `plain: true` it has no brackets and shows its hotkey:

371 

372 ```javascript theme={null}

373 Button({ key: 'more', label: 'Add one', onPress: addOne })

374 Button({ key: 'tab-one', label: 'One', hotkey: '1', plain: true, onPress: showTabOne })

375 ```

376 

377 ```text theme={null}

378 [ Add one ]

379 1: One

380 ```

381 </Tab>

382 

383 <Tab title="Input">

384 `Input` is a text field. It runs your `onSubmit` callback with the text when the user presses Enter:

385 

386 ```javascript theme={null}

387 Input({

388 key: 'new-note',

389 label: 'Note',

390 placeholder: 'Type a note and press Enter',

391 value: '',

392 submitLabel: 'add',

393 onSubmit: addNote,

394 })

395 ```

396 

397 ```text theme={null}

398 Note: Type a note and press Enter ⏎ add

399 ```

400 </Tab>

401</Tabs>

402 

403This table lists every element:

404 

405| Element | What it draws | Where |

406| :- | :- | :- |

407| `Box` | A flex container. Takes layout props such as `flexDirection`, `columnGap`, `padding`, `borderStyle`, and `width`. | Everywhere |

408| `Text` | Styled text. Takes `color`, `bold`, `dimColor`, `italic`, and `wrap`. A `color` is a theme key or a color such as `'red'`. A `wrap` is `'wrap'`, `'truncate'`, `'truncate-start'`, `'truncate-middle'`, or `'truncate-end'`. | Everywhere |

409| `Button` | A control that calls `onPress` | Everywhere |

410| `Link`, `Code`, `Markdown` | A link with `href` and an optional `label`, a code block, and text formatted the way Claude's replies are. `Markdown` takes its content in a `text` prop, not in `children`, and needs a `key` when you pass `onLinkPress`. | Everywhere |

411| `Input`, `Select` | A text field and a picker | Terminal, Desktop |

412| `Svg` | An SVG document | Desktop |

413| `Client` | A region drawn by a second file of yours, for animation and pointer input. That file gets no mods API. It reaches your hooks only by posting data, which arrives as a `ui.message` event. | Terminal, Desktop |

414| `Raster`, `Image` | A [grid of colored cells](#draw-a-grid-of-colored-cells), and a picture | Terminal |

415 

416If your module is a `.tsx` or `.jsx` file, you can write the tree as JSX. Destructure the elements from `$.ui.resolve(e)` first, because a hooks module has no element globals.

417 

418If a tree uses an element the app doesn't have, a prop an element doesn't take, or a child where none goes, Claude Code draws its own version of the site.

419 

420In a session started with `--plugin-dir`, a transcript line says so, such as `ui.render (Pane) refused: Text prop "bogusProp" is not allowed; the engine drew its own`. The [debug log](/docs/en/plugins/mods/troubleshoot#read-the-debug-log) records it as `ui.render (Pane): a hook returned a tree that does not validate` with the same reason. Nothing else appears in the session, so when a drawing doesn't show up, check that line or the log.

421 

422### Draw a grid of colored cells

423 

424For a heat map, a sparkline, or a game board in the terminal, draw one `Raster` and not a `Box` for each cell. A `Raster` takes a `key`, its size in `columns` and `rows`, and `cells`, which packs every cell into one string. Each cell is three numbers: the character's code point, its color, and its background color. A color is a hexadecimal number with two digits each for red, green, and blue, such as `0xc62828` for a red, or `0x01000000` for the terminal's default.

425 

426The Desktop app has no `Raster`, so check `e.surface` and draw text there. This pane body draws a three by two heat map:

427 

428```javascript theme={null}

429// The value that means "use the terminal's default color"

430const DEFAULT_COLOR = 0x01000000

431 

432// Pack rows of [character, color] pairs into the one string a Raster takes

433// One cell is three numbers: the character's code point, its color, and its background

434function cellsOf(rows) {

435 const numbers = rows.flat().flatMap(([char, color]) => [char.codePointAt(0), color, DEFAULT_COLOR])

436 return new Uint8Array(Uint32Array.from(numbers).buffer).toBase64()

437}

438 

439on('ui.render', { component: 'Pane' }, async ($, e, next) => {

440 // Draw only in the pane opened with the id 'heat'

441 if (e.requestId !== 'heat') return next(e)

442 const { Box, Text, Raster } = $.ui.resolve(e)

443 // Two rows of three cells, each a block character and its color

444 const rows = [

445 [['█', 0x2e7d32], ['█', 0xf9a825], ['█', 0xc62828]],

446 [['█', 0x2e7d32], ['█', 0x2e7d32], ['█', 0xf9a825]],

447 ]

448 if (e.surface !== 'terminal') {

449 return Text({ children: ['The heat map needs the terminal.'] })

450 }

451 return Box({

452 flexDirection: 'column',

453 children: [Raster({ key: 'grid', columns: 3, rows: 2, cells: cellsOf(rows) })],

454 })

455})

456```

457 

458In the terminal, the pane shows the grid:

459 

460<img src="https://mintcdn.com/claude-code/dgiVO_Od1X1faduV/images/mods-heat-map.svg?fit=max&auto=format&n=dgiVO_Od1X1faduV&q=85&s=b91bcce3bad74bc851149133d4acc5d5" alt="A pane in the terminal that holds a small grid of colored blocks, two rows of three. The top row is green, amber, and red. The bottom row is green, green, and amber." width="360" height="132" data-path="images/mods-heat-map.svg" />

461 

462The `rows` array is the part you'd change, and `cellsOf` turns it into the packed string. The hook draws only in a pane whose `id` is `heat`, so open one with `$.ui.open({ id: 'heat' })` from a command, as the [`hello-tabs` example](#build-a-pane-with-tabs) opens its pane.

463 

464Each character has to be one cell wide. To animate a `Raster` that's already on screen, call `$.ui.blit` with the pane's `id` as `requestId`, the `Raster`'s `key`, the same size, and new cells. For this example, that's `$.ui.blit({ requestId: 'heat', key: 'grid', columns: 3, rows: 2, cells: cellsOf(newRows) })`. It repaints that one element without running your `ui.render` hook again.

465 

466## Respond to presses and typing

467 

468When the user presses a button, types into a field, or picks from a list your mod drew, Claude Code calls the function you gave that control, and it runs in your module. Each control takes its own callbacks:

469 

470* **`Button`**: takes `onPress(e)`, where `e.surface` is the app the press came from

471* **`Input`**: takes `onSubmit(value)` and `onInput(value)`

472* **`Select`**: takes `onSelect(value)` with its choices in `options`, a list of at least one choice with unique values, such as `[{ value: 'sm', label: 'Small' }, { value: 'lg', label: 'Large' }]`

473 

474A test presses or types into a control by its `key`, so give each control one. Each use of a control also fires [`ui.press`, `ui.input`, or `ui.select`](/docs/en/plugins/mods/reference#interface) with the `key` in `e.element`, and another mod can hook those events. Its hook runs before your callback, so it sees what the user types into your `Input` and can change it or answer in place of your callback. The mods API has no method that presses another mod's button.

475 

476<h3 id="know-which-keys-your-mod-can-receive">

477 Keyboard focus and hotkeys

478</h3>

479 

480Your mod never reads the keyboard itself. The user presses a key, Claude Code decides which of your controls it's for, and that control's callback runs. Apart from a [digit hotkey on the band](/docs/en/plugins/mods/reference#elements), that happens only while your pane or band has keyboard focus. The rest of the time, keys go to the prompt.

481 

482#### How a pane gets keyboard focus

483 

484A pane gets keyboard focus in one of three ways:

485 

486* Your mod opens it with `focus: true` from a command or a press

487* The user presses Ctrl+X then Tab

488* The user clicks it

489 

490Claude Code grants `focus: true` only while the prompt is empty and nothing else has keyboard focus. A pane that opens while the user is typing doesn't take their keystrokes.

491 

492#### What each key does

493 

494This table lists what a key does while your pane or band has keyboard focus:

495 

496| Key | What it does |

497| :- | :- |

498| Tab | Moves to the next control |

499| Up and Down | Move between controls while your drawing fits. When the pane or band has more rows than it can show, they scroll it. |

500| Enter | Presses the focused `Button`, submits the focused `Input`, or picks in a `Select` |

501| A button's hotkey | Presses that button. While an `Input` has the focus, every printable key goes to the field. |

502| Esc | Returns keyboard focus to the prompt. With `closeOnEscape: true`, it also closes the pane. |

503 

504A mod can't bind Tab or the arrow keys to anything else, so a game steers with `w`, `a`, `s`, and `d`.

505 

506#### Set a hotkey and the first focus

507 

508Two props on a control decide how the keyboard reaches it:

509 

510* **`hotkey`**: to let the user press a `Button` with one key, give it a `hotkey` of one digit or one lowercase letter, as in `hotkey: 'a'`

511* **`autoFocus`**: to choose which control has the focus when the pane opens, add `autoFocus: true` to it. Leave the prop off the others, because Claude Code refuses `autoFocus: false`.

512 

513How a hotkey shows depends on the button and the app:

514 

515| Button | In the terminal | In the Desktop app |

516| :- | :- | :- |

517| With brackets, the default | `[ Add one ]`, with no hotkey shown | The label with a small key beside it |

518| With `plain: true` | `1: One` | The label with a small key beside it |

519 

520In the terminal, name the key in a bracketed button's label, or use `plain: true`, so the user can see what to press. The [elements reference](/docs/en/plugins/mods/reference#elements) has the other `Button` rules: `action`, digit hotkeys on the band, and two buttons on one hotkey.

521 

522### Take typed input and draw a row for each item

523 

524Many panes are a text field with a list under it. The example in this section is a notes pane: you type a note and press Enter to add it, and each note has an `x` button that deletes it. With two notes added, the terminal draws the pane this way:

525 

526```text theme={null}

527╭──────────────────────────────────────────────────────────╮

528│ Note: Type a note and press Enter ⏎ add ✕ │

529│ x buy milk │

530│ x call bob │

531╰──────────────────────────────────────────────────────────╯

532```

533 

534The example uses two techniques:

535 

536* **Take typed input**: an `Input` calls `onSubmit(value)` with the field's text when the user presses Enter, and `onInput(value)` on every change

537* **Draw a list**: map your data to one row each, and give every row's button its own `key`

538 

539This hook draws the pane's content:

540 

541```javascript theme={null}

542// The list the pane draws

543let notes = []

544 

545on('ui.render', { component: 'Pane' }, async ($, e, next) => {

546 // Draw only in the pane opened with the id 'notes'

547 if (e.requestId !== 'notes') return next(e)

548 const { Box, Text, Button, Input } = $.ui.resolve(e)

549 const redraw = () => $.ui.invalidate('ui.render')

550 

551 return Box({

552 flexDirection: 'column',

553 children: [

554 Input({

555 key: 'new-note',

556 label: 'Note',

557 placeholder: 'Type a note and press Enter',

558 // Draw the field empty each time, which clears it after a submit

559 value: '',

560 submitLabel: 'add',

561 autoFocus: true,

562 // Runs when you press Enter in the field

563 onSubmit: async (value) => {

564 // Ignore an empty line

565 if (!value.trim()) return

566 notes = [...notes, value.trim()]

567 redraw()

568 await $.store.set('notes', notes)

569 },

570 }),

571 // One row for each note: a delete button, then the note's text

572 ...notes.map((note, i) =>

573 Box({

574 flexDirection: 'row',

575 columnGap: 1,

576 children: [

577 Button({

578 // A key of its own, so each row's button can be told apart

579 key: 'delete-' + i,

580 label: 'x',

581 plain: true,

582 onPress: async () => {

583 notes = notes.filter((_, j) => j !== i)

584 redraw()

585 await $.store.set('notes', notes)

586 },

587 }),

588 Text({ children: [note] }),

589 ],

590 }),

591 ),

592 ],

593 })

594})

595```

596 

597To try the pane:

598 

599* **Add a note**: type a line and press Enter. The line appears as a new row, and the field empties.

600* **Delete a note**: press Tab until the note's `x` button has the focus, then press Enter. The `x` is the button's label and not a hotkey, so typing the letter doesn't press it.

601 

602Each change follows the same render cycle as `hello-tabs`: the callback changes `notes`, calls `redraw`, and saves the list to `$.store`.

603 

604The field empties after each submit because of its `value` prop. `value` is the text the field holds when it's drawn, and the user's typing replaces it until your hook draws the field again. The example always draws the field with `''`.

605 

606The example saves the notes and doesn't load them. To bring them back in the next session, read them in a `session.start` hook, the way `hello-tabs` reads `count`.

607 

608Three props make up the field's line, `Note: Type a note and press Enter ⏎ add`:

609 

610| Prop | In the example | What it is |

611| :- | :- | :- |

612| `label` | `Note` | The text before the field. The terminal draws `: ` after it. |

613| `placeholder` | `Type a note and press Enter` | Dim text that shows while the field is empty |

614| `submitLabel` | `add` | The word after `⏎` that says what Enter does |

615 

616Submitting an `Input` doesn't start a turn unless your callback calls [`$.prompt.submit`](/docs/en/plugins/mods/api#start-a-turn-from-a-background-job).

617 

618<h2 id="redraw-when-something-changes">

619 Redraw a site

620</h2>

621 

622A drawing is a snapshot: it shows what your `ui.render` hook returned the last time the hook ran. To show something new, the hook has to run again. Claude Code runs it again for some changes, and your mod asks for the rest.

623 

624### When Claude Code redraws without being asked

625 

626Claude Code runs your `ui.render` hook again when the site's props change or the terminal's width changes. It doesn't run the hook on a timer, and it can't tell when a variable in your module changes.

627 

628### Redraw when your data changes

629 

630To have your sites drawn again after your own data changes, call `$.ui.invalidate('ui.render')`. This pane counts presses. The button's callback changes `count`, then asks for a redraw:

631 

632```javascript theme={null}

633let count = 0

634 

635on('ui.render', { component: 'Pane' }, async ($, e, next) => {

636 if (e.requestId !== 'counter') return next(e)

637 const { Box, Text, Button } = $.ui.resolve(e)

638 return Box({

639 flexDirection: 'row',

640 columnGap: 2,

641 children: [

642 Button({

643 key: 'more',

644 label: 'Add one',

645 onPress: () => {

646 count += 1

647 // The data changed, so ask Claude Code to draw the pane again

648 $.ui.invalidate('ui.render')

649 },

650 }),

651 Text({ children: ['Count: ' + count] }),

652 ],

653 })

654})

655```

656 

657Each press raises the number in the pane. The [`hello-tabs` example](#build-a-pane-with-tabs) wraps the same call in its `redraw` function.

658 

659A value you keep in [`$.state`](#keep-a-value-in-\$-state) doesn't need the call, because writing the value redraws the sites that read it.

660 

661### Redraw on a timer

662 

663To keep a clock, a countdown, or a value from outside the session current, redraw on a schedule. Start a timer in the module's `session.start` hook. If the module already has one, as `hello-tabs` does, add the [`$.clock.every`](/docs/en/plugins/mods/api#run-work-in-the-background) line to it:

664 

665```javascript theme={null}

666on('session.start', async ($, e, next) => {

667 // Every 1000 milliseconds, ask Claude Code to draw your sites again

668 $.clock.every(1000, () => $.ui.invalidate('ui.render'))

669 return next(e)

670})

671```

672 

673Claude Code now runs your `ui.render` hook once a second. The timer stops when the module reloads, and the new copy of the module starts its own.

674 

675### How often a site can redraw

676 

677Claude Code limits how often it redraws a site, so your mod can call `$.ui.invalidate` as often as its data changes. The visible pane and the band have a higher limit than other sites, and the [limits table](/docs/en/plugins/mods/reference#limits) has the numbers.

678 

679Calls that come faster than the limit are combined into one redraw. That redraw runs your hook once, and the hook reads your data as it is at that moment, so the latest value shows and the values in between don't. An animation can't run faster than the limit.

680 

681## Keep state

682 

683A mod has three places to keep a value, and they differ in how long the value lasts: until the module reloads, until the session ends, or from one session to the next. Choose by how long the value has to last:

684 

685| Keep it in | It lasts until | Use it for |

686| :- | :- | :- |

687| A module-level variable | The module reloads, which happens every time you save a file during development | Values you can lose, as `tab` is in `hello-tabs` |

688| `$.state` | The session ends, or the user runs `/clear`, `/resume`, or `/branch` | Values a drawing depends on that should survive a reload |

689| `$.store` | Your mod deletes it, or no session reads or writes the store for [`cleanupPeriodDays`](/docs/en/settings-reference#cleanupperioddays). The store is a key-value store, saved as a JSON file of your plugin's own under `~/.claude/plugins/store/`. | Settings, history, anything the user expects to find next time |

690 

691`$.store.get(key)` resolves to the value or `undefined`, and `$.store.set(key, value)` takes any JSON value.

692 

693### Keep a value in `$.state`

694 

695`$.state` holds values for the length of a session, and it redraws for you. It's reactive state: a `ui.render` hook that reads a value subscribes to it, so Claude Code redraws that site each time you write the value, and you don't call `$.ui.invalidate`. A value in `$.state` also survives a reload of the module, which a variable doesn't.

696 

697To set it up, declare your values, point your manifest at the declaration, then define and use each value. The examples move the `count` from `hello-tabs` into `$.state`.

698 

699#### Declare the values

700 

701Declare the values in a types file. The outer key is your plugin's name, and each entry under it is a value and its type. Save this as `hello-tabs/types/index.d.ts`:

702 

703```typescript hello-tabs/types/index.d.ts theme={null}

704declare module 'claude-code' {

705 interface PluginState {

706 'hello-tabs': {

707 tab: 'one' | 'two'

708 count: number

709 }

710 }

711}

712```

713 

714#### Point the manifest at the declaration

715 

716To let `claude plugin validate` check your code against that file, add a `types` field to the manifest with its path:

717 

718```json hello-tabs/.claude-plugin/plugin.json theme={null}

719{

720 "name": "hello-tabs",

721 "version": "0.1.0",

722 "description": "Opens a pane with two tabs and a counter",

723 "author": { "name": "Your Name" },

724 "types": "./types/index.d.ts"

725}

726```

727 

728#### Define, read, and write a value

729 

730In your module, define each value with a default, read it while drawing, and write it from a callback. `atom` names a value and its default, `read` returns it, and `update` writes it. The three helpers call `$.state.get` and `$.state.set` for you:

731 

732```javascript theme={null}

733import { atom, read, update } from 'claude-code'

734 

735// At the top of the module: name the value and give its default

736const count = atom({ plugin: 'hello-tabs', key: 'count' }, 0)

737 

738// In the ui.render hook: read the value to draw it

739const n = await read($, count)

740 

741// In a Button: write a new value from the old one

742onPress: () => update($, count, (value) => value + 1)

743```

744 

745Because the `ui.render` hook read `count`, Claude Code runs the hook again each time the button writes it.

746 

747Three rules apply to the code:

748 

749* **Write `plugin` and `key` as literal strings**: `claude plugin validate` reads them from your source

750* **Declare every value in the types file**: otherwise validation fails with `hello-tabs.count is not declared`

751* **Write from a callback or another event's hook**: a `ui.render` hook can read state and can't write it, so write from `onPress`, `onSubmit`, or a hook for another event

752 

753#### Change `hello-tabs` to use `$.state`

754 

755To move `count` in `hello-tabs` into `$.state`, change every line that uses it:

756 

757* **At the top of the module**: add the `import` line, and replace `let count = 0` with the `atom` line

758* **In the `ui.render` hook**: add the `read` line before `tabButton`, and draw `'Count: ' + n` in the `Text`

759* **In the Add one button**: replace `onPress` with the one in [Save from more than one session](#save-from-more-than-one-session), which saves the count as well as writing it

760* **In the `session.start` hook**: replace the two lines that read `saved` with the `loadCount` call from [Load a saved value again after `/clear`](#load-a-saved-value-again-after-clear)

761 

762Keep `redraw` for the tab buttons, because `tab` is still a variable.

763 

764<h3 id="load-a-saved-value-again-after-clear">

765 Load a saved value again after `/clear`

766</h3>

767 

768If your mod copies a saved value from `$.store` into `$.state` at `session.start`, it has to copy it again after `/clear`, `/resume`, or `/branch`. Those commands put every `$.state` value back to its default, and `session.start` doesn't fire again. [`classic.SessionStart`](/docs/en/plugins/mods/events#hook-the-settings-hook-events) does fire after each of them, with `e.source` set to `clear`, `resume`, or `fork`, so copy the value again in a hook on it. Otherwise your drawing shows the default, and a callback that saves the `$.state` value writes the default over what you stored.

769 

770This code loads `count` from both hooks. It builds on the `$.state` version of `hello-tabs`, where `count` is an atom and `update` is imported. Put `loadCount` above `register`, and add the `loadCount` call to the `session.start` hook you already have. `classic.SessionStart` also fires at startup and after compaction, which doesn't reset `$.state`, so the filter on `source` keeps the hook to the three resets:

771 

772```javascript theme={null}

773// Copy the saved count from $.store into $.state, or 0 if nothing is saved

774async function loadCount($) {

775 const saved = Number((await $.store.get('count')) ?? 0)

776 await update($, count, () => saved)

777}

778 

779// Runs before your first prompt, and again after a reload

780on('session.start', async ($, e, next) => {

781 await loadCount($)

782 return next(e)

783})

784 

785// Runs again after /clear, /resume, and /branch, which reports fork

786on('classic.SessionStart', { source: ['clear', 'resume', 'fork'] }, async ($, e, next) => {

787 await loadCount($)

788 return next(e)

789})

790```

791 

792With both hooks in place, the pane shows the saved count after `/clear` and not `0`, and the next press of **Add one** adds to the saved count.

793 

794`loadCount` writes the stored value over the one in `$.state`, and `session.start` fires again each time the module reloads. To keep the store from falling behind, save on every change, as the **Add one** button does.

795 

796To check the reload without a session, [test the drawing after `/clear`](/docs/en/plugins/mods/test#test-a-drawing-after-clear).

797 

798### Save from more than one session

799 

800Every session on your machine that runs your mod shares one `$.store`. A `get` followed by a `set` isn't atomic. When two sessions each read a value, change it, and write it back, they race, and the second write replaces the first.

801 

802Two choices make that less likely:

803 

804* **Give each item its own key**: a `set` changes only its own key, so sessions that write different keys don't overwrite each other

805* **Read again right before you write**: for a value that several sessions change, `get` the key in the callback and build the new value from that, not from a copy you loaded at `session.start`. Another session's write is still lost if it lands between your `get` and your `set`.

806 

807This button adds one to whatever the store holds now, then updates the drawing:

808 

809```javascript theme={null}

810onPress: async () => {

811 // Read what the store holds now, which another session may have changed

812 const saved = Number((await $.store.get('count')) ?? 0)

813 // Save the new count, then show it

814 await $.store.set('count', saved + 1)

815 await update($, count, () => saved + 1)

816}

817```

818 

819If a second session has pressed its own button three times since this session started, this press shows and saves a count that includes those three.

820 

821## Next steps

822 

823* [React to events](/docs/en/plugins/mods/events): feed your drawing from tool calls and turns

824* [Use the mods API](/docs/en/plugins/mods/api): feed your drawing from timers and model calls

825* [Test a drawing](/docs/en/plugins/mods/test#test-a-drawing): press your buttons from a test, on more than one surface

826* [Render sites](/docs/en/plugins/mods/reference#render-sites) and [elements](/docs/en/plugins/mods/reference#elements): each site's props and each element's props

plugins/mods/overview.md +237 −0 created

Details

1> ## Documentation Index

2> Fetch the complete documentation index at: https://code.claude.com/docs/llms.txt

3> Use this file to discover all available pages before exploring further.

4 

5# Mods overview

6 

7> Add panes, commands, and tool call rules to Claude Code with a mod. See what a mod can do, how to make or install one, and where mods run.

8 

9A mod is a [plugin](/docs/en/plugins/overview) that changes how Claude Code looks and behaves. It's made of JavaScript or TypeScript event handlers: Claude Code calls one when an event happens, such as a tool call, a submitted prompt, or a part of the interface being drawn, and the handler can watch the event, change it, or take it over. Use a mod to add a feature of your own to Claude Code, such as a pane that charts how full your context is after each request. For the files in a mod and a complete example, see [How a mod works](#how-a-mod-works).

10 

11<Note>

12 Claude Code's existing [hooks](/docs/en/hooks) also run on events, as a shell command, HTTP request, or prompt you configure in a settings file. A mod's handlers are functions that run inside Claude Code instead. Claude Code calls both kinds hooks: on these pages, "hook" means a mod's handler, and the settings-file kind is a "settings hook".

13</Note>

14 

15## What a mod can do

16 

17Settings hooks, skills, status lines, and MCP servers work from outside Claude Code: each one runs a script, or gives Claude text or tools. A mod runs inside Claude Code, so it can do things they can't:

18 

19* **Draw an interface you can use**: a pane beside the transcript or a band above the prompt, with tabs, buttons, and text fields. See [Draw in the interface](/docs/en/plugins/mods/interface).

20* **Redraw Claude Code's own interface**: replace or restyle parts Claude Code draws itself, such as a tool call's row, the spinner, or the dialog Claude asks questions in. See [Change what Claude Code already draws](/docs/en/plugins/mods/interface#change-what-claude-code-already-draws).

21* **Step into a tool call or a request**: for example, hold a tool call while you ask the user a question, answer it without running the tool, or send one request to a different model. See [Guard or change a tool call](/docs/en/plugins/mods/events#guard-or-change-a-tool-call) and [Follow a turn](/docs/en/plugins/mods/events#follow-a-turn).

22* **Run your own code on a command**: a `/command` that runs your function at once, with no Claude turn, even while Claude is working. See [Add a command or a tool](/docs/en/plugins/mods/api#add-a-command-or-a-tool).

23* **Share data between hooks**: a mod's hooks share the variables in its file, so what one hook records, another can show. For example, one hook can count tool calls while another shows the count beside the spinner, or one can read each request's token usage while another charts it in a pane. See [React to events](/docs/en/plugins/mods/events).

24 

25Mods work in the Claude Code CLI and in the Code tab of the Claude Desktop app. See [Where mods run](#where-mods-run) to understand how they behave elsewhere, such as in the VS Code extension, `claude -p`, and cloud sessions. If a settings hook, a skill, or an MCP server already does what you need, [compare them](#compare-mods-settings-hooks-skills-and-mcp-servers) before you write a mod. To manage mods for an organization, see [Manage mods for your organization](/docs/en/plugins/mods/admin).

26 

27## Get a mod

28 

29You can start with a mod in one of three ways:

30 

31* **Use one you already have**: some of Claude Code's own features are mods, such as `/diff`. See [Mods built into Claude Code](#mods-built-into-claude-code).

32* **Make one**: describe what you want in a Claude Code session, and Claude writes the mod. See [Ask Claude for a mod](/docs/en/plugins/mods/create#ask-claude-for-a-mod). To learn how a mod's code works, [write one yourself](/docs/en/plugins/mods/create#write-a-mod-yourself).

33* **Install one**: see [Install or update a mod](#install-or-update-a-mod)

34 

35### Install or update a mod

36 

37<Warning>

38 A mod is code that runs with your permissions. It can read and write your files, start processes, and make network requests. Install mods only from authors and marketplaces you trust. See [Decide whether to trust a mod](#decide-whether-to-trust-a-mod).

39</Warning>

40 

41A mod installs as a plugin, from a marketplace. Give the plugin's name, an `@`, and the marketplace's name. These examples install a plugin named `token-chart` from a marketplace named `your-org`:

42 

43* In a Claude Code session, run `/plugin install token-chart@your-org`.

44* In your shell, run `claude plugin install token-chart@your-org`.

45 

46[Install plugins](/docs/en/plugins/install) covers marketplaces, scopes, the VS Code extension and the Desktop app, and [keeping plugins updated](/docs/en/plugins/install#keep-plugins-updated), all of which apply to a plugin that contains a mod without changes.

47 

48If you install or update a mod from your shell while a session is open, run `/reload-plugins` in that session to load it. Otherwise it loads the next time you start Claude Code.

49 

50## Decide whether to trust a mod

51 

52A mod is code that runs with your permissions, inside Claude Code. Install mods only from authors and [marketplaces you trust](/docs/en/plugins/security).

53 

54### What a mod can reach

55 

56A mod runs with your permissions, so before you install one, know what it has access to. Once it loads, a mod can:

57 

58* **Act on your machine as you**: read and write files anywhere your user account can, start programs, and make network requests

59* **Read your secrets**: environment variables and settings files, including an API key you keep in either

60* **See your session**: every prompt you send and every tool call Claude makes

61* **Change your session**: rewrite a prompt or a tool call, submit a prompt as if you had typed it, or send a message to another of your sessions

62* **Act without asking you**: approve a tool call before you're asked

63* **Spend your usage**: call a model on your plan or API key

64 

65A mod that approves tool calls can approve one that an `ask` rule would prompt for, or that one of your own `PreToolUse` hooks blocked. [Extend permissions with hooks](/docs/en/permissions#extend-permissions-with-hooks) lists what such a mod can approve, including when it can approve a call that a `deny` rule refuses.

66 

67A mod can restyle much of Claude Code's interface, but not the permission prompt. It can't change what a prompt shows you.

68 

69### List what a mod does before you install one

70 

71Before you install a mod, you can list which events it hooks and what it asks Claude Code to do, such as read a file or make a network request, without running it. Get the plugin's files first, for example by cloning its repository. Then, in your shell, run `claude plugin validate` on the plugin's directory:

72 

73```bash theme={null}

74claude plugin validate ./some-mod

75```

76 

77The `hooks:` and `calls:` lines in the output list the events the mod handles and what it asks Claude Code to do. [Review what a mod can do](/docs/en/plugins/mods/admin#review-what-a-mod-can-do) shows the output and which calls to look for.

78 

79## Turn mods on or off

80 

81Mods require Claude Code v2.1.287 or later, and they're on by default. In your shell, run `claude --version` to check, and update Claude Code if yours is older.

82 

83To turn mods off, choose how many to stop, and for how long. To turn them back on, undo the same change:

84 

85* **One mod**: disable or uninstall its plugin from the [**Installed** tab in `/plugin`](/docs/en/plugins/install#manage-installed-plugins)

86* **Every installed mod, for one session**: start Claude Code with [`--safe-mode`](/docs/en/cli-reference#cli-flags), which also leaves out your other customizations

87* **Every mod you installed, in every session**: set [`"disableAllHooks": true`](/docs/en/settings-reference#disableallhooks) in `~/.claude/settings.json`. Your settings hooks and custom status line stop too. What your organization manages keeps running.

88 

89If you use Claude Code through an organization, an administrator can also limit which mods load. Administrators start at [Stop user-installed mods from loading](/docs/en/plugins/mods/admin#stop-user-installed-mods-from-loading).

90 

91To find out whether mods can load for you, see [Check whether mods can load](/docs/en/plugins/mods/troubleshoot#check-whether-mods-can-load).

92 

93<Note>

94 If you set `CLAUDE_CODE_ENABLE_FUNCTION_HOOKS` during early access, remove it. Claude Code v2.1.287 and later ignores it, so setting it to `0` doesn't keep mods off.

95</Note>

96 

97### See which mods a session loaded

98 

99To see which mods a terminal session loaded, run `/plugin` at the Claude Code prompt. A dim line under the tabs gives the count and the names, such as `1 mod active · first-mod`. If a mod you installed isn't named there, see [Find out why a mod does nothing](/docs/en/plugins/mods/troubleshoot#find-out-why-a-mod-does-nothing).

100 

101## How a mod works

102 

103A mod is a [plugin](/docs/en/plugins/overview) whose code registers event handlers, called hooks. Claude Code runs a hook when its event happens, such as when Claude calls a tool or when the spinner is drawn. A small mod has three files:

104 

105```text theme={null}

106first-mod/

107├── .claude-plugin/

108│ └── plugin.json

109└── hooks/

110 ├── hooks.json

111 └── register.js

112```

113 

114* **`plugin.json`**: the plugin's [manifest](/docs/en/plugins/manifest-reference)

115* **`hooks.json`**: [points to your code file](/docs/en/plugins/mods/reference#files)

116* **`register.js`**: [your code](/docs/en/plugins/mods/create#write-a-mod-yourself), called the hooks module. It tells Claude Code which events to run your functions on.

117 

118This is a complete `register.js`. It counts the tool calls Claude makes and shows the count beside the spinner while Claude works, as in `Thinking · tool calls: 3…`.

119 

120```javascript hooks/register.js theme={null}

121// The count, shared by the two hooks below

122let calls = 0

123 

124// Claude Code calls this once when the mod loads

125export function register(on) {

126 // Runs each time Claude is about to use a tool

127 on('tool.call', async ($, e, next) => {

128 calls += 1

129 // Ask Claude Code to draw the interface again, so the new count shows

130 $.ui.invalidate('ui.render')

131 // Let the tool run as usual

132 return next(e)

133 })

134 

135 // Runs each time Claude Code draws the spinner

136 on('ui.render', { component: 'Spinner' }, async ($, e, next) => {

137 // Keep Claude Code's spinner, with the count added after its word

138 return next({ ...e, props: { ...e.props, suffix: ' · tool calls: ' + calls + '…' } })

139 })

140}

141```

142 

143The file registers two hooks, and both use the `calls` variable at the top:

144 

145* **The [`tool.call`](/docs/en/plugins/mods/reference#tools) hook** runs each time Claude is about to use a tool. It adds one to `calls`, asks Claude Code to draw the interface again, and lets the tool run as usual.

146* **The [`ui.render`](/docs/en/plugins/mods/reference#interface) hook** runs each time Claude Code draws the spinner. It keeps Claude Code's own spinner and adds the count after the word.

147 

148This recording shows the mod at work. Watch the spinner line above the prompt box: while Claude lists a directory and reads two files, it reads `Thinking · tool calls: 1…`, then `2…`, then `3…`.

149 

150<Frame>

151 <video autoPlay muted loop playsInline controls className="w-full dark:hidden" src="https://mintcdn.com/claude-code/dgiVO_Od1X1faduV/images/mods-overview-light.mp4?fit=max&auto=format&n=dgiVO_Od1X1faduV&q=85&s=00a18aa0743b59a700f0275ce226e6d1" aria-label="In a Claude Code session, the prompt 'list the files here and read the README' is typed and sent. While Claude works, the spinner reads 'Thinking · tool calls: 1', then 2, then 3, as Claude lists the files and reads two of them." data-path="images/mods-overview-light.mp4" />

152 

153 <video autoPlay muted loop playsInline controls className="w-full hidden dark:block" src="https://mintcdn.com/claude-code/dgiVO_Od1X1faduV/images/mods-overview-dark.mp4?fit=max&auto=format&n=dgiVO_Od1X1faduV&q=85&s=d5223da2fef16ceaaa214a36d72c0536" aria-label="In a Claude Code session, the prompt 'list the files here and read the README' is typed and sent. While Claude works, the spinner reads 'Thinking · tool calls: 1', then 2, then 3, as Claude lists the files and reads two of them." data-path="images/mods-overview-dark.mp4" />

154</Frame>

155 

156### What a hook can do with an event

157 

158Claude Code runs your hook before it acts on the event, so the hook decides what happens next. It has three choices:

159 

160* **Observe**: note what's happening and let it continue unchanged, as the `tool.call` hook in the example does

161* **Rewrite**: change the event before it continues, as the `ui.render` hook does when it adds the count to the spinner

162* **Answer**: handle the event itself, so the usual behavior doesn't run, such as refusing a command

163 

164To do anything outside its own code, such as draw, add a command, call a model, read a file, start a process, or make a network request, a hook calls the mods API. A hook has no other way to do those things, which is why Claude Code can [list what a mod does](#list-what-a-mod-does-before-you-install-one) before you install it.

165 

166For the code behind each choice, see [React to events](/docs/en/plugins/mods/events#how-a-hook-handles-an-event). For what a hook can call, see [Use the mods API](/docs/en/plugins/mods/api).

167 

168### Where mods run

169 

170A mod's hooks run in every kind of session that loads the plugin. Drawing is narrower: only the terminal and the Desktop app show a mod's panes, bands, and replaced rows. This table lists each place you might run Claude Code:

171 

172| Where you run Claude Code | Hooks run | What the mod draws appears |

173| :- | :- | :- |

174| `claude` in a terminal, including an editor's integrated terminal and the JetBrains plugin | Yes | Yes |

175| The Code tab of the Desktop app, except in a WSL session | Yes | Yes, except elements the [elements table](/docs/en/plugins/mods/reference#elements) marks terminal-only |

176| A [WSL session](/docs/en/desktop-wsl) in the Desktop app | No, because plugins aren't available in WSL sessions | No |

177| The VS Code extension's chat panel | Yes | No |

178| `claude -p` and the [Agent SDK](/docs/en/agent-sdk/overview) | Yes | No |

179| [Remote Control](/docs/en/remote-control) from claude.ai or the mobile app | Yes, in the session on your machine | In the terminal on your machine |

180| A [cloud session](/docs/en/claude-code-on-the-web) | Yes, for a plugin that [reaches the cloud session](/docs/en/cloud-environments#what-carries-over-from-your-setup) | No |

181 

182A mod that draws can check which app it's running in, and fall back to a line in the transcript or a command's text reply where nothing draws.

183 

184## Control mods for your organization

185 

186Administrators decide whether mods run and which ones, through [managed settings](/docs/en/managed-settings). [Manage mods for your organization](/docs/en/plugins/mods/admin) covers what happens by default, how to review a mod, and how to enforce a policy with a mod of your own.

187 

188## Compare mods, settings hooks, skills, and MCP servers

189 

190Mods, settings hooks, skills, and MCP servers overlap. This table shows what each one is and when to pick it.

191 

192| | Mod | Settings hook | Skill | MCP server |

193| :- | :- | :- | :- | :- |

194| What it is | Functions in a plugin that Claude Code calls in its own process | A shell command, HTTP request, or prompt that Claude Code runs on a lifecycle event | A `SKILL.md` file of instructions Claude reads | An external process or service that gives Claude tools |

195| What it can change | Tool calls, prompts, commands, turns, and what the interface draws | Whether a tool call or prompt goes ahead, a tool call's arguments and result, and context added for Claude | What Claude knows and does | Which tools Claude has |

196| Can it draw in the interface | Yes | No | No | No |

197| What you write | JavaScript or TypeScript | A script and a `settings.json` entry | Markdown | A server in any language |

198| Pick it when | You want a pane, a band above the prompt, a custom command, or to rewrite an event | You want to block, allow, or log an event with a script you already have | You keep pasting the same instructions into chat | Claude needs to reach an external system |

199 

200Each of the others has its own page: [Hooks](/docs/en/hooks), [Skills](/docs/en/skills), and [MCP](/docs/en/mcp). A plugin can hold all four, so a mod can ship in the same plugin as a skill and an MCP server.

201 

202## Mods built into Claude Code

203 

204Some of Claude Code's own features are mods. To see the ones your session has, run `/plugin` at the Claude Code prompt and go to the **Installed** tab, which lists them under **Built-in**. You can't update or uninstall a built-in mod, and the table's last column says how to turn each one off. The [`mods active` line](#see-which-mods-a-session-loaded) leaves built-in mods out.

205 

206This table lists each entry by the name `/plugin` shows:

207 

208| Name in `/plugin` | What it does | Where it's on | How to turn it off |

209| :- | :- | :- | :- |

210| `cc-plugin-agents-md` | Loads `AGENTS.md` as project instructions | Every session, apart from [the ones that can't read `AGENTS.md`](/docs/en/memory#when-agents-md-support-is-unavailable) | Disable it in `/plugin`, or [choose which instruction files load](/docs/en/memory#choose-which-instruction-files-load) |

211| `cc-plugin-diff` | Takes over [`/diff`](/docs/en/interactive-mode#review-changes-with-%2Fdiff) and draws its pane | Interactive terminal sessions | Disable it in `/plugin`. `/diff` stays, and Claude Code's built-in version of the command answers it. |

212| `cc-plugin-plugin-authoring` | Gives Claude the [`plugin-authoring` skill](/docs/en/plugins/mods/create#ask-claude-for-a-mod) for writing mods. It holds a skill and no mod code. | Unless Anthropic has turned installed mods off remotely | Disable it in `/plugin` |

213| `cc-plugin-sec-default` | Guards what your organization manages from the mods a user installs | [Where the guard loads](/docs/en/plugins/mods/admin#know-what-happens-by-default) | You can't. An administrator [sets the order](/docs/en/plugins/mods/admin#install-your-organizations-mods) in managed settings |

214| `cc-plugin-telemetry` | Sends the analytics records that Claude Code and its built-in mods log | Wherever Claude Code's own analytics are on | Disable it in `/plugin`, or turn analytics off, for example with [`DISABLE_TELEMETRY`](/docs/en/env-vars) |

215| `cc-plugin-you-should-know` | Runs a side agent that watches your back while Claude works on longer tasks. When it finds something worth knowing that you might miss, it shows you a note above the prompt. | Disabled by default. Listed in `/plugin` -> **Installed** -> **Show disabled** if available for your org. Enable with [`/plugin enable cc-plugin-you-should-know@builtin`](/docs/en/plugins/cli-reference#plugin-in-a-session). | Disable it in `/plugin` |

216 

217The settings and flags that stop installed mods, such as `disableAllHooks`, `--bare`, and `--safe-mode`, don't stop built-in mods.

218 

219### Read the source of built-in mods

220 

221The source of four of these mods is public in the [`mods` directory of the Claude Code repository](https://github.com/anthropics/claude-code/tree/main/mods). Each one is a complete plugin with its hooks module and tests:

222 

223* [`diff`](https://github.com/anthropics/claude-code/tree/main/mods/diff): the `/diff` pane, with buttons bound to keyboard actions and scrolling the mod handles itself

224* [`agents-md`](https://github.com/anthropics/claude-code/tree/main/mods/agents-md): loads `AGENTS.md` as project instructions, with a [`userConfig`](/docs/en/plugins/components#user-configuration) option

225* [`sec-default`](https://github.com/anthropics/claude-code/tree/main/mods/sec-default): the guard described in [Know what happens by default](/docs/en/plugins/mods/admin#know-what-happens-by-default), a model for a mod that enforces policy

226* [`telemetry`](https://github.com/anthropics/claude-code/tree/main/mods/telemetry): adds methods that other mods can call, and ships their types

227 

228## Next steps

229 

230* [Create a mod](/docs/en/plugins/mods/create): build one that counts tool calls, shows the count beside the spinner, and adds a command, and learn the edit and reload loop

231* [Draw in the interface](/docs/en/plugins/mods/interface): panes, the band above the prompt, buttons, text fields, and state

232* [React to events](/docs/en/plugins/mods/events): tool calls, prompts, turns, and the order mods run in

233* [Use the mods API](/docs/en/plugins/mods/api): commands, tools, model calls, timers, and files

234* [Test a mod](/docs/en/plugins/mods/test): automated tests that run without a session

235* [Troubleshoot a mod](/docs/en/plugins/mods/troubleshoot): the reasons a mod does nothing, and the debug log

236* [Manage mods for your organization](/docs/en/plugins/mods/admin): defaults, managed settings, reviewing a mod, and policy mods

237* [Mods reference](/docs/en/plugins/mods/reference): every event, method, element, and limit

plugins/mods/reference.md +288 −0 created

Details

1> ## Documentation Index

2> Fetch the complete documentation index at: https://code.claude.com/docs/llms.txt

3> Use this file to discover all available pages before exploring further.

4 

5# Mods reference

6 

7> Complete reference for Claude Code mods: hooks module layout, every event, every mods API method, render sites, elements by surface, limits, and settings.

8 

9Look up any event a [mod](/docs/en/plugins/mods/overview) can hook, mods API method it can call, or render site it can draw in, for the Claude Code CLI and the Desktop app as of v2.1.287. Each entry gives the name and a one-line description, and links to the guide section that explains it where there is one.

10 

11<Note>

12 The complete reference is Claude Code's [TypeScript declarations for mods](https://github.com/anthropics/claude-code/blob/main/mods/types/claude-code.d.ts), which describe every event, method, and element, with examples. The copy on GitHub can be older than the Claude Code version you have installed. When the two disagree, trust [the copy Claude Code writes for your version](/docs/en/plugins/mods/create#get-the-types-for-your-build).

13</Note>

14 

15## Files

16 

17A mod is a plugin directory with these files:

18 

19| File | Required | Contents |

20| :- | :- | :- |

21| `.claude-plugin/plugin.json` | Yes | The plugin [manifest](/docs/en/plugins/manifest-reference). Mods add no required fields. |

22| `hooks/hooks.json` | Yes | `modules`: an array with one path, relative to this file, to the hooks module, as in `"modules": ["./register.js"]`. Can also hold [settings hooks](/docs/en/hooks) under `hooks`. |

23| The hooks module, such as [`hooks/register.js`](/docs/en/plugins/mods/create#write-a-mod-yourself) | Yes | The mod's entry point. Exports `register(on, options)`. Named `.js`, `.mjs`, `.cjs`, `.jsx`, `.ts`, `.mts`, `.cts`, or `.tsx`. An ES module. |

24| [`types/index.d.ts`](/docs/en/plugins/mods/interface#declare-the-values), named by `types` in the manifest | When the mod uses `$.state` or adds a namespace to the mods API | Declares `PluginState` values and any namespace the mod adds |

25| Files whose names end in `.test.ts` or `.test.tsx` | No | Tests that [`claude plugin test`](/docs/en/plugins/mods/test#write-a-test) runs |

26 

27`register` receives `on` and `options`. `options` holds the values of the [`userConfig`](/docs/en/plugins/components#user-configuration) fields the manifest declares, with defaults filled in.

28 

29## The hook function

30 

31A mod registers each of its hooks, which are event handlers, by calling `on` inside `register`. `on` takes the event's name, an optional [matcher](/docs/en/plugins/mods/events#filter-which-events-a-hook-handles), which is a filter on the event's fields, and the hook, as in `on('tool.call', { tool: 'Bash' }, async ($, e, next) => next(e))`. `on` returns a registration with one method, `.catch(handler)`, which sets the hook's [error handler](/docs/en/plugins/mods/events#handle-a-hook-that-fails).

32 

33| Argument | What it is |

34| :- | :- |

35| [`$`](/docs/en/plugins/mods/events#how-a-hook-handles-an-event) | The mods API: every method in [mods API methods](#mods-api-methods). Write each call in full, namespace then method, as in `$.fs.read('notes.md')`. |

36| [`e`](/docs/en/plugins/mods/events#how-a-hook-handles-an-event) | The event's input, as deeply frozen plain data. To change it, pass a copy to `next`. |

37| [`next(e)`](/docs/en/plugins/mods/events#how-a-hook-handles-an-event) | The next handler, as in middleware. Runs the hooks after this one, then Claude Code's behavior. Resolves to the event's result. |

38| [`next.signal`](/docs/en/plugins/mods/api#stop-background-work) | An `AbortSignal` that fires when the event is abandoned |

39| `next.origin` | `{ plugin, tier }` of whoever raised the event. Claude Code itself is `{ plugin: 'engine', tier: 'core' }`. A mod's `tier` is its priority group in the [order mods run in](/docs/en/plugins/mods/events#the-order-mods-run-in): `prepend`, `user`, `append`, or `builtin`. |

40| `next.budget` | The hook's time limit in milliseconds: `next.budget.ms` is the whole limit, and `next.budget.remainingMs` is what's left now |

41| `next.to(e, tier)` | Skips to a later tier, which is `append`, `builtin`, or `core`. `next.to(e, 'append')` skips the mods a user installed. Only a mod in `prependPlugins` or `appendPlugins` can call it. |

42| `next.error`, `next.called` | In a `.catch` handler only. `next.error.kind` is `throw` or `timeout`, `next.error.message` is the error's text, and `next.called` is `true` when the failed hook had called `next`. |

43 

44## Events

45 

46Every event a mod can hook is listed here, grouped by what it concerns, with when it fires and what a hook on it can return. Hooks on `turn.step` and `process.spawn` are async generators, and every other hook is an async function.

47 

48The last column of each table uses shorthand. `next(e)` passes the event on unchanged. `next({ ...e, text })` passes on a copy with the named field changed, as in `next({ ...e, text: e.text.trim() })`. An object answers the event without calling `next`, and a word such as `reason` stands for a string you write, as in `{ deny: 'Use the file tools.' }`.

49 

50### Tools

51 

52Tool events fire around each tool call Claude makes, from the description Claude reads to the decision on whether the call runs:

53 

54| Event | Fires when | A hook can return |

55| :- | :- | :- |

56| [`tool.call`](/docs/en/plugins/mods/events#guard-or-change-a-tool-call) | A tool is about to run | `next(e)`, `{ deny: reason }`, or `{ result }` |

57| [`tool.check`](/docs/en/plugins/mods/events#where-settings-hooks-run-in-the-order) | Claude Code decides whether a tool call may run, after the `tool.call` and `PreToolUse` hooks. `next(e)` resolves to the decision the rules, the permission mode, and those hooks reached. | `{ decision }`, which is `allow`, `ask`, or `deny` |

58| `tool.describe` | Once for each tool, when its description is first sent to Claude | `{ description }` |

59 

60### Prompts and what Claude reads

61 

62Prompt events cover the text the user types and the text Claude Code sends to Claude on its own, such as the system prompt and reminders:

63 

64| Event | Fires when | A hook can return |

65| :- | :- | :- |

66| [`prompt.submit`](/docs/en/plugins/mods/events#rewrite-or-add-to-a-prompt) | A prompt is submitted | `next({ ...e, text })`, `next({ ...e, context })`, or `{ drop: reason }` |

67| `prompt.fill`, `prompt.suggest` | Text is about to go into the prompt box as a draft, or as a dim suggestion | `next(e)` with changed text |

68| `prompt.edit` | The user edits the prompt box | `next(e)` |

69| `prompt.compose` | Claude Code renders a system prompt | `{ sections }`, a list of `{ id, text, scope }` in the order they're sent |

70| [`prompt.section`](/docs/en/plugins/mods/events#rewrite-or-add-to-a-prompt) | Once for each named section of the system prompt. `e.name` is the section's `id` in `prompt.compose`. | `{ text }`, or `{ text: null }` to leave the section out |

71| [`prompt.context`](/docs/en/plugins/mods/events#rewrite-or-add-to-a-prompt) | Once for each conversation, for the context sent with the first message | `{ blocks }` |

72| `prompt.attachment` | Claude Code adds a message of its own for Claude, such as a reminder. `e.type` names the kind, and for the kinds the types declare, `e.detail` holds the facts the text was written from. | `{ text }`, or `{ text: null }` to leave it out |

73| [`skill.prompt`](/docs/en/plugins/mods/events#rewrite-or-add-to-a-prompt) | A skill's text is expanded for Claude | `{ text }` |

74| `attribution.text` | Claude Code composes commit or pull request attribution text | `{ text }` |

75 

76### Commands and configuration

77 

78Command and configuration events fire when a command runs or is listed, and when a `/config` row is shown or changed:

79 

80| Event | Fires when | A hook can return |

81| :- | :- | :- |

82| [`command.run`](/docs/en/plugins/mods/api#add-a-command) | A command is about to run | `{ text }`, `{}`, or `next(e)` |

83| `command.describe` | Once for each command, for the command list | `{ description, argumentHint, isHidden }` |

84| `config.set` | A `/config` row is about to change | `next({ ...e, value })` or `{ deny: reason }` |

85| `config.describe` | Once for each `/config` row | `{ label, description, isHidden }` |

86 

87### Turns

88 

89Turn events follow one answer from start to finish, including each request to the model within it:

90 

91| Event | Fires when | A hook can return |

92| :- | :- | :- |

93| [`turn.start`](/docs/en/plugins/mods/events#follow-a-turn) | A turn begins | `next(e)` |

94| [`turn.step`](/docs/en/plugins/mods/events#follow-a-turn) | One request is about to go to the model | `yield* next(e)`, or `next({ ...e, model })`, `next({ ...e, effort })` |

95| [`turn.complete`](/docs/en/plugins/mods/events#follow-a-turn) | A turn ended | `next(e)`, or `{ text }` to show a line under the answer |

96 

97<h3 id="session">

98 Session

99</h3>

100 

101Session events mark the session starting, ending, compacting, and exchanging messages with other sessions:

102 

103| Event | Fires when | A hook can return |

104| :- | :- | :- |

105| [`session.start`](/docs/en/plugins/mods/api#add-a-command-or-a-tool) | Once for each loaded mod, before the first prompt, and again after a reload of that mod. Not after `/clear`, `/resume`, or `/branch`. | `next(e)` |

106| `session.end` | The session ends, or `/clear`, `/resume`, or `/branch` runs. `e.reason` is `clear`, `resume`, `logout`, `prompt_input_exit`, or `other`. `/branch` reports `resume`. | `next(e)` |

107| `session.compact` | The conversation is about to be compacted | `{ skip: reason }` |

108| [`session.receive`](/docs/en/plugins/mods/api#send-and-receive-messages-between-sessions), [`session.send`](/docs/en/plugins/mods/api#send-and-receive-messages-between-sessions) | A message arrives from, or is about to go to, another agent or session. See [Send and receive messages between sessions](/docs/en/plugins/mods/api#send-and-receive-messages-between-sessions). | `{ consumed: reason }` for `receive`, `{ isDelivered: false, reason }` for `send` |

109| `session.append` | Once for each row the conversation keeps, such as a prompt, a response block, a tool result, or a notice, before it's stored | `next({ ...e, message })` to rewrite the row's `content` |

110| `session.attach`, `session.detach` | Another app connects to or disconnects from the session | `next(e)` |

111| `session.measure` | After each turn, and when a plan limit's percent used changes | `next(e)` |

112 

113### Subagents

114 

115Subagent events fire when a subagent type is offered to Claude and when one is about to start:

116 

117| Event | Fires when | A hook can return |

118| :- | :- | :- |

119| `agent.offer` | A subagent type is offered to Claude | `{ isOffered: false }` to withhold it |

120| `agent.spawn` | A subagent is about to start | `{ model }` or `{ deny: reason }` |

121 

122### Interface

123 

124Interface events fire when Claude Code draws a render site and when the user uses a control a mod drew. [Draw in the interface](/docs/en/plugins/mods/interface) shows what a `ui.render` hook returns:

125 

126| Event | Fires when |

127| :- | :- |

128| [`ui.render`](/docs/en/plugins/mods/interface#pick-where-to-draw) | A [render site](#render-sites) is about to be drawn |

129| `ui.resolve` | Mods load, once for each app, render site, and mod. The result is the element table that `$.ui.resolve(e)` reads. |

130| [`ui.press`](/docs/en/plugins/mods/interface#respond-to-presses-and-typing), [`ui.input`](/docs/en/plugins/mods/interface#respond-to-presses-and-typing), [`ui.select`](/docs/en/plugins/mods/interface#respond-to-presses-and-typing) | A `Button`, `Input`, or `Select` a mod drew is used |

131| `ui.focus`, `ui.scroll` | The focused control or the scroll position of a pane or the band is about to change |

132| `ui.close` | A pane is about to close. `e.id` is the pane and `e.origin.kind` is `plugin`, `person`, or `unload`. |

133| [`ui.message`](/docs/en/plugins/mods/interface#build-a-tree-from-elements) | A `Client` element posts data to its mod |

134 

135### Other mods

136 

137Two events let a mod act on other mods as they load, to refuse one or change the mods API it receives:

138 

139| Event | Fires when | A hook can return |

140| :- | :- | :- |

141| [`plugin.register`](/docs/en/plugins/mods/admin#enforce-a-policy-with-a-mod-of-your-own) | A hooks module is about to load. `e.uses` lists its events, mods API calls, environment variables, and state, as `claude plugin validate` prints them. Each call is spelled without the `$.` prefix, such as `fs.read`. | `{ refuse: reason }` |

142| `engine.create` | The mods API is being built for this mod | A changed mods API, to add a namespace or withhold one |

143 

144### Telemetry

145 

146Telemetry events fire for the usage records Claude Code logs:

147 

148| Event | Fires when | A hook can return |

149| :- | :- | :- |

150| `telemetry.log`, `telemetry.mark` | A telemetry record is about to be logged, or one use of a feature is marked. Hook them by name or as `telemetry.*`, because `*` in a mod you install doesn't select them. | `next(e)`, or `{ deny: reason }` |

151 

152### Settings hook events

153 

154Each [settings hook event](/docs/en/hooks#hook-events) is an event named `classic.<Event>`, such as `classic.Stop` or `classic.PostToolUse`. `e` is the hook's stdin JSON.

155 

156### Mods API calls

157 

158Every [mods API method](#mods-api-methods) is also an event, named for its namespace and method, such as `fs.read`, `model.complete`, or `ui.open`. A hook on one intercepts calls from the mods that run after it, and can return `next(e)`, `{ deny: reason }`, or `{ value }`.

159 

160## Mods API methods

161 

162The mods API is the `$` argument every hook receives. Its methods are grouped in namespaces, such as `$.ui`. This table lists each namespace's methods by name, so `open` in the `$.ui` row is the call `$.ui.open(...)`. The guides show the common ones in use, and [the types for your build](/docs/en/plugins/mods/create#get-the-types-for-your-build) document every method with an example.

163 

164| Namespace | Methods |

165| :- | :- |

166| `$.plugin` | `name`, `root`: this plugin's name and directory |

167| [`$.ui`](/docs/en/plugins/mods/interface#pick-where-to-draw) | `resolve`, `invalidate`, `open`, `close`, `panes`, `focus`, `scroll`, `toast`, `status`, `log`, `notice`, `ask`, `copy`, `blit` |

168| [`$.command`](/docs/en/plugins/mods/api#add-a-command) | `register`, `run`, `list` |

169| [`$.tool`](/docs/en/plugins/mods/api#add-a-tool) | `register`, `call`, `check`, `list` |

170| `$.agent` | `register`, `spawn`, `list` |

171| [`$.model`](/docs/en/plugins/mods/api#call-a-model) | `complete`, `fork`, `classify` |

172| [`$.prompt`](/docs/en/plugins/mods/api#start-a-turn-from-a-background-job) | `submit`, `read`, `fill`, `suggest`, `compose`. Claude reads text from `submit({ text })` after a sentence that names your mod as the sender. `submit({ text, asUser: true })` sends the text as the user's own words, without that sentence. |

173| `$.turn` | `abort` |

174| [`$.session`](/docs/en/plugins/mods/api#send-and-receive-messages-between-sessions) | `messages`, `cwd`, `root`, `model`, `turns`, `id`, `repo`, `surfaces`, `usage`, `version`, `compact`, `send`, `append`, `authorize`. `usage()` returns `{ startedAt, context, rateLimits, cost }`: `context` has `tokens`, `window`, and `percent`, and `rateLimits` is a list of `{ kind, percentUsed, resetsAt }`. |

175| `$.config` | `list`, `set` |

176| [`$.settings`](/docs/en/plugins/mods/api#reach-files-processes-and-the-network) | `read` |

177| [`$.env`](/docs/en/plugins/mods/api#reach-files-processes-and-the-network) | `get`, `set` |

178| [`$.fs`](/docs/en/plugins/mods/api#reach-files-processes-and-the-network) | `read`, `write`, `list`, `exists`, `stat`, `ancestors`. `write` isn't atomic: it replaces the file's content in place, so another process can read a partly written file. Keep data that several sessions change in `$.store`. |

179| [`$.store`](/docs/en/plugins/mods/interface#keep-state) | `get`, `set`, `delete`, `keys`. A key-value store that every session on the machine shares. See [Save from more than one session](/docs/en/plugins/mods/interface#save-from-more-than-one-session). |

180| [`$.state`](/docs/en/plugins/mods/interface#keep-a-value-in-\$-state) | Reactive state: `get`, `set`, with the helpers `atom`, `read`, `update`, `derive`, and `memberOf` imported from `claude-code` |

181| [`$.clock`](/docs/en/plugins/mods/api#run-work-in-the-background) | `now`, `sleep`, `after`, `every` |

182| [`$.http`](/docs/en/plugins/mods/api#reach-files-processes-and-the-network) | `fetch` |

183| [`$.process`](/docs/en/plugins/mods/api#reach-files-processes-and-the-network) | `run`, `spawn` |

184| [`$.mcp`](/docs/en/plugins/mods/api#reach-files-processes-and-the-network) | `call`, `connect`. `connect(server)` connects an MCP server that your own plugin's manifest lists. |

185| `$.audio` | `play`, `speak` |

186| `$.telemetry` | `log`, `mark`. A record is sent only when Claude Code or a built-in mod raised it. |

187 

188## Render sites

189 

190A render site is an extension point in Claude Code's interface. Each row is a value of `e.component` in a `ui.render` hook, with the fields of `e.props` and the apps that raise it. `e.surface` is `terminal` or `desktop`. [Change what Claude Code already draws](/docs/en/plugins/mods/interface#change-what-claude-code-already-draws) shows what a hook can do at a site, with an example of each choice.

191 

192| Site | `e.props` | `e.requestId` | Raised on |

193| :- | :- | :- | :- |

194| [`Pane`](/docs/en/plugins/mods/interface#pick-where-to-draw) | `title`, `isFocused`, `bodyColumns`, `placement`, `scroll`, `view` | The pane's `id` | Terminal, Desktop |

195| [`AbovePrompt`](/docs/en/plugins/mods/interface#pick-where-to-draw) | `hasSurvey`, `isWorking`, `maxRows`, `bodyColumns`, `scroll`, `view` | One instance | Terminal, Desktop |

196| `UserMessage` | `text`, `origin`, `isExpanded`, and `task` or `from` by origin | The message id | Terminal, Desktop |

197| `AssistantMessage` | The reply's text | The message id | Terminal, Desktop |

198| `ToolUse`, `ToolResult`, `ToolGroup` | The tool's name, input, and result | The tool call id | Terminal, Desktop |

199| `CommandOutput` | `command`, `text` | The message id | Terminal, Desktop |

200| [`AskUserQuestion`](/docs/en/plugins/mods/interface#change-what-claude-code-already-draws) | The question and options | The tool call id | Terminal, Desktop |

201| `ToolProgress` | `kind` | The tool call id | Terminal |

202| [`Spinner`](/docs/en/plugins/mods/interface#change-what-claude-code-already-draws) | `word`, `message`, `suffix`, `mode` | The agent id | Terminal, Desktop |

203| `TurnDuration` | `word`, `durationMs` | The message id | Terminal |

204| `InfoNotice` | `text`, `command` | The message id | Terminal |

205| `SessionMode` | `modes` | One instance | Terminal, Desktop |

206| `PromptHint` | `isDraft`, `isWorking`, `hint` | One instance | Terminal, Desktop |

207 

208`e.viewport` holds `columns`, `rows`, and `isFullscreen`. It's absent until the app has measured its window. Its `rows` is the height of the whole window, not of your pane.

209 

210To fit a tree to its site, read these props in the hook:

211 

212* **Width of a `Pane` or the band**: draw to `e.props.bodyColumns`

213* **Height of a `Pane` beside the transcript**: where `e.props.placement` is `'dock'`, `e.props.scroll.bodyRows` is the number of rows the pane has

214* **Height of a `Pane` above the prompt**: where `e.props.placement` is `'inline'`, the pane grows with your tree up to a limit, and `bodyRows` counts only the rows showing now. The [`rows` field of `$.ui.open`](/docs/en/plugins/mods/interface#open-a-pane-at-the-right-time) asks for a different limit.

215 

216A tree taller than the pane scrolls as a whole.

217 

218## Elements

219 

220Elements are the building blocks of a tree a `ui.render` hook returns, and you get them from `$.ui.resolve(e)`. [Build a tree from elements](/docs/en/plugins/mods/interface#build-a-tree-from-elements) shows the common ones with how the terminal draws them. A check mark means the app can draw the element.

221 

222| Element | Main props | Terminal | Desktop |

223| :- | :- | :-: | :-: |

224| [`Box`](/docs/en/plugins/mods/interface#build-a-tree-from-elements) | `key`, flex layout, `gap`, `padding`, `margin`, `width`, `height`, `borderStyle`, `backgroundColor`, `position`, `hover` | ✓ | ✓ |

225| [`Text`](/docs/en/plugins/mods/interface#build-a-tree-from-elements) | `color`, `backgroundColor`, `bold`, `italic`, `underline`, `dimColor`, `inverse`, `wrap` | ✓ | ✓ |

226| [`Button`](/docs/en/plugins/mods/interface#respond-to-presses-and-typing) | `key`, `label`, `onPress`, `hotkey`, `plain`, `dimColor`, `autoFocus`, `action` | ✓ | ✓ |

227| `Link` | `href`, `label` | ✓ | ✓ |

228| `Code` | The code, up to 10,000 characters | ✓ | ✓ |

229| `Markdown` | `text`, up to 10,000 characters, `key`, `dimColor`, `onLinkPress`, `pressableLinks` | ✓ | ✓ |

230| [`Input`](/docs/en/plugins/mods/interface#take-typed-input-and-draw-a-row-for-each-item) | `key`, `label`, `placeholder`, `value`, `submitLabel`, `onSubmit`, `onInput`, `autoFocus` | ✓ | ✓ |

231| `Select` | `key`, `label`, `options`, `value`, `onSelect`, `autoFocus` | ✓ | ✓ |

232| `Svg` | An SVG document, up to 131,072 characters | | ✓ |

233| [`Client`](/docs/en/plugins/mods/interface#build-a-tree-from-elements) | `module`, `key` | ✓ | ✓ |

234| [`Raster`](/docs/en/plugins/mods/interface#draw-a-grid-of-colored-cells) | `key`, `columns` up to 512, `rows` up to 256, `cells`. See [Draw a grid of colored cells](/docs/en/plugins/mods/interface#draw-a-grid-of-colored-cells). | ✓ | |

235| `Image` | PNG or RGBA bytes up to 2 MiB, or a file path | ✓ | |

236 

237Three more `Button` rules: `action` names one of Claude Code's own [keybinding actions](/docs/en/keybindings), and the user's binding for it presses the button when that binding is a chord or a modified key. A digit `hotkey` on a button in the band also fires when the user types that digit alone into an empty prompt and pauses. When two buttons in one drawing name the same `hotkey`, the later one gets it. Claude Code refuses `autoFocus: false` on any control, so leave the prop off instead.

238 

239## Limits

240 

241Hooks and mods API calls run under time and size limits. Claude Code skips a hook that runs past a time limit and rejects a call that passes a size limit.

242 

243| Limit | Value |

244| :- | :- |

245| A hook's own running time for one event, not counting time inside `next` or a mods API call other than `$.clock.sleep` | 10 seconds |

246| A `.catch` handler's running time | 1 second |

247| All `session.end` hooks together | 1.5 seconds |

248| `$.process.run` timeout | 30 seconds by default, 10 minutes at most |

249| `$.model.complete` `maxTokens` | 1024 by default, up to 64,000 or the model's output limit |

250| `$.fs.read` and `$.fs.write` | 4 MiB for one file |

251| One string child of a `Text` | 10,000 characters |

252| `$.store` | 4 MiB of JSON in total |

253| `$.session.messages()` | The newest 4,096 entries |

254| `$.ui.invalidate('ui.render')` redraws | Throttled to 10 a second, 30 for the visible pane and the band. Calls that come sooner are coalesced. |

255| `$.ui.toast` | Shown for 4 seconds unless you pass `{ timeoutMs }` |

256| A pane opened without the user asking | Placed from 144 terminal columns, 110 after they've opened it once |

257| Command, tool, subagent type, and pane names | Letters, digits, `_`, and `-`, up to 64 characters |

258| One `claude plugin test` test | 5 seconds unless the test sets `timeoutMs` |

259 

260## Settings and environment variables

261 

262These are the settings and environment variables that affect mods. The Where column says which settings file or environment each one is read from:

263 

264| Name | Where | What it does |

265| :- | :- | :- |

266| `CLAUDE_CODE_PLUGIN_DIRS` | Environment, or `env` in `~/.claude/settings.json` | Plugin directories to load as `--plugin-dir` does, for apps you can't pass a flag to. Absolute paths separated by `:`, or `;` on Windows. |

267| `CLAUDE_CODE_PLUGIN_DIR_WATCH` | Environment | `1` makes a long-running non-interactive session reload `--plugin-dir` mods on save |

268| `prependPlugins`, `appendPlugins` | Managed settings. User settings only on a machine with no managed settings, for a user who isn't signed in with a Team or Enterprise plan. | Lists of plugin ids, such as `acme-guard@acme-tools`. Mods in `prependPlugins` run before every mod a user installs, and mods in `appendPlugins` run after, in the listed order. See [The order mods run in](/docs/en/plugins/mods/events#the-order-mods-run-in). |

269| `allowManagedModsOnly` | Managed settings, as an [option on the built-in guard](/docs/en/plugins/mods/admin#set-options-on-the-built-in-guard) | Only mods that [count as your organization's](/docs/en/plugins/mods/admin#install-your-organizations-mods), and mods built into Claude Code, load. Users' settings hooks keep running. |

270| `allowModsToOverrideDenyRules` | Managed settings, as an [option on the built-in guard](/docs/en/plugins/mods/admin#set-options-on-the-built-in-guard) | Lets a mod a user installed approve a tool call that a `deny` rule refuses |

271| `allowManagedHooksOnly` | Managed settings | Blocks hooks and installed mods that aren't your organization's. See [what keeps running](/docs/en/settings-reference#what-runs-under-allowmanagedhooksonly). |

272| `disableAllHooks` | Any settings file | In managed settings, no mod or hook from an installed plugin runs. In your own settings, what your organization manages keeps running. See [`disableAllHooks`](/docs/en/settings-reference#disableallhooks). |

273| `disableSideloadFlags` | Managed settings | Rejects `--plugin-dir` and `--plugin-url` at startup |

274| `pluginConfigs` | User or managed settings | Holds `userConfig` values for a mod, keyed by the plugin's id, such as `acme-guard@acme-tools`, or its name and `@inline`, such as `first-mod@inline`, for one loaded with `--plugin-dir` |

275 

276`sec-default@builtin` is a guard built into Claude Code, listed as `cc-plugin-sec-default` in `/plugin` and the debug log. It loads ahead of every mod a person installs on a machine with managed settings, or for a user signed in with a Team or Enterprise plan. If managed `prependPlugins` is set, the guard loads only when that list names it, at the position listed. Its source is in the [`mods/sec-default` directory of the Claude Code repository](https://github.com/anthropics/claude-code/tree/main/mods/sec-default).

277 

278## Commands

279 

280These commands and flags load, inspect, and test a mod. The `claude` commands run in your shell and the `/` commands at the Claude Code prompt. In the table, `<directory>` stands for a path you type, as in `claude plugin validate ./first-mod`. Square brackets mark an argument you can leave out.

281 

282| Command | What it does |

283| :- | :- |

284| [`/plugin`](/docs/en/plugins/mods/overview#see-which-mods-a-session-loaded) | Shows a line such as `1 mod active · first-mod` under its tabs when a mod that isn't built in has loaded |

285| [`claude plugin validate <directory>`](/docs/en/plugins/mods/create#check-what-claude-code-reads-from-your-mod) | Reads a plugin's manifest and hooks module and reports errors, the events it hooks, and the mods API calls it makes. `--strict` treats warnings as errors and `--json` prints a machine-readable report. |

286| [`claude plugin test [directory]`](/docs/en/plugins/mods/test#write-a-test) | Runs every file under the directory, or the current directory when you give none, whose name ends in `.test.ts` or `.test.tsx`. Exits with status 1 when a test fails. |

287| [`claude --plugin-dir <directory>`](/docs/en/plugins/mods/create#write-a-mod-yourself) | Loads a plugin directory for one session and reloads its hooks module when you save. Repeat the flag to load several. |

288| `/reload-plugins` | Reloads plugins when you run it |

plugins/mods/test.md +406 −0 created

Details

1> ## Documentation Index

2> Fetch the complete documentation index at: https://code.claude.com/docs/llms.txt

3> Use this file to discover all available pages before exploring further.

4 

5# Test a mod

6 

7> Write automated tests for a Claude Code mod that raise events, stub Claude Code's answers, and press buttons, with no session, sign-in, or network.

8 

9You can write automated tests for a mod and run them from your shell with [`claude plugin test`](/docs/en/plugins/mods/reference#commands). A test raises the events your hooks handle and checks what the hooks did, so you catch a problem before it reaches a session. The first example tests the mod from [Create a mod](/docs/en/plugins/mods/create).

10 

11## Write a test

12 

13A test loads your mod, sends events through its hooks the way Claude Code would, and checks what the hooks did, without a session, a sign-in, or a network. You run tests from your shell with `claude plugin test`, and each test file imports the test kit, a test library in the `claude-code/testing` module.

14 

15Give each test file a name that ends in `.test.ts`, such as `first-mod.test.ts`, and save it anywhere in the plugin directory. Every test file needs at least one `test()`, or the run fails with `declares no test(): nothing ran`. A test file can import your mod's own files and sibling `.ts` helpers, so you can unit test plain functions, such as a game's rules, without the kit.

16 

17This test raises two tool calls, runs the `/tally` command from [Create a mod](/docs/en/plugins/mods/create), and checks that the reply counts both. Its first line is a [stub](#stub-what-claude-code-would-answer), which answers the tool calls in Claude Code's place. Save it as `first-mod/tests/first-mod.test.ts`:

18 

19```typescript first-mod/tests/first-mod.test.ts theme={null}

20import { expect, test } from 'claude-code/testing'

21 

22test('/tally reports the tool calls the mod has seen', async ($, on) => {

23 // Answer each tool call in Claude Code's place, so no tool runs

24 on('tool.call', () => ({ result: 'ok' }))

25 

26 // Raise two tool calls, which the mod's tool.call hook counts

27 await $.tool.call({ tool: 'Bash', command: 'ls' })

28 await $.tool.call({ tool: 'Read', file_path: 'README.md' })

29 

30 // Run /tally and check the text its hook returns

31 const answer = await $.command.run({ command: 'tally', args: '' })

32 expect(answer.text).toBe('Claude has made 2 tool calls since this mod loaded')

33})

34```

35 

36In your shell, run the tests from the `first-mod` directory:

37 

38```bash theme={null}

39claude plugin test

40```

41 

42The output names each test and whether it passed, with timings that vary from run to run:

43 

44```text theme={null}

45tests/first-mod.test.ts:

46(pass) /tally reports the tool calls the mod has seen [22.87ms]

47 

48 1 pass

49 0 fail

50Ran 1 test across 1 file. [0.19s]

51```

52 

53Each `$.tool.call` went through the mod's [`tool.call`](/docs/en/plugins/mods/reference#tools) hook, which added one to its count and passed the call on to the stub. No `ls` ran and no file was read. `$.command.run` then went to the mod's [`command.run`](/docs/en/plugins/mods/reference#commands-and-configuration) hook, and `answer` is the object that hook returned.

54 

55The command exits with status 1 when a test fails, so it works in CI. If your own mods can't load in the shell that runs it, it prints a line starting `claude plugin test: hooks modules are turned off` with the reason, and exits with status 1.

56 

57### Stub what Claude Code would answer

58 

59No model, store, or tool runs in a test, so wherever your mod expects Claude Code to answer, the test supplies the answer with a stub. A test function receives two arguments for that:

60 

61* **`$`**: the test's own `$`, which stands where Claude Code does. It isn't the [mods API](/docs/en/plugins/mods/reference#mods-api-methods) that a hook receives. Each of its methods raises the event of the same name, sends it through your mod's hooks, and resolves to the result: `$.tool.call({ tool: 'Bash', command: 'ls' })` raises `tool.call`. `$.command.run`, `$.prompt.submit`, `$.session.start`, and `$.turn.complete` work the same way, and `$.classic.Stop` and the other `$.classic` methods raise a [settings hook event](/docs/en/plugins/mods/events#hook-the-settings-hook-events). A test can't raise a mods API call such as `ui.close` directly. Trigger it through your mod, for example by pressing the button that closes the pane.

62* **`on`**: call it to register stubs, which are hooks that answer in Claude Code's place. Name a stub for a mods API call without the `$.`, so a stub registered as `store.get` answers your mod's `$.store.get`. When your mod calls [`$.model.complete`](/docs/en/plugins/mods/api#call-a-model) or [`$.store.get`](/docs/en/plugins/mods/interface#keep-state), a stub supplies the answer.

63 

64This example stubs a model call. The hook belongs to a mod named `grader`, and handles a `/grade` command that sends a sentence to a model and reports whether the reply starts with `PASS`. The file holds only the hook under test, so the mod also needs a `plugin.json` and a `hooks.json`, as in [Create a mod](/docs/en/plugins/mods/create#write-a-mod-yourself). To type `/grade` in a session, the mod also has to [register the command](/docs/en/plugins/mods/api#add-a-command):

65 

66```javascript grader/hooks/register.js theme={null}

67export function register(on) {

68 on('command.run', { command: 'grade' }, async ($, e) => {

69 // e.args is the text typed after /grade

70 const reply = await $.model.complete({

71 model: 'haiku',

72 system: 'Grade the sentence. Start your reply with PASS or FAIL.',

73 prompt: e.args,

74 })

75 const passed = reply.isAnswered && reply.text.startsWith('PASS')

76 return { text: passed ? 'Passed' : 'Try again' }

77 })

78}

79```

80 

81This test stubs the model call to check what the hook does with a passing reply:

82 

83```typescript grader/tests/grader.test.ts theme={null}

84import { expect, test } from 'claude-code/testing'

85 

86test('a passing grade is reported', async ($, on) => {

87 // Answer the mod's $.model.complete call with a fixed reply, so no model runs

88 on('model.complete', () => ({

89 value: {

90 isAnswered: true,

91 text: 'PASS\nNice sentence.',

92 usage: { input_tokens: 10, output_tokens: 5, cache_read_input_tokens: 0, cache_creation_input_tokens: 0 },

93 },

94 }))

95 

96 // Run /grade, which makes the mod call the model

97 const answer = await $.command.run({ command: 'grade', args: 'The cat sat on the mat.' })

98 expect(answer.text).toBe('Passed')

99})

100```

101 

102The test passes because the hook's `reply` is the object under `value`, whose `text` starts with `PASS`. To check the other branch, add a second test whose stub returns a `text` that starts with `FAIL`, and expect `Try again`.

103 

104A stub for a mods API call returns an object with a `value` field, which holds what the call resolves to in your mod: `{ value: 7 }` makes `$.store.get` resolve to `7`. A stub for one of Claude Code's events, such as [`turn.step`](/docs/en/plugins/mods/reference#turns) or `tool.call`, returns that event's own result, such as `{ result: 'ok' }`. `$.session.send` and `$.prompt.fill` take their event's result too, as the table shows. [Look up what a stub returns](#look-up-what-a-stub-returns) shows which form each common name takes. Two errors mean a stub is wrong or missing. A failed test's output includes a block headed `the engine reported:`, and each error appears there:

105 

106* `returned neither { value } nor { deny }`: a stub for a mods API call returned a bare value

107* `no implementation for` followed by a name: your mod made that call and no stub answers it

108 

109The kit also exports in-memory mocks that answer a whole namespace for you. `mock.clock(on)` answers [`$.clock`](/docs/en/plugins/mods/api#run-work-in-the-background), `mock.store(on, { count: 7 })` answers `$.store` from a store that starts with those entries, and `mock.env(on, { CI: 'true' })` answers `$.env.get` from those variables. `mock.clock` returns a mock clock that your test advances, so a test of a timer doesn't wait. `mock.store` returns nothing, so to check what your mod saved, write the two `store` stubs yourself as the [drawing test](#test-a-drawing) does.

110 

111### Follow the test kit's rules

112 

113The test kit has a few rules of its own, and breaking one produces the errors new test authors hit first:

114 

115* **Register every stub before the test's first call on `$`.** Calling `on` after that throws an error such as `on("ui.render") after the test first called $`.

116 

117* **[`session.start`](/docs/en/plugins/mods/reference#session) doesn't run by itself.** Each test starts with your module freshly loaded and none of its hooks called, so module-level variables hold their initial values. If a hook depends on what `session.start` sets up, raise it first:

118 

119 ```typescript theme={null}

120 // Answer the event after your hook passes it on with next(e)

121 on('session.start', () => ({ cwd: '/work' }))

122 // Answer the $.command.register call your hook makes

123 on('command.register', () => ({ value: undefined }))

124 // Raise the event, which runs your session.start hook

125 await $.session.start({ surface: 'terminal', isInteractive: true, cwd: '/work' })

126 ```

127 

128 The second stub answers the `$.command.register` call that a `session.start` hook such as the [tutorial's](/docs/en/plugins/mods/create#write-a-mod-yourself) makes. Without it, that call rejects with `no implementation for command.register` and the kit skips your hook, so nothing after the call in the hook runs. The test doesn't fail at that point. The skipped hook is listed under `the engine reported:` only if a later check fails.

129 

130* **A hook that returns `next(e)` needs a stub to answer.** When your [`ui.render`](/docs/en/plugins/mods/reference#interface) hook returns `next(e)`, for example to draw nothing while Claude is idle, [mounting it](#test-a-drawing) fails with `no implementation for ui.render`. Register a stub that returns an element as plain data:

131 

132 ```typescript theme={null}

133 // Stands for what Claude Code would draw at the site

134 on('ui.render', () => ({ type: 'Text', props: {}, children: ['drawn by Claude Code'] }))

135 ```

136 

137 With the stub registered, the mount succeeds, and `ui.find({ type: 'Text' })` returns that element whenever your hook returned `next(e)`.

138 

139* **A stub for `turn.step` is an async generator**, and the test reads the stream to its end to get the result:

140 

141 ```typescript theme={null}

142 on('turn.step', async function* ($, e) {

143 // Each yield is one piece of the model's streamed reply

144 yield { kind: 'text', index: 0, text: 'ok' }

145 // The return value is the result of the whole request

146 return { turnId: e.turnId, index: e.index, answer: 'ok', toolUses: [], stopReason: 'end_turn', usage: null }

147 })

148 

149 // Raise one request to the model, which runs your turn.step hook

150 const stream = $.turn.step({ turnId: 't', index: 0, model: 'claude-test', messageCount: 1 })

151 // Read every piece until the stream says it's done

152 let step = await stream.next()

153 while (step.done !== true) step = await stream.next()

154 const result = step.value

155 ```

156 

157 When the loop ends, `result` is the object the stub returned, after your `turn.step` hook has had the chance to change it. Here `result.answer` is `'ok'`.

158 

159* **Raise a tool call with the tool's name and arguments as fields**, such as `await $.tool.call({ tool: 'Bash', command: 'ls' })`, and register a `tool.call` stub that returns `{ result }`.

160 

161### Look up what a stub returns

162 

163Every mods API call your mod makes in a test needs a stub that answers in Claude Code's place, except the few the kit answers itself: [`$.ui.invalidate`](/docs/en/plugins/mods/interface#redraw-when-something-changes) and [`$.state`](/docs/en/plugins/mods/interface#keep-state) calls. For `$.clock` calls, use `mock.clock(on)`, or your mod's `$.clock.now()` fails with `no implementation for clock.now`.

164 

165This table lists the ones mods use most. The first column is the call your mod makes or the event it passes on with `next(e)`. The second is the function to pass to `on` under that name, so the `$.store.get` row becomes `on('store.get', ($, e) => ({ value: saved.get(e.key) }))`. A `'...'` in a stub marks text for you to fill in:

166 

167| Your mod calls or passes on | Stub |

168| :- | :- |

169| `$.command.register`, `$.tool.register`, `$.ui.toast`, `$.ui.log`, `$.ui.status`, `$.ui.close`, `$.store.set` | `() => ({ value: undefined })`. For `ui.toast` and `ui.log`, the text is `e.text`. |

170| `$.store.get` | `($, e) => ({ value: saved.get(e.key) })` |

171| `$.fs.read` | `($, e) => ({ value: e.path.endsWith('notes.md') ? '# Notes' : '' })`. `e.path` arrives as an absolute path, so compare with `endsWith`. |

172| `$.ui.open` | `() => ({ value: { isPlaced: true } })` |

173| `$.ui.ask` | A `tool.call` stub, because the question reaches it as a call to the `AskUserQuestion` tool: `($, e) => ({ result: { answers: { [e.questions[0].question]: 'Run it' } } })`. Check `e.tool` first if your mod passes on other tool calls. |

174| `$.model.complete` | `() => ({ value: { isAnswered: true, text: '...', usage } })` |

175| `$.process.run` | `($, e) => ({ value: { exitCode: 0, stdout: '...', stderr: '' } })`. `e.argv` is the argument list and `e.init` holds `cwd` and `timeoutMs`. |

176| Any mods API call that should fail | `() => ({ deny: 'the reason' })`, which makes the call reject in your mod. A stub that throws is skipped instead. |

177| `session.start` | `() => ({ cwd: '/work' })` |

178| `turn.start` | `($, e) => ({ turnId: e.turnId })` |

179| `tool.call` | `() => ({ result: '...' })` |

180| `turn.complete` | `() => ({ text: '' })`. Raise it with `$.turn.complete({ turnId, answer, durationMs, isAborted: false, usage: null })`. |

181| `prompt.submit` | `($, e) => ({ text: e.text })` |

182| `prompt.fill` | `() => ({ isFilled: true })` |

183| `$.prompt.read` | `() => ({ value: { text: '...', cursor: 0 } })` |

184| `$.ui.copy` | `() => ({ value: { isCopied: true } })` |

185| `$.session.messages` | `() => ({ value: [{ role: 'assistant', text: '...', toolUses: [] }] })` |

186| `$.session.id`, `$.agent.list` | `() => ({ value: 'abc123' })`, `() => ({ value: [] })` |

187| `session.send` | `() => ({ isDelivered: true })`. `e.to` arrives as a string even when your mod passed `{ sessionId }`. |

188| `session.receive` | `($, e) => ({ text: e.text })`. Raise it with `$.session.receive({ origin: { kind: 'peer-send-message' }, text })`. |

189| `ui.render` | `() => ({ type: 'Text', props: {}, children: ['...'] })` |

190 

191`expect` has the assertions `toBe`, `toEqual`, `toMatch`, `toMatchObject`, `toContain`, `toBeDefined`, `toBeUndefined`, and `toThrow`, and `.not` before any of them.

192 

193## Test a timer

194 

195A mod that runs work on a timer needs a clock the test controls, so the test can move time forward instead of waiting. `const clock = mock.clock(on)` returns a mock clock that starts at `0` and moves only when your test moves it. To start at another time, pass it in milliseconds, as in `mock.clock(on, { now: 5000 })`. The clock has these methods:

196 

197| Method | What it does |

198| :- | :- |

199| `await clock.advance(1000)` | Moves the time forward by that many milliseconds and runs each timer that comes due |

200| `await clock.set(5000)` | Moves the time forward to that value, as `advance` would |

201| `clock.now()` | Returns the time, which is what your mod's `$.clock.now()` resolves to |

202| `await clock.settle()` | Runs timers that are already due, such as a chain of zero-delay `$.clock.after` calls, without moving the time |

203| `await clock.sleep(2000)` | Inside a stub, makes that stub answer only once the test has advanced that far, which is how you simulate a slow model or process |

204 

205This hook belongs to a mod named `countdown`, and handles a `/countdown` command that takes a number of seconds, starts a one-second `$.clock.every` timer, and shows a toast at zero. As with `grader`, the file holds only the hook under test and doesn't register the command:

206 

207```javascript countdown/hooks/register.js theme={null}

208export function register(on) {

209 on('command.run', { command: 'countdown' }, async ($, e) => {

210 // e.args is the text typed after /countdown

211 let left = Number(e.args)

212 const timer = $.clock.every(1000, () => {

213 left -= 1

214 if (left === 0) {

215 timer.cancel()

216 $.ui.toast('Time is up')

217 }

218 })

219 // Print nothing in the transcript

220 return {}

221 })

222}

223```

224 

225This test runs `/countdown 3` and moves the mock clock, so it checks three seconds of behavior without waiting three seconds:

226 

227```typescript countdown/tests/countdown.test.ts theme={null}

228import { expect, mock, test } from 'claude-code/testing'

229 

230test('the countdown ends with a toast', async ($, on) => {

231 // Answer every $.clock call from a clock the test controls

232 const clock = mock.clock(on)

233 // Collect the text of each toast the mod shows

234 const toasts: string[] = []

235 on('ui.toast', ($, e) => {

236 toasts.push(e.text)

237 return { value: undefined }

238 })

239 

240 await $.command.run({ command: 'countdown', args: '3' })

241 // After two seconds the timer has fired twice, and no toast is due

242 await clock.advance(2000)

243 expect(toasts).toEqual([])

244 // The third second brings the count to zero

245 await clock.advance(1000)

246 expect(toasts).toEqual(['Time is up'])

247})

248```

249 

250The first `expect` shows that the toast doesn't come early, and the second shows that it comes once. Each `advance` resolves after the timers that came due have run, so the check on the next line sees their effect.

251 

252## Test a drawing

253 

254A test can draw one of your mod's [render sites](/docs/en/plugins/mods/reference#render-sites), then press, type into, and find the elements it drew. `$.ui.mount` draws the site through your mod's `ui.render` hook and returns a handle with a method for each of those. To cover several apps in one test, set `surface` to the app to draw for. This test opens the pane from [Build a pane with tabs](/docs/en/plugins/mods/interface#build-a-pane-with-tabs), switches tabs, presses the button, and checks the count in the terminal and the Desktop app:

255 

256```typescript hello-tabs/tests/hello-tabs.test.ts theme={null}

257import { expect, test } from 'claude-code/testing'

258 

259// What Claude Code passes to a ui.render hook for this pane, apart from the app

260const PANE = {

261 plugin: 'hello-tabs',

262 component: 'Pane',

263 requestId: 'hello-tabs',

264 viewport: { columns: 100, rows: 30 },

265 props: {

266 title: 'Hello tabs',

267 isFocused: true,

268 bodyColumns: 60,

269 placement: 'inline',

270 scroll: { offset: 0, bodyRows: 10 },

271 view: {},

272 },

273} as const

274 

275test('the second tab counts presses and saves the count', async ($, on) => {

276 // Stub $.store with a Map, so the test can read what the mod saved

277 const saved = new Map<string, unknown>()

278 on('store.get', ($, e) => ({ value: saved.get(e.key) }))

279 on('store.set', ($, e) => {

280 saved.set(e.key, e.value)

281 return { value: undefined }

282 })

283 

284 // Draw the pane once for each app

285 for (const surface of ['terminal', 'desktop'] as const) {

286 const ui = await $.ui.mount({ ...PANE, surface })

287 // Press the buttons by the key the mod gave them

288 await ui.press({ key: 'tab-two' })

289 await ui.press({ key: 'more' })

290 // The second tab's count line is in the drawing

291 expect(await ui.find({ type: 'Text', text: /^Count: \d+$/ })).toBeDefined()

292 await ui.unmount()

293 }

294 

295 // One press in each app makes two

296 expect(saved.get('count')).toBe(2)

297})

298```

299 

300In your shell, run `claude plugin test` from the `hello-tabs` directory. The test passes when both apps draw the count line and the mod has saved `2`. The count carries over from the first app to the second because both mounts use the same loaded module.

301 

302The handle that `$.ui.mount` returns has these methods, which address elements by the `key` you gave them:

303 

304| Method | What it does |

305| :- | :- |

306| `press({ key: 'more' })` | Presses the `Button` with that key |

307| `input({ key: 'new-note', text: 'buy milk' })` | Types the text into the `Input` with that key and presses Enter. Add `kind: 'change'` to type without submitting. |

308| `select({ key: 'size', value: 'large' })` | Picks the option with that value in the `Select` with that key |

309| `find({ key: 'more' })` or `find({ type: 'Text', text: 'Count: 2' })` | Returns the first matching element as `{ type, props, children }`, or `undefined`. `text` can be a string or a regular expression. |

310| `unmount()` | Removes the drawing |

311 

312Each method resolves after your handler has finished, so you can check the result on the next line. Set `props` to what Claude Code would pass for that site. The [render sites table](/docs/en/plugins/mods/reference#render-sites) lists each site's props, and [the types for your build](/docs/en/plugins/mods/create#get-the-types-for-your-build) have their types.

313 

314A drawing test checks the tree your hook returns and whether it's valid for that app. It doesn't check how the app paints it, so look at a new layout in a real session as well.

315 

316<h3 id="test-a-drawing-after-clear">

317 Test a drawing after `/clear`

318</h3>

319 

320Each test starts with every `$.state` value at its default, which is how `/clear` leaves them. To test what your mod does next, skip `session.start`, raise `classic.SessionStart` with `source: 'clear'`, and check what your mod draws.

321 

322This test checks the module from [Load a saved value again after `/clear`](/docs/en/plugins/mods/interface#load-a-saved-value-again-after-clear). Add it to the file from [Test a drawing](#test-a-drawing), where `PANE` is defined. That file's first test expects the button to save the count, as the button in [Save from more than one session](/docs/en/plugins/mods/interface#save-from-more-than-one-session) does:

323 

324```typescript hello-tabs/tests/hello-tabs.test.ts theme={null}

325test('the saved count comes back after /clear', async ($, on) => {

326 // The store already holds a count of 7

327 on('store.get', () => ({ value: 7 }))

328 // Answer the event after your hook passes it on with next(e)

329 on('classic.SessionStart', () => ({}))

330 

331 // Raise the event that fires after /clear, which runs your hook

332 await $.classic.SessionStart({ source: 'clear' })

333 

334 const ui = await $.ui.mount({ ...PANE, surface: 'terminal' })

335 await ui.press({ key: 'tab-two' })

336 // The pane shows the stored count, not the default of 0

337 expect(await ui.find({ type: 'Text', text: 'Count: 7' })).toBeDefined()

338})

339```

340 

341The test passes when your `classic.SessionStart` hook has copied the stored `7` into `$.state` before the pane draws. Without that hook in your module, the pane draws `Count: 0`, `find` returns `undefined`, and the test fails at `toBeDefined`.

342 

343## Test a mod that judges other mods

344 

345A mod your organization lists in [`prependPlugins`](/docs/en/plugins/mods/admin) can refuse another mod before it loads. To test one, set your mod's tier and give the test a second mod for yours to admit or refuse:

346 

347* **`tier`**: call it once at the top of the test file, as in `tier('prepend')`, to load your mod as `prepend`, `append`, or `builtin`, its place in the [order mods run in](/docs/en/plugins/mods/events#the-order-mods-run-in). Without it, your mod loads as `user`.

348* **`plugins`**: pass `test` an options object ahead of the test body. Its `plugins` array holds mods you write inline, each with a `name` and a `register` function. To load one somewhere other than `user`, add `tier` to it.

349 

350This test file loads the [policy mod from the admin page](/docs/en/plugins/mods/admin#enforce-a-policy-with-a-mod-of-your-own) first. It checks that the policy mod refuses a mod that starts a process and admits one that doesn't:

351 

352```typescript acme-guard/tests/guard.test.ts theme={null}

353import { expect, test, tier } from 'claude-code/testing'

354 

355// Load the mod under test ahead of every other mod

356tier('prepend')

357 

358// A second mod whose code calls $.process.run, which the policy blocks

359const runner = {

360 name: 'runner',

361 register(on) {

362 on('tool.call', async ($, e, next) => {

363 await $.process.run(['ls'])

364 return { result: 'runner answered' }

365 })

366 },

367}

368 

369// A second mod that calls nothing the policy blocks

370const reader = {

371 name: 'reader',

372 register(on) {

373 on('tool.call', async ($, e, next) => {

374 return { result: 'reader answered' }

375 })

376 },

377}

378 

379test('refuses a mod that starts a process', { plugins: [runner] }, async ($, on) => {

380 on('tool.call', () => ({ result: 'claude code answered' }))

381 let message = ''

382 try {

383 // The first call on $ loads the mods, so the refusal is thrown here

384 await $.tool.call({ tool: 'Bash', command: 'ls' })

385 } catch (error) {

386 message = error.message

387 }

388 expect(message).toBe('runner: refused by acme-guard: Acme policy: mods may not call process.run')

389})

390 

391test('admits a mod that starts no process', { plugins: [reader] }, async ($, on) => {

392 on('tool.call', () => ({ result: 'claude code answered' }))

393 const out = await $.tool.call({ tool: 'Bash', command: 'ls' })

394 // The answer comes from reader, which shows that it loaded

395 expect(out).toEqual({ result: 'reader answered' })

396})

397```

398 

399In your shell, run `claude plugin test` from the `acme-guard` directory. Both tests pass with the policy mod as the admin page shows it.

400 

401The kit loads every mod at the test's first call on `$`. When your mod refuses one, that call throws, and the message names the refused mod, the mod that refused it, and your reason. In the second test nothing is refused, so `reader` answers the tool call before it reaches the stub.

402 

403## Next steps

404 

405* [Troubleshoot a mod](/docs/en/plugins/mods/troubleshoot): find out why a mod does nothing in a session

406* [Mods reference](/docs/en/plugins/mods/reference): every event's input and result, for writing stubs

plugins/mods/troubleshoot.md +231 −0 created

Details

1> ## Documentation Index

2> Fetch the complete documentation index at: https://code.claude.com/docs/llms.txt

3> Use this file to discover all available pages before exploring further.

4 

5# Troubleshoot a mod

6 

7> Find out why a Claude Code mod does nothing: match the symptom or message to its cause, look up refusal messages, and read the debug log.

8 

9When a mod's module or one of its hooks fails, Claude Code skips it and the session continues, so a broken mod can look like one that does nothing. Start by checking what Claude Code read from your mod and where it reports a problem, then find the symptom or message you have.

10 

11## Find out why a mod does nothing

12 

13When a mod does nothing, two checks find the reason: what Claude Code reads from the mod's files, and the line it writes when it skips something. For the first, in your shell run [`claude plugin validate`](/docs/en/plugins/mods/create#check-what-claude-code-reads-from-your-mod) with the mod's directory, as in `claude plugin validate ./first-mod`. It catches a misspelled event, a bad manifest, and a module Claude Code can't read, without starting a session.

14 

15When a module doesn't load, a hook is skipped, or another mod refuses yours, Claude Code writes one line that names your mod. Where you read that line depends on the session:

16 

17* **A session that hot-reloads a plugin directory**: a dim line in the transcript. That's an interactive session you started with `--plugin-dir`, or one where you [enabled hot reloading](/docs/en/plugins/mods/create#ask-claude-for-a-mod) for mods Claude wrote.

18* **Any other interactive session, such as one that runs a mod you installed from a marketplace**: the [debug log](#read-the-debug-log) only. To get one, start the session with `claude --debug`.

19* **A `claude -p` run with `--plugin-dir`**: stderr, in the default text output format. A refusal by another mod goes to the debug log only.

20 

21## Check whether mods can load

22 

23To check whether your setup lets mods load at all, without installing one, run `claude plugin test` in your shell, from a directory that doesn't hold a mod. You don't need a session. The message it prints tells you the state:

24 

25| Message includes | What it means |

26| :- | :- |

27| `no hooks module to load` | Mods can load. The command found no mod to test in this directory. |

28| `hooks modules are turned off here` | A setting is keeping your mods out: `disableAllHooks` in your own settings, or your organization's policy |

29| `hooks modules are turned off in this process` | Anthropic has turned installed mods off remotely. No setting on your machine turns them back on. |

30 

31An organization can also set `allowManagedModsOnly` to allow only its own mods, which this command doesn't report. In that case a mod you install doesn't load, and [a message says why](/docs/en/plugins/mods/troubleshoot#messages-from-the-built-in-guard).

32 

33## The mod doesn't load

34 

35Nothing the mod adds appears: no command, no drawing, and no change in behavior.

36 

37### Your version is older than 2.1.287

38 

39`claude --version` prints a version older than 2.1.287. Your version predates mods being on by default.

40 

41[Update Claude Code](/docs/en/setup#update-claude-code).

42 

43### The `mods active` line doesn't name the mod

44 

45Nothing the mod adds appears, and the [`mods active` line](/docs/en/plugins/mods/overview#see-which-mods-a-session-loaded) in `/plugin` doesn't name it. The hooks module didn't load. When Claude Code refused it, the debug log has a line that starts with `hooks module`, the mod's name, and `not loaded:`, as in `hooks module first-mod@inline not loaded: disableAllHooks in managed settings` for a mod loaded with `--plugin-dir`.

46 

47Read the reason after the colon. The [refusal messages](#refusal-messages) section lists each one. If the log has no such line, work through the other entries in this group.

48 

49### A `claude -p` run prints `hooks module not loaded`

50 

51The line starts with the mod's name and goes to stderr. The hooks module was refused. A non-interactive run has no transcript, so the message goes to stderr.

52 

53Read the reason after the colon. The [refusal messages](#refusal-messages) section lists each one.

54 

55### Refusal messages

56 

57Each of these follows `hooks module`, the mod's name, and `not loaded:` in the debug log.

58 

59| Message starts with | What it means |

60| :- | :- |

61| `hooks modules are turned off for installed plugins in this process` | Anthropic has turned installed mods off remotely. No setting on your machine turns them back on. |

62| `disableAllHooks in managed settings` | Your organization turned off hooks from installed plugins |

63| `only managed plugins and built-in plugins run` | `allowManagedHooksOnly` is set, or `disableAllHooks` is set in a settings file other than managed settings |

64| `installed plugins that are not managed load no hooks module in this mode (--bare)` | You started Claude Code with `--bare` |

65| `another plugin of that name loads first` | Two plugins share a name. The managed one, or the one loaded first, is used. |

66 

67### Messages from the built-in guard

68 

69On a machine with managed settings, or for a user signed in with a Team or Enterprise plan, the [built-in guard](/docs/en/plugins/mods/admin#know-what-happens-by-default) can refuse a mod or one of its answers. Each message names the option your organization's administrator sets to change the rule.

70 

71| Message contains | What it means | Where it appears |

72| :- | :- | :- |

73| `mods are limited to your organization's by policy (allowManagedModsOnly)` | Your organization allows only [its own mods](/docs/en/plugins/mods/admin#install-your-organizations-mods), so yours wasn't loaded | The debug log, and the transcript in a [session that hot-reloads a plugin directory](#find-out-why-a-mod-does-nothing) |

74| `tried to lift a deny rule in your settings` | Your mod's [`tool.check`](/docs/en/plugins/mods/reference#tools) hook approved a call that a `deny` rule refuses. The call stays denied. | The transcript and the debug log, once for each mod in a session. In a `claude -p` run, the debug log only. |

75| `the deny rules in your settings could not be checked for this call, so it is refused` | The guard failed while checking a call that a mod approved, so it refused the call | The reason Claude reads for the denied call |

76 

77### `validate` passes and lists no `hooks` line

78 

79`hooks/hooks.json` has no `modules` key, or the key is misspelled.

80 

81Add `"modules": ["./register.js"]`.

82 

83### `hooks module did not load`

84 

85The line starts with the mod's name, then `hooks module did not load:` and a reason, which gives the file and line when the problem is in your code. Claude Code couldn't load the module, for example because its top-level code threw.

86 

87Fix the error the reason names.

88 

89### `options do not fit plugin.json userConfig`

90 

91The line starts with the mod's name, then `hooks module did not load: options do not fit plugin.json userConfig:` and a reason. An option doesn't fit its [`userConfig`](/docs/en/plugins/components#user-configuration) field, such as a number above the field's `max`, or a required field has no value.

92 

93Set or change the value. The end of the line names its `pluginConfigs` entry in `settings.json`.

94 

95### No mod loads in a directory you opened for the first time

96 

97You haven't answered the trust prompt for the directory.

98 

99Start an interactive session in that directory with `claude`, and accept the trust prompt it opens with.

100 

101### No installed plugin loads at all

102 

103You started Claude Code with `--safe-mode`.

104 

105Start without the flag.

106 

107## A hook is skipped or a mod is unloaded

108 

109The mod loaded, and then Claude Code skipped one of its hooks or unloaded it.

110 

111### `hook skipped`

112 

113The line names the mod and the event, then says `hook skipped:` and a reason, as in `first-mod: tool.call hook skipped: threw Error: boom`. A hook threw, ran past its [10-second time limit](/docs/en/plugins/mods/reference#limits), or returned a result of the wrong shape. The line appears once for each event and kind of failure until the mod reloads.

114 

115Fix the error. The debug log has a line for every occurrence.

116 

117### `no command.run hook answered it`

118 

119You run a command your mod added, and the reply names the mod and the command, as in `first-mod registered /tally but no command.run hook answered it`, then tells you to add a hook. Claude Code prints that reply when the command reaches the end of the chain with no answer, which happens in two cases:

120 

121* **No hook answered the command**: the module has no `command.run` hook, the hook's [filter](/docs/en/plugins/mods/events#filter-which-events-a-hook-handles) names a different command, or the hook returned `next(e)`

122* **Claude Code skipped the hook**: [`hook skipped`](#hook-skipped) lists the reasons. Passing `focus: false` to [`$.ui.open`](/docs/en/plugins/mods/interface#open-a-pane-at-the-right-time) is one way to get there.

123 

124If the module already has the hook the reply describes, look for a `hook skipped` line that names `command.run`, which gives the reason. A [test](/docs/en/plugins/mods/test) that runs the command fails with the same reason.

125 

126### `it crashed the hooks worker`

127 

128The line starts with the mod's name, as in `first-mod was unloaded: it crashed the hooks worker`. Installed mods share one worker thread. The worker stopped responding or crashed, and Claude Code traced that to this mod and unloaded it. A hook that blocks the thread, such as a loop that never awaits, is one cause.

129 

130Fix the hook.

131 

132### `mods that run in the hooks worker are off for this session`

133 

134The line reads `hooks: mods that run in the hooks worker are off for this session: it crashed 3 times`. The worker stopped three times and Claude Code couldn't trace the stops to one mod, so it unloaded every mod that isn't built in, including mods your organization installs. This line reaches the transcript in every interactive session.

135 

136Run `/reload-plugins` to load them again.

137 

138## A tool call is denied

139 

140The mod loaded and its hooks run, and a tool call it touched is refused.

141 

142### `a hook changed this call's input after the model wrote it`

143 

144In auto mode, a denied tool call gives this reason. A hook changed the tool call's input after the [server-side classifier](/docs/en/permission-modes#server-side-classifier-review) reviewed it, so that review doesn't cover what would run. The hook can be a mod's [`tool.call`](/docs/en/plugins/mods/reference#tools) or [`turn.step`](/docs/en/plugins/mods/reference#turns) hook, or a [`PreToolUse`](/docs/en/hooks#pretooluse) settings hook. The message doesn't say which.

145 

146The message tells Claude to issue the call once more as recorded. If that's denied too, the hook changes the input every time, so turn off the mod or hook, or leave auto mode and approve the call yourself.

147 

148### A message about the deny rules in your settings

149 

150`tried to lift a deny rule in your settings` and `the deny rules in your settings could not be checked for this call, so it is refused` both come from the built-in guard.

151 

152Look them up in [Messages from the built-in guard](#messages-from-the-built-in-guard).

153 

154## A drawing doesn't appear or respond

155 

156The mod loaded, and its pane, band, or controls don't behave as you expect.

157 

158### A pane or band is empty or shows Claude Code's usual content

159 

160The [tree](/docs/en/plugins/mods/interface#build-a-tree-from-elements) your hook returned didn't validate. With `--plugin-dir`, the transcript says `ui.render (Pane) refused:` with the reason, as in `first-mod: ui.render (Pane) refused: Box prop "flexDirection" must be one of row, column, row-reverse, column-reverse; the engine drew its own`. The debug log has `a hook returned a tree that does not validate` with the same reason.

161 

162Read the reason on that line. Common causes are a prop the element doesn't take and an element the app doesn't have.

163 

164### `$.ui.open` runs and no pane appears

165 

166The call didn't come from something the user did, and the terminal is narrower than 144 columns.

167 

168Open the pane from a command or a button, or check the call's `isPlaced` result. See [Open a pane at the right time](/docs/en/plugins/mods/interface#open-a-pane-at-the-right-time).

169 

170### Hotkeys do nothing

171 

172Your pane doesn't have keyboard focus.

173 

174Press Ctrl+X then Tab, or click the pane. Open it with `focus: true` from a command.

175 

176### A drawing works in the terminal and not in the Desktop app

177 

178The site or element isn't available there.

179 

180Check the [render sites](/docs/en/plugins/mods/reference#render-sites) and [elements](/docs/en/plugins/mods/reference#elements) tables.

181 

182## An edit or a value is lost

183 

184The mod runs, and a change you made or a value it kept isn't there.

185 

186### Your edits don't take effect

187 

188You're editing a plugin you installed. Claude Code runs the cached copy for the installed version.

189 

190Develop with `--plugin-dir` pointed at your working copy, as in `claude --plugin-dir ./first-mod`, which reloads when you save.

191 

192### A value resets when the module reloads

193 

194Module-level variables are re-initialized on each reload.

195 

196[Keep the value in `$.state` or `$.store`](/docs/en/plugins/mods/interface#keep-state).

197 

198### A value resets after `/clear`, `/resume`, or `/branch`

199 

200A value resets, or a saved value is replaced by its default. Each of those commands resets `$.state` to its defaults, and `session.start` doesn't fire again.

201 

202[Load the saved value again](/docs/en/plugins/mods/interface#load-a-saved-value-again-after-clear) in a `classic.SessionStart` hook.

203 

204## Read the debug log

205 

206The debug log has a line for every module Claude Code loads or refuses, every hook that fails, and every result it refuses, so it's where to look when the transcript shows nothing. To write one, in your shell start Claude Code with `--debug`, or with `--debug-file <path>` to choose where it goes:

207 

208```bash theme={null}

209claude --debug-file ./mod-debug.log --plugin-dir ./first-mod

210```

211 

212In another terminal, follow the file and filter for your mod's name:

213 

214```bash theme={null}

215tail -f ./mod-debug.log | grep first-mod

216```

217 

218A mod that loaded has a line that names it and lists the events it hooks. A mod loaded with `--plugin-dir` appears under its name followed by `@inline`:

219 

220```text theme={null}

221hooks module first-mod@inline loaded (worker, environment 2, tier user); events: session.start,tool.call,command.run,ui.render

222```

223 

224A drawing that didn't validate counts as a refused result and gets a line too. To write your own lines in the log, call [`$.ui.log`](/docs/en/plugins/mods/api#show-something-without-starting-a-turn) with a second argument, as in `$.ui.log('message', { to: 'debug' })`. Without the second argument, `$.ui.log` adds a dim line to the transcript.

225 

226While you edit a mod loaded with `--plugin-dir`, the transcript shows a line for each reload that names the mod and lists its hooks. If a save breaks the module, the line says `reload failed, the previous version stays loaded:` with the reason, and the last working version keeps running.

227 

228## Next steps

229 

230* [Test a mod](/docs/en/plugins/mods/test): catch problems before they reach a session

231* [Troubleshoot plugins](/docs/en/plugins/troubleshooting): problems with installing and loading a plugin that aren't specific to mods

plugins/org.md +4 −1

Details

192| `pluginTrustMessage` | Appends your text to the trust warning that `/plugin` shows before a plugin installs | Doesn't change the warning's own text |192| `pluginTrustMessage` | Appends your text to the trust warning that `/plugin` shows before a plugin installs | Doesn't change the warning's own text |

193| `allowedChannelPlugins` | Replaces the default list of plugins allowed to push channel messages. Requires `channelsEnabled: true` | See [Restrict which channel plugins can run](/docs/en/channels#restrict-which-channel-plugins-can-run) |193| `allowedChannelPlugins` | Replaces the default list of plugins allowed to push channel messages. Requires `channelsEnabled: true` | See [Restrict which channel plugins can run](/docs/en/channels#restrict-which-channel-plugins-can-run) |

194| [`CLAUDE_CODE_DISABLE_OFFICIAL_MARKETPLACE_AUTOINSTALL=1`](/docs/en/env-vars) | Stops interactive terminal sessions from auto-registering the official marketplace | Doesn't remove a marketplace already registered. The allowlist and blocklist gate the same auto-registration without it. A machine that started once with it set doesn't resume auto-registration after you unset it |194| [`CLAUDE_CODE_DISABLE_OFFICIAL_MARKETPLACE_AUTOINSTALL=1`](/docs/en/env-vars) | Stops interactive terminal sessions from auto-registering the official marketplace | Doesn't remove a marketplace already registered. The allowlist and blocklist gate the same auto-registration without it. A machine that started once with it set doesn't resume auto-registration after you unset it |

195| [`allowManagedModsOnly`](/docs/en/plugins/mods/admin#stop-user-installed-mods-from-loading) | Stops every installed [mod](/docs/en/plugins/mods/overview) that doesn't [count as your organization's](/docs/en/plugins/mods/admin#install-your-organizations-mods) from loading | Doesn't stop a plugin that contains a mod from installing. For that, use the marketplace keys in this table |

195 196 

196Every key in the table is a managed setting, apart from `enabledPlugins`, `syncClaudeAiPlugins`, and `CLAUDE_CODE_DISABLE_OFFICIAL_MARKETPLACE_AUTOINSTALL`:197Every key in the table is a managed setting, apart from `enabledPlugins`, `syncClaudeAiPlugins`, `CLAUDE_CODE_DISABLE_OFFICIAL_MARKETPLACE_AUTOINSTALL`, and `allowManagedModsOnly`:

197 198 

198* **`enabledPlugins`**: you can set it in any scope, and managed settings lock it.199* **`enabledPlugins`**: you can set it in any scope, and managed settings lock it.

199* **`syncClaudeAiPlugins`**: each user can also set it in their own user or local settings. See its [scope in the settings reference](/docs/en/settings-reference#syncclaudeaiplugins).200* **`syncClaudeAiPlugins`**: each user can also set it in their own user or local settings. See its [scope in the settings reference](/docs/en/settings-reference#syncclaudeaiplugins).

200* **`CLAUDE_CODE_DISABLE_OFFICIAL_MARKETPLACE_AUTOINSTALL`**: this is an environment variable that you deliver through the managed `env` block shown under [Turn updates off for the whole fleet](#turn-updates-off-for-the-whole-fleet).201* **`CLAUDE_CODE_DISABLE_OFFICIAL_MARKETPLACE_AUTOINSTALL`**: this is an environment variable that you deliver through the managed `env` block shown under [Turn updates off for the whole fleet](#turn-updates-off-for-the-whole-fleet).

202* **`allowManagedModsOnly`**: this is an option on a built-in plugin, which you set under `pluginConfigs` in managed settings. See [Stop user-installed mods from loading](/docs/en/plugins/mods/admin#stop-user-installed-mods-from-loading).

201 203 

202Each settings key here has an entry in the [settings reference](/docs/en/settings-reference).204Each settings key here has an entry in the [settings reference](/docs/en/settings-reference).

203 205 


399* [Marketplace reference](/docs/en/plugins/marketplace-reference#marketplace-sources): the `source` values `extraKnownMarketplaces`, `strictKnownMarketplaces`, and `blockedMarketplaces` accept401* [Marketplace reference](/docs/en/plugins/marketplace-reference#marketplace-sources): the `source` values `extraKnownMarketplaces`, `strictKnownMarketplaces`, and `blockedMarketplaces` accept

400* [Host and maintain a marketplace](/docs/en/plugins/host-marketplace): run the marketplace your policy points at402* [Host and maintain a marketplace](/docs/en/plugins/host-marketplace): run the marketplace your policy points at

401* [Plugin security and trust](/docs/en/plugins/security): what a plugin can do on a machine and how to review one before installing403* [Plugin security and trust](/docs/en/plugins/security): what a plugin can do on a machine and how to review one before installing

404* [Manage mods for your organization](/docs/en/plugins/mods/admin): turn off or limit mods, the plugins that run JavaScript inside Claude Code

402* [Server-managed settings](/docs/en/server-managed-settings): deliver these keys from the claude.ai admin console405* [Server-managed settings](/docs/en/server-managed-settings): deliver these keys from the claude.ai admin console

403* [Troubleshoot plugins](/docs/en/plugins/troubleshooting#blocked-by-your-organization): the messages users see when policy blocks them406* [Troubleshoot plugins](/docs/en/plugins/troubleshooting#blocked-by-your-organization): the messages users see when policy blocks them

Details

15 * **You built an MCP server and want it in Anthropic's directory**: see [Publish to the directory](https://claude.com/docs/directory/publish)15 * **You built an MCP server and want it in Anthropic's directory**: see [Publish to the directory](https://claude.com/docs/directory/publish)

16</Note>16</Note>

17 17 

18To try a plugin now, run `/plugin` in a Claude Code terminal session and install one from the **Discover** tab, which lists the plugins from Anthropic's official marketplace and any marketplace you've added. From there:18To try a plugin now, run `/plugin` in a Claude Code terminal session and install one from the **Discover** tab, which lists the plugins from your marketplaces. From there:

19 19 

20* [Install and manage plugins](/docs/en/plugins/install): the full install steps, scopes, and other surfaces20* [Install and manage plugins](/docs/en/plugins/install): the full install steps, scopes, and other surfaces

21* [Create a plugin](/docs/en/plugins/create): build your own21* [Create a plugin](/docs/en/plugins/create): build your own


28* [**Skills**](/docs/en/plugins/components#skills): `SKILL.md` instructions Claude loads when relevant, and that you can also run as a command28* [**Skills**](/docs/en/plugins/components#skills): `SKILL.md` instructions Claude loads when relevant, and that you can also run as a command

29* [**Agents**](/docs/en/plugins/components#agents): subagent definitions Claude can delegate to29* [**Agents**](/docs/en/plugins/components#agents): subagent definitions Claude can delegate to

30* [**Hooks**](/docs/en/plugins/components#hooks): commands Claude Code runs at points in its lifecycle, such as after every edit30* [**Hooks**](/docs/en/plugins/components#hooks): commands Claude Code runs at points in its lifecycle, such as after every edit

31* [**A hooks module**](/docs/en/plugins/mods/overview): hooks written as JavaScript functions, which can also draw panes and add commands. A plugin that has one is called a mod

31* [**MCP servers**](/docs/en/plugins/components#mcp-servers): tool servers Claude Code connects to while the plugin is enabled32* [**MCP servers**](/docs/en/plugins/components#mcp-servers): tool servers Claude Code connects to while the plugin is enabled

32 33 

33This diagram shows a plugin named `my-plugin` that holds one of each of those components, and what you get from each file once the plugin loads.34This diagram shows a plugin named `my-plugin` that holds a skill, an agent, hooks, and an MCP server, and what you get from each file once the plugin loads.

34 35 

35<img src="https://mintcdn.com/claude-code/2Q_GtOEovg5qaBem/images/plugin-directory.svg?fit=max&auto=format&n=2Q_GtOEovg5qaBem&q=85&s=f623b64e82713b830e48174f0a922888" className="dark:hidden" alt="Diagram in two columns joined by five straight arrows. Left, the directory of a plugin named my-plugin, holding a manifest at .claude-plugin/plugin.json, skills/review/SKILL.md, agents/reviewer.md, hooks/hooks.json, .mcp.json, and other components. Right, what each file gives you in your session: the manifest sets the plugin name, my-plugin; the skill runs as /my-plugin:review; the agent file is a subagent Claude can delegate to; the hooks file holds hooks that run on lifecycle events; and .mcp.json adds an MCP server that gives Claude tools." width="760" height="336" data-path="images/plugin-directory.svg" />36<img src="https://mintcdn.com/claude-code/2Q_GtOEovg5qaBem/images/plugin-directory.svg?fit=max&auto=format&n=2Q_GtOEovg5qaBem&q=85&s=f623b64e82713b830e48174f0a922888" className="dark:hidden" alt="Diagram in two columns joined by five straight arrows. Left, the directory of a plugin named my-plugin, holding a manifest at .claude-plugin/plugin.json, skills/review/SKILL.md, agents/reviewer.md, hooks/hooks.json, .mcp.json, and other components. Right, what each file gives you in your session: the manifest sets the plugin name, my-plugin; the skill runs as /my-plugin:review; the agent file is a subagent Claude can delegate to; the hooks file holds hooks that run on lifecycle events; and .mcp.json adds an MCP server that gives Claude tools." width="760" height="336" data-path="images/plugin-directory.svg" />

36 37 


66 A plugin marketplace isn't [Claude Marketplace](https://claude.com/marketplace). Claude Marketplace is the website at claude.com/marketplace where you browse plugins, connectors, partner products, and service partners. It isn't a marketplace you add with `/plugin marketplace add`.67 A plugin marketplace isn't [Claude Marketplace](https://claude.com/marketplace). Claude Marketplace is the website at claude.com/marketplace where you browse plugins, connectors, partner products, and service partners. It isn't a marketplace you add with `/plugin marketplace add`.

67</Note>68</Note>

68 69 

69Claude Code adds Anthropic's official marketplace the first time you start an interactive terminal session, unless a [managed policy](/docs/en/plugins/org#allow-the-official-marketplace-and-your-own) blocks it. Claude Code adds no other marketplace on its own, including Anthropic's community and demo marketplaces. To distinguish the three Anthropic marketplaces, read [Anthropic's marketplaces](/docs/en/plugins/anthropic-marketplaces). To see what the official one lists, open the **Discover** tab of `/plugin` in a session or browse [Claude Marketplace](https://claude.com/marketplace/plugins).70Claude Code adds Anthropic's official marketplace the first time you start an interactive terminal session, unless a [managed policy](/docs/en/plugins/org#allow-the-official-marketplace-and-your-own) blocks it. Claude Code doesn't add Anthropic's community and demo marketplaces on its own. To distinguish the three Anthropic marketplaces, read [Anthropic's marketplaces](/docs/en/plugins/anthropic-marketplaces). To see what the official one lists, open the **Discover** tab of `/plugin` in a session or browse [Claude Marketplace](https://claude.com/marketplace/plugins).

70 71 

71This diagram shows the path from a marketplace to your session. A marketplace lists a plugin, you install that plugin, and Claude Code loads its components.72This diagram shows the path from a marketplace to your session. A marketplace lists a plugin, you install that plugin, and Claude Code loads its components.

72 73 

Details

27A plugin can carry content that runs code on your machine with your user privileges and content that enters Claude's context as instructions, so [review a plugin before you install it](#review-a-plugin-before-you-install). Here's what an installed plugin can do:27A plugin can carry content that runs code on your machine with your user privileges and content that enters Claude's context as instructions, so [review a plugin before you install it](#review-a-plugin-before-you-install). Here's what an installed plugin can do:

28 28 

29* **Hooks**: a plugin's [hooks](/docs/en/hooks) run as shell commands at points in Claude Code's lifecycle, such as before or after a tool call.29* **Hooks**: a plugin's [hooks](/docs/en/hooks) run as shell commands at points in Claude Code's lifecycle, such as before or after a tool call.

30* **Mods**: a plugin's [mod](/docs/en/plugins/mods/overview) runs JavaScript inside Claude Code with your permissions. To list what a mod does before you install it, see [Decide whether to trust a mod](/docs/en/plugins/mods/overview#decide-whether-to-trust-a-mod).

30* **MCP and LSP servers**: Claude Code connects to the [MCP servers](/docs/en/mcp) an enabled plugin declares and gives Claude their tools. A stdio MCP server runs as a process that Claude Code starts on your machine. Claude Code also starts the language servers the plugin declares.31* **MCP and LSP servers**: Claude Code connects to the [MCP servers](/docs/en/mcp) an enabled plugin declares and gives Claude their tools. A stdio MCP server runs as a process that Claude Code starts on your machine. Claude Code also starts the language servers the plugin declares.

31* **`bin/` directory**: Claude Code adds each enabled plugin's `bin/` directory to the `PATH` of the Bash tool's shell, so Claude's Bash commands can run any executable there.32* **`bin/` directory**: Claude Code adds each enabled plugin's `bin/` directory to the `PATH` of the Bash tool's shell, so Claude's Bash commands can run any executable there.

32* **Skills, commands, and agents**: these enter Claude's context as instructions, so they influence what Claude does with the tools it already has.33* **Skills, commands, and agents**: these enter Claude's context as instructions, so they influence what Claude does with the tools it already has.


35Claude Code's [permission rules](/docs/en/permissions) and [sandbox](/docs/en/sandboxing) cover the tool calls Claude makes, not the code a plugin runs by itself:36Claude Code's [permission rules](/docs/en/permissions) and [sandbox](/docs/en/sandboxing) cover the tool calls Claude makes, not the code a plugin runs by itself:

36 37 

37* **Hooks and server processes**: command hooks execute shell commands with your full user permissions. Claude Code runs hooks and MCP servers outside the sandbox.38* **Hooks and server processes**: command hooks execute shell commands with your full user permissions. Claude Code runs hooks and MCP servers outside the sandbox.

38* **Claude's tool calls**: a call to one of the plugin's MCP tools, and a Bash command that runs an executable from the plugin's `bin/`, are tool calls, so your permission rules apply to them.39* **Claude's tool calls**: a call to one of the plugin's MCP tools, and a Bash command that runs an executable from the plugin's `bin/`, are tool calls, so your permission rules apply to them. For what a mod can do to a tool call, see [Decide whether to trust a mod](/docs/en/plugins/mods/overview#decide-whether-to-trust-a-mod).

39 40 

40Installing a plugin also enables it, unless its manifest or marketplace entry sets [`defaultEnabled: false`](/docs/en/plugins/install#choose-an-install-scope) and you haven't enabled it yourself.41Installing a plugin also enables it, unless its manifest or marketplace entry sets [`defaultEnabled: false`](/docs/en/plugins/install#choose-an-install-scope) and you haven't enabled it yourself.

41 42 

Details

197 197 

198A successful add prints `Successfully added marketplace: <name>`.198A successful add prints `Successfully added marketplace: <name>`.

199 199 

200<h3 id="invalid-git-url">

201 `Invalid git URL`

202</h3>

203 

204You added a marketplace, installed a plugin, or ran an update from a git address, and the command failed with `Invalid git URL` in its message.

205 

206Claude Code checks every git address before running git. It refuses an address whose protocol it doesn't support. It also refuses an address that git could read as naming a different server or folder than the one the address shows.

207 

208The text after the address names what to change. Rewrite the address as the message says and run the command again.

209 

210A refusal that instead says `is blocked by enterprise policy` comes from your organization's settings. See [Marketplace source is blocked by enterprise policy](#marketplace-source-is-blocked-by-enterprise-policy).

211 

200<h3 id="path-does-not-exist">212<h3 id="path-does-not-exist">

201 `Path does not exist: <path>`213 `Path does not exist: <path>`

202</h3>214</h3>


404 416 

405`claude plugin install` in your shell prints a different message. For a plugin already installed at the target scope, it prints `Plugin "<name>@<marketplace>" is already installed (scope: user)` and exits 0. If its cache directory is missing, the same command re-downloads it.417`claude plugin install` in your shell prints a different message. For a plugin already installed at the target scope, it prints `Plugin "<name>@<marketplace>" is already installed (scope: user)` and exits 0. If its cache directory is missing, the same command re-downloads it.

406 418 

419<h3 id="plugin-would-share-its-folder">

420 `"<plugin>" was not installed: it would share its folder with "<other>"`

421</h3>

422 

423You installed a plugin through `claude plugin install`, `/plugin`, or an install suggestion in a session, and Claude Code refused it with this line, or with `would share its saved data with`.

424 

425The refused plugin's id and an installed plugin's id map to the same folder on disk: they are the same once `.` and `@` are written as `-`. On macOS and Windows, ids that differ only in capitals map to the same folder too. Installing both would put one plugin's files in the other's folder, so Claude Code refuses and the installed plugin keeps its files.

426 

427The message names the way out:

428 

429* **The other plugin is installed**: the message says `Only one of the two can be installed.` and names the `claude plugin uninstall` command, or the uninstall step in `/plugin`, that removes the other plugin. Run it, then install again. For what the uninstall removes, see [What an uninstall deletes and keeps](/docs/en/plugins/cli-reference#what-an-uninstall-deletes-and-keeps).

430* **Both ids arrive in one install**, such as a plugin and a dependency it needs: no install order helps. Only a maintainer of the marketplace that lists the two plugins can fix it, by renaming one of them. When the two come from different marketplaces, a maintainer of either one can.

431 

407<h3 id="this-plugin-uses-a-source-type-your-claude-code-version-does-not-suppo">432<h3 id="this-plugin-uses-a-source-type-your-claude-code-version-does-not-suppo">

408 `This plugin uses a source type your Claude Code version does not support`433 `This plugin uses a source type your Claude Code version does not support`

409</h3>434</h3>


772* **`URL is unset or invalid`**: a `${user_config.*}` option that the URL uses isn't set. Run `/plugin configure <plugin>` to set it797* **`URL is unset or invalid`**: a `${user_config.*}` option that the URL uses isn't set. Run `/plugin configure <plugin>` to set it

773* **`has an invalid MCP url`** or **`headersHelper for MCP server '<server>' references ${user_config.*}`**: the plugin's own configuration is at fault. Fix the `url` or `headersHelper` in your plugin's MCP configuration, or report it to the plugin's author if the plugin isn't yours. The `headersHelper` case has its own entry under [plugin command references user\_config](/docs/en/errors#plugin-command-references-user-config)798* **`has an invalid MCP url`** or **`headersHelper for MCP server '<server>' references ${user_config.*}`**: the plugin's own configuration is at fault. Fix the `url` or `headersHelper` in your plugin's MCP configuration, or report it to the plugin's author if the plugin isn't yours. The `headersHelper` case has its own entry under [plugin command references user\_config](/docs/en/errors#plugin-command-references-user-config)

774 799 

800#### `Bundled MCP server "<name>" was not started: it needs configuration`

801 

802The plugin includes the server as an [MCPB bundle](/docs/en/plugins/components#include-a-packaged-mcpb-server) that declares `user_config`, and a required setting has no saved value yet or a saved value fails the bundle's own validation, so Claude Code skips starting the server. The rest of the plugin works.

803 

804Select the plugin on `/plugin`'s **Installed** tab and choose **Configure** to supply the values. After you save, `/plugin` shows `Configuration saved.` and closes, and Claude Code reloads plugins as described under [Manage installed plugins](/docs/en/plugins/install#manage-installed-plugins). The server starts once that reload applies. Before v2.1.285, Claude Code skipped the server without showing this line.

805 

775#### Server is configured but never connects806#### Server is configured but never connects

776 807 

777Run `/mcp` to see the server's status. When the server is healthy, `/mcp` lists it as connected.808Run `/mcp` to see the server's status. When the server is healthy, `/mcp` lists it as connected.


904 935 

905Your plugin declares `userConfig` options, but no configuration dialog appears when you install it.936Your plugin declares `userConfig` options, but no configuration dialog appears when you install it.

906 937 

907The interactive install shows the dialog, and the shell command takes the values as flags instead:938Whether the install asks for the values depends on where you run it:

908 939 

909* **`/plugin install` in a session, or the Discover tab in `/plugin`**: the dialog is part of this interactive install940* **`/plugin install` in a session, or the Discover tab in `/plugin`**: the dialog is part of this interactive install

941* **The VS Code extension's Manage plugins dialog**: asks for unset options as a form after the install. Before v2.1.285, installing there showed no options form, so set the values from a terminal session with `/plugin configure <plugin>@<marketplace>`

910* **`claude plugin install` in your shell**: never prompts for `userConfig` values. It saves any `--config KEY=VALUE` values you pass, and when options remain unset it prints `N userConfig options not yet set — run /plugin configure <plugin>@<marketplace> in Claude Code, or pass --config KEY=VALUE.` When any of the unset options is required, `(M required)` follows `not yet set`.942* **`claude plugin install` in your shell**: never prompts for `userConfig` values. It saves any `--config KEY=VALUE` values you pass, and when options remain unset it prints `N userConfig options not yet set — run /plugin configure <plugin>@<marketplace> in Claude Code, or pass --config KEY=VALUE.` When any of the unset options is required, `(M required)` follows `not yet set`.

911 943 

912If you installed from the shell, pass the values with `--config`, one flag per option:944If you installed from the shell, pass the values with `--config`, one flag per option:


915claude plugin install my-plugin@my-marketplace --config api_url=https://example.com947claude plugin install my-plugin@my-marketplace --config api_url=https://example.com

916```948```

917 949 

918When every option is set, the install output carries no `not yet set` line. To open the dialog afterwards instead, run `/plugin configure my-plugin@my-marketplace` in a session.950When every option is set, the install output carries no `not yet set` line.

951 

952To open the dialog afterwards instead, run `/plugin configure my-plugin@my-marketplace` in a session. From the shell, [`claude plugin configure`](/docs/en/plugins/cli-reference#plugin-configure) shows which options are still unset and saves values piped in on stdin. It requires Claude Code v2.1.285 or later.

919 953 

920If you pass a `--config` key the manifest doesn't declare, the plugin still installs, and the command prints `⚠ Installed, but --config not applied: --config key "<key>" isn't declared in this plugin's userConfig.` followed by the keys the plugin does declare.954If you pass a `--config` key the manifest doesn't declare, the plugin still installs, and the command prints `⚠ Installed, but --config not applied: --config key "<key>" isn't declared in this plugin's userConfig.` followed by the keys the plugin does declare.

921 955 

956For a plugin that ships an [MCPB bundle file](/docs/en/plugins/components#include-a-packaged-mcpb-server) declaring `user_config` of its own, the message reads `isn't declared in this plugin's userConfig or by its bundled MCP servers.` instead, and the known keys include that server's keys, written `<server>.<key>`. A bundle the manifest references by URL isn't read at install time, so its keys aren't listed and the message says to configure it in `/plugin`. Setting `<server>.<key>` keys requires Claude Code v2.1.285 or later.

957 

922<h3 id="claude-plugin-validate-reports-errors">958<h3 id="claude-plugin-validate-reports-errors">

923 `claude plugin validate` reports errors959 `claude plugin validate` reports errors

924</h3>960</h3>


938| `Path contains ".." which could be a path traversal attempt: <path>` | A component path escapes the plugin directory. | Use paths inside the plugin root. |974| `Path contains ".." which could be a path traversal attempt: <path>` | A component path escapes the plugin directory. | Use paths inside the plugin root. |

939| `Path is a file; skills entries must be directories containing SKILL.md` | A `skills` entry points at `SKILL.md` instead of its directory. | Point at the parent directory, or `.` for a root-level `SKILL.md`. |975| `Path is a file; skills entries must be directories containing SKILL.md` | A `skills` entry points at `SKILL.md` instead of its directory. | Point at the parent directory, or `.` for a root-level `SKILL.md`. |

940| `No frontmatter block found` or `YAML frontmatter failed to parse: <error>` | A skill, agent, or command file has missing or invalid YAML frontmatter. | Add or fix the frontmatter between `---` delimiters. Reported when validating a plugin directory. |976| `No frontmatter block found` or `YAML frontmatter failed to parse: <error>` | A skill, agent, or command file has missing or invalid YAML frontmatter. | Add or fix the frontmatter between `---` delimiters. Reported when validating a plugin directory. |

977| `Plugin name "<name>" is reserved: it passes as one of Anthropic's own` | The plugin's `name` is one of the [reserved names](/docs/en/plugins/manifest-reference#name). | Rename the plugin for what it does. |

941| `Unknown field '<key>'` | The manifest has a field the schema doesn't define. | Remove it, or use the name the message suggests. Claude Code ignores unknown fields at load time. |978| `Unknown field '<key>'` | The manifest has a field the schema doesn't define. | Remove it, or use the name the message suggests. Claude Code ignores unknown fields at load time. |

942 979 

943Run the command again after each fix until it prints no errors.980Run the command again after each fix until it prints no errors.

routines.md +1 −1

Details

367 </Step>367 </Step>

368 368 

369 <Step title="Change the network access level">369 <Step title="Change the network access level">

370 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.370 In the **Edit 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.

371 </Step>371 </Step>

372 372 

373 <Step title="Save">373 <Step title="Save">

Details

76<span id="loop-provider-differences" />76<span id="loop-provider-differences" />

77 77 

78<Note>78<Note>

79 Dynamically chosen intervals and the [built-in maintenance prompt](#run-the-built-in-maintenance-prompt) work on every provider, and with [feature-flag fetching](/docs/en/env-vars#features-that-need-feature-flag-fetching) turned off. On Amazon Bedrock, Claude Platform on AWS, Google Cloud's Agent Platform, and Microsoft Foundry, or with fetching turned off, both require Claude Code v2.1.248 or later. In those cases, on earlier versions, a prompt with no interval runs on a fixed 10-minute schedule, and a `/loop` with no prompt prints the usage message.79 Dynamically chosen intervals and the [built-in maintenance prompt](#run-the-built-in-maintenance-prompt) work on every provider, and with [feature-flag fetching](/docs/en/env-vars#features-that-need-feature-flag-fetching) turned off. On Amazon Bedrock, Claude Platform on AWS, Google Cloud's Agent Platform, and Microsoft Foundry, or with fetching turned off, both require Claude Code v2.1.248 or later.

80</Note>80</Note>

81 81 

82### Run the built-in maintenance prompt82### Run the built-in maintenance prompt

Details

77A runner serves one owner at a time. The first session a runner picks up locks the runner to that session's owner, and the runner then runs sessions only for that owner, up to a configured capacity. Who the owner is depends on how the session started:77A runner serves one owner at a time. The first session a runner picks up locks the runner to that session's owner, and the runner then runs sessions only for that owner, up to a configured capacity. Who the owner is depends on how the session started:

78 78 

79* **Sessions a user starts**: the owner is that user's account.79* **Sessions a user starts**: the owner is that user's account.

80* **Claude Tag channel sessions**: Claude runs them with no user account attached, so the owner is the [Claude Tag agent](https://claude.com/docs/claude-tag/concepts/glossary#agent-identity) that started the session. Every channel session that agent starts has the same owner, whoever sent the Slack message, so a runner locked to it serves sessions that different people started when you run it at a `--capacity` above one or with a positive `--drain-grace-sec`. A runner locked to a user never picks these up, and a runner locked to a Claude Tag agent never picks up a user's sessions.80* **Claude Tag channel sessions**: Claude runs them with no user account attached, so the owner is the [Claude Tag agent](https://claude.com/docs/claude-tag/concepts/glossary#agent-identity) that started the session. Every channel session that agent starts has the same owner, whoever sent the Slack message, so a runner locked to it serves sessions that different people started when you run it at a `--capacity` above one or with a positive `--drain-grace-sec`.

81 81 

82The minimum fleet size is therefore the number of owners you expect to be active at once, counting users and Claude Tag agents.82The minimum fleet size is therefore the number of owners you expect to be active at once, counting users and Claude Tag agents.

83 83 

Details

57 57 

58Don't close or reuse file descriptor 3 in the wrapper. Redirecting the child's stdout and stderr is fine.58Don't close or reuse file descriptor 3 in the wrapper. Redirecting the child's stdout and stderr is fine.

59 59 

60### Pass the system prompt flags through

61 

62The system prompt and appended system prompt that Anthropic's control plane sends for a session reach your wrapper as file paths, not as inline text. The runner writes each prompt to a file in the session's config directory, `CLAUDE_CONFIG_DIR`, and passes its path in the arguments your wrapper receives, as [`--system-prompt-file <path>` or `--append-system-prompt-file <path>`](/docs/en/cli-reference#system-prompt-flags).

63 

64Runners on Claude Code v2.1.281 or later deliver the prompts as files. Before v2.1.281, the runner passed them as `--system-prompt <text>` and `--append-system-prompt <text>`.

65 

66In your wrapper script or [`command` hook](#command), handle these flags as follows:

67 

68* **Pass them through**: end the wrapper with `exec "$CLAUDE_RUNNER_CLAUDE_BIN" "$@"`, which forwards the file flags along with every other argument. Don't drop or rewrite them. If a session loses a prompt file flag, it runs without the instructions the control plane sent for it.

69* **On a runner at v2.1.281 or later, a file flag you append replaces the server's, never adds to it**: each prompt file flag takes a single value and Claude Code keeps the last occurrence, so if you append `--append-system-prompt-file <path>` after `"$@"`, your file's contents replace the server's appended instructions. To add instructions on top of the server's, put them in the runner image's `CLAUDE.md`, which the runner [seeds into every session's user-level config](#how-each-session’s-config-is-assembled).

70 

60### Provision credentials scoped to the session creator71### Provision credentials scoped to the session creator

61 72 

62Use the `decode-token` subcommand to read claims from the session JWT. It reads the token from an argument, from `CLAUDE_CODE_SESSION_ACCESS_TOKEN`, or from stdin, in that order; see [Verify the token inside the session](/docs/en/self-hosted-environments-identity#verify-the-token-inside-the-session) for what it checks. The example below decodes the creator identity, exchanges it for short-lived AWS credentials, and execs into Claude Code:73Use the `decode-token` subcommand to read claims from the session JWT. It reads the token from an argument, from `CLAUDE_CODE_SESSION_ACCESS_TOKEN`, or from stdin, in that order; see [Verify the token inside the session](/docs/en/self-hosted-environments-identity#verify-the-token-inside-the-session) for what it checks. The example below decodes the creator identity, exchanges it for short-lived AWS credentials, and execs into Claude Code:

Details

143 143 

144### Per-key exceptions across managed sources144### Per-key exceptions across managed sources

145 145 

146Three kinds of keys are exceptions to the no-merge rule:146These keys are exceptions to the no-merge rule:

147 147 

148* **Cross-source lock keys**: a small set of keys, such as the sandbox allowlist locks, [listed on the managed settings page](/docs/en/managed-settings#precedence-within-the-managed-tier). Claude Code honors them when any admin-controlled managed source sets them; the user-writable HKCU registry tier is excluded.148* **Cross-source lock keys**: a small set of keys, such as the sandbox allowlist locks, [listed on the managed settings page](/docs/en/managed-settings#precedence-within-the-managed-tier). Claude Code honors them when any admin-controlled managed source sets them; the user-writable HKCU registry tier is excluded.

149 149 


151* **The `env` block**: apart from the telemetry unit and routing variables paired with a credential key, both covered below, it merges per key across the admin-controlled sources. For each environment variable, the highest-priority source defining it wins, and lower admin sources fill in variables the higher sources leave unset. An endpoint-managed `env` entry therefore applies whenever the server-managed configuration leaves that variable unset, or while a cached server value for it is [withheld pending server confirmation](#fetch-and-caching-behavior). Requires Claude Code v2.1.223 or later. Before v2.1.223, Claude Code applies the selected source's whole `env` block only.151* **The `env` block**: apart from the telemetry unit and routing variables paired with a credential key, both covered below, it merges per key across the admin-controlled sources. For each environment variable, the highest-priority source defining it wins, and lower admin sources fill in variables the higher sources leave unset. An endpoint-managed `env` entry therefore applies whenever the server-managed configuration leaves that variable unset, or while a cached server value for it is [withheld pending server confirmation](#fetch-and-caching-behavior). Requires Claude Code v2.1.223 or later. Before v2.1.223, Claude Code applies the selected source's whole `env` block only.

152 * **Telemetry unit**: the `OTEL_EXPORTER_OTLP_*` exporter keys, the `OTEL_LOG_*` content-capture toggles, `OTEL_LOGS_EXPORTER`, and the beta tracing variables `ENABLE_BETA_TRACING_DETAILED` and `BETA_TRACING_ENDPOINT` follow the highest source that sets any of them as a unit. A source that delivers the `otelHeadersHelper` credential key claims the unit too, but lands these variables only when it is the selected source: a source that isn't selected but delivers the key contributes none of them and still blocks lower sources from filling them in. Either way, an exporter endpoint from one source can never pair with credentials from another.152 * **Telemetry unit**: the `OTEL_EXPORTER_OTLP_*` exporter keys, the `OTEL_LOG_*` content-capture toggles, `OTEL_LOGS_EXPORTER`, and the beta tracing variables `ENABLE_BETA_TRACING_DETAILED` and `BETA_TRACING_ENDPOINT` follow the highest source that sets any of them as a unit. A source that delivers the `otelHeadersHelper` credential key claims the unit too, but lands these variables only when it is the selected source: a source that isn't selected but delivers the key contributes none of them and still blocks lower sources from filling them in. Either way, an exporter endpoint from one source can never pair with credentials from another.

153 * **Credential-paired routing**: a source that pairs routing variables with a selected-source-only credential key, such as `apiKeyHelper` or `otelHeadersHelper`, contributes those routing variables only when it wins the slot.153 * **Credential-paired routing**: a source that pairs routing variables with a selected-source-only credential key, such as `apiKeyHelper` or `otelHeadersHelper`, contributes those routing variables only when it wins the slot.

154* **`allowedProviders`**: a list set on the machine and a server-managed list combine as [its entry's Scope note](/docs/en/settings-reference#allowedproviders) states. Requires Claude Code v2.1.285 or later

154* **Gateway sign-in keys**: Claude Code never reads [`forceLoginGatewayUrl`](/docs/en/settings-reference#forcelogingatewayurl), [`gatewayInternalNetworks`](/docs/en/settings-reference#gatewayinternalnetworks), or the `"gateway"` value of [`forceLoginMethod`](/docs/en/settings-reference#forceloginmethod) from server-managed settings, so a value there neither applies nor hides one set in an MDM policy or managed settings file. The [`managedSourcesBehavior` entry](/docs/en/settings-reference#managedsourcesbehavior) says which admin source on the machine supplies them.155* **Gateway sign-in keys**: Claude Code never reads [`forceLoginGatewayUrl`](/docs/en/settings-reference#forcelogingatewayurl), [`gatewayInternalNetworks`](/docs/en/settings-reference#gatewayinternalnetworks), or the `"gateway"` value of [`forceLoginMethod`](/docs/en/settings-reference#forceloginmethod) from server-managed settings, so a value there neither applies nor hides one set in an MDM policy or managed settings file. The [`managedSourcesBehavior` entry](/docs/en/settings-reference#managedsourcesbehavior) says which admin source on the machine supplies them.

155 156 

156### Fetch and caching behavior157### Fetch and caching behavior

sessions.md +20 −2

Details

27 27 

28`claude --continue` opens a [background session](/docs/en/agent-view) that has finished, but not one that is still running; opening finished background sessions requires Claude Code v2.1.257 or later. If your most recent conversation is one you [moved to the background](/docs/en/agent-view#send-the-session-to-the-background) and it is still running there, Claude Code exits with `Your most recent conversation is running in the background` and that session's ID. Attach to the session from [`claude agents`](/docs/en/agent-view#attach-to-a-session), or run `claude --resume` to pick another one.28`claude --continue` opens a [background session](/docs/en/agent-view) that has finished, but not one that is still running; opening finished background sessions requires Claude Code v2.1.257 or later. If your most recent conversation is one you [moved to the background](/docs/en/agent-view#send-the-session-to-the-background) and it is still running there, Claude Code exits with `Your most recent conversation is running in the background` and that session's ID. Attach to the session from [`claude agents`](/docs/en/agent-view#attach-to-a-session), or run `claude --resume` to pick another one.

29 29 

30<span id="resume-a-running-background-session" />

31 

32When the conversation you resume with `claude --resume` or `/resume` belongs to a [background session](/docs/en/agent-view) that is still running, Claude Code opens the running session itself. With `--bg` on the command line, the resume is a [background dispatch](/docs/en/agent-view#from-your-shell) instead. Before v2.1.285, Claude Code refused and told you to open the session with `claude attach <id>`, or to stop it with `claude stop <id>` first.

33 

34* **From your shell**: `claude --resume <session>` runs [`claude attach`](/docs/en/agent-view#attach-to-a-session) on that session in the same terminal instead of loading the transcript itself. A prompt you pass on the command line, as in `claude --resume <session> "check the tests too"`, goes to the session as its next turn first, and Claude Code prints `Sent your prompt to the background session (<id>); opening it…` before attaching. `claude -p --resume <session> "prompt"` typed at a terminal does the same, so `-p` doesn't keep that run non-interactive.

35 

36 Claude Code doesn't open the session when the command line has any of these:

37 

38 * Piped or redirected input or output

39 * Flags that configure the session, such as `--permission-mode`, `--model`, or `--settings`

40 * Flags that read the output, such as `--output-format json` or `--json-schema`

41 * Flags that limit or rewind the run, such as `--max-turns` or `--max-budget-usd`

42 

43 With any of these, or when [agent view is turned off](/docs/en/agent-view#turn-off-agent-view), Claude Code sends nothing and exits with status 1, printing that the session is running in the background along with the `claude attach <id>` command that opens it, or telling you to find it in `claude agents` when it can't determine the ID. Add `--fork-session` to resume a copy of the conversation instead. To continue the conversation itself in a session of your own, with your flags applied, run `claude stop <id>` and then repeat the command.

44 

45 A prompt that starts with `/` or `!` isn't sent, and neither is any prompt while the session waits on your answer to a question. In both cases Claude Code doesn't open the session, and the message includes `Your prompt was not sent to it` with the reason.

46* **From inside a session**: `/resume` moves your current conversation to the background and attaches this terminal to the running session, printing `Opening "<title>", running in the background (<id>)`. Press `←` on an empty prompt to return to agent view, which also lists the conversation you left. When the current conversation can't move to the background, for example because you're already attached to a background session or session persistence is off, `/resume` prints the `claude attach` command to run instead.

47 

30You can run `claude --resume <session-id>` from any directory: Claude Code looks for the ID in the current project directory and its git worktrees first, then in every other project on this machine, so it finds a session that started elsewhere or moved with [`/cd`](/docs/en/commands). The cross-project search resolves the ID only when exactly one other project holds a transcript with messages for it, so a hand-copied duplicate makes Claude Code report not-found rather than resume an arbitrary copy. If no stored session matches the ID, Claude Code reports `No conversation found with session ID: <session-id>`. Before v2.1.223, the lookup stopped at the current project directory and its git worktrees, so you had to resume from the directory the session last worked in.48You can run `claude --resume <session-id>` from any directory: Claude Code looks for the ID in the current project directory and its git worktrees first, then in every other project on this machine, so it finds a session that started elsewhere or moved with [`/cd`](/docs/en/commands). The cross-project search resolves the ID only when exactly one other project holds a transcript with messages for it, so a hand-copied duplicate makes Claude Code report not-found rather than resume an arbitrary copy. If no stored session matches the ID, Claude Code reports `No conversation found with session ID: <session-id>`. Before v2.1.223, the lookup stopped at the current project directory and its git worktrees, so you had to resume from the directory the session last worked in.

31 49 

32### What a resumed session restores50### What a resumed session restores

33 51 

34A resumed session restores the conversation along with the state saved in it:52When Claude Code loads a conversation from its transcript, the resumed session restores the conversation along with the state saved in it:

35 53 

36* Conversation history: the full history, including tool calls and results. A tool that was still running when the previous process ended, for example in a crash, doesn't finish or run again when you resume. Claude sees the call marked as cut off before its result was recorded and is told to check whether it took effect before running it again, unless [`CLAUDE_CODE_RESUME_INTERRUPTED_TURN`](/docs/en/env-vars#variables) is set. Before v2.1.281, Claude Code dropped the cut-off call from the conversation or showed it to Claude as one you interrupted.54* Conversation history: the full history, including tool calls and results. A tool that was still running when the previous process ended, for example in a crash, doesn't finish or run again when you resume. Claude sees the call marked as cut off before its result was recorded and is told to check whether it took effect before running it again, unless [`CLAUDE_CODE_RESUME_INTERRUPTED_TURN`](/docs/en/env-vars#variables) is set. Before v2.1.281, Claude Code dropped the cut-off call from the conversation or showed it to Claude as one you interrupted.

37* Model: the session continues on the model it was using. The model isn't restored when it has been retired or isn't allowed by `availableModels`, when a `--model` flag or `ANTHROPIC_MODEL`-family environment variable picks one at launch, or on providers that use provider-specific deployment IDs, such as [Amazon Bedrock, Google Cloud's Agent Platform, and Microsoft Foundry](/docs/en/third-party-integrations); see [model configuration](/docs/en/model-config#setting-your-model) for the resolution order.55* Model: the session continues on the model it was using. The model isn't restored when it has been retired or isn't allowed by `availableModels`, when a `--model` flag or `ANTHROPIC_MODEL`-family environment variable picks one at launch, or on providers that use provider-specific deployment IDs, such as [Amazon Bedrock, Google Cloud's Agent Platform, and Microsoft Foundry](/docs/en/third-party-integrations); see [model configuration](/docs/en/model-config#setting-your-model) for the resolution order.


45 63 

46#### Permission mode on resume64#### Permission mode on resume

47 65 

48Which permission mode Claude Code starts a resumed session in depends on how you resume:66Which permission mode Claude Code starts a resumed session in depends on how you resume. The cases below apply when Claude Code loads the conversation from its transcript; when you [open a background session that is still running](#resume-a-running-background-session) instead, that session keeps the permission mode it is in.

49 67 

50* Terminal: `claude --continue`, `claude --resume <session-id>`, or `claude --resume <name>` when the name matches one session, without `-p`. Claude Code restores the permission mode the session was in, except in the cases in the table. Pass `--permission-mode` or `--dangerously-skip-permissions` to override the restored mode.68* Terminal: `claude --continue`, `claude --resume <session-id>`, or `claude --resume <name>` when the name matches one session, without `-p`. Claude Code restores the permission mode the session was in, except in the cases in the table. Pass `--permission-mode` or `--dangerously-skip-permissions` to override the restored mode.

51* Non-interactive: `claude -p --resume` or `claude -p --continue`. Claude Code starts the run in the permission mode a new `claude -p` run would start in, except that a session that ended in plan mode resumes in plan mode under the [conditions below](#resume-in-plan-mode-with-p).69* Non-interactive: `claude -p --resume` or `claude -p --continue`. Claude Code starts the run in the permission mode a new `claude -p` run would start in, except that a session that ended in plan mode resumes in plan mode under the [conditions below](#resume-in-plan-mode-with-p).

Details

597| [`allowedChannelPlugins`](#allowedchannelplugins) | Replace the default allowlist of [channel plugins](/docs/en/channels#restrict-which-channel-plugins-can-run) that can push messages | Plugins and skills | Managed |597| [`allowedChannelPlugins`](#allowedchannelplugins) | Replace the default allowlist of [channel plugins](/docs/en/channels#restrict-which-channel-plugins-can-run) that can push messages | Plugins and skills | Managed |

598| [`allowedHttpHookUrls`](#allowedhttphookurls) | Limit which URLs [HTTP hooks](/docs/en/hooks) can target | Hooks and automation | Any file |598| [`allowedHttpHookUrls`](#allowedhttphookurls) | Limit which URLs [HTTP hooks](/docs/en/hooks) can target | Hooks and automation | Any file |

599| [`allowedMcpServers`](#allowedmcpservers) | Allowlist which [MCP servers](/docs/en/mcp) users can add | MCP | Any file |599| [`allowedMcpServers`](#allowedmcpservers) | Allowlist which [MCP servers](/docs/en/mcp) users can add | MCP | Any file |

600| [`allowedProviders`](#allowedproviders) | Limit which [API providers](/docs/en/third-party-integrations) a machine may use | Authentication and providers | Managed |

600| [`allowManagedHooksOnly`](#allowmanagedhooksonly) | Run only the [hooks](/docs/en/hooks) your organization deploys | Hooks and automation | Managed |601| [`allowManagedHooksOnly`](#allowmanagedhooksonly) | Run only the [hooks](/docs/en/hooks) your organization deploys | Hooks and automation | Managed |

601| [`allowManagedMcpServersOnly`](#allowmanagedmcpserversonly) | Make the managed [MCP](/docs/en/mcp) allowlist the only one that applies | MCP | Managed |602| [`allowManagedMcpServersOnly`](#allowmanagedmcpserversonly) | Make the managed [MCP](/docs/en/mcp) allowlist the only one that applies | MCP | Managed |

602| [`allowManagedPermissionRulesOnly`](#allowmanagedpermissionrulesonly) | Make [managed settings](/docs/en/managed-settings) the only settings source of [permission rules](/docs/en/permissions#managed-settings) | Permission settings | Managed |603| [`allowManagedPermissionRulesOnly`](#allowmanagedpermissionrulesonly) | Make [managed settings](/docs/en/managed-settings) the only settings source of [permission rules](/docs/en/permissions#managed-settings) | Permission settings | Managed |

603| [`alwaysThinkingEnabled`](#alwaysthinkingenabled) | Turn [extended thinking](/docs/en/model-config#extended-thinking) off for every session | Model and responses | Any file |604| [`alwaysThinkingEnabled`](#alwaysthinkingenabled) | Turn [extended thinking](/docs/en/model-config#extended-thinking) off for every session | Model and responses | Any file |

604| [`apiKeyHelper`](#apikeyhelper) | Generate the [API credential](/docs/en/authentication#credential-management) with your own command | Authentication and providers | Any file |605| [`apiKeyHelper`](#apikeyhelper) | Generate the [API credential](/docs/en/authentication#credential-management) with your own command | Authentication and providers | Any file |

605| [`askUserQuestionTimeout`](#askuserquestiontimeout) | Let an unanswered question [auto-continue](/docs/en/tools-reference#question-auto-continue-timeout) after idle time | Interface and terminal | User or managed |606| [`askUserQuestionTimeout`](#askuserquestiontimeout) | Let an unanswered question [auto-continue](/docs/en/tools-reference#question-auto-continue-timeout) after idle time | Interface and terminal | User or managed |

607| [`appendPlugins`](#appendplugins) | Run your organization's [mods](/docs/en/plugins/mods/admin) after every mod a user installs | Plugins and skills | User or managed |

606| [`attribution`](#attribution) | Customize the attribution Claude Code adds to commits and pull requests | Git and attribution | Any file |608| [`attribution`](#attribution) | Customize the attribution Claude Code adds to commits and pull requests | Git and attribution | Any file |

607| [`attribution.commit`](#attribution-commit) | Change or hide the trailer Claude Code adds to commits | Git and attribution | Any file |609| [`attribution.commit`](#attribution-commit) | Change or hide the trailer Claude Code adds to commits | Git and attribution | Any file |

608| [`attribution.pr`](#attribution-pr) | Change or hide the attribution line in pull request descriptions | Git and attribution | Any file |610| [`attribution.pr`](#attribution-pr) | Change or hide the attribution line in pull request descriptions | Git and attribution | Any file |


727| [`policyHelper.timeoutMs`](#policyhelper-timeoutms) | Set how long Claude Code waits for the [helper](/docs/en/managed-settings#compute-the-policy-with-a-helper-program) | Enterprise and managed settings | Managed |729| [`policyHelper.timeoutMs`](#policyhelper-timeoutms) | Set how long Claude Code waits for the [helper](/docs/en/managed-settings#compute-the-policy-with-a-helper-program) | Enterprise and managed settings | Managed |

728| [`preferredNotifChannel`](#preferrednotifchannel) | Choose a [terminal bell or desktop notification](/docs/en/terminal-config#get-a-terminal-bell-or-notification) for task completion | Remote, desktop, and notifications | Any file |730| [`preferredNotifChannel`](#preferrednotifchannel) | Choose a [terminal bell or desktop notification](/docs/en/terminal-config#get-a-terminal-bell-or-notification) for task completion | Remote, desktop, and notifications | Any file |

729| [`prefersReducedMotion`](#prefersreducedmotion) | [Reduce or turn off](/docs/en/accessibility#accessibility-settings) spinner, shimmer, and flash animations | Interface and terminal | Any file |731| [`prefersReducedMotion`](#prefersreducedmotion) | [Reduce or turn off](/docs/en/accessibility#accessibility-settings) spinner, shimmer, and flash animations | Interface and terminal | Any file |

732| [`prependPlugins`](#prependplugins) | Run your organization's [mods](/docs/en/plugins/mods/admin) before every mod a user installs | Plugins and skills | User or managed |

730| [`processWrapper`](#processwrapper) | Run Claude Code's background processes through a [corporate launcher](/docs/en/corporate-launcher) on macOS and Linux | Agents, sessions, and worktrees | User or managed |733| [`processWrapper`](#processwrapper) | Run Claude Code's background processes through a [corporate launcher](/docs/en/corporate-launcher) on macOS and Linux | Agents, sessions, and worktrees | User or managed |

731| [`promptCacheTtl`](#promptcachettl) | Choose the [prompt cache lifetime](/docs/en/prompt-caching#cache-lifetime) for the main conversation | Model and responses | Any file |734| [`promptCacheTtl`](#promptcachettl) | Choose the [prompt cache lifetime](/docs/en/prompt-caching#cache-lifetime) for the main conversation | Model and responses | Any file |

732| [`promptSuggestionEnabled`](#promptsuggestionenabled) | Hide the grayed-out [prompt suggestions](/docs/en/interactive-mode#prompt-suggestions) in the input box | Interface and terminal | Any file |735| [`promptSuggestionEnabled`](#promptsuggestionenabled) | Hide the grayed-out [prompt suggestions](/docs/en/interactive-mode#prompt-suggestions) in the input box | Interface and terminal | Any file |


1851* A command substitution, a subshell, or a control-flow block such as `if` or `for`1854* A command substitution, a subshell, or a control-flow block such as `if` or `for`

1852* A redirection, such as `docker build . > build.log`, other than one that only duplicates a file descriptor, as `2>&1` does1855* A redirection, such as `docker build . > build.log`, other than one that only duplicates a file descriptor, as `2>&1` does

1853* A command name that comes from a variable1856* A command name that comes from a variable

1857* A `git clone`, `git init`, `git worktree add`, `git worktree move`, or `git bundle create` with a path argument that is absolute, starts with `~`, or contains a `..` segment

1854 1858 

1855For example, `cd build && docker compose up` stays sandboxed under a `docker *` entry, and adding a `cd` entry doesn't change that.1859For example, `cd build && docker compose up` stays sandboxed under a `docker *` entry, and adding a `cd` entry doesn't change that. Under a `git *` entry, `git clone <url> vendor/lib` runs outside the sandbox, but `git clone <url> ~/tools` stays sandboxed. A clone writes a whole tree of files, possibly executable ones, wherever its destination path points.

1856 1860 

1857Excluded commands still go through the regular permission flow. Exclusion is a convenience, not a security boundary: prefer [`filesystem.allowWrite`](#sandbox-filesystem-allowwrite) when a tool only needs to write somewhere specific. Claude Code merges entries across every settings scope the session loads, and there is no managed-only lock for this list, so keep a managed list narrow.1861Excluded commands still go through the regular permission flow. Exclusion is a convenience, not a security boundary: prefer [`filesystem.allowWrite`](#sandbox-filesystem-allowwrite) when a tool only needs to write somewhere specific. Claude Code merges entries across every settings scope the session loads, and there is no managed-only lock for this list, so keep a managed list narrow.

1858 1862 


3411 3415 

3412### `spinnerTipsOverride`3416### `spinnerTipsOverride`

3413 3417 

3414Add your own tips to the [spinner tips](#spinnertipsenabled) that Claude Code shows while Claude works, or replace the built-in tips with yours. Claude Code puts your tips in the same rotation as the built-in ones: it picks the tip that has gone unshown the longest, skips tips still in their cooldown, and breaks ties by priority.3418Add your own tips to the [spinner tips](#spinnertipsenabled) that Claude Code shows while Claude works, or replace the built-in tips with yours. Claude Code puts your tips in the same rotation as the built-in ones.

3415 3419 

3416If you set [`spinnerTipsEnabled`](#spinnertipsenabled) to `false`, Claude Code hides all tips, yours included.3420If you set [`spinnerTipsEnabled`](#spinnertipsenabled) to `false`, Claude Code hides all tips, yours included.

3417 3421 


3419* **Type**: object with `tips`, `tipsFile`, `label`, and `excludeDefault` fields, each optional3423* **Type**: object with `tips`, `tipsFile`, `label`, and `excludeDefault` fields, each optional

3420* **Default**: unset, so Claude Code shows only the built-in tips3424* **Default**: unset, so Claude Code shows only the built-in tips

3421 3425 

3422Tip objects, `tipsFile`, `label`, and the Scope line's rule that project and local settings contribute plain strings only require Claude Code v2.1.247 or later. On earlier versions, a project or local file's `excludeDefault` applies too.3426Tip objects, `tipsFile`, `label`, and the Scope line's rule that project and local settings contribute plain strings only require Claude Code v2.1.247 or later.

3423 3427 

3424Each `tips` entry is a plain string or an object with these fields:3428Each `tips` entry is a plain string or an object with these fields:

3425 3429 


3985When you set it to `true`, Claude Code changes which hooks and hook-like commands load:3989When you set it to `true`, Claude Code changes which hooks and hook-like commands load:

3986 3990 

3987* **Managed and SDK hooks run**: hooks from managed settings and hooks the [Agent SDK](/docs/en/agent-sdk/overview) registers in process3991* **Managed and SDK hooks run**: hooks from managed settings and hooks the [Agent SDK](/docs/en/agent-sdk/overview) registers in process

3988* **Force-enabled plugin hooks run**: hooks from plugins your managed settings force-enable through [`enabledPlugins`](#enabledplugins). Claude Code matches on the full `plugin@marketplace` ID, so a plugin with the same name from a different marketplace stays blocked. This lets you distribute vetted hooks through an organization marketplace while blocking everything else3992* **Force-enabled plugin hooks run**: hooks from plugins your managed settings force-enable through [`enabledPlugins`](#enabledplugins). Claude Code matches on the full `plugin@marketplace` ID, so a plugin with the same name from a different marketplace stays blocked. This lets you distribute vetted hooks through an organization marketplace while blocking everything else. A [mod](/docs/en/plugins/mods/overview) in such a plugin loads only when it [counts as your organization's](/docs/en/plugins/mods/admin#install-your-organizations-mods)

3989* **Everything else is blocked**: user, project, and local hooks, hooks from other plugins, and hooks declared in agent frontmatter3993* **Everything else is blocked**: user, project, and local hooks, hooks and mods from other installed plugins, and hooks declared in agent frontmatter. [Mods built into Claude Code](/docs/en/plugins/mods/overview#mods-built-into-claude-code) keep running. To block only users' mods, set [`allowManagedModsOnly`](/docs/en/plugins/mods/admin#set-options-on-the-built-in-guard) instead.

3990* **Command-sourced plugins are disabled**: Claude Code also disables plugins with a [`command` source](/docs/en/plugins/marketplace-reference#command-plugin-source), including plugins force-enabled in managed `enabledPlugins`, unless you set [`disableCommandPluginSources`](#disablecommandpluginsources) to `false` explicitly3994* **Command-sourced plugins are disabled**: Claude Code also disables plugins with a [`command` source](/docs/en/plugins/marketplace-reference#command-plugin-source), including plugins force-enabled in managed `enabledPlugins`, unless you set [`disableCommandPluginSources`](#disablecommandpluginsources) to `false` explicitly

3991* **Marketplace `headersHelper` commands are blocked**: Claude Code also blocks marketplace [`headersHelper` commands](/docs/en/plugins/host-marketplace#authenticate-archive-downloads) unless [`disableCommandPluginSources`](#disablecommandpluginsources) is explicitly set to `false`, except for a marketplace that managed settings themselves declare. Requires Claude Code v2.1.238 or later3995* **Marketplace `headersHelper` commands are blocked**: Claude Code also blocks marketplace [`headersHelper` commands](/docs/en/plugins/host-marketplace#authenticate-archive-downloads) unless [`disableCommandPluginSources`](#disablecommandpluginsources) is explicitly set to `false`, except for a marketplace that managed settings themselves declare. Requires Claude Code v2.1.238 or later

3992* **Status line and file suggestion narrow to managed settings**: Claude Code reads [`statusLine`](/docs/en/statusline), [`fileSuggestion`](#filesuggestion), and [`subagentStatusLine`](/docs/en/statusline#subagent-status-lines) from managed settings only, following the [status line and file suggestion gates](#status-line-and-file-suggestion-gates)3996* **Status line and file suggestion narrow to managed settings**: Claude Code reads [`statusLine`](/docs/en/statusline), [`fileSuggestion`](#filesuggestion), and [`subagentStatusLine`](/docs/en/statusline#subagent-status-lines) from managed settings only, following the [status line and file suggestion gates](#status-line-and-file-suggestion-gates)


4658* **`git`**: any git URL, with `url`4662* **`git`**: any git URL, with `url`

4659* **`url`**: a direct URL to a `marketplace.json` file, with `url` and optional `headers` and `headersHelper` for authenticated access. `headersHelper` names a command that prints headers whose values are too short-lived to list in `headers`, and requires Claude Code v2.1.238 or later4663* **`url`**: a direct URL to a `marketplace.json` file, with `url` and optional `headers` and `headersHelper` for authenticated access. `headersHelper` names a command that prints headers whose values are too short-lived to list in `headers`, and requires Claude Code v2.1.238 or later

4660* **`file`**: a local path to a `marketplace.json` file, with `path`4664* **`file`**: a local path to a `marketplace.json` file, with `path`

4661* **`directory`**: a local filesystem path, with `path`, for development only4665* **`directory`**: a local filesystem path, with `path`. Use it for development, or for a marketplace your organization [deploys to each machine](/docs/en/plugins/mods/admin#install-your-organizations-mods).

4662* **`settings`**: an inline marketplace declared directly in the settings file without a hosted repository, with `name` and `plugins`4666* **`settings`**: an inline marketplace declared directly in the settings file without a hosted repository, with `name` and `plugins`

4663 4667 

4664The `git` source type works with any git hosting service, including self-hosted GitLab and Bitbucket. Claude Code clones the repository with the same authentication that `git clone` would use on that machine: configured credential helpers or SSH keys. A provider token such as `GITHUB_TOKEN` takes effect through a credential helper that reads it. See [Private repositories](/docs/en/plugins/host-marketplace#grant-access-to-a-private-marketplace) for setup details.4668The `git` source type works with any git hosting service, including self-hosted GitLab and Bitbucket. Claude Code clones the repository with the same authentication that `git clone` would use on that machine: configured credential helpers or SSH keys. A provider token such as `GITHUB_TOKEN` takes effect through a credential helper that reads it. See [Private repositories](/docs/en/plugins/host-marketplace#grant-access-to-a-private-marketplace) for setup details.


4741 4745 

4742Claude Code ignores project and local entries because it substitutes these values into plugin hook, MCP, and LSP configurations, and a cloned repository must not be able to supply them. Before v2.1.207, project and local settings were also read.4746Claude Code ignores project and local entries because it substitutes these values into plugin hook, MCP, and LSP configurations, and a cloned repository must not be able to supply them. Before v2.1.207, project and local settings were also read.

4743 4747 

4748### `prependPlugins`

4749 

4750List the managed plugins whose [mods](/docs/en/plugins/mods/overview) run before every mod a user installs, in the listed order. When you set this key in managed settings, name `sec-default@builtin` in the list to keep the built-in guard. In managed settings, Claude Code skips an id whose plugin doesn't count as your organization's. See [Install your organization's mods and set the order](/docs/en/plugins/mods/admin#install-your-organizations-mods) for those conditions and for how the two ordering keys work together.

4751 

4752* **Scope**: [`User or managed`](#scopes). Claude Code reads the key from managed settings. It reads the key from user settings only on a machine with no managed settings, for a user who isn't signed in with a Team or Enterprise plan. It ignores the key in project and local settings and in a `--settings` file.

4753* **Type**: array of `plugin-name@marketplace-name` strings

4754* **Default**: unset

4755 

4756```json managed-settings.json theme={null}

4757{

4758 "extraKnownMarketplaces": {

4759 "acme-tools": {

4760 "source": { "source": "directory", "path": "/opt/acme/claude-plugins" }

4761 }

4762 },

4763 "enabledPlugins": { "acme-guard@acme-tools": true },

4764 "prependPlugins": ["acme-guard@acme-tools", "sec-default@builtin"]

4765}

4766```

4767 

4768### `appendPlugins`

4769 

4770List the managed plugins whose [mods](/docs/en/plugins/mods/overview) run after every mod a user installs, in the listed order. An id listed in both `prependPlugins` and `appendPlugins` is prepended. In managed settings, Claude Code skips an id whose plugin doesn't [count as your organization's](/docs/en/plugins/mods/admin#install-your-organizations-mods).

4771 

4772* **Scope**: [`User or managed`](#scopes). Claude Code reads the key from managed settings. It reads the key from user settings only on a machine with no managed settings, for a user who isn't signed in with a Team or Enterprise plan. It ignores the key in project and local settings and in a `--settings` file.

4773* **Type**: array of `plugin-name@marketplace-name` strings

4774* **Default**: unset

4775 

4776```json managed-settings.json theme={null}

4777{

4778 "extraKnownMarketplaces": {

4779 "acme-tools": {

4780 "source": { "source": "directory", "path": "/opt/acme/claude-plugins" }

4781 }

4782 },

4783 "enabledPlugins": { "acme-audit@acme-tools": true },

4784 "appendPlugins": ["acme-audit@acme-tools"]

4785}

4786```

4787 

4744## MCP4788## MCP

4745 4789 

4746Control which MCP servers Claude Code connects to and which an organization allows. See [Connect to external tools with MCP](/docs/en/mcp) and [Managed MCP configuration](/docs/en/managed-mcp).4790Control which MCP servers Claude Code connects to and which an organization allows. See [Connect to external tools with MCP](/docs/en/mcp) and [Managed MCP configuration](/docs/en/managed-mcp).


5207 5251 

5208* **Scope**: [`Any file`](#scopes)5252* **Scope**: [`Any file`](#scopes)

5209* **Type**: Boolean5253* **Type**: Boolean

5210 * `true`: Claude Code turns the Artifact tool off for every session the file applies to, and no other file turns it back on. Before v2.1.242, a higher-precedence file could override a lower file's `true` rather than the key acting as a lock5254 * `true`: Claude Code turns the Artifact tool off for every session the file applies to, and no other file turns it back on

5211 * `false`: ignored; to leave the tool on, remove the key5255 * `false`: ignored; to leave the tool on, remove the key

5212* **Default**: unset, so the tool follows your account's [availability](/docs/en/artifacts#availability)5256* **Default**: unset, so the tool follows your account's [availability](/docs/en/artifacts#availability)

5213* **Per-session overrides**: [`CLAUDE_CODE_DISABLE_ARTIFACT`](/docs/en/env-vars) set to `1` turns the tool off for one session5257* **Per-session overrides**: [`CLAUDE_CODE_DISABLE_ARTIFACT`](/docs/en/env-vars) set to `1` turns the tool off for one session


5286}5330}

5287```5331```

5288 5332 

5289While a source other than your own user settings keeps the tool turned off, Claude Code hides the **Artifacts** row in `/config`, because turning it on there wouldn't change anything. [Disable artifacts](/docs/en/artifacts#disable-artifacts) lists every way to turn the tool off. Before v2.1.242, Claude Code ignored this key in project and local settings, and a file higher in the [precedence stack](/docs/en/settings#settings-precedence) could turn the tool back on over a lower file's off.5333While a source other than your own user settings keeps the tool turned off, Claude Code hides the **Artifacts** row in `/config`, because turning it on there wouldn't change anything. [Disable artifacts](/docs/en/artifacts#disable-artifacts) lists every way to turn the tool off.

5290 5334 

5291### `inputNeededNotifEnabled`5335### `inputNeededNotifEnabled`

5292 5336 


5411 5455 

5412Supply credentials through helper scripts and, for organizations, force a login method or organization. See [Authentication](/docs/en/authentication).5456Supply credentials through helper scripts and, for organizations, force a login method or organization. See [Authentication](/docs/en/authentication).

5413 5457 

5458### `allowedProviders`

5459 

5460List the services a machine may reach Claude through, such as the Anthropic API, Amazon Bedrock, or an LLM gateway. A session on a provider that isn't listed is refused at startup, at login, and when it next contacts the API, so switching to an unlisted provider mid-session is refused too. The [refusal message](/docs/en/errors#managed-settings-dont-allow-this-api-provider) names what selected the provider and the steps to continue. Requires Claude Code v2.1.285 or later.

5461 

5462* **Scope**: [`Managed`](#scopes). A list that the machine's own admin sources set, MDM policies and managed settings files, keeps applying when server-managed settings also deliver one: a session may then use only the providers on both lists, so a server-managed list can narrow what the machine allows but never widen it. Which machine source's `allowedProviders` counts follows [how Claude Code combines managed sources](/docs/en/managed-settings#how-claude-code-combines-managed-sources). A list delivered through server-managed settings alone reaches only the sessions that [fetch server-managed settings](/docs/en/server-managed-settings#platform-availability).

5463* **Type**: array of strings, each one of:

5464 * `"anthropic"`: the Anthropic API on Anthropic's own host, through a claude.ai or Console sign-in or an API key. Pair it with [`forceLoginMethod`](#forceloginmethod) or [`forceLoginOrgUUID`](#forceloginorguuid) to also restrict the sign-in

5465 * `"bedrock"`: [Amazon Bedrock](/docs/en/amazon-bedrock)

5466 * `"vertex"`: [Google Cloud's Agent Platform](/docs/en/google-vertex-ai), formerly Vertex AI

5467 * `"foundry"`: [Microsoft Foundry](/docs/en/microsoft-foundry)

5468 * `"anthropicAws"`: [Claude Platform on AWS](/docs/en/claude-platform-on-aws)

5469 * `"mantle"`: the Amazon Bedrock [Mantle endpoint](/docs/en/amazon-bedrock#use-the-mantle-endpoint). A session that [runs Mantle alongside the Invoke API](/docs/en/amazon-bedrock#run-mantle-alongside-the-invoke-api) uses both providers, so list `"bedrock"` and `"mantle"` together for it

5470 * `"customEndpoint"`: the Anthropic API or a cloud provider's API sent to another host, such as an [LLM gateway](/docs/en/llm-gateway) named by `ANTHROPIC_BASE_URL`, a provider's `ANTHROPIC_*_BASE_URL` variable, or an `ANTHROPIC_FOUNDRY_RESOURCE` value that isn't a bare resource name. Claude Code admits it only for the exact value a managed [`env`](#env) block pins

5471 * `"gateway"`: a [Cloud gateway](/docs/en/claude-apps-gateway) sign-in

5472* **Default**: unset, so any provider can be used

5473 

5474```json managed-settings.json theme={null}

5475{

5476 "allowedProviders": ["anthropic", "bedrock"]

5477}

5478```

5479 

5480Each cloud provider's entry means that provider's own service, including its regional, FIPS, and private endpoints.

5481 

5482An entry Claude Code doesn't recognize as a provider name is dropped and reported, and the rest of the list stays enforced. With an empty list, or one whose every entry is unrecognized, Claude Code refuses every provider and doesn't start on the machine.

5483 

5484#### Endpoints that need a pin in managed `env`

5485 

5486A pin is an endpoint variable's value set in a managed [`env`](#env) block. When a session sends a provider's traffic somewhere other than that provider's own service, Claude Code admits it only if the session's value is the same as the pin. These endpoints need one:

5487 

5488* **`"customEndpoint"` sessions**: the variable that names the host, such as `ANTHROPIC_BASE_URL`

5489* **Amazon Bedrock**: the AWS SDK's `AWS_ENDPOINT_URL`, `AWS_ENDPOINT_URL_BEDROCK`, and `AWS_ENDPOINT_URL_BEDROCK_RUNTIME` variables when they point outside Bedrock's own service. The session stays under `"bedrock"` rather than `"customEndpoint"`

5490* **A gateway sign-in's URL**: the session stays under `"gateway"`, and [`forceLoginGatewayUrl`](#forcelogingatewayurl) also counts as the pin

5491 

5492Which `env` blocks count as pins depends on where the list is set:

5493 

5494* **An administrator source on the machine sets a list**: only the `env` blocks of the machine's own administrator sources count

5495* **Only server-managed settings set a list**: an `env` value in those server-managed settings counts too

5496 

5497The list doesn't judge a cloud provider's credential and tenancy variables or the network path, such as `HTTPS_PROXY` and certificate settings. Set those for the fleet in the managed `env` block.

5498 

5414### `apiKeyHelper`5499### `apiKeyHelper`

5415 5500 

5416Run your own command to produce the credential Claude Code sends with model requests. Claude Code runs the command through the system shell, `/bin/sh` on macOS and Linux and `cmd` on Windows, and sends its output as both the `X-Api-Key` and `Authorization: Bearer` headers. Use it for dynamic or rotating credentials, such as short-lived tokens fetched from a vault.5501Run your own command to produce the credential Claude Code sends with model requests. Claude Code runs the command through the system shell, `/bin/sh` on macOS and Linux and `cmd` on Windows, and sends its output as both the `X-Api-Key` and `Authorization: Bearer` headers. Use it for dynamic or rotating credentials, such as short-lived tokens fetched from a vault.


5877| :- | :- | :- |5962| :- | :- | :- |

5878| Lists | Combines entries from every source | [`permissions.allow`](#permissions-allow), [`sandbox.network.allowedDomains`](#sandbox-network-alloweddomains), and other list keys |5963| Lists | Combines entries from every source | [`permissions.allow`](#permissions-allow), [`sandbox.network.allowedDomains`](#sandbox-network-alloweddomains), and other list keys |

5879| Locks | Applies the strictest value any source sets. When no source sets a strict value, applies a looser value only from the highest source | [`allowManagedPermissionRulesOnly`](#allowmanagedpermissionrulesonly), [`permissions.disableBypassPermissionsMode`](#permissions-disablebypasspermissionsmode), and other boolean or enum locks |5964| Locks | Applies the strictest value any source sets. When no source sets a strict value, applies a looser value only from the highest source | [`allowManagedPermissionRulesOnly`](#allowmanagedpermissionrulesonly), [`permissions.disableBypassPermissionsMode`](#permissions-disablebypasspermissionsmode), and other boolean or enum locks |

5880| Restriction allowlists | Takes the list whole from the highest source that sets it, without adding entries from lower sources. When the highest source doesn't set one, takes it whole from the next source down | [`availableModels`](#availablemodels), [`allowedMcpServers`](#allowedmcpservers), [`strictKnownMarketplaces`](#strictknownmarketplaces), [`allowedChannelPlugins`](#allowedchannelplugins), and the [`fallbackModel`](#fallbackmodel) chain |5965| Restriction allowlists | Takes the list whole from the highest source that sets it, without adding entries from lower sources. When the highest source doesn't set one, takes it whole from the next source down | [`availableModels`](#availablemodels), [`allowedMcpServers`](#allowedmcpservers), [`allowedProviders`](#allowedproviders), [`strictKnownMarketplaces`](#strictknownmarketplaces), [`allowedChannelPlugins`](#allowedchannelplugins), and the [`fallbackModel`](#fallbackmodel) chain |

5881| Values taken whole | Takes the value whole from the highest source that sets it, without combining entries or fields from lower sources. When the highest source doesn't set it, takes it whole from the next source down | [`sandbox.credentials.awsPairs`](#sandbox-credentials-awspairs), [`sandbox.ripgrep`](#sandbox-ripgrep) |5966| Values taken whole | Takes the value whole from the highest source that sets it, without combining entries or fields from lower sources. When the highest source doesn't set it, takes it whole from the next source down | [`sandbox.credentials.awsPairs`](#sandbox-credentials-awspairs), [`sandbox.ripgrep`](#sandbox-ripgrep) |

5882| Provided MCP servers | Combines the server names from every source. When two sources set the same name, applies the higher source's whole entry | [`managedMcpServers`](#managedmcpservers) |5967| Provided MCP servers | Combines the server names from every source. When two sources set the same name, applies the higher source's whole entry | [`managedMcpServers`](#managedmcpservers) |

5883| Read from the highest-priority source only | Reads the key only from the highest-priority source that carries a policy key, so a lower source's value is ignored even when the highest source sets none | [`apiKeyHelper`](#apikeyhelper), [`awsAuthRefresh`](#awsauthrefresh), [`awsCredentialExport`](#awscredentialexport), [`gcpAuthRefresh`](#gcpauthrefresh), [`otelHeadersHelper`](#otelheadershelper), `proxyAuthHelper`, [`forceLoginOrgUUID`](#forceloginorguuid), the `"claudeai"` and `"console"` values of [`forceLoginMethod`](#forceloginmethod), [`parentSettingsBehavior`](#parentsettingsbehavior), [`modelPicker`](#modelpicker), [`policyHelper`](#policyhelper), [`permissions.defaultMode`](#permissions-defaultmode) |5968| Read from the highest-priority source only | Reads the key only from the highest-priority source that carries a policy key, so a lower source's value is ignored even when the highest source sets none | [`apiKeyHelper`](#apikeyhelper), [`awsAuthRefresh`](#awsauthrefresh), [`awsCredentialExport`](#awscredentialexport), [`gcpAuthRefresh`](#gcpauthrefresh), [`otelHeadersHelper`](#otelheadershelper), `proxyAuthHelper`, [`forceLoginOrgUUID`](#forceloginorguuid), the `"claudeai"` and `"console"` values of [`forceLoginMethod`](#forceloginmethod), [`parentSettingsBehavior`](#parentsettingsbehavior), [`modelPicker`](#modelpicker), [`policyHelper`](#policyhelper), [`permissions.defaultMode`](#permissions-defaultmode) |


5891* **[`policyHelper`](#policyhelper)**: Claude Code honors it only when the highest source that carries a policy key is an MDM policy or a managed settings file, so under server-managed settings it doesn't apply.5976* **[`policyHelper`](#policyhelper)**: Claude Code honors it only when the highest source that carries a policy key is an MDM policy or a managed settings file, so under server-managed settings it doesn't apply.

5892* **[`modelOverrides`](#modeloverrides)**: pairs with `availableModels`. Claude Code takes `modelOverrides` from the highest source that sets it, unless a higher source sets `availableModels` without `modelOverrides`. In that case it ignores `modelOverrides` from every source.5977* **[`modelOverrides`](#modeloverrides)**: pairs with `availableModels`. Claude Code takes `modelOverrides` from the highest source that sets it, unless a higher source sets `availableModels` without `modelOverrides`. In that case it ignores `modelOverrides` from every source.

5893* **[`forceLoginGatewayUrl`](#forcelogingatewayurl), [`gatewayInternalNetworks`](#gatewayinternalnetworks), and the `"gateway"` value of [`forceLoginMethod`](#forceloginmethod)**: Claude Code never reads any of them from server-managed settings, so a value there neither applies nor hides one set in an MDM policy or managed settings file. Among the admin sources on the machine, only the highest-ranked one that carries a policy key supplies them, whether or not server-managed settings are also present.5978* **[`forceLoginGatewayUrl`](#forcelogingatewayurl), [`gatewayInternalNetworks`](#gatewayinternalnetworks), and the `"gateway"` value of [`forceLoginMethod`](#forceloginmethod)**: Claude Code never reads any of them from server-managed settings, so a value there neither applies nor hides one set in an MDM policy or managed settings file. Among the admin sources on the machine, only the highest-ranked one that carries a policy key supplies them, whether or not server-managed settings are also present.

5979* **[`allowedProviders`](#allowedproviders)**: after the table's rule, the machine's own list still limits the result, as its entry's Scope note states.

5894 5980 

5895To confirm which sources combined on a machine, run `/status` and [read the `Setting sources` line](/docs/en/managed-settings#read-the-source-in-/status).5981To confirm which sources combined on a machine, run `/status` and [read the `Setting sources` line](/docs/en/managed-settings#read-the-source-in-/status).

5896 5982 

skills.md +1 −1

Details

690 690 

691* **Working directory**: Claude Code runs each command in the session shell's current working directory. That directory moves when Claude runs `cd`. Use [`${CLAUDE_SKILL_DIR}` or `${CLAUDE_PROJECT_DIR}`](#available-string-substitutions) in paths that must resolve the same way every time.691* **Working directory**: Claude Code runs each command in the session shell's current working directory. That directory moves when Claude runs `cd`. Use [`${CLAUDE_SKILL_DIR}` or `${CLAUDE_PROJECT_DIR}`](#available-string-substitutions) in paths that must resolve the same way every time.

692* **stderr**: with the default `bash` shell, Claude Code merges stderr into stdout. Anything the command writes to stderr appears in the injected text.692* **stderr**: with the default `bash` shell, Claude Code merges stderr into stdout. Anything the command writes to stderr appears in the injected text.

693* **Timeout**: each command runs under the Bash tool's default 2-minute [timeout](/docs/en/tools-reference#timeout-and-output-limits). When the Bash tool [moves a timed-out command to the background](/docs/en/tools-reference#background-commands), the skill still renders. The injected text reports the move and names the background task and the file collecting the command's output. When the command is one the Bash tool never auto-backgrounds, Claude Code kills it at the timeout. That failure [aborts the invocation](#when-an-injected-command-fails).693* **Timeout**: each command runs under the Bash tool's default 2-minute [timeout](/docs/en/tools-reference#timeout-and-output-limits). When the Bash tool [moves a timed-out command to the background](/docs/en/tools-reference#foreground-commands-that-move-to-the-background), the skill still renders. The injected text reports the move and names the background task and the file collecting the command's output. When the command is one the Bash tool never auto-backgrounds, Claude Code kills it at the timeout. That failure [aborts the invocation](#when-an-injected-command-fails).

694* **Output size**: output past the Bash tool's inline ceiling arrives as a file path plus a short preview, not truncated text. [Output limits](/docs/en/tools-reference#output-limits) covers the ceiling and how to adjust each boundary.694* **Output size**: output past the Bash tool's inline ceiling arrives as a file path plus a short preview, not truncated text. [Output limits](/docs/en/tools-reference#output-limits) covers the ceiling and how to adjust each boundary.

695 695 

696The PowerShell tool applies the same timeout, backgrounding, and output-ceiling behavior to the commands it runs. See the [PowerShell tool](/docs/en/tools-reference#powershell-tool) section for its specifics.696The PowerShell tool applies the same timeout, backgrounding, and output-ceiling behavior to the commands it runs. See the [PowerShell tool](/docs/en/tools-reference#powershell-tool) section for its specifics.

statusline.md +7 −7

Details

20Here's an example of a [multi-line status line](#display-multiple-lines) that displays git info on the first line and a color-coded context bar on the second.20Here's an example of a [multi-line status line](#display-multiple-lines) that displays git info on the first line and a color-coded context bar on the second.

21 21 

22<Frame>22<Frame>

23 <img src="https://mintcdn.com/claude-code/nibzesLaJVh4ydOq/images/statusline-multiline.png?fit=max&auto=format&n=nibzesLaJVh4ydOq&q=85&s=60f11387658acc9ff75158ae85f2ac87" alt="A multi-line status line showing model name, directory, git branch on the first line, and a context usage progress bar with cost and duration on the second line" width="776" height="212" data-path="images/statusline-multiline.png" />23 <img src="https://mintcdn.com/claude-code/HDAmBwgbrZVk0pOt/images/statusline-multiline.png?fit=max&auto=format&n=HDAmBwgbrZVk0pOt&q=85&s=a9d0a2fe8e446d80b1abc46da3f93270" alt="A multi-line status line showing model name, directory, git branch on the first line, and a context usage progress bar with cost and duration on the second line" width="1224" height="262" data-path="images/statusline-multiline.png" />

24</Frame>24</Frame>

25 25 

26This page walks through [setting up a basic status line](#set-up-a-status-line), explains [how the data flows](#how-status-lines-work) from Claude Code to your script, lists [all the fields you can display](#available-data), and provides [ready-to-use examples](#examples) for common patterns like git status, cost tracking, and progress bars.26This page walks through [setting up a basic status line](#set-up-a-status-line), explains [how the data flows](#how-status-lines-work) from Claude Code to your script, lists [all the fields you can display](#available-data), and provides [ready-to-use examples](#examples) for common patterns like git status, cost tracking, and progress bars.


83These examples use Bash scripts, which work on macOS and Linux. On Windows, see [Windows configuration](#windows-configuration) for PowerShell and Git Bash examples.83These examples use Bash scripts, which work on macOS and Linux. On Windows, see [Windows configuration](#windows-configuration) for PowerShell and Git Bash examples.

84 84 

85<Frame>85<Frame>

86 <img src="https://mintcdn.com/claude-code/nibzesLaJVh4ydOq/images/statusline-quickstart.png?fit=max&auto=format&n=nibzesLaJVh4ydOq&q=85&s=696445e59ca0059213250651ad23db6b" alt="A status line showing model name, directory, and context percentage" width="726" height="164" data-path="images/statusline-quickstart.png" />86 <img src="https://mintcdn.com/claude-code/HDAmBwgbrZVk0pOt/images/statusline-quickstart.png?fit=max&auto=format&n=HDAmBwgbrZVk0pOt&q=85&s=88a7eab9c1038dd098ee8e284d96b7e6" alt="A status line showing model name, directory, and context percentage" width="1224" height="224" data-path="images/statusline-quickstart.png" />

87</Frame>87</Frame>

88 88 

89<Steps>89<Steps>


422Display the current model and context window usage with a visual progress bar. Each script reads JSON from stdin, extracts the `used_percentage` field, and builds a 10-character bar where filled blocks (▓) represent usage:422Display the current model and context window usage with a visual progress bar. Each script reads JSON from stdin, extracts the `used_percentage` field, and builds a 10-character bar where filled blocks (▓) represent usage:

423 423 

424<Frame>424<Frame>

425 <img src="https://mintcdn.com/claude-code/nibzesLaJVh4ydOq/images/statusline-context-window-usage.png?fit=max&auto=format&n=nibzesLaJVh4ydOq&q=85&s=15b58ab3602f036939145dde3165c6f7" alt="A status line showing model name and a progress bar with percentage" width="448" height="152" data-path="images/statusline-context-window-usage.png" />425 <img src="https://mintcdn.com/claude-code/HDAmBwgbrZVk0pOt/images/statusline-context-window-usage.png?fit=max&auto=format&n=HDAmBwgbrZVk0pOt&q=85&s=f3918a549912dc47e90f2b69e68bc847" alt="A status line showing model name and a progress bar with percentage" width="1224" height="224" data-path="images/statusline-context-window-usage.png" />

426</Frame>426</Frame>

427 427 

428<CodeGroup>428<CodeGroup>


489Show git branch with color-coded indicators for staged and modified files. This script uses [ANSI escape codes](https://en.wikipedia.org/wiki/ANSI_escape_code#Colors) for terminal colors: `\033[32m` is green, `\033[33m` is yellow, and `\033[0m` resets to default.489Show git branch with color-coded indicators for staged and modified files. This script uses [ANSI escape codes](https://en.wikipedia.org/wiki/ANSI_escape_code#Colors) for terminal colors: `\033[32m` is green, `\033[33m` is yellow, and `\033[0m` resets to default.

490 490 

491<Frame>491<Frame>

492 <img src="https://mintcdn.com/claude-code/nibzesLaJVh4ydOq/images/statusline-git-context.png?fit=max&auto=format&n=nibzesLaJVh4ydOq&q=85&s=e656f34f90d1d9a1d0e220988914345f" alt="A status line showing model, directory, git branch, and colored indicators for staged and modified files" width="742" height="178" data-path="images/statusline-git-context.png" />492 <img src="https://mintcdn.com/claude-code/HDAmBwgbrZVk0pOt/images/statusline-git-context.png?fit=max&auto=format&n=HDAmBwgbrZVk0pOt&q=85&s=f13c190724d9ec7188c17cd2f98b7bf4" alt="A status line showing model, directory, git branch, and colored indicators for staged and modified files" width="1224" height="224" data-path="images/statusline-git-context.png" />

493</Frame>493</Frame>

494 494 

495Each script checks if the current directory is a git repository, counts staged and modified files, and displays color-coded indicators:495Each script checks if the current directory is a git repository, counts staged and modified files, and displays color-coded indicators:


585Each script formats cost as currency and converts milliseconds to minutes and seconds:585Each script formats cost as currency and converts milliseconds to minutes and seconds:

586 586 

587<Frame>587<Frame>

588 <img src="https://mintcdn.com/claude-code/nibzesLaJVh4ydOq/images/statusline-cost-tracking.png?fit=max&auto=format&n=nibzesLaJVh4ydOq&q=85&s=e3444a51fe6f3440c134bd5f1f08ad29" alt="A status line showing model name, session cost, and duration" width="588" height="180" data-path="images/statusline-cost-tracking.png" />588 <img src="https://mintcdn.com/claude-code/HDAmBwgbrZVk0pOt/images/statusline-cost-tracking.png?fit=max&auto=format&n=HDAmBwgbrZVk0pOt&q=85&s=925f7024c3b38be0f0eca63564bfb52f" alt="A status line showing model name, session cost, and duration" width="1224" height="224" data-path="images/statusline-cost-tracking.png" />

589</Frame>589</Frame>

590 590 

591<CodeGroup>591<CodeGroup>


644Your script can output multiple lines to create a richer display.644Your script can output multiple lines to create a richer display.

645 645 

646<Frame>646<Frame>

647 <img src="https://mintcdn.com/claude-code/nibzesLaJVh4ydOq/images/statusline-multiline.png?fit=max&auto=format&n=nibzesLaJVh4ydOq&q=85&s=60f11387658acc9ff75158ae85f2ac87" alt="A multi-line status line showing model name, directory, git branch on the first line, and a context usage progress bar with cost and duration on the second line" width="776" height="212" data-path="images/statusline-multiline.png" />647 <img src="https://mintcdn.com/claude-code/HDAmBwgbrZVk0pOt/images/statusline-multiline.png?fit=max&auto=format&n=HDAmBwgbrZVk0pOt&q=85&s=a9d0a2fe8e446d80b1abc46da3f93270" alt="A multi-line status line showing model name, directory, git branch on the first line, and a context usage progress bar with cost and duration on the second line" width="1224" height="262" data-path="images/statusline-multiline.png" />

648</Frame>648</Frame>

649 649 

650This example combines several techniques: threshold-based colors (green under 70%, yellow 70-89%, red 90%+), a progress bar, and git branch info. Each `print` or `echo` statement creates a separate row:650This example combines several techniques: threshold-based colors (green under 70%, yellow 70-89%, red 90%+), a progress bar, and git branch info. Each `print` or `echo` statement creates a separate row:


751This example creates a clickable link to your GitHub repository. Hold Cmd (macOS) or Ctrl (Windows/Linux) and click to open the link in your browser.751This example creates a clickable link to your GitHub repository. Hold Cmd (macOS) or Ctrl (Windows/Linux) and click to open the link in your browser.

752 752 

753<Frame>753<Frame>

754 <img src="https://mintcdn.com/claude-code/nibzesLaJVh4ydOq/images/statusline-links.png?fit=max&auto=format&n=nibzesLaJVh4ydOq&q=85&s=4bcc6e7deb7cf52f41ab85a219b52661" alt="A status line showing a clickable link to a GitHub repository" width="726" height="198" data-path="images/statusline-links.png" />754 <img src="https://mintcdn.com/claude-code/HDAmBwgbrZVk0pOt/images/statusline-links.png?fit=max&auto=format&n=HDAmBwgbrZVk0pOt&q=85&s=4778a144a28cb498c99d5fa018bb374a" alt="A status line showing a clickable link to a GitHub repository" width="1224" height="224" data-path="images/statusline-links.png" />

755</Frame>755</Frame>

756 756 

757Each script gets the git remote URL, converts SSH format to HTTPS, and wraps the repo name in OSC 8 escape codes. The Bash version uses `printf '%b'` which interprets backslash escapes more reliably than `echo -e` across different shells:757Each script gets the git remote URL, converts SSH format to HTTPS, and wraps the repo name in OSC 8 escape codes. The Bash version uses `printf '%b'` which interprets backslash escapes more reliably than `echo -e` across different shells:

Details

237 237 

238Security teams can configure managed permissions for what Claude Code is and is not allowed to do, which cannot be overwritten by local configuration. [Learn more](/docs/en/security).238Security teams can configure managed permissions for what Claude Code is and is not allowed to do, which cannot be overwritten by local configuration. [Learn more](/docs/en/security).

239 239 

240To limit which of these deployment options a managed machine may use, set [`allowedProviders`](/docs/en/settings-reference#allowedproviders) in managed settings. For example, `["bedrock"]` allows Amazon Bedrock and nothing else; a Bedrock fleet that also enables the Mantle endpoint lists `"mantle"` too. The entry says which endpoint variables also need a managed `env` pin. Requires Claude Code v2.1.285 or later.

241 

240<h3 id="leverage-mcp-for-integrations">242<h3 id="leverage-mcp-for-integrations">

241 Use MCP for integrations243 Use MCP for integrations

242</h3>244</h3>

Details

160* `BASH_DEFAULT_TIMEOUT_MS` — the default when Claude passes no timeout; two minutes out of the box160* `BASH_DEFAULT_TIMEOUT_MS` — the default when Claude passes no timeout; two minutes out of the box

161* `BASH_MAX_TIMEOUT_MS` — with the default, sets the ceiling that caps whatever Claude requests: the effective ceiling is the larger of the two, ten minutes out of the box161* `BASH_MAX_TIMEOUT_MS` — with the default, sets the ceiling that caps whatever Claude requests: the effective ceiling is the larger of the two, ten minutes out of the box

162 162 

163For a command that Claude starts in the background, `timeout` instead sets how long the command may run there, with the separate default and maximum described under [Background commands](#background-commands). The [PowerShell tool](#powershell-tool) follows the same timeout rules and reads the same two variables.163For a command that Claude starts in the background, `timeout` instead sets how long the command may run there, with the separate default and maximum described under [Time limit for background commands](#time-limit-for-background-commands). The [PowerShell tool](#powershell-tool) follows the same timeout rules and reads the same two variables.

164 164 

165#### Output limits165#### Output limits

166 166 


181 181 

182For long-running processes such as dev servers or watch builds, Claude can set `run_in_background: true` to start the command as a background task and continue working while it runs. List and stop background tasks with `/tasks`. After you stop one there, or from a connected client such as the desktop app, Claude moves on instead of waiting for it. If a subagent started the command, it's that subagent that moves on.182For long-running processes such as dev servers or watch builds, Claude can set `run_in_background: true` to start the command as a background task and continue working while it runs. List and stop background tasks with `/tasks`. After you stop one there, or from a connected client such as the desktop app, Claude moves on instead of waiting for it. If a subagent started the command, it's that subagent that moves on.

183 183 

184A command that a [foreground subagent](/docs/en/sub-agents#run-subagents-in-foreground-or-background) started stops when that subagent's run ends, whether it finished, failed, or was interrupted. A command that the main conversation or a background subagent started keeps running after a final response, until it exits, is stopped, or reaches its time limit. In non-interactive mode with the `-p` flag, [background commands end shortly after the run's final result](/docs/en/headless#background-tasks-at-exit).184#### When a background command stops

185 

186A command that a [foreground subagent](/docs/en/sub-agents#run-subagents-in-foreground-or-background) started stops when that subagent's run ends, whether it finished, failed, or was interrupted. A command that the main conversation or a background subagent started keeps running after a final response, until it exits, is stopped, or reaches its [time limit](#time-limit-for-background-commands). In non-interactive mode with the `-p` flag, [background commands end shortly after the run's final result](/docs/en/headless#background-tasks-at-exit).

187 

188#### Time limit for background commands

185 189 

186Background Bash and PowerShell commands have a time limit, counted from the moment the command enters the background:190Background Bash and PowerShell commands have a time limit, counted from the moment the command enters the background:

187 191 

188* A command that Claude starts in the background gets 30 minutes, or the `timeout` Claude passes with `run_in_background`, up to a maximum of 2 hours192* A command that Claude starts in the background gets 30 minutes, or the `timeout` Claude passes with `run_in_background`, up to a maximum of 2 hours

189* A command that starts in the foreground and then moves to the background, for example with `Ctrl+B` or at its timeout, gets 30 minutes from the move193* A command that starts in the foreground and then moves to the background, for example with `Ctrl+B` or at its timeout, gets 30 minutes from the move

190 194 

195When a background command reaches its time limit, Claude Code stops it and tells Claude why, and Claude can start the command again with a longer `timeout` if the work still needs it. The stop notice reads `Background command "<description>" was stopped after reaching its background time limit`.

196 

197#### Raise the time limit for background commands

198 

191Two [environment variables](/docs/en/env-vars) raise these limits, for Bash and PowerShell commands alike. Both take milliseconds, and neither can shorten a limit: a lower value leaves the 30-minute default and the 2-hour maximum in place.199Two [environment variables](/docs/en/env-vars) raise these limits, for Bash and PowerShell commands alike. Both take milliseconds, and neither can shorten a limit: a lower value leaves the 30-minute default and the 2-hour maximum in place.

192 200 

193* Set `BASH_DEFAULT_TIMEOUT_MS` above `1800000` to replace the 30-minute default with that value, both for commands Claude starts without a `timeout` and for moved commands201* Set `BASH_DEFAULT_TIMEOUT_MS` above `1800000` to replace the 30-minute default with that value, both for commands Claude starts without a `timeout` and for moved commands

194* Set `BASH_MAX_TIMEOUT_MS` above `7200000` to raise the 2-hour maximum to that value. Setting `BASH_DEFAULT_TIMEOUT_MS` above `7200000` raises the maximum the same way202* Set `BASH_MAX_TIMEOUT_MS` above `7200000` to raise the 2-hour maximum to that value. Setting `BASH_DEFAULT_TIMEOUT_MS` above `7200000` raises the maximum the same way

195 203 

196When a background command reaches its time limit, Claude Code stops it and tells Claude why, and Claude can start the command again with a longer `timeout` if the work still needs it. The stop notice reads `Background command "<description>" was stopped after reaching its background time limit`.204#### Foreground commands that move to the background

197 205 

198When a foreground command reaches its timeout without finishing, Claude Code moves it to the background instead of stopping it, unless the command starts with `sleep`. A moved command's time limit counts from the move, and a foreground subagent's moved command still stops when that subagent's run ends.206When a foreground command reaches its timeout without finishing, Claude Code moves it to the background instead of stopping it, unless the command starts with `sleep`. A moved command's [time limit](#time-limit-for-background-commands) counts from the move, and a foreground subagent's moved command still stops when that subagent's run ends.

199 207 

200Setting [`CLAUDE_CODE_DISABLE_BACKGROUND_TASKS=1`](/docs/en/env-vars#variables) disables auto-backgrounding along with the rest of the background task functionality.208Setting [`CLAUDE_CODE_DISABLE_BACKGROUND_TASKS=1`](/docs/en/env-vars#variables) disables auto-backgrounding along with the rest of the background task functionality.

201 209 


226Whatever you list, these rules apply:234Whatever you list, these rules apply:

227 235 

228* **Unknown names**: Claude Code ignores names it doesn't recognize236* **Unknown names**: Claude Code ignores names it doesn't recognize

229* **Bash, PowerShell, and Monitor**: Claude Code keeps Bash, PowerShell, and Monitor tool commands under the cap whatever you list

230* **Variable unset**: Claude Code takes the set of other capped kinds from configuration Anthropic delivers from the server, and that set can change over time, so set the variable when you need a set that doesn't change237* **Variable unset**: Claude Code takes the set of other capped kinds from configuration Anthropic delivers from the server, and that set can change over time, so set the variable when you need a set that doesn't change

231* **Permission-gating hooks**: even with every kind capped, Claude Code excludes from the cap a hook that can block or change the outcome of an action, and any MCP server that such a hook calls, so the kernel killing a permission-gating hook can't allow the action it was blocking238* **Permission-gating hooks**: even with every kind capped, Claude Code excludes from the cap a hook that can block or change the outcome of an action, and any MCP server that such a hook calls, so the kernel killing a permission-gating hook can't allow the action it was blocking

232 239 


584 591 

585Sandboxed commands don't inherit WebFetch's built-in set of preapproved documentation domains. To let a sandboxed command reach a domain without a prompt, add the domain to [`allowedDomains`](/docs/en/settings-reference#sandbox-network-alloweddomains) or allow it with a `WebFetch(domain:...)` rule, which the [sandbox also honors](/docs/en/sandboxing#network-isolation). WebFetch never reads the sandbox allowlist in return, so adding a domain to a sandbox or organization network allowlist doesn't stop WebFetch from prompting for it.592Sandboxed commands don't inherit WebFetch's built-in set of preapproved documentation domains. To let a sandboxed command reach a domain without a prompt, add the domain to [`allowedDomains`](/docs/en/settings-reference#sandbox-network-alloweddomains) or allow it with a `WebFetch(domain:...)` rule, which the [sandbox also honors](/docs/en/sandboxing#network-isolation). WebFetch never reads the sandbox allowlist in return, so adding a domain to a sandbox or organization network allowlist doesn't stop WebFetch from prompting for it.

586 593 

594### WebFetch availability

595 

596On Claude Code v2.1.285 or later, set [`CLAUDE_CODE_DISABLE_WEB_FETCH`](/docs/en/env-vars#variables) to `1` to turn WebFetch off.

597 

598If you sign in with a Team or Enterprise claude.ai account and don't connect through an [LLM gateway](/docs/en/llm-gateway), WebFetch also depends on your organization's policy, which Claude Code requests from `api.anthropic.com` when a session starts. The same goes for a session whose plan Claude Code can't determine, such as one running under a claude.ai token that another app supplied.

599 

600If WebFetch is missing from a session, run `/status` in the session. If its `Organization policy` line reports that the policy didn't load and names web fetch among the features that wait for it, Claude Code is withholding WebFetch until it can confirm that your organization allows it. Outside a session, `claude doctor` makes its own request and prints the same line. Once a policy that allows WebFetch loads, the tool returns without a restart.

601 

587## WebSearch tool behavior602## WebSearch tool behavior

588 603 

589WebSearch runs a query against Anthropic's [web search](https://platform.claude.com/docs/en/agents-and-tools/tool-use/web-search-tool) backend and returns result titles and URLs. It doesn't fetch the result pages. To read a page Claude finds in search results, it follows up with [WebFetch](#webfetch-tool-behavior).604WebSearch runs a query against Anthropic's [web search](https://platform.claude.com/docs/en/agents-and-tools/tool-use/web-search-tool) backend and returns result titles and URLs. It doesn't fetch the result pages. To read a page Claude finds in search results, it follows up with [WebFetch](#webfetch-tool-behavior).

ultrareview.md +1 −1

Details

60 60 

61In PR mode, the cloud sandbox clones the pull request directly from the host rather than bundling your local working tree. PR mode works with repositories on `github.com` and on [GitHub Enterprise Server](/docs/en/github-enterprise-server) instances that an Owner has connected to Claude Code.61In PR mode, the cloud sandbox clones the pull request directly from the host rather than bundling your local working tree. PR mode works with repositories on `github.com` and on [GitHub Enterprise Server](/docs/en/github-enterprise-server) instances that an Owner has connected to Claude Code.

62 62 

63For repositories on `github.com`, the sandbox clones with the GitHub account connected to your Claude account, so the account must be able to read the PR's repository. Claude Code checks this before creating the cloud session, unless you've set [`CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC`](/docs/en/env-vars#variables), and refuses the launch when [no account is connected](/docs/en/errors#no-github-account-is-connected-to-your-claude-account) or [the account can't see the repository](/docs/en/errors#your-connected-github-account-cant-see-the-repository); the refusal names the fix. Before v2.1.248, Claude Code didn't check this before launch.63For repositories on `github.com`, the sandbox clones with the GitHub account connected to your Claude account, so the account must be able to read the PR's repository.

64 64 

65Run [`/web-setup`](/docs/en/web-quickstart#connect-from-your-terminal) to connect your GitHub CLI login to your Claude account.65Run [`/web-setup`](/docs/en/web-quickstart#connect-from-your-terminal) to connect your GitHub CLI login to your Claude account.

66 66 

vs-code.md +23 −5

Details

52 52 

53 * **Activity Bar**: click the Spark icon in the left sidebar to open the sessions list. Click any session to open it in your [preferred location](#extension-settings), or start a new one. This icon is always visible in the Activity Bar.53 * **Activity Bar**: click the Spark icon in the left sidebar to open the sessions list. Click any session to open it in your [preferred location](#extension-settings), or start a new one. This icon is always visible in the Activity Bar.

54 * **Command Palette**: `Cmd+Shift+P` (Mac) or `Ctrl+Shift+P` (Windows/Linux), type "Claude Code", and select an option like "Open in New Tab"54 * **Command Palette**: `Cmd+Shift+P` (Mac) or `Ctrl+Shift+P` (Windows/Linux), type "Claude Code", and select an option like "Open in New Tab"

55 * **Status Bar**: if you've set [`preferredLocation`](#extension-settings) to `sidebar`, or opened Claude with **Claude Code: Open in Side Bar**, click **✻ Claude Code** in the bottom-right corner of the window. This works even when no file is open.55 * **Status Bar**: click **✻ Claude Code** in the bottom-right corner of the window. This works even when no file is open.

56 56 

57 You can drag the Claude panel to reposition it anywhere in VS Code. See [Customize your workflow](#customize-your-workflow) for details.57 You can drag the Claude panel to reposition it anywhere in VS Code. See [Customize your workflow](#customize-your-workflow) for details.

58 </Step>58 </Step>


328 328 

329In the Plugins tab:329In the Plugins tab:

330 330 

331* **Installed plugins** appear at the top with toggle switches to enable or disable them331* **Installed plugins** appear at the top with toggle switches to enable or disable them.

332 * If you turn off a plugin that your project's shared `.claude/settings.json` turns on, the extension asks first: **Disable for me** turns it off only for you, while **Disable for everyone** changes the shared file.

332* **Available plugins** from your configured marketplaces appear below333* **Available plugins** from your configured marketplaces appear below

333* Search to filter plugins by name or description334* Search to filter plugins by name or description

334* Click **Install** on any available plugin335* Click **Install** on any available plugin


339* **Install for this project**: shared with project collaborators (project scope)340* **Install for this project**: shared with project collaborators (project scope)

340* **Install locally**: only for you, only in this repository (local scope)341* **Install locally**: only for you, only in this repository (local scope)

341 342 

343Once the install finishes, a form asks for any of the plugin's [configuration options](/docs/en/plugins/components#user-configuration) that aren't set yet. To review or change the options later, click the gear icon on the plugin's row.

344 

345Sensitive text fields are masked, and a secret you saved earlier shows **(unchanged)**. Leave the field blank to keep the saved value.

346 

347After you save changes, the open sessions reload their plugins and the dialog shows **Restart Claude to apply plugin changes**.

348 

349### Uninstall plugins

350 

351Each installed row names the [scope](/docs/en/plugins/install#choose-an-install-scope) it's installed at. To uninstall that installation, click the row's trash icon. A dimmed trash icon marks a row you can't uninstall from this workspace, such as a plugin your organization manages or one installed for another project.

352 

353The extension asks first in two cases:

354 

355* **A plugin your project's shared `.claude/settings.json` turns on**: choose **Disable for me**, which keeps the plugin installed for your collaborators, or **Uninstall for everyone**, which removes the project's installation with [`--keep-data`](/docs/en/plugins/cli-reference#what-an-uninstall-deletes-and-keeps), so the plugin's saved data directory stays. If you already turned the plugin off for yourself, the trash icon removes your own installation without the question.

356* **Otherwise, the last installation of a plugin with saved data**: choose whether to keep or delete the data; **Keep** is the default

357 

342### Share a plugin install link358### Share a plugin install link

343 359 

344To send someone straight to installing a specific plugin, give them the extension's `install-plugin` URL. Opening it launches or focuses VS Code, opens the Claude Code panel, and opens the **Manage plugins** dialog on that plugin's scope choice. Nothing installs until the person picks a scope. If the plugin's marketplace isn't configured in their Claude Code yet, the dialog first asks them to add it.360To send someone straight to installing a specific plugin, give them the extension's `install-plugin` URL. Opening it launches or focuses VS Code, opens the Claude Code panel, and opens the **Manage plugins** dialog on that plugin's scope choice. Nothing installs until the person picks a scope. If the plugin's marketplace isn't configured in their Claude Code yet, the dialog first asks them to add it.


369 385 

370* Enter a GitHub repo, URL, or local path to add a new marketplace386* Enter a GitHub repo, URL, or local path to add a new marketplace

371* Click the refresh icon to update a marketplace's plugin list387* Click the refresh icon to update a marketplace's plugin list

372* Click the trash icon to remove a marketplace388* Click the trash icon to remove a marketplace. Removing it [uninstalls every plugin you installed from it](/docs/en/plugins/install#manage-marketplaces), so a confirmation names those plugins first

389 

390Plugin changes you make in the dialog apply right away to the Claude Code sessions open in that VS Code window.

373 391 

374Plugin changes you make in the dialog apply right away to the Claude Code sessions open in that VS Code window. If the session you opened the dialog from can't reload its plugins, the dialog offers to try again or to restart Claude in that session.392If the session you opened the dialog from can't reload its plugins, the dialog offers to try again or to restart Claude in that session.

375 393 

376<Note>394<Note>

377 Plugin management in VS Code uses the same CLI commands under the hood. Plugins and marketplaces you configure in the extension are also available in the CLI, and vice versa.395 Plugin management in VS Code uses the same CLI commands under the hood. Plugins and marketplaces you configure in the extension are also available in the CLI, and vice versa.


7084. **Disable conflicting extensions**: Temporarily disable other AI extensions (Cline, Continue, etc.)7264. **Disable conflicting extensions**: Temporarily disable other AI extensions (Cline, Continue, etc.)

7095. **Check workspace trust**: The extension doesn't work in Restricted Mode7275. **Check workspace trust**: The extension doesn't work in Restricted Mode

710 728 

711Alternatively, if you've set [`preferredLocation`](#extension-settings) to `sidebar`, or opened Claude with **Claude Code: Open in Side Bar**, click "✻ Claude Code" in the **Status Bar** (bottom-right corner). This works even without a file open. You can also use the **Command Palette** (`Cmd+Shift+P` / `Ctrl+Shift+P`) and type "Claude Code".729Alternatively, click **✻ Claude Code** in the **Status Bar** at the bottom-right corner of the window. This works even without a file open. You can also use the **Command Palette** (`Cmd+Shift+P` / `Ctrl+Shift+P`) and type "Claude Code".

712 730 

713### Cmd+Esc does nothing on macOS731### Cmd+Esc does nothing on macOS

714 732 

worktrees.md +1 −1

Details

129* The worktree belongs to a `--worktree` session you haven't backgrounded, whatever its age.129* The worktree belongs to a `--worktree` session you haven't backgrounded, whatever its age.

130* You created the worktree yourself with `git worktree add`, even if you then ran a `--worktree <name>` session in it and backgrounded that session.130* You created the worktree yourself with `git worktree add`, even if you then ran a `--worktree <name>` session in it and backgrounded that session.

131 131 

132Claude Code writes a marker into the git metadata of every worktree it creates with git, and the sweep keeps any worktree without one, including a worktree a [`WorktreeCreate` hook](#non-git-version-control) created. Before v2.1.246, the sweep didn't check for the marker, and could remove a worktree you created yourself when an old background-session record pointed at it.132Claude Code writes a marker into the git metadata of every worktree it creates with git, and the sweep keeps any worktree without one, including a worktree a [`WorktreeCreate` hook](#non-git-version-control) created.

133 133 

134While an agent is running, Claude Code holds a `git worktree lock` on its worktree so that concurrent cleanup can't remove it, and releases the lock when the agent finishes. Claude Code holds the same lock on the worktree it created for a backgrounded session while the session runs, so the sweep leaves the worktree in place and `git worktree remove` refuses to remove it.134While an agent is running, Claude Code holds a `git worktree lock` on its worktree so that concurrent cleanup can't remove it, and releases the lock when the agent finishes. Claude Code holds the same lock on the worktree it created for a backgrounded session while the session runs, so the sweep leaves the worktree in place and `git worktree remove` refuses to remove it.

135 135