Claude Agent SDK Development

中级 Intermediate 参考型 Reference ⚡ Claude Code 专属 ⚡ Claude Code Optimized
12 min read · 577 lines

Claude Agent SDK bilingual reference: query(), hooks, MCP, subagents, structured outputs, sandbox

Claude Agent SDK Development

Overview

The Claude Agent SDK enables building autonomous AI agents that leverage Claude Code's full capabilities -- file operations, terminal commands, web search, and more -- through a programmatic API. Both TypeScript and Python SDKs wrap the Claude Code CLI, providing language-native interfaces for query execution, tool definition, hook registration, and multi-agent orchestration.

Key distinction: The SDK is not an LLM API client. It controls a Claude Code agent process, giving your code access to the same tools and capabilities available in the interactive CLI.

When to Use

  • Building custom AI agents that need file system access, code execution, or shell commands
  • Creating automated workflows with Claude Code as the reasoning engine
  • Integrating Claude agents into CI/CD pipelines or backend services
  • Defining custom tools (MCP servers) that extend agent capabilities
  • Orchestrating multi-agent systems with subagent delegation
  • Implementing structured output validation for agent responses

SDK Versions and Resources

TypeScript Python
Version v0.2.50 v0.1.39
Package @anthropic-ai/claude-agent-sdk claude-agent-sdk (PyPI)
Install npm install @anthropic-ai/claude-agent-sdk pip install claude-agent-sdk
Docs TypeScript SDK Python SDK
Repo claude-agent-sdk-typescript claude-agent-sdk-python
Requires Node.js 18+ Python 3.10+

Migration note: Both SDKs were renamed from their previous identifiers. TypeScript was @anthropic-ai/claude-code; Python was claude-code-sdk. The ClaudeCodeOptions type is now ClaudeAgentOptions.

Core API

query()

The primary entry point. Sends a prompt to a Claude Code agent and returns a stream of messages.

TypeScript:

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

const q = query({
  prompt: "Analyze the authentication module and suggest improvements",
  options: {
    systemPrompt: "You are a senior security engineer",
    permissionMode: "acceptEdits",
    cwd: "/home/user/project",
    allowedTools: ["Read", "Grep", "Glob"],
    maxTurns: 20,
  },
});

for await (const message of q) {
  if (message.type === "assistant") {
    console.log(message.content);
  }
}

Python:

import asyncio
from claude_agent_sdk import query, ClaudeAgentOptions

async def main():
    options = ClaudeAgentOptions(
        system_prompt="You are a senior security engineer",
        permission_mode="acceptEdits",
        cwd="/home/user/project",
        allowed_tools=["Read", "Grep", "Glob"],
        max_turns=20,
    )
    async for message in query(prompt="Analyze the auth module", options=options):
        print(message)

asyncio.run(main())

tool() / @tool

Define custom tools with type-safe schemas that agents can invoke.

TypeScript:

import { tool } from "@anthropic-ai/claude-agent-sdk";
import { z } from "zod";

const fetchPrTool = tool(
  "fetch_pr",
  "Fetch pull request details from GitHub",
  { prNumber: z.number().describe("PR number to fetch") },
  async (args) => {
    const pr = await github.getPR(args.prNumber);
    return { content: [{ type: "text", text: JSON.stringify(pr) }] };
  }
);

Python:

from claude_agent_sdk import tool

@tool("fetch_pr", "Fetch pull request details from GitHub", {
    "type": "object",
    "properties": {
        "pr_number": {"type": "integer", "description": "PR number to fetch"}
    },
    "required": ["pr_number"]
})
async def fetch_pr(args):
    pr = await github.get_pr(args["pr_number"])
    return {"content": [{"type": "text", "text": json.dumps(pr)}]}

createSdkMcpServer() / create_sdk_mcp_server()

Create an in-process MCP server to expose custom tools to the agent.

TypeScript:

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

const server = createSdkMcpServer({
  name: "project-tools",
  version: "1.0.0",
  tools: [fetchPrTool, runTestsTool, deployTool],
});

