Referensi Agent SDK - Python
Instalasi
pip install claude-agent-sdkMemilih Antara query() dan ClaudeSDKClient
query() dan ClaudeSDKClientPython SDK menyediakan dua cara untuk berinteraksi dengan Claude Code:
Perbandingan Cepat
| Fitur | query() | ClaudeSDKClient |
|---|---|---|
| Sesi | Membuat sesi baru setiap kali | Menggunakan kembali sesi yang sama |
| Percakapan | Pertukaran tunggal | Pertukaran ganda dalam konteks yang sama |
| Koneksi | Dikelola secara otomatis | Kontrol manual |
| Input Streaming | ✅ Didukung | ✅ Didukung |
| Interupsi | ❌ Tidak didukung | ✅ Didukung |
| Hooks | ❌ Tidak didukung | ✅ Didukung |
| Alat Kustom | ❌ Tidak didukung | ✅ Didukung |
| Lanjutkan Chat | ❌ Sesi baru setiap kali | ✅ Mempertahankan percakapan |
| Kasus Penggunaan | Tugas sekali jalan | Percakapan berkelanjutan |
Kapan Menggunakan query() (Sesi Baru Setiap Kali)
query() (Sesi Baru Setiap Kali)Terbaik untuk:
- Pertanyaan sekali jalan di mana Anda tidak memerlukan riwayat percakapan
- Tugas independen yang tidak memerlukan konteks dari pertukaran sebelumnya
- Skrip otomasi sederhana
- Ketika Anda menginginkan awal yang segar setiap kali
Kapan Menggunakan ClaudeSDKClient (Percakapan Berkelanjutan)
ClaudeSDKClient (Percakapan Berkelanjutan)Terbaik untuk:
- Melanjutkan percakapan - Ketika Anda memerlukan Claude untuk mengingat konteks
- Pertanyaan lanjutan - Membangun berdasarkan respons sebelumnya
- Aplikasi interaktif - Antarmuka obrolan, REPL
- Logika berbasis respons - Ketika tindakan berikutnya bergantung pada respons Claude
- Kontrol sesi - Mengelola siklus hidup percakapan secara eksplisit
Fungsi
query()
query()Membuat sesi baru untuk setiap interaksi dengan Claude Code. Mengembalikan iterator async yang menghasilkan pesan saat tiba. Setiap panggilan ke query() dimulai segar tanpa memori interaksi sebelumnya.
async def query(
*,
prompt: str | AsyncIterable[dict[str, Any]],
options: ClaudeAgentOptions | None = None
) -> AsyncIterator[Message]Parameter
| Parameter | Tipe | Deskripsi |
|---|---|---|
prompt | str | AsyncIterable[dict] | Prompt input sebagai string atau async iterable untuk mode streaming |
options | ClaudeAgentOptions | None | Objek konfigurasi opsional (default ke ClaudeAgentOptions() jika None) |
Pengembalian
Mengembalikan AsyncIterator[Message] yang menghasilkan pesan dari percakapan.
Contoh - Dengan opsi
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',
cwd="/home/user/project"
)
async for message in query(
prompt="Create a Python web server",
options=options
):
print(message)
asyncio.run(main())tool()
tool()Dekorator untuk mendefinisikan alat MCP dengan keamanan tipe.
def tool(
name: str,
description: str,
input_schema: type | dict[str, Any]
) -> Callable[[Callable[[Any], Awaitable[dict[str, Any]]]], SdkMcpTool[Any]]Parameter
| Parameter | Tipe | Deskripsi |
|---|---|---|
name | str | Pengidentifikasi unik untuk alat |
description | str | Deskripsi yang dapat dibaca manusia tentang apa yang dilakukan alat |
input_schema | type | dict[str, Any] | Skema yang mendefinisikan parameter input alat (lihat di bawah) |
Opsi Input Schema
-
Pemetaan tipe sederhana (direkomendasikan):
{"text": str, "count": int, "enabled": bool} -
Format JSON Schema (untuk validasi kompleks):
{ "type": "object", "properties": { "text": {"type": "string"}, "count": {"type": "integer", "minimum": 0} }, "required": ["text"] }
Pengembalian
Fungsi dekorator yang membungkus implementasi alat dan mengembalikan instance SdkMcpTool.
Contoh
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']}!"
}]
}create_sdk_mcp_server()
create_sdk_mcp_server()Buat server MCP dalam proses yang berjalan dalam aplikasi Python Anda.
def create_sdk_mcp_server(
name: str,
version: str = "1.0.0",
tools: list[SdkMcpTool[Any]] | None = None
) -> McpSdkServerConfigParameter
| Parameter | Tipe | Default | Deskripsi |
|---|---|---|---|
name | str | - | Pengidentifikasi unik untuk server |
version | str | "1.0.0" | String versi server |
tools | list[SdkMcpTool[Any]] | None | None | Daftar fungsi alat yang dibuat dengan dekorator @tool |
Pengembalian
Mengembalikan objek McpSdkServerConfig yang dapat diteruskan ke ClaudeAgentOptions.mcp_servers.
Contoh
from claude_agent_sdk import tool, create_sdk_mcp_server
@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"]
)Kelas
ClaudeSDKClient
ClaudeSDKClientMempertahankan sesi percakapan di seluruh pertukaran ganda. Ini adalah setara Python dari cara fungsi query() SDK TypeScript bekerja secara internal - ia membuat objek klien yang dapat melanjutkan percakapan.
Fitur Utama
- Kontinuitas Sesi: Mempertahankan konteks percakapan di seluruh panggilan
query()ganda - Percakapan Sama: Claude mengingat pesan sebelumnya dalam sesi
- Dukungan Interupsi: Dapat menghentikan Claude di tengah eksekusi
- Siklus Hidup Eksplisit: Anda mengontrol kapan sesi dimulai dan berakhir
- Alur Berbasis Respons: Dapat bereaksi terhadap respons dan mengirim tindak lanjut
- Alat Kustom & Hooks: Mendukung alat kustom (dibuat dengan dekorator
@tool) dan hooks
class ClaudeSDKClient:
def __init__(self, options: ClaudeAgentOptions | 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 disconnect(self) -> NoneMetode
| Metode | Deskripsi |
|---|---|
__init__(options) | Inisialisasi klien dengan konfigurasi opsional |
connect(prompt) | Terhubung ke Claude dengan prompt awal opsional atau aliran pesan |
query(prompt, session_id) | Kirim permintaan baru dalam mode streaming |
receive_messages() | Terima semua pesan dari Claude sebagai iterator async |
receive_response() | Terima pesan hingga dan termasuk ResultMessage |
interrupt() | Kirim sinyal interupsi (hanya bekerja dalam mode streaming) |
disconnect() | Putuskan koneksi dari Claude |
Dukungan Context Manager
Klien dapat digunakan sebagai async context manager untuk manajemen koneksi otomatis:
async with ClaudeSDKClient() as client:
await client.query("Hello Claude")
async for message in client.receive_response():
print(message)Penting: Saat melakukan iterasi atas pesan, hindari menggunakan
breakuntuk keluar lebih awal karena ini dapat menyebabkan masalah pembersihan asyncio. Sebaliknya, biarkan iterasi selesai secara alami atau gunakan flag untuk melacak kapan Anda telah menemukan apa yang Anda butuhkan.
Contoh - Melanjutkan percakapan
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 - Claude remembers 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())Contoh - Input streaming dengan ClaudeSDKClient
import asyncio
from claude_agent_sdk import ClaudeSDKClient
async def message_stream():
"""Generate messages dynamically."""
yield {"type": "text", "text": "Analyze the following data:"}
await asyncio.sleep(0.5)
yield {"type": "text", "text": "Temperature: 25°C"}
await asyncio.sleep(0.5)
yield {"type": "text", "text": "Humidity: 60%"}
await asyncio.sleep(0.5)
yield {"type": "text", "text": "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())Contoh - Menggunakan interupsi
import asyncio
from claude_agent_sdk import ClaudeSDKClient, ClaudeAgentOptions
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")
# Let it run for a bit
await asyncio.sleep(2)
# Interrupt the task
await client.interrupt()
print("Task interrupted!")
# Send a new command
await client.query("Just say hello instead")
async for message in client.receive_response():
# Process the new response
pass
asyncio.run(interruptible_task())Contoh - Kontrol izin tingkat lanjut
from claude_agent_sdk import (
ClaudeSDKClient,
ClaudeAgentOptions
)
async def custom_permission_handler(
tool_name: str,
input_data: dict,
context: dict
):
"""Custom logic for tool permissions."""
# Block writes to system directories
if tool_name == "Write" and input_data.get("file_path", "").startswith("/system/"):
return {
"behavior": "deny",
"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 {
"behavior": "allow",
"updatedInput": {**input_data, "file_path": safe_path}
}
# Allow everything else
return {
"behavior": "allow",
"updatedInput": input_data
}
async def main():
options = ClaudeAgentOptions(
can_use_tool=custom_permission_handler,
allowed_tools=["Read", "Write", "Edit"]
)
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())Tipe
SdkMcpTool
SdkMcpToolDefinisi untuk alat MCP SDK yang dibuat dengan dekorator @tool.
@dataclass
class SdkMcpTool(Generic[T]):
name: str
description: str
input_schema: type[T] | dict[str, Any]
handler: Callable[[T], Awaitable[dict[str, Any]]]| Properti | Tipe | Deskripsi |
|---|---|---|
name | str | Pengidentifikasi unik untuk alat |
description | str | Deskripsi yang dapat dibaca manusia |
input_schema | type[T] | dict[str, Any] | Skema untuk validasi input |
handler | Callable[[T], Awaitable[dict[str, Any]]] | Fungsi async yang menangani eksekusi alat |
ClaudeAgentOptions
ClaudeAgentOptionsDataclass konfigurasi untuk kueri Claude Code.
@dataclass
class ClaudeAgentOptions:
allowed_tools: list[str] = field(default_factory=list)
system_prompt: str | SystemPromptPreset | None = None
mcp_servers: dict[str, McpServerConfig] | str | Path = field(default_factory=dict)
permission_mode: PermissionMode | None = None
continue_conversation: bool = False
resume: str | None = None
max_turns: int | None = None
disallowed_tools: list[str] = field(default_factory=list)
model: str | None = None
permission_prompt_tool_name: str | None = None
cwd: 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
fork_session: bool = False
agents: dict[str, AgentDefinition] | None = None
setting_sources: list[SettingSource] | None = None| Properti | Tipe | Default | Deskripsi |
|---|---|---|---|
allowed_tools | list[str] | [] | Daftar nama alat yang diizinkan |
system_prompt | str | SystemPromptPreset | None | None | Konfigurasi prompt sistem. Teruskan string untuk prompt kustom, atau gunakan {"type": "preset", "preset": "claude_code"} untuk prompt sistem Claude Code. Tambahkan "append" untuk memperluas preset |
mcp_servers | dict[str, McpServerConfig] | str | Path | {} | Konfigurasi server MCP atau jalur ke file konfigurasi |
permission_mode | PermissionMode | None | None | Mode izin untuk penggunaan alat |
continue_conversation | bool | False | Lanjutkan percakapan terbaru |
resume | str | None | None | ID sesi untuk dilanjutkan |
max_turns | int | None | None | Jumlah maksimal putaran percakapan |
disallowed_tools | list[str] | [] | Daftar nama alat yang tidak diizinkan |
model | str | None | None | Model Claude yang digunakan |
permission_prompt_tool_name | str | None | None | Nama alat MCP untuk prompt izin |
cwd | str | Path | None | None | Direktori kerja saat ini |
settings | str | None | None | Jalur ke file pengaturan |
add_dirs | list[str | Path] | [] | Direktori tambahan yang dapat diakses Claude |
env | dict[str, str] | {} | Variabel lingkungan |
extra_args | dict[str, str | None] | {} | Argumen CLI tambahan untuk diteruskan langsung ke CLI |
max_buffer_size | int | None | None | Jumlah byte maksimal saat membuffer stdout CLI |
debug_stderr | Any | sys.stderr | Tidak digunakan lagi - Objek seperti file untuk output debug. Gunakan callback stderr sebagai gantinya |
stderr | Callable[[str], None] | None | None | Fungsi callback untuk output stderr dari CLI |
can_use_tool | CanUseTool | None | None | Fungsi callback izin alat |
hooks | dict[HookEvent, list[HookMatcher]] | None | None | Konfigurasi hook untuk mengintersepsi acara |
user | str | None | None | Pengidentifikasi pengguna |
include_partial_messages | bool | False | Sertakan acara streaming pesan parsial |
fork_session | bool | False | Saat melanjutkan dengan resume, fork ke ID sesi baru alih-alih melanjutkan sesi asli |
agents | dict[str, AgentDefinition] | None | None | Subagen yang didefinisikan secara terprogram |
plugins | list[SdkPluginConfig] | [] | Muat plugin kustom dari jalur lokal. Lihat Plugins untuk detail |
setting_sources | list[SettingSource] | None | None (tidak ada pengaturan) | Kontrol sumber pengaturan sistem file mana yang akan dimuat. Jika dihilangkan, tidak ada pengaturan yang dimuat. Catatan: Harus menyertakan "project" untuk memuat file CLAUDE.md |
SystemPromptPreset
SystemPromptPresetKonfigurasi untuk menggunakan prompt sistem preset Claude Code dengan penambahan opsional.
class SystemPromptPreset(TypedDict):
type: Literal["preset"]
preset: Literal["claude_code"]
append: NotRequired[str]| Bidang | Diperlukan | Deskripsi |
|---|---|---|
type | Ya | Harus "preset" untuk menggunakan prompt sistem preset |
preset | Ya | Harus "claude_code" untuk menggunakan prompt sistem Claude Code |
append | Tidak | Instruksi tambahan untuk ditambahkan ke prompt sistem preset |
SettingSource
SettingSourceMengontrol sumber konfigurasi berbasis sistem file mana yang dimuat pengaturan SDK.
SettingSource = Literal["user", "project", "local"]| Nilai | Deskripsi | Lokasi |
|---|---|---|
"user" | Pengaturan pengguna global | ~/.claude/settings.json |
"project" | Pengaturan proyek bersama (dikontrol versi) | .claude/settings.json |
"local" | Pengaturan proyek lokal (diabaikan git) | .claude/settings.local.json |
Perilaku default
Ketika setting_sources dihilangkan atau None, SDK tidak memuat pengaturan sistem file apa pun. Ini memberikan isolasi untuk aplikasi SDK.
Mengapa menggunakan setting_sources?
Muat semua pengaturan sistem file (perilaku warisan):
# Load all settings like SDK v0.0.x did
from claude_agent_sdk import query, ClaudeAgentOptions
async for message in query(
prompt="Analyze this code",
options=ClaudeAgentOptions(
setting_sources=["user", "project", "local"] # Load all settings
)
):
print(message)Muat hanya sumber pengaturan tertentu:
# Load only project settings, ignore user and local
async for message in query(
prompt="Run CI checks",
options=ClaudeAgentOptions(
setting_sources=["project"] # Only .claude/settings.json
)
):
print(message)Lingkungan pengujian dan CI:
# Ensure consistent behavior in CI by excluding local settings
async for message in query(
prompt="Run tests",
options=ClaudeAgentOptions(
setting_sources=["project"], # Only team-shared settings
permission_mode="bypassPermissions"
)
):
print(message)Aplikasi hanya SDK:
# Define everything programmatically (default behavior)
# No filesystem dependencies - setting_sources defaults to None
async for message in query(
prompt="Review this PR",
options=ClaudeAgentOptions(
# setting_sources=None is the default, no need to specify
agents={ /* ... */ },
mcp_servers={ /* ... */ },
allowed_tools=["Read", "Grep", "Glob"]
)
):
print(message)Memuat instruksi proyek CLAUDE.md:
# Load project settings to include CLAUDE.md files
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"], # Required to load CLAUDE.md from project
allowed_tools=["Read", "Write", "Edit"]
)
):
print(message)Preseden pengaturan
Ketika beberapa sumber dimuat, pengaturan digabungkan dengan preseden ini (tertinggi ke terendah):
- Pengaturan lokal (
.claude/settings.local.json) - Pengaturan proyek (
.claude/settings.json) - Pengaturan pengguna (
~/.claude/settings.json)
Opsi terprogram (seperti agents, allowed_tools) selalu menggantikan pengaturan sistem file.
AgentDefinition
AgentDefinitionKonfigurasi untuk subagen yang didefinisikan secara terprogram.
@dataclass
class AgentDefinition:
description: str
prompt: str
tools: list[str] | None = None
model: Literal["sonnet", "opus", "haiku", "inherit"] | None = None| Bidang | Diperlukan | Deskripsi |
|---|---|---|
description | Ya | Deskripsi bahasa alami tentang kapan menggunakan agen ini |
tools | Tidak | Array nama alat yang diizinkan. Jika dihilangkan, mewarisi semua alat |
prompt | Ya | Prompt sistem agen |
model | Tidak | Penggantian model untuk agen ini. Jika dihilangkan, menggunakan model utama |
PermissionMode
PermissionModeMode izin untuk mengontrol eksekusi alat.
PermissionMode = Literal[
"default", # Standard permission behavior
"acceptEdits", # Auto-accept file edits
"plan", # Planning mode - no execution
"bypassPermissions" # Bypass all permission checks (use with caution)
]McpSdkServerConfig
McpSdkServerConfigKonfigurasi untuk server MCP SDK yang dibuat dengan create_sdk_mcp_server().
class McpSdkServerConfig(TypedDict):
type: Literal["sdk"]
name: str
instance: Any # MCP Server instanceMcpServerConfig
McpServerConfigTipe union untuk konfigurasi server MCP.
McpServerConfig = McpStdioServerConfig | McpSSEServerConfig | McpHttpServerConfig | McpSdkServerConfigMcpStdioServerConfig
class McpStdioServerConfig(TypedDict):
type: NotRequired[Literal["stdio"]] # Optional for backwards compatibility
command: str
args: NotRequired[list[str]]
env: NotRequired[dict[str, str]]McpSSEServerConfig
class McpSSEServerConfig(TypedDict):
type: Literal["sse"]
url: str
headers: NotRequired[dict[str, str]]McpHttpServerConfig
class McpHttpServerConfig(TypedDict):
type: Literal["http"]
url: str
headers: NotRequired[dict[str, str]]SdkPluginConfig
SdkPluginConfigKonfigurasi untuk memuat plugin di SDK.
class SdkPluginConfig(TypedDict):
type: Literal["local"]
path: str| Bidang | Tipe | Deskripsi |
|---|---|---|
type | Literal["local"] | Harus "local" (hanya plugin lokal yang didukung saat ini) |
path | str | Jalur absolut atau relatif ke direktori plugin |
Contoh:
plugins=[
{"type": "local", "path": "./my-plugin"},
{"type": "local", "path": "/absolute/path/to/plugin"}
]Untuk informasi lengkap tentang membuat dan menggunakan plugin, lihat Plugins.
Tipe Pesan
Message
MessageTipe union dari semua pesan yang mungkin.
Message = UserMessage | AssistantMessage | SystemMessage | ResultMessageUserMessage
UserMessagePesan input pengguna.
@dataclass
class UserMessage:
content: str | list[ContentBlock]AssistantMessage
AssistantMessagePesan respons asisten dengan blok konten.
@dataclass
class AssistantMessage:
content: list[ContentBlock]
model: strSystemMessage
SystemMessagePesan sistem dengan metadata.
@dataclass
class SystemMessage:
subtype: str
data: dict[str, Any]ResultMessage
ResultMessagePesan hasil akhir dengan informasi biaya dan penggunaan.
@dataclass
class ResultMessage:
subtype: str
duration_ms: int
duration_api_ms: int
is_error: bool
num_turns: int
session_id: str
total_cost_usd: float | None = None
usage: dict[str, Any] | None = None
result: str | None = NoneTipe Blok Konten
ContentBlock
ContentBlockTipe union dari semua blok konten.
ContentBlock = TextBlock | ThinkingBlock | ToolUseBlock | ToolResultBlockTextBlock
TextBlockBlok konten teks.
@dataclass
class TextBlock:
text: strThinkingBlock
ThinkingBlockBlok konten pemikiran (untuk model dengan kemampuan pemikiran).
@dataclass
class ThinkingBlock:
thinking: str
signature: strToolUseBlock
ToolUseBlockBlok permintaan penggunaan alat.
@dataclass
class ToolUseBlock:
id: str
name: str
input: dict[str, Any]ToolResultBlock
ToolResultBlockBlok hasil eksekusi alat.
@dataclass
class ToolResultBlock:
tool_use_id: str
content: str | list[dict[str, Any]] | None = None
is_error: bool | None = NoneTipe Kesalahan
ClaudeSDKError
ClaudeSDKErrorKelas pengecualian dasar untuk semua kesalahan SDK.
class ClaudeSDKError(Exception):
"""Base error for Claude SDK."""CLINotFoundError
CLINotFoundErrorDimunculkan ketika Claude Code CLI tidak diinstal atau tidak ditemukan.
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
"""CLIConnectionError
CLIConnectionErrorDimunculkan ketika koneksi ke Claude Code gagal.
class CLIConnectionError(ClaudeSDKError):
"""Failed to connect to Claude Code."""ProcessError
ProcessErrorDimunculkan ketika proses Claude Code gagal.
class ProcessError(ClaudeSDKError):
def __init__(self, message: str, exit_code: int | None = None, stderr: str | None = None):
self.exit_code = exit_code
self.stderr = stderrCLIJSONDecodeError
CLIJSONDecodeErrorDimunculkan ketika penguraian JSON gagal.
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_errorTipe Hook
HookEvent
HookEventTipe acara hook yang didukung. Perhatikan bahwa karena keterbatasan penyiapan, Python SDK tidak mendukung hook SessionStart, SessionEnd, dan Notification.
HookEvent = Literal[
"PreToolUse", # Called before tool execution
"PostToolUse", # Called after tool execution
"UserPromptSubmit", # Called when user submits a prompt
"Stop", # Called when stopping execution
"SubagentStop", # Called when a subagent stops
"PreCompact" # Called before message compaction
]HookCallback
HookCallbackDefinisi tipe untuk fungsi callback hook.
HookCallback = Callable[
[dict[str, Any], str | None, HookContext],
Awaitable[dict[str, Any]]
]Parameter:
input_data: Data input spesifik hook (lihat dokumentasi hook)tool_use_id: Pengidentifikasi penggunaan alat opsional (untuk hook terkait alat)context: Konteks hook dengan informasi tambahan
Mengembalikan kamus yang mungkin berisi:
decision:"block"untuk memblokir tindakansystemMessage: Pesan sistem untuk ditambahkan ke transkriphookSpecificOutput: Data output spesifik hook
HookContext
HookContextInformasi konteks yang diteruskan ke callback hook.
@dataclass
class HookContext:
signal: Any | None = None # Future: abort signal supportHookMatcher
HookMatcherKonfigurasi untuk mencocokkan hook ke acara atau alat tertentu.
@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 executeContoh Penggunaan Hook
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]),
HookMatcher(hooks=[log_tool_use]) # Applies to all tools
],
'PostToolUse': [
HookMatcher(hooks=[log_tool_use])
]
}
)
async for message in query(
prompt="Analyze this codebase",
options=options
):
print(message)Tipe Input/Output Alat
Dokumentasi skema input/output untuk semua alat Claude Code bawaan. Meskipun Python SDK tidak mengekspor ini sebagai tipe, mereka mewakili struktur input dan output alat dalam pesan.
Task
Nama alat: Task
Input:
{
"description": str, # A short (3-5 word) description of the task
"prompt": str, # The task for the agent to perform
"subagent_type": str # The type of specialized agent to use
}Output:
{
"result": str, # Final result from the subagent
"usage": dict | None, # Token usage statistics
"total_cost_usd": float | None, # Total cost in USD
"duration_ms": int | None # Execution duration in milliseconds
}Bash
Nama alat: Bash
Input:
{
"command": str, # The command to execute
"timeout": int | None, # Optional timeout in milliseconds (max 600000)
"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
}Edit
Nama alat: 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
}Read
Nama alat: 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 teks):
{
"content": str, # File contents with line numbers
"total_lines": int, # Total number of lines in file
"lines_returned": int # Lines actually returned
}Output (Gambar):
{
"image": str, # Base64 encoded image data
"mime_type": str, # Image MIME type
"file_size": int # File size in bytes
}Write
Nama alat: 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
}Glob
Nama alat: 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
}Grep
Nama alat: 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 (mode konten):
{
"matches": [
{
"file": str,
"line_number": int | None,
"line": str,
"before_context": list[str] | None,
"after_context": list[str] | None
}
],
"total_matches": int
}Output (mode files_with_matches):
{
"files": list[str], # Files containing matches
"count": int # Number of files with matches
}NotebookEdit
Nama alat: 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
}WebFetch
Nama alat: WebFetch
Input:
{
"url": str, # The URL to fetch content from
"prompt": str # The prompt to run on the fetched content
}Output:
{
"response": str, # AI model's response to the prompt
"url": str, # URL that was fetched
"final_url": str | None, # Final URL after redirects
"status_code": int | None # HTTP status code
}WebSearch
Nama alat: 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:
{
"results": [
{
"title": str,
"url": str,
"snippet": str,
"metadata": dict | None
}
],
"total_results": int,
"query": str
}TodoWrite
Nama alat: 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
}
}BashOutput
Nama alat: BashOutput
Input:
{
"bash_id": str, # The ID of the background shell
"filter": str | None # Optional regex to filter output lines
}Output:
{
"output": str, # New output since last check
"status": "running" | "completed" | "failed", # Current shell status
"exitCode": int | None # Exit code when completed
}KillBash
Nama alat: KillBash
Input:
{
"shell_id": str # The ID of the background shell to kill
}Output:
{
"message": str, # Success message
"shell_id": str # ID of the killed shell
}ExitPlanMode
Nama alat: 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
}ListMcpResources
Nama alat: ListMcpResources
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
}ReadMcpResource
Nama alat: ReadMcpResource
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
}Fitur Lanjutan dengan ClaudeSDKClient
Membangun Antarmuka Percakapan Berkelanjutan
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):
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 - Claude remembers all previous messages in this session
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())Menggunakan Hooks untuk Modifikasi Perilaku
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 timestamp to all prompts
from datetime import datetime
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
return {
'hookSpecificOutput': {
'hookEventName': 'UserPromptSubmit',
'updatedPrompt': f"[{timestamp}] {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())Pemantauan Kemajuan Real-time
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
files_created = []
async for message in client.receive_messages():
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(f"✅ Completed tool execution")
elif isinstance(block, TextBlock):
print(f"💭 Claude says: {block.text[:100]}...")
# Check if we've received the final result
if hasattr(message, 'subtype') and message.subtype in ['success', 'error']:
print(f"\n🎯 Task completed!")
break
asyncio.run(monitor_progress())Contoh Penggunaan
Operasi file dasar (menggunakan query)
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',
cwd="/home/user/project"
)
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())Penanganan kesalahan
from claude_agent_sdk import (
query,
CLINotFoundError,
ProcessError,
CLIJSONDecodeError
)
try:
async for message in query(prompt="Hello"):
print(message)
except CLINotFoundError:
print("Please install Claude Code: npm install -g @anthropic-ai/claude-code")
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}")Mode streaming dengan klien
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())Menggunakan alat kustom dengan ClaudeSDKClient
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())Lihat juga
- Panduan Python SDK - Tutorial dan contoh
- Gambaran umum SDK - Konsep SDK umum
- Referensi TypeScript SDK - Dokumentasi SDK TypeScript
- Referensi CLI - Antarmuka baris perintah
- Alur kerja umum - Panduan langkah demi langkah