Atlas survey

Claude Agent SDK & Headless Agents

Complete technical reference: the query() API, claude -p headless mode, subagents, hooks, dynamic workflows, and the June 15 2026 billing separation—all from official docs.

18 sources ~8 min read #181 claude · agent-sdk · headless · anthropic · workflow · subagents · mcp · developer-tools

TL;DR: The Claude Agent SDK (renamed from Claude Code SDK) gives you Claude’s full agentic loop—built-in tools, subagents, hooks, MCP, sessions—as a Python or TypeScript library running in your own process [1]. Use claude -p for one-shot CLI tasks; use query() for programmatic control with structured output. Starting June 15, 2026, all headless/SDK usage draws from a separate monthly credit ($20–$200 by plan), separate from interactive limits [13].

What it is

Renamed from “Claude Code SDK” in late 2025, the Agent SDK packages the same engine as Claude Code ⭐ 130k (Jun 2026) as an embeddable library [15]. The fundamental design principle: Gather Context → Take Action → Verify Work → Repeat — the same loop an expert programmer follows, delegated to Claude [2].

The key distinction from the Anthropic Client SDK: the Client SDK hands you raw message objects and requires you to implement the tool loop; the Agent SDK handles that loop autonomously [1].

# Client SDK: you implement the tool loop
response = client.messages.create(...)
while response.stop_reason == "tool_use":
    result = your_tool_executor(response.tool_use)
    response = client.messages.create(tool_result=result, **params)

# Agent SDK: Claude handles tools autonomously
async for message in query(prompt="Fix the bug in auth.py"):
    print(message)

Installation

pip install claude-agent-sdk          # Python 3.10+
npm install @anthropic-ai/claude-agent-sdk   # bundles native binary; no separate Claude Code install

Set ANTHROPIC_API_KEY (or configure Bedrock/Vertex/Foundry env vars). Third-party cloud provider support: set CLAUDE_CODE_USE_BEDROCK=1, CLAUDE_CODE_USE_VERTEX=1, or CLAUDE_CODE_USE_FOUNDRY=1 with the respective provider credentials [1].

Headless CLI: claude -p

The -p / --print flag makes Claude Code non-interactive — one turn, stdout output, exits [5].

# Basic question
claude -p "What does the auth module do?"

# Pipe data in, redirect out (stdin capped at 10MB as of v2.1.128)
cat build-error.txt | claude -p 'explain the root cause concisely' > output.txt

# Structured JSON output with schema enforcement
claude -p "Extract function names from auth.py" \
  --output-format json \
  --json-schema '{"type":"object","properties":{"functions":{"type":"array","items":{"type":"string"}}},"required":["functions"]}'

# CI-safe: bare mode, no config discovery, no permission prompts
claude --bare -p "Run tests and summarize failures" \
  --allowedTools "Bash,Read" \
  --permission-mode acceptEdits

# Stream tokens in real time (NDJSON)
claude -p "Write a migration plan" --output-format stream-json \
  --verbose --include-partial-messages \
  | jq -rj 'select(.type=="stream_event" and .event.delta.type?=="text_delta") | .event.delta.text'

Key flags [5]:

Flag Effect
--bare Skips hooks, skills, MCP servers, CLAUDE.md, memory — identical across machines
--allowedTools "Read,Edit,Bash" Auto-approve named tools; no permission prompt
--permission-mode acceptEdits Auto-approve file writes; still prompts for shell/network calls
--permission-mode dontAsk Never prompt — blocks anything not in allowlist; safe for locked-down CI
--output-format json Returns {result, session_id, total_cost_usd, ...} — parseable with jq
--output-format stream-json NDJSON token stream; filter with jq for live display
--append-system-prompt "..." Extend the system prompt without replacing Claude Code’s defaults
--continue / --resume <id> Continue most recent or a specific past session
--settings <file-or-json> Load settings explicitly (useful in --bare mode where auto-discovery is off)

--bare will become the default for -p in a future release. Without it, a hook in any contributor’s ~/.claude or an MCP server in .mcp.json will silently execute in your CI [5].

Programmatic: query()

import asyncio
from claude_agent_sdk import query, ClaudeAgentOptions

async def main():
    async for message in query(
        prompt="Find and fix the bug in auth.py",
        options=ClaudeAgentOptions(allowed_tools=["Read", "Edit", "Bash"]),
    ):
        if hasattr(message, "result"):
            print(message.result)

asyncio.run(main())
import { query } from "@anthropic-ai/claude-agent-sdk";

for await (const message of query({
  prompt: "Find and fix the bug in auth.ts",
  options: { allowedTools: ["Read", "Edit", "Bash"] }
})) {
  if ("result" in message) console.log(message.result);
}

query() yields typed message objects. The final ResultMessage carries result, session_id, total_cost_usd, and subtype (success / error_max_turns / error_max_budget_usd). For multi-turn conversations, use Python’s ClaudeSDKClient (tracks session state automatically across client.query() calls) or TypeScript’s continue: true option [12].

