AI Cert Prep
Type to search documentation.

Appendix · Claude

Agent SDK Cheat Sheet

Claude Agent SDK quickstarts in Python and TypeScript, options, hooks, subagents, MCP servers, permission modes, streaming events and headless patterns.

The Claude Agent SDK (claude-agent-sdk, renamed from “Claude Code SDK”) exposes the Claude Code agent harness programmatically: the agentic loop, tools, hooks, permissions, subagents and MCP servers. You host it — contrast Managed Agents, where Anthropic hosts the loop and sandbox.

When to reach for the SDK

Choose the Agent SDK when you need to control the runtime, network, data locality or the harness itself. Choose Managed Agents for least operational overhead. Choose a plain Messages API loop when you do not need Claude Code’s file/tool machinery.

Install

Terminal window
pip install claude-agent-sdk # Python
npm install @anthropic-ai/claude-agent-sdk # TypeScript

Quickstart

python
import anyio
from claude_agent_sdk import query, ClaudeAgentOptions
async def main():
options = ClaudeAgentOptions(
model="claude-opus-5",
system_prompt="You are a precise refactoring assistant.",
allowed_tools=["Read", "Edit", "Bash"],
permission_mode="acceptEdits",
cwd="/repo",
)
async for message in query(prompt="Rename getUserName to getUsername repo-wide, then run tests.",
options=options):
print(message)
anyio.run(main)

Options

Option (Py / TS)Purpose
modelModel ID (claude-opus-5, claude-sonnet-5, …)
system_prompt / systemPromptBase instructions; may append to the built-in Claude Code prompt
allowed_tools / allowedToolsTool allowlist (Read, Edit, Bash, Grep, …)
disallowed_tools / disallowedToolsExplicit denies
permission_mode / permissionModedefault | acceptEdits | plan | bypassPermissions
cwdWorking directory
mcp_servers / mcpServersMCP server configs available to the agent
hooksLifecycle handlers (see below)
agentsSubagent definitions
setting_sources / settingSourcesWhether to load CLAUDE.md/settings from disk
max_turns / maxTurnsSafety cap on agentic turns (not the primary stop)
envEnvironment variables for tool execution

max_turns is a guard, not a stop

max_turns is a runaway safety net. The real loop termination is still stop_reason: end_turn. Using a turn cap as the primary stop is anti-pattern 2.

Permission modes

ModeBehaviourUse
defaultAsk per the permission rulesInteractive, cautious
acceptEditsAuto-accept file edits, ask for shellTrusted edit loops
planRead-only; produce a plan, execute nothingReview before acting
bypassPermissionsNo promptsSandboxed CI only

Programmatic permission callback

Fine-grained control beyond allow/deny lists — decide per call:

python
async def can_use_tool(tool_name, tool_input, context):
if tool_name == "Bash" and "rm -rf" in tool_input.get("command", ""):
return {"behavior": "deny", "message": "destructive command blocked"}
if tool_name == "Edit" and tool_input.get("file_path", "").endswith(".env"):
return {"behavior": "deny", "message": "secrets are read-only"}
return {"behavior": "allow"}
options = ClaudeAgentOptions(can_use_tool=can_use_tool, allowed_tools=["Read", "Edit", "Bash"])

Hooks

Same lifecycle events as Claude Code (PreToolUse, PostToolUse, UserPromptSubmit, Stop, SessionStart, SubagentStop, PreCompact, Notification), registered in code. A PreToolUse hook returning a deny decision blocks the tool — the deterministic enforcement mechanism.

python
async def block_destructive(input_data, tool_use_id, context):
cmd = input_data.get("tool_input", {}).get("command", "")
if any(bad in cmd for bad in ("rm -rf", "git push --force", "drop table")):
return {"decision": "block", "reason": "destructive command"}
return {}
options = ClaudeAgentOptions(
hooks={"PreToolUse": [{"matcher": "Bash", "hooks": [block_destructive]}]})

Subagents

Define delegated agents with isolated context, their own tools and model:

python
options = ClaudeAgentOptions(
agents={
"security-reviewer": {
"description": "Reviews diffs for injection, secrets, authz. Use after auth edits.",
"prompt": "Report findings only: file:line, severity, fix. Do not edit.",
"tools": ["Read", "Grep", "Glob"],
"model": "claude-sonnet-5",
}
})

Subagents receive only what the parent passes; never assume inheritance. Use a cheaper model for mechanical delegated work.

MCP servers

