Языки: EN RU

Claude Agent SDK: Building Agents Programmatically

Everything you do in the terminal by hand — claude reads a file, finds a bug, fixes it, runs tests — can be programmed. The Claude Agent SDK is a TypeScript and Python library that provides the same agentic loop, the same tools, and the same memory model that underlie interactive Claude Code. The only difference is that instead of a terminal session, you write regular code.

Until recently, the library was called "Claude Code SDK" — worth keeping in mind if you come across older articles. The official name is now: Claude Agent SDK.

Important as of today. Starting June 15, 2026, Agent SDK usage on subscription plans (Pro/Max/Team/Enterprise) is billed via a separate monthly Agent SDK Credit, independent of the interactive usage limit. See the Anthropic support article for details.


Installation

# TypeScript (Node.js 18+)
npm install @anthropic-ai/claude-agent-sdk

# Python (3.10+)
pip install claude-agent-sdk

The TypeScript package ships with a native Claude Code binary for your platform — no separate CLI installation required. Python requires 3.10+: if you get No matching distribution found, check python3 --version.

Authentication: set the ANTHROPIC_API_KEY environment variable. The SDK also supports Amazon Bedrock (CLAUDE_CODE_USE_BEDROCK=1), Google Vertex AI (CLAUDE_CODE_USE_VERTEX=1), and Microsoft Azure Foundry (CLAUDE_CODE_USE_FOUNDRY=1) — for enterprise environments where data region control is required.


query(): The Heart of the SDK

The single entry point is the query() function. It starts the agentic loop and returns an async iterator: each iteration yields one event from Claude's stream of work.

import asyncio
from claude_agent_sdk import query, ClaudeAgentOptions

async def main():
    async for message in query(
        prompt="Find and fix the division-by-zero bug in utils.py",
        options=ClaudeAgentOptions(
            allowed_tools=["Read", "Edit", "Glob"],
            permission_mode="acceptEdits",
        ),
    ):
        if hasattr(message, "result"):
            print(message.result)

asyncio.run(main())

The same in TypeScript:

import { query } from "@anthropic-ai/claude-agent-sdk";

for await (const message of query({
  prompt: "Find and fix the division-by-zero bug in utils.ts",
  options: {
    allowedTools: ["Read", "Edit", "Glob"],
    permissionMode: "acceptEdits"
  }
})) {
  if ("result" in message) console.log(message.result);
}

Note the naming difference: Python uses snake_case (allowed_tools, permission_mode), TypeScript uses camelCase (allowedTools, permissionMode). The concepts are identical.

The async for loop runs while Claude thinks, invokes tools, and observes results — and decides on its own when the task is complete. You simply read the stream.

sequenceDiagram participant App as Your code participant SDK as Agent SDK participant Claude as Claude participant Tools as Tools (Read/Edit/Bash...) App->>SDK: query(prompt, options) SDK->>Claude: prompt + tool list Claude-->>App: SystemMessage (session_id) loop Agent loop Claude->>Claude: reasoning Claude-->>App: AssistantMessage (text) Claude->>Tools: tool call Tools-->>Claude: tool result Claude-->>App: AssistantMessage (tool call) end Claude-->>App: ResultMessage (success/error) SDK-->>App: iterator closed
sequenceDiagram
    participant App as Your code
    participant SDK as Agent SDK
    participant Claude as Claude
    participant Tools as Tools (Read/Edit/Bash...)

    App->>SDK: query(prompt, options)
    SDK->>Claude: prompt + tool list
    Claude-->>App: SystemMessage (session_id)
    loop Agent loop
        Claude->>Claude: reasoning
        Claude-->>App: AssistantMessage (text)
        Claude->>Tools: tool call
        Tools-->>Claude: tool result
        Claude-->>App: AssistantMessage (tool call)
    end
    Claude-->>App: ResultMessage (success/error)
    SDK-->>App: iterator closed
The query() stream: each step of the agent loop is a separate message in the async iterator

