SpyBara
Go Premium

Documentation 2026-04-07 21:14 UTC to 2026-04-08 21:13 UTC

67 files changed +18,419 −648. View all changes and history on the product overview
2026
Wed 29 21:21 Tue 28 21:21 Mon 27 21:20 Sun 26 04:08 Sat 25 21:10 Fri 24 18:11 Thu 23 18:19 Wed 22 21:15 Tue 21 21:14 Mon 20 21:14 Sat 18 18:09 Fri 17 21:13 Thu 16 21:13 Wed 15 18:20 Tue 14 21:14 Mon 13 21:14 Sat 11 00:11 Fri 10 21:09 Thu 9 21:14 Wed 8 21:13 Tue 7 21:14 Sat 4 18:05 Fri 3 21:07 Thu 2 21:08 Wed 1 21:12

agent-sdk/agent-loop.md +394 −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# How the agent loop works

6 

7> Understand the message lifecycle, tool execution, context window, and architecture that power your SDK agents.

8 

9The Agent SDK lets you embed Claude Code's autonomous agent loop in your own applications. The SDK is a standalone package that gives you programmatic control over tools, permissions, cost limits, and output. You don't need the Claude Code CLI installed to use it.

10 

11When you start an agent, the SDK runs the same [execution loop that powers Claude Code](/en/how-claude-code-works#the-agentic-loop): Claude evaluates your prompt, calls tools to take action, receives the results, and repeats until the task is complete. This page explains what happens inside that loop so you can build, debug, and optimize your agents effectively.

12 

13## The loop at a glance

14 

15Every agent session follows the same cycle:

16 

17<img src="https://mintcdn.com/claude-code/gvy2DIUELtNA8qD3/images/agent-loop-diagram.svg?fit=max&auto=format&n=gvy2DIUELtNA8qD3&q=85&s=192e1bd6c8a2950a16e5ee0b94e27e26" alt="Agent loop: prompt enters, Claude evaluates, branches to tool calls or final answer" width="680" height="150" data-path="images/agent-loop-diagram.svg" />

18 

191. **Receive prompt.** Claude receives your prompt, along with the system prompt, tool definitions, and conversation history. The SDK yields a [`SystemMessage`](#message-types) with subtype `"init"` containing session metadata.

202. **Evaluate and respond.** Claude evaluates the current state and determines how to proceed. It may respond with text, request one or more tool calls, or both. The SDK yields an [`AssistantMessage`](#message-types) containing the text and any tool call requests.

213. **Execute tools.** The SDK runs each requested tool and collects the results. Each set of tool results feeds back to Claude for the next decision. You can use [hooks](/en/agent-sdk/hooks) to intercept, modify, or block tool calls before they run.

224. **Repeat.** Steps 2 and 3 repeat as a cycle. Each full cycle is one turn. Claude continues calling tools and processing results until it produces a response with no tool calls.

235. **Return result.** The SDK yields a final [`AssistantMessage`](#message-types) with the text response (no tool calls), followed by a [`ResultMessage`](#message-types) with the final text, token usage, cost, and session ID.

24 

25A quick question ("what files are here?") might take one or two turns of calling `Glob` and responding with the results. A complex task ("refactor the auth module and update the tests") can chain dozens of tool calls across many turns, reading files, editing code, and running tests, with Claude adjusting its approach based on each result.

26 

27## Turns and messages

28 

29A turn is one round trip inside the loop: Claude produces output that includes tool calls, the SDK executes those tools, and the results feed back to Claude automatically. This happens without yielding control back to your code. Turns continue until Claude produces output with no tool calls, at which point the loop ends and the final result is delivered.

30 

31Consider what a full session might look like for the prompt "Fix the failing tests in auth.ts".

32 

33First, the SDK sends your prompt to Claude and yields a [`SystemMessage`](#message-types) with the session metadata. Then the loop begins:

34 

351. **Turn 1:** Claude calls `Bash` to run `npm test`. The SDK yields an [`AssistantMessage`](#message-types) with the tool call, executes the command, then yields a [`UserMessage`](#message-types) with the output (three failures).

362. **Turn 2:** Claude calls `Read` on `auth.ts` and `auth.test.ts`. The SDK returns the file contents and yields an `AssistantMessage`.

373. **Turn 3:** Claude calls `Edit` to fix `auth.ts`, then calls `Bash` to re-run `npm test`. All three tests pass. The SDK yields an `AssistantMessage`.

384. **Final turn:** Claude produces a text-only response with no tool calls: "Fixed the auth bug, all three tests pass now." The SDK yields a final `AssistantMessage` with this text, then a [`ResultMessage`](#message-types) with the same text plus cost and usage.

39 

40That was four turns: three with tool calls, one final text-only response.

41 

42You can cap the loop with `max_turns` / `maxTurns`, which counts tool-use turns only. For example, `max_turns=2` in the loop above would have stopped before the edit step. You can also use `max_budget_usd` / `maxBudgetUsd` to cap turns based on a spend threshold.

43 

44Without limits, the loop runs until Claude finishes on its own, which is fine for well-scoped tasks but can run long on open-ended prompts ("improve this codebase"). Setting a budget is a good default for production agents. See [Turns and budget](#turns-and-budget) below for the option reference.

45 

46## Message types

47 

48As the loop runs, the SDK yields a stream of messages. Each message carries a type that tells you what stage of the loop it came from. The five core types are:

49 

50* **`SystemMessage`:** session lifecycle events. The `subtype` field distinguishes them: `"init"` is the first message (session metadata), and `"compact_boundary"` fires after [compaction](#automatic-compaction). In TypeScript, the compact boundary is its own [`SDKCompactBoundaryMessage`](/en/agent-sdk/typescript#sdk-compact-boundary-message) type rather than a subtype of `SDKSystemMessage`.

51* **`AssistantMessage`:** emitted after each Claude response, including the final text-only one. Contains text content blocks and tool call blocks from that turn.

52* **`UserMessage`:** emitted after each tool execution with the tool result content sent back to Claude. Also emitted for any user inputs you stream mid-loop.

53* **`StreamEvent`:** only emitted when partial messages are enabled. Contains raw API streaming events (text deltas, tool input chunks). See [Stream responses](/en/agent-sdk/streaming-output).

54* **`ResultMessage`:** the last message, always. Contains the final text result, token usage, cost, and session ID. Check the `subtype` field to determine whether the task succeeded or hit a limit. See [Handle the result](#handle-the-result).

55 

56These five types cover the full agent loop lifecycle in both SDKs. The TypeScript SDK also yields additional observability events (hook events, tool progress, rate limits, task notifications) that provide extra detail but are not required to drive the loop. See the [Python message types reference](/en/agent-sdk/python#message-types) and [TypeScript message types reference](/en/agent-sdk/typescript#message-types) for the complete lists.

57 

58### Handle messages

59 

60Which messages you handle depends on what you're building:

61 

62* **Final results only:** handle `ResultMessage` to get the output, cost, and whether the task succeeded or hit a limit.

63* **Progress updates:** handle `AssistantMessage` to see what Claude is doing each turn, including which tools it called.

64* **Live streaming:** enable partial messages (`include_partial_messages` in Python, `includePartialMessages` in TypeScript) to get `StreamEvent` messages in real time. See [Stream responses in real-time](/en/agent-sdk/streaming-output).

65 

66How you check message types depends on the SDK:

67 

68* **Python:** check message types with `isinstance()` against classes imported from `claude_agent_sdk` (for example, `isinstance(message, ResultMessage)`).

69* **TypeScript:** check the `type` string field (for example, `message.type === "result"`). `AssistantMessage` and `UserMessage` wrap the raw API message in a `.message` field, so content blocks are at `message.message.content`, not `message.content`.

70 

71<Accordion title="Example: Check message types and handle results">

72 <CodeGroup>

73 ```python Python theme={null}

74 from claude_agent_sdk import query, AssistantMessage, ResultMessage

75 

76 async for message in query(prompt="Summarize this project"):

77 if isinstance(message, AssistantMessage):

78 print(f"Turn completed: {len(message.content)} content blocks")

79 if isinstance(message, ResultMessage):

80 if message.subtype == "success":

81 print(message.result)

82 else:

83 print(f"Stopped: {message.subtype}")

84 ```

85 

86 ```typescript TypeScript theme={null}

87 import { query } from "@anthropic-ai/claude-agent-sdk";

88 

89 for await (const message of query({ prompt: "Summarize this project" })) {

90 if (message.type === "assistant") {

91 console.log(`Turn completed: ${message.message.content.length} content blocks`);

92 }

93 if (message.type === "result") {

94 if (message.subtype === "success") {

95 console.log(message.result);

96 } else {

97 console.log(`Stopped: ${message.subtype}`);

98 }

99 }

100 }

101 ```

102 </CodeGroup>

103</Accordion>

104 

105## Tool execution

106 

107Tools give your agent the ability to take action. Without tools, Claude can only respond with text. With tools, Claude can read files, run commands, search code, and interact with external services.

108 

109### Built-in tools

110 

111The SDK includes the same tools that power Claude Code:

112 

113| Category | Tools | What they do |

114| :------------------ | :----------------------------------------------- | :-------------------------------------------------------------------------- |

115| **File operations** | `Read`, `Edit`, `Write` | Read, modify, and create files |

116| **Search** | `Glob`, `Grep` | Find files by pattern, search content with regex |

117| **Execution** | `Bash` | Run shell commands, scripts, git operations |

118| **Web** | `WebSearch`, `WebFetch` | Search the web, fetch and parse pages |

119| **Discovery** | `ToolSearch` | Dynamically find and load tools on-demand instead of preloading all of them |

120| **Orchestration** | `Agent`, `Skill`, `AskUserQuestion`, `TodoWrite` | Spawn subagents, invoke skills, ask the user, track tasks |

121 

122Beyond built-in tools, you can:

123 

124* **Connect external services** with [MCP servers](/en/agent-sdk/mcp) (databases, browsers, APIs)

125* **Define custom tools** with [custom tool handlers](/en/agent-sdk/custom-tools)

126* **Load project skills** via [setting sources](/en/agent-sdk/claude-code-features) for reusable workflows

127 

128### Tool permissions

129 

130Claude determines which tools to call based on the task, but you control whether those calls are allowed to execute. You can auto-approve specific tools, block others entirely, or require approval for everything. Three options work together to determine what runs:

131 

132* **`allowed_tools` / `allowedTools`** auto-approves listed tools. A read-only agent with `["Read", "Glob", "Grep"]` in its allowed tools list runs those tools without prompting. Tools not listed are still available but require permission.

133* **`disallowed_tools` / `disallowedTools`** blocks listed tools, regardless of other settings. See [Permissions](/en/agent-sdk/permissions) for the order that rules are checked before a tool runs.

134* **`permission_mode` / `permissionMode`** controls what happens to tools that aren't covered by allow or deny rules. See [Permission mode](#permission-mode) for available modes.

135 

136You can also scope individual tools with rules like `"Bash(npm:*)"` to allow only specific commands. See [Permissions](/en/agent-sdk/permissions) for the full rule syntax.

137 

138When a tool is denied, Claude receives a rejection message as the tool result and typically attempts a different approach or reports that it couldn't proceed.

139 

140### Parallel tool execution

141 

142When Claude requests multiple tool calls in a single turn, both SDKs can run them concurrently or sequentially depending on the tool. Read-only tools (like `Read`, `Glob`, `Grep`, and MCP tools marked as read-only) can run concurrently. Tools that modify state (like `Edit`, `Write`, and `Bash`) run sequentially to avoid conflicts.

143 

144Custom tools default to sequential execution. To enable parallel execution for a custom tool, mark it as read-only in its annotations: `readOnly` in [TypeScript](/en/agent-sdk/typescript#tool) or `readOnlyHint` in [Python](/en/agent-sdk/python#tool).

145 

146## Control how the loop runs

147 

148You can limit how many turns the loop takes, how much it costs, how deeply Claude reasons, and whether tools require approval before running. All of these are fields on [`ClaudeAgentOptions`](/en/agent-sdk/python#claude-agent-options) (Python) / [`Options`](/en/agent-sdk/typescript#options) (TypeScript).

149 

150### Turns and budget

151 

152| Option | What it controls | Default |

153| :--------------------------------------------- | :--------------------------- | :------- |

154| Max turns (`max_turns` / `maxTurns`) | Maximum tool-use round trips | No limit |

155| Max budget (`max_budget_usd` / `maxBudgetUsd`) | Maximum cost before stopping | No limit |

156 

157When either limit is hit, the SDK returns a `ResultMessage` with a corresponding error subtype (`error_max_turns` or `error_max_budget_usd`). See [Handle the result](#handle-the-result) for how to check these subtypes and [`ClaudeAgentOptions`](/en/agent-sdk/python#claude-agent-options) / [`Options`](/en/agent-sdk/typescript#options) for syntax.

158 

159### Effort level

160 

161The `effort` option controls how much reasoning Claude applies. Lower effort levels use fewer tokens per turn and reduce cost. Not all models support the effort parameter. See [Effort](https://platform.claude.com/docs/en/build-with-claude/effort) for which models support it.

162 

163| Level | Behavior | Good for |

164| :--------- | :-------------------------------- | :------------------------------------------ |

165| `"low"` | Minimal reasoning, fast responses | File lookups, listing directories |

166| `"medium"` | Balanced reasoning | Routine edits, standard tasks |

167| `"high"` | Thorough analysis | Refactors, debugging |

168| `"max"` | Maximum reasoning depth | Multi-step problems requiring deep analysis |

169 

170If you don't set `effort`, the Python SDK leaves the parameter unset and defers to the model's default behavior. The TypeScript SDK defaults to `"high"`.

171 

172<Note>

173 `effort` trades latency and token cost for reasoning depth within each response. [Extended thinking](https://platform.claude.com/docs/en/build-with-claude/extended-thinking) is a separate feature that produces visible chain-of-thought blocks in the output. They are independent: you can set `effort: "low"` with extended thinking enabled, or `effort: "max"` without it.

174</Note>

175 

176Use lower effort for agents doing simple, well-scoped tasks (like listing files or running a single grep) to reduce cost and latency. `effort` is set at the top-level `query()` options, not per-subagent.

177 

178### Permission mode

179 

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

181 

182| Mode | Behavior |

183| :------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |

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

185| `"acceptEdits"` | Auto-approves file edits, other tools follow default rules |

186| `"plan"` | No tool execution; Claude produces a plan for review |

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

188| `"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 |

189| `"bypassPermissions"` | Runs all allowed tools without asking. 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 |

190 

191For 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 while still gating `Bash` behind allow rules. Reserve `"bypassPermissions"` for CI, containers, or other isolated environments. See [Permissions](/en/agent-sdk/permissions) for full details.

192 

193### Model

194 

195If you don't set `model`, the SDK uses Claude Code's default, which depends on your authentication method and subscription. Set it explicitly (for example, `model="claude-sonnet-4-6"`) to pin a specific model or to use a smaller model for faster, cheaper agents. See [models](https://platform.claude.com/docs/en/about-claude/models) for available IDs.

196 

197## The context window

198 

199The context window is the total amount of information available to Claude during a session. It does not reset between turns within a session. Everything accumulates: the system prompt, tool definitions, conversation history, tool inputs, and tool outputs. Content that stays the same across turns (system prompt, tool definitions, CLAUDE.md) is automatically [prompt cached](https://platform.claude.com/docs/en/build-with-claude/prompt-caching), which reduces cost and latency for repeated prefixes.

200 

201### What consumes context

202 

203Here's how each component affects context in the SDK:

204 

205| Source | When it loads | Impact |

206| :----------------------- | :------------------------------------------------------------------------------------ | :--------------------------------------------------------------------------------------------------------------------------------- |

207| **System prompt** | Every request | Small fixed cost, always present |

208| **CLAUDE.md files** | Session start, when [`settingSources`](/en/agent-sdk/claude-code-features) is enabled | Full content in every request (but prompt-cached, so only the first request pays full cost) |

209| **Tool definitions** | Every request | Each tool adds its schema; use [MCP tool search](/en/agent-sdk/mcp#mcp-tool-search) to load tools on-demand instead of all at once |

210| **Conversation history** | Accumulates over turns | Grows with each turn: prompts, responses, tool inputs, tool outputs |

211| **Skill descriptions** | Session start (with setting sources enabled) | Short summaries; full content loads only when invoked |

212 

213Large tool outputs consume significant context. Reading a big file or running a command with verbose output can use thousands of tokens in a single turn. Context accumulates across turns, so longer sessions with many tool calls build up significantly more context than short ones.

214 

215### Automatic compaction

216 

217When the context window approaches its limit, the SDK automatically compacts the conversation: it summarizes older history to free space, keeping your most recent exchanges and key decisions intact. The SDK emits a message with `type: "system"` and `subtype: "compact_boundary"` in the stream when this happens (in Python this is a `SystemMessage`; in TypeScript it is a separate `SDKCompactBoundaryMessage` type).

218 

219Compaction replaces older messages with a summary, so specific instructions from early in the conversation may not be preserved. Persistent rules belong in CLAUDE.md (loaded via [`settingSources`](/en/agent-sdk/claude-code-features)) rather than in the initial prompt, because CLAUDE.md content is re-injected on every request.

220 

221You can customize compaction behavior in several ways:

222 

223* **Summarization instructions in CLAUDE.md:** The compactor reads your CLAUDE.md like any other context, so you can include a section telling it what to preserve when summarizing. The section header is free-form (not a magic string); the compactor matches on intent.

224* **`PreCompact` hook:** Run custom logic before compaction occurs, for example to archive the full transcript. The hook receives a `trigger` field (`manual` or `auto`). See [hooks](/en/agent-sdk/hooks).

225* **Manual compaction:** Send `/compact` as a prompt string to trigger compaction on demand. (Slash commands sent this way are SDK inputs, not CLI-only shortcuts. See [slash commands in the SDK](/en/agent-sdk/slash-commands).)

226 

227<Accordion title="Example: Summarization instructions in CLAUDE.md">

228 Add a section to your project's CLAUDE.md telling the compactor what to preserve. The header name isn't special; use any clear label.

229 

230 ```markdown CLAUDE.md theme={null}

231 # Summary instructions

232 

233 When summarizing this conversation, always preserve:

234 - The current task objective and acceptance criteria

235 - File paths that have been read or modified

236 - Test results and error messages

237 - Decisions made and the reasoning behind them

238 ```

239</Accordion>

240 

241### Keep context efficient

242 

243A few strategies for long-running agents:

244 

245* **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](/en/agent-sdk/subagents#what-subagents-inherit) for details.

246* **Be selective with tools.** Every tool definition takes context space. Use the `tools` field on [`AgentDefinition`](/en/agent-sdk/subagents#agent-definition-configuration) to scope subagents to the minimum set they need, and use [MCP tool search](/en/agent-sdk/mcp#mcp-tool-search) to load tools on demand instead of preloading all of them.

247* **Watch MCP server costs.** Each MCP server adds all its tool schemas to every request. A few servers with many tools can consume significant context before the agent does any work. The `ToolSearch` tool can help by loading tools on-demand instead of preloading all of them. See [MCP tool search](/en/agent-sdk/mcp#mcp-tool-search) for configuration.

248* **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.

249 

250For a detailed breakdown of per-feature context costs, see [Understand context costs](/en/features-overview#understand-context-costs).

251 

252## Sessions and continuity

253 

254Each interaction with the SDK creates or continues a session. Capture the session ID from `ResultMessage.session_id` (available in both SDKs) to resume later. The TypeScript SDK also exposes it as a direct field on the init `SystemMessage`; in Python it's nested in `SystemMessage.data`.

255 

256When you resume, the full context from previous turns is restored: files that were read, analysis that was performed, and actions that were taken. You can also fork a session to branch into a different approach without modifying the original.

257 

258See [Session management](/en/agent-sdk/sessions) for the full guide on resume, continue, and fork patterns.

259 

260<Note>

261 In Python, `ClaudeSDKClient` handles session IDs automatically across multiple calls. See the [Python SDK reference](/en/agent-sdk/python#choosing-between-query-and-claude-sdk-client) for details.

262</Note>

263 

264## Handle the result

265 

266When the loop ends, the `ResultMessage` tells you what happened and gives you the output. The `subtype` field (available in both SDKs) is the primary way to check termination state.

267 

268| Result subtype | What happened | `result` field available? |

269| :------------------------------------ | :------------------------------------------------------------------------------- | :-----------------------: |

270| `success` | Claude finished the task normally | Yes |

271| `error_max_turns` | Hit the `maxTurns` limit before finishing | No |

272| `error_max_budget_usd` | Hit the `maxBudgetUsd` limit before finishing | No |

273| `error_during_execution` | An error interrupted the loop (for example, an API failure or cancelled request) | No |

274| `error_max_structured_output_retries` | Structured output validation failed after the configured retry limit | No |

275 

276The `result` field (the final text output) is only present on the `success` variant, so always check the subtype before reading it. All result subtypes carry `total_cost_usd`, `usage`, `num_turns`, and `session_id` so you can track cost and resume even after errors. In Python, `total_cost_usd` and `usage` are typed as optional and may be `None` on some error paths, so guard before formatting them. See [Tracking costs and usage](/en/agent-sdk/cost-tracking) for details on interpreting the `usage` fields.

277 

278The result also includes a `stop_reason` field (`string | null` in TypeScript, `str | None` in Python) indicating why the model stopped generating on its final turn. Common values are `end_turn` (model finished normally), `max_tokens` (hit the output token limit), and `refusal` (the model declined the request). On error result subtypes, `stop_reason` carries the value from the last assistant response before the loop ended. To detect refusals, check `stop_reason === "refusal"` (TypeScript) or `stop_reason == "refusal"` (Python). See [`SDKResultMessage`](/en/agent-sdk/typescript#sdk-result-message) (TypeScript) or [`ResultMessage`](/en/agent-sdk/python#result-message) (Python) for the full type.

279 

280## Hooks

281 

282[Hooks](/en/agent-sdk/hooks) are callbacks that fire at specific points in the loop: before a tool runs, after it returns, when the agent finishes, and so on. Some commonly used hooks are:

283 

284| Hook | When it fires | Common uses |

285| :------------------------------- | :---------------------------------- | :----------------------------------------- |

286| `PreToolUse` | Before a tool executes | Validate inputs, block dangerous commands |

287| `PostToolUse` | After a tool returns | Audit outputs, trigger side effects |

288| `UserPromptSubmit` | When a prompt is sent | Inject additional context into prompts |

289| `Stop` | When the agent finishes | Validate the result, save session state |

290| `SubagentStart` / `SubagentStop` | When a subagent spawns or completes | Track and aggregate parallel task results |

291| `PreCompact` | Before context compaction | Archive full transcript before summarizing |

292 

293Hooks run in your application process, not inside the agent's context window, so they don't consume context. Hooks can also short-circuit the loop: a `PreToolUse` hook that rejects a tool call prevents it from executing, and Claude receives the rejection message instead.

294 

295Both SDKs support all the events above. The TypeScript SDK includes additional events that Python does not yet support. See [Control execution with hooks](/en/agent-sdk/hooks) for the complete event list, per-SDK availability, and the full callback API.

296 

297## Put it all together

298 

299This example combines the key concepts from this page into a single agent that fixes failing tests. It configures the agent with allowed tools (auto-approved so the agent runs autonomously), project settings, and safety limits on turns and reasoning effort. As the loop runs, it captures the session ID for potential resumption, handles the final result, and prints the total cost.

300 

301<CodeGroup>

302 ```python Python theme={null}

303 import asyncio

304 from claude_agent_sdk import query, ClaudeAgentOptions, ResultMessage

305 

306 

307 async def run_agent():

308 session_id = None

309 

310 async for message in query(

311 prompt="Find and fix the bug causing test failures in the auth module",

312 options=ClaudeAgentOptions(

313 allowed_tools=[

314 "Read",

315 "Edit",

316 "Bash",

317 "Glob",

318 "Grep",

319 ], # Listing tools here auto-approves them (no prompting)

320 setting_sources=[

321 "project"

322 ], # Load CLAUDE.md, skills, hooks from current directory

323 max_turns=30, # Prevent runaway sessions

324 effort="high", # Thorough reasoning for complex debugging

325 ),

326 ):

327 # Handle the final result

328 if isinstance(message, ResultMessage):

329 session_id = message.session_id # Save for potential resumption

330 

331 if message.subtype == "success":

332 print(f"Done: {message.result}")

333 elif message.subtype == "error_max_turns":

334 # Agent ran out of turns. Resume with a higher limit.

335 print(f"Hit turn limit. Resume session {session_id} to continue.")

336 elif message.subtype == "error_max_budget_usd":

337 print("Hit budget limit.")

338 else:

339 print(f"Stopped: {message.subtype}")

340 if message.total_cost_usd is not None:

341 print(f"Cost: ${message.total_cost_usd:.4f}")

342 

343 

344 asyncio.run(run_agent())

345 ```

346 

347 ```typescript TypeScript theme={null}

348 import { query } from "@anthropic-ai/claude-agent-sdk";

349 

350 let sessionId: string | undefined;

351 

352 for await (const message of query({

353 prompt: "Find and fix the bug causing test failures in the auth module",

354 options: {

355 allowedTools: ["Read", "Edit", "Bash", "Glob", "Grep"], // Listing tools here auto-approves them (no prompting)

356 settingSources: ["project"], // Load CLAUDE.md, skills, hooks from current directory

357 maxTurns: 30, // Prevent runaway sessions

358 effort: "high" // Thorough reasoning for complex debugging

359 }

360 })) {

361 // Save the session ID to resume later if needed

362 if (message.type === "system" && message.subtype === "init") {

363 sessionId = message.session_id;

364 }

365 

366 // Handle the final result

367 if (message.type === "result") {

368 if (message.subtype === "success") {

369 console.log(`Done: ${message.result}`);

370 } else if (message.subtype === "error_max_turns") {

371 // Agent ran out of turns. Resume with a higher limit.

372 console.log(`Hit turn limit. Resume session ${sessionId} to continue.`);

373 } else if (message.subtype === "error_max_budget_usd") {

374 console.log("Hit budget limit.");

375 } else {

376 console.log(`Stopped: ${message.subtype}`);

377 }

378 console.log(`Cost: $${message.total_cost_usd.toFixed(4)}`);

379 }

380 }

381 ```

382</CodeGroup>

383 

384## Next steps

385 

386Now that you understand the loop, here's where to go depending on what you're building:

387 

388* **Haven't run an agent yet?** Start with the [quickstart](/en/agent-sdk/quickstart) to get the SDK installed and see a full example running end to end.

389* **Ready to hook into your project?** [Load CLAUDE.md, skills, and filesystem hooks](/en/agent-sdk/claude-code-features) so the agent follows your project conventions automatically.

390* **Building an interactive UI?** Enable [streaming](/en/agent-sdk/streaming-output) to show live text and tool calls as the loop runs.

391* **Need tighter control over what the agent can do?** Lock down tool access with [permissions](/en/agent-sdk/permissions), and use [hooks](/en/agent-sdk/hooks) to audit, block, or transform tool calls before they execute.

392* **Running long or expensive tasks?** Offload isolated work to [subagents](/en/agent-sdk/subagents) to keep your main context lean.

393 

394For the broader conceptual picture of the agentic loop (not SDK-specific), see [How Claude Code works](/en/how-claude-code-works).

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 Claude Code features in the SDK

6 

7> Load project instructions, skills, hooks, and other Claude Code features into your SDK agents.

8 

9The Agent SDK is built on the same foundation as Claude Code, which means your SDK agents have access to the same filesystem-based features: project instructions (`CLAUDE.md` and rules), skills, hooks, and more.

10 

11By default, the SDK loads no filesystem settings. Your agent runs in isolation mode with only what you pass programmatically. To load CLAUDE.md, skills, or filesystem hooks, set `settingSources` to tell the SDK where to look.

12 

13For a conceptual overview of what each feature does and when to use it, see [Extend Claude Code](/en/features-overview).

14 

15## Enable Claude Code features with settingSources

16 

17The setting sources option ([`setting_sources`](/en/agent-sdk/python#claude-agent-options) in Python, [`settingSources`](/en/agent-sdk/typescript#setting-source) in TypeScript) controls which filesystem-based settings the SDK loads. Without it, your agent won't discover skills, `CLAUDE.md` files, or project-level hooks.

18 

19This example loads both user-level and project-level settings by setting `settingSources` to `["user", "project"]`:

20 

21<CodeGroup>

22 ```python Python theme={null}

23 from claude_agent_sdk import query, ClaudeAgentOptions, AssistantMessage, ResultMessage

24 

25 async for message in query(

26 prompt="Help me refactor the auth module",

27 options=ClaudeAgentOptions(

28 # "user" loads from ~/.claude/, "project" loads from ./.claude/ in cwd.

29 # Together they give the agent access to CLAUDE.md, skills, hooks, and

30 # permissions from both locations.

31 setting_sources=["user", "project"],

32 allowed_tools=["Read", "Edit", "Bash"],

33 ),

34 ):

35 if isinstance(message, AssistantMessage):

36 for block in message.content:

37 if hasattr(block, "text"):

38 print(block.text)

39 if isinstance(message, ResultMessage) and message.subtype == "success":

40 print(f"\nResult: {message.result}")

41 ```

42 

43 ```typescript TypeScript theme={null}

44 import { query } from "@anthropic-ai/claude-agent-sdk";

45 

46 for await (const message of query({

47 prompt: "Help me refactor the auth module",

48 options: {

49 // "user" loads from ~/.claude/, "project" loads from ./.claude/ in cwd.

50 // Together they give the agent access to CLAUDE.md, skills, hooks, and

51 // permissions from both locations.

52 settingSources: ["user", "project"],

53 allowedTools: ["Read", "Edit", "Bash"]

54 }

55 })) {

56 if (message.type === "assistant") {

57 for (const block of message.message.content) {

58 if (block.type === "text") console.log(block.text);

59 }

60 }

61 if (message.type === "result" && message.subtype === "success") {

62 console.log(`\nResult: ${message.result}`);

63 }

64 }

65 ```

66</CodeGroup>

67 

68Each source loads settings from a specific location, where `<cwd>` is the working directory you pass via the `cwd` option (or the process's current directory if unset). For the full type definition, see [`SettingSource`](/en/agent-sdk/typescript#setting-source) (TypeScript) or [`SettingSource`](/en/agent-sdk/python#setting-source) (Python).

69 

70| Source | What it loads | Location |

71| :---------- | :---------------------------------------------------------------------------------------------- | :---------------------------------------------------------------------------------------------------------------------------------- |

72| `"project"` | Project CLAUDE.md, `.claude/rules/*.md`, project skills, project hooks, project `settings.json` | `<cwd>/.claude/` and each parent directory up to the filesystem root (stopping when a `.claude/` is found or no more parents exist) |

73| `"user"` | User CLAUDE.md, `~/.claude/rules/*.md`, user skills, user settings | `~/.claude/` |

74| `"local"` | CLAUDE.local.md (gitignored), `.claude/settings.local.json` | `<cwd>/` |

75 

76To match the full Claude Code CLI behavior, use `["user", "project", "local"]`.

77 

78<Warning>

79 The `cwd` option determines where the SDK looks for project settings. If neither `cwd` nor any of its parent directories contains a `.claude/` folder, project-level features won't load. Auto memory (the `~/.claude/projects/<project>/memory/` directory that Claude Code uses to persist notes across interactive sessions) is a CLI-only feature and is never loaded by the SDK.

80</Warning>

81 

82## Project instructions (CLAUDE.md and rules)

83 

84`CLAUDE.md` files and `.claude/rules/*.md` files give your agent persistent context about your project: coding conventions, build commands, architecture decisions, and instructions. When `settingSources` includes `"project"` (as in the example above), the SDK loads these files into context at session start. The agent then follows your project conventions without you repeating them in every prompt.

85 

86### CLAUDE.md load locations

87 

88| Level | Location | When loaded |

89| :-------------------- | :--------------------------------------------- | :-------------------------------------------------------------------------------------------------- |

90| Project (root) | `<cwd>/CLAUDE.md` or `<cwd>/.claude/CLAUDE.md` | `settingSources` includes `"project"` |

91| Project rules | `<cwd>/.claude/rules/*.md` | `settingSources` includes `"project"` |

92| Project (parent dirs) | `CLAUDE.md` files in directories above `cwd` | `settingSources` includes `"project"`, loaded at session start |

93| Project (child dirs) | `CLAUDE.md` files in subdirectories of `cwd` | `settingSources` includes `"project"`, loaded on demand when the agent reads a file in that subtree |

94| Local (gitignored) | `<cwd>/CLAUDE.local.md` | `settingSources` includes `"local"` |

95| User | `~/.claude/CLAUDE.md` | `settingSources` includes `"user"` |

96| User rules | `~/.claude/rules/*.md` | `settingSources` includes `"user"` |

97 

98All levels are additive: if both project and user CLAUDE.md files exist, the agent sees both. There is no hard precedence rule between levels; if instructions conflict, the outcome depends on how Claude interprets them. Write non-conflicting rules, or state precedence explicitly in the more specific file ("These project instructions override any conflicting user-level defaults").

99 

100<Tip>

101 You can also inject context directly via `systemPrompt` without using CLAUDE.md files. See [Modify system prompts](/en/agent-sdk/modifying-system-prompts). Use CLAUDE.md when you want the same context shared between interactive Claude Code sessions and your SDK agents.

102</Tip>

103 

104For how to structure and organize CLAUDE.md content, see [Manage Claude's memory](/en/memory).

105 

106## Skills

107 

108Skills are markdown files that give your agent specialized knowledge and invocable workflows. Unlike `CLAUDE.md` (which loads every session), skills load on demand. The agent receives skill descriptions at startup and loads the full content when relevant.

109 

110To use skills in the SDK, set `settingSources` so the agent discovers skill files from the filesystem. The `Skill` tool is enabled by default when you don't specify `allowedTools`. If you are using an `allowedTools` allowlist, include `"Skill"` explicitly.

111 

112<CodeGroup>

113 ```python Python theme={null}

114 from claude_agent_sdk import query, ClaudeAgentOptions, ResultMessage

115 

116 # Skills in .claude/skills/ are discovered automatically

117 # when settingSources includes "project"

118 async for message in query(

119 prompt="Review this PR using our code review checklist",

120 options=ClaudeAgentOptions(

121 setting_sources=["user", "project"],

122 allowed_tools=["Skill", "Read", "Grep", "Glob"],

123 ),

124 ):

125 if isinstance(message, ResultMessage) and message.subtype == "success":

126 print(message.result)

127 ```

128 

129 ```typescript TypeScript theme={null}

130 import { query } from "@anthropic-ai/claude-agent-sdk";

131 

132 // Skills in .claude/skills/ are discovered automatically

133 // when settingSources includes "project"

134 for await (const message of query({

135 prompt: "Review this PR using our code review checklist",

136 options: {

137 settingSources: ["user", "project"],

138 allowedTools: ["Skill", "Read", "Grep", "Glob"]

139 }

140 })) {

141 if (message.type === "result" && message.subtype === "success") {

142 console.log(message.result);

143 }

144 }

145 ```

146</CodeGroup>

147 

148<Note>

149 Skills must be created as filesystem artifacts (`.claude/skills/<name>/SKILL.md`). The SDK does not have a programmatic API for registering skills. See [Agent Skills in the SDK](/en/agent-sdk/skills) for full details.

150</Note>

151 

152For more on creating and using skills, see [Agent Skills in the SDK](/en/agent-sdk/skills).

153 

154## Hooks

155 

156The SDK supports two ways to define hooks, and they run side by side:

157 

158* **Filesystem hooks:** shell commands defined in `settings.json`, loaded when `settingSources` includes the relevant source. These are the same hooks you'd configure for [interactive Claude Code sessions](/en/hooks-guide).

159* **Programmatic hooks:** callback functions passed directly to `query()`. These run in your application process and can return structured decisions. See [Control execution with hooks](/en/agent-sdk/hooks).

160 

161Both types execute during the same hook lifecycle. If you already have hooks in your project's `.claude/settings.json` and you set `settingSources: ["project"]`, those hooks run automatically in the SDK with no extra configuration.

162 

163Hook callbacks receive the tool input and return a decision dict. Returning `{}` (an empty dict) means allow the tool to proceed. Returning `{"decision": "block", "reason": "..."}` prevents execution and the reason is sent to Claude as the tool result. See the [hooks guide](/en/agent-sdk/hooks) for the full callback signature and return types.

164 

165<CodeGroup>

166 ```python Python theme={null}

167 from claude_agent_sdk import query, ClaudeAgentOptions, HookMatcher, ResultMessage

168 

169 

170 # PreToolUse hook callback. Positional args:

171 # input_data: HookInput dict with tool_name, tool_input, hook_event_name

172 # tool_use_id: str | None, the ID of the tool call being intercepted

173 # context: HookContext, carries session metadata

174 async def audit_bash(input_data, tool_use_id, context):

175 command = input_data.get("tool_input", {}).get("command", "")

176 if "rm -rf" in command:

177 return {"decision": "block", "reason": "Destructive command blocked"}

178 return {} # Empty dict: allow the tool to proceed

179 

180 

181 # Filesystem hooks from .claude/settings.json run automatically

182 # when settingSources loads them. You can also add programmatic hooks:

183 async for message in query(

184 prompt="Refactor the auth module",

185 options=ClaudeAgentOptions(

186 setting_sources=["project"], # Loads hooks from .claude/settings.json

187 hooks={

188 "PreToolUse": [

189 HookMatcher(matcher="Bash", hooks=[audit_bash]),

190 ]

191 },

192 ),

193 ):

194 if isinstance(message, ResultMessage) and message.subtype == "success":

195 print(message.result)

196 ```

197 

198 ```typescript TypeScript theme={null}

199 import { query, type HookInput, type HookJSONOutput } from "@anthropic-ai/claude-agent-sdk";

200 

201 // PreToolUse hook callback. HookInput is a discriminated union on

202 // hook_event_name, so narrowing on it gives TypeScript the right

203 // tool_input shape for this event.

204 const auditBash = async (input: HookInput): Promise<HookJSONOutput> => {

205 if (input.hook_event_name !== "PreToolUse") return {};

206 const toolInput = input.tool_input as { command?: string };

207 if (toolInput.command?.includes("rm -rf")) {

208 return { decision: "block", reason: "Destructive command blocked" };

209 }

210 return {}; // Empty object: allow the tool to proceed

211 };

212 

213 // Filesystem hooks from .claude/settings.json run automatically

214 // when settingSources loads them. You can also add programmatic hooks:

215 for await (const message of query({

216 prompt: "Refactor the auth module",

217 options: {

218 settingSources: ["project"], // Loads hooks from .claude/settings.json

219 hooks: {

220 PreToolUse: [{ matcher: "Bash", hooks: [auditBash] }]

221 }

222 }

223 })) {

224 if (message.type === "result" && message.subtype === "success") {

225 console.log(message.result);

226 }

227 }

228 ```

229</CodeGroup>

230 

231### When to use which hook type

232 

233| Hook type | Best for |

234| :---------------------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |

235| **Filesystem** (`settings.json`) | Sharing hooks between CLI and SDK sessions. Supports `"command"` (shell scripts), `"http"` (POST to an endpoint), `"prompt"` (LLM evaluates a prompt), and `"agent"` (spawns a verifier agent). These fire in the main agent and any subagents it spawns. |

236| **Programmatic** (callbacks in `query()`) | Application-specific logic; returning structured decisions; in-process integration. Scoped to the main session only. |

237 

238<Note>

239 The TypeScript SDK supports additional hook events beyond Python, including `SessionStart`, `SessionEnd`, `TeammateIdle`, and `TaskCompleted`. See the [hooks guide](/en/agent-sdk/hooks) for the full event compatibility table.

240</Note>

241 

242For full details on programmatic hooks, see [Control execution with hooks](/en/agent-sdk/hooks). For filesystem hook syntax, see [Hooks](/en/hooks).

243 

244## Choose the right feature

245 

246The Agent SDK gives you access to several ways to extend your agent's behavior. If you're unsure which to use, this table maps common goals to the right approach.

247 

248| You want to... | Use | SDK surface |

249| :------------------------------------------------------------------------------------------------ | :-------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------- |

250| Set project conventions your agent always follows | [CLAUDE.md](/en/memory) | `settingSources: ["project"]` loads it automatically |

251| Give the agent reference material it loads when relevant | [Skills](/en/agent-sdk/skills) | `settingSources` + `allowedTools: ["Skill"]` |

252| Run a reusable workflow (deploy, review, release) | [User-invocable skills](/en/agent-sdk/skills) | `settingSources` + `allowedTools: ["Skill"]` |

253| Delegate an isolated subtask to a fresh context (research, review) | [Subagents](/en/agent-sdk/subagents) | `agents` parameter + `allowedTools: ["Agent"]` |

254| Coordinate multiple Claude Code instances with shared task lists and direct inter-agent messaging | [Agent teams](/en/agent-teams) | Not directly configured via SDK options. Agent teams are a CLI feature where one session acts as the team lead, coordinating work across independent teammates |

255| Run deterministic logic on tool calls (audit, block, transform) | [Hooks](/en/agent-sdk/hooks) | `hooks` parameter with callbacks, or shell scripts loaded via `settingSources` |

256| Give Claude structured tool access to an external service | [MCP](/en/agent-sdk/mcp) | `mcpServers` parameter |

257 

258<Tip>

259 **Subagents versus agent teams:** Subagents are ephemeral and isolated: fresh conversation, one task, summary returned to parent. Agent teams coordinate multiple independent Claude Code instances that share a task list and message each other directly. Agent teams are a CLI feature. See [What subagents inherit](/en/agent-sdk/subagents#what-subagents-inherit) and the [agent teams comparison](/en/agent-teams#compare-with-subagents) for details.

260</Tip>

261 

262Every feature you enable adds to your agent's context window. For per-feature costs and how these features layer together, see [Extend Claude Code](/en/features-overview#understand-context-costs).

263 

264## Related resources

265 

266* [Extend Claude Code](/en/features-overview): Conceptual overview of all extension features, with comparison tables and context cost analysis

267* [Skills in the SDK](/en/agent-sdk/skills): Full guide to using skills programmatically

268* [Subagents](/en/agent-sdk/subagents): Define and invoke subagents for isolated subtasks

269* [Hooks](/en/agent-sdk/hooks): Intercept and control agent behavior at key execution points

270* [Permissions](/en/agent-sdk/permissions): Control tool access with modes, rules, and callbacks

271* [System prompts](/en/agent-sdk/modifying-system-prompts): Inject context without CLAUDE.md files

agent-sdk/cost-tracking.md +222 −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# Track cost and usage

6 

7> Learn how to track token usage, deduplicate parallel tool calls, and calculate costs with the Claude Agent SDK.

8 

9The Claude Agent SDK provides detailed token usage information for each interaction with Claude. This guide explains how to properly track costs and understand usage reporting, especially when dealing with parallel tool uses and multi-step conversations.

10 

11For complete API documentation, see the [TypeScript SDK reference](/en/agent-sdk/typescript) and [Python SDK reference](/en/agent-sdk/python).

12 

13## Understand token usage

14 

15The TypeScript and Python SDKs expose the same usage data with different field names:

16 

17* **TypeScript** provides per-step token breakdowns on each assistant message (`message.message.id`, `message.message.usage`), per-model cost via `modelUsage` on the result message, and a cumulative total on the result message.

18* **Python** provides per-step token breakdowns on each assistant message (`message.usage`, `message.message_id`), per-model cost via `model_usage` on the result message, and the accumulated total on the result message (`total_cost_usd` and `usage` dict).

19 

20Both SDKs use the same underlying cost model and expose the same granularity. The difference is in field naming and where per-step usage is nested.

21 

22Cost tracking depends on understanding how the SDK scopes usage data:

23 

24* **`query()` call:** one invocation of the SDK's `query()` function. A single call can involve multiple steps (Claude responds, uses tools, gets results, responds again). Each call produces one [`result`](/en/agent-sdk/typescript#sdk-result-message) message at the end.

25* **Step:** a single request/response cycle within a `query()` call. Each step produces assistant messages with token usage.

26* **Session:** a series of `query()` calls linked by a session ID (using the `resume` option). Each `query()` call within a session reports its own cost independently.

27 

28The following diagram shows the message stream from a single `query()` call, with token usage reported at each step and the authoritative total at the end:

29 

30<img src="https://mintcdn.com/claude-code/gvy2DIUELtNA8qD3/images/agent-sdk/message-usage-flow.svg?fit=max&auto=format&n=gvy2DIUELtNA8qD3&q=85&s=88cba82134f8f7994d780c3f153b83fc" alt="Diagram showing a query producing two steps of messages. Step 1 has four assistant messages sharing the same ID and usage (count once), Step 2 has one assistant message with a new ID, and the final result message shows total_cost_usd for billing." width="760" height="520" data-path="images/agent-sdk/message-usage-flow.svg" />

31 

32<Steps>

33 <Step title="Each step produces assistant messages">

34 When Claude responds, it sends one or more assistant messages. In TypeScript, each assistant message contains a nested `BetaMessage` (accessed via `message.message`) with an `id` and a [`usage`](https://platform.claude.com/docs/en/api/messages) object with token counts (`input_tokens`, `output_tokens`). In Python, the `AssistantMessage` dataclass exposes the same data directly via `message.usage` and `message.message_id`. When Claude uses multiple tools in one turn, all messages in that turn share the same ID, so deduplicate by ID to avoid double-counting.

35 </Step>

36 

37 <Step title="The result message provides the authoritative total">

38 When the `query()` call completes, the SDK emits a result message with `total_cost_usd` and cumulative `usage`. This is available in both TypeScript ([`SDKResultMessage`](/en/agent-sdk/typescript#sdk-result-message)) and Python ([`ResultMessage`](/en/agent-sdk/python#result-message)). If you make multiple `query()` calls (for example, in a multi-turn session), each result only reflects the cost of that individual call. If you only need the total cost, you can ignore the per-step usage and read this single value.

39 </Step>

40</Steps>

41 

42## Get the total cost of a query

43 

44The result message ([TypeScript](/en/agent-sdk/typescript#sdk-result-message), [Python](/en/agent-sdk/python#result-message)) is the last message in every `query()` call. It includes `total_cost_usd`, the cumulative cost across all steps in that call. This works for both success and error results. If you use sessions to make multiple `query()` calls, each result only reflects the cost of that individual call.

45 

46The following examples iterate over the message stream from a `query()` call and print the total cost when the `result` message arrives:

47 

48<CodeGroup>

49 ```typescript TypeScript theme={null}

50 import { query } from "@anthropic-ai/claude-agent-sdk";

51 

52 for await (const message of query({ prompt: "Summarize this project" })) {

53 if (message.type === "result") {

54 console.log(`Total cost: $${message.total_cost_usd}`);

55 }

56 }

57 ```

58 

59 ```python Python theme={null}

60 from claude_agent_sdk import query, ResultMessage

61 import asyncio

62 

63 

64 async def main():

65 async for message in query(prompt="Summarize this project"):

66 if isinstance(message, ResultMessage):

67 print(f"Total cost: ${message.total_cost_usd or 0}")

68 

69 

70 asyncio.run(main())

71 ```

72</CodeGroup>

73 

74## Track per-step and per-model usage

75 

76The examples in this section use TypeScript field names. In Python, the equivalent fields are [`AssistantMessage.usage`](/en/agent-sdk/python#assistant-message) and `AssistantMessage.message_id` for per-step usage, and [`ResultMessage.model_usage`](/en/agent-sdk/python#result-message) for per-model breakdowns.

77 

78### Track per-step usage

79 

80Each assistant message contains a nested `BetaMessage` (accessed via `message.message`) with an `id` and `usage` object with token counts. When Claude uses tools in parallel, multiple messages share the same `id` with identical usage data. Track which IDs you've already counted and skip duplicates to avoid inflated totals.

81 

82<Warning>

83 Parallel tool calls produce multiple assistant messages whose nested `BetaMessage` shares the same `id` and identical usage. Always deduplicate by ID to get accurate per-step token counts.

84</Warning>

85 

86The following example accumulates input and output tokens across all steps, counting each unique message ID only once:

87 

88```typescript theme={null}

89import { query } from "@anthropic-ai/claude-agent-sdk";

90 

91const seenIds = new Set<string>();

92let totalInputTokens = 0;

93let totalOutputTokens = 0;

94 

95for await (const message of query({ prompt: "Summarize this project" })) {

96 if (message.type === "assistant") {

97 const msgId = message.message.id;

98 

99 // Parallel tool calls share the same ID, only count once

100 if (!seenIds.has(msgId)) {

101 seenIds.add(msgId);

102 totalInputTokens += message.message.usage.input_tokens;

103 totalOutputTokens += message.message.usage.output_tokens;

104 }

105 }

106}

107 

108console.log(`Steps: ${seenIds.size}`);

109console.log(`Input tokens: ${totalInputTokens}`);

110console.log(`Output tokens: ${totalOutputTokens}`);

111```

112 

113### Break down usage per model

114 

115The result message includes [`modelUsage`](/en/agent-sdk/typescript#model-usage), a map of model name to per-model token counts and cost. This is useful when you run multiple models (for example, Haiku for subagents and Opus for the main agent) and want to see where tokens are going.

116 

117The following example runs a query and prints the cost and token breakdown for each model used:

118 

119```typescript theme={null}

120import { query } from "@anthropic-ai/claude-agent-sdk";

121 

122for await (const message of query({ prompt: "Summarize this project" })) {

123 if (message.type !== "result") continue;

124 

125 for (const [modelName, usage] of Object.entries(message.modelUsage)) {

126 console.log(`${modelName}: $${usage.costUSD.toFixed(4)}`);

127 console.log(` Input tokens: ${usage.inputTokens}`);

128 console.log(` Output tokens: ${usage.outputTokens}`);

129 console.log(` Cache read: ${usage.cacheReadInputTokens}`);

130 console.log(` Cache creation: ${usage.cacheCreationInputTokens}`);

131 }

132}

133```

134 

135## Accumulate costs across multiple calls

136 

137Each `query()` call returns its own `total_cost_usd`. The SDK does not provide a session-level total, so if your application makes multiple `query()` calls (for example, in a multi-turn session or across different users), accumulate the totals yourself.

138 

139The following examples run two `query()` calls sequentially, add each call's `total_cost_usd` to a running total, and print both the per-call and combined cost:

140 

141<CodeGroup>

142 ```typescript TypeScript theme={null}

143 import { query } from "@anthropic-ai/claude-agent-sdk";

144 

145 // Track cumulative cost across multiple query() calls

146 let totalSpend = 0;

147 

148 const prompts = [

149 "Read the files in src/ and summarize the architecture",

150 "List all exported functions in src/auth.ts"

151 ];

152 

153 for (const prompt of prompts) {

154 for await (const message of query({ prompt })) {

155 if (message.type === "result") {

156 totalSpend += message.total_cost_usd;

157 console.log(`This call: $${message.total_cost_usd}`);

158 }

159 }

160 }

161 

162 console.log(`Total spend: $${totalSpend.toFixed(4)}`);

163 ```

164 

165 ```python Python theme={null}

166 from claude_agent_sdk import query, ResultMessage

167 import asyncio

168 

169 

170 async def main():

171 # Track cumulative cost across multiple query() calls

172 total_spend = 0.0

173 

174 prompts = [

175 "Read the files in src/ and summarize the architecture",

176 "List all exported functions in src/auth.ts",

177 ]

178 

179 for prompt in prompts:

180 async for message in query(prompt=prompt):

181 if isinstance(message, ResultMessage):

182 cost = message.total_cost_usd or 0

183 total_spend += cost

184 print(f"This call: ${cost}")

185 

186 print(f"Total spend: ${total_spend:.4f}")

187 

188 

189 asyncio.run(main())

190 ```

191</CodeGroup>

192 

193## Handle errors, caching, and token discrepancies

194 

195For accurate cost tracking, account for failed conversations, cache token pricing, and occasional reporting inconsistencies.

196 

197### Resolve output token discrepancies

198 

199In rare cases, you might observe different `output_tokens` values for messages with the same ID. When this occurs:

200 

2011. **Use the highest value:** the final message in a group typically contains the accurate total.

2022. **Verify against total cost:** the `total_cost_usd` in the result message is authoritative.

2033. **Report inconsistencies:** file issues at the [Claude Code GitHub repository](https://github.com/anthropics/claude-code/issues).

204 

205### Track costs on failed conversations

206 

207Both success and error result messages include `usage` and `total_cost_usd`. If a conversation fails mid-way, you still consumed tokens up to the point of failure. Always read cost data from the result message regardless of its `subtype`.

208 

209### Track cache tokens

210 

211The Agent SDK automatically uses [prompt caching](https://platform.claude.com/docs/en/build-with-claude/prompt-caching) to reduce costs on repeated content. You do not need to configure caching yourself. The usage object includes two additional fields for cache tracking:

212 

213* `cache_creation_input_tokens`: tokens used to create new cache entries (charged at a higher rate than standard input tokens).

214* `cache_read_input_tokens`: tokens read from existing cache entries (charged at a reduced rate).

215 

216Track these separately from `input_tokens` to understand caching savings. In TypeScript, these fields are typed on the [`Usage`](/en/agent-sdk/typescript#usage) object. In Python, they appear as keys in the [`ResultMessage.usage`](/en/agent-sdk/python#result-message) dict (for example, `message.usage.get("cache_read_input_tokens", 0)`).

217 

218## Related documentation

219 

220* [TypeScript SDK Reference](/en/agent-sdk/typescript) - Complete API documentation

221* [SDK Overview](/en/agent-sdk/overview) - Getting started with the SDK

222* [SDK Permissions](/en/agent-sdk/permissions) - Managing tool permissions

agent-sdk/custom-tools.md +804 −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# Give Claude custom tools

6 

7> Define custom tools with the Claude Agent SDK's in-process MCP server so Claude can call your functions, hit your APIs, and perform domain-specific operations.

8 

9Custom tools extend the Agent SDK by letting you define your own functions that Claude can call during a conversation. Using the SDK's in-process MCP server, you can give Claude access to databases, external APIs, domain-specific logic, or any other capability your application needs.

10 

11This guide covers how to define tools with input schemas and handlers, bundle them into an MCP server, pass them to `query`, and control which tools Claude can access. It also covers error handling, tool annotations, and returning non-text content like images.

12 

13## Quick reference

14 

15| If you want to... | Do this |

16| :------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |

17| Define a tool | Use [`@tool`](/en/agent-sdk/python#tool) (Python) or [`tool()`](/en/agent-sdk/typescript#tool) (TypeScript) with a name, description, schema, and handler. See [Create a custom tool](#create-a-custom-tool). |

18| Register a tool with Claude | Wrap in `create_sdk_mcp_server` / `createSdkMcpServer` and pass to `mcpServers` in `query()`. See [Call a custom tool](#call-a-custom-tool). |

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

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

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

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

25 

26## Create a custom tool

27 

28A tool is defined by four parts, passed as arguments to the [`tool()`](/en/agent-sdk/typescript#tool) helper in TypeScript or the [`@tool`](/en/agent-sdk/python#tool) decorator in Python:

29 

30* **Name:** a unique identifier Claude uses to call the tool.

31* **Description:** what the tool does. Claude reads this to decide when to call it.

32* **Input schema:** the arguments Claude must provide. In TypeScript this is always a [Zod schema](https://zod.dev/), and the handler's `args` are typed from it automatically. In Python this is a dict mapping names to types, like `{"latitude": float}`, which the SDK converts to JSON Schema for you. The Python decorator also accepts a full [JSON Schema](https://json-schema.org/understanding-json-schema/about) dict directly when you need enums, ranges, optional fields, or nested objects.

33* **Handler:** the async function that runs when Claude calls the tool. It receives the validated arguments and must return an object with:

34 * `content` (required): an array of result blocks, each with a `type` of `"text"`, `"image"`, or `"resource"`. See [Return images and resources](#return-images-and-resources) for non-text blocks.

35 * `isError` (optional): set to `true` to signal a tool failure so Claude can react to it. See [Handle errors](#handle-errors).

36 

37After defining a tool, wrap it in a server with [`createSdkMcpServer`](/en/agent-sdk/typescript#create-sdk-mcp-server) (TypeScript) or [`create_sdk_mcp_server`](/en/agent-sdk/python#create-sdk-mcp-server) (Python). The server runs in-process inside your application, not as a separate process.

38 

39### Weather tool example

40 

41This example defines a `get_temperature` tool and wraps it in an MCP server. It only sets up the tool; to pass it to `query` and run it, see [Call a custom tool](#call-a-custom-tool) below.

42 

43<CodeGroup>

44 ```python Python theme={null}

45 from typing import Any

46 import httpx

47 from claude_agent_sdk import tool, create_sdk_mcp_server

48 

49 

50 # Define a tool: name, description, input schema, handler

51 @tool(

52 "get_temperature",

53 "Get the current temperature at a location",

54 {"latitude": float, "longitude": float},

55 )

56 async def get_temperature(args: dict[str, Any]) -> dict[str, Any]:

57 async with httpx.AsyncClient() as client:

58 response = await client.get(

59 "https://api.open-meteo.com/v1/forecast",

60 params={

61 "latitude": args["latitude"],

62 "longitude": args["longitude"],

63 "current": "temperature_2m",

64 "temperature_unit": "fahrenheit",

65 },

66 )

67 data = response.json()

68 

69 # Return a content array - Claude sees this as the tool result

70 return {

71 "content": [

72 {

73 "type": "text",

74 "text": f"Temperature: {data['current']['temperature_2m']}°F",

75 }

76 ]

77 }

78 

79 

80 # Wrap the tool in an in-process MCP server

81 weather_server = create_sdk_mcp_server(

82 name="weather",

83 version="1.0.0",

84 tools=[get_temperature],

85 )

86 ```

87 

88 ```typescript TypeScript theme={null}

89 import { tool, createSdkMcpServer } from "@anthropic-ai/claude-agent-sdk";

90 import { z } from "zod";

91 

92 // Define a tool: name, description, input schema, handler

93 const getTemperature = tool(

94 "get_temperature",

95 "Get the current temperature at a location",

96 {

97 latitude: z.number().describe("Latitude coordinate"), // .describe() adds a field description Claude sees

98 longitude: z.number().describe("Longitude coordinate")

99 },

100 async (args) => {

101 // args is typed from the schema: { latitude: number; longitude: number }

102 const response = await fetch(

103 `https://api.open-meteo.com/v1/forecast?latitude=${args.latitude}&longitude=${args.longitude}&current=temperature_2m&temperature_unit=fahrenheit`

104 );

105 const data: any = await response.json();

106 

107 // Return a content array - Claude sees this as the tool result

108 return {

109 content: [{ type: "text", text: `Temperature: ${data.current.temperature_2m}°F` }]

110 };

111 }

112 );

113 

114 // Wrap the tool in an in-process MCP server

115 const weatherServer = createSdkMcpServer({

116 name: "weather",

117 version: "1.0.0",

118 tools: [getTemperature]

119 });

120 ```

121</CodeGroup>

122 

123See the [`tool()`](/en/agent-sdk/typescript#tool) TypeScript reference or the [`@tool`](/en/agent-sdk/python#tool) Python reference for full parameter details, including JSON Schema input formats and return value structure.

124 

125<Tip>

126 To make a parameter optional: in TypeScript, add `.default()` to the Zod field. In Python, the dict schema treats every key as required, so leave the parameter out of the schema, mention it in the description string, and read it with `args.get()` in the handler. The [`get_precipitation_chance` tool below](#add-more-tools) shows both patterns.

127</Tip>

128 

129### Call a custom tool

130 

131Pass the MCP server you created to `query` via the `mcpServers` option. The key in `mcpServers` becomes the `{server_name}` segment in each tool's fully qualified name: `mcp__{server_name}__{tool_name}`. List that name in `allowedTools` so the tool runs without a permission prompt.

132 

133These snippets reuse the `weatherServer` from the [example above](#weather-tool-example) to ask Claude what the weather is in a specific location.

134 

135<CodeGroup>

136 ```python Python theme={null}

137 import asyncio

138 from claude_agent_sdk import query, ClaudeAgentOptions, ResultMessage

139 

140 

141 async def main():

142 options = ClaudeAgentOptions(

143 mcp_servers={"weather": weather_server},

144 allowed_tools=["mcp__weather__get_temperature"],

145 )

146 

147 async for message in query(

148 prompt="What's the temperature in San Francisco?",

149 options=options,

150 ):

151 # ResultMessage is the final message after all tool calls complete

152 if isinstance(message, ResultMessage) and message.subtype == "success":

153 print(message.result)

154 

155 

156 asyncio.run(main())

157 ```

158 

159 ```typescript TypeScript theme={null}

160 import { query } from "@anthropic-ai/claude-agent-sdk";

161 

162 for await (const message of query({

163 prompt: "What's the temperature in San Francisco?",

164 options: {

165 mcpServers: { weather: weatherServer },

166 allowedTools: ["mcp__weather__get_temperature"]

167 }

168 })) {

169 // "result" is the final message after all tool calls complete

170 if (message.type === "result" && message.subtype === "success") {

171 console.log(message.result);

172 }

173 }

174 ```

175</CodeGroup>

176 

177### Add more tools

178 

179A server holds as many tools as you list in its `tools` array. With more than one tool on a server, you can list each one in `allowedTools` individually or use the wildcard `mcp__weather__*` to cover every tool the server exposes.

180 

181The example below adds a second tool, `get_precipitation_chance`, to the `weatherServer` from the [weather tool example](#weather-tool-example) and rebuilds it with both tools in the array.

182 

183<CodeGroup>

184 ```python Python theme={null}

185 # Define a second tool for the same server

186 @tool(

187 "get_precipitation_chance",

188 "Get the hourly precipitation probability for a location. "

189 "Optionally pass 'hours' (1-24) to control how many hours to return.",

190 {"latitude": float, "longitude": float},

191 )

192 async def get_precipitation_chance(args: dict[str, Any]) -> dict[str, Any]:

193 # 'hours' isn't in the schema - read it with .get() to make it optional

194 hours = args.get("hours", 12)

195 async with httpx.AsyncClient() as client:

196 response = await client.get(

197 "https://api.open-meteo.com/v1/forecast",

198 params={

199 "latitude": args["latitude"],

200 "longitude": args["longitude"],

201 "hourly": "precipitation_probability",

202 "forecast_days": 1,

203 },

204 )

205 data = response.json()

206 chances = data["hourly"]["precipitation_probability"][:hours]

207 

208 return {

209 "content": [

210 {

211 "type": "text",

212 "text": f"Next {hours} hours: {'%, '.join(map(str, chances))}%",

213 }

214 ]

215 }

216 

217 

218 # Rebuild the server with both tools in the array

219 weather_server = create_sdk_mcp_server(

220 name="weather",

221 version="1.0.0",

222 tools=[get_temperature, get_precipitation_chance],

223 )

224 ```

225 

226 ```typescript TypeScript theme={null}

227 // Define a second tool for the same server

228 const getPrecipitationChance = tool(

229 "get_precipitation_chance",

230 "Get the hourly precipitation probability for a location",

231 {

232 latitude: z.number(),

233 longitude: z.number(),

234 hours: z

235 .number()

236 .int()

237 .min(1)

238 .max(24)

239 .default(12) // .default() makes the parameter optional

240 .describe("How many hours of forecast to return")

241 },

242 async (args) => {

243 const response = await fetch(

244 `https://api.open-meteo.com/v1/forecast?latitude=${args.latitude}&longitude=${args.longitude}&hourly=precipitation_probability&forecast_days=1`

245 );

246 const data: any = await response.json();

247 const chances = data.hourly.precipitation_probability.slice(0, args.hours);

248 

249 return {

250 content: [{ type: "text", text: `Next ${args.hours} hours: ${chances.join("%, ")}%` }]

251 };

252 }

253 );

254 

255 // Rebuild the server with both tools in the array

256 const weatherServer = createSdkMcpServer({

257 name: "weather",

258 version: "1.0.0",

259 tools: [getTemperature, getPrecipitationChance]

260 });

261 ```

262</CodeGroup>

263 

264Every tool in this array consumes context window space on every turn. If you're defining dozens of tools, see [tool search](/en/agent-sdk/tool-search) to load them on demand instead.

265 

266### Add tool annotations

267 

268[Tool annotations](https://modelcontextprotocol.io/docs/concepts/tools#tool-annotations) are optional metadata describing how a tool behaves. Pass them as the fifth argument to `tool()` helper in TypeScript or via the `annotations` keyword argument for the `@tool` decorator in Python. All hint fields are Booleans.

269 

270| Field | Default | Meaning |

271| :---------------- | :------ | :-------------------------------------------------------------------------------------------------------------------- |

272| `readOnlyHint` | `false` | Tool does not modify its environment. Controls whether the tool can be called in parallel with other read-only tools. |

273| `destructiveHint` | `true` | Tool may perform destructive updates. Informational only. |

274| `idempotentHint` | `false` | Repeated calls with the same arguments have no additional effect. Informational only. |

275| `openWorldHint` | `true` | Tool reaches systems outside your process. Informational only. |

276 

277Annotations are metadata, not enforcement. A tool marked `readOnlyHint: true` can still write to disk if that's what the handler does. Keep the annotation accurate to the handler.

278 

279This example adds `readOnlyHint` to the `get_temperature` tool from the [weather tool example](#weather-tool-example).

280 

281<CodeGroup>

282 ```python Python theme={null}

283 from claude_agent_sdk import tool, ToolAnnotations

284 

285 

286 @tool(

287 "get_temperature",

288 "Get the current temperature at a location",

289 {"latitude": float, "longitude": float},

290 annotations=ToolAnnotations(

291 readOnlyHint=True

292 ), # Lets Claude batch this with other read-only calls

293 )

294 async def get_temperature(args):

295 return {"content": [{"type": "text", "text": "..."}]}

296 ```

297 

298 ```typescript TypeScript theme={null}

299 tool(

300 "get_temperature",

301 "Get the current temperature at a location",

302 { latitude: z.number(), longitude: z.number() },

303 async (args) => ({ content: [{ type: "text", text: `...` }] }),

304 { annotations: { readOnlyHint: true } } // Lets Claude batch this with other read-only calls

305 );

306 ```

307</CodeGroup>

308 

309See `ToolAnnotations` in the [TypeScript](/en/agent-sdk/typescript#tool-annotations) or [Python](/en/agent-sdk/python#tool-annotations) reference.

310 

311## Control tool access

312 

313The [weather tool example](#weather-tool-example) registered a server and listed tools in `allowedTools`. This section covers how tool names are constructed and how to scope access when you have multiple tools or want to restrict built-ins.

314 

315### Tool name format

316 

317When MCP tools are exposed to Claude, their names follow a specific format:

318 

319* Pattern: `mcp__{server_name}__{tool_name}`

320* Example: A tool named `get_temperature` in server `weather` becomes `mcp__weather__get_temperature`

321 

322### Configure allowed tools

323 

324The `tools` option and the allowed/disallowed lists operate on separate layers. `tools` controls which built-in tools appear in Claude's context. Allowed and disallowed tool lists control whether calls are approved or denied once Claude attempts them.

325 

326| Option | Layer | Effect |

327| :------------------------ | :----------- | :------------------------------------------------------------------------------------------------------------------------------------------------ |

328| `tools: ["Read", "Grep"]` | Availability | Only the listed built-ins are in Claude's context. Unlisted built-ins are removed. MCP tools are unaffected. |

329| `tools: []` | Availability | All built-ins are removed. Claude can only use your MCP tools. |

330| allowed tools | Permission | Listed tools run without a permission prompt. Unlisted tools remain available; calls go through the [permission flow](/en/agent-sdk/permissions). |

331| disallowed tools | Permission | Every call to a listed tool is denied. The tool stays in Claude's context, so Claude may still attempt it before the call is rejected. |

332 

333To limit which built-ins Claude can use, prefer `tools` over disallowed tools. Omitting a tool from `tools` removes it from context so Claude never attempts it; listing it in `disallowedTools` (Python: `disallowed_tools`) blocks the call but leaves the tool visible, so Claude may waste a turn trying it. See [Configure permissions](/en/agent-sdk/permissions) for the full evaluation order.

334 

335## Handle errors

336 

337How your handler reports errors determines whether the agent loop continues or stops:

338 

339| What happens | Result |

340| :--------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------- |

341| Handler throws an uncaught exception | Agent loop stops. Claude never sees the error, and the `query` call fails. |

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

343 

344The 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.

345 

346<CodeGroup>

347 ```python Python theme={null}

348 import json

349 import httpx

350 from typing import Any

351 

352 

353 @tool(

354 "fetch_data",

355 "Fetch data from an API",

356 {"endpoint": str}, # Simple schema

357 )

358 async def fetch_data(args: dict[str, Any]) -> dict[str, Any]:

359 try:

360 async with httpx.AsyncClient() as client:

361 response = await client.get(args["endpoint"])

362 if response.status_code != 200:

363 # Return the failure as a tool result so Claude can react to it.

364 # is_error marks this as a failed call rather than odd-looking data.

365 return {

366 "content": [

367 {

368 "type": "text",

369 "text": f"API error: {response.status_code} {response.reason_phrase}",

370 }

371 ],

372 "is_error": True,

373 }

374 

375 data = response.json()

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

377 except Exception as e:

378 # Catching here keeps the agent loop alive. An uncaught exception

379 # would end the whole query() call.

380 return {

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

382 "is_error": True,

383 }

384 ```

385 

386 ```typescript TypeScript theme={null}

387 tool(

388 "fetch_data",

389 "Fetch data from an API",

390 {

391 endpoint: z.string().url().describe("API endpoint URL")

392 },

393 async (args) => {

394 try {

395 const response = await fetch(args.endpoint);

396 

397 if (!response.ok) {

398 // Return the failure as a tool result so Claude can react to it.

399 // isError marks this as a failed call rather than odd-looking data.

400 return {

401 content: [

402 {

403 type: "text",

404 text: `API error: ${response.status} ${response.statusText}`

405 }

406 ],

407 isError: true

408 };

409 }

410 

411 const data = await response.json();

412 return {

413 content: [

414 {

415 type: "text",

416 text: JSON.stringify(data, null, 2)

417 }

418 ]

419 };

420 } catch (error) {

421 // Catching here keeps the agent loop alive. An uncaught throw

422 // would end the whole query() call.

423 return {

424 content: [

425 {

426 type: "text",

427 text: `Failed to fetch data: ${error instanceof Error ? error.message : String(error)}`

428 }

429 ],

430 isError: true

431 };

432 }

433 }

434 );

435 ```

436</CodeGroup>

437 

438## Return images and resources

439 

440The `content` array in a tool result accepts `text`, `image`, and `resource` blocks. You can mix them in the same response.

441 

442### Images

443 

444An image block carries the image bytes inline, encoded as base64. There is no URL field. To return an image that lives at a URL, fetch it in the handler, read the response bytes, and base64-encode them before returning. The result is processed as visual input.

445 

446| Field | Type | Notes |

447| :--------- | :-------- | :------------------------------------------------------------------------- |

448| `type` | `"image"` | |

449| `data` | `string` | Base64-encoded bytes. Raw base64 only, no `data:image/...;base64,` prefix |

450| `mimeType` | `string` | Required. For example `image/png`, `image/jpeg`, `image/webp`, `image/gif` |

451 

452<CodeGroup>

453 ```python Python theme={null}

454 import base64

455 import httpx

456 

457 

458 # Define a tool that fetches an image from a URL and returns it to Claude

459 @tool("fetch_image", "Fetch an image from a URL and return it to Claude", {"url": str})

460 async def fetch_image(args):

461 async with httpx.AsyncClient() as client: # Fetch the image bytes

462 response = await client.get(args["url"])

463 

464 return {

465 "content": [

466 {

467 "type": "image",

468 "data": base64.b64encode(response.content).decode(

469 "ascii"

470 ), # Base64-encode the raw bytes

471 "mimeType": response.headers.get(

472 "content-type", "image/png"

473 ), # Read MIME type from the response

474 }

475 ]

476 }

477 ```

478 

479 ```typescript TypeScript theme={null}

480 tool(

481 "fetch_image",

482 "Fetch an image from a URL and return it to Claude",

483 {

484 url: z.string().url()

485 },

486 async (args) => {

487 const response = await fetch(args.url); // Fetch the image bytes

488 const buffer = Buffer.from(await response.arrayBuffer()); // Read into a Buffer for base64 encoding

489 const mimeType = response.headers.get("content-type") ?? "image/png";

490 

491 return {

492 content: [

493 {

494 type: "image",

495 data: buffer.toString("base64"), // Base64-encode the raw bytes

496 mimeType

497 }

498 ]

499 };

500 }

501 );

502 ```

503</CodeGroup>

504 

505### Resources

506 

507A 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.

508 

509| Field | Type | Notes |

510| :------------------ | :----------- | :---------------------------------------------------------- |

511| `type` | `"resource"` | |

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

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

514| `resource.blob` | `string` | The content base64-encoded, if it's binary |

515| `resource.mimeType` | `string` | Optional |

516 

517This 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.

518 

519<CodeGroup>

520 ```typescript TypeScript theme={null}

521 return {

522 content: [

523 {

524 type: "resource",

525 resource: {

526 uri: "file:///tmp/report.md", // Label for Claude to reference, not a path the SDK reads

527 mimeType: "text/markdown",

528 text: "# Report\n..." // The actual content, inline

529 }

530 }

531 ]

532 };

533 ```

534 

535 ```python Python theme={null}

536 return {

537 "content": [

538 {

539 "type": "resource",

540 "resource": {

541 "uri": "file:///tmp/report.md", # Label for Claude to reference, not a path the SDK reads

542 "mimeType": "text/markdown",

543 "text": "# Report\n...", # The actual content, inline

544 },

545 }

546 ]

547 }

548 ```

549</CodeGroup>

550 

551These block shapes come from the MCP `CallToolResult` type. See the [MCP specification](https://modelcontextprotocol.io/specification/2025-06-18/server/tools#tool-result) for the full definition.

552 

553## Example: unit converter

554 

555This tool converts values between units of length, temperature, and weight. A user can ask "convert 100 kilometers to miles" or "what is 72°F in Celsius," and Claude picks the right unit type and units from the request.

556 

557It demonstrates two patterns:

558 

559* **Enum schemas:** `unit_type` is constrained to a fixed set of values. In TypeScript, use `z.enum()`. In Python, the dict schema doesn't support enums, so the full JSON Schema dict is required.

560* **Unsupported input handling:** when a conversion pair isn't found, the handler returns `isError: true` so Claude can tell the user what went wrong rather than treating a failure as a normal result.

561 

562<CodeGroup>

563 ```python Python theme={null}

564 from typing import Any

565 from claude_agent_sdk import tool, create_sdk_mcp_server

566 

567 

568 # z.enum() in TypeScript becomes an "enum" constraint in JSON Schema.

569 # The dict schema has no equivalent, so full JSON Schema is required.

570 @tool(

571 "convert_units",

572 "Convert a value from one unit to another",

573 {

574 "type": "object",

575 "properties": {

576 "unit_type": {

577 "type": "string",

578 "enum": ["length", "temperature", "weight"],

579 "description": "Category of unit",

580 },

581 "from_unit": {

582 "type": "string",

583 "description": "Unit to convert from, e.g. kilometers, fahrenheit, pounds",

584 },

585 "to_unit": {"type": "string", "description": "Unit to convert to"},

586 "value": {"type": "number", "description": "Value to convert"},

587 },

588 "required": ["unit_type", "from_unit", "to_unit", "value"],

589 },

590 )

591 async def convert_units(args: dict[str, Any]) -> dict[str, Any]:

592 conversions = {

593 "length": {

594 "kilometers_to_miles": lambda v: v * 0.621371,

595 "miles_to_kilometers": lambda v: v * 1.60934,

596 "meters_to_feet": lambda v: v * 3.28084,

597 "feet_to_meters": lambda v: v * 0.3048,

598 },

599 "temperature": {

600 "celsius_to_fahrenheit": lambda v: (v * 9) / 5 + 32,

601 "fahrenheit_to_celsius": lambda v: (v - 32) * 5 / 9,

602 "celsius_to_kelvin": lambda v: v + 273.15,

603 "kelvin_to_celsius": lambda v: v - 273.15,

604 },

605 "weight": {

606 "kilograms_to_pounds": lambda v: v * 2.20462,

607 "pounds_to_kilograms": lambda v: v * 0.453592,

608 "grams_to_ounces": lambda v: v * 0.035274,

609 "ounces_to_grams": lambda v: v * 28.3495,

610 },

611 }

612 

613 key = f"{args['from_unit']}_to_{args['to_unit']}"

614 fn = conversions.get(args["unit_type"], {}).get(key)

615 

616 if not fn:

617 return {

618 "content": [

619 {

620 "type": "text",

621 "text": f"Unsupported conversion: {args['from_unit']} to {args['to_unit']}",

622 }

623 ],

624 "is_error": True,

625 }

626 

627 result = fn(args["value"])

628 return {

629 "content": [

630 {

631 "type": "text",

632 "text": f"{args['value']} {args['from_unit']} = {result:.4f} {args['to_unit']}",

633 }

634 ]

635 }

636 

637 

638 converter_server = create_sdk_mcp_server(

639 name="converter",

640 version="1.0.0",

641 tools=[convert_units],

642 )

643 ```

644 

645 ```typescript TypeScript theme={null}

646 import { tool, createSdkMcpServer } from "@anthropic-ai/claude-agent-sdk";

647 import { z } from "zod";

648 

649 const convert = tool(

650 "convert_units",

651 "Convert a value from one unit to another",

652 {

653 unit_type: z.enum(["length", "temperature", "weight"]).describe("Category of unit"),

654 from_unit: z

655 .string()

656 .describe("Unit to convert from, e.g. kilometers, fahrenheit, pounds"),

657 to_unit: z.string().describe("Unit to convert to"),

658 value: z.number().describe("Value to convert")

659 },

660 async (args) => {

661 type Conversions = Record<string, Record<string, (v: number) => number>>;

662 

663 const conversions: Conversions = {

664 length: {

665 kilometers_to_miles: (v) => v * 0.621371,

666 miles_to_kilometers: (v) => v * 1.60934,

667 meters_to_feet: (v) => v * 3.28084,

668 feet_to_meters: (v) => v * 0.3048

669 },

670 temperature: {

671 celsius_to_fahrenheit: (v) => (v * 9) / 5 + 32,

672 fahrenheit_to_celsius: (v) => ((v - 32) * 5) / 9,

673 celsius_to_kelvin: (v) => v + 273.15,

674 kelvin_to_celsius: (v) => v - 273.15

675 },

676 weight: {

677 kilograms_to_pounds: (v) => v * 2.20462,

678 pounds_to_kilograms: (v) => v * 0.453592,

679 grams_to_ounces: (v) => v * 0.035274,

680 ounces_to_grams: (v) => v * 28.3495

681 }

682 };

683 

684 const key = `${args.from_unit}_to_${args.to_unit}`;

685 const fn = conversions[args.unit_type]?.[key];

686 

687 if (!fn) {

688 return {

689 content: [

690 {

691 type: "text",

692 text: `Unsupported conversion: ${args.from_unit} to ${args.to_unit}`

693 }

694 ],

695 isError: true

696 };

697 }

698 

699 const result = fn(args.value);

700 return {

701 content: [

702 {

703 type: "text",

704 text: `${args.value} ${args.from_unit} = ${result.toFixed(4)} ${args.to_unit}`

705 }

706 ]

707 };

708 }

709 );

710 

711 const converterServer = createSdkMcpServer({

712 name: "converter",

713 version: "1.0.0",

714 tools: [convert]

715 });

716 ```

717</CodeGroup>

718 

719Once the server is defined, pass it to `query` the same way as the weather example. This example sends three different prompts in a loop to show the same tool handling different unit types. For each response, it inspects `AssistantMessage` objects (which contain the tool calls Claude made during that turn) and prints each `ToolUseBlock` before printing the final `ResultMessage` text. This lets you see when Claude is using the tool versus answering from its own knowledge.

720 

721<CodeGroup>

722 ```python Python theme={null}

723 import asyncio

724 from claude_agent_sdk import (

725 query,

726 ClaudeAgentOptions,

727 ResultMessage,

728 AssistantMessage,

729 ToolUseBlock,

730 )

731 

732 

733 async def main():

734 options = ClaudeAgentOptions(

735 mcp_servers={"converter": converter_server},

736 allowed_tools=["mcp__converter__convert_units"],

737 )

738 

739 prompts = [

740 "Convert 100 kilometers to miles.",

741 "What is 72°F in Celsius?",

742 "How many pounds is 5 kilograms?",

743 ]

744 

745 for prompt in prompts:

746 async for message in query(prompt=prompt, options=options):

747 if isinstance(message, AssistantMessage):

748 for block in message.content:

749 if isinstance(block, ToolUseBlock):

750 print(f"[tool call] {block.name}({block.input})")

751 elif isinstance(message, ResultMessage) and message.subtype == "success":

752 print(f"Q: {prompt}\nA: {message.result}\n")

753 

754 

755 asyncio.run(main())

756 ```

757 

758 ```typescript TypeScript theme={null}

759 import { query } from "@anthropic-ai/claude-agent-sdk";

760 

761 const prompts = [

762 "Convert 100 kilometers to miles.",

763 "What is 72°F in Celsius?",

764 "How many pounds is 5 kilograms?"

765 ];

766 

767 for (const prompt of prompts) {

768 for await (const message of query({

769 prompt,

770 options: {

771 mcpServers: { converter: converterServer },

772 allowedTools: ["mcp__converter__convert_units"]

773 }

774 })) {

775 if (message.type === "assistant") {

776 for (const block of message.message.content) {

777 if (block.type === "tool_use") {

778 console.log(`[tool call] ${block.name}`, block.input);

779 }

780 }

781 } else if (message.type === "result" && message.subtype === "success") {

782 console.log(`Q: ${prompt}\nA: ${message.result}\n`);

783 }

784 }

785 }

786 ```

787</CodeGroup>

788 

789## Next steps

790 

791Custom tools wrap async functions in a standard interface. You can mix the patterns on this page in the same server: a single server can hold a database tool, an API gateway tool, and an image renderer alongside each other.

792 

793From here:

794 

795* If your server grows to dozens of tools, see [tool search](/en/agent-sdk/tool-search) to defer loading them until Claude needs them.

796* To connect to external MCP servers (filesystem, GitHub, Slack) instead of building your own, see [Connect MCP servers](/en/agent-sdk/mcp).

797* To control which tools run automatically versus requiring approval, see [Configure permissions](/en/agent-sdk/permissions).

798 

799## Related documentation

800 

801* [TypeScript SDK Reference](/en/agent-sdk/typescript)

802* [Python SDK Reference](/en/agent-sdk/python)

803* [MCP Documentation](https://modelcontextprotocol.io)

804* [SDK Overview](/en/agent-sdk/overview)

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# Rewind file changes with checkpointing

6 

7> Track file changes during agent sessions and restore files to any previous state

8 

9File checkpointing tracks file modifications made through the Write, Edit, and NotebookEdit tools during an agent session, allowing you to rewind files to any previous state. Want to try it out? Jump to the [interactive example](#try-it-out).

10 

11With checkpointing, you can:

12 

13* **Undo unwanted changes** by restoring files to a known good state

14* **Explore alternatives** by restoring to a checkpoint and trying a different approach

15* **Recover from errors** when the agent makes incorrect modifications

16 

17<Warning>

18 Only changes made through the Write, Edit, and NotebookEdit tools are tracked. Changes made through Bash commands (like `echo > file.txt` or `sed -i`) are not captured by the checkpoint system.

19</Warning>

20 

21## How checkpointing works

22 

23When you enable file checkpointing, the SDK creates backups of files before modifying them through the Write, Edit, or NotebookEdit tools. User messages in the response stream include a checkpoint UUID that you can use as a restore point.

24 

25Checkpoint works with these built-in tools that the agent uses to modify files:

26 

27| Tool | Description |

28| ------------ | ------------------------------------------------------------------ |

29| Write | Creates a new file or overwrites an existing file with new content |

30| Edit | Makes targeted edits to specific parts of an existing file |

31| NotebookEdit | Modifies cells in Jupyter notebooks (`.ipynb` files) |

32 

33<Note>

34 File rewinding restores files on disk to a previous state. It does not rewind the conversation itself. The conversation history and context remain intact after calling `rewindFiles()` (TypeScript) or `rewind_files()` (Python).

35</Note>

36 

37The checkpoint system tracks:

38 

39* Files created during the session

40* Files modified during the session

41* The original content of modified files

42 

43When you rewind to a checkpoint, created files are deleted and modified files are restored to their content at that point.

44 

45## Implement checkpointing

46 

47To use file checkpointing, enable it in your options, capture checkpoint UUIDs from the response stream, then call `rewindFiles()` (TypeScript) or `rewind_files()` (Python) when you need to restore.

48 

49The following example shows the complete flow: enable checkpointing, capture the checkpoint UUID and session ID from the response stream, then resume the session later to rewind files. Each step is explained in detail below.

50 

51<CodeGroup>

52 ```python Python theme={null}

53 import asyncio

54 from claude_agent_sdk import (

55 ClaudeSDKClient,

56 ClaudeAgentOptions,

57 UserMessage,

58 ResultMessage,

59 )

60 

61 

62 async def main():

63 # Step 1: Enable checkpointing

64 options = ClaudeAgentOptions(

65 enable_file_checkpointing=True,

66 permission_mode="acceptEdits", # Auto-accept file edits without prompting

67 extra_args={

68 "replay-user-messages": None

69 }, # Required to receive checkpoint UUIDs in the response stream

70 )

71 

72 checkpoint_id = None

73 session_id = None

74 

75 # Run the query and capture checkpoint UUID and session ID

76 async with ClaudeSDKClient(options) as client:

77 await client.query("Refactor the authentication module")

78 

79 # Step 2: Capture checkpoint UUID from the first user message

80 async for message in client.receive_response():

81 if isinstance(message, UserMessage) and message.uuid and not checkpoint_id:

82 checkpoint_id = message.uuid

83 if isinstance(message, ResultMessage) and not session_id:

84 session_id = message.session_id

85 

86 # Step 3: Later, rewind by resuming the session with an empty prompt

87 if checkpoint_id and session_id:

88 async with ClaudeSDKClient(

89 ClaudeAgentOptions(enable_file_checkpointing=True, resume=session_id)

90 ) as client:

91 await client.query("") # Empty prompt to open the connection

92 async for message in client.receive_response():

93 await client.rewind_files(checkpoint_id)

94 break

95 print(f"Rewound to checkpoint: {checkpoint_id}")

96 

97 

98 asyncio.run(main())

99 ```

100 

101 ```typescript TypeScript theme={null}

102 import { query } from "@anthropic-ai/claude-agent-sdk";

103 

104 async function main() {

105 // Step 1: Enable checkpointing

106 const opts = {

107 enableFileCheckpointing: true,

108 permissionMode: "acceptEdits" as const, // Auto-accept file edits without prompting

109 extraArgs: { "replay-user-messages": null } // Required to receive checkpoint UUIDs in the response stream

110 };

111 

112 const response = query({

113 prompt: "Refactor the authentication module",

114 options: opts

115 });

116 

117 let checkpointId: string | undefined;

118 let sessionId: string | undefined;

119 

120 // Step 2: Capture checkpoint UUID from the first user message

121 for await (const message of response) {

122 if (message.type === "user" && message.uuid && !checkpointId) {

123 checkpointId = message.uuid;

124 }

125 if ("session_id" in message && !sessionId) {

126 sessionId = message.session_id;

127 }

128 }

129 

130 // Step 3: Later, rewind by resuming the session with an empty prompt

131 if (checkpointId && sessionId) {

132 const rewindQuery = query({

133 prompt: "", // Empty prompt to open the connection

134 options: { ...opts, resume: sessionId }

135 });

136 

137 for await (const msg of rewindQuery) {

138 await rewindQuery.rewindFiles(checkpointId);

139 break;

140 }

141 console.log(`Rewound to checkpoint: ${checkpointId}`);

142 }

143 }

144 

145 main();

146 ```

147</CodeGroup>

148 

149<Steps>

150 <Step title="Enable checkpointing">

151 Configure your SDK options to enable checkpointing and receive checkpoint UUIDs:

152 

153 | Option | Python | TypeScript | Description |

154 | ------------------------ | ------------------------------------------- | --------------------------------------------- | ------------------------------------------------ |

155 | Enable checkpointing | `enable_file_checkpointing=True` | `enableFileCheckpointing: true` | Tracks file changes for rewinding |

156 | Receive checkpoint UUIDs | `extra_args={"replay-user-messages": None}` | `extraArgs: { 'replay-user-messages': null }` | Required to get user message UUIDs in the stream |

157 

158 <CodeGroup>

159 ```python Python theme={null}

160 options = ClaudeAgentOptions(

161 enable_file_checkpointing=True,

162 permission_mode="acceptEdits",

163 extra_args={"replay-user-messages": None},

164 )

165 

166 async with ClaudeSDKClient(options) as client:

167 await client.query("Refactor the authentication module")

168 ```

169 

170 ```typescript TypeScript theme={null}

171 const response = query({

172 prompt: "Refactor the authentication module",

173 options: {

174 enableFileCheckpointing: true,

175 permissionMode: "acceptEdits" as const,

176 extraArgs: { "replay-user-messages": null }

177 }

178 });

179 ```

180 </CodeGroup>

181 </Step>

182 

183 <Step title="Capture checkpoint UUID and session ID">

184 With the `replay-user-messages` option set (shown above), each user message in the response stream has a UUID that serves as a checkpoint.

185 

186 For most use cases, capture the first user message UUID (`message.uuid`); rewinding to it restores all files to their original state. To store multiple checkpoints and rewind to intermediate states, see [Multiple restore points](#multiple-restore-points).

187 

188 Capturing the session ID (`message.session_id`) is optional; you only need it if you want to rewind later, after the stream completes. If you're calling `rewindFiles()` immediately while still processing messages (as the example in [Checkpoint before risky operations](#checkpoint-before-risky-operations) does), you can skip capturing the session ID.

189 

190 <CodeGroup>

191 ```python Python theme={null}

192 checkpoint_id = None

193 session_id = None

194 

195 async for message in client.receive_response():

196 # Update checkpoint on each user message (keeps the latest)

197 if isinstance(message, UserMessage) and message.uuid:

198 checkpoint_id = message.uuid

199 # Capture session ID from the result message

200 if isinstance(message, ResultMessage):

201 session_id = message.session_id

202 ```

203 

204 ```typescript TypeScript theme={null}

205 let checkpointId: string | undefined;

206 let sessionId: string | undefined;

207 

208 for await (const message of response) {

209 // Update checkpoint on each user message (keeps the latest)

210 if (message.type === "user" && message.uuid) {

211 checkpointId = message.uuid;

212 }

213 // Capture session ID from any message that has it

214 if ("session_id" in message) {

215 sessionId = message.session_id;

216 }

217 }

218 ```

219 </CodeGroup>

220 </Step>

221 

222 <Step title="Rewind files">

223 To rewind after the stream completes, resume the session with an empty prompt and call `rewind_files()` (Python) or `rewindFiles()` (TypeScript) with your checkpoint UUID. You can also rewind during the stream; see [Checkpoint before risky operations](#checkpoint-before-risky-operations) for that pattern.

224 

225 <CodeGroup>

226 ```python Python theme={null}

227 async with ClaudeSDKClient(

228 ClaudeAgentOptions(enable_file_checkpointing=True, resume=session_id)

229 ) as client:

230 await client.query("") # Empty prompt to open the connection

231 async for message in client.receive_response():

232 await client.rewind_files(checkpoint_id)

233 break

234 ```

235 

236 ```typescript TypeScript theme={null}

237 const rewindQuery = query({

238 prompt: "", // Empty prompt to open the connection

239 options: { ...opts, resume: sessionId }

240 });

241 

242 for await (const msg of rewindQuery) {

243 await rewindQuery.rewindFiles(checkpointId);

244 break;

245 }

246 ```

247 </CodeGroup>

248 

249 If you capture the session ID and checkpoint ID, you can also rewind from the CLI:

250 

251 ```bash theme={null}

252 claude -p --resume <session-id> --rewind-files <checkpoint-uuid>

253 ```

254 </Step>

255</Steps>

256 

257## Common patterns

258 

259These patterns show different ways to capture and use checkpoint UUIDs depending on your use case.

260 

261### Checkpoint before risky operations

262 

263This pattern keeps only the most recent checkpoint UUID, updating it before each agent turn. If something goes wrong during processing, you can immediately rewind to the last safe state and break out of the loop.

264 

265<CodeGroup>

266 ```python Python theme={null}

267 import asyncio

268 from claude_agent_sdk import ClaudeSDKClient, ClaudeAgentOptions, UserMessage

269 

270 

271 async def main():

272 options = ClaudeAgentOptions(

273 enable_file_checkpointing=True,

274 permission_mode="acceptEdits",

275 extra_args={"replay-user-messages": None},

276 )

277 

278 safe_checkpoint = None

279 

280 async with ClaudeSDKClient(options) as client:

281 await client.query("Refactor the authentication module")

282 

283 async for message in client.receive_response():

284 # Update checkpoint before each agent turn starts

285 # This overwrites the previous checkpoint. Only keep the latest

286 if isinstance(message, UserMessage) and message.uuid:

287 safe_checkpoint = message.uuid

288 

289 # Decide when to revert based on your own logic

290 # For example: error detection, validation failure, or user input

291 if your_revert_condition and safe_checkpoint:

292 await client.rewind_files(safe_checkpoint)

293 # Exit the loop after rewinding, files are restored

294 break

295 

296 

297 asyncio.run(main())

298 ```

299 

300 ```typescript TypeScript theme={null}

301 import { query } from "@anthropic-ai/claude-agent-sdk";

302 

303 async function main() {

304 const response = query({

305 prompt: "Refactor the authentication module",

306 options: {

307 enableFileCheckpointing: true,

308 permissionMode: "acceptEdits" as const,

309 extraArgs: { "replay-user-messages": null }

310 }

311 });

312 

313 let safeCheckpoint: string | undefined;

314 

315 for await (const message of response) {

316 // Update checkpoint before each agent turn starts

317 // This overwrites the previous checkpoint. Only keep the latest

318 if (message.type === "user" && message.uuid) {

319 safeCheckpoint = message.uuid;

320 }

321 

322 // Decide when to revert based on your own logic

323 // For example: error detection, validation failure, or user input

324 if (yourRevertCondition && safeCheckpoint) {

325 await response.rewindFiles(safeCheckpoint);

326 // Exit the loop after rewinding, files are restored

327 break;

328 }

329 }

330 }

331 

332 main();

333 ```

334</CodeGroup>

335 

336### Multiple restore points

337 

338If Claude makes changes across multiple turns, you might want to rewind to a specific point rather than all the way back. For example, if Claude refactors a file in turn one and adds tests in turn two, you might want to keep the refactor but undo the tests.

339 

340This pattern stores all checkpoint UUIDs in an array with metadata. After the session completes, you can rewind to any previous checkpoint:

341 

342<CodeGroup>

343 ```python Python theme={null}

344 import asyncio

345 from dataclasses import dataclass

346 from datetime import datetime

347 from claude_agent_sdk import (

348 ClaudeSDKClient,

349 ClaudeAgentOptions,

350 UserMessage,

351 ResultMessage,

352 )

353 

354 

355 # Store checkpoint metadata for better tracking

356 @dataclass

357 class Checkpoint:

358 id: str

359 description: str

360 timestamp: datetime

361 

362 

363 async def main():

364 options = ClaudeAgentOptions(

365 enable_file_checkpointing=True,

366 permission_mode="acceptEdits",

367 extra_args={"replay-user-messages": None},

368 )

369 

370 checkpoints = []

371 session_id = None

372 

373 async with ClaudeSDKClient(options) as client:

374 await client.query("Refactor the authentication module")

375 

376 async for message in client.receive_response():

377 if isinstance(message, UserMessage) and message.uuid:

378 checkpoints.append(

379 Checkpoint(

380 id=message.uuid,

381 description=f"After turn {len(checkpoints) + 1}",

382 timestamp=datetime.now(),

383 )

384 )

385 if isinstance(message, ResultMessage) and not session_id:

386 session_id = message.session_id

387 

388 # Later: rewind to any checkpoint by resuming the session

389 if checkpoints and session_id:

390 target = checkpoints[0] # Pick any checkpoint

391 async with ClaudeSDKClient(

392 ClaudeAgentOptions(enable_file_checkpointing=True, resume=session_id)

393 ) as client:

394 await client.query("") # Empty prompt to open the connection

395 async for message in client.receive_response():

396 await client.rewind_files(target.id)

397 break

398 print(f"Rewound to: {target.description}")

399 

400 

401 asyncio.run(main())

402 ```

403 

404 ```typescript TypeScript theme={null}

405 import { query } from "@anthropic-ai/claude-agent-sdk";

406 

407 // Store checkpoint metadata for better tracking

408 interface Checkpoint {

409 id: string;

410 description: string;

411 timestamp: Date;

412 }

413 

414 async function main() {

415 const opts = {

416 enableFileCheckpointing: true,

417 permissionMode: "acceptEdits" as const,

418 extraArgs: { "replay-user-messages": null }

419 };

420 

421 const response = query({

422 prompt: "Refactor the authentication module",

423 options: opts

424 });

425 

426 const checkpoints: Checkpoint[] = [];

427 let sessionId: string | undefined;

428 

429 for await (const message of response) {

430 if (message.type === "user" && message.uuid) {

431 checkpoints.push({

432 id: message.uuid,

433 description: `After turn ${checkpoints.length + 1}`,

434 timestamp: new Date()

435 });

436 }

437 if ("session_id" in message && !sessionId) {

438 sessionId = message.session_id;

439 }

440 }

441 

442 // Later: rewind to any checkpoint by resuming the session

443 if (checkpoints.length > 0 && sessionId) {

444 const target = checkpoints[0]; // Pick any checkpoint

445 const rewindQuery = query({

446 prompt: "", // Empty prompt to open the connection

447 options: { ...opts, resume: sessionId }

448 });

449 

450 for await (const msg of rewindQuery) {

451 await rewindQuery.rewindFiles(target.id);

452 break;

453 }

454 console.log(`Rewound to: ${target.description}`);

455 }

456 }

457 

458 main();

459 ```

460</CodeGroup>

461 

462## Try it out

463 

464This complete example creates a small utility file, has the agent add documentation comments, shows you the changes, then asks if you want to rewind.

465 

466Before you begin, make sure you have the [Claude Agent SDK installed](/en/agent-sdk/quickstart).

467 

468<Steps>

469 <Step title="Create a test file">

470 Create a new file called `utils.py` (Python) or `utils.ts` (TypeScript) and paste the following code:

471 

472 <CodeGroup>

473 ```python utils.py theme={null}

474 def add(a, b):

475 return a + b

476 

477 

478 def subtract(a, b):

479 return a - b

480 

481 

482 def multiply(a, b):

483 return a * b

484 

485 

486 def divide(a, b):

487 if b == 0:

488 raise ValueError("Cannot divide by zero")

489 return a / b

490 ```

491 

492 ```typescript utils.ts theme={null}

493 export function add(a: number, b: number): number {

494 return a + b;

495 }

496 

497 export function subtract(a: number, b: number): number {

498 return a - b;

499 }

500 

501 export function multiply(a: number, b: number): number {

502 return a * b;

503 }

504 

505 export function divide(a: number, b: number): number {

506 if (b === 0) {

507 throw new Error("Cannot divide by zero");

508 }

509 return a / b;

510 }

511 ```

512 </CodeGroup>

513 </Step>

514 

515 <Step title="Run the interactive example">

516 Create a new file called `try_checkpointing.py` (Python) or `try_checkpointing.ts` (TypeScript) in the same directory as your utility file, and paste the following code.

517 

518 This script asks Claude to add doc comments to your utility file, then gives you the option to rewind and restore the original.

519 

520 <CodeGroup>

521 ```python try_checkpointing.py theme={null}

522 import asyncio

523 from claude_agent_sdk import (

524 ClaudeSDKClient,

525 ClaudeAgentOptions,

526 UserMessage,

527 ResultMessage,

528 )

529 

530 

531 async def main():

532 # Configure the SDK with checkpointing enabled

533 # - enable_file_checkpointing: Track file changes for rewinding

534 # - permission_mode: Auto-accept file edits without prompting

535 # - extra_args: Required to receive user message UUIDs in the stream

536 options = ClaudeAgentOptions(

537 enable_file_checkpointing=True,

538 permission_mode="acceptEdits",

539 extra_args={"replay-user-messages": None},

540 )

541 

542 checkpoint_id = None # Store the user message UUID for rewinding

543 session_id = None # Store the session ID for resuming

544 

545 print("Running agent to add doc comments to utils.py...\n")

546 

547 # Run the agent and capture checkpoint data from the response stream

548 async with ClaudeSDKClient(options) as client:

549 await client.query("Add doc comments to utils.py")

550 

551 async for message in client.receive_response():

552 # Capture the first user message UUID - this is our restore point

553 if isinstance(message, UserMessage) and message.uuid and not checkpoint_id:

554 checkpoint_id = message.uuid

555 # Capture the session ID so we can resume later

556 if isinstance(message, ResultMessage):

557 session_id = message.session_id

558 

559 print("Done! Open utils.py to see the added doc comments.\n")

560 

561 # Ask the user if they want to rewind the changes

562 if checkpoint_id and session_id:

563 response = input("Rewind to remove the doc comments? (y/n): ")

564 

565 if response.lower() == "y":

566 # Resume the session with an empty prompt, then rewind

567 async with ClaudeSDKClient(

568 ClaudeAgentOptions(enable_file_checkpointing=True, resume=session_id)

569 ) as client:

570 await client.query("") # Empty prompt opens the connection

571 async for message in client.receive_response():

572 await client.rewind_files(checkpoint_id) # Restore files

573 break

574 

575 print(

576 "\n✓ File restored! Open utils.py to verify the doc comments are gone."

577 )

578 else:

579 print("\nKept the modified file.")

580 

581 

582 asyncio.run(main())

583 ```

584 

585 ```typescript try_checkpointing.ts theme={null}

586 import { query } from "@anthropic-ai/claude-agent-sdk";

587 import * as readline from "readline";

588 

589 async function main() {

590 // Configure the SDK with checkpointing enabled

591 // - enableFileCheckpointing: Track file changes for rewinding

592 // - permissionMode: Auto-accept file edits without prompting

593 // - extraArgs: Required to receive user message UUIDs in the stream

594 const opts = {

595 enableFileCheckpointing: true,

596 permissionMode: "acceptEdits" as const,

597 extraArgs: { "replay-user-messages": null }

598 };

599 

600 let sessionId: string | undefined; // Store the session ID for resuming

601 let checkpointId: string | undefined; // Store the user message UUID for rewinding

602 

603 console.log("Running agent to add doc comments to utils.ts...\n");

604 

605 // Run the agent and capture checkpoint data from the response stream

606 const response = query({

607 prompt: "Add doc comments to utils.ts",

608 options: opts

609 });

610 

611 for await (const message of response) {

612 // Capture the first user message UUID - this is our restore point

613 if (message.type === "user" && message.uuid && !checkpointId) {

614 checkpointId = message.uuid;

615 }

616 // Capture the session ID so we can resume later

617 if ("session_id" in message) {

618 sessionId = message.session_id;

619 }

620 }

621 

622 console.log("Done! Open utils.ts to see the added doc comments.\n");

623 

624 // Ask the user if they want to rewind the changes

625 if (checkpointId && sessionId) {

626 const rl = readline.createInterface({

627 input: process.stdin,

628 output: process.stdout

629 });

630 

631 const answer = await new Promise<string>((resolve) => {

632 rl.question("Rewind to remove the doc comments? (y/n): ", resolve);

633 });

634 rl.close();

635 

636 if (answer.toLowerCase() === "y") {

637 // Resume the session with an empty prompt, then rewind

638 const rewindQuery = query({

639 prompt: "", // Empty prompt opens the connection

640 options: { ...opts, resume: sessionId }

641 });

642 

643 for await (const msg of rewindQuery) {

644 await rewindQuery.rewindFiles(checkpointId); // Restore files

645 break;

646 }

647 

648 console.log("\n✓ File restored! Open utils.ts to verify the doc comments are gone.");

649 } else {

650 console.log("\nKept the modified file.");

651 }

652 }

653 }

654 

655 main();

656 ```

657 </CodeGroup>

658 

659 This example demonstrates the complete checkpointing workflow:

660 

661 1. **Enable checkpointing**: configure the SDK with `enable_file_checkpointing=True` and `permission_mode="acceptEdits"` to auto-approve file edits

662 2. **Capture checkpoint data**: as the agent runs, store the first user message UUID (your restore point) and the session ID

663 3. **Prompt for rewind**: after the agent finishes, check your utility file to see the doc comments, then decide if you want to undo the changes

664 4. **Resume and rewind**: if yes, resume the session with an empty prompt and call `rewind_files()` to restore the original file

665 </Step>

666 

667 <Step title="Run the example">

668 Run the script from the same directory as your utility file.

669 

670 <Tip>

671 Open your utility file (`utils.py` or `utils.ts`) in your IDE or editor before running the script. You'll see the file update in real-time as the agent adds doc comments, then revert back to the original when you choose to rewind.

672 </Tip>

673 

674 <Tabs>

675 <Tab title="Python">

676 ```bash theme={null}

677 python try_checkpointing.py

678 ```

679 </Tab>

680 

681 <Tab title="TypeScript">

682 ```bash theme={null}

683 npx tsx try_checkpointing.ts

684 ```

685 </Tab>

686 </Tabs>

687 

688 You'll see the agent add doc comments, then a prompt asking if you want to rewind. If you choose yes, the file is restored to its original state.

689 </Step>

690</Steps>

691 

692## Limitations

693 

694File checkpointing has the following limitations:

695 

696| Limitation | Description |

697| ---------------------------------- | -------------------------------------------------------------------- |

698| Write/Edit/NotebookEdit tools only | Changes made through Bash commands are not tracked |

699| Same session | Checkpoints are tied to the session that created them |

700| File content only | Creating, moving, or deleting directories is not undone by rewinding |

701| Local files | Remote or network files are not tracked |

702 

703## Troubleshooting

704 

705### Checkpointing options not recognized

706 

707If `enableFileCheckpointing` or `rewindFiles()` isn't available, you may be on an older SDK version.

708 

709**Solution**: Update to the latest SDK version:

710 

711* **Python**: `pip install --upgrade claude-agent-sdk`

712* **TypeScript**: `npm install @anthropic-ai/claude-agent-sdk@latest`

713 

714### User messages don't have UUIDs

715 

716If `message.uuid` is `undefined` or missing, you're not receiving checkpoint UUIDs.

717 

718**Cause**: The `replay-user-messages` option isn't set.

719 

720**Solution**: Add `extra_args={"replay-user-messages": None}` (Python) or `extraArgs: { 'replay-user-messages': null }` (TypeScript) to your options.

721 

722### "No file checkpoint found for message" error

723 

724This error occurs when the checkpoint data doesn't exist for the specified user message UUID.

725 

726**Common causes**:

727 

728* File checkpointing was not enabled on the original session (`enable_file_checkpointing` or `enableFileCheckpointing` was not set to `true`)

729* The session wasn't properly completed before attempting to resume and rewind

730 

731**Solution**: Ensure `enable_file_checkpointing=True` (Python) or `enableFileCheckpointing: true` (TypeScript) was set on the original session, then use the pattern shown in the examples: capture the first user message UUID, complete the session fully, then resume with an empty prompt and call `rewindFiles()` once.

732 

733### "ProcessTransport is not ready for writing" error

734 

735This error occurs when you call `rewindFiles()` or `rewind_files()` after you've finished iterating through the response. The connection to the CLI process closes when the loop completes.

736 

737**Solution**: Resume the session with an empty prompt, then call rewind on the new query:

738 

739<CodeGroup>

740 ```python Python theme={null}

741 # Resume session with empty prompt, then rewind

742 async with ClaudeSDKClient(

743 ClaudeAgentOptions(enable_file_checkpointing=True, resume=session_id)

744 ) as client:

745 await client.query("")

746 async for message in client.receive_response():

747 await client.rewind_files(checkpoint_id)

748 break

749 ```

750 

751 ```typescript TypeScript theme={null}

752 // Resume session with empty prompt, then rewind

753 const rewindQuery = query({

754 prompt: "",

755 options: { ...opts, resume: sessionId }

756 });

757 

758 for await (const msg of rewindQuery) {

759 await rewindQuery.rewindFiles(checkpointId);

760 break;

761 }

762 ```

763</CodeGroup>

764 

765## Next steps

766 

767* **[Sessions](/en/agent-sdk/sessions)**: learn how to resume sessions, which is required for rewinding after the stream completes. Covers session IDs, resuming conversations, and session forking.

768* **[Permissions](/en/agent-sdk/permissions)**: configure which tools Claude can use and how file modifications are approved. Useful if you want more control over when edits happen.

769* **[TypeScript SDK reference](/en/agent-sdk/typescript)**: complete API reference including all options for `query()` and the `rewindFiles()` method.

770* **[Python SDK reference](/en/agent-sdk/python)**: complete API reference including all options for `ClaudeAgentOptions` and the `rewind_files()` method.

agent-sdk/hooks.md +818 −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# Intercept and control agent behavior with hooks

6 

7> Intercept and customize agent behavior at key execution points with hooks

8 

9Hooks are callback functions that run your code in response to agent events, like a tool being called, a session starting, or execution stopping. With hooks, you can:

10 

11* **Block dangerous operations** before they execute, like destructive shell commands or unauthorized file access

12* **Log and audit** every tool call for compliance, debugging, or analytics

13* **Transform inputs and outputs** to sanitize data, inject credentials, or redirect file paths

14* **Require human approval** for sensitive actions like database writes or API calls

15* **Track session lifecycle** to manage state, clean up resources, or send notifications

16 

17This guide covers how hooks work, how to configure them, and provides examples for common patterns like blocking tools, modifying inputs, and forwarding notifications.

18 

19## How hooks work

20 

21<Steps>

22 <Step title="An event fires">

23 Something happens during agent execution and the SDK fires an event: a tool is about to be called (`PreToolUse`), a tool returned a result (`PostToolUse`), a subagent started or stopped, the agent is idle, or execution finished. See the [full list of events](#available-hooks).

24 </Step>

25 

26 <Step title="The SDK collects registered hooks">

27 The SDK checks for hooks registered for that event type. This includes callback hooks you pass in `options.hooks` and shell command hooks from settings files, but only if you explicitly load them with [`settingSources`](/en/agent-sdk/typescript#setting-source) or [`setting_sources`](/en/agent-sdk/python#setting-source).

28 </Step>

29 

30 <Step title="Matchers filter which hooks run">

31 If a hook has a [`matcher`](#matchers) pattern (like `"Write|Edit"`), the SDK tests it against the event's target (for example, the tool name). Hooks without a matcher run for every event of that type.

32 </Step>

33 

34 <Step title="Callback functions execute">

35 Each matching hook's [callback function](#callback-functions) receives input about what's happening: the tool name, its arguments, the session ID, and other event-specific details.

36 </Step>

37 

38 <Step title="Your callback returns a decision">

39 After performing any operations (logging, API calls, validation), your callback returns an [output object](#outputs) that tells the agent what to do: allow the operation, block it, modify the input, or inject context into the conversation.

40 </Step>

41</Steps>

42 

43The following example puts these steps together. It registers a `PreToolUse` hook (step 1) with a `"Write|Edit"` matcher (step 3) so the callback only fires for file-writing tools. When triggered, the callback receives the tool's input (step 4), checks if the file path targets a `.env` file, and returns `permissionDecision: "deny"` to block the operation (step 5):

44 

45<CodeGroup>

46 ```python Python theme={null}

47 import asyncio

48 from claude_agent_sdk import (

49 AssistantMessage,

50 ClaudeSDKClient,

51 ClaudeAgentOptions,

52 HookMatcher,

53 ResultMessage,

54 )

55 

56 

57 # Define a hook callback that receives tool call details

58 async def protect_env_files(input_data, tool_use_id, context):

59 # Extract the file path from the tool's input arguments

60 file_path = input_data["tool_input"].get("file_path", "")

61 file_name = file_path.split("/")[-1]

62 

63 # Block the operation if targeting a .env file

64 if file_name == ".env":

65 return {

66 "hookSpecificOutput": {

67 "hookEventName": input_data["hook_event_name"],

68 "permissionDecision": "deny",

69 "permissionDecisionReason": "Cannot modify .env files",

70 }

71 }

72 

73 # Return empty object to allow the operation

74 return {}

75 

76 

77 async def main():

78 options = ClaudeAgentOptions(

79 hooks={

80 # Register the hook for PreToolUse events

81 # The matcher filters to only Write and Edit tool calls

82 "PreToolUse": [HookMatcher(matcher="Write|Edit", hooks=[protect_env_files])]

83 }

84 )

85 

86 async with ClaudeSDKClient(options=options) as client:

87 await client.query("Update the database configuration")

88 async for message in client.receive_response():

89 # Filter for assistant and result messages

90 if isinstance(message, (AssistantMessage, ResultMessage)):

91 print(message)

92 

93 

94 asyncio.run(main())

95 ```

96 

97 ```typescript TypeScript theme={null}

98 import { query, HookCallback, PreToolUseHookInput } from "@anthropic-ai/claude-agent-sdk";

99 

100 // Define a hook callback with the HookCallback type

101 const protectEnvFiles: HookCallback = async (input, toolUseID, { signal }) => {

102 // Cast input to the specific hook type for type safety

103 const preInput = input as PreToolUseHookInput;

104 

105 // Cast tool_input to access its properties (typed as unknown in the SDK)

106 const toolInput = preInput.tool_input as Record<string, unknown>;

107 const filePath = toolInput?.file_path as string;

108 const fileName = filePath?.split("/").pop();

109 

110 // Block the operation if targeting a .env file

111 if (fileName === ".env") {

112 return {

113 hookSpecificOutput: {

114 hookEventName: preInput.hook_event_name,

115 permissionDecision: "deny",

116 permissionDecisionReason: "Cannot modify .env files"

117 }

118 };

119 }

120 

121 // Return empty object to allow the operation

122 return {};

123 };

124 

125 for await (const message of query({

126 prompt: "Update the database configuration",

127 options: {

128 hooks: {

129 // Register the hook for PreToolUse events

130 // The matcher filters to only Write and Edit tool calls

131 PreToolUse: [{ matcher: "Write|Edit", hooks: [protectEnvFiles] }]

132 }

133 }

134 })) {

135 // Filter for assistant and result messages

136 if (message.type === "assistant" || message.type === "result") {

137 console.log(message);

138 }

139 }

140 ```

141</CodeGroup>

142 

143## Available hooks

144 

145The SDK provides hooks for different stages of agent execution. Some hooks are available in both SDKs, while others are TypeScript-only.

146 

147| Hook Event | Python SDK | TypeScript SDK | What triggers it | Example use case |

148| -------------------- | ---------- | -------------- | --------------------------------------- | ----------------------------------------------- |

149| `PreToolUse` | Yes | Yes | Tool call request (can block or modify) | Block dangerous shell commands |

150| `PostToolUse` | Yes | Yes | Tool execution result | Log all file changes to audit trail |

151| `PostToolUseFailure` | Yes | Yes | Tool execution failure | Handle or log tool errors |

152| `UserPromptSubmit` | Yes | Yes | User prompt submission | Inject additional context into prompts |

153| `Stop` | Yes | Yes | Agent execution stop | Save session state before exit |

154| `SubagentStart` | Yes | Yes | Subagent initialization | Track parallel task spawning |

155| `SubagentStop` | Yes | Yes | Subagent completion | Aggregate results from parallel tasks |

156| `PreCompact` | Yes | Yes | Conversation compaction request | Archive full transcript before summarizing |

157| `PermissionRequest` | Yes | Yes | Permission dialog would be displayed | Custom permission handling |

158| `SessionStart` | No | Yes | Session initialization | Initialize logging and telemetry |

159| `SessionEnd` | No | Yes | Session termination | Clean up temporary resources |

160| `Notification` | Yes | Yes | Agent status messages | Send agent status updates to Slack or PagerDuty |

161| `Setup` | No | Yes | Session setup/maintenance | Run initialization tasks |

162| `TeammateIdle` | No | Yes | Teammate becomes idle | Reassign work or notify |

163| `TaskCompleted` | No | Yes | Background task completes | Aggregate results from parallel tasks |

164| `ConfigChange` | No | Yes | Configuration file changes | Reload settings dynamically |

165| `WorktreeCreate` | No | Yes | Git worktree created | Track isolated workspaces |

166| `WorktreeRemove` | No | Yes | Git worktree removed | Clean up workspace resources |

167 

168## Configure hooks

169 

170To configure a hook, pass it in the `hooks` field of your agent options (`ClaudeAgentOptions` in Python, the `options` object in TypeScript):

171 

172<CodeGroup>

173 ```python Python theme={null}

174 options = ClaudeAgentOptions(

175 hooks={"PreToolUse": [HookMatcher(matcher="Bash", hooks=[my_callback])]}

176 )

177 

178 async with ClaudeSDKClient(options=options) as client:

179 await client.query("Your prompt")

180 async for message in client.receive_response():

181 print(message)

182 ```

183 

184 ```typescript TypeScript theme={null}

185 for await (const message of query({

186 prompt: "Your prompt",

187 options: {

188 hooks: {

189 PreToolUse: [{ matcher: "Bash", hooks: [myCallback] }]

190 }

191 }

192 })) {

193 console.log(message);

194 }

195 ```

196</CodeGroup>

197 

198The `hooks` option is a dictionary (Python) or object (TypeScript) where:

199 

200* **Keys** are [hook event names](#available-hooks) (e.g., `'PreToolUse'`, `'PostToolUse'`, `'Stop'`)

201* **Values** are arrays of [matchers](#matchers), each containing an optional filter pattern and your [callback functions](#callback-functions)

202 

203### Matchers

204 

205Use matchers to filter when your callbacks fire. The `matcher` field is a regex string that matches against a different value depending on the hook event type. For example, tool-based hooks match against the tool name, while `Notification` hooks match against the notification type. See the [Claude Code hooks reference](/en/hooks#matcher-patterns) for the full list of matcher values for each event type.

206 

207| Option | Type | Default | Description |

208| --------- | ---------------- | ----------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |

209| `matcher` | `string` | `undefined` | Regex pattern matched against the event's filter field. For tool hooks, this is the tool name. Built-in tools include `Bash`, `Read`, `Write`, `Edit`, `Glob`, `Grep`, `WebFetch`, `Agent`, and others (see [Tool Input Types](/en/agent-sdk/typescript#tool-input-types) for the full list). MCP tools use the pattern `mcp__<server>__<action>`. |

210| `hooks` | `HookCallback[]` | - | Required. Array of callback functions to execute when the pattern matches |

211| `timeout` | `number` | `60` | Timeout in seconds |

212 

213Use the `matcher` pattern to target specific tools whenever possible. A matcher with `'Bash'` only runs for Bash commands, while omitting the pattern runs your callbacks for every occurrence of the event. Note that for tool-based hooks, matchers only filter by **tool name**, not by file paths or other arguments. To filter by file path, check `tool_input.file_path` inside your callback.

214 

215<Tip>

216 **Discovering tool names:** See [Tool Input Types](/en/agent-sdk/typescript#tool-input-types) for the full list of built-in tool names, or add a hook without a matcher to log all tool calls your session makes.

217 

218 **MCP tool naming:** MCP tools always start with `mcp__` followed by the server name and action: `mcp__<server>__<action>`. For example, if you configure a server named `playwright`, its tools will be named `mcp__playwright__browser_screenshot`, `mcp__playwright__browser_click`, etc. The server name comes from the key you use in the `mcpServers` configuration.

219</Tip>

220 

221### Callback functions

222 

223#### Inputs

224 

225Every hook callback receives three arguments:

226 

227* **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#hook-input) and [Python](/en/agent-sdk/python#hook-input) SDK references.

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

229 * `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.

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

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

232 

233#### Outputs

234 

235Your callback returns an object with two categories of fields:

236 

237* **Top-level fields** control the conversation: `systemMessage` injects a message into the conversation visible to the model, and `continue` (`continue_` in Python) determines whether the agent keeps running after this hook.

238* **`hookSpecificOutput`** controls the current operation. The fields inside depend on the hook event type. For `PreToolUse` hooks, this is where you set `permissionDecision` (`"allow"`, `"deny"`, or `"ask"`), `permissionDecisionReason`, and `updatedInput`. For `PostToolUse` hooks, you can set `additionalContext` to append information to the tool result.

239 

240Return `{}` to allow the operation without changes. SDK callback hooks use the same JSON output format as [Claude Code shell command hooks](/en/hooks#json-output), which documents every field and event-specific option. For the SDK type definitions, see the [TypeScript](/en/agent-sdk/typescript#sync-hook-json-output) and [Python](/en/agent-sdk/python#sync-hook-json-output) SDK references.

241 

242<Note>

243 When multiple hooks or permission rules apply, **deny** takes priority over **ask**, which takes priority over **allow**. If any hook returns `deny`, the operation is blocked regardless of other hooks.

244</Note>

245 

246#### Asynchronous output

247 

248By default, the agent waits for your hook to return before proceeding. If your hook performs a side effect (logging, sending a webhook) and doesn't need to influence the agent's behavior, you can return an async output instead. This tells the agent to continue immediately without waiting for the hook to finish:

249 

250<CodeGroup>

251 ```python Python theme={null}

252 async def async_hook(input_data, tool_use_id, context):

253 # Start a background task, then return immediately

254 asyncio.create_task(send_to_logging_service(input_data))

255 return {"async_": True, "asyncTimeout": 30000}

256 ```

257 

258 ```typescript TypeScript theme={null}

259 const asyncHook: HookCallback = async (input, toolUseID, { signal }) => {

260 // Start a background task, then return immediately

261 sendToLoggingService(input).catch(console.error);

262 return { async: true, asyncTimeout: 30000 };

263 };

264 ```

265</CodeGroup>

266 

267| Field | Type | Description |

268| -------------- | -------- | -------------------------------------------------------------------------------------------------------------- |

269| `async` | `true` | Signals async mode. The agent proceeds without waiting. In Python, use `async_` to avoid the reserved keyword. |

270| `asyncTimeout` | `number` | Optional timeout in milliseconds for the background operation |

271 

272<Note>

273 Async outputs cannot block, modify, or inject context into the operation since the agent has already moved on. Use them only for side effects like logging, metrics, or notifications.

274</Note>

275 

276## Examples

277 

278### Modify tool input

279 

280This example intercepts Write tool calls and rewrites the `file_path` argument to prepend `/sandbox`, redirecting all file writes to a sandboxed directory. The callback returns `updatedInput` with the modified path and `permissionDecision: 'allow'` to auto-approve the rewritten operation:

281 

282<CodeGroup>

283 ```python Python theme={null}

284 async def redirect_to_sandbox(input_data, tool_use_id, context):

285 if input_data["hook_event_name"] != "PreToolUse":

286 return {}

287 

288 if input_data["tool_name"] == "Write":

289 original_path = input_data["tool_input"].get("file_path", "")

290 return {

291 "hookSpecificOutput": {

292 "hookEventName": input_data["hook_event_name"],

293 "permissionDecision": "allow",

294 "updatedInput": {

295 **input_data["tool_input"],

296 "file_path": f"/sandbox{original_path}",

297 },

298 }

299 }

300 return {}

301 ```

302 

303 ```typescript TypeScript theme={null}

304 const redirectToSandbox: HookCallback = async (input, toolUseID, { signal }) => {

305 if (input.hook_event_name !== "PreToolUse") return {};

306 

307 const preInput = input as PreToolUseHookInput;

308 const toolInput = preInput.tool_input as Record<string, unknown>;

309 if (preInput.tool_name === "Write") {

310 const originalPath = toolInput.file_path as string;

311 return {

312 hookSpecificOutput: {

313 hookEventName: preInput.hook_event_name,

314 permissionDecision: "allow",

315 updatedInput: {

316 ...toolInput,

317 file_path: `/sandbox${originalPath}`

318 }

319 }

320 };

321 }

322 return {};

323 };

324 ```

325</CodeGroup>

326 

327<Note>

328 When using `updatedInput`, you must also include `permissionDecision: 'allow'`. Always return a new object rather than mutating the original `tool_input`.

329</Note>

330 

331### Add context and block a tool

332 

333This example blocks any attempt to write to the `/etc` directory and uses two output fields together: `permissionDecision: 'deny'` stops the tool call, while `systemMessage` injects a reminder into the conversation so the agent receives context about why the operation was blocked and avoids retrying it:

334 

335<CodeGroup>

336 ```python Python theme={null}

337 async def block_etc_writes(input_data, tool_use_id, context):

338 file_path = input_data["tool_input"].get("file_path", "")

339 

340 if file_path.startswith("/etc"):

341 return {

342 # Top-level field: inject guidance into the conversation

343 "systemMessage": "Remember: system directories like /etc are protected.",

344 # hookSpecificOutput: block the operation

345 "hookSpecificOutput": {

346 "hookEventName": input_data["hook_event_name"],

347 "permissionDecision": "deny",

348 "permissionDecisionReason": "Writing to /etc is not allowed",

349 },

350 }

351 return {}

352 ```

353 

354 ```typescript TypeScript theme={null}

355 const blockEtcWrites: HookCallback = async (input, toolUseID, { signal }) => {

356 const preInput = input as PreToolUseHookInput;

357 const toolInput = preInput.tool_input as Record<string, unknown>;

358 const filePath = toolInput?.file_path as string;

359 

360 if (filePath?.startsWith("/etc")) {

361 return {

362 // Top-level field: inject guidance into the conversation

363 systemMessage: "Remember: system directories like /etc are protected.",

364 // hookSpecificOutput: block the operation

365 hookSpecificOutput: {

366 hookEventName: preInput.hook_event_name,

367 permissionDecision: "deny",

368 permissionDecisionReason: "Writing to /etc is not allowed"

369 }

370 };

371 }

372 return {};

373 };

374 ```

375</CodeGroup>

376 

377### Auto-approve specific tools

378 

379By 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:

380 

381<CodeGroup>

382 ```python Python theme={null}

383 async def auto_approve_read_only(input_data, tool_use_id, context):

384 if input_data["hook_event_name"] != "PreToolUse":

385 return {}

386 

387 read_only_tools = ["Read", "Glob", "Grep"]

388 if input_data["tool_name"] in read_only_tools:

389 return {

390 "hookSpecificOutput": {

391 "hookEventName": input_data["hook_event_name"],

392 "permissionDecision": "allow",

393 "permissionDecisionReason": "Read-only tool auto-approved",

394 }

395 }

396 return {}

397 ```

398 

399 ```typescript TypeScript theme={null}

400 const autoApproveReadOnly: HookCallback = async (input, toolUseID, { signal }) => {

401 if (input.hook_event_name !== "PreToolUse") return {};

402 

403 const preInput = input as PreToolUseHookInput;

404 const readOnlyTools = ["Read", "Glob", "Grep"];

405 if (readOnlyTools.includes(preInput.tool_name)) {

406 return {

407 hookSpecificOutput: {

408 hookEventName: preInput.hook_event_name,

409 permissionDecision: "allow",

410 permissionDecisionReason: "Read-only tool auto-approved"

411 }

412 };

413 }

414 return {};

415 };

416 ```

417</CodeGroup>

418 

419### Chain multiple hooks

420 

421Hooks execute in the order they appear in the array. Keep each hook focused on a single responsibility and chain multiple hooks for complex logic:

422 

423<CodeGroup>

424 ```python Python theme={null}

425 options = ClaudeAgentOptions(

426 hooks={

427 "PreToolUse": [

428 HookMatcher(hooks=[rate_limiter]), # First: check rate limits

429 HookMatcher(hooks=[authorization_check]), # Second: verify permissions

430 HookMatcher(hooks=[input_sanitizer]), # Third: sanitize inputs

431 HookMatcher(hooks=[audit_logger]), # Last: log the action

432 ]

433 }

434 )

435 ```

436 

437 ```typescript TypeScript theme={null}

438 const options = {

439 hooks: {

440 PreToolUse: [

441 { hooks: [rateLimiter] }, // First: check rate limits

442 { hooks: [authorizationCheck] }, // Second: verify permissions

443 { hooks: [inputSanitizer] }, // Third: sanitize inputs

444 { hooks: [auditLogger] } // Last: log the action

445 ]

446 }

447 };

448 ```

449</CodeGroup>

450 

451### Filter with regex matchers

452 

453Use regex patterns to match multiple tools. This example registers three matchers with different scopes: the first triggers `file_security_hook` only for file modification tools, the second triggers `mcp_audit_hook` for any MCP tool (tools whose names start with `mcp__`), and the third triggers `global_logger` for every tool call regardless of name:

454 

455<CodeGroup>

456 ```python Python theme={null}

457 options = ClaudeAgentOptions(

458 hooks={

459 "PreToolUse": [

460 # Match file modification tools

461 HookMatcher(matcher="Write|Edit|Delete", hooks=[file_security_hook]),

462 # Match all MCP tools

463 HookMatcher(matcher="^mcp__", hooks=[mcp_audit_hook]),

464 # Match everything (no matcher)

465 HookMatcher(hooks=[global_logger]),

466 ]

467 }

468 )

469 ```

470 

471 ```typescript TypeScript theme={null}

472 const options = {

473 hooks: {

474 PreToolUse: [

475 // Match file modification tools

476 { matcher: "Write|Edit|Delete", hooks: [fileSecurityHook] },

477 

478 // Match all MCP tools

479 { matcher: "^mcp__", hooks: [mcpAuditHook] },

480 

481 // Match everything (no matcher)

482 { hooks: [globalLogger] }

483 ]

484 }

485 };

486 ```

487</CodeGroup>

488 

489### Track subagent activity

490 

491Use `SubagentStop` hooks to monitor when subagents finish their work. See the full input type in the [TypeScript](/en/agent-sdk/typescript#hook-input) and [Python](/en/agent-sdk/python#hook-input) SDK references. This example logs a summary each time a subagent completes:

492 

493<CodeGroup>

494 ```python Python theme={null}

495 async def subagent_tracker(input_data, tool_use_id, context):

496 # Log subagent details when it finishes

497 print(f"[SUBAGENT] Completed: {input_data['agent_id']}")

498 print(f" Transcript: {input_data['agent_transcript_path']}")

499 print(f" Tool use ID: {tool_use_id}")

500 print(f" Stop hook active: {input_data.get('stop_hook_active')}")

501 return {}

502 

503 

504 options = ClaudeAgentOptions(

505 hooks={"SubagentStop": [HookMatcher(hooks=[subagent_tracker])]}

506 )

507 ```

508 

509 ```typescript TypeScript theme={null}

510 import { HookCallback, SubagentStopHookInput } from "@anthropic-ai/claude-agent-sdk";

511 

512 const subagentTracker: HookCallback = async (input, toolUseID, { signal }) => {

513 // Cast to SubagentStopHookInput to access subagent-specific fields

514 const subInput = input as SubagentStopHookInput;

515 

516 // Log subagent details when it finishes

517 console.log(`[SUBAGENT] Completed: ${subInput.agent_id}`);

518 console.log(` Transcript: ${subInput.agent_transcript_path}`);

519 console.log(` Tool use ID: ${toolUseID}`);

520 console.log(` Stop hook active: ${subInput.stop_hook_active}`);

521 return {};

522 };

523 

524 const options = {

525 hooks: {

526 SubagentStop: [{ hooks: [subagentTracker] }]

527 }

528 };

529 ```

530</CodeGroup>

531 

532### Make HTTP requests from hooks

533 

534Hooks can perform asynchronous operations like HTTP requests. Catch errors inside your hook instead of letting them propagate, since an unhandled exception can interrupt the agent.

535 

536This example sends a webhook after each tool completes, logging which tool ran and when. The hook catches errors so a failed webhook doesn't interrupt the agent:

537 

538<CodeGroup>

539 ```python Python theme={null}

540 import asyncio

541 import json

542 import urllib.request

543 from datetime import datetime

544 

545 

546 def _send_webhook(tool_name):

547 """Synchronous helper that POSTs tool usage data to an external webhook."""

548 data = json.dumps(

549 {

550 "tool": tool_name,

551 "timestamp": datetime.now().isoformat(),

552 }

553 ).encode()

554 req = urllib.request.Request(

555 "https://api.example.com/webhook",

556 data=data,

557 headers={"Content-Type": "application/json"},

558 method="POST",

559 )

560 urllib.request.urlopen(req)

561 

562 

563 async def webhook_notifier(input_data, tool_use_id, context):

564 # Only fire after a tool completes (PostToolUse), not before

565 if input_data["hook_event_name"] != "PostToolUse":

566 return {}

567 

568 try:

569 # Run the blocking HTTP call in a thread to avoid blocking the event loop

570 await asyncio.to_thread(_send_webhook, input_data["tool_name"])

571 except Exception as e:

572 # Log the error but don't raise. A failed webhook shouldn't stop the agent

573 print(f"Webhook request failed: {e}")

574 

575 return {}

576 ```

577 

578 ```typescript TypeScript theme={null}

579 import { query, HookCallback, PostToolUseHookInput } from "@anthropic-ai/claude-agent-sdk";

580 

581 const webhookNotifier: HookCallback = async (input, toolUseID, { signal }) => {

582 // Only fire after a tool completes (PostToolUse), not before

583 if (input.hook_event_name !== "PostToolUse") return {};

584 

585 try {

586 await fetch("https://api.example.com/webhook", {

587 method: "POST",

588 headers: { "Content-Type": "application/json" },

589 body: JSON.stringify({

590 tool: (input as PostToolUseHookInput).tool_name,

591 timestamp: new Date().toISOString()

592 }),

593 // Pass signal so the request cancels if the hook times out

594 signal

595 });

596 } catch (error) {

597 // Handle cancellation separately from other errors

598 if (error instanceof Error && error.name === "AbortError") {

599 console.log("Webhook request cancelled");

600 }

601 // Don't re-throw. A failed webhook shouldn't stop the agent

602 }

603 

604 return {};

605 };

606 

607 // Register as a PostToolUse hook

608 for await (const message of query({

609 prompt: "Refactor the auth module",

610 options: {

611 hooks: {

612 PostToolUse: [{ hooks: [webhookNotifier] }]

613 }

614 }

615 })) {

616 console.log(message);

617 }

618 ```

619</CodeGroup>

620 

621### Forward notifications to Slack

622 

623Use `Notification` hooks to receive system notifications from the agent and forward them to external services. Notifications fire for specific event types: `permission_prompt` (Claude needs permission), `idle_prompt` (Claude is waiting for input), `auth_success` (authentication completed), and `elicitation_dialog` (Claude is prompting the user). Each notification includes a `message` field with a human-readable description and optionally a `title`.

624 

625This example forwards every notification to a Slack channel. It requires a [Slack incoming webhook URL](https://api.slack.com/messaging/webhooks), which you create by adding an app to your Slack workspace and enabling incoming webhooks:

626 

627<CodeGroup>

628 ```python Python theme={null}

629 import asyncio

630 import json

631 import urllib.request

632 

633 from claude_agent_sdk import ClaudeSDKClient, ClaudeAgentOptions, HookMatcher

634 

635 

636 def _send_slack_notification(message):

637 """Synchronous helper that sends a message to Slack via incoming webhook."""

638 data = json.dumps({"text": f"Agent status: {message}"}).encode()

639 req = urllib.request.Request(

640 "https://hooks.slack.com/services/YOUR/WEBHOOK/URL",

641 data=data,

642 headers={"Content-Type": "application/json"},

643 method="POST",

644 )

645 urllib.request.urlopen(req)

646 

647 

648 async def notification_handler(input_data, tool_use_id, context):

649 try:

650 # Run the blocking HTTP call in a thread to avoid blocking the event loop

651 await asyncio.to_thread(_send_slack_notification, input_data.get("message", ""))

652 except Exception as e:

653 print(f"Failed to send notification: {e}")

654 

655 # Return empty object. Notification hooks don't modify agent behavior

656 return {}

657 

658 

659 async def main():

660 options = ClaudeAgentOptions(

661 hooks={

662 # Register the hook for Notification events (no matcher needed)

663 "Notification": [HookMatcher(hooks=[notification_handler])],

664 },

665 )

666 

667 async with ClaudeSDKClient(options=options) as client:

668 await client.query("Analyze this codebase")

669 async for message in client.receive_response():

670 print(message)

671 

672 

673 asyncio.run(main())

674 ```

675 

676 ```typescript TypeScript theme={null}

677 import { query, HookCallback, NotificationHookInput } from "@anthropic-ai/claude-agent-sdk";

678 

679 // Define a hook callback that sends notifications to Slack

680 const notificationHandler: HookCallback = async (input, toolUseID, { signal }) => {

681 // Cast to NotificationHookInput to access the message field

682 const notification = input as NotificationHookInput;

683 

684 try {

685 // POST the notification message to a Slack incoming webhook

686 await fetch("https://hooks.slack.com/services/YOUR/WEBHOOK/URL", {

687 method: "POST",

688 headers: { "Content-Type": "application/json" },

689 body: JSON.stringify({

690 text: `Agent status: ${notification.message}`

691 }),

692 // Pass signal so the request cancels if the hook times out

693 signal

694 });

695 } catch (error) {

696 if (error instanceof Error && error.name === "AbortError") {

697 console.log("Notification cancelled");

698 } else {

699 console.error("Failed to send notification:", error);

700 }

701 }

702 

703 // Return empty object. Notification hooks don't modify agent behavior

704 return {};

705 };

706 

707 // Register the hook for Notification events (no matcher needed)

708 for await (const message of query({

709 prompt: "Analyze this codebase",

710 options: {

711 hooks: {

712 Notification: [{ hooks: [notificationHandler] }]

713 }

714 }

715 })) {

716 console.log(message);

717 }

718 ```

719</CodeGroup>

720 

721## Fix common issues

722 

723### Hook not firing

724 

725* Verify the hook event name is correct and case-sensitive (`PreToolUse`, not `preToolUse`)

726* Check that your matcher pattern matches the tool name exactly

727* Ensure the hook is under the correct event type in `options.hooks`

728* For non-tool hooks like `Stop` and `SubagentStop`, matchers match against different fields (see [matcher patterns](/en/hooks#matcher-patterns))

729* Hooks may not fire when the agent hits the [`max_turns`](/en/agent-sdk/python#claude-agent-options) limit because the session ends before hooks can execute

730 

731### Matcher not filtering as expected

732 

733Matchers only match **tool names**, not file paths or other arguments. To filter by file path, check `tool_input.file_path` inside your hook:

734 

735```typescript theme={null}

736const myHook: HookCallback = async (input, toolUseID, { signal }) => {

737 const preInput = input as PreToolUseHookInput;

738 const toolInput = preInput.tool_input as Record<string, unknown>;

739 const filePath = toolInput?.file_path as string;

740 if (!filePath?.endsWith(".md")) return {}; // Skip non-markdown files

741 // Process markdown files...

742 return {};

743};

744```

745 

746### Hook timeout

747 

748* Increase the `timeout` value in the `HookMatcher` configuration

749* Use the `AbortSignal` from the third callback argument to handle cancellation gracefully in TypeScript

750 

751### Tool blocked unexpectedly

752 

753* Check all `PreToolUse` hooks for `permissionDecision: 'deny'` returns

754* Add logging to your hooks to see what `permissionDecisionReason` they're returning

755* Verify matcher patterns aren't too broad (an empty matcher matches all tools)

756 

757### Modified input not applied

758 

759* Ensure `updatedInput` is inside `hookSpecificOutput`, not at the top level:

760 

761 ```typescript theme={null}

762 return {

763 hookSpecificOutput: {

764 hookEventName: "PreToolUse",

765 permissionDecision: "allow",

766 updatedInput: { command: "new command" }

767 }

768 };

769 ```

770 

771* You must also return `permissionDecision: 'allow'` for the input modification to take effect

772 

773* Include `hookEventName` in `hookSpecificOutput` to identify which hook type the output is for

774 

775### Session hooks not available in Python

776 

777`SessionStart` and `SessionEnd` can be registered as SDK callback hooks in TypeScript, but are not available in the Python SDK (`HookEvent` omits them). In Python, they are only available as [shell command hooks](/en/hooks#hook-events) defined in settings files (for example, `.claude/settings.json`). To load shell command hooks from your SDK application, include the appropriate setting source with [`setting_sources`](/en/agent-sdk/python#setting-source) or [`settingSources`](/en/agent-sdk/typescript#setting-source):

778 

779<CodeGroup>

780 ```python Python theme={null}

781 options = ClaudeAgentOptions(

782 setting_sources=["project"], # Loads .claude/settings.json including hooks

783 )

784 ```

785 

786 ```typescript TypeScript theme={null}

787 const options = {

788 settingSources: ["project"] // Loads .claude/settings.json including hooks

789 };

790 ```

791</CodeGroup>

792 

793To run initialization logic as a Python SDK callback instead, use the first message from `client.receive_response()` as your trigger.

794 

795### Subagent permission prompts multiplying

796 

797When spawning multiple subagents, each one may request permissions separately. Subagents do not automatically inherit parent agent permissions. To avoid repeated prompts, use `PreToolUse` hooks to auto-approve specific tools, or configure permission rules that apply to subagent sessions.

798 

799### Recursive hook loops with subagents

800 

801A `UserPromptSubmit` hook that spawns subagents can create infinite loops if those subagents trigger the same hook. To prevent this:

802 

803* Check for a subagent indicator in the hook input before spawning

804* Use a shared variable or session state to track whether you're already inside a subagent

805* Scope hooks to only run for the top-level agent session

806 

807### systemMessage not appearing in output

808 

809The `systemMessage` field adds context to the conversation that the model sees, but it may not appear in all SDK output modes. If you need to surface hook decisions to your application, log them separately or use a dedicated output channel.

810 

811## Related resources

812 

813* [Claude Code hooks reference](/en/hooks): full JSON input/output schemas, event documentation, and matcher patterns

814* [Claude Code hooks guide](/en/hooks-guide): shell command hook examples and walkthroughs

815* [TypeScript SDK reference](/en/agent-sdk/typescript): hook types, input/output definitions, and configuration options

816* [Python SDK reference](/en/agent-sdk/python): hook types, input/output definitions, and configuration options

817* [Permissions](/en/agent-sdk/permissions): control what your agent can do

818* [Custom tools](/en/agent-sdk/custom-tools): build tools to extend agent capabilities

agent-sdk/hosting.md +142 −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# Hosting the Agent SDK

6 

7> Deploy and host Claude Agent SDK in production environments

8 

9The Claude Agent SDK differs from traditional stateless LLM APIs in that it maintains conversational state and executes commands in a persistent environment. This guide covers the architecture, hosting considerations, and best practices for deploying SDK-based agents in production.

10 

11<Info>

12 For security hardening beyond basic sandboxing (including network controls, credential management, and isolation options), see [Secure Deployment](/en/agent-sdk/secure-deployment).

13</Info>

14 

15## Hosting Requirements

16 

17### Container-Based Sandboxing

18 

19For security and isolation, the SDK should run inside a sandboxed container environment. This provides process isolation, resource limits, network control, and ephemeral filesystems.

20 

21The SDK also supports [programmatic sandbox configuration](/en/agent-sdk/typescript#sandbox-settings) for command execution.

22 

23### System Requirements

24 

25Each SDK instance requires:

26 

27* **Runtime dependencies**

28 * Python 3.10+ (for Python SDK) or Node.js 18+ (for TypeScript SDK)

29 * Node.js (required by the bundled Claude Code CLI that the SDK spawns; both SDK packages include it, so no separate install is needed)

30 

31* **Resource allocation**

32 * Recommended: 1GiB RAM, 5GiB of disk, and 1 CPU (vary this based on your task as needed)

33 

34* **Network access**

35 * Outbound HTTPS to `api.anthropic.com`

36 * Optional: Access to MCP servers or external tools

37 

38## Understanding the SDK Architecture

39 

40Unlike stateless API calls, the Claude Agent SDK operates as a **long-running process** that:

41 

42* **Executes commands** in a persistent shell environment

43* **Manages file operations** within a working directory

44* **Handles tool execution** with context from previous interactions

45 

46## Sandbox Provider Options

47 

48Several providers specialize in secure container environments for AI code execution:

49 

50* **[Modal Sandbox](https://modal.com/docs/guide/sandbox)** - [demo implementation](https://modal.com/docs/examples/claude-slack-gif-creator)

51* **[Cloudflare Sandboxes](https://github.com/cloudflare/sandbox-sdk)**

52* **[Daytona](https://www.daytona.io/)**

53* **[E2B](https://e2b.dev/)**

54* **[Fly Machines](https://fly.io/docs/machines/)**

55* **[Vercel Sandbox](https://vercel.com/docs/functions/sandbox)**

56 

57For self-hosted options (Docker, gVisor, Firecracker) and detailed isolation configuration, see [Isolation Technologies](/en/agent-sdk/secure-deployment#isolation-technologies).

58 

59## Production Deployment Patterns

60 

61### Pattern 1: Ephemeral Sessions

62 

63Create a new container for each user task, then destroy it when complete.

64 

65Best for one-off tasks, the user may still interact with the AI while the task is completing, but once completed the container is destroyed.

66 

67**Examples:**

68 

69* Bug Investigation & Fix: Debug and resolve a specific issue with relevant context

70* Invoice Processing: Extract and structure data from receipts/invoices for accounting systems

71* Translation Tasks: Translate documents or content batches between languages

72* Image/Video Processing: Apply transformations, optimizations, or extract metadata from media files

73 

74### Pattern 2: Long-Running Sessions

75 

76Maintain persistent container instances for long running tasks. Often times running *multiple* Claude Agent processes inside of the container based on demand.

77 

78Best for proactive agents that take action without the users input, agents that serve content or agents that process high amounts of messages.

79 

80**Examples:**

81 

82* Email Agent: Monitors incoming emails and autonomously triages, responds, or takes actions based on content

83* Site Builder: Hosts custom websites per user with live editing capabilities served through container ports

84* High-Frequency Chat Bots: Handles continuous message streams from platforms like Slack where rapid response times are critical

85 

86### Pattern 3: Hybrid Sessions

87 

88Ephemeral containers that are hydrated with history and state, possibly from a database or from the SDK's session resumption features.

89 

90Best for containers with intermittent interaction from the user that kicks off work and spins down when the work is completed but can be continued.

91 

92**Examples:**

93 

94* Personal Project Manager: Helps manage ongoing projects with intermittent check-ins, maintains context of tasks, decisions, and progress

95* Deep Research: Conducts multi-hour research tasks, saves findings and resumes investigation when user returns

96* Customer Support Agent: Handles support tickets that span multiple interactions, loads ticket history and customer context

97 

98### Pattern 4: Single Containers

99 

100Run multiple Claude Agent SDK processes in one global container.

101 

102Best for agents that must collaborate closely together. This is likely the least popular pattern because you will have to prevent agents from overwriting each other.

103 

104**Examples:**

105 

106* **Simulations**: Agents that interact with each other in simulations such as video games.

107 

108## FAQ

109 

110### How do I communicate with my sandboxes?

111 

112When hosting in containers, expose ports to communicate with your SDK instances. Your application can expose HTTP/WebSocket endpoints for external clients while the SDK runs internally within the container.

113 

114### What is the cost of hosting a container?

115 

116The dominant cost of serving agents is the tokens; containers vary based on what you provision, but a minimum cost is roughly 5 cents per hour running.

117 

118### When should I shut down idle containers vs. keeping them warm?

119 

120This is likely provider dependent, different sandbox providers will let you set different criteria for idle timeouts after which a sandbox might spin down.

121You will want to tune this timeout based on how frequent you think user response might be.

122 

123### How often should I update the Claude Code CLI?

124 

125The Claude Code CLI is versioned with semver, so any breaking changes will be versioned.

126 

127### How do I monitor container health and agent performance?

128 

129Since containers are just servers the same logging infrastructure you use for the backend will work for containers.

130 

131### How long can an agent session run before timing out?

132 

133An agent session will not timeout, but consider setting a 'maxTurns' property to prevent Claude from getting stuck in a loop.

134 

135## Next Steps

136 

137* [Secure Deployment](/en/agent-sdk/secure-deployment) - Network controls, credential management, and isolation hardening

138* [TypeScript SDK - Sandbox Settings](/en/agent-sdk/typescript#sandbox-settings) - Configure sandbox programmatically

139* [Sessions Guide](/en/agent-sdk/sessions) - Learn about session management

140* [Permissions](/en/agent-sdk/permissions) - Configure tool permissions

141* [Cost Tracking](/en/agent-sdk/cost-tracking) - Monitor API usage

142* [MCP Integration](/en/agent-sdk/mcp) - Extend with custom tools

agent-sdk/mcp.md +768 −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# Connect to external tools with MCP

6 

7> Configure MCP servers to extend your agent with external tools. Covers transport types, tool search for large tool sets, authentication, and error handling.

8 

9The [Model Context Protocol (MCP)](https://modelcontextprotocol.io/docs/getting-started/intro) is an open standard for connecting AI agents to external tools and data sources. With MCP, your agent can query databases, integrate with APIs like Slack and GitHub, and connect to other services without writing custom tool implementations.

10 

11MCP servers can run as local processes, connect over HTTP, or execute directly within your SDK application.

12 

13## Quickstart

14 

15This example connects to the [Claude Code documentation](https://code.claude.com/docs) MCP server using [HTTP transport](#httpsse-servers) and uses [`allowedTools`](#allow-mcp-tools) with a wildcard to permit all tools from the server.

16 

17<CodeGroup>

18 ```typescript TypeScript theme={null}

19 import { query } from "@anthropic-ai/claude-agent-sdk";

20 

21 for await (const message of query({

22 prompt: "Use the docs MCP server to explain what hooks are in Claude Code",

23 options: {

24 mcpServers: {

25 "claude-code-docs": {

26 type: "http",

27 url: "https://code.claude.com/docs/mcp"

28 }

29 },

30 allowedTools: ["mcp__claude-code-docs__*"]

31 }

32 })) {

33 if (message.type === "result" && message.subtype === "success") {

34 console.log(message.result);

35 }

36 }

37 ```

38 

39 ```python Python theme={null}

40 import asyncio

41 from claude_agent_sdk import query, ClaudeAgentOptions, ResultMessage

42 

43 

44 async def main():

45 options = ClaudeAgentOptions(

46 mcp_servers={

47 "claude-code-docs": {

48 "type": "http",

49 "url": "https://code.claude.com/docs/mcp",

50 }

51 },

52 allowed_tools=["mcp__claude-code-docs__*"],

53 )

54 

55 async for message in query(

56 prompt="Use the docs MCP server to explain what hooks are in Claude Code",

57 options=options,

58 ):

59 if isinstance(message, ResultMessage) and message.subtype == "success":

60 print(message.result)

61 

62 

63 asyncio.run(main())

64 ```

65</CodeGroup>

66 

67The agent connects to the documentation server, searches for information about hooks, and returns the results.

68 

69## Add an MCP server

70 

71You can configure MCP servers in code when calling `query()`, or in a `.mcp.json` file loaded via [`settingSources`](#from-a-config-file).

72 

73### In code

74 

75Pass MCP servers directly in the `mcpServers` option:

76 

77<CodeGroup>

78 ```typescript TypeScript theme={null}

79 import { query } from "@anthropic-ai/claude-agent-sdk";

80 

81 for await (const message of query({

82 prompt: "List files in my project",

83 options: {

84 mcpServers: {

85 filesystem: {

86 command: "npx",

87 args: ["-y", "@modelcontextprotocol/server-filesystem", "/Users/me/projects"]

88 }

89 },

90 allowedTools: ["mcp__filesystem__*"]

91 }

92 })) {

93 if (message.type === "result" && message.subtype === "success") {

94 console.log(message.result);

95 }

96 }

97 ```

98 

99 ```python Python theme={null}

100 import asyncio

101 from claude_agent_sdk import query, ClaudeAgentOptions, ResultMessage

102 

103 

104 async def main():

105 options = ClaudeAgentOptions(

106 mcp_servers={

107 "filesystem": {

108 "command": "npx",

109 "args": [

110 "-y",

111 "@modelcontextprotocol/server-filesystem",

112 "/Users/me/projects",

113 ],

114 }

115 },

116 allowed_tools=["mcp__filesystem__*"],

117 )

118 

119 async for message in query(prompt="List files in my project", options=options):

120 if isinstance(message, ResultMessage) and message.subtype == "success":

121 print(message.result)

122 

123 

124 asyncio.run(main())

125 ```

126</CodeGroup>

127 

128### From a config file

129 

130Create a `.mcp.json` file at your project root. The SDK does not load filesystem settings by default, so set `settingSources: ["project"]` (Python: `setting_sources=["project"]`) in your options for the file to be picked up:

131 

132```json theme={null}

133{

134 "mcpServers": {

135 "filesystem": {

136 "command": "npx",

137 "args": ["-y", "@modelcontextprotocol/server-filesystem", "/Users/me/projects"]

138 }

139 }

140}

141```

142 

143## Allow MCP tools

144 

145MCP tools require explicit permission before Claude can use them. Without permission, Claude will see that tools are available but won't be able to call them.

146 

147### Tool naming convention

148 

149MCP tools follow the naming pattern `mcp__<server-name>__<tool-name>`. For example, a GitHub server named `"github"` with a `list_issues` tool becomes `mcp__github__list_issues`.

150 

151### Grant access with allowedTools

152 

153Use `allowedTools` to specify which MCP tools Claude can use:

154 

155```typescript hidelines={1,-1} theme={null}

156const _ = {

157 options: {

158 mcpServers: {

159 // your servers

160 },

161 allowedTools: [

162 "mcp__github__*", // All tools from the github server

163 "mcp__db__query", // Only the query tool from db server

164 "mcp__slack__send_message" // Only send_message from slack server

165 ]

166 }

167};

168```

169 

170Wildcards (`*`) let you allow all tools from a server without listing each one individually.

171 

172<Note>

173 **Prefer `allowedTools` over permission modes for MCP access.** `permissionMode: "acceptEdits"` does not auto-approve MCP tools (only file edits and filesystem Bash commands). `permissionMode: "bypassPermissions"` does auto-approve MCP tools but also disables all other safety prompts, which is broader than necessary. A wildcard in `allowedTools` grants exactly the MCP server you want and nothing more. See [Permission modes](/en/agent-sdk/permissions#permission-modes) for a full comparison.

174</Note>

175 

176### Discover available tools

177 

178To see what tools an MCP server provides, check the server's documentation or connect to the server and inspect the `system` init message:

179 

180```typescript theme={null}

181for await (const message of query({ prompt: "...", options })) {

182 if (message.type === "system" && message.subtype === "init") {

183 console.log("Available MCP tools:", message.mcp_servers);

184 }

185}

186```

187 

188## Transport types

189 

190MCP servers communicate with your agent using different transport protocols. Check the server's documentation to see which transport it supports:

191 

192* If the docs give you a **command to run** (like `npx @modelcontextprotocol/server-github`), use stdio

193* If the docs give you a **URL**, use HTTP or SSE

194* If you're building your own tools in code, use an SDK MCP server

195 

196### stdio servers

197 

198Local processes that communicate via stdin/stdout. Use this for MCP servers you run on the same machine:

199 

200<Tabs>

201 <Tab title="In code">

202 <CodeGroup>

203 ```typescript TypeScript hidelines={1,-1} theme={null}

204 const _ = {

205 options: {

206 mcpServers: {

207 github: {

208 command: "npx",

209 args: ["-y", "@modelcontextprotocol/server-github"],

210 env: {

211 GITHUB_TOKEN: process.env.GITHUB_TOKEN

212 }

213 }

214 },

215 allowedTools: ["mcp__github__list_issues", "mcp__github__search_issues"]

216 }

217 };

218 ```

219 

220 ```python Python theme={null}

221 options = ClaudeAgentOptions(

222 mcp_servers={

223 "github": {

224 "command": "npx",

225 "args": ["-y", "@modelcontextprotocol/server-github"],

226 "env": {"GITHUB_TOKEN": os.environ["GITHUB_TOKEN"]},

227 }

228 },

229 allowed_tools=["mcp__github__list_issues", "mcp__github__search_issues"],

230 )

231 ```

232 </CodeGroup>

233 </Tab>

234 

235 <Tab title=".mcp.json">

236 ```json theme={null}

237 {

238 "mcpServers": {

239 "github": {

240 "command": "npx",

241 "args": ["-y", "@modelcontextprotocol/server-github"],

242 "env": {

243 "GITHUB_TOKEN": "${GITHUB_TOKEN}"

244 }

245 }

246 }

247 }

248 ```

249 </Tab>

250</Tabs>

251 

252### HTTP/SSE servers

253 

254Use HTTP or SSE for cloud-hosted MCP servers and remote APIs:

255 

256<Tabs>

257 <Tab title="In code">

258 <CodeGroup>

259 ```typescript TypeScript hidelines={1,-1} theme={null}

260 const _ = {

261 options: {

262 mcpServers: {

263 "remote-api": {

264 type: "sse",

265 url: "https://api.example.com/mcp/sse",

266 headers: {

267 Authorization: `Bearer ${process.env.API_TOKEN}`

268 }

269 }

270 },

271 allowedTools: ["mcp__remote-api__*"]

272 }

273 };

274 ```

275 

276 ```python Python theme={null}

277 options = ClaudeAgentOptions(

278 mcp_servers={

279 "remote-api": {

280 "type": "sse",

281 "url": "https://api.example.com/mcp/sse",

282 "headers": {"Authorization": f"Bearer {os.environ['API_TOKEN']}"},

283 }

284 },

285 allowed_tools=["mcp__remote-api__*"],

286 )

287 ```

288 </CodeGroup>

289 </Tab>

290 

291 <Tab title=".mcp.json">

292 ```json theme={null}

293 {

294 "mcpServers": {

295 "remote-api": {

296 "type": "sse",

297 "url": "https://api.example.com/mcp/sse",

298 "headers": {

299 "Authorization": "Bearer ${API_TOKEN}"

300 }

301 }

302 }

303 }

304 ```

305 </Tab>

306</Tabs>

307 

308For HTTP (non-streaming), use `"type": "http"` instead.

309 

310### SDK MCP servers

311 

312Define custom tools directly in your application code instead of running a separate server process. See the [custom tools guide](/en/agent-sdk/custom-tools) for implementation details.

313 

314## MCP tool search

315 

316When you have many MCP tools configured, tool definitions can consume a significant portion of your context window. Tool search solves this by withholding tool definitions from context and loading only the ones Claude needs for each turn.

317 

318Tool search is enabled by default. See [Tool search](/en/agent-sdk/tool-search) for configuration options and details.

319 

320For more detail, including best practices and using tool search with custom SDK tools, see the [tool search guide](/en/agent-sdk/tool-search).

321 

322## Authentication

323 

324Most MCP servers require authentication to access external services. Pass credentials through environment variables in the server configuration.

325 

326### Pass credentials via environment variables

327 

328Use the `env` field to pass API keys, tokens, and other credentials to the MCP server:

329 

330<Tabs>

331 <Tab title="In code">

332 <CodeGroup>

333 ```typescript TypeScript hidelines={1,-1} theme={null}

334 const _ = {

335 options: {

336 mcpServers: {

337 github: {

338 command: "npx",

339 args: ["-y", "@modelcontextprotocol/server-github"],

340 env: {

341 GITHUB_TOKEN: process.env.GITHUB_TOKEN

342 }

343 }

344 },

345 allowedTools: ["mcp__github__list_issues"]

346 }

347 };

348 ```

349 

350 ```python Python theme={null}

351 options = ClaudeAgentOptions(

352 mcp_servers={

353 "github": {

354 "command": "npx",

355 "args": ["-y", "@modelcontextprotocol/server-github"],

356 "env": {"GITHUB_TOKEN": os.environ["GITHUB_TOKEN"]},

357 }

358 },

359 allowed_tools=["mcp__github__list_issues"],

360 )

361 ```

362 </CodeGroup>

363 </Tab>

364 

365 <Tab title=".mcp.json">

366 ```json theme={null}

367 {

368 "mcpServers": {

369 "github": {

370 "command": "npx",

371 "args": ["-y", "@modelcontextprotocol/server-github"],

372 "env": {

373 "GITHUB_TOKEN": "${GITHUB_TOKEN}"

374 }

375 }

376 }

377 }

378 ```

379 

380 The `${GITHUB_TOKEN}` syntax expands environment variables at runtime.

381 </Tab>

382</Tabs>

383 

384See [List issues from a repository](#list-issues-from-a-repository) for a complete working example with debug logging.

385 

386### HTTP headers for remote servers

387 

388For HTTP and SSE servers, pass authentication headers directly in the server configuration:

389 

390<Tabs>

391 <Tab title="In code">

392 <CodeGroup>

393 ```typescript TypeScript hidelines={1,-1} theme={null}

394 const _ = {

395 options: {

396 mcpServers: {

397 "secure-api": {

398 type: "http",

399 url: "https://api.example.com/mcp",

400 headers: {

401 Authorization: `Bearer ${process.env.API_TOKEN}`

402 }

403 }

404 },

405 allowedTools: ["mcp__secure-api__*"]

406 }

407 };

408 ```

409 

410 ```python Python theme={null}

411 options = ClaudeAgentOptions(

412 mcp_servers={

413 "secure-api": {

414 "type": "http",

415 "url": "https://api.example.com/mcp",

416 "headers": {"Authorization": f"Bearer {os.environ['API_TOKEN']}"},

417 }

418 },

419 allowed_tools=["mcp__secure-api__*"],

420 )

421 ```

422 </CodeGroup>

423 </Tab>

424 

425 <Tab title=".mcp.json">

426 ```json theme={null}

427 {

428 "mcpServers": {

429 "secure-api": {

430 "type": "http",

431 "url": "https://api.example.com/mcp",

432 "headers": {

433 "Authorization": "Bearer ${API_TOKEN}"

434 }

435 }

436 }

437 }

438 ```

439 

440 The `${API_TOKEN}` syntax expands environment variables at runtime.

441 </Tab>

442</Tabs>

443 

444### OAuth2 authentication

445 

446The [MCP specification supports OAuth 2.1](https://modelcontextprotocol.io/specification/2025-03-26/basic/authorization) for authorization. The SDK doesn't handle OAuth flows automatically, but you can pass access tokens via headers after completing the OAuth flow in your application:

447 

448<CodeGroup>

449 ```typescript TypeScript theme={null}

450 // After completing OAuth flow in your app

451 const accessToken = await getAccessTokenFromOAuthFlow();

452 

453 const options = {

454 mcpServers: {

455 "oauth-api": {

456 type: "http",

457 url: "https://api.example.com/mcp",

458 headers: {

459 Authorization: `Bearer ${accessToken}`

460 }

461 }

462 },

463 allowedTools: ["mcp__oauth-api__*"]

464 };

465 ```

466 

467 ```python Python theme={null}

468 # After completing OAuth flow in your app

469 access_token = await get_access_token_from_oauth_flow()

470 

471 options = ClaudeAgentOptions(

472 mcp_servers={

473 "oauth-api": {

474 "type": "http",

475 "url": "https://api.example.com/mcp",

476 "headers": {"Authorization": f"Bearer {access_token}"},

477 }

478 },

479 allowed_tools=["mcp__oauth-api__*"],

480 )

481 ```

482</CodeGroup>

483 

484## Examples

485 

486### List issues from a repository

487 

488This example connects to the [GitHub MCP server](https://github.com/modelcontextprotocol/servers/tree/main/src/github) to list recent issues. The example includes debug logging to verify the MCP connection and tool calls.

489 

490Before running, create a [GitHub personal access token](https://github.com/settings/tokens) with `repo` scope and set it as an environment variable:

491 

492```bash theme={null}

493export GITHUB_TOKEN=ghp_xxxxxxxxxxxxxxxxxxxx

494```

495 

496<CodeGroup>

497 ```typescript TypeScript theme={null}

498 import { query } from "@anthropic-ai/claude-agent-sdk";

499 

500 for await (const message of query({

501 prompt: "List the 3 most recent issues in anthropics/claude-code",

502 options: {

503 mcpServers: {

504 github: {

505 command: "npx",

506 args: ["-y", "@modelcontextprotocol/server-github"],

507 env: {

508 GITHUB_TOKEN: process.env.GITHUB_TOKEN

509 }

510 }

511 },

512 allowedTools: ["mcp__github__list_issues"]

513 }

514 })) {

515 // Verify MCP server connected successfully

516 if (message.type === "system" && message.subtype === "init") {

517 console.log("MCP servers:", message.mcp_servers);

518 }

519 

520 // Log when Claude calls an MCP tool

521 if (message.type === "assistant") {

522 for (const block of message.message.content) {

523 if (block.type === "tool_use" && block.name.startsWith("mcp__")) {

524 console.log("MCP tool called:", block.name);

525 }

526 }

527 }

528 

529 // Print the final result

530 if (message.type === "result" && message.subtype === "success") {

531 console.log(message.result);

532 }

533 }

534 ```

535 

536 ```python Python theme={null}

537 import asyncio

538 import os

539 from claude_agent_sdk import (

540 query,

541 ClaudeAgentOptions,

542 ResultMessage,

543 SystemMessage,

544 AssistantMessage,

545 )

546 

547 

548 async def main():

549 options = ClaudeAgentOptions(

550 mcp_servers={

551 "github": {

552 "command": "npx",

553 "args": ["-y", "@modelcontextprotocol/server-github"],

554 "env": {"GITHUB_TOKEN": os.environ["GITHUB_TOKEN"]},

555 }

556 },

557 allowed_tools=["mcp__github__list_issues"],

558 )

559 

560 async for message in query(

561 prompt="List the 3 most recent issues in anthropics/claude-code",

562 options=options,

563 ):

564 # Verify MCP server connected successfully

565 if isinstance(message, SystemMessage) and message.subtype == "init":

566 print("MCP servers:", message.data.get("mcp_servers"))

567 

568 # Log when Claude calls an MCP tool

569 if isinstance(message, AssistantMessage):

570 for block in message.content:

571 if hasattr(block, "name") and block.name.startswith("mcp__"):

572 print("MCP tool called:", block.name)

573 

574 # Print the final result

575 if isinstance(message, ResultMessage) and message.subtype == "success":

576 print(message.result)

577 

578 

579 asyncio.run(main())

580 ```

581</CodeGroup>

582 

583### Query a database

584 

585This example uses the [Postgres MCP server](https://github.com/modelcontextprotocol/servers/tree/main/src/postgres) to query a database. The connection string is passed as an argument to the server. The agent automatically discovers the database schema, writes the SQL query, and returns the results:

586 

587<CodeGroup>

588 ```typescript TypeScript theme={null}

589 import { query } from "@anthropic-ai/claude-agent-sdk";

590 

591 // Connection string from environment variable

592 const connectionString = process.env.DATABASE_URL;

593 

594 for await (const message of query({

595 // Natural language query - Claude writes the SQL

596 prompt: "How many users signed up last week? Break it down by day.",

597 options: {

598 mcpServers: {

599 postgres: {

600 command: "npx",

601 // Pass connection string as argument to the server

602 args: ["-y", "@modelcontextprotocol/server-postgres", connectionString]

603 }

604 },

605 // Allow only read queries, not writes

606 allowedTools: ["mcp__postgres__query"]

607 }

608 })) {

609 if (message.type === "result" && message.subtype === "success") {

610 console.log(message.result);

611 }

612 }

613 ```

614 

615 ```python Python theme={null}

616 import asyncio

617 import os

618 from claude_agent_sdk import query, ClaudeAgentOptions, ResultMessage

619 

620 

621 async def main():

622 # Connection string from environment variable

623 connection_string = os.environ["DATABASE_URL"]

624 

625 options = ClaudeAgentOptions(

626 mcp_servers={

627 "postgres": {

628 "command": "npx",

629 # Pass connection string as argument to the server

630 "args": [

631 "-y",

632 "@modelcontextprotocol/server-postgres",

633 connection_string,

634 ],

635 }

636 },

637 # Allow only read queries, not writes

638 allowed_tools=["mcp__postgres__query"],

639 )

640 

641 # Natural language query - Claude writes the SQL

642 async for message in query(

643 prompt="How many users signed up last week? Break it down by day.",

644 options=options,

645 ):

646 if isinstance(message, ResultMessage) and message.subtype == "success":

647 print(message.result)

648 

649 

650 asyncio.run(main())

651 ```

652</CodeGroup>

653 

654## Error handling

655 

656MCP servers can fail to connect for various reasons: the server process might not be installed, credentials might be invalid, or a remote server might be unreachable.

657 

658The SDK emits a `system` message with subtype `init` at the start of each query. This message includes the connection status for each MCP server. Check the `status` field to detect connection failures before the agent starts working:

659 

660<CodeGroup>

661 ```typescript TypeScript theme={null}

662 import { query } from "@anthropic-ai/claude-agent-sdk";

663 

664 for await (const message of query({

665 prompt: "Process data",

666 options: {

667 mcpServers: {

668 "data-processor": dataServer

669 }

670 }

671 })) {

672 if (message.type === "system" && message.subtype === "init") {

673 const failedServers = message.mcp_servers.filter((s) => s.status !== "connected");

674 

675 if (failedServers.length > 0) {

676 console.warn("Failed to connect:", failedServers);

677 }

678 }

679 

680 if (message.type === "result" && message.subtype === "error_during_execution") {

681 console.error("Execution failed");

682 }

683 }

684 ```

685 

686 ```python Python theme={null}

687 import asyncio

688 from claude_agent_sdk import query, ClaudeAgentOptions, SystemMessage, ResultMessage

689 

690 

691 async def main():

692 options = ClaudeAgentOptions(mcp_servers={"data-processor": data_server})

693 

694 async for message in query(prompt="Process data", options=options):

695 if isinstance(message, SystemMessage) and message.subtype == "init":

696 failed_servers = [

697 s

698 for s in message.data.get("mcp_servers", [])

699 if s.get("status") != "connected"

700 ]

701 

702 if failed_servers:

703 print(f"Failed to connect: {failed_servers}")

704 

705 if (

706 isinstance(message, ResultMessage)

707 and message.subtype == "error_during_execution"

708 ):

709 print("Execution failed")

710 

711 

712 asyncio.run(main())

713 ```

714</CodeGroup>

715 

716## Troubleshooting

717 

718### Server shows "failed" status

719 

720Check the `init` message to see which servers failed to connect:

721 

722```typescript theme={null}

723if (message.type === "system" && message.subtype === "init") {

724 for (const server of message.mcp_servers) {

725 if (server.status === "failed") {

726 console.error(`Server ${server.name} failed to connect`);

727 }

728 }

729}

730```

731 

732Common causes:

733 

734* **Missing environment variables**: Ensure required tokens and credentials are set. For stdio servers, check the `env` field matches what the server expects.

735* **Server not installed**: For `npx` commands, verify the package exists and Node.js is in your PATH.

736* **Invalid connection string**: For database servers, verify the connection string format and that the database is accessible.

737* **Network issues**: For remote HTTP/SSE servers, check the URL is reachable and any firewalls allow the connection.

738 

739### Tools not being called

740 

741If Claude sees tools but doesn't use them, check that you've granted permission with `allowedTools`:

742 

743```typescript hidelines={1,-1} theme={null}

744const _ = {

745 options: {

746 mcpServers: {

747 // your servers

748 },

749 allowedTools: ["mcp__servername__*"] // Required for Claude to use the tools

750 }

751};

752```

753 

754### Connection timeouts

755 

756The MCP SDK has a default timeout of 60 seconds for server connections. If your server takes longer to start, the connection will fail. For servers that need more startup time, consider:

757 

758* Using a lighter-weight server if available

759* Pre-warming the server before starting your agent

760* Checking server logs for slow initialization causes

761 

762## Related resources

763 

764* **[Custom tools guide](/en/agent-sdk/custom-tools)**: Build your own MCP server that runs in-process with your SDK application

765* **[Permissions](/en/agent-sdk/permissions)**: Control which MCP tools your agent can use with `allowedTools` and `disallowedTools`

766* **[TypeScript SDK reference](/en/agent-sdk/typescript)**: Full API reference including MCP configuration options

767* **[Python SDK reference](/en/agent-sdk/python)**: Full API reference including MCP configuration options

768* **[MCP server directory](https://github.com/modelcontextprotocol/servers)**: Browse available MCP servers for databases, APIs, and more

agent-sdk/migration-guide.md +315 −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# Migrate to Claude Agent SDK

6 

7> Guide for migrating the Claude Code TypeScript and Python SDKs to the Claude Agent SDK

8 

9## Overview

10 

11The Claude Code SDK has been renamed to the **Claude Agent SDK** and its documentation has been reorganized. This change reflects the SDK's broader capabilities for building AI agents beyond just coding tasks.

12 

13## What's Changed

14 

15| Aspect | Old | New |

16| :------------------------- | :-------------------------- | :------------------------------- |

17| **Package Name (TS/JS)** | `@anthropic-ai/claude-code` | `@anthropic-ai/claude-agent-sdk` |

18| **Python Package** | `claude-code-sdk` | `claude-agent-sdk` |

19| **Documentation Location** | Claude Code docs | API Guide → Agent SDK section |

20 

21<Note>

22 **Documentation Changes:** The Agent SDK documentation has moved from the Claude Code docs to the API Guide under a dedicated [Agent SDK](/en/agent-sdk/overview) section. The Claude Code docs now focus on the CLI tool and automation features.

23</Note>

24 

25## Migration Steps

26 

27### For TypeScript/JavaScript Projects

28 

29**1. Uninstall the old package:**

30 

31```bash theme={null}

32npm uninstall @anthropic-ai/claude-code

33```

34 

35**2. Install the new package:**

36 

37```bash theme={null}

38npm install @anthropic-ai/claude-agent-sdk

39```

40 

41**3. Update your imports:**

42 

43Change all imports from `@anthropic-ai/claude-code` to `@anthropic-ai/claude-agent-sdk`:

44 

45```typescript theme={null}

46// Before

47import { query, tool, createSdkMcpServer } from "@anthropic-ai/claude-code";

48 

49// After

50import { query, tool, createSdkMcpServer } from "@anthropic-ai/claude-agent-sdk";

51```

52 

53**4. Update package.json dependencies:**

54 

55If you have the package listed in your `package.json`, update it:

56 

57Before:

58 

59```json theme={null}

60{

61 "dependencies": {

62 "@anthropic-ai/claude-code": "^0.0.42"

63 }

64}

65```

66 

67After:

68 

69```json theme={null}

70{

71 "dependencies": {

72 "@anthropic-ai/claude-agent-sdk": "^0.2.0"

73 }

74}

75```

76 

77That's it! No other code changes are required.

78 

79### For Python Projects

80 

81**1. Uninstall the old package:**

82 

83```bash theme={null}

84pip uninstall claude-code-sdk

85```

86 

87**2. Install the new package:**

88 

89```bash theme={null}

90pip install claude-agent-sdk

91```

92 

93**3. Update your imports:**

94 

95Change all imports from `claude_code_sdk` to `claude_agent_sdk`:

96 

97```python theme={null}

98# Before

99from claude_code_sdk import query, ClaudeCodeOptions

100 

101# After

102from claude_agent_sdk import query, ClaudeAgentOptions

103```

104 

105**4. Update type names:**

106 

107Change `ClaudeCodeOptions` to `ClaudeAgentOptions`:

108 

109```python theme={null}

110# Before

111from claude_code_sdk import query, ClaudeCodeOptions

112 

113options = ClaudeCodeOptions(model="claude-opus-4-6")

114 

115# After

116from claude_agent_sdk import query, ClaudeAgentOptions

117 

118options = ClaudeAgentOptions(model="claude-opus-4-6")

119```

120 

121**5. Review [breaking changes](#breaking-changes)**

122 

123Make any code changes needed to complete the migration.

124 

125## Breaking changes

126 

127<Warning>

128 To improve isolation and explicit configuration, Claude Agent SDK v0.1.0 introduces breaking changes for users migrating from Claude Code SDK. Review this section carefully before migrating.

129</Warning>

130 

131### Python: ClaudeCodeOptions renamed to ClaudeAgentOptions

132 

133**What changed:** The Python SDK type `ClaudeCodeOptions` has been renamed to `ClaudeAgentOptions`.

134 

135**Migration:**

136 

137```python theme={null}

138# BEFORE (claude-code-sdk)

139from claude_code_sdk import query, ClaudeCodeOptions

140 

141options = ClaudeCodeOptions(model="claude-opus-4-6", permission_mode="acceptEdits")

142 

143# AFTER (claude-agent-sdk)

144from claude_agent_sdk import query, ClaudeAgentOptions

145 

146options = ClaudeAgentOptions(model="claude-opus-4-6", permission_mode="acceptEdits")

147```

148 

149**Why this changed:** The type name now matches the "Claude Agent SDK" branding and provides consistency across the SDK's naming conventions.

150 

151### System prompt no longer default

152 

153**What changed:** The SDK no longer uses Claude Code's system prompt by default.

154 

155**Migration:**

156 

157<CodeGroup>

158 ```typescript TypeScript theme={null}

159 // BEFORE (v0.0.x) - Used Claude Code's system prompt by default

160 const result = query({ prompt: "Hello" });

161 

162 // AFTER (v0.1.0) - Uses minimal system prompt by default

163 // To get the old behavior, explicitly request Claude Code's preset:

164 const result = query({

165 prompt: "Hello",

166 options: {

167 systemPrompt: { type: "preset", preset: "claude_code" }

168 }

169 });

170 

171 // Or use a custom system prompt:

172 const result = query({

173 prompt: "Hello",

174 options: {

175 systemPrompt: "You are a helpful coding assistant"

176 }

177 });

178 ```

179 

180 ```python Python theme={null}

181 # BEFORE (v0.0.x) - Used Claude Code's system prompt by default

182 async for message in query(prompt="Hello"):

183 print(message)

184 

185 # AFTER (v0.1.0) - Uses minimal system prompt by default

186 # To get the old behavior, explicitly request Claude Code's preset:

187 from claude_agent_sdk import query, ClaudeAgentOptions

188 

189 async for message in query(

190 prompt="Hello",

191 options=ClaudeAgentOptions(

192 system_prompt={"type": "preset", "preset": "claude_code"} # Use the preset

193 ),

194 ):

195 print(message)

196 

197 # Or use a custom system prompt:

198 async for message in query(

199 prompt="Hello",

200 options=ClaudeAgentOptions(system_prompt="You are a helpful coding assistant"),

201 ):

202 print(message)

203 ```

204</CodeGroup>

205 

206**Why this changed:** Provides better control and isolation for SDK applications. You can now build agents with custom behavior without inheriting Claude Code's CLI-focused instructions.

207 

208### Settings Sources No Longer Loaded by Default

209 

210**What changed:** The SDK no longer reads from filesystem settings (CLAUDE.md, settings.json, slash commands, etc.) by default.

211 

212**Migration:**

213 

214<CodeGroup>

215 ```typescript TypeScript theme={null}

216 // BEFORE (v0.0.x) - Loaded all settings automatically

217 const result = query({ prompt: "Hello" });

218 // Would read from:

219 // - ~/.claude/settings.json (user)

220 // - .claude/settings.json (project)

221 // - .claude/settings.local.json (local)

222 // - CLAUDE.md files

223 // - Custom slash commands

224 

225 // AFTER (v0.1.0) - No settings loaded by default

226 // To get the old behavior:

227 const result = query({

228 prompt: "Hello",

229 options: {

230 settingSources: ["user", "project", "local"]

231 }

232 });

233 

234 // Or load only specific sources:

235 const result = query({

236 prompt: "Hello",

237 options: {

238 settingSources: ["project"] // Only project settings

239 }

240 });

241 ```

242 

243 ```python Python theme={null}

244 # BEFORE (v0.0.x) - Loaded all settings automatically

245 async for message in query(prompt="Hello"):

246 print(message)

247 # Would read from:

248 # - ~/.claude/settings.json (user)

249 # - .claude/settings.json (project)

250 # - .claude/settings.local.json (local)

251 # - CLAUDE.md files

252 # - Custom slash commands

253 

254 # AFTER (v0.1.0) - No settings loaded by default

255 # To get the old behavior:

256 from claude_agent_sdk import query, ClaudeAgentOptions

257 

258 async for message in query(

259 prompt="Hello",

260 options=ClaudeAgentOptions(setting_sources=["user", "project", "local"]),

261 ):

262 print(message)

263 

264 # Or load only specific sources:

265 async for message in query(

266 prompt="Hello",

267 options=ClaudeAgentOptions(

268 setting_sources=["project"] # Only project settings

269 ),

270 ):

271 print(message)

272 ```

273</CodeGroup>

274 

275**Why this changed:** Ensures SDK applications have predictable behavior independent of local filesystem configurations. This is especially important for:

276 

277* **CI/CD environments** - Consistent behavior without local customizations

278* **Deployed applications** - No dependency on filesystem settings

279* **Testing** - Isolated test environments

280* **Multi-tenant systems** - Prevent settings leakage between users

281 

282<Note>

283 **Backward compatibility:** If your application relied on filesystem settings (custom slash commands, CLAUDE.md instructions, etc.), add `settingSources: ['user', 'project', 'local']` to your options.

284</Note>

285 

286## Why the Rename?

287 

288The Claude Code SDK was originally designed for coding tasks, but it has evolved into a powerful framework for building all types of AI agents. The new name "Claude Agent SDK" better reflects its capabilities:

289 

290* Building business agents (legal assistants, finance advisors, customer support)

291* Creating specialized coding agents (SRE bots, security reviewers, code review agents)

292* Developing custom agents for any domain with tool use, MCP integration, and more

293 

294## Getting Help

295 

296If you encounter any issues during migration:

297 

298**For TypeScript/JavaScript:**

299 

3001. Check that all imports are updated to use `@anthropic-ai/claude-agent-sdk`

3012. Verify your package.json has the new package name

3023. Run `npm install` to ensure dependencies are updated

303 

304**For Python:**

305 

3061. Check that all imports are updated to use `claude_agent_sdk`

3072. Verify your requirements.txt or pyproject.toml has the new package name

3083. Run `pip install claude-agent-sdk` to ensure the package is installed

309 

310## Next Steps

311 

312* Explore the [Agent SDK Overview](/en/agent-sdk/overview) to learn about available features

313* Check out the [TypeScript SDK Reference](/en/agent-sdk/typescript) for detailed API documentation

314* Review the [Python SDK Reference](/en/agent-sdk/python) for Python-specific documentation

315* Learn about [Custom Tools](/en/agent-sdk/custom-tools) and [MCP Integration](/en/agent-sdk/mcp)

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# Modifying system prompts

6 

7> Learn how to customize Claude's behavior by modifying system prompts using three approaches - output styles, systemPrompt with append, and custom system prompts.

8 

9System prompts define Claude's behavior, capabilities, and response style. The Claude Agent SDK provides three ways to customize system prompts: using output styles (persistent, file-based configurations), appending to Claude Code's prompt, or using a fully custom prompt.

10 

11## Understanding system prompts

12 

13A system prompt is the initial instruction set that shapes how Claude behaves throughout a conversation.

14 

15<Note>

16 **Default behavior:** The Agent SDK uses a **minimal system prompt** by default. It contains only essential tool instructions but omits Claude Code's coding guidelines, response style, and project context. To include the full Claude Code system prompt, specify `systemPrompt: { type: "preset", preset: "claude_code" }` in TypeScript or `system_prompt={"type": "preset", "preset": "claude_code"}` in Python.

17</Note>

18 

19Claude Code's system prompt includes:

20 

21* Tool usage instructions and available tools

22* Code style and formatting guidelines

23* Response tone and verbosity settings

24* Security and safety instructions

25* Context about the current working directory and environment

26 

27## Methods of modification

28 

29### Method 1: CLAUDE.md files (project-level instructions)

30 

31CLAUDE.md files provide project-specific context and instructions that are automatically read by the Agent SDK when it runs in a directory. They serve as persistent "memory" for your project.

32 

33#### How CLAUDE.md works with the SDK

34 

35**Location and discovery:**

36 

37* **Project-level:** `CLAUDE.md` or `.claude/CLAUDE.md` in your working directory

38* **User-level:** `~/.claude/CLAUDE.md` for global instructions across all projects

39 

40**IMPORTANT:** The SDK only reads CLAUDE.md files when you explicitly configure `settingSources` (TypeScript) or `setting_sources` (Python):

41 

42* Include `'project'` to load project-level CLAUDE.md

43* Include `'user'` to load user-level CLAUDE.md (`~/.claude/CLAUDE.md`)

44 

45The `claude_code` system prompt preset does NOT automatically load CLAUDE.md - you must also specify setting sources.

46 

47**Content format:**

48CLAUDE.md files use plain markdown and can contain:

49 

50* Coding guidelines and standards

51* Project-specific context

52* Common commands or workflows

53* API conventions

54* Testing requirements

55 

56#### Example CLAUDE.md

57 

58```markdown theme={null}

59# Project Guidelines

60 

61## Code Style

62 

63- Use TypeScript strict mode

64- Prefer functional components in React

65- Always include JSDoc comments for public APIs

66 

67## Testing

68 

69- Run `npm test` before committing

70- Maintain >80% code coverage

71- Use jest for unit tests, playwright for E2E

72 

73## Commands

74 

75- Build: `npm run build`

76- Dev server: `npm run dev`

77- Type check: `npm run typecheck`

78```

79 

80#### Using CLAUDE.md with the SDK

81 

82<CodeGroup>

83 ```typescript TypeScript theme={null}

84 import { query } from "@anthropic-ai/claude-agent-sdk";

85 

86 // IMPORTANT: You must specify settingSources to load CLAUDE.md

87 // The claude_code preset alone does NOT load CLAUDE.md files

88 const messages = [];

89 

90 for await (const message of query({

91 prompt: "Add a new React component for user profiles",

92 options: {

93 systemPrompt: {

94 type: "preset",

95 preset: "claude_code" // Use Claude Code's system prompt

96 },

97 settingSources: ["project"] // Required to load CLAUDE.md from project

98 }

99 })) {

100 messages.push(message);

101 }

102 

103 // Now Claude has access to your project guidelines from CLAUDE.md

104 ```

105 

106 ```python Python theme={null}

107 from claude_agent_sdk import query, ClaudeAgentOptions

108 

109 # IMPORTANT: You must specify setting_sources to load CLAUDE.md

110 # The claude_code preset alone does NOT load CLAUDE.md files

111 messages = []

112 

113 async for message in query(

114 prompt="Add a new React component for user profiles",

115 options=ClaudeAgentOptions(

116 system_prompt={

117 "type": "preset",

118 "preset": "claude_code", # Use Claude Code's system prompt

119 },

120 setting_sources=["project"], # Required to load CLAUDE.md from project

121 ),

122 ):

123 messages.append(message)

124 

125 # Now Claude has access to your project guidelines from CLAUDE.md

126 ```

127</CodeGroup>

128 

129#### When to use CLAUDE.md

130 

131**Best for:**

132 

133* **Team-shared context** - Guidelines everyone should follow

134* **Project conventions** - Coding standards, file structure, naming patterns

135* **Common commands** - Build, test, deploy commands specific to your project

136* **Long-term memory** - Context that should persist across all sessions

137* **Version-controlled instructions** - Commit to git so the team stays in sync

138 

139**Key characteristics:**

140 

141* ✅ Persistent across all sessions in a project

142* ✅ Shared with team via git

143* ✅ Automatic discovery (no code changes needed)

144* ⚠️ Requires loading settings via `settingSources`

145 

146### Method 2: Output styles (persistent configurations)

147 

148Output styles are saved configurations that modify Claude's system prompt. They're stored as markdown files and can be reused across sessions and projects.

149 

150#### Creating an output style

151 

152<CodeGroup>

153 ```typescript TypeScript theme={null}

154 import { writeFile, mkdir } from "fs/promises";

155 import { join } from "path";

156 import { homedir } from "os";

157 

158 async function createOutputStyle(name: string, description: string, prompt: string) {

159 // User-level: ~/.claude/output-styles

160 // Project-level: .claude/output-styles

161 const outputStylesDir = join(homedir(), ".claude", "output-styles");

162 

163 await mkdir(outputStylesDir, { recursive: true });

164 

165 const content = `---

166 name: ${name}

167 description: ${description}

168 ---

169 

170 ${prompt}`;

171 

172 const filePath = join(outputStylesDir, `${name.toLowerCase().replace(/\s+/g, "-")}.md`);

173 await writeFile(filePath, content, "utf-8");

174 }

175 

176 // Example: Create a code review specialist

177 await createOutputStyle(

178 "Code Reviewer",

179 "Thorough code review assistant",

180 `You are an expert code reviewer.

181 

182 For every code submission:

183 1. Check for bugs and security issues

184 2. Evaluate performance

185 3. Suggest improvements

186 4. Rate code quality (1-10)`

187 );

188 ```

189 

190 ```python Python theme={null}

191 from pathlib import Path

192 

193 

194 async def create_output_style(name: str, description: str, prompt: str):

195 # User-level: ~/.claude/output-styles

196 # Project-level: .claude/output-styles

197 output_styles_dir = Path.home() / ".claude" / "output-styles"

198 

199 output_styles_dir.mkdir(parents=True, exist_ok=True)

200 

201 content = f"""---

202 name: {name}

203 description: {description}

204 ---

205 

206 {prompt}"""

207 

208 file_name = name.lower().replace(" ", "-") + ".md"

209 file_path = output_styles_dir / file_name

210 file_path.write_text(content, encoding="utf-8")

211 

212 

213 # Example: Create a code review specialist

214 await create_output_style(

215 "Code Reviewer",

216 "Thorough code review assistant",

217 """You are an expert code reviewer.

218 

219 For every code submission:

220 1. Check for bugs and security issues

221 2. Evaluate performance

222 3. Suggest improvements

223 4. Rate code quality (1-10)""",

224 )

225 ```

226</CodeGroup>

227 

228#### Using output styles

229 

230Once created, activate output styles via:

231 

232* **CLI**: `/output-style [style-name]`

233* **Settings**: `.claude/settings.local.json`

234* **Create new**: `/output-style:new [description]`

235 

236**Note for SDK users:** Output styles are loaded when you include `settingSources: ['user']` or `settingSources: ['project']` (TypeScript) / `setting_sources=["user"]` or `setting_sources=["project"]` (Python) in your options.

237 

238### Method 3: Using `systemPrompt` with append

239 

240You can use the Claude Code preset with an `append` property to add your custom instructions while preserving all built-in functionality.

241 

242<CodeGroup>

243 ```typescript TypeScript theme={null}

244 import { query } from "@anthropic-ai/claude-agent-sdk";

245 

246 const messages = [];

247 

248 for await (const message of query({

249 prompt: "Help me write a Python function to calculate fibonacci numbers",

250 options: {

251 systemPrompt: {

252 type: "preset",

253 preset: "claude_code",

254 append: "Always include detailed docstrings and type hints in Python code."

255 }

256 }

257 })) {

258 messages.push(message);

259 if (message.type === "assistant") {

260 console.log(message.message.content);

261 }

262 }

263 ```

264 

265 ```python Python theme={null}

266 from claude_agent_sdk import query, ClaudeAgentOptions

267 

268 messages = []

269 

270 async for message in query(

271 prompt="Help me write a Python function to calculate fibonacci numbers",

272 options=ClaudeAgentOptions(

273 system_prompt={

274 "type": "preset",

275 "preset": "claude_code",

276 "append": "Always include detailed docstrings and type hints in Python code.",

277 }

278 ),

279 ):

280 messages.append(message)

281 if message.type == "assistant":

282 print(message.message.content)

283 ```

284</CodeGroup>

285 

286### Method 4: Custom system prompts

287 

288You can provide a custom string as `systemPrompt` to replace the default entirely with your own instructions.

289 

290<CodeGroup>

291 ```typescript TypeScript theme={null}

292 import { query } from "@anthropic-ai/claude-agent-sdk";

293 

294 const customPrompt = `You are a Python coding specialist.

295 Follow these guidelines:

296 - Write clean, well-documented code

297 - Use type hints for all functions

298 - Include comprehensive docstrings

299 - Prefer functional programming patterns when appropriate

300 - Always explain your code choices`;

301 

302 const messages = [];

303 

304 for await (const message of query({

305 prompt: "Create a data processing pipeline",

306 options: {

307 systemPrompt: customPrompt

308 }

309 })) {

310 messages.push(message);

311 if (message.type === "assistant") {

312 console.log(message.message.content);

313 }

314 }

315 ```

316 

317 ```python Python theme={null}

318 from claude_agent_sdk import query, ClaudeAgentOptions

319 

320 custom_prompt = """You are a Python coding specialist.

321 Follow these guidelines:

322 - Write clean, well-documented code

323 - Use type hints for all functions

324 - Include comprehensive docstrings

325 - Prefer functional programming patterns when appropriate

326 - Always explain your code choices"""

327 

328 messages = []

329 

330 async for message in query(

331 prompt="Create a data processing pipeline",

332 options=ClaudeAgentOptions(system_prompt=custom_prompt),

333 ):

334 messages.append(message)

335 if message.type == "assistant":

336 print(message.message.content)

337 ```

338</CodeGroup>

339 

340## Comparison of all four approaches

341 

342| Feature | CLAUDE.md | Output Styles | `systemPrompt` with append | Custom `systemPrompt` |

343| ----------------------- | ---------------- | --------------- | -------------------------- | ---------------------- |

344| **Persistence** | Per-project file | Saved as files | Session only | Session only |

345| **Reusability** | Per-project | Across projects | Code duplication | Code duplication |

346| **Management** | On filesystem | CLI + files | In code | In code |

347| **Default tools** | Preserved | Preserved | Preserved | Lost (unless included) |

348| **Built-in safety** | Maintained | Maintained | Maintained | Must be added |

349| **Environment context** | Automatic | Automatic | Automatic | Must be provided |

350| **Customization level** | Additions only | Replace default | Additions only | Complete control |

351| **Version control** | With project | Yes | With code | With code |

352| **Scope** | Project-specific | User or project | Code session | Code session |

353 

354**Note:** "With append" means using `systemPrompt: { type: "preset", preset: "claude_code", append: "..." }` in TypeScript or `system_prompt={"type": "preset", "preset": "claude_code", "append": "..."}` in Python.

355 

356## Use cases and best practices

357 

358### When to use CLAUDE.md

359 

360**Best for:**

361 

362* Project-specific coding standards and conventions

363* Documenting project structure and architecture

364* Listing common commands (build, test, deploy)

365* Team-shared context that should be version controlled

366* Instructions that apply to all SDK usage in a project

367 

368**Examples:**

369 

370* "All API endpoints should use async/await patterns"

371* "Run `npm run lint:fix` before committing"

372* "Database migrations are in the `migrations/` directory"

373 

374**Important:** To load CLAUDE.md files, you must explicitly set `settingSources: ['project']` (TypeScript) or `setting_sources=["project"]` (Python). The `claude_code` system prompt preset does NOT automatically load CLAUDE.md without this setting.

375 

376### When to use output styles

377 

378**Best for:**

379 

380* Persistent behavior changes across sessions

381* Team-shared configurations

382* Specialized assistants (code reviewer, data scientist, DevOps)

383* Complex prompt modifications that need versioning

384 

385**Examples:**

386 

387* Creating a dedicated SQL optimization assistant

388* Building a security-focused code reviewer

389* Developing a teaching assistant with specific pedagogy

390 

391### When to use `systemPrompt` with append

392 

393**Best for:**

394 

395* Adding specific coding standards or preferences

396* Customizing output formatting

397* Adding domain-specific knowledge

398* Modifying response verbosity

399* Enhancing Claude Code's default behavior without losing tool instructions

400 

401### When to use custom `systemPrompt`

402 

403**Best for:**

404 

405* Complete control over Claude's behavior

406* Specialized single-session tasks

407* Testing new prompt strategies

408* Situations where default tools aren't needed

409* Building specialized agents with unique behavior

410 

411## Combining approaches

412 

413You can combine these methods for maximum flexibility:

414 

415### Example: Output style with session-specific additions

416 

417<CodeGroup>

418 ```typescript TypeScript theme={null}

419 import { query } from "@anthropic-ai/claude-agent-sdk";

420 

421 // Assuming "Code Reviewer" output style is active (via /output-style)

422 // Add session-specific focus areas

423 const messages = [];

424 

425 for await (const message of query({

426 prompt: "Review this authentication module",

427 options: {

428 systemPrompt: {

429 type: "preset",

430 preset: "claude_code",

431 append: `

432 For this review, prioritize:

433 - OAuth 2.0 compliance

434 - Token storage security

435 - Session management

436 `

437 }

438 }

439 })) {

440 messages.push(message);

441 }

442 ```

443 

444 ```python Python theme={null}

445 from claude_agent_sdk import query, ClaudeAgentOptions

446 

447 # Assuming "Code Reviewer" output style is active (via /output-style)

448 # Add session-specific focus areas

449 messages = []

450 

451 async for message in query(

452 prompt="Review this authentication module",

453 options=ClaudeAgentOptions(

454 system_prompt={

455 "type": "preset",

456 "preset": "claude_code",

457 "append": """

458 For this review, prioritize:

459 - OAuth 2.0 compliance

460 - Token storage security

461 - Session management

462 """,

463 }

464 ),

465 ):

466 messages.append(message)

467 ```

468</CodeGroup>

469 

470## See also

471 

472* [Output styles](/en/output-styles) - Complete output styles documentation

473* [TypeScript SDK guide](/en/agent-sdk/typescript) - Complete SDK usage guide

474* [Configuration guide](/en/settings) - General configuration options

agent-sdk/observability.md +205 −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# Observability with OpenTelemetry

6 

7> Export traces, metrics, and events from the Agent SDK to your observability backend using OpenTelemetry.

8 

9When you run agents in production, you need visibility into what they did:

10 

11* which tools they called

12* how long each model request took

13* how many tokens were spent

14* where failures occurred

15 

16The Agent SDK can export this data as OpenTelemetry traces, metrics, and log events to any backend that accepts the OpenTelemetry Protocol (OTLP), such as Honeycomb, Datadog, Grafana, Langfuse, or a self-hosted collector.

17 

18This guide explains how the SDK emits telemetry, how to configure the export, and how to tag and filter the data once it reaches your backend. To read token usage and cost directly from the SDK response stream instead of exporting to a backend, see [Track cost and usage](/en/agent-sdk/cost-tracking).

19 

20## How telemetry flows from the SDK

21 

22The Agent SDK runs the Claude Code CLI as a child process and communicates with it over a local pipe. The CLI has OpenTelemetry instrumentation built in: it records spans around each model request and tool execution, emits metrics for token and cost counters, and emits structured log events for prompts and tool results. The SDK does not produce telemetry of its own. Instead, it passes configuration through to the CLI process, and the CLI exports directly to your collector.

23 

24Configuration is passed as environment variables. By default, the child process inherits your application's environment, so you can configure telemetry in either of two places:

25 

26* **Process environment:** set the variables in your shell, container, or orchestrator before your application starts. Every `query()` call picks them up automatically with no code change. This is the recommended approach for production deployments.

27* **Per-call options:** set the variables in `ClaudeAgentOptions.env` (Python) or `options.env` (TypeScript). Use this when different agents in the same process need different telemetry settings. In Python, `env` is merged on top of the inherited environment. In TypeScript, `env` replaces the inherited environment entirely, so include `...process.env` in the object you pass.

28 

29The CLI exports three independent OpenTelemetry signals. Each has its own enable switch and its own exporter, so you can turn on only the ones you need.

30 

31| Signal | What it contains | Enable with |

32| ---------- | --------------------------------------------------------------------------- | ------------------------------------------------------------------- |

33| Metrics | Counters for tokens, cost, sessions, lines of code, and tool decisions | `OTEL_METRICS_EXPORTER` |

34| Log events | Structured records for each prompt, API request, API error, and tool result | `OTEL_LOGS_EXPORTER` |

35| Traces | Spans for each interaction, model request, tool call, and hook (beta) | `OTEL_TRACES_EXPORTER` plus `CLAUDE_CODE_ENHANCED_TELEMETRY_BETA=1` |

36 

37For the complete list of metric names, event names, and attributes, see the Claude Code [Monitoring](/en/monitoring-usage) reference. The Agent SDK emits the same data because it runs the same CLI. Span names are listed in [Read agent traces](#read-agent-traces) below.

38 

39## Enable telemetry export

40 

41Telemetry is off until you set `CLAUDE_CODE_ENABLE_TELEMETRY=1` and choose at least one exporter. The most common configuration sends all three signals over OTLP HTTP to a collector.

42 

43The following example sets the variables in a dictionary and passes them through `options.env`. The agent runs a single task, and the CLI exports spans, metrics, and events to the collector at `collector.example.com` while the loop consumes the response stream:

44 

45<CodeGroup>

46 ```python Python theme={null}

47 import asyncio

48 from claude_agent_sdk import query, ClaudeAgentOptions

49 

50 OTEL_ENV = {

51 "CLAUDE_CODE_ENABLE_TELEMETRY": "1",

52 # Required for traces, which are in beta. Metrics and log events do not need this.

53 "CLAUDE_CODE_ENHANCED_TELEMETRY_BETA": "1",

54 # Choose an exporter per signal. Use otlp for the SDK; see the Note below.

55 "OTEL_TRACES_EXPORTER": "otlp",

56 "OTEL_METRICS_EXPORTER": "otlp",

57 "OTEL_LOGS_EXPORTER": "otlp",

58 # Standard OTLP transport configuration.

59 "OTEL_EXPORTER_OTLP_PROTOCOL": "http/protobuf",

60 "OTEL_EXPORTER_OTLP_ENDPOINT": "http://collector.example.com:4318",

61 "OTEL_EXPORTER_OTLP_HEADERS": "Authorization=Bearer your-token",

62 }

63 

64 

65 async def main():

66 options = ClaudeAgentOptions(env=OTEL_ENV)

67 async for message in query(

68 prompt="List the files in this directory", options=options

69 ):

70 print(message)

71 

72 

73 asyncio.run(main())

74 ```

75 

76 ```typescript TypeScript theme={null}

77 import { query } from "@anthropic-ai/claude-agent-sdk";

78 

79 const otelEnv = {

80 CLAUDE_CODE_ENABLE_TELEMETRY: "1",

81 // Required for traces, which are in beta. Metrics and log events do not need this.

82 CLAUDE_CODE_ENHANCED_TELEMETRY_BETA: "1",

83 // Choose an exporter per signal. Use otlp for the SDK; see the Note below.

84 OTEL_TRACES_EXPORTER: "otlp",

85 OTEL_METRICS_EXPORTER: "otlp",

86 OTEL_LOGS_EXPORTER: "otlp",

87 // Standard OTLP transport configuration.

88 OTEL_EXPORTER_OTLP_PROTOCOL: "http/protobuf",

89 OTEL_EXPORTER_OTLP_ENDPOINT: "http://collector.example.com:4318",

90 OTEL_EXPORTER_OTLP_HEADERS: "Authorization=Bearer your-token",

91 };

92 

93 for await (const message of query({

94 prompt: "List the files in this directory",

95 // env replaces the inherited environment in TypeScript, so spread

96 // process.env first to keep PATH, ANTHROPIC_API_KEY, and other variables.

97 options: { env: { ...process.env, ...otelEnv } },

98 })) {

99 console.log(message);

100 }

101 ```

102</CodeGroup>

103 

104Because the child process inherits your application's environment by default, you can achieve the same result by exporting these variables in a Dockerfile, Kubernetes manifest, or shell profile and omitting `options.env` entirely.

105 

106<Note>

107 The `console` exporter writes telemetry to standard output, which the SDK uses

108 as its message channel. Do not set `console` as an exporter value when running

109 through the SDK. To inspect telemetry locally, point

110 `OTEL_EXPORTER_OTLP_ENDPOINT` at a local collector or an all-in-one Jaeger

111 container instead.

112</Note>

113 

114### Flush telemetry from short-lived calls

115 

116The CLI batches telemetry and exports on an interval. It flushes any pending data when the process exits cleanly, so a `query()` call that completes normally does not lose spans. However, if your process is killed before the CLI shuts down, anything still in the batch buffer is lost. Lowering the export intervals reduces that window.

117 

118By default, metrics export every 60 seconds and traces and logs export every 5 seconds. The following example shortens all three intervals so that data reaches the collector while a short task is still running:

119 

120<CodeGroup>

121 ```python Python theme={null}

122 OTEL_ENV = {

123 # ... exporter configuration from the previous example ...

124 "OTEL_METRIC_EXPORT_INTERVAL": "1000",

125 "OTEL_LOGS_EXPORT_INTERVAL": "1000",

126 "OTEL_TRACES_EXPORT_INTERVAL": "1000",

127 }

128 ```

129 

130 ```typescript TypeScript theme={null}

131 const otelEnv = {

132 // ... exporter configuration from the previous example ...

133 OTEL_METRIC_EXPORT_INTERVAL: "1000",

134 OTEL_LOGS_EXPORT_INTERVAL: "1000",

135 OTEL_TRACES_EXPORT_INTERVAL: "1000",

136 };

137 ```

138</CodeGroup>

139 

140## Read agent traces

141 

142Traces give you the most detailed view of an agent run. With `CLAUDE_CODE_ENHANCED_TELEMETRY_BETA=1` set, each step of the agent loop becomes a span you can inspect in your tracing backend:

143 

144* **`claude_code.interaction`:** wraps a single turn of the agent loop, from receiving a prompt to producing a response.

145* **`claude_code.llm_request`:** wraps each call to the Claude API, with model name, latency, and token counts as attributes.

146* **`claude_code.tool`:** wraps each tool invocation, with child spans for the permission wait (`claude_code.tool.blocked_on_user`) and the execution itself (`claude_code.tool.execution`).

147* **`claude_code.hook`:** wraps each [hook](/en/agent-sdk/hooks) execution.

148 

149Every span carries a `session.id` attribute. When you make several `query()` calls against the same [session](/en/agent-sdk/sessions), filter on `session.id` in your backend to see them as one timeline.

150 

151<Note>

152 Tracing is in beta. Span names and attributes may change between releases. See

153 [Traces (beta)](/en/monitoring-usage#traces-beta) in the Monitoring reference

154 for the trace exporter configuration variables.

155</Note>

156 

157## Tag telemetry from your agent

158 

159By default, the CLI reports `service.name` as `claude-code`. If you run several agents, or run the SDK alongside other services that export to the same collector, override the service name and add resource attributes so you can filter by agent in your backend.

160 

161The following example renames the service and attaches deployment metadata. These values are applied as OpenTelemetry resource attributes on every span, metric, and event the agent emits:

162 

163<CodeGroup>

164 ```python Python theme={null}

165 options = ClaudeAgentOptions(

166 env={

167 # ... exporter configuration ...

168 "OTEL_SERVICE_NAME": "support-triage-agent",

169 "OTEL_RESOURCE_ATTRIBUTES": "service.version=1.4.0,deployment.environment=production",

170 },

171 )

172 ```

173 

174 ```typescript TypeScript theme={null}

175 const options = {

176 env: {

177 ...process.env,

178 // ... exporter configuration ...

179 OTEL_SERVICE_NAME: "support-triage-agent",

180 OTEL_RESOURCE_ATTRIBUTES:

181 "service.version=1.4.0,deployment.environment=production",

182 },

183 };

184 ```

185</CodeGroup>

186 

187## Control sensitive data in exports

188 

189Telemetry is structural by default. Token counts, durations, model names, and tool names are always recorded, but the content your agent reads and writes is not. Three opt-in variables add content to the exported data:

190 

191| Variable | Adds |

192| ------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ |

193| `OTEL_LOG_USER_PROMPTS=1` | Prompt text on `claude_code.user_prompt` events and on the `claude_code.interaction` span |

194| `OTEL_LOG_TOOL_DETAILS=1` | Tool input arguments (file paths, shell commands, search patterns) on `claude_code.tool_result` events |

195| `OTEL_LOG_TOOL_CONTENT=1` | Full tool input and output bodies as span events on `claude_code.tool`, truncated at 60 KB. Requires [tracing](#read-agent-traces) to be enabled |

196 

197Leave these unset unless your observability pipeline is approved to store the data your agent handles. See [Security and privacy](/en/monitoring-usage#security-and-privacy) in the Monitoring reference for the full list of attributes and redaction behavior.

198 

199## Related documentation

200 

201These guides cover adjacent topics for monitoring and deploying agents:

202 

203* [Track cost and usage](/en/agent-sdk/cost-tracking): read token and cost data from the message stream without an external backend.

204* [Hosting the Agent SDK](/en/agent-sdk/hosting): deploy agents in containers where you can set OpenTelemetry variables at the environment level.

205* [Monitoring](/en/monitoring-usage): the complete reference for every environment variable, metric, and event the CLI emits.

agent-sdk/overview.md +583 −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# Agent SDK overview

6 

7> Build production AI agents with Claude Code as a library

8 

9<Note>

10 The Claude Code SDK has been renamed to the Claude Agent SDK. If you're migrating from the old SDK, see the [Migration Guide](/en/agent-sdk/migration-guide).

11</Note>

12 

13Build AI agents that autonomously read files, run commands, search the web, edit code, and more. The Agent SDK gives you the same tools, agent loop, and context management that power Claude Code, programmable in Python and TypeScript.

14 

15<CodeGroup>

16 ```python Python theme={null}

17 import asyncio

18 from claude_agent_sdk import query, ClaudeAgentOptions

19 

20 

21 async def main():

22 async for message in query(

23 prompt="Find and fix the bug in auth.py",

24 options=ClaudeAgentOptions(allowed_tools=["Read", "Edit", "Bash"]),

25 ):

26 print(message) # Claude reads the file, finds the bug, edits it

27 

28 

29 asyncio.run(main())

30 ```

31 

32 ```typescript TypeScript theme={null}

33 import { query } from "@anthropic-ai/claude-agent-sdk";

34 

35 for await (const message of query({

36 prompt: "Find and fix the bug in auth.py",

37 options: { allowedTools: ["Read", "Edit", "Bash"] }

38 })) {

39 console.log(message); // Claude reads the file, finds the bug, edits it

40 }

41 ```

42</CodeGroup>

43 

44The Agent SDK includes built-in tools for reading files, running commands, and editing code, so your agent can start working immediately without you implementing tool execution. Dive into the quickstart or explore real agents built with the SDK:

45 

46<CardGroup cols={2}>

47 <Card title="Quickstart" icon="play" href="/en/agent-sdk/quickstart">

48 Build a bug-fixing agent in minutes

49 </Card>

50 

51 <Card title="Example agents" icon="star" href="https://github.com/anthropics/claude-agent-sdk-demos">

52 Email assistant, research agent, and more

53 </Card>

54</CardGroup>

55 

56## Get started

57 

58<Steps>

59 <Step title="Install the SDK">

60 <Tabs>

61 <Tab title="TypeScript">

62 ```bash theme={null}

63 npm install @anthropic-ai/claude-agent-sdk

64 ```

65 </Tab>

66 

67 <Tab title="Python">

68 ```bash theme={null}

69 pip install claude-agent-sdk

70 ```

71 </Tab>

72 </Tabs>

73 </Step>

74 

75 <Step title="Set your API key">

76 Get an API key from the [Console](https://platform.claude.com/), then set it as an environment variable:

77 

78 ```bash theme={null}

79 export ANTHROPIC_API_KEY=your-api-key

80 ```

81 

82 The SDK also supports authentication via third-party API providers:

83 

84 * **Amazon Bedrock**: set `CLAUDE_CODE_USE_BEDROCK=1` environment variable and configure AWS credentials

85 * **Google Vertex AI**: set `CLAUDE_CODE_USE_VERTEX=1` environment variable and configure Google Cloud credentials

86 * **Microsoft Azure**: set `CLAUDE_CODE_USE_FOUNDRY=1` environment variable and configure Azure credentials

87 

88 See the setup guides for [Bedrock](/en/amazon-bedrock), [Vertex AI](/en/google-vertex-ai), or [Azure AI Foundry](/en/microsoft-foundry) for details.

89 

90 <Note>

91 Unless previously approved, Anthropic does not allow third party developers to offer claude.ai login or rate limits for their products, including agents built on the Claude Agent SDK. Please use the API key authentication methods described in this document instead.

92 </Note>

93 </Step>

94 

95 <Step title="Run your first agent">

96 This example creates an agent that lists files in your current directory using built-in tools.

97 

98 <CodeGroup>

99 ```python Python theme={null}

100 import asyncio

101 from claude_agent_sdk import query, ClaudeAgentOptions

102 

103 

104 async def main():

105 async for message in query(

106 prompt="What files are in this directory?",

107 options=ClaudeAgentOptions(allowed_tools=["Bash", "Glob"]),

108 ):

109 if hasattr(message, "result"):

110 print(message.result)

111 

112 

113 asyncio.run(main())

114 ```

115 

116 ```typescript TypeScript theme={null}

117 import { query } from "@anthropic-ai/claude-agent-sdk";

118 

119 for await (const message of query({

120 prompt: "What files are in this directory?",

121 options: { allowedTools: ["Bash", "Glob"] }

122 })) {

123 if ("result" in message) console.log(message.result);

124 }

125 ```

126 </CodeGroup>

127 </Step>

128</Steps>

129 

130**Ready to build?** Follow the [Quickstart](/en/agent-sdk/quickstart) to create an agent that finds and fixes bugs in minutes.

131 

132## Capabilities

133 

134Everything that makes Claude Code powerful is available in the SDK:

135 

136<Tabs>

137 <Tab title="Built-in tools">

138 Your agent can read files, run commands, and search codebases out of the box. Key tools include:

139 

140 | Tool | What it does |

141 | --------------------------------------------------------------------------- | -------------------------------------------------------------- |

142 | **Read** | Read any file in the working directory |

143 | **Write** | Create new files |

144 | **Edit** | Make precise edits to existing files |

145 | **Bash** | Run terminal commands, scripts, git operations |

146 | **Glob** | Find files by pattern (`**/*.ts`, `src/**/*.py`) |

147 | **Grep** | Search file contents with regex |

148 | **WebSearch** | Search the web for current information |

149 | **WebFetch** | Fetch and parse web page content |

150 | **[AskUserQuestion](/en/agent-sdk/user-input#handle-clarifying-questions)** | Ask the user clarifying questions with multiple choice options |

151 

152 This example creates an agent that searches your codebase for TODO comments:

153 

154 <CodeGroup>

155 ```python Python theme={null}

156 import asyncio

157 from claude_agent_sdk import query, ClaudeAgentOptions

158 

159 

160 async def main():

161 async for message in query(

162 prompt="Find all TODO comments and create a summary",

163 options=ClaudeAgentOptions(allowed_tools=["Read", "Glob", "Grep"]),

164 ):

165 if hasattr(message, "result"):

166 print(message.result)

167 

168 

169 asyncio.run(main())

170 ```

171 

172 ```typescript TypeScript theme={null}

173 import { query } from "@anthropic-ai/claude-agent-sdk";

174 

175 for await (const message of query({

176 prompt: "Find all TODO comments and create a summary",

177 options: { allowedTools: ["Read", "Glob", "Grep"] }

178 })) {

179 if ("result" in message) console.log(message.result);

180 }

181 ```

182 </CodeGroup>

183 </Tab>

184 

185 <Tab title="Hooks">

186 Run custom code at key points in the agent lifecycle. SDK hooks use callback functions to validate, log, block, or transform agent behavior.

187 

188 **Available hooks:** `PreToolUse`, `PostToolUse`, `Stop`, `SessionStart`, `SessionEnd`, `UserPromptSubmit`, and more.

189 

190 This example logs all file changes to an audit file:

191 

192 <CodeGroup>

193 ```python Python theme={null}

194 import asyncio

195 from datetime import datetime

196 from claude_agent_sdk import query, ClaudeAgentOptions, HookMatcher

197 

198 

199 async def log_file_change(input_data, tool_use_id, context):

200 file_path = input_data.get("tool_input", {}).get("file_path", "unknown")

201 with open("./audit.log", "a") as f:

202 f.write(f"{datetime.now()}: modified {file_path}\n")

203 return {}

204 

205 

206 async def main():

207 async for message in query(

208 prompt="Refactor utils.py to improve readability",

209 options=ClaudeAgentOptions(

210 permission_mode="acceptEdits",

211 hooks={

212 "PostToolUse": [

213 HookMatcher(matcher="Edit|Write", hooks=[log_file_change])

214 ]

215 },

216 ),

217 ):

218 if hasattr(message, "result"):

219 print(message.result)

220 

221 

222 asyncio.run(main())

223 ```

224 

225 ```typescript TypeScript theme={null}

226 import { query, HookCallback } from "@anthropic-ai/claude-agent-sdk";

227 import { appendFile } from "fs/promises";

228 

229 const logFileChange: HookCallback = async (input) => {

230 const filePath = (input as any).tool_input?.file_path ?? "unknown";

231 await appendFile("./audit.log", `${new Date().toISOString()}: modified ${filePath}\n`);

232 return {};

233 };

234 

235 for await (const message of query({

236 prompt: "Refactor utils.py to improve readability",

237 options: {

238 permissionMode: "acceptEdits",

239 hooks: {

240 PostToolUse: [{ matcher: "Edit|Write", hooks: [logFileChange] }]

241 }

242 }

243 })) {

244 if ("result" in message) console.log(message.result);

245 }

246 ```

247 </CodeGroup>

248 

249 [Learn more about hooks →](/en/agent-sdk/hooks)

250 </Tab>

251 

252 <Tab title="Subagents">

253 Spawn specialized agents to handle focused subtasks. Your main agent delegates work, and subagents report back with results.

254 

255 Define custom agents with specialized instructions. Include `Agent` in `allowedTools` since subagents are invoked via the Agent tool:

256 

257 <CodeGroup>

258 ```python Python theme={null}

259 import asyncio

260 from claude_agent_sdk import query, ClaudeAgentOptions, AgentDefinition

261 

262 

263 async def main():

264 async for message in query(

265 prompt="Use the code-reviewer agent to review this codebase",

266 options=ClaudeAgentOptions(

267 allowed_tools=["Read", "Glob", "Grep", "Agent"],

268 agents={

269 "code-reviewer": AgentDefinition(

270 description="Expert code reviewer for quality and security reviews.",

271 prompt="Analyze code quality and suggest improvements.",

272 tools=["Read", "Glob", "Grep"],

273 )

274 },

275 ),

276 ):

277 if hasattr(message, "result"):

278 print(message.result)

279 

280 

281 asyncio.run(main())

282 ```

283 

284 ```typescript TypeScript theme={null}

285 import { query } from "@anthropic-ai/claude-agent-sdk";

286 

287 for await (const message of query({

288 prompt: "Use the code-reviewer agent to review this codebase",

289 options: {

290 allowedTools: ["Read", "Glob", "Grep", "Agent"],

291 agents: {

292 "code-reviewer": {

293 description: "Expert code reviewer for quality and security reviews.",

294 prompt: "Analyze code quality and suggest improvements.",

295 tools: ["Read", "Glob", "Grep"]

296 }

297 }

298 }

299 })) {

300 if ("result" in message) console.log(message.result);

301 }

302 ```

303 </CodeGroup>

304 

305 Messages from within a subagent's context include a `parent_tool_use_id` field, letting you track which messages belong to which subagent execution.

306 

307 [Learn more about subagents →](/en/agent-sdk/subagents)

308 </Tab>

309 

310 <Tab title="MCP">

311 Connect to external systems via the Model Context Protocol: databases, browsers, APIs, and [hundreds more](https://github.com/modelcontextprotocol/servers).

312 

313 This example connects the [Playwright MCP server](https://github.com/microsoft/playwright-mcp) to give your agent browser automation capabilities:

314 

315 <CodeGroup>

316 ```python Python theme={null}

317 import asyncio

318 from claude_agent_sdk import query, ClaudeAgentOptions

319 

320 

321 async def main():

322 async for message in query(

323 prompt="Open example.com and describe what you see",

324 options=ClaudeAgentOptions(

325 mcp_servers={

326 "playwright": {"command": "npx", "args": ["@playwright/mcp@latest"]}

327 }

328 ),

329 ):

330 if hasattr(message, "result"):

331 print(message.result)

332 

333 

334 asyncio.run(main())

335 ```

336 

337 ```typescript TypeScript theme={null}

338 import { query } from "@anthropic-ai/claude-agent-sdk";

339 

340 for await (const message of query({

341 prompt: "Open example.com and describe what you see",

342 options: {

343 mcpServers: {

344 playwright: { command: "npx", args: ["@playwright/mcp@latest"] }

345 }

346 }

347 })) {

348 if ("result" in message) console.log(message.result);

349 }

350 ```

351 </CodeGroup>

352 

353 [Learn more about MCP →](/en/agent-sdk/mcp)

354 </Tab>

355 

356 <Tab title="Permissions">

357 Control exactly which tools your agent can use. Allow safe operations, block dangerous ones, or require approval for sensitive actions.

358 

359 <Note>

360 For interactive approval prompts and the `AskUserQuestion` tool, see [Handle approvals and user input](/en/agent-sdk/user-input).

361 </Note>

362 

363 This example creates a read-only agent that can analyze but not modify code. `allowed_tools` pre-approves `Read`, `Glob`, and `Grep`.

364 

365 <CodeGroup>

366 ```python Python theme={null}

367 import asyncio

368 from claude_agent_sdk import query, ClaudeAgentOptions

369 

370 

371 async def main():

372 async for message in query(

373 prompt="Review this code for best practices",

374 options=ClaudeAgentOptions(

375 allowed_tools=["Read", "Glob", "Grep"],

376 ),

377 ):

378 if hasattr(message, "result"):

379 print(message.result)

380 

381 

382 asyncio.run(main())

383 ```

384 

385 ```typescript TypeScript theme={null}

386 import { query } from "@anthropic-ai/claude-agent-sdk";

387 

388 for await (const message of query({

389 prompt: "Review this code for best practices",

390 options: {

391 allowedTools: ["Read", "Glob", "Grep"]

392 }

393 })) {

394 if ("result" in message) console.log(message.result);

395 }

396 ```

397 </CodeGroup>

398 

399 [Learn more about permissions →](/en/agent-sdk/permissions)

400 </Tab>

401 

402 <Tab title="Sessions">

403 Maintain context across multiple exchanges. Claude remembers files read, analysis done, and conversation history. Resume sessions later, or fork them to explore different approaches.

404 

405 This example captures the session ID from the first query, then resumes to continue with full context:

406 

407 <CodeGroup>

408 ```python Python theme={null}

409 import asyncio

410 from claude_agent_sdk import query, ClaudeAgentOptions, SystemMessage, ResultMessage

411 

412 

413 async def main():

414 session_id = None

415 

416 # First query: capture the session ID

417 async for message in query(

418 prompt="Read the authentication module",

419 options=ClaudeAgentOptions(allowed_tools=["Read", "Glob"]),

420 ):

421 if isinstance(message, SystemMessage) and message.subtype == "init":

422 session_id = message.data["session_id"]

423 

424 # Resume with full context from the first query

425 async for message in query(

426 prompt="Now find all places that call it", # "it" = auth module

427 options=ClaudeAgentOptions(resume=session_id),

428 ):

429 if isinstance(message, ResultMessage):

430 print(message.result)

431 

432 

433 asyncio.run(main())

434 ```

435 

436 ```typescript TypeScript theme={null}

437 import { query } from "@anthropic-ai/claude-agent-sdk";

438 

439 let sessionId: string | undefined;

440 

441 // First query: capture the session ID

442 for await (const message of query({

443 prompt: "Read the authentication module",

444 options: { allowedTools: ["Read", "Glob"] }

445 })) {

446 if (message.type === "system" && message.subtype === "init") {

447 sessionId = message.session_id;

448 }

449 }

450 

451 // Resume with full context from the first query

452 for await (const message of query({

453 prompt: "Now find all places that call it", // "it" = auth module

454 options: { resume: sessionId }

455 })) {

456 if ("result" in message) console.log(message.result);

457 }

458 ```

459 </CodeGroup>

460 

461 [Learn more about sessions →](/en/agent-sdk/sessions)

462 </Tab>

463</Tabs>

464 

465### Claude Code features

466 

467The SDK also supports Claude Code's filesystem-based configuration. To use these features, set `setting_sources=["project"]` (Python) or `settingSources: ['project']` (TypeScript) in your options.

468 

469| Feature | Description | Location |

470| ------------------------------------------------ | ---------------------------------------------------- | ---------------------------------- |

471| [Skills](/en/agent-sdk/skills) | Specialized capabilities defined in Markdown | `.claude/skills/*/SKILL.md` |

472| [Slash commands](/en/agent-sdk/slash-commands) | Custom commands for common tasks | `.claude/commands/*.md` |

473| [Memory](/en/agent-sdk/modifying-system-prompts) | Project context and instructions | `CLAUDE.md` or `.claude/CLAUDE.md` |

474| [Plugins](/en/agent-sdk/plugins) | Extend with custom commands, agents, and MCP servers | Programmatic via `plugins` option |

475 

476## Compare the Agent SDK to other Claude tools

477 

478The Claude Platform offers multiple ways to build with Claude. Here's how the Agent SDK fits in:

479 

480<Tabs>

481 <Tab title="Agent SDK vs Client SDK">

482 The [Anthropic Client SDK](https://platform.claude.com/docs/en/api/client-sdks) gives you direct API access: you send prompts and implement tool execution yourself. The **Agent SDK** gives you Claude with built-in tool execution.

483 

484 With the Client SDK, you implement a tool loop. With the Agent SDK, Claude handles it:

485 

486 <CodeGroup>

487 ```python Python theme={null}

488 # Client SDK: You implement the tool loop

489 response = client.messages.create(...)

490 while response.stop_reason == "tool_use":

491 result = your_tool_executor(response.tool_use)

492 response = client.messages.create(tool_result=result, **params)

493 

494 # Agent SDK: Claude handles tools autonomously

495 async for message in query(prompt="Fix the bug in auth.py"):

496 print(message)

497 ```

498 

499 ```typescript TypeScript theme={null}

500 // Client SDK: You implement the tool loop

501 let response = await client.messages.create({ ...params });

502 while (response.stop_reason === "tool_use") {

503 const result = yourToolExecutor(response.tool_use);

504 response = await client.messages.create({ tool_result: result, ...params });

505 }

506 

507 // Agent SDK: Claude handles tools autonomously

508 for await (const message of query({ prompt: "Fix the bug in auth.py" })) {

509 console.log(message);

510 }

511 ```

512 </CodeGroup>

513 </Tab>

514 

515 <Tab title="Agent SDK vs Claude Code CLI">

516 Same capabilities, different interface:

517 

518 | Use case | Best choice |

519 | ----------------------- | ----------- |

520 | Interactive development | CLI |

521 | CI/CD pipelines | SDK |

522 | Custom applications | SDK |

523 | One-off tasks | CLI |

524 | Production automation | SDK |

525 

526 Many teams use both: CLI for daily development, SDK for production. Workflows translate directly between them.

527 </Tab>

528</Tabs>

529 

530## Changelog

531 

532View the full changelog for SDK updates, bug fixes, and new features:

533 

534* **TypeScript SDK**: [view CHANGELOG.md](https://github.com/anthropics/claude-agent-sdk-typescript/blob/main/CHANGELOG.md)

535* **Python SDK**: [view CHANGELOG.md](https://github.com/anthropics/claude-agent-sdk-python/blob/main/CHANGELOG.md)

536 

537## Reporting bugs

538 

539If you encounter bugs or issues with the Agent SDK:

540 

541* **TypeScript SDK**: [report issues on GitHub](https://github.com/anthropics/claude-agent-sdk-typescript/issues)

542* **Python SDK**: [report issues on GitHub](https://github.com/anthropics/claude-agent-sdk-python/issues)

543 

544## Branding guidelines

545 

546For partners integrating the Claude Agent SDK, use of Claude branding is optional. When referencing Claude in your product:

547 

548**Allowed:**

549 

550* "Claude Agent" (preferred for dropdown menus)

551* "Claude" (when within a menu already labeled "Agents")

552* "{YourAgentName} Powered by Claude" (if you have an existing agent name)

553 

554**Not permitted:**

555 

556* "Claude Code" or "Claude Code Agent"

557* Claude Code-branded ASCII art or visual elements that mimic Claude Code

558 

559Your product should maintain its own branding and not appear to be Claude Code or any Anthropic product. For questions about branding compliance, contact the Anthropic [sales team](https://www.anthropic.com/contact-sales).

560 

561## License and terms

562 

563Use of the Claude Agent SDK is governed by [Anthropic's Commercial Terms of Service](https://www.anthropic.com/legal/commercial-terms), including when you use it to power products and services that you make available to your own customers and end users, except to the extent a specific component or dependency is covered by a different license as indicated in that component's LICENSE file.

564 

565## Next steps

566 

567<CardGroup cols={2}>

568 <Card title="Quickstart" icon="play" href="/en/agent-sdk/quickstart">

569 Build an agent that finds and fixes bugs in minutes

570 </Card>

571 

572 <Card title="Example agents" icon="star" href="https://github.com/anthropics/claude-agent-sdk-demos">

573 Email assistant, research agent, and more

574 </Card>

575 

576 <Card title="TypeScript SDK" icon="code" href="/en/agent-sdk/typescript">

577 Full TypeScript API reference and examples

578 </Card>

579 

580 <Card title="Python SDK" icon="code" href="/en/agent-sdk/python">

581 Full Python API reference and examples

582 </Card>

583</CardGroup>

agent-sdk/permissions.md +240 −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# Configure permissions

6 

7> Control how your agent uses tools with permission modes, hooks, and declarative allow/deny rules.

8 

9The Claude Agent SDK provides permission controls to manage how Claude uses tools. Use permission modes and rules to define what's allowed automatically, and the [`canUseTool` callback](/en/agent-sdk/user-input) to handle everything else at runtime.

10 

11<Note>

12 This page covers permission modes and rules. To build interactive approval flows where users approve or deny tool requests at runtime, see [Handle approvals and user input](/en/agent-sdk/user-input).

13</Note>

14 

15## How permissions are evaluated

16 

17When Claude requests a tool, the SDK checks permissions in this order:

18 

19<Steps>

20 <Step title="Hooks">

21 Run [hooks](/en/agent-sdk/hooks) first, which can allow, deny, or continue to the next step

22 </Step>

23 

24 <Step title="Deny rules">

25 Check `deny` rules (from `disallowed_tools` and [settings.json](/en/settings#permission-settings)). If a deny rule matches, the tool is blocked, even in `bypassPermissions` mode.

26 </Step>

27 

28 <Step title="Permission mode">

29 Apply the active [permission mode](#permission-modes). `bypassPermissions` approves everything that reaches this step. `acceptEdits` approves file operations. Other modes fall through.

30 </Step>

31 

32 <Step title="Allow rules">

33 Check `allow` rules (from `allowed_tools` and settings.json). If a rule matches, the tool is approved.

34 </Step>

35 

36 <Step title="canUseTool callback">

37 If not resolved by any of the above, call your [`canUseTool` callback](/en/agent-sdk/user-input) for a decision. In `dontAsk` mode, this step is skipped and the tool is denied.

38 </Step>

39</Steps>

40 

41<img src="https://mintcdn.com/claude-code/gvy2DIUELtNA8qD3/images/agent-sdk/permissions-flow.svg?fit=max&auto=format&n=gvy2DIUELtNA8qD3&q=85&s=0ccd63043a9ffc2a34d863602e043f72" alt="Permission evaluation flow diagram" width="920" height="260" data-path="images/agent-sdk/permissions-flow.svg" />

42 

43This page focuses on **allow and deny rules** and **permission modes**. For the other steps:

44 

45* **Hooks:** run custom code to allow, deny, or modify tool requests. See [Control execution with hooks](/en/agent-sdk/hooks).

46* **canUseTool callback:** prompt users for approval at runtime. See [Handle approvals and user input](/en/agent-sdk/user-input).

47 

48## Allow and deny rules

49 

50`allowed_tools` and `disallowed_tools` (TypeScript: `allowedTools` / `disallowedTools`) add entries to the allow and deny rule lists in the evaluation flow above. They control whether a tool call is approved, not whether the tool is available to Claude.

51 

52| Option | Effect |

53| :------------------------------- | :------------------------------------------------------------------------------------------------------------------------------- |

54| `allowed_tools=["Read", "Grep"]` | `Read` and `Grep` are auto-approved. Tools not listed here still exist and fall through to the permission mode and `canUseTool`. |

55| `disallowed_tools=["Bash"]` | `Bash` is always denied. Deny rules are checked first and hold in every permission mode, including `bypassPermissions`. |

56 

57For a locked-down agent, pair `allowedTools` with `permissionMode: "dontAsk"`. Listed tools are approved; anything else is denied outright instead of prompting:

58 

59```typescript theme={null}

60const options = {

61 allowedTools: ["Read", "Glob", "Grep"],

62 permissionMode: "dontAsk"

63};

64```

65 

66<Warning>

67 **`allowed_tools` does not constrain `bypassPermissions`.** `allowed_tools` only pre-approves the tools you list. Unlisted tools are not matched by any allow rule and fall through to the permission mode, where `bypassPermissions` approves them. Setting `allowed_tools=["Read"]` alongside `permission_mode="bypassPermissions"` still approves every tool, including `Bash`, `Write`, and `Edit`. If you need `bypassPermissions` but want specific tools blocked, use `disallowed_tools`.

68</Warning>

69 

70You can also configure allow, deny, and ask rules declaratively in `.claude/settings.json`. The SDK does not load filesystem settings by default, so you must set `setting_sources=["project"]` (TypeScript: `settingSources: ["project"]`) in your options for these rules to apply. See [Permission settings](/en/settings#permission-settings) for the rule syntax.

71 

72## Permission modes

73 

74Permission 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.

75 

76### Available modes

77 

78The SDK supports these permission modes:

79 

80| Mode | Description | Tool behavior |

81| :----------------------- | :--------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------- |

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

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

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

85| `bypassPermissions` | Bypass all permission checks | All tools run without permission prompts (use with caution) |

86| `plan` | Planning mode | No tool execution; Claude plans without making changes |

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

88 

89<Warning>

90 **Subagent inheritance:** When using `bypassPermissions`, all subagents inherit this mode and it cannot be overridden. Subagents may have different system prompts and less constrained behavior than your main agent. Enabling `bypassPermissions` grants them full, autonomous system access without any approval prompts.

91</Warning>

92 

93### Set permission mode

94 

95You can set the permission mode once when starting a query, or change it dynamically while the session is active.

96 

97<Tabs>

98 <Tab title="At query time">

99 Pass `permission_mode` (Python) or `permissionMode` (TypeScript) when creating a query. This mode applies for the entire session unless changed dynamically.

100 

101 <CodeGroup>

102 ```python Python theme={null}

103 import asyncio

104 from claude_agent_sdk import query, ClaudeAgentOptions

105 

106 

107 async def main():

108 async for message in query(

109 prompt="Help me refactor this code",

110 options=ClaudeAgentOptions(

111 permission_mode="default", # Set the mode here

112 ),

113 ):

114 if hasattr(message, "result"):

115 print(message.result)

116 

117 

118 asyncio.run(main())

119 ```

120 

121 ```typescript TypeScript theme={null}

122 import { query } from "@anthropic-ai/claude-agent-sdk";

123 

124 async function main() {

125 for await (const message of query({

126 prompt: "Help me refactor this code",

127 options: {

128 permissionMode: "default" // Set the mode here

129 }

130 })) {

131 if ("result" in message) {

132 console.log(message.result);

133 }

134 }

135 }

136 

137 main();

138 ```

139 </CodeGroup>

140 </Tab>

141 

142 <Tab title="During streaming">

143 Call `set_permission_mode()` (Python) or `setPermissionMode()` (TypeScript) to change the mode mid-session. The new mode takes effect immediately for all subsequent tool requests. This lets you start restrictive and loosen permissions as trust builds, for example switching to `acceptEdits` after reviewing Claude's initial approach.

144 

145 <CodeGroup>

146 ```python Python theme={null}

147 import asyncio

148 from claude_agent_sdk import query, ClaudeAgentOptions

149 

150 

151 async def main():

152 q = query(

153 prompt="Help me refactor this code",

154 options=ClaudeAgentOptions(

155 permission_mode="default", # Start in default mode

156 ),

157 )

158 

159 # Change mode dynamically mid-session

160 await q.set_permission_mode("acceptEdits")

161 

162 # Process messages with the new permission mode

163 async for message in q:

164 if hasattr(message, "result"):

165 print(message.result)

166 

167 

168 asyncio.run(main())

169 ```

170 

171 ```typescript TypeScript theme={null}

172 import { query } from "@anthropic-ai/claude-agent-sdk";

173 

174 async function main() {

175 const q = query({

176 prompt: "Help me refactor this code",

177 options: {

178 permissionMode: "default" // Start in default mode

179 }

180 });

181 

182 // Change mode dynamically mid-session

183 await q.setPermissionMode("acceptEdits");

184 

185 // Process messages with the new permission mode

186 for await (const message of q) {

187 if ("result" in message) {

188 console.log(message.result);

189 }

190 }

191 }

192 

193 main();

194 ```

195 </CodeGroup>

196 </Tab>

197</Tabs>

198 

199### Mode details

200 

201#### Accept edits mode (`acceptEdits`)

202 

203Auto-approves file operations so Claude can edit code without prompting. Other tools (like Bash commands that aren't filesystem operations) still require normal permissions.

204 

205**Auto-approved operations:**

206 

207* File edits (Edit, Write tools)

208* Filesystem commands: `mkdir`, `touch`, `rm`, `mv`, `cp`

209 

210**Use when:** you trust Claude's edits and want faster iteration, such as during prototyping or when working in an isolated directory.

211 

212#### Don't ask mode (`dontAsk`)

213 

214Converts any permission prompt into a denial. Tools pre-approved by `allowed_tools`, `settings.json` allow rules, or a hook run as normal. Everything else is denied without calling `canUseTool`.

215 

216**Use when:** you want a fixed, explicit tool surface for a headless agent and prefer a hard deny over silent reliance on `canUseTool` being absent.

217 

218#### Bypass permissions mode (`bypassPermissions`)

219 

220Auto-approves all tool uses without prompts. Hooks still execute and can block operations if needed.

221 

222<Warning>

223 Use with extreme caution. Claude has full system access in this mode. Only use in controlled environments where you trust all possible operations.

224 

225 `allowed_tools` does not constrain this mode. Every tool is approved, not just the ones you listed. Deny rules (`disallowed_tools`), explicit `ask` rules, and hooks are evaluated before the mode check and can still block a tool.

226</Warning>

227 

228#### Plan mode (`plan`)

229 

230Prevents tool execution entirely. Claude can analyze code and create plans but cannot make changes. Claude may use `AskUserQuestion` to clarify requirements before finalizing the plan. See [Handle approvals and user input](/en/agent-sdk/user-input#handle-clarifying-questions) for handling these prompts.

231 

232**Use when:** you want Claude to propose changes without executing them, such as during code review or when you need to approve changes before they're made.

233 

234## Related resources

235 

236For the other steps in the permission evaluation flow:

237 

238* [Handle approvals and user input](/en/agent-sdk/user-input): interactive approval prompts and clarifying questions

239* [Hooks guide](/en/agent-sdk/hooks): run custom code at key points in the agent lifecycle

240* [Permission rules](/en/settings#permission-settings): declarative allow/deny rules in `settings.json`

agent-sdk/plugins.md +342 −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# Plugins in the SDK

6 

7> Load custom plugins to extend Claude Code with commands, agents, skills, and hooks through the Agent SDK

8 

9Plugins allow you to extend Claude Code with custom functionality that can be shared across projects. Through the Agent SDK, you can programmatically load plugins from local directories to add custom slash commands, agents, skills, hooks, and MCP servers to your agent sessions.

10 

11## What are plugins?

12 

13Plugins are packages of Claude Code extensions that can include:

14 

15* **Skills**: Model-invoked capabilities that Claude uses autonomously (can also be invoked with `/skill-name`)

16* **Agents**: Specialized subagents for specific tasks

17* **Hooks**: Event handlers that respond to tool use and other events

18* **MCP servers**: External tool integrations via Model Context Protocol

19 

20<Note>

21 The `commands/` directory is a legacy format. Use `skills/` for new plugins. Claude Code continues to support both formats for backward compatibility.

22</Note>

23 

24For complete information on plugin structure and how to create plugins, see [Plugins](/en/plugins).

25 

26## Loading plugins

27 

28Load plugins by providing their local file system paths in your options configuration. The SDK supports loading multiple plugins from different locations.

29 

30<CodeGroup>

31 ```typescript TypeScript theme={null}

32 import { query } from "@anthropic-ai/claude-agent-sdk";

33 

34 for await (const message of query({

35 prompt: "Hello",

36 options: {

37 plugins: [

38 { type: "local", path: "./my-plugin" },

39 { type: "local", path: "/absolute/path/to/another-plugin" }

40 ]

41 }

42 })) {

43 // Plugin commands, agents, and other features are now available

44 }

45 ```

46 

47 ```python Python theme={null}

48 import asyncio

49 from claude_agent_sdk import query

50 

51 

52 async def main():

53 async for message in query(

54 prompt="Hello",

55 options={

56 "plugins": [

57 {"type": "local", "path": "./my-plugin"},

58 {"type": "local", "path": "/absolute/path/to/another-plugin"},

59 ]

60 },

61 ):

62 # Plugin commands, agents, and other features are now available

63 pass

64 

65 

66 asyncio.run(main())

67 ```

68</CodeGroup>

69 

70### Path specifications

71 

72Plugin paths can be:

73 

74* **Relative paths**: Resolved relative to your current working directory (for example, `"./plugins/my-plugin"`)

75* **Absolute paths**: Full file system paths (for example, `"/home/user/plugins/my-plugin"`)

76 

77<Note>

78 The path should point to the plugin's root directory (the directory containing `.claude-plugin/plugin.json`).

79</Note>

80 

81## Verifying plugin installation

82 

83When plugins load successfully, they appear in the system initialization message. You can verify that your plugins are available:

84 

85<CodeGroup>

86 ```typescript TypeScript theme={null}

87 import { query } from "@anthropic-ai/claude-agent-sdk";

88 

89 for await (const message of query({

90 prompt: "Hello",

91 options: {

92 plugins: [{ type: "local", path: "./my-plugin" }]

93 }

94 })) {

95 if (message.type === "system" && message.subtype === "init") {

96 // Check loaded plugins

97 console.log("Plugins:", message.plugins);

98 // Example: [{ name: "my-plugin", path: "./my-plugin" }]

99 

100 // Check available commands from plugins

101 console.log("Commands:", message.slash_commands);

102 // Example: ["/help", "/compact", "my-plugin:custom-command"]

103 }

104 }

105 ```

106 

107 ```python Python theme={null}

108 import asyncio

109 from claude_agent_sdk import query

110 

111 

112 async def main():

113 async for message in query(

114 prompt="Hello", options={"plugins": [{"type": "local", "path": "./my-plugin"}]}

115 ):

116 if message.type == "system" and message.subtype == "init":

117 # Check loaded plugins

118 print("Plugins:", message.data.get("plugins"))

119 # Example: [{"name": "my-plugin", "path": "./my-plugin"}]

120 

121 # Check available commands from plugins

122 print("Commands:", message.data.get("slash_commands"))

123 # Example: ["/help", "/compact", "my-plugin:custom-command"]

124 

125 

126 asyncio.run(main())

127 ```

128</CodeGroup>

129 

130## Using plugin skills

131 

132Skills from plugins are automatically namespaced with the plugin name to avoid conflicts. When invoked as slash commands, the format is `plugin-name:skill-name`.

133 

134<CodeGroup>

135 ```typescript TypeScript theme={null}

136 import { query } from "@anthropic-ai/claude-agent-sdk";

137 

138 // Load a plugin with a custom /greet skill

139 for await (const message of query({

140 prompt: "/my-plugin:greet", // Use plugin skill with namespace

141 options: {

142 plugins: [{ type: "local", path: "./my-plugin" }]

143 }

144 })) {

145 // Claude executes the custom greeting skill from the plugin

146 if (message.type === "assistant") {

147 console.log(message.message.content);

148 }

149 }

150 ```

151 

152 ```python Python theme={null}

153 import asyncio

154 from claude_agent_sdk import query, AssistantMessage, TextBlock

155 

156 

157 async def main():

158 # Load a plugin with a custom /greet skill

159 async for message in query(

160 prompt="/demo-plugin:greet", # Use plugin skill with namespace

161 options={"plugins": [{"type": "local", "path": "./plugins/demo-plugin"}]},

162 ):

163 # Claude executes the custom greeting skill from the plugin

164 if isinstance(message, AssistantMessage):

165 for block in message.content:

166 if isinstance(block, TextBlock):

167 print(f"Claude: {block.text}")

168 

169 

170 asyncio.run(main())

171 ```

172</CodeGroup>

173 

174<Note>

175 If you installed a plugin via the CLI (for example, `/plugin install my-plugin@marketplace`), you can still use it in the SDK by providing its installation path. Check `~/.claude/plugins/` for CLI-installed plugins.

176</Note>

177 

178## Complete example

179 

180Here's a full example demonstrating plugin loading and usage:

181 

182<CodeGroup>

183 ```typescript TypeScript theme={null}

184 import { query } from "@anthropic-ai/claude-agent-sdk";

185 import * as path from "path";

186 

187 async function runWithPlugin() {

188 const pluginPath = path.join(__dirname, "plugins", "my-plugin");

189 

190 console.log("Loading plugin from:", pluginPath);

191 

192 for await (const message of query({

193 prompt: "What custom commands do you have available?",

194 options: {

195 plugins: [{ type: "local", path: pluginPath }],

196 maxTurns: 3

197 }

198 })) {

199 if (message.type === "system" && message.subtype === "init") {

200 console.log("Loaded plugins:", message.plugins);

201 console.log("Available commands:", message.slash_commands);

202 }

203 

204 if (message.type === "assistant") {

205 console.log("Assistant:", message.message.content);

206 }

207 }

208 }

209 

210 runWithPlugin().catch(console.error);

211 ```

212 

213 ```python Python theme={null}

214 #!/usr/bin/env python3

215 """Example demonstrating how to use plugins with the Agent SDK."""

216 

217 from pathlib import Path

218 import anyio

219 from claude_agent_sdk import (

220 AssistantMessage,

221 ClaudeAgentOptions,

222 TextBlock,

223 query,

224 )

225 

226 

227 async def run_with_plugin():

228 """Example using a custom plugin."""

229 plugin_path = Path(__file__).parent / "plugins" / "demo-plugin"

230 

231 print(f"Loading plugin from: {plugin_path}")

232 

233 options = ClaudeAgentOptions(

234 plugins=[{"type": "local", "path": str(plugin_path)}],

235 max_turns=3,

236 )

237 

238 async for message in query(

239 prompt="What custom commands do you have available?", options=options

240 ):

241 if message.type == "system" and message.subtype == "init":

242 print(f"Loaded plugins: {message.data.get('plugins')}")

243 print(f"Available commands: {message.data.get('slash_commands')}")

244 

245 if isinstance(message, AssistantMessage):

246 for block in message.content:

247 if isinstance(block, TextBlock):

248 print(f"Assistant: {block.text}")

249 

250 

251 if __name__ == "__main__":

252 anyio.run(run_with_plugin)

253 ```

254</CodeGroup>

255 

256## Plugin structure reference

257 

258A plugin directory must contain a `.claude-plugin/plugin.json` manifest file. It can optionally include:

259 

260```text theme={null}

261my-plugin/

262├── .claude-plugin/

263│ └── plugin.json # Required: plugin manifest

264├── skills/ # Agent Skills (invoked autonomously or via /skill-name)

265│ └── my-skill/

266│ └── SKILL.md

267├── commands/ # Legacy: use skills/ instead

268│ └── custom-cmd.md

269├── agents/ # Custom agents

270│ └── specialist.md

271├── hooks/ # Event handlers

272│ └── hooks.json

273└── .mcp.json # MCP server definitions

274```

275 

276For detailed information on creating plugins, see:

277 

278* [Plugins](/en/plugins) - Complete plugin development guide

279* [Plugins reference](/en/plugins-reference) - Technical specifications and schemas

280 

281## Common use cases

282 

283### Development and testing

284 

285Load plugins during development without installing them globally:

286 

287```typescript theme={null}

288plugins: [{ type: "local", path: "./dev-plugins/my-plugin" }];

289```

290 

291### Project-specific extensions

292 

293Include plugins in your project repository for team-wide consistency:

294 

295```typescript theme={null}

296plugins: [{ type: "local", path: "./project-plugins/team-workflows" }];

297```

298 

299### Multiple plugin sources

300 

301Combine plugins from different locations:

302 

303```typescript theme={null}

304plugins: [

305 { type: "local", path: "./local-plugin" },

306 { type: "local", path: "~/.claude/custom-plugins/shared-plugin" }

307];

308```

309 

310## Troubleshooting

311 

312### Plugin not loading

313 

314If your plugin doesn't appear in the init message:

315 

3161. **Check the path**: Ensure the path points to the plugin root directory (containing `.claude-plugin/`)

3172. **Validate plugin.json**: Ensure your manifest file has valid JSON syntax

3183. **Check file permissions**: Ensure the plugin directory is readable

319 

320### Skills not appearing

321 

322If plugin skills don't work:

323 

3241. **Use the namespace**: Plugin skills require the `plugin-name:skill-name` format when invoked as slash commands

3252. **Check init message**: Verify the skill appears in `slash_commands` with the correct namespace

3263. **Validate skill files**: Ensure each skill has a `SKILL.md` file in its own subdirectory under `skills/` (for example, `skills/my-skill/SKILL.md`)

327 

328### Path resolution issues

329 

330If relative paths don't work:

331 

3321. **Check working directory**: Relative paths are resolved from your current working directory

3332. **Use absolute paths**: For reliability, consider using absolute paths

3343. **Normalize paths**: Use path utilities to construct paths correctly

335 

336## See also

337 

338* [Plugins](/en/plugins) - Complete plugin development guide

339* [Plugins reference](/en/plugins-reference) - Technical specifications

340* [Slash Commands](/en/agent-sdk/slash-commands) - Using slash commands in the SDK

341* [Subagents](/en/agent-sdk/subagents) - Working with specialized agents

342* [Skills](/en/agent-sdk/skills) - Using Agent Skills

agent-sdk/python.md +3207 −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# Agent SDK reference - Python

6 

7> Complete API reference for the Python Agent SDK, including all functions, types, and classes.

8 

9## Installation

10 

11```bash theme={null}

12pip install claude-agent-sdk

13```

14 

15## Choosing between `query()` and `ClaudeSDKClient`

16 

17The Python SDK provides two ways to interact with Claude Code:

18 

19### Quick comparison

20 

21| Feature | `query()` | `ClaudeSDKClient` |

22| :------------------ | :---------------------------- | :--------------------------------- |

23| **Session** | Creates new session each time | Reuses same session |

24| **Conversation** | Single exchange | Multiple exchanges in same context |

25| **Connection** | Managed automatically | Manual control |

26| **Streaming Input** | ✅ Supported | ✅ Supported |

27| **Interrupts** | ❌ Not supported | ✅ Supported |

28| **Hooks** | ✅ Supported | ✅ Supported |

29| **Custom Tools** | ✅ Supported | ✅ Supported |

30| **Continue Chat** | ❌ New session each time | ✅ Maintains conversation |

31| **Use Case** | One-off tasks | Continuous conversations |

32 

33### When to use `query()` (new session each time)

34 

35**Best for:**

36 

37* One-off questions where you don't need conversation history

38* Independent tasks that don't require context from previous exchanges

39* Simple automation scripts

40* When you want a fresh start each time

41 

42### When to use `ClaudeSDKClient` (continuous conversation)

43 

44**Best for:**

45 

46* **Continuing conversations** - When you need Claude to remember context

47* **Follow-up questions** - Building on previous responses

48* **Interactive applications** - Chat interfaces, REPLs

49* **Response-driven logic** - When next action depends on Claude's response

50* **Session control** - Managing conversation lifecycle explicitly

51 

52## Functions

53 

54### `query()`

55 

56Creates a new session for each interaction with Claude Code. Returns an async iterator that yields messages as they arrive. Each call to `query()` starts fresh with no memory of previous interactions.

57 

58```python theme={null}

59async def query(

60 *,

61 prompt: str | AsyncIterable[dict[str, Any]],

62 options: ClaudeAgentOptions | None = None,

63 transport: Transport | None = None

64) -> AsyncIterator[Message]

65```

66 

67#### Parameters

68 

69| Parameter | Type | Description |

70| :---------- | :--------------------------- | :------------------------------------------------------------------------- |

71| `prompt` | `str \| AsyncIterable[dict]` | The input prompt as a string or async iterable for streaming mode |

72| `options` | `ClaudeAgentOptions \| None` | Optional configuration object (defaults to `ClaudeAgentOptions()` if None) |

73| `transport` | `Transport \| None` | Optional custom transport for communicating with the CLI process |

74 

75#### Returns

76 

77Returns an `AsyncIterator[Message]` that yields messages from the conversation.

78 

79#### Example - With options

80 

81```python theme={null}

82import asyncio

83from claude_agent_sdk import query, ClaudeAgentOptions

84 

85 

86async def main():

87 options = ClaudeAgentOptions(

88 system_prompt="You are an expert Python developer",

89 permission_mode="acceptEdits",

90 cwd="/home/user/project",

91 )

92 

93 async for message in query(prompt="Create a Python web server", options=options):

94 print(message)

95 

96 

97asyncio.run(main())

98```

99 

100### `tool()`

101 

102Decorator for defining MCP tools with type safety.

103 

104```python theme={null}

105def tool(

106 name: str,

107 description: str,

108 input_schema: type | dict[str, Any],

109 annotations: ToolAnnotations | None = None

110) -> Callable[[Callable[[Any], Awaitable[dict[str, Any]]]], SdkMcpTool[Any]]

111```

112 

113#### Parameters

114 

115| Parameter | Type | Description |

116| :------------- | :----------------------------------------------- | :------------------------------------------------------------------ |

117| `name` | `str` | Unique identifier for the tool |

118| `description` | `str` | Human-readable description of what the tool does |

119| `input_schema` | `type \| dict[str, Any]` | Schema defining the tool's input parameters (see below) |

120| `annotations` | [`ToolAnnotations`](#tool-annotations)` \| None` | Optional MCP tool annotations providing behavioral hints to clients |

121 

122#### Input schema options

123 

1241. **Simple type mapping** (recommended):

125 

126 ```python theme={null}

127 {"text": str, "count": int, "enabled": bool}

128 ```

129 

1302. **JSON Schema format** (for complex validation):

131 ```python theme={null}

132 {

133 "type": "object",

134 "properties": {

135 "text": {"type": "string"},

136 "count": {"type": "integer", "minimum": 0},

137 },

138 "required": ["text"],

139 }

140 ```

141 

142#### Returns

143 

144A decorator function that wraps the tool implementation and returns an `SdkMcpTool` instance.

145 

146#### Example

147 

148```python theme={null}

149from claude_agent_sdk import tool

150from typing import Any

151 

152 

153@tool("greet", "Greet a user", {"name": str})

154async def greet(args: dict[str, Any]) -> dict[str, Any]:

155 return {"content": [{"type": "text", "text": f"Hello, {args['name']}!"}]}

156```

157 

158#### `ToolAnnotations`

159 

160Re-exported from `mcp.types` (also available as `from claude_agent_sdk import ToolAnnotations`). All fields are optional hints; clients should not rely on them for security decisions.

161 

162| Field | Type | Default | Description |

163| :---------------- | :------------- | :------ | :--------------------------------------------------------------------------------------------------------------------------------------------------- |

164| `title` | `str \| None` | `None` | Human-readable title for the tool |

165| `readOnlyHint` | `bool \| None` | `False` | If `True`, the tool does not modify its environment |

166| `destructiveHint` | `bool \| None` | `True` | If `True`, the tool may perform destructive updates (only meaningful when `readOnlyHint` is `False`) |

167| `idempotentHint` | `bool \| None` | `False` | If `True`, repeated calls with the same arguments have no additional effect (only meaningful when `readOnlyHint` is `False`) |

168| `openWorldHint` | `bool \| None` | `True` | If `True`, the tool interacts with external entities (for example, web search). If `False`, the tool's domain is closed (for example, a memory tool) |

169 

170```python theme={null}

171from claude_agent_sdk import tool, ToolAnnotations

172from typing import Any

173 

174 

175@tool(

176 "search",

177 "Search the web",

178 {"query": str},

179 annotations=ToolAnnotations(readOnlyHint=True, openWorldHint=True),

180)

181async def search(args: dict[str, Any]) -> dict[str, Any]:

182 return {"content": [{"type": "text", "text": f"Results for: {args['query']}"}]}

183```

184 

185### `create_sdk_mcp_server()`

186 

187Create an in-process MCP server that runs within your Python application.

188 

189```python theme={null}

190def create_sdk_mcp_server(

191 name: str,

192 version: str = "1.0.0",

193 tools: list[SdkMcpTool[Any]] | None = None

194) -> McpSdkServerConfig

195```

196 

197#### Parameters

198 

199| Parameter | Type | Default | Description |

200| :-------- | :------------------------------ | :-------- | :---------------------------------------------------- |

201| `name` | `str` | - | Unique identifier for the server |

202| `version` | `str` | `"1.0.0"` | Server version string |

203| `tools` | `list[SdkMcpTool[Any]] \| None` | `None` | List of tool functions created with `@tool` decorator |

204 

205#### Returns

206 

207Returns an `McpSdkServerConfig` object that can be passed to `ClaudeAgentOptions.mcp_servers`.

208 

209#### Example

210 

211```python theme={null}

212from claude_agent_sdk import tool, create_sdk_mcp_server

213 

214 

215@tool("add", "Add two numbers", {"a": float, "b": float})

216async def add(args):

217 return {"content": [{"type": "text", "text": f"Sum: {args['a'] + args['b']}"}]}

218 

219 

220@tool("multiply", "Multiply two numbers", {"a": float, "b": float})

221async def multiply(args):

222 return {"content": [{"type": "text", "text": f"Product: {args['a'] * args['b']}"}]}

223 

224 

225calculator = create_sdk_mcp_server(

226 name="calculator",

227 version="2.0.0",

228 tools=[add, multiply], # Pass decorated functions

229)

230 

231# Use with Claude

232options = ClaudeAgentOptions(

233 mcp_servers={"calc": calculator},

234 allowed_tools=["mcp__calc__add", "mcp__calc__multiply"],

235)

236```

237 

238### `list_sessions()`

239 

240Lists past sessions with metadata. Filter by project directory or list sessions across all projects. Synchronous; returns immediately.

241 

242```python theme={null}

243def list_sessions(

244 directory: str | None = None,

245 limit: int | None = None,

246 include_worktrees: bool = True

247) -> list[SDKSessionInfo]

248```

249 

250#### Parameters

251 

252| Parameter | Type | Default | Description |

253| :------------------ | :------------ | :------ | :------------------------------------------------------------------------------------ |

254| `directory` | `str \| None` | `None` | Directory to list sessions for. When omitted, returns sessions across all projects |

255| `limit` | `int \| None` | `None` | Maximum number of sessions to return |

256| `include_worktrees` | `bool` | `True` | When `directory` is inside a git repository, include sessions from all worktree paths |

257 

258#### Return type: `SDKSessionInfo`

259 

260| Property | Type | Description |

261| :-------------- | :------------ | :------------------------------------------------------------------- |

262| `session_id` | `str` | Unique session identifier |

263| `summary` | `str` | Display title: custom title, auto-generated summary, or first prompt |

264| `last_modified` | `int` | Last modified time in milliseconds since epoch |

265| `file_size` | `int \| None` | Session file size in bytes (`None` for remote storage backends) |

266| `custom_title` | `str \| None` | User-set session title |

267| `first_prompt` | `str \| None` | First meaningful user prompt in the session |

268| `git_branch` | `str \| None` | Git branch at the end of the session |

269| `cwd` | `str \| None` | Working directory for the session |

270| `tag` | `str \| None` | User-set session tag (see [`tag_session()`](#tag-session)) |

271| `created_at` | `int \| None` | Session creation time in milliseconds since epoch |

272 

273#### Example

274 

275Print the 10 most recent sessions for a project. Results are sorted by `last_modified` descending, so the first item is the newest. Omit `directory` to search across all projects.

276 

277```python theme={null}

278from claude_agent_sdk import list_sessions

279 

280for session in list_sessions(directory="/path/to/project", limit=10):

281 print(f"{session.summary} ({session.session_id})")

282```

283 

284### `get_session_messages()`

285 

286Retrieves messages from a past session. Synchronous; returns immediately.

287 

288```python theme={null}

289def get_session_messages(

290 session_id: str,

291 directory: str | None = None,

292 limit: int | None = None,

293 offset: int = 0

294) -> list[SessionMessage]

295```

296 

297#### Parameters

298 

299| Parameter | Type | Default | Description |

300| :----------- | :------------ | :------- | :---------------------------------------------------------------- |

301| `session_id` | `str` | required | The session ID to retrieve messages for |

302| `directory` | `str \| None` | `None` | Project directory to look in. When omitted, searches all projects |

303| `limit` | `int \| None` | `None` | Maximum number of messages to return |

304| `offset` | `int` | `0` | Number of messages to skip from the start |

305 

306#### Return type: `SessionMessage`

307 

308| Property | Type | Description |

309| :------------------- | :----------------------------- | :------------------------ |

310| `type` | `Literal["user", "assistant"]` | Message role |

311| `uuid` | `str` | Unique message identifier |

312| `session_id` | `str` | Session identifier |

313| `message` | `Any` | Raw message content |

314| `parent_tool_use_id` | `None` | Reserved for future use |

315 

316#### Example

317 

318```python theme={null}

319from claude_agent_sdk import list_sessions, get_session_messages

320 

321sessions = list_sessions(limit=1)

322if sessions:

323 messages = get_session_messages(sessions[0].session_id)

324 for msg in messages:

325 print(f"[{msg.type}] {msg.uuid}")

326```

327 

328### `get_session_info()`

329 

330Reads metadata for a single session by ID without scanning the full project directory. Synchronous; returns immediately.

331 

332```python theme={null}

333def get_session_info(

334 session_id: str,

335 directory: str | None = None,

336) -> SDKSessionInfo | None

337```

338 

339#### Parameters

340 

341| Parameter | Type | Default | Description |

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

343| `session_id` | `str` | required | UUID of the session to look up |

344| `directory` | `str \| None` | `None` | Project directory path. When omitted, searches all project directories |

345 

346Returns [`SDKSessionInfo`](#return-type-sdk-session-info), or `None` if the session is not found.

347 

348#### Example

349 

350Look up a single session's metadata without scanning the project directory. Useful when you already have a session ID from a previous run.

351 

352```python theme={null}

353from claude_agent_sdk import get_session_info

354 

355info = get_session_info("550e8400-e29b-41d4-a716-446655440000")

356if info:

357 print(f"{info.summary} (branch: {info.git_branch}, tag: {info.tag})")

358```

359 

360### `rename_session()`

361 

362Renames a session by appending a custom-title entry. Repeated calls are safe; the most recent title wins. Synchronous.

363 

364```python theme={null}

365def rename_session(

366 session_id: str,

367 title: str,

368 directory: str | None = None,

369) -> None

370```

371 

372#### Parameters

373 

374| Parameter | Type | Default | Description |

375| :----------- | :------------ | :------- | :--------------------------------------------------------------------- |

376| `session_id` | `str` | required | UUID of the session to rename |

377| `title` | `str` | required | New title. Must be non-empty after stripping whitespace |

378| `directory` | `str \| None` | `None` | Project directory path. When omitted, searches all project directories |

379 

380Raises `ValueError` if `session_id` is not a valid UUID or `title` is empty; `FileNotFoundError` if the session cannot be found.

381 

382#### Example

383 

384Rename the most recent session so it's easier to find later. The new title appears in [`SDKSessionInfo.custom_title`](#return-type-sdk-session-info) on subsequent reads.

385 

386```python theme={null}

387from claude_agent_sdk import list_sessions, rename_session

388 

389sessions = list_sessions(directory="/path/to/project", limit=1)

390if sessions:

391 rename_session(sessions[0].session_id, "Refactor auth module")

392```

393 

394### `tag_session()`

395 

396Tags a session. Pass `None` to clear the tag. Repeated calls are safe; the most recent tag wins. Synchronous.

397 

398```python theme={null}

399def tag_session(

400 session_id: str,

401 tag: str | None,

402 directory: str | None = None,

403) -> None

404```

405 

406#### Parameters

407 

408| Parameter | Type | Default | Description |

409| :----------- | :------------ | :------- | :--------------------------------------------------------------------- |

410| `session_id` | `str` | required | UUID of the session to tag |

411| `tag` | `str \| None` | required | Tag string, or `None` to clear. Unicode-sanitized before storing |

412| `directory` | `str \| None` | `None` | Project directory path. When omitted, searches all project directories |

413 

414Raises `ValueError` if `session_id` is not a valid UUID or `tag` is empty after sanitization; `FileNotFoundError` if the session cannot be found.

415 

416#### Example

417 

418Tag a session, then filter by that tag on a later read. Pass `None` to clear an existing tag.

419 

420```python theme={null}

421from claude_agent_sdk import list_sessions, tag_session

422 

423# Tag a session

424tag_session("550e8400-e29b-41d4-a716-446655440000", "needs-review")

425 

426# Later: find all sessions with that tag

427for session in list_sessions(directory="/path/to/project"):

428 if session.tag == "needs-review":

429 print(session.summary)

430```

431 

432## Classes

433 

434### `ClaudeSDKClient`

435 

436**Maintains a conversation session across multiple exchanges.** This is the Python equivalent of how the TypeScript SDK's `query()` function works internally - it creates a client object that can continue conversations.

437 

438#### Key Features

439 

440* **Session continuity**: Maintains conversation context across multiple `query()` calls

441* **Same conversation**: The session retains previous messages

442* **Interrupt support**: Can stop execution mid-task

443* **Explicit lifecycle**: You control when the session starts and ends

444* **Response-driven flow**: Can react to responses and send follow-ups

445* **Custom tools and hooks**: Supports custom tools (created with `@tool` decorator) and hooks

446 

447```python theme={null}

448class ClaudeSDKClient:

449 def __init__(self, options: ClaudeAgentOptions | None = None, transport: Transport | None = None)

450 async def connect(self, prompt: str | AsyncIterable[dict] | None = None) -> None

451 async def query(self, prompt: str | AsyncIterable[dict], session_id: str = "default") -> None

452 async def receive_messages(self) -> AsyncIterator[Message]

453 async def receive_response(self) -> AsyncIterator[Message]

454 async def interrupt(self) -> None

455 async def set_permission_mode(self, mode: str) -> None

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

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

458 async def get_mcp_status(self) -> McpStatusResponse

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

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

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

462 async def get_server_info(self) -> dict[str, Any] | None

463 async def disconnect(self) -> None

464```

465 

466#### Methods

467 

468| Method | Description |

469| :---------------------------------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------- |

470| `__init__(options)` | Initialize the client with optional configuration |

471| `connect(prompt)` | Connect to Claude with an optional initial prompt or message stream |

472| `query(prompt, session_id)` | Send a new request in streaming mode |

473| `receive_messages()` | Receive all messages from Claude as an async iterator |

474| `receive_response()` | Receive messages until and including a ResultMessage |

475| `interrupt()` | Send interrupt signal (only works in streaming mode) |

476| `set_permission_mode(mode)` | Change the permission mode for the current session |

477| `set_model(model)` | Change the model for the current session. Pass `None` to reset to default |

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

479| `get_mcp_status()` | Get the status of all configured MCP servers. Returns [`McpStatusResponse`](#mcp-status-response) |

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

481| `toggle_mcp_server(server_name, enabled)` | Enable or disable an MCP server mid-session. Disabling removes its tools |

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

483| `get_server_info()` | Get server information including session ID and capabilities |

484| `disconnect()` | Disconnect from Claude |

485 

486#### Context Manager Support

487 

488The client can be used as an async context manager for automatic connection management:

489 

490```python theme={null}

491async with ClaudeSDKClient() as client:

492 await client.query("Hello Claude")

493 async for message in client.receive_response():

494 print(message)

495```

496 

497> **Important:** When iterating over messages, avoid using `break` to exit early as this can cause asyncio cleanup issues. Instead, let the iteration complete naturally or use flags to track when you've found what you need.

498 

499#### Example - Continuing a conversation

500 

501```python theme={null}

502import asyncio

503from claude_agent_sdk import ClaudeSDKClient, AssistantMessage, TextBlock, ResultMessage

504 

505 

506async def main():

507 async with ClaudeSDKClient() as client:

508 # First question

509 await client.query("What's the capital of France?")

510 

511 # Process response

512 async for message in client.receive_response():

513 if isinstance(message, AssistantMessage):

514 for block in message.content:

515 if isinstance(block, TextBlock):

516 print(f"Claude: {block.text}")

517 

518 # Follow-up question - the session retains the previous context

519 await client.query("What's the population of that city?")

520 

521 async for message in client.receive_response():

522 if isinstance(message, AssistantMessage):

523 for block in message.content:

524 if isinstance(block, TextBlock):

525 print(f"Claude: {block.text}")

526 

527 # Another follow-up - still in the same conversation

528 await client.query("What are some famous landmarks there?")

529 

530 async for message in client.receive_response():

531 if isinstance(message, AssistantMessage):

532 for block in message.content:

533 if isinstance(block, TextBlock):

534 print(f"Claude: {block.text}")

535 

536 

537asyncio.run(main())

538```

539 

540#### Example - Streaming input with ClaudeSDKClient

541 

542```python theme={null}

543import asyncio

544from claude_agent_sdk import ClaudeSDKClient

545 

546 

547async def message_stream():

548 """Generate messages dynamically."""

549 yield {

550 "type": "user",

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

552 }

553 await asyncio.sleep(0.5)

554 yield {

555 "type": "user",

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

557 }

558 await asyncio.sleep(0.5)

559 yield {

560 "type": "user",

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

562 }

563 

564 

565async def main():

566 async with ClaudeSDKClient() as client:

567 # Stream input to Claude

568 await client.query(message_stream())

569 

570 # Process response

571 async for message in client.receive_response():

572 print(message)

573 

574 # Follow-up in same session

575 await client.query("Should we be concerned about these readings?")

576 

577 async for message in client.receive_response():

578 print(message)

579 

580 

581asyncio.run(main())

582```

583 

584#### Example - Using interrupts

585 

586```python theme={null}

587import asyncio

588from claude_agent_sdk import ClaudeSDKClient, ClaudeAgentOptions, ResultMessage

589 

590 

591async def interruptible_task():

592 options = ClaudeAgentOptions(allowed_tools=["Bash"], permission_mode="acceptEdits")

593 

594 async with ClaudeSDKClient(options=options) as client:

595 # Start a long-running task

596 await client.query("Count from 1 to 100 slowly, using the bash sleep command")

597 

598 # Let it run for a bit

599 await asyncio.sleep(2)

600 

601 # Interrupt the task

602 await client.interrupt()

603 print("Task interrupted!")

604 

605 # Drain the interrupted task's messages (including its ResultMessage)

606 async for message in client.receive_response():

607 if isinstance(message, ResultMessage):

608 print(f"Interrupted task finished with subtype={message.subtype!r}")

609 # subtype is "error_during_execution" for interrupted tasks

610 

611 # Send a new command

612 await client.query("Just say hello instead")

613 

614 # Now receive the new response

615 async for message in client.receive_response():

616 if isinstance(message, ResultMessage) and message.subtype == "success":

617 print(f"New result: {message.result}")

618 

619 

620asyncio.run(interruptible_task())

621```

622 

623<Note>

624 **Buffer behavior after interrupt:** `interrupt()` sends a stop signal but does not clear the message buffer. Messages already produced by the interrupted task, including its `ResultMessage` (with `subtype="error_during_execution"`), remain in the stream. You must drain them with `receive_response()` before reading the response to a new query. If you send a new query immediately after `interrupt()` and call `receive_response()` only once, you'll receive the interrupted task's messages, not the new query's response.

625</Note>

626 

627#### Example - Advanced permission control

628 

629```python theme={null}

630from claude_agent_sdk import ClaudeSDKClient, ClaudeAgentOptions

631from claude_agent_sdk.types import (

632 PermissionResultAllow,

633 PermissionResultDeny,

634 ToolPermissionContext,

635)

636 

637 

638async def custom_permission_handler(

639 tool_name: str, input_data: dict, context: ToolPermissionContext

640) -> PermissionResultAllow | PermissionResultDeny:

641 """Custom logic for tool permissions."""

642 

643 # Block writes to system directories

644 if tool_name == "Write" and input_data.get("file_path", "").startswith("/system/"):

645 return PermissionResultDeny(

646 message="System directory write not allowed", interrupt=True

647 )

648 

649 # Redirect sensitive file operations

650 if tool_name in ["Write", "Edit"] and "config" in input_data.get("file_path", ""):

651 safe_path = f"./sandbox/{input_data['file_path']}"

652 return PermissionResultAllow(

653 updated_input={**input_data, "file_path": safe_path}

654 )

655 

656 # Allow everything else

657 return PermissionResultAllow(updated_input=input_data)

658 

659 

660async def main():

661 options = ClaudeAgentOptions(

662 can_use_tool=custom_permission_handler, allowed_tools=["Read", "Write", "Edit"]

663 )

664 

665 async with ClaudeSDKClient(options=options) as client:

666 await client.query("Update the system config file")

667 

668 async for message in client.receive_response():

669 # Will use sandbox path instead

670 print(message)

671 

672 

673asyncio.run(main())

674```

675 

676## Types

677 

678<Note>

679 **`@dataclass` vs `TypedDict`:** This SDK uses two kinds of types. Classes decorated with `@dataclass` (such as `ResultMessage`, `AgentDefinition`, `TextBlock`) are object instances at runtime and support attribute access: `msg.result`. Classes defined with `TypedDict` (such as `ThinkingConfigEnabled`, `McpStdioServerConfig`, `SyncHookJSONOutput`) are **plain dicts at runtime** and require key access: `config["budget_tokens"]`, not `config.budget_tokens`. The `ClassName(field=value)` call syntax works for both, but only dataclasses produce objects with attributes.

680</Note>

681 

682### `SdkMcpTool`

683 

684Definition for an SDK MCP tool created with the `@tool` decorator.

685 

686```python theme={null}

687@dataclass

688class SdkMcpTool(Generic[T]):

689 name: str

690 description: str

691 input_schema: type[T] | dict[str, Any]

692 handler: Callable[[T], Awaitable[dict[str, Any]]]

693 annotations: ToolAnnotations | None = None

694```

695 

696| Property | Type | Description |

697| :------------- | :----------------------------------------- | :--------------------------------------------------------------------------------------------------------- |

698| `name` | `str` | Unique identifier for the tool |

699| `description` | `str` | Human-readable description |

700| `input_schema` | `type[T] \| dict[str, Any]` | Schema for input validation |

701| `handler` | `Callable[[T], Awaitable[dict[str, Any]]]` | Async function that handles tool execution |

702| `annotations` | `ToolAnnotations \| None` | Optional MCP tool annotations (e.g., `readOnlyHint`, `destructiveHint`, `openWorldHint`). From `mcp.types` |

703 

704### `Transport`

705 

706Abstract base class for custom transport implementations. Use this to communicate with the Claude process over a custom channel (for example, a remote connection instead of a local subprocess).

707 

708<Warning>

709 This is a low-level internal API. The interface may change in future releases. Custom implementations must be updated to match any interface changes.

710</Warning>

711 

712```python theme={null}

713from abc import ABC, abstractmethod

714from collections.abc import AsyncIterator

715from typing import Any

716 

717 

718class Transport(ABC):

719 @abstractmethod

720 async def connect(self) -> None: ...

721 

722 @abstractmethod

723 async def write(self, data: str) -> None: ...

724 

725 @abstractmethod

726 def read_messages(self) -> AsyncIterator[dict[str, Any]]: ...

727 

728 @abstractmethod

729 async def close(self) -> None: ...

730 

731 @abstractmethod

732 def is_ready(self) -> bool: ...

733 

734 @abstractmethod

735 async def end_input(self) -> None: ...

736```

737 

738| Method | Description |

739| :---------------- | :-------------------------------------------------------------------------- |

740| `connect()` | Connect the transport and prepare for communication |

741| `write(data)` | Write raw data (JSON + newline) to the transport |

742| `read_messages()` | Async iterator that yields parsed JSON messages |

743| `close()` | Close the connection and clean up resources |

744| `is_ready()` | Returns `True` if the transport can send and receive |

745| `end_input()` | Close the input stream (for example, close stdin for subprocess transports) |

746 

747Import: `from claude_agent_sdk import Transport`

748 

749### `ClaudeAgentOptions`

750 

751Configuration dataclass for Claude Code queries.

752 

753```python theme={null}

754@dataclass

755class ClaudeAgentOptions:

756 tools: list[str] | ToolsPreset | None = None

757 allowed_tools: list[str] = field(default_factory=list)

758 system_prompt: str | SystemPromptPreset | None = None

759 mcp_servers: dict[str, McpServerConfig] | str | Path = field(default_factory=dict)

760 permission_mode: PermissionMode | None = None

761 continue_conversation: bool = False

762 resume: str | None = None

763 max_turns: int | None = None

764 max_budget_usd: float | None = None

765 disallowed_tools: list[str] = field(default_factory=list)

766 model: str | None = None

767 fallback_model: str | None = None

768 betas: list[SdkBeta] = field(default_factory=list)

769 output_format: dict[str, Any] | None = None

770 permission_prompt_tool_name: str | None = None

771 cwd: str | Path | None = None

772 cli_path: str | Path | None = None

773 settings: str | None = None

774 add_dirs: list[str | Path] = field(default_factory=list)

775 env: dict[str, str] = field(default_factory=dict)

776 extra_args: dict[str, str | None] = field(default_factory=dict)

777 max_buffer_size: int | None = None

778 debug_stderr: Any = sys.stderr # Deprecated

779 stderr: Callable[[str], None] | None = None

780 can_use_tool: CanUseTool | None = None

781 hooks: dict[HookEvent, list[HookMatcher]] | None = None

782 user: str | None = None

783 include_partial_messages: bool = False

784 fork_session: bool = False

785 agents: dict[str, AgentDefinition] | None = None

786 setting_sources: list[SettingSource] | None = None

787 sandbox: SandboxSettings | None = None

788 plugins: list[SdkPluginConfig] = field(default_factory=list)

789 max_thinking_tokens: int | None = None # Deprecated: use thinking instead

790 thinking: ThinkingConfig | None = None

791 effort: Literal["low", "medium", "high", "max"] | None = None

792 enable_file_checkpointing: bool = False

793```

794 

795| Property | Type | Default | Description |

796| :---------------------------- | :------------------------------------------------ | :------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |

797| `tools` | `list[str] \| ToolsPreset \| None` | `None` | Tools configuration. Use `{"type": "preset", "preset": "claude_code"}` for Claude Code's default tools |

798| `allowed_tools` | `list[str]` | `[]` | Tools to auto-approve without prompting. This does not restrict Claude to only these tools; unlisted tools fall through to `permission_mode` and `can_use_tool`. Use `disallowed_tools` to block tools. See [Permissions](/en/agent-sdk/permissions#allow-and-deny-rules) |

799| `system_prompt` | `str \| SystemPromptPreset \| None` | `None` | System prompt configuration. Pass a string for custom prompt, or use `{"type": "preset", "preset": "claude_code"}` for Claude Code's system prompt. Add `"append"` to extend the preset |

800| `mcp_servers` | `dict[str, McpServerConfig] \| str \| Path` | `{}` | MCP server configurations or path to config file |

801| `permission_mode` | `PermissionMode \| None` | `None` | Permission mode for tool usage |

802| `continue_conversation` | `bool` | `False` | Continue the most recent conversation |

803| `resume` | `str \| None` | `None` | Session ID to resume |

804| `max_turns` | `int \| None` | `None` | Maximum agentic turns (tool-use round trips) |

805| `max_budget_usd` | `float \| None` | `None` | Maximum budget in USD for the session |

806| `disallowed_tools` | `list[str]` | `[]` | Tools to always deny. Deny rules are checked first and override `allowed_tools` and `permission_mode` (including `bypassPermissions`) |

807| `enable_file_checkpointing` | `bool` | `False` | Enable file change tracking for rewinding. See [File checkpointing](/en/agent-sdk/file-checkpointing) |

808| `model` | `str \| None` | `None` | Claude model to use |

809| `fallback_model` | `str \| None` | `None` | Fallback model to use if the primary model fails |

810| `betas` | `list[SdkBeta]` | `[]` | Beta features to enable. See [`SdkBeta`](#sdk-beta) for available options |

811| `output_format` | `dict[str, Any] \| None` | `None` | Output format for structured responses (e.g., `{"type": "json_schema", "schema": {...}}`). See [Structured outputs](/en/agent-sdk/structured-outputs) for details |

812| `permission_prompt_tool_name` | `str \| None` | `None` | MCP tool name for permission prompts |

813| `cwd` | `str \| Path \| None` | `None` | Current working directory |

814| `cli_path` | `str \| Path \| None` | `None` | Custom path to the Claude Code CLI executable |

815| `settings` | `str \| None` | `None` | Path to settings file |

816| `add_dirs` | `list[str \| Path]` | `[]` | Additional directories Claude can access |

817| `env` | `dict[str, str]` | `{}` | Environment variables |

818| `extra_args` | `dict[str, str \| None]` | `{}` | Additional CLI arguments to pass directly to the CLI |

819| `max_buffer_size` | `int \| None` | `None` | Maximum bytes when buffering CLI stdout |

820| `debug_stderr` | `Any` | `sys.stderr` | *Deprecated* - File-like object for debug output. Use `stderr` callback instead |

821| `stderr` | `Callable[[str], None] \| None` | `None` | Callback function for stderr output from CLI |

822| `can_use_tool` | [`CanUseTool`](#can-use-tool) ` \| None` | `None` | Tool permission callback function. See [Permission types](#can-use-tool) for details |

823| `hooks` | `dict[HookEvent, list[HookMatcher]] \| None` | `None` | Hook configurations for intercepting events |

824| `user` | `str \| None` | `None` | User identifier |

825| `include_partial_messages` | `bool` | `False` | Include partial message streaming events. When enabled, [`StreamEvent`](#stream-event) messages are yielded |

826| `fork_session` | `bool` | `False` | When resuming with `resume`, fork to a new session ID instead of continuing the original session |

827| `agents` | `dict[str, AgentDefinition] \| None` | `None` | Programmatically defined subagents |

828| `plugins` | `list[SdkPluginConfig]` | `[]` | Load custom plugins from local paths. See [Plugins](/en/agent-sdk/plugins) for details |

829| `sandbox` | [`SandboxSettings`](#sandbox-settings) ` \| None` | `None` | Configure sandbox behavior programmatically. See [Sandbox settings](#sandbox-settings) for details |

830| `setting_sources` | `list[SettingSource] \| None` | `None` (no settings) | Control which filesystem settings to load. When omitted, no settings are loaded. **Note:** Must include `"project"` to load CLAUDE.md files |

831| `max_thinking_tokens` | `int \| None` | `None` | *Deprecated* - Maximum tokens for thinking blocks. Use `thinking` instead |

832| `thinking` | [`ThinkingConfig`](#thinking-config) ` \| None` | `None` | Controls extended thinking behavior. Takes precedence over `max_thinking_tokens` |

833| `effort` | `Literal["low", "medium", "high", "max"] \| None` | `None` | Effort level for thinking depth |

834 

835### `OutputFormat`

836 

837Configuration for structured output validation. Pass this as a `dict` to the `output_format` field on `ClaudeAgentOptions`:

838 

839```python theme={null}

840# Expected dict shape for output_format

841{

842 "type": "json_schema",

843 "schema": {...}, # Your JSON Schema definition

844}

845```

846 

847| Field | Required | Description |

848| :------- | :------- | :------------------------------------------------- |

849| `type` | Yes | Must be `"json_schema"` for JSON Schema validation |

850| `schema` | Yes | JSON Schema definition for output validation |

851 

852### `SystemPromptPreset`

853 

854Configuration for using Claude Code's preset system prompt with optional additions.

855 

856```python theme={null}

857class SystemPromptPreset(TypedDict):

858 type: Literal["preset"]

859 preset: Literal["claude_code"]

860 append: NotRequired[str]

861```

862 

863| Field | Required | Description |

864| :------- | :------- | :------------------------------------------------------------ |

865| `type` | Yes | Must be `"preset"` to use a preset system prompt |

866| `preset` | Yes | Must be `"claude_code"` to use Claude Code's system prompt |

867| `append` | No | Additional instructions to append to the preset system prompt |

868 

869### `SettingSource`

870 

871Controls which filesystem-based configuration sources the SDK loads settings from.

872 

873```python theme={null}

874SettingSource = Literal["user", "project", "local"]

875```

876 

877| Value | Description | Location |

878| :---------- | :------------------------------------------- | :---------------------------- |

879| `"user"` | Global user settings | `~/.claude/settings.json` |

880| `"project"` | Shared project settings (version controlled) | `.claude/settings.json` |

881| `"local"` | Local project settings (gitignored) | `.claude/settings.local.json` |

882 

883#### Default behavior

884 

885When `setting_sources` is **omitted** or **`None`**, the SDK does **not** load any filesystem settings. This provides isolation for SDK applications.

886 

887#### Why use setting\_sources

888 

889**Load all filesystem settings (legacy behavior):**

890 

891```python theme={null}

892# Load all settings like SDK v0.0.x did

893from claude_agent_sdk import query, ClaudeAgentOptions

894 

895async for message in query(

896 prompt="Analyze this code",

897 options=ClaudeAgentOptions(

898 setting_sources=["user", "project", "local"] # Load all settings

899 ),

900):

901 print(message)

902```

903 

904**Load only specific setting sources:**

905 

906```python theme={null}

907# Load only project settings, ignore user and local

908async for message in query(

909 prompt="Run CI checks",

910 options=ClaudeAgentOptions(

911 setting_sources=["project"] # Only .claude/settings.json

912 ),

913):

914 print(message)

915```

916 

917**Testing and CI environments:**

918 

919```python theme={null}

920# Ensure consistent behavior in CI by excluding local settings

921async for message in query(

922 prompt="Run tests",

923 options=ClaudeAgentOptions(

924 setting_sources=["project"], # Only team-shared settings

925 permission_mode="bypassPermissions",

926 ),

927):

928 print(message)

929```

930 

931**SDK-only applications:**

932 

933```python theme={null}

934# Define everything programmatically (default behavior)

935# No filesystem dependencies - setting_sources defaults to None

936async for message in query(

937 prompt="Review this PR",

938 options=ClaudeAgentOptions(

939 # setting_sources=None is the default, no need to specify

940 agents={...},

941 mcp_servers={...},

942 allowed_tools=["Read", "Grep", "Glob"],

943 ),

944):

945 print(message)

946```

947 

948**Loading CLAUDE.md project instructions:**

949 

950```python theme={null}

951# Load project settings to include CLAUDE.md files

952async for message in query(

953 prompt="Add a new feature following project conventions",

954 options=ClaudeAgentOptions(

955 system_prompt={

956 "type": "preset",

957 "preset": "claude_code", # Use Claude Code's system prompt

958 },

959 setting_sources=["project"], # Required to load CLAUDE.md from project

960 allowed_tools=["Read", "Write", "Edit"],

961 ),

962):

963 print(message)

964```

965 

966#### Settings precedence

967 

968When multiple sources are loaded, settings are merged with this precedence (highest to lowest):

969 

9701. Local settings (`.claude/settings.local.json`)

9712. Project settings (`.claude/settings.json`)

9723. User settings (`~/.claude/settings.json`)

973 

974Programmatic options (like `agents`, `allowed_tools`) always override filesystem settings.

975 

976### `AgentDefinition`

977 

978Configuration for a subagent defined programmatically.

979 

980```python theme={null}

981@dataclass

982class AgentDefinition:

983 description: str

984 prompt: str

985 tools: list[str] | None = None

986 model: Literal["sonnet", "opus", "haiku", "inherit"] | None = None

987 skills: list[str] | None = None

988 memory: Literal["user", "project", "local"] | None = None

989 mcpServers: list[str | dict[str, Any]] | None = None

990```

991 

992| Field | Required | Description |

993| :------------ | :------- | :-------------------------------------------------------------------------------------------------- |

994| `description` | Yes | Natural language description of when to use this agent |

995| `prompt` | Yes | The agent's system prompt |

996| `tools` | No | Array of allowed tool names. If omitted, inherits all tools |

997| `model` | No | Model override for this agent. If omitted, uses the main model |

998| `skills` | No | List of skill names available to this agent |

999| `memory` | No | Memory source for this agent: `"user"`, `"project"`, or `"local"` |

1000| `mcpServers` | No | MCP servers available to this agent. Each entry is a server name or an inline `{name: config}` dict |

1001 

1002### `PermissionMode`

1003 

1004Permission modes for controlling tool execution.

1005 

1006```python theme={null}

1007PermissionMode = Literal[

1008 "default", # Standard permission behavior

1009 "acceptEdits", # Auto-accept file edits

1010 "plan", # Planning mode - no execution

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

1012 "bypassPermissions", # Bypass all permission checks (use with caution)

1013]

1014```

1015 

1016### `CanUseTool`

1017 

1018Type alias for tool permission callback functions.

1019 

1020```python theme={null}

1021CanUseTool = Callable[

1022 [str, dict[str, Any], ToolPermissionContext], Awaitable[PermissionResult]

1023]

1024```

1025 

1026The callback receives:

1027 

1028* `tool_name`: Name of the tool being called

1029* `input_data`: The tool's input parameters

1030* `context`: A `ToolPermissionContext` with additional information

1031 

1032Returns a `PermissionResult` (either `PermissionResultAllow` or `PermissionResultDeny`).

1033 

1034### `ToolPermissionContext`

1035 

1036Context information passed to tool permission callbacks.

1037 

1038```python theme={null}

1039@dataclass

1040class ToolPermissionContext:

1041 signal: Any | None = None # Future: abort signal support

1042 suggestions: list[PermissionUpdate] = field(default_factory=list)

1043```

1044 

1045| Field | Type | Description |

1046| :------------ | :----------------------- | :----------------------------------------- |

1047| `signal` | `Any \| None` | Reserved for future abort signal support |

1048| `suggestions` | `list[PermissionUpdate]` | Permission update suggestions from the CLI |

1049 

1050### `PermissionResult`

1051 

1052Union type for permission callback results.

1053 

1054```python theme={null}

1055PermissionResult = PermissionResultAllow | PermissionResultDeny

1056```

1057 

1058### `PermissionResultAllow`

1059 

1060Result indicating the tool call should be allowed.

1061 

1062```python theme={null}

1063@dataclass

1064class PermissionResultAllow:

1065 behavior: Literal["allow"] = "allow"

1066 updated_input: dict[str, Any] | None = None

1067 updated_permissions: list[PermissionUpdate] | None = None

1068```

1069 

1070| Field | Type | Default | Description |

1071| :-------------------- | :------------------------------- | :-------- | :---------------------------------------- |

1072| `behavior` | `Literal["allow"]` | `"allow"` | Must be "allow" |

1073| `updated_input` | `dict[str, Any] \| None` | `None` | Modified input to use instead of original |

1074| `updated_permissions` | `list[PermissionUpdate] \| None` | `None` | Permission updates to apply |

1075 

1076### `PermissionResultDeny`

1077 

1078Result indicating the tool call should be denied.

1079 

1080```python theme={null}

1081@dataclass

1082class PermissionResultDeny:

1083 behavior: Literal["deny"] = "deny"

1084 message: str = ""

1085 interrupt: bool = False

1086```

1087 

1088| Field | Type | Default | Description |

1089| :---------- | :---------------- | :------- | :----------------------------------------- |

1090| `behavior` | `Literal["deny"]` | `"deny"` | Must be "deny" |

1091| `message` | `str` | `""` | Message explaining why the tool was denied |

1092| `interrupt` | `bool` | `False` | Whether to interrupt the current execution |

1093 

1094### `PermissionUpdate`

1095 

1096Configuration for updating permissions programmatically.

1097 

1098```python theme={null}

1099@dataclass

1100class PermissionUpdate:

1101 type: Literal[

1102 "addRules",

1103 "replaceRules",

1104 "removeRules",

1105 "setMode",

1106 "addDirectories",

1107 "removeDirectories",

1108 ]

1109 rules: list[PermissionRuleValue] | None = None

1110 behavior: Literal["allow", "deny", "ask"] | None = None

1111 mode: PermissionMode | None = None

1112 directories: list[str] | None = None

1113 destination: (

1114 Literal["userSettings", "projectSettings", "localSettings", "session"] | None

1115 ) = None

1116```

1117 

1118| Field | Type | Description |

1119| :------------ | :---------------------------------------- | :---------------------------------------------- |

1120| `type` | `Literal[...]` | The type of permission update operation |

1121| `rules` | `list[PermissionRuleValue] \| None` | Rules for add/replace/remove operations |

1122| `behavior` | `Literal["allow", "deny", "ask"] \| None` | Behavior for rule-based operations |

1123| `mode` | `PermissionMode \| None` | Mode for setMode operation |

1124| `directories` | `list[str] \| None` | Directories for add/remove directory operations |

1125| `destination` | `Literal[...] \| None` | Where to apply the permission update |

1126 

1127### `PermissionRuleValue`

1128 

1129A rule to add, replace, or remove in a permission update.

1130 

1131```python theme={null}

1132@dataclass

1133class PermissionRuleValue:

1134 tool_name: str

1135 rule_content: str | None = None

1136```

1137 

1138### `ToolsPreset`

1139 

1140Preset tools configuration for using Claude Code's default tool set.

1141 

1142```python theme={null}

1143class ToolsPreset(TypedDict):

1144 type: Literal["preset"]

1145 preset: Literal["claude_code"]

1146```

1147 

1148### `ThinkingConfig`

1149 

1150Controls extended thinking behavior. A union of three configurations:

1151 

1152```python theme={null}

1153class ThinkingConfigAdaptive(TypedDict):

1154 type: Literal["adaptive"]

1155 

1156 

1157class ThinkingConfigEnabled(TypedDict):

1158 type: Literal["enabled"]

1159 budget_tokens: int

1160 

1161 

1162class ThinkingConfigDisabled(TypedDict):

1163 type: Literal["disabled"]

1164 

1165 

1166ThinkingConfig = ThinkingConfigAdaptive | ThinkingConfigEnabled | ThinkingConfigDisabled

1167```

1168 

1169| Variant | Fields | Description |

1170| :--------- | :---------------------- | :------------------------------------------- |

1171| `adaptive` | `type` | Claude adaptively decides when to think |

1172| `enabled` | `type`, `budget_tokens` | Enable thinking with a specific token budget |

1173| `disabled` | `type` | Disable thinking |

1174 

1175Because these are `TypedDict` classes, they're plain dicts at runtime. Either construct them as dict literals or call the class like a constructor; both produce a `dict`. Access fields with `config["budget_tokens"]`, not `config.budget_tokens`:

1176 

1177```python theme={null}

1178from claude_agent_sdk import ClaudeAgentOptions, ThinkingConfigEnabled

1179 

1180# Option 1: dict literal (recommended, no import needed)

1181options = ClaudeAgentOptions(thinking={"type": "enabled", "budget_tokens": 20000})

1182 

1183# Option 2: constructor-style (returns a plain dict)

1184config = ThinkingConfigEnabled(type="enabled", budget_tokens=20000)

1185print(config["budget_tokens"]) # 20000

1186# config.budget_tokens would raise AttributeError

1187```

1188 

1189### `SdkBeta`

1190 

1191Literal type for SDK beta features.

1192 

1193```python theme={null}

1194SdkBeta = Literal["context-1m-2025-08-07"]

1195```

1196 

1197Use with the `betas` field in `ClaudeAgentOptions` to enable beta features.

1198 

1199<Warning>

1200 The `context-1m-2025-08-07` beta is retired as of April 30, 2026. Passing this header with Claude Sonnet 4.5 or Sonnet 4 has no effect, and requests that exceed the standard 200k-token context window return an error. To use a 1M-token context window, migrate to [Claude Sonnet 4.6 or Claude Opus 4.6](https://platform.claude.com/docs/en/about-claude/models/overview), which include 1M context at standard pricing with no beta header required.

1201</Warning>

1202 

1203### `McpSdkServerConfig`

1204 

1205Configuration for SDK MCP servers created with `create_sdk_mcp_server()`.

1206 

1207```python theme={null}

1208class McpSdkServerConfig(TypedDict):

1209 type: Literal["sdk"]

1210 name: str

1211 instance: Any # MCP Server instance

1212```

1213 

1214### `McpServerConfig`

1215 

1216Union type for MCP server configurations.

1217 

1218```python theme={null}

1219McpServerConfig = (

1220 McpStdioServerConfig | McpSSEServerConfig | McpHttpServerConfig | McpSdkServerConfig

1221)

1222```

1223 

1224#### `McpStdioServerConfig`

1225 

1226```python theme={null}

1227class McpStdioServerConfig(TypedDict):

1228 type: NotRequired[Literal["stdio"]] # Optional for backwards compatibility

1229 command: str

1230 args: NotRequired[list[str]]

1231 env: NotRequired[dict[str, str]]

1232```

1233 

1234#### `McpSSEServerConfig`

1235 

1236```python theme={null}

1237class McpSSEServerConfig(TypedDict):

1238 type: Literal["sse"]

1239 url: str

1240 headers: NotRequired[dict[str, str]]

1241```

1242 

1243#### `McpHttpServerConfig`

1244 

1245```python theme={null}

1246class McpHttpServerConfig(TypedDict):

1247 type: Literal["http"]

1248 url: str

1249 headers: NotRequired[dict[str, str]]

1250```

1251 

1252### `McpServerStatusConfig`

1253 

1254The configuration of an MCP server as reported by [`get_mcp_status()`](#methods). This is the union of all [`McpServerConfig`](#mcp-server-config) transport variants plus an output-only `claudeai-proxy` variant for servers proxied through claude.ai.

1255 

1256```python theme={null}

1257McpServerStatusConfig = (

1258 McpStdioServerConfig

1259 | McpSSEServerConfig

1260 | McpHttpServerConfig

1261 | McpSdkServerConfigStatus

1262 | McpClaudeAIProxyServerConfig

1263)

1264```

1265 

1266`McpSdkServerConfigStatus` is the serializable form of [`McpSdkServerConfig`](#mcp-sdk-server-config) with only `type` (`"sdk"`) and `name` (`str`) fields; the in-process `instance` is omitted. `McpClaudeAIProxyServerConfig` has `type` (`"claudeai-proxy"`), `url` (`str`), and `id` (`str`) fields.

1267 

1268### `McpStatusResponse`

1269 

1270Response from [`ClaudeSDKClient.get_mcp_status()`](#methods). Wraps the list of server statuses under the `mcpServers` key.

1271 

1272```python theme={null}

1273class McpStatusResponse(TypedDict):

1274 mcpServers: list[McpServerStatus]

1275```

1276 

1277### `McpServerStatus`

1278 

1279Status of a connected MCP server, contained in [`McpStatusResponse`](#mcp-status-response).

1280 

1281```python theme={null}

1282class McpServerStatus(TypedDict):

1283 name: str

1284 status: McpServerConnectionStatus # "connected" | "failed" | "needs-auth" | "pending" | "disabled"

1285 serverInfo: NotRequired[McpServerInfo]

1286 error: NotRequired[str]

1287 config: NotRequired[McpServerStatusConfig]

1288 scope: NotRequired[str]

1289 tools: NotRequired[list[McpToolInfo]]

1290```

1291 

1292| Field | Type | Description |

1293| :----------- | :-------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |

1294| `name` | `str` | Server name |

1295| `status` | `str` | One of `"connected"`, `"failed"`, `"needs-auth"`, `"pending"`, or `"disabled"` |

1296| `serverInfo` | `dict` (optional) | Server name and version (`{"name": str, "version": str}`) |

1297| `error` | `str` (optional) | Error message if the server failed to connect |

1298| `config` | [`McpServerStatusConfig`](#mcp-server-status-config) (optional) | Server configuration. Same shape as [`McpServerConfig`](#mcp-server-config) (stdio, SSE, HTTP, or SDK), plus a `claudeai-proxy` variant for servers connected through claude.ai |

1299| `scope` | `str` (optional) | Configuration scope |

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

1301 

1302### `SdkPluginConfig`

1303 

1304Configuration for loading plugins in the SDK.

1305 

1306```python theme={null}

1307class SdkPluginConfig(TypedDict):

1308 type: Literal["local"]

1309 path: str

1310```

1311 

1312| Field | Type | Description |

1313| :----- | :----------------- | :--------------------------------------------------------- |

1314| `type` | `Literal["local"]` | Must be `"local"` (only local plugins currently supported) |

1315| `path` | `str` | Absolute or relative path to the plugin directory |

1316 

1317**Example:**

1318 

1319```python theme={null}

1320plugins = [

1321 {"type": "local", "path": "./my-plugin"},

1322 {"type": "local", "path": "/absolute/path/to/plugin"},

1323]

1324```

1325 

1326For complete information on creating and using plugins, see [Plugins](/en/agent-sdk/plugins).

1327 

1328## Message Types

1329 

1330### `Message`

1331 

1332Union type of all possible messages.

1333 

1334```python theme={null}

1335Message = (

1336 UserMessage

1337 | AssistantMessage

1338 | SystemMessage

1339 | ResultMessage

1340 | StreamEvent

1341 | RateLimitEvent

1342)

1343```

1344 

1345### `UserMessage`

1346 

1347User input message.

1348 

1349```python theme={null}

1350@dataclass

1351class UserMessage:

1352 content: str | list[ContentBlock]

1353 uuid: str | None = None

1354 parent_tool_use_id: str | None = None

1355 tool_use_result: dict[str, Any] | None = None

1356```

1357 

1358| Field | Type | Description |

1359| :------------------- | :-------------------------- | :---------------------------------------------------- |

1360| `content` | `str \| list[ContentBlock]` | Message content as text or content blocks |

1361| `uuid` | `str \| None` | Unique message identifier |

1362| `parent_tool_use_id` | `str \| None` | Tool use ID if this message is a tool result response |

1363| `tool_use_result` | `dict[str, Any] \| None` | Tool result data if applicable |

1364 

1365### `AssistantMessage`

1366 

1367Assistant response message with content blocks.

1368 

1369```python theme={null}

1370@dataclass

1371class AssistantMessage:

1372 content: list[ContentBlock]

1373 model: str

1374 parent_tool_use_id: str | None = None

1375 error: AssistantMessageError | None = None

1376 usage: dict[str, Any] | None = None

1377 message_id: str | None = None

1378```

1379 

1380| Field | Type | Description |

1381| :------------------- | :------------------------------------------------------------- | :------------------------------------------------------------------------------ |

1382| `content` | `list[ContentBlock]` | List of content blocks in the response |

1383| `model` | `str` | Model that generated the response |

1384| `parent_tool_use_id` | `str \| None` | Tool use ID if this is a nested response |

1385| `error` | [`AssistantMessageError`](#assistant-message-error) ` \| None` | Error type if the response encountered an error |

1386| `usage` | `dict[str, Any] \| None` | Per-message token usage (same keys as [`ResultMessage.usage`](#result-message)) |

1387| `message_id` | `str \| None` | API message ID. Multiple messages from one turn share the same ID |

1388 

1389### `AssistantMessageError`

1390 

1391Possible error types for assistant messages.

1392 

1393```python theme={null}

1394AssistantMessageError = Literal[

1395 "authentication_failed",

1396 "billing_error",

1397 "rate_limit",

1398 "invalid_request",

1399 "server_error",

1400 "max_output_tokens",

1401 "unknown",

1402]

1403```

1404 

1405### `SystemMessage`

1406 

1407System message with metadata.

1408 

1409```python theme={null}

1410@dataclass

1411class SystemMessage:

1412 subtype: str

1413 data: dict[str, Any]

1414```

1415 

1416### `ResultMessage`

1417 

1418Final result message with cost and usage information.

1419 

1420```python theme={null}

1421@dataclass

1422class ResultMessage:

1423 subtype: str

1424 duration_ms: int

1425 duration_api_ms: int

1426 is_error: bool

1427 num_turns: int

1428 session_id: str

1429 total_cost_usd: float | None = None

1430 usage: dict[str, Any] | None = None

1431 result: str | None = None

1432 stop_reason: str | None = None

1433 structured_output: Any = None

1434 model_usage: dict[str, Any] | None = None

1435```

1436 

1437The `usage` dict contains the following keys when present:

1438 

1439| Key | Type | Description |

1440| ----------------------------- | ----- | ---------------------------------------- |

1441| `input_tokens` | `int` | Total input tokens consumed. |

1442| `output_tokens` | `int` | Total output tokens generated. |

1443| `cache_creation_input_tokens` | `int` | Tokens used to create new cache entries. |

1444| `cache_read_input_tokens` | `int` | Tokens read from existing cache entries. |

1445 

1446The `model_usage` dict maps model names to per-model usage. The inner dict keys use camelCase because the value is passed through unmodified from the underlying CLI process, matching the TypeScript [`ModelUsage`](/en/agent-sdk/typescript#model-usage) type:

1447 

1448| Key | Type | Description |

1449| -------------------------- | ------- | ------------------------------------------ |

1450| `inputTokens` | `int` | Input tokens for this model. |

1451| `outputTokens` | `int` | Output tokens for this model. |

1452| `cacheReadInputTokens` | `int` | Cache read tokens for this model. |

1453| `cacheCreationInputTokens` | `int` | Cache creation tokens for this model. |

1454| `webSearchRequests` | `int` | Web search requests made by this model. |

1455| `costUSD` | `float` | Cost in USD for this model. |

1456| `contextWindow` | `int` | Context window size for this model. |

1457| `maxOutputTokens` | `int` | Maximum output token limit for this model. |

1458 

1459### `StreamEvent`

1460 

1461Stream event for partial message updates during streaming. Only received when `include_partial_messages=True` in `ClaudeAgentOptions`. Import via `from claude_agent_sdk.types import StreamEvent`.

1462 

1463```python theme={null}

1464@dataclass

1465class StreamEvent:

1466 uuid: str

1467 session_id: str

1468 event: dict[str, Any] # The raw Claude API stream event

1469 parent_tool_use_id: str | None = None

1470```

1471 

1472| Field | Type | Description |

1473| :------------------- | :--------------- | :-------------------------------------------------- |

1474| `uuid` | `str` | Unique identifier for this event |

1475| `session_id` | `str` | Session identifier |

1476| `event` | `dict[str, Any]` | The raw Claude API stream event data |

1477| `parent_tool_use_id` | `str \| None` | Parent tool use ID if this event is from a subagent |

1478 

1479### `RateLimitEvent`

1480 

1481Emitted when rate limit status changes (for example, from `"allowed"` to `"allowed_warning"`). Use this to warn users before they hit a hard limit, or to back off when status is `"rejected"`.

1482 

1483```python theme={null}

1484@dataclass

1485class RateLimitEvent:

1486 rate_limit_info: RateLimitInfo

1487 uuid: str

1488 session_id: str

1489```

1490 

1491| Field | Type | Description |

1492| :---------------- | :---------------------------------- | :----------------------- |

1493| `rate_limit_info` | [`RateLimitInfo`](#rate-limit-info) | Current rate limit state |

1494| `uuid` | `str` | Unique event identifier |

1495| `session_id` | `str` | Session identifier |

1496 

1497### `RateLimitInfo`

1498 

1499Rate limit state carried by [`RateLimitEvent`](#rate-limit-event).

1500 

1501```python theme={null}

1502RateLimitStatus = Literal["allowed", "allowed_warning", "rejected"]

1503RateLimitType = Literal[

1504 "five_hour", "seven_day", "seven_day_opus", "seven_day_sonnet", "overage"

1505]

1506 

1507 

1508@dataclass

1509class RateLimitInfo:

1510 status: RateLimitStatus

1511 resets_at: int | None = None

1512 rate_limit_type: RateLimitType | None = None

1513 utilization: float | None = None

1514 overage_status: RateLimitStatus | None = None

1515 overage_resets_at: int | None = None

1516 overage_disabled_reason: str | None = None

1517 raw: dict[str, Any] = field(default_factory=dict)

1518```

1519 

1520| Field | Type | Description |

1521| :------------------------ | :------------------------ | :---------------------------------------------------------------------------------------------------- |

1522| `status` | `RateLimitStatus` | Current status. `"allowed_warning"` means approaching the limit; `"rejected"` means the limit was hit |

1523| `resets_at` | `int \| None` | Unix timestamp when the rate limit window resets |

1524| `rate_limit_type` | `RateLimitType \| None` | Which rate limit window applies |

1525| `utilization` | `float \| None` | Fraction of the rate limit consumed (0.0 to 1.0) |

1526| `overage_status` | `RateLimitStatus \| None` | Status of pay-as-you-go overage usage, if applicable |

1527| `overage_resets_at` | `int \| None` | Unix timestamp when the overage window resets |

1528| `overage_disabled_reason` | `str \| None` | Why overage is unavailable, if status is `"rejected"` |

1529| `raw` | `dict[str, Any]` | Full raw dict from the CLI, including fields not modeled above |

1530 

1531### `TaskStartedMessage`

1532 

1533Emitted when a background task starts. A background task is anything tracked outside the main turn: a backgrounded Bash command, a subagent spawned via the Agent tool, or a remote agent. The `task_type` field tells you which. This naming is unrelated to the `Task`-to-`Agent` tool rename.

1534 

1535```python theme={null}

1536@dataclass

1537class TaskStartedMessage(SystemMessage):

1538 task_id: str

1539 description: str

1540 uuid: str

1541 session_id: str

1542 tool_use_id: str | None = None

1543 task_type: str | None = None

1544```

1545 

1546| Field | Type | Description |

1547| :------------ | :------------ | :------------------------------------------------------------------------------- |

1548| `task_id` | `str` | Unique identifier for the task |

1549| `description` | `str` | Description of the task |

1550| `uuid` | `str` | Unique message identifier |

1551| `session_id` | `str` | Session identifier |

1552| `tool_use_id` | `str \| None` | Associated tool use ID |

1553| `task_type` | `str \| None` | Which kind of background task: `"local_bash"`, `"local_agent"`, `"remote_agent"` |

1554 

1555### `TaskUsage`

1556 

1557Token and timing data for a background task.

1558 

1559```python theme={null}

1560class TaskUsage(TypedDict):

1561 total_tokens: int

1562 tool_uses: int

1563 duration_ms: int

1564```

1565 

1566### `TaskProgressMessage`

1567 

1568Emitted periodically with progress updates for a running background task.

1569 

1570```python theme={null}

1571@dataclass

1572class TaskProgressMessage(SystemMessage):

1573 task_id: str

1574 description: str

1575 usage: TaskUsage

1576 uuid: str

1577 session_id: str

1578 tool_use_id: str | None = None

1579 last_tool_name: str | None = None

1580```

1581 

1582| Field | Type | Description |

1583| :--------------- | :------------ | :---------------------------------- |

1584| `task_id` | `str` | Unique identifier for the task |

1585| `description` | `str` | Current status description |

1586| `usage` | `TaskUsage` | Token usage for this task so far |

1587| `uuid` | `str` | Unique message identifier |

1588| `session_id` | `str` | Session identifier |

1589| `tool_use_id` | `str \| None` | Associated tool use ID |

1590| `last_tool_name` | `str \| None` | Name of the last tool the task used |

1591 

1592### `TaskNotificationMessage`

1593 

1594Emitted when a task completes, fails, or is stopped.

1595 

1596```python theme={null}

1597@dataclass

1598class TaskNotificationMessage(SystemMessage):

1599 task_id: str

1600 status: TaskNotificationStatus # "completed" | "failed" | "stopped"

1601 output_file: str

1602 summary: str

1603 uuid: str

1604 session_id: str

1605 tool_use_id: str | None = None

1606 usage: TaskUsage | None = None

1607```

1608 

1609| Field | Type | Description |

1610| :------------ | :----------------------- | :----------------------------------------------- |

1611| `task_id` | `str` | Unique identifier for the task |

1612| `status` | `TaskNotificationStatus` | One of `"completed"`, `"failed"`, or `"stopped"` |

1613| `output_file` | `str` | Path to the task output file |

1614| `summary` | `str` | Summary of the task result |

1615| `uuid` | `str` | Unique message identifier |

1616| `session_id` | `str` | Session identifier |

1617| `tool_use_id` | `str \| None` | Associated tool use ID |

1618| `usage` | `TaskUsage \| None` | Final token usage for the task |

1619 

1620## Content Block Types

1621 

1622### `ContentBlock`

1623 

1624Union type of all content blocks.

1625 

1626```python theme={null}

1627ContentBlock = TextBlock | ThinkingBlock | ToolUseBlock | ToolResultBlock

1628```

1629 

1630### `TextBlock`

1631 

1632Text content block.

1633 

1634```python theme={null}

1635@dataclass

1636class TextBlock:

1637 text: str

1638```

1639 

1640### `ThinkingBlock`

1641 

1642Thinking content block (for models with thinking capability).

1643 

1644```python theme={null}

1645@dataclass

1646class ThinkingBlock:

1647 thinking: str

1648 signature: str

1649```

1650 

1651### `ToolUseBlock`

1652 

1653Tool use request block.

1654 

1655```python theme={null}

1656@dataclass

1657class ToolUseBlock:

1658 id: str

1659 name: str

1660 input: dict[str, Any]

1661```

1662 

1663### `ToolResultBlock`

1664 

1665Tool execution result block.

1666 

1667```python theme={null}

1668@dataclass

1669class ToolResultBlock:

1670 tool_use_id: str

1671 content: str | list[dict[str, Any]] | None = None

1672 is_error: bool | None = None

1673```

1674 

1675## Error Types

1676 

1677### `ClaudeSDKError`

1678 

1679Base exception class for all SDK errors.

1680 

1681```python theme={null}

1682class ClaudeSDKError(Exception):

1683 """Base error for Claude SDK."""

1684```

1685 

1686### `CLINotFoundError`

1687 

1688Raised when Claude Code CLI is not installed or not found.

1689 

1690```python theme={null}

1691class CLINotFoundError(CLIConnectionError):

1692 def __init__(

1693 self, message: str = "Claude Code not found", cli_path: str | None = None

1694 ):

1695 """

1696 Args:

1697 message: Error message (default: "Claude Code not found")

1698 cli_path: Optional path to the CLI that was not found

1699 """

1700```

1701 

1702### `CLIConnectionError`

1703 

1704Raised when connection to Claude Code fails.

1705 

1706```python theme={null}

1707class CLIConnectionError(ClaudeSDKError):

1708 """Failed to connect to Claude Code."""

1709```

1710 

1711### `ProcessError`

1712 

1713Raised when the Claude Code process fails.

1714 

1715```python theme={null}

1716class ProcessError(ClaudeSDKError):

1717 def __init__(

1718 self, message: str, exit_code: int | None = None, stderr: str | None = None

1719 ):

1720 self.exit_code = exit_code

1721 self.stderr = stderr

1722```

1723 

1724### `CLIJSONDecodeError`

1725 

1726Raised when JSON parsing fails.

1727 

1728```python theme={null}

1729class CLIJSONDecodeError(ClaudeSDKError):

1730 def __init__(self, line: str, original_error: Exception):

1731 """

1732 Args:

1733 line: The line that failed to parse

1734 original_error: The original JSON decode exception

1735 """

1736 self.line = line

1737 self.original_error = original_error

1738```

1739 

1740## Hook Types

1741 

1742For a comprehensive guide on using hooks with examples and common patterns, see the [Hooks guide](/en/agent-sdk/hooks).

1743 

1744### `HookEvent`

1745 

1746Supported hook event types.

1747 

1748```python theme={null}

1749HookEvent = Literal[

1750 "PreToolUse", # Called before tool execution

1751 "PostToolUse", # Called after tool execution

1752 "PostToolUseFailure", # Called when a tool execution fails

1753 "UserPromptSubmit", # Called when user submits a prompt

1754 "Stop", # Called when stopping execution

1755 "SubagentStop", # Called when a subagent stops

1756 "PreCompact", # Called before message compaction

1757 "Notification", # Called for notification events

1758 "SubagentStart", # Called when a subagent starts

1759 "PermissionRequest", # Called when a permission decision is needed

1760]

1761```

1762 

1763<Note>

1764 The TypeScript SDK supports additional hook events not yet available in Python: `SessionStart`, `SessionEnd`, `Setup`, `TeammateIdle`, `TaskCompleted`, `ConfigChange`, `WorktreeCreate`, and `WorktreeRemove`.

1765</Note>

1766 

1767### `HookCallback`

1768 

1769Type definition for hook callback functions.

1770 

1771```python theme={null}

1772HookCallback = Callable[[HookInput, str | None, HookContext], Awaitable[HookJSONOutput]]

1773```

1774 

1775Parameters:

1776 

1777* `input`: Strongly-typed hook input with discriminated unions based on `hook_event_name` (see [`HookInput`](#hook-input))

1778* `tool_use_id`: Optional tool use identifier (for tool-related hooks)

1779* `context`: Hook context with additional information

1780 

1781Returns a [`HookJSONOutput`](#hook-json-output) that may contain:

1782 

1783* `decision`: `"block"` to block the action

1784* `systemMessage`: System message to add to the transcript

1785* `hookSpecificOutput`: Hook-specific output data

1786 

1787### `HookContext`

1788 

1789Context information passed to hook callbacks.

1790 

1791```python theme={null}

1792class HookContext(TypedDict):

1793 signal: Any | None # Future: abort signal support

1794```

1795 

1796### `HookMatcher`

1797 

1798Configuration for matching hooks to specific events or tools.

1799 

1800```python theme={null}

1801@dataclass

1802class HookMatcher:

1803 matcher: str | None = (

1804 None # Tool name or pattern to match (e.g., "Bash", "Write|Edit")

1805 )

1806 hooks: list[HookCallback] = field(

1807 default_factory=list

1808 ) # List of callbacks to execute

1809 timeout: float | None = (

1810 None # Timeout in seconds for all hooks in this matcher (default: 60)

1811 )

1812```

1813 

1814### `HookInput`

1815 

1816Union type of all hook input types. The actual type depends on the `hook_event_name` field.

1817 

1818```python theme={null}

1819HookInput = (

1820 PreToolUseHookInput

1821 | PostToolUseHookInput

1822 | PostToolUseFailureHookInput

1823 | UserPromptSubmitHookInput

1824 | StopHookInput

1825 | SubagentStopHookInput

1826 | PreCompactHookInput

1827 | NotificationHookInput

1828 | SubagentStartHookInput

1829 | PermissionRequestHookInput

1830)

1831```

1832 

1833### `BaseHookInput`

1834 

1835Base fields present in all hook input types.

1836 

1837```python theme={null}

1838class BaseHookInput(TypedDict):

1839 session_id: str

1840 transcript_path: str

1841 cwd: str

1842 permission_mode: NotRequired[str]

1843```

1844 

1845| Field | Type | Description |

1846| :---------------- | :--------------- | :---------------------------------- |

1847| `session_id` | `str` | Current session identifier |

1848| `transcript_path` | `str` | Path to the session transcript file |

1849| `cwd` | `str` | Current working directory |

1850| `permission_mode` | `str` (optional) | Current permission mode |

1851 

1852### `PreToolUseHookInput`

1853 

1854Input data for `PreToolUse` hook events.

1855 

1856```python theme={null}

1857class PreToolUseHookInput(BaseHookInput):

1858 hook_event_name: Literal["PreToolUse"]

1859 tool_name: str

1860 tool_input: dict[str, Any]

1861 tool_use_id: str

1862 agent_id: NotRequired[str]

1863 agent_type: NotRequired[str]

1864```

1865 

1866| Field | Type | Description |

1867| :---------------- | :---------------------- | :----------------------------------------------------------------- |

1868| `hook_event_name` | `Literal["PreToolUse"]` | Always "PreToolUse" |

1869| `tool_name` | `str` | Name of the tool about to be executed |

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

1871| `tool_use_id` | `str` | Unique identifier for this tool use |

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

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

1874 

1875### `PostToolUseHookInput`

1876 

1877Input data for `PostToolUse` hook events.

1878 

1879```python theme={null}

1880class PostToolUseHookInput(BaseHookInput):

1881 hook_event_name: Literal["PostToolUse"]

1882 tool_name: str

1883 tool_input: dict[str, Any]

1884 tool_response: Any

1885 tool_use_id: str

1886 agent_id: NotRequired[str]

1887 agent_type: NotRequired[str]

1888```

1889 

1890| Field | Type | Description |

1891| :---------------- | :----------------------- | :----------------------------------------------------------------- |

1892| `hook_event_name` | `Literal["PostToolUse"]` | Always "PostToolUse" |

1893| `tool_name` | `str` | Name of the tool that was executed |

1894| `tool_input` | `dict[str, Any]` | Input parameters that were used |

1895| `tool_response` | `Any` | Response from the tool execution |

1896| `tool_use_id` | `str` | Unique identifier for this tool use |

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

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

1899 

1900### `PostToolUseFailureHookInput`

1901 

1902Input data for `PostToolUseFailure` hook events. Called when a tool execution fails.

1903 

1904```python theme={null}

1905class PostToolUseFailureHookInput(BaseHookInput):

1906 hook_event_name: Literal["PostToolUseFailure"]

1907 tool_name: str

1908 tool_input: dict[str, Any]

1909 tool_use_id: str

1910 error: str

1911 is_interrupt: NotRequired[bool]

1912 agent_id: NotRequired[str]

1913 agent_type: NotRequired[str]

1914```

1915 

1916| Field | Type | Description |

1917| :---------------- | :------------------------------ | :----------------------------------------------------------------- |

1918| `hook_event_name` | `Literal["PostToolUseFailure"]` | Always "PostToolUseFailure" |

1919| `tool_name` | `str` | Name of the tool that failed |

1920| `tool_input` | `dict[str, Any]` | Input parameters that were used |

1921| `tool_use_id` | `str` | Unique identifier for this tool use |

1922| `error` | `str` | Error message from the failed execution |

1923| `is_interrupt` | `bool` (optional) | Whether the failure was caused by an interrupt |

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

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

1926 

1927### `UserPromptSubmitHookInput`

1928 

1929Input data for `UserPromptSubmit` hook events.

1930 

1931```python theme={null}

1932class UserPromptSubmitHookInput(BaseHookInput):

1933 hook_event_name: Literal["UserPromptSubmit"]

1934 prompt: str

1935```

1936 

1937| Field | Type | Description |

1938| :---------------- | :---------------------------- | :-------------------------- |

1939| `hook_event_name` | `Literal["UserPromptSubmit"]` | Always "UserPromptSubmit" |

1940| `prompt` | `str` | The user's submitted prompt |

1941 

1942### `StopHookInput`

1943 

1944Input data for `Stop` hook events.

1945 

1946```python theme={null}

1947class StopHookInput(BaseHookInput):

1948 hook_event_name: Literal["Stop"]

1949 stop_hook_active: bool

1950```

1951 

1952| Field | Type | Description |

1953| :----------------- | :---------------- | :------------------------------ |

1954| `hook_event_name` | `Literal["Stop"]` | Always "Stop" |

1955| `stop_hook_active` | `bool` | Whether the stop hook is active |

1956 

1957### `SubagentStopHookInput`

1958 

1959Input data for `SubagentStop` hook events.

1960 

1961```python theme={null}

1962class SubagentStopHookInput(BaseHookInput):

1963 hook_event_name: Literal["SubagentStop"]

1964 stop_hook_active: bool

1965 agent_id: str

1966 agent_transcript_path: str

1967 agent_type: str

1968```

1969 

1970| Field | Type | Description |

1971| :---------------------- | :------------------------ | :------------------------------------- |

1972| `hook_event_name` | `Literal["SubagentStop"]` | Always "SubagentStop" |

1973| `stop_hook_active` | `bool` | Whether the stop hook is active |

1974| `agent_id` | `str` | Unique identifier for the subagent |

1975| `agent_transcript_path` | `str` | Path to the subagent's transcript file |

1976| `agent_type` | `str` | Type of the subagent |

1977 

1978### `PreCompactHookInput`

1979 

1980Input data for `PreCompact` hook events.

1981 

1982```python theme={null}

1983class PreCompactHookInput(BaseHookInput):

1984 hook_event_name: Literal["PreCompact"]

1985 trigger: Literal["manual", "auto"]

1986 custom_instructions: str | None

1987```

1988 

1989| Field | Type | Description |

1990| :-------------------- | :-------------------------- | :--------------------------------- |

1991| `hook_event_name` | `Literal["PreCompact"]` | Always "PreCompact" |

1992| `trigger` | `Literal["manual", "auto"]` | What triggered the compaction |

1993| `custom_instructions` | `str \| None` | Custom instructions for compaction |

1994 

1995### `NotificationHookInput`

1996 

1997Input data for `Notification` hook events.

1998 

1999```python theme={null}

2000class NotificationHookInput(BaseHookInput):

2001 hook_event_name: Literal["Notification"]

2002 message: str

2003 title: NotRequired[str]

2004 notification_type: str

2005```

2006 

2007| Field | Type | Description |

2008| :------------------ | :------------------------ | :--------------------------- |

2009| `hook_event_name` | `Literal["Notification"]` | Always "Notification" |

2010| `message` | `str` | Notification message content |

2011| `title` | `str` (optional) | Notification title |

2012| `notification_type` | `str` | Type of notification |

2013 

2014### `SubagentStartHookInput`

2015 

2016Input data for `SubagentStart` hook events.

2017 

2018```python theme={null}

2019class SubagentStartHookInput(BaseHookInput):

2020 hook_event_name: Literal["SubagentStart"]

2021 agent_id: str

2022 agent_type: str

2023```

2024 

2025| Field | Type | Description |

2026| :---------------- | :------------------------- | :--------------------------------- |

2027| `hook_event_name` | `Literal["SubagentStart"]` | Always "SubagentStart" |

2028| `agent_id` | `str` | Unique identifier for the subagent |

2029| `agent_type` | `str` | Type of the subagent |

2030 

2031### `PermissionRequestHookInput`

2032 

2033Input data for `PermissionRequest` hook events. Allows hooks to handle permission decisions programmatically.

2034 

2035```python theme={null}

2036class PermissionRequestHookInput(BaseHookInput):

2037 hook_event_name: Literal["PermissionRequest"]

2038 tool_name: str

2039 tool_input: dict[str, Any]

2040 permission_suggestions: NotRequired[list[Any]]

2041```

2042 

2043| Field | Type | Description |

2044| :----------------------- | :----------------------------- | :---------------------------------------- |

2045| `hook_event_name` | `Literal["PermissionRequest"]` | Always "PermissionRequest" |

2046| `tool_name` | `str` | Name of the tool requesting permission |

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

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

2049 

2050### `HookJSONOutput`

2051 

2052Union type for hook callback return values.

2053 

2054```python theme={null}

2055HookJSONOutput = AsyncHookJSONOutput | SyncHookJSONOutput

2056```

2057 

2058#### `SyncHookJSONOutput`

2059 

2060Synchronous hook output with control and decision fields.

2061 

2062```python theme={null}

2063class SyncHookJSONOutput(TypedDict):

2064 # Control fields

2065 continue_: NotRequired[bool] # Whether to proceed (default: True)

2066 suppressOutput: NotRequired[bool] # Hide stdout from transcript

2067 stopReason: NotRequired[str] # Message when continue is False

2068 

2069 # Decision fields

2070 decision: NotRequired[Literal["block"]]

2071 systemMessage: NotRequired[str] # Warning message for user

2072 reason: NotRequired[str] # Feedback for Claude

2073 

2074 # Hook-specific output

2075 hookSpecificOutput: NotRequired[HookSpecificOutput]

2076```

2077 

2078<Note>

2079 Use `continue_` (with underscore) in Python code. It is automatically converted to `continue` when sent to the CLI.

2080</Note>

2081 

2082#### `HookSpecificOutput`

2083 

2084A `TypedDict` containing the hook event name and event-specific fields. The shape depends on the `hookEventName` value. For full details on available fields per hook event, see [Control execution with hooks](/en/agent-sdk/hooks#outputs).

2085 

2086A discriminated union of event-specific output types. The `hookEventName` field determines which fields are valid.

2087 

2088```python theme={null}

2089class PreToolUseHookSpecificOutput(TypedDict):

2090 hookEventName: Literal["PreToolUse"]

2091 permissionDecision: NotRequired[Literal["allow", "deny", "ask"]]

2092 permissionDecisionReason: NotRequired[str]

2093 updatedInput: NotRequired[dict[str, Any]]

2094 additionalContext: NotRequired[str]

2095 

2096 

2097class PostToolUseHookSpecificOutput(TypedDict):

2098 hookEventName: Literal["PostToolUse"]

2099 additionalContext: NotRequired[str]

2100 updatedMCPToolOutput: NotRequired[Any]

2101 

2102 

2103class PostToolUseFailureHookSpecificOutput(TypedDict):

2104 hookEventName: Literal["PostToolUseFailure"]

2105 additionalContext: NotRequired[str]

2106 

2107 

2108class UserPromptSubmitHookSpecificOutput(TypedDict):

2109 hookEventName: Literal["UserPromptSubmit"]

2110 additionalContext: NotRequired[str]

2111 

2112 

2113class NotificationHookSpecificOutput(TypedDict):

2114 hookEventName: Literal["Notification"]

2115 additionalContext: NotRequired[str]

2116 

2117 

2118class SubagentStartHookSpecificOutput(TypedDict):

2119 hookEventName: Literal["SubagentStart"]

2120 additionalContext: NotRequired[str]

2121 

2122 

2123class PermissionRequestHookSpecificOutput(TypedDict):

2124 hookEventName: Literal["PermissionRequest"]

2125 decision: dict[str, Any]

2126 

2127 

2128HookSpecificOutput = (

2129 PreToolUseHookSpecificOutput

2130 | PostToolUseHookSpecificOutput

2131 | PostToolUseFailureHookSpecificOutput

2132 | UserPromptSubmitHookSpecificOutput

2133 | NotificationHookSpecificOutput

2134 | SubagentStartHookSpecificOutput

2135 | PermissionRequestHookSpecificOutput

2136)

2137```

2138 

2139#### `AsyncHookJSONOutput`

2140 

2141Async hook output that defers hook execution.

2142 

2143```python theme={null}

2144class AsyncHookJSONOutput(TypedDict):

2145 async_: Literal[True] # Set to True to defer execution

2146 asyncTimeout: NotRequired[int] # Timeout in milliseconds

2147```

2148 

2149<Note>

2150 Use `async_` (with underscore) in Python code. It is automatically converted to `async` when sent to the CLI.

2151</Note>

2152 

2153### Hook Usage Example

2154 

2155This example registers two hooks: one that blocks dangerous bash commands like `rm -rf /`, and another that logs all tool usage for auditing. The security hook only runs on Bash commands (via the `matcher`), while the logging hook runs on all tools.

2156 

2157```python theme={null}

2158from claude_agent_sdk import query, ClaudeAgentOptions, HookMatcher, HookContext

2159from typing import Any

2160 

2161 

2162async def validate_bash_command(

2163 input_data: dict[str, Any], tool_use_id: str | None, context: HookContext

2164) -> dict[str, Any]:

2165 """Validate and potentially block dangerous bash commands."""

2166 if input_data["tool_name"] == "Bash":

2167 command = input_data["tool_input"].get("command", "")

2168 if "rm -rf /" in command:

2169 return {

2170 "hookSpecificOutput": {

2171 "hookEventName": "PreToolUse",

2172 "permissionDecision": "deny",

2173 "permissionDecisionReason": "Dangerous command blocked",

2174 }

2175 }

2176 return {}

2177 

2178 

2179async def log_tool_use(

2180 input_data: dict[str, Any], tool_use_id: str | None, context: HookContext

2181) -> dict[str, Any]:

2182 """Log all tool usage for auditing."""

2183 print(f"Tool used: {input_data.get('tool_name')}")

2184 return {}

2185 

2186 

2187options = ClaudeAgentOptions(

2188 hooks={

2189 "PreToolUse": [

2190 HookMatcher(

2191 matcher="Bash", hooks=[validate_bash_command], timeout=120

2192 ), # 2 min for validation

2193 HookMatcher(

2194 hooks=[log_tool_use]

2195 ), # Applies to all tools (default 60s timeout)

2196 ],

2197 "PostToolUse": [HookMatcher(hooks=[log_tool_use])],

2198 }

2199)

2200 

2201async for message in query(prompt="Analyze this codebase", options=options):

2202 print(message)

2203```

2204 

2205## Tool Input/Output Types

2206 

2207Documentation 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.

2208 

2209### Agent

2210 

2211**Tool name:** `Agent` (previously `Task`, which is still accepted as an alias)

2212 

2213**Input:**

2214 

2215```python theme={null}

2216{

2217 "description": str, # A short (3-5 word) description of the task

2218 "prompt": str, # The task for the agent to perform

2219 "subagent_type": str, # The type of specialized agent to use

2220}

2221```

2222 

2223**Output:**

2224 

2225```python theme={null}

2226{

2227 "result": str, # Final result from the subagent

2228 "usage": dict | None, # Token usage statistics

2229 "total_cost_usd": float | None, # Total cost in USD

2230 "duration_ms": int | None, # Execution duration in milliseconds

2231}

2232```

2233 

2234### AskUserQuestion

2235 

2236**Tool name:** `AskUserQuestion`

2237 

2238Asks the user clarifying questions during execution. See [Handle approvals and user input](/en/agent-sdk/user-input#handle-clarifying-questions) for usage details.

2239 

2240**Input:**

2241 

2242```python theme={null}

2243{

2244 "questions": [ # Questions to ask the user (1-4 questions)

2245 {

2246 "question": str, # The complete question to ask the user

2247 "header": str, # Very short label displayed as a chip/tag (max 12 chars)

2248 "options": [ # The available choices (2-4 options)

2249 {

2250 "label": str, # Display text for this option (1-5 words)

2251 "description": str, # Explanation of what this option means

2252 }

2253 ],

2254 "multiSelect": bool, # Set to true to allow multiple selections

2255 }

2256 ],

2257 "answers": dict | None, # User answers populated by the permission system

2258}

2259```

2260 

2261**Output:**

2262 

2263```python theme={null}

2264{

2265 "questions": [ # The questions that were asked

2266 {

2267 "question": str,

2268 "header": str,

2269 "options": [{"label": str, "description": str}],

2270 "multiSelect": bool,

2271 }

2272 ],

2273 "answers": dict[str, str], # Maps question text to answer string

2274 # Multi-select answers are comma-separated

2275}

2276```

2277 

2278### Bash

2279 

2280**Tool name:** `Bash`

2281 

2282**Input:**

2283 

2284```python theme={null}

2285{

2286 "command": str, # The command to execute

2287 "timeout": int | None, # Optional timeout in milliseconds (max 600000)

2288 "description": str | None, # Clear, concise description (5-10 words)

2289 "run_in_background": bool | None, # Set to true to run in background

2290}

2291```

2292 

2293**Output:**

2294 

2295```python theme={null}

2296{

2297 "output": str, # Combined stdout and stderr output

2298 "exitCode": int, # Exit code of the command

2299 "killed": bool | None, # Whether command was killed due to timeout

2300 "shellId": str | None, # Shell ID for background processes

2301}

2302```

2303 

2304### Edit

2305 

2306**Tool name:** `Edit`

2307 

2308**Input:**

2309 

2310```python theme={null}

2311{

2312 "file_path": str, # The absolute path to the file to modify

2313 "old_string": str, # The text to replace

2314 "new_string": str, # The text to replace it with

2315 "replace_all": bool | None, # Replace all occurrences (default False)

2316}

2317```

2318 

2319**Output:**

2320 

2321```python theme={null}

2322{

2323 "message": str, # Confirmation message

2324 "replacements": int, # Number of replacements made

2325 "file_path": str, # File path that was edited

2326}

2327```

2328 

2329### Read

2330 

2331**Tool name:** `Read`

2332 

2333**Input:**

2334 

2335```python theme={null}

2336{

2337 "file_path": str, # The absolute path to the file to read

2338 "offset": int | None, # The line number to start reading from

2339 "limit": int | None, # The number of lines to read

2340}

2341```

2342 

2343**Output (Text files):**

2344 

2345```python theme={null}

2346{

2347 "content": str, # File contents with line numbers

2348 "total_lines": int, # Total number of lines in file

2349 "lines_returned": int, # Lines actually returned

2350}

2351```

2352 

2353**Output (Images):**

2354 

2355```python theme={null}

2356{

2357 "image": str, # Base64 encoded image data

2358 "mime_type": str, # Image MIME type

2359 "file_size": int, # File size in bytes

2360}

2361```

2362 

2363### Write

2364 

2365**Tool name:** `Write`

2366 

2367**Input:**

2368 

2369```python theme={null}

2370{

2371 "file_path": str, # The absolute path to the file to write

2372 "content": str, # The content to write to the file

2373}

2374```

2375 

2376**Output:**

2377 

2378```python theme={null}

2379{

2380 "message": str, # Success message

2381 "bytes_written": int, # Number of bytes written

2382 "file_path": str, # File path that was written

2383}

2384```

2385 

2386### Glob

2387 

2388**Tool name:** `Glob`

2389 

2390**Input:**

2391 

2392```python theme={null}

2393{

2394 "pattern": str, # The glob pattern to match files against

2395 "path": str | None, # The directory to search in (defaults to cwd)

2396}

2397```

2398 

2399**Output:**

2400 

2401```python theme={null}

2402{

2403 "matches": list[str], # Array of matching file paths

2404 "count": int, # Number of matches found

2405 "search_path": str, # Search directory used

2406}

2407```

2408 

2409### Grep

2410 

2411**Tool name:** `Grep`

2412 

2413**Input:**

2414 

2415```python theme={null}

2416{

2417 "pattern": str, # The regular expression pattern

2418 "path": str | None, # File or directory to search in

2419 "glob": str | None, # Glob pattern to filter files

2420 "type": str | None, # File type to search

2421 "output_mode": str | None, # "content", "files_with_matches", or "count"

2422 "-i": bool | None, # Case insensitive search

2423 "-n": bool | None, # Show line numbers

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

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

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

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

2428 "multiline": bool | None, # Enable multiline mode

2429}

2430```

2431 

2432**Output (content mode):**

2433 

2434```python theme={null}

2435{

2436 "matches": [

2437 {

2438 "file": str,

2439 "line_number": int | None,

2440 "line": str,

2441 "before_context": list[str] | None,

2442 "after_context": list[str] | None,

2443 }

2444 ],

2445 "total_matches": int,

2446}

2447```

2448 

2449**Output (files\_with\_matches mode):**

2450 

2451```python theme={null}

2452{

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

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

2455}

2456```

2457 

2458### NotebookEdit

2459 

2460**Tool name:** `NotebookEdit`

2461 

2462**Input:**

2463 

2464```python theme={null}

2465{

2466 "notebook_path": str, # Absolute path to the Jupyter notebook

2467 "cell_id": str | None, # The ID of the cell to edit

2468 "new_source": str, # The new source for the cell

2469 "cell_type": "code" | "markdown" | None, # The type of the cell

2470 "edit_mode": "replace" | "insert" | "delete" | None, # Edit operation type

2471}

2472```

2473 

2474**Output:**

2475 

2476```python theme={null}

2477{

2478 "message": str, # Success message

2479 "edit_type": "replaced" | "inserted" | "deleted", # Type of edit performed

2480 "cell_id": str | None, # Cell ID that was affected

2481 "total_cells": int, # Total cells in notebook after edit

2482}

2483```

2484 

2485### WebFetch

2486 

2487**Tool name:** `WebFetch`

2488 

2489**Input:**

2490 

2491```python theme={null}

2492{

2493 "url": str, # The URL to fetch content from

2494 "prompt": str, # The prompt to run on the fetched content

2495}

2496```

2497 

2498**Output:**

2499 

2500```python theme={null}

2501{

2502 "response": str, # AI model's response to the prompt

2503 "url": str, # URL that was fetched

2504 "final_url": str | None, # Final URL after redirects

2505 "status_code": int | None, # HTTP status code

2506}

2507```

2508 

2509### WebSearch

2510 

2511**Tool name:** `WebSearch`

2512 

2513**Input:**

2514 

2515```python theme={null}

2516{

2517 "query": str, # The search query to use

2518 "allowed_domains": list[str] | None, # Only include results from these domains

2519 "blocked_domains": list[str] | None, # Never include results from these domains

2520}

2521```

2522 

2523**Output:**

2524 

2525```python theme={null}

2526{

2527 "results": [{"title": str, "url": str, "snippet": str, "metadata": dict | None}],

2528 "total_results": int,

2529 "query": str,

2530}

2531```

2532 

2533### TodoWrite

2534 

2535**Tool name:** `TodoWrite`

2536 

2537**Input:**

2538 

2539```python theme={null}

2540{

2541 "todos": [

2542 {

2543 "content": str, # The task description

2544 "status": "pending" | "in_progress" | "completed", # Task status

2545 "activeForm": str, # Active form of the description

2546 }

2547 ]

2548}

2549```

2550 

2551**Output:**

2552 

2553```python theme={null}

2554{

2555 "message": str, # Success message

2556 "stats": {"total": int, "pending": int, "in_progress": int, "completed": int},

2557}

2558```

2559 

2560### BashOutput

2561 

2562**Tool name:** `BashOutput`

2563 

2564**Input:**

2565 

2566```python theme={null}

2567{

2568 "bash_id": str, # The ID of the background shell

2569 "filter": str | None, # Optional regex to filter output lines

2570}

2571```

2572 

2573**Output:**

2574 

2575```python theme={null}

2576{

2577 "output": str, # New output since last check

2578 "status": "running" | "completed" | "failed", # Current shell status

2579 "exitCode": int | None, # Exit code when completed

2580}

2581```

2582 

2583### KillBash

2584 

2585**Tool name:** `KillBash`

2586 

2587**Input:**

2588 

2589```python theme={null}

2590{

2591 "shell_id": str # The ID of the background shell to kill

2592}

2593```

2594 

2595**Output:**

2596 

2597```python theme={null}

2598{

2599 "message": str, # Success message

2600 "shell_id": str, # ID of the killed shell

2601}

2602```

2603 

2604### ExitPlanMode

2605 

2606**Tool name:** `ExitPlanMode`

2607 

2608**Input:**

2609 

2610```python theme={null}

2611{

2612 "plan": str # The plan to run by the user for approval

2613}

2614```

2615 

2616**Output:**

2617 

2618```python theme={null}

2619{

2620 "message": str, # Confirmation message

2621 "approved": bool | None, # Whether user approved the plan

2622}

2623```

2624 

2625### ListMcpResources

2626 

2627**Tool name:** `ListMcpResources`

2628 

2629**Input:**

2630 

2631```python theme={null}

2632{

2633 "server": str | None # Optional server name to filter resources by

2634}

2635```

2636 

2637**Output:**

2638 

2639```python theme={null}

2640{

2641 "resources": [

2642 {

2643 "uri": str,

2644 "name": str,

2645 "description": str | None,

2646 "mimeType": str | None,

2647 "server": str,

2648 }

2649 ],

2650 "total": int,

2651}

2652```

2653 

2654### ReadMcpResource

2655 

2656**Tool name:** `ReadMcpResource`

2657 

2658**Input:**

2659 

2660```python theme={null}

2661{

2662 "server": str, # The MCP server name

2663 "uri": str, # The resource URI to read

2664}

2665```

2666 

2667**Output:**

2668 

2669```python theme={null}

2670{

2671 "contents": [

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

2673 ],

2674 "server": str,

2675}

2676```

2677 

2678## Advanced Features with ClaudeSDKClient

2679 

2680### Building a Continuous Conversation Interface

2681 

2682```python theme={null}

2683from claude_agent_sdk import (

2684 ClaudeSDKClient,

2685 ClaudeAgentOptions,

2686 AssistantMessage,

2687 TextBlock,

2688)

2689import asyncio

2690 

2691 

2692class ConversationSession:

2693 """Maintains a single conversation session with Claude."""

2694 

2695 def __init__(self, options: ClaudeAgentOptions | None = None):

2696 self.client = ClaudeSDKClient(options)

2697 self.turn_count = 0

2698 

2699 async def start(self):

2700 await self.client.connect()

2701 print("Starting conversation session. Claude will remember context.")

2702 print(

2703 "Commands: 'exit' to quit, 'interrupt' to stop current task, 'new' for new session"

2704 )

2705 

2706 while True:

2707 user_input = input(f"\n[Turn {self.turn_count + 1}] You: ")

2708 

2709 if user_input.lower() == "exit":

2710 break

2711 elif user_input.lower() == "interrupt":

2712 await self.client.interrupt()

2713 print("Task interrupted!")

2714 continue

2715 elif user_input.lower() == "new":

2716 # Disconnect and reconnect for a fresh session

2717 await self.client.disconnect()

2718 await self.client.connect()

2719 self.turn_count = 0

2720 print("Started new conversation session (previous context cleared)")

2721 continue

2722 

2723 # Send message - the session retains all previous messages

2724 await self.client.query(user_input)

2725 self.turn_count += 1

2726 

2727 # Process response

2728 print(f"[Turn {self.turn_count}] Claude: ", end="")

2729 async for message in self.client.receive_response():

2730 if isinstance(message, AssistantMessage):

2731 for block in message.content:

2732 if isinstance(block, TextBlock):

2733 print(block.text, end="")

2734 print() # New line after response

2735 

2736 await self.client.disconnect()

2737 print(f"Conversation ended after {self.turn_count} turns.")

2738 

2739 

2740async def main():

2741 options = ClaudeAgentOptions(

2742 allowed_tools=["Read", "Write", "Bash"], permission_mode="acceptEdits"

2743 )

2744 session = ConversationSession(options)

2745 await session.start()

2746 

2747 

2748# Example conversation:

2749# Turn 1 - You: "Create a file called hello.py"

2750# Turn 1 - Claude: "I'll create a hello.py file for you..."

2751# Turn 2 - You: "What's in that file?"

2752# Turn 2 - Claude: "The hello.py file I just created contains..." (remembers!)

2753# Turn 3 - You: "Add a main function to it"

2754# Turn 3 - Claude: "I'll add a main function to hello.py..." (knows which file!)

2755 

2756asyncio.run(main())

2757```

2758 

2759### Using Hooks for Behavior Modification

2760 

2761```python theme={null}

2762from claude_agent_sdk import (

2763 ClaudeSDKClient,

2764 ClaudeAgentOptions,

2765 HookMatcher,

2766 HookContext,

2767)

2768import asyncio

2769from typing import Any

2770 

2771 

2772async def pre_tool_logger(

2773 input_data: dict[str, Any], tool_use_id: str | None, context: HookContext

2774) -> dict[str, Any]:

2775 """Log all tool usage before execution."""

2776 tool_name = input_data.get("tool_name", "unknown")

2777 print(f"[PRE-TOOL] About to use: {tool_name}")

2778 

2779 # You can modify or block the tool execution here

2780 if tool_name == "Bash" and "rm -rf" in str(input_data.get("tool_input", {})):

2781 return {

2782 "hookSpecificOutput": {

2783 "hookEventName": "PreToolUse",

2784 "permissionDecision": "deny",

2785 "permissionDecisionReason": "Dangerous command blocked",

2786 }

2787 }

2788 return {}

2789 

2790 

2791async def post_tool_logger(

2792 input_data: dict[str, Any], tool_use_id: str | None, context: HookContext

2793) -> dict[str, Any]:

2794 """Log results after tool execution."""

2795 tool_name = input_data.get("tool_name", "unknown")

2796 print(f"[POST-TOOL] Completed: {tool_name}")

2797 return {}

2798 

2799 

2800async def user_prompt_modifier(

2801 input_data: dict[str, Any], tool_use_id: str | None, context: HookContext

2802) -> dict[str, Any]:

2803 """Add context to user prompts."""

2804 original_prompt = input_data.get("prompt", "")

2805 

2806 # Add a timestamp as additional context for Claude to see

2807 from datetime import datetime

2808 

2809 timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")

2810 

2811 return {

2812 "hookSpecificOutput": {

2813 "hookEventName": "UserPromptSubmit",

2814 "additionalContext": f"[Submitted at {timestamp}] Original prompt: {original_prompt}",

2815 }

2816 }

2817 

2818 

2819async def main():

2820 options = ClaudeAgentOptions(

2821 hooks={

2822 "PreToolUse": [

2823 HookMatcher(hooks=[pre_tool_logger]),

2824 HookMatcher(matcher="Bash", hooks=[pre_tool_logger]),

2825 ],

2826 "PostToolUse": [HookMatcher(hooks=[post_tool_logger])],

2827 "UserPromptSubmit": [HookMatcher(hooks=[user_prompt_modifier])],

2828 },

2829 allowed_tools=["Read", "Write", "Bash"],

2830 )

2831 

2832 async with ClaudeSDKClient(options=options) as client:

2833 await client.query("List files in current directory")

2834 

2835 async for message in client.receive_response():

2836 # Hooks will automatically log tool usage

2837 pass

2838 

2839 

2840asyncio.run(main())

2841```

2842 

2843### Real-time Progress Monitoring

2844 

2845```python theme={null}

2846from claude_agent_sdk import (

2847 ClaudeSDKClient,

2848 ClaudeAgentOptions,

2849 AssistantMessage,

2850 ToolUseBlock,

2851 ToolResultBlock,

2852 TextBlock,

2853)

2854import asyncio

2855 

2856 

2857async def monitor_progress():

2858 options = ClaudeAgentOptions(

2859 allowed_tools=["Write", "Bash"], permission_mode="acceptEdits"

2860 )

2861 

2862 async with ClaudeSDKClient(options=options) as client:

2863 await client.query("Create 5 Python files with different sorting algorithms")

2864 

2865 # Monitor progress in real-time

2866 async for message in client.receive_response():

2867 if isinstance(message, AssistantMessage):

2868 for block in message.content:

2869 if isinstance(block, ToolUseBlock):

2870 if block.name == "Write":

2871 file_path = block.input.get("file_path", "")

2872 print(f"Creating: {file_path}")

2873 elif isinstance(block, ToolResultBlock):

2874 print("Completed tool execution")

2875 elif isinstance(block, TextBlock):

2876 print(f"Claude says: {block.text[:100]}...")

2877 

2878 print("Task completed!")

2879 

2880 

2881asyncio.run(monitor_progress())

2882```

2883 

2884## Example Usage

2885 

2886### Basic file operations (using query)

2887 

2888```python theme={null}

2889from claude_agent_sdk import query, ClaudeAgentOptions, AssistantMessage, ToolUseBlock

2890import asyncio

2891 

2892 

2893async def create_project():

2894 options = ClaudeAgentOptions(

2895 allowed_tools=["Read", "Write", "Bash"],

2896 permission_mode="acceptEdits",

2897 cwd="/home/user/project",

2898 )

2899 

2900 async for message in query(

2901 prompt="Create a Python project structure with setup.py", options=options

2902 ):

2903 if isinstance(message, AssistantMessage):

2904 for block in message.content:

2905 if isinstance(block, ToolUseBlock):

2906 print(f"Using tool: {block.name}")

2907 

2908 

2909asyncio.run(create_project())

2910```

2911 

2912### Error handling

2913 

2914```python theme={null}

2915from claude_agent_sdk import query, CLINotFoundError, ProcessError, CLIJSONDecodeError

2916 

2917try:

2918 async for message in query(prompt="Hello"):

2919 print(message)

2920except CLINotFoundError:

2921 print(

2922 "Claude Code CLI not found. Try reinstalling: pip install --force-reinstall claude-agent-sdk"

2923 )

2924except ProcessError as e:

2925 print(f"Process failed with exit code: {e.exit_code}")

2926except CLIJSONDecodeError as e:

2927 print(f"Failed to parse response: {e}")

2928```

2929 

2930### Streaming mode with client

2931 

2932```python theme={null}

2933from claude_agent_sdk import ClaudeSDKClient

2934import asyncio

2935 

2936 

2937async def interactive_session():

2938 async with ClaudeSDKClient() as client:

2939 # Send initial message

2940 await client.query("What's the weather like?")

2941 

2942 # Process responses

2943 async for msg in client.receive_response():

2944 print(msg)

2945 

2946 # Send follow-up

2947 await client.query("Tell me more about that")

2948 

2949 # Process follow-up response

2950 async for msg in client.receive_response():

2951 print(msg)

2952 

2953 

2954asyncio.run(interactive_session())

2955```

2956 

2957### Using custom tools with ClaudeSDKClient

2958 

2959```python theme={null}

2960from claude_agent_sdk import (

2961 ClaudeSDKClient,

2962 ClaudeAgentOptions,

2963 tool,

2964 create_sdk_mcp_server,

2965 AssistantMessage,

2966 TextBlock,

2967)

2968import asyncio

2969from typing import Any

2970 

2971 

2972# Define custom tools with @tool decorator

2973@tool("calculate", "Perform mathematical calculations", {"expression": str})

2974async def calculate(args: dict[str, Any]) -> dict[str, Any]:

2975 try:

2976 result = eval(args["expression"], {"__builtins__": {}})

2977 return {"content": [{"type": "text", "text": f"Result: {result}"}]}

2978 except Exception as e:

2979 return {

2980 "content": [{"type": "text", "text": f"Error: {str(e)}"}],

2981 "is_error": True,

2982 }

2983 

2984 

2985@tool("get_time", "Get current time", {})

2986async def get_time(args: dict[str, Any]) -> dict[str, Any]:

2987 from datetime import datetime

2988 

2989 current_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")

2990 return {"content": [{"type": "text", "text": f"Current time: {current_time}"}]}

2991 

2992 

2993async def main():

2994 # Create SDK MCP server with custom tools

2995 my_server = create_sdk_mcp_server(

2996 name="utilities", version="1.0.0", tools=[calculate, get_time]

2997 )

2998 

2999 # Configure options with the server

3000 options = ClaudeAgentOptions(

3001 mcp_servers={"utils": my_server},

3002 allowed_tools=["mcp__utils__calculate", "mcp__utils__get_time"],

3003 )

3004 

3005 # Use ClaudeSDKClient for interactive tool usage

3006 async with ClaudeSDKClient(options=options) as client:

3007 await client.query("What's 123 * 456?")

3008 

3009 # Process calculation response

3010 async for message in client.receive_response():

3011 if isinstance(message, AssistantMessage):

3012 for block in message.content:

3013 if isinstance(block, TextBlock):

3014 print(f"Calculation: {block.text}")

3015 

3016 # Follow up with time query

3017 await client.query("What time is it now?")

3018 

3019 async for message in client.receive_response():

3020 if isinstance(message, AssistantMessage):

3021 for block in message.content:

3022 if isinstance(block, TextBlock):

3023 print(f"Time: {block.text}")

3024 

3025 

3026asyncio.run(main())

3027```

3028 

3029## Sandbox Configuration

3030 

3031### `SandboxSettings`

3032 

3033Configuration for sandbox behavior. Use this to enable command sandboxing and configure network restrictions programmatically.

3034 

3035```python theme={null}

3036class SandboxSettings(TypedDict, total=False):

3037 enabled: bool

3038 autoAllowBashIfSandboxed: bool

3039 excludedCommands: list[str]

3040 allowUnsandboxedCommands: bool

3041 network: SandboxNetworkConfig

3042 ignoreViolations: SandboxIgnoreViolations

3043 enableWeakerNestedSandbox: bool

3044```

3045 

3046| Property | Type | Default | Description |

3047| :-------------------------- | :------------------------------------------------------ | :------ | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |

3048| `enabled` | `bool` | `False` | Enable sandbox mode for command execution |

3049| `autoAllowBashIfSandboxed` | `bool` | `True` | Auto-approve bash commands when sandbox is enabled |

3050| `excludedCommands` | `list[str]` | `[]` | Commands that always bypass sandbox restrictions (e.g., `["docker"]`). These run unsandboxed automatically without model involvement |

3051| `allowUnsandboxedCommands` | `bool` | `True` | Allow the model to request running commands outside the sandbox. When `True`, the model can set `dangerouslyDisableSandbox` in tool input, which falls back to the [permissions system](#permissions-fallback-for-unsandboxed-commands) |

3052| `network` | [`SandboxNetworkConfig`](#sandbox-network-config) | `None` | Network-specific sandbox configuration |

3053| `ignoreViolations` | [`SandboxIgnoreViolations`](#sandbox-ignore-violations) | `None` | Configure which sandbox violations to ignore |

3054| `enableWeakerNestedSandbox` | `bool` | `False` | Enable a weaker nested sandbox for compatibility |

3055 

3056<Note>

3057 **Filesystem and network access restrictions** are NOT configured via sandbox settings. Instead, they are derived from [permission rules](/en/settings#permission-settings):

3058 

3059 * **Filesystem read restrictions**: Read deny rules

3060 * **Filesystem write restrictions**: Edit allow/deny rules

3061 * **Network restrictions**: WebFetch allow/deny rules

3062 

3063 Use sandbox settings for command execution sandboxing, and permission rules for filesystem and network access control.

3064</Note>

3065 

3066#### Example usage

3067 

3068```python theme={null}

3069from claude_agent_sdk import query, ClaudeAgentOptions, SandboxSettings

3070 

3071sandbox_settings: SandboxSettings = {

3072 "enabled": True,

3073 "autoAllowBashIfSandboxed": True,

3074 "network": {"allowLocalBinding": True},

3075}

3076 

3077async for message in query(

3078 prompt="Build and test my project",

3079 options=ClaudeAgentOptions(sandbox=sandbox_settings),

3080):

3081 print(message)

3082```

3083 

3084<Warning>

3085 **Unix socket security**: The `allowUnixSockets` option can grant access to powerful system services. For example, allowing `/var/run/docker.sock` effectively grants full host system access through the Docker API, bypassing sandbox isolation. Only allow Unix sockets that are strictly necessary and understand the security implications of each.

3086</Warning>

3087 

3088### `SandboxNetworkConfig`

3089 

3090Network-specific configuration for sandbox mode.

3091 

3092```python theme={null}

3093class SandboxNetworkConfig(TypedDict, total=False):

3094 allowLocalBinding: bool

3095 allowUnixSockets: list[str]

3096 allowAllUnixSockets: bool

3097 httpProxyPort: int

3098 socksProxyPort: int

3099```

3100 

3101| Property | Type | Default | Description |

3102| :-------------------- | :---------- | :------ | :---------------------------------------------------------------- |

3103| `allowLocalBinding` | `bool` | `False` | Allow processes to bind to local ports (e.g., for dev servers) |

3104| `allowUnixSockets` | `list[str]` | `[]` | Unix socket paths that processes can access (e.g., Docker socket) |

3105| `allowAllUnixSockets` | `bool` | `False` | Allow access to all Unix sockets |

3106| `httpProxyPort` | `int` | `None` | HTTP proxy port for network requests |

3107| `socksProxyPort` | `int` | `None` | SOCKS proxy port for network requests |

3108 

3109### `SandboxIgnoreViolations`

3110 

3111Configuration for ignoring specific sandbox violations.

3112 

3113```python theme={null}

3114class SandboxIgnoreViolations(TypedDict, total=False):

3115 file: list[str]

3116 network: list[str]

3117```

3118 

3119| Property | Type | Default | Description |

3120| :-------- | :---------- | :------ | :------------------------------------------ |

3121| `file` | `list[str]` | `[]` | File path patterns to ignore violations for |

3122| `network` | `list[str]` | `[]` | Network patterns to ignore violations for |

3123 

3124### Permissions Fallback for Unsandboxed Commands

3125 

3126When `allowUnsandboxedCommands` is enabled, the model can request to run commands outside the sandbox by setting `dangerouslyDisableSandbox: True` in the tool input. These requests fall back to the existing permissions system, meaning your `can_use_tool` handler will be invoked, allowing you to implement custom authorization logic.

3127 

3128<Note>

3129 **`excludedCommands` vs `allowUnsandboxedCommands`:**

3130 

3131 * `excludedCommands`: A static list of commands that always bypass the sandbox automatically (e.g., `["docker"]`). The model has no control over this.

3132 * `allowUnsandboxedCommands`: Lets the model decide at runtime whether to request unsandboxed execution by setting `dangerouslyDisableSandbox: True` in the tool input.

3133</Note>

3134 

3135```python theme={null}

3136from claude_agent_sdk import (

3137 query,

3138 ClaudeAgentOptions,

3139 HookMatcher,

3140 PermissionResultAllow,

3141 PermissionResultDeny,

3142 ToolPermissionContext,

3143)

3144 

3145 

3146async def can_use_tool(

3147 tool: str, input: dict, context: ToolPermissionContext

3148) -> PermissionResultAllow | PermissionResultDeny:

3149 # Check if the model is requesting to bypass the sandbox

3150 if tool == "Bash" and input.get("dangerouslyDisableSandbox"):

3151 # The model is requesting to run this command outside the sandbox

3152 print(f"Unsandboxed command requested: {input.get('command')}")

3153 

3154 if is_command_authorized(input.get("command")):

3155 return PermissionResultAllow()

3156 return PermissionResultDeny(

3157 message="Command not authorized for unsandboxed execution"

3158 )

3159 return PermissionResultAllow()

3160 

3161 

3162# Required: dummy hook keeps the stream open for can_use_tool

3163async def dummy_hook(input_data, tool_use_id, context):

3164 return {"continue_": True}

3165 

3166 

3167async def prompt_stream():

3168 yield {

3169 "type": "user",

3170 "message": {"role": "user", "content": "Deploy my application"},

3171 }

3172 

3173 

3174async def main():

3175 async for message in query(

3176 prompt=prompt_stream(),

3177 options=ClaudeAgentOptions(

3178 sandbox={

3179 "enabled": True,

3180 "allowUnsandboxedCommands": True, # Model can request unsandboxed execution

3181 },

3182 permission_mode="default",

3183 can_use_tool=can_use_tool,

3184 hooks={"PreToolUse": [HookMatcher(matcher=None, hooks=[dummy_hook])]},

3185 ),

3186 ):

3187 print(message)

3188```

3189 

3190This pattern enables you to:

3191 

3192* **Audit model requests**: Log when the model requests unsandboxed execution

3193* **Implement allowlists**: Only permit specific commands to run unsandboxed

3194* **Add approval workflows**: Require explicit authorization for privileged operations

3195 

3196<Warning>

3197 Commands running with `dangerouslyDisableSandbox: True` have full system access. Ensure your `can_use_tool` handler validates these requests carefully.

3198 

3199 If `permission_mode` is set to `bypassPermissions` and `allow_unsandboxed_commands` is enabled, the model can autonomously execute commands outside the sandbox without any approval prompts. This combination effectively allows the model to escape sandbox isolation silently.

3200</Warning>

3201 

3202## See also

3203 

3204* [SDK overview](/en/agent-sdk/overview) - General SDK concepts

3205* [TypeScript SDK reference](/en/agent-sdk/typescript) - TypeScript SDK documentation

3206* [CLI reference](/en/cli-reference) - Command-line interface

3207* [Common workflows](/en/common-workflows) - Step-by-step guides

agent-sdk/quickstart.md +317 −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# Quickstart

6 

7> Get started with the Python or TypeScript Agent SDK to build AI agents that work autonomously

8 

9Use the Agent SDK to build an AI agent that reads your code, finds bugs, and fixes them, all without manual intervention.

10 

11**What you'll do:**

12 

131. Set up a project with the Agent SDK

142. Create a file with some buggy code

153. Run an agent that finds and fixes the bugs automatically

16 

17## Prerequisites

18 

19* **Node.js 18+** or **Python 3.10+**

20* An **Anthropic account** ([sign up here](https://platform.claude.com/))

21 

22## Setup

23 

24<Steps>

25 <Step title="Create a project folder">

26 Create a new directory for this quickstart:

27 

28 ```bash theme={null}

29 mkdir my-agent && cd my-agent

30 ```

31 

32 For your own projects, you can run the SDK from any folder; it will have access to files in that directory and its subdirectories by default.

33 </Step>

34 

35 <Step title="Install the SDK">

36 Install the Agent SDK package for your language:

37 

38 <Tabs>

39 <Tab title="TypeScript">

40 ```bash theme={null}

41 npm install @anthropic-ai/claude-agent-sdk

42 ```

43 </Tab>

44 

45 <Tab title="Python (uv)">

46 [uv Python package manager](https://docs.astral.sh/uv/) is a fast Python package manager that handles virtual environments automatically:

47 

48 ```bash theme={null}

49 uv init && uv add claude-agent-sdk

50 ```

51 </Tab>

52 

53 <Tab title="Python (pip)">

54 Create a virtual environment first, then install:

55 

56 ```bash theme={null}

57 python3 -m venv .venv && source .venv/bin/activate

58 pip3 install claude-agent-sdk

59 ```

60 </Tab>

61 </Tabs>

62 </Step>

63 

64 <Step title="Set your API key">

65 Get an API key from the [Claude Console](https://platform.claude.com/), then create a `.env` file in your project directory:

66 

67 ```bash theme={null}

68 ANTHROPIC_API_KEY=your-api-key

69 ```

70 

71 The SDK also supports authentication via third-party API providers:

72 

73 * **Amazon Bedrock**: set `CLAUDE_CODE_USE_BEDROCK=1` environment variable and configure AWS credentials

74 * **Google Vertex AI**: set `CLAUDE_CODE_USE_VERTEX=1` environment variable and configure Google Cloud credentials

75 * **Microsoft Azure**: set `CLAUDE_CODE_USE_FOUNDRY=1` environment variable and configure Azure credentials

76 

77 See the setup guides for [Bedrock](/en/amazon-bedrock), [Vertex AI](/en/google-vertex-ai), or [Azure AI Foundry](/en/microsoft-foundry) for details.

78 

79 <Note>

80 Unless previously approved, Anthropic does not allow third party developers to offer claude.ai login or rate limits for their products, including agents built on the Claude Agent SDK. Please use the API key authentication methods described in this document instead.

81 </Note>

82 </Step>

83</Steps>

84 

85## Create a buggy file

86 

87This quickstart walks you through building an agent that can find and fix bugs in code. First, you need a file with some intentional bugs for the agent to fix. Create `utils.py` in the `my-agent` directory and paste the following code:

88 

89```python theme={null}

90def calculate_average(numbers):

91 total = 0

92 for num in numbers:

93 total += num

94 return total / len(numbers)

95 

96 

97def get_user_name(user):

98 return user["name"].upper()

99```

100 

101This code has two bugs:

102 

1031. `calculate_average([])` crashes with division by zero

1042. `get_user_name(None)` crashes with a TypeError

105 

106## Build an agent that finds and fixes bugs

107 

108Create `agent.py` if you're using the Python SDK, or `agent.ts` for TypeScript:

109 

110<CodeGroup>

111 ```python Python theme={null}

112 import asyncio

113 from claude_agent_sdk import query, ClaudeAgentOptions, AssistantMessage, ResultMessage

114 

115 

116 async def main():

117 # Agentic loop: streams messages as Claude works

118 async for message in query(

119 prompt="Review utils.py for bugs that would cause crashes. Fix any issues you find.",

120 options=ClaudeAgentOptions(

121 allowed_tools=["Read", "Edit", "Glob"], # Tools Claude can use

122 permission_mode="acceptEdits", # Auto-approve file edits

123 ),

124 ):

125 # Print human-readable output

126 if isinstance(message, AssistantMessage):

127 for block in message.content:

128 if hasattr(block, "text"):

129 print(block.text) # Claude's reasoning

130 elif hasattr(block, "name"):

131 print(f"Tool: {block.name}") # Tool being called

132 elif isinstance(message, ResultMessage):

133 print(f"Done: {message.subtype}") # Final result

134 

135 

136 asyncio.run(main())

137 ```

138 

139 ```typescript TypeScript theme={null}

140 import { query } from "@anthropic-ai/claude-agent-sdk";

141 

142 // Agentic loop: streams messages as Claude works

143 for await (const message of query({

144 prompt: "Review utils.py for bugs that would cause crashes. Fix any issues you find.",

145 options: {

146 allowedTools: ["Read", "Edit", "Glob"], // Tools Claude can use

147 permissionMode: "acceptEdits" // Auto-approve file edits

148 }

149 })) {

150 // Print human-readable output

151 if (message.type === "assistant" && message.message?.content) {

152 for (const block of message.message.content) {

153 if ("text" in block) {

154 console.log(block.text); // Claude's reasoning

155 } else if ("name" in block) {

156 console.log(`Tool: ${block.name}`); // Tool being called

157 }

158 }

159 } else if (message.type === "result") {

160 console.log(`Done: ${message.subtype}`); // Final result

161 }

162 }

163 ```

164</CodeGroup>

165 

166This code has three main parts:

167 

1681. **`query`**: the main entry point that creates the agentic loop. It returns an async iterator, so you use `async for` to stream messages as Claude works. See the full API in the [Python](/en/agent-sdk/python#query) or [TypeScript](/en/agent-sdk/typescript#query) SDK reference.

169 

1702. **`prompt`**: what you want Claude to do. Claude figures out which tools to use based on the task.

171 

1723. **`options`**: configuration for the agent. This example uses `allowedTools` to pre-approve `Read`, `Edit`, and `Glob`, and `permissionMode: "acceptEdits"` to auto-approve file changes. Other options include `systemPrompt`, `mcpServers`, and more. See all options for [Python](/en/agent-sdk/python#claude-agent-options) or [TypeScript](/en/agent-sdk/typescript#options).

173 

174The `async for` loop keeps running as Claude thinks, calls tools, observes results, and decides what to do next. Each iteration yields a message: Claude's reasoning, a tool call, a tool result, or the final outcome. The SDK handles the orchestration (tool execution, context management, retries) so you just consume the stream. The loop ends when Claude finishes the task or hits an error.

175 

176The message handling inside the loop filters for human-readable output. Without filtering, you'd see raw message objects including system initialization and internal state, which is useful for debugging but noisy otherwise.

177 

178<Note>

179 This example uses streaming to show progress in real-time. If you don't need live output (e.g., for background jobs or CI pipelines), you can collect all messages at once. See [Streaming vs. single-turn mode](/en/agent-sdk/streaming-vs-single-mode) for details.

180</Note>

181 

182### Run your agent

183 

184Your agent is ready. Run it with the following command:

185 

186<Tabs>

187 <Tab title="Python">

188 ```bash theme={null}

189 python3 agent.py

190 ```

191 </Tab>

192 

193 <Tab title="TypeScript">

194 ```bash theme={null}

195 npx tsx agent.ts

196 ```

197 </Tab>

198</Tabs>

199 

200After running, check `utils.py`. You'll see defensive code handling empty lists and null users. Your agent autonomously:

201 

2021. **Read** `utils.py` to understand the code

2032. **Analyzed** the logic and identified edge cases that would crash

2043. **Edited** the file to add proper error handling

205 

206This is what makes the Agent SDK different: Claude executes tools directly instead of asking you to implement them.

207 

208<Note>

209 If you see "API key not found", make sure you've set the `ANTHROPIC_API_KEY` environment variable in your `.env` file or shell environment. See the [full troubleshooting guide](/en/troubleshooting) for more help.

210</Note>

211 

212### Try other prompts

213 

214Now that your agent is set up, try some different prompts:

215 

216* `"Add docstrings to all functions in utils.py"`

217* `"Add type hints to all functions in utils.py"`

218* `"Create a README.md documenting the functions in utils.py"`

219 

220### Customize your agent

221 

222You can modify your agent's behavior by changing the options. Here are a few examples:

223 

224**Add web search capability:**

225 

226<CodeGroup>

227 ```python Python theme={null}

228 options = ClaudeAgentOptions(

229 allowed_tools=["Read", "Edit", "Glob", "WebSearch"], permission_mode="acceptEdits"

230 )

231 ```

232 

233 ```typescript TypeScript hidelines={1,-1} theme={null}

234 const _ = {

235 options: {

236 allowedTools: ["Read", "Edit", "Glob", "WebSearch"],

237 permissionMode: "acceptEdits"

238 }

239 };

240 ```

241</CodeGroup>

242 

243**Give Claude a custom system prompt:**

244 

245<CodeGroup>

246 ```python Python theme={null}

247 options = ClaudeAgentOptions(

248 allowed_tools=["Read", "Edit", "Glob"],

249 permission_mode="acceptEdits",

250 system_prompt="You are a senior Python developer. Always follow PEP 8 style guidelines.",

251 )

252 ```

253 

254 ```typescript TypeScript hidelines={1,-1} theme={null}

255 const _ = {

256 options: {

257 allowedTools: ["Read", "Edit", "Glob"],

258 permissionMode: "acceptEdits",

259 systemPrompt: "You are a senior Python developer. Always follow PEP 8 style guidelines."

260 }

261 };

262 ```

263</CodeGroup>

264 

265**Run commands in the terminal:**

266 

267<CodeGroup>

268 ```python Python theme={null}

269 options = ClaudeAgentOptions(

270 allowed_tools=["Read", "Edit", "Glob", "Bash"], permission_mode="acceptEdits"

271 )

272 ```

273 

274 ```typescript TypeScript hidelines={1,-1} theme={null}

275 const _ = {

276 options: {

277 allowedTools: ["Read", "Edit", "Glob", "Bash"],

278 permissionMode: "acceptEdits"

279 }

280 };

281 ```

282</CodeGroup>

283 

284With `Bash` enabled, try: `"Write unit tests for utils.py, run them, and fix any failures"`

285 

286## Key concepts

287 

288**Tools** control what your agent can do:

289 

290| Tools | What the agent can do |

291| -------------------------------------- | ----------------------- |

292| `Read`, `Glob`, `Grep` | Read-only analysis |

293| `Read`, `Edit`, `Glob` | Analyze and modify code |

294| `Read`, `Edit`, `Bash`, `Glob`, `Grep` | Full automation |

295 

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

297 

298| Mode | Behavior | Use case |

299| ------------------------ | ---------------------------------------------------- | ---------------------------------------- |

300| `acceptEdits` | Auto-approves file edits, asks for other actions | Trusted development workflows |

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

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

303| `bypassPermissions` | Runs every tool without prompts | Sandboxed CI, fully trusted environments |

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

305 

306The example above uses `acceptEdits` mode, which auto-approves file operations so the agent can run without interactive prompts. If you want to prompt users for approval, use `default` mode and provide a [`canUseTool` callback](/en/agent-sdk/user-input) that collects user input. For more control, see [Permissions](/en/agent-sdk/permissions).

307 

308## Next steps

309 

310Now that you've created your first agent, learn how to extend its capabilities and tailor it to your use case:

311 

312* **[Permissions](/en/agent-sdk/permissions)**: control what your agent can do and when it needs approval

313* **[Hooks](/en/agent-sdk/hooks)**: run custom code before or after tool calls

314* **[Sessions](/en/agent-sdk/sessions)**: build multi-turn agents that maintain context

315* **[MCP servers](/en/agent-sdk/mcp)**: connect to databases, browsers, APIs, and other external systems

316* **[Hosting](/en/agent-sdk/hosting)**: deploy agents to Docker, cloud, and CI/CD

317* **[Example agents](https://github.com/anthropics/claude-agent-sdk-demos)**: see complete examples: email assistant, research agent, and more

agent-sdk/secure-deployment.md +350 −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# Securely deploying AI agents

6 

7> A guide to securing Claude Code and Agent SDK deployments with isolation, credential management, and network controls

8 

9Claude Code and the Agent SDK are powerful tools that can execute code, access files, and interact with external services on your behalf. Like any tool with these capabilities, deploying them thoughtfully ensures you get the benefits while maintaining appropriate controls.

10 

11Unlike traditional software that follows predetermined code paths, these tools generate their actions dynamically based on context and goals. This flexibility is what makes them useful, but it also means their behavior can be influenced by the content they process: files, webpages, or user input. This is sometimes called prompt injection. For example, if a repository's README contains unusual instructions, Claude Code might incorporate those into its actions in ways the operator didn't anticipate. This guide covers practical ways to reduce this risk.

12 

13The good news is that securing an agent deployment doesn't require exotic infrastructure. The same principles that apply to running any semi-trusted code apply here: isolation, least privilege, and defense in depth. Claude Code includes several security features that help with common concerns, and this guide walks through these along with additional hardening options for those who need them.

14 

15Not every deployment needs maximum security. A developer running Claude Code on their laptop has different requirements than a company processing customer data in a multi-tenant environment. This guide presents options ranging from Claude Code's built-in security features to hardened production architectures, so you can choose what fits your situation.

16 

17## Threat model

18 

19Agents can take unintended actions due to prompt injection (instructions embedded in content they process) or model error. Claude models are designed to resist this, and as analyzed in the [model card](https://www.anthropic.com/claude-opus-4-6-system-card), Claude Opus 4.6 is the most robust frontier model available.

20 

21Defense in depth is still good practice though. For example, if an agent processes a malicious file that instructs it to send customer data to an external server, network controls can block that request entirely.

22 

23## Built-in security features

24 

25Claude Code includes several security features that address common concerns. See the [security documentation](/en/security) for full details.

26 

27* **Permissions system**: Every tool and bash command can be configured to allow, block, or prompt the user for approval. Use glob patterns to create rules like "allow all npm commands" or "block any command with sudo". Organizations can set policies that apply across all users. See [permissions](/en/permissions).

28* **Static analysis**: Before executing bash commands, Claude Code runs static analysis to identify potentially risky operations. Commands that modify system files or access sensitive directories are flagged and require explicit user approval.

29* **Web search summarization**: Search results are summarized rather than passing raw content directly into the context, reducing the risk of prompt injection from malicious web content.

30* **Sandbox mode**: Bash commands can run in a sandboxed environment that restricts filesystem and network access. See the [sandboxing documentation](/en/sandboxing) for details.

31 

32## Security principles

33 

34For deployments that require additional hardening beyond Claude Code's defaults, these principles guide the available options.

35 

36### Security boundaries

37 

38A security boundary separates components with different trust levels. For high-security deployments, you can place sensitive resources (like credentials) outside the boundary containing the agent. If something goes wrong in the agent's environment, resources outside that boundary remain protected.

39 

40For example, rather than giving an agent direct access to an API key, you could run a proxy outside the agent's environment that injects the key into requests. The agent can make API calls, but it never sees the credential itself. This pattern is useful for multi-tenant deployments or when processing untrusted content.

41 

42### Least privilege

43 

44When needed, you can restrict the agent to only the capabilities required for its specific task:

45 

46| Resource | Restriction options |

47| ------------------- | ----------------------------------------------- |

48| Filesystem | Mount only needed directories, prefer read-only |

49| Network | Restrict to specific endpoints via proxy |

50| Credentials | Inject via proxy rather than exposing directly |

51| System capabilities | Drop Linux capabilities in containers |

52 

53### Defense in depth

54 

55For high-security environments, layering multiple controls provides additional protection. Options include:

56 

57* Container isolation

58* Network restrictions

59* Filesystem controls

60* Request validation at a proxy

61 

62The right combination depends on your threat model and operational requirements.

63 

64## Isolation technologies

65 

66Different isolation technologies offer different tradeoffs between security strength, performance, and operational complexity.

67 

68<Info>

69 In all of these configurations, Claude Code (or your Agent SDK application) runs inside the isolation boundary (the sandbox, container, or VM). The security controls described below restrict what the agent can access from within that boundary.

70</Info>

71 

72| Technology | Isolation strength | Performance overhead | Complexity |

73| ----------------------- | ------------------------------ | -------------------- | ----------- |

74| Sandbox runtime | Good (secure defaults) | Very low | Low |

75| Containers (Docker) | Setup dependent | Low | Medium |

76| gVisor | Excellent (with correct setup) | Medium/High | Medium |

77| VMs (Firecracker, QEMU) | Excellent (with correct setup) | High | Medium/High |

78 

79### Sandbox runtime

80 

81For lightweight isolation without containers, [sandbox-runtime](https://github.com/anthropic-experimental/sandbox-runtime) enforces filesystem and network restrictions at the OS level.

82 

83The main advantage is simplicity: no Docker configuration, container images, or networking setup required. The proxy and filesystem restrictions are built in. You provide a settings file specifying allowed domains and paths.

84 

85**How it works:**

86 

87* **Filesystem**: Uses OS primitives (`bubblewrap` on Linux, `sandbox-exec` on macOS) to restrict read/write access to configured paths

88* **Network**: Removes network namespace (Linux) or uses Seatbelt profiles (macOS) to route network traffic through a built-in proxy

89* **Configuration**: JSON-based allowlists for domains and filesystem paths

90 

91**Setup:**

92 

93```bash theme={null}

94npm install @anthropic-ai/sandbox-runtime

95```

96 

97Then create a configuration file specifying allowed paths and domains.

98 

99**Security considerations:**

100 

1011. **Same-host kernel**: Unlike VMs, sandboxed processes share the host kernel. A kernel vulnerability could theoretically enable escape. For some threat models this is acceptable, but if you need kernel-level isolation, use gVisor or a separate VM.

102 

1032. **No TLS inspection**: The proxy allowlists domains but doesn't inspect encrypted traffic. If the agent has permissive credentials for an allowed domain, ensure it isn't possible to use that domain to trigger other network requests or to exfiltrate data.

104 

105For many single-developer and CI/CD use cases, sandbox-runtime raises the bar significantly with minimal setup. The sections below cover containers and VMs for deployments requiring stronger isolation.

106 

107### Containers

108 

109Containers provide isolation through Linux namespaces. Each container has its own view of the filesystem, process tree, and network stack, while sharing the host kernel.

110 

111A security-hardened container configuration might look like this:

112 

113```bash theme={null}

114docker run \

115 --cap-drop ALL \

116 --security-opt no-new-privileges \

117 --security-opt seccomp=/path/to/seccomp-profile.json \

118 --read-only \

119 --tmpfs /tmp:rw,noexec,nosuid,size=100m \

120 --tmpfs /home/agent:rw,noexec,nosuid,size=500m \

121 --network none \

122 --memory 2g \

123 --cpus 2 \

124 --pids-limit 100 \

125 --user 1000:1000 \

126 -v /path/to/code:/workspace:ro \

127 -v /var/run/proxy.sock:/var/run/proxy.sock:ro \

128 agent-image

129```

130 

131Here's what each option does:

132 

133| Option | Purpose |

134| ---------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- |

135| `--cap-drop ALL` | Removes Linux capabilities like `NET_ADMIN` and `SYS_ADMIN` that could enable privilege escalation |

136| `--security-opt no-new-privileges` | Prevents processes from gaining privileges through setuid binaries |

137| `--security-opt seccomp=...` | Restricts available syscalls; Docker's default blocks \~44, custom profiles can block more |

138| `--read-only` | Makes the container's root filesystem immutable, preventing the agent from persisting changes |

139| `--tmpfs /tmp:...` | Provides a writable temporary directory that's cleared when the container stops |

140| `--network none` | Removes all network interfaces; the agent communicates through the mounted Unix socket below |

141| `--memory 2g` | Limits memory usage to prevent resource exhaustion |

142| `--pids-limit 100` | Limits process count to prevent fork bombs |

143| `--user 1000:1000` | Runs as a non-root user |

144| `-v ...:/workspace:ro` | Mounts code read-only so the agent can analyze but not modify it. **Avoid mounting sensitive host directories like `~/.ssh`, `~/.aws`, or `~/.config`** |

145| `-v .../proxy.sock:...` | Mounts a Unix socket connected to a proxy running outside the container (see below) |

146 

147**Unix socket architecture:**

148 

149With `--network none`, the container has no network interfaces at all. The only way for the agent to reach the outside world is through the mounted Unix socket, which connects to a proxy running on the host. This proxy can enforce domain allowlists, inject credentials, and log all traffic.

150 

151This is the same architecture used by [sandbox-runtime](https://github.com/anthropic-experimental/sandbox-runtime). Even if the agent is compromised via prompt injection, it cannot exfiltrate data to arbitrary servers. It can only communicate through the proxy, which controls what domains are reachable. For more details, see the [Claude Code sandboxing blog post](https://www.anthropic.com/engineering/claude-code-sandboxing).

152 

153**Additional hardening options:**

154 

155| Option | Purpose |

156| ---------------- | -------------------------------------------------------------------------------------------------------------------- |

157| `--userns-remap` | Maps container root to unprivileged host user; requires daemon configuration but limits damage from container escape |

158| `--ipc private` | Isolates inter-process communication to prevent cross-container attacks |

159 

160### gVisor

161 

162Standard containers share the host kernel: when code inside a container makes a system call, it goes directly to the same kernel that runs the host. This means a kernel vulnerability could allow container escape. gVisor addresses this by intercepting system calls in userspace before they reach the host kernel, implementing its own compatibility layer that handles most syscalls without involving the real kernel.

163 

164If an agent runs malicious code (perhaps due to prompt injection), that code runs in the container and could attempt kernel exploits. With gVisor, the attack surface is much smaller: the malicious code would need to exploit gVisor's userspace implementation first and would have limited access to the real kernel.

165 

166To use gVisor with Docker, install the `runsc` runtime and configure the daemon:

167 

168```json theme={null}

169// /etc/docker/daemon.json

170{

171 "runtimes": {

172 "runsc": {

173 "path": "/usr/local/bin/runsc"

174 }

175 }

176}

177```

178 

179Then run containers with:

180 

181```bash theme={null}

182docker run --runtime=runsc agent-image

183```

184 

185**Performance considerations:**

186 

187| Workload | Overhead |

188| --------------------- | -------------------------------------------------- |

189| CPU-bound computation | \~0% (no syscall interception) |

190| Simple syscalls | \~2× slower |

191| File I/O intensive | Up to 10-200× slower for heavy open/close patterns |

192 

193For multi-tenant environments or when processing untrusted content, the additional isolation is often worth the overhead.

194 

195### Virtual machines

196 

197VMs provide hardware-level isolation through CPU virtualization extensions. Each VM runs its own kernel, creating a strong boundary. A vulnerability in the guest kernel doesn't directly compromise the host. However, VMs aren't automatically "more secure" than alternatives like gVisor. VM security depends heavily on the hypervisor and device emulation code.

198 

199Firecracker is designed for lightweight microVM isolation. It can boot VMs in under 125ms with less than 5 MiB memory overhead, stripping away unnecessary device emulation to reduce attack surface.

200 

201With this approach, the agent VM has no external network interface. Instead, it communicates through `vsock` (virtual sockets). All traffic routes through vsock to a proxy on the host, which enforces allowlists and injects credentials before forwarding requests.

202 

203### Cloud deployments

204 

205For cloud deployments, you can combine any of the above isolation technologies with cloud-native network controls:

206 

2071. Run agent containers in a private subnet with no internet gateway

2082. Configure cloud firewall rules (AWS Security Groups, GCP VPC firewall) to block all egress except to your proxy

2093. Run a proxy (such as [Envoy](https://www.envoyproxy.io/) with its `credential_injector` filter) that validates requests, enforces domain allowlists, injects credentials, and forwards to external APIs

2104. Assign minimal IAM permissions to the agent's service account, routing sensitive access through the proxy where possible

2115. Log all traffic at the proxy for audit purposes

212 

213## Credential management

214 

215Agents often need credentials to call APIs, access repositories, or interact with cloud services. The challenge is providing this access without exposing the credentials themselves.

216 

217### The proxy pattern

218 

219The recommended approach is to run a proxy outside the agent's security boundary that injects credentials into outgoing requests. The agent sends requests without credentials, the proxy adds them, and forwards the request to its destination.

220 

221This pattern has several benefits:

222 

2231. The agent never sees the actual credentials

2242. The proxy can enforce an allowlist of permitted endpoints

2253. The proxy can log all requests for auditing

2264. Credentials are stored in one secure location rather than distributed to each agent

227 

228### Configuring Claude Code to use a proxy

229 

230Claude Code supports two methods for routing sampling requests through a proxy:

231 

232**Option 1: ANTHROPIC\_BASE\_URL (simple but only for sampling API requests)**

233 

234```bash theme={null}

235export ANTHROPIC_BASE_URL="http://localhost:8080"

236```

237 

238This tells Claude Code and the Agent SDK to send sampling requests to your proxy instead of the Claude API directly. Your proxy receives plaintext HTTP requests, can inspect and modify them (including injecting credentials), then forwards to the real API.

239 

240**Option 2: HTTP\_PROXY / HTTPS\_PROXY (system-wide)**

241 

242```bash theme={null}

243export HTTP_PROXY="http://localhost:8080"

244export HTTPS_PROXY="http://localhost:8080"

245```

246 

247Claude Code and the Agent SDK respect these standard environment variables, routing all HTTP traffic through the proxy. For HTTPS, the proxy creates an encrypted CONNECT tunnel: it cannot see or modify request contents without TLS interception.

248 

249### Implementing a proxy

250 

251You can build your own proxy or use an existing one:

252 

253* [Envoy Proxy](https://www.envoyproxy.io/): production-grade proxy with `credential_injector` filter for adding auth headers

254* [mitmproxy](https://mitmproxy.org/): TLS-terminating proxy for inspecting and modifying HTTPS traffic

255* [Squid](http://www.squid-cache.org/): caching proxy with access control lists

256* [LiteLLM](https://github.com/BerriAI/litellm): LLM gateway with credential injection and rate limiting

257 

258### Credentials for other services

259 

260Beyond sampling from the Claude API, agents often need authenticated access to other services, such as git repositories, databases, and internal APIs. There are two main approaches:

261 

262#### Custom tools

263 

264Provide access through an MCP server or custom tool that routes requests to a service running outside the agent's security boundary. The agent calls the tool, but the actual authenticated request happens outside. The tool calls to a proxy which injects the credentials.

265 

266For example, a git MCP server could accept commands from the agent but forward them to a git proxy running on the host, which adds authentication before contacting the remote repository. The agent never sees the credentials.

267 

268Advantages:

269 

270* **No TLS interception**: The external service makes authenticated requests directly

271* **Credentials stay outside**: The agent only sees the tool interface, not the underlying credentials

272 

273#### Traffic forwarding

274 

275For Claude API calls, `ANTHROPIC_BASE_URL` lets you route requests to a proxy that can inspect and modify them in plaintext. But for other HTTPS services (GitHub, npm registries, internal APIs), the traffic is often encrypted end-to-end. Even if you route it through a proxy via `HTTP_PROXY`, the proxy only sees an opaque TLS tunnel and can't inject credentials.

276 

277To modify HTTPS traffic to arbitrary services, without using a custom tool, you need a TLS-terminating proxy that decrypts traffic, inspects or modifies it, then re-encrypts it before forwarding. This requires:

278 

2791. Running the proxy outside the agent's container

2802. Installing the proxy's CA certificate in the agent's trust store (so the agent trusts the proxy's certificates)

2813. Configuring `HTTP_PROXY`/`HTTPS_PROXY` to route traffic through the proxy

282 

283This approach handles any HTTP-based service without writing custom tools, but adds complexity around certificate management.

284 

285Note that not all programs respect `HTTP_PROXY`/`HTTPS_PROXY`. Most tools (curl, pip, npm, git) do, but some may bypass these variables and connect directly. For example, Node.js `fetch()` ignores these variables by default; in Node 24+ you can set `NODE_USE_ENV_PROXY=1` to enable support. For comprehensive coverage, you can use [proxychains](https://github.com/haad/proxychains) to intercept network calls, or configure iptables to redirect outbound traffic to a transparent proxy.

286 

287<Info>

288 A **transparent proxy** intercepts traffic at the network level, so the client doesn't need to be configured to use it. Regular proxies require clients to explicitly connect and speak HTTP CONNECT or SOCKS. Transparent proxies (like Squid or mitmproxy in transparent mode) can handle raw redirected TCP connections.

289</Info>

290 

291Both approaches still require the TLS-terminating proxy and trusted CA certificate. They just ensure traffic actually reaches the proxy.

292 

293## Filesystem configuration

294 

295Filesystem controls determine what files the agent can read and write.

296 

297### Read-only code mounting

298 

299When the agent needs to analyze code but not modify it, mount the directory read-only:

300 

301```bash theme={null}

302docker run -v /path/to/code:/workspace:ro agent-image

303```

304 

305<Warning>

306 Even read-only access to a code directory can expose credentials. Common files to exclude or sanitize before mounting:

307 

308 | File | Risk |

309 | ------------------------------------------------------- | ------------------------------------- |

310 | `.env`, `.env.local` | API keys, database passwords, secrets |

311 | `~/.git-credentials` | Git passwords/tokens in plaintext |

312 | `~/.aws/credentials` | AWS access keys |

313 | `~/.config/gcloud/application_default_credentials.json` | Google Cloud ADC tokens |

314 | `~/.azure/` | Azure CLI credentials |

315 | `~/.docker/config.json` | Docker registry auth tokens |

316 | `~/.kube/config` | Kubernetes cluster credentials |

317 | `.npmrc`, `.pypirc` | Package registry tokens |

318 | `*-service-account.json` | GCP service account keys |

319 | `*.pem`, `*.key` | Private keys |

320 

321 Consider copying only the source files needed, or using `.dockerignore`-style filtering.

322</Warning>

323 

324### Writable locations

325 

326If the agent needs to write files, you have a few options depending on whether you want changes to persist:

327 

328For ephemeral workspaces in containers, use `tmpfs` mounts that exist only in memory and are cleared when the container stops:

329 

330```bash theme={null}

331docker run \

332 --read-only \

333 --tmpfs /tmp:rw,noexec,nosuid,size=100m \

334 --tmpfs /workspace:rw,noexec,size=500m \

335 agent-image

336```

337 

338If you want to review changes before persisting them, an overlay filesystem lets the agent write without modifying underlying files. Changes are stored in a separate layer you can inspect, apply, or discard. For fully persistent output, mount a dedicated volume but keep it separate from sensitive directories.

339 

340## Further reading

341 

342* [Claude Code security documentation](/en/security)

343* [Hosting the Agent SDK](/en/agent-sdk/hosting)

344* [Handling permissions](/en/agent-sdk/permissions)

345* [Sandbox runtime](https://github.com/anthropic-experimental/sandbox-runtime)

346* [The Lethal Trifecta for AI Agents](https://simonwillison.net/2025/Jun/16/the-lethal-trifecta/)

347* [OWASP Top 10 for LLM Applications](https://owasp.org/www-project-top-10-for-large-language-model-applications/)

348* [Docker Security Best Practices](https://docs.docker.com/engine/security/)

349* [gVisor Documentation](https://gvisor.dev/docs/)

350* [Firecracker Documentation](https://firecracker-microvm.github.io/)

agent-sdk/sessions.md +322 −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# Work with sessions

6 

7> How sessions persist agent conversation history, and when to use continue, resume, and fork to return to a prior run.

8 

9A session is the conversation history the SDK accumulates while your agent works. It contains your prompt, every tool call the agent made, every tool result, and every response. The SDK writes it to disk automatically so you can return to it later.

10 

11Returning to a session means the agent has full context from before: files it already read, analysis it already performed, decisions it already made. You can ask a follow-up question, recover from an interruption, or branch off to try a different approach.

12 

13<Note>

14 Sessions persist the **conversation**, not the filesystem. To snapshot and revert file changes the agent made, use [file checkpointing](/en/agent-sdk/file-checkpointing).

15</Note>

16 

17This guide covers how to pick the right approach for your app, the SDK interfaces that track sessions automatically, how to capture session IDs and use `resume` and `fork` manually, and what to know about resuming sessions across hosts.

18 

19## Choose an approach

20 

21How much session handling you need depends on your application's shape. Session management comes into play when you send multiple prompts that should share context. Within a single `query()` call, the agent already takes as many turns as it needs, and permission prompts and `AskUserQuestion` are [handled in-loop](/en/agent-sdk/user-input) (they don't end the call).

22 

23| What you're building | What to use |

24| :-------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------- |

25| One-shot task: single prompt, no follow-up | Nothing extra. One `query()` call handles it. |

26| Multi-turn chat in one process | [`ClaudeSDKClient` (Python) or `continue: true` (TypeScript)](#automatic-session-management). The SDK tracks the session for you with no ID handling. |

27| Pick up where you left off after a process restart | `continue_conversation=True` (Python) / `continue: true` (TypeScript). Resumes the most recent session in the directory, no ID needed. |

28| Resume a specific past session (not the most recent) | Capture the session ID and pass it to `resume`. |

29| Try an alternative approach without losing the original | Fork the session. |

30| Stateless task, don't want anything written to disk (TypeScript only) | Set [`persistSession: false`](/en/agent-sdk/typescript#options). The session exists only in memory for the duration of the call. Python always persists to disk. |

31 

32### Continue, resume, and fork

33 

34Continue, resume, and fork are option fields you set on `query()` ([`ClaudeAgentOptions`](/en/agent-sdk/python#claude-agent-options) in Python, [`Options`](/en/agent-sdk/typescript#options) in TypeScript).

35 

36**Continue** and **resume** both pick up an existing session and add to it. The difference is how they find that session:

37 

38* **Continue** finds the most recent session in the current directory. You don't track anything. Works well when your app runs one conversation at a time.

39* **Resume** takes a specific session ID. You track the ID. Required when you have multiple sessions (for example, one per user in a multi-user app) or want to return to one that isn't the most recent.

40 

41**Fork** is different: it creates a new session that starts with a copy of the original's history. The original stays unchanged. Use fork to try a different direction while keeping the option to go back.

42 

43## Automatic session management

44 

45Both SDKs offer an interface that tracks session state for you across calls, so you don't pass IDs around manually. Use these for multi-turn conversations within a single process.

46 

47### Python: `ClaudeSDKClient`

48 

49[`ClaudeSDKClient`](/en/agent-sdk/python#claude-sdk-client) handles session IDs internally. Each call to `client.query()` automatically continues the same session. Call [`client.receive_response()`](/en/agent-sdk/python#claude-sdk-client) to iterate over the messages for the current query. The client must be used as an async context manager.

50 

51This example runs two queries against the same `client`. The first asks the agent to analyze a module; the second asks it to refactor that module. Because both calls go through the same client instance, the second query has full context from the first without any explicit `resume` or session ID:

52 

53```python Python theme={null}

54import asyncio

55from claude_agent_sdk import (

56 ClaudeSDKClient,

57 ClaudeAgentOptions,

58 AssistantMessage,

59 ResultMessage,

60 TextBlock,

61)

62 

63 

64def print_response(message):

65 """Print only the human-readable parts of a message."""

66 if isinstance(message, AssistantMessage):

67 for block in message.content:

68 if isinstance(block, TextBlock):

69 print(block.text)

70 elif isinstance(message, ResultMessage):

71 cost = (

72 f"${message.total_cost_usd:.4f}"

73 if message.total_cost_usd is not None

74 else "N/A"

75 )

76 print(f"[done: {message.subtype}, cost: {cost}]")

77 

78 

79async def main():

80 options = ClaudeAgentOptions(

81 allowed_tools=["Read", "Edit", "Glob", "Grep"],

82 )

83 

84 async with ClaudeSDKClient(options=options) as client:

85 # First query: client captures the session ID internally

86 await client.query("Analyze the auth module")

87 async for message in client.receive_response():

88 print_response(message)

89 

90 # Second query: automatically continues the same session

91 await client.query("Now refactor it to use JWT")

92 async for message in client.receive_response():

93 print_response(message)

94 

95 

96asyncio.run(main())

97```

98 

99See the [Python SDK reference](/en/agent-sdk/python#choosing-between-query-and-claude-sdk-client) for details on when to use `ClaudeSDKClient` vs the standalone `query()` function.

100 

101### TypeScript: `continue: true`

102 

103The stable TypeScript SDK (the `query()` function used throughout these docs, sometimes called V1) doesn't have a session-holding client object like Python's `ClaudeSDKClient`. Instead, pass `continue: true` on each subsequent `query()` call and the SDK picks up the most recent session in the current directory. No ID tracking required.

104 

105This example makes two separate `query()` calls. The first creates a fresh session; the second sets `continue: true`, which tells the SDK to find and resume the most recent session on disk. The agent has full context from the first call:

106 

107```typescript TypeScript theme={null}

108import { query } from "@anthropic-ai/claude-agent-sdk";

109 

110// First query: creates a new session

111for await (const message of query({

112 prompt: "Analyze the auth module",

113 options: { allowedTools: ["Read", "Glob", "Grep"] }

114})) {

115 if (message.type === "result" && message.subtype === "success") {

116 console.log(message.result);

117 }

118}

119 

120// Second query: continue: true resumes the most recent session

121for await (const message of query({

122 prompt: "Now refactor it to use JWT",

123 options: {

124 continue: true,

125 allowedTools: ["Read", "Edit", "Write", "Glob", "Grep"]

126 }

127})) {

128 if (message.type === "result" && message.subtype === "success") {

129 console.log(message.result);

130 }

131}

132```

133 

134<Note>

135 There's also a [V2 preview](/en/agent-sdk/typescript-v2-preview) of the TypeScript SDK that provides `createSession()` with a `send` / `stream` pattern, closer to Python's `ClaudeSDKClient` in feel. V2 is unstable and its APIs may change; the rest of this documentation uses the stable V1 `query()` function.

136</Note>

137 

138## Use session options with `query()`

139 

140### Capture the session ID

141 

142Resume and fork require a session ID. Read it from the `session_id` field on the result message ([`ResultMessage`](/en/agent-sdk/python#result-message) in Python, [`SDKResultMessage`](/en/agent-sdk/typescript#sdk-result-message) in TypeScript), which is present on every result regardless of success or error. In TypeScript the ID is also available earlier as a direct field on the init `SystemMessage`; in Python it's nested inside `SystemMessage.data`.

143 

144<CodeGroup>

145 ```python Python theme={null}

146 import asyncio

147 from claude_agent_sdk import query, ClaudeAgentOptions, ResultMessage

148 

149 

150 async def main():

151 session_id = None

152 

153 async for message in query(

154 prompt="Analyze the auth module and suggest improvements",

155 options=ClaudeAgentOptions(

156 allowed_tools=["Read", "Glob", "Grep"],

157 ),

158 ):

159 if isinstance(message, ResultMessage):

160 session_id = message.session_id

161 if message.subtype == "success":

162 print(message.result)

163 

164 print(f"Session ID: {session_id}")

165 return session_id

166 

167 

168 session_id = asyncio.run(main())

169 ```

170 

171 ```typescript TypeScript theme={null}

172 import { query } from "@anthropic-ai/claude-agent-sdk";

173 

174 let sessionId: string | undefined;

175 

176 for await (const message of query({

177 prompt: "Analyze the auth module and suggest improvements",

178 options: { allowedTools: ["Read", "Glob", "Grep"] }

179 })) {

180 if (message.type === "result") {

181 sessionId = message.session_id;

182 if (message.subtype === "success") {

183 console.log(message.result);

184 }

185 }

186 }

187 

188 console.log(`Session ID: ${sessionId}`);

189 ```

190</CodeGroup>

191 

192### Resume by ID

193 

194Pass a session ID to `resume` to return to that specific session. The agent picks up with full context from wherever the session left off. Common reasons to resume:

195 

196* **Follow up on a completed task.** The agent already analyzed something; now you want it to act on that analysis without re-reading files.

197* **Recover from a limit.** The first run ended with `error_max_turns` or `error_max_budget_usd` (see [Handle the result](/en/agent-sdk/agent-loop#handle-the-result)); resume with a higher limit.

198* **Restart your process.** You captured the ID before shutdown and want to restore the conversation.

199 

200This example resumes the session from [Capture the session ID](#capture-the-session-id) with a follow-up prompt. Because you're resuming, the agent already has the prior analysis in context:

201 

202<CodeGroup>

203 ```python Python theme={null}

204 # Earlier session analyzed the code; now build on that analysis

205 async for message in query(

206 prompt="Now implement the refactoring you suggested",

207 options=ClaudeAgentOptions(

208 resume=session_id,

209 allowed_tools=["Read", "Edit", "Write", "Glob", "Grep"],

210 ),

211 ):

212 if isinstance(message, ResultMessage) and message.subtype == "success":

213 print(message.result)

214 ```

215 

216 ```typescript TypeScript theme={null}

217 // Earlier session analyzed the code; now build on that analysis

218 for await (const message of query({

219 prompt: "Now implement the refactoring you suggested",

220 options: {

221 resume: sessionId,

222 allowedTools: ["Read", "Edit", "Write", "Glob", "Grep"]

223 }

224 })) {

225 if (message.type === "result" && message.subtype === "success") {

226 console.log(message.result);

227 }

228 }

229 ```

230</CodeGroup>

231 

232<Tip>

233 If a `resume` call returns a fresh session instead of the expected history, the most common cause is a mismatched `cwd`. Sessions are stored under `~/.claude/projects/<encoded-cwd>/*.jsonl`, where `<encoded-cwd>` is the absolute working directory with every non-alphanumeric character replaced by `-` (so `/Users/me/proj` becomes `-Users-me-proj`). If your resume call runs from a different directory, the SDK looks in the wrong place. The session file also needs to exist on the current machine.

234</Tip>

235 

236### Fork to explore alternatives

237 

238Forking creates a new session that starts with a copy of the original's history but diverges from that point. The fork gets its own session ID; the original's ID and history stay unchanged. You end up with two independent sessions you can resume separately.

239 

240<Note>

241 Forking branches the conversation history, not the filesystem. If a forked agent edits files, those changes are real and visible to any session working in the same directory. To branch and revert file changes, use [file checkpointing](/en/agent-sdk/file-checkpointing).

242</Note>

243 

244This example builds on [Capture the session ID](#capture-the-session-id): you've already analyzed an auth module in `session_id` and want to explore OAuth2 without losing the JWT-focused thread. The first block forks the session and captures the fork's ID (`forked_id`); the second block resumes the original `session_id` to continue down the JWT path. You now have two session IDs pointing at two separate histories:

245 

246<CodeGroup>

247 ```python Python theme={null}

248 # Fork: branch from session_id into a new session

249 forked_id = None

250 async for message in query(

251 prompt="Instead of JWT, implement OAuth2 for the auth module",

252 options=ClaudeAgentOptions(

253 resume=session_id,

254 fork_session=True,

255 ),

256 ):

257 if isinstance(message, ResultMessage):

258 forked_id = message.session_id # The fork's ID, distinct from session_id

259 if message.subtype == "success":

260 print(message.result)

261 

262 print(f"Forked session: {forked_id}")

263 

264 # Original session is untouched; resuming it continues the JWT thread

265 async for message in query(

266 prompt="Continue with the JWT approach",

267 options=ClaudeAgentOptions(resume=session_id),

268 ):

269 if isinstance(message, ResultMessage) and message.subtype == "success":

270 print(message.result)

271 ```

272 

273 ```typescript TypeScript theme={null}

274 // Fork: branch from sessionId into a new session

275 let forkedId: string | undefined;

276 

277 for await (const message of query({

278 prompt: "Instead of JWT, implement OAuth2 for the auth module",

279 options: {

280 resume: sessionId,

281 forkSession: true

282 }

283 })) {

284 if (message.type === "system" && message.subtype === "init") {

285 forkedId = message.session_id; // The fork's ID, distinct from sessionId

286 }

287 if (message.type === "result" && message.subtype === "success") {

288 console.log(message.result);

289 }

290 }

291 

292 console.log(`Forked session: ${forkedId}`);

293 

294 // Original session is untouched; resuming it continues the JWT thread

295 for await (const message of query({

296 prompt: "Continue with the JWT approach",

297 options: { resume: sessionId }

298 })) {

299 if (message.type === "result" && message.subtype === "success") {

300 console.log(message.result);

301 }

302 }

303 ```

304</CodeGroup>

305 

306## Resume across hosts

307 

308Session files are local to the machine that created them. To resume a session on a different host (CI workers, ephemeral containers, serverless), you have two options:

309 

310* **Move the session file.** Persist `~/.claude/projects/<encoded-cwd>/<session-id>.jsonl` from the first run and restore it to the same path on the new host before calling `resume`. The `cwd` must match.

311* **Don't rely on session resume.** Capture the results you need (analysis output, decisions, file diffs) as application state and pass them into a fresh session's prompt. This is often more robust than shipping transcript files around.

312 

313Both SDKs expose functions for enumerating sessions on disk and reading their messages: [`listSessions()`](/en/agent-sdk/typescript#list-sessions) and [`getSessionMessages()`](/en/agent-sdk/typescript#get-session-messages) in TypeScript, [`list_sessions()`](/en/agent-sdk/python#list-sessions) and [`get_session_messages()`](/en/agent-sdk/python#get-session-messages) in Python. Use them to build custom session pickers, cleanup logic, or transcript viewers.

314 

315Both SDKs also expose functions for looking up and mutating individual sessions: [`get_session_info()`](/en/agent-sdk/python#get-session-info), [`rename_session()`](/en/agent-sdk/python#rename-session), and [`tag_session()`](/en/agent-sdk/python#tag-session) in Python, and [`getSessionInfo()`](/en/agent-sdk/typescript#get-session-info), [`renameSession()`](/en/agent-sdk/typescript#rename-session), and [`tagSession()`](/en/agent-sdk/typescript#tag-session) in TypeScript. Use them to organize sessions by tag or give them human-readable titles.

316 

317## Related resources

318 

319* [How the agent loop works](/en/agent-sdk/agent-loop): Understand turns, messages, and context accumulation within a session

320* [File checkpointing](/en/agent-sdk/file-checkpointing): Track and revert file changes across sessions

321* [Python `ClaudeAgentOptions`](/en/agent-sdk/python#claude-agent-options): Full session option reference for Python

322* [TypeScript `Options`](/en/agent-sdk/typescript#options): Full session option reference for TypeScript

agent-sdk/skills.md +296 −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# Agent Skills in the SDK

6 

7> Extend Claude with specialized capabilities using Agent Skills in the Claude Agent SDK

8 

9## Overview

10 

11Agent Skills extend Claude with specialized capabilities that Claude autonomously invokes when relevant. Skills are packaged as `SKILL.md` files containing instructions, descriptions, and optional supporting resources.

12 

13For comprehensive information about Skills, including benefits, architecture, and authoring guidelines, see the [Agent Skills overview](https://platform.claude.com/docs/en/agents-and-tools/agent-skills/overview).

14 

15## How Skills Work with the SDK

16 

17When using the Claude Agent SDK, Skills are:

18 

191. **Defined as filesystem artifacts**: Created as `SKILL.md` files in specific directories (`.claude/skills/`)

202. **Loaded from filesystem**: Skills are loaded from configured filesystem locations. You must specify `settingSources` (TypeScript) or `setting_sources` (Python) to load Skills from the filesystem

213. **Automatically discovered**: Once filesystem settings are loaded, Skill metadata is discovered at startup from user and project directories; full content loaded when triggered

224. **Model-invoked**: Claude autonomously chooses when to use them based on context

235. **Enabled via allowed\_tools**: Add `"Skill"` to your `allowed_tools` to enable Skills

24 

25Unlike subagents (which can be defined programmatically), Skills must be created as filesystem artifacts. The SDK does not provide a programmatic API for registering Skills.

26 

27<Note>

28 **Default behavior**: By default, the SDK does not load any filesystem settings. To use Skills, you must explicitly configure `settingSources: ['user', 'project']` (TypeScript) or `setting_sources=["user", "project"]` (Python) in your options.

29</Note>

30 

31## Using Skills with the SDK

32 

33To use Skills with the SDK, you need to:

34 

351. Include `"Skill"` in your `allowed_tools` configuration

362. Configure `settingSources`/`setting_sources` to load Skills from the filesystem

37 

38Once configured, Claude automatically discovers Skills from the specified directories and invokes them when relevant to the user's request.

39 

40<CodeGroup>

41 ```python Python theme={null}

42 import asyncio

43 from claude_agent_sdk import query, ClaudeAgentOptions

44 

45 

46 async def main():

47 options = ClaudeAgentOptions(

48 cwd="/path/to/project", # Project with .claude/skills/

49 setting_sources=["user", "project"], # Load Skills from filesystem

50 allowed_tools=["Skill", "Read", "Write", "Bash"], # Enable Skill tool

51 )

52 

53 async for message in query(

54 prompt="Help me process this PDF document", options=options

55 ):

56 print(message)

57 

58 

59 asyncio.run(main())

60 ```

61 

62 ```typescript TypeScript theme={null}

63 import { query } from "@anthropic-ai/claude-agent-sdk";

64 

65 for await (const message of query({

66 prompt: "Help me process this PDF document",

67 options: {

68 cwd: "/path/to/project", // Project with .claude/skills/

69 settingSources: ["user", "project"], // Load Skills from filesystem

70 allowedTools: ["Skill", "Read", "Write", "Bash"] // Enable Skill tool

71 }

72 })) {

73 console.log(message);

74 }

75 ```

76</CodeGroup>

77 

78## Skill Locations

79 

80Skills are loaded from filesystem directories based on your `settingSources`/`setting_sources` configuration:

81 

82* **Project Skills** (`.claude/skills/`): Shared with your team via git - loaded when `setting_sources` includes `"project"`

83* **User Skills** (`~/.claude/skills/`): Personal Skills across all projects - loaded when `setting_sources` includes `"user"`

84* **Plugin Skills**: Bundled with installed Claude Code plugins

85 

86## Creating Skills

87 

88Skills are defined as directories containing a `SKILL.md` file with YAML frontmatter and Markdown content. The `description` field determines when Claude invokes your Skill.

89 

90**Example directory structure**:

91 

92```bash theme={null}

93.claude/skills/processing-pdfs/

94└── SKILL.md

95```

96 

97For complete guidance on creating Skills, including SKILL.md structure, multi-file Skills, and examples, see:

98 

99* [Agent Skills in Claude Code](/en/skills): Complete guide with examples

100* [Agent Skills Best Practices](https://platform.claude.com/docs/en/agents-and-tools/agent-skills/best-practices): Authoring guidelines and naming conventions

101 

102## Tool Restrictions

103 

104<Note>

105 The `allowed-tools` frontmatter field in SKILL.md is only supported when using Claude Code CLI directly. **It does not apply when using Skills through the SDK**.

106 

107 When using the SDK, control tool access through the main `allowedTools` option in your query configuration.

108</Note>

109 

110To control tool access for Skills in SDK applications, use `allowedTools` to pre-approve specific tools. Without a `canUseTool` callback, anything not in the list is denied:

111 

112<Note>

113 Import statements from the first example are assumed in the following code snippets.

114</Note>

115 

116<CodeGroup>

117 ```python Python theme={null}

118 options = ClaudeAgentOptions(

119 setting_sources=["user", "project"], # Load Skills from filesystem

120 allowed_tools=["Skill", "Read", "Grep", "Glob"],

121 )

122 

123 async for message in query(prompt="Analyze the codebase structure", options=options):

124 print(message)

125 ```

126 

127 ```typescript TypeScript theme={null}

128 for await (const message of query({

129 prompt: "Analyze the codebase structure",

130 options: {

131 settingSources: ["user", "project"], // Load Skills from filesystem

132 allowedTools: ["Skill", "Read", "Grep", "Glob"],

133 permissionMode: "dontAsk" // Deny anything not in allowedTools

134 }

135 })) {

136 console.log(message);

137 }

138 ```

139</CodeGroup>

140 

141## Discovering Available Skills

142 

143To see which Skills are available in your SDK application, simply ask Claude:

144 

145<CodeGroup>

146 ```python Python theme={null}

147 options = ClaudeAgentOptions(

148 setting_sources=["user", "project"], # Load Skills from filesystem

149 allowed_tools=["Skill"],

150 )

151 

152 async for message in query(prompt="What Skills are available?", options=options):

153 print(message)

154 ```

155 

156 ```typescript TypeScript theme={null}

157 for await (const message of query({

158 prompt: "What Skills are available?",

159 options: {

160 settingSources: ["user", "project"], // Load Skills from filesystem

161 allowedTools: ["Skill"]

162 }

163 })) {

164 console.log(message);

165 }

166 ```

167</CodeGroup>

168 

169Claude will list the available Skills based on your current working directory and installed plugins.

170 

171## Testing Skills

172 

173Test Skills by asking questions that match their descriptions:

174 

175<CodeGroup>

176 ```python Python theme={null}

177 options = ClaudeAgentOptions(

178 cwd="/path/to/project",

179 setting_sources=["user", "project"], # Load Skills from filesystem

180 allowed_tools=["Skill", "Read", "Bash"],

181 )

182 

183 async for message in query(prompt="Extract text from invoice.pdf", options=options):

184 print(message)

185 ```

186 

187 ```typescript TypeScript theme={null}

188 for await (const message of query({

189 prompt: "Extract text from invoice.pdf",

190 options: {

191 cwd: "/path/to/project",

192 settingSources: ["user", "project"], // Load Skills from filesystem

193 allowedTools: ["Skill", "Read", "Bash"]

194 }

195 })) {

196 console.log(message);

197 }

198 ```

199</CodeGroup>

200 

201Claude automatically invokes the relevant Skill if the description matches your request.

202 

203## Troubleshooting

204 

205### Skills Not Found

206 

207**Check settingSources configuration**: Skills are only loaded when you explicitly configure `settingSources`/`setting_sources`. This is the most common issue:

208 

209<CodeGroup>

210 ```python Python theme={null}

211 # Wrong - Skills won't be loaded

212 options = ClaudeAgentOptions(allowed_tools=["Skill"])

213 

214 # Correct - Skills will be loaded

215 options = ClaudeAgentOptions(

216 setting_sources=["user", "project"], # Required to load Skills

217 allowed_tools=["Skill"],

218 )

219 ```

220 

221 ```typescript TypeScript theme={null}

222 // Wrong - Skills won't be loaded

223 const options = {

224 allowedTools: ["Skill"]

225 };

226 

227 // Correct - Skills will be loaded

228 const options = {

229 settingSources: ["user", "project"], // Required to load Skills

230 allowedTools: ["Skill"]

231 };

232 ```

233</CodeGroup>

234 

235For more details on `settingSources`/`setting_sources`, see the [TypeScript SDK reference](/en/agent-sdk/typescript#setting-source) or [Python SDK reference](/en/agent-sdk/python#setting-source).

236 

237**Check working directory**: The SDK loads Skills relative to the `cwd` option. Ensure it points to a directory containing `.claude/skills/`:

238 

239<CodeGroup>

240 ```python Python theme={null}

241 # Ensure your cwd points to the directory containing .claude/skills/

242 options = ClaudeAgentOptions(

243 cwd="/path/to/project", # Must contain .claude/skills/

244 setting_sources=["user", "project"], # Required to load Skills

245 allowed_tools=["Skill"],

246 )

247 ```

248 

249 ```typescript TypeScript theme={null}

250 // Ensure your cwd points to the directory containing .claude/skills/

251 const options = {

252 cwd: "/path/to/project", // Must contain .claude/skills/

253 settingSources: ["user", "project"], // Required to load Skills

254 allowedTools: ["Skill"]

255 };

256 ```

257</CodeGroup>

258 

259See the "Using Skills with the SDK" section above for the complete pattern.

260 

261**Verify filesystem location**:

262 

263```bash theme={null}

264# Check project Skills

265ls .claude/skills/*/SKILL.md

266 

267# Check personal Skills

268ls ~/.claude/skills/*/SKILL.md

269```

270 

271### Skill Not Being Used

272 

273**Check the Skill tool is enabled**: Confirm `"Skill"` is in your `allowedTools`.

274 

275**Check the description**: Ensure it's specific and includes relevant keywords. See [Agent Skills Best Practices](https://platform.claude.com/docs/en/agents-and-tools/agent-skills/best-practices#writing-effective-descriptions) for guidance on writing effective descriptions.

276 

277### Additional Troubleshooting

278 

279For general Skills troubleshooting (YAML syntax, debugging, etc.), see the [Claude Code Skills troubleshooting section](/en/skills#troubleshooting).

280 

281## Related Documentation

282 

283### Skills Guides

284 

285* [Agent Skills in Claude Code](/en/skills): Complete Skills guide with creation, examples, and troubleshooting

286* [Agent Skills Overview](https://platform.claude.com/docs/en/agents-and-tools/agent-skills/overview): Conceptual overview, benefits, and architecture

287* [Agent Skills Best Practices](https://platform.claude.com/docs/en/agents-and-tools/agent-skills/best-practices): Authoring guidelines for effective Skills

288* [Agent Skills Cookbook](https://platform.claude.com/cookbook/skills-notebooks-01-skills-introduction): Example Skills and templates

289 

290### SDK Resources

291 

292* [Subagents in the SDK](/en/agent-sdk/subagents): Similar filesystem-based agents with programmatic options

293* [Slash Commands in the SDK](/en/agent-sdk/slash-commands): User-invoked commands

294* [SDK Overview](/en/agent-sdk/overview): General SDK concepts

295* [TypeScript SDK Reference](/en/agent-sdk/typescript): Complete API documentation

296* [Python SDK Reference](/en/agent-sdk/python): Complete API documentation

agent-sdk/slash-commands.md +477 −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# Slash Commands in the SDK

6 

7> Learn how to use slash commands to control Claude Code sessions through the SDK

8 

9Slash commands provide a way to control Claude Code sessions with special commands that start with `/`. These commands can be sent through the SDK to perform actions like clearing conversation history, compacting messages, or getting help.

10 

11## Discovering Available Slash Commands

12 

13The Claude Agent SDK provides information about available slash commands in the system initialization message. Access this information when your session starts:

14 

15<CodeGroup>

16 ```typescript TypeScript theme={null}

17 import { query } from "@anthropic-ai/claude-agent-sdk";

18 

19 for await (const message of query({

20 prompt: "Hello Claude",

21 options: { maxTurns: 1 }

22 })) {

23 if (message.type === "system" && message.subtype === "init") {

24 console.log("Available slash commands:", message.slash_commands);

25 // Example output: ["/compact", "/clear", "/help"]

26 }

27 }

28 ```

29 

30 ```python Python theme={null}

31 import asyncio

32 from claude_agent_sdk import query, ClaudeAgentOptions, SystemMessage

33 

34 

35 async def main():

36 async for message in query(prompt="Hello Claude", options=ClaudeAgentOptions(max_turns=1)):

37 if isinstance(message, SystemMessage) and message.subtype == "init":

38 print("Available slash commands:", message.data["slash_commands"])

39 # Example output: ["/compact", "/clear", "/help"]

40 

41 

42 asyncio.run(main())

43 ```

44</CodeGroup>

45 

46## Sending Slash Commands

47 

48Send slash commands by including them in your prompt string, just like regular text:

49 

50<CodeGroup>

51 ```typescript TypeScript theme={null}

52 import { query } from "@anthropic-ai/claude-agent-sdk";

53 

54 // Send a slash command

55 for await (const message of query({

56 prompt: "/compact",

57 options: { maxTurns: 1 }

58 })) {

59 if (message.type === "result") {

60 console.log("Command executed:", message.result);

61 }

62 }

63 ```

64 

65 ```python Python theme={null}

66 import asyncio

67 from claude_agent_sdk import query, ClaudeAgentOptions, ResultMessage

68 

69 

70 async def main():

71 # Send a slash command

72 async for message in query(prompt="/compact", options=ClaudeAgentOptions(max_turns=1)):

73 if isinstance(message, ResultMessage):

74 print("Command executed:", message.result)

75 

76 

77 asyncio.run(main())

78 ```

79</CodeGroup>

80 

81## Common Slash Commands

82 

83### `/compact` - Compact Conversation History

84 

85The `/compact` command reduces the size of your conversation history by summarizing older messages while preserving important context:

86 

87<CodeGroup>

88 ```typescript TypeScript theme={null}

89 import { query } from "@anthropic-ai/claude-agent-sdk";

90 

91 for await (const message of query({

92 prompt: "/compact",

93 options: { maxTurns: 1 }

94 })) {

95 if (message.type === "system" && message.subtype === "compact_boundary") {

96 console.log("Compaction completed");

97 console.log("Pre-compaction tokens:", message.compact_metadata.pre_tokens);

98 console.log("Trigger:", message.compact_metadata.trigger);

99 }

100 }

101 ```

102 

103 ```python Python theme={null}

104 import asyncio

105 from claude_agent_sdk import query, ClaudeAgentOptions, SystemMessage

106 

107 

108 async def main():

109 async for message in query(prompt="/compact", options=ClaudeAgentOptions(max_turns=1)):

110 if isinstance(message, SystemMessage) and message.subtype == "compact_boundary":

111 print("Compaction completed")

112 print("Pre-compaction tokens:", message.data["compact_metadata"]["pre_tokens"])

113 print("Trigger:", message.data["compact_metadata"]["trigger"])

114 

115 

116 asyncio.run(main())

117 ```

118</CodeGroup>

119 

120### `/clear` - Clear Conversation

121 

122The `/clear` command starts a fresh conversation by clearing all previous history:

123 

124<CodeGroup>

125 ```typescript TypeScript theme={null}

126 import { query } from "@anthropic-ai/claude-agent-sdk";

127 

128 // Clear conversation and start fresh

129 for await (const message of query({

130 prompt: "/clear",

131 options: { maxTurns: 1 }

132 })) {

133 if (message.type === "system" && message.subtype === "init") {

134 console.log("Conversation cleared, new session started");

135 console.log("Session ID:", message.session_id);

136 }

137 }

138 ```

139 

140 ```python Python theme={null}

141 import asyncio

142 from claude_agent_sdk import query, ClaudeAgentOptions, SystemMessage

143 

144 

145 async def main():

146 # Clear conversation and start fresh

147 async for message in query(prompt="/clear", options=ClaudeAgentOptions(max_turns=1)):

148 if isinstance(message, SystemMessage) and message.subtype == "init":

149 print("Conversation cleared, new session started")

150 print("Session ID:", message.data["session_id"])

151 

152 

153 asyncio.run(main())

154 ```

155</CodeGroup>

156 

157## Creating Custom Slash Commands

158 

159In addition to using built-in slash commands, you can create your own custom commands that are available through the SDK. Custom commands are defined as markdown files in specific directories, similar to how subagents are configured.

160 

161<Note>

162 The `.claude/commands/` directory is the legacy format. The recommended format is `.claude/skills/<name>/SKILL.md`, which supports the same slash-command invocation (`/name`) plus autonomous invocation by Claude. See [Skills](/en/agent-sdk/skills) for the current format. The CLI continues to support both formats, and the examples below remain accurate for `.claude/commands/`.

163</Note>

164 

165### File Locations

166 

167Custom slash commands are stored in designated directories based on their scope:

168 

169* **Project commands**: `.claude/commands/` - Available only in the current project (legacy; prefer `.claude/skills/`)

170* **Personal commands**: `~/.claude/commands/` - Available across all your projects (legacy; prefer `~/.claude/skills/`)

171 

172### File Format

173 

174Each custom command is a markdown file where:

175 

176* The filename (without `.md` extension) becomes the command name

177* The file content defines what the command does

178* Optional YAML frontmatter provides configuration

179 

180#### Basic Example

181 

182Create `.claude/commands/refactor.md`:

183 

184```markdown theme={null}

185Refactor the selected code to improve readability and maintainability.

186Focus on clean code principles and best practices.

187```

188 

189This creates the `/refactor` command that you can use through the SDK.

190 

191#### With Frontmatter

192 

193Create `.claude/commands/security-check.md`:

194 

195```markdown theme={null}

196---

197allowed-tools: Read, Grep, Glob

198description: Run security vulnerability scan

199model: claude-opus-4-6

200---

201 

202Analyze the codebase for security vulnerabilities including:

203- SQL injection risks

204- XSS vulnerabilities

205- Exposed credentials

206- Insecure configurations

207```

208 

209### Using Custom Commands in the SDK

210 

211Once defined in the filesystem, custom commands are automatically available through the SDK:

212 

213<CodeGroup>

214 ```typescript TypeScript theme={null}

215 import { query } from "@anthropic-ai/claude-agent-sdk";

216 

217 // Use a custom command

218 for await (const message of query({

219 prompt: "/refactor src/auth/login.ts",

220 options: { maxTurns: 3 }

221 })) {

222 if (message.type === "assistant") {

223 console.log("Refactoring suggestions:", message.message);

224 }

225 }

226 

227 // Custom commands appear in the slash_commands list

228 for await (const message of query({

229 prompt: "Hello",

230 options: { maxTurns: 1 }

231 })) {

232 if (message.type === "system" && message.subtype === "init") {

233 // Will include both built-in and custom commands

234 console.log("Available commands:", message.slash_commands);

235 // Example: ["/compact", "/clear", "/help", "/refactor", "/security-check"]

236 }

237 }

238 ```

239 

240 ```python Python theme={null}

241 import asyncio

242 from claude_agent_sdk import query, ClaudeAgentOptions, AssistantMessage, SystemMessage

243 

244 

245 async def main():

246 # Use a custom command

247 async for message in query(

248 prompt="/refactor src/auth/login.py", options=ClaudeAgentOptions(max_turns=3)

249 ):

250 if isinstance(message, AssistantMessage):

251 for block in message.content:

252 if hasattr(block, "text"):

253 print("Refactoring suggestions:", block.text)

254 

255 # Custom commands appear in the slash_commands list

256 async for message in query(prompt="Hello", options=ClaudeAgentOptions(max_turns=1)):

257 if isinstance(message, SystemMessage) and message.subtype == "init":

258 # Will include both built-in and custom commands

259 print("Available commands:", message.data["slash_commands"])

260 # Example: ["/compact", "/clear", "/help", "/refactor", "/security-check"]

261 

262 

263 asyncio.run(main())

264 ```

265</CodeGroup>

266 

267### Advanced Features

268 

269#### Arguments and Placeholders

270 

271Custom commands support dynamic arguments using placeholders:

272 

273Create `.claude/commands/fix-issue.md`:

274 

275```markdown theme={null}

276---

277argument-hint: [issue-number] [priority]

278description: Fix a GitHub issue

279---

280 

281Fix issue #$1 with priority $2.

282Check the issue description and implement the necessary changes.

283```

284 

285Use in SDK:

286 

287<CodeGroup>

288 ```typescript TypeScript theme={null}

289 import { query } from "@anthropic-ai/claude-agent-sdk";

290 

291 // Pass arguments to custom command

292 for await (const message of query({

293 prompt: "/fix-issue 123 high",

294 options: { maxTurns: 5 }

295 })) {

296 // Command will process with $1="123" and $2="high"

297 if (message.type === "result") {

298 console.log("Issue fixed:", message.result);

299 }

300 }

301 ```

302 

303 ```python Python theme={null}

304 import asyncio

305 from claude_agent_sdk import query, ClaudeAgentOptions, ResultMessage

306 

307 

308 async def main():

309 # Pass arguments to custom command

310 async for message in query(prompt="/fix-issue 123 high", options=ClaudeAgentOptions(max_turns=5)):

311 # Command will process with $1="123" and $2="high"

312 if isinstance(message, ResultMessage):

313 print("Issue fixed:", message.result)

314 

315 

316 asyncio.run(main())

317 ```

318</CodeGroup>

319 

320#### Bash Command Execution

321 

322Custom commands can execute bash commands and include their output:

323 

324Create `.claude/commands/git-commit.md`:

325 

326```markdown theme={null}

327---

328allowed-tools: Bash(git add:*), Bash(git status:*), Bash(git commit:*)

329description: Create a git commit

330---

331 

332## Context

333 

334- Current status: !`git status`

335- Current diff: !`git diff HEAD`

336 

337## Task

338 

339Create a git commit with appropriate message based on the changes.

340```

341 

342#### File References

343 

344Include file contents using the `@` prefix:

345 

346Create `.claude/commands/review-config.md`:

347 

348```markdown theme={null}

349---

350description: Review configuration files

351---

352 

353Review the following configuration files for issues:

354- Package config: @package.json

355- TypeScript config: @tsconfig.json

356- Environment config: @.env

357 

358Check for security issues, outdated dependencies, and misconfigurations.

359```

360 

361### Organization with Namespacing

362 

363Organize commands in subdirectories for better structure:

364 

365```bash theme={null}

366.claude/commands/

367├── frontend/

368│ ├── component.md # Creates /component (project:frontend)

369│ └── style-check.md # Creates /style-check (project:frontend)

370├── backend/

371│ ├── api-test.md # Creates /api-test (project:backend)

372│ └── db-migrate.md # Creates /db-migrate (project:backend)

373└── review.md # Creates /review (project)

374```

375 

376The subdirectory appears in the command description but doesn't affect the command name itself.

377 

378### Practical Examples

379 

380#### Code Review Command

381 

382Create `.claude/commands/code-review.md`:

383 

384```markdown theme={null}

385---

386allowed-tools: Read, Grep, Glob, Bash(git diff:*)

387description: Comprehensive code review

388---

389 

390## Changed Files

391!`git diff --name-only HEAD~1`

392 

393## Detailed Changes

394!`git diff HEAD~1`

395 

396## Review Checklist

397 

398Review the above changes for:

3991. Code quality and readability

4002. Security vulnerabilities

4013. Performance implications

4024. Test coverage

4035. Documentation completeness

404 

405Provide specific, actionable feedback organized by priority.

406```

407 

408#### Test Runner Command

409 

410Create `.claude/commands/test.md`:

411 

412```markdown theme={null}

413---

414allowed-tools: Bash, Read, Edit

415argument-hint: [test-pattern]

416description: Run tests with optional pattern

417---

418 

419Run tests matching pattern: $ARGUMENTS

420 

4211. Detect the test framework (Jest, pytest, etc.)

4222. Run tests with the provided pattern

4233. If tests fail, analyze and fix them

4244. Re-run to verify fixes

425```

426 

427Use these commands through the SDK:

428 

429<CodeGroup>

430 ```typescript TypeScript theme={null}

431 import { query } from "@anthropic-ai/claude-agent-sdk";

432 

433 // Run code review

434 for await (const message of query({

435 prompt: "/code-review",

436 options: { maxTurns: 3 }

437 })) {

438 // Process review feedback

439 }

440 

441 // Run specific tests

442 for await (const message of query({

443 prompt: "/test auth",

444 options: { maxTurns: 5 }

445 })) {

446 // Handle test results

447 }

448 ```

449 

450 ```python Python theme={null}

451 import asyncio

452 from claude_agent_sdk import query, ClaudeAgentOptions

453 

454 

455 async def main():

456 # Run code review

457 async for message in query(prompt="/code-review", options=ClaudeAgentOptions(max_turns=3)):

458 # Process review feedback

459 pass

460 

461 # Run specific tests

462 async for message in query(prompt="/test auth", options=ClaudeAgentOptions(max_turns=5)):

463 # Handle test results

464 pass

465 

466 

467 asyncio.run(main())

468 ```

469</CodeGroup>

470 

471## See Also

472 

473* [Slash Commands](/en/skills) - Complete slash command documentation

474* [Subagents in the SDK](/en/agent-sdk/subagents) - Similar filesystem-based configuration for subagents

475* [TypeScript SDK reference](/en/agent-sdk/typescript) - Complete API documentation

476* [SDK overview](/en/agent-sdk/overview) - General SDK concepts

477* [CLI reference](/en/cli-reference) - Command-line interface

agent-sdk/streaming-output.md +396 −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# Stream responses in real-time

6 

7> Get real-time responses from the Agent SDK as text and tool calls stream in

8 

9By default, the Agent SDK yields complete `AssistantMessage` objects after Claude finishes generating each response. To receive incremental updates as text and tool calls are generated, enable partial message streaming by setting `include_partial_messages` (Python) or `includePartialMessages` (TypeScript) to `true` in your options.

10 

11<Tip>

12 This page covers output streaming (receiving tokens in real-time). For input modes (how you send messages), see [Send messages to agents](/en/agent-sdk/streaming-vs-single-mode). You can also [stream responses using the Agent SDK via the CLI](/en/headless).

13</Tip>

14 

15## Enable streaming output

16 

17To enable streaming, set `include_partial_messages` (Python) or `includePartialMessages` (TypeScript) to `true` in your options. This causes the SDK to yield `StreamEvent` messages containing raw API events as they arrive, in addition to the usual `AssistantMessage` and `ResultMessage`.

18 

19Your code then needs to:

20 

211. Check each message's type to distinguish `StreamEvent` from other message types

222. For `StreamEvent`, extract the `event` field and check its `type`

233. Look for `content_block_delta` events where `delta.type` is `text_delta`, which contain the actual text chunks

24 

25The example below enables streaming and prints text chunks as they arrive. Notice the nested type checks: first for `StreamEvent`, then for `content_block_delta`, then for `text_delta`:

26 

27<CodeGroup>

28 ```python Python theme={null}

29 from claude_agent_sdk import query, ClaudeAgentOptions

30 from claude_agent_sdk.types import StreamEvent

31 import asyncio

32 

33 

34 async def stream_response():

35 options = ClaudeAgentOptions(

36 include_partial_messages=True,

37 allowed_tools=["Bash", "Read"],

38 )

39 

40 async for message in query(prompt="List the files in my project", options=options):

41 if isinstance(message, StreamEvent):

42 event = message.event

43 if event.get("type") == "content_block_delta":

44 delta = event.get("delta", {})

45 if delta.get("type") == "text_delta":

46 print(delta.get("text", ""), end="", flush=True)

47 

48 

49 asyncio.run(stream_response())

50 ```

51 

52 ```typescript TypeScript theme={null}

53 import { query } from "@anthropic-ai/claude-agent-sdk";

54 

55 for await (const message of query({

56 prompt: "List the files in my project",

57 options: {

58 includePartialMessages: true,

59 allowedTools: ["Bash", "Read"]

60 }

61 })) {

62 if (message.type === "stream_event") {

63 const event = message.event;

64 if (event.type === "content_block_delta") {

65 if (event.delta.type === "text_delta") {

66 process.stdout.write(event.delta.text);

67 }

68 }

69 }

70 }

71 ```

72</CodeGroup>

73 

74## StreamEvent reference

75 

76When partial messages are enabled, you receive raw Claude API streaming events wrapped in an object. The type has different names in each SDK:

77 

78* **Python**: `StreamEvent` (import from `claude_agent_sdk.types`)

79* **TypeScript**: `SDKPartialAssistantMessage` with `type: 'stream_event'`

80 

81Both contain raw Claude API events, not accumulated text. You need to extract and accumulate text deltas yourself. Here's the structure of each type:

82 

83<CodeGroup>

84 ```python Python theme={null}

85 @dataclass

86 class StreamEvent:

87 uuid: str # Unique identifier for this event

88 session_id: str # Session identifier

89 event: dict[str, Any] # The raw Claude API stream event

90 parent_tool_use_id: str | None # Parent tool ID if from a subagent

91 ```

92 

93 ```typescript TypeScript theme={null}

94 type SDKPartialAssistantMessage = {

95 type: "stream_event";

96 event: RawMessageStreamEvent; // From Anthropic SDK

97 parent_tool_use_id: string | null;

98 uuid: UUID;

99 session_id: string;

100 };

101 ```

102</CodeGroup>

103 

104The `event` field contains the raw streaming event from the [Claude API](https://platform.claude.com/docs/en/build-with-claude/streaming#event-types). Common event types include:

105 

106| Event Type | Description |

107| :-------------------- | :---------------------------------------------- |

108| `message_start` | Start of a new message |

109| `content_block_start` | Start of a new content block (text or tool use) |

110| `content_block_delta` | Incremental update to content |

111| `content_block_stop` | End of a content block |

112| `message_delta` | Message-level updates (stop reason, usage) |

113| `message_stop` | End of the message |

114 

115## Message flow

116 

117With partial messages enabled, you receive messages in this order:

118 

119```text theme={null}

120StreamEvent (message_start)

121StreamEvent (content_block_start) - text block

122StreamEvent (content_block_delta) - text chunks...

123StreamEvent (content_block_stop)

124StreamEvent (content_block_start) - tool_use block

125StreamEvent (content_block_delta) - tool input chunks...

126StreamEvent (content_block_stop)

127StreamEvent (message_delta)

128StreamEvent (message_stop)

129AssistantMessage - complete message with all content

130... tool executes ...

131... more streaming events for next turn ...

132ResultMessage - final result

133```

134 

135Without partial messages enabled (`include_partial_messages` in Python, `includePartialMessages` in TypeScript), you receive all message types except `StreamEvent`. Common types include `SystemMessage` (session initialization), `AssistantMessage` (complete responses), `ResultMessage` (final result), and a compact boundary message indicating when conversation history was compacted (`SDKCompactBoundaryMessage` in TypeScript; `SystemMessage` with subtype `"compact_boundary"` in Python).

136 

137## Stream text responses

138 

139To display text as it's generated, look for `content_block_delta` events where `delta.type` is `text_delta`. These contain the incremental text chunks. The example below prints each chunk as it arrives:

140 

141<CodeGroup>

142 ```python Python theme={null}

143 from claude_agent_sdk import query, ClaudeAgentOptions

144 from claude_agent_sdk.types import StreamEvent

145 import asyncio

146 

147 

148 async def stream_text():

149 options = ClaudeAgentOptions(include_partial_messages=True)

150 

151 async for message in query(prompt="Explain how databases work", options=options):

152 if isinstance(message, StreamEvent):

153 event = message.event

154 if event.get("type") == "content_block_delta":

155 delta = event.get("delta", {})

156 if delta.get("type") == "text_delta":

157 # Print each text chunk as it arrives

158 print(delta.get("text", ""), end="", flush=True)

159 

160 print() # Final newline

161 

162 

163 asyncio.run(stream_text())

164 ```

165 

166 ```typescript TypeScript theme={null}

167 import { query } from "@anthropic-ai/claude-agent-sdk";

168 

169 for await (const message of query({

170 prompt: "Explain how databases work",

171 options: { includePartialMessages: true }

172 })) {

173 if (message.type === "stream_event") {

174 const event = message.event;

175 if (event.type === "content_block_delta" && event.delta.type === "text_delta") {

176 process.stdout.write(event.delta.text);

177 }

178 }

179 }

180 

181 console.log(); // Final newline

182 ```

183</CodeGroup>

184 

185## Stream tool calls

186 

187Tool calls also stream incrementally. You can track when tools start, receive their input as it's generated, and see when they complete. The example below tracks the current tool being called and accumulates the JSON input as it streams in. It uses three event types:

188 

189* `content_block_start`: tool begins

190* `content_block_delta` with `input_json_delta`: input chunks arrive

191* `content_block_stop`: tool call complete

192 

193<CodeGroup>

194 ```python Python theme={null}

195 from claude_agent_sdk import query, ClaudeAgentOptions

196 from claude_agent_sdk.types import StreamEvent

197 import asyncio

198 

199 

200 async def stream_tool_calls():

201 options = ClaudeAgentOptions(

202 include_partial_messages=True,

203 allowed_tools=["Read", "Bash"],

204 )

205 

206 # Track the current tool and accumulate its input JSON

207 current_tool = None

208 tool_input = ""

209 

210 async for message in query(prompt="Read the README.md file", options=options):

211 if isinstance(message, StreamEvent):

212 event = message.event

213 event_type = event.get("type")

214 

215 if event_type == "content_block_start":

216 # New tool call is starting

217 content_block = event.get("content_block", {})

218 if content_block.get("type") == "tool_use":

219 current_tool = content_block.get("name")

220 tool_input = ""

221 print(f"Starting tool: {current_tool}")

222 

223 elif event_type == "content_block_delta":

224 delta = event.get("delta", {})

225 if delta.get("type") == "input_json_delta":

226 # Accumulate JSON input as it streams in

227 chunk = delta.get("partial_json", "")

228 tool_input += chunk

229 print(f" Input chunk: {chunk}")

230 

231 elif event_type == "content_block_stop":

232 # Tool call complete - show final input

233 if current_tool:

234 print(f"Tool {current_tool} called with: {tool_input}")

235 current_tool = None

236 

237 

238 asyncio.run(stream_tool_calls())

239 ```

240 

241 ```typescript TypeScript theme={null}

242 import { query } from "@anthropic-ai/claude-agent-sdk";

243 

244 // Track the current tool and accumulate its input JSON

245 let currentTool: string | null = null;

246 let toolInput = "";

247 

248 for await (const message of query({

249 prompt: "Read the README.md file",

250 options: {

251 includePartialMessages: true,

252 allowedTools: ["Read", "Bash"]

253 }

254 })) {

255 if (message.type === "stream_event") {

256 const event = message.event;

257 

258 if (event.type === "content_block_start") {

259 // New tool call is starting

260 if (event.content_block.type === "tool_use") {

261 currentTool = event.content_block.name;

262 toolInput = "";

263 console.log(`Starting tool: ${currentTool}`);

264 }

265 } else if (event.type === "content_block_delta") {

266 if (event.delta.type === "input_json_delta") {

267 // Accumulate JSON input as it streams in

268 const chunk = event.delta.partial_json;

269 toolInput += chunk;

270 console.log(` Input chunk: ${chunk}`);

271 }

272 } else if (event.type === "content_block_stop") {

273 // Tool call complete - show final input

274 if (currentTool) {

275 console.log(`Tool ${currentTool} called with: ${toolInput}`);

276 currentTool = null;

277 }

278 }

279 }

280 }

281 ```

282</CodeGroup>

283 

284## Build a streaming UI

285 

286This example combines text and tool streaming into a cohesive UI. It tracks whether the agent is currently executing a tool (using an `in_tool` flag) to show status indicators like `[Using Read...]` while tools run. Text streams normally when not in a tool, and tool completion triggers a "done" message. This pattern is useful for chat interfaces that need to show progress during multi-step agent tasks.

287 

288<CodeGroup>

289 ```python Python theme={null}

290 from claude_agent_sdk import query, ClaudeAgentOptions, ResultMessage

291 from claude_agent_sdk.types import StreamEvent

292 import asyncio

293 import sys

294 

295 

296 async def streaming_ui():

297 options = ClaudeAgentOptions(

298 include_partial_messages=True,

299 allowed_tools=["Read", "Bash", "Grep"],

300 )

301 

302 # Track whether we're currently in a tool call

303 in_tool = False

304 

305 async for message in query(

306 prompt="Find all TODO comments in the codebase", options=options

307 ):

308 if isinstance(message, StreamEvent):

309 event = message.event

310 event_type = event.get("type")

311 

312 if event_type == "content_block_start":

313 content_block = event.get("content_block", {})

314 if content_block.get("type") == "tool_use":

315 # Tool call is starting - show status indicator

316 tool_name = content_block.get("name")

317 print(f"\n[Using {tool_name}...]", end="", flush=True)

318 in_tool = True

319 

320 elif event_type == "content_block_delta":

321 delta = event.get("delta", {})

322 # Only stream text when not executing a tool

323 if delta.get("type") == "text_delta" and not in_tool:

324 sys.stdout.write(delta.get("text", ""))

325 sys.stdout.flush()

326 

327 elif event_type == "content_block_stop":

328 if in_tool:

329 # Tool call finished

330 print(" done", flush=True)

331 in_tool = False

332 

333 elif isinstance(message, ResultMessage):

334 # Agent finished all work

335 print(f"\n\n--- Complete ---")

336 

337 

338 asyncio.run(streaming_ui())

339 ```

340 

341 ```typescript TypeScript theme={null}

342 import { query } from "@anthropic-ai/claude-agent-sdk";

343 

344 // Track whether we're currently in a tool call

345 let inTool = false;

346 

347 for await (const message of query({

348 prompt: "Find all TODO comments in the codebase",

349 options: {

350 includePartialMessages: true,

351 allowedTools: ["Read", "Bash", "Grep"]

352 }

353 })) {

354 if (message.type === "stream_event") {

355 const event = message.event;

356 

357 if (event.type === "content_block_start") {

358 if (event.content_block.type === "tool_use") {

359 // Tool call is starting - show status indicator

360 process.stdout.write(`\n[Using ${event.content_block.name}...]`);

361 inTool = true;

362 }

363 } else if (event.type === "content_block_delta") {

364 // Only stream text when not executing a tool

365 if (event.delta.type === "text_delta" && !inTool) {

366 process.stdout.write(event.delta.text);

367 }

368 } else if (event.type === "content_block_stop") {

369 if (inTool) {

370 // Tool call finished

371 console.log(" done");

372 inTool = false;

373 }

374 }

375 } else if (message.type === "result") {

376 // Agent finished all work

377 console.log("\n\n--- Complete ---");

378 }

379 }

380 ```

381</CodeGroup>

382 

383## Known limitations

384 

385Some SDK features are incompatible with streaming:

386 

387* **Extended thinking**: when you explicitly set `max_thinking_tokens` (Python) or `maxThinkingTokens` (TypeScript), `StreamEvent` messages are not emitted. You'll only receive complete messages after each turn. Note that thinking is disabled by default in the SDK, so streaming works unless you enable it.

388* **Structured output**: the JSON result appears only in the final `ResultMessage.structured_output`, not as streaming deltas. See [structured outputs](/en/agent-sdk/structured-outputs) for details.

389 

390## Next steps

391 

392Now that you can stream text and tool calls in real-time, explore these related topics:

393 

394* [Interactive vs one-shot queries](/en/agent-sdk/streaming-vs-single-mode): choose between input modes for your use case

395* [Structured outputs](/en/agent-sdk/structured-outputs): get typed JSON responses from the agent

396* [Permissions](/en/agent-sdk/permissions): control which tools the agent can use

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# Streaming Input

6 

7> Understanding the two input modes for Claude Agent SDK and when to use each

8 

9## Overview

10 

11The Claude Agent SDK supports two distinct input modes for interacting with agents:

12 

13* **Streaming Input Mode** (Default & Recommended) - A persistent, interactive session

14* **Single Message Input** - One-shot queries that use session state and resuming

15 

16This guide explains the differences, benefits, and use cases for each mode to help you choose the right approach for your application.

17 

18## Streaming Input Mode (Recommended)

19 

20Streaming input mode is the **preferred** way to use the Claude Agent SDK. It provides full access to the agent's capabilities and enables rich, interactive experiences.

21 

22It allows the agent to operate as a long lived process that takes in user input, handles interruptions, surfaces permission requests, and handles session management.

23 

24### How It Works

25 

26```mermaid theme={null}

27sequenceDiagram

28 participant App as Your Application

29 participant Agent as Claude Agent

30 participant Tools as Tools/Hooks

31 participant FS as Environment/<br/>File System

32 

33 App->>Agent: Initialize with AsyncGenerator

34 activate Agent

35 

36 App->>Agent: Yield Message 1

37 Agent->>Tools: Execute tools

38 Tools->>FS: Read files

39 FS-->>Tools: File contents

40 Tools->>FS: Write/Edit files

41 FS-->>Tools: Success/Error

42 Agent-->>App: Stream partial response

43 Agent-->>App: Stream more content...

44 Agent->>App: Complete Message 1

45 

46 App->>Agent: Yield Message 2 + Image

47 Agent->>Tools: Process image & execute

48 Tools->>FS: Access filesystem

49 FS-->>Tools: Operation results

50 Agent-->>App: Stream response 2

51 

52 App->>Agent: Queue Message 3

53 App->>Agent: Interrupt/Cancel

54 Agent->>App: Handle interruption

55 

56 Note over App,Agent: Session stays alive

57 Note over Tools,FS: Persistent file system<br/>state maintained

58 

59 deactivate Agent

60```

61 

62### Benefits

63 

64<CardGroup cols={2}>

65 <Card title="Image Uploads" icon="image">

66 Attach images directly to messages for visual analysis and understanding

67 </Card>

68 

69 <Card title="Queued Messages" icon="stack">

70 Send multiple messages that process sequentially, with ability to interrupt

71 </Card>

72 

73 <Card title="Tool Integration" icon="wrench">

74 Full access to all tools and custom MCP servers during the session

75 </Card>

76 

77 <Card title="Hooks Support" icon="link">

78 Use lifecycle hooks to customize behavior at various points

79 </Card>

80 

81 <Card title="Real-time Feedback" icon="lightning">

82 See responses as they're generated, not just final results

83 </Card>

84 

85 <Card title="Context Persistence" icon="database">

86 Maintain conversation context across multiple turns naturally

87 </Card>

88</CardGroup>

89 

90### Implementation Example

91 

92<CodeGroup>

93 ```typescript TypeScript theme={null}

94 import { query } from "@anthropic-ai/claude-agent-sdk";

95 import { readFile } from "fs/promises";

96 

97 async function* generateMessages() {

98 // First message

99 yield {

100 type: "user" as const,

101 message: {

102 role: "user" as const,

103 content: "Analyze this codebase for security issues"

104 }

105 };

106 

107 // Wait for conditions or user input

108 await new Promise((resolve) => setTimeout(resolve, 2000));

109 

110 // Follow-up with image

111 yield {

112 type: "user" as const,

113 message: {

114 role: "user" as const,

115 content: [

116 {

117 type: "text",

118 text: "Review this architecture diagram"

119 },

120 {

121 type: "image",

122 source: {

123 type: "base64",

124 media_type: "image/png",

125 data: await readFile("diagram.png", "base64")

126 }

127 }

128 ]

129 }

130 };

131 }

132 

133 // Process streaming responses

134 for await (const message of query({

135 prompt: generateMessages(),

136 options: {

137 maxTurns: 10,

138 allowedTools: ["Read", "Grep"]

139 }

140 })) {

141 if (message.type === "result") {

142 console.log(message.result);

143 }

144 }

145 ```

146 

147 ```python Python theme={null}

148 from claude_agent_sdk import (

149 ClaudeSDKClient,

150 ClaudeAgentOptions,

151 AssistantMessage,

152 TextBlock,

153 )

154 import asyncio

155 import base64

156 

157 

158 async def streaming_analysis():

159 async def message_generator():

160 # First message

161 yield {

162 "type": "user",

163 "message": {

164 "role": "user",

165 "content": "Analyze this codebase for security issues",

166 },

167 }

168 

169 # Wait for conditions

170 await asyncio.sleep(2)

171 

172 # Follow-up with image

173 with open("diagram.png", "rb") as f:

174 image_data = base64.b64encode(f.read()).decode()

175 

176 yield {

177 "type": "user",

178 "message": {

179 "role": "user",

180 "content": [

181 {"type": "text", "text": "Review this architecture diagram"},

182 {

183 "type": "image",

184 "source": {

185 "type": "base64",

186 "media_type": "image/png",

187 "data": image_data,

188 },

189 },

190 ],

191 },

192 }

193 

194 # Use ClaudeSDKClient for streaming input

195 options = ClaudeAgentOptions(max_turns=10, allowed_tools=["Read", "Grep"])

196 

197 async with ClaudeSDKClient(options) as client:

198 # Send streaming input

199 await client.query(message_generator())

200 

201 # Process responses

202 async for message in client.receive_response():

203 if isinstance(message, AssistantMessage):

204 for block in message.content:

205 if isinstance(block, TextBlock):

206 print(block.text)

207 

208 

209 asyncio.run(streaming_analysis())

210 ```

211</CodeGroup>

212 

213## Single Message Input

214 

215Single message input is simpler but more limited.

216 

217### When to Use Single Message Input

218 

219Use single message input when:

220 

221* You need a one-shot response

222* You do not need image attachments, hooks, etc.

223* You need to operate in a stateless environment, such as a lambda function

224 

225### Limitations

226 

227<Warning>

228 Single message input mode does **not** support:

229 

230 * Direct image attachments in messages

231 * Dynamic message queueing

232 * Real-time interruption

233 * Hook integration

234 * Natural multi-turn conversations

235</Warning>

236 

237### Implementation Example

238 

239<CodeGroup>

240 ```typescript TypeScript theme={null}

241 import { query } from "@anthropic-ai/claude-agent-sdk";

242 

243 // Simple one-shot query

244 for await (const message of query({

245 prompt: "Explain the authentication flow",

246 options: {

247 maxTurns: 1,

248 allowedTools: ["Read", "Grep"]

249 }

250 })) {

251 if (message.type === "result") {

252 console.log(message.result);

253 }

254 }

255 

256 // Continue conversation with session management

257 for await (const message of query({

258 prompt: "Now explain the authorization process",

259 options: {

260 continue: true,

261 maxTurns: 1

262 }

263 })) {

264 if (message.type === "result") {

265 console.log(message.result);

266 }

267 }

268 ```

269 

270 ```python Python theme={null}

271 from claude_agent_sdk import query, ClaudeAgentOptions, ResultMessage

272 import asyncio

273 

274 

275 async def single_message_example():

276 # Simple one-shot query using query() function

277 async for message in query(

278 prompt="Explain the authentication flow",

279 options=ClaudeAgentOptions(max_turns=1, allowed_tools=["Read", "Grep"]),

280 ):

281 if isinstance(message, ResultMessage):

282 print(message.result)

283 

284 # Continue conversation with session management

285 async for message in query(

286 prompt="Now explain the authorization process",

287 options=ClaudeAgentOptions(continue_conversation=True, max_turns=1),

288 ):

289 if isinstance(message, ResultMessage):

290 print(message.result)

291 

292 

293 asyncio.run(single_message_example())

294 ```

295</CodeGroup>

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# Get structured output from agents

6 

7> Return validated JSON from agent workflows using JSON Schema, Zod, or Pydantic. Get type-safe, structured data after multi-turn tool use.

8 

9Structured outputs let you define the exact shape of data you want back from an agent. The agent can use any tools it needs to complete the task, and you still get validated JSON matching your schema at the end. Define a [JSON Schema](https://json-schema.org/understanding-json-schema/about) for the structure you need, and the SDK guarantees the output matches it.

10 

11For full type safety, use [Zod](#type-safe-schemas-with-zod-and-pydantic) (TypeScript) or [Pydantic](#type-safe-schemas-with-zod-and-pydantic) (Python) to define your schema and get strongly-typed objects back.

12 

13## Why structured outputs?

14 

15Agents return free-form text by default, which works for chat but not when you need to use the output programmatically. Structured outputs give you typed data you can pass directly to your application logic, database, or UI components.

16 

17Consider a recipe app where an agent searches the web and brings back recipes. Without structured outputs, you get free-form text that you'd need to parse yourself. With structured outputs, you define the shape you want and get typed data you can use directly in your app.

18 

19<AccordionGroup>

20 <Accordion title="Without structured outputs">

21 ```text theme={null}

22 Here's a classic chocolate chip cookie recipe!

23 

24 **Chocolate Chip Cookies**

25 Prep time: 15 minutes | Cook time: 10 minutes

26 

27 Ingredients:

28 - 2 1/4 cups all-purpose flour

29 - 1 cup butter, softened

30 ...

31 ```

32 

33 To use this in your app, you'd need to parse out the title, convert "15 minutes" to a number, separate ingredients from instructions, and handle inconsistent formatting across responses.

34 </Accordion>

35 

36 <Accordion title="With structured outputs">

37 ```json theme={null}

38 {

39 "name": "Chocolate Chip Cookies",

40 "prep_time_minutes": 15,

41 "cook_time_minutes": 10,

42 "ingredients": [

43 { "item": "all-purpose flour", "amount": 2.25, "unit": "cups" },

44 { "item": "butter, softened", "amount": 1, "unit": "cup" }

45 // ...

46 ],

47 "steps": ["Preheat oven to 375°F", "Cream butter and sugar" /* ... */]

48 }

49 ```

50 

51 Typed data you can use directly in your UI.

52 </Accordion>

53</AccordionGroup>

54 

55## Quick start

56 

57To use structured outputs, define a [JSON Schema](https://json-schema.org/understanding-json-schema/about) describing the shape of data you want, then pass it to `query()` via the `outputFormat` option (TypeScript) or `output_format` option (Python). When the agent finishes, the result message includes a `structured_output` field with validated data matching your schema.

58 

59The example below asks the agent to research Anthropic and return the company name, year founded, and headquarters as structured output.

60 

61<CodeGroup>

62 ```typescript TypeScript theme={null}

63 import { query } from "@anthropic-ai/claude-agent-sdk";

64 

65 // Define the shape of data you want back

66 const schema = {

67 type: "object",

68 properties: {

69 company_name: { type: "string" },

70 founded_year: { type: "number" },

71 headquarters: { type: "string" }

72 },

73 required: ["company_name"]

74 };

75 

76 for await (const message of query({

77 prompt: "Research Anthropic and provide key company information",

78 options: {

79 outputFormat: {

80 type: "json_schema",

81 schema: schema

82 }

83 }

84 })) {

85 // The result message contains structured_output with validated data

86 if (message.type === "result" && message.subtype === "success" && message.structured_output) {

87 console.log(message.structured_output);

88 // { company_name: "Anthropic", founded_year: 2021, headquarters: "San Francisco, CA" }

89 }

90 }

91 ```

92 

93 ```python Python theme={null}

94 import asyncio

95 from claude_agent_sdk import query, ClaudeAgentOptions, ResultMessage

96 

97 # Define the shape of data you want back

98 schema = {

99 "type": "object",

100 "properties": {

101 "company_name": {"type": "string"},

102 "founded_year": {"type": "number"},

103 "headquarters": {"type": "string"},

104 },

105 "required": ["company_name"],

106 }

107 

108 

109 async def main():

110 async for message in query(

111 prompt="Research Anthropic and provide key company information",

112 options=ClaudeAgentOptions(

113 output_format={"type": "json_schema", "schema": schema}

114 ),

115 ):

116 # The result message contains structured_output with validated data

117 if isinstance(message, ResultMessage) and message.structured_output:

118 print(message.structured_output)

119 # {'company_name': 'Anthropic', 'founded_year': 2021, 'headquarters': 'San Francisco, CA'}

120 

121 

122 asyncio.run(main())

123 ```

124</CodeGroup>

125 

126## Type-safe schemas with Zod and Pydantic

127 

128Instead of writing JSON Schema by hand, you can use [Zod](https://zod.dev/) (TypeScript) or [Pydantic](https://docs.pydantic.dev/latest/) (Python) to define your schema. These libraries generate the JSON Schema for you and let you parse the response into a fully-typed object you can use throughout your codebase with autocomplete and type checking.

129 

130The example below defines a schema for a feature implementation plan with a summary, list of steps (each with complexity level), and potential risks. The agent plans the feature and returns a typed `FeaturePlan` object. You can then access properties like `plan.summary` and iterate over `plan.steps` with full type safety.

131 

132<CodeGroup>

133 ```typescript TypeScript theme={null}

134 import { z } from "zod";

135 import { query } from "@anthropic-ai/claude-agent-sdk";

136 

137 // Define schema with Zod

138 const FeaturePlan = z.object({

139 feature_name: z.string(),

140 summary: z.string(),

141 steps: z.array(

142 z.object({

143 step_number: z.number(),

144 description: z.string(),

145 estimated_complexity: z.enum(["low", "medium", "high"])

146 })

147 ),

148 risks: z.array(z.string())

149 });

150 

151 type FeaturePlan = z.infer<typeof FeaturePlan>;

152 

153 // Convert to JSON Schema

154 const schema = z.toJSONSchema(FeaturePlan);

155 

156 // Use in query

157 for await (const message of query({

158 prompt:

159 "Plan how to add dark mode support to a React app. Break it into implementation steps.",

160 options: {

161 outputFormat: {

162 type: "json_schema",

163 schema: schema

164 }

165 }

166 })) {

167 if (message.type === "result" && message.subtype === "success" && message.structured_output) {

168 // Validate and get fully typed result

169 const parsed = FeaturePlan.safeParse(message.structured_output);

170 if (parsed.success) {

171 const plan: FeaturePlan = parsed.data;

172 console.log(`Feature: ${plan.feature_name}`);

173 console.log(`Summary: ${plan.summary}`);

174 plan.steps.forEach((step) => {

175 console.log(`${step.step_number}. [${step.estimated_complexity}] ${step.description}`);

176 });

177 }

178 }

179 }

180 ```

181 

182 ```python Python theme={null}

183 import asyncio

184 from pydantic import BaseModel

185 from claude_agent_sdk import query, ClaudeAgentOptions, ResultMessage

186 

187 

188 class Step(BaseModel):

189 step_number: int

190 description: str

191 estimated_complexity: str # 'low', 'medium', 'high'

192 

193 

194 class FeaturePlan(BaseModel):

195 feature_name: str

196 summary: str

197 steps: list[Step]

198 risks: list[str]

199 

200 

201 async def main():

202 async for message in query(

203 prompt="Plan how to add dark mode support to a React app. Break it into implementation steps.",

204 options=ClaudeAgentOptions(

205 output_format={

206 "type": "json_schema",

207 "schema": FeaturePlan.model_json_schema(),

208 }

209 ),

210 ):

211 if isinstance(message, ResultMessage) and message.structured_output:

212 # Validate and get fully typed result

213 plan = FeaturePlan.model_validate(message.structured_output)

214 print(f"Feature: {plan.feature_name}")

215 print(f"Summary: {plan.summary}")

216 for step in plan.steps:

217 print(

218 f"{step.step_number}. [{step.estimated_complexity}] {step.description}"

219 )

220 

221 

222 asyncio.run(main())

223 ```

224</CodeGroup>

225 

226**Benefits:**

227 

228* Full type inference (TypeScript) and type hints (Python)

229* Runtime validation with `safeParse()` or `model_validate()`

230* Better error messages

231* Composable, reusable schemas

232 

233## Output format configuration

234 

235The `outputFormat` (TypeScript) or `output_format` (Python) option accepts an object with:

236 

237* `type`: Set to `"json_schema"` for structured outputs

238* `schema`: A [JSON Schema](https://json-schema.org/understanding-json-schema/about) object defining your output structure. You can generate this from a Zod schema with `z.toJSONSchema()` or a Pydantic model with `.model_json_schema()`

239 

240The SDK supports standard JSON Schema features including all basic types (object, array, string, number, boolean, null), `enum`, `const`, `required`, nested objects, and `$ref` definitions. For the full list of supported features and limitations, see [JSON Schema limitations](https://platform.claude.com/docs/en/build-with-claude/structured-outputs#json-schema-limitations).

241 

242## Example: TODO tracking agent

243 

244This example demonstrates how structured outputs work with multi-step tool use. The agent needs to find TODO comments in the codebase, then look up git blame information for each one. It autonomously decides which tools to use (Grep to search, Bash to run git commands) and combines the results into a single structured response.

245 

246The schema includes optional fields (`author` and `date`) since git blame information might not be available for all files. The agent fills in what it can find and omits the rest.

247 

248<CodeGroup>

249 ```typescript TypeScript theme={null}

250 import { query } from "@anthropic-ai/claude-agent-sdk";

251 

252 // Define structure for TODO extraction

253 const todoSchema = {

254 type: "object",

255 properties: {

256 todos: {

257 type: "array",

258 items: {

259 type: "object",

260 properties: {

261 text: { type: "string" },

262 file: { type: "string" },

263 line: { type: "number" },

264 author: { type: "string" },

265 date: { type: "string" }

266 },

267 required: ["text", "file", "line"]

268 }

269 },

270 total_count: { type: "number" }

271 },

272 required: ["todos", "total_count"]

273 };

274 

275 // Agent uses Grep to find TODOs, Bash to get git blame info

276 for await (const message of query({

277 prompt: "Find all TODO comments in this codebase and identify who added them",

278 options: {

279 outputFormat: {

280 type: "json_schema",

281 schema: todoSchema

282 }

283 }

284 })) {

285 if (message.type === "result" && message.subtype === "success" && message.structured_output) {

286 const data = message.structured_output as { total_count: number; todos: Array<{ file: string; line: number; text: string; author?: string; date?: string }> };

287 console.log(`Found ${data.total_count} TODOs`);

288 data.todos.forEach((todo) => {

289 console.log(`${todo.file}:${todo.line} - ${todo.text}`);

290 if (todo.author) {

291 console.log(` Added by ${todo.author} on ${todo.date}`);

292 }

293 });

294 }

295 }

296 ```

297 

298 ```python Python theme={null}

299 import asyncio

300 from claude_agent_sdk import query, ClaudeAgentOptions, ResultMessage

301 

302 # Define structure for TODO extraction

303 todo_schema = {

304 "type": "object",

305 "properties": {

306 "todos": {

307 "type": "array",

308 "items": {

309 "type": "object",

310 "properties": {

311 "text": {"type": "string"},

312 "file": {"type": "string"},

313 "line": {"type": "number"},

314 "author": {"type": "string"},

315 "date": {"type": "string"},

316 },

317 "required": ["text", "file", "line"],

318 },

319 },

320 "total_count": {"type": "number"},

321 },

322 "required": ["todos", "total_count"],

323 }

324 

325 

326 async def main():

327 # Agent uses Grep to find TODOs, Bash to get git blame info

328 async for message in query(

329 prompt="Find all TODO comments in this codebase and identify who added them",

330 options=ClaudeAgentOptions(

331 output_format={"type": "json_schema", "schema": todo_schema}

332 ),

333 ):

334 if isinstance(message, ResultMessage) and message.structured_output:

335 data = message.structured_output

336 print(f"Found {data['total_count']} TODOs")

337 for todo in data["todos"]:

338 print(f"{todo['file']}:{todo['line']} - {todo['text']}")

339 if "author" in todo:

340 print(f" Added by {todo['author']} on {todo['date']}")

341 

342 

343 asyncio.run(main())

344 ```

345</CodeGroup>

346 

347## Error handling

348 

349Structured output generation can fail when the agent cannot produce valid JSON matching your schema. This typically happens when the schema is too complex for the task, the task itself is ambiguous, or the agent hits its retry limit trying to fix validation errors.

350 

351When an error occurs, the result message has a `subtype` indicating what went wrong:

352 

353| Subtype | Meaning |

354| ------------------------------------- | ----------------------------------------------------------- |

355| `success` | Output was generated and validated successfully |

356| `error_max_structured_output_retries` | Agent couldn't produce valid output after multiple attempts |

357 

358The example below checks the `subtype` field to determine whether the output was generated successfully or if you need to handle a failure:

359 

360<CodeGroup>

361 ```typescript TypeScript theme={null}

362 for await (const msg of query({

363 prompt: "Extract contact info from the document",

364 options: {

365 outputFormat: {

366 type: "json_schema",

367 schema: contactSchema

368 }

369 }

370 })) {

371 if (msg.type === "result") {

372 if (msg.subtype === "success" && msg.structured_output) {

373 // Use the validated output

374 console.log(msg.structured_output);

375 } else if (msg.subtype === "error_max_structured_output_retries") {

376 // Handle the failure - retry with simpler prompt, fall back to unstructured, etc.

377 console.error("Could not produce valid output");

378 }

379 }

380 }

381 ```

382 

383 ```python Python theme={null}

384 async for message in query(

385 prompt="Extract contact info from the document",

386 options=ClaudeAgentOptions(

387 output_format={"type": "json_schema", "schema": contact_schema}

388 ),

389 ):

390 if isinstance(message, ResultMessage):

391 if message.subtype == "success" and message.structured_output:

392 # Use the validated output

393 print(message.structured_output)

394 elif message.subtype == "error_max_structured_output_retries":

395 # Handle the failure

396 print("Could not produce valid output")

397 ```

398</CodeGroup>

399 

400**Tips for avoiding errors:**

401 

402* **Keep schemas focused.** Deeply nested schemas with many required fields are harder to satisfy. Start simple and add complexity as needed.

403* **Match schema to task.** If the task might not have all the information your schema requires, make those fields optional.

404* **Use clear prompts.** Ambiguous prompts make it harder for the agent to know what output to produce.

405 

406## Related resources

407 

408* [JSON Schema documentation](https://json-schema.org/): learn JSON Schema syntax for defining complex schemas with nested objects, arrays, enums, and validation constraints

409* [API Structured Outputs](https://platform.claude.com/docs/en/build-with-claude/structured-outputs): use structured outputs with the Claude API directly for single-turn requests without tool use

410* [Custom tools](/en/agent-sdk/custom-tools): give your agent custom tools to call during execution before returning structured output

agent-sdk/subagents.md +594 −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# Subagents in the SDK

6 

7> Define and invoke subagents to isolate context, run tasks in parallel, and apply specialized instructions in your Claude Agent SDK applications.

8 

9Subagents are separate agent instances that your main agent can spawn to handle focused subtasks.

10Use subagents to isolate context for focused subtasks, run multiple analyses in parallel, and apply specialized instructions without bloating the main agent's prompt.

11 

12This guide explains how to define and use subagents in the SDK using the `agents` parameter.

13 

14## Overview

15 

16You can create subagents in three ways:

17 

18* **Programmatically**: use the `agents` parameter in your `query()` options ([TypeScript](/en/agent-sdk/typescript#agent-definition), [Python](/en/agent-sdk/python#agent-definition))

19* **Filesystem-based**: define agents as markdown files in `.claude/agents/` directories (see [defining subagents as files](/en/sub-agents))

20* **Built-in general-purpose**: Claude can invoke the built-in `general-purpose` subagent at any time via the Agent tool without you defining anything

21 

22This guide focuses on the programmatic approach, which is recommended for SDK applications.

23 

24When you define subagents, Claude determines whether to invoke them based on each subagent's `description` field. Write clear descriptions that explain when the subagent should be used, and Claude will automatically delegate appropriate tasks. You can also explicitly request a subagent by name in your prompt (for example, "Use the code-reviewer agent to...").

25 

26## Benefits of using subagents

27 

28### Context isolation

29 

30Each subagent runs in its own fresh conversation. Intermediate tool calls and results stay inside the subagent; only its final message returns to the parent. See [What subagents inherit](#what-subagents-inherit) for exactly what's in the subagent's context.

31 

32**Example:** a `research-assistant` subagent can explore dozens of files without any of that content accumulating in the main conversation. The parent receives a concise summary, not every file the subagent read.

33 

34### Parallelization

35 

36Multiple subagents can run concurrently, dramatically speeding up complex workflows.

37 

38**Example:** during a code review, you can run `style-checker`, `security-scanner`, and `test-coverage` subagents simultaneously, reducing review time from minutes to seconds.

39 

40### Specialized instructions and knowledge

41 

42Each subagent can have tailored system prompts with specific expertise, best practices, and constraints.

43 

44**Example:** a `database-migration` subagent can have detailed knowledge about SQL best practices, rollback strategies, and data integrity checks that would be unnecessary noise in the main agent's instructions.

45 

46### Tool restrictions

47 

48Subagents can be limited to specific tools, reducing the risk of unintended actions.

49 

50**Example:** a `doc-reviewer` subagent might only have access to Read and Grep tools, ensuring it can analyze but never accidentally modify your documentation files.

51 

52## Creating subagents

53 

54### Programmatic definition (recommended)

55 

56Define subagents directly in your code using the `agents` parameter. This example creates two subagents: a code reviewer with read-only access and a test runner that can execute commands. The `Agent` tool must be included in `allowedTools` since Claude invokes subagents through the Agent tool.

57 

58<CodeGroup>

59 ```python Python theme={null}

60 import asyncio

61 from claude_agent_sdk import query, ClaudeAgentOptions, AgentDefinition

62 

63 

64 async def main():

65 async for message in query(

66 prompt="Review the authentication module for security issues",

67 options=ClaudeAgentOptions(

68 # Agent tool is required for subagent invocation

69 allowed_tools=["Read", "Grep", "Glob", "Agent"],

70 agents={

71 "code-reviewer": AgentDefinition(

72 # description tells Claude when to use this subagent

73 description="Expert code review specialist. Use for quality, security, and maintainability reviews.",

74 # prompt defines the subagent's behavior and expertise

75 prompt="""You are a code review specialist with expertise in security, performance, and best practices.

76 

77 When reviewing code:

78 - Identify security vulnerabilities

79 - Check for performance issues

80 - Verify adherence to coding standards

81 - Suggest specific improvements

82 

83 Be thorough but concise in your feedback.""",

84 # tools restricts what the subagent can do (read-only here)

85 tools=["Read", "Grep", "Glob"],

86 # model overrides the default model for this subagent

87 model="sonnet",

88 ),

89 "test-runner": AgentDefinition(

90 description="Runs and analyzes test suites. Use for test execution and coverage analysis.",

91 prompt="""You are a test execution specialist. Run tests and provide clear analysis of results.

92 

93 Focus on:

94 - Running test commands

95 - Analyzing test output

96 - Identifying failing tests

97 - Suggesting fixes for failures""",

98 # Bash access lets this subagent run test commands

99 tools=["Bash", "Read", "Grep"],

100 ),

101 },

102 ),

103 ):

104 if hasattr(message, "result"):

105 print(message.result)

106 

107 

108 asyncio.run(main())

109 ```

110 

111 ```typescript TypeScript theme={null}

112 import { query } from "@anthropic-ai/claude-agent-sdk";

113 

114 for await (const message of query({

115 prompt: "Review the authentication module for security issues",

116 options: {

117 // Agent tool is required for subagent invocation

118 allowedTools: ["Read", "Grep", "Glob", "Agent"],

119 agents: {

120 "code-reviewer": {

121 // description tells Claude when to use this subagent

122 description:

123 "Expert code review specialist. Use for quality, security, and maintainability reviews.",

124 // prompt defines the subagent's behavior and expertise

125 prompt: `You are a code review specialist with expertise in security, performance, and best practices.

126 

127 When reviewing code:

128 - Identify security vulnerabilities

129 - Check for performance issues

130 - Verify adherence to coding standards

131 - Suggest specific improvements

132 

133 Be thorough but concise in your feedback.`,

134 // tools restricts what the subagent can do (read-only here)

135 tools: ["Read", "Grep", "Glob"],

136 // model overrides the default model for this subagent

137 model: "sonnet"

138 },

139 "test-runner": {

140 description:

141 "Runs and analyzes test suites. Use for test execution and coverage analysis.",

142 prompt: `You are a test execution specialist. Run tests and provide clear analysis of results.

143 

144 Focus on:

145 - Running test commands

146 - Analyzing test output

147 - Identifying failing tests

148 - Suggesting fixes for failures`,

149 // Bash access lets this subagent run test commands

150 tools: ["Bash", "Read", "Grep"]

151 }

152 }

153 }

154 })) {

155 if ("result" in message) console.log(message.result);

156 }

157 ```

158</CodeGroup>

159 

160### AgentDefinition configuration

161 

162| Field | Type | Required | Description |

163| :------------ | :------------------------------------------- | :------- | :--------------------------------------------------------------- |

164| `description` | `string` | Yes | Natural language description of when to use this agent |

165| `prompt` | `string` | Yes | The agent's system prompt defining its role and behavior |

166| `tools` | `string[]` | No | Array of allowed tool names. If omitted, inherits all tools |

167| `model` | `'sonnet' \| 'opus' \| 'haiku' \| 'inherit'` | No | Model override for this agent. Defaults to main model if omitted |

168| `skills` | `string[]` | No | List of skill names available to this agent |

169| `memory` | `'user' \| 'project' \| 'local'` | No | Memory source for this agent (Python only) |

170| `mcpServers` | `(string \| object)[]` | No | MCP servers available to this agent, by name or inline config |

171 

172<Note>

173 Subagents cannot spawn their own subagents. Don't include `Agent` in a subagent's `tools` array.

174</Note>

175 

176### Filesystem-based definition (alternative)

177 

178You can also define subagents as markdown files in `.claude/agents/` directories. See the [Claude Code subagents documentation](/en/sub-agents) for details on this approach. Programmatically defined agents take precedence over filesystem-based agents with the same name.

179 

180<Note>

181 Even without defining custom subagents, Claude can spawn the built-in `general-purpose` subagent when `Agent` is in your `allowedTools`. This is useful for delegating research or exploration tasks without creating specialized agents.

182</Note>

183 

184## What subagents inherit

185 

186A subagent's context window starts fresh (no parent conversation) but isn't empty. The only channel from parent to subagent is the Agent tool's prompt string, so include any file paths, error messages, or decisions the subagent needs directly in that prompt.

187 

188| The subagent receives | The subagent does not receive |

189| :--------------------------------------------------------------------------- | :------------------------------------------------- |

190| Its own system prompt (`AgentDefinition.prompt`) and the Agent tool's prompt | The parent's conversation history or tool results |

191| Project CLAUDE.md (loaded via `settingSources`) | Skills (unless listed in `AgentDefinition.skills`) |

192| Tool definitions (inherited from parent, or the subset in `tools`) | The parent's system prompt |

193 

194<Note>

195 The parent receives the subagent's final message verbatim 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.

196</Note>

197 

198## Invoking subagents

199 

200### Automatic invocation

201 

202Claude automatically decides when to invoke subagents based on the task and each subagent's `description`. For example, if you define a `performance-optimizer` subagent with the description "Performance optimization specialist for query tuning", Claude will invoke it when your prompt mentions optimizing queries.

203 

204Write clear, specific descriptions so Claude can match tasks to the right subagent.

205 

206### Explicit invocation

207 

208To guarantee Claude uses a specific subagent, mention it by name in your prompt:

209 

210```text theme={null}

211"Use the code-reviewer agent to check the authentication module"

212```

213 

214This bypasses automatic matching and directly invokes the named subagent.

215 

216### Dynamic agent configuration

217 

218You can create agent definitions dynamically based on runtime conditions. This example creates a security reviewer with different strictness levels, using a more powerful model for strict reviews.

219 

220<CodeGroup>

221 ```python Python theme={null}

222 import asyncio

223 from claude_agent_sdk import query, ClaudeAgentOptions, AgentDefinition

224 

225 

226 # Factory function that returns an AgentDefinition

227 # This pattern lets you customize agents based on runtime conditions

228 def create_security_agent(security_level: str) -> AgentDefinition:

229 is_strict = security_level == "strict"

230 return AgentDefinition(

231 description="Security code reviewer",

232 # Customize the prompt based on strictness level

233 prompt=f"You are a {'strict' if is_strict else 'balanced'} security reviewer...",

234 tools=["Read", "Grep", "Glob"],

235 # Key insight: use a more capable model for high-stakes reviews

236 model="opus" if is_strict else "sonnet",

237 )

238 

239 

240 async def main():

241 # The agent is created at query time, so each request can use different settings

242 async for message in query(

243 prompt="Review this PR for security issues",

244 options=ClaudeAgentOptions(

245 allowed_tools=["Read", "Grep", "Glob", "Agent"],

246 agents={

247 # Call the factory with your desired configuration

248 "security-reviewer": create_security_agent("strict")

249 },

250 ),

251 ):

252 if hasattr(message, "result"):

253 print(message.result)

254 

255 

256 asyncio.run(main())

257 ```

258 

259 ```typescript TypeScript theme={null}

260 import { query, type AgentDefinition } from "@anthropic-ai/claude-agent-sdk";

261 

262 // Factory function that returns an AgentDefinition

263 // This pattern lets you customize agents based on runtime conditions

264 function createSecurityAgent(securityLevel: "basic" | "strict"): AgentDefinition {

265 const isStrict = securityLevel === "strict";

266 return {

267 description: "Security code reviewer",

268 // Customize the prompt based on strictness level

269 prompt: `You are a ${isStrict ? "strict" : "balanced"} security reviewer...`,

270 tools: ["Read", "Grep", "Glob"],

271 // Key insight: use a more capable model for high-stakes reviews

272 model: isStrict ? "opus" : "sonnet"

273 };

274 }

275 

276 // The agent is created at query time, so each request can use different settings

277 for await (const message of query({

278 prompt: "Review this PR for security issues",

279 options: {

280 allowedTools: ["Read", "Grep", "Glob", "Agent"],

281 agents: {

282 // Call the factory with your desired configuration

283 "security-reviewer": createSecurityAgent("strict")

284 }

285 }

286 })) {

287 if ("result" in message) console.log(message.result);

288 }

289 ```

290</CodeGroup>

291 

292## Detecting subagent invocation

293 

294Subagents are invoked via the Agent tool. To detect when a subagent is invoked, check for `tool_use` blocks where `name` is `"Agent"`. Messages from within a subagent's context include a `parent_tool_use_id` field.

295 

296<Note>

297 The tool name was renamed from `"Task"` to `"Agent"` in Claude Code v2.1.63. Current SDK releases emit `"Agent"` in `tool_use` blocks but still use `"Task"` in the `system:init` tools list and in `result.permission_denials[].tool_name`. Checking both values in `block.name` ensures compatibility across SDK versions.

298</Note>

299 

300This example iterates through streamed messages, logging when a subagent is invoked and when subsequent messages originate from within that subagent's execution context.

301 

302<Note>

303 The message structure differs between SDKs. In Python, content blocks are accessed directly via `message.content`. In TypeScript, `SDKAssistantMessage` wraps the Claude API message, so content is accessed via `message.message.content`.

304</Note>

305 

306<CodeGroup>

307 ```python Python theme={null}

308 import asyncio

309 from claude_agent_sdk import query, ClaudeAgentOptions, AgentDefinition

310 

311 

312 async def main():

313 async for message in query(

314 prompt="Use the code-reviewer agent to review this codebase",

315 options=ClaudeAgentOptions(

316 allowed_tools=["Read", "Glob", "Grep", "Agent"],

317 agents={

318 "code-reviewer": AgentDefinition(

319 description="Expert code reviewer.",

320 prompt="Analyze code quality and suggest improvements.",

321 tools=["Read", "Glob", "Grep"],

322 )

323 },

324 ),

325 ):

326 # Check for subagent invocation. Match both names: older SDK

327 # versions emitted "Task", current versions emit "Agent".

328 if hasattr(message, "content") and message.content:

329 for block in message.content:

330 if getattr(block, "type", None) == "tool_use" and block.name in (

331 "Task",

332 "Agent",

333 ):

334 print(f"Subagent invoked: {block.input.get('subagent_type')}")

335 

336 # Check if this message is from within a subagent's context

337 if hasattr(message, "parent_tool_use_id") and message.parent_tool_use_id:

338 print(" (running inside subagent)")

339 

340 if hasattr(message, "result"):

341 print(message.result)

342 

343 

344 asyncio.run(main())

345 ```

346 

347 ```typescript TypeScript theme={null}

348 import { query } from "@anthropic-ai/claude-agent-sdk";

349 

350 for await (const message of query({

351 prompt: "Use the code-reviewer agent to review this codebase",

352 options: {

353 allowedTools: ["Read", "Glob", "Grep", "Agent"],

354 agents: {

355 "code-reviewer": {

356 description: "Expert code reviewer.",

357 prompt: "Analyze code quality and suggest improvements.",

358 tools: ["Read", "Glob", "Grep"]

359 }

360 }

361 }

362 })) {

363 const msg = message as any;

364 

365 // Check for subagent invocation. Match both names: older SDK versions

366 // emitted "Task", current versions emit "Agent".

367 for (const block of msg.message?.content ?? []) {

368 if (block.type === "tool_use" && (block.name === "Task" || block.name === "Agent")) {

369 console.log(`Subagent invoked: ${block.input.subagent_type}`);

370 }

371 }

372 

373 // Check if this message is from within a subagent's context

374 if (msg.parent_tool_use_id) {

375 console.log(" (running inside subagent)");

376 }

377 

378 if ("result" in message) {

379 console.log(message.result);

380 }

381 }

382 ```

383</CodeGroup>

384 

385## Resuming subagents

386 

387Subagents can be resumed to continue where they left off. Resumed subagents retain their full conversation history, including all previous tool calls, results, and reasoning. The subagent picks up exactly where it stopped rather than starting fresh.

388 

389When a subagent completes, Claude receives its agent ID in the Agent tool result. To resume a subagent programmatically:

390 

3911. **Capture the session ID**: Extract `session_id` from messages during the first query

3922. **Extract the agent ID**: Parse `agentId` from the message content

3933. **Resume the session**: Pass `resume: sessionId` in the second query's options, and include the agent ID in your prompt

394 

395<Note>

396 You must resume the same session to access the subagent's transcript. Each `query()` call starts a new session by default, so pass `resume: sessionId` to continue in the same session.

397 

398 If you're using a custom agent (not a built-in one), you also need to pass the same agent definition in the `agents` parameter for both queries.

399</Note>

400 

401The example below demonstrates this flow: the first query runs a subagent and captures the session ID and agent ID, then the second query resumes the session to ask a follow-up question that requires context from the first analysis.

402 

403<CodeGroup>

404 ```typescript TypeScript theme={null}

405 import { query, type SDKMessage } from "@anthropic-ai/claude-agent-sdk";

406 

407 // Helper to extract agentId from message content

408 // Stringify to avoid traversing different block types (TextBlock, ToolResultBlock, etc.)

409 function extractAgentId(message: SDKMessage): string | undefined {

410 if (!("message" in message)) return undefined;

411 // Stringify the content so we can search it without traversing nested blocks

412 const content = JSON.stringify(message.message.content);

413 const match = content.match(/agentId:\s*([a-f0-9-]+)/);

414 return match?.[1];

415 }

416 

417 let agentId: string | undefined;

418 let sessionId: string | undefined;

419 

420 // First invocation - use the Explore agent to find API endpoints

421 for await (const message of query({

422 prompt: "Use the Explore agent to find all API endpoints in this codebase",

423 options: { allowedTools: ["Read", "Grep", "Glob", "Agent"] }

424 })) {

425 // Capture session_id from ResultMessage (needed to resume this session)

426 if ("session_id" in message) sessionId = message.session_id;

427 // Search message content for the agentId (appears in Agent tool results)

428 const extractedId = extractAgentId(message);

429 if (extractedId) agentId = extractedId;

430 // Print the final result

431 if ("result" in message) console.log(message.result);

432 }

433 

434 // Second invocation - resume and ask follow-up

435 if (agentId && sessionId) {

436 for await (const message of query({

437 prompt: `Resume agent ${agentId} and list the top 3 most complex endpoints`,

438 options: { allowedTools: ["Read", "Grep", "Glob", "Agent"], resume: sessionId }

439 })) {

440 if ("result" in message) console.log(message.result);

441 }

442 }

443 ```

444 

445 ```python Python theme={null}

446 import asyncio

447 import json

448 import re

449 from claude_agent_sdk import query, ClaudeAgentOptions

450 

451 

452 def extract_agent_id(text: str) -> str | None:

453 """Extract agentId from Agent tool result text."""

454 match = re.search(r"agentId:\s*([a-f0-9-]+)", text)

455 return match.group(1) if match else None

456 

457 

458 async def main():

459 agent_id = None

460 session_id = None

461 

462 # First invocation - use the Explore agent to find API endpoints

463 async for message in query(

464 prompt="Use the Explore agent to find all API endpoints in this codebase",

465 options=ClaudeAgentOptions(allowed_tools=["Read", "Grep", "Glob", "Agent"]),

466 ):

467 # Capture session_id from ResultMessage (needed to resume this session)

468 if hasattr(message, "session_id"):

469 session_id = message.session_id

470 # Search message content for the agentId (appears in Agent tool results)

471 if hasattr(message, "content"):

472 # Stringify the content so we can search it without traversing nested blocks

473 content_str = json.dumps(message.content, default=str)

474 extracted = extract_agent_id(content_str)

475 if extracted:

476 agent_id = extracted

477 # Print the final result

478 if hasattr(message, "result"):

479 print(message.result)

480 

481 # Second invocation - resume and ask follow-up

482 if agent_id and session_id:

483 async for message in query(

484 prompt=f"Resume agent {agent_id} and list the top 3 most complex endpoints",

485 options=ClaudeAgentOptions(

486 allowed_tools=["Read", "Grep", "Glob", "Agent"], resume=session_id

487 ),

488 ):

489 if hasattr(message, "result"):

490 print(message.result)

491 

492 

493 asyncio.run(main())

494 ```

495</CodeGroup>

496 

497Subagent transcripts persist independently of the main conversation:

498 

499* **Main conversation compaction**: When the main conversation compacts, subagent transcripts are unaffected. They're stored in separate files.

500* **Session persistence**: Subagent transcripts persist within their session. You can resume a subagent after restarting Claude Code by resuming the same session.

501* **Automatic cleanup**: Transcripts are cleaned up based on the `cleanupPeriodDays` setting (default: 30 days).

502 

503## Tool restrictions

504 

505Subagents can have restricted tool access via the `tools` field:

506 

507* **Omit the field**: agent inherits all available tools (default)

508* **Specify tools**: agent can only use listed tools

509 

510This example creates a read-only analysis agent that can examine code but cannot modify files or run commands.

511 

512<CodeGroup>

513 ```python Python theme={null}

514 import asyncio

515 from claude_agent_sdk import query, ClaudeAgentOptions, AgentDefinition

516 

517 

518 async def main():

519 async for message in query(

520 prompt="Analyze the architecture of this codebase",

521 options=ClaudeAgentOptions(

522 allowed_tools=["Read", "Grep", "Glob", "Agent"],

523 agents={

524 "code-analyzer": AgentDefinition(

525 description="Static code analysis and architecture review",

526 prompt="""You are a code architecture analyst. Analyze code structure,

527 identify patterns, and suggest improvements without making changes.""",

528 # Read-only tools: no Edit, Write, or Bash access

529 tools=["Read", "Grep", "Glob"],

530 )

531 },

532 ),

533 ):

534 if hasattr(message, "result"):

535 print(message.result)

536 

537 

538 asyncio.run(main())

539 ```

540 

541 ```typescript TypeScript theme={null}

542 import { query } from "@anthropic-ai/claude-agent-sdk";

543 

544 for await (const message of query({

545 prompt: "Analyze the architecture of this codebase",

546 options: {

547 allowedTools: ["Read", "Grep", "Glob", "Agent"],

548 agents: {

549 "code-analyzer": {

550 description: "Static code analysis and architecture review",

551 prompt: `You are a code architecture analyst. Analyze code structure,

552 identify patterns, and suggest improvements without making changes.`,

553 // Read-only tools: no Edit, Write, or Bash access

554 tools: ["Read", "Grep", "Glob"]

555 }

556 }

557 }

558 })) {

559 if ("result" in message) console.log(message.result);

560 }

561 ```

562</CodeGroup>

563 

564### Common tool combinations

565 

566| Use case | Tools | Description |

567| :----------------- | :-------------------------------------- | :-------------------------------------------------- |

568| Read-only analysis | `Read`, `Grep`, `Glob` | Can examine code but not modify or execute |

569| Test execution | `Bash`, `Read`, `Grep` | Can run commands and analyze output |

570| Code modification | `Read`, `Edit`, `Write`, `Grep`, `Glob` | Full read/write access without command execution |

571| Full access | All tools | Inherits all tools from parent (omit `tools` field) |

572 

573## Troubleshooting

574 

575### Claude not delegating to subagents

576 

577If Claude completes tasks directly instead of delegating to your subagent:

578 

5791. **Include the Agent tool**: subagents are invoked via the Agent tool, so it must be in `allowedTools`

5802. **Use explicit prompting**: mention the subagent by name in your prompt (for example, "Use the code-reviewer agent to...")

5813. **Write a clear description**: explain exactly when the subagent should be used so Claude can match tasks appropriately

582 

583### Filesystem-based agents not loading

584 

585Agents defined in `.claude/agents/` are loaded at startup only. If you create a new agent file while Claude Code is running, restart the session to load it.

586 

587### Windows: long prompt failures

588 

589On Windows, subagents with very long prompts may fail due to command line length limits (8191 chars). Keep prompts concise or use filesystem-based agents for complex instructions.

590 

591## Related documentation

592 

593* [Claude Code subagents](/en/sub-agents): comprehensive subagent documentation including filesystem-based definitions

594* [SDK overview](/en/agent-sdk/overview): getting started with the Claude Agent SDK

agent-sdk/todo-tracking.md +189 −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# Todo Lists

6 

7> Track and display todos using the Claude Agent SDK for organized task management

8 

9Todo tracking provides a structured way to manage tasks and display progress to users. The Claude Agent SDK includes built-in todo functionality that helps organize complex workflows and keep users informed about task progression.

10 

11### Todo Lifecycle

12 

13Todos follow a predictable lifecycle:

14 

151. **Created** as `pending` when tasks are identified

162. **Activated** to `in_progress` when work begins

173. **Completed** when the task finishes successfully

184. **Removed** when all tasks in a group are completed

19 

20### When Todos Are Used

21 

22The SDK automatically creates todos for:

23 

24* **Complex multi-step tasks** requiring 3 or more distinct actions

25* **User-provided task lists** when multiple items are mentioned

26* **Non-trivial operations** that benefit from progress tracking

27* **Explicit requests** when users ask for todo organization

28 

29## Examples

30 

31### Monitoring Todo Changes

32 

33<CodeGroup>

34 ```typescript TypeScript theme={null}

35 import { query } from "@anthropic-ai/claude-agent-sdk";

36 

37 for await (const message of query({

38 prompt: "Optimize my React app performance and track progress with todos",

39 options: { maxTurns: 15 }

40 })) {

41 // Todo updates are reflected in the message stream

42 if (message.type === "assistant") {

43 for (const block of message.message.content) {

44 if (block.type === "tool_use" && block.name === "TodoWrite") {

45 const todos = block.input.todos;

46 

47 console.log("Todo Status Update:");

48 todos.forEach((todo, index) => {

49 const status =

50 todo.status === "completed" ? "✅" : todo.status === "in_progress" ? "🔧" : "❌";

51 console.log(`${index + 1}. ${status} ${todo.content}`);

52 });

53 }

54 }

55 }

56 }

57 ```

58 

59 ```python Python theme={null}

60 from claude_agent_sdk import query, AssistantMessage, ToolUseBlock

61 

62 async for message in query(

63 prompt="Optimize my React app performance and track progress with todos",

64 options={"max_turns": 15},

65 ):

66 # Todo updates are reflected in the message stream

67 if isinstance(message, AssistantMessage):

68 for block in message.content:

69 if isinstance(block, ToolUseBlock) and block.name == "TodoWrite":

70 todos = block.input["todos"]

71 

72 print("Todo Status Update:")

73 for i, todo in enumerate(todos):

74 status = (

75 "✅"

76 if todo["status"] == "completed"

77 else "🔧"

78 if todo["status"] == "in_progress"

79 else "❌"

80 )

81 print(f"{i + 1}. {status} {todo['content']}")

82 ```

83</CodeGroup>

84 

85### Real-time Progress Display

86 

87<CodeGroup>

88 ```typescript TypeScript theme={null}

89 import { query } from "@anthropic-ai/claude-agent-sdk";

90 

91 class TodoTracker {

92 private todos: any[] = [];

93 

94 displayProgress() {

95 if (this.todos.length === 0) return;

96 

97 const completed = this.todos.filter((t) => t.status === "completed").length;

98 const inProgress = this.todos.filter((t) => t.status === "in_progress").length;

99 const total = this.todos.length;

100 

101 console.log(`\nProgress: ${completed}/${total} completed`);

102 console.log(`Currently working on: ${inProgress} task(s)\n`);

103 

104 this.todos.forEach((todo, index) => {

105 const icon =

106 todo.status === "completed" ? "✅" : todo.status === "in_progress" ? "🔧" : "❌";

107 const text = todo.status === "in_progress" ? todo.activeForm : todo.content;

108 console.log(`${index + 1}. ${icon} ${text}`);

109 });

110 }

111 

112 async trackQuery(prompt: string) {

113 for await (const message of query({

114 prompt,

115 options: { maxTurns: 20 }

116 })) {

117 if (message.type === "assistant") {

118 for (const block of message.message.content) {

119 if (block.type === "tool_use" && block.name === "TodoWrite") {

120 this.todos = block.input.todos;

121 this.displayProgress();

122 }

123 }

124 }

125 }

126 }

127 }

128 

129 // Usage

130 const tracker = new TodoTracker();

131 await tracker.trackQuery("Build a complete authentication system with todos");

132 ```

133 

134 ```python Python theme={null}

135 from claude_agent_sdk import query, AssistantMessage, ToolUseBlock

136 from typing import List, Dict

137 

138 

139 class TodoTracker:

140 def __init__(self):

141 self.todos: List[Dict] = []

142 

143 def display_progress(self):

144 if not self.todos:

145 return

146 

147 completed = len([t for t in self.todos if t["status"] == "completed"])

148 in_progress = len([t for t in self.todos if t["status"] == "in_progress"])

149 total = len(self.todos)

150 

151 print(f"\nProgress: {completed}/{total} completed")

152 print(f"Currently working on: {in_progress} task(s)\n")

153 

154 for i, todo in enumerate(self.todos):

155 icon = (

156 "✅"

157 if todo["status"] == "completed"

158 else "🔧"

159 if todo["status"] == "in_progress"

160 else "❌"

161 )

162 text = (

163 todo["activeForm"]

164 if todo["status"] == "in_progress"

165 else todo["content"]

166 )

167 print(f"{i + 1}. {icon} {text}")

168 

169 async def track_query(self, prompt: str):

170 async for message in query(prompt=prompt, options={"max_turns": 20}):

171 if isinstance(message, AssistantMessage):

172 for block in message.content:

173 if isinstance(block, ToolUseBlock) and block.name == "TodoWrite":

174 self.todos = block.input["todos"]

175 self.display_progress()

176 

177 

178 # Usage

179 tracker = TodoTracker()

180 await tracker.track_query("Build a complete authentication system with todos")

181 ```

182</CodeGroup>

183 

184## Related Documentation

185 

186* [TypeScript SDK Reference](/en/agent-sdk/typescript)

187* [Python SDK Reference](/en/agent-sdk/python)

188* [Streaming vs Single Mode](/en/agent-sdk/streaming-vs-single-mode)

189* [Custom Tools](/en/agent-sdk/custom-tools)

agent-sdk/typescript.md +2786 −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# Agent SDK reference - TypeScript

6 

7> Complete API reference for the TypeScript Agent SDK, including all functions, types, and interfaces.

8 

9<script src="/components/typescript-sdk-type-links.js" defer />

10 

11<Note>

12 **Try the new V2 interface (preview):** A simplified interface with `send()` and `stream()` patterns is now available, making multi-turn conversations easier. [Learn more about the TypeScript V2 preview](/en/agent-sdk/typescript-v2-preview)

13</Note>

14 

15## Installation

16 

17```bash theme={null}

18npm install @anthropic-ai/claude-agent-sdk

19```

20 

21## Functions

22 

23### `query()`

24 

25The primary function for interacting with Claude Code. Creates an async generator that streams messages as they arrive.

26 

27```typescript theme={null}

28function query({

29 prompt,

30 options

31}: {

32 prompt: string | AsyncIterable<SDKUserMessage>;

33 options?: Options;

34}): Query;

35```

36 

37#### Parameters

38 

39| Parameter | Type | Description |

40| :-------- | :---------------------------------------------------------------- | :---------------------------------------------------------------- |

41| `prompt` | `string \| AsyncIterable<`[`SDKUserMessage`](#sdkuser-message)`>` | The input prompt as a string or async iterable for streaming mode |

42| `options` | [`Options`](#options) | Optional configuration object (see Options type below) |

43 

44#### Returns

45 

46Returns a [`Query`](#query-object) object that extends `AsyncGenerator<`[`SDKMessage`](#sdk-message)`, void>` with additional methods.

47 

48### `tool()`

49 

50Creates a type-safe MCP tool definition for use with SDK MCP servers.

51 

52```typescript theme={null}

53function tool<Schema extends AnyZodRawShape>(

54 name: string,

55 description: string,

56 inputSchema: Schema,

57 handler: (args: InferShape<Schema>, extra: unknown) => Promise<CallToolResult>,

58 extras?: { annotations?: ToolAnnotations }

59): SdkMcpToolDefinition<Schema>;

60```

61 

62#### Parameters

63 

64| Parameter | Type | Description |

65| :------------ | :------------------------------------------------------------------ | :------------------------------------------------------------------------------ |

66| `name` | `string` | The name of the tool |

67| `description` | `string` | A description of what the tool does |

68| `inputSchema` | `Schema extends AnyZodRawShape` | Zod schema defining the tool's input parameters (supports both Zod 3 and Zod 4) |

69| `handler` | `(args, extra) => Promise<`[`CallToolResult`](#call-tool-result)`>` | Async function that executes the tool logic |

70| `extras` | `{ annotations?: `[`ToolAnnotations`](#tool-annotations)` }` | Optional MCP tool annotations providing behavioral hints to clients |

71 

72#### `ToolAnnotations`

73 

74Re-exported from `@modelcontextprotocol/sdk/types.js`. All fields are optional hints; clients should not rely on them for security decisions.

75 

76| Field | Type | Default | Description |

77| :---------------- | :-------- | :---------- | :--------------------------------------------------------------------------------------------------------------------------------------------------- |

78| `title` | `string` | `undefined` | Human-readable title for the tool |

79| `readOnlyHint` | `boolean` | `false` | If `true`, the tool does not modify its environment |

80| `destructiveHint` | `boolean` | `true` | If `true`, the tool may perform destructive updates (only meaningful when `readOnlyHint` is `false`) |

81| `idempotentHint` | `boolean` | `false` | If `true`, repeated calls with the same arguments have no additional effect (only meaningful when `readOnlyHint` is `false`) |

82| `openWorldHint` | `boolean` | `true` | If `true`, the tool interacts with external entities (for example, web search). If `false`, the tool's domain is closed (for example, a memory tool) |

83 

84```typescript theme={null}

85import { tool } from "@anthropic-ai/claude-agent-sdk";

86import { z } from "zod";

87 

88const searchTool = tool(

89 "search",

90 "Search the web",

91 { query: z.string() },

92 async ({ query }) => {

93 return { content: [{ type: "text", text: `Results for: ${query}` }] };

94 },

95 { annotations: { readOnlyHint: true, openWorldHint: true } }

96);

97```

98 

99### `createSdkMcpServer()`

100 

101Creates an MCP server instance that runs in the same process as your application.

102 

103```typescript theme={null}

104function createSdkMcpServer(options: {

105 name: string;

106 version?: string;

107 tools?: Array<SdkMcpToolDefinition<any>>;

108}): McpSdkServerConfigWithInstance;

109```

110 

111#### Parameters

112 

113| Parameter | Type | Description |

114| :---------------- | :---------------------------- | :------------------------------------------------------- |

115| `options.name` | `string` | The name of the MCP server |

116| `options.version` | `string` | Optional version string |

117| `options.tools` | `Array<SdkMcpToolDefinition>` | Array of tool definitions created with [`tool()`](#tool) |

118 

119### `listSessions()`

120 

121Discovers and lists past sessions with light metadata. Filter by project directory or list sessions across all projects.

122 

123```typescript theme={null}

124function listSessions(options?: ListSessionsOptions): Promise<SDKSessionInfo[]>;

125```

126 

127#### Parameters

128 

129| Parameter | Type | Default | Description |

130| :------------------------- | :-------- | :---------- | :--------------------------------------------------------------------------------- |

131| `options.dir` | `string` | `undefined` | Directory to list sessions for. When omitted, returns sessions across all projects |

132| `options.limit` | `number` | `undefined` | Maximum number of sessions to return |

133| `options.includeWorktrees` | `boolean` | `true` | When `dir` is inside a git repository, include sessions from all worktree paths |

134 

135#### Return type: `SDKSessionInfo`

136 

137| Property | Type | Description |

138| :------------- | :-------------------- | :-------------------------------------------------------------------------- |

139| `sessionId` | `string` | Unique session identifier (UUID) |

140| `summary` | `string` | Display title: custom title, auto-generated summary, or first prompt |

141| `lastModified` | `number` | Last modified time in milliseconds since epoch |

142| `fileSize` | `number \| undefined` | Session file size in bytes. Only populated for local JSONL storage |

143| `customTitle` | `string \| undefined` | User-set session title (via `/rename`) |

144| `firstPrompt` | `string \| undefined` | First meaningful user prompt in the session |

145| `gitBranch` | `string \| undefined` | Git branch at the end of the session |

146| `cwd` | `string \| undefined` | Working directory for the session |

147| `tag` | `string \| undefined` | User-set session tag (see [`tagSession()`](#tag-session)) |

148| `createdAt` | `number \| undefined` | Creation time in milliseconds since epoch, from the first entry's timestamp |

149 

150#### Example

151 

152Print the 10 most recent sessions for a project. Results are sorted by `lastModified` descending, so the first item is the newest. Omit `dir` to search across all projects.

153 

154```typescript theme={null}

155import { listSessions } from "@anthropic-ai/claude-agent-sdk";

156 

157const sessions = await listSessions({ dir: "/path/to/project", limit: 10 });

158 

159for (const session of sessions) {

160 console.log(`${session.summary} (${session.sessionId})`);

161}

162```

163 

164### `getSessionMessages()`

165 

166Reads user and assistant messages from a past session transcript.

167 

168```typescript theme={null}

169function getSessionMessages(

170 sessionId: string,

171 options?: GetSessionMessagesOptions

172): Promise<SessionMessage[]>;

173```

174 

175#### Parameters

176 

177| Parameter | Type | Default | Description |

178| :--------------- | :------- | :---------- | :---------------------------------------------------------------------------- |

179| `sessionId` | `string` | required | Session UUID to read (see `listSessions()`) |

180| `options.dir` | `string` | `undefined` | Project directory to find the session in. When omitted, searches all projects |

181| `options.limit` | `number` | `undefined` | Maximum number of messages to return |

182| `options.offset` | `number` | `undefined` | Number of messages to skip from the start |

183 

184#### Return type: `SessionMessage`

185 

186| Property | Type | Description |

187| :------------------- | :---------------------- | :-------------------------------------- |

188| `type` | `"user" \| "assistant"` | Message role |

189| `uuid` | `string` | Unique message identifier |

190| `session_id` | `string` | Session this message belongs to |

191| `message` | `unknown` | Raw message payload from the transcript |

192| `parent_tool_use_id` | `null` | Reserved |

193 

194#### Example

195 

196```typescript theme={null}

197import { listSessions, getSessionMessages } from "@anthropic-ai/claude-agent-sdk";

198 

199const [latest] = await listSessions({ dir: "/path/to/project", limit: 1 });

200 

201if (latest) {

202 const messages = await getSessionMessages(latest.sessionId, {

203 dir: "/path/to/project",

204 limit: 20

205 });

206 

207 for (const msg of messages) {

208 console.log(`[${msg.type}] ${msg.uuid}`);

209 }

210}

211```

212 

213### `getSessionInfo()`

214 

215Reads metadata for a single session by ID without scanning the full project directory.

216 

217```typescript theme={null}

218function getSessionInfo(

219 sessionId: string,

220 options?: GetSessionInfoOptions

221): Promise<SDKSessionInfo | undefined>;

222```

223 

224#### Parameters

225 

226| Parameter | Type | Default | Description |

227| :------------ | :------- | :---------- | :--------------------------------------------------------------------- |

228| `sessionId` | `string` | required | UUID of the session to look up |

229| `options.dir` | `string` | `undefined` | Project directory path. When omitted, searches all project directories |

230 

231Returns [`SDKSessionInfo`](#return-type-sdk-session-info), or `undefined` if the session is not found.

232 

233### `renameSession()`

234 

235Renames a session by appending a custom-title entry. Repeated calls are safe; the most recent title wins.

236 

237```typescript theme={null}

238function renameSession(

239 sessionId: string,

240 title: string,

241 options?: SessionMutationOptions

242): Promise<void>;

243```

244 

245#### Parameters

246 

247| Parameter | Type | Default | Description |

248| :------------ | :------- | :---------- | :--------------------------------------------------------------------- |

249| `sessionId` | `string` | required | UUID of the session to rename |

250| `title` | `string` | required | New title. Must be non-empty after trimming whitespace |

251| `options.dir` | `string` | `undefined` | Project directory path. When omitted, searches all project directories |

252 

253### `tagSession()`

254 

255Tags a session. Pass `null` to clear the tag. Repeated calls are safe; the most recent tag wins.

256 

257```typescript theme={null}

258function tagSession(

259 sessionId: string,

260 tag: string | null,

261 options?: SessionMutationOptions

262): Promise<void>;

263```

264 

265#### Parameters

266 

267| Parameter | Type | Default | Description |

268| :------------ | :--------------- | :---------- | :--------------------------------------------------------------------- |

269| `sessionId` | `string` | required | UUID of the session to tag |

270| `tag` | `string \| null` | required | Tag string, or `null` to clear |

271| `options.dir` | `string` | `undefined` | Project directory path. When omitted, searches all project directories |

272 

273## Types

274 

275### `Options`

276 

277Configuration object for the `query()` function.

278 

279| Property | Type | Default | Description |

280| :-------------------------------- | :--------------------------------------------------------------------------------------------------- | :------------------------------------------ | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |

281| `abortController` | `AbortController` | `new AbortController()` | Controller for cancelling operations |

282| `additionalDirectories` | `string[]` | `[]` | Additional directories Claude can access |

283| `agent` | `string` | `undefined` | Agent name for the main thread. The agent must be defined in the `agents` option or in settings |

284| `agents` | `Record<string, [`AgentDefinition`](#agent-definition)>` | `undefined` | Programmatically define subagents |

285| `allowDangerouslySkipPermissions` | `boolean` | `false` | Enable bypassing permissions. Required when using `permissionMode: 'bypassPermissions'` |

286| `allowedTools` | `string[]` | `[]` | Tools to auto-approve without prompting. This does not restrict Claude to only these tools; unlisted tools fall through to `permissionMode` and `canUseTool`. Use `disallowedTools` to block tools. See [Permissions](/en/agent-sdk/permissions#allow-and-deny-rules) |

287| `betas` | [`SdkBeta`](#sdk-beta)`[]` | `[]` | Enable beta features |

288| `canUseTool` | [`CanUseTool`](#can-use-tool) | `undefined` | Custom permission function for tool usage |

289| `continue` | `boolean` | `false` | Continue the most recent conversation |

290| `cwd` | `string` | `process.cwd()` | Current working directory |

291| `debug` | `boolean` | `false` | Enable debug mode for the Claude Code process |

292| `debugFile` | `string` | `undefined` | Write debug logs to a specific file path. Implicitly enables debug mode |

293| `disallowedTools` | `string[]` | `[]` | Tools to always deny. Deny rules are checked first and override `allowedTools` and `permissionMode` (including `bypassPermissions`) |

294| `effort` | `'low' \| 'medium' \| 'high' \| 'max'` | `'high'` | Controls how much effort Claude puts into its response. Works with adaptive thinking to guide thinking depth |

295| `enableFileCheckpointing` | `boolean` | `false` | Enable file change tracking for rewinding. See [File checkpointing](/en/agent-sdk/file-checkpointing) |

296| `env` | `Record<string, string \| undefined>` | `process.env` | Environment variables. Set `CLAUDE_AGENT_SDK_CLIENT_APP` to identify your app in the User-Agent header |

297| `executable` | `'bun' \| 'deno' \| 'node'` | Auto-detected | JavaScript runtime to use |

298| `executableArgs` | `string[]` | `[]` | Arguments to pass to the executable |

299| `extraArgs` | `Record<string, string \| null>` | `{}` | Additional arguments |

300| `fallbackModel` | `string` | `undefined` | Model to use if primary fails |

301| `forkSession` | `boolean` | `false` | When resuming with `resume`, fork to a new session ID instead of continuing the original session |

302| `hooks` | `Partial<Record<`[`HookEvent`](#hook-event)`, `[`HookCallbackMatcher`](#hook-callback-matcher)`[]>>` | `{}` | Hook callbacks for events |

303| `includePartialMessages` | `boolean` | `false` | Include partial message events |

304| `maxBudgetUsd` | `number` | `undefined` | Maximum budget in USD for the query |

305| `maxThinkingTokens` | `number` | `undefined` | *Deprecated:* Use `thinking` instead. Maximum tokens for thinking process |

306| `maxTurns` | `number` | `undefined` | Maximum agentic turns (tool-use round trips) |

307| `mcpServers` | `Record<string, [`McpServerConfig`](#mcp-server-config)>` | `{}` | MCP server configurations |

308| `model` | `string` | Default from CLI | Claude model to use |

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

310| `pathToClaudeCodeExecutable` | `string` | Uses built-in executable | Path to Claude Code executable |

311| `permissionMode` | [`PermissionMode`](#permission-mode) | `'default'` | Permission mode for the session |

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

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

314| `plugins` | [`SdkPluginConfig`](#sdk-plugin-config)`[]` | `[]` | Load custom plugins from local paths. See [Plugins](/en/agent-sdk/plugins) for details |

315| `promptSuggestions` | `boolean` | `false` | Enable prompt suggestions. Emits a `prompt_suggestion` message after each turn with a predicted next user prompt |

316| `resume` | `string` | `undefined` | Session ID to resume |

317| `resumeSessionAt` | `string` | `undefined` | Resume session at a specific message UUID |

318| `sandbox` | [`SandboxSettings`](#sandbox-settings) | `undefined` | Configure sandbox behavior programmatically. See [Sandbox settings](#sandbox-settings) for details |

319| `sessionId` | `string` | Auto-generated | Use a specific UUID for the session instead of auto-generating one |

320| `settingSources` | [`SettingSource`](#setting-source)`[]` | `[]` (no settings) | Control which filesystem settings to load. When omitted, no settings are loaded. **Note:** Must include `'project'` to load CLAUDE.md files |

321| `spawnClaudeCodeProcess` | `(options: SpawnOptions) => SpawnedProcess` | `undefined` | Custom function to spawn the Claude Code process. Use to run Claude Code in VMs, containers, or remote environments |

322| `stderr` | `(data: string) => void` | `undefined` | Callback for stderr output |

323| `strictMcpConfig` | `boolean` | `false` | Enforce strict MCP validation |

324| `systemPrompt` | `string \| { type: 'preset'; preset: 'claude_code'; append?: string }` | `undefined` (minimal prompt) | System prompt configuration. Pass a string for custom prompt, or `{ type: 'preset', preset: 'claude_code' }` to use Claude Code's system prompt. When using the preset object form, add `append` to extend the system prompt with additional instructions |

325| `thinking` | [`ThinkingConfig`](#thinking-config) | `{ type: 'adaptive' }` for supported models | Controls Claude's thinking/reasoning behavior. See [`ThinkingConfig`](#thinking-config) for options |

326| `toolConfig` | [`ToolConfig`](#tool-config) | `undefined` | Configuration for built-in tool behavior. See [`ToolConfig`](#tool-config) for details |

327| `tools` | `string[] \| { type: 'preset'; preset: 'claude_code' }` | `undefined` | Tool configuration. Pass an array of tool names or use the preset to get Claude Code's default tools |

328 

329### `Query` object

330 

331Interface returned by the `query()` function.

332 

333```typescript theme={null}

334interface Query extends AsyncGenerator<SDKMessage, void> {

335 interrupt(): Promise<void>;

336 rewindFiles(

337 userMessageId: string,

338 options?: { dryRun?: boolean }

339 ): Promise<RewindFilesResult>;

340 setPermissionMode(mode: PermissionMode): Promise<void>;

341 setModel(model?: string): Promise<void>;

342 setMaxThinkingTokens(maxThinkingTokens: number | null): Promise<void>;

343 initializationResult(): Promise<SDKControlInitializeResponse>;

344 supportedCommands(): Promise<SlashCommand[]>;

345 supportedModels(): Promise<ModelInfo[]>;

346 supportedAgents(): Promise<AgentInfo[]>;

347 mcpServerStatus(): Promise<McpServerStatus[]>;

348 accountInfo(): Promise<AccountInfo>;

349 reconnectMcpServer(serverName: string): Promise<void>;

350 toggleMcpServer(serverName: string, enabled: boolean): Promise<void>;

351 setMcpServers(servers: Record<string, McpServerConfig>): Promise<McpSetServersResult>;

352 streamInput(stream: AsyncIterable<SDKUserMessage>): Promise<void>;

353 stopTask(taskId: string): Promise<void>;

354 close(): void;

355}

356```

357 

358#### Methods

359 

360| Method | Description |

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

362| `interrupt()` | Interrupts the query (only available in streaming input mode) |

363| `rewindFiles(userMessageId, options?)` | Restores files to their state at the specified user message. Pass `{ dryRun: true }` to preview changes. Requires `enableFileCheckpointing: true`. See [File checkpointing](/en/agent-sdk/file-checkpointing) |

364| `setPermissionMode()` | Changes the permission mode (only available in streaming input mode) |

365| `setModel()` | Changes the model (only available in streaming input mode) |

366| `setMaxThinkingTokens()` | *Deprecated:* Use the `thinking` option instead. Changes the maximum thinking tokens |

367| `initializationResult()` | Returns the full initialization result including supported commands, models, account info, and output style configuration |

368| `supportedCommands()` | Returns available slash commands |

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

370| `supportedAgents()` | Returns available subagents as [`AgentInfo`](#agent-info)`[]` |

371| `mcpServerStatus()` | Returns status of connected MCP servers |

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

373| `reconnectMcpServer(serverName)` | Reconnect an MCP server by name |

374| `toggleMcpServer(serverName, enabled)` | Enable or disable an MCP server by name |

375| `setMcpServers(servers)` | Dynamically replace the set of MCP servers for this session. Returns info about which servers were added, removed, and any errors |

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

377| `stopTask(taskId)` | Stop a running background task by ID |

378| `close()` | Close the query and terminate the underlying process. Forcefully ends the query and cleans up all resources |

379 

380### `SDKControlInitializeResponse`

381 

382Return type of `initializationResult()`. Contains session initialization data.

383 

384```typescript theme={null}

385type SDKControlInitializeResponse = {

386 commands: SlashCommand[];

387 agents: AgentInfo[];

388 output_style: string;

389 available_output_styles: string[];

390 models: ModelInfo[];

391 account: AccountInfo;

392 fast_mode_state?: "off" | "cooldown" | "on";

393};

394```

395 

396### `AgentDefinition`

397 

398Configuration for a subagent defined programmatically.

399 

400```typescript theme={null}

401type AgentDefinition = {

402 description: string;

403 tools?: string[];

404 disallowedTools?: string[];

405 prompt: string;

406 model?: "sonnet" | "opus" | "haiku" | "inherit";

407 mcpServers?: AgentMcpServerSpec[];

408 skills?: string[];

409 maxTurns?: number;

410 criticalSystemReminder_EXPERIMENTAL?: string;

411};

412```

413 

414| Field | Required | Description |

415| :------------------------------------ | :------- | :---------------------------------------------------------------------------- |

416| `description` | Yes | Natural language description of when to use this agent |

417| `tools` | No | Array of allowed tool names. If omitted, inherits all tools from parent |

418| `disallowedTools` | No | Array of tool names to explicitly disallow for this agent |

419| `prompt` | Yes | The agent's system prompt |

420| `model` | No | Model override for this agent. If omitted or `'inherit'`, uses the main model |

421| `mcpServers` | No | MCP server specifications for this agent |

422| `skills` | No | Array of skill names to preload into the agent context |

423| `maxTurns` | No | Maximum number of agentic turns (API round-trips) before stopping |

424| `criticalSystemReminder_EXPERIMENTAL` | No | Experimental: Critical reminder added to the system prompt |

425 

426### `AgentMcpServerSpec`

427 

428Specifies MCP servers available to a subagent. Can be a server name (string referencing a server from the parent's `mcpServers` config) or an inline server configuration record mapping server names to configs.

429 

430```typescript theme={null}

431type AgentMcpServerSpec = string | Record<string, McpServerConfigForProcessTransport>;

432```

433 

434Where `McpServerConfigForProcessTransport` is `McpStdioServerConfig | McpSSEServerConfig | McpHttpServerConfig | McpSdkServerConfig`.

435 

436### `SettingSource`

437 

438Controls which filesystem-based configuration sources the SDK loads settings from.

439 

440```typescript theme={null}

441type SettingSource = "user" | "project" | "local";

442```

443 

444| Value | Description | Location |

445| :---------- | :------------------------------------------- | :---------------------------- |

446| `'user'` | Global user settings | `~/.claude/settings.json` |

447| `'project'` | Shared project settings (version controlled) | `.claude/settings.json` |

448| `'local'` | Local project settings (gitignored) | `.claude/settings.local.json` |

449 

450#### Default behavior

451 

452When `settingSources` is **omitted** or **undefined**, the SDK does **not** load any filesystem settings. This provides isolation for SDK applications.

453 

454#### Why use settingSources

455 

456**Load all filesystem settings (legacy behavior):**

457 

458```typescript theme={null}

459// Load all settings like SDK v0.0.x did

460const result = query({

461 prompt: "Analyze this code",

462 options: {

463 settingSources: ["user", "project", "local"] // Load all settings

464 }

465});

466```

467 

468**Load only specific setting sources:**

469 

470```typescript theme={null}

471// Load only project settings, ignore user and local

472const result = query({

473 prompt: "Run CI checks",

474 options: {

475 settingSources: ["project"] // Only .claude/settings.json

476 }

477});

478```

479 

480**Testing and CI environments:**

481 

482```typescript theme={null}

483// Ensure consistent behavior in CI by excluding local settings

484const result = query({

485 prompt: "Run tests",

486 options: {

487 settingSources: ["project"], // Only team-shared settings

488 permissionMode: "bypassPermissions"

489 }

490});

491```

492 

493**SDK-only applications:**

494 

495```typescript theme={null}

496// Define everything programmatically (default behavior)

497// No filesystem dependencies - settingSources defaults to []

498const result = query({

499 prompt: "Review this PR",

500 options: {

501 // settingSources: [] is the default, no need to specify

502 agents: {

503 /* ... */

504 },

505 mcpServers: {

506 /* ... */

507 },

508 allowedTools: ["Read", "Grep", "Glob"]

509 }

510});

511```

512 

513**Loading CLAUDE.md project instructions:**

514 

515```typescript theme={null}

516// Load project settings to include CLAUDE.md files

517const result = query({

518 prompt: "Add a new feature following project conventions",

519 options: {

520 systemPrompt: {

521 type: "preset",

522 preset: "claude_code" // Required to use CLAUDE.md

523 },

524 settingSources: ["project"], // Loads CLAUDE.md from project directory

525 allowedTools: ["Read", "Write", "Edit"]

526 }

527});

528```

529 

530#### Settings precedence

531 

532When multiple sources are loaded, settings are merged with this precedence (highest to lowest):

533 

5341. Local settings (`.claude/settings.local.json`)

5352. Project settings (`.claude/settings.json`)

5363. User settings (`~/.claude/settings.json`)

537 

538Programmatic options (like `agents`, `allowedTools`) always override filesystem settings.

539 

540### `PermissionMode`

541 

542```typescript theme={null}

543type PermissionMode =

544 | "default" // Standard permission behavior

545 | "acceptEdits" // Auto-accept file edits

546 | "bypassPermissions" // Bypass all permission checks

547 | "plan" // Planning mode - no execution

548 | "dontAsk" // Don't prompt for permissions, deny if not pre-approved

549 | "auto"; // Use a model classifier to approve or deny each tool call

550```

551 

552### `CanUseTool`

553 

554Custom permission function type for controlling tool usage.

555 

556```typescript theme={null}

557type CanUseTool = (

558 toolName: string,

559 input: Record<string, unknown>,

560 options: {

561 signal: AbortSignal;

562 suggestions?: PermissionUpdate[];

563 blockedPath?: string;

564 decisionReason?: string;

565 toolUseID: string;

566 agentID?: string;

567 }

568) => Promise<PermissionResult>;

569```

570 

571| Option | Type | Description |

572| :--------------- | :------------------------------------------- | :--------------------------------------------------------------------------- |

573| `signal` | `AbortSignal` | Signaled if the operation should be aborted |

574| `suggestions` | [`PermissionUpdate`](#permission-update)`[]` | Suggested permission updates so the user is not prompted again for this tool |

575| `blockedPath` | `string` | The file path that triggered the permission request, if applicable |

576| `decisionReason` | `string` | Explains why this permission request was triggered |

577| `toolUseID` | `string` | Unique identifier for this specific tool call within the assistant message |

578| `agentID` | `string` | If running within a sub-agent, the sub-agent's ID |

579 

580### `PermissionResult`

581 

582Result of a permission check.

583 

584```typescript theme={null}

585type PermissionResult =

586 | {

587 behavior: "allow";

588 updatedInput?: Record<string, unknown>;

589 updatedPermissions?: PermissionUpdate[];

590 toolUseID?: string;

591 }

592 | {

593 behavior: "deny";

594 message: string;

595 interrupt?: boolean;

596 toolUseID?: string;

597 };

598```

599 

600### `ToolConfig`

601 

602Configuration for built-in tool behavior.

603 

604```typescript theme={null}

605type ToolConfig = {

606 askUserQuestion?: {

607 previewFormat?: "markdown" | "html";

608 };

609};

610```

611 

612| Field | Type | Description |

613| :------------------------------ | :--------------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |

614| `askUserQuestion.previewFormat` | `'markdown' \| 'html'` | Opts into the `preview` field on [`AskUserQuestion`](/en/agent-sdk/user-input#question-format) options and sets its content format. When unset, Claude does not emit previews |

615 

616### `McpServerConfig`

617 

618Configuration for MCP servers.

619 

620```typescript theme={null}

621type McpServerConfig =

622 | McpStdioServerConfig

623 | McpSSEServerConfig

624 | McpHttpServerConfig

625 | McpSdkServerConfigWithInstance;

626```

627 

628#### `McpStdioServerConfig`

629 

630```typescript theme={null}

631type McpStdioServerConfig = {

632 type?: "stdio";

633 command: string;

634 args?: string[];

635 env?: Record<string, string>;

636};

637```

638 

639#### `McpSSEServerConfig`

640 

641```typescript theme={null}

642type McpSSEServerConfig = {

643 type: "sse";

644 url: string;

645 headers?: Record<string, string>;

646};

647```

648 

649#### `McpHttpServerConfig`

650 

651```typescript theme={null}

652type McpHttpServerConfig = {

653 type: "http";

654 url: string;

655 headers?: Record<string, string>;

656};

657```

658 

659#### `McpSdkServerConfigWithInstance`

660 

661```typescript theme={null}

662type McpSdkServerConfigWithInstance = {

663 type: "sdk";

664 name: string;

665 instance: McpServer;

666};

667```

668 

669#### `McpClaudeAIProxyServerConfig`

670 

671```typescript theme={null}

672type McpClaudeAIProxyServerConfig = {

673 type: "claudeai-proxy";

674 url: string;

675 id: string;

676};

677```

678 

679### `SdkPluginConfig`

680 

681Configuration for loading plugins in the SDK.

682 

683```typescript theme={null}

684type SdkPluginConfig = {

685 type: "local";

686 path: string;

687};

688```

689 

690| Field | Type | Description |

691| :----- | :-------- | :--------------------------------------------------------- |

692| `type` | `'local'` | Must be `'local'` (only local plugins currently supported) |

693| `path` | `string` | Absolute or relative path to the plugin directory |

694 

695**Example:**

696 

697```typescript theme={null}

698plugins: [

699 { type: "local", path: "./my-plugin" },

700 { type: "local", path: "/absolute/path/to/plugin" }

701];

702```

703 

704For complete information on creating and using plugins, see [Plugins](/en/agent-sdk/plugins).

705 

706## Message Types

707 

708### `SDKMessage`

709 

710Union type of all possible messages returned by the query.

711 

712```typescript theme={null}

713type SDKMessage =

714 | SDKAssistantMessage

715 | SDKUserMessage

716 | SDKUserMessageReplay

717 | SDKResultMessage

718 | SDKSystemMessage

719 | SDKPartialAssistantMessage

720 | SDKCompactBoundaryMessage

721 | SDKStatusMessage

722 | SDKLocalCommandOutputMessage

723 | SDKHookStartedMessage

724 | SDKHookProgressMessage

725 | SDKHookResponseMessage

726 | SDKToolProgressMessage

727 | SDKAuthStatusMessage

728 | SDKTaskNotificationMessage

729 | SDKTaskStartedMessage

730 | SDKTaskProgressMessage

731 | SDKFilesPersistedEvent

732 | SDKToolUseSummaryMessage

733 | SDKRateLimitEvent

734 | SDKPromptSuggestionMessage;

735```

736 

737### `SDKAssistantMessage`

738 

739Assistant response message.

740 

741```typescript theme={null}

742type SDKAssistantMessage = {

743 type: "assistant";

744 uuid: UUID;

745 session_id: string;

746 message: BetaMessage; // From Anthropic SDK

747 parent_tool_use_id: string | null;

748 error?: SDKAssistantMessageError;

749};

750```

751 

752The `message` field is a [`BetaMessage`](https://platform.claude.com/docs/en/api/messages/create) from the Anthropic SDK. It includes fields like `id`, `content`, `model`, `stop_reason`, and `usage`.

753 

754`SDKAssistantMessageError` is one of: `'authentication_failed'`, `'billing_error'`, `'rate_limit'`, `'invalid_request'`, `'server_error'`, `'max_output_tokens'`, or `'unknown'`.

755 

756### `SDKUserMessage`

757 

758User input message.

759 

760```typescript theme={null}

761type SDKUserMessage = {

762 type: "user";

763 uuid?: UUID;

764 session_id: string;

765 message: MessageParam; // From Anthropic SDK

766 parent_tool_use_id: string | null;

767 isSynthetic?: boolean;

768 tool_use_result?: unknown;

769};

770```

771 

772### `SDKUserMessageReplay`

773 

774Replayed user message with required UUID.

775 

776```typescript theme={null}

777type SDKUserMessageReplay = {

778 type: "user";

779 uuid: UUID;

780 session_id: string;

781 message: MessageParam;

782 parent_tool_use_id: string | null;

783 isSynthetic?: boolean;

784 tool_use_result?: unknown;

785 isReplay: true;

786};

787```

788 

789### `SDKResultMessage`

790 

791Final result message.

792 

793```typescript theme={null}

794type SDKResultMessage =

795 | {

796 type: "result";

797 subtype: "success";

798 uuid: UUID;

799 session_id: string;

800 duration_ms: number;

801 duration_api_ms: number;

802 is_error: boolean;

803 num_turns: number;

804 result: string;

805 stop_reason: string | null;

806 total_cost_usd: number;

807 usage: NonNullableUsage;

808 modelUsage: { [modelName: string]: ModelUsage };

809 permission_denials: SDKPermissionDenial[];

810 structured_output?: unknown;

811 }

812 | {

813 type: "result";

814 subtype:

815 | "error_max_turns"

816 | "error_during_execution"

817 | "error_max_budget_usd"

818 | "error_max_structured_output_retries";

819 uuid: UUID;

820 session_id: string;

821 duration_ms: number;

822 duration_api_ms: number;

823 is_error: boolean;

824 num_turns: number;

825 stop_reason: string | null;

826 total_cost_usd: number;

827 usage: NonNullableUsage;

828 modelUsage: { [modelName: string]: ModelUsage };

829 permission_denials: SDKPermissionDenial[];

830 errors: string[];

831 };

832```

833 

834### `SDKSystemMessage`

835 

836System initialization message.

837 

838```typescript theme={null}

839type SDKSystemMessage = {

840 type: "system";

841 subtype: "init";

842 uuid: UUID;

843 session_id: string;

844 agents?: string[];

845 apiKeySource: ApiKeySource;

846 betas?: string[];

847 claude_code_version: string;

848 cwd: string;

849 tools: string[];

850 mcp_servers: {

851 name: string;

852 status: string;

853 }[];

854 model: string;

855 permissionMode: PermissionMode;

856 slash_commands: string[];

857 output_style: string;

858 skills: string[];

859 plugins: { name: string; path: string }[];

860};

861```

862 

863### `SDKPartialAssistantMessage`

864 

865Streaming partial message (only when `includePartialMessages` is true).

866 

867```typescript theme={null}

868type SDKPartialAssistantMessage = {

869 type: "stream_event";

870 event: BetaRawMessageStreamEvent; // From Anthropic SDK

871 parent_tool_use_id: string | null;

872 uuid: UUID;

873 session_id: string;

874};

875```

876 

877### `SDKCompactBoundaryMessage`

878 

879Message indicating a conversation compaction boundary.

880 

881```typescript theme={null}

882type SDKCompactBoundaryMessage = {

883 type: "system";

884 subtype: "compact_boundary";

885 uuid: UUID;

886 session_id: string;

887 compact_metadata: {

888 trigger: "manual" | "auto";

889 pre_tokens: number;

890 };

891};

892```

893 

894### `SDKPermissionDenial`

895 

896Information about a denied tool use.

897 

898```typescript theme={null}

899type SDKPermissionDenial = {

900 tool_name: string;

901 tool_use_id: string;

902 tool_input: Record<string, unknown>;

903};

904```

905 

906## Hook Types

907 

908For a comprehensive guide on using hooks with examples and common patterns, see the [Hooks guide](/en/agent-sdk/hooks).

909 

910### `HookEvent`

911 

912Available hook events.

913 

914```typescript theme={null}

915type HookEvent =

916 | "PreToolUse"

917 | "PostToolUse"

918 | "PostToolUseFailure"

919 | "Notification"

920 | "UserPromptSubmit"

921 | "SessionStart"

922 | "SessionEnd"

923 | "Stop"

924 | "SubagentStart"

925 | "SubagentStop"

926 | "PreCompact"

927 | "PermissionRequest"

928 | "Setup"

929 | "TeammateIdle"

930 | "TaskCompleted"

931 | "ConfigChange"

932 | "WorktreeCreate"

933 | "WorktreeRemove";

934```

935 

936### `HookCallback`

937 

938Hook callback function type.

939 

940```typescript theme={null}

941type HookCallback = (

942 input: HookInput, // Union of all hook input types

943 toolUseID: string | undefined,

944 options: { signal: AbortSignal }

945) => Promise<HookJSONOutput>;

946```

947 

948### `HookCallbackMatcher`

949 

950Hook configuration with optional matcher.

951 

952```typescript theme={null}

953interface HookCallbackMatcher {

954 matcher?: string;

955 hooks: HookCallback[];

956 timeout?: number; // Timeout in seconds for all hooks in this matcher

957}

958```

959 

960### `HookInput`

961 

962Union type of all hook input types.

963 

964```typescript theme={null}

965type HookInput =

966 | PreToolUseHookInput

967 | PostToolUseHookInput

968 | PostToolUseFailureHookInput

969 | NotificationHookInput

970 | UserPromptSubmitHookInput

971 | SessionStartHookInput

972 | SessionEndHookInput

973 | StopHookInput

974 | SubagentStartHookInput

975 | SubagentStopHookInput

976 | PreCompactHookInput

977 | PermissionRequestHookInput

978 | SetupHookInput

979 | TeammateIdleHookInput

980 | TaskCompletedHookInput

981 | ConfigChangeHookInput

982 | WorktreeCreateHookInput

983 | WorktreeRemoveHookInput;

984```

985 

986### `BaseHookInput`

987 

988Base interface that all hook input types extend.

989 

990```typescript theme={null}

991type BaseHookInput = {

992 session_id: string;

993 transcript_path: string;

994 cwd: string;

995 permission_mode?: string;

996 agent_id?: string;

997 agent_type?: string;

998};

999```

1000 

1001#### `PreToolUseHookInput`

1002 

1003```typescript theme={null}

1004type PreToolUseHookInput = BaseHookInput & {

1005 hook_event_name: "PreToolUse";

1006 tool_name: string;

1007 tool_input: unknown;

1008 tool_use_id: string;

1009};

1010```

1011 

1012#### `PostToolUseHookInput`

1013 

1014```typescript theme={null}

1015type PostToolUseHookInput = BaseHookInput & {

1016 hook_event_name: "PostToolUse";

1017 tool_name: string;

1018 tool_input: unknown;

1019 tool_response: unknown;

1020 tool_use_id: string;

1021};

1022```

1023 

1024#### `PostToolUseFailureHookInput`

1025 

1026```typescript theme={null}

1027type PostToolUseFailureHookInput = BaseHookInput & {

1028 hook_event_name: "PostToolUseFailure";

1029 tool_name: string;

1030 tool_input: unknown;

1031 tool_use_id: string;

1032 error: string;

1033 is_interrupt?: boolean;

1034};

1035```

1036 

1037#### `NotificationHookInput`

1038 

1039```typescript theme={null}

1040type NotificationHookInput = BaseHookInput & {

1041 hook_event_name: "Notification";

1042 message: string;

1043 title?: string;

1044 notification_type: string;

1045};

1046```

1047 

1048#### `UserPromptSubmitHookInput`

1049 

1050```typescript theme={null}

1051type UserPromptSubmitHookInput = BaseHookInput & {

1052 hook_event_name: "UserPromptSubmit";

1053 prompt: string;

1054};

1055```

1056 

1057#### `SessionStartHookInput`

1058 

1059```typescript theme={null}

1060type SessionStartHookInput = BaseHookInput & {

1061 hook_event_name: "SessionStart";

1062 source: "startup" | "resume" | "clear" | "compact";

1063 agent_type?: string;

1064 model?: string;

1065};

1066```

1067 

1068#### `SessionEndHookInput`

1069 

1070```typescript theme={null}

1071type SessionEndHookInput = BaseHookInput & {

1072 hook_event_name: "SessionEnd";

1073 reason: ExitReason; // String from EXIT_REASONS array

1074};

1075```

1076 

1077#### `StopHookInput`

1078 

1079```typescript theme={null}

1080type StopHookInput = BaseHookInput & {

1081 hook_event_name: "Stop";

1082 stop_hook_active: boolean;

1083 last_assistant_message?: string;

1084};

1085```

1086 

1087#### `SubagentStartHookInput`

1088 

1089```typescript theme={null}

1090type SubagentStartHookInput = BaseHookInput & {

1091 hook_event_name: "SubagentStart";

1092 agent_id: string;

1093 agent_type: string;

1094};

1095```

1096 

1097#### `SubagentStopHookInput`

1098 

1099```typescript theme={null}

1100type SubagentStopHookInput = BaseHookInput & {

1101 hook_event_name: "SubagentStop";

1102 stop_hook_active: boolean;

1103 agent_id: string;

1104 agent_transcript_path: string;

1105 agent_type: string;

1106 last_assistant_message?: string;

1107};

1108```

1109 

1110#### `PreCompactHookInput`

1111 

1112```typescript theme={null}

1113type PreCompactHookInput = BaseHookInput & {

1114 hook_event_name: "PreCompact";

1115 trigger: "manual" | "auto";

1116 custom_instructions: string | null;

1117};

1118```

1119 

1120#### `PermissionRequestHookInput`

1121 

1122```typescript theme={null}

1123type PermissionRequestHookInput = BaseHookInput & {

1124 hook_event_name: "PermissionRequest";

1125 tool_name: string;

1126 tool_input: unknown;

1127 permission_suggestions?: PermissionUpdate[];

1128};

1129```

1130 

1131#### `SetupHookInput`

1132 

1133```typescript theme={null}

1134type SetupHookInput = BaseHookInput & {

1135 hook_event_name: "Setup";

1136 trigger: "init" | "maintenance";

1137};

1138```

1139 

1140#### `TeammateIdleHookInput`

1141 

1142```typescript theme={null}

1143type TeammateIdleHookInput = BaseHookInput & {

1144 hook_event_name: "TeammateIdle";

1145 teammate_name: string;

1146 team_name: string;

1147};

1148```

1149 

1150#### `TaskCompletedHookInput`

1151 

1152```typescript theme={null}

1153type TaskCompletedHookInput = BaseHookInput & {

1154 hook_event_name: "TaskCompleted";

1155 task_id: string;

1156 task_subject: string;

1157 task_description?: string;

1158 teammate_name?: string;

1159 team_name?: string;

1160};

1161```

1162 

1163#### `ConfigChangeHookInput`

1164 

1165```typescript theme={null}

1166type ConfigChangeHookInput = BaseHookInput & {

1167 hook_event_name: "ConfigChange";

1168 source:

1169 | "user_settings"

1170 | "project_settings"

1171 | "local_settings"

1172 | "policy_settings"

1173 | "skills";

1174 file_path?: string;

1175};

1176```

1177 

1178#### `WorktreeCreateHookInput`

1179 

1180```typescript theme={null}

1181type WorktreeCreateHookInput = BaseHookInput & {

1182 hook_event_name: "WorktreeCreate";

1183 name: string;

1184};

1185```

1186 

1187#### `WorktreeRemoveHookInput`

1188 

1189```typescript theme={null}

1190type WorktreeRemoveHookInput = BaseHookInput & {

1191 hook_event_name: "WorktreeRemove";

1192 worktree_path: string;

1193};

1194```

1195 

1196### `HookJSONOutput`

1197 

1198Hook return value.

1199 

1200```typescript theme={null}

1201type HookJSONOutput = AsyncHookJSONOutput | SyncHookJSONOutput;

1202```

1203 

1204#### `AsyncHookJSONOutput`

1205 

1206```typescript theme={null}

1207type AsyncHookJSONOutput = {

1208 async: true;

1209 asyncTimeout?: number;

1210};

1211```

1212 

1213#### `SyncHookJSONOutput`

1214 

1215```typescript theme={null}

1216type SyncHookJSONOutput = {

1217 continue?: boolean;

1218 suppressOutput?: boolean;

1219 stopReason?: string;

1220 decision?: "approve" | "block";

1221 systemMessage?: string;

1222 reason?: string;

1223 hookSpecificOutput?:

1224 | {

1225 hookEventName: "PreToolUse";

1226 permissionDecision?: "allow" | "deny" | "ask";

1227 permissionDecisionReason?: string;

1228 updatedInput?: Record<string, unknown>;

1229 additionalContext?: string;

1230 }

1231 | {

1232 hookEventName: "UserPromptSubmit";

1233 additionalContext?: string;

1234 }

1235 | {

1236 hookEventName: "SessionStart";

1237 additionalContext?: string;

1238 }

1239 | {

1240 hookEventName: "Setup";

1241 additionalContext?: string;

1242 }

1243 | {

1244 hookEventName: "SubagentStart";

1245 additionalContext?: string;

1246 }

1247 | {

1248 hookEventName: "PostToolUse";

1249 additionalContext?: string;

1250 updatedMCPToolOutput?: unknown;

1251 }

1252 | {

1253 hookEventName: "PostToolUseFailure";

1254 additionalContext?: string;

1255 }

1256 | {

1257 hookEventName: "Notification";

1258 additionalContext?: string;

1259 }

1260 | {

1261 hookEventName: "PermissionRequest";

1262 decision:

1263 | {

1264 behavior: "allow";

1265 updatedInput?: Record<string, unknown>;

1266 updatedPermissions?: PermissionUpdate[];

1267 }

1268 | {

1269 behavior: "deny";

1270 message?: string;

1271 interrupt?: boolean;

1272 };

1273 };

1274};

1275```

1276 

1277## Tool Input Types

1278 

1279Documentation of input schemas for all built-in Claude Code tools. These types are exported from `@anthropic-ai/claude-agent-sdk` and can be used for type-safe tool interactions.

1280 

1281### `ToolInputSchemas`

1282 

1283Union of all tool input types, exported from `@anthropic-ai/claude-agent-sdk`.

1284 

1285```typescript theme={null}

1286type ToolInputSchemas =

1287 | AgentInput

1288 | AskUserQuestionInput

1289 | BashInput

1290 | TaskOutputInput

1291 | ConfigInput

1292 | EnterWorktreeInput

1293 | ExitPlanModeInput

1294 | FileEditInput

1295 | FileReadInput

1296 | FileWriteInput

1297 | GlobInput

1298 | GrepInput

1299 | ListMcpResourcesInput

1300 | McpInput

1301 | NotebookEditInput

1302 | ReadMcpResourceInput

1303 | SubscribeMcpResourceInput

1304 | SubscribePollingInput

1305 | TaskStopInput

1306 | TodoWriteInput

1307 | UnsubscribeMcpResourceInput

1308 | UnsubscribePollingInput

1309 | WebFetchInput

1310 | WebSearchInput;

1311```

1312 

1313### Agent

1314 

1315**Tool name:** `Agent` (previously `Task`, which is still accepted as an alias)

1316 

1317```typescript theme={null}

1318type AgentInput = {

1319 description: string;

1320 prompt: string;

1321 subagent_type: string;

1322 model?: "sonnet" | "opus" | "haiku";

1323 resume?: string;

1324 run_in_background?: boolean;

1325 max_turns?: number;

1326 name?: string;

1327 team_name?: string;

1328 mode?: "acceptEdits" | "bypassPermissions" | "default" | "dontAsk" | "plan";

1329 isolation?: "worktree";

1330};

1331```

1332 

1333Launches a new agent to handle complex, multi-step tasks autonomously.

1334 

1335### AskUserQuestion

1336 

1337**Tool name:** `AskUserQuestion`

1338 

1339```typescript theme={null}

1340type AskUserQuestionInput = {

1341 questions: Array<{

1342 question: string;

1343 header: string;

1344 options: Array<{ label: string; description: string; preview?: string }>;

1345 multiSelect: boolean;

1346 }>;

1347};

1348```

1349 

1350Asks the user clarifying questions during execution. See [Handle approvals and user input](/en/agent-sdk/user-input#handle-clarifying-questions) for usage details.

1351 

1352### Bash

1353 

1354**Tool name:** `Bash`

1355 

1356```typescript theme={null}

1357type BashInput = {

1358 command: string;

1359 timeout?: number;

1360 description?: string;

1361 run_in_background?: boolean;

1362 dangerouslyDisableSandbox?: boolean;

1363};

1364```

1365 

1366Executes bash commands in a persistent shell session with optional timeout and background execution.

1367 

1368### TaskOutput

1369 

1370**Tool name:** `TaskOutput`

1371 

1372```typescript theme={null}

1373type TaskOutputInput = {

1374 task_id: string;

1375 block: boolean;

1376 timeout: number;

1377};

1378```

1379 

1380Retrieves output from a running or completed background task.

1381 

1382### Edit

1383 

1384**Tool name:** `Edit`

1385 

1386```typescript theme={null}

1387type FileEditInput = {

1388 file_path: string;

1389 old_string: string;

1390 new_string: string;

1391 replace_all?: boolean;

1392};

1393```

1394 

1395Performs exact string replacements in files.

1396 

1397### Read

1398 

1399**Tool name:** `Read`

1400 

1401```typescript theme={null}

1402type FileReadInput = {

1403 file_path: string;

1404 offset?: number;

1405 limit?: number;

1406 pages?: string;

1407};

1408```

1409 

1410Reads files from the local filesystem, including text, images, PDFs, and Jupyter notebooks. Use `pages` for PDF page ranges (for example, `"1-5"`).

1411 

1412### Write

1413 

1414**Tool name:** `Write`

1415 

1416```typescript theme={null}

1417type FileWriteInput = {

1418 file_path: string;

1419 content: string;

1420};

1421```

1422 

1423Writes a file to the local filesystem, overwriting if it exists.

1424 

1425### Glob

1426 

1427**Tool name:** `Glob`

1428 

1429```typescript theme={null}

1430type GlobInput = {

1431 pattern: string;

1432 path?: string;

1433};

1434```

1435 

1436Fast file pattern matching that works with any codebase size.

1437 

1438### Grep

1439 

1440**Tool name:** `Grep`

1441 

1442```typescript theme={null}

1443type GrepInput = {

1444 pattern: string;

1445 path?: string;

1446 glob?: string;

1447 type?: string;

1448 output_mode?: "content" | "files_with_matches" | "count";

1449 "-i"?: boolean;

1450 "-n"?: boolean;

1451 "-B"?: number;

1452 "-A"?: number;

1453 "-C"?: number;

1454 context?: number;

1455 head_limit?: number;

1456 offset?: number;

1457 multiline?: boolean;

1458};

1459```

1460 

1461Powerful search tool built on ripgrep with regex support.

1462 

1463### TaskStop

1464 

1465**Tool name:** `TaskStop`

1466 

1467```typescript theme={null}

1468type TaskStopInput = {

1469 task_id?: string;

1470 shell_id?: string; // Deprecated: use task_id

1471};

1472```

1473 

1474Stops a running background task or shell by ID.

1475 

1476### NotebookEdit

1477 

1478**Tool name:** `NotebookEdit`

1479 

1480```typescript theme={null}

1481type NotebookEditInput = {

1482 notebook_path: string;

1483 cell_id?: string;

1484 new_source: string;

1485 cell_type?: "code" | "markdown";

1486 edit_mode?: "replace" | "insert" | "delete";

1487};

1488```

1489 

1490Edits cells in Jupyter notebook files.

1491 

1492### WebFetch

1493 

1494**Tool name:** `WebFetch`

1495 

1496```typescript theme={null}

1497type WebFetchInput = {

1498 url: string;

1499 prompt: string;

1500};

1501```

1502 

1503Fetches content from a URL and processes it with an AI model.

1504 

1505### WebSearch

1506 

1507**Tool name:** `WebSearch`

1508 

1509```typescript theme={null}

1510type WebSearchInput = {

1511 query: string;

1512 allowed_domains?: string[];

1513 blocked_domains?: string[];

1514};

1515```

1516 

1517Searches the web and returns formatted results.

1518 

1519### TodoWrite

1520 

1521**Tool name:** `TodoWrite`

1522 

1523```typescript theme={null}

1524type TodoWriteInput = {

1525 todos: Array<{

1526 content: string;

1527 status: "pending" | "in_progress" | "completed";

1528 activeForm: string;

1529 }>;

1530};

1531```

1532 

1533Creates and manages a structured task list for tracking progress.

1534 

1535### ExitPlanMode

1536 

1537**Tool name:** `ExitPlanMode`

1538 

1539```typescript theme={null}

1540type ExitPlanModeInput = {

1541 allowedPrompts?: Array<{

1542 tool: "Bash";

1543 prompt: string;

1544 }>;

1545};

1546```

1547 

1548Exits planning mode. Optionally specifies prompt-based permissions needed to implement the plan.

1549 

1550### ListMcpResources

1551 

1552**Tool name:** `ListMcpResources`

1553 

1554```typescript theme={null}

1555type ListMcpResourcesInput = {

1556 server?: string;

1557};

1558```

1559 

1560Lists available MCP resources from connected servers.

1561 

1562### ReadMcpResource

1563 

1564**Tool name:** `ReadMcpResource`

1565 

1566```typescript theme={null}

1567type ReadMcpResourceInput = {

1568 server: string;

1569 uri: string;

1570};

1571```

1572 

1573Reads a specific MCP resource from a server.

1574 

1575### Config

1576 

1577**Tool name:** `Config`

1578 

1579```typescript theme={null}

1580type ConfigInput = {

1581 setting: string;

1582 value?: string | boolean | number;

1583};

1584```

1585 

1586Gets or sets a configuration value.

1587 

1588### EnterWorktree

1589 

1590**Tool name:** `EnterWorktree`

1591 

1592```typescript theme={null}

1593type EnterWorktreeInput = {

1594 name?: string;

1595};

1596```

1597 

1598Creates and enters a temporary git worktree for isolated work.

1599 

1600## Tool Output Types

1601 

1602Documentation of output schemas for all built-in Claude Code tools. These types are exported from `@anthropic-ai/claude-agent-sdk` and represent the actual response data returned by each tool.

1603 

1604### `ToolOutputSchemas`

1605 

1606Union of all tool output types.

1607 

1608```typescript theme={null}

1609type ToolOutputSchemas =

1610 | AgentOutput

1611 | AskUserQuestionOutput

1612 | BashOutput

1613 | ConfigOutput

1614 | EnterWorktreeOutput

1615 | ExitPlanModeOutput

1616 | FileEditOutput

1617 | FileReadOutput

1618 | FileWriteOutput

1619 | GlobOutput

1620 | GrepOutput

1621 | ListMcpResourcesOutput

1622 | NotebookEditOutput

1623 | ReadMcpResourceOutput

1624 | TaskStopOutput

1625 | TodoWriteOutput

1626 | WebFetchOutput

1627 | WebSearchOutput;

1628```

1629 

1630### Agent

1631 

1632**Tool name:** `Agent` (previously `Task`, which is still accepted as an alias)

1633 

1634```typescript theme={null}

1635type AgentOutput =

1636 | {

1637 status: "completed";

1638 agentId: string;

1639 content: Array<{ type: "text"; text: string }>;

1640 totalToolUseCount: number;

1641 totalDurationMs: number;

1642 totalTokens: number;

1643 usage: {

1644 input_tokens: number;

1645 output_tokens: number;

1646 cache_creation_input_tokens: number | null;

1647 cache_read_input_tokens: number | null;

1648 server_tool_use: {

1649 web_search_requests: number;

1650 web_fetch_requests: number;

1651 } | null;

1652 service_tier: ("standard" | "priority" | "batch") | null;

1653 cache_creation: {

1654 ephemeral_1h_input_tokens: number;

1655 ephemeral_5m_input_tokens: number;

1656 } | null;

1657 };

1658 prompt: string;

1659 }

1660 | {

1661 status: "async_launched";

1662 agentId: string;

1663 description: string;

1664 prompt: string;

1665 outputFile: string;

1666 canReadOutputFile?: boolean;

1667 }

1668 | {

1669 status: "sub_agent_entered";

1670 description: string;

1671 message: string;

1672 };

1673```

1674 

1675Returns the result from the subagent. Discriminated on the `status` field: `"completed"` for finished tasks, `"async_launched"` for background tasks, and `"sub_agent_entered"` for interactive subagents.

1676 

1677### AskUserQuestion

1678 

1679**Tool name:** `AskUserQuestion`

1680 

1681```typescript theme={null}

1682type AskUserQuestionOutput = {

1683 questions: Array<{

1684 question: string;

1685 header: string;

1686 options: Array<{ label: string; description: string; preview?: string }>;

1687 multiSelect: boolean;

1688 }>;

1689 answers: Record<string, string>;

1690};

1691```

1692 

1693Returns the questions asked and the user's answers.

1694 

1695### Bash

1696 

1697**Tool name:** `Bash`

1698 

1699```typescript theme={null}

1700type BashOutput = {

1701 stdout: string;

1702 stderr: string;

1703 rawOutputPath?: string;

1704 interrupted: boolean;

1705 isImage?: boolean;

1706 backgroundTaskId?: string;

1707 backgroundedByUser?: boolean;

1708 dangerouslyDisableSandbox?: boolean;

1709 returnCodeInterpretation?: string;

1710 structuredContent?: unknown[];

1711 persistedOutputPath?: string;

1712 persistedOutputSize?: number;

1713};

1714```

1715 

1716Returns command output with stdout/stderr split. Background commands include a `backgroundTaskId`.

1717 

1718### Edit

1719 

1720**Tool name:** `Edit`

1721 

1722```typescript theme={null}

1723type FileEditOutput = {

1724 filePath: string;

1725 oldString: string;

1726 newString: string;

1727 originalFile: string;

1728 structuredPatch: Array<{

1729 oldStart: number;

1730 oldLines: number;

1731 newStart: number;

1732 newLines: number;

1733 lines: string[];

1734 }>;

1735 userModified: boolean;

1736 replaceAll: boolean;

1737 gitDiff?: {

1738 filename: string;

1739 status: "modified" | "added";

1740 additions: number;

1741 deletions: number;

1742 changes: number;

1743 patch: string;

1744 };

1745};

1746```

1747 

1748Returns the structured diff of the edit operation.

1749 

1750### Read

1751 

1752**Tool name:** `Read`

1753 

1754```typescript theme={null}

1755type FileReadOutput =

1756 | {

1757 type: "text";

1758 file: {

1759 filePath: string;

1760 content: string;

1761 numLines: number;

1762 startLine: number;

1763 totalLines: number;

1764 };

1765 }

1766 | {

1767 type: "image";

1768 file: {

1769 base64: string;

1770 type: "image/jpeg" | "image/png" | "image/gif" | "image/webp";

1771 originalSize: number;

1772 dimensions?: {

1773 originalWidth?: number;

1774 originalHeight?: number;

1775 displayWidth?: number;

1776 displayHeight?: number;

1777 };

1778 };

1779 }

1780 | {

1781 type: "notebook";

1782 file: {

1783 filePath: string;

1784 cells: unknown[];

1785 };

1786 }

1787 | {

1788 type: "pdf";

1789 file: {

1790 filePath: string;

1791 base64: string;

1792 originalSize: number;

1793 };

1794 }

1795 | {

1796 type: "parts";

1797 file: {

1798 filePath: string;

1799 originalSize: number;

1800 count: number;

1801 outputDir: string;

1802 };

1803 };

1804```

1805 

1806Returns file contents in a format appropriate to the file type. Discriminated on the `type` field.

1807 

1808### Write

1809 

1810**Tool name:** `Write`

1811 

1812```typescript theme={null}

1813type FileWriteOutput = {

1814 type: "create" | "update";

1815 filePath: string;

1816 content: string;

1817 structuredPatch: Array<{

1818 oldStart: number;

1819 oldLines: number;

1820 newStart: number;

1821 newLines: number;

1822 lines: string[];

1823 }>;

1824 originalFile: string | null;

1825 gitDiff?: {

1826 filename: string;

1827 status: "modified" | "added";

1828 additions: number;

1829 deletions: number;

1830 changes: number;

1831 patch: string;

1832 };

1833};

1834```

1835 

1836Returns the write result with structured diff information.

1837 

1838### Glob

1839 

1840**Tool name:** `Glob`

1841 

1842```typescript theme={null}

1843type GlobOutput = {

1844 durationMs: number;

1845 numFiles: number;

1846 filenames: string[];

1847 truncated: boolean;

1848};

1849```

1850 

1851Returns file paths matching the glob pattern, sorted by modification time.

1852 

1853### Grep

1854 

1855**Tool name:** `Grep`

1856 

1857```typescript theme={null}

1858type GrepOutput = {

1859 mode?: "content" | "files_with_matches" | "count";

1860 numFiles: number;

1861 filenames: string[];

1862 content?: string;

1863 numLines?: number;

1864 numMatches?: number;

1865 appliedLimit?: number;

1866 appliedOffset?: number;

1867};

1868```

1869 

1870Returns search results. The shape varies by `mode`: file list, content with matches, or match counts.

1871 

1872### TaskStop

1873 

1874**Tool name:** `TaskStop`

1875 

1876```typescript theme={null}

1877type TaskStopOutput = {

1878 message: string;

1879 task_id: string;

1880 task_type: string;

1881 command?: string;

1882};

1883```

1884 

1885Returns confirmation after stopping the background task.

1886 

1887### NotebookEdit

1888 

1889**Tool name:** `NotebookEdit`

1890 

1891```typescript theme={null}

1892type NotebookEditOutput = {

1893 new_source: string;

1894 cell_id?: string;

1895 cell_type: "code" | "markdown";

1896 language: string;

1897 edit_mode: string;

1898 error?: string;

1899 notebook_path: string;

1900 original_file: string;

1901 updated_file: string;

1902};

1903```

1904 

1905Returns the result of the notebook edit with original and updated file contents.

1906 

1907### WebFetch

1908 

1909**Tool name:** `WebFetch`

1910 

1911```typescript theme={null}

1912type WebFetchOutput = {

1913 bytes: number;

1914 code: number;

1915 codeText: string;

1916 result: string;

1917 durationMs: number;

1918 url: string;

1919};

1920```

1921 

1922Returns the fetched content with HTTP status and metadata.

1923 

1924### WebSearch

1925 

1926**Tool name:** `WebSearch`

1927 

1928```typescript theme={null}

1929type WebSearchOutput = {

1930 query: string;

1931 results: Array<

1932 | {

1933 tool_use_id: string;

1934 content: Array<{ title: string; url: string }>;

1935 }

1936 | string

1937 >;

1938 durationSeconds: number;

1939};

1940```

1941 

1942Returns search results from the web.

1943 

1944### TodoWrite

1945 

1946**Tool name:** `TodoWrite`

1947 

1948```typescript theme={null}

1949type TodoWriteOutput = {

1950 oldTodos: Array<{

1951 content: string;

1952 status: "pending" | "in_progress" | "completed";

1953 activeForm: string;

1954 }>;

1955 newTodos: Array<{

1956 content: string;

1957 status: "pending" | "in_progress" | "completed";

1958 activeForm: string;

1959 }>;

1960};

1961```

1962 

1963Returns the previous and updated task lists.

1964 

1965### ExitPlanMode

1966 

1967**Tool name:** `ExitPlanMode`

1968 

1969```typescript theme={null}

1970type ExitPlanModeOutput = {

1971 plan: string | null;

1972 isAgent: boolean;

1973 filePath?: string;

1974 hasTaskTool?: boolean;

1975 awaitingLeaderApproval?: boolean;

1976 requestId?: string;

1977};

1978```

1979 

1980Returns the plan state after exiting plan mode.

1981 

1982### ListMcpResources

1983 

1984**Tool name:** `ListMcpResources`

1985 

1986```typescript theme={null}

1987type ListMcpResourcesOutput = Array<{

1988 uri: string;

1989 name: string;

1990 mimeType?: string;

1991 description?: string;

1992 server: string;

1993}>;

1994```

1995 

1996Returns an array of available MCP resources.

1997 

1998### ReadMcpResource

1999 

2000**Tool name:** `ReadMcpResource`

2001 

2002```typescript theme={null}

2003type ReadMcpResourceOutput = {

2004 contents: Array<{

2005 uri: string;

2006 mimeType?: string;

2007 text?: string;

2008 }>;

2009};

2010```

2011 

2012Returns the contents of the requested MCP resource.

2013 

2014### Config

2015 

2016**Tool name:** `Config`

2017 

2018```typescript theme={null}

2019type ConfigOutput = {

2020 success: boolean;

2021 operation?: "get" | "set";

2022 setting?: string;

2023 value?: unknown;

2024 previousValue?: unknown;

2025 newValue?: unknown;

2026 error?: string;

2027};

2028```

2029 

2030Returns the result of a configuration get or set operation.

2031 

2032### EnterWorktree

2033 

2034**Tool name:** `EnterWorktree`

2035 

2036```typescript theme={null}

2037type EnterWorktreeOutput = {

2038 worktreePath: string;

2039 worktreeBranch?: string;

2040 message: string;

2041};

2042```

2043 

2044Returns information about the created git worktree.

2045 

2046## Permission Types

2047 

2048### `PermissionUpdate`

2049 

2050Operations for updating permissions.

2051 

2052```typescript theme={null}

2053type PermissionUpdate =

2054 | {

2055 type: "addRules";

2056 rules: PermissionRuleValue[];

2057 behavior: PermissionBehavior;

2058 destination: PermissionUpdateDestination;

2059 }

2060 | {

2061 type: "replaceRules";

2062 rules: PermissionRuleValue[];

2063 behavior: PermissionBehavior;

2064 destination: PermissionUpdateDestination;

2065 }

2066 | {

2067 type: "removeRules";

2068 rules: PermissionRuleValue[];

2069 behavior: PermissionBehavior;

2070 destination: PermissionUpdateDestination;

2071 }

2072 | {

2073 type: "setMode";

2074 mode: PermissionMode;

2075 destination: PermissionUpdateDestination;

2076 }

2077 | {

2078 type: "addDirectories";

2079 directories: string[];

2080 destination: PermissionUpdateDestination;

2081 }

2082 | {

2083 type: "removeDirectories";

2084 directories: string[];

2085 destination: PermissionUpdateDestination;

2086 };

2087```

2088 

2089### `PermissionBehavior`

2090 

2091```typescript theme={null}

2092type PermissionBehavior = "allow" | "deny" | "ask";

2093```

2094 

2095### `PermissionUpdateDestination`

2096 

2097```typescript theme={null}

2098type PermissionUpdateDestination =

2099 | "userSettings" // Global user settings

2100 | "projectSettings" // Per-directory project settings

2101 | "localSettings" // Gitignored local settings

2102 | "session" // Current session only

2103 | "cliArg"; // CLI argument

2104```

2105 

2106### `PermissionRuleValue`

2107 

2108```typescript theme={null}

2109type PermissionRuleValue = {

2110 toolName: string;

2111 ruleContent?: string;

2112};

2113```

2114 

2115## Other Types

2116 

2117### `ApiKeySource`

2118 

2119```typescript theme={null}

2120type ApiKeySource = "user" | "project" | "org" | "temporary" | "oauth";

2121```

2122 

2123### `SdkBeta`

2124 

2125Available beta features that can be enabled via the `betas` option. See [Beta headers](https://platform.claude.com/docs/en/api/beta-headers) for more information.

2126 

2127```typescript theme={null}

2128type SdkBeta = "context-1m-2025-08-07";

2129```

2130 

2131<Warning>

2132 The `context-1m-2025-08-07` beta is retired as of April 30, 2026. Passing this value with Claude Sonnet 4.5 or Sonnet 4 has no effect, and requests that exceed the standard 200k-token context window return an error. To use a 1M-token context window, migrate to [Claude Sonnet 4.6 or Claude Opus 4.6](https://platform.claude.com/docs/en/about-claude/models/overview), which include 1M context at standard pricing with no beta header required.

2133</Warning>

2134 

2135### `SlashCommand`

2136 

2137Information about an available slash command.

2138 

2139```typescript theme={null}

2140type SlashCommand = {

2141 name: string;

2142 description: string;

2143 argumentHint: string;

2144};

2145```

2146 

2147### `ModelInfo`

2148 

2149Information about an available model.

2150 

2151```typescript theme={null}

2152type ModelInfo = {

2153 value: string;

2154 displayName: string;

2155 description: string;

2156 supportsEffort?: boolean;

2157 supportedEffortLevels?: ("low" | "medium" | "high" | "max")[];

2158 supportsAdaptiveThinking?: boolean;

2159 supportsFastMode?: boolean;

2160};

2161```

2162 

2163### `AgentInfo`

2164 

2165Information about an available subagent that can be invoked via the Agent tool.

2166 

2167```typescript theme={null}

2168type AgentInfo = {

2169 name: string;

2170 description: string;

2171 model?: string;

2172};

2173```

2174 

2175| Field | Type | Description |

2176| :------------ | :-------------------- | :------------------------------------------------------------------- |

2177| `name` | `string` | Agent type identifier (e.g., `"Explore"`, `"general-purpose"`) |

2178| `description` | `string` | Description of when to use this agent |

2179| `model` | `string \| undefined` | Model alias this agent uses. If omitted, inherits the parent's model |

2180 

2181### `McpServerStatus`

2182 

2183Status of a connected MCP server.

2184 

2185```typescript theme={null}

2186type McpServerStatus = {

2187 name: string;

2188 status: "connected" | "failed" | "needs-auth" | "pending" | "disabled";

2189 serverInfo?: {

2190 name: string;

2191 version: string;

2192 };

2193 error?: string;

2194 config?: McpServerStatusConfig;

2195 scope?: string;

2196 tools?: {

2197 name: string;

2198 description?: string;

2199 annotations?: {

2200 readOnly?: boolean;

2201 destructive?: boolean;

2202 openWorld?: boolean;

2203 };

2204 }[];

2205};

2206```

2207 

2208### `McpServerStatusConfig`

2209 

2210The configuration of an MCP server as reported by `mcpServerStatus()`. This is the union of all MCP server transport types.

2211 

2212```typescript theme={null}

2213type McpServerStatusConfig =

2214 | McpStdioServerConfig

2215 | McpSSEServerConfig

2216 | McpHttpServerConfig

2217 | McpSdkServerConfig

2218 | McpClaudeAIProxyServerConfig;

2219```

2220 

2221See [`McpServerConfig`](#mcp-server-config) for details on each transport type.

2222 

2223### `AccountInfo`

2224 

2225Account information for the authenticated user.

2226 

2227```typescript theme={null}

2228type AccountInfo = {

2229 email?: string;

2230 organization?: string;

2231 subscriptionType?: string;

2232 tokenSource?: string;

2233 apiKeySource?: string;

2234};

2235```

2236 

2237### `ModelUsage`

2238 

2239Per-model usage statistics returned in result messages.

2240 

2241```typescript theme={null}

2242type ModelUsage = {

2243 inputTokens: number;

2244 outputTokens: number;

2245 cacheReadInputTokens: number;

2246 cacheCreationInputTokens: number;

2247 webSearchRequests: number;

2248 costUSD: number;

2249 contextWindow: number;

2250 maxOutputTokens: number;

2251};

2252```

2253 

2254### `ConfigScope`

2255 

2256```typescript theme={null}

2257type ConfigScope = "local" | "user" | "project";

2258```

2259 

2260### `NonNullableUsage`

2261 

2262A version of [`Usage`](#usage) with all nullable fields made non-nullable.

2263 

2264```typescript theme={null}

2265type NonNullableUsage = {

2266 [K in keyof Usage]: NonNullable<Usage[K]>;

2267};

2268```

2269 

2270### `Usage`

2271 

2272Token usage statistics (from `@anthropic-ai/sdk`).

2273 

2274```typescript theme={null}

2275type Usage = {

2276 input_tokens: number | null;

2277 output_tokens: number | null;

2278 cache_creation_input_tokens?: number | null;

2279 cache_read_input_tokens?: number | null;

2280};

2281```

2282 

2283### `CallToolResult`

2284 

2285MCP tool result type (from `@modelcontextprotocol/sdk/types.js`).

2286 

2287```typescript theme={null}

2288type CallToolResult = {

2289 content: Array<{

2290 type: "text" | "image" | "resource";

2291 // Additional fields vary by type

2292 }>;

2293 isError?: boolean;

2294};

2295```

2296 

2297### `ThinkingConfig`

2298 

2299Controls Claude's thinking/reasoning behavior. Takes precedence over the deprecated `maxThinkingTokens`.

2300 

2301```typescript theme={null}

2302type ThinkingConfig =

2303 | { type: "adaptive" } // The model determines when and how much to reason (Opus 4.6+)

2304 | { type: "enabled"; budgetTokens?: number } // Fixed thinking token budget

2305 | { type: "disabled" }; // No extended thinking

2306```

2307 

2308### `SpawnedProcess`

2309 

2310Interface for custom process spawning (used with `spawnClaudeCodeProcess` option). `ChildProcess` already satisfies this interface.

2311 

2312```typescript theme={null}

2313interface SpawnedProcess {

2314 stdin: Writable;

2315 stdout: Readable;

2316 readonly killed: boolean;

2317 readonly exitCode: number | null;

2318 kill(signal: NodeJS.Signals): boolean;

2319 on(

2320 event: "exit",

2321 listener: (code: number | null, signal: NodeJS.Signals | null) => void

2322 ): void;

2323 on(event: "error", listener: (error: Error) => void): void;

2324 once(

2325 event: "exit",

2326 listener: (code: number | null, signal: NodeJS.Signals | null) => void

2327 ): void;

2328 once(event: "error", listener: (error: Error) => void): void;

2329 off(

2330 event: "exit",

2331 listener: (code: number | null, signal: NodeJS.Signals | null) => void

2332 ): void;

2333 off(event: "error", listener: (error: Error) => void): void;

2334}

2335```

2336 

2337### `SpawnOptions`

2338 

2339Options passed to the custom spawn function.

2340 

2341```typescript theme={null}

2342interface SpawnOptions {

2343 command: string;

2344 args: string[];

2345 cwd?: string;

2346 env: Record<string, string | undefined>;

2347 signal: AbortSignal;

2348}

2349```

2350 

2351### `McpSetServersResult`

2352 

2353Result of a `setMcpServers()` operation.

2354 

2355```typescript theme={null}

2356type McpSetServersResult = {

2357 added: string[];

2358 removed: string[];

2359 errors: Record<string, string>;

2360};

2361```

2362 

2363### `RewindFilesResult`

2364 

2365Result of a `rewindFiles()` operation.

2366 

2367```typescript theme={null}

2368type RewindFilesResult = {

2369 canRewind: boolean;

2370 error?: string;

2371 filesChanged?: string[];

2372 insertions?: number;

2373 deletions?: number;

2374};

2375```

2376 

2377### `SDKStatusMessage`

2378 

2379Status update message (e.g., compacting).

2380 

2381```typescript theme={null}

2382type SDKStatusMessage = {

2383 type: "system";

2384 subtype: "status";

2385 status: "compacting" | null;

2386 permissionMode?: PermissionMode;

2387 uuid: UUID;

2388 session_id: string;

2389};

2390```

2391 

2392### `SDKTaskNotificationMessage`

2393 

2394Notification when a background task completes, fails, or is stopped.

2395 

2396```typescript theme={null}

2397type SDKTaskNotificationMessage = {

2398 type: "system";

2399 subtype: "task_notification";

2400 task_id: string;

2401 tool_use_id?: string;

2402 status: "completed" | "failed" | "stopped";

2403 output_file: string;

2404 summary: string;

2405 usage?: {

2406 total_tokens: number;

2407 tool_uses: number;

2408 duration_ms: number;

2409 };

2410 uuid: UUID;

2411 session_id: string;

2412};

2413```

2414 

2415### `SDKToolUseSummaryMessage`

2416 

2417Summary of tool usage in a conversation.

2418 

2419```typescript theme={null}

2420type SDKToolUseSummaryMessage = {

2421 type: "tool_use_summary";

2422 summary: string;

2423 preceding_tool_use_ids: string[];

2424 uuid: UUID;

2425 session_id: string;

2426};

2427```

2428 

2429### `SDKHookStartedMessage`

2430 

2431Emitted when a hook begins executing.

2432 

2433```typescript theme={null}

2434type SDKHookStartedMessage = {

2435 type: "system";

2436 subtype: "hook_started";

2437 hook_id: string;

2438 hook_name: string;

2439 hook_event: string;

2440 uuid: UUID;

2441 session_id: string;

2442};

2443```

2444 

2445### `SDKHookProgressMessage`

2446 

2447Emitted while a hook is running, with stdout/stderr output.

2448 

2449```typescript theme={null}

2450type SDKHookProgressMessage = {

2451 type: "system";

2452 subtype: "hook_progress";

2453 hook_id: string;

2454 hook_name: string;

2455 hook_event: string;

2456 stdout: string;

2457 stderr: string;

2458 output: string;

2459 uuid: UUID;

2460 session_id: string;

2461};

2462```

2463 

2464### `SDKHookResponseMessage`

2465 

2466Emitted when a hook finishes executing.

2467 

2468```typescript theme={null}

2469type SDKHookResponseMessage = {

2470 type: "system";

2471 subtype: "hook_response";

2472 hook_id: string;

2473 hook_name: string;

2474 hook_event: string;

2475 output: string;

2476 stdout: string;

2477 stderr: string;

2478 exit_code?: number;

2479 outcome: "success" | "error" | "cancelled";

2480 uuid: UUID;

2481 session_id: string;

2482};

2483```

2484 

2485### `SDKToolProgressMessage`

2486 

2487Emitted periodically while a tool is executing to indicate progress.

2488 

2489```typescript theme={null}

2490type SDKToolProgressMessage = {

2491 type: "tool_progress";

2492 tool_use_id: string;

2493 tool_name: string;

2494 parent_tool_use_id: string | null;

2495 elapsed_time_seconds: number;

2496 task_id?: string;

2497 uuid: UUID;

2498 session_id: string;

2499};

2500```

2501 

2502### `SDKAuthStatusMessage`

2503 

2504Emitted during authentication flows.

2505 

2506```typescript theme={null}

2507type SDKAuthStatusMessage = {

2508 type: "auth_status";

2509 isAuthenticating: boolean;

2510 output: string[];

2511 error?: string;

2512 uuid: UUID;

2513 session_id: string;

2514};

2515```

2516 

2517### `SDKTaskStartedMessage`

2518 

2519Emitted when a background task begins.

2520 

2521```typescript theme={null}

2522type SDKTaskStartedMessage = {

2523 type: "system";

2524 subtype: "task_started";

2525 task_id: string;

2526 tool_use_id?: string;

2527 description: string;

2528 task_type?: string;

2529 uuid: UUID;

2530 session_id: string;

2531};

2532```

2533 

2534### `SDKTaskProgressMessage`

2535 

2536Emitted periodically while a background task is running.

2537 

2538```typescript theme={null}

2539type SDKTaskProgressMessage = {

2540 type: "system";

2541 subtype: "task_progress";

2542 task_id: string;

2543 tool_use_id?: string;

2544 description: string;

2545 usage: {

2546 total_tokens: number;

2547 tool_uses: number;

2548 duration_ms: number;

2549 };

2550 last_tool_name?: string;

2551 uuid: UUID;

2552 session_id: string;

2553};

2554```

2555 

2556### `SDKFilesPersistedEvent`

2557 

2558Emitted when file checkpoints are persisted to disk.

2559 

2560```typescript theme={null}

2561type SDKFilesPersistedEvent = {

2562 type: "system";

2563 subtype: "files_persisted";

2564 files: { filename: string; file_id: string }[];

2565 failed: { filename: string; error: string }[];

2566 processed_at: string;

2567 uuid: UUID;

2568 session_id: string;

2569};

2570```

2571 

2572### `SDKRateLimitEvent`

2573 

2574Emitted when the session encounters a rate limit.

2575 

2576```typescript theme={null}

2577type SDKRateLimitEvent = {

2578 type: "rate_limit_event";

2579 rate_limit_info: {

2580 status: "allowed" | "allowed_warning" | "rejected";

2581 resetsAt?: number;

2582 utilization?: number;

2583 };

2584 uuid: UUID;

2585 session_id: string;

2586};

2587```

2588 

2589### `SDKLocalCommandOutputMessage`

2590 

2591Output from a local slash command (for example, `/voice` or `/cost`). Displayed as assistant-style text in the transcript.

2592 

2593```typescript theme={null}

2594type SDKLocalCommandOutputMessage = {

2595 type: "system";

2596 subtype: "local_command_output";

2597 content: string;

2598 uuid: UUID;

2599 session_id: string;

2600};

2601```

2602 

2603### `SDKPromptSuggestionMessage`

2604 

2605Emitted after each turn when `promptSuggestions` is enabled. Contains a predicted next user prompt.

2606 

2607```typescript theme={null}

2608type SDKPromptSuggestionMessage = {

2609 type: "prompt_suggestion";

2610 suggestion: string;

2611 uuid: UUID;

2612 session_id: string;

2613};

2614```

2615 

2616### `AbortError`

2617 

2618Custom error class for abort operations.

2619 

2620```typescript theme={null}

2621class AbortError extends Error {}

2622```

2623 

2624## Sandbox Configuration

2625 

2626### `SandboxSettings`

2627 

2628Configuration for sandbox behavior. Use this to enable command sandboxing and configure network restrictions programmatically.

2629 

2630```typescript theme={null}

2631type SandboxSettings = {

2632 enabled?: boolean;

2633 autoAllowBashIfSandboxed?: boolean;

2634 excludedCommands?: string[];

2635 allowUnsandboxedCommands?: boolean;

2636 network?: SandboxNetworkConfig;

2637 filesystem?: SandboxFilesystemConfig;

2638 ignoreViolations?: Record<string, string[]>;

2639 enableWeakerNestedSandbox?: boolean;

2640 ripgrep?: { command: string; args?: string[] };

2641};

2642```

2643 

2644| Property | Type | Default | Description |

2645| :-------------------------- | :------------------------------------------------------ | :---------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |

2646| `enabled` | `boolean` | `false` | Enable sandbox mode for command execution |

2647| `autoAllowBashIfSandboxed` | `boolean` | `true` | Auto-approve bash commands when sandbox is enabled |

2648| `excludedCommands` | `string[]` | `[]` | Commands that always bypass sandbox restrictions (e.g., `['docker']`). These run unsandboxed automatically without model involvement |

2649| `allowUnsandboxedCommands` | `boolean` | `true` | Allow the model to request running commands outside the sandbox. When `true`, the model can set `dangerouslyDisableSandbox` in tool input, which falls back to the [permissions system](#permissions-fallback-for-unsandboxed-commands) |

2650| `network` | [`SandboxNetworkConfig`](#sandbox-network-config) | `undefined` | Network-specific sandbox configuration |

2651| `filesystem` | [`SandboxFilesystemConfig`](#sandbox-filesystem-config) | `undefined` | Filesystem-specific sandbox configuration for read/write restrictions |

2652| `ignoreViolations` | `Record<string, string[]>` | `undefined` | Map of violation categories to patterns to ignore (e.g., `{ file: ['/tmp/*'], network: ['localhost'] }`) |

2653| `enableWeakerNestedSandbox` | `boolean` | `false` | Enable a weaker nested sandbox for compatibility |

2654| `ripgrep` | `{ command: string; args?: string[] }` | `undefined` | Custom ripgrep binary configuration for sandbox environments |

2655 

2656#### Example usage

2657 

2658```typescript theme={null}

2659import { query } from "@anthropic-ai/claude-agent-sdk";

2660 

2661for await (const message of query({

2662 prompt: "Build and test my project",

2663 options: {

2664 sandbox: {

2665 enabled: true,

2666 autoAllowBashIfSandboxed: true,

2667 network: {

2668 allowLocalBinding: true

2669 }

2670 }

2671 }

2672})) {

2673 if ("result" in message) console.log(message.result);

2674}

2675```

2676 

2677<Warning>

2678 **Unix socket security:** The `allowUnixSockets` option can grant access to powerful system services. For example, allowing `/var/run/docker.sock` effectively grants full host system access through the Docker API, bypassing sandbox isolation. Only allow Unix sockets that are strictly necessary and understand the security implications of each.

2679</Warning>

2680 

2681### `SandboxNetworkConfig`

2682 

2683Network-specific configuration for sandbox mode.

2684 

2685```typescript theme={null}

2686type SandboxNetworkConfig = {

2687 allowedDomains?: string[];

2688 allowManagedDomainsOnly?: boolean;

2689 allowLocalBinding?: boolean;

2690 allowUnixSockets?: string[];

2691 allowAllUnixSockets?: boolean;

2692 httpProxyPort?: number;

2693 socksProxyPort?: number;

2694};

2695```

2696 

2697| Property | Type | Default | Description |

2698| :------------------------ | :--------- | :---------- | :---------------------------------------------------------------- |

2699| `allowedDomains` | `string[]` | `[]` | Domain names that sandboxed processes can access |

2700| `allowManagedDomainsOnly` | `boolean` | `false` | Restrict network access to only the domains in `allowedDomains` |

2701| `allowLocalBinding` | `boolean` | `false` | Allow processes to bind to local ports (e.g., for dev servers) |

2702| `allowUnixSockets` | `string[]` | `[]` | Unix socket paths that processes can access (e.g., Docker socket) |

2703| `allowAllUnixSockets` | `boolean` | `false` | Allow access to all Unix sockets |

2704| `httpProxyPort` | `number` | `undefined` | HTTP proxy port for network requests |

2705| `socksProxyPort` | `number` | `undefined` | SOCKS proxy port for network requests |

2706 

2707### `SandboxFilesystemConfig`

2708 

2709Filesystem-specific configuration for sandbox mode.

2710 

2711```typescript theme={null}

2712type SandboxFilesystemConfig = {

2713 allowWrite?: string[];

2714 denyWrite?: string[];

2715 denyRead?: string[];

2716};

2717```

2718 

2719| Property | Type | Default | Description |

2720| :----------- | :--------- | :------ | :------------------------------------------ |

2721| `allowWrite` | `string[]` | `[]` | File path patterns to allow write access to |

2722| `denyWrite` | `string[]` | `[]` | File path patterns to deny write access to |

2723| `denyRead` | `string[]` | `[]` | File path patterns to deny read access to |

2724 

2725### Permissions Fallback for Unsandboxed Commands

2726 

2727When `allowUnsandboxedCommands` is enabled, the model can request to run commands outside the sandbox by setting `dangerouslyDisableSandbox: true` in the tool input. These requests fall back to the existing permissions system, meaning your `canUseTool` handler is invoked, allowing you to implement custom authorization logic.

2728 

2729<Note>

2730 **`excludedCommands` vs `allowUnsandboxedCommands`:**

2731 

2732 * `excludedCommands`: A static list of commands that always bypass the sandbox automatically (e.g., `['docker']`). The model has no control over this.

2733 * `allowUnsandboxedCommands`: Lets the model decide at runtime whether to request unsandboxed execution by setting `dangerouslyDisableSandbox: true` in the tool input.

2734</Note>

2735 

2736```typescript theme={null}

2737import { query } from "@anthropic-ai/claude-agent-sdk";

2738 

2739for await (const message of query({

2740 prompt: "Deploy my application",

2741 options: {

2742 sandbox: {

2743 enabled: true,

2744 allowUnsandboxedCommands: true // Model can request unsandboxed execution

2745 },

2746 permissionMode: "default",

2747 canUseTool: async (tool, input) => {

2748 // Check if the model is requesting to bypass the sandbox

2749 if (tool === "Bash" && input.dangerouslyDisableSandbox) {

2750 // The model is requesting to run this command outside the sandbox

2751 console.log(`Unsandboxed command requested: ${input.command}`);

2752 

2753 if (isCommandAuthorized(input.command)) {

2754 return { behavior: "allow" as const, updatedInput: input };

2755 }

2756 return {

2757 behavior: "deny" as const,

2758 message: "Command not authorized for unsandboxed execution"

2759 };

2760 }

2761 return { behavior: "allow" as const, updatedInput: input };

2762 }

2763 }

2764})) {

2765 if ("result" in message) console.log(message.result);

2766}

2767```

2768 

2769This pattern enables you to:

2770 

2771* **Audit model requests:** Log when the model requests unsandboxed execution

2772* **Implement allowlists:** Only permit specific commands to run unsandboxed

2773* **Add approval workflows:** Require explicit authorization for privileged operations

2774 

2775<Warning>

2776 Commands running with `dangerouslyDisableSandbox: true` have full system access. Ensure your `canUseTool` handler validates these requests carefully.

2777 

2778 If `permissionMode` is set to `bypassPermissions` and `allowUnsandboxedCommands` is enabled, the model can autonomously execute commands outside the sandbox without any approval prompts. This combination effectively allows the model to escape sandbox isolation silently.

2779</Warning>

2780 

2781## See also

2782 

2783* [SDK overview](/en/agent-sdk/overview) - General SDK concepts

2784* [Python SDK reference](/en/agent-sdk/python) - Python SDK documentation

2785* [CLI reference](/en/cli-reference) - Command-line interface

2786* [Common workflows](/en/common-workflows) - Step-by-step guides

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# TypeScript SDK V2 interface (preview)

6 

7> Preview of the simplified V2 TypeScript Agent SDK, with session-based send/stream patterns for multi-turn conversations.

8 

9<Warning>

10 The V2 interface is an **unstable preview**. APIs may change based on feedback before becoming stable. Some features like session forking are only available in the [V1 SDK](/en/agent-sdk/typescript).

11</Warning>

12 

13The V2 Claude Agent TypeScript SDK removes the need for async generators and yield coordination. This makes multi-turn conversations simpler, instead of managing generator state across turns, each turn is a separate `send()`/`stream()` cycle. The API surface reduces to three concepts:

14 

15* `createSession()` / `resumeSession()`: Start or continue a conversation

16* `session.send()`: Send a message

17* `session.stream()`: Get the response

18 

19## Installation

20 

21The V2 interface is included in the existing SDK package:

22 

23```bash theme={null}

24npm install @anthropic-ai/claude-agent-sdk

25```

26 

27## Quick start

28 

29### One-shot prompt

30 

31For simple single-turn queries where you don't need to maintain a session, use `unstable_v2_prompt()`. This example sends a math question and logs the answer:

32 

33```typescript theme={null}

34import { unstable_v2_prompt } from "@anthropic-ai/claude-agent-sdk";

35 

36const result = await unstable_v2_prompt("What is 2 + 2?", {

37 model: "claude-opus-4-6"

38});

39if (result.subtype === "success") {

40 console.log(result.result);

41}

42```

43 

44<details>

45 <summary>See the same operation in V1</summary>

46 

47 ```typescript theme={null}

48 import { query } from "@anthropic-ai/claude-agent-sdk";

49 

50 const q = query({

51 prompt: "What is 2 + 2?",

52 options: { model: "claude-opus-4-6" }

53 });

54 

55 for await (const msg of q) {

56 if (msg.type === "result" && msg.subtype === "success") {

57 console.log(msg.result);

58 }

59 }

60 ```

61</details>

62 

63### Basic session

64 

65For interactions beyond a single prompt, create a session. V2 separates sending and streaming into distinct steps:

66 

67* `send()` dispatches your message

68* `stream()` streams back the response

69 

70This explicit separation makes it easier to add logic between turns (like processing responses before sending follow-ups).

71 

72The example below creates a session, sends "Hello!" to Claude, and prints the text response. It uses [`await using`](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-5-2.html#using-declarations-and-explicit-resource-management) (TypeScript 5.2+) to automatically close the session when the block exits. You can also call `session.close()` manually.

73 

74```typescript theme={null}

75import { unstable_v2_createSession } from "@anthropic-ai/claude-agent-sdk";

76 

77await using session = unstable_v2_createSession({

78 model: "claude-opus-4-6"

79});

80 

81await session.send("Hello!");

82for await (const msg of session.stream()) {

83 // Filter for assistant messages to get human-readable output

84 if (msg.type === "assistant") {

85 const text = msg.message.content

86 .filter((block) => block.type === "text")

87 .map((block) => block.text)

88 .join("");

89 console.log(text);

90 }

91}

92```

93 

94<details>

95 <summary>See the same operation in V1</summary>

96 

97 In V1, both input and output flow through a single async generator. For a basic prompt this looks similar, but adding multi-turn logic requires restructuring to use an input generator.

98 

99 ```typescript theme={null}

100 import { query } from "@anthropic-ai/claude-agent-sdk";

101 

102 const q = query({

103 prompt: "Hello!",

104 options: { model: "claude-opus-4-6" }

105 });

106 

107 for await (const msg of q) {

108 if (msg.type === "assistant") {

109 const text = msg.message.content

110 .filter((block) => block.type === "text")

111 .map((block) => block.text)

112 .join("");

113 console.log(text);

114 }

115 }

116 ```

117</details>

118 

119### Multi-turn conversation

120 

121Sessions persist context across multiple exchanges. To continue a conversation, call `send()` again on the same session. Claude remembers the previous turns.

122 

123This example asks a math question, then asks a follow-up that references the previous answer:

124 

125```typescript theme={null}

126import { unstable_v2_createSession } from "@anthropic-ai/claude-agent-sdk";

127 

128await using session = unstable_v2_createSession({

129 model: "claude-opus-4-6"

130});

131 

132// Turn 1

133await session.send("What is 5 + 3?");

134for await (const msg of session.stream()) {

135 // Filter for assistant messages to get human-readable output

136 if (msg.type === "assistant") {

137 const text = msg.message.content

138 .filter((block) => block.type === "text")

139 .map((block) => block.text)

140 .join("");

141 console.log(text);

142 }

143}

144 

145// Turn 2

146await session.send("Multiply that by 2");

147for await (const msg of session.stream()) {

148 if (msg.type === "assistant") {

149 const text = msg.message.content

150 .filter((block) => block.type === "text")

151 .map((block) => block.text)

152 .join("");

153 console.log(text);

154 }

155}

156```

157 

158<details>

159 <summary>See the same operation in V1</summary>

160 

161 ```typescript theme={null}

162 import { query } from "@anthropic-ai/claude-agent-sdk";

163 

164 // Must create an async iterable to feed messages

165 async function* createInputStream() {

166 yield {

167 type: "user",

168 session_id: "",

169 message: { role: "user", content: [{ type: "text", text: "What is 5 + 3?" }] },

170 parent_tool_use_id: null

171 };

172 // Must coordinate when to yield next message

173 yield {

174 type: "user",

175 session_id: "",

176 message: { role: "user", content: [{ type: "text", text: "Multiply by 2" }] },

177 parent_tool_use_id: null

178 };

179 }

180 

181 const q = query({

182 prompt: createInputStream(),

183 options: { model: "claude-opus-4-6" }

184 });

185 

186 for await (const msg of q) {

187 if (msg.type === "assistant") {

188 const text = msg.message.content

189 .filter((block) => block.type === "text")

190 .map((block) => block.text)

191 .join("");

192 console.log(text);

193 }

194 }

195 ```

196</details>

197 

198### Session resume

199 

200If you have a session ID from a previous interaction, you can resume it later. This is useful for long-running workflows or when you need to persist conversations across application restarts.

201 

202This example creates a session, stores its ID, closes it, then resumes the conversation:

203 

204```typescript theme={null}

205import {

206 unstable_v2_createSession,

207 unstable_v2_resumeSession,

208 type SDKMessage

209} from "@anthropic-ai/claude-agent-sdk";

210 

211// Helper to extract text from assistant messages

212function getAssistantText(msg: SDKMessage): string | null {

213 if (msg.type !== "assistant") return null;

214 return msg.message.content

215 .filter((block) => block.type === "text")

216 .map((block) => block.text)

217 .join("");

218}

219 

220// Create initial session and have a conversation

221const session = unstable_v2_createSession({

222 model: "claude-opus-4-6"

223});

224 

225await session.send("Remember this number: 42");

226 

227// Get the session ID from any received message

228let sessionId: string | undefined;

229for await (const msg of session.stream()) {

230 sessionId = msg.session_id;

231 const text = getAssistantText(msg);

232 if (text) console.log("Initial response:", text);

233}

234 

235console.log("Session ID:", sessionId);

236session.close();

237 

238// Later: resume the session using the stored ID

239await using resumedSession = unstable_v2_resumeSession(sessionId!, {

240 model: "claude-opus-4-6"

241});

242 

243await resumedSession.send("What number did I ask you to remember?");

244for await (const msg of resumedSession.stream()) {

245 const text = getAssistantText(msg);

246 if (text) console.log("Resumed response:", text);

247}

248```

249 

250<details>

251 <summary>See the same operation in V1</summary>

252 

253 ```typescript theme={null}

254 import { query } from "@anthropic-ai/claude-agent-sdk";

255 

256 // Create initial session

257 const initialQuery = query({

258 prompt: "Remember this number: 42",

259 options: { model: "claude-opus-4-6" }

260 });

261 

262 // Get session ID from any message

263 let sessionId: string | undefined;

264 for await (const msg of initialQuery) {

265 sessionId = msg.session_id;

266 if (msg.type === "assistant") {

267 const text = msg.message.content

268 .filter((block) => block.type === "text")

269 .map((block) => block.text)

270 .join("");

271 console.log("Initial response:", text);

272 }

273 }

274 

275 console.log("Session ID:", sessionId);

276 

277 // Later: resume the session

278 const resumedQuery = query({

279 prompt: "What number did I ask you to remember?",

280 options: {

281 model: "claude-opus-4-6",

282 resume: sessionId

283 }

284 });

285 

286 for await (const msg of resumedQuery) {

287 if (msg.type === "assistant") {

288 const text = msg.message.content

289 .filter((block) => block.type === "text")

290 .map((block) => block.text)

291 .join("");

292 console.log("Resumed response:", text);

293 }

294 }

295 ```

296</details>

297 

298### Cleanup

299 

300Sessions can be closed manually or automatically using [`await using`](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-5-2.html#using-declarations-and-explicit-resource-management), a TypeScript 5.2+ feature for automatic resource cleanup. If you're using an older TypeScript version or encounter compatibility issues, use manual cleanup instead.

301 

302**Automatic cleanup (TypeScript 5.2+):**

303 

304```typescript theme={null}

305import { unstable_v2_createSession } from "@anthropic-ai/claude-agent-sdk";

306 

307await using session = unstable_v2_createSession({

308 model: "claude-opus-4-6"

309});

310// Session closes automatically when the block exits

311```

312 

313**Manual cleanup:**

314 

315```typescript theme={null}

316import { unstable_v2_createSession } from "@anthropic-ai/claude-agent-sdk";

317 

318const session = unstable_v2_createSession({

319 model: "claude-opus-4-6"

320});

321// ... use the session ...

322session.close();

323```

324 

325## API reference

326 

327### `unstable_v2_createSession()`

328 

329Creates a new session for multi-turn conversations.

330 

331```typescript theme={null}

332function unstable_v2_createSession(options: {

333 model: string;

334 // Additional options supported

335}): SDKSession;

336```

337 

338### `unstable_v2_resumeSession()`

339 

340Resumes an existing session by ID.

341 

342```typescript theme={null}

343function unstable_v2_resumeSession(

344 sessionId: string,

345 options: {

346 model: string;

347 // Additional options supported

348 }

349): SDKSession;

350```

351 

352### `unstable_v2_prompt()`

353 

354One-shot convenience function for single-turn queries.

355 

356```typescript theme={null}

357function unstable_v2_prompt(

358 prompt: string,

359 options: {

360 model: string;

361 // Additional options supported

362 }

363): Promise<SDKResultMessage>;

364```

365 

366### SDKSession interface

367 

368```typescript theme={null}

369interface SDKSession {

370 readonly sessionId: string;

371 send(message: string | SDKUserMessage): Promise<void>;

372 stream(): AsyncGenerator<SDKMessage, void>;

373 close(): void;

374}

375```

376 

377## Feature availability

378 

379Not all V1 features are available in V2 yet. The following require using the [V1 SDK](/en/agent-sdk/typescript):

380 

381* Session forking (`forkSession` option)

382* Some advanced streaming input patterns

383 

384## Feedback

385 

386Share your feedback on the V2 interface before it becomes stable. Report issues and suggestions through [GitHub Issues](https://github.com/anthropics/claude-code/issues).

387 

388## See also

389 

390* [TypeScript SDK reference (V1)](/en/agent-sdk/typescript) - Full V1 SDK documentation

391* [SDK overview](/en/agent-sdk/overview) - General SDK concepts

392* [V2 examples on GitHub](https://github.com/anthropics/claude-agent-sdk-demos/tree/main/hello-world-v2) - Working code examples

agent-sdk/user-input.md +808 −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# Handle approvals and user input

6 

7> Surface Claude's approval requests and clarifying questions to users, then return their decisions to the SDK.

8 

9While working on a task, Claude sometimes needs to check in with users. It might need permission before deleting files, or need to ask which database to use for a new project. Your application needs to surface these requests to users so Claude can continue with their input.

10 

11Claude requests user input in two situations: when it needs **permission to use a tool** (like deleting files or running commands), and when it has **clarifying questions** (via the `AskUserQuestion` tool). Both trigger your `canUseTool` callback, which pauses execution until you return a response. This is different from normal conversation turns where Claude finishes and waits for your next message.

12 

13For clarifying questions, Claude generates the questions and options. Your role is to present them to users and return their selections. You can't add your own questions to this flow; if you need to ask users something yourself, do that separately in your application logic.

14 

15This guide shows you how to detect each type of request and respond appropriately.

16 

17## Detect when Claude needs input

18 

19Pass a `canUseTool` callback in your query options. The callback fires whenever Claude needs user input, receiving the tool name and input as arguments:

20 

21<CodeGroup>

22 ```python Python theme={null}

23 async def handle_tool_request(tool_name, input_data, context):

24 # Prompt user and return allow or deny

25 ...

26 

27 

28 options = ClaudeAgentOptions(can_use_tool=handle_tool_request)

29 ```

30 

31 ```typescript TypeScript theme={null}

32 async function handleToolRequest(toolName, input, options) {

33 // options includes { signal: AbortSignal, suggestions?: PermissionUpdate[] }

34 // Prompt user and return allow or deny

35 }

36 

37 const options = { canUseTool: handleToolRequest };

38 ```

39</CodeGroup>

40 

41The callback fires in two cases:

42 

431. **Tool needs approval**: Claude wants to use a tool that isn't auto-approved by [permission rules](/en/agent-sdk/permissions) or modes. Check `tool_name` for the tool (e.g., `"Bash"`, `"Write"`).

442. **Claude asks a question**: Claude calls the `AskUserQuestion` tool. Check if `tool_name == "AskUserQuestion"` to handle it differently. If you specify a `tools` array, include `AskUserQuestion` for this to work. See [Handle clarifying questions](#handle-clarifying-questions) for details.

45 

46<Note>

47 To automatically allow or deny tools without prompting users, use [hooks](/en/agent-sdk/hooks) instead. Hooks execute before `canUseTool` and can allow, deny, or modify requests based on your own logic. You can also use the [`PermissionRequest` hook](/en/agent-sdk/hooks#available-hooks) to send external notifications (Slack, email, push) when Claude is waiting for approval.

48</Note>

49 

50## Handle tool approval requests

51 

52Once you've passed a `canUseTool` callback in your query options, it fires when Claude wants to use a tool that isn't auto-approved. Your callback receives three arguments:

53 

54| Argument | Description |

55| ----------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |

56| `toolName` | The name of the tool Claude wants to use (e.g., `"Bash"`, `"Write"`, `"Edit"`) |

57| `input` | The parameters Claude is passing to the tool. Contents vary by tool. |

58| `options` (TS) / `context` (Python) | Additional context including optional `suggestions` (proposed `PermissionUpdate` entries to avoid re-prompting) and a cancellation signal. In TypeScript, `signal` is an `AbortSignal`; in Python, the signal field is reserved for future use. See [`ToolPermissionContext`](/en/agent-sdk/python#tool-permission-context) for Python. |

59 

60The `input` object contains tool-specific parameters. Common examples:

61 

62| Tool | Input fields |

63| ------- | --------------------------------------- |

64| `Bash` | `command`, `description`, `timeout` |

65| `Write` | `file_path`, `content` |

66| `Edit` | `file_path`, `old_string`, `new_string` |

67| `Read` | `file_path`, `offset`, `limit` |

68 

69See the SDK reference for complete input schemas: [Python](/en/agent-sdk/python#tool-input-output-types) | [TypeScript](/en/agent-sdk/typescript#tool-input-types).

70 

71You can display this information to the user so they can decide whether to allow or reject the action, then return the appropriate response.

72 

73The following example asks Claude to create and delete a test file. When Claude attempts each operation, the callback prints the tool request to the terminal and prompts for y/n approval.

74 

75<CodeGroup>

76 ```python Python theme={null}

77 import asyncio

78 

79 from claude_agent_sdk import ClaudeAgentOptions, ResultMessage, query

80 from claude_agent_sdk.types import (

81 HookMatcher,

82 PermissionResultAllow,

83 PermissionResultDeny,

84 ToolPermissionContext,

85 )

86 

87 

88 async def can_use_tool(

89 tool_name: str, input_data: dict, context: ToolPermissionContext

90 ) -> PermissionResultAllow | PermissionResultDeny:

91 # Display the tool request

92 print(f"\nTool: {tool_name}")

93 if tool_name == "Bash":

94 print(f"Command: {input_data.get('command')}")

95 if input_data.get("description"):

96 print(f"Description: {input_data.get('description')}")

97 else:

98 print(f"Input: {input_data}")

99 

100 # Get user approval

101 response = input("Allow this action? (y/n): ")

102 

103 # Return allow or deny based on user's response

104 if response.lower() == "y":

105 # Allow: tool executes with the original (or modified) input

106 return PermissionResultAllow(updated_input=input_data)

107 else:

108 # Deny: tool doesn't execute, Claude sees the message

109 return PermissionResultDeny(message="User denied this action")

110 

111 

112 # Required workaround: dummy hook keeps the stream open for can_use_tool

113 async def dummy_hook(input_data, tool_use_id, context):

114 return {"continue_": True}

115 

116 

117 async def prompt_stream():

118 yield {

119 "type": "user",

120 "message": {

121 "role": "user",

122 "content": "Create a test file in /tmp and then delete it",

123 },

124 }

125 

126 

127 async def main():

128 async for message in query(

129 prompt=prompt_stream(),

130 options=ClaudeAgentOptions(

131 can_use_tool=can_use_tool,

132 hooks={"PreToolUse": [HookMatcher(matcher=None, hooks=[dummy_hook])]},

133 ),

134 ):

135 if isinstance(message, ResultMessage) and message.subtype == "success":

136 print(message.result)

137 

138 

139 asyncio.run(main())

140 ```

141 

142 ```typescript TypeScript theme={null}

143 import { query } from "@anthropic-ai/claude-agent-sdk";

144 import * as readline from "readline";

145 

146 // Helper to prompt user for input in the terminal

147 function prompt(question: string): Promise<string> {

148 const rl = readline.createInterface({

149 input: process.stdin,

150 output: process.stdout

151 });

152 return new Promise((resolve) =>

153 rl.question(question, (answer) => {

154 rl.close();

155 resolve(answer);

156 })

157 );

158 }

159 

160 for await (const message of query({

161 prompt: "Create a test file in /tmp and then delete it",

162 options: {

163 canUseTool: async (toolName, input) => {

164 // Display the tool request

165 console.log(`\nTool: ${toolName}`);

166 if (toolName === "Bash") {

167 console.log(`Command: ${input.command}`);

168 if (input.description) console.log(`Description: ${input.description}`);

169 } else {

170 console.log(`Input: ${JSON.stringify(input, null, 2)}`);

171 }

172 

173 // Get user approval

174 const response = await prompt("Allow this action? (y/n): ");

175 

176 // Return allow or deny based on user's response

177 if (response.toLowerCase() === "y") {

178 // Allow: tool executes with the original (or modified) input

179 return { behavior: "allow", updatedInput: input };

180 } else {

181 // Deny: tool doesn't execute, Claude sees the message

182 return { behavior: "deny", message: "User denied this action" };

183 }

184 }

185 }

186 })) {

187 if ("result" in message) console.log(message.result);

188 }

189 ```

190</CodeGroup>

191 

192<Note>

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

194</Note>

195 

196This 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.

197 

198### Respond to tool requests

199 

200Your callback returns one of two response types:

201 

202| Response | Python | TypeScript |

203| --------- | ------------------------------------------ | ------------------------------------- |

204| **Allow** | `PermissionResultAllow(updated_input=...)` | `{ behavior: "allow", updatedInput }` |

205| **Deny** | `PermissionResultDeny(message=...)` | `{ behavior: "deny", message }` |

206 

207When allowing, pass the tool input (original or modified). When denying, provide a message explaining why. Claude sees this message and may adjust its approach.

208 

209<CodeGroup>

210 ```python Python theme={null}

211 from claude_agent_sdk.types import PermissionResultAllow, PermissionResultDeny

212 

213 # Allow the tool to execute

214 return PermissionResultAllow(updated_input=input_data)

215 

216 # Block the tool

217 return PermissionResultDeny(message="User rejected this action")

218 ```

219 

220 ```typescript TypeScript theme={null}

221 // Allow the tool to execute

222 return { behavior: "allow", updatedInput: input };

223 

224 // Block the tool

225 return { behavior: "deny", message: "User rejected this action" };

226 ```

227</CodeGroup>

228 

229Beyond allowing or denying, you can modify the tool's input or provide context that helps Claude adjust its approach:

230 

231* **Approve**: let the tool execute as Claude requested

232* **Approve with changes**: modify the input before execution (e.g., sanitize paths, add constraints)

233* **Reject**: block the tool and tell Claude why

234* **Suggest alternative**: block but guide Claude toward what the user wants instead

235* **Redirect entirely**: use [streaming input](/en/agent-sdk/streaming-vs-single-mode) to send Claude a completely new instruction

236 

237<Tabs>

238 <Tab title="Approve">

239 The user approves the action as-is. Pass through the `input` from your callback unchanged and the tool executes exactly as Claude requested.

240 

241 <CodeGroup>

242 ```python Python theme={null}

243 async def can_use_tool(tool_name, input_data, context):

244 print(f"Claude wants to use {tool_name}")

245 approved = await ask_user("Allow this action?")

246 

247 if approved:

248 return PermissionResultAllow(updated_input=input_data)

249 return PermissionResultDeny(message="User declined")

250 ```

251 

252 ```typescript TypeScript theme={null}

253 canUseTool: async (toolName, input) => {

254 console.log(`Claude wants to use ${toolName}`);

255 const approved = await askUser("Allow this action?");

256 

257 if (approved) {

258 return { behavior: "allow", updatedInput: input };

259 }

260 return { behavior: "deny", message: "User declined" };

261 };

262 ```

263 </CodeGroup>

264 </Tab>

265 

266 <Tab title="Approve with changes">

267 The user approves but wants to modify the request first. You can change the input before the tool executes. Claude sees the result but isn't told you changed anything. Useful for sanitizing parameters, adding constraints, or scoping access.

268 

269 <CodeGroup>

270 ```python Python theme={null}

271 async def can_use_tool(tool_name, input_data, context):

272 if tool_name == "Bash":

273 # User approved, but scope all commands to sandbox

274 sandboxed_input = {**input_data}

275 sandboxed_input["command"] = input_data["command"].replace(

276 "/tmp", "/tmp/sandbox"

277 )

278 return PermissionResultAllow(updated_input=sandboxed_input)

279 return PermissionResultAllow(updated_input=input_data)

280 ```

281 

282 ```typescript TypeScript theme={null}

283 canUseTool: async (toolName, input) => {

284 if (toolName === "Bash") {

285 // User approved, but scope all commands to sandbox

286 const sandboxedInput = {

287 ...input,

288 command: input.command.replace("/tmp", "/tmp/sandbox")

289 };

290 return { behavior: "allow", updatedInput: sandboxedInput };

291 }

292 return { behavior: "allow", updatedInput: input };

293 };

294 ```

295 </CodeGroup>

296 </Tab>

297 

298 <Tab title="Reject">

299 The user doesn't want this action to happen. Block the tool and provide a message explaining why. Claude sees this message and may try a different approach.

300 

301 <CodeGroup>

302 ```python Python theme={null}

303 async def can_use_tool(tool_name, input_data, context):

304 approved = await ask_user(f"Allow {tool_name}?")

305 

306 if not approved:

307 return PermissionResultDeny(message="User rejected this action")

308 return PermissionResultAllow(updated_input=input_data)

309 ```

310 

311 ```typescript TypeScript theme={null}

312 canUseTool: async (toolName, input) => {

313 const approved = await askUser(`Allow ${toolName}?`);

314 

315 if (!approved) {

316 return {

317 behavior: "deny",

318 message: "User rejected this action"

319 };

320 }

321 return { behavior: "allow", updatedInput: input };

322 };

323 ```

324 </CodeGroup>

325 </Tab>

326 

327 <Tab title="Suggest alternative">

328 The user doesn't want this specific action, but has a different idea. Block the tool and include guidance in your message. Claude will read this and decide how to proceed based on your feedback.

329 

330 <CodeGroup>

331 ```python Python theme={null}

332 async def can_use_tool(tool_name, input_data, context):

333 if tool_name == "Bash" and "rm" in input_data.get("command", ""):

334 # User doesn't want to delete, suggest archiving instead

335 return PermissionResultDeny(

336 message="User doesn't want to delete files. They asked if you could compress them into an archive instead."

337 )

338 return PermissionResultAllow(updated_input=input_data)

339 ```

340 

341 ```typescript TypeScript theme={null}

342 canUseTool: async (toolName, input) => {

343 if (toolName === "Bash" && input.command.includes("rm")) {

344 // User doesn't want to delete, suggest archiving instead

345 return {

346 behavior: "deny",

347 message:

348 "User doesn't want to delete files. They asked if you could compress them into an archive instead."

349 };

350 }

351 return { behavior: "allow", updatedInput: input };

352 };

353 ```

354 </CodeGroup>

355 </Tab>

356 

357 <Tab title="Redirect entirely">

358 For a complete change of direction (not just a nudge), use [streaming input](/en/agent-sdk/streaming-vs-single-mode) to send Claude a new instruction directly. This bypasses the current tool request and gives Claude entirely new instructions to follow.

359 </Tab>

360</Tabs>

361 

362## Handle clarifying questions

363 

364When Claude needs more direction on a task with multiple valid approaches, it calls the `AskUserQuestion` tool. This triggers your `canUseTool` callback with `toolName` set to `AskUserQuestion`. The input contains Claude's questions as multiple-choice options, which you display to the user and return their selections.

365 

366<Tip>

367 Clarifying questions are especially common in [`plan` mode](/en/agent-sdk/permissions#plan-mode-plan), where Claude explores the codebase and asks questions before proposing a plan. This makes plan mode ideal for interactive workflows where you want Claude to gather requirements before making changes.

368</Tip>

369 

370The following steps show how to handle clarifying questions:

371 

372<Steps>

373 <Step title="Pass a canUseTool callback">

374 Pass a `canUseTool` callback in your query options. By default, `AskUserQuestion` is available. If you specify a `tools` array to restrict Claude's capabilities (for example, a read-only agent with only `Read`, `Glob`, and `Grep`), include `AskUserQuestion` in that array. Otherwise, Claude won't be able to ask clarifying questions:

375 

376 <CodeGroup>

377 ```python Python theme={null}

378 async for message in query(

379 prompt="Analyze this codebase",

380 options=ClaudeAgentOptions(

381 # Include AskUserQuestion in your tools list

382 tools=["Read", "Glob", "Grep", "AskUserQuestion"],

383 can_use_tool=can_use_tool,

384 ),

385 ):

386 print(message)

387 ```

388 

389 ```typescript TypeScript theme={null}

390 for await (const message of query({

391 prompt: "Analyze this codebase",

392 options: {

393 // Include AskUserQuestion in your tools list

394 tools: ["Read", "Glob", "Grep", "AskUserQuestion"],

395 canUseTool: async (toolName, input) => {

396 // Handle clarifying questions here

397 }

398 }

399 })) {

400 console.log(message);

401 }

402 ```

403 </CodeGroup>

404 </Step>

405 

406 <Step title="Detect AskUserQuestion">

407 In your callback, check if `toolName` equals `AskUserQuestion` to handle it differently from other tools:

408 

409 <CodeGroup>

410 ```python Python theme={null}

411 async def can_use_tool(tool_name: str, input_data: dict, context):

412 if tool_name == "AskUserQuestion":

413 # Your implementation to collect answers from the user

414 return await handle_clarifying_questions(input_data)

415 # Handle other tools normally

416 return await prompt_for_approval(tool_name, input_data)

417 ```

418 

419 ```typescript TypeScript theme={null}

420 canUseTool: async (toolName, input) => {

421 if (toolName === "AskUserQuestion") {

422 // Your implementation to collect answers from the user

423 return handleClarifyingQuestions(input);

424 }

425 // Handle other tools normally

426 return promptForApproval(toolName, input);

427 };

428 ```

429 </CodeGroup>

430 </Step>

431 

432 <Step title="Parse the question input">

433 The input contains Claude's questions in a `questions` array. Each question has a `question` (the text to display), `options` (the choices), and `multiSelect` (whether multiple selections are allowed):

434 

435 ```json theme={null}

436 {

437 "questions": [

438 {

439 "question": "How should I format the output?",

440 "header": "Format",

441 "options": [

442 { "label": "Summary", "description": "Brief overview" },

443 { "label": "Detailed", "description": "Full explanation" }

444 ],

445 "multiSelect": false

446 },

447 {

448 "question": "Which sections should I include?",

449 "header": "Sections",

450 "options": [

451 { "label": "Introduction", "description": "Opening context" },

452 { "label": "Conclusion", "description": "Final summary" }

453 ],

454 "multiSelect": true

455 }

456 ]

457 }

458 ```

459 

460 See [Question format](#question-format) for full field descriptions.

461 </Step>

462 

463 <Step title="Collect answers from the user">

464 Present the questions to the user and collect their selections. How you do this depends on your application: a terminal prompt, a web form, a mobile dialog, etc.

465 </Step>

466 

467 <Step title="Return answers to Claude">

468 Build the `answers` object as a record where each key is the `question` text and each value is the selected option's `label`:

469 

470 | From the question object | Use as |

471 | ------------------------------------------------------------ | ------ |

472 | `question` field (e.g., `"How should I format the output?"`) | Key |

473 | Selected option's `label` field (e.g., `"Summary"`) | Value |

474 

475 For multi-select questions, join multiple labels with `", "`. If you [support free-text input](#support-free-text-input), use the user's custom text as the value.

476 

477 <CodeGroup>

478 ```python Python theme={null}

479 return PermissionResultAllow(

480 updated_input={

481 "questions": input_data.get("questions", []),

482 "answers": {

483 "How should I format the output?": "Summary",

484 "Which sections should I include?": "Introduction, Conclusion",

485 },

486 }

487 )

488 ```

489 

490 ```typescript TypeScript theme={null}

491 return {

492 behavior: "allow",

493 updatedInput: {

494 questions: input.questions,

495 answers: {

496 "How should I format the output?": "Summary",

497 "Which sections should I include?": "Introduction, Conclusion"

498 }

499 }

500 };

501 ```

502 </CodeGroup>

503 </Step>

504</Steps>

505 

506### Question format

507 

508The input contains Claude's generated questions in a `questions` array. Each question has these fields:

509 

510| Field | Description |

511| ------------- | --------------------------------------------------------------------------------------------------------------------------------------- |

512| `question` | The full question text to display |

513| `header` | Short label for the question (max 12 characters) |

514| `options` | Array of 2-4 choices, each with `label` and `description`. TypeScript: optionally `preview` (see [below](#option-previews-type-script)) |

515| `multiSelect` | If `true`, users can select multiple options |

516 

517The structure your callback receives:

518 

519```json theme={null}

520{

521 "questions": [

522 {

523 "question": "How should I format the output?",

524 "header": "Format",

525 "options": [

526 { "label": "Summary", "description": "Brief overview of key points" },

527 { "label": "Detailed", "description": "Full explanation with examples" }

528 ],

529 "multiSelect": false

530 }

531 ]

532}

533```

534 

535#### Option previews (TypeScript)

536 

537`toolConfig.askUserQuestion.previewFormat` adds a `preview` field to each option so your app can show a visual mockup alongside the label. Without this setting, Claude does not generate previews and the field is absent.

538 

539| `previewFormat` | `preview` contains |

540| :-------------- | :------------------------------------------------------------------------------------------------------------ |

541| unset (default) | Field is absent. Claude does not generate previews. |

542| `"markdown"` | ASCII art and fenced code blocks |

543| `"html"` | A styled `<div>` fragment (the SDK rejects `<script>`, `<style>`, and `<!DOCTYPE>` before your callback runs) |

544 

545The format applies to all questions in the session. Claude includes `preview` on options where a visual comparison helps (layout choices, color schemes) and omits it where one wouldn't (yes/no confirmations, text-only choices). Check for `undefined` before rendering.

546 

547```typescript theme={null}

548import { query } from "@anthropic-ai/claude-agent-sdk";

549 

550for await (const message of query({

551 prompt: "Help me choose a card layout",

552 options: {

553 toolConfig: {

554 askUserQuestion: { previewFormat: "html" }

555 },

556 canUseTool: async (toolName, input) => {

557 // input.questions[].options[].preview is an HTML string or undefined

558 return { behavior: "allow", updatedInput: input };

559 }

560 }

561})) {

562 // ...

563}

564```

565 

566An option with an HTML preview:

567 

568```json theme={null}

569{

570 "label": "Compact",

571 "description": "Title and metric value only",

572 "preview": "<div style=\"padding:12px;border:1px solid #ddd;border-radius:8px\"><div style=\"font-size:12px;color:#666\">Active users</div><div style=\"font-size:28px;font-weight:600\">1,284</div></div>"

573}

574```

575 

576### Response format

577 

578Return an `answers` object mapping each question's `question` field to the selected option's `label`:

579 

580| Field | Description |

581| ----------- | ------------------------------------------------------------------------ |

582| `questions` | Pass through the original questions array (required for tool processing) |

583| `answers` | Object where keys are question text and values are selected labels |

584 

585For multi-select questions, join multiple labels with `", "`. For free-text input, use the user's custom text directly.

586 

587```json theme={null}

588{

589 "questions": [

590 // ...

591 ],

592 "answers": {

593 "How should I format the output?": "Summary",

594 "Which sections should I include?": "Introduction, Conclusion"

595 }

596}

597```

598 

599#### Support free-text input

600 

601Claude's predefined options won't always cover what users want. To let users type their own answer:

602 

603* Display an additional "Other" choice after Claude's options that accepts text input

604* Use the user's custom text as the answer value (not the word "Other")

605 

606See the [complete example](#complete-example) below for a full implementation.

607 

608### Complete example

609 

610Claude asks clarifying questions when it needs user input to proceed. For example, when asked to help decide on a tech stack for a mobile app, Claude might ask about cross-platform vs native, backend preferences, or target platforms. These questions help Claude make decisions that match the user's preferences rather than guessing.

611 

612This example handles those questions in a terminal application. Here's what happens at each step:

613 

6141. **Route the request**: The `canUseTool` callback checks if the tool name is `"AskUserQuestion"` and routes to a dedicated handler

6152. **Display questions**: The handler loops through the `questions` array and prints each question with numbered options

6163. **Collect input**: The user can enter a number to select an option, or type free text directly (e.g., "jquery", "i don't know")

6174. **Map answers**: The code checks if input is numeric (uses the option's label) or free text (uses the text directly)

6185. **Return to Claude**: The response includes both the original `questions` array and the `answers` mapping

619 

620<CodeGroup>

621 ```python Python theme={null}

622 import asyncio

623 

624 from claude_agent_sdk import ClaudeAgentOptions, ResultMessage, query

625 from claude_agent_sdk.types import HookMatcher, PermissionResultAllow

626 

627 

628 def parse_response(response: str, options: list) -> str:

629 """Parse user input as option number(s) or free text."""

630 try:

631 indices = [int(s.strip()) - 1 for s in response.split(",")]

632 labels = [options[i]["label"] for i in indices if 0 <= i < len(options)]

633 return ", ".join(labels) if labels else response

634 except ValueError:

635 return response

636 

637 

638 async def handle_ask_user_question(input_data: dict) -> PermissionResultAllow:

639 """Display Claude's questions and collect user answers."""

640 answers = {}

641 

642 for q in input_data.get("questions", []):

643 print(f"\n{q['header']}: {q['question']}")

644 

645 options = q["options"]

646 for i, opt in enumerate(options):

647 print(f" {i + 1}. {opt['label']} - {opt['description']}")

648 if q.get("multiSelect"):

649 print(" (Enter numbers separated by commas, or type your own answer)")

650 else:

651 print(" (Enter a number, or type your own answer)")

652 

653 response = input("Your choice: ").strip()

654 answers[q["question"]] = parse_response(response, options)

655 

656 return PermissionResultAllow(

657 updated_input={

658 "questions": input_data.get("questions", []),

659 "answers": answers,

660 }

661 )

662 

663 

664 async def can_use_tool(

665 tool_name: str, input_data: dict, context

666 ) -> PermissionResultAllow:

667 # Route AskUserQuestion to our question handler

668 if tool_name == "AskUserQuestion":

669 return await handle_ask_user_question(input_data)

670 # Auto-approve other tools for this example

671 return PermissionResultAllow(updated_input=input_data)

672 

673 

674 async def prompt_stream():

675 yield {

676 "type": "user",

677 "message": {

678 "role": "user",

679 "content": "Help me decide on the tech stack for a new mobile app",

680 },

681 }

682 

683 

684 # Required workaround: dummy hook keeps the stream open for can_use_tool

685 async def dummy_hook(input_data, tool_use_id, context):

686 return {"continue_": True}

687 

688 

689 async def main():

690 async for message in query(

691 prompt=prompt_stream(),

692 options=ClaudeAgentOptions(

693 can_use_tool=can_use_tool,

694 hooks={"PreToolUse": [HookMatcher(matcher=None, hooks=[dummy_hook])]},

695 ),

696 ):

697 if isinstance(message, ResultMessage) and message.subtype == "success":

698 print(message.result)

699 

700 

701 asyncio.run(main())

702 ```

703 

704 ```typescript TypeScript theme={null}

705 import { query } from "@anthropic-ai/claude-agent-sdk";

706 import * as readline from "readline/promises";

707 

708 // Helper to prompt user for input in the terminal

709 async function prompt(question: string): Promise<string> {

710 const rl = readline.createInterface({ input: process.stdin, output: process.stdout });

711 const answer = await rl.question(question);

712 rl.close();

713 return answer;

714 }

715 

716 // Parse user input as option number(s) or free text

717 function parseResponse(response: string, options: any[]): string {

718 const indices = response.split(",").map((s) => parseInt(s.trim()) - 1);

719 const labels = indices

720 .filter((i) => !isNaN(i) && i >= 0 && i < options.length)

721 .map((i) => options[i].label);

722 return labels.length > 0 ? labels.join(", ") : response;

723 }

724 

725 // Display Claude's questions and collect user answers

726 async function handleAskUserQuestion(input: any) {

727 const answers: Record<string, string> = {};

728 

729 for (const q of input.questions) {

730 console.log(`\n${q.header}: ${q.question}`);

731 

732 const options = q.options;

733 options.forEach((opt: any, i: number) => {

734 console.log(` ${i + 1}. ${opt.label} - ${opt.description}`);

735 });

736 if (q.multiSelect) {

737 console.log(" (Enter numbers separated by commas, or type your own answer)");

738 } else {

739 console.log(" (Enter a number, or type your own answer)");

740 }

741 

742 const response = (await prompt("Your choice: ")).trim();

743 answers[q.question] = parseResponse(response, options);

744 }

745 

746 // Return the answers to Claude (must include original questions)

747 return {

748 behavior: "allow",

749 updatedInput: { questions: input.questions, answers }

750 };

751 }

752 

753 async function main() {

754 for await (const message of query({

755 prompt: "Help me decide on the tech stack for a new mobile app",

756 options: {

757 canUseTool: async (toolName, input) => {

758 // Route AskUserQuestion to our question handler

759 if (toolName === "AskUserQuestion") {

760 return handleAskUserQuestion(input);

761 }

762 // Auto-approve other tools for this example

763 return { behavior: "allow", updatedInput: input };

764 }

765 }

766 })) {

767 if ("result" in message) console.log(message.result);

768 }

769 }

770 

771 main();

772 ```

773</CodeGroup>

774 

775## Limitations

776 

777* **Subagents**: `AskUserQuestion` is not currently available in subagents spawned via the Agent tool

778* **Question limits**: each `AskUserQuestion` call supports 1-4 questions with 2-4 options each

779 

780## Other ways to get user input

781 

782The `canUseTool` callback and `AskUserQuestion` tool cover most approval and clarification scenarios, but the SDK offers other ways to get input from users:

783 

784### Streaming input

785 

786Use [streaming input](/en/agent-sdk/streaming-vs-single-mode) when you need to:

787 

788* **Interrupt the agent mid-task**: send a cancel signal or change direction while Claude is working

789* **Provide additional context**: add information Claude needs without waiting for it to ask

790* **Build chat interfaces**: let users send follow-up messages during long-running operations

791 

792Streaming input is ideal for conversational UIs where users interact with the agent throughout execution, not just at approval checkpoints.

793 

794### Custom tools

795 

796Use [custom tools](/en/agent-sdk/custom-tools) when you need to:

797 

798* **Collect structured input**: build forms, wizards, or multi-step workflows that go beyond `AskUserQuestion`'s multiple-choice format

799* **Integrate external approval systems**: connect to existing ticketing, workflow, or approval platforms

800* **Implement domain-specific interactions**: create tools tailored to your application's needs, like code review interfaces or deployment checklists

801 

802Custom tools give you full control over the interaction, but require more implementation work than using the built-in `canUseTool` callback.

803 

804## Related resources

805 

806* [Configure permissions](/en/agent-sdk/permissions): set up permission modes and rules

807* [Control execution with hooks](/en/agent-sdk/hooks): run custom code at key points in the agent lifecycle

808* [TypeScript SDK reference](/en/agent-sdk/typescript#can-use-tool): full canUseTool API documentation

Details

265}265}

266```266```

267 267 

268## Use the Mantle endpoint

269 

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

271 

272<Note>

273 Mantle requires Claude Code v2.1.94 or later. Run `claude --version` to check.

274</Note>

275 

276### Enable Mantle

277 

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

279 

280```bash theme={null}

281export CLAUDE_CODE_USE_MANTLE=1

282export AWS_REGION=us-east-1

283```

284 

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

286 

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

288 

289### Select a Mantle model

290 

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

292 

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

294 

295```bash theme={null}

296claude --model anthropic.claude-haiku-4-5

297```

298 

299### Run Mantle alongside the Invoke API

300 

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

302 

303```bash theme={null}

304export CLAUDE_CODE_USE_BEDROCK=1

305export CLAUDE_CODE_USE_MANTLE=1

306```

307 

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

309 

310```json theme={null}

311{

312 "availableModels": ["opus", "sonnet", "haiku", "anthropic.claude-haiku-4-5"]

313}

314```

315 

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

317 

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

319 

320### Route Mantle through a gateway

321 

322If your organization routes model traffic through a centralized [LLM gateway](/en/llm-gateway) that injects AWS credentials server-side, disable client-side authentication so Claude Code sends requests without SigV4 signatures or `x-api-key` headers:

323 

324```bash theme={null}

325export CLAUDE_CODE_USE_MANTLE=1

326export CLAUDE_CODE_SKIP_MANTLE_AUTH=1

327export ANTHROPIC_BEDROCK_MANTLE_BASE_URL=https://your-gateway.example.com

328```

329 

330### Mantle environment variables

331 

332These variables are specific to the Mantle endpoint. See [Environment variables](/en/env-vars) for the full list.

333 

334| Variable | Purpose |

335| :---------------------------------- | :------------------------------------------------ |

336| `CLAUDE_CODE_USE_MANTLE` | Enable the Mantle endpoint. Set to `1` or `true`. |

337| `ANTHROPIC_BEDROCK_MANTLE_BASE_URL` | Override the default Mantle endpoint URL |

338| `CLAUDE_CODE_SKIP_MANTLE_AUTH` | Skip client-side authentication for proxy setups |

339 

268## Troubleshooting340## Troubleshooting

269 341 

270### Authentication loop with SSO and corporate proxies342### Authentication loop with SSO and corporate proxies


287 359 

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

289 361 

362### Mantle endpoint errors

363 

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

365 

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

367 

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

369 

290## Additional resources370## Additional resources

291 371 

292* [Bedrock documentation](https://docs.aws.amazon.com/bedrock/)372* [Bedrock documentation](https://docs.aws.amazon.com/bedrock/)

Details

14 14 

15If the browser doesn't open automatically, press `c` to copy the login URL to your clipboard, then paste it into your browser.15If the browser doesn't open automatically, press `c` to copy the login URL to your clipboard, then paste it into your browser.

16 16 

17If your browser shows a login code instead of redirecting back after you sign in, paste it into the terminal at the `Paste code here if prompted` prompt.

18 

17You can authenticate with any of these account types:19You can authenticate with any of these account types:

18 20 

19* **Claude Pro or Max subscription**: log in with your Claude.ai account. Subscribe at [claude.com/pricing](https://claude.com/pricing?utm_source=claude_code\&utm_medium=docs\&utm_content=authentication_pro_max).21* **Claude Pro or Max subscription**: log in with your Claude.ai account. Subscribe at [claude.com/pricing](https://claude.com/pricing?utm_source=claude_code\&utm_medium=docs\&utm_content=authentication_pro_max).

Details

2> Fetch the complete documentation index at: https://code.claude.com/docs/llms.txt2> 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.3> Use this file to discover all available pages before exploring further.

4 4 

5# Claude Code on the web5# Use Claude Code on the web

6 6 

7> Run Claude Code tasks asynchronously on secure cloud infrastructure7> Configure cloud environments, setup scripts, network access, and Docker in Anthropic's sandbox. Move sessions between web and terminal with `--remote` and `--teleport`.

8 8 

9<Note>9<Note>

10 Claude Code on the web is currently in research preview.10 Claude Code on the web is in research preview for Pro, Max, and Team users, and for Enterprise users with premium seats or Chat + Claude Code seats.

11</Note>11</Note>

12 12 

13## What is Claude Code on the web?13Claude Code on the web runs tasks on Anthropic-managed cloud infrastructure at [claude.ai/code](https://claude.ai/code). Sessions persist even if you close your browser, and you can monitor them from the Claude mobile app.

14 14 

15Claude Code on the web lets developers kick off Claude Code from the Claude app. This is perfect for:15<Tip>

16 16 New to Claude Code on the web? Start with [Get started](/en/web-quickstart) to connect your GitHub account and submit your first task.

17* **Answering questions**: Ask about code architecture and how features are implemented17</Tip>

18* **Bug fixes and routine tasks**: Well-defined tasks that don't require frequent steering

19* **Parallel work**: Tackle multiple bug fixes in parallel

20* **Repositories not on your local machine**: Work on code you don't have checked out locally

21* **Backend changes**: Where Claude Code can write tests and then write code to pass those tests

22 

23Claude Code is also available on the Claude app for [iOS](https://apps.apple.com/us/app/claude-by-anthropic/id6473753684) and [Android](https://play.google.com/store/apps/details?id=com.anthropic.claude) for kicking off tasks on the go and monitoring work in progress.

24 

25You can [kick off new tasks on the web from your terminal](#from-terminal-to-web) with `--remote`, or [teleport web sessions back to your terminal](#from-web-to-terminal) to continue locally. To use the web interface while running Claude Code on your own machine instead of cloud infrastructure, see [Remote Control](/en/remote-control).

26 

27## Who can use Claude Code on the web?

28 

29Claude Code on the web is available in research preview to:

30 

31* **Pro users**

32* **Max users**

33* **Team users**

34* **Enterprise users** with premium seats or Chat + Claude Code seats

35 18 

36## Getting started19This page covers:

37 20 

38Set up Claude Code on the web from the browser or from your terminal.21* [GitHub authentication options](#github-authentication-options): two ways to connect GitHub

22* [The cloud environment](#the-cloud-environment): what config carries over, what tools are installed, and how to configure environments

23* [Setup scripts](#setup-scripts) and dependency management

24* [Network access](#network-access): levels, proxies, and the default allowlist

25* [Move tasks between web and terminal](#move-tasks-between-web-and-terminal) with `--remote` and `--teleport`

26* [Work with sessions](#work-with-sessions): reviewing, sharing, archiving, deleting

27* [Auto-fix pull requests](#auto-fix-pull-requests): respond automatically to CI failures and review comments

28* [Security and isolation](#security-and-isolation): how sessions are isolated

29* [Limitations](#limitations): rate limits and platform restrictions

39 30 

40### From the browser31## GitHub authentication options

41 32 

421. Visit [claude.ai/code](https://claude.ai/code)33Cloud sessions need access to your GitHub repositories to clone code and push branches. You can grant access in two ways:

432. Connect your GitHub account

443. Install the Claude GitHub App in your repositories

454. Select your default environment

465. Submit your coding task

476. Review changes in diff view, iterate with comments, then create a pull request

48 34 

49### From the terminal35| Method | How it works | Best for |

36| :--------------- | :------------------------------------------------------------------------------------------------------------------------------------------- | :---------------------------------------------- |

37| **GitHub App** | Install the Claude GitHub App on specific repositories during [web onboarding](/en/web-quickstart). Access is scoped per repository. | Teams that want explicit per-repo authorization |

38| **`/web-setup`** | Run `/web-setup` in your terminal to sync your local `gh` CLI token to your Claude account. Access matches whatever your `gh` token can see. | Individual developers who already use `gh` |

50 39 

51Run `/web-setup` inside Claude Code to connect GitHub using your local `gh` CLI credentials. The command syncs your `gh auth token` to Claude Code on the web, creates a default cloud environment, and opens claude.ai/code in your browser when it finishes.40Either method works. [`/schedule`](/en/web-scheduled-tasks) checks for either form of access and prompts you to run `/web-setup` if neither is configured. See [Connect from your terminal](/en/web-quickstart#connect-from-your-terminal) for the `/web-setup` walkthrough.

52 41 

53This path requires the `gh` CLI to be installed and authenticated with `gh auth login`. If `gh` is not available, `/web-setup` opens claude.ai/code so you can connect GitHub from the browser instead.42The GitHub App is required for [Auto-fix](#auto-fix-pull-requests), which uses the App to receive PR webhooks. If you connect with `/web-setup` and later want Auto-fix, install the App on those repositories.

54 43 

55Your `gh` credentials give Claude access to clone and push, so you can skip the GitHub App for basic sessions. Install the App later if you want [Auto-fix](#auto-fix-pull-requests), which uses the App to receive PR webhooks.44Team and Enterprise admins can disable `/web-setup` with the Quick web setup toggle at [claude.ai/admin-settings/claude-code](https://claude.ai/admin-settings/claude-code).

56 45 

57<Note>46<Note>

58 Team and Enterprise admins can disable terminal setup with the Quick web setup toggle at [claude.ai/admin-settings/claude-code](https://claude.ai/admin-settings/claude-code).47 Organizations with [Zero Data Retention](/en/zero-data-retention) enabled cannot use `/web-setup` or other cloud session features.

59</Note>48</Note>

60 49 

61## How it works50## The cloud environment

62 

63When you start a task on Claude Code on the web:

64 51 

651. **Repository cloning**: Your repository is cloned to an Anthropic-managed virtual machine52Each session runs in a fresh Anthropic-managed VM with your repository cloned. This section covers what's available when a session starts and how to customize it.

662. **Environment setup**: Claude prepares a secure cloud environment with your code, then runs your [setup script](#setup-scripts) if configured

673. **Network configuration**: Internet access is configured based on your settings

684. **Task execution**: Claude analyzes code, makes changes, runs tests, and checks its work

695. **Completion**: You're notified when finished and can create a PR with the changes

706. **Results**: Changes are pushed to a branch, ready for pull request creation

71 53 

72## Review changes with diff view54### What's available in cloud sessions

73 55 

74Diff view lets you see exactly what Claude changed before creating a pull request. Instead of clicking "Create PR" to review changes in GitHub, view the diff directly in the app and iterate with Claude until the changes are ready.56Cloud sessions start from a fresh clone of your repository. Anything committed to the repo is available. Anything you've installed or configured only on your own machine is not.

75 57 

76When Claude makes changes to files, a diff stats indicator appears showing the number of lines added and removed (for example, `+12 -1`). Select this indicator to open the diff viewer, which displays a file list on the left and the changes for each file on the right.58| | Available in cloud sessions | Why |

59| :-------------------------------------------------------------------- | :-------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------- |

60| Your repo's `CLAUDE.md` | Yes | Part of the clone |

61| Your repo's `.claude/settings.json` hooks | Yes | Part of the clone |

62| Your repo's `.mcp.json` MCP servers | Yes | Part of the clone |

63| Your repo's `.claude/rules/` | Yes | Part of the clone |

64| Your repo's `.claude/skills/`, `.claude/agents/`, `.claude/commands/` | Yes | Part of the clone |

65| Plugins declared in `.claude/settings.json` | Yes | Installed at session start from the [marketplace](/en/plugin-marketplaces) you declared. Requires network access to reach the marketplace source |

66| Your user `~/.claude/CLAUDE.md` | No | Lives on your machine, not in the repo |

67| Plugins enabled only in your user settings | No | User-scoped `enabledPlugins` lives in `~/.claude/settings.json`. Declare them in the repo's `.claude/settings.json` instead |

68| MCP servers you added with `claude mcp add` | No | Those write to your local user config, not the repo. Declare the server in [`.mcp.json`](/en/mcp#project-scope) instead |

69| Static API tokens and credentials | No | No dedicated secrets store exists yet. See below |

70| Interactive auth like AWS SSO | No | Not supported. SSO requires browser-based login that can't run in a cloud session |

77 71 

78From the diff view, you can:72To make configuration available in cloud sessions, commit it to the repo. A dedicated secrets store is not yet available. Both environment variables and setup scripts are stored in the environment configuration, visible to anyone who can edit that environment. If you need secrets in a cloud session, add them as environment variables with that visibility in mind.

79 73 

80* Review changes file by file74### Installed tools

81* Comment on specific changes to request modifications

82* Continue iterating with Claude based on what you see

83 75 

84This lets you refine changes through multiple rounds of feedback without creating draft PRs or switching to GitHub.76Cloud sessions come with common language runtimes, build tools, and databases pre-installed. The table below summarizes what's included by category.

85 77 

86## Auto-fix pull requests78| Category | Included |

79| :------------ | :--------------------------------------------------------------------------------- |

80| **Python** | Python 3.x with pip, poetry, uv, black, mypy, pytest, ruff |

81| **Node.js** | 20, 21, and 22 via nvm, with npm, yarn, pnpm, bun¹, eslint, prettier, chromedriver |

82| **Ruby** | 3.1, 3.2, 3.3 with gem, bundler, rbenv |

83| **PHP** | 8.4 with Composer |

84| **Java** | OpenJDK 21 with Maven and Gradle |

85| **Go** | latest stable with module support |

86| **Rust** | rustc and cargo |

87| **C/C++** | GCC, Clang, cmake, ninja, conan |

88| **Docker** | docker, dockerd, docker compose |

89| **Databases** | PostgreSQL 16, Redis 7.0 |

90| **Utilities** | git, jq, yq, ripgrep, tmux, vim, nano |

87 91 

88Claude can watch a pull request and automatically respond to CI failures and review comments. Claude subscribes to GitHub activity on the PR, and when a check fails or a reviewer leaves a comment, Claude investigates and pushes a fix if one is clear.92¹ Bun is installed but has known [proxy compatibility issues](#install-dependencies-with-a-sessionstart-hook) for package fetching.

89 

90<Note>

91 Auto-fix requires the Claude GitHub App to be installed on your repository. If you haven't already, install it from the [GitHub App page](https://github.com/apps/claude) or when prompted during [setup](#getting-started).

92</Note>

93 93 

94There are a few ways to turn on auto-fix depending on where the PR came from and what device you're using:94For exact versions, ask Claude to run `check-tools` in a cloud session. This command only exists in cloud sessions.

95 95 

96* **PRs created in Claude Code on the web**: open the CI status bar and select **Auto-fix**96### Work with GitHub issues and pull requests

97* **From the mobile app**: tell Claude to auto-fix the PR, for example "watch this PR and fix any CI failures or review comments"

98* **Any existing PR**: paste the PR URL into a session and tell Claude to auto-fix it

99 97 

100### How Claude responds to PR activity98Cloud sessions include built-in GitHub tools that let Claude read issues, list pull requests, fetch diffs, and post comments without any setup. These tools authenticate through the [GitHub proxy](#github-proxy) using whichever method you configured under [GitHub authentication options](#github-authentication-options), so your token never enters the container.

101 99 

102When auto-fix is active, Claude receives GitHub events for the PR including new review comments and CI check failures. For each event, Claude investigates and decides how to proceed:100The `gh` CLI is not pre-installed. If you need a `gh` command the built-in tools don't cover, like `gh release` or `gh workflow run`, install and authenticate it yourself:

103 101 

104* **Clear fixes**: if Claude is confident in a fix and it doesn't conflict with earlier instructions, Claude makes the change, pushes it, and explains what was done in the session102<Steps>

105* **Ambiguous requests**: if a reviewer's comment could be interpreted multiple ways or involves something architecturally significant, Claude asks you before acting103 <Step title="Install gh in your setup script">

106* **Duplicate or no-action events**: if an event is a duplicate or requires no change, Claude notes it in the session and moves on104 Add `apt update && apt install -y gh` to your [setup script](#setup-scripts).

105 </Step>

107 106 

108Claude may reply to review comment threads on GitHub as part of resolving them. These replies are posted using your GitHub account, so they appear under your username, but each reply is labeled as coming from Claude Code so reviewers know it was written by the agent and not by you directly.107 <Step title="Provide a token">

108 Add a `GH_TOKEN` environment variable to your [environment settings](#configure-your-environment) with a GitHub personal access token. `gh` reads `GH_TOKEN` automatically, so no `gh auth login` step is needed.

109 </Step>

110</Steps>

109 111 

110<Warning>112### Run tests, start services, and add packages

111 If your repository uses comment-triggered automation such as Atlantis, Terraform Cloud, or custom GitHub Actions that run on `issue_comment` events, be aware that Claude can reply on your behalf, which can trigger those workflows. Review your repository's automation before enabling auto-fix, and consider disabling auto-fix for repositories where a PR comment can deploy infrastructure or run privileged operations.

112</Warning>

113 113 

114## Moving tasks between web and terminal114Claude runs tests as part of working on a task. Ask for it in your prompt, like "fix the failing tests in `tests/`" or "run pytest after each change." Test runners like pytest, jest, and cargo test work out of the box since they're pre-installed.

115 115 

116You can start new tasks on the web from your terminal, or pull web sessions into your terminal to continue locally. Web sessions persist even if you close your laptop, and you can monitor them from anywhere including the Claude mobile app.116PostgreSQL and Redis are pre-installed but not running by default. Start each one in a [setup script](#setup-scripts) or ask Claude to start it during the session:

117 117 

118<Note>118```bash theme={null}

119 Session handoff is one-way: you can pull web sessions into your terminal, but you can't push an existing terminal session to the web. The `--remote` flag creates a *new* web session for your current repository.119service postgresql start

120</Note>120```

121 

122### From terminal to web

123 

124Start a web session from the command line with the `--remote` flag:

125 121 

126```bash theme={null}122```bash theme={null}

127claude --remote "Fix the authentication bug in src/auth/login.ts"123service redis-server start

128```124```

129 125 

130This creates a new web session on claude.ai. The task runs in the cloud while you continue working locally. Use `/tasks` to check progress, or open the session on claude.ai or the Claude mobile app to interact directly. From there you can steer Claude, provide feedback, or answer questions just like any other conversation.126Docker is available for running containerized services. Network access to pull images follows your environment's [access level](#access-levels).

131 127 

132#### Tips for remote tasks128To add packages that aren't pre-installed, use a [setup script](#setup-scripts) so they're available at session start. You can also ask Claude to install packages during the session, but those installs don't persist across sessions.

133 129 

134**Plan locally, execute remotely**: For complex tasks, start Claude in plan mode to collaborate on the approach, then send work to the web:130### Resource limits

135 131 

136```bash theme={null}132Cloud sessions run with approximate resource ceilings that may change over time:

137claude --permission-mode plan

138```

139 133 

140In plan mode, Claude can only read files and explore the codebase. Once you're satisfied with the plan, start a remote session for autonomous execution:134* 4 vCPUs

135* 16 GB of RAM

136* 30 GB of disk

141 137 

142```bash theme={null}138Tasks requiring significantly more memory, such as large build jobs or memory-intensive tests, may fail or be terminated. For workloads beyond these limits, use [Remote Control](/en/remote-control) to run Claude Code on your own hardware.

143claude --remote "Execute the migration plan in docs/migration-plan.md"

144```

145 139 

146This pattern gives you control over the strategy while letting Claude execute autonomously in the cloud.140### Configure your environment

147 141 

148**Plan in the cloud with ultraplan**: To draft and review the plan itself in a web session, use [ultraplan](/en/ultraplan). Claude generates the plan on Claude Code on the web while you keep working, then you comment on sections in your browser and choose to execute remotely or send the plan back to your terminal.142Environments control [network access](#network-access), environment variables, and the [setup script](#setup-scripts) that runs before a session starts. See [Installed tools](#installed-tools) for what's available without any configuration. You can manage environments from the web interface or the terminal:

149 143 

150**Run tasks in parallel**: Each `--remote` command creates its own web session that runs independently. You can kick off multiple tasks and they'll all run simultaneously in separate sessions:144| Action | How |

145| :----------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |

146| Add an environment | Select the current environment to open the selector, then select **Add environment**. The dialog includes name, network access level, environment variables, and setup script. |

147| Edit an environment | Select the settings icon to the right of the environment name. |

148| Archive an environment | Open the environment for editing and select **Archive**. Archived environments are hidden from the selector but existing sessions keep running. |

149| Set the default for `--remote` | Run `/remote-env` in your terminal. If you have a single environment, this command shows your current configuration. `/remote-env` only selects the default; add, edit, and archive environments from the web interface. |

151 150 

152```bash theme={null}151Environment variables use `.env` format with one `KEY=value` pair per line. Don't wrap values in quotes, since quotes are stored as part of the value.

153claude --remote "Fix the flaky test in auth.spec.ts"152 

154claude --remote "Update the API documentation"153```text theme={null}

155claude --remote "Refactor the logger to use structured output"154NODE_ENV=development

155LOG_LEVEL=debug

156DATABASE_URL=postgres://localhost:5432/myapp

156```157```

157 158 

158Monitor all sessions with `/tasks`. When a session completes, you can create a PR from the web interface or [teleport](#from-web-to-terminal) the session to your terminal to continue working.159## Setup scripts

159 160 

160### From web to terminal161A setup script is a Bash script that runs when a new cloud session starts, before Claude Code launches. Use setup scripts to install dependencies, configure tools, or fetch anything the session needs that isn't pre-installed.

161 162 

162There are several ways to pull a web session into your terminal:163Scripts run as root on Ubuntu 24.04, so `apt install` and most language package managers work.

163 164 

164* **Using `/teleport`**: From within Claude Code, run `/teleport` (or `/tp`) to see an interactive picker of your web sessions. If you have uncommitted changes, you'll be prompted to stash them first.165To add a setup script, open the environment settings dialog and enter your script in the **Setup script** field.

165* **Using `--teleport`**: From the command line, run `claude --teleport` for an interactive session picker, or `claude --teleport <session-id>` to resume a specific session directly.

166* **From `/tasks`**: Run `/tasks` to see your background sessions, then press `t` to teleport into one

167* **From the web interface**: Click "Open in CLI" to copy a command you can paste into your terminal

168 166 

169When you teleport a session, Claude verifies you're in the correct repository, fetches and checks out the branch from the remote session, and loads the full conversation history into your terminal.167This example installs the `gh` CLI, which isn't pre-installed:

170 168 

171#### Requirements for teleporting169```bash theme={null}

170#!/bin/bash

171apt update && apt install -y gh

172```

172 173 

173Teleport checks these requirements before resuming a session. If any requirement isn't met, you'll see an error or be prompted to resolve the issue.174Setup scripts run only when creating a new session. They are skipped when resuming an existing session.

174 175 

175| Requirement | Details |176If the script exits non-zero, the session fails to start. Append `|| true` to non-critical commands to avoid blocking the session on an intermittent install failure.

176| ------------------ | ---------------------------------------------------------------------------------------------------------------------- |

177| Clean git state | Your working directory must have no uncommitted changes. Teleport prompts you to stash changes if needed. |

178| Correct repository | You must run `--teleport` from a checkout of the same repository, not a fork. |

179| Branch available | The branch from the web session must have been pushed to the remote. Teleport automatically fetches and checks it out. |

180| Same account | You must be authenticated to the same Claude.ai account used in the web session. |

181 177 

182### Sharing sessions178<Note>

179 Setup scripts that install packages need network access to reach registries. The default **Trusted** network access allows connections to [common package registries](#default-allowed-domains) including npm, PyPI, RubyGems, and crates.io. Scripts will fail to install packages if your environment uses **None** network access.

180</Note>

183 181 

184To share a session, toggle its visibility according to the account182### Setup scripts vs. SessionStart hooks

185types below. After that, share the session link as-is. Recipients who open your

186shared session will see the latest state of the session upon load, but the

187recipient's page will not update in real time.

188 183 

189#### Sharing from an Enterprise or Team account184Use a setup script to install things the cloud needs but your laptop already has, like a language runtime or CLI tool. Use a [SessionStart hook](/en/hooks#sessionstart) for project setup that should run everywhere, cloud and local, like `npm install`.

190 185 

191For Enterprise and Team accounts, the two visibility options are **Private**186Both run at the start of a session, but they belong to different places:

192and **Team**. Team visibility makes the session visible to other members of your

193Claude.ai organization. Repository access verification is enabled by default,

194based on the GitHub account connected to the recipient's account. Your account's

195display name is visible to all recipients with access. [Claude in Slack](/en/slack)

196sessions are automatically shared with Team visibility.

197 187 

198#### Sharing from a Max or Pro account188| | Setup scripts | SessionStart hooks |

189| ------------- | ------------------------------------------------- | -------------------------------------------------------------- |

190| Attached to | The cloud environment | Your repository |

191| Configured in | Cloud environment UI | `.claude/settings.json` in your repo |

192| Runs | Before Claude Code launches, on new sessions only | After Claude Code launches, on every session including resumed |

193| Scope | Cloud environments only | Both local and cloud |

199 194 

200For Max and Pro accounts, the two visibility options are **Private**195SessionStart hooks can also be defined in your user-level `~/.claude/settings.json` locally, but user-level settings don't carry over to cloud sessions. In the cloud, only hooks committed to the repo run.

201and **Public**. Public visibility makes the session visible to any user logged

202into claude.ai.

203 196 

204Check your session for sensitive content before sharing. Sessions may contain197### Install dependencies with a SessionStart hook

205code and credentials from private GitHub repositories. Repository access

206verification is not enabled by default.

207 198 

208Enable repository access verification and/or withhold your name from your shared199To install dependencies only in cloud sessions, add a SessionStart hook to your repo's `.claude/settings.json`:

209sessions by going to Settings > Claude Code > Sharing settings.

210 200 

211## Schedule recurring tasks201```json theme={null}

202{

203 "hooks": {

204 "SessionStart": [

205 {

206 "matcher": "startup|resume",

207 "hooks": [

208 {

209 "type": "command",

210 "command": "\"$CLAUDE_PROJECT_DIR\"/scripts/install_pkgs.sh"

211 }

212 ]

213 }

214 ]

215 }

216}

217```

212 218 

213Run Claude on a recurring schedule to automate work like daily PR reviews, dependency audits, and CI failure analysis. See [Schedule tasks on the web](/en/web-scheduled-tasks) for the full guide.219Create the script at `scripts/install_pkgs.sh` and make it executable with `chmod +x`. The `CLAUDE_CODE_REMOTE` environment variable is set to `true` in cloud sessions, so you can use it to skip local execution:

214 220 

215## Managing sessions221```bash theme={null}

222#!/bin/bash

216 223 

217### Archiving sessions224if [ "$CLAUDE_CODE_REMOTE" != "true" ]; then

225 exit 0

226fi

218 227 

219You can archive sessions to keep your session list organized. Archived sessions are hidden from the default session list but can be viewed by filtering for archived sessions.228npm install

229pip install -r requirements.txt

230exit 0

231```

220 232 

221To archive a session, hover over the session in the sidebar and click the archive icon.233SessionStart hooks have some limitations in cloud sessions:

222 234 

223### Deleting sessions235* **No cloud-only scoping**: hooks run in both local and cloud sessions. To skip local execution, check the `CLAUDE_CODE_REMOTE` environment variable as shown above.

236* **Requires network access**: install commands need to reach package registries. If your environment uses **None** network access, these hooks fail. The [default allowlist](#default-allowed-domains) under **Trusted** covers npm, PyPI, RubyGems, and crates.io.

237* **Proxy compatibility**: all outbound traffic passes through a [security proxy](#security-proxy). Some package managers don't work correctly with this proxy. Bun is a known example.

238* **Adds startup latency**: hooks run each time a session starts or resumes. Keep install scripts fast by checking whether dependencies are already present before reinstalling.

224 239 

225Deleting a session permanently removes the session and its data. This action cannot be undone. You can delete a session in two ways:240To persist environment variables for subsequent Bash commands, write to the file at `$CLAUDE_ENV_FILE`. See [SessionStart hooks](/en/hooks#sessionstart) for details.

226 241 

227* **From the sidebar**: Filter for archived sessions, then hover over the session you want to delete and click the delete icon242Custom environment images and snapshots are not yet supported.

228* **From the session menu**: Open a session, click the dropdown next to the session title, and select **Delete**

229 243 

230You will be asked to confirm before a session is deleted.244## Network access

245 

246Network access controls outbound connections from the cloud environment. Each environment specifies one access level, and you can extend it with custom allowed domains. The default is **Trusted**, which allows package registries and other [allowlisted domains](#default-allowed-domains).

231 247 

232## Cloud environment248### Access levels

233 249 

234### Default image250Choose an access level when you create or edit an environment:

235 251 

236We build and maintain a universal image with common toolchains and language ecosystems pre-installed. This image includes:252| Level | Outbound connections |

253| :---------- | :------------------------------------------------------------------------------------------- |

254| **None** | No outbound network access |

255| **Trusted** | [Allowlisted domains](#default-allowed-domains) only: package registries, GitHub, cloud SDKs |

256| **Full** | Any domain |

257| **Custom** | Your own allowlist, optionally including the defaults |

237 258 

238* Popular programming languages and runtimes259GitHub operations use a [separate proxy](#github-proxy) that is independent of this setting.

239* Common build tools and package managers

240* Testing frameworks and linters

241 260 

242#### Checking available tools261### Allow specific domains

243 262 

244To see what's pre-installed in your environment, ask Claude Code to run:263To allow domains that aren't in the Trusted list, select **Custom** in the environment's network access settings. An **Allowed domains** field appears. Enter one domain per line:

245 264 

246```bash theme={null}265```text theme={null}

247check-tools266api.example.com

267*.internal.example.com

268registry.example.com

248```269```

249 270 

250This command displays:271Use `*.` for wildcard subdomain matching. Check **Also include default list of common package managers** to keep the [Trusted domains](#default-allowed-domains) alongside your custom entries, or leave it unchecked to allow only what you list.

251 272 

252* Programming languages and their versions273### GitHub proxy

253* Available package managers

254* Installed development tools

255 274 

256#### Language-specific setups275For security, all GitHub operations go through a dedicated proxy service that transparently handles all git interactions. Inside the sandbox, the git client authenticates using a custom-built scoped credential. This proxy:

257 276 

258The universal image includes pre-configured environments for:277* Manages GitHub authentication securely: the git client uses a scoped credential inside the sandbox, which the proxy verifies and translates to your actual GitHub authentication token

278* Restricts git push operations to the current working branch for safety

279* Enables cloning, fetching, and PR operations while maintaining security boundaries

259 280 

260* **Python**: Python 3.x with pip, poetry, and common scientific libraries281### Security proxy

261* **Node.js**: Latest LTS versions with npm, yarn, pnpm, and bun

262* **Ruby**: Versions 3.1.6, 3.2.6, 3.3.6 (default: 3.3.6) with gem, bundler, and rbenv for version management

263* **PHP**: Version 8.4.14

264* **Java**: OpenJDK with Maven and Gradle

265* **Go**: Latest stable version with module support

266* **Rust**: Rust toolchain with cargo

267* **C++**: GCC and Clang compilers

268 282 

269#### Databases283Environments run behind an HTTP/HTTPS network proxy for security and abuse prevention purposes. All outbound internet traffic passes through this proxy, which provides:

270 284 

271The universal image includes the following databases:285* Protection against malicious requests

286* Rate limiting and abuse prevention

287* Content filtering for enhanced security

272 288 

273* **PostgreSQL**: Version 16289### Default allowed domains

274* **Redis**: Version 7.0

275 290 

276### Environment configuration291When using **Trusted** network access, the following domains are allowed by default. Domains marked with `*` indicate wildcard subdomain matching, so `*.gcr.io` allows any subdomain of `gcr.io`.

292 

293<AccordionGroup>

294 <Accordion title="Anthropic services">

295 * api.anthropic.com

296 * statsig.anthropic.com

297 * docs.claude.com

298 * platform.claude.com

299 * code.claude.com

300 * claude.ai

301 </Accordion>

302 

303 <Accordion title="Version control">

304 * github.com

305 * [www.github.com](http://www.github.com)

306 * api.github.com

307 * npm.pkg.github.com

308 * raw\.githubusercontent.com

309 * pkg-npm.githubusercontent.com

310 * objects.githubusercontent.com

311 * release-assets.githubusercontent.com

312 * codeload.github.com

313 * avatars.githubusercontent.com

314 * camo.githubusercontent.com

315 * gist.github.com

316 * gitlab.com

317 * [www.gitlab.com](http://www.gitlab.com)

318 * registry.gitlab.com

319 * bitbucket.org

320 * [www.bitbucket.org](http://www.bitbucket.org)

321 * api.bitbucket.org

322 </Accordion>

323 

324 <Accordion title="Container registries">

325 * registry-1.docker.io

326 * auth.docker.io

327 * index.docker.io

328 * hub.docker.com

329 * [www.docker.com](http://www.docker.com)

330 * production.cloudflare.docker.com

331 * download.docker.com

332 * gcr.io

333 * \*.gcr.io

334 * ghcr.io

335 * mcr.microsoft.com

336 * \*.data.mcr.microsoft.com

337 * public.ecr.aws

338 </Accordion>

339 

340 <Accordion title="Cloud platforms">

341 * cloud.google.com

342 * accounts.google.com

343 * gcloud.google.com

344 * \*.googleapis.com

345 * storage.googleapis.com

346 * compute.googleapis.com

347 * container.googleapis.com

348 * azure.com

349 * portal.azure.com

350 * microsoft.com

351 * [www.microsoft.com](http://www.microsoft.com)

352 * \*.microsoftonline.com

353 * packages.microsoft.com

354 * dotnet.microsoft.com

355 * dot.net

356 * visualstudio.com

357 * dev.azure.com

358 * \*.amazonaws.com

359 * \*.api.aws

360 * oracle.com

361 * [www.oracle.com](http://www.oracle.com)

362 * java.com

363 * [www.java.com](http://www.java.com)

364 * java.net

365 * [www.java.net](http://www.java.net)

366 * download.oracle.com

367 * yum.oracle.com

368 </Accordion>

369 

370 <Accordion title="JavaScript and Node package managers">

371 * registry.npmjs.org

372 * [www.npmjs.com](http://www.npmjs.com)

373 * [www.npmjs.org](http://www.npmjs.org)

374 * npmjs.com

375 * npmjs.org

376 * yarnpkg.com

377 * registry.yarnpkg.com

378 </Accordion>

379 

380 <Accordion title="Python package managers">

381 * pypi.org

382 * [www.pypi.org](http://www.pypi.org)

383 * files.pythonhosted.org

384 * pythonhosted.org

385 * test.pypi.org

386 * pypi.python.org

387 * pypa.io

388 * [www.pypa.io](http://www.pypa.io)

389 </Accordion>

390 

391 <Accordion title="Ruby package managers">

392 * rubygems.org

393 * [www.rubygems.org](http://www.rubygems.org)

394 * api.rubygems.org

395 * index.rubygems.org

396 * ruby-lang.org

397 * [www.ruby-lang.org](http://www.ruby-lang.org)

398 * rubyforge.org

399 * [www.rubyforge.org](http://www.rubyforge.org)

400 * rubyonrails.org

401 * [www.rubyonrails.org](http://www.rubyonrails.org)

402 * rvm.io

403 * get.rvm.io

404 </Accordion>

405 

406 <Accordion title="Rust package managers">

407 * crates.io

408 * [www.crates.io](http://www.crates.io)

409 * index.crates.io

410 * static.crates.io

411 * rustup.rs

412 * static.rust-lang.org

413 * [www.rust-lang.org](http://www.rust-lang.org)

414 </Accordion>

415 

416 <Accordion title="Go package managers">

417 * proxy.golang.org

418 * sum.golang.org

419 * index.golang.org

420 * golang.org

421 * [www.golang.org](http://www.golang.org)

422 * goproxy.io

423 * pkg.go.dev

424 </Accordion>

425 

426 <Accordion title="JVM package managers">

427 * maven.org

428 * repo.maven.org

429 * central.maven.org

430 * repo1.maven.org

431 * repo.maven.apache.org

432 * jcenter.bintray.com

433 * gradle.org

434 * [www.gradle.org](http://www.gradle.org)

435 * services.gradle.org

436 * plugins.gradle.org

437 * kotlinlang.org

438 * [www.kotlinlang.org](http://www.kotlinlang.org)

439 * spring.io

440 * repo.spring.io

441 </Accordion>

442 

443 <Accordion title="Other package managers">

444 * packagist.org (PHP Composer)

445 * [www.packagist.org](http://www.packagist.org)

446 * repo.packagist.org

447 * nuget.org (.NET NuGet)

448 * [www.nuget.org](http://www.nuget.org)

449 * api.nuget.org

450 * pub.dev (Dart/Flutter)

451 * api.pub.dev

452 * hex.pm (Elixir/Erlang)

453 * [www.hex.pm](http://www.hex.pm)

454 * cpan.org (Perl CPAN)

455 * [www.cpan.org](http://www.cpan.org)

456 * metacpan.org

457 * [www.metacpan.org](http://www.metacpan.org)

458 * api.metacpan.org

459 * cocoapods.org (iOS/macOS)

460 * [www.cocoapods.org](http://www.cocoapods.org)

461 * cdn.cocoapods.org

462 * haskell.org

463 * [www.haskell.org](http://www.haskell.org)

464 * hackage.haskell.org

465 * swift.org

466 * [www.swift.org](http://www.swift.org)

467 </Accordion>

468 

469 <Accordion title="Linux distributions">

470 * archive.ubuntu.com

471 * security.ubuntu.com

472 * ubuntu.com

473 * [www.ubuntu.com](http://www.ubuntu.com)

474 * \*.ubuntu.com

475 * ppa.launchpad.net

476 * launchpad.net

477 * [www.launchpad.net](http://www.launchpad.net)

478 * \*.nixos.org

479 </Accordion>

480 

481 <Accordion title="Development tools and platforms">

482 * dl.k8s.io (Kubernetes)

483 * pkgs.k8s.io

484 * k8s.io

485 * [www.k8s.io](http://www.k8s.io)

486 * releases.hashicorp.com (HashiCorp)

487 * apt.releases.hashicorp.com

488 * rpm.releases.hashicorp.com

489 * archive.releases.hashicorp.com

490 * hashicorp.com

491 * [www.hashicorp.com](http://www.hashicorp.com)

492 * repo.anaconda.com (Anaconda/Conda)

493 * conda.anaconda.org

494 * anaconda.org

495 * [www.anaconda.com](http://www.anaconda.com)

496 * anaconda.com

497 * continuum.io

498 * apache.org (Apache)

499 * [www.apache.org](http://www.apache.org)

500 * archive.apache.org

501 * downloads.apache.org

502 * eclipse.org (Eclipse)

503 * [www.eclipse.org](http://www.eclipse.org)

504 * download.eclipse.org

505 * nodejs.org (Node.js)

506 * [www.nodejs.org](http://www.nodejs.org)

507 * developer.apple.com

508 * developer.android.com

509 * pkg.stainless.com

510 * binaries.prisma.sh

511 </Accordion>

512 

513 <Accordion title="Cloud services and monitoring">

514 * statsig.com

515 * [www.statsig.com](http://www.statsig.com)

516 * api.statsig.com

517 * sentry.io

518 * \*.sentry.io

519 * downloads.sentry-cdn.com

520 * http-intake.logs.datadoghq.com

521 * \*.datadoghq.com

522 * \*.datadoghq.eu

523 * api.honeycomb.io

524 </Accordion>

525 

526 <Accordion title="Content delivery and mirrors">

527 * sourceforge.net

528 * \*.sourceforge.net

529 * packagecloud.io

530 * \*.packagecloud.io

531 * fonts.googleapis.com

532 * fonts.gstatic.com

533 </Accordion>

534 

535 <Accordion title="Schema and configuration">

536 * json-schema.org

537 * [www.json-schema.org](http://www.json-schema.org)

538 * json.schemastore.org

539 * [www.schemastore.org](http://www.schemastore.org)

540 </Accordion>

541 

542 <Accordion title="Model Context Protocol">

543 * \*.modelcontextprotocol.io

544 </Accordion>

545</AccordionGroup>

546 

547## Move tasks between web and terminal

548 

549These workflows require the [Claude Code CLI](/en/quickstart) signed in to the same claude.ai account. You can start new cloud sessions from your terminal, or pull cloud sessions into your terminal to continue locally. Cloud sessions persist even if you close your laptop, and you can monitor them from anywhere including the Claude mobile app.

277 550 

278When you start a session in Claude Code on the web, here's what happens under the hood:551<Note>

552 From the CLI, session handoff is one-way: you can pull cloud sessions into your terminal with `--teleport`, but you can't push an existing terminal session to the web. The `--remote` flag creates a new cloud session for your current repository. The [Desktop app](/en/desktop#continue-in-another-surface) provides a Continue in menu that can send a local session to the web.

553</Note>

279 554 

2801. **Environment preparation**: We clone your repository and run any configured [setup script](#setup-scripts). The repo will be cloned with the default branch on your GitHub repo. If you would like to check out a specific branch, you can specify that in the prompt.555### From terminal to web

281 556 

2822. **Network configuration**: We configure internet access for the agent. Internet access is limited by default, but you can configure the environment to have no internet or full internet access based on your needs.557Start a cloud session from the command line with the `--remote` flag:

283 558 

2843. **Claude Code execution**: Claude Code runs to complete your task, writing code, running tests, and checking its work. You can guide and steer Claude throughout the session via the web interface. Claude respects context you've defined in your `CLAUDE.md`.559```bash theme={null}

560claude --remote "Fix the authentication bug in src/auth/login.ts"

561```

285 562 

2864. **Outcome**: When Claude completes its work, it will push the branch to remote. You will be able to create a PR for the branch.563This creates a new cloud session on claude.ai. The session clones your current directory's GitHub remote at your current branch, so push first if you have local commits, since the VM clones from GitHub rather than your machine. `--remote` works with a single repository at a time. The task runs in the cloud while you continue working locally.

287 564 

288<Note>565<Note>

289 Claude operates entirely through the terminal and CLI tools available in the environment. It uses the pre-installed tools in the universal image and any additional tools you install through hooks or dependency management.566 `--remote` creates cloud sessions. `--remote-control` is unrelated: it exposes a local CLI session for monitoring from the web. See [Remote Control](/en/remote-control).

290</Note>567</Note>

291 568 

292**To add a new environment:** Select the current environment to open the environment selector, and then select "Add environment". This will open a dialog where you can specify the environment name, network access level, environment variables, and a [setup script](#setup-scripts).569Use `/tasks` in the Claude Code CLI to check progress, or open the session on claude.ai or the Claude mobile app to interact directly. From there you can steer Claude, provide feedback, or answer questions just like any other conversation.

293 570 

294**To update an existing environment:** Select the current environment, to the right of the environment name, and select the settings button. This will open a dialog where you can update the environment name, network access, environment variables, and setup script.571#### Tips for cloud tasks

295 572 

296**To select your default environment from the terminal:** If you have multiple environments configured, run `/remote-env` to choose which one to use when starting web sessions from your terminal with `--remote`. With a single environment, this command shows your current configuration.573**Plan locally, execute remotely**: for complex tasks, start Claude in plan mode to collaborate on the approach, then send work to the cloud:

297 574 

298<Note>575```bash theme={null}

299 Environment variables must be specified as key-value pairs, in [`.env` format](https://www.dotenv.org/). For example:576claude --permission-mode plan

577```

300 578 

301 ```text theme={null}579In plan mode, Claude reads files, runs commands to explore, and proposes a plan without editing source code. Once you're satisfied, save the plan to the repo, commit, and push so the cloud VM can clone it. Then start a cloud session for autonomous execution:

302 API_KEY=your_api_key

303 DEBUG=true

304 ```

305</Note>

306 580 

307### Setup scripts581```bash theme={null}

582claude --remote "Execute the migration plan in docs/migration-plan.md"

583```

308 584 

309A setup script is a Bash script that runs when a new cloud session starts, before Claude Code launches. Use setup scripts to install dependencies, configure tools, or prepare anything the cloud environment needs that isn't in the [default image](#default-image).585This pattern gives you control over the strategy while letting Claude execute autonomously in the cloud.

310 586 

311Scripts run as root on Ubuntu 24.04, so `apt install` and most language package managers work.587**Plan in the cloud with ultraplan**: to draft and review the plan itself in a web session, use [ultraplan](/en/ultraplan). Claude generates the plan on Claude Code on the web while you keep working, then you comment on sections in your browser and choose to execute remotely or send the plan back to your terminal.

312 588 

313<Tip>589**Run tasks in parallel**: each `--remote` command creates its own cloud session that runs independently. You can start multiple tasks and they'll all run simultaneously in separate sessions:

314 To check what's already installed before adding it to your script, ask Claude to run `check-tools` in a cloud session.

315</Tip>

316 590 

317To add a setup script, open the environment settings dialog and enter your script in the **Setup script** field.591```bash theme={null}

592claude --remote "Fix the flaky test in auth.spec.ts"

593claude --remote "Update the API documentation"

594claude --remote "Refactor the logger to use structured output"

595```

596 

597Monitor all sessions with `/tasks` in the Claude Code CLI. When a session completes, you can create a PR from the web interface or [teleport](#from-web-to-terminal) the session to your terminal to continue working.

598 

599#### Send local repositories without GitHub

600 

601When you run `claude --remote` from a repository that isn't connected to GitHub, Claude Code bundles your local repository and uploads it directly to the cloud session. The bundle includes your full repository history across all branches, plus any uncommitted changes to tracked files.

318 602 

319This example installs the `gh` CLI, which isn't in the default image:603This fallback activates automatically when GitHub access isn't available. To force it even when GitHub is connected, set `CCR_FORCE_BUNDLE=1`:

320 604 

321```bash theme={null}605```bash theme={null}

322#!/bin/bash606CCR_FORCE_BUNDLE=1 claude --remote "Run the test suite and fix any failures"

323apt update && apt install -y gh

324```607```

325 608 

326Setup scripts run only when creating a new session. They are skipped when resuming an existing session.609Bundled repositories must meet these limits:

327 610 

328If the script exits non-zero, the session fails to start. Append `|| true` to non-critical commands to avoid blocking the session on a flaky install.611* The directory must be a git repository with at least one commit

612* The bundled repository must be under 100 MB. Larger repositories fall back to bundling only the current branch, then to a single squashed snapshot of the working tree, and fail only if the snapshot is still too large

613* Untracked files are not included; run `git add` on files you want the cloud session to see

614* Sessions created from a bundle can't push back to a remote unless you also have [GitHub authentication](#github-authentication-options) configured

329 615 

330<Note>616### From web to terminal

331 Setup scripts that install packages need network access to reach registries. The default network access allows connections to [common package registries](#default-allowed-domains) including npm, PyPI, RubyGems, and crates.io. Scripts will fail to install packages if your environment has network access disabled.

332</Note>

333 617 

334#### Setup scripts vs. SessionStart hooks618Pull a cloud session into your terminal using any of these:

335 619 

336Use a setup script to install things the cloud needs but your laptop already has, like a language runtime or CLI tool. Use a [SessionStart hook](/en/hooks#sessionstart) for project setup that should run everywhere, cloud and local, like `npm install`.620* **Using `--teleport`**: from the command line, run `claude --teleport` for an interactive session picker, or `claude --teleport <session-id>` to resume a specific session directly. If you have uncommitted changes, you'll be prompted to stash them first.

621* **Using `/teleport`**: inside an existing CLI session, run `/teleport` (or `/tp`) to open the same session picker without restarting Claude Code.

622* **From `/tasks`**: run `/tasks` to see your background sessions, then press `t` to teleport into one

623* **From the web interface**: select **Open in CLI** to copy a command you can paste into your terminal

337 624 

338Both run at the start of a session, but they belong to different places:625When you teleport a session, Claude verifies you're in the correct repository, fetches and checks out the branch from the cloud session, and loads the full conversation history into your terminal.

339 626 

340| | Setup scripts | SessionStart hooks |627`--teleport` is distinct from `--resume`. `--resume` reopens a conversation from this machine's local history and doesn't list cloud sessions; `--teleport` pulls a cloud session and its branch.

341| ------------- | ------------------------------------------------- | -------------------------------------------------------------- |

342| Attached to | The cloud environment | Your repository |

343| Configured in | Cloud environment UI | `.claude/settings.json` in your repo |

344| Runs | Before Claude Code launches, on new sessions only | After Claude Code launches, on every session including resumed |

345| Scope | Cloud environments only | Both local and cloud |

346 628 

347SessionStart hooks can also be defined in your user-level `~/.claude/settings.json` locally, but user-level settings don't carry over to cloud sessions. In the cloud, only hooks committed to the repo run.629#### Teleport requirements

348 630 

349### Dependency management631Teleport checks these requirements before resuming a session. If any requirement isn't met, you'll see an error or be prompted to resolve the issue.

350 632 

351Custom environment images and snapshots are not yet supported. Use [setup scripts](#setup-scripts) to install packages when a session starts, or [SessionStart hooks](/en/hooks#sessionstart) for dependency installation that should also run in local environments. SessionStart hooks have [known limitations](#dependency-management-limitations).633| Requirement | Details |

634| ------------------ | ------------------------------------------------------------------------------------------------------------------------ |

635| Clean git state | Your working directory must have no uncommitted changes. Teleport prompts you to stash changes if needed. |

636| Correct repository | You must run `--teleport` from a checkout of the same repository, not a fork. |

637| Branch available | The branch from the cloud session must have been pushed to the remote. Teleport automatically fetches and checks it out. |

638| Same account | You must be authenticated to the same claude.ai account used in the cloud session. |

352 639 

353To configure automatic dependency installation with a setup script, open your environment settings and add a script:640#### `--teleport` is unavailable

354 641 

355```bash theme={null}642Teleport requires claude.ai subscription authentication. If you're authenticated via API key, Bedrock, Vertex AI, or Microsoft Foundry, run `/login` to sign in with your claude.ai account instead. If you're already signed in via claude.ai and `--teleport` is still unavailable, your organization may have disabled cloud sessions.

356#!/bin/bash

357npm install

358pip install -r requirements.txt

359```

360 643 

361Alternatively, you can use SessionStart hooks in your repository's `.claude/settings.json` file for dependency installation that should also run in local environments:644## Work with sessions

362 645 

363```json theme={null}646Sessions appear in the sidebar at claude.ai/code. From there you can review changes, share with teammates, archive finished work, or delete sessions permanently.

364{

365 "hooks": {

366 "SessionStart": [

367 {

368 "matcher": "startup",

369 "hooks": [

370 {

371 "type": "command",

372 "command": "\"$CLAUDE_PROJECT_DIR\"/scripts/install_pkgs.sh"

373 }

374 ]

375 }

376 ]

377 }

378}

379```

380 647 

381Create the corresponding script at `scripts/install_pkgs.sh`:648### Manage context

382 649 

383```bash theme={null}650Cloud sessions support [built-in commands](/en/commands) that produce text output. Commands that open an interactive terminal picker, like `/model` or `/config`, are not available.

384#!/bin/bash

385 651 

386# Only run in remote environments652For context management specifically:

387if [ "$CLAUDE_CODE_REMOTE" != "true" ]; then

388 exit 0

389fi

390 653 

391npm install654| Command | Works in cloud sessions | Notes |

392pip install -r requirements.txt655| :--------- | :---------------------- | :----------------------------------------------------------------------------------------------------------------------- |

393exit 0656| `/compact` | Yes | Summarizes the conversation to free up context. Accepts optional focus instructions like `/compact keep the test output` |

394```657| `/context` | Yes | Shows what's currently in the context window |

658| `/clear` | No | Start a new session from the sidebar instead |

395 659 

396Make it executable: `chmod +x scripts/install_pkgs.sh`660Auto-compaction runs automatically when the context window approaches capacity, the same as in the CLI. To trigger it earlier, set [`CLAUDE_AUTOCOMPACT_PCT_OVERRIDE`](/en/env-vars) in your [environment variables](#configure-your-environment). For example, `CLAUDE_AUTOCOMPACT_PCT_OVERRIDE=70` compacts at 70% capacity instead of the default \~95%. To change the effective window size for compaction calculations, use [`CLAUDE_CODE_AUTO_COMPACT_WINDOW`](/en/env-vars).

397 661 

398#### Persist environment variables662[Subagents](/en/sub-agents) work the same way they do locally. Claude can spawn them with the Task tool to offload research or parallel work into a separate context window, keeping the main conversation lighter. Subagents defined in your repo's `.claude/agents/` are picked up automatically. [Agent teams](/en/agent-teams) are off by default but can be enabled by adding `CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1` to your [environment variables](#configure-your-environment).

399 663 

400SessionStart hooks can persist environment variables for subsequent Bash commands by writing to the file specified in the `CLAUDE_ENV_FILE` environment variable. For details, see [SessionStart hooks](/en/hooks#sessionstart) in the hooks reference.664### Review changes

401 665 

402#### Dependency management limitations666Each session shows a diff indicator with lines added and removed, like `+42 -18`. Select it to open the diff view, leave inline comments on specific lines, and send them to Claude with your next message. See [Review and iterate](/en/web-quickstart#review-and-iterate) for the full walkthrough including PR creation. To have Claude monitor the PR for CI failures and review comments automatically, see [Auto-fix pull requests](#auto-fix-pull-requests).

403 667 

404* **Hooks fire for all sessions**: SessionStart hooks run in both local and remote environments. There is no hook configuration to scope a hook to remote sessions only. To skip local execution, check the `CLAUDE_CODE_REMOTE` environment variable in your script as shown above.668### Share sessions

405* **Requires network access**: Install commands need network access to reach package registries. If your environment is configured with "No internet" access, these hooks will fail. Use "Limited" (the default) or "Full" network access. The [default allowlist](#default-allowed-domains) includes common registries like npm, PyPI, RubyGems, and crates.io.

406* **Proxy compatibility**: All outbound traffic in remote environments passes through a [security proxy](#security-proxy). Some package managers do not work correctly with this proxy. Bun is a known example.

407* **Runs on every session start**: Hooks run each time a session starts or resumes, adding startup latency. Keep install scripts fast by checking whether dependencies are already present before reinstalling.

408 669 

409## Network access and security670To share a session, toggle its visibility according to the account types below. After that, share the session link as-is. Recipients see the latest state when they open the link, but their view doesn't update in real time.

410 671 

411### Network policy672#### Share from an Enterprise or Team account

412 673 

413#### GitHub proxy674For Enterprise and Team accounts, the two visibility options are **Private** and **Team**. Team visibility makes the session visible to other members of your claude.ai organization. Repository access verification is enabled by default, based on the GitHub account connected to the recipient's account. Your account's display name is visible to all recipients with access. [Claude in Slack](/en/slack) sessions are automatically shared with Team visibility.

414 675 

415For security, all GitHub operations go through a dedicated proxy service that transparently handles all git interactions. Inside the sandbox, the git client authenticates using a custom-built scoped credential. This proxy:676#### Share from a Max or Pro account

416 677 

417* Manages GitHub authentication securely - the git client uses a scoped credential inside the sandbox, which the proxy verifies and translates to your actual GitHub authentication token678For Max and Pro accounts, the two visibility options are **Private** and **Public**. Public visibility makes the session visible to any user logged into claude.ai.

418* Restricts git push operations to the current working branch for safety

419* Enables seamless cloning, fetching, and PR operations while maintaining security boundaries

420 679 

421#### Security proxy680Check your session for sensitive content before sharing. Sessions may contain code and credentials from private GitHub repositories. Repository access verification is not enabled by default.

422 681 

423Environments run behind an HTTP/HTTPS network proxy for security and abuse prevention purposes. All outbound internet traffic passes through this proxy, which provides:682To require recipients to have repository access, or to hide your name from shared sessions, go to Settings > Claude Code > Sharing settings.

424 683 

425* Protection against malicious requests684### Archive sessions

426* Rate limiting and abuse prevention

427* Content filtering for enhanced security

428 685 

429### Access levels686You can archive sessions to keep your session list organized. Archived sessions are hidden from the default session list but can be viewed by filtering for archived sessions.

430 687 

431By default, network access is limited to [allowlisted domains](#default-allowed-domains).688To archive a session, hover over the session in the sidebar and select the archive icon.

432 689 

433You can configure custom network access, including disabling network access.690### Delete sessions

434 691 

435### Default allowed domains692Deleting a session permanently removes the session and its data. This action cannot be undone. You can delete a session in two ways:

693 

694* **From the sidebar**: filter for archived sessions, then hover over the session you want to delete and select the delete icon

695* **From the session menu**: open a session, select the dropdown next to the session title, and select **Delete**

696 

697You will be asked to confirm before a session is deleted.

698 

699## Auto-fix pull requests

436 700 

437When using "Limited" network access, the following domains are allowed by default:701Claude can watch a pull request and automatically respond to CI failures and review comments. Claude subscribes to GitHub activity on the PR, and when a check fails or a reviewer leaves a comment, Claude investigates and pushes a fix if one is clear.

438 

439#### Anthropic Services

440 

441* api.anthropic.com

442* statsig.anthropic.com

443* platform.claude.com

444* code.claude.com

445* claude.ai

446 

447#### Version Control

448 

449* github.com

450* [www.github.com](http://www.github.com)

451* api.github.com

452* npm.pkg.github.com

453* raw\.githubusercontent.com

454* pkg-npm.githubusercontent.com

455* objects.githubusercontent.com

456* codeload.github.com

457* avatars.githubusercontent.com

458* camo.githubusercontent.com

459* gist.github.com

460* gitlab.com

461* [www.gitlab.com](http://www.gitlab.com)

462* registry.gitlab.com

463* bitbucket.org

464* [www.bitbucket.org](http://www.bitbucket.org)

465* api.bitbucket.org

466 

467#### Container Registries

468 

469* registry-1.docker.io

470* auth.docker.io

471* index.docker.io

472* hub.docker.com

473* [www.docker.com](http://www.docker.com)

474* production.cloudflare.docker.com

475* download.docker.com

476* gcr.io

477* \*.gcr.io

478* ghcr.io

479* mcr.microsoft.com

480* \*.data.mcr.microsoft.com

481* public.ecr.aws

482 

483#### Cloud Platforms

484 

485* cloud.google.com

486* accounts.google.com

487* gcloud.google.com

488* \*.googleapis.com

489* storage.googleapis.com

490* compute.googleapis.com

491* container.googleapis.com

492* azure.com

493* portal.azure.com

494* microsoft.com

495* [www.microsoft.com](http://www.microsoft.com)

496* \*.microsoftonline.com

497* packages.microsoft.com

498* dotnet.microsoft.com

499* dot.net

500* visualstudio.com

501* dev.azure.com

502* \*.amazonaws.com

503* \*.api.aws

504* oracle.com

505* [www.oracle.com](http://www.oracle.com)

506* java.com

507* [www.java.com](http://www.java.com)

508* java.net

509* [www.java.net](http://www.java.net)

510* download.oracle.com

511* yum.oracle.com

512 

513#### Package Managers - JavaScript/Node

514 

515* registry.npmjs.org

516* [www.npmjs.com](http://www.npmjs.com)

517* [www.npmjs.org](http://www.npmjs.org)

518* npmjs.com

519* npmjs.org

520* yarnpkg.com

521* registry.yarnpkg.com

522 

523#### Package Managers - Python

524 

525* pypi.org

526* [www.pypi.org](http://www.pypi.org)

527* files.pythonhosted.org

528* pythonhosted.org

529* test.pypi.org

530* pypi.python.org

531* pypa.io

532* [www.pypa.io](http://www.pypa.io)

533 

534#### Package Managers - Ruby

535 

536* rubygems.org

537* [www.rubygems.org](http://www.rubygems.org)

538* api.rubygems.org

539* index.rubygems.org

540* ruby-lang.org

541* [www.ruby-lang.org](http://www.ruby-lang.org)

542* rubyforge.org

543* [www.rubyforge.org](http://www.rubyforge.org)

544* rubyonrails.org

545* [www.rubyonrails.org](http://www.rubyonrails.org)

546* rvm.io

547* get.rvm.io

548 

549#### Package Managers - Rust

550 

551* crates.io

552* [www.crates.io](http://www.crates.io)

553* index.crates.io

554* static.crates.io

555* rustup.rs

556* static.rust-lang.org

557* [www.rust-lang.org](http://www.rust-lang.org)

558 

559#### Package Managers - Go

560 

561* proxy.golang.org

562* sum.golang.org

563* index.golang.org

564* golang.org

565* [www.golang.org](http://www.golang.org)

566* goproxy.io

567* pkg.go.dev

568 

569#### Package Managers - JVM

570 

571* maven.org

572* repo.maven.org

573* central.maven.org

574* repo1.maven.org

575* jcenter.bintray.com

576* gradle.org

577* [www.gradle.org](http://www.gradle.org)

578* services.gradle.org

579* plugins.gradle.org

580* kotlin.org

581* [www.kotlin.org](http://www.kotlin.org)

582* spring.io

583* repo.spring.io

584 

585#### Package Managers - Other Languages

586 

587* packagist.org (PHP Composer)

588* [www.packagist.org](http://www.packagist.org)

589* repo.packagist.org

590* nuget.org (.NET NuGet)

591* [www.nuget.org](http://www.nuget.org)

592* api.nuget.org

593* pub.dev (Dart/Flutter)

594* api.pub.dev

595* hex.pm (Elixir/Erlang)

596* [www.hex.pm](http://www.hex.pm)

597* cpan.org (Perl CPAN)

598* [www.cpan.org](http://www.cpan.org)

599* metacpan.org

600* [www.metacpan.org](http://www.metacpan.org)

601* api.metacpan.org

602* cocoapods.org (iOS/macOS)

603* [www.cocoapods.org](http://www.cocoapods.org)

604* cdn.cocoapods.org

605* haskell.org

606* [www.haskell.org](http://www.haskell.org)

607* hackage.haskell.org

608* swift.org

609* [www.swift.org](http://www.swift.org)

610 

611#### Linux Distributions

612 

613* archive.ubuntu.com

614* security.ubuntu.com

615* ubuntu.com

616* [www.ubuntu.com](http://www.ubuntu.com)

617* \*.ubuntu.com

618* ppa.launchpad.net

619* launchpad.net

620* [www.launchpad.net](http://www.launchpad.net)

621 

622#### Development Tools & Platforms

623 

624* dl.k8s.io (Kubernetes)

625* pkgs.k8s.io

626* k8s.io

627* [www.k8s.io](http://www.k8s.io)

628* releases.hashicorp.com (HashiCorp)

629* apt.releases.hashicorp.com

630* rpm.releases.hashicorp.com

631* archive.releases.hashicorp.com

632* hashicorp.com

633* [www.hashicorp.com](http://www.hashicorp.com)

634* repo.anaconda.com (Anaconda/Conda)

635* conda.anaconda.org

636* anaconda.org

637* [www.anaconda.com](http://www.anaconda.com)

638* anaconda.com

639* continuum.io

640* apache.org (Apache)

641* [www.apache.org](http://www.apache.org)

642* archive.apache.org

643* downloads.apache.org

644* eclipse.org (Eclipse)

645* [www.eclipse.org](http://www.eclipse.org)

646* download.eclipse.org

647* nodejs.org (Node.js)

648* [www.nodejs.org](http://www.nodejs.org)

649 

650#### Cloud Services & Monitoring

651 

652* statsig.com

653* [www.statsig.com](http://www.statsig.com)

654* api.statsig.com

655* sentry.io

656* \*.sentry.io

657* http-intake.logs.datadoghq.com

658* \*.datadoghq.com

659* \*.datadoghq.eu

660 

661#### Content Delivery & Mirrors

662 

663* sourceforge.net

664* \*.sourceforge.net

665* packagecloud.io

666* \*.packagecloud.io

667 

668#### Schema & Configuration

669 

670* json-schema.org

671* [www.json-schema.org](http://www.json-schema.org)

672* json.schemastore.org

673* [www.schemastore.org](http://www.schemastore.org)

674 

675#### Model Context Protocol

676 

677* \*.modelcontextprotocol.io

678 702 

679<Note>703<Note>

680 Domains marked with `*` indicate wildcard subdomain matching. For example, `*.gcr.io` allows access to any subdomain of `gcr.io`.704 Auto-fix requires the Claude GitHub App to be installed on your repository. If you haven't already, install it from the [GitHub App page](https://github.com/apps/claude) or when prompted during [setup](/en/web-quickstart#connect-github-and-create-an-environment).

681</Note>705</Note>

682 706 

683### Security best practices for customized network access707There are a few ways to turn on auto-fix depending on where the PR came from and what device you're using:

684 708 

6851. **Principle of least privilege**: Only enable the minimum network access required709* **PRs created in Claude Code on the web**: open the CI status bar and select **Auto-fix**

6862. **Audit regularly**: Review allowed domains periodically710* **From the mobile app**: tell Claude to auto-fix the PR, for example "watch this PR and fix any CI failures or review comments"

6873. **Use HTTPS**: Always prefer HTTPS endpoints over HTTP711* **Any existing PR**: paste the PR URL into a session and tell Claude to auto-fix it

688 712 

689## Security and isolation713### How Claude responds to PR activity

690 714 

691Claude Code on the web provides strong security guarantees:715When auto-fix is active, Claude receives GitHub events for the PR including new review comments and CI check failures. For each event, Claude investigates and decides how to proceed:

692 716 

693* **Isolated virtual machines**: Each session runs in an isolated, Anthropic-managed VM717* **Clear fixes**: if Claude is confident in a fix and it doesn't conflict with earlier instructions, Claude makes the change, pushes it, and explains what was done in the session

694* **Network access controls**: Network access is limited by default, and can be disabled718* **Ambiguous requests**: if a reviewer's comment could be interpreted multiple ways or involves something architecturally significant, Claude asks you before acting

719* **Duplicate or no-action events**: if an event is a duplicate or requires no change, Claude notes it in the session and moves on

695 720 

696<Note>721Claude may reply to review comment threads on GitHub as part of resolving them. These replies are posted using your GitHub account, so they appear under your username, but each reply is labeled as coming from Claude Code so reviewers know it was written by the agent and not by you directly.

697 When running with network access disabled, Claude Code is allowed to communicate with the Anthropic API which may still allow data to exit the isolated Claude Code VM.

698</Note>

699 722 

700* **Credential protection**: Sensitive credentials (such as git credentials or signing keys) are never inside the sandbox with Claude Code. Authentication is handled through a secure proxy using scoped credentials723<Warning>

701* **Secure analysis**: Code is analyzed and modified within isolated VMs before creating PRs724 If your repository uses comment-triggered automation such as Atlantis, Terraform Cloud, or custom GitHub Actions that run on `issue_comment` events, be aware that Claude can reply on your behalf, which can trigger those workflows. Review your repository's automation before enabling auto-fix, and consider disabling auto-fix for repositories where a PR comment can deploy infrastructure or run privileged operations.

725</Warning>

702 726 

703## Pricing and rate limits727## Security and isolation

704 728 

705Claude Code on the web shares rate limits with all other Claude and Claude Code usage within your account. Running multiple tasks in parallel will consume more rate limits proportionately.729Each cloud session is separated from your machine and from other sessions through several layers:

706 730 

707## Limitations731* **Isolated virtual machines**: each session runs in an isolated, Anthropic-managed VM

732* **Network access controls**: network access is limited by default, and can be disabled. When running with network access disabled, Claude Code can still communicate with the Anthropic API, which may allow data to exit the VM.

733* **Credential protection**: sensitive credentials such as git credentials or signing keys are never inside the sandbox with Claude Code. Authentication is handled through a secure proxy using scoped credentials.

734* **Secure analysis**: code is analyzed and modified within isolated VMs before creating PRs

708 735 

709* **Repository authentication**: You can only move sessions from web to local when you are authenticated to the same account736## Limitations

710* **Platform restrictions**: Claude Code on the web only works with code hosted in GitHub. Self-hosted [GitHub Enterprise Server](/en/github-enterprise-server) instances are supported for Team and Enterprise plans. GitLab and other non-GitHub repositories cannot be used with cloud sessions

711 737 

712## Best practices738Before relying on cloud sessions for a workflow, account for these constraints:

713 739 

7141. **Automate environment setup**: Use [setup scripts](#setup-scripts) to install dependencies and configure tools before Claude Code launches. For more advanced scenarios, configure [SessionStart hooks](/en/hooks#sessionstart).740* **Rate limits**: Claude Code on the web shares rate limits with all other Claude and Claude Code usage within your account. Running multiple tasks in parallel consumes more rate limits proportionately. There is no separate compute charge for the cloud VM.

7152. **Document requirements**: Clearly specify dependencies and commands in your `CLAUDE.md` file. If you have an `AGENTS.md` file, you can source it in your `CLAUDE.md` using `@AGENTS.md` to maintain a single source of truth.741* **Repository authentication**: you can only move sessions from web to local when you are authenticated to the same account

742* **Platform restrictions**: repository cloning and pull request creation require GitHub. Self-hosted [GitHub Enterprise Server](/en/github-enterprise-server) instances are supported for Team and Enterprise plans. GitLab, Bitbucket, and other non-GitHub repositories can be sent to cloud sessions as a [local bundle](#send-local-repositories-without-github), but the session can't push results back to the remote

716 743 

717## Related resources744## Related resources

718 745 

719* [Hooks configuration](/en/hooks)746* [Schedule tasks on the web](/en/web-scheduled-tasks): automate recurring work like daily PR reviews and dependency audits

720* [Settings reference](/en/settings)747* [Hooks configuration](/en/hooks): run scripts at session lifecycle events

721* [Security](/en/security)748* [Settings reference](/en/settings): all configuration options

722* [Data usage](/en/data-usage)749* [Security](/en/security): isolation guarantees and data handling

750* [Data usage](/en/data-usage): what Anthropic retains from cloud sessions

Details

1390 1390 

1391Claude Code reads instructions, settings, skills, subagents, and memory from your project directory and from `~/.claude` in your home directory. Commit project files to git to share them with your team; files in `~/.claude` are personal configuration that applies across all your projects.1391Claude Code reads instructions, settings, skills, subagents, and memory from your project directory and from `~/.claude` in your home directory. Commit project files to git to share them with your team; files in `~/.claude` are personal configuration that applies across all your projects.

1392 1392 

1393If you set [`CLAUDE_CONFIG_DIR`](/en/env-vars), every `~/.claude` path on this page lives under that directory instead.

1394 

1393Most users only edit `CLAUDE.md` and `settings.json`. The rest of the directory is optional: add skills, rules, or subagents as you need them.1395Most users only edit `CLAUDE.md` and `settings.json`. The rest of the directory is optional: add skills, rules, or subagents as you need them.

1394 1396 

1395This page is an interactive explorer: click files in the tree to see what each one does, when it loads, and an example. For a quick reference, see the [file reference table](#file-reference) below.1397This page is an interactive explorer: click files in the tree to see what each one does, when it loads, and an example. For a quick reference, see the [file reference table](#file-reference) below.


1398 1400 

1399## What's not shown1401## What's not shown

1400 1402 

1401The explorer covers the files you'll interact with most. A few things live elsewhere:1403The explorer covers files you author and edit. A few related files live elsewhere:

1402 1404 

1403| File | Location | Purpose |1405| File | Location | Purpose |

1404| ----------------------- | -------------------------- | --------------------------------------------------------------------------------------------------------------------- |1406| ----------------------- | -------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |

1405| `managed-settings.json` | System-level, varies by OS | Enterprise-enforced settings that you can't override. See [server-managed settings](/en/server-managed-settings). |1407| `managed-settings.json` | System-level, varies by OS | Enterprise-enforced settings that you can't override. See [server-managed settings](/en/server-managed-settings). |

1406| `CLAUDE.local.md` | Project root | Your private preferences for this project, loaded alongside CLAUDE.md. Create it manually and add it to `.gitignore`. |1408| `CLAUDE.local.md` | Project root | Your private preferences for this project, loaded alongside CLAUDE.md. Create it manually and add it to `.gitignore`. |

1409| Installed plugins | `~/.claude/plugins/` | Cloned marketplaces, installed plugin versions, and per-plugin data, managed by `claude plugin` commands. Orphaned versions are deleted 7 days after a plugin update or uninstall. See [plugin caching](/en/plugins-reference#plugin-caching-and-file-resolution). |

1410 

1411`~/.claude` also holds data Claude Code writes as you work: transcripts, prompt history, file snapshots, caches, and logs. See [application data](#application-data) below.

1407 1412 

1408## File reference1413## File reference

1409 1414 


1455 1460 

1456Run `/context` first for the overview, then the specific command for the area you want to investigate.1461Run `/context` first for the overview, then the specific command for the area you want to investigate.

1457 1462 

1463## Application data

1464 

1465Beyond the config you author, `~/.claude` holds data Claude Code writes during sessions. These files are plaintext. Anything that passes through a tool lands in a transcript on disk: file contents, command output, pasted text.

1466 

1467### Cleaned up automatically

1468 

1469Files in the paths below are deleted on startup once they're older than [`cleanupPeriodDays`](/en/settings#available-settings). The default is 30 days.

1470 

1471| Path under `~/.claude/` | Contents |

1472| -------------------------------------------- | -------------------------------------------------------------------------------------------------- |

1473| `projects/<project>/<session>.jsonl` | Full conversation transcript: every message, tool call, and tool result |

1474| `projects/<project>/<session>/tool-results/` | Large tool outputs spilled to separate files |

1475| `file-history/<session>/` | Pre-edit snapshots of files Claude changed, used for [checkpoint restore](/en/checkpointing) |

1476| `plans/` | Plan files written during [plan mode](/en/permission-modes#analyze-before-you-edit-with-plan-mode) |

1477| `debug/` | Per-session debug logs, written only when you start with `--debug` or run `/debug` |

1478| `paste-cache/`, `image-cache/` | Contents of large pastes and attached images |

1479| `session-env/` | Per-session environment metadata |

1480 

1481### Kept until you delete them

1482 

1483The following paths are not covered by automatic cleanup and persist indefinitely.

1484 

1485| Path under `~/.claude/` | Contents |

1486| ----------------------- | ------------------------------------------------------------------------------------- |

1487| `history.jsonl` | Every prompt you've typed, with timestamp and project path. Used for up-arrow recall. |

1488| `stats-cache.json` | Aggregated token and cost counts shown by `/cost` |

1489| `backups/` | Timestamped copies of `~/.claude.json` taken before config migrations |

1490| `todos/` | Legacy per-session task lists. No longer written by current versions; safe to delete. |

1491 

1492`shell-snapshots/` holds runtime files removed when the session exits cleanly. Other small cache and lock files appear depending on which features you use and are safe to delete.

1493 

1494### Plaintext storage

1495 

1496Transcripts and history are not encrypted at rest. OS file permissions are the only protection. If a tool reads a `.env` file or a command prints a credential, that value is written to `projects/<project>/<session>.jsonl`. To reduce exposure:

1497 

1498* Lower `cleanupPeriodDays` to shorten how long transcripts are kept

1499* In non-interactive mode, pass `--no-session-persistence` alongside `-p` to skip writing transcripts entirely. In the Agent SDK, set `persistSession: false`. There is no interactive-mode equivalent.

1500* Use [permission rules](/en/permissions) to deny reads of credential files

1501 

1502### Clear local data

1503 

1504You can delete any of the application-data paths above at any time. New sessions are unaffected. The table below shows what you lose for past sessions.

1505 

1506| Delete | You lose |

1507| -------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------- |

1508| `~/.claude/projects/` | Resume, continue, and rewind for past sessions |

1509| `~/.claude/history.jsonl` | Up-arrow prompt recall |

1510| `~/.claude/file-history/` | Checkpoint restore for past sessions |

1511| `~/.claude/stats-cache.json` | Historical totals shown by `/cost` |

1512| `~/.claude/backups/` | Rollback copies of `~/.claude.json` from past config migrations |

1513| `~/.claude/debug/`, `~/.claude/plans/`, `~/.claude/paste-cache/`, `~/.claude/image-cache/`, `~/.claude/session-env/` | Nothing user-facing |

1514| `~/.claude/todos/` | Nothing. Legacy directory not written by current versions. |

1515 

1516Don't delete `~/.claude.json`, `~/.claude/settings.json`, or `~/.claude/plugins/`: those hold your auth, preferences, and installed plugins.

1517 

1458## Related resources1518## Related resources

1459 1519 

1460* [Manage Claude's memory](/en/memory): write and organize CLAUDE.md, rules, and auto memory1520* [Manage Claude's memory](/en/memory): write and organize CLAUDE.md, rules, and auto memory

Details

63| `--include-hook-events` | Include all hook lifecycle events in the output stream. Requires `--output-format stream-json` | `claude -p --output-format stream-json --include-hook-events "query"` |63| `--include-hook-events` | Include all hook lifecycle events in the output stream. Requires `--output-format stream-json` | `claude -p --output-format stream-json --include-hook-events "query"` |

64| `--include-partial-messages` | Include partial streaming events in output. Requires `--print` and `--output-format stream-json` | `claude -p --output-format stream-json --include-partial-messages "query"` |64| `--include-partial-messages` | Include partial streaming events in output. Requires `--print` and `--output-format stream-json` | `claude -p --output-format stream-json --include-partial-messages "query"` |

65| `--input-format` | Specify input format for print mode (options: `text`, `stream-json`) | `claude -p --output-format json --input-format stream-json` |65| `--input-format` | Specify input format for print mode (options: `text`, `stream-json`) | `claude -p --output-format json --input-format stream-json` |

66| `--json-schema` | Get validated JSON output matching a JSON Schema after agent completes its workflow (print mode only, see [structured outputs](https://platform.claude.com/docs/en/agent-sdk/structured-outputs)) | `claude -p --json-schema '{"type":"object","properties":{...}}' "query"` |66| `--json-schema` | Get validated JSON output matching a JSON Schema after agent completes its workflow (print mode only, see [structured outputs](/en/agent-sdk/structured-outputs)) | `claude -p --json-schema '{"type":"object","properties":{...}}' "query"` |

67| `--maintenance` | Run maintenance hooks and start interactive mode | `claude --maintenance` |67| `--maintenance` | Run maintenance hooks and start interactive mode | `claude --maintenance` |

68| `--max-budget-usd` | Maximum dollar amount to spend on API calls before stopping (print mode only) | `claude -p --max-budget-usd 5.00 "query"` |68| `--max-budget-usd` | Maximum dollar amount to spend on API calls before stopping (print mode only) | `claude -p --max-budget-usd 5.00 "query"` |

69| `--max-turns` | Limit the number of agentic turns (print mode only). Exits with an error when the limit is reached. No limit by default | `claude -p --max-turns 3 "query"` |69| `--max-turns` | Limit the number of agentic turns (print mode only). Exits with an error when the limit is reached. No limit by default | `claude -p --max-turns 3 "query"` |


77| `--permission-mode` | Begin in a specified [permission mode](/en/permission-modes). Accepts `default`, `acceptEdits`, `plan`, `auto`, `dontAsk`, or `bypassPermissions`. Overrides `defaultMode` from settings files | `claude --permission-mode plan` |77| `--permission-mode` | Begin in a specified [permission mode](/en/permission-modes). Accepts `default`, `acceptEdits`, `plan`, `auto`, `dontAsk`, or `bypassPermissions`. Overrides `defaultMode` from settings files | `claude --permission-mode plan` |

78| `--permission-prompt-tool` | Specify an MCP tool to handle permission prompts in non-interactive mode | `claude -p --permission-prompt-tool mcp_auth_tool "query"` |78| `--permission-prompt-tool` | Specify an MCP tool to handle permission prompts in non-interactive mode | `claude -p --permission-prompt-tool mcp_auth_tool "query"` |

79| `--plugin-dir` | Load plugins from a directory for this session only. Each flag takes one path. Repeat the flag for multiple directories: `--plugin-dir A --plugin-dir B` | `claude --plugin-dir ./my-plugins` |79| `--plugin-dir` | Load plugins from a directory for this session only. Each flag takes one path. Repeat the flag for multiple directories: `--plugin-dir A --plugin-dir B` | `claude --plugin-dir ./my-plugins` |

80| `--print`, `-p` | Print response without interactive mode (see [Agent SDK documentation](https://platform.claude.com/docs/en/agent-sdk/overview) for programmatic usage details) | `claude -p "query"` |80| `--print`, `-p` | Print response without interactive mode (see [Agent SDK documentation](/en/agent-sdk/overview) for programmatic usage details) | `claude -p "query"` |

81| `--remote` | Create a new [web session](/en/claude-code-on-the-web) on claude.ai with the provided task description | `claude --remote "Fix the login bug"` |81| `--remote` | Create a new [web session](/en/claude-code-on-the-web) on claude.ai with the provided task description | `claude --remote "Fix the login bug"` |

82| `--remote-control`, `--rc` | Start an interactive session with [Remote Control](/en/remote-control#start-a-remote-control-session) enabled so you can also control it from claude.ai or the Claude app. Optionally pass a name for the session | `claude --remote-control "My Project"` |82| `--remote-control`, `--rc` | Start an interactive session with [Remote Control](/en/remote-control#start-a-remote-control-session) enabled so you can also control it from claude.ai or the Claude app. Optionally pass a name for the session | `claude --remote-control "My Project"` |

83| `--remote-control-session-name-prefix <prefix>` | Prefix for auto-generated [Remote Control](/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` |83| `--remote-control-session-name-prefix <prefix>` | Prefix for auto-generated [Remote Control](/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` |


119* [Quickstart guide](/en/quickstart) - Getting started with Claude Code119* [Quickstart guide](/en/quickstart) - Getting started with Claude Code

120* [Common workflows](/en/common-workflows) - Advanced workflows and patterns120* [Common workflows](/en/common-workflows) - Advanced workflows and patterns

121* [Settings](/en/settings) - Configuration options121* [Settings](/en/settings) - Configuration options

122* [Agent SDK documentation](https://platform.claude.com/docs/en/agent-sdk/overview) - Programmatic usage and integrations122* [Agent SDK documentation](/en/agent-sdk/overview) - Programmatic usage and integrations

commands.md +3 −1

Details

59| `/release-notes` | View the changelog in an interactive version picker. Select a specific version to see its release notes, or choose to show all versions |59| `/release-notes` | View the changelog in an interactive version picker. Select a specific version to see its release notes, or choose to show all versions |

60| `/reload-plugins` | Reload all active [plugins](/en/plugins) to apply pending changes without restarting. Reports counts for each reloaded component and flags any load errors |60| `/reload-plugins` | Reload all active [plugins](/en/plugins) to apply pending changes without restarting. Reports counts for each reloaded component and flags any load errors |

61| `/remote-control` | Make this session available for [remote control](/en/remote-control) from claude.ai. Alias: `/rc` |61| `/remote-control` | Make this session available for [remote control](/en/remote-control) from claude.ai. Alias: `/rc` |

62| `/remote-env` | Configure the default remote environment for [web sessions started with `--remote`](/en/claude-code-on-the-web#environment-configuration) |62| `/remote-env` | Configure the default remote environment for [web sessions started with `--remote`](/en/claude-code-on-the-web#configure-your-environment) |

63| `/rename [name]` | Rename the current session and show the name on the prompt bar. Without a name, auto-generates one from conversation history |63| `/rename [name]` | Rename the current session and show the name on the prompt bar. Without a name, auto-generates one from conversation history |

64| `/resume [session]` | Resume a conversation by ID or name, or open the session picker. Alias: `/continue` |64| `/resume [session]` | Resume a conversation by ID or name, or open the session picker. Alias: `/continue` |

65| `/review` | Deprecated. Install the [`code-review` plugin](https://github.com/anthropics/claude-plugins-official/tree/main/plugins/code-review) instead: `claude plugin install code-review@claude-plugins-official` |65| `/review` | Deprecated. Install the [`code-review` plugin](https://github.com/anthropics/claude-plugins-official/tree/main/plugins/code-review) instead: `claude plugin install code-review@claude-plugins-official` |


74| `/statusline` | Configure Claude Code's [status line](/en/statusline). Describe what you want, or run without arguments to auto-configure from your shell prompt |74| `/statusline` | Configure Claude Code's [status line](/en/statusline). Describe what you want, or run without arguments to auto-configure from your shell prompt |

75| `/stickers` | Order Claude Code stickers |75| `/stickers` | Order Claude Code stickers |

76| `/tasks` | List and manage background tasks. Also available as `/bashes` |76| `/tasks` | List and manage background tasks. Also available as `/bashes` |

77| `/teleport` | Pull a [Claude Code on the web](/en/claude-code-on-the-web#from-web-to-terminal) session into this terminal: opens a picker, then fetches the branch and conversation. Also available as `/tp`. Requires a claude.ai subscription |

77| `/terminal-setup` | Configure terminal keybindings for Shift+Enter and other shortcuts. Only visible in terminals that need it, like VS Code, Alacritty, or Warp |78| `/terminal-setup` | Configure terminal keybindings for Shift+Enter and other shortcuts. Only visible in terminals that need it, like VS Code, Alacritty, or Warp |

78| `/theme` | Change the color theme. Includes light and dark variants, colorblind-accessible (daltonized) themes, and ANSI themes that use your terminal's color palette |79| `/theme` | Change the color theme. Includes light and dark variants, colorblind-accessible (daltonized) themes, and ANSI themes that use your terminal's color palette |

79| `/ultraplan <prompt>` | Draft a plan in an [ultraplan](/en/ultraplan) session, review it in your browser, then execute remotely or send it back to your terminal |80| `/ultraplan <prompt>` | Draft a plan in an [ultraplan](/en/ultraplan) session, review it in your browser, then execute remotely or send it back to your terminal |


81| `/usage` | Show plan usage limits and rate limit status |82| `/usage` | Show plan usage limits and rate limit status |

82| `/vim` | {/* max-version: 2.1.91 */}Removed in v2.1.92. To toggle between Vim and Normal editing modes, use `/config` → Editor mode |83| `/vim` | {/* max-version: 2.1.91 */}Removed in v2.1.92. To toggle between Vim and Normal editing modes, use `/config` → Editor mode |

83| `/voice` | Toggle push-to-talk [voice dictation](/en/voice-dictation). Requires a Claude.ai account |84| `/voice` | Toggle push-to-talk [voice dictation](/en/voice-dictation). Requires a Claude.ai account |

85| `/web-setup` | Connect your GitHub account to [Claude Code on the web](/en/web-quickstart#connect-from-your-terminal) using your local `gh` CLI credentials. `/schedule` prompts for this automatically if GitHub isn't connected |

84 86 

85## MCP prompts87## MCP prompts

86 88 

Details

556 556 

557From inside an active session, use `/resume` to switch to a different conversation.557From inside an active session, use `/resume` to switch to a different conversation.

558 558 

559Sessions are stored per project directory. The `/resume` picker shows interactive sessions from the same git repository, including worktrees. Sessions created by `claude -p` or SDK invocations do not appear in the picker, but you can still resume one by passing its session ID directly to `claude --resume <session-id>`.559Sessions are stored per project directory. The `/resume` picker shows interactive sessions from the same git repository, including worktrees. When you select a session from another worktree of the same repository, Claude Code resumes it directly without requiring you to switch directories first. Sessions created by `claude -p` or SDK invocations do not appear in the picker, but you can still resume one by passing its session ID directly to `claude --resume <session-id>`.

560 560 

561### Name your sessions561### Name your sessions

562 562 

Details

173 tokens: 120,173 tokens: 120,

174 color: '#B8860B',174 color: '#B8860B',

175 vis: 'hidden',175 vis: 'hidden',

176 desc: 'A PostToolUse hook in `settings.json` runs prettier after every file edit and reports back via `hookSpecificOutput.additionalContext`. That field enters Claude\'s context. Plain stdout on exit 0 does not. It only appears in verbose mode via Ctrl+O.',176 desc: 'A PostToolUse hook in `settings.json` runs prettier after every file edit and reports back via `hookSpecificOutput.additionalContext`. That field enters Claude\'s context. Plain stdout on exit 0 does not. It is written to the debug log only.',

177 tip: 'Output JSON with `additionalContext` to send info to Claude. For PostToolUse hooks, exit code 2 surfaces stderr as an error but cannot block since the tool already ran. Keep output concise since it enters context without truncation.',177 tip: 'Output JSON with `additionalContext` to send info to Claude. For PostToolUse hooks, exit code 2 surfaces stderr as an error but cannot block since the tool already ran. Keep output concise since it enters context without truncation.',

178 link: '/en/hooks-guide'178 link: '/en/hooks-guide'

179 }, {179 }, {


617 if (detailRef.current) detailRef.current.scrollTop = 0;617 if (detailRef.current) detailRef.current.scrollTop = 0;

618 }, [hovEvent]);618 }, [hovEvent]);

619 const focusT = hovEvent ? hovEvent.t : time;619 const focusT = hovEvent ? hovEvent.t : time;

620 const takeaway = isCompacted ? 'Compaction replaces the conversation with a structured summary. System prompt, CLAUDE.md, memory, and MCP tools reload automatically. The skill listing is the one exception. Only skills you actually invoked are preserved.' : focusT < STARTUP_END ? 'A lot loads before you type anything. CLAUDE.md, memory, skills, and MCP tools are all in context before your first prompt.' : focusT < 0.28 ? "Your prompt is tiny compared to what's already loaded. Most of Claude's context is project knowledge, not your words." : focusT < 0.50 ? 'Each file Claude reads grows the context. Path-scoped rules load automatically alongside matching files.' : focusT < 0.71 ? 'Hooks fire automatically on tool events. Output reaches Claude via additionalContext JSON. Exit code 2 surfaces stderr to Claude. Plain stdout stays in verbose mode only.' : focusT < 0.79 ? 'Follow-up questions keep building on the same context. Everything from earlier is still there.' : focusT < 0.87 ? "The subagent works in its own separate context window. None of its file reads touch yours. Only the final summary comes back." : focusT < 0.88 ? 'Bang commands run in your shell and prefix the output to your next message. Useful for grounding Claude in command results without it running them.' : focusT < 0.90 ? 'User-only skills stay out of context entirely until you invoke them. The skill index at startup only lists skills Claude can call on its own.' : '/compact summarizes the conversation to free space while keeping key information. In a real session, run it when context starts affecting performance or before a long new task.';620 const takeaway = isCompacted ? 'Compaction replaces the conversation with a structured summary. System prompt, CLAUDE.md, memory, and MCP tools reload automatically. The skill listing is the one exception. Only skills you actually invoked are preserved.' : focusT < STARTUP_END ? 'A lot loads before you type anything. CLAUDE.md, memory, skills, and MCP tools are all in context before your first prompt.' : focusT < 0.28 ? "Your prompt is tiny compared to what's already loaded. Most of Claude's context is project knowledge, not your words." : focusT < 0.50 ? 'Each file Claude reads grows the context. Path-scoped rules load automatically alongside matching files.' : focusT < 0.71 ? 'Hooks fire automatically on tool events. Output reaches Claude via additionalContext JSON. Exit code 2 surfaces stderr to Claude. Plain stdout on exit 0 goes to the debug log, not the transcript.' : focusT < 0.79 ? 'Follow-up questions keep building on the same context. Everything from earlier is still there.' : focusT < 0.87 ? "The subagent works in its own separate context window. None of its file reads touch yours. Only the final summary comes back." : focusT < 0.88 ? 'Bang commands run in your shell and prefix the output to your next message. Useful for grounding Claude in command results without it running them.' : focusT < 0.90 ? 'User-only skills stay out of context entirely until you invoke them. The skill index at startup only lists skills Claude can call on its own.' : '/compact summarizes the conversation to free space while keeping key information. In a real session, run it when context starts affecting performance or before a long new task.';

621 const terminalView = isCompacted ? 'A "Conversation compacted" message. The summarization happens silently.' : focusT < STARTUP_END ? 'The input box, waiting for your first message. Everything above loads silently before you type anything.' : focusT < 0.28 ? 'Your prompt. Claude hasn\'t started working yet.' : focusT < 0.52 ? 'Your prompt and "Reading files...". Rules show as one-line "Loaded" notices, not their content.' : focusT < 0.72 ? "Claude's response and file diffs. Hooks fire silently. Tool output like npm test shows as a brief summary, not the full content." : focusT < 0.79 ? 'Your follow-up prompt.' : focusT < 0.86 ? "A brief notice that a subagent is working, then its result. You don't see the subagent's individual file reads." : focusT < 0.90 ? "Claude's response, your git status output, and the commit-push skill running." : 'Your full conversation. /compact is available to run.';621 const terminalView = isCompacted ? 'A "Conversation compacted" message. The summarization happens silently.' : focusT < STARTUP_END ? 'The input box, waiting for your first message. Everything above loads silently before you type anything.' : focusT < 0.28 ? 'Your prompt. Claude hasn\'t started working yet.' : focusT < 0.52 ? 'Your prompt and "Reading files...". Rules show as one-line "Loaded" notices, not their content.' : focusT < 0.72 ? "Claude's response and file diffs. Hooks fire silently. Tool output like npm test shows as a brief summary, not the full content." : focusT < 0.79 ? 'Your follow-up prompt.' : focusT < 0.86 ? "A brief notice that a subagent is working, then its result. You don't see the subagent's individual file reads." : focusT < 0.90 ? "Claude's response, your git status output, and the commit-push skill running." : 'Your full conversation. /compact is available to run.';

622 const mono = 'var(--font-mono, ui-monospace, SFMono-Regular, Menlo, monospace)';622 const mono = 'var(--font-mono, ui-monospace, SFMono-Regular, Menlo, monospace)';

623 const renderWithCode = s => s.split('`').map((part, i) => i % 2 === 1 ? <code key={i} style={{623 const renderWithCode = s => s.split('`').map((part, i) => i % 2 === 1 ? <code key={i} style={{

data-usage.md +2 −2

Details

43 43 

44* Standard: 30-day retention period44* Standard: 30-day retention period

45* [Zero data retention](/en/zero-data-retention): available for Claude Code on Claude for Enterprise. ZDR is enabled on a per-organization basis; each new organization must have ZDR enabled separately by your account team45* [Zero data retention](/en/zero-data-retention): available for Claude Code on Claude for Enterprise. ZDR is enabled on a per-organization basis; each new organization must have ZDR enabled separately by your account team

46* Local caching: Claude Code clients may store sessions locally for up to 30 days to enable session resumption (configurable)46* Local caching: Claude Code clients store session transcripts locally in plaintext under `~/.claude/projects/` for 30 days by default to enable session resumption. Adjust the period with `cleanupPeriodDays`. See [application data](/en/claude-directory#application-data) for what's stored and how to clear it.

47 47 

48You can delete individual Claude Code on the web sessions at any time. Deleting a session permanently removes the session's event data. For instructions on how to delete sessions, see [Managing sessions](/en/claude-code-on-the-web#managing-sessions).48You can delete individual Claude Code on the web sessions at any time. Deleting a session permanently removes the session's event data. For instructions on how to delete sessions, see [Delete sessions](/en/claude-code-on-the-web#delete-sessions).

49 49 

50Learn more about data retention practices in our [Privacy Center](https://privacy.anthropic.com/).50Learn more about data retention practices in our [Privacy Center](https://privacy.anthropic.com/).

51 51 

desktop.md +9 −7

Details

62| ---------------------- | ------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |62| ---------------------- | ------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |

63| **Ask permissions** | `default` | Claude asks before editing files or running commands. You see a diff and can accept or reject each change. Recommended for new users. |63| **Ask permissions** | `default` | Claude asks before editing files or running commands. You see a diff and can accept or reject each change. Recommended for new users. |

64| **Auto accept edits** | `acceptEdits` | Claude auto-accepts file edits but still asks before running terminal commands. Use this when you trust file changes and want faster iteration. |64| **Auto accept edits** | `acceptEdits` | Claude auto-accepts file edits but still asks before running terminal commands. Use this when you trust file changes and want faster iteration. |

65| **Plan mode** | `plan` | Claude analyzes your code and creates a plan without modifying files or running commands. Good for complex tasks where you want to review the approach first. |65| **Plan mode** | `plan` | Claude reads files and runs commands to explore, then proposes a plan without editing your source code. Good for complex tasks where you want to review the approach first. |

66| **Auto** | `auto` | Claude executes all actions with background safety checks that verify alignment with your request. Reduces permission prompts while maintaining oversight. Currently a research preview. Available on Team, Enterprise, and API plans. Requires Claude Sonnet 4.6 or Opus 4.6. Enable in your Settings → Claude Code. |66| **Auto** | `auto` | Claude executes all actions with background safety checks that verify alignment with your request. Reduces permission prompts while maintaining oversight. Currently a research preview. Available on Team, Enterprise, and API plans. Requires Claude Sonnet 4.6 or Opus 4.6. Enable in your Settings → Claude Code. |

67| **Bypass permissions** | `bypassPermissions` | Claude runs without any permission prompts, equivalent to `--dangerously-skip-permissions` in the CLI. Enable in your Settings → Claude Code under "Allow bypass permissions mode". Only use this in sandboxed containers or VMs. Enterprise admins can disable this option. |67| **Bypass permissions** | `bypassPermissions` | Claude runs without any permission prompts, equivalent to `--dangerously-skip-permissions` in the CLI. Enable in your Settings → Claude Code under "Allow bypass permissions mode". Only use this in sandboxed containers or VMs. Enterprise admins can disable this option. |

68 68 


309Each entry in the `configurations` array accepts the following fields:309Each entry in the `configurations` array accepts the following fields:

310 310 

311| Field | Type | Description |311| Field | Type | Description |

312| ------------------- | --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |312| ------------------- | --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |

313| `name` | string | A unique identifier for this server |313| `name` | string | A unique identifier for this server |

314| `runtimeExecutable` | string | The command to run, such as `npm`, `yarn`, or `node` |314| `runtimeExecutable` | string | The command to run, such as `npm`, `yarn`, or `node` |

315| `runtimeArgs` | string\[] | Arguments passed to `runtimeExecutable`, such as `["run", "dev"]` |315| `runtimeArgs` | string\[] | Arguments passed to `runtimeExecutable`, such as `["run", "dev"]` |

316| `port` | number | The port your server listens on. Defaults to 3000 |316| `port` | number | The port your server listens on. Defaults to 3000 |

317| `cwd` | string | Working directory relative to your project root. Defaults to the project root. Use `${workspaceFolder}` to reference the project root explicitly |317| `cwd` | string | Working directory relative to your project root. Defaults to the project root. Use `${workspaceFolder}` to reference the project root explicitly |

318| `env` | object | Additional environment variables as key-value pairs, such as `{ "NODE_ENV": "development" }`. Don't put secrets here since this file is committed to your repo. Secrets set in your shell profile are inherited automatically. |318| `env` | object | Additional environment variables as key-value pairs, such as `{ "NODE_ENV": "development" }`. Don't put secrets here since this file is committed to your repo. To pass secrets to your dev server, set them in the [local environment editor](#local-sessions) instead. |

319| `autoPort` | boolean | How to handle port conflicts. See below |319| `autoPort` | boolean | How to handle port conflicts. See below |

320| `program` | string | A script to run with `node`. See [when to use `program` vs `runtimeExecutable`](#when-to-use-program-vs-runtimeexecutable) |320| `program` | string | A script to run with `node`. See [when to use `program` vs `runtimeExecutable`](#when-to-use-program-vs-runtimeexecutable) |

321| `args` | string\[] | Arguments passed to `program`. Only used when `program` is set |321| `args` | string\[] | Arguments passed to `program`. Only used when `program` is set |


417 417 

418### Local sessions418### Local sessions

419 419 

420Local sessions inherit environment variables from your shell. If you need additional variables, set them in your shell profile, such as `~/.zshrc` or `~/.bashrc`, and restart the desktop app. See [environment variables](/en/env-vars) for the full list of supported variables.420The desktop app does not always inherit your full shell environment. On macOS, when you launch the app from the Dock or Finder, it reads your shell profile, such as `~/.zshrc` or `~/.bashrc`, to extract `PATH` and a fixed set of Claude Code variables, but other variables you export there are not picked up. On Windows, the app inherits user and system environment variables but does not read PowerShell profiles.

421 421 

422[Extended thinking](/en/common-workflows#use-extended-thinking-thinking-mode) is enabled by default, which improves performance on complex reasoning tasks but uses additional tokens. To disable thinking entirely, set `MAX_THINKING_TOKENS=0` in your shell profile. On Opus, `MAX_THINKING_TOKENS` is ignored except for `0` because adaptive reasoning controls thinking depth instead.422To set environment variables for local sessions and dev servers on any platform, open the environment dropdown in the prompt box, hover over **Local**, and click the gear icon to open the local environment editor. Variables you save here are stored encrypted on your machine and apply to every local session and preview server you start. You can also add variables to the `env` key in your `~/.claude/settings.json` file, though these reach Claude sessions only and not dev servers. See [environment variables](/en/env-vars) for the full list of supported variables.

423 

424[Extended thinking](/en/common-workflows#use-extended-thinking-thinking-mode) is enabled by default, which improves performance on complex reasoning tasks but uses additional tokens. To disable thinking entirely, set `MAX_THINKING_TOKENS` to `0` in the local environment editor. On Opus 4.6 and Sonnet 4.6, any other `MAX_THINKING_TOKENS` value is ignored because adaptive reasoning controls thinking depth instead. To use a fixed thinking budget on these models, also set `CLAUDE_CODE_DISABLE_ADAPTIVE_THINKING` to `1`.

423 425 

424### Remote sessions426### Remote sessions

425 427 

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

427 429 

428You can create custom cloud environments with different network access levels and environment variables. Select the environment dropdown when starting a remote session and choose **Add environment**. See [cloud environments](/en/claude-code-on-the-web#cloud-environment) for details on configuring network access and environment variables.430You can create custom cloud environments with different network access levels and environment variables. Select the environment dropdown when starting a remote session and choose **Add environment**. See [the cloud environment](/en/claude-code-on-the-web#the-cloud-environment) for details on configuring network access and environment variables.

429 431 

430### SSH sessions432### SSH sessions

431 433 


520| `--verbose` | Not available. Check system logs: Console.app on macOS, Event Viewer → Windows Logs → Application on Windows |522| `--verbose` | Not available. Check system logs: Console.app on macOS, Event Viewer → Windows Logs → Application on Windows |

521| `--print`, `--output-format` | Not available. Desktop is interactive only. |523| `--print`, `--output-format` | Not available. Desktop is interactive only. |

522| `ANTHROPIC_MODEL` env var | Model dropdown next to the send button |524| `ANTHROPIC_MODEL` env var | Model dropdown next to the send button |

523| `MAX_THINKING_TOKENS` env var | Set in shell profile; applies to local sessions. See [environment configuration](#environment-configuration). |525| `MAX_THINKING_TOKENS` env var | Set in the local environment editor. See [environment configuration](#environment-configuration). |

524 526 

525### Shared configuration527### Shared configuration

526 528 

devcontainer.md +2 −0

Details

333. Open the repository in VS Code333. Open the repository in VS Code

344. When prompted, click "Reopen in Container" (or use Command Palette: Cmd+Shift+P → "Dev Containers: Reopen in Container")344. When prompted, click "Reopen in Container" (or use Command Palette: Cmd+Shift+P → "Dev Containers: Reopen in Container")

35 35 

36Once the container finishes building, open a terminal in VS Code with `` Ctrl+` `` and run `claude` to authenticate and start your first session. The container has Claude Code preinstalled, so you can begin working immediately. Your project files are mounted into the container, and any code Claude writes appears in your local repository.

37 

36## Configuration breakdown38## Configuration breakdown

37 39 

38The devcontainer setup consists of three primary components:40The devcontainer setup consists of three primary components:

env-vars.md +4 −0

Details

14| `ANTHROPIC_AUTH_TOKEN` | Custom value for the `Authorization` header (the value you set here will be prefixed with `Bearer `) |14| `ANTHROPIC_AUTH_TOKEN` | Custom value for the `Authorization` header (the value you set here will be prefixed with `Bearer `) |

15| `ANTHROPIC_BASE_URL` | Override the API endpoint to route requests through a proxy or gateway. When set to a non-first-party host, [MCP tool search](/en/mcp#scale-with-mcp-tool-search) is disabled by default. Set `ENABLE_TOOL_SEARCH=true` if your proxy forwards `tool_reference` blocks |15| `ANTHROPIC_BASE_URL` | Override the API endpoint to route requests through a proxy or gateway. When set to a non-first-party host, [MCP tool search](/en/mcp#scale-with-mcp-tool-search) is disabled by default. Set `ENABLE_TOOL_SEARCH=true` if your proxy forwards `tool_reference` blocks |

16| `ANTHROPIC_BEDROCK_BASE_URL` | Override the Bedrock endpoint URL. Use for custom Bedrock endpoints or when routing through an [LLM gateway](/en/llm-gateway). See [Amazon Bedrock](/en/amazon-bedrock) |16| `ANTHROPIC_BEDROCK_BASE_URL` | Override the Bedrock endpoint URL. Use for custom Bedrock endpoints or when routing through an [LLM gateway](/en/llm-gateway). See [Amazon Bedrock](/en/amazon-bedrock) |

17| `ANTHROPIC_BEDROCK_MANTLE_BASE_URL` | Override the Bedrock Mantle endpoint URL. See [Mantle endpoint](/en/amazon-bedrock#use-the-mantle-endpoint) |

17| `ANTHROPIC_BETAS` | Comma-separated list of additional `anthropic-beta` header values to include in API requests. Claude Code already sends the beta headers it needs; use this to opt into an [Anthropic API beta](https://platform.claude.com/docs/en/api/beta-headers) before Claude Code adds native support. Unlike the [`--betas` flag](/en/cli-reference#cli-flags), which requires API key authentication, this variable works with all auth methods including Claude.ai subscription |18| `ANTHROPIC_BETAS` | Comma-separated list of additional `anthropic-beta` header values to include in API requests. Claude Code already sends the beta headers it needs; use this to opt into an [Anthropic API beta](https://platform.claude.com/docs/en/api/beta-headers) before Claude Code adds native support. Unlike the [`--betas` flag](/en/cli-reference#cli-flags), which requires API key authentication, this variable works with all auth methods including Claude.ai subscription |

18| `ANTHROPIC_CUSTOM_HEADERS` | Custom headers to add to requests (`Name: Value` format, newline-separated for multiple headers) |19| `ANTHROPIC_CUSTOM_HEADERS` | Custom headers to add to requests (`Name: Value` format, newline-separated for multiple headers) |

19| `ANTHROPIC_CUSTOM_MODEL_OPTION` | Model ID to add as a custom entry in the `/model` picker. Use this to make a non-standard or gateway-specific model selectable without replacing built-in aliases. See [Model configuration](/en/model-config#add-a-custom-model-option) |20| `ANTHROPIC_CUSTOM_MODEL_OPTION` | Model ID to add as a custom entry in the `/model` picker. Use this to make a non-standard or gateway-specific model selectable without replacing built-in aliases. See [Model configuration](/en/model-config#add-a-custom-model-option) |


44| `BASH_DEFAULT_TIMEOUT_MS` | Default timeout for long-running bash commands (default: 120000, or 2 minutes) |45| `BASH_DEFAULT_TIMEOUT_MS` | Default timeout for long-running bash commands (default: 120000, or 2 minutes) |

45| `BASH_MAX_OUTPUT_LENGTH` | Maximum number of characters in bash outputs before they are middle-truncated |46| `BASH_MAX_OUTPUT_LENGTH` | Maximum number of characters in bash outputs before they are middle-truncated |

46| `BASH_MAX_TIMEOUT_MS` | Maximum timeout the model can set for long-running bash commands (default: 600000, or 10 minutes) |47| `BASH_MAX_TIMEOUT_MS` | Maximum timeout the model can set for long-running bash commands (default: 600000, or 10 minutes) |

48| `CCR_FORCE_BUNDLE` | Set to `1` to force [`claude --remote`](/en/claude-code-on-the-web#send-local-repositories-without-github) to bundle and upload your local repository even when GitHub access is available |

47| `CLAUDECODE` | Set to `1` in shell environments Claude Code spawns (Bash tool, tmux sessions). Not set in [hooks](/en/hooks) or [status line](/en/statusline) commands. Use to detect when a script is running inside a shell spawned by Claude Code |49| `CLAUDECODE` | Set to `1` in shell environments Claude Code spawns (Bash tool, tmux sessions). Not set in [hooks](/en/hooks) or [status line](/en/statusline) commands. Use to detect when a script is running inside a shell spawned by Claude Code |

48| `CLAUDE_AGENT_SDK_DISABLE_BUILTIN_AGENTS` | Set to `1` to disable all built-in [subagent](/en/sub-agents) types such as Explore and Plan. Only applies in non-interactive mode (the `-p` flag). Useful for SDK users who want a blank slate |50| `CLAUDE_AGENT_SDK_DISABLE_BUILTIN_AGENTS` | Set to `1` to disable all built-in [subagent](/en/sub-agents) types such as Explore and Plan. Only applies in non-interactive mode (the `-p` flag). Useful for SDK users who want a blank slate |

49| `CLAUDE_AGENT_SDK_MCP_NO_PREFIX` | Set to `1` to skip the `mcp__<server>__` prefix on tool names from SDK-created MCP servers. Tools use their original names. SDK usage only |51| `CLAUDE_AGENT_SDK_MCP_NO_PREFIX` | Set to `1` to skip the `mcp__<server>__` prefix on tool names from SDK-created MCP servers. Tools use their original names. SDK usage only |


118| `CLAUDE_CODE_SIMPLE` | Set to `1` to run with a minimal system prompt and only the Bash, file read, and file edit tools. MCP tools from `--mcp-config` are still available. Disables auto-discovery of hooks, skills, plugins, MCP servers, auto memory, and CLAUDE.md. The [`--bare`](/en/headless#start-faster-with-bare-mode) CLI flag sets this |120| `CLAUDE_CODE_SIMPLE` | Set to `1` to run with a minimal system prompt and only the Bash, file read, and file edit tools. MCP tools from `--mcp-config` are still available. Disables auto-discovery of hooks, skills, plugins, MCP servers, auto memory, and CLAUDE.md. The [`--bare`](/en/headless#start-faster-with-bare-mode) CLI flag sets this |

119| `CLAUDE_CODE_SKIP_BEDROCK_AUTH` | Skip AWS authentication for Bedrock (for example, when using an LLM gateway) |121| `CLAUDE_CODE_SKIP_BEDROCK_AUTH` | Skip AWS authentication for Bedrock (for example, when using an LLM gateway) |

120| `CLAUDE_CODE_SKIP_FOUNDRY_AUTH` | Skip Azure authentication for Microsoft Foundry (for example, when using an LLM gateway) |122| `CLAUDE_CODE_SKIP_FOUNDRY_AUTH` | Skip Azure authentication for Microsoft Foundry (for example, when using an LLM gateway) |

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

121| `CLAUDE_CODE_SKIP_VERTEX_AUTH` | Skip Google authentication for Vertex (for example, when using an LLM gateway) |124| `CLAUDE_CODE_SKIP_VERTEX_AUTH` | Skip Google authentication for Vertex (for example, when using an LLM gateway) |

122| `CLAUDE_CODE_SUBAGENT_MODEL` | See [Model configuration](/en/model-config) |125| `CLAUDE_CODE_SUBAGENT_MODEL` | See [Model configuration](/en/model-config) |

123| `CLAUDE_CODE_SUBPROCESS_ENV_SCRUB` | Set to `1` to strip Anthropic and cloud provider credentials from subprocess environments (Bash tool, hooks, MCP stdio servers). The parent Claude process keeps these credentials for API calls, but child processes cannot read them, reducing exposure to prompt injection attacks that attempt to exfiltrate secrets via shell expansion. `claude-code-action` sets this automatically when `allowed_non_write_users` is configured |126| `CLAUDE_CODE_SUBPROCESS_ENV_SCRUB` | Set to `1` to strip Anthropic and cloud provider credentials from subprocess environments (Bash tool, hooks, MCP stdio servers). The parent Claude process keeps these credentials for API calls, but child processes cannot read them, reducing exposure to prompt injection attacks that attempt to exfiltrate secrets via shell expansion. `claude-code-action` sets this automatically when `allowed_non_write_users` is configured |


129| `CLAUDE_CODE_TMPDIR` | Override the temp directory used for internal temp files. Claude Code appends `/claude-{uid}/` (Unix) or `/claude/` (Windows) to this path. Default: `/tmp` on macOS, `os.tmpdir()` on Linux/Windows |132| `CLAUDE_CODE_TMPDIR` | Override the temp directory used for internal temp files. Claude Code appends `/claude-{uid}/` (Unix) or `/claude/` (Windows) to this path. Default: `/tmp` on macOS, `os.tmpdir()` on Linux/Windows |

130| `CLAUDE_CODE_USE_BEDROCK` | Use [Bedrock](/en/amazon-bedrock) |133| `CLAUDE_CODE_USE_BEDROCK` | Use [Bedrock](/en/amazon-bedrock) |

131| `CLAUDE_CODE_USE_FOUNDRY` | Use [Microsoft Foundry](/en/microsoft-foundry) |134| `CLAUDE_CODE_USE_FOUNDRY` | Use [Microsoft Foundry](/en/microsoft-foundry) |

135| `CLAUDE_CODE_USE_MANTLE` | Use the Bedrock [Mantle endpoint](/en/amazon-bedrock#use-the-mantle-endpoint) |

132| `CLAUDE_CODE_USE_POWERSHELL_TOOL` | Set to `1` to enable the PowerShell tool on Windows (opt-in preview). When enabled, Claude can run PowerShell commands natively instead of routing through Git Bash. Only supported on native Windows, not WSL. See [PowerShell tool](/en/tools-reference#powershell-tool) |136| `CLAUDE_CODE_USE_POWERSHELL_TOOL` | Set to `1` to enable the PowerShell tool on Windows (opt-in preview). When enabled, Claude can run PowerShell commands natively instead of routing through Git Bash. Only supported on native Windows, not WSL. See [PowerShell tool](/en/tools-reference#powershell-tool) |

133| `CLAUDE_CODE_USE_VERTEX` | Use [Vertex](/en/google-vertex-ai) |137| `CLAUDE_CODE_USE_VERTEX` | Use [Vertex](/en/google-vertex-ai) |

134| `CLAUDE_CONFIG_DIR` | Override the configuration directory (default: `~/.claude`). All settings, credentials, session history, and plugins are stored under this path. Useful for running multiple accounts side by side: for example, `alias claude-work='CLAUDE_CONFIG_DIR=~/.claude-work claude'` |138| `CLAUDE_CONFIG_DIR` | Override the configuration directory (default: `~/.claude`). All settings, credentials, session history, and plugins are stored under this path. Useful for running multiple accounts side by side: for example, `alias claude-work='CLAUDE_CONFIG_DIR=~/.claude-work claude'` |

fast-mode.md +1 −1

Details

124 124 

125## Handle rate limits125## Handle rate limits

126 126 

127Fast mode has separate rate limits from standard Opus 4.6. When you hit the fast mode rate limit or run out of extra usage credits:127Fast mode has separate rate limits from standard Opus 4.6. When you hit the fast mode rate limit or run out of extra usage:

128 128 

1291. Fast mode automatically falls back to standard Opus 4.61291. Fast mode automatically falls back to standard Opus 4.6

1302. The `↯` icon turns gray to indicate cooldown1302. The `↯` icon turns gray to indicate cooldown

Details

9Claude Code GitHub Actions brings AI-powered automation to your GitHub workflow. With a simple `@claude` mention in any PR or issue, Claude can analyze your code, create pull requests, implement features, and fix bugs - all while following your project's standards. For automatic reviews posted on every PR without a trigger, see [GitHub Code Review](/en/code-review).9Claude Code GitHub Actions brings AI-powered automation to your GitHub workflow. With a simple `@claude` mention in any PR or issue, Claude can analyze your code, create pull requests, implement features, and fix bugs - all while following your project's standards. For automatic reviews posted on every PR without a trigger, see [GitHub Code Review](/en/code-review).

10 10 

11<Note>11<Note>

12 Claude Code GitHub Actions is built on top of the [Claude Agent SDK](https://platform.claude.com/docs/en/agent-sdk/overview), which enables programmatic integration of Claude Code into your applications. You can use the SDK to build custom automation workflows beyond GitHub Actions.12 Claude Code GitHub Actions is built on top of the [Claude Agent SDK](/en/agent-sdk/overview), which enables programmatic integration of Claude Code into your applications. You can use the SDK to build custom automation workflows beyond GitHub Actions.

13</Note>13</Note>

14 14 

15<Info>15<Info>

Details

22| :--------------------- | :-------------- | :--------------------------------------------------------------------------------------------------------------------------- |22| :--------------------- | :-------------- | :--------------------------------------------------------------------------------------------------------------------------- |

23| Claude Code on the web | ✅ Supported | Admin connects the GHES instance once; developers use `claude --remote` or [claude.ai/code](https://claude.ai/code) as usual |23| Claude Code on the web | ✅ Supported | Admin connects the GHES instance once; developers use `claude --remote` or [claude.ai/code](https://claude.ai/code) as usual |

24| Code Review | ✅ Supported | Same automated PR reviews as github.com |24| Code Review | ✅ Supported | Same automated PR reviews as github.com |

25| Teleport sessions | ✅ Supported | Move sessions between web and terminal with `/teleport` |25| Teleport sessions | ✅ Supported | Move sessions between web and terminal with `--teleport` |

26| Plugin marketplaces | ✅ Supported | Use full git URLs instead of `owner/repo` shorthand |26| Plugin marketplaces | ✅ Supported | Use full git URLs instead of `owner/repo` shorthand |

27| Contribution metrics | ✅ Supported | Delivered via webhooks to the [analytics dashboard](/en/analytics) |27| Contribution metrics | ✅ Supported | Delivered via webhooks to the [analytics dashboard](/en/analytics) |

28| GitHub Actions | ✅ Supported | Requires manual workflow setup; `/install-github-app` is github.com only |28| GitHub Actions | ✅ Supported | Requires manual workflow setup; `/install-github-app` is github.com only |


101 101 

102### Teleport sessions to your terminal102### Teleport sessions to your terminal

103 103 

104Pull a web session into your local terminal with `/teleport` or `claude --teleport`. Teleport verifies you're in a checkout of the same GHES repository before fetching the branch and loading the session history. See [teleport requirements](/en/claude-code-on-the-web#requirements-for-teleporting) for details.104Pull a web session into your local terminal with `claude --teleport`. Teleport verifies you're in a checkout of the same GHES repository before fetching the branch and loading the session history. See [teleport requirements](/en/claude-code-on-the-web#teleport-requirements) for details.

105 105 

106## Plugin marketplaces on GHES106## Plugin marketplaces on GHES

107 107 

gitlab-ci-cd.md +1 −1

Details

13</Info>13</Info>

14 14 

15<Note>15<Note>

16 This integration is built on top of the [Claude Code CLI and Agent SDK](https://platform.claude.com/docs/en/agent-sdk/overview), enabling programmatic use of Claude in your CI/CD jobs and custom automation workflows.16 This integration is built on top of the [Claude Code CLI and Agent SDK](/en/agent-sdk/overview), enabling programmatic use of Claude in your CI/CD jobs and custom automation workflows.

17</Note>17</Note>

18 18 

19## Why use Claude Code with GitLab?19## Why use Claude Code with GitLab?

headless.md +4 −4

Details

6 6 

7> Use the Agent SDK to run Claude Code programmatically from the CLI, Python, or TypeScript.7> Use the Agent SDK to run Claude Code programmatically from the CLI, Python, or TypeScript.

8 8 

9The [Agent SDK](https://platform.claude.com/docs/en/agent-sdk/overview) gives you the same tools, agent loop, and context management that power Claude Code. It's available as a CLI for scripts and CI/CD, or as [Python](https://platform.claude.com/docs/en/agent-sdk/python) and [TypeScript](https://platform.claude.com/docs/en/agent-sdk/typescript) packages for full programmatic control.9The [Agent SDK](/en/agent-sdk/overview) gives you the same tools, agent loop, and context management that power Claude Code. It's available as a CLI for scripts and CI/CD, or as [Python](/en/agent-sdk/python) and [TypeScript](/en/agent-sdk/typescript) packages for full programmatic control.

10 10 

11<Note>11<Note>

12 The CLI was previously called "headless mode." The `-p` flag and all CLI options work the same way.12 The CLI was previously called "headless mode." The `-p` flag and all CLI options work the same way.


18claude -p "Find and fix the bug in auth.py" --allowedTools "Read,Edit,Bash"18claude -p "Find and fix the bug in auth.py" --allowedTools "Read,Edit,Bash"

19```19```

20 20 

21This page covers using the Agent SDK via the CLI (`claude -p`). For the Python and TypeScript SDK packages with structured outputs, tool approval callbacks, and native message objects, see the [full Agent SDK documentation](https://platform.claude.com/docs/en/agent-sdk/overview).21This page covers using the Agent SDK via the CLI (`claude -p`). For the Python and TypeScript SDK packages with structured outputs, tool approval callbacks, and native message objects, see the [full Agent SDK documentation](/en/agent-sdk/overview).

22 22 

23## Basic usage23## Basic usage

24 24 


134| `uuid` | string | unique event identifier |134| `uuid` | string | unique event identifier |

135| `session_id` | string | session the event belongs to |135| `session_id` | string | session the event belongs to |

136 136 

137For programmatic streaming with callbacks and message objects, see [Stream responses in real-time](https://platform.claude.com/docs/en/agent-sdk/streaming-output) in the Agent SDK documentation.137For programmatic streaming with callbacks and message objects, see [Stream responses in real-time](/en/agent-sdk/streaming-output) in the Agent SDK documentation.

138 138 

139### Auto-approve tools139### Auto-approve tools

140 140 


200 200 

201## Next steps201## Next steps

202 202 

203* [Agent SDK quickstart](https://platform.claude.com/docs/en/agent-sdk/quickstart): build your first agent with Python or TypeScript203* [Agent SDK quickstart](/en/agent-sdk/quickstart): build your first agent with Python or TypeScript

204* [CLI reference](/en/cli-reference): all CLI flags and options204* [CLI reference](/en/cli-reference): all CLI flags and options

205* [GitHub Actions](/en/github-actions): use the Agent SDK in GitHub workflows205* [GitHub Actions](/en/github-actions): use the Agent SDK in GitHub workflows

206* [GitLab CI/CD](/en/gitlab-ci-cd): use the Agent SDK in GitLab pipelines206* [GitLab CI/CD](/en/gitlab-ci-cd): use the Agent SDK in GitLab pipelines

hooks.md +7 −5

Details

499 499 

500The exit code from your hook command tells Claude Code whether the action should proceed, be blocked, or be ignored.500The exit code from your hook command tells Claude Code whether the action should proceed, be blocked, or be ignored.

501 501 

502**Exit 0** means success. Claude Code parses stdout for [JSON output fields](#json-output). JSON output is only processed on exit 0. For most events, stdout is only shown in verbose mode (`Ctrl+O`). The exceptions are `UserPromptSubmit` and `SessionStart`, where stdout is added as context that Claude can see and act on.502**Exit 0** means success. Claude Code parses stdout for [JSON output fields](#json-output). JSON output is only processed on exit 0. For most events, stdout is written to the debug log but not shown in the transcript. The exceptions are `UserPromptSubmit` and `SessionStart`, where stdout is added as context that Claude can see and act on.

503 503 

504**Exit 2** means a blocking error. Claude Code ignores stdout and any JSON in it. Instead, stderr text is fed back to Claude as an error message. The effect depends on the event: `PreToolUse` blocks the tool call, `UserPromptSubmit` rejects the prompt, and so on. See [exit code 2 behavior](#exit-code-2-behavior-per-event) for the full list.504**Exit 2** means a blocking error. Claude Code ignores stdout and any JSON in it. Instead, stderr text is fed back to Claude as an error message. The effect depends on the event: `PreToolUse` blocks the tool call, `UserPromptSubmit` rejects the prompt, and so on. See [exit code 2 behavior](#exit-code-2-behavior-per-event) for the full list.

505 505 

506**Any other exit code** is a non-blocking error for most hook events. stderr is shown in verbose mode (`Ctrl+O`) and execution continues.506**Any other exit code** is a non-blocking error for most hook events. The transcript shows a one-line `<hook name> hook error` notice and execution continues. The full stderr is written to the debug log.

507 507 

508For example, a hook command script that blocks dangerous Bash commands:508For example, a hook command script that blocks dangerous Bash commands:

509 509 


591| :--------------- | :------ | :------------------------------------------------------------------------------------------------------------------------- |591| :--------------- | :------ | :------------------------------------------------------------------------------------------------------------------------- |

592| `continue` | `true` | If `false`, Claude stops processing entirely after the hook runs. Takes precedence over any event-specific decision fields |592| `continue` | `true` | If `false`, Claude stops processing entirely after the hook runs. Takes precedence over any event-specific decision fields |

593| `stopReason` | none | Message shown to the user when `continue` is `false`. Not shown to Claude |593| `stopReason` | none | Message shown to the user when `continue` is `false`. Not shown to Claude |

594| `suppressOutput` | `false` | If `true`, hides stdout from verbose mode output |594| `suppressOutput` | `false` | If `true`, omits stdout from the debug log |

595| `systemMessage` | none | Warning message shown to the user |595| `systemMessage` | none | Warning message shown to the user |

596 596 

597To stop Claude entirely regardless of event type:597To stop Claude entirely regardless of event type:


833| `decision` | `"block"` prevents the prompt from being processed and erases it from context. Omit to allow the prompt to proceed |833| `decision` | `"block"` prevents the prompt from being processed and erases it from context. Omit to allow the prompt to proceed |

834| `reason` | Shown to the user when `decision` is `"block"`. Not added to context |834| `reason` | Shown to the user when `decision` is `"block"`. Not added to context |

835| `additionalContext` | String added to Claude's context |835| `additionalContext` | String added to Claude's context |

836| `sessionTitle` | Sets the session title, same effect as `/rename`. Use to name sessions automatically based on the prompt content |

836 837 

837```json theme={null}838```json theme={null}

838{839{


840 "reason": "Explanation for decision",841 "reason": "Explanation for decision",

841 "hookSpecificOutput": {842 "hookSpecificOutput": {

842 "hookEventName": "UserPromptSubmit",843 "hookEventName": "UserPromptSubmit",

843 "additionalContext": "My additional context here"844 "additionalContext": "My additional context here",

845 "sessionTitle": "My session title"

844 }846 }

845}847}

846```848```


2350 2352 

2351## Debug hooks2353## Debug hooks

2352 2354 

2353Run `claude --debug` to see hook execution details, including which hooks matched, their exit codes, and output.2355Hook execution details, including which hooks matched, their exit codes, and full stdout and stderr, are written to the debug log file. Start Claude Code with `claude --debug-file <path>` to write the log to a known location, or run `claude --debug` and read the log at `~/.claude/debug/<session-id>.txt`. The `--debug` flag does not print to the terminal.

2354 2356 

2355```text theme={null}2357```text theme={null}

2356[DEBUG] Executing hooks for PostToolUse:Write2358[DEBUG] Executing hooks for PostToolUse:Write

hooks-guide.md +5 −3

Details

468 468 

469* **Exit 0**: the action proceeds. For `UserPromptSubmit` and `SessionStart` hooks, anything you write to stdout is added to Claude's context.469* **Exit 0**: the action proceeds. For `UserPromptSubmit` and `SessionStart` hooks, anything you write to stdout is added to Claude's context.

470* **Exit 2**: the action is blocked. Write a reason to stderr, and Claude receives it as feedback so it can adjust.470* **Exit 2**: the action is blocked. Write a reason to stderr, and Claude receives it as feedback so it can adjust.

471* **Any other exit code**: the action proceeds. Stderr is logged but not shown to Claude. Toggle verbose mode with `Ctrl+O` to see these messages in the transcript.471* **Any other exit code**: the action proceeds. The transcript shows a one-line error notice; the full stderr goes to the [debug log](/en/hooks#debug-hooks).

472 472 

473#### Structured JSON output473#### Structured JSON output

474 474 


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

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

574 574 

575 The command below extracts the tool name from the hook's JSON input with `jq` and writes it to stderr, where it shows up in verbose mode (`Ctrl+O`):575 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):

576 576 

577 ```json theme={null}577 ```json theme={null}

578 {578 {


849 849 

850### Debug techniques850### Debug techniques

851 851 

852Toggle verbose mode with `Ctrl+O` to see hook output in the transcript, or run `claude --debug` for full execution details including which hooks matched and their exit codes.852The transcript view, toggled with `Ctrl+O`, shows a one-line summary for each hook that fired: success is silent, blocking errors show stderr, and non-blocking errors show only the hook name.

853 

854For full execution details including which hooks matched, their exit codes, stdout, and stderr, read the debug log. Start Claude Code with `claude --debug-file /tmp/claude.log` to write to a known path, then `tail -f /tmp/claude.log` in another terminal. If you started without that flag, run `/debug` mid-session to enable logging and find the log path.

853 855 

854## Learn more856## Learn more

855 857 

Details

94 94 

95## Work with sessions95## Work with sessions

96 96 

97Claude Code saves your conversation locally as you work. Each message, tool use, and result is stored, which enables [rewinding](#undo-changes-with-checkpoints), [resuming, and forking](#resume-or-fork-sessions) sessions. Before Claude makes code changes, it also snapshots the affected files so you can revert if needed.97Claude Code saves your conversation locally as you work. Each message, tool use, and result is written to a plaintext JSONL file under `~/.claude/projects/`, which enables [rewinding](#undo-changes-with-checkpoints), [resuming, and forking](#resume-or-fork-sessions) sessions. Before Claude makes code changes, it also snapshots the affected files so you can revert if needed. For paths, retention, and how to clear this data, see [application data in `~/.claude`](/en/claude-directory#application-data).

98 98 

99**Sessions are independent.** Each new session starts with a fresh context window, without the conversation history from previous sessions. Claude can persist learnings across sessions using [auto memory](/en/memory#auto-memory), and you can add your own persistent instructions in [CLAUDE.md](/en/memory).99**Sessions are independent.** Each new session starts with a fresh context window, without the conversation history from previous sessions. Claude can persist learnings across sessions using [auto memory](/en/memory#auto-memory), and you can add your own persistent instructions in [CLAUDE.md](/en/memory).

100 100 


134 134 

135To control what's preserved during compaction, add a "Compact Instructions" section to CLAUDE.md or run `/compact` with a focus (like `/compact focus on the API changes`).135To control what's preserved during compaction, add a "Compact Instructions" section to CLAUDE.md or run `/compact` with a focus (like `/compact focus on the API changes`).

136 136 

137If a single file or tool output is so large that context refills immediately after each summary, Claude Code stops auto-compacting after a few attempts and shows an error instead of looping. See [Auto-compaction stops with a thrashing error](/en/troubleshooting#auto-compaction-stops-with-a-thrashing-error) for recovery steps.

138 

137Run `/context` to see what's using space. MCP tool definitions are deferred by default and loaded on demand via [tool search](/en/mcp#scale-with-mcp-tool-search), so only tool names consume context until Claude uses a specific tool. Run `/mcp` to check per-server costs.139Run `/context` to see what's using space. MCP tool definitions are deferred by default and loaded on demand via [tool search](/en/mcp#scale-with-mcp-tool-search), so only tool names consume context until Claude uses a specific tool. Run `/mcp` to check per-server costs.

138 140 

139#### Manage context with skills and subagents141#### Manage context with skills and subagents

Details

28| `Ctrl+X Ctrl+K` | Kill all background agents. Press twice within 3 seconds to confirm | Background agent control |28| `Ctrl+X Ctrl+K` | Kill all background agents. Press twice within 3 seconds to confirm | Background agent control |

29| `Ctrl+D` | Exit Claude Code session | EOF signal |29| `Ctrl+D` | Exit Claude Code session | EOF signal |

30| `Ctrl+G` or `Ctrl+X Ctrl+E` | Open in default text editor | Edit your prompt or custom response in your default text editor. `Ctrl+X Ctrl+E` is the readline-native binding |30| `Ctrl+G` or `Ctrl+X Ctrl+E` | Open in default text editor | Edit your prompt or custom response in your default text editor. `Ctrl+X Ctrl+E` is the readline-native binding |

31| `Ctrl+L` | Redraw the screen | Repaints the current UI without clearing conversation history |31| `Ctrl+L` | Clear prompt input | Clears typed text, keeps conversation history |

32| `Ctrl+O` | Toggle verbose output | Shows detailed tool usage and execution. Also expands MCP read and search calls, which collapse to a single line like "Queried slack" by default |32| `Ctrl+O` | Toggle verbose output | Shows detailed tool usage and execution. Also expands MCP read and search calls, which collapse to a single line like "Queried slack" by default |

33| `Ctrl+R` | Reverse search command history | Search through previous commands interactively |33| `Ctrl+R` | Reverse search command history | Search through previous commands interactively |

34| `Ctrl+V` or `Cmd+V` (iTerm2) or `Alt+V` (Windows) | Paste image from clipboard | Inserts an `[Image #N]` chip at the cursor so you can reference it positionally in your prompt |34| `Ctrl+V` or `Cmd+V` (iTerm2) or `Alt+V` (Windows) | Paste image from clipboard | Inserts an `[Image #N]` chip at the cursor so you can reference it positionally in your prompt |

keybindings.md +3 −2

Details

76Actions available in the `Global` context:76Actions available in the `Global` context:

77 77 

78| Action | Default | Description |78| Action | Default | Description |

79| :--------------------- | :------ | :-------------------------- |79| :--------------------- | :-------- | :-------------------------- |

80| `app:interrupt` | Ctrl+C | Cancel current operation |80| `app:interrupt` | Ctrl+C | Cancel current operation |

81| `app:exit` | Ctrl+D | Exit Claude Code |81| `app:exit` | Ctrl+D | Exit Claude Code |

82| `app:redraw` | Ctrl+L | Redraw the screen |82| `app:redraw` | (unbound) | Force terminal redraw |

83| `app:toggleTodos` | Ctrl+T | Toggle task list visibility |83| `app:toggleTodos` | Ctrl+T | Toggle task list visibility |

84| `app:toggleTranscript` | Ctrl+O | Toggle verbose transcript |84| `app:toggleTranscript` | Ctrl+O | Toggle verbose transcript |

85 85 


100| Action | Default | Description |100| Action | Default | Description |

101| :-------------------- | :------------------------ | :---------------------------------- |101| :-------------------- | :------------------------ | :---------------------------------- |

102| `chat:cancel` | Escape | Cancel current input |102| `chat:cancel` | Escape | Cancel current input |

103| `chat:clearInput` | Ctrl+L | Clear prompt input |

103| `chat:killAgents` | Ctrl+X Ctrl+K | Kill all background agents |104| `chat:killAgents` | Ctrl+X Ctrl+K | Kill all background agents |

104| `chat:cycleMode` | Shift+Tab\* | Cycle permission modes |105| `chat:cycleMode` | Shift+Tab\* | Cycle permission modes |

105| `chat:modelPicker` | Cmd+P / Meta+P | Open model picker |106| `chat:modelPicker` | Cmd+P / Meta+P | Open model picker |

mcp.md +25 −18

Details

426 426 

427## MCP installation scopes427## MCP installation scopes

428 428 

429MCP servers can be configured at three different scope levels, each serving distinct purposes for managing server accessibility and sharing. Understanding these scopes helps you determine the best way to configure servers for your specific needs.429MCP servers can be configured at three scopes. The scope you choose controls which projects the server loads in and whether the configuration is shared with your team.

430 

431| Scope | Loads in | Shared with team | Stored in |

432| ------------------------- | -------------------- | ------------------------ | --------------------------- |

433| [Local](#local-scope) | Current project only | No | `~/.claude.json` |

434| [Project](#project-scope) | Current project only | Yes, via version control | `.mcp.json` in project root |

435| [User](#user-scope) | All your projects | No | `~/.claude.json` |

430 436 

431### Local scope437### Local scope

432 438 

433Local-scoped servers represent the default configuration level and are stored in `~/.claude.json` under your project's path. These servers remain private to you and are only accessible when working within the current project directory. This scope is ideal for personal development servers, experimental configurations, or servers containing sensitive credentials that shouldn't be shared.439Local scope is the default. A local-scoped server loads only in the project where you added it and stays private to you. Claude Code stores it in `~/.claude.json` under that project's path, so the same server won't appear in your other projects. Use local scope for personal development servers, experimental configurations, or servers with credentials you don't want in version control.

434 440 

435<Note>441<Note>

436 The term "local scope" for MCP servers differs from general local settings. MCP local-scoped servers are stored in `~/.claude.json` (your home directory), while general local settings use `.claude/settings.local.json` (in the project directory). See [Settings](/en/settings#settings-files) for details on settings file locations.442 The term "local scope" for MCP servers differs from general local settings. MCP local-scoped servers are stored in `~/.claude.json` (your home directory), while general local settings use `.claude/settings.local.json` (in the project directory). See [Settings](/en/settings#settings-files) for details on settings file locations.


444claude mcp add --transport http stripe --scope local https://mcp.stripe.com450claude mcp add --transport http stripe --scope local https://mcp.stripe.com

445```451```

446 452 

453The command writes the server into the entry for your current project inside `~/.claude.json`. The example below shows the result when you run it from `/path/to/your/project`:

454 

455```json theme={null}

456{

457 "projects": {

458 "/path/to/your/project": {

459 "mcpServers": {

460 "stripe": {

461 "type": "http",

462 "url": "https://mcp.stripe.com"

463 }

464 }

465 }

466 }

467}

468```

469 

447### Project scope470### Project scope

448 471 

449Project-scoped servers enable team collaboration by storing configurations in a `.mcp.json` file at your project's root directory. This file is designed to be checked into version control, ensuring all team members have access to the same MCP tools and services. When you add a project-scoped server, Claude Code automatically creates or updates this file with the appropriate configuration structure.472Project-scoped servers enable team collaboration by storing configurations in a `.mcp.json` file at your project's root directory. This file is designed to be checked into version control, ensuring all team members have access to the same MCP tools and services. When you add a project-scoped server, Claude Code automatically creates or updates this file with the appropriate configuration structure.


478claude mcp add --transport http hubspot --scope user https://mcp.hubspot.com/anthropic501claude mcp add --transport http hubspot --scope user https://mcp.hubspot.com/anthropic

479```502```

480 503 

481### Choosing the right scope

482 

483Select your scope based on:

484 

485* **Local scope**: Personal servers, experimental configurations, or sensitive credentials specific to one project

486* **Project scope**: Team-shared servers, project-specific tools, or services required for collaboration

487* **User scope**: Personal utilities needed across multiple projects, development tools, or frequently used services

488 

489<Note>

490 **Where are MCP servers stored?**

491 

492 * **User and local scope**: `~/.claude.json` (in the `mcpServers` field or under project paths)

493 * **Project scope**: `.mcp.json` in your project root (checked into source control)

494 * **Managed**: `managed-mcp.json` in system directories (see [Managed MCP configuration](#managed-mcp-configuration))

495</Note>

496 

497### Scope hierarchy and precedence504### Scope hierarchy and precedence

498 505 

499MCP server configurations follow a clear precedence hierarchy. When servers with the same name exist at multiple scopes, the system resolves conflicts by prioritizing local-scoped servers first, followed by project-scoped servers, and finally user-scoped servers. This design ensures that personal configurations can override shared ones when needed.506MCP server configurations follow a clear precedence hierarchy. When servers with the same name exist at multiple scopes, the system resolves conflicts by prioritizing local-scoped servers first, followed by project-scoped servers, and finally user-scoped servers. This design ensures that personal configurations can override shared ones when needed.

model-config.md +7 −3

Details

112 112 

113When `availableModels` is set at multiple levels, such as user settings and project settings, arrays are merged and deduplicated. To enforce a strict allowlist, set `availableModels` in managed or policy settings which take highest priority.113When `availableModels` is set at multiple levels, such as user settings and project settings, arrays are merged and deduplicated. To enforce a strict allowlist, set `availableModels` in managed or policy settings which take highest priority.

114 114 

115### Mantle model IDs

116 

117When the [Bedrock Mantle endpoint](/en/amazon-bedrock#use-the-mantle-endpoint) is enabled, entries in `availableModels` that start with `anthropic.` are added to the `/model` picker as custom options and routed to the Mantle endpoint. This is an exception to the alias-only matching described in [Pin models for third-party deployments](#pin-models-for-third-party-deployments). The setting still restricts the picker to listed entries, so include the standard aliases alongside any Mantle IDs.

118 

115## Special model behavior119## Special model behavior

116 120 

117### `default` model setting121### `default` model setting


142 146 

143Three levels persist across sessions: **low**, **medium**, and **high**. A fourth level, **max**, provides the deepest reasoning with no constraint on token spending, so responses are slower and cost more than at `high`. `max` is available on Opus 4.6 only and does not persist across sessions except through the `CLAUDE_CODE_EFFORT_LEVEL` environment variable.147Three levels persist across sessions: **low**, **medium**, and **high**. A fourth level, **max**, provides the deepest reasoning with no constraint on token spending, so responses are slower and cost more than at `high`. `max` is available on Opus 4.6 only and does not persist across sessions except through the `CLAUDE_CODE_EFFORT_LEVEL` environment variable.

144 148 

145Opus 4.6 and Sonnet 4.6 default to medium effort. This applies to all providers, including Bedrock, Vertex AI, and direct API access.149The default effort level depends on your plan. Pro and Max subscribers default to medium effort. All other users default to high effort: API key, Team, Enterprise, and third-party provider (Bedrock, Vertex AI, Foundry) users.

146 150 

147Medium is the recommended level for most coding tasks: it balances speed and reasoning depth, and higher levels can cause the model to overthink routine work. Reserve `high` or `max` for tasks that genuinely benefit from deeper reasoning, such as hard debugging problems or complex architectural decisions.151Your plan's default suits most coding tasks. Raise effort for work that benefits from deeper reasoning, such as hard debugging problems or complex architectural decisions. Higher levels can cause the model to overthink routine work.

148 152 

149For one-off deep reasoning without changing your session setting, include "ultrathink" in your prompt to trigger high effort for that turn.153For one-off deep reasoning without changing your session setting, include "ultrathink" in your prompt to trigger high effort for that turn. This has no effect if your session is already at high or max.

150 154 

151**Setting effort:**155**Setting effort:**

152 156 

Details

88```88```

89 89 

90You can save these files at the user level (`~/.claude/output-styles`) or90You can save these files at the user level (`~/.claude/output-styles`) or

91project level (`.claude/output-styles`).91project level (`.claude/output-styles`). [Plugins](/en/plugins-reference) can

92also ship output styles in an `output-styles/` directory.

92 93 

93### Frontmatter94### Frontmatter

94 95 

overview.md +8 −8

Details

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

23 **macOS, Linux, WSL:**23 **macOS, Linux, WSL:**

24 24 

25 ```bash theme={null}25 ```bash theme={null} theme={null} theme={null} theme={null} theme={null} theme={null} theme={null} theme={null} theme={null} theme={null} theme={null} theme={null} theme={null} theme={null} theme={null}

26 curl -fsSL https://claude.ai/install.sh | bash26 curl -fsSL https://claude.ai/install.sh | bash

27 ```27 ```

28 28 

29 **Windows PowerShell:**29 **Windows PowerShell:**

30 30 

31 ```powershell theme={null}31 ```powershell theme={null} theme={null} theme={null} theme={null} theme={null} theme={null} theme={null} theme={null} theme={null} theme={null} theme={null} theme={null} theme={null} theme={null} theme={null}

32 irm https://claude.ai/install.ps1 | iex32 irm https://claude.ai/install.ps1 | iex

33 ```33 ```

34 34 

35 **Windows CMD:**35 **Windows CMD:**

36 36 

37 ```batch theme={null}37 ```batch theme={null} theme={null} theme={null} theme={null} theme={null} theme={null} theme={null} theme={null} theme={null} theme={null} theme={null} theme={null} theme={null} theme={null} theme={null}

38 curl -fsSL https://claude.ai/install.cmd -o install.cmd && install.cmd && del install.cmd38 curl -fsSL https://claude.ai/install.cmd -o install.cmd && install.cmd && del install.cmd

39 ```39 ```

40 40 


48 </Tab>48 </Tab>

49 49 

50 <Tab title="Homebrew">50 <Tab title="Homebrew">

51 ```bash theme={null}51 ```bash theme={null} theme={null} theme={null} theme={null} theme={null} theme={null} theme={null} theme={null} theme={null} theme={null} theme={null} theme={null} theme={null} theme={null} theme={null}

52 brew install --cask claude-code52 brew install --cask claude-code

53 ```53 ```

54 54 


58 </Tab>58 </Tab>

59 59 

60 <Tab title="WinGet">60 <Tab title="WinGet">

61 ```powershell theme={null}61 ```powershell theme={null} theme={null} theme={null} theme={null} theme={null} theme={null} theme={null} theme={null} theme={null} theme={null} theme={null} theme={null} theme={null} theme={null} theme={null}

62 winget install Anthropic.ClaudeCode62 winget install Anthropic.ClaudeCode

63 ```63 ```

64 64 


112 112 

113 Start coding at [claude.ai/code](https://claude.ai/code).113 Start coding at [claude.ai/code](https://claude.ai/code).

114 114 

115 [Get started on the web →](/en/claude-code-on-the-web#getting-started)115 [Get started on the web →](/en/web-quickstart)

116 </Tab>116 </Tab>

117 117 

118 <Tab title="JetBrains">118 <Tab title="JetBrains">


168 <Accordion title="Run agent teams and build custom agents" icon="users">168 <Accordion title="Run agent teams and build custom agents" icon="users">

169 Spawn [multiple Claude Code agents](/en/sub-agents) that work on different parts of a task simultaneously. A lead agent coordinates the work, assigns subtasks, and merges results.169 Spawn [multiple Claude Code agents](/en/sub-agents) that work on different parts of a task simultaneously. A lead agent coordinates the work, assigns subtasks, and merges results.

170 170 

171 For fully custom workflows, the [Agent SDK](https://platform.claude.com/docs/en/agent-sdk/overview) lets you build your own agents powered by Claude Code's tools and capabilities, with full control over orchestration, tool access, and permissions.171 For fully custom workflows, the [Agent SDK](/en/agent-sdk/overview) lets you build your own agents powered by Claude Code's tools and capabilities, with full control over orchestration, tool access, and permissions.

172 </Accordion>172 </Accordion>

173 173 

174 <Accordion title="Pipe, script, and automate with the CLI" icon="terminal">174 <Accordion title="Pipe, script, and automate with the CLI" icon="terminal">


223| Get automatic code review on every PR | [GitHub Code Review](/en/code-review) |223| Get automatic code review on every PR | [GitHub Code Review](/en/code-review) |

224| Route bug reports from Slack to pull requests | [Slack](/en/slack) |224| Route bug reports from Slack to pull requests | [Slack](/en/slack) |

225| Debug live web applications | [Chrome](/en/chrome) |225| Debug live web applications | [Chrome](/en/chrome) |

226| Build custom agents for your own workflows | [Agent SDK](https://platform.claude.com/docs/en/agent-sdk/overview) |226| Build custom agents for your own workflows | [Agent SDK](/en/agent-sdk/overview) |

227 227 

228## Next steps228## Next steps

229 229 

platforms.md +5 −3

Details

4 4 

5# Platforms and integrations5# Platforms and integrations

6 6 

7> Choose where to run Claude Code and what to connect it to. Compare the CLI, Desktop, VS Code, JetBrains, web, and integrations like Chrome, Slack, and CI/CD.7> Choose where to run Claude Code and what to connect it to. Compare the CLI, Desktop, VS Code, JetBrains, web, mobile, and integrations like Chrome, Slack, and CI/CD.

8 8 

9Claude Code runs the same underlying engine everywhere, but each surface is tuned for a different way of working. This page helps you pick the right platform for your workflow and connect the tools you already use.9Claude Code runs the same underlying engine everywhere, but each surface is tuned for a different way of working. This page helps you pick the right platform for your workflow and connect the tools you already use.

10 10 


13Choose a platform based on how you like to work and where your project lives.13Choose a platform based on how you like to work and where your project lives.

14 14 

15| Platform | Best for | What you get |15| Platform | Best for | What you get |

16| :-------------------------------- | :------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------- |16| :-------------------------------- | :------------------------------------------------------------------------------------------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |

17| [CLI](/en/quickstart) | Terminal workflows, scripting, remote servers | Full feature set, [Agent SDK](/en/headless), [computer use](/en/computer-use) on macOS (Pro and Max), third-party providers |17| [CLI](/en/quickstart) | Terminal workflows, scripting, remote servers | Full feature set, [Agent SDK](/en/headless), [computer use](/en/computer-use) on macOS (Pro and Max), third-party providers |

18| [Desktop](/en/desktop) | Visual review, parallel sessions, managed setup | Diff viewer, app preview, [computer use](/en/desktop#let-claude-use-your-computer) and [Dispatch](/en/desktop#sessions-from-dispatch) on Pro and Max |18| [Desktop](/en/desktop) | Visual review, parallel sessions, managed setup | Diff viewer, app preview, [computer use](/en/desktop#let-claude-use-your-computer) and [Dispatch](/en/desktop#sessions-from-dispatch) on Pro and Max |

19| [VS Code](/en/vs-code) | Working inside VS Code without switching to a terminal | Inline diffs, integrated terminal, file context |19| [VS Code](/en/vs-code) | Working inside VS Code without switching to a terminal | Inline diffs, integrated terminal, file context |

20| [JetBrains](/en/jetbrains) | Working inside IntelliJ, PyCharm, WebStorm, or other JetBrains IDEs | Diff viewer, selection sharing, terminal session |20| [JetBrains](/en/jetbrains) | Working inside IntelliJ, PyCharm, WebStorm, or other JetBrains IDEs | Diff viewer, selection sharing, terminal session |

21| [Web](/en/claude-code-on-the-web) | Long-running tasks that don't need much steering, or work that should continue when you're offline | Anthropic-managed cloud, continues after you disconnect |21| [Web](/en/claude-code-on-the-web) | Long-running tasks that don't need much steering, or work that should continue when you're offline | Anthropic-managed cloud, continues after you disconnect |

22| Mobile | Starting and monitoring tasks while away from your computer | Cloud sessions from the Claude app for iOS and Android, [Remote Control](/en/remote-control) for local sessions, [Dispatch](/en/desktop#sessions-from-dispatch) to Desktop on Pro and Max |

22 23 

23The CLI is the most complete surface for terminal-native work: scripting, third-party providers, and the Agent SDK are CLI-only. Desktop and the IDE extensions trade some CLI-only features for visual review and tighter editor integration. The web runs in Anthropic's cloud, so tasks keep going after you disconnect.24The CLI is the most complete surface for terminal-native work: scripting, third-party providers, and the Agent SDK are CLI-only. Desktop and the IDE extensions trade some CLI-only features for visual review and tighter editor integration. The web runs in Anthropic's cloud, so tasks keep going after you disconnect. Mobile is a thin client into those same cloud sessions or into a local session via Remote Control, and can send tasks to Desktop with Dispatch.

24 25 

25You can mix surfaces on the same project. Configuration, project memory, and MCP servers are shared across the local surfaces.26You can mix surfaces on the same project. Configuration, project memory, and MCP servers are shared across the local surfaces.

26 27 


61* [VS Code](/en/vs-code): the Claude Code extension inside your editor62* [VS Code](/en/vs-code): the Claude Code extension inside your editor

62* [JetBrains](/en/jetbrains): the extension for IntelliJ, PyCharm, and other JetBrains IDEs63* [JetBrains](/en/jetbrains): the extension for IntelliJ, PyCharm, and other JetBrains IDEs

63* [Claude Code on the web](/en/claude-code-on-the-web): cloud sessions that keep running when you disconnect64* [Claude Code on the web](/en/claude-code-on-the-web): cloud sessions that keep running when you disconnect

65* Mobile: the Claude app for [iOS](https://apps.apple.com/us/app/claude-by-anthropic/id6473753684) and [Android](https://play.google.com/store/apps/details?id=com.anthropic.claude) for starting and monitoring tasks while away from your computer

64 66 

65### Integrations67### Integrations

66 68 

Details

403* Components from custom paths use the same naming and namespacing rules403* Components from custom paths use the same naming and namespacing rules

404* Multiple paths can be specified as arrays404* Multiple paths can be specified as arrays

405* To keep the default directory and add more paths for commands, agents, skills, or output styles, include the default in your array: `"commands": ["./commands/", "./extras/deploy.md"]`405* To keep the default directory and add more paths for commands, agents, skills, or output styles, include the default in your array: `"commands": ["./commands/", "./extras/deploy.md"]`

406* When a skill path points to a directory that contains a `SKILL.md` directly, for example `"skills": ["./"]` pointing to the plugin root, the frontmatter `name` field in `SKILL.md` determines the skill's invocation name. This gives a stable name regardless of the install directory. If `name` is not set in the frontmatter, the directory basename is used as a fallback.

406 407 

407**Path examples**:408**Path examples**:

408 409 


500 501 

501For security and verification purposes, Claude Code copies *marketplace* plugins to the user's local **plugin cache** (`~/.claude/plugins/cache`) rather than using them in-place. Understanding this behavior is important when developing plugins that reference external files.502For security and verification purposes, Claude Code copies *marketplace* plugins to the user's local **plugin cache** (`~/.claude/plugins/cache`) rather than using them in-place. Understanding this behavior is important when developing plugins that reference external files.

502 503 

504Each installed version is a separate directory in the cache. When you update or uninstall a plugin, the previous version directory is marked as orphaned and removed automatically 7 days later. The grace period lets concurrent Claude Code sessions that already loaded the old version keep running without errors.

505 

503### Path traversal limitations506### Path traversal limitations

504 507 

505Installed plugins cannot reference files outside their directory. Paths that traverse outside the plugin root (such as `../shared-utils`) will not work after installation because those external files are not copied to the cache.508Installed plugins cannot reference files outside their directory. Paths that traverse outside the plugin root (such as `../shared-utils`) will not work after installation because those external files are not copied to the cache.

settings.md +1 −1

Details

172| `awsCredentialExport` | Custom script that outputs JSON with AWS credentials (see [advanced credential configuration](/en/amazon-bedrock#advanced-credential-configuration)) | `/bin/generate_aws_grant.sh` |172| `awsCredentialExport` | Custom script that outputs JSON with AWS credentials (see [advanced credential configuration](/en/amazon-bedrock#advanced-credential-configuration)) | `/bin/generate_aws_grant.sh` |

173| `blockedMarketplaces` | (Managed settings only) Blocklist of marketplace sources. Blocked sources are checked before downloading, so they never touch the filesystem. See [Managed marketplace restrictions](/en/plugin-marketplaces#managed-marketplace-restrictions) | `[{ "source": "github", "repo": "untrusted/plugins" }]` |173| `blockedMarketplaces` | (Managed settings only) Blocklist of marketplace sources. Blocked sources are checked before downloading, so they never touch the filesystem. See [Managed marketplace restrictions](/en/plugin-marketplaces#managed-marketplace-restrictions) | `[{ "source": "github", "repo": "untrusted/plugins" }]` |

174| `channelsEnabled` | (Managed settings only) Allow [channels](/en/channels) for Team and Enterprise users. Unset or `false` blocks channel message delivery regardless of what users pass to `--channels` | `true` |174| `channelsEnabled` | (Managed settings only) Allow [channels](/en/channels) for Team and Enterprise users. Unset or `false` blocks channel message delivery regardless of what users pass to `--channels` | `true` |

175| `cleanupPeriodDays` | Sessions inactive for longer than this period are deleted at startup (default: 30 days, minimum 1). Setting to `0` is rejected with a validation error. Also controls the age cutoff for automatic removal of [orphaned subagent worktrees](/en/common-workflows#worktree-cleanup) at startup. To disable transcript writes entirely in non-interactive mode (`-p`), use the `--no-session-persistence` flag or the `persistSession: false` SDK option; there is no interactive-mode equivalent. | `20` |175| `cleanupPeriodDays` | Session files older than this period are deleted at startup (default: 30 days, minimum 1). Setting to `0` is rejected with a validation error. Also controls the age cutoff for automatic removal of [orphaned subagent worktrees](/en/common-workflows#worktree-cleanup) at startup. To disable transcript writes entirely in non-interactive mode (`-p`), use the `--no-session-persistence` flag or the `persistSession: false` SDK option; there is no interactive-mode equivalent. | `20` |

176| `companyAnnouncements` | Announcement to display to users at startup. If multiple announcements are provided, they will be cycled through at random. | `["Welcome to Acme Corp! Review our code guidelines at docs.acme.com"]` |176| `companyAnnouncements` | Announcement to display to users at startup. If multiple announcements are provided, they will be cycled through at random. | `["Welcome to Acme Corp! Review our code guidelines at docs.acme.com"]` |

177| `defaultShell` | Default shell for input-box `!` commands. Accepts `"bash"` (default) or `"powershell"`. Setting `"powershell"` routes interactive `!` commands through PowerShell on Windows. Requires `CLAUDE_CODE_USE_POWERSHELL_TOOL=1`. See [PowerShell tool](/en/tools-reference#powershell-tool) | `"powershell"` |177| `defaultShell` | Default shell for input-box `!` commands. Accepts `"bash"` (default) or `"powershell"`. Setting `"powershell"` routes interactive `!` commands through PowerShell on Windows. Requires `CLAUDE_CODE_USE_POWERSHELL_TOOL=1`. See [PowerShell tool](/en/tools-reference#powershell-tool) | `"powershell"` |

178| `deniedMcpServers` | When set in managed-settings.json, denylist of MCP servers that are explicitly blocked. Applies to all scopes including managed servers. Denylist takes precedence over allowlist. See [Managed MCP configuration](/en/mcp#managed-mcp-configuration) | `[{ "serverName": "filesystem" }]` |178| `deniedMcpServers` | When set in managed-settings.json, denylist of MCP servers that are explicitly blocked. Applies to all scopes including managed servers. Denylist takes precedence over allowlist. See [Managed MCP configuration](/en/mcp#managed-mcp-configuration) | `[{ "serverName": "filesystem" }]` |

setup.md +6 −6

Details

18 * Ubuntu 20.04+18 * Ubuntu 20.04+

19 * Debian 10+19 * Debian 10+

20 * Alpine Linux 3.19+20 * Alpine Linux 3.19+

21* **Hardware**: 4 GB+ RAM21* **Hardware**: 4 GB+ RAM, x64 or ARM64 processor

22* **Network**: internet connection required. See [network configuration](/en/network-config#network-access-requirements).22* **Network**: internet connection required. See [network configuration](/en/network-config#network-access-requirements).

23* **Shell**: Bash, Zsh, PowerShell, or CMD. On Windows, [Git for Windows](https://git-scm.com/downloads/win) is required.23* **Shell**: Bash, Zsh, PowerShell, or CMD. On Windows, [Git for Windows](https://git-scm.com/downloads/win) is required.

24* **Location**: [Anthropic supported countries](https://www.anthropic.com/supported-countries)24* **Location**: [Anthropic supported countries](https://www.anthropic.com/supported-countries)


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}44 ```bash theme={null} theme={null} theme={null} theme={null} theme={null} theme={null} theme={null} theme={null} 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}50 ```powershell theme={null} theme={null} theme={null} theme={null} theme={null} theme={null} theme={null} theme={null} 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}56 ```batch theme={null} theme={null} theme={null} theme={null} theme={null} theme={null} theme={null} theme={null} 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 


67 </Tab>67 </Tab>

68 68 

69 <Tab title="Homebrew">69 <Tab title="Homebrew">

70 ```bash theme={null}70 ```bash theme={null} theme={null} theme={null} theme={null} theme={null} theme={null} theme={null} theme={null} theme={null}

71 brew install --cask claude-code71 brew install --cask claude-code

72 ```72 ```

73 73 


77 </Tab>77 </Tab>

78 78 

79 <Tab title="WinGet">79 <Tab title="WinGet">

80 ```powershell theme={null}80 ```powershell theme={null} theme={null} theme={null} theme={null} theme={null} theme={null} theme={null} theme={null} theme={null}

81 winget install Anthropic.ClaudeCode81 winget install Anthropic.ClaudeCode

82 ```82 ```

83 83 

skills.md +20 −5

Details

216| `${CLAUDE_SESSION_ID}` | The current session ID. Useful for logging, creating session-specific files, or correlating skill output with sessions. |216| `${CLAUDE_SESSION_ID}` | The current session ID. Useful for logging, creating session-specific files, or correlating skill output with sessions. |

217| `${CLAUDE_SKILL_DIR}` | The directory containing the skill's `SKILL.md` file. For plugin skills, this is the skill's subdirectory within the plugin, not the plugin root. Use this in bash injection commands to reference scripts or files bundled with the skill, regardless of the current working directory. |217| `${CLAUDE_SKILL_DIR}` | The directory containing the skill's `SKILL.md` file. For plugin skills, this is the skill's subdirectory within the plugin, not the plugin root. Use this in bash injection commands to reference scripts or files bundled with the skill, regardless of the current working directory. |

218 218 

219Indexed arguments use shell-style quoting, so wrap multi-word values in quotes to pass them as a single argument. For example, `/my-skill "hello world" second` makes `$0` expand to `hello world` and `$1` to `second`. The `$ARGUMENTS` placeholder always expands to the full argument string as typed.

220 

219**Example using substitutions:**221**Example using substitutions:**

220 222 

221```yaml theme={null}223```yaml theme={null}


290 In a regular session, skill descriptions are loaded into context so Claude knows what's available, but full skill content only loads when invoked. [Subagents with preloaded skills](/en/sub-agents#preload-skills-into-subagents) work differently: the full skill content is injected at startup.292 In a regular session, skill descriptions are loaded into context so Claude knows what's available, but full skill content only loads when invoked. [Subagents with preloaded skills](/en/sub-agents#preload-skills-into-subagents) work differently: the full skill content is injected at startup.

291</Note>293</Note>

292 294 

293### Restrict tool access295### Skill content lifecycle

296 

297When you or Claude invoke a skill, the rendered `SKILL.md` content enters the conversation as a single message and stays there for the rest of the session. Claude Code does not re-read the skill file on later turns, so write guidance that should apply throughout a task as standing instructions rather than one-time steps.

298 

299[Auto-compaction](/en/how-claude-code-works#when-context-fills-up) preserves invoked skills. When the conversation is summarized to free context, Claude Code re-attaches the most recent invocation of each skill after the summary (truncated if the skill is very large). If you invoke the same skill more than once, only the latest copy is carried forward through compaction.

300 

301If a skill seems to stop influencing behavior after the first response, the skill content is still present. The model is choosing other tools or approaches. Strengthen the skill's `description` and instructions so the model keeps preferring it, or use [hooks](/en/hooks) to enforce behavior deterministically.

294 302 

295Use the `allowed-tools` field to limit which tools Claude can use when a skill is active. This skill creates a read-only mode where Claude can explore files but not modify them:303### Pre-approve tools for a skill

304 

305The `allowed-tools` field grants permission for the listed tools while the skill is active, so Claude can use them without prompting you for approval. It does not restrict which tools are available: every tool remains callable, and your [permission settings](/en/permissions) still govern tools that are not listed.

306 

307This skill lets Claude run git commands without per-use approval whenever you invoke it:

296 308 

297```yaml theme={null}309```yaml theme={null}

298---310---

299name: safe-reader311name: commit

300description: Read files without making changes312description: Stage and commit the current changes

301allowed-tools: Read Grep Glob313disable-model-invocation: true

314allowed-tools: Bash(git add *) Bash(git commit *) Bash(git status *)

302---315---

303```316```

304 317 

318To block a skill from using certain tools, add deny rules in your [permission settings](/en/permissions) instead.

319 

305### Pass arguments to skills320### Pass arguments to skills

306 321 

307Both you and Claude can pass arguments when invoking a skill. Arguments are available via the `$ARGUMENTS` placeholder.322Both you and Claude can pass arguments when invoking a skill. Arguments are available via the `$ARGUMENTS` placeholder.

slack.md +3 −3

Details

22Before using Claude Code in Slack, ensure you have the following:22Before using Claude Code in Slack, ensure you have the following:

23 23 

24| Requirement | Details |24| Requirement | Details |

25| :--------------------- | :----------------------------------------------------------------------------- |25| :--------------------- | :------------------------------------------------------------------------------------------------ |

26| Claude Plan | Pro, Max, Team, or Enterprise with Claude Code access (premium seats) |26| Claude Plan | Pro, Max, Team, or Enterprise with Claude Code access (premium seats or Chat + Claude Code seats) |

27| Claude Code on the web | Access to [Claude Code on the web](/en/claude-code-on-the-web) must be enabled |27| Claude Code on the web | Access to [Claude Code on the web](/en/claude-code-on-the-web) must be enabled |

28| GitHub Account | Connected to Claude Code on the web with at least one repository authenticated |28| GitHub Account | Connected to Claude Code on the web with at least one repository authenticated |

29| Slack Authentication | Your Slack account linked to your Claude account via the Claude app |29| Slack Authentication | Your Slack account linked to your Claude account via the Claude app |


159**On the web**: The complete Claude Code session with full conversation history, all code changes, file operations, and the ability to continue the session or create pull requests.159**On the web**: The complete Claude Code session with full conversation history, all code changes, file operations, and the ability to continue the session or create pull requests.

160 160 

161For Enterprise and Team accounts, sessions created from Claude in Slack are161For Enterprise and Team accounts, sessions created from Claude in Slack are

162automatically visible to the organization. See [Claude Code on the Web sharing](/en/claude-code-on-the-web#sharing-sessions)162automatically visible to the organization. See [Claude Code on the Web sharing](/en/claude-code-on-the-web#share-sessions)

163for more details.163for more details.

164 164 

165## Best practices165## Best practices

statusline.md +23 −5

Details

769 769 

770Your status line script runs frequently during active sessions. Commands like `git status` or `git diff` can be slow, especially in large repositories. This example caches git information to a temp file and only refreshes it every 5 seconds.770Your status line script runs frequently during active sessions. Commands like `git status` or `git diff` can be slow, especially in large repositories. This example caches git information to a temp file and only refreshes it every 5 seconds.

771 771 

772Use a stable, fixed filename for the cache file like `/tmp/statusline-git-cache`. Each status line invocation runs as a new process, so process-based identifiers like `$$`, `os.getpid()`, or `process.pid` produce a different value every time and the cache is never reused.772The cache filename needs to be stable across status line invocations within a session, but unique across sessions so concurrent sessions in different repositories don't read each other's cached git state. Process-based identifiers like `$$`, `os.getpid()`, or `process.pid` change on every invocation and defeat the cache. Use the `session_id` from the JSON input instead: it's stable for the lifetime of a session and unique per session.

773 773 

774Each script checks if the cache file is missing or older than 5 seconds before running git commands:774Each script checks if the cache file is missing or older than 5 seconds before running git commands:

775 775 


780 780 

781 MODEL=$(echo "$input" | jq -r '.model.display_name')781 MODEL=$(echo "$input" | jq -r '.model.display_name')

782 DIR=$(echo "$input" | jq -r '.workspace.current_dir')782 DIR=$(echo "$input" | jq -r '.workspace.current_dir')

783 SESSION_ID=$(echo "$input" | jq -r '.session_id')

783 784 

784 CACHE_FILE="/tmp/statusline-git-cache"785 CACHE_FILE="/tmp/statusline-git-cache-$SESSION_ID"

785 CACHE_MAX_AGE=5 # seconds786 CACHE_MAX_AGE=5 # seconds

786 787 

787 cache_is_stale() {788 cache_is_stale() {


817 data = json.load(sys.stdin)818 data = json.load(sys.stdin)

818 model = data['model']['display_name']819 model = data['model']['display_name']

819 directory = os.path.basename(data['workspace']['current_dir'])820 directory = os.path.basename(data['workspace']['current_dir'])

821 session_id = data['session_id']

820 822 

821 CACHE_FILE = "/tmp/statusline-git-cache"823 CACHE_FILE = f"/tmp/statusline-git-cache-{session_id}"

822 CACHE_MAX_AGE = 5 # seconds824 CACHE_MAX_AGE = 5 # seconds

823 825 

824 def cache_is_stale():826 def cache_is_stale():


861 const data = JSON.parse(input);863 const data = JSON.parse(input);

862 const model = data.model.display_name;864 const model = data.model.display_name;

863 const dir = path.basename(data.workspace.current_dir);865 const dir = path.basename(data.workspace.current_dir);

866 const sessionId = data.session_id;

864 867 

865 const CACHE_FILE = '/tmp/statusline-git-cache';868 const CACHE_FILE = `/tmp/statusline-git-cache-${sessionId}`;

866 const CACHE_MAX_AGE = 5; // seconds869 const CACHE_MAX_AGE = 5; // seconds

867 870 

868 const cacheIsStale = () => {871 const cacheIsStale = () => {


946 949 

947## Tips950## Tips

948 951 

949* **Test with mock input**: `echo '{"model":{"display_name":"Opus"},"context_window":{"used_percentage":25}}' | ./statusline.sh`952* **Test with mock input**: `echo '{"model":{"display_name":"Opus"},"workspace":{"current_dir":"/home/user/project"},"context_window":{"used_percentage":25},"session_id":"test-session-abc"}' | ./statusline.sh`

950* **Keep output short**: the status bar has limited width, so long output may get truncated or wrap awkwardly953* **Keep output short**: the status bar has limited width, so long output may get truncated or wrap awkwardly

951* **Cache slow operations**: your script runs frequently during active sessions, so commands like `git status` can cause lag. See the [caching example](#cache-expensive-operations) for how to handle this.954* **Cache slow operations**: your script runs frequently during active sessions, so commands like `git status` can cause lag. See the [caching example](#cache-expensive-operations) for how to handle this.

952 955 


978**OSC 8 links not clickable**981**OSC 8 links not clickable**

979 982 

980* Verify your terminal supports OSC 8 hyperlinks (iTerm2, Kitty, WezTerm)983* Verify your terminal supports OSC 8 hyperlinks (iTerm2, Kitty, WezTerm)

984 

981* Terminal.app does not support clickable links985* Terminal.app does not support clickable links

986 

987* If link text appears but isn't clickable, Claude Code may not have detected hyperlink support in your terminal. This commonly affects Windows Terminal and other emulators not in the auto-detection list. Set the `FORCE_HYPERLINK` environment variable to override detection before launching Claude Code:

988 

989 ```bash theme={null}

990 FORCE_HYPERLINK=1 claude

991 ```

992 

993 In PowerShell, set the variable in the current session first:

994 

995 ```powershell theme={null}

996 $env:FORCE_HYPERLINK = "1"; claude

997 ```

998 

982* SSH and tmux sessions may strip OSC sequences depending on configuration999* SSH and tmux sessions may strip OSC sequences depending on configuration

1000 

983* If escape sequences appear as literal text like `\e]8;;`, use `printf '%b'` instead of `echo -e` for more reliable escape handling1001* If escape sequences appear as literal text like `\e]8;;`, use `printf '%b'` instead of `echo -e` for more reliable escape handling

984 1002 

985**Display glitches with escape sequences**1003**Display glitches with escape sequences**

Details

696 696 

697Run `/login` to re-authenticate. If this happens frequently, check that your system clock is accurate, as token validation depends on correct timestamps.697Run `/login` to re-authenticate. If this happens frequently, check that your system clock is accurate, as token validation depends on correct timestamps.

698 698 

699On macOS, login can also fail when the Keychain is locked or its password is out of sync with your account password, which prevents Claude Code from saving credentials. Run `claude doctor` to check Keychain access. To unlock the Keychain manually, run `security unlock-keychain ~/Library/Keychains/login.keychain-db`. If unlocking doesn't help, open Keychain Access, select the `login` keychain, and choose Edit > Change Password for Keychain "login" to resync it with your account password.

700 

699## Configuration file locations701## Configuration file locations

700 702 

701Claude Code stores configuration in several locations:703Claude Code stores configuration in several locations:


7442. Close and restart Claude Code between major tasks7462. Close and restart Claude Code between major tasks

7453. Consider adding large build directories to your `.gitignore` file7473. Consider adding large build directories to your `.gitignore` file

746 748 

749### Auto-compaction stops with a thrashing error

750 

751If you see `Autocompact is thrashing: the context refilled to the limit...`, automatic compaction succeeded but a file or tool output immediately refilled the context window several times in a row. Claude Code stops retrying to avoid wasting API calls on a loop that isn't making progress.

752 

753To recover:

754 

7551. Ask Claude to read the oversized file in smaller chunks, such as a specific line range or function, instead of the whole file

7562. Run `/compact` with a focus that drops the large output, for example `/compact keep only the plan and the diff`

7573. Move the large-file work to a [subagent](/en/sub-agents) so it runs in a separate context window

7584. Run `/clear` if the earlier conversation is no longer needed

759 

747### Command hangs or freezes760### Command hangs or freezes

748 761 

749If Claude Code seems unresponsive:762If Claude Code seems unresponsive:

ultraplan.md +3 −3

Details

7> Start a plan from your CLI, draft it on Claude Code on the web, then execute it remotely or back in your terminal7> Start a plan from your CLI, draft it on Claude Code on the web, then execute it remotely or back in your terminal

8 8 

9<Note>9<Note>

10 Ultraplan is in research preview. Behavior and capabilities may change based on feedback.10 Ultraplan is in research preview and requires Claude Code v2.1.91 or later. Behavior and capabilities may change based on feedback.

11</Note>11</Note>

12 12 

13Ultraplan hands a planning task from your local CLI to a [Claude Code on the web](/en/claude-code-on-the-web) session running in [plan mode](/en/permission-modes#analyze-before-you-edit-with-plan-mode). Claude drafts the plan in the cloud while you keep working in your terminal. When the plan is ready, you open it in your browser to comment on specific sections, ask for revisions, and choose where to execute it.13Ultraplan hands a planning task from your local CLI to a [Claude Code on the web](/en/claude-code-on-the-web) session running in [plan mode](/en/permission-modes#analyze-before-you-edit-with-plan-mode). Claude drafts the plan in the cloud while you keep working in your terminal. When the plan is ready, you open it in your browser to comment on specific sections, ask for revisions, and choose where to execute it.


18* **Hands-off drafting**: the plan is generated remotely, so your terminal stays free for other work18* **Hands-off drafting**: the plan is generated remotely, so your terminal stays free for other work

19* **Flexible execution**: approve the plan to run on the web and open a pull request, or send it back to your terminal19* **Flexible execution**: approve the plan to run on the web and open a pull request, or send it back to your terminal

20 20 

21Ultraplan requires a [Claude Code on the web](/en/claude-code-on-the-web#who-can-use-claude-code-on-the-web) account and a GitHub repository. The cloud session runs in your account's default [cloud environment](/en/claude-code-on-the-web#cloud-environment).21Ultraplan requires a [Claude Code on the web](/en/claude-code-on-the-web) account and a GitHub repository. Because it runs on Anthropic's cloud infrastructure, it is not available when using Amazon Bedrock, Google Cloud Vertex AI, or Microsoft Foundry. The cloud session runs in your account's default [cloud environment](/en/claude-code-on-the-web#the-cloud-environment).

22 22 

23## Launch ultraplan from the CLI23## Launch ultraplan from the CLI

24 24 


62 62 

63### Execute on the web63### Execute on the web

64 64 

65Select **Approve Claude's plan and start coding** in your browser to have Claude implement it in the same Claude Code on the web session. Your terminal shows a confirmation, the status indicator clears, and the work continues in the cloud. When the implementation finishes, [review the diff](/en/claude-code-on-the-web#review-changes-with-diff-view) and create a pull request from the web interface.65Select **Approve Claude's plan and start coding** in your browser to have Claude implement it in the same Claude Code on the web session. Your terminal shows a confirmation, the status indicator clears, and the work continues in the cloud. When the implementation finishes, [review the diff](/en/claude-code-on-the-web#review-changes) and create a pull request from the web interface.

66 66 

67### Send the plan back to your terminal67### Send the plan back to your terminal

68 68 

web-quickstart.md +203 −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# Get started with Claude Code on the web

6 

7> Run Claude Code in the cloud from your browser or phone. Connect a GitHub repository, submit a task, and review the PR without local setup.

8 

9<Note>

10 Claude Code on the web is in research preview for Pro, Max, and Team users, and for Enterprise users with premium seats or Chat + Claude Code seats.

11</Note>

12 

13Claude Code on the web runs on Anthropic-managed cloud infrastructure instead of your machine. Submit tasks from [claude.ai/code](https://claude.ai/code) in your browser or the Claude mobile app.

14 

15You'll need a GitHub repository to [get started](#connect-github-and-create-an-environment). Claude clones it into an isolated virtual machine, makes changes, and pushes a branch for you to review. Sessions persist across devices, so a task you start on your laptop is ready to review from your phone later.

16 

17Claude Code on the web works well for:

18 

19* **Parallel tasks**: run several independent tasks at once, each in its own session and branch, without managing multiple worktrees

20* **Repos you don't have locally**: Claude clones the repo fresh every session, so you don't need it checked out

21* **Tasks that don't need frequent steering**: submit a well-defined task, do something else, and review the result when Claude is done

22* **Code questions and exploration**: understand a codebase or trace how a feature is implemented without a local checkout

23 

24For work that needs your local config, tools, or environment, running Claude Code locally or using [Remote Control](/en/remote-control) is a better fit.

25 

26## How sessions run

27 

28When you submit a task:

29 

301. **Clone and prepare**: your repository is cloned to an Anthropic-managed VM, and your [setup script](/en/claude-code-on-the-web#setup-scripts) runs if configured.

312. **Configure network**: internet access is set based on your environment's [access level](/en/claude-code-on-the-web#access-levels).

323. **Work**: Claude analyzes code, makes changes, runs tests, and checks its work. You can watch and steer throughout, or step away and come back when it's done.

334. **Push the branch**: when Claude reaches a stopping point, it pushes its branch to GitHub. You review the diff, leave inline comments, create a PR, or send another message to keep going.

34 

35The session doesn't close when the branch is pushed. PR creation and further edits all happen within the same conversation.

36 

37## Compare ways to run Claude Code

38 

39Claude Code behaves the same everywhere. What changes is where code executes and whether your local config is available. The Desktop app offers both local and cloud sessions, so its answers below depend on which you choose:

40 

41| | On the web | Remote Control | Terminal CLI | Desktop app |

42| :------------------------------------------- | :-------------------------------------------------------------------------------------------------------------- | :--------------------------- | :--------------------- | :-------------------------- |

43| **Code runs on** | Anthropic cloud VM | Your machine | Your machine | Your machine or cloud VM |

44| **You chat from** | claude.ai or mobile app | claude.ai or mobile app | Your terminal | The Desktop UI |

45| **Uses your local config** | No, repo only | Yes | Yes | Yes for local, no for cloud |

46| **Requires GitHub** | Yes, or [bundle a local repo](/en/claude-code-on-the-web#send-local-repositories-without-github) via `--remote` | No | No | Only for cloud sessions |

47| **Keeps running if you disconnect** | Yes | While terminal stays open | No | Depends on session type |

48| **[Permission modes](/en/permission-modes)** | Auto accept edits, Plan | Ask, Auto accept edits, Plan | All modes | Depends on session type |

49| **Network access** | Configurable per environment | Your machine's network | Your machine's network | Depends on session type |

50 

51See the [terminal quickstart](/en/quickstart), [Desktop app](/en/desktop), or [Remote Control](/en/remote-control) docs to set those up.

52 

53## Connect GitHub and create an environment

54 

55Setup is a one-time process. If you already use the GitHub CLI, you can [do this from your terminal](#connect-from-your-terminal) instead of the browser.

56 

57<Steps>

58 <Step title="Visit claude.ai/code">

59 Go to [claude.ai/code](https://claude.ai/code) and sign in with your Anthropic account.

60 </Step>

61 

62 <Step title="Install the Claude GitHub App">

63 After signing in, claude.ai/code prompts you to connect GitHub. Follow the prompt to install the Claude GitHub App and grant it access to your repositories. Cloud sessions work with existing GitHub repositories, so to start a new project, [create an empty repository on GitHub](https://github.com/new) first.

64 </Step>

65 

66 <Step title="Create your environment">

67 After connecting GitHub, you'll be prompted to create a cloud environment. The environment controls what network access Claude has during sessions and what runs when a new session is created. See [Installed tools](/en/claude-code-on-the-web#installed-tools) for what's available without any configuration.

68 

69 The form has these fields:

70 

71 * **Name**: a display label. Useful when you have multiple environments for different projects or access levels.

72 * **Network access**: controls what the session can reach on the internet. The default, `Trusted`, allows connections to [common package registries](/en/claude-code-on-the-web#default-allowed-domains) like npm, PyPI, and RubyGems while blocking general internet access.

73 * **Environment variables**: optional variables available in every session, in `.env` format. Don't wrap values in quotes, since quotes are stored as part of the value. These are visible to anyone who can edit this environment.

74 * **Setup script**: an optional Bash script that runs before Claude Code launches when a new session is created. Use it to install system tools the cloud VM doesn't include, like `apt install -y gh`, or to start services your project needs. See [Setup scripts](/en/claude-code-on-the-web#setup-scripts) for examples and debugging tips.

75 

76 For a first project, leave the defaults and click **Create environment**. You can [edit it later or create additional environments](/en/claude-code-on-the-web#configure-your-environment) for different projects.

77 </Step>

78</Steps>

79 

80### Connect from your terminal

81 

82If you already use the GitHub CLI (`gh`), you can set up Claude Code on the web without opening a browser. This requires the [Claude Code CLI](/en/quickstart). `/web-setup` reads your local `gh` token, links it to your Claude account, and creates a default cloud environment if you don't have one.

83 

84<Note>

85 Organizations with [Zero Data Retention](/en/zero-data-retention) enabled cannot use `/web-setup` or other cloud session features. If the GitHub CLI isn't installed or authenticated, `/web-setup` opens the browser onboarding flow instead.

86</Note>

87 

88<Steps>

89 <Step title="Authenticate with the GitHub CLI">

90 In your shell, authenticate the GitHub CLI if you haven't already:

91 

92 ```bash theme={null}

93 gh auth login

94 ```

95 </Step>

96 

97 <Step title="Sign in to Claude">

98 In the Claude Code CLI, run `/login` to sign in with your claude.ai account. Skip this step if you're already signed in.

99 </Step>

100 

101 <Step title="Run /web-setup">

102 In the Claude Code CLI, run:

103 

104 ```text theme={null}

105 /web-setup

106 ```

107 

108 This syncs your `gh` token to your Claude account. If you don't have a cloud environment yet, `/web-setup` creates one with Trusted network access and no setup script. You can [edit the environment or add variables](/en/claude-code-on-the-web#configure-your-environment) afterward. Once `/web-setup` completes, you can start cloud sessions from your terminal with [`--remote`](/en/claude-code-on-the-web#from-terminal-to-web) or set up recurring tasks with [`/schedule`](/en/web-scheduled-tasks).

109 </Step>

110</Steps>

111 

112## Start a task

113 

114With GitHub connected and an environment created, you're ready to submit tasks.

115 

116<Steps>

117 <Step title="Select a repository and branch">

118 From [claude.ai/code](https://claude.ai/code) or the Code tab in the Claude mobile app, click the repository selector below the input box and choose a repository for Claude to work in. Each repository shows a branch selector. Change it to start Claude from a feature branch instead of the default. You can add multiple repositories to work across them in one session.

119 </Step>

120 

121 <Step title="Choose a permission mode">

122 The mode dropdown next to the input defaults to **Auto accept edits**, where Claude makes changes and pushes a branch without stopping for approval. Switch to **Plan mode** if you want Claude to propose an approach and wait for your go-ahead before editing files. Cloud sessions don't offer Ask permissions, Auto mode, or Bypass permissions. See [Permission modes](/en/permission-modes) for the full list.

123 </Step>

124 

125 <Step title="Describe the task and submit">

126 Type a description of what you want and press Enter. Be specific:

127 

128 * Name the file or function: "Add a README with setup instructions" or "Fix the failing auth test in `tests/test_auth.py`" is better than "fix tests"

129 * Paste error output if you have it

130 * Describe the expected behavior, not just the symptom

131 

132 Claude clones the repositories, runs your setup script if configured, and starts working. Each task gets its own session and its own branch, so you don't need to wait for one to finish before starting another.

133 </Step>

134</Steps>

135 

136## Review and iterate

137 

138When Claude finishes, review the changes, leave feedback on specific lines, and keep going until the diff looks right.

139 

140<Steps>

141 <Step title="Open the diff view">

142 A diff indicator shows lines added and removed across the session, for example `+42 -18`. Select it to open the diff view, with a file list on the left and changes on the right.

143 </Step>

144 

145 <Step title="Leave inline comments">

146 Select any line in the diff, type your feedback, and press Enter. Comments queue up until you send your next message, then they're bundled with it. Claude sees "at `src/auth.ts:47`, don't catch the error here" alongside your main instruction, so you don't have to describe where the problem is.

147 </Step>

148 

149 <Step title="Create a pull request">

150 When the diff looks right, select **Create PR** at the top of the diff view. You can open it as a full PR, a draft, or jump to GitHub's compose page with a generated title and description.

151 </Step>

152 

153 <Step title="Keep iterating after the PR">

154 The session stays live after the PR is created. Paste CI failure output or reviewer comments into the chat and ask Claude to address them. To have Claude monitor the PR automatically, see [Auto-fix pull requests](/en/claude-code-on-the-web#auto-fix-pull-requests).

155 </Step>

156</Steps>

157 

158## Troubleshoot setup

159 

160### No repositories appear after connecting GitHub

161 

162The Claude GitHub App needs explicit access to each repository you want to use. On github.com, open **Settings → Applications → Claude → Configure** and verify your repo is listed under **Repository access**. Private repositories need the same authorization as public ones.

163 

164### The page only shows a GitHub login button

165 

166Cloud sessions require a connected GitHub account. Connect via the browser flow above, or run `/web-setup` from your terminal if you use the GitHub CLI. If you'd rather not connect GitHub at all, see [Remote Control](/en/remote-control) to run Claude Code on your own machine and monitor it from the web.

167 

168### "Not available for the selected organization"

169 

170Enterprise organizations may need an admin to enable Claude Code on the web. Contact your Anthropic account team.

171 

172### `/web-setup` returns "Unknown command"

173 

174`/web-setup` runs inside the Claude Code CLI, not your shell. Launch `claude` first, then type `/web-setup` at the prompt.

175 

176If you typed it inside Claude Code and still see the error, your CLI is older than v2.1.80 or you're authenticated with an API key or third-party provider instead of a claude.ai subscription. Run `claude update`, then `/login` to sign in with your claude.ai account.

177 

178### "No cloud environment available" when using `--remote`

179 

180You haven't created a cloud environment yet. Run `/web-setup` in the Claude Code CLI to create one, or visit [claude.ai/code](https://claude.ai/code) and follow the **Create your environment** step above.

181 

182### Setup script failed

183 

184The setup script exited with a non-zero status, which blocks the session from starting. Common causes:

185 

186* A package install failed because the registry isn't in your [network access level](/en/claude-code-on-the-web#access-levels). `Trusted` covers most package managers; `None` blocks them all.

187* The script references a file or path that doesn't exist in a fresh clone.

188* A command that works locally needs a different invocation on Ubuntu.

189 

190To debug, add `set -x` at the top of the script to see which command failed. For non-critical commands, append `|| true` so they don't block session start.

191 

192### Session keeps running after closing the tab

193 

194This is by design. Closing the tab or navigating away doesn't stop the session. It continues running in the background until Claude finishes the current task, then idles. From the sidebar, you can [archive a session](/en/claude-code-on-the-web#archive-sessions) to hide it from your list, or [delete it](/en/claude-code-on-the-web#delete-sessions) to remove it permanently.

195 

196## Next steps

197 

198Now that you can submit and review tasks, these pages cover what comes next: starting cloud sessions from your terminal, scheduling recurring work, and giving Claude standing instructions.

199 

200* [Use Claude Code on the web](/en/claude-code-on-the-web): the full reference, including teleporting sessions to your terminal, setup scripts, environment variables, and network config

201* [Schedule tasks on the web](/en/web-scheduled-tasks): automate recurring work like daily PR reviews and dependency audits

202* [CLAUDE.md](/en/memory): give Claude persistent instructions and context that load at the start of every session

203* Install the Claude mobile app for [iOS](https://apps.apple.com/us/app/claude-by-anthropic/id6473753684) or [Android](https://play.google.com/store/apps/details?id=com.anthropic.claude) to monitor sessions from your phone. From the Claude Code CLI, `/mobile` shows a QR code.

Details

4 4 

5# Schedule tasks on the web5# Schedule tasks on the web

6 6 

7> Automate recurring work with cloud scheduled tasks7> Schedule recurring Claude Code tasks on a cron-like interval. Automate PR reviews, dependency audits, and CI triage in cloud sessions.

8 8 

9A scheduled task runs a prompt on a recurring cadence using Anthropic-managed infrastructure. Tasks keep working even when your computer is off.9A scheduled task runs a prompt on a recurring cadence using Anthropic-managed infrastructure. Tasks keep working even when your computer is off.

10 10 


65 </Step>65 </Step>

66 66 

67 <Step title="Select an environment">67 <Step title="Select an environment">

68 Select a [cloud environment](/en/claude-code-on-the-web#cloud-environment) for the task. Environments control what the cloud session has access to:68 Select a [cloud environment](/en/claude-code-on-the-web#the-cloud-environment) for the task. Environments control what the cloud session has access to:

69 69 

70 * **Network access**: set the level of internet access available during each run70 * **Network access**: set the level of internet access available during each run

71 * **Environment variables**: provide API keys, tokens, or other secrets Claude can use71 * **Environment variables**: provide API keys, tokens, or other secrets Claude can use

72 * **Setup script**: run install commands before each session starts, like installing dependencies or configuring tools72 * **Setup script**: run install commands before each session starts, like installing dependencies or configuring tools

73 73 

74 A **Default** environment is available out of the box. To use a custom environment, [create one](/en/claude-code-on-the-web#cloud-environment) before creating the task.74 A **Default** environment is available out of the box. To use a custom environment, [create one](/en/claude-code-on-the-web#the-cloud-environment) before creating the task.

75 </Step>75 </Step>

76 76 

77 <Step title="Choose a schedule">77 <Step title="Choose a schedule">


108 108 

109### Repositories and branch permissions109### Repositories and branch permissions

110 110 

111Scheduled tasks need GitHub access to clone repositories. When you create a task from the CLI with `/schedule`, Claude checks whether your account has GitHub connected and prompts you to run `/web-setup` if it doesn't. See [GitHub authentication options](/en/claude-code-on-the-web#github-authentication-options) for the two ways to grant access.

112 

111Each repository you add is cloned on every run. Claude starts from the repository's default branch unless your prompt specifies otherwise.113Each repository you add is cloned on every run. Claude starts from the repository's default branch unless your prompt specifies otherwise.

112 114 

113By default, Claude can only push to branches prefixed with `claude/`. This prevents scheduled tasks from accidentally modifying protected or long-lived branches.115By default, Claude can only push to branches prefixed with `claude/`. This prevents scheduled tasks from accidentally modifying protected or long-lived branches.


124 126 

125### Environments127### Environments

126 128 

127Each task runs in a [cloud environment](/en/claude-code-on-the-web#cloud-environment) that controls network access, environment variables, and setup scripts. Configure environments before creating a task to give Claude access to APIs, install dependencies, or restrict network scope. See [cloud environment](/en/claude-code-on-the-web#cloud-environment) for the full setup guide.129Each task runs in a [cloud environment](/en/claude-code-on-the-web#the-cloud-environment) that controls network access, environment variables, and setup scripts. Configure environments before creating a task to give Claude access to APIs, install dependencies, or restrict network scope. See [cloud environment](/en/claude-code-on-the-web#the-cloud-environment) for the full setup guide.

128 130 

129## Manage scheduled tasks131## Manage scheduled tasks

130 132 


149 151 

150* [Desktop scheduled tasks](/en/desktop-scheduled-tasks): schedule tasks that run on your machine with access to local files. The Desktop app's **Schedule** page shows both local and remote tasks in the same grid.152* [Desktop scheduled tasks](/en/desktop-scheduled-tasks): schedule tasks that run on your machine with access to local files. The Desktop app's **Schedule** page shows both local and remote tasks in the same grid.

151* [`/loop` and CLI scheduled tasks](/en/scheduled-tasks): lightweight scheduling within a CLI session153* [`/loop` and CLI scheduled tasks](/en/scheduled-tasks): lightweight scheduling within a CLI session

152* [Cloud environment](/en/claude-code-on-the-web#cloud-environment): configure the runtime environment for cloud tasks154* [Cloud environment](/en/claude-code-on-the-web#the-cloud-environment): configure the runtime environment for cloud tasks

153* [MCP connectors](/en/mcp): connect external services like Slack, Linear, and Google Drive155* [MCP connectors](/en/mcp): connect external services like Slack, Linear, and Google Drive

154* [GitHub Actions](/en/github-actions): run Claude in your CI pipeline on repo events156* [GitHub Actions](/en/github-actions): run Claude in your CI pipeline on repo events