SpyBara
Go Premium

Documentation 2026-07-06 23:57 UTC to 2026-07-07 16:02 UTC

13 files changed +51 −37. View all changes and history on the product overview
2026
Fri 10 17:00 Thu 9 23:58 Wed 8 16:02 Tue 7 16:02 Mon 6 23:57 Sat 4 03:01 Fri 3 23:00 Thu 2 23:59 Wed 1 21:01
Details

207The permission mode option (`permission_mode` in Python, `permissionMode` in TypeScript) controls whether the agent asks for approval before using tools:207The permission mode option (`permission_mode` in Python, `permissionMode` in TypeScript) controls whether the agent asks for approval before using tools:

208 208 

209| Mode | Behavior |209| Mode | Behavior |

210| :------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |210| :-------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |

211| `"default"` | Tools not covered by allow rules trigger your approval callback; no callback means deny |211| `"default"` | Tools not covered by allow rules trigger your approval callback; no callback means deny |

212| `"acceptEdits"` | Auto-approves file edits and common filesystem commands (`mkdir`, `touch`, `mv`, `cp`, etc.); other Bash commands follow default rules |212| `"acceptEdits"` | Auto-approves file edits and common filesystem commands (`mkdir`, `touch`, `mv`, `cp`, etc.); other Bash commands follow default rules |

213| `"plan"` | Claude explores and plans without editing your source files; file edits are never auto-approved and prompt through your `canUseTool` callback |213| `"plan"` | Claude explores and plans without editing your source files; file edits are never auto-approved and prompt through your `canUseTool` callback |

