Bỏ qua để đến nội dung

Tham chiếu Python SDK

Bài viết được dịch tự động từ bài viết gốc, chưa được kiểm tra lại bởi con người. Chỉ những bài viết có dấu tick xanh cạnh tiêu đề là đã được kiểm tra.

Tham chiếu API đầy đủ cho Python Agent SDK, gồm mọi function, type, và class.

Cài package vào một virtual environment. Trên các bản cài Debian, Ubuntu, và Homebrew Python gần đây, chạy pip install thẳng vào system Python sẽ lỗi với error: externally-managed-environment.

Terminal window
python3 -m venv .venv
source .venv/bin/activate
pip install claude-agent-sdk

Để biết cách dùng uv, Windows PowerShell, và thiết lập API key, xem phần Setup trong Agent SDK quickstart.

Python SDK cung cấp hai cách để tương tác với Claude Code:

Featurequery()ClaudeSDKClient
SessionTạo session mới mặc địnhDùng lại cùng session
Hội thoạiMột lượt trao đổiNhiều lượt trao đổi trong cùng context
Kết nốiQuản lý tự độngKiểm soát thủ công
Streaming Input✅ Hỗ trợ✅ Hỗ trợ
Interrupts❌ Không hỗ trợ✅ Hỗ trợ
Hooks✅ Hỗ trợ✅ Hỗ trợ
Custom Tools✅ Hỗ trợ✅ Hỗ trợ
Continue ChatThủ công qua continue_conversation hoặc resume✅ Tự động
Use CaseTác vụ một lầnHội thoại liên tục

Phù hợp cho:

  • Câu hỏi một lần không cần lịch sử hội thoại
  • Tác vụ độc lập không cần context từ lượt trao đổi trước
  • Script automation đơn giản
  • Khi bạn muốn bắt đầu mới hoàn toàn mỗi lần

Khi nào dùng ClaudeSDKClient (hội thoại liên tục)

Phần tiêu đề “Khi nào dùng ClaudeSDKClient (hội thoại liên tục)”

Phù hợp cho:

  • Tiếp tục hội thoại - Khi bạn cần Claude nhớ context
  • Câu hỏi follow-up - Xây dựng dựa trên phản hồi trước
  • Ứng dụng tương tác - Giao diện chat, REPL
  • Logic dựa trên phản hồi - Khi hành động tiếp theo phụ thuộc vào phản hồi của Claude
  • Kiểm soát session - Quản lý vòng đời hội thoại tường minh

Tạo một session mới cho mỗi lần tương tác với Claude Code theo mặc định. Trả về một async iterator yield ra message khi chúng đến. Mỗi lần gọi query() bắt đầu mới hoàn toàn, không nhớ tương tác trước đó trừ khi bạn truyền continue_conversation=True hoặc resume trong ClaudeAgentOptions. Xem thêm Sessions.

async def query(
*,
prompt: str | AsyncIterable[dict[str, Any]],
options: ClaudeAgentOptions | None = None,
transport: Transport | None = None
) -> AsyncIterator[Message]
ParameterTypeDescription
promptstr | AsyncIterable[dict]Prompt đầu vào dưới dạng string hoặc async iterable cho streaming mode
optionsClaudeAgentOptions | NoneĐối tượng cấu hình tuỳ chọn (mặc định ClaudeAgentOptions() nếu None)
transportTransport | NoneTransport tuỳ biến tuỳ chọn để giao tiếp với process CLI

Trả về một AsyncIterator[Message] yield ra các message từ hội thoại.

import asyncio
from claude_agent_sdk import query, ClaudeAgentOptions
async def main():
options = ClaudeAgentOptions(
system_prompt="You are an expert Python developer",
permission_mode="acceptEdits",
)
async for message in query(prompt="Create a Python web server", options=options):
print(message)
asyncio.run(main())

Decorator để định nghĩa MCP tool với type safety.

def tool(
name: str,
description: str,
input_schema: type | dict[str, Any],
annotations: ToolAnnotations | None = None
) -> Callable[[Callable[[Any], Awaitable[dict[str, Any]]]], SdkMcpTool[Any]]
ParameterTypeDescription
namestrĐịnh danh duy nhất cho tool
descriptionstrMô tả dễ đọc về việc tool đó làm gì
input_schematype | dict[str, Any]Schema định nghĩa tham số đầu vào của tool (xem bên dưới)
annotationsToolAnnotations | NoneAnnotation MCP tool tuỳ chọn, cung cấp gợi ý hành vi cho client
  1. Ánh xạ type đơn giản (khuyến nghị):

    {"text": str, "count": int, "enabled": bool}
  2. Định dạng JSON Schema (để validate phức tạp hơn):

    {
    "type": "object",
    "properties": {
    "text": {"type": "string"},
    "count": {"type": "integer", "minimum": 0},
    },
    "required": ["text"],
    }

Một hàm decorator bọc phần triển khai tool và trả về một instance SdkMcpTool.

from claude_agent_sdk import tool
from typing import Any
@tool("greet", "Greet a user", {"name": str})
async def greet(args: dict[str, Any]) -> dict[str, Any]:
return {"content": [{"type": "text", "text": f"Hello, {args['name']}!"}]}

Re-export từ mcp.types (cũng khả dụng qua from claude_agent_sdk import ToolAnnotations). Mọi field đều là gợi ý tuỳ chọn; client không nên dựa vào chúng cho quyết định bảo mật.

FieldTypeDefaultDescription
titlestr | NoneNoneTiêu đề dễ đọc cho tool
readOnlyHintbool | NoneFalseNếu True, tool không thay đổi môi trường của nó
destructiveHintbool | NoneTrueNếu True, tool có thể thực hiện thay đổi phá huỷ (chỉ có ý nghĩa khi readOnlyHintFalse)
idempotentHintbool | NoneFalseNếu True, gọi lặp lại với cùng tham số không có thêm tác dụng (chỉ có ý nghĩa khi readOnlyHintFalse)
openWorldHintbool | NoneTrueNếu True, tool tương tác với thực thể bên ngoài (ví dụ, web search). Nếu False, domain của tool khép kín (ví dụ, một memory tool)
from claude_agent_sdk import tool, ToolAnnotations
from typing import Any
@tool(
"search",
"Search the web",
{"query": str},
annotations=ToolAnnotations(readOnlyHint=True, openWorldHint=True),
)
async def search(args: dict[str, Any]) -> dict[str, Any]:
return {"content": [{"type": "text", "text": f"Results for: {args['query']}"}]}

Tạo một MCP server chạy in-process bên trong ứng dụng Python của bạn.

def create_sdk_mcp_server(
name: str,
version: str = "1.0.0",
tools: list[SdkMcpTool[Any]] | None = None
) -> McpSdkServerConfig
ParameterTypeDefaultDescription
namestr-Định danh duy nhất cho server
versionstr"1.0.0"Chuỗi phiên bản server
toolslist[SdkMcpTool[Any]] | NoneNoneDanh sách tool function tạo bằng decorator @tool

Trả về một đối tượng McpSdkServerConfig có thể truyền vào ClaudeAgentOptions.mcp_servers.

from claude_agent_sdk import tool, create_sdk_mcp_server, ClaudeAgentOptions
@tool("add", "Add two numbers", {"a": float, "b": float})
async def add(args):
return {"content": [{"type": "text", "text": f"Sum: {args['a'] + args['b']}"}]}
@tool("multiply", "Multiply two numbers", {"a": float, "b": float})
async def multiply(args):
return {"content": [{"type": "text", "text": f"Product: {args['a'] * args['b']}"}]}
calculator = create_sdk_mcp_server(
name="calculator",
version="2.0.0",
tools=[add, multiply], # Pass decorated functions
)
# Use with Claude
options = ClaudeAgentOptions(
mcp_servers={"calc": calculator},
allowed_tools=["mcp__calc__add", "mcp__calc__multiply"],
)

Liệt kê session đã qua kèm metadata. Lọc theo thư mục project hoặc liệt kê session trên toàn bộ project. Đồng bộ; trả về ngay lập tức.

def list_sessions(
directory: str | None = None,
limit: int | None = None,
offset: int = 0,
include_worktrees: bool = True
) -> list[SDKSessionInfo]
ParameterTypeDefaultDescription
directorystr | NoneNoneThư mục để liệt kê session. Khi bỏ trống, trả về session trên toàn bộ project
limitint | NoneNoneSố session tối đa trả về
offsetint0Số session bỏ qua từ đầu kết quả đã sắp xếp. Dùng cùng limit để phân trang
include_worktreesboolTrueKhi directory nằm trong một git repository, gồm cả session từ mọi worktree path
PropertyTypeDescription
session_idstrĐịnh danh session duy nhất
summarystrTiêu đề hiển thị: tiêu đề tuỳ chỉnh, tóm tắt tự sinh, hoặc prompt đầu tiên
last_modifiedintThời điểm sửa đổi cuối, tính bằng milliseconds từ epoch
file_sizeint | NoneKích thước file session, tính bằng byte (None với backend lưu trữ từ xa)
custom_titlestr | NoneTiêu đề session do người dùng đặt
first_promptstr | NonePrompt người dùng có ý nghĩa đầu tiên trong session
git_branchstr | NoneGit branch tại thời điểm kết thúc session
cwdstr | NoneThư mục làm việc của session
tagstr | NoneTag session do người dùng đặt (xem tag_session())
created_atint | NoneThời điểm tạo session, tính bằng milliseconds từ epoch

In ra 10 session gần nhất của một project. Kết quả được sắp theo last_modified giảm dần, nên phần tử đầu tiên là mới nhất. Bỏ directory để tìm trên toàn bộ project.

from claude_agent_sdk import list_sessions
for session in list_sessions(directory="/path/to/project", limit=10):
print(f"{session.summary} ({session.session_id})")

Lấy message từ một session đã qua. Đồng bộ; trả về ngay lập tức.

def get_session_messages(
session_id: str,
directory: str | None = None,
limit: int | None = None,
offset: int = 0
) -> list[SessionMessage]
ParameterTypeDefaultDescription
session_idstrrequiredSession ID cần lấy message
directorystr | NoneNoneThư mục project để tìm. Khi bỏ trống, tìm trên toàn bộ project
limitint | NoneNoneSố message tối đa trả về
offsetint0Số message bỏ qua từ đầu
PropertyTypeDescription
typeLiteral["user", "assistant"]Vai trò message
uuidstrĐịnh danh message duy nhất
session_idstrĐịnh danh session
messageAnyNội dung message thô
parent_tool_use_idNoneDự phòng cho tương lai
from claude_agent_sdk import list_sessions, get_session_messages
sessions = list_sessions(limit=1)
if sessions:
messages = get_session_messages(sessions[0].session_id)
for msg in messages:
print(f"[{msg.type}] {msg.uuid}")

Đọc metadata của một session theo ID mà không quét toàn bộ thư mục project. Đồng bộ; trả về ngay lập tức.

def get_session_info(
session_id: str,
directory: str | None = None,
) -> SDKSessionInfo | None
ParameterTypeDefaultDescription
session_idstrrequiredUUID của session cần tra cứu
directorystr | NoneNoneĐường dẫn thư mục project. Khi bỏ trống, tìm trên mọi thư mục project

Trả về SDKSessionInfo, hoặc None nếu không tìm thấy session.

Tra cứu metadata của một session mà không quét thư mục project. Hữu ích khi bạn đã có session ID từ lần chạy trước.

from claude_agent_sdk import get_session_info
info = get_session_info("550e8400-e29b-41d4-a716-446655440000")
if info:
print(f"{info.summary} (branch: {info.git_branch}, tag: {info.tag})")

Đổi tên session bằng cách thêm một entry custom-title. Gọi lặp lại vẫn an toàn; tiêu đề gần nhất sẽ thắng. Đồng bộ.

def rename_session(
session_id: str,
title: str,
directory: str | None = None,
) -> None
ParameterTypeDefaultDescription
session_idstrrequiredUUID của session cần đổi tên
titlestrrequiredTiêu đề mới. Phải khác rỗng sau khi strip whitespace
directorystr | NoneNoneĐường dẫn thư mục project. Khi bỏ trống, tìm trên mọi thư mục project

Raise ValueError nếu session_id không phải UUID hợp lệ hoặc title rỗng; FileNotFoundError nếu không tìm thấy session.

Đổi tên session gần nhất để dễ tìm lại sau này. Tiêu đề mới xuất hiện trong SDKSessionInfo.custom_title ở những lần đọc sau.

from claude_agent_sdk import list_sessions, rename_session
sessions = list_sessions(directory="/path/to/project", limit=1)
if sessions:
rename_session(sessions[0].session_id, "Refactor auth module")

Gắn tag cho một session. Truyền None để xoá tag. Gọi lặp lại vẫn an toàn; tag gần nhất sẽ thắng. Đồng bộ.

def tag_session(
session_id: str,
tag: str | None,
directory: str | None = None,
) -> None
ParameterTypeDefaultDescription
session_idstrrequiredUUID của session cần gắn tag
tagstr | NonerequiredChuỗi tag, hoặc None để xoá. Được unicode-sanitize trước khi lưu
directorystr | NoneNoneĐường dẫn thư mục project. Khi bỏ trống, tìm trên mọi thư mục project

Raise ValueError nếu session_id không phải UUID hợp lệ hoặc tag rỗng sau khi sanitize; FileNotFoundError nếu không tìm thấy session.

Gắn tag cho một session, sau đó lọc theo tag đó ở lần đọc sau. Truyền None để xoá tag đã có.

from claude_agent_sdk import list_sessions, tag_session
# Tag the most recent session
sessions = list_sessions(directory="/path/to/project", limit=1)
if sessions:
tag_session(sessions[0].session_id, "needs-review")
# Later: find all sessions with that tag
for session in list_sessions(directory="/path/to/project"):
if session.tag == "needs-review":
print(session.summary)