python
options = ClaudeAgentOptions(
mcp_servers={
"orders": {"command": "python", "args": ["orders_server.py"]}, # stdio
"linear": {"type": "http", "url": "https://mcp.linear.app/mcp"}, # remote
},
allowed_tools=["mcp__orders__get_order", "mcp__linear__list_issues"])

MCP tools are namespaced mcp__<server>__<tool>; allowlist them explicitly.

Streaming events

query() yields a stream of typed messages mirroring the SSE flow: a system init message, assistant messages (text/tool_use blocks), user messages (tool_result), and a final result message carrying stop_reason, usage and cost.

python
async for message in query(prompt="…", options=options):
if message.type == "assistant":
for block in message.content:
if block.type == "text":
print(block.text, end="")
elif message.type == "result":
print(message.stop_reason, message.usage, message.total_cost_usd)

Branch on the result message’s stop_reason, not on parsing assistant text.

Headless patterns

PatternHow
One-shot batch jobquery() once, read the result message, exit non-zero on failure
CI gatepermission_mode="plan" or a tight allowlist; parse result for pass/fail
Fan-out over unitsSpawn N SDK sessions (one per file/module), each in its own worktree, aggregate
Long-running servicePersist the memory tool store; use context editing to bound tokens
Cost controlCheaper model for subagents; cap max_turns; log total_cost_usd
python
# CI gate sketch
import sys
async def review():
async for m in query(prompt="Review the diff for security issues; output PASS or FAIL.",
options=ClaudeAgentOptions(permission_mode="plan",
allowed_tools=["Read", "Grep", "Bash(git diff:*)"])):
if m.type == "result":
sys.exit(0 if "PASS" in (m.result or "") else 1)

Agent SDK vs Managed Agents vs Messages API loop

Messages API loopAgent SDKManaged Agents
You write the loop✓Harness providedNo (hosted)
File tools / Claude Code machineryNo✓✓
Hooks / subagents / permission modesBuild yourself✓Configured
Runtime/network/data controlFullFullLimited
Ops burdenLow (just API)You host harnessMinimal
Choose whenSimple tool loopNeed the harness, self-hostedLeast ops

Common misconceptions

MisconceptionRealityWhy it matters on the exam
“Agent SDK is Anthropic-hosted”You host it; Managed Agents is hostedHosting-confusion distractor
“max_turns is how the loop stops”It is a safety cap; stop_reason stops the loopIteration-cap anti-pattern
“bypassPermissions is fine for convenience”Sandboxed CI onlyExcessive-agency distractor
“Subagents see the parent’s whole context”Only what is passed explicitlySilent-context-loss distractor
“Enforce rules in the system prompt”Use hooks / permission callbacksPrompt-as-enforcement anti-pattern
“Load every MCP tool”Allowlist namespaced tools; keep it smallToo-many-tools anti-pattern

Scenario walkthrough

A platform team wants a self-hosted CI agent that reviews PRs for security issues, must never edit files, must run inside their VPC (data locality), and must fail the build on a finding.

  1. Self-hosted + VPC data locality → Agent SDK, not Managed Agents (which is hosted). A Messages API loop would mean rebuilding the harness.
  2. Must never edit → permission_mode="plan" (read-only) and a tool allowlist without Edit/Write.
  3. Security focus → a security-reviewer subagent on claude-sonnet-5 (cheaper, sufficient), tools Read, Grep, Bash(git diff:*).
  4. Enforcement → a PreToolUse deny for any write, deterministic, not a prompt sentence.
  5. Fail the build → parse the result message; sys.exit(1) on a finding.
  6. Cost/loop safety → cap max_turns; log total_cost_usd; still branch on stop_reason.

Rejected alternatives: Managed Agents (violates data locality), bypassPermissions (excessive agency), enforcing read-only via the system prompt (prompt-as-enforcement), and using a turn cap as the completion signal (iteration-cap anti-pattern).

Key takeaways

  • The Agent SDK is self-hosted; Managed Agents is Anthropic-hosted; pick on runtime/data-control vs ops-burden.
  • Loop termination is stop_reason; max_turns is only a runaway guard.
  • Enforce rules with hooks / can_use_tool, never with prompt text.
  • Subagents get only explicitly-passed context; give them their own (often cheaper) model and a tight tool allowlist.
  • MCP tools are mcp__server__tool; allowlist them and keep the set small.
  • bypassPermissions is for sandboxed CI only.

Last updated Sep 18, 2026