214| `"dontAsk"` | Never prompts. Tools pre-approved by [permission rules](/en/settings#permission-settings) run, everything else is denied |214| `"dontAsk"` | Never prompts. Tools pre-approved by [permission rules](/en/settings#permission-settings) run, everything else is denied |

215| `"auto"` (TypeScript only) | Uses a model classifier to approve or deny each tool call. See [Auto mode](/en/permission-modes#eliminate-prompts-with-auto-mode) for availability and behavior |215| `"auto"` | Uses a model classifier to approve or deny each tool call. See [Auto mode](/en/permission-modes#eliminate-prompts-with-auto-mode) for availability and behavior |

216| `"bypassPermissions"` | Runs all allowed tools without asking, unless an explicit [`ask` rule](/en/settings#permission-settings) matches; see [How permissions are evaluated](/en/agent-sdk/permissions#how-permissions-are-evaluated) for where ask rules sit in the precedence order. Cannot be used when running as root on Unix. Use only in isolated environments where the agent's actions cannot affect systems you care about |216| `"bypassPermissions"` | Runs all allowed tools without asking, unless an explicit [`ask` rule](/en/settings#permission-settings) matches; see [How permissions are evaluated](/en/agent-sdk/permissions#how-permissions-are-evaluated) for where ask rules sit in the precedence order. Cannot be used when running as root on Unix. Use only in isolated environments where the agent's actions cannot affect systems you care about |

217 217 

218For interactive applications, use `"default"` with a tool approval callback to surface approval prompts. For autonomous agents on a dev machine, `"acceptEdits"` auto-approves file edits and common filesystem commands (`mkdir`, `touch`, `mv`, `cp`, etc.) while still gating other `Bash` commands behind allow rules. Reserve `"bypassPermissions"` for CI, containers, or other isolated environments. See [Permissions](/en/agent-sdk/permissions) for full details.218For interactive applications, use `"default"` with a tool approval callback to surface approval prompts. For autonomous agents on a dev machine, `"acceptEdits"` auto-approves file edits and common filesystem commands (`mkdir`, `touch`, `mv`, `cp`, etc.) while still gating other `Bash` commands behind allow rules. Reserve `"bypassPermissions"` for CI, containers, or other isolated environments. See [Permissions](/en/agent-sdk/permissions) for full details.

Details

19| Pre-approve a tool | Add to your allowed tools. See [Configure allowed tools](#configure-allowed-tools). |19| Pre-approve a tool | Add to your allowed tools. See [Configure allowed tools](#configure-allowed-tools). |

20| Remove a built-in tool from Claude's context | Pass a `tools` array listing only the built-ins you want. See [Configure allowed tools](#configure-allowed-tools). |20| Remove a built-in tool from Claude's context | Pass a `tools` array listing only the built-ins you want. See [Configure allowed tools](#configure-allowed-tools). |

21| Let Claude call tools in parallel | Set `readOnlyHint: true` on tools with no side effects. See [Add tool annotations](#add-tool-annotations). |21| Let Claude call tools in parallel | Set `readOnlyHint: true` on tools with no side effects. See [Add tool annotations](#add-tool-annotations). |

22| Handle errors without stopping the loop | Return `isError: true` instead of throwing. See [Handle errors](#handle-errors). |22| Control the error message Claude reads | Return `isError: true` to compose the message instead of surfacing the raw exception. See [Handle errors](#handle-errors). |

23| Return images or files | Use `image` or `resource` blocks in the content array. See [Return images and resources](#return-images-and-resources). |23| Return images or files | Use `image` or `resource` blocks in the content array. See [Return images and resources](#return-images-and-resources). |

24| Return a machine-readable JSON result | Set `structuredContent` on the result. See [Return structured data](#return-structured-data). |24| Return a machine-readable JSON result | Set `structuredContent` on the result. See [Return structured data](#return-structured-data). |

25| Scale to many tools | Use [tool search](/en/agent-sdk/tool-search) to load tools on demand. |25| Scale to many tools | Use [tool search](/en/agent-sdk/tool-search) to load tools on demand. |


336 336 

337## Handle errors337## Handle errors

338 338 

339How your handler reports errors determines whether the agent loop continues or stops:339A handler error doesn't stop the agent loop. The SDK's in-process MCP server catches uncaught exceptions and returns them as error results, so how you report an error determines what Claude reads, not whether the query fails:

340 340 

341| What happens | Result |341| What happens | Result |

342| :--------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------- |342| :--------------------------------------------------------------------------------------- | :---------------------------------------------------------------------------------------------------------------------------------------- |

343| Handler throws an uncaught exception | Agent loop stops. Claude never sees the error, and the `query` call fails. |343| Handler throws an uncaught exception | The MCP server converts it to an error result carrying the raw exception message. Claude sees that message, and the agent loop continues. |

344| Handler catches the error and returns `isError: true` (TS) / `"is_error": True` (Python) | Agent loop continues. Claude sees the error as data and can retry, try a different tool, or explain the failure. |344| Handler catches the error and returns `isError: true` (TS) / `"is_error": True` (Python) | Claude sees the message you compose. You can add context the raw exception lacks, such as which request failed or what to try instead. |

345 345 

346The example below catches two kinds of failures inside the handler instead of letting them throw. A non-200 HTTP status is caught from the response and returned as an error result. A network error or invalid JSON is caught by the surrounding `try/except` (Python) or `try/catch` (TypeScript) and also returned as an error result. In both cases the handler returns normally and the agent loop continues.346In both cases Claude can retry, try a different tool, or explain the failure. Catch errors yourself when the raw exception message isn't enough for Claude to act on.

347 

348The example below catches two kinds of failures inside the handler and composes the error message Claude reads. A non-200 HTTP status is caught from the response and returned as an error result. A network error or invalid JSON is caught by the surrounding `try/except` (Python) or `try/catch` (TypeScript) and also returned as an error result. In both cases Claude receives a message that describes the failure instead of a bare exception string.

347 349 

348<CodeGroup>350<CodeGroup>

349 ```python Python theme={null}351 ```python Python theme={null}


377 data = response.json()379 data = response.json()

378 return {"content": [{"type": "text", "text": json.dumps(data, indent=2)}]}380 return {"content": [{"type": "text", "text": json.dumps(data, indent=2)}]}

379 except Exception as e:381 except Exception as e:

380 # Catching here keeps the agent loop alive. An uncaught exception382 # Composes the message Claude reads. An uncaught exception would

381 # would end the whole query() call.383 # reach Claude as the raw str(e) with no context.

382 return {384 return {

383 "content": [{"type": "text", "text": f"Failed to fetch data: {str(e)}"}],385 "content": [{"type": "text", "text": f"Failed to fetch data: {str(e)}"}],

384 "is_error": True,386 "is_error": True,


420 ]422 ]

421 };423 };

422 } catch (error) {424 } catch (error) {

423 // Catching here keeps the agent loop alive. An uncaught throw425 // Composes the message Claude reads. An uncaught throw would

424 // would end the whole query() call.426 // reach Claude as the raw error message with no context.

425 return {427 return {

426 content: [428 content: [

427 {429 {


439 441 

440## Return images and resources442## Return images and resources

441 443 

442The `content` array in a tool result accepts `text`, `image`, `audio`, `resource`, and `resource_link` blocks. You can mix them in the same response. Audio blocks are saved to disk and Claude receives a text block with the saved file path. Resource link blocks are converted to a text block containing the link's name, URI, and description.444The `content` array in a tool result accepts `text`, `image`, `audio`, `resource`, and `resource_link` blocks. You can mix them in the same response. In TypeScript, audio blocks are saved to disk and Claude receives a text block with the saved file path; in Python, the SDK drops audio blocks from the tool result and logs a warning. Resource link blocks are converted to a text block containing the link's name, URI, and description.

443 445 

444### Images446### Images

445 447 


509A resource block embeds a piece of content identified by a URI. The URI is a label for Claude to reference; the actual content rides in the block's `text` or `blob` field. Use this when your tool produces something that makes sense to address by name later, such as a generated file or a record from an external system.511A resource block embeds a piece of content identified by a URI. The URI is a label for Claude to reference; the actual content rides in the block's `text` or `blob` field. Use this when your tool produces something that makes sense to address by name later, such as a generated file or a record from an external system.

510 512 

511| Field | Type | Notes |513| Field | Type | Notes |

512| :------------------ | :----------- | :---------------------------------------------------------- |514| :------------------ | :----------- | :----------------------------------------------------------------------------------------------------------------------------------------- |

513| `type` | `"resource"` | |515| `type` | `"resource"` | |

514| `resource.uri` | `string` | Identifier for the content. Any URI scheme |516| `resource.uri` | `string` | Identifier for the content. Any URI scheme |

515| `resource.text` | `string` | The content, if it's text. Provide this or `blob`, not both |517| `resource.text` | `string` | The content, if it's text. Provide this or `blob`, not both |

516| `resource.blob` | `string` | The content base64-encoded, if it's binary |518| `resource.blob` | `string` | The content base64-encoded, if it's binary. TypeScript only: the Python SDK drops binary resources from the tool result and logs a warning |

517| `resource.mimeType` | `string` | Optional |519| `resource.mimeType` | `string` | Optional |

518 520 

519This example shows a resource block returned from inside a tool handler. The URI `file:///tmp/report.md` is a label that Claude can reference later; the SDK does not read from that path.521This example shows a resource block returned from inside a tool handler. The URI `file:///tmp/report.md` is a label that Claude can reference later; the SDK does not read from that path.

Details

238 238 

239* **Input data:** a typed object containing event details. Each hook type has its own input shape. For example, `PreToolUseHookInput` includes `tool_name` and `tool_input`, while `NotificationHookInput` includes `message`. See the full type definitions in the [TypeScript](/en/agent-sdk/typescript#hookinput) and [Python](/en/agent-sdk/python#hookinput) SDK references.239* **Input data:** a typed object containing event details. Each hook type has its own input shape. For example, `PreToolUseHookInput` includes `tool_name` and `tool_input`, while `NotificationHookInput` includes `message`. See the full type definitions in the [TypeScript](/en/agent-sdk/typescript#hookinput) and [Python](/en/agent-sdk/python#hookinput) SDK references.

240 * All hook inputs share `session_id`, `cwd`, and `hook_event_name`.240 * All hook inputs share `session_id`, `cwd`, and `hook_event_name`.

241 * `agent_id` and `agent_type` are populated when the hook fires inside a subagent. In TypeScript, these are on the base hook input and available to all hook types. In Python, they are on `PreToolUse`, `PostToolUse`, and `PostToolUseFailure` only.241 * `agent_id` and `agent_type` are populated when the hook fires inside a subagent. In TypeScript, these are on the base hook input and available to all hook types. In Python, they are optional fields on `PreToolUse`, `PostToolUse`, `PostToolUseFailure`, and `PermissionRequest`, and required fields on `SubagentStart` and `SubagentStop`.

242* **Tool use ID** (`str | None` / `string | undefined`): correlates `PreToolUse` and `PostToolUse` events for the same tool call.242* **Tool use ID** (`str | None` / `string | undefined`): correlates `PreToolUse` and `PostToolUse` events for the same tool call.

243* **Context:** in TypeScript, contains a `signal` property (`AbortSignal`) for cancellation. In Python, this argument is reserved for future use.243* **Context:** in TypeScript, contains a `signal` property (`AbortSignal`) for cancellation. In Python, this argument is reserved for future use.

244 244 

Details

101The SDK supports these permission modes:101The SDK supports these permission modes:

102 102 

103| Mode | Description | Tool behavior |103| Mode | Description | Tool behavior |

104| :----------------------- | :--------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------- |104| :------------------ | :--------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------- |

105| `default` | Standard permission behavior | No auto-approvals; unmatched tools trigger your `canUseTool` callback |105| `default` | Standard permission behavior | No auto-approvals; unmatched tools trigger your `canUseTool` callback |

106| `dontAsk` | Deny instead of prompting | Anything not pre-approved by `allowed_tools` or rules is denied; `canUseTool` is never called |106| `dontAsk` | Deny instead of prompting | Anything not pre-approved by `allowed_tools` or rules is denied; `canUseTool` is never called |

107| `acceptEdits` | Auto-accept file edits | File edits and [filesystem operations](#accept-edits-mode-acceptedits) (`mkdir`, `rm`, `mv`, etc.) are automatically approved |107| `acceptEdits` | Auto-accept file edits | File edits and [filesystem operations](#accept-edits-mode-acceptedits) (`mkdir`, `rm`, `mv`, etc.) are automatically approved |

108| `bypassPermissions` | Bypass permission checks | Tools run without permission prompts, unless an explicit [`ask` rule](#how-permissions-are-evaluated) matches (use with caution) |108| `bypassPermissions` | Bypass permission checks | Tools run without permission prompts, unless an explicit [`ask` rule](#how-permissions-are-evaluated) matches (use with caution) |

109| `plan` | Planning mode | Claude explores and plans without editing your source files; file edits are never auto-approved and prompt through your `canUseTool` callback |109| `plan` | Planning mode | Claude explores and plans without editing your source files; file edits are never auto-approved and prompt through your `canUseTool` callback |

110| `auto` (TypeScript only) | Model-classified approvals | A model classifier approves or denies each tool call. See [Auto mode](/en/permission-modes#eliminate-prompts-with-auto-mode) for availability |110| `auto` | Model-classified approvals | A model classifier approves or denies each tool call. See [Auto mode](/en/permission-modes#eliminate-prompts-with-auto-mode) for availability |

111 111 

112<Warning>112<Warning>

113 **Subagent inheritance:** When the parent uses `bypassPermissions`, `acceptEdits`, or `auto`, all subagents inherit that mode and it cannot be overridden per subagent. Subagents may have different system prompts and less constrained behavior than your main agent, so inheriting `bypassPermissions` grants them full, autonomous system access. An explicit [`ask` rule](#how-permissions-are-evaluated) still forces a prompt.113 **Subagent inheritance:** When the parent uses `bypassPermissions`, `acceptEdits`, or `auto`, all subagents inherit that mode and it cannot be overridden per subagent. Subagents may have different system prompts and less constrained behavior than your main agent, so inheriting `bypassPermissions` grants them full, autonomous system access. An explicit [`ask` rule](#how-permissions-are-evaluated) still forces a prompt.

Details

1074 "plan", # Planning mode - explore without editing1074 "plan", # Planning mode - explore without editing

1075 "dontAsk", # Deny anything not pre-approved instead of prompting1075 "dontAsk", # Deny anything not pre-approved instead of prompting

1076 "bypassPermissions", # Bypass permission checks; explicit ask rules still prompt (use with caution)1076 "bypassPermissions", # Bypass permission checks; explicit ask rules still prompt (use with caution)

1077 "auto", # A model classifier approves or denies each tool call

1077]1078]

1078```1079```

1079 1080 


2149 tool_name: str2150 tool_name: str

2150 tool_input: dict[str, Any]2151 tool_input: dict[str, Any]

2151 permission_suggestions: NotRequired[list[Any]]2152 permission_suggestions: NotRequired[list[Any]]

2153 agent_id: NotRequired[str]

2154 agent_type: NotRequired[str]

2152```2155```

2153 2156 

2154| Field | Type | Description |2157| Field | Type | Description |

2155| :----------------------- | :----------------------------- | :---------------------------------------- |2158| :----------------------- | :----------------------------- | :----------------------------------------------------------------- |

2156| `hook_event_name` | `Literal["PermissionRequest"]` | Always "PermissionRequest" |2159| `hook_event_name` | `Literal["PermissionRequest"]` | Always "PermissionRequest" |

2157| `tool_name` | `str` | Name of the tool requesting permission |2160| `tool_name` | `str` | Name of the tool requesting permission |

2158| `tool_input` | `dict[str, Any]` | Input parameters for the tool |2161| `tool_input` | `dict[str, Any]` | Input parameters for the tool |

2159| `permission_suggestions` | `list[Any]` (optional) | Suggested permission updates from the CLI |2162| `permission_suggestions` | `list[Any]` (optional) | Suggested permission updates from the CLI |

2163| `agent_id` | `str` (optional) | Subagent identifier, present when the hook fires inside a subagent |

2164| `agent_type` | `str` (optional) | Subagent type, present when the hook fires inside a subagent |

2160 2165 

2161### `HookJSONOutput`2166### `HookJSONOutput`

2162 2167 

Details

352**Permission modes** control how much human oversight you want:352**Permission modes** control how much human oversight you want:

353 353 

354| Mode | Behavior | Use case |354| Mode | Behavior | Use case |

355| ------------------------ | ----------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------- |355| ------------------- | ----------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------- |

356| `acceptEdits` | Auto-approves file edits and common filesystem commands, asks for other actions | Trusted development workflows |356| `acceptEdits` | Auto-approves file edits and common filesystem commands, asks for other actions | Trusted development workflows |

357| `plan` | Runs read-only tools; file edits are never auto-approved and reach your `canUseTool` callback | Scoping a task before approving execution |357| `plan` | Runs read-only tools; file edits are never auto-approved and reach your `canUseTool` callback | Scoping a task before approving execution |

358| `dontAsk` | Denies anything not in `allowedTools` | Locked-down headless agents |358| `dontAsk` | Denies anything not in `allowedTools` | Locked-down headless agents |

359| `auto` (TypeScript only) | A model classifier approves or denies each tool call | Autonomous agents with safety guardrails |359| `auto` | A model classifier approves or denies each tool call | Autonomous agents with safety guardrails |

360| `bypassPermissions` | Runs every tool without prompting, unless an explicit [`ask` rule](/en/agent-sdk/permissions#how-permissions-are-evaluated) matches | Sandboxed CI, fully trusted environments |360| `bypassPermissions` | Runs every tool without prompting, unless an explicit [`ask` rule](/en/agent-sdk/permissions#how-permissions-are-evaluated) matches | Sandboxed CI, fully trusted environments |

361| `default` | Requires a `canUseTool` callback to handle approval | Custom approval flows |361| `default` | Requires a `canUseTool` callback to handle approval | Custom approval flows |

362 362 

Details

194</CodeGroup>194</CodeGroup>

195 195 

196<Note>196<Note>

197 In Python, `can_use_tool` requires [streaming mode](/en/agent-sdk/streaming-vs-single-mode) and a `PreToolUse` hook that returns `{"continue_": True}` to keep the stream open. Without this hook, the stream closes before the permission callback can be invoked.197 In Python, `can_use_tool` requires [streaming mode](/en/agent-sdk/streaming-vs-single-mode). When you pass a finite message stream through `query(prompt=generator)` or `ClaudeSDKClient.connect(prompt=async_iterable)`, the SDK closes the input stream after the last message, before the permission callback can be invoked, unless a registered hook or in-process MCP server is keeping it open. The example above keeps it open with a `PreToolUse` hook that returns `{"continue_": True}`. Connecting with no prompt and sending messages through `ClaudeSDKClient.query()` keeps the stream open on its own and needs no hook.

198</Note>198</Note>

199 199 

200This example uses a `y/n` flow where any input other than `y` is treated as a denial. In practice, you might build a richer UI that lets users modify the request, provide feedback, or redirect Claude entirely. See [Respond to tool requests](#respond-to-tool-requests) for all the ways you can respond.200This example uses a `y/n` flow where any input other than `y` is treated as a denial. In practice, you might build a richer UI that lets users modify the request, provide feedback, or redirect Claude entirely. See [Respond to tool requests](#respond-to-tool-requests) for all the ways you can respond.

Details

34* **Chat platforms** (Telegram, Discord): your plugin runs locally and polls the platform's API for new messages. When someone DMs your bot, the plugin receives the message and forwards it to Claude. No URL to expose.34* **Chat platforms** (Telegram, Discord): your plugin runs locally and polls the platform's API for new messages. When someone DMs your bot, the plugin receives the message and forwards it to Claude. No URL to expose.

35* **Webhooks** (CI, monitoring): your server listens on a local HTTP port. External systems POST to that port, and your server pushes the payload to Claude.35* **Webhooks** (CI, monitoring): your server listens on a local HTTP port. External systems POST to that port, and your server pushes the payload to Claude.

36 36 

37<img src="https://mintcdn.com/claude-code/zbUxPYi8065L3Y_P/en/images/channel-architecture.svg?fit=max&auto=format&n=zbUxPYi8065L3Y_P&q=85&s=fd6b6b949eab38264043d2a96285a57c" alt="Architecture diagram showing external systems connecting to your local channel server, which communicates with Claude Code over stdio" width="600" height="220" data-path="en/images/channel-architecture.svg" />37<img src="https://mintcdn.com/claude-code/9FG0ZKj9uKYiHmbi/images/channel-architecture.svg?fit=max&auto=format&n=9FG0ZKj9uKYiHmbi&q=85&s=9a037b7da80184ae49015c0256b21a1f" alt="Architecture diagram showing external systems connecting to your local channel server, which communicates with Claude Code over stdio" width="600" height="220" data-path="images/channel-architecture.svg" />

38 38 

39## What you need39## What you need

40 40 


454 454 

455The local terminal dialog stays open through all of this. If someone at the terminal answers before the remote verdict arrives, that answer is applied instead and the pending remote request is dropped.455The local terminal dialog stays open through all of this. If someone at the terminal answers before the remote verdict arrives, that answer is applied instead and the pending remote request is dropped.

456 456 

457<img src="https://mintcdn.com/claude-code/DsZvsJII1OmzIjIs/en/images/channel-permission-relay.svg?fit=max&auto=format&n=DsZvsJII1OmzIjIs&q=85&s=c1d75f6ee34c2757983e2cca899b90d1" alt="Sequence diagram: Claude Code sends a permission_request notification to the channel server, the server formats and sends the prompt to the chat app, the human replies with a verdict, and the server parses that reply into a permission notification back to Claude Code" width="600" height="230" data-path="en/images/channel-permission-relay.svg" />457<img src="https://mintcdn.com/claude-code/9FG0ZKj9uKYiHmbi/images/channel-permission-relay.svg?fit=max&auto=format&n=9FG0ZKj9uKYiHmbi&q=85&s=97d57f128f0da55f105ab1e3a7e10240" alt="Sequence diagram: Claude Code sends a permission_request notification to the channel server, the server formats and sends the prompt to the chat app, the human replies with a verdict, and the server parses that reply into a permission notification back to Claude Code" width="600" height="230" data-path="images/channel-permission-relay.svg" />

458 458 

459### Permission request fields459### Permission request fields

460 460 

hooks.md +6 −4

Details

270 270 

271Hyphens in the exact-match set require Claude Code v2.1.195 or later. On earlier versions a bare hyphenated prefix like `mcp__brave-search` is evaluated as an unanchored regular expression and matches every tool from that server. The `mcp__brave-search__.*` form works on every version.271Hyphens in the exact-match set require Claude Code v2.1.195 or later. On earlier versions a bare hyphenated prefix like `mcp__brave-search` is evaluated as an unanchored regular expression and matches every tool from that server. The `mcp__brave-search__.*` form works on every version.

272 272 

273Tools from a [plugin-bundled MCP server](/en/mcp#plugin-provided-mcp-servers) use a scoped server segment that includes the plugin name: `mcp__plugin_<plugin-name>_<server-name>__<tool>`. A matcher written against the bare server key never fires for these tools. For a plugin named `my-plugin` that bundles a server under the key `db`, a `query` tool appears as `mcp__plugin_my-plugin_db__query`, so the matcher for every tool from that server is `mcp__plugin_my-plugin_db__.*`. Use the same scoped tool name in a handler's [`if` field](#common-fields). See [Plugin-provided MCP servers](/en/mcp#plugin-provided-mcp-servers) for how the scoped name is built.

274 

273This example logs all memory server operations and validates write operations from any MCP server:275This example logs all memory server operations and validates write operations from any MCP server:

274 276 

275```json theme={null}277```json theme={null}


434In addition to the [common fields](#common-fields), MCP tool hooks accept these fields:436In addition to the [common fields](#common-fields), MCP tool hooks accept these fields:

435 437 

436| Field | Required | Description |438| Field | Required | Description |

437| :------- | :------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------- |439| :------- | :------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |

438| `server` | yes | Name of a configured MCP server. The server must already be connected; the hook never triggers an OAuth or connection flow |440| `server` | yes | Name of a configured MCP server. For a [plugin-bundled server](/en/mcp#plugin-provided-mcp-servers), this is the scoped name `plugin:<plugin-name>:<server-name>`, such as `plugin:my-plugin:db`, not the bare server key. The server must already be connected; the hook never triggers an OAuth or connection flow |

439| `tool` | yes | Name of the tool to call on that server |441| `tool` | yes | Name of the tool to call on that server |

440| `input` | no | Arguments passed to the tool. String values support `${path}` substitution from the hook's [JSON input](#hook-input-and-output), such as `"${tool_input.file_path}"` |442| `input` | no | Arguments passed to the tool. String values support `${path}` substitution from the hook's [JSON input](#hook-input-and-output), such as `"${tool_input.file_path}"` |

441 443 


602| :---------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |604| :---------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |

603| `session_id` | Current session identifier |605| `session_id` | Current session identifier |

604| `prompt_id` | UUID identifying the user prompt currently being processed. Matches the [`prompt.id` attribute on OpenTelemetry events](/en/monitoring-usage#event-correlation-attributes), so you can correlate hook output with telemetry for a single prompt. Absent until the first user input. {/* min-version: 2.1.196 */}Requires Claude Code v2.1.196 or later |606| `prompt_id` | UUID identifying the user prompt currently being processed. Matches the [`prompt.id` attribute on OpenTelemetry events](/en/monitoring-usage#event-correlation-attributes), so you can correlate hook output with telemetry for a single prompt. Absent until the first user input. {/* min-version: 2.1.196 */}Requires Claude Code v2.1.196 or later |

605| `transcript_path` | Path to conversation JSON |607| `transcript_path` | Path to conversation JSON. The transcript file is written asynchronously and may lag the in-memory conversation, so it may not yet include the current turn's most recent messages when a hook fires. Hooks that need the final assistant text of the current turn should use `last_assistant_message` on [Stop](#stop) and [SubagentStop](#subagentstop) instead of reading the transcript |

606| `cwd` | Current working directory when the hook is invoked |608| `cwd` | Current working directory when the hook is invoked |

607| `permission_mode` | Current [permission mode](/en/permissions#permission-modes): `"default"`, `"plan"`, `"acceptEdits"`, `"auto"`, `"dontAsk"`, or `"bypassPermissions"`. The mode labeled **Manual** arrives as `"default"`, never as `"manual"`, so scripts that match `"default"` keep working. Not all events receive this field. Check the JSON example in each [hook event](#hook-events) section |609| `permission_mode` | Current [permission mode](/en/permissions#permission-modes): `"default"`, `"plan"`, `"acceptEdits"`, `"auto"`, `"dontAsk"`, or `"bypassPermissions"`. The mode labeled **Manual** arrives as `"default"`, never as `"manual"`, so scripts that match `"default"` keep working. Not all events receive this field. Check the JSON example in each [hook event](#hook-events) section |

608| `effort` | Object with a `level` field holding the active [effort level](/en/model-config#adjust-effort-level) for the turn: `"low"`, `"medium"`, `"high"`, `"xhigh"`, or `"max"`. If the requested model effort exceeds what the current model supports, this is the downgraded level the model actually used. Ultracode is not a distinct level and reports as `"xhigh"`. The object matches the [status line](/en/statusline#available-data) `effort` field. Present for events that fire within a tool-use context, such as `PreToolUse`, `PostToolUse`, `Stop`, and `SubagentStop`, when the current model supports the effort parameter. The level is also available to hook commands and the Bash tool as the `$CLAUDE_EFFORT` environment variable. |610| `effort` | Object with a `level` field holding the active [effort level](/en/model-config#adjust-effort-level) for the turn: `"low"`, `"medium"`, `"high"`, `"xhigh"`, or `"max"`. If the requested model effort exceeds what the current model supports, this is the downgraded level the model actually used. Ultracode is not a distinct level and reports as `"xhigh"`. The object matches the [status line](/en/statusline#available-data) `effort` field. Present for events that fire within a tool-use context, such as `PreToolUse`, `PostToolUse`, `Stop`, and `SubagentStop`, when the current model supports the effort parameter. The level is also available to hook commands and the Bash tool as the `$CLAUDE_EFFORT` environment variable. |


2133 2135 

2134In addition to the [common input fields](#common-input-fields), Stop hooks receive `stop_hook_active`, `last_assistant_message`, `background_tasks`, and `session_crons`. The `stop_hook_active` field is `true` when Claude Code is already continuing as a result of a stop hook. Check this value or process the transcript to avoid blocking on a condition that will never resolve. Claude Code overrides the hook and ends the turn after 8 consecutive blocks.2136In addition to the [common input fields](#common-input-fields), Stop hooks receive `stop_hook_active`, `last_assistant_message`, `background_tasks`, and `session_crons`. The `stop_hook_active` field is `true` when Claude Code is already continuing as a result of a stop hook. Check this value or process the transcript to avoid blocking on a condition that will never resolve. Claude Code overrides the hook and ends the turn after 8 consecutive blocks.

2135 2137 

2136The `last_assistant_message` field contains the text content of Claude's final response, so hooks can access it without parsing the transcript file.2138The `last_assistant_message` field contains the text content of Claude's final response, so hooks can access it without parsing the transcript file. For hooks that act on the just-completed turn, such as read-aloud or notification hooks, use this field rather than reading `transcript_path`: the transcript file isn't guaranteed to include the final message at Stop time on all versions.

2137 2139 

2138The `background_tasks` and `session_crons` arrays, available in Claude Code v2.1.145 or later, let hooks distinguish "session is done" from "session is paused waiting for background work to wake it back up". Both arrays are present when the task registry is reachable and are empty when nothing is in flight or scheduled.2140The `background_tasks` and `session_crons` arrays, available in Claude Code v2.1.145 or later, let hooks distinguish "session is done" from "session is paused waiting for background work to wake it back up". Both arrays are present when the task registry is reachable and are empty when nothing is in flight or scheduled.

2139 2141 

hooks-guide.md +1 −1

Details

663 </Tab>663 </Tab>

664 664 

665 <Tab title="Match MCP tools">665 <Tab title="Match MCP tools">

666 MCP tools use a different naming convention than built-in tools: `mcp__<server>__<tool>`, where `<server>` is the MCP server name and `<tool>` is the tool it provides. For example, `mcp__github__search_repositories` or `mcp__filesystem__read_file`. Use a regex matcher to target all tools from a specific server, or match across servers with a pattern like `mcp__.*__write.*`. See [Match MCP tools](/en/hooks#match-mcp-tools) in the reference for the full list of examples.666 MCP tools use a different naming convention than built-in tools: `mcp__<server>__<tool>`, where `<server>` is the MCP server name and `<tool>` is the tool it provides. For example, `mcp__github__search_repositories` or `mcp__filesystem__read_file`. Tools from a [plugin-bundled server](/en/mcp#plugin-provided-mcp-servers) use a scoped server segment instead, such as `mcp__plugin_my-plugin_db__query`. Use a regex matcher to target all tools from a specific server, or match across servers with a pattern like `mcp__.*__write.*`. See [Match MCP tools](/en/hooks#match-mcp-tools) in the reference for the full list of examples.

667 667 

668 The command below extracts the tool name from the hook's JSON input with `jq` and writes it to stderr. Writing to stderr keeps stdout clean for JSON output and sends the message to the [debug log](/en/hooks#debug-hooks):668 The command below extracts the tool name from the hook's JSON input with `jq` and writes it to stderr. Writing to stderr keeps stdout clean for JSON output and sends the message to the [debug log](/en/hooks#debug-hooks):

669 669 

mcp.md +6 −3

Details

200<Tip>200<Tip>

201 Tips:201 Tips:

202 202 

203 * Use the `--scope` flag to specify where the configuration is stored:203 * Use the `-s` or `--scope` flag to specify where the configuration is stored:

204 * `local` (default): available only to you in the current project. Older versions called this scope `project`204 * `local` (default): available only to you in the current project. Older versions called this scope `project`

205 * `project`: shared with everyone in the project via the `.mcp.json` file205 * `project`: shared with everyone in the project via the `.mcp.json` file

206 * `user`: available to you across all projects. Older versions called this scope `global`206 * `user`: available to you across all projects. Older versions called this scope `global`

207 * Set environment variables with `--env` flags (for example, `--env KEY=value`)207 * Set environment variables with `-e` or `--env` flags (for example, `-e KEY=value`)

208 * The `--transport` and `--header` flags also accept `-t` and `-H` short forms

208 * Configure MCP server startup timeout using the `MCP_TIMEOUT` environment variable (for example, `MCP_TIMEOUT=10000 claude` sets a 10-second timeout)209 * Configure MCP server startup timeout using the `MCP_TIMEOUT` environment variable (for example, `MCP_TIMEOUT=10000 claude` sets a 10-second timeout)

209 * Set a per-server tool execution timeout by adding a `timeout` field in milliseconds to that server's `.mcp.json` entry, for example `"timeout": 600000` for ten minutes. This overrides the `MCP_TOOL_TIMEOUT` environment variable for that server only210 * Set a per-server tool execution timeout by adding a `timeout` field in milliseconds to that server's `.mcp.json` entry, for example `"timeout": 600000` for ten minutes. This overrides the `MCP_TOOL_TIMEOUT` environment variable for that server only

210 * Claude Code displays a warning when MCP tool output exceeds 10,000 tokens. To increase this limit, set the `MAX_MCP_OUTPUT_TOKENS` environment variable (for example, `MAX_MCP_OUTPUT_TOKENS=50000`)211 * Claude Code displays a warning when MCP tool output exceeds 10,000 tokens. To increase this limit, set the `MAX_MCP_OUTPUT_TOKENS` environment variable (for example, `MAX_MCP_OUTPUT_TOKENS=50000`)


284mcp__plugin_my-plugin_database-tools__query285mcp__plugin_my-plugin_database-tools__query

285```286```

286 287 

287Use this full name when referencing the tool in [permission rules](/en/permissions), a skill's `allowed-tools` list, or a [subagent's `tools` field](/en/sub-agents#available-tools).288Use this full name when referencing the tool in [permission rules](/en/permissions), a skill's `allowed-tools` list, a [subagent's `tools` field](/en/sub-agents#available-tools), or a [hook matcher](/en/hooks#match-mcp-tools). A hook matcher written against the bare server key, such as `mcp__database-tools__.*`, never fires for a plugin-bundled server.

289 

290The server itself registers under the scoped name `plugin:<plugin-name>:<server-name>`, such as `plugin:my-plugin:database-tools`. Use that name where a configured server name is expected, such as an [`mcp_tool` hook's `server` field](/en/hooks#mcp-tool-hook-fields).

288 291 

289**Benefits of plugin MCP servers**:292**Benefits of plugin MCP servers**:

290 293 

Details

151* `prompt`: evaluate a prompt with an LLM (uses `$ARGUMENTS` placeholder for context)151* `prompt`: evaluate a prompt with an LLM (uses `$ARGUMENTS` placeholder for context)

152* `agent`: run an agentic verifier with tools for complex verification tasks152* `agent`: run an agentic verifier with tools for complex verification tasks

153 153 

154Hooks that target the plugin's own [bundled MCP server](#mcp-servers) must use its scoped names. Tool matchers and `if` fields take the scoped tool name `mcp__plugin_<plugin-name>_<server-name>__<tool>`, and an `mcp_tool` hook's `server` field takes `plugin:<plugin-name>:<server-name>`. A matcher written against the bare server key never fires. See [Match MCP tools](/en/hooks#match-mcp-tools) and [Plugin-provided MCP servers](/en/mcp#plugin-provided-mcp-servers).

155 

154### MCP servers156### MCP servers

155 157 

156Plugins can bundle Model Context Protocol (MCP) servers to connect Claude Code with external tools and services.158Plugins can bundle Model Context Protocol (MCP) servers to connect Claude Code with external tools and services.

setup.md +5 −5

Details

41 <Tab title="Native Install (Recommended)">41 <Tab title="Native Install (Recommended)">

42 **macOS, Linux, WSL:**42 **macOS, Linux, WSL:**

43 43 

44 ```bash theme={null} theme={null} theme={null} theme={null} theme={null}44 ```bash theme={null}

45 curl -fsSL https://claude.ai/install.sh | bash45 curl -fsSL https://claude.ai/install.sh | bash

46 ```46 ```

47 47 

48 **Windows PowerShell:**48 **Windows PowerShell:**

49 49 

50 ```powershell theme={null} theme={null} theme={null} theme={null} theme={null}50 ```powershell theme={null}

51 irm https://claude.ai/install.ps1 | iex51 irm https://claude.ai/install.ps1 | iex

52 ```52 ```

53 53 

54 **Windows CMD:**54 **Windows CMD:**

55 55 

56 ```batch theme={null} theme={null} theme={null} theme={null} theme={null}56 ```batch theme={null}

57 curl -fsSL https://claude.ai/install.cmd -o install.cmd && install.cmd && del install.cmd57 curl -fsSL https://claude.ai/install.cmd -o install.cmd && install.cmd && del install.cmd

58 ```58 ```

59 59 


69 </Tab>69 </Tab>

70 70 

71 <Tab title="Homebrew">71 <Tab title="Homebrew">

72 ```bash theme={null} theme={null} theme={null} theme={null} theme={null}72 ```bash theme={null}

73 brew install --cask claude-code73 brew install --cask claude-code

74 ```74 ```

75 75 


81 </Tab>81 </Tab>

82 82 

83 <Tab title="WinGet">83 <Tab title="WinGet">

84 ```powershell theme={null} theme={null} theme={null} theme={null} theme={null}84 ```powershell theme={null}

85 winget install Anthropic.ClaudeCode85 winget install Anthropic.ClaudeCode

86 ```86 ```

87 87