Duy trì một session hội thoại xuyên nhiều lượt trao đổi. Đây là phần tương đương phía Python với cách function query() của TypeScript SDK hoạt động nội bộ - nó tạo một đối tượng client có thể tiếp tục hội thoại.

  • Session liên tục: Duy trì context hội thoại xuyên nhiều lần gọi query()
  • Cùng hội thoại: Session giữ lại message trước đó
  • Hỗ trợ interrupt: Có thể dừng thực thi giữa chừng
  • Vòng đời tường minh: Bạn kiểm soát khi nào session bắt đầu và kết thúc
  • Luồng dựa trên phản hồi: Có thể phản ứng với phản hồi và gửi follow-up
  • Custom tools và hooks: Hỗ trợ custom tools (tạo bằng decorator @tool) và hooks
class ClaudeSDKClient:
def __init__(self, options: ClaudeAgentOptions | None = None, transport: Transport | None = None)
async def connect(self, prompt: str | AsyncIterable[dict] | None = None) -> None
async def query(self, prompt: str | AsyncIterable[dict], session_id: str = "default") -> None
async def receive_messages(self) -> AsyncIterator[Message]
async def receive_response(self) -> AsyncIterator[Message]
async def interrupt(self) -> None
async def set_permission_mode(self, mode: str) -> None
async def set_model(self, model: str | None = None) -> None
async def rewind_files(self, user_message_id: str) -> None
async def get_mcp_status(self) -> McpStatusResponse
async def reconnect_mcp_server(self, server_name: str) -> None
async def toggle_mcp_server(self, server_name: str, enabled: bool) -> None
async def stop_task(self, task_id: str) -> None
async def get_server_info(self) -> dict[str, Any] | None
async def disconnect(self) -> None
MethodDescription
__init__(options)Khởi tạo client với cấu hình tuỳ chọn
connect(prompt)Kết nối tới Claude với một prompt khởi tạo tuỳ chọn hoặc message stream
query(prompt, session_id)Gửi một request mới ở streaming mode
receive_messages()Nhận toàn bộ message từ Claude dưới dạng async iterator
receive_response()Nhận message cho tới và bao gồm một ResultMessage
interrupt()Gửi tín hiệu interrupt (chỉ hoạt động ở streaming mode)
set_permission_mode(mode)Đổi permission mode cho session hiện tại
set_model(model)Đổi model cho session hiện tại. Truyền None để reset về mặc định
rewind_files(user_message_id)Khôi phục file về trạng thái tại user message chỉ định. Cần enable_file_checkpointing=True. Xem File checkpointing
get_mcp_status()Lấy trạng thái của mọi MCP server đã cấu hình. Trả về McpStatusResponse
reconnect_mcp_server(server_name)Thử kết nối lại một MCP server đã lỗi hoặc bị ngắt kết nối
toggle_mcp_server(server_name, enabled)Bật hoặc tắt một MCP server giữa session. Tắt sẽ gỡ tool của nó
stop_task(task_id)Dừng một background task đang chạy. Một TaskNotificationMessage với status "stopped" sẽ theo sau trong message stream
get_server_info()Lấy thông tin server gồm session ID và capabilities
disconnect()Ngắt kết nối khỏi Claude

Client có thể dùng như một async context manager để tự động quản lý kết nối:

import asyncio
from claude_agent_sdk import ClaudeSDKClient
async def main():
async with ClaudeSDKClient() as client:
await client.query("Hello Claude")
async for message in client.receive_response():
print(message)
asyncio.run(main())

Quan trọng: Khi lặp qua message, tránh dùng break để thoát sớm vì có thể gây lỗi cleanup asyncio. Thay vào đó, để vòng lặp hoàn tất tự nhiên hoặc dùng flag để đánh dấu khi bạn đã tìm thấy thứ cần tìm.

import asyncio
from claude_agent_sdk import ClaudeSDKClient, AssistantMessage, TextBlock, ResultMessage
async def main():
async with ClaudeSDKClient() as client:
# First question
await client.query("What's the capital of France?")
# Process response
async for message in client.receive_response():
if isinstance(message, AssistantMessage):
for block in message.content:
if isinstance(block, TextBlock):
print(f"Claude: {block.text}")
# Follow-up question - the session retains the previous context
await client.query("What's the population of that city?")
async for message in client.receive_response():
if isinstance(message, AssistantMessage):
for block in message.content:
if isinstance(block, TextBlock):
print(f"Claude: {block.text}")
# Another follow-up - still in the same conversation
await client.query("What are some famous landmarks there?")
async for message in client.receive_response():
if isinstance(message, AssistantMessage):
for block in message.content:
if isinstance(block, TextBlock):
print(f"Claude: {block.text}")
asyncio.run(main())
import asyncio
from claude_agent_sdk import ClaudeSDKClient
async def message_stream():
"""Generate messages dynamically."""
yield {
"type": "user",
"message": {"role": "user", "content": "Analyze the following data:"},
}
await asyncio.sleep(0.5)
yield {
"type": "user",
"message": {"role": "user", "content": "Temperature: 25°C, Humidity: 60%"},
}
await asyncio.sleep(0.5)
yield {
"type": "user",
"message": {"role": "user", "content": "What patterns do you see?"},
}
async def main():
async with ClaudeSDKClient() as client:
# Stream input to Claude
await client.query(message_stream())
# Process response
async for message in client.receive_response():
print(message)
# Follow-up in same session
await client.query("Should we be concerned about these readings?")
async for message in client.receive_response():
print(message)
asyncio.run(main())
import asyncio
from claude_agent_sdk import ClaudeSDKClient, ClaudeAgentOptions, ResultMessage
async def interruptible_task():
options = ClaudeAgentOptions(allowed_tools=["Bash"], permission_mode="acceptEdits")
async with ClaudeSDKClient(options=options) as client:
# Start a long-running task
await client.query("Count from 1 to 100 slowly, using the bash sleep command")
# Let it run for a bit
await asyncio.sleep(2)
# Interrupt the task
await client.interrupt()
print("Task interrupted!")
# Drain the interrupted task's messages (including its ResultMessage)
async for message in client.receive_response():
if isinstance(message, ResultMessage):
print(f"Interrupted task: terminal_reason={message.terminal_reason!r}")
# terminal_reason is "aborted_streaming" or "aborted_tools"
# for interrupted turns
# Send a new command
await client.query("Just say hello instead")
# Now receive the new response
async for message in client.receive_response():
if isinstance(message, ResultMessage) and message.subtype == "success":
print(f"New result: {message.result}")
asyncio.run(interruptible_task())
import asyncio
from claude_agent_sdk import ClaudeSDKClient, ClaudeAgentOptions
from claude_agent_sdk.types import (
PermissionResultAllow,
PermissionResultDeny,
ToolPermissionContext,
)
async def custom_permission_handler(
tool_name: str, input_data: dict, context: ToolPermissionContext
) -> PermissionResultAllow | PermissionResultDeny:
"""Custom logic for tool permissions."""
# Block writes to system directories
if tool_name == "Write" and input_data.get("file_path", "").startswith("/system/"):
return PermissionResultDeny(
message="System directory write not allowed", interrupt=True
)
# Redirect sensitive file operations
if tool_name in ["Write", "Edit"] and "config" in input_data.get("file_path", ""):
safe_path = f"./sandbox/{input_data['file_path']}"
return PermissionResultAllow(
updated_input={**input_data, "file_path": safe_path}
)
# Allow everything else
return PermissionResultAllow(updated_input=input_data)
async def main():
# Don't also list the gated tools in allowed_tools: allow rules approve calls before can_use_tool runs
options = ClaudeAgentOptions(can_use_tool=custom_permission_handler)
async with ClaudeSDKClient(options=options) as client:
await client.query("Update the system config file")
async for message in client.receive_response():
# Will use sandbox path instead
print(message)
asyncio.run(main())

Định nghĩa cho một SDK MCP tool tạo bằng decorator @tool.

@dataclass
class SdkMcpTool(Generic[T]):
name: str
description: str
input_schema: type[T] | dict[str, Any]
handler: Callable[[T], Awaitable[dict[str, Any]]]
annotations: ToolAnnotations | None = None
PropertyTypeDescription
namestrĐịnh danh duy nhất cho tool
descriptionstrMô tả dễ đọc
input_schematype[T] | dict[str, Any]Schema để validate input
handlerCallable[[T], Awaitable[dict[str, Any]]]Hàm async xử lý thực thi tool
annotationsToolAnnotations | NoneAnnotation MCP tool tuỳ chọn (ví dụ, readOnlyHint, destructiveHint, openWorldHint). Từ mcp.types

Abstract base class cho triển khai transport tuỳ biến. Dùng để giao tiếp với process Claude qua một kênh tuỳ biến (ví dụ, một kết nối từ xa thay vì subprocess cục bộ).

from abc import ABC, abstractmethod
from collections.abc import AsyncIterator
from typing import Any
class Transport(ABC):
@abstractmethod
async def connect(self) -> None: ...
@abstractmethod
async def write(self, data: str) -> None: ...
@abstractmethod
def read_messages(self) -> AsyncIterator[dict[str, Any]]: ...
@abstractmethod
async def close(self) -> None: ...
@abstractmethod
def is_ready(self) -> bool: ...
@abstractmethod
async def end_input(self) -> None: ...
MethodDescription
connect()Kết nối transport và chuẩn bị giao tiếp
write(data)Ghi dữ liệu thô (JSON + newline) vào transport
read_messages()Async iterator yield ra message JSON đã parse
close()Đóng kết nối và dọn dẹp tài nguyên
is_ready()Trả về True nếu transport có thể gửi và nhận
end_input()Đóng input stream (ví dụ, đóng stdin cho transport subprocess)

Import: from claude_agent_sdk import Transport

Dataclass cấu hình cho query Claude Code.

