TypeScript SDK reference
The complete @anthropic-ai/claude-agent-sdk API: functions, Options, the Query object, every message type, hook types, tool schemas, permissions and sandbox settings.
This is the full reference for the @anthropic-ai/claude-agent-sdk package. It runs from installation through functions, the Options object and the Query handle, then message types, hooks, built-in tool schemas, permission types, supporting types and sandbox settings. Guides that explain the ideas behind each area are linked as you go.
Version notes matter a lot in this SDK. The SDK version tracks the bundled Claude Code version: SDK v0.3.191 ships Claude Code v2.1.191. So when a feature says "Claude Code v2.1.N", you need SDK v0.3.N or later unless you point at your own binary.
Installing
npm install @anthropic-ai/claude-agent-sdk
A native Claude Code binary for your platform comes in as an optional dependency, such as @anthropic-ai/claude-agent-sdk-darwin-arm64, so most installs need nothing else.
- If your package manager skips optional dependencies, you get
Native CLI binary for <platform>-<arch> not found. Install Claude Code separately and setpathToClaudeCodeExecutable. - Package managers that ignore npm's
libcfield (Yarn 1.x, for example) install both the glibc and musl Linux packages. From SDK v0.2.141 the right one is still chosen. In a container image you can delete the unused one, for examplerm -rf node_modules/@anthropic-ai/claude-agent-sdk-linux-x64-muslon a glibc x64 runtime.
Single-file executables with Bun
bun build --compile breaks binary resolution, because require.resolve cannot see into the compiled executable's $bunfs filesystem. From SDK v0.3.144, embed the binary as a file asset and extract it at start-up:
import cliAsset from "@anthropic-ai/claude-agent-sdk-linux-x64/claude" with { type: "file" };
import { extractFromBunfs } from "@anthropic-ai/claude-agent-sdk/extract";
import { query } from "@anthropic-ai/claude-agent-sdk";
const cli = extractFromBunfs(cliAsset); // copies to a per-user temp dir; a no-op outside a compiled binary
for await (const m of query({ prompt: "List the services in docker-compose.yml", options: { pathToClaudeCodeExecutable: cli } })) {
if (m.type === "result") console.log(m.subtype);
}
Each executable embeds one platform's binary, so match the import to your --target. To cross-compile, force-install the other platform package (npm install @anthropic-ai/claude-agent-sdk-linux-x64 --force). On Windows the subpath is claude.exe.
The /core entry point for bundlers
If you bundle the SDK with your own dependencies, import from @anthropic-ai/claude-agent-sdk/core (SDK v0.3.282+, TypeScript 5.0+ for its types).
- It exports
query(),startup(),tool(),createSdkMcpServer(),resolveSettings(), the rename, tag and delete session helpers,AbortError, runtime constants and every type. - It omits
prewarm(),InMemorySessionStore, and the helpers that list, read, fork, import and summarise sessions. Use the root entry if you need those. - The root entry inlines its own
zodand@modelcontextprotocol/sdk;/coreimports them from yournode_modulesat the peer dependency ranges, avoiding duplicate copies. - Use one entry per process. Loading both gives you two copies of the SDK's classes and state.
Functions
query()
function query(params: {
prompt: string | AsyncIterable<SDKUserMessage>;
options?: Options;
}): Query;
The main entry point. Pass a string for a one-shot run, or an async iterable of SDKUserMessage for streaming input. Returns a Query, which is an async generator of SDKMessage with extra control methods.
startup()
Spawns the CLI and completes its initialise handshake before you have a prompt, so the first real query starts instantly.
function startup(params?: { options?: Options; initializeTimeoutMs?: number }): Promise<WarmQuery>;
initializeTimeoutMs defaults to 60000; the promise rejects if the handshake takes longer.
import { startup } from "@anthropic-ai/claude-agent-sdk";
const warm = await startup({ options: { cwd: "/srv/app", maxTurns: 10 } }); // at boot
// ...later, on the first request:
for await (const m of warm.query("Summarise today's error log")) { /* ... */ }
If you will not know the working directory until later, use prewarm().
prewarm()
Alpha, SDK v0.3.282+. Starts a spare process that is not yet tied to a session, which you later bind with claim(). Useful for apps that boot before the user picks a folder.
function prewarm(params?: { options?: Options; initializeTimeoutMs?: number }): Promise<SpareProcess>;
- The spare waits in
options.cwdif set, otherwise in a private temp directory under your Claude Code config directory. IfspawnClaudeCodeProcessruns Claude Code elsewhere, setoptions.cwdto a path that exists there. - The working directory,
SessionStarthooks, stdio MCP servers, CLAUDE.md and git context all wait for the claim. - A spare uses roughly 230 to 260 MB while waiting.
prewarm()throws ifoptionssetsresume,continueorforkSession.- Anything a claim cannot change (
mcpServers,hooks,canUseTool,settingSources,systemPrompt,pluginsand so on) is fixed for the spare's life, so keep one spare per distinct configuration.
import { prewarm } from "@anthropic-ai/claude-agent-sdk";
const spare = await prewarm({ options: { maxTurns: 20 } });
const q = spare.claim({ prompt: "What does this repo do?", options: { cwd: chosenFolder } });
spare.claimed.catch(err => {
if (!String(err.message).startsWith("option_not_applied")) startFreshWithQuery(); // prompt never ran
});
try {
for await (const m of q) { /* ... */ }
} catch (err) { /* a refused claim throws after yielding its error result */ }
tool()
Defines a type-safe tool for an in-process MCP server, using a Zod shape (Zod 3 or 4).
function tool<Schema extends AnyZodRawShape>(
name: string,
description: string,
inputSchema: Schema,
handler: (args: InferShape<Schema>, extra: unknown) => Promise<CallToolResult>,
extras?: { annotations?: ToolAnnotations; searchHint?: string; alwaysLoad?: boolean }
): SdkMcpToolDefinition<Schema>;
| Extra | Purpose |
|---|---|
annotations | MCP behaviour hints (below) |
searchHint | One-line capability phrase shown in the deferred-tool list when tool search is active |
alwaysLoad | Keep this tool's full schema in the initial prompt instead of deferring it |
ToolAnnotations (from @modelcontextprotocol/sdk/types.js; hints only, never a security control):
| Field | Default | Meaning |
|---|---|---|
title | none | Display title |
readOnlyHint | false | Does not change its environment |
destructiveHint | true | May make destructive changes (only meaningful when not read-only) |
idempotentHint | false | Repeat calls have no extra effect (only meaningful when not read-only) |
openWorldHint | true | Touches external systems |
import { tool } from "@anthropic-ai/claude-agent-sdk";
import { z } from "zod";
const lookupPostcode = tool(
"lookup_postcode",
"Return the local authority and ward for a UK postcode",
{ postcode: z.string().regex(/^[A-Z0-9 ]{5,8}$/i) },
async ({ postcode }) => {
const info = await geo.lookup(postcode);
return { content: [{ type: "text", text: `${info.authority}, ${info.ward}` }] };
},
{ annotations: { readOnlyHint: true, openWorldHint: true } }
);
createSdkMcpServer()
function createSdkMcpServer(options: {
name: string;
version?: string;
instructions?: string;
tools?: Array<SdkMcpToolDefinition<any>>;
alwaysLoad?: boolean;
timeout?: number;
}): McpSdkServerConfigWithInstance;
| Option | Meaning |
|---|---|
name | Server name |
version | Optional version string |
instructions | Returned from initialize and shown to the model as an MCP instructions block |
tools | Tools from tool() |
alwaysLoad | Keep every tool from this server out of tool search deferral; combines with per-tool alwaysLoad |
timeout | Per-call timeout in ms for this server, replacing MCP_TOOL_TIMEOUT. Whole number of at least 1000; other values ignored (SDK v0.3.248+) |
See custom tools for a full walkthrough.
Session helpers
| Function | Signature | Notes |
|---|---|---|
listSessions | (options?: { dir?, limit?, includeWorktrees? = true }) => Promise<SDKSessionInfo[]> | Newest first. Omit dir for all projects |
getSessionMessages | (sessionId, options?: { dir?, limit?, offset? }) => Promise<SessionMessage[]> | User and assistant messages |
getSessionInfo | (sessionId, options?: { dir? }) => Promise<SDKSessionInfo | undefined> | One session's metadata without a scan |
renameSession | (sessionId, title, options?: { dir? }) => Promise<void> | Appends a title; latest wins. Title must be non-empty after trimming |
tagSession | (sessionId, tag | null, options?: { dir? }) => Promise<void> | null clears |
SDKSessionInfo
| Field | Meaning |
|---|---|
sessionId | UUID |
summary | Custom title, latest prompt, generated summary or first prompt |
lastModified | ms since epoch |
fileSize | Bytes; local JSONL storage only |
customTitle | Title set via --name, /rename, a hook's sessionTitle or renameSession(), else the generated title |
firstPrompt | First meaningful prompt |
gitBranch | Branch at session end |
cwd | Working directory |
tag | From tagSession() |
createdAt | From the first entry's timestamp |
SessionMessage: type (user or assistant), uuid, session_id, message (raw payload), parent_tool_use_id (the Agent or Skill call that started a subagent, else null), parent_agent_id (the spawning subagent's ID for nested subagents; Claude Code v2.1.202+).
import { listSessions, getSessionMessages } from "@anthropic-ai/claude-agent-sdk";
const [last] = await listSessions({ dir: "/srv/app", limit: 1 });
if (last) {
for (const m of await getSessionMessages(last.sessionId, { dir: "/srv/app", limit: 50 })) {
console.log(m.type, m.uuid);
}
}
resolveSettings()
Alpha. Computes the effective settings for a directory with the CLI's own merge engine, without spawning anything.
function resolveSettings(options?: {
cwd?: string; // default process.cwd()
settingSources?: SettingSource[]; // default: all
managedSettings?: Settings;
serverManagedSettings?: Settings;
}): Promise<ResolvedSettings>;
It returns effective (the merged settings), provenance (which source supplied each top-level key) and sources (each source's raw settings, lowest precedence first, with optional path and policyOrigin).
It differs from a live session in three ways: it reads MDM sources but does not run a policyHelper; it only includes server-managed settings you pass in; and it reports permissions.defaultMode verbatim from every tier, including auto and bypassPermissions from project or local files that a live session would ignore.
const { effective, provenance } = await resolveSettings({ cwd: "/srv/app" });
console.log(effective.cleanupPeriodDays, "from", provenance.cleanupPeriodDays?.source);
Options
Grouped by purpose. All are optional.
Tools and permissions
| Option | Type | Default | Meaning |
|---|---|---|---|
tools | string[] | { type: 'preset'; preset: 'claude_code' } | none | Built-in tool set |
allowedTools | string[] | [] | Auto-approve these. Does not restrict others. Naming a task-tracking tool opts the session in |
disallowedTools | string[] | [] | Bare name removes the tool; scoped rule denies matching calls in every mode |
permissionMode | PermissionMode | none | Starting mode; omitted may mean auto mode |
allowDangerouslySkipPermissions | boolean | false | Required to use bypassPermissions, at start or later via setPermissionMode() |
canUseTool | CanUseTool | none | Called only when the flow reaches a prompt |
permissionPromptToolName | string | none | MCP tool that answers permission prompts |
permissionPrompts | 'host' | 'none' | 'host' | 'none' denies calls that would prompt (Claude Code v2.1.259+) |
planModeInstructions | string | none | Replaces the plan-mode workflow body; the read-only preamble and ExitPlanMode footer are kept |
toolAliases | Record<string, string> | none | Map built-ins to MCP tools, e.g. { Bash: 'mcp__workspace__bash' } |
toolConfig | ToolConfig | none | Built-in tool behaviour |
sandbox | SandboxSettings | none | See Sandbox |
Prompt, model and reasoning
| Option | Type | Default | Meaning |
|---|---|---|---|
systemPrompt | see below | minimal prompt | |
model | string | CLI default | Alias or full ID |
fallbackModel | string | none | Comma-separated list allowed |
thinking | ThinkingConfig | { type: 'adaptive' } on supported models | |
maxThinkingTokens | number | none | Deprecated |
effort | 'low' | 'medium' | 'high' | 'xhigh' | 'max' | none | Works with adaptive thinking |
betas | SdkBeta[] | [] | |
outputFormat | { type: 'json_schema'; schema } | none | Structured outputs |
taskBudget | { total: number } | none | Alpha. Tells the model its remaining token budget so it can pace itself |
agent | string | none | Agent to run as the main thread; must be defined in agents or settings |
agents | Record<string, AgentDefinition> | none | Programmatic subagents |
systemPrompt accepts:
- a string (custom prompt);
- a
string[]withSYSTEM_PROMPT_DYNAMIC_BOUNDARYbetween static and per-request parts; { type: 'custom'; prompt: string | string[]; snapshot?: boolean };{ type: 'preset'; preset: 'claude_code'; append?: string; excludeDynamicSections?: boolean; snapshot?: boolean }.
The custom object form and snapshot need SDK v0.3.257. Details in modifying system prompts. Note that outputStyle is not an option: set it inside settings.
Limits
| Option | Type | Meaning |
|---|---|---|
maxTurns | number | Cap on tool-use round trips |
maxBudgetUsd | number | Stop when this call's estimated spend reaches the value; restored totals excluded |
Sessions
| Option | Type | Default | Meaning |
|---|---|---|---|
continue | boolean | false | Continue the latest conversation |
resume | string | none | Session ID to resume |
forkSession | boolean | false | Branch to a new ID when resuming |
resumeSessionAt | string | none | Resume at a specific message UUID |
resumeDropsTurn | string | none | With resumeSessionAt: the prompt UUID whose turn is being discarded. Resume is refused if the discarded range holds anything else (Claude Code v2.1.223+) |
sessionId | string | generated | Use this UUID |
title | string | none | Display title. A resumed session's stored title wins |
persistSession | boolean | true | false writes nothing to disk; cannot be resumed |
sessionStore | SessionStore | none | Mirror transcripts to external storage |
sessionStoreFlush | 'batched' | 'eager' | 'batched' | Alpha |
loadTimeoutMs | number | 60000 | Alpha. Timeout per load() / listSubkeys() while resuming from a store |
enableFileCheckpointing | boolean | false | File checkpointing |
Process and environment
| Option | Type | Default | Meaning |
|---|---|---|---|
cwd | string | process.cwd() | Working directory |
additionalDirectories | string[] | [] | Extra directories, passed as --add-dir (their skills, commands and agents load with the project source) |
projectConfigRoot | string | none | Absolute path of the trusted checkout that cwd is a worktree of. Project settings, .mcp.json and .claude/ content load from here, CLAUDE_PROJECT_DIR points here, and hooks, helpers and stdio MCP servers start here. CLAUDE.md and .claude/rules/ still load from cwd (Claude Code v2.1.275+) |
env | Record<string, string | undefined> | process.env | Replaces the environment. Spread process.env. CLAUDE_AGENT_SDK_CLIENT_APP names your app in the User-Agent |
executable | 'bun' | 'deno' | 'node' | detected | JS runtime |
executableArgs | string[] | [] | |
extraArgs | Record<string, string | null> | {} | Extra CLI flags |
pathToClaudeCodeExecutable | string | bundled binary | Only when the bundled one is missing or unsupported |
spawnClaudeCodeProcess | (o: SpawnOptions) => SpawnedProcess | none | Run Claude Code in a VM, container or remote host |
abortController | AbortController | new one | Cancel the run |
stderr | (data: string) => void | none | CLI stderr |
debug | boolean | false | Debug mode |
debugFile | string | none | Debug log path (implies debug) |
Configuration sources and extensions
| Option | Type | Default | Meaning |
|---|---|---|---|
settingSources | SettingSource[] | all | [] skips user, project and local |
settings | string | Settings | none | Inline object, file path or JSON string; fills the flag-settings layer. Change later with applyFlagSettings() |
managedSettings | Settings | none | Policy-tier settings from your host. Ignored on machines with admin-deployed managed settings unless the top managed source sets parentSettingsBehavior: 'merge', and never merged while a policyHelper supplies settings. Merged values pass a restrictive-only filter. With CLAUDE_CODE_PROVIDER_MANAGED_BY_HOST, three keys are read directly from it: model configuration (v2.1.222+), modelPricing when no managed source sets it (v2.1.246+), and the ENABLE_TOOL_SEARCH env entry (v2.1.247+) |
mcpServers | Record<string, McpServerConfig> | {} | |
strictMcpConfig | boolean | false | Ignore .mcp.json, user settings, plugin servers and claude.ai connectors |
plugins | SdkPluginConfig[] | [] | Plugins |
skills | string[] | 'all' | none | Skills Claude may invoke. Adds Skill to allowedTools; include 'Skill' if you pass tools. Bad names throw (SDK v0.3.221+) |
hooks | Partial<Record<HookEvent, HookCallbackMatcher[]>> | {} | |
onElicitation | (req, { signal }) => Promise<ElicitationResult> | none | Handles MCP elicitation when no hook does; unhandled requests are declined |
Stream shape and extras
| Option | Type | Default | Meaning |
|---|---|---|---|
includePartialMessages | boolean | false | Yield stream_event messages |
includeHookEvents | boolean | false | Yield hook started, progress and response messages. SessionStart and Setup always do. Notification, SessionEnd, PreCompact and PostCompact never produce a started message; they still produce progress while a command hook running over a second prints output, and a response only for background hooks |
forwardSubagentText | boolean | false | Forward foreground subagents' text and thinking as messages with parent_tool_use_id set |
agentProgressSummaries | boolean | false | One-line subagent summaries on task_progress events |
promptSuggestions | boolean | false | Emit prompt_suggestion messages after turns |
verbatimPrompts | boolean | false | Set client_composed: true on every user message (SDK v0.3.280+, Claude Code v2.1.248+) |
Slow or stalled APIs
Pass these in env (remember to spread process.env):
| Variable | Default | Effect |
|---|---|---|
API_TIMEOUT_MS | 600000 | Per-request timeout for main loop and subagents |
CLAUDE_CODE_MAX_RETRIES | 10, max 15 | Each retry gets a full timeout window. CLAUDE_CODE_RETRY_WATCHDOG=1 retries capacity errors indefinitely and from v2.1.199 raises other transient retries to 300 and removes the cap |
CLAUDE_ASYNC_AGENT_STALL_TIMEOUT_MS | stream idle timeout + 5 min (or 600000 with the watchdog off) | Aborts a silent subagent; background ones are marked failed with partial results |
CLAUDE_ENABLE_STREAM_WATCHDOG | on | 0 disables the body-stall watchdog |
CLAUDE_STREAM_IDLE_TIMEOUT_MS | 300000 minimum | Watchdog idle limit |
With includePartialMessages, a gateway that keeps a response alive with pings yields ping stream events; treat them as liveness.
The Query object
query() returns this. It is an AsyncGenerator<SDKMessage, void> with control methods. Several only work in streaming input mode.
| Method | Returns | Purpose |
|---|---|---|
interrupt() | SDKControlInterruptResponse | undefined | Stop the current turn (streaming only). Returns a receipt when the CLI advertises interrupt_receipt_v1 (v2.1.205+) |
rewindFiles(userMessageId, { dryRun? }) | RewindFilesResult | Restore files; needs enableFileCheckpointing |
setPermissionMode(mode) | void | Streaming only |
setModel(model?) | void | Streaming only; undefined or "default" resets |
setMaxThinkingTokens(n | null) | void | Deprecated; null resets to the session default |
applyFlagSettings(settings) | void | Change settings mid-session (streaming only) |
updateSettings(source, settings) | void | Persist one allowlisted key to disk (SDK v0.3.257+) |
initializationResult() | SDKControlInitializeResponse | Cached init data |
reinitialize() | SDKControlInitializeResponse | Re-send initialize after a transport gap so pending permission requests reach canUseTool again. Make the callback idempotent per request ID (v2.1.195+) |
supportedCommands() | SlashCommand[] | Tracks mid-session changes from SDK v0.3.216 |
supportedModels() | ModelInfo[] | |
supportedAgents() | AgentInfo[] | Captured at init |
mcpServerStatus() | McpServerStatus[] | |
getContextUsage({ detail? }) | SDKControlGetContextUsageResponse | /context data; detail needs SDK v0.3.257 |
readFile(path, { maxBytes?, encoding? }) | SDKControlReadFileResponse | null | Default cap 1 MB, ceiling 10 MB; 'base64' for binaries (SDK v0.2.121+) |
reloadPlugins({ holdOnCacheImpact? }) | SDKControlReloadPluginsResponse | SDK v0.2.85+; option v0.3.268+ |
reloadSkills() | SDKControlReloadSkillsResponse | SDK v0.3.163+ |
reloadOutputStyles() | SDKControlReloadOutputStylesResponse | SDK v0.3.261+ |
accountInfo() | AccountInfo | |
reconnectMcpServer(name) | void | Prefers servers you configured via mcpServers or setMcpServers() over settings-file entries of the same name (v2.1.257+) |
toggleMcpServer(name, enabled) | void | Disable removes the server's tools |
setMcpServers(servers) | McpSetServersResult | Replace SDK-managed servers |
readMcpResource(server, uri) | SDKControlMcpReadResourceResponse | Alpha. MCP Apps ui:// resources (SDK v0.3.280+) |
streamInput(stream) | void | Add turns |
stopTask(taskId) | void | Stop a background task |
close() | void | Kill the process and clean up |
applyFlagSettings()
Changes settings on a running session (TypeScript only; streaming input only). It writes to the flag-settings layer over whatever settings set at start-up.
| When it applies | Keys |
|---|---|
| Next turn | effortLevel, ultracode, permissions, hooks, skillOverrides, fastMode, agent (switching agent also applies its model and hooks; its system prompt applies next turn, or after compaction in sessions that reuse a recorded prompt) |
| Current turn | model: the in-flight response finishes on the old model, then the rest of the turn uses the new one. Subagents keep their own model. (Before v2.1.212, waited for the next turn.) |
| Never mid-session | System prompt options; resolved once at start |
Rules:
- Successive calls shallow-merge top-level keys, so a second
permissionsobject replaces the first entirely. - Pass
nullto clear a key; it falls back to thesettingsoption and then lower sources.undefineddoes nothing (JSON drops it). - Clearing
modelresets to Claude Code's default even if a settings file sets one. effortLevel: nullreturns to the model's default effort;agent: nullruns with no agent from next turn (and drops any model the agent applied);ultracode: nullturns it off without changing effort.effortLevelalso accepts"ultracode"at runtime (v2.1.203+), but the type does not declare it, so in TypeScript pass{ ultracode: true, effortLevel: "xhigh" }instead. Before v2.1.284,ultracodealone also setxhigh.
const q = query({ prompt: userTurns() });
await q.applyFlagSettings({ permissions: { deny: ["WebFetch", "Bash(curl *)"] } }); // after reading untrusted input
await q.applyFlagSettings({ model: "claude-opus-4-6" });
await q.applyFlagSettings({ model: null }); // back to the default
updateSettings()
Persists exactly one allowlisted string key:
"localSettings"acceptsoutputStyle, merged into.claude/settings.local.json; applies on the next request."userSettings"acceptseffortLevel, saved as the default for the current model undermodelSettingsin your user settings.maxwrites nothing (session-only). The running session's effort is unchanged; useapplyFlagSettings()for that. Needs SDK v0.3.277.
It rejects other keys, remote transports, and sources excluded by settingSources. Deleting keys is not supported.
toggleMcpServer() version notes
Disabling a stdio, SSE or HTTP server added with setMcpServers() removes its tools from Claude Code v2.1.285. Disabling an in-process server (from createSdkMcpServer()) disconnects it from v2.1.286, and its running calls fail immediately with error results.
WarmQuery
Returned by startup(). Implements AsyncDisposable, so await using works.
| Method | Purpose |
|---|---|
query(prompt) | Send the first prompt to the ready process. Once only |
close() | Discard without sending |
SpareProcess
Alpha. Returned by prewarm(). Implements AsyncDisposable.
| Member | Purpose |
|---|---|
claim({ prompt, options }) | Bind to a session in options.cwd (required) and send the first message. Returns a Query synchronously. Once only |
claimed | Resolves with { cwd, sessionId, parkedMs?, sdkMcpSettled }. Rejects if refused, if the process died or was closed, or with a message starting option_not_applied when model or maxThinkingTokens was not honoured |
exited | Settles when the process exits. Replace a spare that exits unclaimed |
close() | Kill it; before a claim this rejects claimed |
A claim can set additionalDirectories, model, permissionMode, maxThinkingTokens, a flag-settings overlay in settings, appendSystemPrompt, title, agents and per-session tokens in env. Claims are refused for a folder that does not exist or whose project settings set env, agent or model; the prompt then gets an error result starting not_claimed and the query throws. On any rejection other than option_not_applied, the prompt did not run.
Control responses
SDKControlInitializeResponse
From initializationResult() and reinitialize():
| Field | Meaning |
|---|---|
commands | SlashCommand[] |
agents | AgentInfo[] |
output_style, available_output_styles | Current and available styles |
models | ModelInfo[] |
account | AccountInfo |
fast_mode_state | off, cooldown or on; always reported from v2.1.219 |
fast_mode_disabled_reason | Why fast mode is blocked (codes under result messages) |
hooks_applied | Whether the request's hooks were registered (SDK v0.3.238+). true on first init and on repeats over stdin (new hooks replace old); false on repeats to a remote session; absent if no hooks were sent |
sdk_mcp_manifests_parked | Internal to in-process SDK MCP servers; you do not set or read it |
The response wrapper (not the payload) also carries pending_permission_requests: complete control_request messages this process issued and has not resolved. The SDK re-dispatches them to canUseTool, so handle repeated request IDs idempotently. Always present from v2.1.268.
SDKControlInterruptResponse
type SDKControlInterruptResponse = { still_queued: string[]; cancelled?: string[] };
still_queued lists UUIDs of user messages pending when the interrupt arrived (including ones already taken for the next turn). After the first turn has started, they will still be processed, possibly merged into one turn, so do not resend them. If you interrupt before the first turn starts, that turn aborts and its messages get no reply.
Caveats: only messages with a UUID appear; only main-thread messages; and you may see UUIDs you never sent (scheduled task triggers, for example), which you should ignore.
Clients driving the control protocol directly can send cancel_queued: true (advertised as interrupt_cancel_queued_v1, v2.1.219+) to cancel those messages; they then appear under cancelled. interrupt() itself never sends it. The receipt is a snapshot taken when the interrupt is processed and arrives before the interrupted turn's result, so read it rather than inspecting the queue afterwards.
SDKControlGetContextUsageResponse
From getContextUsage(). The default detail: 'full' counts each category with token-counting requests (not in the stream, not billed on the Anthropic API). detail: 'summary' uses the last response and local estimates instead: no extra requests, approximate numbers.
| Field | Meaning |
|---|---|
categories | { name, tokens, color, isDeferred?, kind }, where kind is used, free, buffer or deferred (SDK v0.3.268+). Classify by kind, not name |
totalTokens, maxTokens, rawMaxTokens, percentage | Usage against the model window or the lower auto-compact window; rawMaxTokens equals maxTokens |
gridRows | Display grid cells (color, isFilled, categoryName, tokens, percentage, squareFullness) |
model | Model |
memoryFiles | { path, type, tokens } |
mcpTools | { name, serverName, tokens, isLoaded? } |
agents | { agentType, source, tokens } |
slashCommands? | { totalCommands, includedCommands, tokens } |
skills? | { totalSkills, includedSkills, tokens, skillFrontmatter: { name, source, tokens }[] }; per-skill counts measure the listing entry actually sent |
autoCompactThreshold?, isAutoCompactEnabled | Auto-compact |
messageBreakdown? | Tool call, tool result, attachment, assistant, user, redirected and unattributed tokens, plus toolCallsByType and attachmentsByType |
apiUsage | Latest response's usage, or null |
deferredBuiltinTools, systemTools and systemPromptSections are declared but left unset. Sending /context as a prompt instead attaches an SDKContextUsage to the reply (SDK v0.3.232+).
Other control responses
| Type | Shape and notes |
|---|---|
SDKControlReadFileResponse | { contents, absPath, truncated?, encoding?: 'base64' }. readFile() only serves regular files inside the session's working directories plus a few of Claude Code's own session files; Read deny and ask rules still apply, and a broad Read allow does not widen it. Anything else returns null |
SDKControlReloadPluginsResponse | commands, agents, plugins (name, path, optional source, version from the manifest, so validate it), mcpServers, error_count, plus held and cache_impact when you pass holdOnCacheImpact. held: true means not applied (call again without the option to force); false means applied with no cache impact. cache_impact lists mcp_servers_added, mcp_servers_removed (as plugin:<plugin>:<server>) and lsp_tool_change (adds, may-add, removes, may-remove or null). Executables older than v2.1.268 ignore the option. Read agents here after a reload because supportedAgents() keeps the init list |
SDKControlReloadSkillsResponse | { skills: SlashCommand[] } |
SDKControlReloadOutputStylesResponse | { available_output_styles: string[] } |
SDKControlMcpReadResourceResponse | { contents: { uri, mimeType?, text?, blob?, _meta? }[] }. Only ui:// URIs on a connected, non-SDK server; available when init capabilities include mcp_read_resource_v1. _meta keys under com.anthropic/ are stripped. Treat the HTML as untrusted and render it sandboxed |
Configuration types
AgentDefinition
type AgentDefinition = {
description: string;
prompt: string;
tools?: string[];
disallowedTools?: string[];
model?: string;
mcpServers?: AgentMcpServerSpec[];
skills?: string[];
initialPrompt?: string;
maxTurns?: number;
background?: boolean;
omitClaudeMd?: boolean;
memory?: "user" | "project" | "local";
effort?: "low" | "medium" | "high" | "xhigh" | "max" | number;
permissionMode?: PermissionMode;
criticalSystemReminder_EXPERIMENTAL?: string;
};
| Field | Meaning |
|---|---|
description, prompt | Required: when to use it, and its system prompt |
tools | Allowlist; omit to inherit. Use skills, not 'Skill' here, to preload skills |
disallowedTools | Also accepts mcp__server, mcp__server__*, mcp__* |
model | fable, opus, sonnet, haiku, inherit or a full ID |
mcpServers | Names from the parent's config or inline records |
skills | Preloaded skills |
initialPrompt | First turn when used as the main-thread agent |
maxTurns, background, effort, memory | As named |
omitClaudeMd | Skip user, project and local CLAUDE.md as a subagent (SDK v0.3.271+) |
permissionMode | Subject to inheritance rules |
criticalSystemReminder_EXPERIMENTAL | Experimental reminder added to the system prompt |
AgentMcpServerSpec = string | Record<string, McpServerConfigForProcessTransport>, where the latter is any of stdio, SSE, HTTP or SDK config. See subagents.
SettingSource and precedence
| Value | File |
|---|---|
'user' | ~/.claude/settings.json |
'project' | .claude/settings.json |
'local' | .claude/settings.local.json |
Omitting settingSources loads all three, like the CLI. Include 'project' to load CLAUDE.md. Precedence, highest first: local, project, user; programmatic options (agents, allowedTools, settings) beat all three; managed policy beats programmatic options. Claude Code features in the SDK lists inputs that load regardless.
PermissionMode
"default" | "acceptEdits" | "bypassPermissions" | "plan" | "dontAsk" | "auto". Behaviour is described in permissions.
CanUseTool
type CanUseTool = (
toolName: string,
input: Record<string, unknown>,
options: {
signal: AbortSignal;
suggestions?: PermissionUpdate[];
blockedPath?: string;
mcpServer?: { name: string; source: string };
decisionReason?: string;
defaultToNo?: boolean;
suppressAlwaysAllowRule?: boolean;
toolUseID: string;
agentID?: string;
requestId: string;
}
) => Promise<PermissionResult | null>;
| Option | Meaning |
|---|---|
signal | Aborts if the request is cancelled |
suggestions | Permission updates that would stop future prompts. Bash suggestions target localSettings, so returning one in updatedPermissions persists it to .claude/settings.local.json |
blockedPath | Path that triggered the prompt |
mcpServer | For mcp__* tools, the server and where it was defined (SDK v0.3.274+) |
decisionReason | Why the prompt happened |
defaultToNo | Focus your UI on decline, no pre-selected approve, no one-key approve (SDK v0.3.268+) |
suppressAlwaysAllowRule | Do not offer "always allow" (SDK v0.3.268+) |
toolUseID | The call |
agentID | Subagent ID if inside one |
requestId | The control_request ID; echo it if you answer out of band |
Return a PermissionResult normally. Return null only if your app already sent the control_response for this requestId through its own channel; otherwise the call blocks forever, since prompts never time out. requestId and null need Claude Code v2.1.199.
type PermissionResult =
| { behavior: "allow"; updatedInput?: Record<string, unknown>; updatedPermissions?: PermissionUpdate[]; toolUseID?: string }
| { behavior: "deny"; message: string; interrupt?: boolean; toolUseID?: string };
ToolConfig
{ askUserQuestion?: { previewFormat?: "markdown" | "html" } }. Setting previewFormat opts in to the preview field on AskUserQuestion options; without it Claude sends no previews. See user input.
MCP server configs
| Type | Fields |
|---|---|
McpStdioServerConfig | type?: "stdio", command, args?, env? |
McpSSEServerConfig | type: "sse", url, headers? |
McpHttpServerConfig | type: "http", url, headers? |
McpSdkServerConfigWithInstance | type: "sdk", name, timeout?, instance |
McpClaudeAIProxyServerConfig | type: "claudeai-proxy", url, id (status reports only) |
McpServerConfig is the union of the first four.
SdkPluginConfig
{ type: "local"; path: string; skipMcpDiscovery?: boolean }. skipMcpDiscovery loads the plugin's skills, hooks, agents and commands but ignores its .mcp.json and manifest mcpServers, for when your app manages those connections.
Messages
The SDKMessage union
Everything the query yields is one of these. Switch on type, and for type: "system" on subtype.
type / subtype | TypeScript type | What it is |
|---|---|---|
assistant | SDKAssistantMessage | Claude's output |
user | SDKUserMessage, SDKUserMessageReplay | Your input, tool results, replays |
result | SDKResultMessage | End of a turn or call |
stream_event | SDKPartialAssistantMessage | Raw stream events (with includePartialMessages) |
system / init | SDKSystemMessage | Session start-up details |
system / compact_boundary | SDKCompactBoundaryMessage | Compaction happened |
system / status | SDKStatusMessage | Status changes such as compacting |
system / informational | SDKInformationalMessage | Notices, warnings, hook feedback |
system / hook_started, hook_progress, hook_response | Hook lifecycle messages | |
system / plugin_install | SDKPluginInstallMessage | Marketplace install progress |
system / permission_denied | SDKPermissionDeniedMessage | A denial decided without a prompt |
system / task_started, task_progress, task_updated, task_notification | Background task messages | |
system / background_tasks_changed | SDKBackgroundTasksChangedMessage | Full live task set |
system / thinking_tokens | SDKThinkingTokensMessage | Thinking progress estimates |
system / session_state_changed | SDKSessionStateChangedMessage | Running, idle or needs action |
system / files_persisted | SDKFilesPersistedEvent | Checkpoints saved |
system / commands_changed | SDKCommandsChangedMessage | Command list changed |
system / worker_shutting_down | SDKWorkerShuttingDownMessage | Graceful worker exit |
system / local_command_output | SDKLocalCommandOutputMessage | Declared but not emitted |
tool_progress | SDKToolProgressMessage | Long-running tool heartbeat |
tool_use_summary | SDKToolUseSummaryMessage | Summary of tool use |
auth_status | SDKAuthStatusMessage | Authentication flow |
rate_limit_event | SDKRateLimitEvent | Rate limit state |
prompt_suggestion | SDKPromptSuggestionMessage | Predicted next prompt |
conversation_reset | SDKConversationResetMessage | /clear and friends |
The union also includes SDKNotificationMessage, SDKMemoryRecallMessage, SDKElicitationCompleteMessage, SDKAPIRetryMessage and SDKMirrorErrorMessage (the mirror_error system message emitted when a session store batch cannot be delivered).
SDKAssistantMessage
| Field | Meaning |
|---|---|
uuid, session_id | Identifiers |
message | A BetaMessage from the Anthropic SDK: id, content, model, stop_reason, usage |
parent_tool_use_id | Set inside subagents |
error | SDKAssistantMessageError when the response failed |
aborted | true when an interrupt cut the message short: no stop_reason, content may stop mid-word (SDK v0.3.214+) |
timestamp | ISO 8601 time the content finished, from the producing machine's clock. Display only; do not sort by it |
context_usage | SDKContextUsage on the reply to a /context prompt (SDK v0.3.232+) |
user_message_uuid, user_message_uuids, resume_reason | Reply matching, described below |
SDKAssistantMessageError values: authentication_failed, oauth_org_not_allowed, account_on_hold, billing_error, rate_limit (429 against your quota), overloaded (529, server at capacity), invalid_request, model_not_found (model missing or not available to you), server_error, max_output_tokens, cloud_credential_error (no usable AWS or Google Cloud credentials on the machine; usually an expired sign-in; SDK v0.3.267+) and unknown.
Per-message usage.output_tokens is a placeholder from message_start; read real output counts from the result. One API response can produce several assistant messages sharing a message.id.
SDKUserMessage
type SDKUserMessage = {
type: "user";
uuid?: UUID;
session_id?: string;
message: MessageParam;
pasted_content?: MessageParam["content"][];
parent_tool_use_id: string | null;
isSynthetic?: boolean;
shouldQuery?: boolean;
client_composed?: true;
tool_use_result?: unknown;
priority?: "now" | "next" | "later";
origin?: SDKMessageOrigin;
inline_pastes?: string[];
};
Fields you set when sending:
| Field | Effect |
|---|---|
uuid | Set one if you want replies matched back to this message |
origin | Set { kind: "human" } for text the user typed. Without it, the message is unattributed and human-only checks (such as the ultracode workflow keyword) reject it. (Before v2.1.210, absent meant human.) |
shouldQuery: false | Append to the transcript without starting a turn; merged into the next message that does |
client_composed: true | Deliver text as written: no @path or @server:resource expansion, no /command dispatch (SDK v0.3.280+, Claude Code v2.1.248+) |
pasted_content | Content pasted into your UI, one entry per paste (string or blocks); appended after typed text, possibly wrapped in <pasted_content> tags. Non-text blocks ignored (SDK v0.3.277+) |
inline_pastes | Substrings of message.content that were pasted, so Claude Code can tag them where they sit; only in the last text block (SDK v0.3.280+) |
priority | When a message sent mid-turn is read (below) |
isSynthetic | Marks a message as not typed by the user |
priority:
'next'or absent: read in the same turn once current tool calls finish; starts the next turn if the turn ends first.'later': held until the turn ends, then sent as a new turn.'now'withorigin: { kind: "human" }: from v2.1.286, movable work (shell commands, subagents, MCP calls; WebFetch and WebSearch from v2.1.287) moves to the background and Claude reads the message in the same turn. Otherwise the turn is interrupted.'now'without that origin: interrupt, then read.
const redirect: SDKUserMessage = {
type: "user",
message: { role: "user", content: "Stop the migration and just report what is done" },
parent_tool_use_id: null,
priority: "now",
origin: { kind: "human" }
};
Reading tool_use_result
On a user message carrying a tool_result, tool_use_result is the tool's structured output (see tool output types), typed unknown. Special cases:
- Agent: an
AgentOutput. Render from it, not from the text. Acompletedresult'scontentholds the report, or a short note if the subagent reported through aSubagentHandbackcall. In auto mode from v2.1.271 every non-fork subagent reports that way, and Claude receives the report as a separate message. - Detached WebFetch or WebSearch: when moved to the background to deliver a
'now'message,tool_use_resultis{ detachedToolCall: true }and no second result follows for thattool_use_id. Mark the row as backgrounded (v2.1.287+). - MCP
resource_linkblocks:{ resourceLinks: SDKMcpResourceLink[] }. Omitted when none and on subagent results; at most 50 links or 64 KiB (SDK v0.3.257+). - MCP
structuredContent:tool_use_result.structuredContentholds it and.contentholds theMcpOutput. Not on subagent results. - Oversized
structuredContent: above 1,048,576 characters of JSON it is dropped andstructuredContentOmitted: trueis set instead; in-process SDK servers and tools with an MCP Apps_meta.uiresource are exempt (v2.1.287+).
SDKUserMessageReplay
Like SDKUserMessage but with a required uuid and session_id, and isReplay: true. Turns injected from outside (origin peer or channel) always appear as replays, whether they arrived mid-turn or started a turn while idle (from v2.1.207).
SDKResultMessage
Two arms: subtype: "success" and the error subtypes error_max_turns, error_during_execution, error_max_budget_usd, error_max_structured_output_retries.
Fields on both arms:
| Field | Meaning |
|---|---|
uuid, session_id | Identifiers |
duration_ms, duration_api_ms | Wall and API time |
is_error | True on error arms; on success, true if the last request failed |
num_turns | Turns |
stop_reason | API stop reason, or tool_deferred |
total_cost_usd | Estimate covering the same calls as modelUsage |
usage | NonNullableUsage, main loop only, per turn in streaming mode |
modelUsage | Per-model totals: main loop, subagents, compaction, Workflow agents. Excludes helpers like the permission classifier and token counting. Includes restored totals on resume; cumulative in streaming mode |
permission_denials | SDKPermissionDenial[] (tool_name, tool_use_id, tool_input); the authoritative denial record |
queued_turn_count | Human-origin messages still queued (SDK v0.3.242+). 0 does not exclude a further turn; absent after a crash |
terminal_reason | Why the loop ended (below) |
result_index | Position in delivery order from 0; a gap means a lost result (SDK v0.3.268+) |
fast_mode_state, fast_mode_disabled_reason | Fast mode status |
origin | Origin of the triggering message |
user_message_uuid, user_message_uuids, resume_reason | Reply matching |
Success only: result (final text), api_error_status, ttft_ms (to first complete assistant message), ttft_stream_ms (to message_start), local_command (the command a turn ran without entering the loop, lowercased with underscores, e.g. reload_plugins; mcp for MCP commands and /mcp; custom for your own; SDK v0.3.268+), request_sent_wall_ms (epoch ms the API request went out, alongside user_message_uuid), first_content_frame_ms (to the first content block event, thinking included; SDK v0.3.260+), structured_output, deferred_tool_use ({ id, name, input } when a hook returned defer), and the first_stream_post_* / first_text_post_* upload timings that only claude.ai-streamed sessions record.
Error only: errors (strings) and startup_failure_reason.
terminal_reason values: completed, max_turns, tool_deferred, aborted_streaming, aborted_tools, hook_stopped, stop_hook_prevented, background_requested, blocking_limit, rapid_refill_breaker, prompt_too_long, image_error, model_error, api_error, malformed_tool_use_exhausted, budget_exhausted, structured_output_retry_exhausted, tool_deferred_unavailable, turn_setup_failed.
fast_mode_disabled_reason codes (v2.1.219+; also on the init message and initialise response):
| Code | Meaning |
|---|---|
free | No paid subscription or usage credits |
preference | Organisation disabled fast mode |
extra_usage_disabled | Usage credits are off |
network_error | Availability check could not reach api.anthropic.com |
unknown | Could not determine |
not_first_party | Not on the Anthropic API |
disabled_by_env | CLAUDE_CODE_DISABLE_FAST_MODE is set |
model_not_allowed | Fast-mode Opus model not in availableModels |
sdk_opt_in_required | Pass fastMode: true in settings or via applyFlagSettings() |
pending | Check still running |
During a post-rate-limit cooldown you get fast_mode_state: "cooldown" with no reason.
Origins on results. Injected follow-ups (finished background tasks, routine triggers, verified messages from your other sessions) carry origin.kind === "task-notification". Declared scheduled runs do too, so do not suppress on kind alone. When several task completions are answered in one turn, each still gets a result, with all but the last empty and num_turns: 0. Results before any user turn (start-up errors) have no origin.
startup_failure_reason
On the error_during_execution result written before exit on a known start-up failure (SDK v0.3.274+), with zeroed totals and errors matching stderr. By default only worktree resume failures and a refused continue of a background-held session produce this result; set CLAUDE_CODE_STARTUP_FAILURE_RESULTS=1 to get it for every reason.
| Value | Cause |
|---|---|
org_pin_api_key_conflict | Managed settings require a first-party or Cloud gateway sign-in but an API key, auth token or apiKeyHelper is configured |
provider_not_allowed | Provider or endpoint not in the managed allowlist (v2.1.285+) |
org_verify_failed | Could not verify the organisation against the pin |
org_pin_mismatch | Signed in to a disallowed organisation |
managed_settings_invalid | Managed settings unreadable, pin names no org, or model restrictions leave no Default model |
remote_settings_required_unavailable | Required managed settings could not load |
gateway_signin_required | Cloud gateway ended the sign-in |
gateway_access_denied | Cloud gateway returned 403 for managed settings |
proxy_invalid | Proxy setting is not a full URL |
temp_dir_unusable | Per-user temp directory unsafe or not creatable |
cwd_unavailable | Working directory gone or unreadable |
shell_tool_missing | Windows with no Git Bash and no usable PowerShell |
session_held_by_background | Conversation is running as a background session |
worktree_resume_refused | Worktree failed safety checks, or resume launched from inside it |
worktree_unverified | Worktree could not be verified now; retry may work |
cli_version_too_old | Below Anthropic's minimum version |
bypass_root | Bypass mode requested as root |
Matching replies to your messages
Set a uuid on each SDKUserMessage and Claude Code echoes it back.
user_message_uuidnames the message a turn is answering. If several messages were merged into one turn, it names the last;user_message_uuids(SDK v0.3.259+, up to 64 entries, always includinguser_message_uuid) lists all of them, plus any regular message picked up mid-turn.- For a synthetic message (
isSynthetic: true), the turn answers it until a regular message is picked up between tool calls (echo needs SDK v0.3.265). - For a turn re-run under
CLAUDE_CODE_RESUME_INTERRUPTED_TURN, the re-run answers the interrupted turn's last regular message (SDK v0.3.268) and carriesresume_reason(a token such asinterrupted_turn) on its result and on reply frames that carryuser_message_uuid. - Other self-generated turns answer nothing of yours until they pick up a message.
Where the echo appears: every result of a turn that answered one of your messages (complete from SDK v0.3.265); the turn's first assistant message and, with partial messages on, the first non-ping stream event (SDK v0.3.246+), again whenever the answered message changes; and every thinking_tokens frame (SDK v0.3.260+). It never appears on other reply frames, subagent frames, turns that answer nothing of yours, messages you sent without a uuid, or crash results.
SDKSystemMessage (init)
| Field | Meaning |
|---|---|
agents | Agent names |
apiKeySource | ApiKeySource |
betas | Active betas |
claude_code_version | Version |
cwd, model, permissionMode, output_style | Session state |
tools | Tool names (the Agent tool is still listed as Task) |
mcp_servers | { name, status, source? } (source SDK v0.3.274+) |
slash_commands | Usable commands |
terminal_slash_commands | Those bound to a local terminal, such as exit, for remote clients to hide (SDK v0.3.229+) |
skills | User-invocable skills |
plugins | { name, path } |
plugin_errors | { plugin, type, message, path? } (declared SDK v0.3.283+). plugin is a tag such as inline[0] when the directory itself failed; path is then its absolute path. type is an open set such as path-not-found or manifest-validation-error |
fast_mode_state, fast_mode_disabled_reason | Fast mode |
effort | Only sent to Remote Control clients |
capabilities | Feature flags (v2.1.205+) |
capabilities values: interrupt_receipt_v1, interrupt_cancel_queued_v1 (v2.1.219+), sdk_mcp_manifests and sdk_mcp_tools_list_changed (v2.1.286+; the latter means a tools/list_changed from an SDK MCP server re-lists its tools), plus mcp_read_resource_v1 and mcp_tool_ui_meta_v1 for MCP Apps. Treat it as an open set and feature-detect.
Other common messages
| Type | Fields and notes |
|---|---|
SDKPartialAssistantMessage | type: "stream_event", event (raw BetaRawMessageStreamEvent), parent_tool_use_id (always null: main session only), ttft_ms on message_start, plus reply-matching fields on the first non-ping event |
SDKCompactBoundaryMessage | compact_metadata: { trigger: "manual" | "auto"; pre_tokens } |
SDKStatusMessage | status: "compacting" | null, permissionMode? |
SDKInformationalMessage | content, level (info, notice, suggestion, warning), tool_use_id?, prevent_continuation?. Render as plain text. From v2.1.227 hook systemMessage can arrive here, prefixed like PostToolUse:Bash says: |
SDKWorkerShuttingDownMessage | reason such as host_exit; act on it only when live, as resumed sessions replay it |
SDKPluginInstallMessage | With CLAUDE_CODE_SYNC_PLUGIN_INSTALL: status (started, installed, failed, completed), name?, error? |
SDKToolUseSummaryMessage | summary, preceding_tool_use_ids |
SDKAuthStatusMessage | isAuthenticating, output, error? |
SDKFilesPersistedEvent | files: { filename, file_id }[], failed, processed_at |
SDKPromptSuggestionMessage | suggestion |
SDKLocalCommandOutputMessage | Never emitted; command output such as /usage arrives as an assistant message |
SDKPermissionDeniedMessage
tool_name, tool_use_id, agent_id?, decision_reason_type? (rule, mode, classifier, asyncAgent...), decision_reason?, message (what the model was told).
What it reports depends on configuration:
| Setup | Reported denials |
|---|---|
canUseTool with permissionPrompts: 'host' | Only those Claude Code decides itself |
| No callback and no prompt tool | Those plus every call that would have prompted (unless a PermissionRequest hook allowed it). From v2.1.223 |
MCP prompt tool with 'host' | None at all |
permissionPrompts: 'none' | Self-decided plus would-have-prompted (v2.1.259+) |
Denials on the PreToolUse hook path are never reported here, and emission is best-effort. Use the result's permission_denials as the record.
SDKContextUsage
Attached to the reply to a /context prompt. Snake_case, without display fields.
| Field | Meaning |
|---|---|
model | Main-loop model |
total_tokens | Estimate in use; can exceed the window |
raw_max_tokens | Model window or the lower auto-compact window (for example the 200K boundary some 1M models get) |
percentage | Can exceed 100 |
over_limit? | { tokens_over, kind }; kind is hard_limit (believed model limit) or compaction_window (policy window) |
categories | SDKContextUsageCategory[]: { name, tokens, kind } with kind used, free, buffer (compaction reserve) or deferred (held-out tool schemas) |
mcp_tools | { name, server_name, tokens } |
memory_files | { path, type, tokens }, type such as Project or User |
agents | Custom subagents only, { agent_type, source, tokens } |
skills? | { name, source, plugin_name?, tokens } |
The type grows additively; ignore unknown fields.
SDKMessageOrigin
Where a user-role message came from. Forwarded onto the result.
kind | Meaning |
|---|---|
human | Typed by the end user. Set it explicitly |
channel | Arrived on a channel; server names the MCP server |
peer | From a teammate or another of your sessions |
task-notification | Synthetic turn, such as a finished background task; optional subkind and fireReason |
coordinator | From an agent team coordinator |
auto-continuation | Session continued without user input |
unclassified | Claude Code could not classify a synthetic message (v2.1.223+). Do not set it yourself |
Task-notification subkinds (v2.1.213+), set when Anthropic servers verified the source or when you declare a scheduled run:
scheduled-trigger: a routine fired (schedule, API, GitHub or Run now), or your declared scheduled run. Framed to the model as its assigned task.peer-send-message: a verifiedsend_messagefrom another cloud session in the same private group (v2.1.224+). Not the cross-sessionSendMessagetool, whose messages arekind: "peer".
fireReason is a short token such as scheduled, manual, retry, catch_up or api (SDK v0.3.280+).
Declaring your own scheduled runs (SDK v0.3.280+): start the session with CLAUDE_CODE_HOST_SCHEDULED_RUN=1 in env, then send the run's message with origin: { kind: "task-notification", subkind: "scheduled-trigger", fireReason: "scheduled" } and without isSynthetic. Ignored without that variable, or if the environment has CLAUDECODE or CLAUDE_CODE_CHILD_SESSION. fireReason must be 1 to 32 lowercase letters or underscores.
Peer fields:
| Field | Meaning |
|---|---|
from | Teammate name or sender address; "unknown" for one-way cross-machine messages. Sender-authored, forgeable |
fromMode | bypass or prompting, declared by a relaying host (SDK v0.3.234+) |
name | Normalised display name, max 64 code points (v2.1.205+) |
fromSession | Sender's openable session ID, for navigation only (v2.1.216+) |
senderTaskId | Teammate's task ID |
body | Decoded body, exactly what the model sees (v2.1.205+) |
verifiedPeerPid | Kernel-verified PID of the socket peer (v2.1.216+). Use this, not from, for identity. Absent means unverified; for relays it identifies the relay; PIDs recycle |
Background task messages
A task is a backgrounded Bash command, a Monitor watch, a subagent or a remote agent.
| Message | Key fields |
|---|---|
SDKTaskStartedMessage | task_id, tool_use_id?, description, task_type (local_bash, local_agent, remote_agent), is_backgrounded?, spawn_depth? (agents only; 1 = spawned by main thread), ambient? |
SDKTaskProgressMessage | task_id, description, subagent_type?, usage (total_tokens, tool_uses, duration_ms), last_tool_name?, summary? |
SDKTaskUpdatedMessage | task_id, patch (status pending/running/completed/failed/killed, description, end_time in epoch ms, total_paused_ms, error, is_backgrounded) |
SDKTaskNotificationMessage | task_id, tool_use_id?, status (completed, failed, stopped), output_file, summary, ambient?, usage?, resource_links? |
SDKBackgroundTasksChangedMessage | tasks: full live set of { task_id, task_type, subagent_type?, description, ambient? } (v2.1.203+; subagent_type SDK v0.3.293+) |
Notes:
ambient: truemarks tasks that are not part of the session's work (Claude Code's own, and live-update watchers). Exclude them from activity indicators (SDK v0.3.247+).- Resumed subagents always report
is_backgrounded: true. A foreground task that later moves to the background reports it viatask_updated. summaryon progress is model-generated for subagents only withagentProgressSummaries; for backgrounded MCP calls it is the server's own progress.- Backgrounded MCP calls deliver their real result in the notification, with
resource_links(SDK v0.3.257+). Match bytool_use_id. - Every task notification sent to the model carries a "no human input" notice, except
scheduled-triggerdeliveries. Detect task-notification turns byorigin.kind, not the notice text. - For
background_tasks_changed, replace your cached set with each payload; nothing is sent at start-up, so reset to empty when the process (re)starts. A repeatedinitialize(such asreinitialize()) is followed by a snapshot from SDK v0.3.239.
Progress and state messages
| Message | Notes |
|---|---|
SDKToolProgressMessage | tool_use_id, tool_name, parent_tool_use_id, elapsed_time_seconds, task_id?, heartbeat?, subagent_type?, subagent_retry?. Main-conversation tools emit a heartbeat every 30 seconds (SDK v0.3.214+; foreground Agent calls from v2.1.257). subagent_retry (agent_id, attempt, max_retries, retry_delay_ms, error_status, error_category) appears per retry while a subagent waits out an API error. Track it by parent_tool_use_id, clear it on the next non-heartbeat progress without subagent_retry or on the tool result, and treat error_category (rate_limit, overloaded, authentication_failed, server_error, cloud_credential_error, unknown) as an open set |
SDKThinkingTokensMessage | estimated_tokens, estimated_tokens_delta, user_message_uuid?. For progress display; final counts come from the result (v2.1.153+) |
SDKSessionStateChangedMessage | With CLAUDE_CODE_EMIT_SESSION_STATE_EVENTS=1: state is running, idle or requires_action. May repeat; idle and result can arrive in either order. CLAUDE_CODE_BG_TASKS_REPORT_RUNNING controls whether idle waits for background work |
SDKCommandsChangedMessage | Full new commands list; also fires when MCP prompts join or leave (v2.1.281+) |
SDKHookStartedMessage | hook_id, hook_name, hook_event |
SDKHookProgressMessage | Adds stdout, stderr, output |
SDKHookResponseMessage | Adds exit_code? and outcome (success, error, cancelled) |
Hook lifecycle messages are delivered live, including during start-up hooks (they were batched from v2.1.169 to v2.1.203).
SDKRateLimitEvent
rate_limit_info: status (allowed, allowed_warning, rejected), resetsAt?, utilization?, and for exhausted claude.ai subscriptions errorCode: "credits_required" with canUserPurchaseCredits and hasChargeableSavedPaymentMethod (v2.1.181+).
SDKConversationResetMessage
new_conversation_id, uuid, session_id, and from v2.1.281 trigger? (clear, plan_mode_exit, fresh_session, onboarding), user_message_uuid? and timestamp?. In query() only /clear and its aliases produce it. Start an empty transcript under the new ID and drop any cached title, whatever the trigger. Declared in typings from v2.1.203.
AbortError
class AbortError extends Error {}. The only typed error class. Other failures (process exits, launch failures) reject iteration with untyped errors; see troubleshooting.
Hooks
Guide: hooks in the SDK.
type HookCallback = (input: HookInput, toolUseID: string | undefined, options: { signal: AbortSignal }) => Promise<HookJSONOutput>;
interface HookCallbackMatcher {
matcher?: string;
hooks: HookCallback[];
timeout?: number; // seconds, for every hook in this matcher
}
Base input
Every input has session_id, transcript_path, cwd, and optionally prompt_id (UUID of the prompt being processed, matching OpenTelemetry prompt.id; v2.1.196+), permission_mode, effort: { level }, agent_id and agent_type.
Inputs by event
hook_event_name | Extra fields |
|---|---|
PreToolUse | tool_name, tool_input, tool_use_id, mcp_server? |
PostToolUse | tool_name, tool_input, tool_response, tool_use_id, duration_ms?, mcp_server? |
PostToolUseFailure | tool_name, tool_input, tool_use_id, error, is_interrupt?, duration_ms?, mcp_server? |
PostToolBatch | tool_calls: { tool_name, tool_input, tool_use_id, tool_response? }[], where tool_response is the serialised result the model sees |
PermissionRequest | tool_name, tool_input, permission_suggestions?, mcp_server? |
PermissionDenied | tool_name, tool_input, tool_use_id, reason, mcp_server? |
Notification | message, title?, notification_type |
UserPromptSubmit | prompt, session_title? |
UserPromptExpansion | expansion_type (slash_command, mcp_prompt), command_name, command_args, command_source?, prompt |
SessionStart | source (startup, resume, clear, compact, fork), agent_type?, model?, session_title? |
SessionEnd | reason (an ExitReason) |
Stop | stop_hook_active, last_assistant_message?, background_tasks?, session_crons? |
StopFailure | error (SDKAssistantMessageError), error_details?, last_assistant_message? |
SubagentStart | agent_id, agent_type |
SubagentStop | stop_hook_active, agent_id, agent_transcript_path, agent_type, last_assistant_message?, background_tasks?, session_crons? |
PreCompact | trigger (manual, auto), custom_instructions |
PostCompact | trigger, compact_summary |
PreModelSwitch | from_model, to_model, requested_model, source (command, picker, sdk), context_tokens, prompt_cache_warm, cache_ttl (5m, 1h), estimated_cache_write_usd, pricing (configured, catalog, default) |
PostModelSwitch | Same, with source also auto or resume |
Setup | trigger (init, maintenance) |
TeammateIdle | teammate_name, team_name (deprecated) |
TaskCreated, TaskCompleted | task_id, task_subject, task_description?, teammate_name?, team_name? (deprecated) |
Elicitation | mcp_server_name, message, mode? (form, url), url?, elicitation_id?, requested_schema? |
ElicitationResult | mcp_server_name, elicitation_id?, mode?, action (accept, decline, cancel), content? |
ConfigChange | source (user_settings, project_settings, local_settings, policy_settings, skills), file_path? |
InstructionsLoaded | file_path, memory_type (User, Project, Local, Managed), load_reason (session_start, nested_traversal, path_glob_match, include, compact), globs?, trigger_file_path?, parent_file_path? |
DirectoryAdded | directory (absolute), source (slash_command for /add-dir, register_repo_root for the SDK control request) |
WorktreeCreate | name |
WorktreeRemove | worktree_path |
CwdChanged | old_cwd, new_cwd |
FileChanged | file_path, event (change, add, unlink) |
MessageDisplay | turn_id, message_id, index, final, delta |
BackgroundTaskSummary is { id, type, status, description, command?, agent_type?, server?, tool?, name? }; SessionCronSummary is { id, schedule, recurring, prompt }. mcp_server (an McpServerProvenance) needs SDK v0.3.274.
Outputs
HookJSONOutput = AsyncHookJSONOutput | SyncHookJSONOutput. The async form is { async: true; asyncTimeout?: number }.
Top-level sync fields: continue, suppressOutput, stopReason, decision (approve or block), systemMessage, reason, terminalSequence (an allowed OSC or BEL sequence for the interactive CLI; the SDK ignores it) and hookSpecificOutput.
hookEventName | hookSpecificOutput fields |
|---|---|
PreToolUse | permissionDecision (allow, deny, ask, defer), permissionDecisionReason, updatedInput, additionalContext |
PostToolUse | additionalContext, classifierContext (max 2,000 characters shared across hooks, sync only, never copy untrusted output into it), updatedToolOutput, updatedMCPToolOutput (deprecated) |
PostToolUseFailure, PostToolBatch, Stop, SubagentStop, SubagentStart, Notification, Setup, UserPromptExpansion | additionalContext |
UserPromptSubmit | additionalContext, sessionTitle, suppressOriginalPrompt (omit the prompt from a block message) |
SessionStart | additionalContext, initialUserMessage, sessionTitle, watchPaths, reloadSkills (rescan skills after the hook) |
PreModelSwitch | permissionDecision (allow, deny, ask; ask is a refusal everywhere except interactive /model), permissionDecisionReason |
PostModelSwitch | additionalContext, delivered with the new model's next request |
PermissionDenied | retry |
PermissionRequest | decision: { behavior: "allow", updatedInput?, updatedPermissions? } or { behavior: "deny", message?, interrupt? } |
Elicitation, ElicitationResult | action, content |
CwdChanged, FileChanged | watchPaths |
WorktreeCreate | worktreePath (required) |
MessageDisplay | displayContent to show instead of the delta |
Tool input types
Exported from @anthropic-ai/claude-agent-sdk/sdk-tools as the union ToolInputSchemas (and ToolOutputSchemas for outputs).
Files and search
| Tool | Input type | Fields |
|---|---|---|
Read | FileReadInput | file_path, offset?, limit?, pages? (PDF range such as "1-5") |
Write | FileWriteInput | file_path, content |
Edit | FileEditInput | file_path, old_string, new_string, replace_all? |
NotebookEdit | NotebookEditInput | notebook_path, cell_id?, new_source, cell_type?, edit_mode? (replace, insert, delete) |
Glob | GlobInput | pattern, path? |
Grep | GrepInput | pattern, path?, glob?, type?, output_mode?, -i, -o (needs content mode), -n, -B, -A, -C, context?, head_limit?, offset?, multiline? |
For PDFs, the Read tool_result carries a summary text block plus a document block (pdf output) or one block per page (parts output). Before SDK v0.3.242 PDF contents came in a separate user message.
Shell and background work
| Tool | Fields |
|---|---|
Bash | command, timeout? (ms; foreground capped at 600000 by default; with run_in_background on v2.1.285+ it is the background limit, default 1800000, max 7200000 unless raised), description?, run_in_background?, dangerouslyDisableSandbox?. The working directory persists across commands; exported variables do not |
Monitor | description, timeout_ms (default 300000, max 3600000, effective max 1800000; typed required but defaulted), and exactly one of command or ws: { url, protocols? } (v2.1.195+) |
TaskStop | task_id? (also a teammate or named background agent from v2.1.198), shell_id? (deprecated) |
TaskOutput was removed in v2.1.277; deny rules naming it are silently ignored. The experimental REPL tool was removed in v2.1.275.
Web
| Tool | Fields |
|---|---|
WebFetch | url, prompt |
WebSearch | query, allowed_domains?, blocked_domains? |
Agents, workflows and planning
| Tool | Fields |
|---|---|
Agent (alias Task) | description, prompt, subagent_type?, model? (sonnet, opus, haiku, fable), run_in_background?, name?, isolation? (worktree, remote), team_name? and mode? (both deprecated and ignored; mode from v2.1.212) |
Workflow | At least one of script (inline, starting export const meta = { name, description }, using agent(), parallel(), pipeline(), phase(); optional phases in meta), name (built-in or .claude/workflows/), scriptPath (wins over the others). Plus args (any JSON, exposed as global args), resumeFromRunId (same session only). title and description are ignored. SDK v0.3.149+ |
EnterPlanMode | none |
ExitPlanMode | allowedPrompts? (deprecated, ignored since v2.1.205) |
EnterWorktree | name? or path? (mutually exclusive). path must be a registered worktree of the repo (or nested repo) on first entry, or under .claude/worktrees/ from inside a worktree session |
ExitWorktree | action (keep, remove), discard_changes? (required true to remove with uncommitted or unmerged work) |
AskUserQuestion | questions (each question, header, options[] of label, description, preview?, and multiSelect), answers?, annotations? (preview, notes), metadata? |
ReportFindings | level?, findings[]: file, line?, summary, failure_scenario, short_summary? (60 chars, v2.1.212+), category? (v2.1.199+), verdict? (CONFIRMED, PLAUSIBLE), outcome? (fixed, skipped, no_change_needed). Max 32, most severe first (v2.1.196+) |
Tasks
| Tool | Fields |
|---|---|
TodoWrite | todos[]: content, status, activeForm |
TaskCreate | subject, description, activeForm?, metadata? |
TaskUpdate | taskId, status? (adds deleted), subject?, description?, activeForm?, addBlocks?, addBlockedBy?, owner?, metadata? |
TaskGet | taskId |
TaskList | none |
On by default only for Claude 3.x, Opus 4 to 4.7, Sonnet 4 to 4.6 and Haiku 4.5 (Claude Code v2.1.268+); opt in elsewhere as in todo tracking.
Scheduling and notifications
| Tool | Fields |
|---|---|
CronCreate | cron (5-field, local time), prompt, recurring? (false fires once), durable? (persist to .claude/scheduled_tasks.json where supported; check the output's durable) |
CronDelete | id |
CronList | none |
ScheduleWakeup | delaySeconds (clamped 60 to 3600), reason, prompt, noop (all required unless stop), stop? (ends a self-paced /loop; v2.1.202+). Backs /loop |
RemoteTrigger | action (list, get, create, update, run, create_webhook_trigger (v2.1.225+), list_runs, get_run_log (v2.1.227+)), trigger_id?, session_id?, cursor?, body?. Backs /schedule; only with a claude.ai plan that has routines and cloud sessions allowed |
PushNotification | message (keep under 200 chars), status: "proactive". Not available on Bedrock, Claude Platform on AWS, Agent Platform or Foundry |
MCP and claude.ai
| Tool | Fields |
|---|---|
ListMcpResourcesTool | server? |
ReadMcpResourceTool | server, uri |
ReadMcpResourceDirTool | server, uri. Non-recursive; needs server support and session enablement, otherwise empty resources with an error |
RefreshMcpTools | server?. Only registered with CLAUDE_CODE_ENABLE_REFRESH_MCP_TOOLS=1 and at least one MCP server (v2.1.211+) |
mcp__<server>__<tool> | McpInput: an open object defined by the server |
Artifact | action? (publish default, or list), file_path (required to publish), icon, favicon (deprecated), title, description, label, url (update in place), force (discard a newer version; only on explicit request), capabilities ({} clears; omit to keep; SDK v0.3.235+), contract (latest or a version; SDK v0.3.235+), limit, scope (mine, shared, all). Off by default in SDK sessions and unavailable with API-key auth |
Projects | method (project_info, project_read, project_search, project_write, project_delete), path?, content? or local_path? (exactly one for writes), present_to_user?, query?, n? (default 5) |
ShowOnboardingRolePicker | none; blocks until the user responds |
Tool output types
Files and search
| Tool | Output |
|---|---|
Read | Discriminated on type: text (filePath, content, numLines, startLine, totalLines, truncatedByTokenCap?), image (base64, MIME type, originalSize, dimensions?), notebook (filePath, cells), pdf (filePath, base64, originalSize), parts (filePath, originalSize, count, outputDir, plus firstPage?; pages is in-process only), file_unchanged (filePath, source?: "seeded" when the earlier copy was a start-up CLAUDE.md or memory file) |
Write | type (create, update), filePath, content, structuredPatch, originalFile, gitDiff?, userModified?. New files: originalFile null, empty patch. Previous content over about 10 MB: diff skipped. Patch also empty when nothing changed or the diff timed out |
Edit | filePath, oldString, newString, originalFile, structuredPatch, userModified, replaceAll, gitDiff? |
NotebookEdit | new_source, old_source?, cell_id?, cell_type, language, edit_mode, error?, notebook_path, original_file, updated_file |
Glob | durationMs, numFiles, filenames (sorted by modification time), truncated, totalMatches?, countIsComplete? (v2.1.191+; a lower bound when incomplete) |
Grep | mode?, numFiles, filenames, content?, numLines?, numMatches?, totalFiles? (v2.1.208+), totalLines? (v2.1.210+), appliedLimit?, appliedOffset?. Count-mode totals cover the full result set |
structuredPatch hunks have oldStart, oldLines, newStart, newLines, lines. gitDiff has filename, status (modified, added), additions, deletions, changes, patch, repository?.
Shell
BashOutput: stdout (stdout and stderr interleaved), stderr (the tool's own notices), interrupted, plus optional rawOutputPath, isImage, backgroundTaskId, backgroundedByUser, timedOutAfterMs and backgroundCwdHint (v2.1.210+), backgroundEndsWithFinalResponse (true when a foreground subagent owns the command, so it ends with that subagent; v2.1.227+), dangerouslyDisableSandbox, returnCodeInterpretation, noOutputExpected, structuredContent, persistedOutputPath, persistedOutputSize, staleReadFileStateHint, ghRateLimitHint, and gitOperation:
gitOperation key | Shape |
|---|---|
commit | { sha, kind: committed | amended | cherry-picked, branch? } (branch omitted on detached HEAD; SDK v0.3.227+) |
push | { branch } |
branch | { ref, action: merged | rebased } |
pr | { number, url?, action }, action one of created, edited, merged, commented, closed, reopened (SDK v0.3.234+), ready, draft, auto-merge-enabled, auto-merge-disabled |
MonitorOutput: taskId (use with TaskStop), timeoutMs, persistent?. TaskStopOutput: message, task_id, task_type, command?.
Web
| Tool | Output |
|---|---|
WebFetch | bytes, code, codeText, result, durationMs, url, artifactRead? (slug, ver?, seeded?: false; internal bookkeeping for artifact reads) |
WebSearch | query, results (strings or { tool_use_id, content: { title, url }[] }), durationSeconds, searchCount? |
Agents and workflows
AgentOutput, discriminated on status:
completed:agentId,agentType?,content(text blocks withcitations?),resolvedModel?(v2.1.174+),modelsUsed?(only after a mid-run swap; v2.1.212+),totalToolUseCount,totalDurationMs,totalTokens,usage,toolStats?(readCount,searchCount,bashCount,editFileCount,linesAdded,linesRemoved,otherToolCount,frameCount?),prompt,worktreePath?,worktreeBranch?.async_launched:isAsync?,agentId,description,resolvedModel?(model when backgrounded),modelsUsed?,prompt,outputFile,canReadOutputFile?.remote_launched:taskId,sessionUrl,description,prompt,outputFile.
usage and totalTokens come from the subagent's final request only. usage includes service_tier, cache_creation, server_tool_use, inference_geo, speed, iterations, output_tokens_details (SDK v0.3.228+; guard every level, e.g. usage.output_tokens_details?.thinking_tokens ?? 0) and fallback_credit (SDK v0.3.285+). The type was narrower before v2.1.207, so older results may lack optional fields.
WorkflowOutput returns as soon as the run is accepted; the result arrives later as a task completion.
| Field | Meaning |
|---|---|
status | async_launched (in-process) or remote_launched (cloud session) |
taskId, taskType | Background task (local_workflow or remote_agent) |
workflowName, summary | From the script's meta |
runId | Pass as resumeFromRunId later; absent for remote runs |
transcriptDir | Where subagent transcripts go |
scriptPath | Persisted script; edit and re-run with it |
sessionUrl | Remote runs only |
warning | Non-blocking advice |
error | Syntax check failed: the run did not start despite the launched status |
Planning, worktrees and questions
| Tool | Output |
|---|---|
AskUserQuestion | questions, answers (question to answer; multi-select comma-joined), response? (free-form reply), annotations?, afkTimeoutMs? |
ExitPlanMode | plan, isAgent, filePath?, hasTaskTool?, planWasEdited?, awaitingLeaderApproval?, requestId? |
EnterPlanMode | message |
EnterWorktree | worktreePath, worktreeBranch?, message |
ExitWorktree | action, originalCwd, worktreePath, worktreeBranch?, tmuxSessionName?, discardedFiles?, discardedCommits?, message |
ReportFindings | count, level?, findings echoed back |
Tasks, scheduling and notifications
| Tool | Output |
|---|---|
TodoWrite | oldTodos, newTodos |
TaskCreate | task: { id, subject } |
TaskUpdate | success, taskId, updatedFields, error?, statusChange? |
TaskGet | task (id, subject, description, status, blocks, blockedBy) or null |
TaskList | tasks[]: id, subject, status, owner?, blockedBy |
CronCreate | id, humanSchedule, recurring, durable? |
CronDelete | id |
CronList | jobs[]: id, cron, humanSchedule, prompt, recurring?, durable? (false for session-only; absent for jobs read from disk) |
ScheduleWakeup | scheduledFor (epoch ms), clampedDelaySeconds, wasClamped, stopped? (v2.1.202+), cancelledWakeups? (v2.1.206+; a recurring /loop cron is not cancelled) |
RemoteTrigger | status, json, summary? |
PushNotification | message, pushSent?, localSent?, disabledReason? (config_off, user_present, no_transport), sentAt? |
MCP and claude.ai
| Tool | Output |
|---|---|
ListMcpResourcesTool | Array of uri, name, mimeType?, description?, server |
ReadMcpResourceTool | contents[] (uri, mimeType?, text?, blobSavedTo?), error? |
ReadMcpResourceDirTool | resources[] (uri, name, mimeType?; directories are inode/directory), error? |
RefreshMcpTools | Array of server, status (refreshed, error, not_connected), toolCount?, added?, removed?, error? |
Artifact | Publish: url, path, title?, version?, capabilities?, stored?, warnings?, contract?, updated?, liveSubscription?. List: artifacts[] (title, url, updatedAt?, rel?), truncated?, scope? |
Projects | Discriminated on method. project_read inlines small docs in content and writes larger ones to local_file; project_search returns hits with rag: true or falls back to a docs list |
ShowOnboardingRolePicker | role?, dismissed?; empty means approved without choosing |
| MCP tools | McpOutput: a string or an array of content blocks (the plain-object branch in the type is a schema artefact; may also be undefined at runtime) |
Permission types
type PermissionUpdate =
| { type: "addRules" | "replaceRules" | "removeRules"; rules: PermissionRuleValue[]; behavior: PermissionBehavior; destination: PermissionUpdateDestination }
| { type: "setMode"; mode: PermissionMode; destination: PermissionUpdateDestination }
| { type: "addDirectories" | "removeDirectories"; directories: string[]; destination: PermissionUpdateDestination };
type PermissionBehavior = "allow" | "deny" | "ask";
type PermissionUpdateDestination = "userSettings" | "projectSettings" | "localSettings" | "session" | "cliArg";
type PermissionRuleValue = { toolName: string; ruleContent?: string };
Other types
Account, models and agents
| Type | Fields |
|---|---|
AccountInfo | email?, organization?, subscriptionType?, tokenSource?, apiKeySource? |
ModelInfo | value, resolvedModel? (what an alias resolves to; v2.1.197+), displayName, description, supportsEffort?, supportedEffortLevels?, supportsAdaptiveThinking?, supportsFastMode?, supportsAutoMode? |
AgentInfo | name (e.g. Explore, general-purpose), description, model? |
SlashCommand | name, description, argumentHint, aliases?, builtin? (true for Claude Code's own commands not replaced by name; SDK v0.3.277+) |
ConfigScope | "local" | "user" | "project" |
ApiKeySource reports one of four values: ANTHROPIC_API_KEY, apiKeyHelper, /login managed key (Claude Console login) or none (claude.ai login, bearer token or cloud provider). The type also keeps user, project, org, temporary and oauth for compatibility, but they are not reported.
SdkBeta is "context-1m-2025-08-07". It is retired for Sonnet 4.5 and Sonnet 4 on the Claude API, and over-200K requests then fail. For 1M context, use a model that has it by default such as claude-sonnet-5-5 or claude-opus-5-5, or a [1m] variant such as claude-opus-4-6[1m].
MCP status and provenance
McpServerStatus: name, status (connected, failed, needs-auth, pending, disabled), serverInfo?, error?, config? (an McpServerStatusConfig, any transport plus claudeai-proxy), scope?, source? (SDK v0.3.274+), tools? (name, description?, annotations? with readOnly, destructive, openWorld, and _meta?). Tool _meta carries only the MCP Apps ui object (resourceUri, visibility) and the deprecated ui/resourceUri, when init capabilities include mcp_tool_ui_meta_v1 (SDK v0.3.280+).
McpServerProvenance: { name, source }, carried as mcp_server on tool hook inputs and mcpServer in canUseTool options. source is sdk (only your app can register these), plugin (name is plugin:<plugin>:<server>), or a scope: user, project (.mcp.json), local, dynamic (your mcpServers option, non-SDK), managed, enterprise, claudeai or agent. Base trust on source, never on the name, and escape names from non-sdk sources.
McpSetServersResult: added, removed, errors. Rules for setMcpServers():
- Servers you omit: outside cloud sessions, servers added by earlier
setMcpServers()calls and in-process SDK servers are disconnected and listed inremoved. Servers from themcpServersoption (stdio, HTTP, SSE), settings files and plugins keep running. - Servers you name: an earlier
setMcpServers()stdio, HTTP or SSE server is replaced only if its config changed. An in-process SDK server under that name stays; to swap it, omit it in one call and add it in the next. - Built-in servers started by the CLI are dropped and reported in
errors. - The promise resolves after new servers connect or fail. A failed server appears in both
addedanderrorsand asfailedin status (from v2.1.257).
SDKMcpResourceLink: uri, name, title?, description?, mimeType?, size?, annotations?. Blocks without string uri and name are dropped (SDK v0.3.257+).
CallToolResult: content (blocks of text, image, audio, resource, resource_link), structuredContent?, isError?.
Usage
ModelUsage: inputTokens, outputTokens, thinkingTokens? (already in outputTokens; SDK v0.3.257+), cacheReadInputTokens, cacheCreationInputTokens, webSearchRequests, costUSD, contextWindow, maxOutputTokens, canonicalModel? and provider? (v2.1.218+; providers firstParty, bedrock, vertex, foundry, anthropicAws, mantle, gateway), costBasis? (list, managed, unknown; v2.1.246+).
Usage is BetaUsage from @anthropic-ai/sdk: input_tokens, output_tokens, cache_creation_input_tokens, cache_read_input_tokens, cache_creation (5m and 1h), server_tool_use, service_tier, speed (standard, fast), inference_geo, iterations, output_tokens_details (thinking_tokens; SDK v0.3.228+), fallback_credit (present if your @anthropic-ai/sdk is 0.115.0+). NonNullableUsage makes every field non-null except fallback_credit.
On output_tokens_details.thinking_tokens: use it for observability, not billing (output_tokens is authoritative); it counts raw reasoning including delimiters and may differ by a few tokens; on streamed assistant messages it is a placeholder; on the result it reads 0 when no breakdown is reported; the object is null on synthesised messages such as API errors.
Thinking
type ThinkingConfig =
| { type: "adaptive"; display?: "summarized" | "omitted" }
| { type: "enabled"; budgetTokens?: number; display?: "summarized" | "omitted" }
| { type: "disabled" };
Adaptive needs Opus 4.6 or later. On Opus 4.7+ the API defaults display to omitted; set summarized to get text. Bedrock and Google Cloud's Agent Platform do not receive display, so Opus 4.7+ thinking blocks are empty there.
Custom process spawning
SpawnOptions: command, args, cwd?, env, signal. SpawnedProcess: stdin, stdout, killed, exitCode, kill(signal), and on/once/off for exit and error; a Node ChildProcess already satisfies it.
The signal does not fire the instant your abortController aborts: the SDK closes stdin, waits about two seconds for a clean shutdown, then aborts it. Listen on your own abortController.signal to react immediately.
import { spawn } from "node:child_process";
const options = {
spawnClaudeCodeProcess: ({ command, args, env, signal }) =>
spawn("docker", ["exec", "-i", "agent-box", command, ...args], { env, signal, stdio: ["pipe", "pipe", "inherit"] })
};
RewindFilesResult
canRewind, error?, filesChanged?, insertions?, deletions?, skippedLinks? (paths refused for link safety; v2.1.216+; never set on dryRun).
Sandbox
SandboxSettings
| Key | Default | Meaning |
|---|---|---|
enabled | false | Sandbox Bash |
failIfUnavailable | true | Stop at start-up if the sandbox cannot start; false falls back to unsandboxed with a warning |
autoAllowBashIfSandboxed | true | Auto-approve sandboxed Bash |
excludedCommands | [] | Always run unsandboxed, e.g. ['docker *'], with no model involvement |
allowUnsandboxedCommands | true | Let the model set dangerouslyDisableSandbox, which then goes through permissions |
network | none | SandboxNetworkConfig |
filesystem | none | { allowWrite?, denyWrite?, denyRead? } path patterns |
ignoreViolations | none | Map of command substring (or *) to violation-text substrings, e.g. { "*": ["/etc/hosts"] } |
enableWeakerNestedSandbox | false | Compatibility mode |
ripgrep | none | { command, args? } for a custom ripgrep |
Linux needs bubblewrap and socat. If the sandbox cannot start with the default failIfUnavailable, the run ends in error_during_execution with the reason in errors, and a one-shot query() then throws.
SandboxNetworkConfig
Applies to sandboxed Bash only; WebFetch uses permission rules.
| Key | Meaning |
|---|---|
allowedDomains | Reachable domains |
deniedDomains | Blocked; beats allowed |
strictAllowlist | Deny hosts outside the allowlist instead of prompting; only from user, managed or CLI --settings sources (v2.1.219+) |
allowManagedDomainsOnly | Managed only; from the SDK pass it via managedSettings |
allowLocalBinding | Bind local ports |
allowUnixSockets | Allowed socket paths |
allowAllUnixSockets | Every socket |
httpProxyPort, socksProxyPort | Proxy ports |
Warning: Allowing
/var/run/docker.sockhands over the host. The proxy filters by hostname without inspecting TLS, so domain fronting can bypass it; see secure deployment.
Gating unsandboxed requests
With allowUnsandboxedCommands on, a Bash call with dangerouslyDisableSandbox: true reaches canUseTool:
for await (const m of query({
prompt: "Run the end-to-end suite",
options: {
sandbox: { enabled: true, allowUnsandboxedCommands: true },
permissionMode: "default",
canUseTool: async (tool, input) => {
if (tool === "Bash" && input.dangerouslyDisableSandbox) {
const ok = String(input.command).startsWith("npx playwright ");
return ok
? { behavior: "allow", updatedInput: input }
: { behavior: "deny", message: "Only Playwright may run outside the sandbox" };
}
return { behavior: "allow", updatedInput: input };
}
}
})) { /* ... */ }
Warning: Never combine
bypassPermissionswithallowUnsandboxedCommands: the model could leave the sandbox without asking anyone, apart from the actions no mode auto-approves.