Быстрое повторение
query() в Agent SDK возвращает асинхронный _____—объект, который итерирует события агентного цикла.

Message Types in the Stream

Without filtering, you receive a "raw" stream. Three key types:

TypeWhenContents
SystemMessage (subtype init)Firstsession_id — needed to resume a session
AssistantMessageContinuouslycontent — Claude's reasoning and tool calls
ResultMessageLastresult — the outcome; subtypesuccess or error

For production code, filter for ResultMessage only; for debugging, print everything:

from claude_agent_sdk import AssistantMessage, ResultMessage, SystemMessage

async for message in query(prompt="...", options=opts):
    if isinstance(message, SystemMessage) and message.subtype == "init":
        session_id = message.data["session_id"]   # save for resume
    elif isinstance(message, AssistantMessage):
        for block in message.content:
            if hasattr(block, "text"):
                print("Claude:", block.text)        # reasoning
            elif hasattr(block, "name"):
                print("Tool:", block.name)          # tool call
    elif isinstance(message, ResultMessage):
        print("Done:", message.subtype)             # success / error
Проверь себя
You call query() and see a stream of messages. Where exactly is the session_id located if you need it for a resume? Think before reading the answer.

Tools and Permission Modes

allowed_tools is the list of tools that the SDK approves automatically without prompting. All Claude Code tools are available:

ToolWhat it does
Read, Write, EditFile operations
BashTerminal commands, git, npm
Glob, GrepFile and content search
WebSearch, WebFetchWeb search
MonitorWatch a background process
AgentLaunch subagents (must be explicitly included)
AskUserQuestionAsk the user a clarifying question

Permission modes define what happens with tools not in allowed_tools:

ModeBehaviorWhen to use
acceptEditsAuto-approves file operations, prompts for everything elseTrusted dev scenarios
dontAskBlocks everything not in allowed_toolsHeadless agents with strict control
auto (TS only)A classifier model decides autonomouslyAutonomous agents with guardrails
bypassPermissionsExecutes everything without promptingIsolated CI environments, sandboxes
defaultRequires a canUseTool callbackCustom flows with manual approval
Проверь себя
You created an agent with allowed_tools=["Read", "Grep"] and permission_mode="dontAsk". Claude attempts to run a Bash command. What will happen?

Быстрое повторение
Что происходит с инструментом, добавленным в allowed_tools?

Hooks: Programmatic Callbacks

In settings.json, hooks are shell commands. In the SDK, hooks are functions defined directly in your code. The concept is the same; the implementation is different.

from claude_agent_sdk import query, ClaudeAgentOptions, HookMatcher
from datetime import datetime

async def audit_file_change(input_data, tool_use_id, context):
    path = input_data.get("tool_input", {}).get("file_path", "unknown")
    with open("audit.log", "a") as f:
        f.write(f"{datetime.now()}: modified {path}\n")
    return {}  # empty dict = allow; {"decision": "block"} = deny

async for message in query(
    prompt="Refactor utils.py",
    options=ClaudeAgentOptions(
        permission_mode="acceptEdits",
        hooks={
            "PostToolUse": [
                HookMatcher(matcher="Edit|Write", hooks=[audit_file_change])
            ]
        },
    ),
):
    ...

Available events: PreToolUse, PostToolUse, Stop, SessionStart, SessionEnd, UserPromptSubmit. A hook returns {} (continue) or {"decision": "block", "reason": "..."} (block). This gives you full programmatic control over the agent's lifecycle.


Быстрое повторение
Как в Agent SDK реализуются хуки — как функции или как shell-команды?

Subagents, Sessions, and MCP

Subagents are declared directly in options and launched via the Agent tool (which must be added to allowed_tools):

from claude_agent_sdk import AgentDefinition