@dataclass
class ClaudeAgentOptions:
tools: list[str] | ToolsPreset | None = None
allowed_tools: list[str] = field(default_factory=list)
system_prompt: str | SystemPromptPreset | SystemPromptFile | None = None
mcp_servers: dict[str, McpServerConfig] | str | Path = field(default_factory=dict)
strict_mcp_config: bool = False
permission_mode: PermissionMode | None = None
continue_conversation: bool = False
resume: str | None = None
session_id: str | None = None
max_turns: int | None = None
max_budget_usd: float | None = None
disallowed_tools: list[str] = field(default_factory=list)
model: str | None = None
fallback_model: str | None = None
betas: list[SdkBeta] = field(default_factory=list)
output_format: dict[str, Any] | None = None
permission_prompt_tool_name: str | None = None
cwd: str | Path | None = None
cli_path: str | Path | None = None
settings: str | None = None
add_dirs: list[str | Path] = field(default_factory=list)
env: dict[str, str] = field(default_factory=dict)
extra_args: dict[str, str | None] = field(default_factory=dict)
max_buffer_size: int | None = None
debug_stderr: Any = sys.stderr # Deprecated
stderr: Callable[[str], None] | None = None
can_use_tool: CanUseTool | None = None
hooks: dict[HookEvent, list[HookMatcher]] | None = None
user: str | None = None
include_partial_messages: bool = False
include_hook_events: bool = False
fork_session: bool = False
agents: dict[str, AgentDefinition] | None = None
setting_sources: list[SettingSource] | None = None
skills: list[str] | Literal["all"] | None = None
sandbox: SandboxSettings | None = None
plugins: list[SdkPluginConfig] = field(default_factory=list)
max_thinking_tokens: int | None = None # Deprecated: use thinking instead
thinking: ThinkingConfig | None = None
effort: EffortLevel | None = None
enable_file_checkpointing: bool = False
session_store: SessionStore | None = None
session_store_flush: SessionStoreFlushMode = "batched"
load_timeout_ms: int = 60_000
task_budget: TaskBudget | None = None
PropertyTypeDefaultDescription
toolslist[str] | ToolsPreset | NoneNoneCấu hình tool. Dùng {"type": "preset", "preset": "claude_code"} cho bộ tool mặc định của Claude Code
allowed_toolslist[str][]Tool tự động chấp thuận không cần hỏi. Không giới hạn Claude chỉ dùng các tool này; tool chưa liệt kê sẽ rơi xuống permission_modecan_use_tool. Dùng disallowed_tools để chặn tool. Xem Permissions
system_promptstr | SystemPromptPreset | SystemPromptFile | NoneNoneCấu hình system prompt. Truyền một chuỗi cho prompt tuỳ chỉnh, {"type": "preset", "preset": "claude_code"} cho system prompt của Claude Code kèm "append" tuỳ chọn, hoặc {"type": "file", "path": "..."} để nạp prompt lớn từ đĩa. Xem SystemPromptPresetSystemPromptFile
mcp_serversdict[str, McpServerConfig] | str | Path{}Cấu hình MCP server hoặc đường dẫn tới file config
strict_mcp_configboolFalseKhi True, chỉ dùng server truyền trong mcp_servers và bỏ qua .mcp.json của project, user settings, MCP server do plugin cung cấp, và claude.ai connectors. Ánh xạ tới cờ CLI --strict-mcp-config
permission_modePermissionMode | NoneNonePermission mode cho việc dùng tool
continue_conversationboolFalseTiếp tục hội thoại gần nhất
resumestr | NoneNoneSession ID cần resume
session_idstr | NoneNoneDùng một session ID cụ thể thay vì tự sinh. Phải là UUID hợp lệ. Không thể kết hợp với continue_conversation hoặc resume trừ khi fork_session cũng được đặt
max_turnsint | NoneNoneSố turn agentic tối đa (vòng round-trip dùng tool)
max_budget_usdfloat | NoneNoneDừng query khi ước tính chi phí phía client đạt giá trị USD này. So sánh với cùng ước tính như total_cost_usd; xem Track cost and usage để biết các lưu ý về độ chính xác
disallowed_toolslist[str][]Tool bị từ chối. Một tên trần như "Bash" loại bỏ tool khỏi context của Claude. Một rule có phạm vi như "Bash(rm *)" vẫn giữ tool khả dụng nhưng từ chối các lời gọi khớp ở mọi permission mode, kể cả bypassPermissions. Xem Permissions
enable_file_checkpointingboolFalseBật theo dõi thay đổi file để rewind. Xem File checkpointing
modelstr | NoneNoneAlias model Claude hoặc tên model đầy đủ. Xem các giá trị chấp nhận và ID theo provider
fallback_modelstr | NoneNoneModel dự phòng dùng khi model chính lỗi
betaslist[SdkBeta][]Beta feature cần bật. Xem SdkBeta để biết các lựa chọn khả dụng
output_formatdict[str, Any] | NoneNoneĐịnh dạng output cho phản hồi có cấu trúc (ví dụ, {"type": "json_schema", "schema": {...}}). Xem Structured outputs để biết chi tiết
permission_prompt_tool_namestr | NoneNoneTên MCP tool cho permission prompt
cwdstr | Path | NoneNoneThư mục làm việc hiện tại
cli_pathstr | Path | NoneNoneĐường dẫn tuỳ chỉnh tới file thực thi Claude Code CLI
settingsstr | NoneNoneĐường dẫn tới file settings
add_dirslist[str | Path][]Thư mục bổ sung Claude được phép truy cập
envdict[str, str]{}Biến môi trường được merge chồng lên môi trường process kế thừa. Xem Environment variables cho các biến CLI đọc, và phần “Xử lý phản hồi API chậm hoặc treo” bên dưới cho các biến liên quan đến timeout
extra_argsdict[str, str | None]{}Tham số CLI bổ sung truyền thẳng cho CLI
max_buffer_sizeint | NoneNoneSố byte tối đa khi buffer stdout của CLI
debug_stderrAnysys.stderrDeprecated - Đối tượng file-like cho debug output. Dùng callback stderr thay thế
stderrCallable[[str], None] | NoneNoneHàm callback cho stderr output từ CLI
can_use_toolCanUseTool | NoneNoneCallback permission cho tool, chỉ được gọi khi luồng permission rơi xuống một prompt. Không được gọi cho lời gọi đã tự động chấp thuận bởi allowed_tools, allow rule, hoặc permission_mode. AskUserQuestion, connector tool tổ chức bạn đặt thành ask, và MCP tool đánh dấu requiresUserInteraction vẫn tới được callback dù bạn đã cho phép; ở dontAsk mode những trường hợp này bị từ chối thay vì gọi callback. Xem CanUseTool để biết chi tiết
hooksdict[HookEvent, list[HookMatcher]] | NoneNoneCấu hình hook để chặn (intercept) event
userstr | NoneNoneĐịnh danh người dùng
include_partial_messagesboolFalseGồm cả event streaming message một phần. Khi bật, message StreamEvent sẽ được yield
include_hook_eventsboolFalseGồm cả event vòng đời hook trong message stream dưới dạng đối tượng HookEventMessage
fork_sessionboolFalseKhi resume bằng resume, fork sang một session ID mới thay vì tiếp tục session gốc
agentsdict[str, AgentDefinition] | NoneNoneSubagent định nghĩa bằng code
pluginslist[SdkPluginConfig][]Nạp plugin tuỳ chỉnh từ đường dẫn cục bộ. Xem Plugins để biết chi tiết
sandboxSandboxSettings | NoneNoneCấu hình hành vi sandbox bằng code. Xem Sandbox settings để biết chi tiết
setting_sourceslist[SettingSource] | NoneNone (mặc định CLI: mọi source)Kiểm soát nguồn settings filesystem nào được nạp. Truyền [] để tắt user, project, và local settings. Policy do endpoint quản lý luôn được nạp bất kể; server-managed settings được lấy khi session xác thực bằng credential tổ chức trên một cấu hình đủ điều kiện. Xem Use Claude Code features
skillslist[str] | Literal["all"] | NoneNoneSkill khả dụng cho session. Truyền "all" để bật mọi skill phát hiện được, hoặc một danh sách tên skill. Khi đặt, SDK tự thêm Skill tool vào allowed_tools. Nếu bạn cũng truyền tools, hãy gồm "Skill" trong danh sách đó. Xem Skills
max_thinking_tokensint | NoneNoneDeprecated - Số token tối đa cho thinking block. Dùng thinking thay thế
thinkingThinkingConfig | NoneNoneKiểm soát hành vi extended thinking. Ưu tiên hơn max_thinking_tokens
effortEffortLevel | NoneNoneMức effort cho độ sâu thinking. Xem adjust the effort level
session_storeSessionStore | NoneNoneMirror transcript session sang backend bên ngoài để bất kỳ host nào cũng resume được. Xem Persist sessions to external storage
session_store_flushLiteral["batched", "eager"]"batched"Khi nào flush entry transcript đã mirror sang session_store. "batched" flush một lần mỗi turn hoặc khi buffer đầy; "eager" kích hoạt flush nền sau mỗi frame. Bị bỏ qua khi session_storeNone
load_timeout_msint60000Timeout cho mỗi lần gọi session_store.load()list_subkeys() trong lúc materialize resume, tính bằng milliseconds
task_budgetTaskBudget | NoneNoneTask budget phía API tính bằng token. Gửi dưới dạng output_config.task_budget kèm beta header task-budgets-2026-03-13. Truyền {"total": <int>}.

Process con CLI đọc một số biến môi trường kiểm soát timeout API và phát hiện treo (stall). Truyền chúng qua ClaudeAgentOptions.env:

from claude_agent_sdk import ClaudeAgentOptions
options = ClaudeAgentOptions(
env={
"API_TIMEOUT_MS": "120000",
"CLAUDE_CODE_MAX_RETRIES": "2",
"CLAUDE_ASYNC_AGENT_STALL_TIMEOUT_MS": "120000",
},
)
  • API_TIMEOUT_MS: timeout mỗi request trên client Anthropic, tính bằng milliseconds. Mặc định 600000. Áp dụng cho vòng lặp chính và mọi subagent.
  • CLAUDE_CODE_MAX_RETRIES: số lần retry API tối đa. Mặc định 10, giới hạn trần 15. Mỗi lần retry có riêng khoảng API_TIMEOUT_MS, nên thời gian chờ tệ nhất khoảng API_TIMEOUT_MS × (CLAUDE_CODE_MAX_RETRIES + 1) cộng backoff. Với các lần chạy không giám sát cần chờ qua sự cố dài hơn, đặt CLAUDE_CODE_RETRY_WATCHDOG=1: nó retry lỗi capacity vô hạn, và kể từ Claude Code v2.1.199 nâng mặc định cho các lỗi tạm thời khác lên 300 và bỏ giới hạn trần của biến này.
  • CLAUDE_ASYNC_AGENT_STALL_TIMEOUT_MS: watchdog phát hiện treo cho subagent chạy bằng run_in_background. Mặc định 600000. Reset mỗi khi có stream event; khi treo nó abort subagent, đánh dấu task lỗi, và trả lỗi về parent kèm kết quả một phần nếu có. Không áp dụng cho subagent đồng bộ.
  • CLAUDE_ENABLE_STREAM_WATCHDOG cùng CLAUDE_STREAM_IDLE_TIMEOUT_MS: abort request khi header đã đến nhưng response body ngừng stream. Watchdog bật mặc định cho mọi provider; đặt CLAUDE_ENABLE_STREAM_WATCHDOG=0 để tắt. CLAUDE_STREAM_IDLE_TIMEOUT_MS mặc định 300000 và bị clamp về mức tối thiểu đó. Sau khi abort, Claude Code retry request tối đa một lần, và chỉ khi Claude chưa bắt đầu một khối text hoặc tool call trong phản hồi; một khi Claude đã hoàn tất một khối text hoặc tool call, Claude Code giữ lại output đã hoàn tất, thêm một thông báo phản hồi có thể chưa đầy đủ thay vì retry, và vẫn chạy bất kỳ tool call nào đã hoàn tất.

Cấu hình để validate structured output. Truyền cái này dưới dạng dict cho field output_format trên ClaudeAgentOptions:

# Expected dict shape for output_format
{
"type": "json_schema",
"schema": {...}, # Your JSON Schema definition
}
FieldRequiredDescription
typeYesPhải là "json_schema" để validate theo JSON Schema
schemaYesĐịnh nghĩa JSON Schema để validate output

Cấu hình để dùng preset system prompt của Claude Code kèm bổ sung tuỳ chọn.

class SystemPromptPreset(TypedDict):
type: Literal["preset"]
preset: Literal["claude_code"]
append: NotRequired[str]
exclude_dynamic_sections: NotRequired[bool]
FieldRequiredDescription
typeYesPhải là "preset" để dùng preset system prompt
presetYesPhải là "claude_code" để dùng system prompt của Claude Code
appendNoHướng dẫn bổ sung nối thêm vào preset system prompt
exclude_dynamic_sectionsNoChuyển context riêng theo session như thư mục làm việc, cờ git-repo, và đường dẫn auto-memory ra khỏi system prompt vào user message đầu tiên. Cải thiện việc tái sử dụng prompt cache xuyên user và máy. Xem Modify system prompts

Cấu hình để nạp system prompt tuỳ chỉnh từ file thay vì truyền dưới dạng string. SDK ánh xạ cái này tới cờ CLI --system-prompt-file. Dùng dạng file khi prompt lớn: SDK truyền chuỗi system_prompt trên argv của process con CLI, chịu giới hạn độ dài dòng lệnh của OS trước khi SDK gửi bất kỳ request API nào. Trên Linux, một argument dài hơn khoảng 128 KB sẽ lỗi ngay khi spawn process với Argument list too long. Trên Windows, toàn bộ dòng lệnh bị giới hạn khoảng 32 KB, nên dạng string sẽ lỗi ở ngưỡng thấp hơn.

class SystemPromptFile(TypedDict):
type: Literal["file"]
path: str
FieldRequiredDescription
typeYesPhải là "file" để nạp prompt từ đĩa
pathYesĐường dẫn tới file chứa system prompt

Kiểm soát nguồn cấu hình dựa trên filesystem nào SDK nạp settings từ đó.

SettingSource = Literal["user", "project", "local"]
ValueDescriptionLocation
"user"Settings người dùng toàn cục~/.claude/settings.json
"project"Settings project dùng chung (được version control).claude/settings.json
"local"Settings project cục bộ, bị gitignore khi Claude Code lưu một setting vào đó.claude/settings.local.json

Khi setting_sources bị bỏ trống hoặc None, query() nạp cùng bộ settings filesystem như CLI Claude Code: user, project, và local. Policy do endpoint quản lý luôn được nạp trong mọi trường hợp; server-managed settings được lấy khi session xác thực bằng credential tổ chức trên một cấu hình đủ điều kiện. Xem “What settingSources does not control” để biết các input được đọc bất kể option này, và cách tắt chúng.

Tắt settings filesystem:

# Do not load user, project, or local settings from disk
import asyncio
from claude_agent_sdk import query, ClaudeAgentOptions
async def main():
async for message in query(
prompt="Analyze this code",
options=ClaudeAgentOptions(
setting_sources=[]
),
):
print(message)
asyncio.run(main())

Nạp toàn bộ settings filesystem tường minh:

import asyncio
from claude_agent_sdk import query, ClaudeAgentOptions
async def main():
async for message in query(
prompt="Analyze this code",
options=ClaudeAgentOptions(
setting_sources=["user", "project", "local"]
),
):
print(message)
asyncio.run(main())

Chỉ nạp một số setting source cụ thể:

# Load only project settings, ignore user and local
import asyncio
from claude_agent_sdk import query, ClaudeAgentOptions
async def main():
async for message in query(
prompt="Run CI checks",
options=ClaudeAgentOptions(
setting_sources=["project"] # Only .claude/settings.json
),
):
print(message)
asyncio.run(main())

Môi trường testing và CI:

# Ensure consistent behavior in CI by excluding local settings
import asyncio
from claude_agent_sdk import query, ClaudeAgentOptions
async def main():
async for message in query(
prompt="Run tests",
options=ClaudeAgentOptions(
setting_sources=["project"], # Only team-shared settings
permission_mode="bypassPermissions",
),
):
print(message)
asyncio.run(main())

Ứng dụng chỉ dùng SDK:

# Define everything programmatically.
# Pass [] to opt out of filesystem setting sources.
import asyncio
from claude_agent_sdk import AgentDefinition, ClaudeAgentOptions, query
async def main():
async for message in query(
prompt="Review this PR",
options=ClaudeAgentOptions(
setting_sources=[],
agents={
"code-reviewer": AgentDefinition(
description="Reviews code changes",
prompt="You are a code reviewer. Report issues in the diff.",
),
},
allowed_tools=["Read", "Grep", "Glob"],
),
):
print(message)
asyncio.run(main())

Nạp hướng dẫn project CLAUDE.md:

# Load project settings to include CLAUDE.md files
import asyncio
from claude_agent_sdk import query, ClaudeAgentOptions
async def main():
async for message in query(
prompt="Add a new feature following project conventions",
options=ClaudeAgentOptions(
system_prompt={
"type": "preset",
"preset": "claude_code", # Use Claude Code's system prompt
},
setting_sources=["project"], # Loads CLAUDE.md from project
allowed_tools=["Read", "Write", "Edit"],
),
):
print(message)
asyncio.run(main())

Khi nhiều source được nạp, settings được merge theo thứ tự ưu tiên này (cao nhất đến thấp nhất):

  1. Local settings (.claude/settings.local.json)
  2. Project settings (.claude/settings.json)
  3. User settings (~/.claude/settings.json)

Các option lập trình như agentsallowed_tools ghi đè settings filesystem user, project, và local. Managed policy settings có ưu tiên cao hơn các option lập trình.

Cấu hình cho một subagent định nghĩa bằng code.