Built-in tools

No tool execution code required — these ship with the SDK [1]:

Tool What it does
Read Read any file in the working directory
Write Create new files
Edit Precise edits to existing files (diff-based)
Bash Terminal commands, scripts, git operations
Glob Find files by pattern (**/*.ts, src/**/*.py)
Grep Regex search across file contents
Monitor Watch a background script; react to each output line as an event
WebSearch Live web search
WebFetch Fetch and parse web page content
AskUserQuestion Prompt the user with structured multiple-choice options
Agent Spawn a subagent (include in allowedTools to auto-approve)
Workflow Launch a dynamic workflow script (TypeScript SDK v0.3.149+)

Subagents

Subagents run in fresh context windows, isolating intermediate work from the parent and enabling parallelism [6]:

from claude_agent_sdk import query, ClaudeAgentOptions, AgentDefinition

options = ClaudeAgentOptions(
    allowed_tools=["Read", "Grep", "Glob", "Agent"],
    agents={
        "code-reviewer": AgentDefinition(
            description="Use for quality, security, and maintainability reviews.",
            prompt="You are a code review specialist. Find security vulnerabilities and quality issues.",
            tools=["Read", "Grep", "Glob"],  # read-only restriction
            model="opus",                    # per-agent model override
        ),
        "test-runner": AgentDefinition(
            description="Use for test execution and coverage analysis.",
            prompt="Run tests and summarize failures concisely.",
            tools=["Bash", "Read", "Grep"],
            background=True,                 # non-blocking: parent continues immediately
        ),
    },
)

The description field drives automatic invocation — Claude selects subagents based on it. Mention a subagent by name in the prompt to force invocation. Critical constraint: subagents cannot spawn their own subagents — don’t include Agent in a subagent’s tools list [6].

AgentDefinition fields of note:

Field Purpose
model Per-agent alias ("opus", "sonnet", "haiku") or full model ID
background Non-blocking; parent continues while subagent runs
maxTurns Stop after N agentic turns
effort Reasoning effort: low, medium, high, xhigh, max
permissionMode Override permission mode for this subagent only
mcpServers Subagent-specific MCP servers (subset of parent’s, or new ones)
skills Skills to preload into this subagent’s context at startup

Common tool combinations [6]:

Use case Tools
Read-only analysis Read, Grep, Glob
Test execution Bash, Read, Grep
Code modification Read, Edit, Write, Grep, Glob
Full access Omit tools field — inherits all

Reference implementations in anthropics/claude-agent-sdk-demos ⭐ 2.5k: multi-agent research system, branding assistant, spreadsheet agent, resume generator [7].

Dynamic Workflows

For jobs coordinating dozens to hundreds of agents (codebase audits, large migrations, cross-checked research), the Workflow tool moves orchestration into a JavaScript script the runtime executes outside the conversation context [8]:

export const meta = {
  name: "api-auth-audit",
  description: "Audit every API endpoint for missing auth checks",
  phases: [{ title: "Scan" }, { title: "Verify" }],
};

const FINDING = { type: "object", properties: {
  endpoint: { type: "string" },
  missing_auth: { type: "boolean" },
  details: { type: "string" }
}, required: ["endpoint", "missing_auth"] };

phase("Scan");
const findings = await pipeline(
  endpoints,   // list discovered earlier
  e => agent(`Check ${e} for missing authentication`, { phase: "Scan", schema: FINDING }),
  f => f?.missing_auth
    ? agent(`Adversarially verify: is ${f.endpoint} really unprotected?`, {
        phase: "Verify", label: `verify:${f.endpoint}`
      })
    : null
);
return findings.filter(Boolean);

Script primitives [8]:

Primitive Semantics
agent(prompt, opts) Spawn one agent; returns string or typed object (when schema provided)
pipeline(items, ...stages) No barrier between stages — item A in stage 3 while item B is still in stage 1
parallel(thunks) Barrier: awaits all before returning — use only when cross-item context needed
phase(title) Group subsequent agents in the /workflows progress view
log(message) Emit a narrator line above the progress tree
budget {total, spent(), remaining()} — scale agent count to a token budget

Runtime constraints: up to 16 concurrent agents (fewer on limited-CPU machines), 1,000 agents total per run, runs are resumable within the same session [8].

Trigger methods: include ultracode keyword in your prompt, say “use a workflow” / “run a workflow” in your own words, or set /effort ultracode for automatic workflow planning across the session [18]. Workflow is available in TypeScript Agent SDK v0.3.149+; include it in allowedTools to auto-approve.

Hooks

Hooks are callback functions registered on agent events — block, modify, audit, or react without changing application logic [9]:

async def protect_env_files(input_data, tool_use_id, context):
    file_path = input_data["tool_input"].get("file_path", "")
    if file_path.endswith(".env"):
        return {"hookSpecificOutput": {
            "hookEventName": "PreToolUse",
            "permissionDecision": "deny",
            "permissionDecisionReason": "Cannot modify .env files",
        }}
    return {}

