
Claude Code Docs
- 34 installs
- Updated August 5, 2026
- mehmetbaykar/claude-code-docs-skill
Helps with ai & agent building tasks.
About
claude-code-docs is a Claude Code skill for ai & agent building. It helps solo builders move faster with AI-assisted coding.
- claude-code-docs
- AI & Agent Building
- AI-coding skill
Claude Code Docs by the numbers
- 34 all-time installs (skills.sh)
- Ranked #8,855 of 16,546 AI & Agent Building skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/mehmetbaykar/claude-code-docs-skill --skill claude-code-docsAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 34 |
|---|---|
| Last updated | August 5, 2026 |
| Repository | mehmetbaykar/claude-code-docs-skill ↗ |
What it does
Helps with ai & agent building tasks.
Files
interface:
display_name: "Claude Code Docs"
short_description: "Search and read Claude Code documentation locally."
policy:
allow_implicit_invocation: true
Set up Claude Code for your organization
A decision map for administrators deploying Claude Code, covering API providers, managed settings, policy enforcement, usage monitoring, and data handling.
Claude Code enforces organization policy through managed settings that take precedence over local developer configuration. You deliver those settings from the Claude admin console, your mobile device management (MDM) system, or a file on disk. The settings control which tools, commands, servers, and network destinations Claude can reach.
This page walks through the deployment decisions in order. Each row links to the section below and to the reference page for that area.
SSO, SCIM provisioning, and seat assignment are configured at the Claude account level. See the Claude Enterprise Administrator Guide and seat assignment for those steps.
| Decision | What you're choosing | Reference |
|---|---|---|
| Choose your API provider | Where Claude Code authenticates and how it's billed | Authentication, Bedrock, Vertex AI, Foundry |
| Decide how settings reach devices | How managed policy reaches developer machines | Server-managed settings, Settings files |
| Decide what to enforce | Which tools, commands, and integrations are allowed | Permissions, Sandboxing |
| Set up usage visibility | How you track spend and adoption | Analytics, Monitoring, Costs |
| Review data handling | Data retention and compliance posture | Data usage, Security |
Choose your API provider
Claude Code connects to Claude through one of several API providers. Your choice affects billing, authentication, which compliance posture you inherit, and which Claude Code features your developers can use.
| Provider | Choose this when |
|---|---|
| Claude for Teams / Enterprise | You want Claude Code and claude.ai under one per-seat subscription with no infrastructure to run. This is the default recommendation. |
| Claude Console | You're API-first or want pay-as-you-go billing |
| Amazon Bedrock | You want to inherit existing AWS compliance controls and billing |
| Google Vertex AI | You want to inherit existing GCP compliance controls and billing |
| Microsoft Foundry | You want to inherit existing Azure compliance controls and billing |
Some Claude Code features require a Claude.ai account. Claude Code on the web, Routines, Code Review, Remote Control, and the Chrome extension are not available through Console API keys or cloud-provider credentials alone. If you deploy through Bedrock, Vertex, or Foundry, plan whether developers also need Claude for Teams or Enterprise seats. Each feature page lists its plan requirements.
For the full provider comparison covering authentication, regions, and feature parity, see the enterprise deployment overview. Each provider's auth setup is in Authentication.
Proxy and firewall requirements in Network configuration apply regardless of provider. If you want a single endpoint in front of multiple providers or centralized request logging, see LLM gateway.
Decide how settings reach devices
Managed settings define policy that takes precedence over local developer configuration. Claude Code checks the four sources below in priority order and applies the first one that returns a non-empty configuration.
| Mechanism | Delivery | Priority | Platforms |
|---|---|---|---|
| Server-managed | Claude.ai admin console | Highest | All |
| plist / registry policy | macOS: com.anthropic.claudecode plistWindows: HKLM\SOFTWARE\Policies\ClaudeCode | High | macOS, Windows |
| File-based managed | macOS: /Library/Application Support/ClaudeCode/managed-settings.jsonLinux and WSL: /etc/claude-code/managed-settings.jsonWindows: C:\Program Files\ClaudeCode\managed-settings.json | Medium | All |
| Windows user registry | HKCU\SOFTWARE\Policies\ClaudeCode | Lowest | Windows only |
Server-managed settings reach devices at authentication time and refresh hourly during active sessions, with no endpoint infrastructure. They require a Claude for Teams or Enterprise plan, so deployments on other providers need one of the file-based or OS-level mechanisms instead.
If your organization mixes providers, configure server-managed settings for Claude.ai users plus a file-based or plist/registry fallback so other users still receive managed policy.
The plist and HKLM registry locations work with any provider and resist tampering because they require admin privileges to write. The Windows user registry at HKCU is writable without elevation, so treat it as a convenience default rather than an enforcement channel.
By default WSL reads only the Linux file path at /etc/claude-code. To extend your Windows registry and C:\Program Files\ClaudeCode policy to WSL on the same machine, set `wslInheritsWindowsSettings: true` in either of those admin-only Windows sources.
Whichever mechanism you choose, managed values take precedence over user and project settings. Array settings such as permissions.allow and permissions.deny merge entries from all sources, so developers can extend managed lists but not remove from them.
See Server-managed settings and Settings files and precedence.
Decide what to enforce
Managed settings can lock down tools, sandbox execution, restrict MCP servers and plugin sources, and control which hooks run. Each row is a control surface with the setting keys that drive it.
| Control | What it does | Key settings |
|---|---|---|
| Permission rules | Allow, ask, or deny specific tools and commands | permissions.allow, permissions.deny |
| Permission lockdown | Only managed permission rules apply; disable --dangerously-skip-permissions | allowManagedPermissionRulesOnly, permissions.disableBypassPermissionsMode |
| Sandboxing | OS-level filesystem and network isolation with domain allowlists | sandbox.enabled, sandbox.network.allowedDomains |
| Managed policy CLAUDE.md | Org-wide instructions loaded in every session, cannot be excluded | File at the managed policy path |
| MCP server control | Restrict which MCP servers users can add or connect to, or deploy a fixed set | allowedMcpServers, deniedMcpServers, allowManagedMcpServersOnly, or a deployed managed-mcp.json file |
| Plugin marketplace control | Restrict which marketplace sources users can add and install from | strictKnownMarketplaces, blockedMarketplaces |
| Customization lockdown | Block skills, agents, hooks, and MCP servers from user and project sources, so they can only come from plugins or managed settings | strictPluginOnlyCustomization |
| Hook restrictions | Only managed hooks load; restrict HTTP hook URLs | allowManagedHooksOnly, allowedHttpHookUrls |
| Disable agent view | Turn off claude agents, --bg, /background, and the on-demand supervisor | disableAgentView |
| Version floor | Prevent auto-update from installing below an org-wide minimum | minimumVersion |
| Required version range | Refuse to start at all when the running version is outside an org-approved range. Stronger than minimumVersion, which only blocks downgrades | requiredMinimumVersion, requiredMaximumVersion |
Permission rules and sandboxing cover different layers. Denying WebFetch blocks Claude's fetch tool, but if Bash is allowed, curl and wget can still reach any URL. Sandboxing closes that gap with a network domain allowlist enforced at the OS level.
For the threat model these controls defend against, see Security.
Set up usage visibility
Choose monitoring based on what you need to report on.
| Capability | What you get | Availability | Where to start |
|---|---|---|---|
| Usage monitoring | OpenTelemetry export of sessions, tools, and tokens | All providers | Monitoring usage |
| Analytics dashboard | Per-user metrics, contribution tracking, leaderboard | Anthropic only | Analytics |
| Cost tracking | Spend limits, rate limits, and usage attribution | Anthropic only | Costs |
Cloud providers expose spend through AWS Cost Explorer, GCP Billing, or Azure Cost Management. Claude for Teams and Enterprise plans include a usage dashboard at claude.ai/analytics/claude-code.
Review data handling
On Team, Enterprise, Claude API, and cloud provider plans, Anthropic does not train models on your code or prompts. Your API provider determines retention and compliance posture.
| Topic | What to know | Where to start |
|---|---|---|
| Data usage policy | What Anthropic collects, how long it's retained, what's never used for training | Data usage |
| Zero Data Retention (ZDR) | Nothing stored after the request completes. Available to qualified accounts on Claude for Enterprise | Zero data retention |
| Security architecture | Network model, encryption, authentication, audit trail | Security |
If you need request-level audit logging or to route traffic by data sensitivity, place an LLM gateway between developers and your provider. For regulatory requirements and certifications, see Legal and compliance.
Verify and onboard
After configuring managed settings, have a developer run /status inside Claude Code. On the Status tab, the Setting sources line shows Enterprise managed settings followed by the source in parentheses, one of (remote), (plist), (HKLM), (HKCU), or (file). See Verify active settings.
Share these resources to help developers get started:
- Quickstart: first-session walkthrough from install to working with a project
- Common workflows: patterns for everyday tasks like code review, refactoring, and debugging
- Claude 101 and Claude Code in Action: self-paced Anthropic Academy courses
For login issues, point developers to authentication troubleshooting. The most common fixes are:
- Run
/logoutthen/loginto switch accounts - Run
claude updateif the enterprise auth option is missing - Restart the terminal after updating
If a developer sees "You haven't been added to your organization yet," their seat doesn't include Claude Code access and needs to be updated in the admin console.
Next steps
With provider and delivery mechanism chosen, move on to detailed configuration:
- Server-managed settings: deliver managed policy from the Claude admin console
- Settings reference: every setting key, file location, and precedence rule
- Monorepos and large repos: per-directory configuration patterns for organizations deploying into a monorepo
- Amazon Bedrock, Google Vertex AI, Microsoft Foundry: provider-specific deployment
- Claude Enterprise Administrator Guide: SSO, SCIM, seat management, and rollout playbook
Escalate hard decisions with the advisor tool
Pair your main model with a stronger advisor model that Claude consults at key moments during a task.
{/ plan-availability: feature=advisor providers=anthropic /}
The advisor tool is experimental and requires Claude Code v2.1.98 or later with the Anthropic API. It is not available on Amazon Bedrock, Google Vertex AI, or Microsoft Foundry. Behavior, pricing, and availability may change.
The advisor tool lets Claude consult a second, typically stronger model at key moments during a task, such as before committing to an approach, when stuck on a recurring error, or before declaring a task complete. The advisor receives the full conversation, including every tool call and result, and returns guidance that Claude applies before continuing.
The advisor runs server-side on Anthropic's infrastructure as a server tool, available to both subscription and API-billed accounts. You choose which model acts as the advisor, and Claude decides when to call it.
This page covers how to enable the advisor, which model pairings are accepted, what Claude shows during a consultation, and how advisor usage is billed.
When to use the advisor
The advisor fits long, multi-step tasks where most turns are routine but plan quality determines the outcome. Examples include large refactors, debugging sessions where an error keeps recurring, and tasks you want independently checked before Claude declares them done.
It adds less value on short tasks where there is little to plan, or on work where every turn needs the strongest model. For those, switch the main model instead, or see how the advisor compares with opusplan and subagents for other ways to get a second opinion.
Enable the advisor
You can set the advisor model in three ways:
- `/advisor` command: set or change the advisor mid-session and save it as your default
- `advisorModel` setting: configure a persistent default in your settings file
- `--advisor` flag: set the advisor for a single session at launch
If any of these sets an advisor model, the advisor is enabled for sessions whose main model supports it. To stop using it, see Turn the advisor off.
To use Fable 5 as the advisor, you need Claude Code v2.1.170 or later and Fable 5 access for your organization.
Use the /advisor command
Run /advisor without arguments to open a picker listing the available advisor models, or pass the model directly:
/advisor opusYour selection is saved to advisorModel in your user settings and persists across sessions. If your current main model does not support the advisor, the selection is still saved and activates when you switch to a compatible main model with `/model`.
Set advisorModel in settings
To configure the advisor as a default without opening a session, set it in your settings file:
{
"advisorModel": "opus"
}Use the --advisor flag
To set the advisor for a single session without changing your saved setting, launch with the flag:
claude --advisor opusThe flag takes precedence over the advisorModel setting for that session. Unlike /advisor, which saves an inactive selection, the flag exits with an error if the session's main model does not support the advisor.
Choose an advisor model
The advisor must be at least as capable as the main model. The accepted advisors for each main model are:
| Main model | Accepted advisors | Notes |
|---|---|---|
| Haiku 4.5 | Fable, Opus, Sonnet | Haiku can call the advisor but cannot act as one |
| Sonnet 4.6 | Fable, Opus, Sonnet | |
| Opus 4.6 or later | Fable, Opus at or above the main model's version | An Opus 4.7 main with an Opus 4.6 advisor is rejected |
| Fable 5 ({/ min-version: 2.1.170 /}v2.1.170+) | Fable | An Opus or Sonnet advisor is rejected |
Fable 5 requires Claude Code v2.1.170 or later and Fable 5 access, whether it acts as the main model or the advisor.
Set the advisor as opus, sonnet, or fable. These aliases resolve to the latest version of each model. You can also pass a full model ID such as claude-opus-4-8.
Subagents inherit the configured advisor and apply the same pairing check against their own model.
Claude Code validates the pairing before sending a request:
- If the advisor is less capable than the main model, the advisor is not attached to the main model's requests. The
/advisorcommand output and a notification show this. Subagents whose own model satisfies the pairing may still use the advisor. - If the main model or the advisor is a model Claude Code does not recognize, the advisor is not attached.
Common model pairings
Any accepted pairing works. These combinations balance cost against capability in different ways:
| Pairing | When to use |
|---|---|
| Sonnet main + Opus advisor | Sonnet handles routine work and escalates planning, ambiguous failures, and completion checks to Opus |
| Sonnet main + Fable advisor | Fable 5 guidance at decision points without running Fable 5 throughout. Requires v2.1.170 or later and Fable 5 access |
| Haiku main + Opus advisor | Lowest-cost main model with strong planning. Expect higher cost than Haiku alone but lower than switching the main model to Sonnet or Opus |
| Opus main + Opus advisor | A second Opus reviews the first. Useful for high-stakes tasks where an independent check matters more than cost |
| Fable main + Fable advisor | Highest-capability pairing when Fable 5 is available (v2.1.170+). Fable is a higher tier than Opus and Sonnet, so it is the only accepted advisor for a Fable main model |
| Sonnet main + Sonnet advisor | A lower-cost second opinion for catching routine oversights |
When Claude consults the advisor
Claude decides when to call the advisor. It tends to consult before committing to an approach, when an error keeps recurring, and before declaring a task done, but the timing is model-driven rather than rule-based.
You can ask for a consultation in your prompt the same way you would request any tool, for example consult the advisor before you continue. There is no setting to cap or force advisor calls; if you want Claude to consult more or less often during a task, say so in your instructions.
What you see during a session
When Claude calls the advisor, the transcript shows an Advising line with the advisor model name while the call is in progress. When the result returns, the line confirms that the advisor has reviewed the conversation. Press Ctrl+O to expand it and read the advisor's full guidance.
Claude generally follows the advisor's guidance, but adapts when its own evidence contradicts a specific claim: if a recommended step fails when tried, or the file contents contradict the advice, Claude surfaces the conflict rather than following the guidance unconditionally.
The advisor always receives the full conversation, and Claude controls the timing. For more control or a different configuration, see how the advisor compares with subagents and opusplan.
Cost
Each advisor call sends the conversation to the advisor model, so it consumes tokens at the advisor model's rates in addition to your main model's usage. With API billing, advisor tokens are charged at the advisor model's input and output rates. On subscription plans, advisor usage counts toward your plan's usage limits.
Claude calls the advisor at decision points rather than on every turn, so pairing a faster main model with a stronger advisor typically costs less than running the stronger model throughout. Advisor usage counts toward the session totals shown by `/usage`.
For how advisor tokens are reported in API responses, see Usage and billing in the Claude API documentation.
Impact on prompt caching
Enabling or disabling the advisor mid-session does not invalidate your main model's prompt cache. Unlike changing model or effort level, toggling /advisor keeps the cached prefix intact, and the advisor's returned guidance is cached as part of the transcript on later turns.
The advisor model's own read of the conversation is not cached. Each advisor call processes the full transcript anew, with no reuse between calls.
Requirements
The advisor tool requires all of the following:
- Claude Code v2.1.98 or later: run
claude updateto upgrade. - Anthropic API only: the advisor is a server-executed tool. It is not available on Amazon Bedrock, Google Vertex AI, or Microsoft Foundry. Through an LLM gateway configured with
ANTHROPIC_BASE_URL, availability depends on whether the gateway forwards the request intact to the Anthropic API. - Supported main model: Opus 4.6 or later, Sonnet 4.6, or Haiku 4.5. {/ min-version: 2.1.170 /}Fable 5 also qualifies on Claude Code v2.1.170 or later.
Turn the advisor off
To stop using the advisor and clear your saved advisorModel, run /advisor off or choose No advisor in the /advisor picker:
/advisor offTo disable the advisor tool entirely, including the /advisor command and the --advisor flag, set CLAUDE_CODE_DISABLE_ADVISOR_TOOL=1. See Environment variables.
Compare with related features
The advisor is one of several ways to combine model strengths. Pick based on when you want a second model involved.
| Approach | When the stronger model runs | How it starts |
|---|---|---|
| Advisor tool | At decision points mid-task | Claude calls it when it needs guidance |
| `opusplan` | During plan mode, then switches to Sonnet for execution | You enter plan mode |
Subagents with model set | For the entire delegated subtask | Claude delegates, or you invoke the subagent |
| `/model` | For all subsequent turns | You switch models |
See also
- Model configuration: switch models, set effort levels, and use
opusplan - Manage costs effectively: track token usage across models
- Advisor tool in the Claude API: understand the underlying server tool, or use it directly from the Messages API
- The advisor strategy: why pairing a fast main model with a stronger advisor works
How the agent loop works
Understand the message lifecycle, tool execution, context window, and architecture that power your SDK agents.
The Agent SDK lets you embed Claude Code's autonomous agent loop in your own applications. The SDK is a standalone package that gives you programmatic control over tools, permissions, cost limits, and output. You don't need the Claude Code CLI installed to use it.
When you start an agent, the SDK runs the same execution loop that powers Claude Code: Claude evaluates your prompt, calls tools to take action, receives the results, and repeats until the task is complete. This page explains what happens inside that loop so you can build, debug, and optimize your agents effectively.
The loop at a glance
Every agent session follows the same cycle:
1. Receive prompt. Claude receives your prompt, along with the system prompt, tool definitions, and conversation history. The SDK yields a `SystemMessage` with subtype "init" containing session metadata. 2. Evaluate and respond. Claude evaluates the current state and determines how to proceed. It may respond with text, request one or more tool calls, or both. The SDK yields an `AssistantMessage` containing the text and any tool call requests. 3. Execute tools. The SDK runs each requested tool and collects the results. Each set of tool results feeds back to Claude for the next decision. You can use hooks to intercept, modify, or block tool calls before they run. 4. Repeat. Steps 2 and 3 repeat as a cycle. Each full cycle is one turn. Claude continues calling tools and processing results until it produces a response with no tool calls. 5. Return result. The SDK yields a final `AssistantMessage` with the text response (no tool calls), followed by a `ResultMessage` with the final text, token usage, cost, and session ID.
A quick question ("what files are here?") might take one or two turns of calling Glob and responding with the results. A complex task ("refactor the auth module and update the tests") can chain dozens of tool calls across many turns, reading files, editing code, and running tests, with Claude adjusting its approach based on each result.
Turns and messages
A turn is one round trip inside the loop: Claude produces output that includes tool calls, the SDK executes those tools, and the results feed back to Claude automatically. This happens without yielding control back to your code. Turns continue until Claude produces output with no tool calls, at which point the loop ends and the final result is delivered.
Consider what a full session might look like for the prompt "Fix the failing tests in auth.ts".
First, the SDK sends your prompt to Claude and yields a `SystemMessage` with the session metadata. Then the loop begins:
1. Turn 1: Claude calls Bash to run npm test. The SDK yields an `AssistantMessage` with the tool call, executes the command, then yields a `UserMessage` with the output (three failures). 2. Turn 2: Claude calls Read on auth.ts and auth.test.ts. The SDK returns the file contents and yields an AssistantMessage. 3. Turn 3: Claude calls Edit to fix auth.ts, then calls Bash to re-run npm test. All three tests pass. The SDK yields an AssistantMessage. 4. Final turn: Claude produces a text-only response with no tool calls: "Fixed the auth bug, all three tests pass now." The SDK yields a final AssistantMessage with this text, then a `ResultMessage` with the same text plus cost and usage.
That was four turns: three with tool calls, one final text-only response.
You can cap the loop with max_turns / maxTurns, which counts tool-use turns only. For example, max_turns=2 in the loop above would have stopped before the edit step. You can also use max_budget_usd / maxBudgetUsd to cap turns based on a spend threshold.
Without limits, the loop runs until Claude finishes on its own, which is fine for well-scoped tasks but can run long on open-ended prompts ("improve this codebase"). Setting a budget is a good default for production agents. See Turns and budget below for the option reference.
Message types
As the loop runs, the SDK yields a stream of messages. Each message carries a type that tells you what stage of the loop it came from. The five core types are:
- `SystemMessage`: session lifecycle events. The
subtypefield distinguishes them:
"init": the first message with session metadata"compact_boundary": fires after compaction"informational": plain-text status banners from the loop"worker_shutting_down": the loop will end after the current turn because the host is exiting or Remote Control disconnected
In TypeScript, each subtype other than "init" is its own type in the `SDKMessage` union rather than a subtype of SDKSystemMessage.
- `AssistantMessage`: emitted after each Claude response, including the final text-only one. Contains text content blocks and tool call blocks from that turn.
- `UserMessage`: emitted after each tool execution with the tool result content sent back to Claude. Also emitted for any user inputs you stream mid-loop.
- `StreamEvent`: only emitted when partial messages are enabled. Contains raw API streaming events (text deltas, tool input chunks). See Stream responses.
- `ResultMessage`: marks the end of the agent loop. Contains the final text result, token usage, cost, and session ID. Check the
subtypefield to determine whether the task succeeded or hit a limit. A small number of trailing system events, such asprompt_suggestion, can arrive after it, so iterate the stream to completion rather than breaking on the result. See Handle the result.
These 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 and TypeScript message types reference for the complete lists.
Handle messages
Which messages you handle depends on what you're building:
- Final results only: handle
ResultMessageto get the output, cost, and whether the task succeeded or hit a limit. - Progress updates: handle
AssistantMessageto see what Claude is doing each turn, including which tools it called. - Live streaming: enable partial messages (
include_partial_messagesin Python,includePartialMessagesin TypeScript) to getStreamEventmessages in real time. See Stream responses in real-time.
How you check message types depends on the SDK:
- Python: check message types with
isinstance()against classes imported fromclaude_agent_sdk(for example,isinstance(message, ResultMessage)). - TypeScript: check the
typestring field (for example,message.type === "result").AssistantMessageandUserMessagewrap the raw API message in a.messagefield, so content blocks are atmessage.message.content, notmessage.content.
Example: Check message types and handle results ```python Python from claude_agent_sdk import query, AssistantMessage, ResultMessage
async for message in query(prompt="Summarize this project"): if isinstance(message, AssistantMessage): print(f"Turn completed: {len(message.content)} content blocks") if isinstance(message, ResultMessage): if message.subtype == "success": print(message.result) else: print(f"Stopped: {message.subtype}")
import { query } from "@anthropic-ai/claude-agent-sdk";
for await (const message of query({ prompt: "Summarize this project" })) { if (message.type === "assistant") { console.log(Turn completed: ${message.message.content.length} content blocks); } if (message.type === "result") { if (message.subtype === "success") { console.log(message.result); } else { console.log(Stopped: ${message.subtype}); } } }
## Tool execution
Tools give your agent the ability to take action. Without tools, Claude can only respond with text. With tools, Claude can read files, run commands, search code, and interact with external services.
### Built-in tools
The SDK includes the same tools that power Claude Code:
| Category | Tools | What they do |
| :------------------ | :-------------------------------------------------------------- | :-------------------------------------------------------------------------- |
| **File operations** | `Read`, `Edit`, `Write` | Read, modify, and create files |
| **Search** | `Glob`, `Grep` | Find files by pattern, search content with regex |
| **Execution** | `Bash` | Run shell commands, scripts, git operations |
| **Web** | `WebSearch`, `WebFetch` | Search the web, fetch and parse pages |
| **Discovery** | `ToolSearch` | Dynamically find and load tools on-demand instead of preloading all of them |
| **Orchestration** | `Agent`, `Skill`, `AskUserQuestion`, `TaskCreate`, `TaskUpdate` | Spawn subagents, invoke skills, ask the user, track tasks |
Beyond built-in tools, you can:
* **Connect external services** with [MCP servers](/en/agent-sdk/mcp) (databases, browsers, APIs)
* **Define custom tools** with [custom tool handlers](/en/agent-sdk/custom-tools)
* **Load project skills** via [setting sources](/en/agent-sdk/claude-code-features) for reusable workflows
### Tool permissions
Claude determines which tools to call based on the task, but you control whether those calls are allowed to execute. You can auto-approve specific tools, block others entirely, or require approval for everything. Three options work together to determine what runs:
* **`allowed_tools` / `allowedTools`** auto-approves listed tools. A read-only agent with `["Read", "Glob", "Grep"]` in its allowed tools list runs those tools without prompting. Tools not listed are still available but require permission.
* **`disallowed_tools` / `disallowedTools`** blocks listed tools, regardless of other settings. See [Permissions](/en/agent-sdk/permissions) for the order that rules are checked before a tool runs.
* **`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.
You 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.
When a tool is denied, Claude receives a rejection message as the tool result and typically attempts a different approach or reports that it couldn't proceed.
### Parallel tool execution
When Claude requests multiple tool calls in a single turn, both SDKs can run them concurrently or sequentially depending on the tool. Read-only tools (like `Read`, `Glob`, `Grep`, and MCP tools marked as read-only) can run concurrently. Tools that modify state (like `Edit`, `Write`, and `Bash`) run sequentially to avoid conflicts.
Custom tools default to sequential execution. To enable parallel execution for a custom tool, set `readOnlyHint` in its annotations. Both the [TypeScript](/en/agent-sdk/typescript#tool) and [Python](/en/agent-sdk/python#tool) SDKs use this field name from the MCP SDK.
## Control how the loop runs
You can limit how many turns the loop takes, how much it costs, how deeply Claude reasons, and whether tools require approval before running. All of these are fields on [`ClaudeAgentOptions`](/en/agent-sdk/python#claudeagentoptions) (Python) / [`Options`](/en/agent-sdk/typescript#options) (TypeScript).
### Turns and budget
| Option | What it controls | Default |
| :--------------------------------------------- | :--------------------------- | :------- |
| Max turns (`max_turns` / `maxTurns`) | Maximum tool-use round trips | No limit |
| Max budget (`max_budget_usd` / `maxBudgetUsd`) | Maximum cost before stopping | No limit |
When either limit is hit, the SDK returns a `ResultMessage` with a corresponding error subtype (`error_max_turns` or `error_max_budget_usd`). See [Handle the result](#handle-the-result) for how to check these subtypes and [`ClaudeAgentOptions`](/en/agent-sdk/python#claudeagentoptions) / [`Options`](/en/agent-sdk/typescript#options) for syntax.
### Effort level
The `effort` option controls how much reasoning Claude applies. Lower effort levels use fewer tokens per turn and reduce cost. Not all models support the effort parameter. See [Effort](https://platform.claude.com/docs/en/build-with-claude/effort) for which models support it.
| Level | Behavior | Good for |
| :--------- | :-------------------------------- | :------------------------------------------------------------- |
| `"low"` | Minimal reasoning, fast responses | File lookups, listing directories |
| `"medium"` | Balanced reasoning | Routine edits, standard tasks |
| `"high"` | Thorough analysis | Refactors, debugging |
| `"xhigh"` | Extended reasoning depth | Coding and agentic tasks; recommended on Fable 5 and Opus 4.7+ |
| `"max"` | Maximum reasoning depth | Multi-step problems requiring deep analysis |
If you don't set `effort`, both SDKs leave the parameter unset and defer to the model's default behavior.
`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.
Use lower effort for agents doing simple, well-scoped tasks (like listing files or running a single grep) to reduce cost and latency. Set `effort` in the top-level `query()` options for the whole session, or per subagent with the `effort` field on [`AgentDefinition`](/en/agent-sdk/subagents#agentdefinition-configuration) to override the session level.
### Permission mode
The permission mode option (`permission_mode` in Python, `permissionMode` in TypeScript) controls whether the agent asks for approval before using tools:
| Mode | Behavior |
| :------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `"default"` | Tools not covered by allow rules trigger your approval callback; no callback means deny |
| `"acceptEdits"` | Auto-approves file edits and common filesystem commands (`mkdir`, `touch`, `mv`, `cp`, etc.); other Bash commands follow default rules |
| `"plan"` | Claude explores and plans without editing your source files; file edits are never auto-approved and prompt through your `canUseTool` callback |
| `"dontAsk"` | Never prompts. Tools pre-approved by [permission rules](/en/settings#permission-settings) run, everything else is denied |
| `"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 |
| `"bypassPermissions"` | Runs all allowed tools without asking, unless an explicit [`ask` rule](/en/settings#permission-settings) matches; see [How permissions are evaluated](/en/agent-sdk/permissions#how-permissions-are-evaluated) for where ask rules sit in the precedence order. Cannot be used when running as root on Unix. Use only in isolated environments where the agent's actions cannot affect systems you care about |
For interactive applications, use `"default"` with a tool approval callback to surface approval prompts. For autonomous agents on a dev machine, `"acceptEdits"` auto-approves file edits and common filesystem commands (`mkdir`, `touch`, `mv`, `cp`, etc.) while still gating other `Bash` commands behind allow rules. Reserve `"bypassPermissions"` for CI, containers, or other isolated environments. See [Permissions](/en/agent-sdk/permissions) for full details.
### Model
If you don't set `model`, the SDK uses Claude Code's default, which depends on your authentication method and subscription. Set it explicitly (for example, `model="claude-sonnet-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.
## The context window
The context window is the total amount of information available to Claude during a session. It does not reset between turns within a session. Everything accumulates: the system prompt, tool definitions, conversation history, tool inputs, and tool outputs. Content that stays the same across turns (system prompt, tool definitions, CLAUDE.md) is automatically [prompt cached](https://platform.claude.com/docs/en/build-with-claude/prompt-caching), which reduces cost and latency for repeated prefixes.
### What consumes context
Here's how each component affects context in the SDK:
| Source | When it loads | Impact |
| :----------------------- | :------------------------------------------------------------------------ | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **System prompt** | Every request | Small fixed cost, always present |
| **CLAUDE.md files** | Session start, via [`settingSources`](/en/agent-sdk/claude-code-features) | Full content in every request (but prompt-cached, so only the first request pays full cost) |
| **Tool definitions** | Every request; MCP schemas deferred by default | Built-in tool schemas load every request. [Tool search](/en/agent-sdk/mcp#mcp-tool-search) defers MCP tool schemas by default, falling back to upfront loading on Vertex AI or a non-first-party `ANTHROPIC_BASE_URL`. See [Configure tool search](/en/agent-sdk/tool-search#configure-tool-search) for the full matrix |
| **Conversation history** | Accumulates over turns | Grows with each turn: prompts, responses, tool inputs, tool outputs |
| **Skill descriptions** | Session start, via setting sources | Short summaries; full content loads only when invoked |
Large tool outputs consume significant context. Reading a big file or running a command with verbose output can use thousands of tokens in a single turn. Context accumulates across turns, so longer sessions with many tool calls build up significantly more context than short ones.
### Automatic compaction
When the context window approaches its limit, the SDK automatically compacts the conversation: it summarizes older history to free space, keeping your most recent exchanges and key decisions intact. The SDK emits a message with `type: "system"` and `subtype: "compact_boundary"` in the stream when this happens (in Python this is a `SystemMessage`; in TypeScript it is a separate `SDKCompactBoundaryMessage` type).
Compaction replaces older messages with a summary, so specific instructions from early in the conversation may not be preserved. Persistent rules belong in CLAUDE.md (loaded via [`settingSources`](/en/agent-sdk/claude-code-features)) rather than in the initial prompt, because CLAUDE.md content is re-injected on every request.
You can customize compaction behavior in several ways:
* **Summarization instructions in CLAUDE.md:** The compactor reads your CLAUDE.md like any other context, so you can include a section telling it what to preserve when summarizing. The section header is free-form (not a magic string); the compactor matches on intent.
* **`PreCompact` hook:** Run custom logic before compaction occurs, for example to archive the full transcript. The hook receives a `trigger` field (`manual` or `auto`). See [hooks](/en/agent-sdk/hooks).
* **Manual compaction:** Send `/compact` as a prompt string to trigger compaction on demand. Commands sent this way are SDK inputs, not CLI-only shortcuts. See [commands in the SDK](/en/agent-sdk/slash-commands).
**Example: Summarization instructions in CLAUDE.md**
Add a section to your project's CLAUDE.md telling the compactor what to preserve. The header name isn't special; use any clear label.Summary instructions
When summarizing this conversation, always preserve:
- The current task objective and acceptance criteria
- File paths that have been read or modified
- Test results and error messages
- Decisions made and the reasoning behind them
### Keep context efficient
A few strategies for long-running agents:
* **Use subagents for subtasks.** Each subagent starts with a fresh conversation (no prior message history, though it does load its own system prompt and project-level context like CLAUDE.md). It does not see the parent's turns, and only its final response returns to the parent as a tool result. The main agent's context grows by that summary, not by the full subtask transcript. See [What subagents inherit](/en/agent-sdk/subagents#what-subagents-inherit) for details.
* **Be selective with tools.** Every tool definition takes context space. Use the `tools` field on [`AgentDefinition`](/en/agent-sdk/subagents#agentdefinition-configuration) to scope subagents to the minimum set they need.
* **Watch MCP server costs.** [MCP tool search](/en/agent-sdk/mcp#mcp-tool-search) defers MCP tool schemas by default and loads them on demand. When tool search is off, on Vertex AI, or behind a non-first-party `ANTHROPIC_BASE_URL`, each MCP server adds all its tool schemas to every request, so a few servers with many tools can consume significant context before the agent does any work.
* **Use lower effort for routine tasks.** Set [effort](#effort-level) to `"low"` for agents that only need to read files or list directories. This reduces token usage and cost.
For a detailed breakdown of per-feature context costs, see [Understand context costs](/en/features-overview#understand-context-costs).
## Sessions and continuity
Each interaction with the SDK creates or continues a session. Capture the session ID from `ResultMessage.session_id` (available in both SDKs) to resume later. The TypeScript SDK also exposes it as a direct field on the init `SystemMessage`; in Python it's nested in `SystemMessage.data`.
When you resume, the full context from previous turns is restored: files that were read, analysis that was performed, and actions that were taken. You can also fork a session to branch into a different approach without modifying the original.
See [Session management](/en/agent-sdk/sessions) for the full guide on resume, continue, and fork patterns.
In Python, `ClaudeSDKClient` handles session IDs automatically across multiple calls. See the [Python SDK reference](/en/agent-sdk/python#choosing-between-query-and-claudesdkclient) for details.
## Handle the result
When the loop ends, the `ResultMessage` tells you what happened and gives you the output. The `subtype` field (available in both SDKs) is the primary way to check termination state.
| Result subtype | What happened | `result` field available? |
| :------------------------------------ | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :-----------------------: |
| `success` | Claude finished the task normally | Yes |
| `error_max_turns` | Hit the `maxTurns` limit before finishing | No |
| `error_max_budget_usd` | Hit the `maxBudgetUsd` limit before finishing | No |
| `error_during_execution` | An error interrupted the loop (for example, an API failure or cancelled request) | No |
| `error_max_structured_output_retries` | No valid structured output was produced within the configured retry limit: every attempt failed validation, or a model fallback retracted the completed output with no successful retry | No |
The `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.
The 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#sdkresultmessage) (TypeScript) or [`ResultMessage`](/en/agent-sdk/python#resultmessage) (Python) for the full type.
## Hooks
[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:
| Hook | When it fires | Common uses |
| :------------------------------- | :---------------------------------- | :----------------------------------------- |
| `PreToolUse` | Before a tool executes | Validate inputs, block dangerous commands |
| `PostToolUse` | After a tool returns | Audit outputs, trigger side effects |
| `UserPromptSubmit` | When a prompt is sent | Inject additional context into prompts |
| `Stop` | When the agent finishes | Validate the result, save session state |
| `SubagentStart` / `SubagentStop` | When a subagent spawns or completes | Track and aggregate parallel task results |
| `PreCompact` | Before context compaction | Archive full transcript before summarizing |
Hooks 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.
Both 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.
## Put it all together
This 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.import asyncio from claude_agent_sdk import query, ClaudeAgentOptions, ResultMessage
async def run_agent(): session_id = None
async for message in query( prompt="Find and fix the bug causing test failures in the auth module", options=ClaudeAgentOptions( allowed_tools=[ "Read", "Edit", "Bash", "Glob", "Grep", ], # Listing tools here auto-approves them (no prompting) setting_sources=[ "project" ], # Load CLAUDE.md, skills, hooks from current directory max_turns=30, # Prevent runaway sessions effort="high", # Thorough reasoning for complex debugging ), ):
Handle the final result
if isinstance(message, ResultMessage): session_id = message.session_id # Save for potential resumption
if message.subtype == "success": print(f"Done: {message.result}") elif message.subtype == "error_max_turns":
Agent ran out of turns. Resume with a higher limit.
print(f"Hit turn limit. Resume session {session_id} to continue.") elif message.subtype == "error_max_budget_usd": print("Hit budget limit.") else: print(f"Stopped: {message.subtype}") if message.total_cost_usd is not None: print(f"Cost: ${message.total_cost_usd:.4f}")
asyncio.run(run_agent())
import { query } from "@anthropic-ai/claude-agent-sdk";
let sessionId: string | undefined;
for await (const message of query({ prompt: "Find and fix the bug causing test failures in the auth module", options: { allowedTools: ["Read", "Edit", "Bash", "Glob", "Grep"], // Listing tools here auto-approves them (no prompting) settingSources: ["project"], // Load CLAUDE.md, skills, hooks from current directory maxTurns: 30, // Prevent runaway sessions effort: "high" // Thorough reasoning for complex debugging } })) { // Save the session ID to resume later if needed if (message.type === "system" && message.subtype === "init") { sessionId = message.session_id; }
// Handle the final result if (message.type === "result") { if (message.subtype === "success") { console.log(Done: ${message.result}); } else if (message.subtype === "error_max_turns") { // Agent ran out of turns. Resume with a higher limit. console.log(Hit turn limit. Resume session ${sessionId} to continue.); } else if (message.subtype === "error_max_budget_usd") { console.log("Hit budget limit."); } else { console.log(Stopped: ${message.subtype}); } console.log(Cost: $${message.total_cost_usd.toFixed(4)}); } }
## Next steps
Now that you understand the loop, here's where to go depending on what you're building:
* **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.
* **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.
* **Building an interactive UI?** Enable [streaming](/en/agent-sdk/streaming-output) to show live text and tool calls as the loop runs.
* **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.
* **Running long or expensive tasks?** Offload isolated work to [subagents](/en/agent-sdk/subagents) to keep your main context lean.
For the broader conceptual picture of the agentic loop (not SDK-specific), see [How Claude Code works](/en/how-claude-code-works).
Use Claude Code features in the SDK
Load project instructions, skills, hooks, and other Claude Code features into your SDK agents.
The Agent SDK is built on the same foundation as Claude Code, which means your SDK agents have access to the same filesystem-based features: project instructions (CLAUDE.md and rules), skills, hooks, and more.
When you omit settingSources, query() reads the same filesystem settings as the Claude Code CLI: user, project, and local settings, CLAUDE.md files, and .claude/ skills, agents, and commands. To run without these, pass settingSources: [], which limits the agent to what you configure programmatically. Managed policy settings and the global ~/.claude.json config are read regardless of this option. See What settingSources does not control.
For a conceptual overview of what each feature does and when to use it, see Extend Claude Code.
Control filesystem settings with settingSources
The setting sources option (`setting_sources` in Python, `settingSources` in TypeScript) controls which filesystem-based settings the SDK loads. Pass an explicit list to opt in to specific sources, or pass an empty array to disable user, project, and local settings.
This example loads both user-level and project-level settings by setting settingSources to ["user", "project"]: ```python Python from claude_agent_sdk import query, ClaudeAgentOptions, AssistantMessage, ResultMessage
async for message in query( prompt="Help me refactor the auth module", options=ClaudeAgentOptions(
"user" loads from ~/.claude/, "project" loads from ./.claude/ in cwd.
Together they give the agent access to CLAUDE.md, skills, hooks, and
permissions from both locations.
setting_sources=["user", "project"], allowed_tools=["Read", "Edit", "Bash"], ), ): if isinstance(message, AssistantMessage): for block in message.content: if hasattr(block, "text"): print(block.text) if isinstance(message, ResultMessage) and message.subtype == "success": print(f"\nResult: {message.result}")
import { query } from "@anthropic-ai/claude-agent-sdk";
for await (const message of query({ prompt: "Help me refactor the auth module", options: { // "user" loads from ~/.claude/, "project" loads from ./.claude/ in cwd. // Together they give the agent access to CLAUDE.md, skills, hooks, and // permissions from both locations. settingSources: ["user", "project"], allowedTools: ["Read", "Edit", "Bash"] } })) { if (message.type === "assistant") { for (const block of message.message.content) { if (block.type === "text") console.log(block.text); } } if (message.type === "result" && message.subtype === "success") { console.log(\nResult: ${message.result}); } }
Each source loads settings from a specific location, where `<cwd>` is the working directory you pass via the `cwd` option, or the process's current directory if unset. For the full type definition, see [`SettingSource`](/en/agent-sdk/typescript#settingsource) (TypeScript) or [`SettingSource`](/en/agent-sdk/python#settingsource) (Python).
| Source | What it loads | Location |
| :---------- | :---------------------------------------------------------------------------------------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `"project"` | Project CLAUDE.md, `.claude/rules/*.md`, project skills, project hooks, project `settings.json` | `<cwd>/.claude/` for `settings.json` and hooks; `<cwd>` and every parent directory for CLAUDE.md and rules; `<cwd>` and every parent directory up to the repository root for skills |
| `"user"` | User CLAUDE.md, `~/.claude/rules/*.md`, user skills, user settings | `~/.claude/` |
| `"local"` | CLAUDE.local.md, `.claude/settings.local.json` | `<cwd>/.claude/` for `settings.local.json`; `<cwd>` and every parent directory for CLAUDE.local.md |
Omitting `settingSources` is equivalent to `["user", "project", "local"]`.
The `cwd` option determines where the SDK looks for project-level inputs. CLAUDE.md and rules load from `<cwd>` and from every parent directory. Skills load from `<cwd>` and from every parent directory up to the repository root. Project `settings.json` and hooks load only from `<cwd>/.claude/` with no parent-directory fallback.
### What settingSources does not control
`settingSources` covers user, project, and local settings. A few inputs are read regardless of its value:
| Input | Behavior | To disable |
| :----------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------ | :------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| Managed policy settings | Always loaded when present on the host | Remove the managed settings file |
| `~/.claude.json` global config | Always read | Relocate with `CLAUDE_CONFIG_DIR` in `env` |
| Auto memory at `~/.claude/projects/<project>/memory/` | Loaded by default into the system prompt | Set `autoMemoryEnabled: false` in settings, or `CLAUDE_CODE_DISABLE_AUTO_MEMORY=1` in `env` |
| [claude.ai MCP connectors](/en/mcp#use-mcp-servers-from-claude-ai) | Loaded when the active authentication method is a claude.ai subscription. Passing `mcpServers: {}` does not suppress them | Set `strictMcpConfig: true`, [`disableClaudeAiConnectors: true`](/en/mcp#disable-claude-ai-connectors) in settings, or `ENABLE_CLAUDEAI_MCP_SERVERS=false` in `env` |
Do not rely on default `query()` options for multi-tenant isolation. Because the inputs above are read regardless of `settingSources`, an SDK process can pick up host-level configuration and per-directory memory. For multi-tenant deployments, run each tenant in its own filesystem and set `settingSources: []` plus `CLAUDE_CODE_DISABLE_AUTO_MEMORY=1` in `env`. See [Secure deployment](/en/agent-sdk/secure-deployment).
## Project instructions (CLAUDE.md and rules)
`CLAUDE.md` files and `.claude/rules/*.md` files give your agent persistent context about your project: coding conventions, build commands, architecture decisions, and instructions. When `settingSources` includes `"project"` (as in the example above), the SDK loads these files into context at session start. The agent then follows your project conventions without you repeating them in every prompt.
### CLAUDE.md load locations
| Level | Location | When loaded |
| :-------------------- | :---------------------------------------------------------------------------- | :-------------------------------------------------------------------------------------------------- |
| Project (root) | `<cwd>/CLAUDE.md` or `<cwd>/.claude/CLAUDE.md` | `settingSources` includes `"project"` |
| Project rules | `<cwd>/.claude/rules/*.md` and `.claude/rules/*.md` in every parent directory | `settingSources` includes `"project"` |
| Project (parent dirs) | `CLAUDE.md` files in directories above `cwd` | `settingSources` includes `"project"`, loaded at session start |
| Project (child dirs) | `CLAUDE.md` files in subdirectories of `cwd` | `settingSources` includes `"project"`, loaded on demand when the agent reads a file in that subtree |
| Local | `<cwd>/CLAUDE.local.md` and `CLAUDE.local.md` in every parent directory | `settingSources` includes `"local"` |
| User | `~/.claude/CLAUDE.md` | `settingSources` includes `"user"` |
| User rules | `~/.claude/rules/*.md` | `settingSources` includes `"user"` |
All levels are additive: if both project and user CLAUDE.md files exist, the agent sees both. There is no hard precedence rule between levels; if instructions conflict, the outcome depends on how Claude interprets them. Write non-conflicting rules, or state precedence explicitly in the more specific file ("These project instructions override any conflicting user-level defaults").
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.
For how to structure and organize CLAUDE.md content, see [Manage Claude's memory](/en/memory).
## Skills
Skills are markdown files that give your agent specialized knowledge and invocable workflows. Unlike `CLAUDE.md` (which loads every session), skills load on demand. The agent receives skill descriptions at startup and loads the full content when relevant.
Skills are discovered from the filesystem through `settingSources`. When the `skills` option on `query()` is omitted, discovered user and project skills are enabled and the Skill tool is available, matching CLI behavior. To control which skills are enabled, pass `skills` as `"all"`, a list of skill names, or `[]` to disable all. When `skills` is set, the SDK adds the Skill tool to `allowedTools` automatically. If you also pass an explicit `tools` list, include `"Skill"` in that list so Claude can invoke skills.from claude_agent_sdk import query, ClaudeAgentOptions, ResultMessage
Skills in .claude/skills/ are discovered automatically
when settingSources includes "project"
async for message in query( prompt="Review this PR using our code review checklist", options=ClaudeAgentOptions( setting_sources=["user", "project"], skills="all", allowed_tools=["Read", "Grep", "Glob"], ), ): if isinstance(message, ResultMessage) and message.subtype == "success": print(message.result)
import { query } from "@anthropic-ai/claude-agent-sdk";
// Skills in .claude/skills/ are discovered automatically // when settingSources includes "project" for await (const message of query({ prompt: "Review this PR using our code review checklist", options: { settingSources: ["user", "project"], skills: "all", allowedTools: ["Read", "Grep", "Glob"] } })) { if (message.type === "result" && message.subtype === "success") { console.log(message.result); } }
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.
For more on creating and using skills, see [Agent Skills in the SDK](/en/agent-sdk/skills).
## Hooks
The SDK supports two ways to define hooks, and they run side by side:
* **Filesystem hooks:** shell commands defined in `settings.json`, loaded when `settingSources` includes the relevant source. These are the same hooks you'd configure for [interactive Claude Code sessions](/en/hooks-guide).
* **Programmatic hooks:** callback functions passed directly to `query()`. These run in your application process and can return structured decisions. See [Control execution with hooks](/en/agent-sdk/hooks).
Both types execute during the same hook lifecycle. If you already have hooks in your project's `.claude/settings.json` and you set `settingSources: ["project"]`, those hooks run automatically in the SDK with no extra configuration.
Hook callbacks receive the tool input and return a decision dict. Returning `{}` means allow the tool to proceed. To block execution, return a `hookSpecificOutput` object with `permissionDecision: "deny"` and a `permissionDecisionReason`. The reason is sent to Claude as the tool result. The top-level `decision` and `reason` fields are deprecated for `PreToolUse`. See the [hooks guide](/en/agent-sdk/hooks) for the full callback signature and return types.from claude_agent_sdk import query, ClaudeAgentOptions, HookMatcher, ResultMessage
PreToolUse hook callback. Positional args:
input_data: HookInput dict with tool_name, tool_input, hook_event_name
tool_use_id: str | None, the ID of the tool call being intercepted
context: HookContext, carries session metadata
async def audit_bash(input_data, tool_use_id, context): command = input_data.get("tool_input", {}).get("command", "") if "rm -rf" in command: return { "hookSpecificOutput": { "hookEventName": "PreToolUse", "permissionDecision": "deny", "permissionDecisionReason": "Destructive command blocked", } } return {} # Empty dict: allow the tool to proceed
Filesystem hooks from .claude/settings.json run automatically
when settingSources loads them. You can also add programmatic hooks:
async for message in query( prompt="Refactor the auth module", options=ClaudeAgentOptions( setting_sources=["project"], # Loads hooks from .claude/settings.json hooks={ "PreToolUse": [ HookMatcher(matcher="Bash", hooks=[audit_bash]), ] }, ), ): if isinstance(message, ResultMessage) and message.subtype == "success": print(message.result)
import { query, type HookInput, type HookJSONOutput } from "@anthropic-ai/claude-agent-sdk";
// PreToolUse hook callback. HookInput is a discriminated union on // hook_event_name, so narrowing on it gives TypeScript the right // tool_input shape for this event. const auditBash = async (input: HookInput): Promise<HookJSONOutput> => { if (input.hook_event_name !== "PreToolUse") return {}; const toolInput = input.tool_input as { command?: string }; if (toolInput.command?.includes("rm -rf")) { return { hookSpecificOutput: { hookEventName: "PreToolUse", permissionDecision: "deny", permissionDecisionReason: "Destructive command blocked", }, }; } return {}; // Empty object: allow the tool to proceed };
// Filesystem hooks from .claude/settings.json run automatically // when settingSources loads them. You can also add programmatic hooks: for await (const message of query({ prompt: "Refactor the auth module", options: { settingSources: ["project"], // Loads hooks from .claude/settings.json hooks: { PreToolUse: [{ matcher: "Bash", hooks: [auditBash] }] } } })) { if (message.type === "result" && message.subtype === "success") { console.log(message.result); } }
### When to use which hook type
| Hook type | Best for |
| :---------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Filesystem** (`settings.json`) | Sharing hooks between CLI and SDK sessions. Supports `"command"` (shell scripts), `"http"` (POST to an endpoint), `"mcp_tool"` (call a connected MCP server's tool), `"prompt"` (LLM evaluates a prompt), and `"agent"` (spawns a verifier agent). These fire in the main agent and any subagents it spawns. |
| **Programmatic** (callbacks in `query()`) | Application-specific logic, structured decisions, and in-process integration. These also fire inside subagents. The callback receives `agent_id` and `agent_type` to distinguish. |
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.
For full details on programmatic hooks, see [Control execution with hooks](/en/agent-sdk/hooks). For filesystem hook syntax, see [Hooks](/en/hooks).
## Choose the right feature
The Agent SDK gives you access to several ways to extend your agent's behavior. If you're unsure which to use, this table maps common goals to the right approach.
| You want to... | Use | SDK surface |
| :------------------------------------------------------------------------------------------------ | :-------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Set project conventions your agent always follows | [CLAUDE.md](/en/memory) | `settingSources: ["project"]` loads it automatically |
| Give the agent reference material it loads when relevant | [Skills](/en/agent-sdk/skills) | `settingSources` + `skills` option |
| Run a reusable workflow (deploy, review, release) | [User-invocable skills](/en/agent-sdk/skills) | `settingSources` + `skills` option |
| Delegate an isolated subtask to a fresh context (research, review) | [Subagents](/en/agent-sdk/subagents) | `agents` parameter + `allowedTools: ["Agent"]` |
| 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 |
| Run deterministic logic on tool calls (audit, block, transform) | [Hooks](/en/agent-sdk/hooks) | `hooks` parameter with callbacks, or shell scripts loaded via `settingSources` |
| Give Claude structured tool access to an external service | [MCP](/en/agent-sdk/mcp) | `mcpServers` parameter |
**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.
Every 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).
## Related resources
* [Extend Claude Code](/en/features-overview): Conceptual overview of all extension features, with comparison tables and context cost analysis
* [Skills in the SDK](/en/agent-sdk/skills): Full guide to using skills programmatically
* [Subagents](/en/agent-sdk/subagents): Define and invoke subagents for isolated subtasks
* [Hooks](/en/agent-sdk/hooks): Intercept and control agent behavior at key execution points
* [Permissions](/en/agent-sdk/permissions): Control tool access with modes, rules, and callbacks
* [System prompts](/en/agent-sdk/modifying-system-prompts): Inject context without CLAUDE.md files
Track cost and usage
Learn how to track token usage, estimate costs, and configure prompt caching with the Claude Agent SDK.
The Claude Agent SDK provides detailed token usage information for each interaction with Claude. This guide explains how to properly track usage and understand cost reporting, especially when dealing with parallel tool uses and multi-step conversations.
For complete API documentation, see the TypeScript SDK reference and Python SDK reference.
The total_cost_usd and costUSD fields are client-side estimates, not authoritative billing data. The SDK computes them locally from a price table bundled at build time, so they can drift from what you are actually billed when:
- pricing changes
- the installed SDK version does not recognize a model
- billing rules apply that the client cannot model
Use these fields for development insight and approximate budgeting. For authoritative billing, use the Usage and Cost API or the Usage page in the Claude Console. Do not bill end users or trigger financial decisions from these fields.
Understand token usage
The TypeScript and Python SDKs expose the same usage data with different field names:
- TypeScript provides per-step token breakdowns on each assistant message (
message.message.id,message.message.usage), per-model cost viamodelUsageon the result message, and a cumulative total on the result message. - Python provides per-step token breakdowns on each assistant message (
message.usage,message.message_id), per-model cost viamodel_usageon the result message, and the accumulated total on the result message (total_cost_usdandusagedict).
Both SDKs use the same underlying cost model and expose the same granularity. The difference is in field naming and where per-step usage is nested.
Cost tracking depends on understanding how the SDK scopes usage data:
- `query()` call: one invocation of the SDK's
query()function. A single call can involve multiple steps (Claude responds, uses tools, gets results, responds again). Each call produces one `result` message at the end. - Step: a single request/response cycle within a
query()call. Each step produces assistant messages with token usage. - Session: a series of
query()calls linked by a session ID (using theresumeoption). Eachquery()call within a session reports its own cost independently.
The following diagram shows the message stream from a single query() call, with token usage reported at each step and the cumulative estimate at the end:
Each step produces assistant messages
When Claude responds, it sends one or more assistant messages. In TypeScript, each assistant message contains a nested BetaMessage (accessed via message.message) with an id and a `usage` 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.
The result message provides the cumulative estimate
When the query() call completes, the SDK emits a result message with total_cost_usd and cumulative usage. This is available in both TypeScript (`SDKResultMessage`) and Python (`ResultMessage`). 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 estimated total, you can ignore the per-step usage and read this single value.
Get the total cost of a query
The result message (TypeScript, Python) marks the end of the agent loop for a query() call. It includes total_cost_usd, the cumulative estimated cost across all steps in that call. 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.
The following examples iterate over the message stream from a query() call and print the total cost when the result message arrives: ```typescript TypeScript import { query } from "@anthropic-ai/claude-agent-sdk";
for await (const message of query({ prompt: "Summarize this project" })) { if (message.type === "result") { console.log(Total cost: $${message.total_cost_usd}); } }
from claude_agent_sdk import query, ResultMessage import asyncio
async def main(): async for message in query(prompt="Summarize this project"): if isinstance(message, ResultMessage): print(f"Total cost: ${message.total_cost_usd or 0}")
asyncio.run(main())
## Track per-step and per-model usage
The examples in this section use TypeScript field names. In Python, the equivalent fields are [`AssistantMessage.usage`](/en/agent-sdk/python#assistantmessage) and `AssistantMessage.message_id` for per-step usage, and [`ResultMessage.model_usage`](/en/agent-sdk/python#resultmessage) for per-model breakdowns.
### Track per-step usage
Each assistant message contains a nested `BetaMessage` (accessed via `message.message`) with an `id` and `usage` object with token counts. When Claude uses tools in parallel, multiple messages share the same `id` with identical usage data. Track which IDs you've already counted and skip duplicates to avoid inflated totals.
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.
The following example accumulates input and output tokens across all steps, counting each unique message ID only once:import { query } from "@anthropic-ai/claude-agent-sdk";
const seenIds = new Set<string>(); let totalInputTokens = 0; let totalOutputTokens = 0;
for await (const message of query({ prompt: "Summarize this project" })) { if (message.type === "assistant") { const msgId = message.message.id;
// Parallel tool calls share the same ID, only count once if (!seenIds.has(msgId)) { seenIds.add(msgId); totalInputTokens += message.message.usage.input_tokens; totalOutputTokens += message.message.usage.output_tokens; } } }
console.log(Steps: ${seenIds.size}); console.log(Input tokens: ${totalInputTokens}); console.log(Output tokens: ${totalOutputTokens});
### Break down usage per model
The result message includes [`modelUsage`](/en/agent-sdk/typescript#modelusage), a map of model name to per-model token counts and cost. This is useful when you run multiple models (for example, Haiku for subagents and Opus for the main agent) and want to see where tokens are going.
The following example runs a query and prints the cost and token breakdown for each model used:import { query } from "@anthropic-ai/claude-agent-sdk";
for await (const message of query({ prompt: "Summarize this project" })) { if (message.type !== "result") continue;
for (const [modelName, usage] of Object.entries(message.modelUsage)) { console.log(${modelName}: $${usage.costUSD.toFixed(4)}); console.log( Input tokens: ${usage.inputTokens}); console.log( Output tokens: ${usage.outputTokens}); console.log( Cache read: ${usage.cacheReadInputTokens}); console.log( Cache creation: ${usage.cacheCreationInputTokens}); } }
## Accumulate costs across multiple calls
Each `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.
The following examples run two `query()` calls sequentially, add each call's `total_cost_usd` to a running total, and print both the per-call and combined cost:import { query } from "@anthropic-ai/claude-agent-sdk";
// Track cumulative cost across multiple query() calls let totalSpend = 0;
const prompts = [ "Read the files in src/ and summarize the architecture", "List all exported functions in src/auth.ts" ];
for (const prompt of prompts) { for await (const message of query({ prompt })) { if (message.type === "result") { totalSpend += message.total_cost_usd; console.log(This call: $${message.total_cost_usd}); } } }
console.log(Total spend: $${totalSpend.toFixed(4)});
from claude_agent_sdk import query, ResultMessage import asyncio
async def main():
Track cumulative cost across multiple query() calls
total_spend = 0.0
prompts = [ "Read the files in src/ and summarize the architecture", "List all exported functions in src/auth.ts", ]
for prompt in prompts: async for message in query(prompt=prompt): if isinstance(message, ResultMessage): cost = message.total_cost_usd or 0 total_spend += cost print(f"This call: ${cost}")
print(f"Total spend: ${total_spend:.4f}")
asyncio.run(main())
## Handle errors, caching, and token discrepancies
For accurate cost tracking, account for failed conversations, cache token pricing, and occasional reporting inconsistencies.
### Resolve output token discrepancies
In rare cases, you might observe different `output_tokens` values for messages with the same ID. When this occurs:
1. **Use the highest value:** the final message in a group typically contains the accurate total.
2. **Prefer the result message:** the `total_cost_usd` in the result message reflects the SDK's accumulated estimate across all steps, so it is more reliable than summing per-step values yourself. It is still an estimate and may differ from your actual bill.
3. **Report inconsistencies:** file issues at the [Claude Code GitHub repository](https://github.com/anthropics/claude-code/issues).
### Track costs on failed conversations
Both 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`.
### Track cache tokens
The 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:
* `cache_creation_input_tokens`: tokens used to create new cache entries (charged at a higher rate than standard input tokens).
* `cache_read_input_tokens`: tokens read from existing cache entries (charged at a reduced rate).
Track 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#resultmessage) dict (for example, `message.usage.get("cache_read_input_tokens", 0)`).
### Extend the prompt cache TTL to one hour
Cache entries written by the SDK use a 5-minute TTL by default when you authenticate with an API key or run on Amazon Bedrock, Google Cloud Vertex AI, or Microsoft Foundry. If your workload runs many short sessions against the same system prompt and context with gaps longer than 5 minutes between them, the cache expires between sessions and each new session pays full input price.
To request a 1-hour TTL on cache writes, set the [`ENABLE_PROMPT_CACHING_1H`](/en/env-vars) environment variable. You can export it in your shell or container environment, or pass it through `options.env`.
The following example enables 1-hour TTL for an agent running on Bedrock:from claude_agent_sdk import ClaudeAgentOptions, query import asyncio
async def main(): options = ClaudeAgentOptions( env={ "CLAUDE_CODE_USE_BEDROCK": "1", "ENABLE_PROMPT_CACHING_1H": "1", }, )
async for message in query(prompt="Summarize this project", options=options): print(message)
asyncio.run(main())
import { query } from "@anthropic-ai/claude-agent-sdk";
const options = { env: { ...process.env, CLAUDE_CODE_USE_BEDROCK: "1", ENABLE_PROMPT_CACHING_1H: "1", }, };
for await (const message of query({ prompt: "Summarize this project", options })) { console.log(message); }
Cache writes with a 1-hour TTL are billed at a higher rate than 5-minute writes, so enabling this trades higher write cost for more cache reads. See [prompt caching pricing](https://platform.claude.com/docs/en/build-with-claude/prompt-caching) for details. Claude subscription users already receive 1-hour TTL automatically and do not need to set this variable.
## Related documentation
* [TypeScript SDK Reference](/en/agent-sdk/typescript) - Complete API documentation
* [SDK Overview](/en/agent-sdk/overview) - Getting started with the SDK
* [SDK Permissions](/en/agent-sdk/permissions) - Managing tool permissions
Hosting the Agent SDK
Deploy the Agent SDK in production: subprocess architecture, session persistence, scaling, observability, and multi-tenant isolation for Docker, Kubernetes, and sandbox providers.
The Agent SDK spawns and supervises a claude CLI subprocess that owns a shell, a working directory, and session files on disk. Hosting it is not like hosting a stateless API wrapper. Every running agent is a long-lived process tied to local state, which shapes how you allocate resources, persist sessions, and scale across tenants.
This page covers self-hosting on your own infrastructure: understand the subprocess model, choose a session pattern, provision the container, and handle production concerns like persistence, observability, auth, and multi-tenant isolation. For deployable Dockerfiles and Kubernetes manifests, see the hosting cookbook.
If you do not need infrastructure control, custom isolation, or your own data plane, consider Managed Agents instead: a hosted REST API where Anthropic runs the agent and the sandbox, so your application sends events and streams back results with no hosting infrastructure to operate.
For security hardening beyond basic sandboxing, including network controls, credential management, and isolation options, see Secure Deployment.
The subprocess model
Every hosting decision on this page follows from how the SDK runs the agent. When your code calls query(), the SDK spawns a separate claude CLI process and talks to it over stdio. That subprocess owns the shell, the working directory, and the JSONL session transcripts on local disk.
One agent session maps to one subprocess. Running N concurrent sessions means N subprocesses, each with its own process tree and transcript file. By default they all inherit your application's working directory, so pass cwd on each query() call when sessions need separate filesystems: ```typescript TypeScript query({ prompt, options: { cwd: "/work/session-a" } })
query(prompt=prompt, options=ClaudeAgentOptions(cwd="/work/session-a"))
### State that lives on local disk
Three kinds of agent state live on the container's filesystem by default. None of them survive a container restart, a scale-down, or a move to a different node.
| State | Default location |
| --------------------------- | ------------------------------------------------------------------------------------------------ |
| Session transcripts | `~/.claude/projects/`, or the `projects/` directory under `CLAUDE_CONFIG_DIR` if set |
| `CLAUDE.md` memory files | `~/.claude/CLAUDE.md` for the user tier and the session's working directory for the project tier |
| Working-directory artifacts | The session's working directory |
To persist transcripts across hosts, configure a [`SessionStore` adapter](/en/agent-sdk/session-storage). Memory files and other working-directory artifacts need their own storage strategy, such as a mounted volume or an object-store sync.
For how sessions, resumption, and forking work at the API level, see [Sessions](/en/agent-sdk/sessions).
## Choose a session pattern
These four patterns cover session lifecycle: how long a container lives relative to the sessions it serves. For where the container runs, the [hosting cookbook](https://github.com/anthropics/claude-cookbooks/blob/main/claude_agent_sdk/07_Hosting_the_agent.ipynb) has [deployable code](https://github.com/anthropics/claude-cookbooks/tree/main/claude_agent_sdk/hosting) for local Docker, Modal, and Kubernetes. Choose a session pattern here and a deployment target from the cookbook.
### Ephemeral sessions
Create a container for each user task and destroy it when the task completes. Best for one-off tasks. The user may still interact with the AI while the task is completing, but once completed the container is destroyed.
Example workloads include bug investigation and fix, invoice and receipt extraction, document translation, and media transformation.
The container runs a one-shot entrypoint that calls the SDK and exits. The example below shows a minimal TypeScript version. Save it as `entrypoint.mts` or set `"type": "module"` in `package.json` so top-level `await` is available.import { query } from "@anthropic-ai/claude-agent-sdk";
const prompt = process.env.TASK_PROMPT!; for await (const message of query({ prompt, options: { maxTurns: 20 } })) { console.log(message); }
### Long-running sessions
Run persistent container instances, often hosting multiple SDK processes per container, to serve ongoing work. Best for agents that take autonomous action, serve content, or handle high-volume message streams.
Example workloads include an email agent that triages and responds to incoming mail, a site builder that hosts a per-user editable site through container ports, and a chat bot that handles continuous traffic from a platform like Slack.
The container exposes an HTTP or WebSocket endpoint and maps each active session to a long-lived query and the subprocess behind it. In TypeScript, use [`streamInput()`](/en/agent-sdk/typescript#query-object) to add turns to an active session and [`startup()`](/en/agent-sdk/typescript#startup) to pre-warm subprocesses ahead of incoming traffic. In Python, use [`ClaudeSDKClient`](/en/agent-sdk/python#claudesdkclient) to hold a session open across turns. Size the container so it can hold the maximum number of concurrent sessions in memory.
### Hybrid sessions
Ephemeral containers that hydrate from a [`SessionStore`](/en/agent-sdk/session-storage) on startup and persist updates back. Best for sessions that span many interactions but sit idle between them. The container spins down during idle periods and spins back up when the user returns.
Example workloads include a personal project manager with intermittent check-ins, deep research that pauses and resumes over hours, and a customer support agent that loads ticket history across interactions.
Tune your provider's idle timeout to how frequently you expect users to return. Shutting a container down without a `SessionStore` configured loses the transcript with it, so the store is required for this pattern, not optional.
The pattern hinges on resuming a session by ID with a shared store attached:import { query, type SessionStore } from "@anthropic-ai/claude-agent-sdk";
declare const userInput: string; declare const sessionId: string; // looked up from your database by user declare const sessionStore: SessionStore; // S3, Redis, Postgres, or your own adapter
for await (const message of query({ prompt: userInput, options: { resume: sessionId, sessionStore }, })) { // ... }
from claude_agent_sdk import query, ClaudeAgentOptions
async for message in query( prompt=user_input, options=ClaudeAgentOptions( resume=session_id, # looked up from your database by user session_store=session_store, # S3, Redis, Postgres, or your own adapter ), ): ...
See [Session storage](/en/agent-sdk/session-storage) for the full `SessionStore` interface and reference adapters.
### Multi-agent container
Run multiple SDK subprocesses inside one container. Best for agents that must collaborate closely, for example multi-agent simulations where the agents interact with each other in a shared environment.
Give each agent its own working directory so they do not overwrite each other's files, and isolate settings loading so per-agent `CLAUDE.md` files do not leak across agents. See [Multi-tenant isolation](#multi-tenant-isolation) for the specific options.
## Provision the container
### Container-based sandboxing
Run the SDK inside a sandboxed container for process isolation, resource limits, network control, and an ephemeral filesystem. Several providers specialize in sandboxed container environments that fit the Agent SDK's model.
Questions to answer when choosing a provider:
* **Who runs the sandbox**: a sandbox-as-a-service provider operates the infrastructure for you, while self-hosted options give you software to run on your own.
* **Cold-start latency**: how long from "create a sandbox" to "ready to accept the first request." Ephemeral patterns need sub-second starts. Long-running patterns tolerate more.
* **Persistent storage**: whether the provider offers durable volumes or only ephemeral disk. The hybrid pattern needs durable storage somewhere, whether in the sandbox or alongside it.
* **Pricing model**: per-second, per-request, or flat hourly billing. Per-second pricing suits bursty ephemeral workloads. Hourly suits long-running sessions.
* **Networking**: support for custom egress rules, outbound proxies, and private VPC peering for regulated environments.
Providers to evaluate:
* [Modal Sandbox](https://modal.com/docs/guide/sandbox), with a [demo implementation](https://modal.com/docs/examples/claude-slack-gif-creator)
* [Cloudflare Sandboxes](https://github.com/cloudflare/sandbox-sdk)
* [Daytona](https://www.daytona.io/)
* [E2B](https://e2b.dev/)
* [Fly Machines](https://fly.io/docs/machines/)
* [Vercel Sandbox](https://vercel.com/docs/functions/sandbox)
For self-hosted options such as Docker, gVisor, and Firecracker, and detailed isolation configuration, see [Isolation Technologies](/en/agent-sdk/secure-deployment#isolation-technologies).
### Runtime dependencies
The container needs only your SDK's language runtime:
* Python 3.10+ for the Python SDK, or Node.js 18+ for the TypeScript SDK
* Both SDK packages bundle a native Claude Code binary for the host platform, so no separate Claude Code or Node.js install is needed for the spawned CLI
The bundled binary is pinned to the SDK package version, so updating the SDK is how you update the CLI. The SDK follows semver: take patch releases continuously and review the [TypeScript](https://github.com/anthropics/claude-agent-sdk-typescript/blob/main/CHANGELOG.md) or [Python](https://github.com/anthropics/claude-agent-sdk-python/blob/main/CHANGELOG.md) changelog before taking a minor.
### Resources
1 GiB RAM, 5 GiB disk, and 1 CPU per agent is a reasonable starting point for a freshly started instance. Memory usage grows with session length and tool activity, so size for the session lengths and concurrency you actually need rather than the idle baseline. See [Scaling and concurrency](#scaling-and-concurrency) for how to work out agents per host.
### Network
The SDK needs outbound HTTPS to `api.anthropic.com`, or to your provider's regional endpoint when running on Bedrock or Vertex. If your agents use [MCP servers](/en/agent-sdk/mcp) or external tools, they need outbound access to those endpoints as well. For production, route outbound traffic through an egress proxy that enforces domain allowlists, injects credentials, and logs requests. See [Secure Deployment](/en/agent-sdk/secure-deployment) for the full pattern.
For inbound traffic, expose an HTTP or WebSocket port on the container. Your application handles client requests on that port and calls the SDK internally; the subprocess itself does not listen on the network.
## Handle production concerns
Work through these decisions before shipping a self-hosted agent.
### Session and state persistence
Default local disk is lost on restart, scale-down, or a move to a different node. For any session a user expects to resume, mirror the transcript to durable storage with a [`SessionStore` adapter](/en/agent-sdk/session-storage). See [Reference implementations](/en/agent-sdk/session-storage#reference-implementations) for S3, Redis, and Postgres adapters and a conformance suite for your own.
Three things to know about how `SessionStore` behaves:
* **Transcripts only**: `SessionStore` mirrors transcripts, not `CLAUDE.md` memory files or other working-directory artifacts. Mount a shared volume or sync those separately.
* **Mirror, not replacement**: the subprocess writes to local disk first, and the store receives a copy of each batch. Local writes remain authoritative.
* **`mirror_error` messages**: if the store rejects or times out, the SDK emits a `{ type: "system", subtype: "mirror_error" }` message and continues the query without retry. Alert on these if store durability matters.
### Observability
Agent SDK agents are long-lived processes that spawn tool calls across many API round-trips. Without telemetry you cannot see which tools ran, how long they took, or where a session stalled.
The SDK inherits OpenTelemetry configuration from the environment. Set the OTEL environment variables at the container or orchestrator level so every `query()` call exports spans, metrics, and log events to your collector. The example below enables OTLP export for all three signals. `CLAUDE_CODE_ENHANCED_TELEMETRY_BETA` is required only for traces; omit it if you export metrics and logs alone.CLAUDE_CODE_ENABLE_TELEMETRY=1 CLAUDE_CODE_ENHANCED_TELEMETRY_BETA=1 OTEL_TRACES_EXPORTER=otlp OTEL_METRICS_EXPORTER=otlp OTEL_LOGS_EXPORTER=otlp OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf OTEL_EXPORTER_OTLP_ENDPOINT=http://collector.example.com:4318
Prompt text and tool inputs are not included in exports by default. See [Control sensitive data in exports](/en/agent-sdk/observability#control-sensitive-data-in-exports) for the opt-in flags, and [Observability](/en/agent-sdk/observability) for the full signal catalog.
### Auth and secrets
Three auth concerns matter at hosting time:
* **Anthropic API**: the subprocess reads `ANTHROPIC_API_KEY` from its environment. Supply it from your secret manager, or set `ANTHROPIC_BASE_URL` to route model calls through a proxy that injects the key outside the container. See [Credential management](/en/agent-sdk/secure-deployment#credential-management) for the proxy pattern and the [SDK overview](/en/agent-sdk/overview#get-started) for supported authentication methods.
* **Inbound**: put authentication at a gateway in front of the agent container. The agent should receive pre-authenticated requests and should not be the component that validates user tokens.
* **Outbound tools**: keep tool credentials out of the agent environment. Route outbound calls through a proxy that injects API keys after the request leaves the container. The agent makes the call; the proxy adds the credential.
### Scaling and concurrency
Each session runs in its own subprocess, so concurrency on a host is bounded by how many subprocesses its RAM can hold.
Size each host with this formula:agents per host = (host RAM - overhead) / (per-session RAM ceiling)
Measure the per-session ceiling by running a representative session to your target length under your expected tool load and recording peak RSS. The 1 GiB starting point in [Resources](#resources) is a floor, not the ceiling.
Horizontal-scale routing depends on your pattern. For long-running sessions, where containers hold many sessions, run a pool of containers behind a load balancer and pin each session to one container using consistent hashing on `sessionId`. A pinned session keeps hitting the same container, and therefore the same running subprocess, until it is evicted or the container restarts.
Large fanouts of concurrent [subagents](/en/agent-sdk/subagents) from a single session can hit API rate limits. Break the work into smaller batches rather than issuing one wide dispatch.
### Cost
Anthropic token cost typically dominates container infrastructure cost by an order of magnitude or more. A minimally provisioned container runs roughly \$0.05 per hour, while a single long agent session can spend dollars in tokens. See [Cost tracking](/en/agent-sdk/cost-tracking) for per-session token accounting.
### Multi-tenant isolation
Default SDK behavior reads settings and `CLAUDE.md` memory files from the filesystem. In a shared container that serves multiple tenants, those files can leak one tenant's context into another tenant's session.
To isolate tenants inside a shared container:
* Pass `settingSources: []` in TypeScript or `setting_sources=[]` in Python so no filesystem settings load.
* Set `CLAUDE_CODE_DISABLE_AUTO_MEMORY=1` in `env`. [Auto memory](/en/memory#auto-memory) at `~/.claude/projects/<project>/memory/` loads into the system prompt regardless of `settingSources`. See [What settingSources does not control](/en/agent-sdk/claude-code-features#what-settingsources-does-not-control) for the other inputs that load unconditionally.
* Point `CLAUDE_CONFIG_DIR` at a per-tenant directory so tenants do not share the `~/.claude.json` global config.
* Use a per-tenant working directory. Pass `cwd` explicitly on every `query()` call.
* Apply per-tenant egress rules at your proxy, such as distinct outbound IPs, credentials, or domain allowlists, so a compromised tenant cannot exfiltrate data via another tenant's outbound policy.
The example below applies the four SDK-level options together. Construct `tenantDir` and `configDir` so each tenant gets a path no other tenant can read. In TypeScript, `env` replaces the subprocess environment, so spread `...process.env` to keep inherited variables like `PATH` and `ANTHROPIC_API_KEY`. In Python, `env` is merged on top of the inherited environment.import { query } from "@anthropic-ai/claude-agent-sdk";
declare const prompt: string; declare const tenantDir: string; declare const configDir: string;
for await (const message of query({ prompt, options: { cwd: tenantDir, settingSources: [], env: { ...process.env, CLAUDE_CONFIG_DIR: configDir, CLAUDE_CODE_DISABLE_AUTO_MEMORY: "1", }, }, })) { // ... }
from claude_agent_sdk import query, ClaudeAgentOptions
async for message in query( prompt=prompt, options=ClaudeAgentOptions( cwd=tenant_dir, setting_sources=[], env={ "CLAUDE_CONFIG_DIR": config_dir, "CLAUDE_CODE_DISABLE_AUTO_MEMORY": "1", }, ), ): ...
For per-tenant network controls, see [Secure Deployment](/en/agent-sdk/secure-deployment).
## Known limitations
Plan around these in your deployment design.
| Limitation | What to do |
| --------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| No top-level session timeout | A session does not time out on its own. Set `maxTurns` in `Options` to bound how many tool-use round trips the agent takes before stopping. |
| Memory growth over long sessions | Cap session length or recycle subprocesses periodically. See [Scaling and concurrency](#scaling-and-concurrency). |
| Large parallel-subagent fanouts can hit rate limits | Break work into smaller batches rather than issuing one wide dispatch. |
| No per-subagent wall-clock deadline | Cap each [subagent](/en/agent-sdk/subagents) with `maxTurns` in its `AgentDefinition`. For background subagents only, `CLAUDE_ASYNC_AGENT_STALL_TIMEOUT_MS` sets a stall watchdog that fires when a `run_in_background` subagent stops producing output; it is not a total-runtime deadline. |
## Next steps
* [Hosting cookbook](https://github.com/anthropics/claude-cookbooks/blob/main/claude_agent_sdk/07_Hosting_the_agent.ipynb): notebook walkthrough with [deployable code](https://github.com/anthropics/claude-cookbooks/tree/main/claude_agent_sdk/hosting) for Docker, Modal, and Kubernetes.
* [Session storage](/en/agent-sdk/session-storage): persist transcripts across hosts with a `SessionStore` adapter.
* [Observability](/en/agent-sdk/observability): export OTEL traces, metrics, and logs to your collector.
* [Secure deployment](/en/agent-sdk/secure-deployment): network controls, credential management, and isolation hardening.
* [Cost tracking](/en/agent-sdk/cost-tracking): per-session token and cost accounting.