@dataclass
class AgentDefinition:
description: str
prompt: str
tools: list[str] | None = None
disallowedTools: list[str] | None = None
model: str | None = None
skills: list[str] | None = None
memory: Literal["user", "project", "local"] | None = None
mcpServers: list[str | dict[str, Any]] | None = None
initialPrompt: str | None = None
maxTurns: int | None = None
background: bool | None = None
effort: EffortLevel | int | None = None
permissionMode: PermissionMode | None = None
FieldRequiredDescription
descriptionYesMô tả bằng ngôn ngữ tự nhiên về khi nào dùng agent này
promptYesSystem prompt của agent
toolsNoMảng tên tool được phép. Nếu bỏ trống, kế thừa mọi tool khả dụng cho subagent
disallowedToolsNoMảng tên tool bị gỡ khỏi bộ tool của agent. Pattern cấp MCP server cũng được chấp nhận: mcp__server hoặc mcp__server__* gỡ mọi tool từ server đó, và mcp__* gỡ mọi MCP tool từ mọi server
modelNoGhi đè model cho agent này. Chấp nhận alias như "sonnet", "opus", "haiku", hoặc "inherit", hoặc một model ID đầy đủ. Nếu bỏ trống, dùng model chính
skillsNoDanh sách tên skill nạp trước vào context của agent khi khởi động. Skill chưa liệt kê vẫn gọi được qua Skill tool
memoryNoNguồn memory cho agent này: "user", "project", hoặc "local"
mcpServersNoMCP server khả dụng cho agent này. Mỗi entry là tên server hoặc một dict inline {name: config}
initialPromptNoTự động gửi làm user turn đầu tiên khi agent này chạy vai trò main thread agent
maxTurnsNoSố turn agentic tối đa trước khi agent dừng
backgroundNoChạy agent này như một background task không chặn khi được gọi
effortNoMức reasoning effort cho agent này. Chấp nhận một level đặt tên hoặc số nguyên. Xem EffortLevel
permissionModeNoPermission mode cho việc thực thi tool trong agent này. Xem PermissionMode

Permission mode để kiểm soát việc thực thi tool.

PermissionMode = Literal[
"default", # Standard permission behavior
"acceptEdits", # Auto-accept file edits
"plan", # Planning mode - explore without editing
"dontAsk", # Deny anything not pre-approved instead of prompting
"bypassPermissions", # Bypass permission checks; explicit ask rules still prompt (use with caution)
"auto", # Model classifier approves or denies permission prompts
]

Mức effort để định hướng độ sâu thinking.

EffortLevel = Literal[
"low", # Minimal thinking, fastest responses
"medium", # Moderate thinking
"high", # Deep reasoning
"xhigh", # Extended reasoning; falls back to "high" on models that don't support it
"max", # Maximum effort
]

Type alias cho hàm callback permission tool.

CanUseTool = Callable[
[str, dict[str, Any], ToolPermissionContext], Awaitable[PermissionResult]
]

Callback nhận:

  • tool_name: Tên tool đang được gọi
  • input_data: Tham số đầu vào của tool
  • context: Một ToolPermissionContext kèm thông tin bổ sung

Trả về một PermissionResult (hoặc PermissionResultAllow hoặc PermissionResultDeny).

Callback là bản thay thế phía SDK cho permission prompt tương tác: nó chỉ được gọi khi luồng đánh giá permission dẫn tới một prompt. Lời gọi tool đã được chấp thuận bởi một entry allowed_tools, một allow rule trong settings, hoặc permission mode, như acceptEdits hoặc bypassPermissions, không bao giờ gọi nó. Để chặn (gate) mọi lời gọi tool, dùng một PreToolUse hook thay thế.

AskUserQuestion, MCP tool đánh dấu requiresUserInteraction, và connector tool tổ chức bạn đặt thành ask vẫn tới được callback ngay cả khi một allow rule khớp. Ở dontAsk mode những lời gọi này bị từ chối thay vì gọi callback.

Thông tin context truyền cho callback permission của tool.

@dataclass
class ToolPermissionContext:
signal: Any | None = None # Future: abort signal support
suggestions: list[PermissionUpdate] = field(default_factory=list)
tool_use_id: str | None = None
agent_id: str | None = None
blocked_path: str | None = None
decision_reason: str | None = None
title: str | None = None
display_name: str | None = None
description: str | None = None
FieldTypeDescription
signalAny | NoneDự phòng cho hỗ trợ abort signal trong tương lai
suggestionslist[PermissionUpdate]Gợi ý cập nhật permission từ CLI. Bash prompt gồm một gợi ý với destination localSettings, nên trả về nó trong updated_permissions sẽ ghi rule vào .claude/settings.local.json và duy trì qua các session.
tool_use_idstr | NoneĐịnh danh của lời gọi tool cụ thể mà prompt này dành cho. Luôn có mặt khi truyền tới can_use_tool
agent_idstr | NoneSub-agent ID khi lời gọi bắt nguồn từ một subagent; None cho main agent
blocked_pathstr | NoneĐường dẫn file gây ra yêu cầu permission, nếu áp dụng. Ví dụ, khi một lệnh Bash cố truy cập đường dẫn ngoài thư mục được phép
decision_reasonstr | NoneLý do yêu cầu permission này được kích hoạt. Chuyển tiếp từ permissionDecisionReason của một PreToolUse hook khi hook trả về "ask"
titlestr | NoneCâu prompt permission đầy đủ, ví dụ Claude wants to read foo.txt. Dùng làm text prompt chính khi có mặt
display_namestr | NoneCụm danh từ ngắn cho hành động tool, ví dụ Read file, phù hợp cho nhãn nút
descriptionstr | NonePhụ đề dễ đọc cho UI permission

Union type cho kết quả callback permission.

PermissionResult = PermissionResultAllow | PermissionResultDeny

Kết quả cho biết lời gọi tool nên được cho phép.

@dataclass
class PermissionResultAllow:
behavior: Literal["allow"] = "allow"
updated_input: dict[str, Any] | None = None
updated_permissions: list[PermissionUpdate] | None = None
FieldTypeDefaultDescription
behaviorLiteral["allow"]"allow"Phải là “allow”
updated_inputdict[str, Any] | NoneNoneInput đã sửa dùng thay cho input gốc
updated_permissionslist[PermissionUpdate] | NoneNoneCác cập nhật permission cần áp dụng

Kết quả cho biết lời gọi tool nên bị từ chối.

@dataclass
class PermissionResultDeny:
behavior: Literal["deny"] = "deny"
message: str = ""
interrupt: bool = False
FieldTypeDefaultDescription
behaviorLiteral["deny"]"deny"Phải là “deny”
messagestr""Thông báo giải thích vì sao tool bị từ chối
interruptboolFalseCó interrupt lần thực thi hiện tại hay không

Cấu hình để cập nhật permission bằng code.

@dataclass
class PermissionUpdate:
type: Literal[
"addRules",
"replaceRules",
"removeRules",
"setMode",
"addDirectories",
"removeDirectories",
]
rules: list[PermissionRuleValue] | None = None
behavior: Literal["allow", "deny", "ask"] | None = None
mode: PermissionMode | None = None
directories: list[str] | None = None
destination: (
Literal["userSettings", "projectSettings", "localSettings", "session"] | None
) = None
FieldTypeDescription
typeLiteral[...]Loại thao tác cập nhật permission
ruleslist[PermissionRuleValue] | NoneRule cho thao tác add/replace/remove
behaviorLiteral["allow", "deny", "ask"] | NoneHành vi cho thao tác dựa trên rule
modePermissionMode | NoneMode cho thao tác setMode
directorieslist[str] | NoneThư mục cho thao tác add/remove directory
destinationLiteral[...] | NoneNơi áp dụng cập nhật permission

Một rule để add, replace, hoặc remove trong một cập nhật permission.

@dataclass
class PermissionRuleValue:
tool_name: str
rule_content: str | None = None

Cấu hình preset tools để dùng bộ tool mặc định của Claude Code.

class ToolsPreset(TypedDict):
type: Literal["preset"]
preset: Literal["claude_code"]

Kiểm soát hành vi extended thinking. Một union của ba cấu hình:

ThinkingDisplay = Literal["summarized", "omitted"]
class ThinkingConfigAdaptive(TypedDict):
type: Literal["adaptive"]
display: NotRequired[ThinkingDisplay]
class ThinkingConfigEnabled(TypedDict):
type: Literal["enabled"]
budget_tokens: int
display: NotRequired[ThinkingDisplay]
class ThinkingConfigDisabled(TypedDict):
type: Literal["disabled"]
ThinkingConfig = ThinkingConfigAdaptive | ThinkingConfigEnabled | ThinkingConfigDisabled
VariantFieldsDescription
adaptivetype, displayClaude tự quyết định khi nào cần thinking
enabledtype, budget_tokens, displayBật thinking với một budget token cụ thể
disabledtypeTắt thinking

Field tuỳ chọn display kiểm soát việc thinking text được trả về "summarized" hay "omitted". Trên Claude Opus 4.7 trở lên, mặc định của API là "omitted", nên đặt "summarized" để nhận nội dung thinking trong output ThinkingBlock. Claude Code không gửi display tới Amazon Bedrock hoặc Google Cloud’s Agent Platform, nên trên các provider đó Opus 4.7 trở lên trả về ThinkingBlock rỗng ngay cả khi bạn đặt display thành "summarized".

Vì đây là các class TypedDict, chúng là plain dict khi chạy. Bạn có thể tạo chúng dưới dạng dict literal hoặc gọi class như một constructor; cả hai đều tạo ra một dict. Truy cập field bằng config["budget_tokens"], không phải config.budget_tokens:

from claude_agent_sdk import ClaudeAgentOptions, ThinkingConfigEnabled
# Option 1: dict literal (recommended, no import needed)
options = ClaudeAgentOptions(thinking={"type": "enabled", "budget_tokens": 20000})
# Option 2: constructor-style (returns a plain dict)
config = ThinkingConfigEnabled(type="enabled", budget_tokens=20000)
print(config["budget_tokens"]) # 20000
# config.budget_tokens would raise AttributeError

Task budget phía API tính bằng token, dùng với field task_budget trong ClaudeAgentOptions.

class TaskBudget(TypedDict):
total: int
FieldTypeDescription
totalintTổng token budget cho task

Vì đây là TypedDict, truyền nó dưới dạng plain dict, ví dụ ClaudeAgentOptions(task_budget={"total": 50000}).

Literal type cho beta feature của SDK.

SdkBeta = Literal["context-1m-2025-08-07"]

Dùng với field betas trong ClaudeAgentOptions để bật beta feature.

Cấu hình cho SDK MCP server tạo bằng create_sdk_mcp_server().

class McpSdkServerConfig(TypedDict):
type: Literal["sdk"]
name: str
instance: Any # MCP Server instance

Union type cho cấu hình MCP server.

McpServerConfig = (
McpStdioServerConfig | McpSSEServerConfig | McpHttpServerConfig | McpSdkServerConfig
)
class McpStdioServerConfig(TypedDict):
type: NotRequired[Literal["stdio"]] # Optional for backwards compatibility
command: str
args: NotRequired[list[str]]
env: NotRequired[dict[str, str]]
class McpSSEServerConfig(TypedDict):
type: Literal["sse"]
url: str
headers: NotRequired[dict[str, str]]
class McpHttpServerConfig(TypedDict):
type: Literal["http"]
url: str
headers: NotRequired[dict[str, str]]

Cấu hình của một MCP server như được báo cáo bởi get_mcp_status(). Đây là hợp của mọi biến thể transport của McpServerConfig cộng thêm biến thể chỉ dành cho output claudeai-proxy cho server được proxy qua claude.ai.

McpServerStatusConfig = (
McpStdioServerConfig
| McpSSEServerConfig
| McpHttpServerConfig
| McpSdkServerConfigStatus
| McpClaudeAIProxyServerConfig
)

McpSdkServerConfigStatus là dạng có thể serialize của McpSdkServerConfig chỉ với field type ("sdk") và name (str); instance in-process bị lược bỏ. McpClaudeAIProxyServerConfig có field type ("claudeai-proxy"), url (str), và id (str).

Phản hồi từ ClaudeSDKClient.get_mcp_status(). Bọc danh sách trạng thái server dưới key mcpServers.

class McpStatusResponse(TypedDict):
mcpServers: list[McpServerStatus]

Trạng thái của một MCP server đã kết nối, nằm trong McpStatusResponse.

class McpServerStatus(TypedDict):
name: str
status: McpServerConnectionStatus # "connected" | "failed" | "needs-auth" | "pending" | "disabled"
serverInfo: NotRequired[McpServerInfo]
error: NotRequired[str]
config: NotRequired[McpServerStatusConfig]
scope: NotRequired[str]
tools: NotRequired[list[McpToolInfo]]
FieldTypeDescription
namestrTên server
statusstrMột trong "connected", "failed", "needs-auth", "pending", hoặc "disabled"
serverInfodict (optional)Tên và phiên bản server ({"name": str, "version": str})
errorstr (optional)Thông báo lỗi nếu server kết nối thất bại
configMcpServerStatusConfig (optional)Cấu hình server. Cùng dạng với McpServerConfig (stdio, SSE, HTTP, hoặc SDK), cộng thêm biến thể claudeai-proxy cho server kết nối qua claude.ai
scopestr (optional)Phạm vi cấu hình
toolslist (optional)Tool do server này cung cấp, mỗi cái kèm field name, description, và annotations

Cấu hình để nạp plugin trong SDK.

class SdkPluginConfig(TypedDict):
type: Literal["local"]
path: str
FieldTypeDescription
typeLiteral["local"]Phải là "local" (hiện chỉ hỗ trợ plugin cục bộ)
pathstrĐường dẫn tuyệt đối hoặc tương đối tới thư mục plugin

Ví dụ:

plugins = [
{"type": "local", "path": "./my-plugin"},
{"type": "local", "path": "/absolute/path/to/plugin"},
]

Để biết thông tin đầy đủ về tạo và dùng plugin, xem Plugins.

Union type của mọi message có thể có.

Message = (
UserMessage
| AssistantMessage
| SystemMessage
| ResultMessage
| StreamEvent
| RateLimitEvent
)

Message input người dùng.

@dataclass
class UserMessage:
content: str | list[ContentBlock]
uuid: str | None = None
parent_tool_use_id: str | None = None
tool_use_result: dict[str, Any] | None = None
FieldTypeDescription
contentstr | list[ContentBlock]Nội dung message dạng text hoặc content block
uuidstr | NoneĐịnh danh message duy nhất
parent_tool_use_idstr | NoneTool use ID nếu message này là phản hồi kết quả tool
tool_use_resultdict[str, Any] | NoneDữ liệu kết quả tool nếu có

Message phản hồi của assistant kèm content block.

