tl;dr. the claude agent sdk is anthropic's typescript-first library for embedding a claude-driven agent loop in your own program. it ships the parts you'd otherwise rebuild — a tool-use loop, a permission model, lifecycle hooks, a sub-agent primitive, and conventions for skills and system prompts — and leaves the surface (cli, http, ide, ci) to you. same harness as claude code, exposed as a library.
what is the claude agent sdk?
the claude agent sdk
(npm: @anthropic-ai/claude-agent-sdk) is the package
anthropic publishes for engineers who want to build their own agents
on top of the same harness that powers claude code.
it is opinionated about the loop shape (model → tool calls → tool
results → model, until done) and unopinionated about where the agent
runs (a node cli, a backend service, a vs code extension, a github
action, a worker dyno).
in concrete terms, the sdk is a single async generator —
query() — that takes a prompt and an options bag, returns
a stream of structured events (assistant turns, tool uses, tool
results, sub-agent spawns, the final result), and lets you intercept
almost every step in between. you write what to do with those events;
the sdk owns the parts that are tedious and easy to get wrong.
what the sdk is not: a managed runtime. there is no anthropic-hosted sandbox, no per-invocation pricing for the agent loop itself, and no observability dashboard. you call the messages api directly through your own anthropic key, on your own machine or your own server. token cost is just the cost of the underlying messages api; the sdk adds nothing on top.
seven things the sdk owns
most of what you'd build by hand on top of the raw api lives in here. you don't need all of it for every agent — most production agents touch four or five.
- the query loop a single async generator that drives one or many turns of model + tool execution. you read structured events; the sdk handles the api dance, retries, and tool-call serialisation.
- tools
a built-in toolset (
Read,Write,Edit,Bash,Glob,Grep,WebSearch,WebFetch, and more), plus a clean way to define your own or wire an mcp server. - permissions
a
canUseToolcallback per tool call, plus coarser permission modes —default,acceptEdits,bypassPermissions,plan. the difference between a helpful agent and a destructive one. - sub-agents
the
Agenttool dispatches a focused sub-task in its own isolated context, and only the summary comes back. how you keep a long parent run from drowning in tool noise. - hooks
lifecycle extension points —
PreToolUse,PostToolUse,Stop,UserPromptSubmit, and friends. lint-on-write, telemetry, custom statuslines, hard policy gates. - skills project and user skill files the sdk loads automatically as context. a way to teach the agent your conventions without inlining a wall of prompt strings.
- system prompts and settings
append-mode customisation of claude code's default system
prompt, plus
settingSourcesto control which on-disk settings the agent inherits. tailoring without forking.
the shape of a minimal agent
here is the smallest useful program. query() returns an
async iterable; you loop over it and react to events. this agent
reads files (it's allowed to use Read and
Glob) but cannot write or run shell commands — those
tools aren't in allowedTools, so the agent can't even
try.
import { query } from "@anthropic-ai/claude-agent-sdk";
// the smallest useful program — ask claude to summarise a file in this repo.
// the agent reads files, writes nothing, runs no shell commands.
for await (const event of query({
prompt: "summarise the readme of this repo in three bullets",
options: {
permissionMode: "default",
allowedTools: ["Read", "Glob"],
},
})) {
if (event.type === "assistant") {
for (const block of event.message.content) {
if (block.type === "text") process.stdout.write(block.text);
}
}
if (event.type === "result") {
console.log(`\n--- done in ${event.duration_ms}ms, ${event.num_turns} turns`);
}
} that loop emits a fixed sequence of events whatever you ask it. step through a real run below — six events for the simplest possible agent: a single Read call followed by a summary. each event is the exact shape your code receives.
{
"type": "system",
"subtype": "init",
"session_id": "ses_abc123",
"cwd": "/Users/sadi/projects/scalable",
"model": "claude-sonnet-4-6",
"permissionMode": "default",
"tools": ["Read", "Glob"],
"mcp_servers": [],
"apiKeySource": "env"
} {
"type": "assistant",
"message": {
"role": "assistant",
"content": [
{
"type": "text",
"text": "I'll read the README and summarise it in three bullets."
}
]
}
} {
"type": "assistant",
"message": {
"role": "assistant",
"content": [
{
"type": "tool_use",
"id": "toolu_01a2",
"name": "Read",
"input": { "file_path": "/Users/sadi/projects/scalable/README.md" }
}
]
}
} {
"type": "user",
"message": {
"role": "user",
"content": [
{
"type": "tool_result",
"tool_use_id": "toolu_01a2",
"content": "# scalable\n\nA small studio in Denmark...\n(7,400 chars)"
}
]
}
} {
"type": "assistant",
"message": {
"role": "assistant",
"content": [
{
"type": "text",
"text": "- a small Danish studio run by Sadi Kaya\n- ships indebtio, strata, matematikspillet\n- publishes /ai/, /craft/, /scale/ courses"
}
]
}
} {
"type": "result",
"subtype": "success",
"duration_ms": 4123,
"num_turns": 2,
"total_cost_usd": 0.0184,
"usage": {
"input_tokens": 8214,
"output_tokens": 142,
"cache_read_input_tokens": 0
},
"result": "- a small Danish studio run by Sadi Kaya..."
} fires once. tells you the resolved configuration — model, cwd, tools, mcp servers, permission mode. read it; assert on it in tests.
read the timeline. system fires once at startup with
the resolved configuration. assistant events carry the
model's reasoning and any tool-use blocks it wants to make.
user events carry tool results back to the model.
result fires once at the end with totals — duration,
turn count, token usage, cost. that sequence is the same shape
you'll see whether the agent runs for one turn or fifty.
a few things worth noticing in that snippet:
- no agent class. no run() method.
query()is a function that returns an async iterable. that's the whole api surface for the loop. - tools are an allow-list, not a registry.
omit
allowedToolsand the agent gets the full built-in set — usually not what you want. naming the subset you trust is the cheapest possible safety rail. - events are typed, not a string stream.
you discriminate on
event.type, thenblock.typeinside an assistant message. the types are exported so you can switch on them exhaustively. - no key in the snippet.
ANTHROPIC_API_KEYis read from the environment. this is also whereANTHROPIC_BASE_URLgoes if you're routing through a gateway.
what this course is and isn't
eight lessons for engineers who want a working mental model of the sdk without reading the source. every lesson is one concept with the smallest example that makes it real. we use the real package, the real api, and the real built-in tools — there is no mock layer here, because the sdk is the thing you'd ship.
lesson four is permissions, which is the one most "build an agent"
walkthroughs skim past and the one you'll spend the most time on
once something is in production. lesson eight is the shipping diff:
how the same query() call wraps differently depending
on whether the agent runs as a cli, a backend, or an ide extension.