options = ClaudeAgentOptions(
    hooks={"PreToolUse": [HookMatcher(matcher="Write|Edit", hooks=[protect_env_files])]}
)

Available hooks [9]:

Hook Python TypeScript Fires when
PreToolUse Tool call about to execute — block, modify, or defer
PostToolUse Tool returned — inject context or replace output
PostToolUseFailure Tool execution failed
PostToolBatch Full batch resolved — inject context once per batch
SubagentStart / SubagentStop Subagent spawned / completed
PreCompact Conversation about to be summarized
Notification Agent status events — forward to Slack, PagerDuty
PermissionRequest Permission dialog would appear — custom handling
UserPromptSubmit Prompt submission — inject additional context
MessageDisplay Assistant message completes — redact/reformat display
SessionStart / SessionEnd ✗ (SDK) Session lifecycle [17]

Multiple PreToolUse hooks run in parallel; deny wins over all other decisions [9]. Return {} to allow without change. Return {async_: True, asyncTimeout: 30000} (Python) / {async: true} (TypeScript) for fire-and-forget side effects (logging, webhooks) where the hook must not block the agent.

Matchers: exact tool name ("Write"), pipe-separated list ("Write|Edit"), regex ("^mcp__" matches all MCP tools), or omitted (matches all events of that type).

Sessions

Sessions persist to ~/.claude/projects/<encoded-cwd>/<session-id>.jsonl. Three continuation modes [12]:

Mode How to use When
continue continue=True / continue: true Most recent session; no ID tracking needed
resume resume=session_id Specific session; required for multi-user apps
fork fork_session=True / forkSession: true New branch from a session; original unchanged

Sessions store conversation history only — not filesystem state. For filesystem snapshots, use file checkpointing. To resume across machines (CI workers, containers), either ship the JSONL file to the same cwd-encoded path or extract the relevant results into the next prompt explicitly [12].

TypeScript-only: persistSession: false keeps the session in memory only — useful for stateless single-turn tasks that should leave no disk artifacts.

MCP Integration

Connect to external systems without custom auth or API management [1]:

options = ClaudeAgentOptions(
    mcp_servers={
        "playwright": {"command": "npx", "args": ["@playwright/mcp@latest"]},
        "github":     {"command": "npx", "args": ["@modelcontextprotocol/server-github"]},
    }
)

The MCP servers registry ⭐ 87k covers databases, browsers, Slack, GitHub, Asana, and hundreds more [16]. MCP tools are named mcp__<server>__<action> and can be targeted in hooks with the ^mcp__ regex matcher. Subagents can receive per-agent MCP server subsets via AgentDefinition.mcpServers.

GitHub Actions

anthropics/claude-code-action ⭐ 7.8k powers @claude mentions in PR/issue comments and scheduled automation [11] [14]:

# .github/workflows/claude.yml
- uses: anthropics/claude-code-action@v1
  with:
    anthropic_api_key: $
    prompt: "Review for security vulnerabilities"
    claude_args: "--max-turns 10 --model claude-opus-4-8"

All CLI flags pass via claude_args. Enterprise cloud: set use_bedrock: "true" + AWS OIDC, or use_vertex: "true" + GCP Workload Identity Federation for data-residency compliance [11]. GitHub Actions usage counts against the Agent SDK monthly credit (see Billing below).

Quick setup: run /install-github-app in Claude Code to configure the GitHub App and repository secrets automatically.

SDK vs CLI vs Managed Agents

  Agent SDK (query()) CLI (claude -p) Managed Agents (beta)
Runs in Your process Your process / CI runner Anthropic-managed sandbox
Interface Python / TypeScript Shell command REST API
Tool loop SDK handles automatically SDK handles automatically Anthropic handles
Session state JSONL on your filesystem JSONL on your filesystem Anthropic-hosted
Custom tools In-process functions Not directly You execute, return results
Structured output schema option --json-schema flag SSE event stream
Subagents agents param + Agent tool Not directly Via Managed Agents API
Workflows Workflow tool (TS v0.3.149+) ultracode keyword
Best for Production automation, custom apps Scripts, CI one-shot tasks Long-running async, no-infra

The typical progression: prototype with query() locally → automate via claude -p in CI → move long-running async sessions to Managed Agents when you don’t want to operate sandbox infrastructure [10].

Billing — June 15, 2026

Starting June 15, 2026, headless/SDK usage separates from interactive usage into a dedicated monthly credit [13]:

Plan Monthly SDK credit
Pro $20
Max 5x $100
Max 20x $200
Team Standard seats $20
Team Premium seats $100
Enterprise Premium seats $200

Counts against credit: query() API, claude -p, GitHub Actions integration, third-party apps authenticating via Agent SDK.

Does not count: Interactive Claude Code (TUI/IDE extensions), web/desktop/mobile chat.

Credits are per-user, refresh monthly, don’t carry over. When exhausted, usage flows to API-rate usage credits at standard token rates. Interactive usage limits are unchanged.

Citations · 18 sources

Click the Citations tab to load…