@dataclass
class AssistantMessage:
content: list[ContentBlock]
model: str
parent_tool_use_id: str | None = None
error: AssistantMessageError | None = None
usage: dict[str, Any] | None = None
message_id: str | None = None
stop_reason: str | None = None
session_id: str | None = None
uuid: str | None = None
FieldTypeDescription
contentlist[ContentBlock]Danh sách content block trong phản hồi
modelstrModel đã sinh ra phản hồi
parent_tool_use_idstr | NoneTool use ID nếu đây là phản hồi lồng nhau
errorAssistantMessageError | NoneLoại lỗi nếu phản hồi gặp lỗi
usagedict[str, Any] | NoneToken usage của message này (cùng key với ResultMessage.usage)
message_idstr | NoneID message của API. Nhiều message từ cùng một turn chia sẻ cùng ID
stop_reasonstr | NoneLý do dừng từ API (ví dụ end_turn, tool_use)
session_idstr | NoneID session message này thuộc về
uuidstr | NoneĐịnh danh message duy nhất trong transcript của session

Các loại lỗi có thể có cho assistant message.

AssistantMessageError = Literal[
"authentication_failed",
"billing_error",
"rate_limit",
"invalid_request",
"server_error",
"unknown",
]

Process con CLI bên dưới có thể phát ra loại lỗi không nằm trong Literal này, ví dụ max_output_tokens. SDK chuyển tiếp giá trị nguyên vẹn, nên hãy xử lý các chuỗi ngoài danh sách này giống như unknown. Type TypeScript SDKAssistantMessageError liệt kê đầy đủ tập giá trị mà CLI có thể phát ra.

Message hệ thống kèm metadata.

@dataclass
class SystemMessage:
subtype: str
data: dict[str, Any]

Message kết quả cuối kèm thông tin chi phí và usage.

@dataclass
class ResultMessage:
subtype: str
duration_ms: int
duration_api_ms: int
is_error: bool
num_turns: int
session_id: str
stop_reason: str | None = None
total_cost_usd: float | None = None
usage: dict[str, Any] | None = None
result: str | None = None
structured_output: Any = None
model_usage: dict[str, ModelUsage] | None = None
permission_denials: list[Any] | None = None
deferred_tool_use: DeferredToolUse | None = None
errors: list[str] | None = None
api_error_status: int | None = None
uuid: str | None = None
terminal_reason: str | None = None

Field subtype quyết định field nào khác được điền. Nó là một trong "success", "error_during_execution", "error_max_turns", "error_max_budget_usd", hoặc "error_max_structured_output_retries". Python dataclass gộp mọi biến thể vào một hình dạng duy nhất, nên field không áp dụng cho subtype trả về sẽ là None.

Một số field mang chi tiết chẩn đoán về cách hội thoại kết thúc:

  • is_error: True khi hội thoại kết thúc ở trạng thái lỗi. Luôn True với các subtype error_*. Với subtype="success", nó là True khi request model cuối cùng lỗi, nghĩa là vòng lặp agent đã hoàn tất nhưng lời gọi API cuối trả về lỗi.
  • api_error_status: mã trạng thái HTTP của lỗi API kết thúc turn. None khi turn kết thúc mà không có lỗi. Chỉ được điền khi subtype="success".
  • result: text của assistant message cuối cùng khi subtype="success", hoặc None với các subtype error_*. Khi subtype="success"is_error=True, trường này chứa chuỗi lỗi API nếu có nhưng có thể rỗng, nên kiểm tra api_error_status và nội dung AssistantMessage trước đó để biết chi tiết.
  • errors: chuỗi lỗi cấp vòng lặp như thông báo max-turns. Chỉ được điền với các subtype error_*.
  • terminal_reason: lý do vòng lặp query kết thúc, ví dụ "completed", "max_turns", "api_error", "aborted_streaming", hoặc "aborted_tools". Giá trị "aborted_streaming" hoặc "aborted_tools" nghĩa là turn bị abort trước khi hoàn tất. Nguyên nhân phổ biến là interrupt() và một permission callback trả về PermissionResultDeny với interrupt=True. None trên các phiên bản CLI có trước field này, trên kết quả bỏ qua vòng lặp query như slash command cục bộ, hoặc trên kết quả lỗi tổng hợp phát ra khi session lỗi nghiêm trọng. Tương ứng với SDKResultMessage.terminal_reason của TypeScript SDK, nơi liệt kê đầy đủ tập giá trị.

Dict usage chứa các key sau khi có mặt:

KeyTypeDescription
input_tokensintInput token tiêu thụ bởi vòng lặp agent cấp cao nhất. Token của subagent không được gồm; dùng model_usage để hạch toán toàn bộ cây.
output_tokensintOutput token sinh ra bởi vòng lặp agent cấp cao nhất. Token của subagent không được gồm.
cache_creation_input_tokensintToken dùng để tạo entry cache mới.
cache_read_input_tokensintToken đọc từ entry cache đã có.

Dict model_usage ánh xạ tên model tới usage theo từng model. Mỗi giá trị là một TypedDict ModelUsage có key dùng camelCase, vì giá trị được chuyển qua nguyên vẹn từ process con CLI bên dưới. Import qua from claude_agent_sdk.types import ModelUsage. Các key:

KeyTypeDescription
inputTokensintInput token cho model này.
outputTokensintOutput token cho model này.
cacheReadInputTokensintToken đọc cache cho model này.
cacheCreationInputTokensintToken tạo cache cho model này.
webSearchRequestsintSố request web search model này đã thực hiện.
costUSDfloatChi phí ước tính bằng USD cho model này, tính phía client. Xem Track cost and usage để biết lưu ý về billing.
contextWindowintKích thước context window cho model này.
maxOutputTokensintGiới hạn output token tối đa cho model này.
canonicalModelstrModel ID chuẩn dùng để tra cứu giá. Có thể khác với chuỗi model thô mà entry được key theo, như một ID hoặc alias riêng của provider. Không phải lúc nào cũng có.
providerstrAPI provider phục vụ model này, như firstParty, bedrock, vertex, foundry, anthropicAws, mantle, hoặc gateway. Không phải lúc nào cũng có.

Stream event cho cập nhật message một phần trong lúc streaming. Chỉ nhận được khi include_partial_messages=True trong ClaudeAgentOptions. Import qua from claude_agent_sdk.types import StreamEvent.

@dataclass
class StreamEvent:
uuid: str
session_id: str
event: dict[str, Any] # The raw Claude API stream event
parent_tool_use_id: str | None = None
FieldTypeDescription
uuidstrĐịnh danh duy nhất cho event này
session_idstrĐịnh danh session
eventdict[str, Any]Dữ liệu stream event thô từ Claude API
parent_tool_use_idstr | NoneLuôn None. Stream event chỉ phát cho session chính. Để phân định subagent, dùng message hoàn chỉnh như AssistantMessage

Phát ra khi trạng thái rate limit thay đổi (ví dụ, từ "allowed" sang "allowed_warning"). Dùng cái này để cảnh báo người dùng trước khi họ chạm giới hạn cứng, hoặc để giảm tần suất khi trạng thái là "rejected".

@dataclass
class RateLimitEvent:
rate_limit_info: RateLimitInfo
uuid: str
session_id: str
FieldTypeDescription
rate_limit_infoRateLimitInfoTrạng thái rate limit hiện tại
uuidstrĐịnh danh event duy nhất
session_idstrĐịnh danh session

Trạng thái rate limit mang bởi RateLimitEvent.

RateLimitStatus = Literal["allowed", "allowed_warning", "rejected"]
RateLimitType = Literal[
"five_hour", "seven_day", "seven_day_opus", "seven_day_sonnet", "overage"
]
@dataclass
class RateLimitInfo:
status: RateLimitStatus
resets_at: int | None = None
rate_limit_type: RateLimitType | None = None
utilization: float | None = None
overage_status: RateLimitStatus | None = None
overage_resets_at: int | None = None
overage_disabled_reason: str | None = None
raw: dict[str, Any] = field(default_factory=dict)
FieldTypeDescription
statusRateLimitStatusTrạng thái hiện tại. "allowed_warning" nghĩa là gần chạm giới hạn; "rejected" nghĩa là đã chạm giới hạn
resets_atint | NoneUnix timestamp khi cửa sổ rate limit reset
rate_limit_typeRateLimitType | NoneCửa sổ rate limit nào áp dụng
utilizationfloat | NoneTỷ lệ rate limit đã dùng (0.0 tới 1.0)
overage_statusRateLimitStatus | NoneTrạng thái sử dụng pay-as-you-go overage, nếu áp dụng
overage_resets_atint | NoneUnix timestamp khi cửa sổ overage reset
overage_disabled_reasonstr | NoneVì sao overage không khả dụng, nếu status là "rejected"
rawdict[str, Any]Dict thô đầy đủ từ CLI, gồm cả field chưa mô hình hoá ở trên

Phát ra khi một background task bắt đầu. Một background task là bất kỳ thứ gì được theo dõi ngoài turn chính: một lệnh Bash chạy nền, một watch Monitor, một subagent sinh ra qua Agent tool, hoặc một remote agent. Field task_type cho biết đó là loại nào. Cách đặt tên này không liên quan tới việc đổi tên tool Task thành Agent.

@dataclass
class TaskStartedMessage(SystemMessage):
task_id: str
description: str
uuid: str
session_id: str
tool_use_id: str | None = None
task_type: str | None = None
FieldTypeDescription
task_idstrĐịnh danh duy nhất cho task
descriptionstrMô tả task
uuidstrĐịnh danh message duy nhất
session_idstrĐịnh danh session
tool_use_idstr | NoneTool use ID liên quan
task_typestr | NoneLoại background task: "local_bash" cho Bash chạy nền và Monitor watch, "local_agent", hoặc "remote_agent"

Dữ liệu token và thời gian cho một background task.

class TaskUsage(TypedDict):
total_tokens: int
tool_uses: int
duration_ms: int

Phát ra định kỳ với cập nhật tiến độ cho một background task đang chạy.

@dataclass
class TaskProgressMessage(SystemMessage):
task_id: str
description: str
usage: TaskUsage
uuid: str
session_id: str
tool_use_id: str | None = None
last_tool_name: str | None = None
FieldTypeDescription
task_idstrĐịnh danh duy nhất cho task
descriptionstrMô tả trạng thái hiện tại
usageTaskUsageToken usage của task tính tới hiện tại
uuidstrĐịnh danh message duy nhất
session_idstrĐịnh danh session
tool_use_idstr | NoneTool use ID liên quan
last_tool_namestr | NoneTên tool cuối cùng task đã dùng

Phát ra khi một background task hoàn tất, lỗi, hoặc bị dừng. Background task gồm lệnh Bash chạy run_in_background, Monitor watch, và background subagent.

@dataclass
class TaskNotificationMessage(SystemMessage):
task_id: str
status: TaskNotificationStatus # "completed" | "failed" | "stopped"
output_file: str
summary: str
uuid: str
session_id: str
tool_use_id: str | None = None
usage: TaskUsage | None = None
FieldTypeDescription
task_idstrĐịnh danh duy nhất cho task
statusTaskNotificationStatusMột trong "completed", "failed", hoặc "stopped"
output_filestrĐường dẫn tới file output của task
summarystrTóm tắt kết quả task
uuidstrĐịnh danh message duy nhất
session_idstrĐịnh danh session
tool_use_idstr | NoneTool use ID liên quan
usageTaskUsage | NoneToken usage cuối cùng của task

Union type của mọi content block.

ContentBlock = TextBlock | ThinkingBlock | ToolUseBlock | ToolResultBlock

Content block dạng text.

@dataclass
class TextBlock:
text: str

Content block dạng thinking (cho model có khả năng thinking).

@dataclass
class ThinkingBlock:
thinking: str
signature: str

Block yêu cầu dùng tool.

@dataclass
class ToolUseBlock:
id: str
name: str
input: dict[str, Any]

Block kết quả thực thi tool.

@dataclass
class ToolResultBlock:
tool_use_id: str
content: str | list[dict[str, Any]] | None = None
is_error: bool | None = None

Base exception class cho mọi lỗi SDK.

class ClaudeSDKError(Exception):
"""Base error for Claude SDK."""

Khi một query() single-shot kết thúc bằng kết quả lỗi, ví dụ lỗi giới hạn turn, SDK raise một Exception thường sau khi yield result message cuối, không phải một subclass của ClaudeSDKError.

Raise khi Claude Code CLI chưa được cài hoặc không tìm thấy.

class CLINotFoundError(CLIConnectionError):
def __init__(
self, message: str = "Claude Code not found", cli_path: str | None = None
):
"""
Args:
message: Error message (default: "Claude Code not found")
cli_path: Optional path to the CLI that was not found
"""

Raise khi kết nối tới Claude Code thất bại.

class CLIConnectionError(ClaudeSDKError):
"""Failed to connect to Claude Code."""

Raise khi process Claude Code lỗi.

class ProcessError(ClaudeSDKError):
def __init__(
self, message: str, exit_code: int | None = None, stderr: str | None = None
):
self.exit_code = exit_code
self.stderr = stderr

Raise khi parse JSON thất bại.

class CLIJSONDecodeError(ClaudeSDKError):
def __init__(self, line: str, original_error: Exception):
"""
Args:
line: The line that failed to parse
original_error: The original JSON decode exception
"""
self.line = line
self.original_error = original_error

Để có hướng dẫn đầy đủ về dùng hooks kèm ví dụ và pattern phổ biến, xem hướng dẫn Hooks.

Các loại hook event được hỗ trợ.

HookEvent = Literal[
"PreToolUse", # Called before tool execution
"PostToolUse", # Called after tool execution
"PostToolUseFailure", # Called when a tool execution fails
"UserPromptSubmit", # Called when user submits a prompt
"Stop", # Called when stopping execution
"SubagentStop", # Called when a subagent stops
"PreCompact", # Called before message compaction
"Notification", # Called for notification events
"SubagentStart", # Called when a subagent starts
"PermissionRequest", # Called when a permission decision is needed
]

Định nghĩa type cho hàm callback hook.

HookCallback = Callable[[HookInput, str | None, HookContext], Awaitable[HookJSONOutput]]