// Pass to query options
const q = query({
  prompt: "Run the test suite",
  options: { mcpServers: [server] },
});

Python:

from claude_agent_sdk import create_sdk_mcp_server

server = create_sdk_mcp_server(
    name="project-tools",
    version="1.0.0",
    tools=[fetch_pr, run_tests, deploy],
)

async for msg in query(
    prompt="Run the test suite",
    options=ClaudeAgentOptions(mcp_servers=[server]),
):
    print(msg)

Cross-Language Naming Map

Concept TypeScript Python
One-shot query query(options) query(options)
Stateful client N/A (query manages state) ClaudeSDKClient
Options type Options interface ClaudeAgentOptions dataclass
Tool definition tool(name, schema, handler) @tool(name, desc, schema) decorator
MCP server factory createSdkMcpServer() create_sdk_mcp_server()
Permission callback canUseTool can_use_tool
Permission mode permissionMode: "..." permission_mode="..."
Hook registration hooks: { PreToolUse: [...] } hooks={"PreToolUse": [...]}
System prompt systemPrompt system_prompt
Max turns maxTurns max_turns
Allowed tools allowedTools allowed_tools
MCP servers mcpServers mcp_servers
Subagent def AgentDefinition AgentDefinition dataclass

Shared Concepts

Both SDKs wrap the Claude Code CLI and share these architectural concepts.

Hooks

Hooks are callbacks that fire at specific points in the agent lifecycle. Use them to observe, modify, or block agent actions.

Hook Event When It Fires Common Use
PreToolUse Before any tool invocation Validate parameters, block dangerous operations
PostToolUse After a tool completes Log results, transform outputs
Stop When agent wants to stop Validate completion, force continuation
SubagentStop When a subagent finishes Aggregate results, trigger follow-up
Notification Agent sends a status update Progress tracking, UI updates

Hook return values control flow:

Return Effect
undefined / None Allow the action to proceed normally
{ decision: "block", message: "..." } Block the tool use; inject a message instead
{ decision: "approve" } Approve without user confirmation prompt
{ decision: "modify", toolInput: {...} } Modify the tool's input before execution

Permissions

Control what the agent can do without user confirmation.

Mode Behavior Use Case
default Prompts user for dangerous operations Interactive development
acceptEdits Auto-approves file edits; prompts for shell commands Automated code generation
plan Agent can only read and plan; no writes or executions Safe analysis and review
bypassPermissions All operations approved automatically CI/CD pipelines, sandboxed environments

Fine-grained control with canUseTool / can_use_tool:

// TypeScript
const q = query({
  prompt: "...",
  options: {
    canUseTool: (toolName, toolInput) => {
      if (toolName === "Bash" && toolInput.command.includes("rm")) {
        return { decision: "block", message: "Deletion not allowed" };
      }
      return { decision: "approve" };
    },
  },
});
# Python
def can_use_tool(tool_name: str, tool_input: dict) -> dict:
    if tool_name == "Bash" and "rm" in tool_input.get("command", ""):
        return {"decision": "block", "message": "Deletion not allowed"}
    return {"decision": "approve"}

options = ClaudeAgentOptions(can_use_tool=can_use_tool)

MCP Server Configuration

Agents can connect to MCP (Model Context Protocol) servers via multiple transports.

Transport Description Configuration
stdio Local process communicating via stdin/stdout { type: "stdio", command: "node", args: ["server.js"] }
HTTP Remote server over HTTP { type: "http", url: "https://mcp.example.com" }
SSE Server-Sent Events stream { type: "sse", url: "https://mcp.example.com/sse" }
SDK (in-process) Created via createSdkMcpServer() Fastest; no IPC overhead; tools defined in your code

Subagents

Delegate tasks to child agents with their own scoped tools and permissions.

TypeScript:

const reviewAgent = {
  name: "code-reviewer",
  description: "Reviews code for quality and security issues",
  systemPrompt: "You are a code review specialist...",
  allowedTools: ["Read", "Grep", "Glob"],
  permissionMode: "plan",
};