options = ClaudeAgentOptions(
    allowed_tools=["Read", "Glob", "Grep", "Agent"],
    agents={
        "code-reviewer": AgentDefinition(
            description="Expert code reviewer",
            prompt="Analyze code quality and security. Be concise.",
            tools=["Read", "Glob", "Grep"],
        )
    },
)

Sessions preserve context across multiple query() calls. Capture the session_id from the first call and pass it as resume:

# The second call continues the first — "it" is understood without repeating context
async for message in query(
    prompt="Now find all callers of the function we just analyzed",
    options=ClaudeAgentOptions(resume=session_id),
):
    ...

MCP servers are connected via the mcp_servers option using the same structure as .mcp.json:

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

For more on MCP servers as a concept, see Model Context Protocol: Architecture and Fundamentals.


GitHub Action — A Wrapper Around the Agent SDK

If you understand the SDK, the GitHub Action becomes more transparent. anthropics/claude-code-action is officially described as "built on top of the Claude Agent SDK." The Action runs query() inside a GitHub runner, passing the event context (issue body, PR diff, a comment mentioning @claude) as the prompt.

# What the action effectively does under the hood:
for await (const message of query({
  prompt: buildPromptFromGitHubEvent(event),
  options: {
    allowedTools: parseAllowedTools(claude_args),
    permissionMode: "acceptEdits"
  }
})) { ... }

This explains why claude_args in the Action's YAML file accepts the same flags as the CLI: --max-turns, --model, --allowedTools. Under the hood, it's the same engine.

If the built-in Action isn't enough, you can use the SDK directly inside a runs-on: ubuntu-latest job and build any custom logic you need. For more on the Action itself, see GitHub Actions and Automated Code Review.


SDK, CLI, or Client SDK: What to Choose

The decision path is straightforward.

Agent SDK vs CLI: one engine, different interfaces. The CLI is for daily development at the terminal. The SDK is for CI/CD, custom applications, and automation. Many teams use both.

Agent SDK vs Anthropic Client SDK (Claude API and Anthropic SDK: Fundamentals): the key difference is who implements the tool loop:

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

# Agent SDK: Claude manages the loop itself
async for message in query(prompt="Fix the bug"):
    print(message)

The Client SDK gives you maximum control over every step and cost; the Agent SDK gives you development speed and a ready-made agentic infrastructure.

Agent SDK vs Managed Agents: the SDK runs the agentic loop in your process and on your infrastructure. Managed Agents (a hosted REST API) have Anthropic managing the sandbox and sessions — you only submit tasks. A typical path is: prototype with the SDK → migrate to Managed Agents for production when needed.


What Gets Loaded from the Filesystem

By default, the SDK picks up configuration from the launch directory, just as an interactive Claude Code session does:

PathWhat is loaded
CLAUDE.md / .claude/CLAUDE.mdProject memory, instructions
.claude/skills/*/SKILL.mdSkills
.claude/commands/*.mdCustom commands

To isolate the agent from the local environment (for example, in CI), use setting_sources=[] — the SDK will not read anything from the filesystem and will rely only on options provided in code.


See also

  • Claude API and Anthropic SDK: Fundamentals — the Messages API and Client SDK, which the Agent SDK is not but is often compared to
  • Tool Use, MCP, and Streaming in the API — how tool use works at the API level, for when you need full control
  • Prompt Caching, Batches, and Cost Optimization — how to reduce the cost of agentic requests
  • GitHub Actions and Automated Code Review — claude-code-action as a high-level wrapper around the SDK
  • Subagents and Context Isolation — the subagent concept, applicable in the SDK as well
  • Hooks — Lifecycle Events — hooks in settings.json, the interactive-session counterpart to SDK hooks
  • Model Context Protocol: Architecture and Fundamentals — MCP, connected via mcp_servers in SDK options
  • Headless Mode and Scripting via CLI — an alternative automation approach using claude -p

Источники

  1. Agent SDK overview — Claude Code Docs
  2. Quickstart — Claude Code Docs
  3. Claude Code GitHub Actions — Claude Code Docs