Tham số:

  • input: Hook input được đánh type mạnh với discriminated union dựa trên hook_event_name (xem HookInput)
  • tool_use_id: Định danh tool use tuỳ chọn (cho hook liên quan tới tool)
  • context: Hook context kèm thông tin bổ sung

Trả về một HookJSONOutput có thể chứa:

  • decision: "block" để chặn hành động
  • systemMessage: thông báo cảnh báo hiển thị cho người dùng
  • hookSpecificOutput: Dữ liệu output đặc thù cho hook

Thông tin context truyền cho callback hook.

class HookContext(TypedDict):
signal: Any | None # Future: abort signal support

Cấu hình để khớp hook với event hoặc tool cụ thể.

@dataclass
class HookMatcher:
matcher: str | None = (
None # Tool name or pattern to match (e.g., "Bash", "Write|Edit")
)
hooks: list[HookCallback] = field(
default_factory=list
) # List of callbacks to execute
timeout: float | None = (
None # Timeout in seconds. When omitted, the per-event default applies:
# 600 for most events, 30 for UserPromptSubmit
)

Union type của mọi loại hook input. Type thực tế phụ thuộc vào field hook_event_name.

HookInput = (
PreToolUseHookInput
| PostToolUseHookInput
| PostToolUseFailureHookInput
| UserPromptSubmitHookInput
| StopHookInput
| SubagentStopHookInput
| PreCompactHookInput
| NotificationHookInput
| SubagentStartHookInput
| PermissionRequestHookInput
)

Các field cơ bản có mặt ở mọi loại hook input.

class BaseHookInput(TypedDict):
session_id: str
transcript_path: str
cwd: str
permission_mode: NotRequired[str]
FieldTypeDescription
session_idstrĐịnh danh session hiện tại
transcript_pathstrĐường dẫn tới file transcript của session
cwdstrThư mục làm việc hiện tại
permission_modestr (optional)Permission mode hiện tại

Dữ liệu input cho hook event PreToolUse.

class PreToolUseHookInput(BaseHookInput):
hook_event_name: Literal["PreToolUse"]
tool_name: str
tool_input: dict[str, Any]
tool_use_id: str
agent_id: NotRequired[str]
agent_type: NotRequired[str]
FieldTypeDescription
hook_event_nameLiteral["PreToolUse"]Luôn “PreToolUse”
tool_namestrTên tool sắp được thực thi
tool_inputdict[str, Any]Tham số đầu vào cho tool
tool_use_idstrĐịnh danh duy nhất cho lần dùng tool này
agent_idstr (optional)Định danh subagent, có mặt khi hook chạy trong một subagent
agent_typestr (optional)Loại subagent, có mặt khi hook chạy trong một subagent

Dữ liệu input cho hook event PostToolUse.

class PostToolUseHookInput(BaseHookInput):
hook_event_name: Literal["PostToolUse"]
tool_name: str
tool_input: dict[str, Any]
tool_response: Any
tool_use_id: str
agent_id: NotRequired[str]
agent_type: NotRequired[str]
FieldTypeDescription
hook_event_nameLiteral["PostToolUse"]Luôn “PostToolUse”
tool_namestrTên tool đã được thực thi
tool_inputdict[str, Any]Tham số đầu vào đã dùng
tool_responseAnyPhản hồi từ lần thực thi tool
tool_use_idstrĐịnh danh duy nhất cho lần dùng tool này
agent_idstr (optional)Định danh subagent, có mặt khi hook chạy trong một subagent
agent_typestr (optional)Loại subagent, có mặt khi hook chạy trong một subagent

Dữ liệu input cho hook event PostToolUseFailure. Được gọi khi một lần thực thi tool thất bại.

class PostToolUseFailureHookInput(BaseHookInput):
hook_event_name: Literal["PostToolUseFailure"]
tool_name: str
tool_input: dict[str, Any]
tool_use_id: str
error: str
is_interrupt: NotRequired[bool]
agent_id: NotRequired[str]
agent_type: NotRequired[str]
FieldTypeDescription
hook_event_nameLiteral["PostToolUseFailure"]Luôn “PostToolUseFailure”
tool_namestrTên tool đã thất bại
tool_inputdict[str, Any]Tham số đầu vào đã dùng
tool_use_idstrĐịnh danh duy nhất cho lần dùng tool này
errorstrThông báo lỗi từ lần thực thi thất bại
is_interruptbool (optional)Thất bại có phải do interrupt hay không
agent_idstr (optional)Định danh subagent, có mặt khi hook chạy trong một subagent
agent_typestr (optional)Loại subagent, có mặt khi hook chạy trong một subagent

Dữ liệu input cho hook event UserPromptSubmit.

class UserPromptSubmitHookInput(BaseHookInput):
hook_event_name: Literal["UserPromptSubmit"]
prompt: str
FieldTypeDescription
hook_event_nameLiteral["UserPromptSubmit"]Luôn “UserPromptSubmit”
promptstrPrompt người dùng đã gửi

Dữ liệu input cho hook event Stop.

class StopHookInput(BaseHookInput):
hook_event_name: Literal["Stop"]
stop_hook_active: bool
FieldTypeDescription
hook_event_nameLiteral["Stop"]Luôn “Stop”
stop_hook_activeboolStop hook có đang hoạt động hay không

Dữ liệu input cho hook event SubagentStop.

class SubagentStopHookInput(BaseHookInput):
hook_event_name: Literal["SubagentStop"]
stop_hook_active: bool
agent_id: str
agent_transcript_path: str
agent_type: str
FieldTypeDescription
hook_event_nameLiteral["SubagentStop"]Luôn “SubagentStop”
stop_hook_activeboolStop hook có đang hoạt động hay không
agent_idstrĐịnh danh duy nhất cho subagent
agent_transcript_pathstrĐường dẫn tới file transcript của subagent
agent_typestrLoại subagent

Dữ liệu input cho hook event PreCompact.

class PreCompactHookInput(BaseHookInput):
hook_event_name: Literal["PreCompact"]
trigger: Literal["manual", "auto"]
custom_instructions: str | None
FieldTypeDescription
hook_event_nameLiteral["PreCompact"]Luôn “PreCompact”
triggerLiteral["manual", "auto"]Điều gì kích hoạt compaction
custom_instructionsstr | NoneHướng dẫn tuỳ chỉnh cho compaction

Dữ liệu input cho hook event Notification.

class NotificationHookInput(BaseHookInput):
hook_event_name: Literal["Notification"]
message: str
title: NotRequired[str]
notification_type: str
FieldTypeDescription
hook_event_nameLiteral["Notification"]Luôn “Notification”
messagestrNội dung thông báo
titlestr (optional)Tiêu đề thông báo
notification_typestrLoại thông báo

Dữ liệu input cho hook event SubagentStart.

class SubagentStartHookInput(BaseHookInput):
hook_event_name: Literal["SubagentStart"]
agent_id: str
agent_type: str
FieldTypeDescription
hook_event_nameLiteral["SubagentStart"]Luôn “SubagentStart”
agent_idstrĐịnh danh duy nhất cho subagent
agent_typestrLoại subagent

Dữ liệu input cho hook event PermissionRequest. Cho phép hook xử lý quyết định permission bằng code.

class PermissionRequestHookInput(BaseHookInput):
hook_event_name: Literal["PermissionRequest"]
tool_name: str
tool_input: dict[str, Any]
permission_suggestions: NotRequired[list[Any]]
agent_id: NotRequired[str]
agent_type: NotRequired[str]
FieldTypeDescription
hook_event_nameLiteral["PermissionRequest"]Luôn “PermissionRequest”
tool_namestrTên tool đang yêu cầu permission
tool_inputdict[str, Any]Tham số đầu vào cho tool
permission_suggestionslist[Any] (optional)Gợi ý cập nhật permission từ CLI
agent_idstr (optional)Định danh subagent, có mặt khi hook chạy trong một subagent
agent_typestr (optional)Loại subagent, có mặt khi hook chạy trong một subagent

Union type cho giá trị trả về của callback hook.

HookJSONOutput = AsyncHookJSONOutput | SyncHookJSONOutput

Output hook đồng bộ kèm field control và decision.

class SyncHookJSONOutput(TypedDict):
# Control fields
continue_: NotRequired[bool] # Whether to proceed (default: True)
suppressOutput: NotRequired[bool] # Hide stdout from transcript
stopReason: NotRequired[str] # Message when continue is False
# Decision fields
decision: NotRequired[Literal["block"]]
systemMessage: NotRequired[str] # Warning message for user
reason: NotRequired[str] # Feedback for Claude
# Hook-specific output
hookSpecificOutput: NotRequired[HookSpecificOutput]

Một TypedDict chứa tên hook event và field đặc thù cho event. Hình dạng phụ thuộc vào giá trị hookEventName. Để biết đầy đủ field khả dụng theo từng hook event, xem “Control execution with hooks”.

Một discriminated union của các type output đặc thù cho từng event. Field hookEventName quyết định field nào hợp lệ.

class PreToolUseHookSpecificOutput(TypedDict):
hookEventName: Literal["PreToolUse"]
permissionDecision: NotRequired[Literal["allow", "deny", "ask", "defer"]]
permissionDecisionReason: NotRequired[str]
updatedInput: NotRequired[dict[str, Any]]
additionalContext: NotRequired[str]
class PostToolUseHookSpecificOutput(TypedDict):
hookEventName: Literal["PostToolUse"]
additionalContext: NotRequired[str]
updatedToolOutput: NotRequired[Any]
updatedMCPToolOutput: NotRequired[Any] # Deprecated: use updatedToolOutput, which works for all tools
class PostToolUseFailureHookSpecificOutput(TypedDict):
hookEventName: Literal["PostToolUseFailure"]
additionalContext: NotRequired[str]
class UserPromptSubmitHookSpecificOutput(TypedDict):
hookEventName: Literal["UserPromptSubmit"]
additionalContext: NotRequired[str]
class NotificationHookSpecificOutput(TypedDict):
hookEventName: Literal["Notification"]
additionalContext: NotRequired[str]
class SubagentStartHookSpecificOutput(TypedDict):
hookEventName: Literal["SubagentStart"]
additionalContext: NotRequired[str]
class PermissionRequestHookSpecificOutput(TypedDict):
hookEventName: Literal["PermissionRequest"]
decision: dict[str, Any]
HookSpecificOutput = (
PreToolUseHookSpecificOutput
| PostToolUseHookSpecificOutput
| PostToolUseFailureHookSpecificOutput
| UserPromptSubmitHookSpecificOutput
| NotificationHookSpecificOutput
| SubagentStartHookSpecificOutput
| PermissionRequestHookSpecificOutput
)

Output hook bất đồng bộ, hoãn việc thực thi hook.

class AsyncHookJSONOutput(TypedDict):
async_: Literal[True] # Set to True to defer execution
asyncTimeout: NotRequired[int] # Timeout in milliseconds

Ví dụ này đăng ký hai hook: một hook chặn lệnh bash nguy hiểm như rm -rf /, và một hook khác ghi log mọi lần dùng tool để audit. Hook bảo mật chỉ chạy trên lệnh Bash (qua matcher), trong khi hook logging chạy trên mọi tool.

import asyncio
from claude_agent_sdk import query, ClaudeAgentOptions, HookMatcher, HookContext
from typing import Any
async def validate_bash_command(
input_data: dict[str, Any], tool_use_id: str | None, context: HookContext
) -> dict[str, Any]:
"""Validate and potentially block dangerous bash commands."""
if input_data["tool_name"] == "Bash":
command = input_data["tool_input"].get("command", "")
if "rm -rf /" in command:
return {
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"permissionDecision": "deny",
"permissionDecisionReason": "Dangerous command blocked",
}
}
return {}
async def log_tool_use(
input_data: dict[str, Any], tool_use_id: str | None, context: HookContext
) -> dict[str, Any]:
"""Log all tool usage for auditing."""
print(f"Tool used: {input_data.get('tool_name')}")
return {}
options = ClaudeAgentOptions(
hooks={
"PreToolUse": [
HookMatcher(
matcher="Bash", hooks=[validate_bash_command], timeout=120
), # 2 min for validation
HookMatcher(
hooks=[log_tool_use]
), # Applies to all tools (per-event default timeout)
],
"PostToolUse": [HookMatcher(hooks=[log_tool_use])],
}
)
async def main():
async for message in query(prompt="Analyze this codebase", options=options):
print(message)
asyncio.run(main())

Tài liệu về schema input/output cho mọi tool tích hợp sẵn của Claude Code. Dù Python SDK không export chúng dưới dạng type, chúng thể hiện cấu trúc input và output của tool trong message.

Tên tool: Agent. Tên cũ Task vẫn được chấp nhận như một alias, và danh sách tools trong SystemMessage init báo cáo tool này là Task để tương thích ngược.

Input:

{
"description": str, # A short (3-5 word) description of the task
"prompt": str, # The task for the agent to perform
"subagent_type": str | None, # The type of specialized agent to use
"model": "sonnet" | "opus" | "haiku" | "fable" | None, # Model override for this agent
"run_in_background": bool | None, # Agents run in the background by default; set to False to run synchronously
"name": str | None, # Name for the spawned agent
"team_name": str | None, # Deprecated; ignored
"mode": "acceptEdits" | "auto" | "bypassPermissions" | "default" | "dontAsk" | "plan" | None, # Deprecated; ignored. Subagents inherit the parent session's permission mode; agent-definition frontmatter may override it
"isolation": "worktree" | "remote" | None, # Isolation mode for the agent's changes
}

Khởi chạy một agent mới để xử lý tác vụ nhiều bước phức tạp một cách tự động.

Output (status: "completed"):