const q = query({
  prompt: "Review and then fix the auth module",
  options: {
    agents: [reviewAgent],
    // Parent agent can delegate to code-reviewer
  },
});

Python:

from claude_agent_sdk import AgentDefinition

review_agent = AgentDefinition(
    name="code-reviewer",
    description="Reviews code for quality and security issues",
    system_prompt="You are a code review specialist...",
    allowed_tools=["Read", "Grep", "Glob"],
    permission_mode="plan",
)

async for msg in query(
    prompt="Review and then fix the auth module",
    options=ClaudeAgentOptions(agents=[review_agent]),
):
    print(msg)

Design pattern: Give the parent agent broad permissions and subagents narrow, task-specific permissions. The parent orchestrates; subagents execute.

Structured Outputs

Validate agent output against a JSON Schema to ensure it returns data in the expected format.

TypeScript:

const q = query({
  prompt: "Analyze this codebase and return a summary",
  options: {
    outputSchema: {
      type: "object",
      properties: {
        summary: { type: "string" },
        fileCount: { type: "number" },
        issues: {
          type: "array",
          items: {
            type: "object",
            properties: {
              severity: { type: "string", enum: ["critical", "high", "medium", "low"] },
              description: { type: "string" },
              file: { type: "string" },
            },
            required: ["severity", "description", "file"],
          },
        },
      },
      required: ["summary", "fileCount", "issues"],
    },
  },
});

If the agent's output does not conform to the schema, the SDK will re-prompt the agent to fix the format.

Sandbox

Run agents in isolated container environments for security.

Option Description
Docker Local container isolation; agent runs inside a Docker container
Kubernetes Remote container orchestration; for production workloads
None Direct host execution (default); agent has full access to the host filesystem

Configure sandbox in options:

const q = query({
  prompt: "Run the test suite",
  options: {
    sandbox: { type: "docker", image: "node:20-slim" },
  },
});

Sessions

Capture, resume, and fork conversation state across multiple interactions.

Operation Description
Capture Save the current session state (all messages, tool outputs)
Resume Continue a previously captured session
Fork Create a new session branching from an existing one

TypeScript:

const q = query({ prompt: "Start analyzing the project", options: { sessionId: "analysis-001" } });
for await (const msg of q) { /* process */ }

// Later: resume the same session
const q2 = query({ prompt: "Now focus on the tests", options: { sessionId: "analysis-001" } });

Python (ClaudeSDKClient):

client = ClaudeSDKClient(options=ClaudeAgentOptions())
await client.connect(prompt="Start analyzing the project")
# Process messages...

# Resume later
await client.query(prompt="Now focus on the tests", session_id="default")

TypeScript Quick Start

npm install @anthropic-ai/claude-agent-sdk
import { query, tool, createSdkMcpServer } from "@anthropic-ai/claude-agent-sdk";
import { z } from "zod";

// 1. Define a custom tool
const analyzeTool = tool(
  "analyze_complexity",
  "Analyze code complexity for a given file",
  { filePath: z.string().describe("Path to the file") },
  async (args) => {
    // Your analysis logic here
    return { content: [{ type: "text", text: `Complexity: moderate` }] };
  }
);

// 2. Create an MCP server
const server = createSdkMcpServer({
  name: "my-tools",
  tools: [analyzeTool],
});

// 3. Run the agent
async function main() {
  const q = query({
    prompt: "Analyze the complexity of src/auth/middleware.ts",
    options: {
      systemPrompt: "You are a code analysis expert",
      permissionMode: "plan",
      mcpServers: [server],
    },
  });

  for await (const message of q) {
    if (message.type === "assistant") {
      console.log(message.content);
    }
  }
}

main();

Streaming Input

The prompt parameter accepts AsyncIterable<SDKUserMessage> for real-time multi-message input:

async function* promptStream(): AsyncIterable<SDKUserMessage> {
  yield { type: "user", content: [{ type: "text", text: "First, read the config." }] };
  // Yield additional messages as they become available
  yield { type: "user", content: [{ type: "text", text: "Now analyze the routes." }] };
}

const q = query({ prompt: promptStream() });

Python Quick Start

pip install claude-agent-sdk
import asyncio
from claude_agent_sdk import query, ClaudeAgentOptions, tool, create_sdk_mcp_server

# 1. Define a custom tool
@tool("analyze_complexity", "Analyze code complexity for a given file", {
    "type": "object",
    "properties": {"file_path": {"type": "string", "description": "Path to the file"}},
    "required": ["file_path"],
})
async def analyze_complexity(args):
    return {"content": [{"type": "text", "text": "Complexity: moderate"}]}

# 2. Create an MCP server
server = create_sdk_mcp_server(name="my-tools", tools=[analyze_complexity])

# 3. Run the agent
async def main():
    options = ClaudeAgentOptions(
        system_prompt="You are a code analysis expert",
        permission_mode="plan",
        mcp_servers=[server],
    )
    async for message in query(prompt="Analyze src/auth/middleware.ts", options=options):
        print(message)

asyncio.run(main())

Stateful Client (Python-only)

Python offers ClaudeSDKClient for maintaining session state across multiple exchanges:

from claude_agent_sdk import ClaudeSDKClient, ClaudeAgentOptions

async def main():
    client = ClaudeSDKClient(options=ClaudeAgentOptions(
        system_prompt="You are a project analyst",
        permission_mode="plan",
    ))

    # First interaction
    await client.connect(prompt="Summarize the project structure")
    async for msg in client.messages():
        print(msg)

    # Follow-up (same session, retains context)
    await client.query(prompt="Now identify the most complex module")
    async for msg in client.messages():
        print(msg)

Breaking Changes (v0.1.0)

Three breaking changes apply to both SDKs:

Change Before After Migration
No default system prompt SDK loaded full Claude Code prompt Minimal prompt only Add systemPrompt: { type: 'preset', preset: 'claude_code' } for old behavior
No filesystem settings SDK loaded CLAUDE.md automatically settingSources defaults to [] Add settingSources: ['project'] to load CLAUDE.md
Options type renamed ClaudeCodeOptions ClaudeAgentOptions Search and replace in your codebase

Common Patterns

CI/CD Integration

const q = query({
  prompt: "Run all tests and report failures",
  options: {
    permissionMode: "bypassPermissions",
    maxTurns: 50,
    cwd: process.env.WORKSPACE,
    sandbox: { type: "docker", image: "node:20" },
    outputSchema: {
      type: "object",
      properties: {
        passed: { type: "boolean" },
        failures: { type: "array", items: { type: "string" } },
      },
      required: ["passed", "failures"],
    },
  },
});

Multi-Agent Pipeline

const researcher = { name: "researcher", description: "...", allowedTools: ["Read", "Grep", "WebSearch"] };
const implementer = { name: "implementer", description: "...", allowedTools: ["Read", "Write", "Edit", "Bash"] };
const reviewer = { name: "reviewer", description: "...", allowedTools: ["Read", "Grep"], permissionMode: "plan" };

const q = query({
  prompt: "Research best practices for rate limiting, implement it, then review the code",
  options: {
    agents: [researcher, implementer, reviewer],
    permissionMode: "acceptEdits",
  },
});

Guarded Execution with Hooks

const q = query({
  prompt: "Refactor the database module",
  options: {
    hooks: {
      PreToolUse: [
        (toolName, toolInput) => {
          // Block any database migration commands
          if (toolName === "Bash" && toolInput.command.includes("migrate")) {
            return { decision: "block", message: "Migrations require manual approval" };
          }
          return undefined; // Allow everything else
        },
      ],
      PostToolUse: [
        (toolName, toolInput, toolOutput) => {
          console.log(`[LOG] ${toolName}: ${JSON.stringify(toolInput).slice(0, 100)}`);
          return undefined;
        },
      ],
    },
  },
});

相关技能 Related Skills