scalable/ai/claude agent sdk/lesson 01 cph / /
lesson 01 / 08 · 11 min · updated ·

what is the claude agent sdk

anthropic's typescript-first sdk for building coding agents. the loop, the harness, and the parts the sdk decided to own.

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.

  1. 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.
  2. 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.
  3. permissions a canUseTool callback per tool call, plus coarser permission modes — default, acceptEdits, bypassPermissions, plan. the difference between a helpful agent and a destructive one.
  4. sub-agents the Agent tool 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.
  5. hooks lifecycle extension points — PreToolUse, PostToolUse, Stop, UserPromptSubmit, and friends. lint-on-write, telemetry, custom statuslines, hard policy gates.
  6. 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.
  7. system prompts and settings append-mode customisation of claude code's default system prompt, plus settingSources to control which on-disk settings the agent inherits. tailoring without forking.
why it exists. every team that builds an agent ends up writing the same five hundred lines of permission, hook, and sub-agent plumbing — usually badly. the sdk is anthropic deciding to own that plumbing so you don't have to maintain it. the tradeoff is a soft dependency on anthropic's conventions; for most agents that's a fine trade.

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.

your first agent — typescript, copy-paste runnable
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.

EVENT STREAM · live walk-through 1 / 6
system system · init
{
  "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:

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.

scalable labs·cvr 30091604·github·linkedin·hello@scalable.dk