{
"status": "completed",
"agentId": str, # ID of the agent that ran
"agentType": str | None, # The subagent type that handled the task
"content": [ # Result content blocks
{
"type": "text",
"text": str,
"citations": list | None,
}
],
"resolvedModel": str | None, # Model the subagent started on
"modelsUsed": list[str] | None, # Models used in order, with consecutive repeats collapsed
"totalToolUseCount": int, # Number of tool calls the agent made
"totalDurationMs": int, # Execution duration in milliseconds
"totalTokens": int, # Total tokens used
"usage": { # Token usage statistics
"input_tokens": int,
"output_tokens": int,
"cache_creation_input_tokens": int | None,
"cache_read_input_tokens": int | None,
"server_tool_use": {"web_search_requests": int, "web_fetch_requests": int} | None,
"service_tier": str | None,
"cache_creation": {"ephemeral_1h_input_tokens": int, "ephemeral_5m_input_tokens": int} | None,
"inference_geo": str | None,
"speed": str | None,
"iterations": Any | None,
},
"toolStats": { # Aggregate tool activity for the run
"readCount": int,
"searchCount": int,
"bashCount": int,
"editFileCount": int,
"linesAdded": int,
"linesRemoved": int,
"otherToolCount": int,
"frameCount": int | None,
} | None,
"prompt": str, # The prompt the agent ran
"worktreePath": str | None, # Present for worktree-isolated runs
"worktreeBranch": str | None, # Present for worktree-isolated runs
}

Output (status: "async_launched"):

{
"status": "async_launched",
"isAsync": bool | None, # True on background launches
"agentId": str, # ID of the launched agent
"description": str, # The task description
"resolvedModel": str | None, # Model in use at the backgrounding transition
"modelsUsed": list[str] | None, # Models used before backgrounding, in order, with consecutive repeats collapsed
"prompt": str, # The prompt the agent runs
"outputFile": str, # File path where the agent's output is written
"canReadOutputFile": bool | None, # Whether the output file can be read directly
}

Output (status: "remote_launched"):

{
"status": "remote_launched",
"taskId": str, # ID of the remote task
"sessionUrl": str, # Link to the remote cloud session
"description": str, # The task description
"prompt": str, # The prompt the agent runs
"outputFile": str, # File path where the agent's output is written
}

Trả về kết quả từ subagent. Output được phân biệt theo field status: "completed" cho task đã hoàn tất, "async_launched" cho task chạy nền, và "remote_launched" cho task Claude Code gửi tới một remote cloud session, trong đó sessionUrl trỏ tới session đó và taskId định danh nó. Các lần chạy worktree-isolated gồm worktreePathworktreeBranch trong biến thể completed.

Ở biến thể completed, resolvedModel cho biết tên model subagent đã bắt đầu chạy, có thể khác với model input được yêu cầu khi availableModels hoặc một override khác áp dụng. Field này cần Claude Code v2.1.174 trở lên. Ở biến thể async_launched, resolvedModel cho biết tên model đang dùng khi agent chuyển sang chạy nền, nên một lần đổi model xảy ra trước khi backgrounding sẽ được phản ánh ở đó. Field modelsUsed ở cả hai biến thể liệt kê các model đã dùng theo thứ tự, gộp các lần lặp lại liên tiếp; chỉ được đặt khi model bị đổi giữa chừng. modelsUsed và hành vi resolvedModel tại thời điểm backgrounding cần Claude Code v2.1.212 trở lên.

Tên tool: AskUserQuestion

Hỏi người dùng câu hỏi làm rõ trong lúc thực thi. Xem “Handle approvals and user input” để biết chi tiết cách dùng.

Input:

{
"questions": [ # Questions to ask the user (1-4 questions)
{
"question": str, # The complete question to ask the user
"header": str, # Very short label displayed as a chip/tag (max 12 chars)
"options": [ # The available choices (2-4 options)
{
"label": str, # Display text for this option (1-5 words)
"description": str, # Explanation of what this option means
"preview": str | None, # Preview content rendered when the option is focused
}
],
"multiSelect": bool, # Set to true to allow multiple selections
}
],
"answers": dict[str, str] | None,
# User answers populated by the permission system. Multi-select
# answers are a comma-joined string of the selected labels; a
# list of labels is accepted on input and coerced to that form
"annotations": dict[str, dict] | None,
# Per-question annotations from the user, keyed by question text.
# Each value can carry "preview" (the selected option's preview
# content) and "notes" (free-text notes on the selection)
"metadata": dict | None, # Analytics metadata, such as {"source": "remember"}; not displayed to the user
}

Output:

{
"questions": [ # The questions that were asked
{
"question": str,
"header": str,
"options": [{"label": str, "description": str, "preview": str | None}],
"multiSelect": bool,
}
],
"answers": dict[str, str], # Maps question text to answer string
# Multi-select answers are comma-separated
"response": str | None,
# Freeform reply typed instead of answering the questions; when set,
# Claude receives "The user responded: ..." in place of the answer list
"annotations": dict[str, dict] | None, # Per-question "preview" and "notes" from the user's selections
"afkTimeoutMs": int | None, # Set when the dialog auto-resolved after this many milliseconds of user inactivity; absent when the user answered
}

Tên tool: Bash

Input:

{
"command": str, # The command to execute
"timeout": int | None, # Optional timeout in milliseconds (max 600000; higher values are clamped to the max)
"description": str | None, # Clear, concise description (5-10 words)
"run_in_background": bool | None, # Set to true to run in background
}

Output:

{
"output": str, # Combined stdout and stderr output
"exitCode": int, # Exit code of the command
"killed": bool | None, # Whether command was killed due to timeout
"shellId": str | None, # Shell ID for background processes
}

Tên tool: Monitor

Chạy một nguồn nền và gửi mỗi event tới Claude để nó phản ứng mà không cần polling: command chạy một script và phát một event cho mỗi dòng stdout, và ws mở một WebSocket và phát một event cho mỗi text frame. Cung cấp đúng một trong command hoặc ws.

Khi Monitor chạy một command, nó tuân theo cùng quy tắc permission như Bash; một WebSocket watch được hỏi chấp thuận riêng. Nguồn ws cần Claude Code v2.1.195 trở lên. Xem tham chiếu Monitor tool để biết hành vi và mức khả dụng theo provider.

Input:

{
"command": str | None, # Shell script; each stdout line is an event, exit ends the watch
"ws": dict | None, # WebSocket source: {"url": str, "protocols": list[str] | None}; each text frame is an event
"description": str, # Short description shown in notifications
"timeout_ms": int | None, # Kill after this deadline (default 300000, max 3600000)
"persistent": bool | None, # Run for the lifetime of the session; stop with TaskStop
}

Output:

{
"taskId": str, # ID of the background monitor task
"timeoutMs": int, # Timeout deadline in milliseconds (0 when persistent)
"persistent": bool | None, # True when running until TaskStop or session end
}

Tên tool: Edit

Input:

{
"file_path": str, # The absolute path to the file to modify
"old_string": str, # The text to replace
"new_string": str, # The text to replace it with
"replace_all": bool | None, # Replace all occurrences (default False)
}

Output:

{
"message": str, # Confirmation message
"replacements": int, # Number of replacements made
"file_path": str, # File path that was edited
}

Tên tool: Read

Input:

{
"file_path": str, # The absolute path to the file to read
"offset": int | None, # The line number to start reading from
"limit": int | None, # The number of lines to read
}

Output (file text):

{
"content": str, # File contents with line numbers
"total_lines": int, # Total number of lines in file
"lines_returned": int, # Lines actually returned
}

Output (hình ảnh):

{
"image": str, # Base64 encoded image data
"mime_type": str, # Image MIME type
"file_size": int, # File size in bytes
}

Tên tool: Write

Input:

{
"file_path": str, # The absolute path to the file to write
"content": str, # The content to write to the file
}

Output:

{
"message": str, # Success message
"bytes_written": int, # Number of bytes written
"file_path": str, # File path that was written
}

Tên tool: Glob

Input:

{
"pattern": str, # The glob pattern to match files against
"path": str | None, # The directory to search in (defaults to cwd)
}

Output:

{
"matches": list[str], # Array of matching file paths
"count": int, # Number of matches found
"search_path": str, # Search directory used
}

Tên tool: Grep

Input:

{
"pattern": str, # The regular expression pattern
"path": str | None, # File or directory to search in
"glob": str | None, # Glob pattern to filter files
"type": str | None, # File type to search
"output_mode": str | None, # "content", "files_with_matches", or "count"
"-i": bool | None, # Case insensitive search
"-n": bool | None, # Show line numbers
"-B": int | None, # Lines to show before each match
"-A": int | None, # Lines to show after each match
"-C": int | None, # Lines to show before and after
"head_limit": int | None, # Limit output to first N lines/entries
"multiline": bool | None, # Enable multiline mode
}

Output (content mode):

{
"matches": [
{
"file": str,
"line_number": int | None,
"line": str,
"before_context": list[str] | None,
"after_context": list[str] | None,
}
],
"total_matches": int,
}

Output (files_with_matches mode):

{
"files": list[str], # Files containing matches
"count": int, # Number of files with matches
}

Tên tool: NotebookEdit

Input:

{
"notebook_path": str, # Absolute path to the Jupyter notebook
"cell_id": str | None, # The ID of the cell to edit
"new_source": str, # The new source for the cell
"cell_type": "code" | "markdown" | None, # The type of the cell
"edit_mode": "replace" | "insert" | "delete" | None, # Edit operation type
}

Output:

{
"message": str, # Success message
"edit_type": "replaced" | "inserted" | "deleted", # Type of edit performed
"cell_id": str | None, # Cell ID that was affected
"total_cells": int, # Total cells in notebook after edit
}

Tên tool: WebFetch

Input:

{
"url": str, # The URL to fetch content from
"prompt": str, # The prompt to run on the fetched content
}

Output:

{
"bytes": int, # Size of the fetched content in bytes
"code": int, # HTTP response code
"codeText": str, # HTTP response code text
"result": str, # Processed result from applying the prompt to the content
"durationMs": int, # Time to fetch and process the content, in milliseconds
"url": str, # URL that was fetched
}

Tên tool: WebSearch

Input:

{
"query": str, # The search query to use
"allowed_domains": list[str] | None, # Only include results from these domains
"blocked_domains": list[str] | None, # Never include results from these domains
}

Output:

{
"query": str, # The search query
"results": list[str | {"tool_use_id": str, "content": list[{"title": str, "url": str}]}],
"durationSeconds": float, # Search duration in seconds
}

Tên tool: TodoWrite

Input:

{
"todos": [
{
"content": str, # The task description
"status": "pending" | "in_progress" | "completed", # Task status
"activeForm": str, # Active form of the description
}
]
}

Output:

{
"message": str, # Success message
"stats": {"total": int, "pending": int, "in_progress": int, "completed": int},
}

Tên tool: TaskCreate

Input:

{
"subject": str, # Short task title
"description": str, # Detailed task body
"activeForm": str | None, # Present-tense label shown while in progress
"metadata": dict | None, # Arbitrary caller metadata
}

Output:

{
"task": {"id": str, "subject": str}, # Created task with assigned ID
}

Tên tool: TaskUpdate

Input:

{
"taskId": str, # ID of the task to patch
"status": Literal["pending", "in_progress", "completed", "deleted"] | None,
"subject": str | None,
"description": str | None,
"activeForm": str | None,
"addBlocks": list[str] | None, # Task IDs this task now blocks
"addBlockedBy": list[str] | None, # Task IDs that now block this task
"owner": str | None,
"metadata": dict | None,
}

Output:

{
"success": bool,
"taskId": str,
"updatedFields": list[str], # Names of fields that changed
"error": str | None,
"statusChange": {"from": str, "to": str} | None,
}

Tên tool: TaskGet

Input:

{
"taskId": str, # ID of the task to read
}

Output:

{
"task": {
"id": str,
"subject": str,
"description": str,
"status": Literal["pending", "in_progress", "completed"],
"blocks": list[str],
"blockedBy": list[str],
} | None, # None when the ID is not found
}

Tên tool: TaskList

Input:

{}

Output:

{
"tasks": [
{
"id": str,
"subject": str,
"status": Literal["pending", "in_progress", "completed"],
"owner": str | None,
"blockedBy": list[str],
}
],
}

Tên tool: TaskOutput. Tên cũ BashOutput vẫn được chấp nhận như một alias.

Input:

{
"task_id": str, # The task ID to get output from
"block": bool, # Whether to wait for completion (default True)
"timeout": int, # Max wait time in ms (default 30000)
}

Output:

{
"retrieval_status": "success" | "timeout" | "not_ready", # Whether the output was retrieved
"task": dict | None, # Task details: task_id, task_type, status, description, output, plus type-specific fields such as exitCode
}

Tên tool: TaskStop. Tên cũ KillShellKillBash vẫn được chấp nhận như alias.

Input:

{
"task_id": str | None, # The ID of the background task to stop
"shell_id": str | None, # Deprecated: use task_id instead
}

Output:

{
"message": str, # Status message about the operation
"task_id": str, # The ID of the task that was stopped
"task_type": str, # The type of the task that was stopped
"command": str | None, # The command or description of the stopped task
}

Tên tool: ExitPlanMode

Input:

{
"plan": str # The plan to run by the user for approval
}

Output:

{
"message": str, # Confirmation message
"approved": bool | None, # Whether user approved the plan
}

Tên tool: ListMcpResourcesTool

Input:

{
"server": str | None # Optional server name to filter resources by
}

Output:

{
"resources": [
{
"uri": str,
"name": str,
"description": str | None,
"mimeType": str | None,
"server": str,
}
],
"total": int,
}

Tên tool: ReadMcpResourceTool

Input:

{
"server": str, # The MCP server name
"uri": str, # The resource URI to read
}

Output:

{
"contents": [
{"uri": str, "mimeType": str | None, "text": str | None, "blob": str | None}
],
"server": str,
}
from claude_agent_sdk import (
ClaudeSDKClient,
ClaudeAgentOptions,
AssistantMessage,
TextBlock,
)
import asyncio
class ConversationSession:
"""Maintains a single conversation session with Claude."""
def __init__(self, options: ClaudeAgentOptions | None = None):
self.client = ClaudeSDKClient(options)
self.turn_count = 0
async def start(self):
await self.client.connect()
print("Starting conversation session. Claude will remember context.")
print(
"Commands: 'exit' to quit, 'interrupt' to stop current task, 'new' for new session"
)
while True:
user_input = input(f"\n[Turn {self.turn_count + 1}] You: ")
if user_input.lower() == "exit":
break
elif user_input.lower() == "interrupt":
await self.client.interrupt()
print("Task interrupted!")
continue
elif user_input.lower() == "new":
# Disconnect and reconnect for a fresh session
await self.client.disconnect()
await self.client.connect()
self.turn_count = 0
print("Started new conversation session (previous context cleared)")
continue
# Send message - the session retains all previous messages
await self.client.query(user_input)
self.turn_count += 1
# Process response
print(f"[Turn {self.turn_count}] Claude: ", end="")
async for message in self.client.receive_response():
if isinstance(message, AssistantMessage):
for block in message.content:
if isinstance(block, TextBlock):
print(block.text, end="")
print() # New line after response
await self.client.disconnect()
print(f"Conversation ended after {self.turn_count} turns.")
async def main():
options = ClaudeAgentOptions(
allowed_tools=["Read", "Write", "Bash"], permission_mode="acceptEdits"
)
session = ConversationSession(options)
await session.start()
# Example conversation:
# Turn 1 - You: "Create a file called hello.py"
# Turn 1 - Claude: "I'll create a hello.py file for you..."
# Turn 2 - You: "What's in that file?"
# Turn 2 - Claude: "The hello.py file I just created contains..." (remembers!)
# Turn 3 - You: "Add a main function to it"
# Turn 3 - Claude: "I'll add a main function to hello.py..." (knows which file!)
asyncio.run(main())
from claude_agent_sdk import (
ClaudeSDKClient,
ClaudeAgentOptions,
HookMatcher,
HookContext,
)
import asyncio
from typing import Any
async def pre_tool_logger(
input_data: dict[str, Any], tool_use_id: str | None, context: HookContext
) -> dict[str, Any]:
"""Log all tool usage before execution."""
tool_name = input_data.get("tool_name", "unknown")
print(f"[PRE-TOOL] About to use: {tool_name}")
# You can modify or block the tool execution here
if tool_name == "Bash" and "rm -rf" in str(input_data.get("tool_input", {})):
return {
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"permissionDecision": "deny",
"permissionDecisionReason": "Dangerous command blocked",
}
}
return {}
async def post_tool_logger(
input_data: dict[str, Any], tool_use_id: str | None, context: HookContext
) -> dict[str, Any]:
"""Log results after tool execution."""
tool_name = input_data.get("tool_name", "unknown")
print(f"[POST-TOOL] Completed: {tool_name}")
return {}
async def user_prompt_modifier(
input_data: dict[str, Any], tool_use_id: str | None, context: HookContext
) -> dict[str, Any]:
"""Add context to user prompts."""
original_prompt = input_data.get("prompt", "")
# Add a timestamp as additional context for Claude to see
from datetime import datetime
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
return {
"hookSpecificOutput": {
"hookEventName": "UserPromptSubmit",
"additionalContext": f"[Submitted at {timestamp}] Original prompt: {original_prompt}",
}
}
async def main():
options = ClaudeAgentOptions(
hooks={
"PreToolUse": [
HookMatcher(hooks=[pre_tool_logger]),
HookMatcher(matcher="Bash", hooks=[pre_tool_logger]),
],
"PostToolUse": [HookMatcher(hooks=[post_tool_logger])],
"UserPromptSubmit": [HookMatcher(hooks=[user_prompt_modifier])],
},
allowed_tools=["Read", "Write", "Bash"],
)
async with ClaudeSDKClient(options=options) as client:
await client.query("List files in current directory")
async for message in client.receive_response():
# Hooks will automatically log tool usage
pass
asyncio.run(main())
from claude_agent_sdk import (
ClaudeSDKClient,
ClaudeAgentOptions,
AssistantMessage,
ToolUseBlock,
ToolResultBlock,
TextBlock,
)
import asyncio
async def monitor_progress():
options = ClaudeAgentOptions(
allowed_tools=["Write", "Bash"], permission_mode="acceptEdits"
)
async with ClaudeSDKClient(options=options) as client:
await client.query("Create 5 Python files with different sorting algorithms")
# Monitor progress in real-time
async for message in client.receive_response():
if isinstance(message, AssistantMessage):
for block in message.content:
if isinstance(block, ToolUseBlock):
if block.name == "Write":
file_path = block.input.get("file_path", "")
print(f"Creating: {file_path}")
elif isinstance(block, ToolResultBlock):
print("Completed tool execution")
elif isinstance(block, TextBlock):
print(f"Claude says: {block.text[:100]}...")
print("Task completed!")
asyncio.run(monitor_progress())
from claude_agent_sdk import query, ClaudeAgentOptions, AssistantMessage, ToolUseBlock
import asyncio
async def create_project():
options = ClaudeAgentOptions(
allowed_tools=["Read", "Write", "Bash"],
permission_mode="acceptEdits",
)
async for message in query(
prompt="Create a Python project structure with setup.py", options=options
):
if isinstance(message, AssistantMessage):
for block in message.content:
if isinstance(block, ToolUseBlock):
print(f"Using tool: {block.name}")
asyncio.run(create_project())
import asyncio
from claude_agent_sdk import query, CLINotFoundError, ProcessError, CLIJSONDecodeError
async def main():
try:
async for message in query(prompt="Hello"):
print(message)
except CLINotFoundError:
print(
"Claude Code CLI not found. Try reinstalling: pip install --force-reinstall claude-agent-sdk"
)
except ProcessError as e:
print(f"Process failed with exit code: {e.exit_code}")
except CLIJSONDecodeError as e:
print(f"Failed to parse response: {e}")
# A single-shot query() raises a plain Exception after yielding an error result
except Exception as e:
print(f"Query ended with an error result: {e}")
asyncio.run(main())
from claude_agent_sdk import ClaudeSDKClient
import asyncio
async def interactive_session():
async with ClaudeSDKClient() as client:
# Send initial message
await client.query("What's the weather like?")
# Process responses
async for msg in client.receive_response():
print(msg)
# Send follow-up
await client.query("Tell me more about that")
# Process follow-up response
async for msg in client.receive_response():
print(msg)
asyncio.run(interactive_session())
from claude_agent_sdk import (
ClaudeSDKClient,
ClaudeAgentOptions,
tool,
create_sdk_mcp_server,
AssistantMessage,
TextBlock,
)
import asyncio
from typing import Any
# Define custom tools with @tool decorator
@tool("calculate", "Perform mathematical calculations", {"expression": str})
async def calculate(args: dict[str, Any]) -> dict[str, Any]:
try:
result = eval(args["expression"], {"__builtins__": {}})
return {"content": [{"type": "text", "text": f"Result: {result}"}]}
except Exception as e:
return {
"content": [{"type": "text", "text": f"Error: {str(e)}"}],
"is_error": True,
}
@tool("get_time", "Get current time", {})
async def get_time(args: dict[str, Any]) -> dict[str, Any]:
from datetime import datetime
current_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
return {"content": [{"type": "text", "text": f"Current time: {current_time}"}]}
async def main():
# Create SDK MCP server with custom tools
my_server = create_sdk_mcp_server(
name="utilities", version="1.0.0", tools=[calculate, get_time]
)
# Configure options with the server
options = ClaudeAgentOptions(
mcp_servers={"utils": my_server},
allowed_tools=["mcp__utils__calculate", "mcp__utils__get_time"],
)
# Use ClaudeSDKClient for interactive tool usage
async with ClaudeSDKClient(options=options) as client:
await client.query("What's 123 * 456?")
# Process calculation response
async for message in client.receive_response():
if isinstance(message, AssistantMessage):
for block in message.content:
if isinstance(block, TextBlock):
print(f"Calculation: {block.text}")
# Follow up with time query
await client.query("What time is it now?")
async for message in client.receive_response():
if isinstance(message, AssistantMessage):
for block in message.content:
if isinstance(block, TextBlock):
print(f"Time: {block.text}")
asyncio.run(main())

Cấu hình cho hành vi sandbox. Dùng cái này để bật sandbox cho command và cấu hình giới hạn network bằng code.

class SandboxSettings(TypedDict, total=False):
enabled: bool
autoAllowBashIfSandboxed: bool
excludedCommands: list[str]
allowUnsandboxedCommands: bool
network: SandboxNetworkConfig
ignoreViolations: SandboxIgnoreViolations
enableWeakerNestedSandbox: bool
PropertyTypeDefaultDescription
enabledboolFalseBật sandbox mode cho việc thực thi command
autoAllowBashIfSandboxedboolTrueTự động chấp thuận lệnh bash khi sandbox được bật
excludedCommandslist[str][]Command luôn bỏ qua giới hạn sandbox (ví dụ, ["docker"]). Các command này chạy không sandbox tự động, mà không cần model can thiệp
allowUnsandboxedCommandsboolTrueCho phép model yêu cầu chạy command ngoài sandbox. Khi True, model có thể đặt dangerouslyDisableSandbox trong tool input, việc này rơi xuống hệ thống permissions
networkSandboxNetworkConfigNoneCấu hình sandbox riêng cho network
ignoreViolationsSandboxIgnoreViolationsNoneCấu hình vi phạm sandbox nào cần bỏ qua
enableWeakerNestedSandboxboolFalseBật một sandbox nested yếu hơn để tương thích
import asyncio
from claude_agent_sdk import query, ClaudeAgentOptions
sandbox_settings = {
"enabled": True,
"autoAllowBashIfSandboxed": True,
"failIfUnavailable": True,
"network": {"allowLocalBinding": True},
}
async def main():
try:
async for message in query(
prompt="Build and test my project",
options=ClaudeAgentOptions(sandbox=sandbox_settings),
):
print(message)
except Exception as error:
# A single-shot query() raises after yielding an error result,
# such as when failIfUnavailable is set and the sandbox can't start.
print(f"Session ended with an error: {error}")
asyncio.run(main())

Cấu hình riêng cho network của sandbox mode. Các setting này áp dụng cho lệnh Bash chạy sandbox khi enabledTrue trong SandboxSettings cha. Chúng không giới hạn WebFetch tool, tool này dùng permission rules thay thế.

class SandboxNetworkConfig(TypedDict, total=False):
allowedDomains: list[str]
deniedDomains: list[str]
allowManagedDomainsOnly: bool
allowUnixSockets: list[str]
allowAllUnixSockets: bool
allowLocalBinding: bool
allowMachLookup: list[str]
httpProxyPort: int
socksProxyPort: int
PropertyTypeDefaultDescription
allowedDomainslist[str][]Tên domain mà process sandbox được truy cập
deniedDomainslist[str][]Tên domain mà process sandbox không được truy cập. Ưu tiên hơn allowedDomains
allowManagedDomainsOnlyboolFalseChỉ dành cho managed-settings: khi đặt trong managed settings, bỏ qua allowedDomains từ nguồn settings không phải managed. Không có tác dụng khi đặt qua SDK options
allowUnixSocketslist[str][]Đường dẫn Unix socket mà process được truy cập (ví dụ, Docker socket)
allowAllUnixSocketsboolFalseCho phép truy cập mọi Unix socket
allowLocalBindingboolFalseCho phép process bind vào port cục bộ (ví dụ, cho dev server)
allowMachLookuplist[str][]Chỉ macOS: tên XPC/Mach service được phép. Hỗ trợ wildcard ở cuối
httpProxyPortintNonePort HTTP proxy cho request network
socksProxyPortintNonePort SOCKS proxy cho request network

Cấu hình để bỏ qua các vi phạm sandbox cụ thể.

class SandboxIgnoreViolations(TypedDict, total=False):
file: list[str]
network: list[str]
PropertyTypeDefaultDescription
filelist[str][]Pattern đường dẫn file cần bỏ qua vi phạm
networklist[str][]Pattern network cần bỏ qua vi phạm

Fallback về Permissions cho command không sandbox

Phần tiêu đề “Fallback về Permissions cho command không sandbox”

Khi allowUnsandboxedCommands được bật, model có thể yêu cầu chạy command ngoài sandbox bằng cách đặt dangerouslyDisableSandbox: True trong tool input. Các yêu cầu này rơi xuống hệ thống permissions hiện có, nghĩa là handler can_use_tool của bạn sẽ được gọi, cho phép bạn triển khai logic authorization tuỳ chỉnh.

import asyncio
from claude_agent_sdk import (
query,
ClaudeAgentOptions,
HookMatcher,
PermissionResultAllow,
PermissionResultDeny,
ToolPermissionContext,
)
def is_command_authorized(command: str | None) -> bool:
# Replace with your own authorization logic
return False
async def can_use_tool(
tool: str, input: dict, context: ToolPermissionContext
) -> PermissionResultAllow | PermissionResultDeny:
# Check if the model is requesting to bypass the sandbox
if tool == "Bash" and input.get("dangerouslyDisableSandbox"):
# The model is requesting to run this command outside the sandbox
print(f"Unsandboxed command requested: {input.get('command')}")
if is_command_authorized(input.get("command")):
return PermissionResultAllow()
return PermissionResultDeny(
message="Command not authorized for unsandboxed execution"
)
return PermissionResultAllow()
# Required: dummy hook keeps the stream open for can_use_tool
async def dummy_hook(input_data, tool_use_id, context):
return {"continue_": True}
async def prompt_stream():
yield {
"type": "user",
"message": {"role": "user", "content": "Deploy my application"},
}
async def main():
async for message in query(
prompt=prompt_stream(),
options=ClaudeAgentOptions(
sandbox={
"enabled": True,
"allowUnsandboxedCommands": True, # Model can request unsandboxed execution
},
permission_mode="default",
can_use_tool=can_use_tool,
hooks={"PreToolUse": [HookMatcher(matcher=None, hooks=[dummy_hook])]},
),
):
print(message)
asyncio.run(main())

Pattern này cho phép bạn:

  • Audit yêu cầu của model: Ghi log khi model yêu cầu thực thi không sandbox
  • Triển khai allowlist: Chỉ cho phép một số command cụ thể chạy không sandbox
  • Thêm luồng phê duyệt: Yêu cầu authorization tường minh cho các thao tác đặc quyền
  • SDK overview - Khái niệm SDK tổng quát
  • TypeScript SDK reference - Tài liệu TypeScript SDK
  • CLI reference - Giao diện dòng lệnh
  • Common workflows - Hướng dẫn từng bước