Agent SDK 참조 - Python
설치
pip install claude-agent-sdkquery()와 ClaudeSDKClient 중 선택하기
query()와 ClaudeSDKClient 중 선택하기Python SDK는 Claude Code와 상호작용하는 두 가지 방법을 제공합니다:
빠른 비교
| 기능 | query() | ClaudeSDKClient |
|---|---|---|
| 세션 | 매번 새 세션 생성 | 동일한 세션 재사용 |
| 대화 | 단일 교환 | 동일한 컨텍스트에서 여러 교환 |
| 연결 | 자동으로 관리됨 | 수동 제어 |
| 스트리밍 입력 | ✅ 지원됨 | ✅ 지원됨 |
| 중단 | ❌ 지원되지 않음 | ✅ 지원됨 |
| 훅 | ❌ 지원되지 않음 | ✅ 지원됨 |
| 사용자 정의 도구 | ❌ 지원되지 않음 | ✅ 지원됨 |
| 대화 계속하기 | ❌ 매번 새 세션 | ✅ 대화 유지 |
| 사용 사례 | 일회성 작업 | 지속적인 대화 |
query() 사용 시기 (매번 새 세션)
query() 사용 시기 (매번 새 세션)최적의 경우:
- 대화 기록이 필요 없는 일회성 질문
- 이전 교환의 컨텍스트가 필요 없는 독립적인 작업
- 간단한 자동화 스크립트
- 매번 새로 시작하고 싶을 때
ClaudeSDKClient 사용 시기 (지속적인 대화)
ClaudeSDKClient 사용 시기 (지속적인 대화)최적의 경우:
- 대화 계속하기 - Claude가 컨텍스트를 기억해야 할 때
- 후속 질문 - 이전 응답을 기반으로 구축
- 대화형 애플리케이션 - 채팅 인터페이스, REPL
- 응답 기반 로직 - 다음 작업이 Claude의 응답에 따라 달라질 때
- 세션 제어 - 대화 생명주기를 명시적으로 관리
함수
query()
query()Claude Code와의 각 상호작용을 위해 새 세션을 생성합니다. 메시지가 도착할 때 메시지를 생성하는 비동기 반복자를 반환합니다. query()에 대한 각 호출은 이전 상호작용의 메모리 없이 새로 시작됩니다.
async def query(
*,
prompt: str | AsyncIterable[dict[str, Any]],
options: ClaudeAgentOptions | None = None
) -> AsyncIterator[Message]매개변수
| 매개변수 | 타입 | 설명 |
|---|---|---|
prompt | str | AsyncIterable[dict] | 문자열 또는 스트리밍 모드용 비동기 반복 가능 객체로서의 입력 프롬프트 |
options | ClaudeAgentOptions | None | 선택적 구성 객체 (None인 경우 ClaudeAgentOptions()로 기본값 설정) |
반환
대화에서 메시지를 생성하는 AsyncIterator[Message]를 반환합니다.
예제 - 옵션 포함
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()타입 안전성을 갖춘 MCP 도구를 정의하기 위한 데코레이터입니다.
def tool(
name: str,
description: str,
input_schema: type | dict[str, Any]
) -> Callable[[Callable[[Any], Awaitable[dict[str, Any]]]], SdkMcpTool[Any]]매개변수
| 매개변수 | 타입 | 설명 |
|---|---|---|
name | str | 도구의 고유 식별자 |
description | str | 도구가 수행하는 작업에 대한 사람이 읽을 수 있는 설명 |
input_schema | type | dict[str, Any] | 도구의 입력 매개변수를 정의하는 스키마 (아래 참조) |
입력 스키마 옵션
-
간단한 타입 매핑 (권장):
{"text": str, "count": int, "enabled": bool} -
JSON 스키마 형식 (복잡한 검증용):
{ "type": "object", "properties": { "text": {"type": "string"}, "count": {"type": "integer", "minimum": 0} }, "required": ["text"] }
반환
도구 구현을 래핑하고 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']}!"
}]
}create_sdk_mcp_server()
create_sdk_mcp_server()Python 애플리케이션 내에서 실행되는 프로세스 내 MCP 서버를 생성합니다.
def create_sdk_mcp_server(
name: str,
version: str = "1.0.0",
tools: list[SdkMcpTool[Any]] | None = None
) -> McpSdkServerConfig매개변수
| 매개변수 | 타입 | 기본값 | 설명 |
|---|---|---|---|
name | str | - | 서버의 고유 식별자 |
version | str | "1.0.0" | 서버 버전 문자열 |
tools | list[SdkMcpTool[Any]] | None | None | @tool 데코레이터로 생성된 도구 함수 목록 |
반환
ClaudeAgentOptions.mcp_servers에 전달할 수 있는 McpSdkServerConfig 객체를 반환합니다.
예제
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"]
)클래스
ClaudeSDKClient
ClaudeSDKClient여러 교환에 걸쳐 대화 세션을 유지합니다. 이것은 TypeScript SDK의 query() 함수가 내부적으로 작동하는 방식의 Python 동등물입니다 - 대화를 계속할 수 있는 클라이언트 객체를 생성합니다.
주요 기능
- 세션 연속성: 여러
query()호출에 걸쳐 대화 컨텍스트 유지 - 동일한 대화: Claude가 세션의 이전 메시지를 기억함
- 중단 지원: Claude 실행 중에 중지 가능
- 명시적 생명주기: 세션 시작 및 종료 시기를 제어
- 응답 기반 흐름: 응답에 반응하고 후속 조치 전송 가능
- 사용자 정의 도구 및 훅:
@tool데코레이터로 생성된 사용자 정의 도구 및 훅 지원
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) -> None메서드
| 메서드 | 설명 |
|---|---|
__init__(options) | 선택적 구성으로 클라이언트 초기화 |
connect(prompt) | 선택적 초기 프롬프트 또는 메시지 스트림으로 Claude에 연결 |
query(prompt, session_id) | 스트리밍 모드에서 새 요청 전송 |
receive_messages() | Claude의 모든 메시지를 비동기 반복자로 수신 |
receive_response() | ResultMessage를 포함하여 메시지 수신 |
interrupt() | 중단 신호 전송 (스트리밍 모드에서만 작동) |
disconnect() | Claude에서 연결 해제 |
컨텍스트 관리자 지원
클라이언트는 자동 연결 관리를 위해 비동기 컨텍스트 관리자로 사용할 수 있습니다:
async with ClaudeSDKClient() as client:
await client.query("Hello Claude")
async for message in client.receive_response():
print(message)중요: 메시지를 반복할 때 조기에 종료하기 위해
break를 사용하지 마세요. 이는 asyncio 정리 문제를 야기할 수 있습니다. 대신 반복이 자연스럽게 완료되도록 하거나 필요한 것을 찾았을 때를 추적하기 위해 플래그를 사용하세요.
예제 - 대화 계속하기
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())예제 - 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())예제 - 중단 사용
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())예제 - 고급 권한 제어
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())타입
SdkMcpTool
SdkMcpTool@tool 데코레이터로 생성된 SDK MCP 도구의 정의입니다.
@dataclass
class SdkMcpTool(Generic[T]):
name: str
description: str
input_schema: type[T] | dict[str, Any]
handler: Callable[[T], Awaitable[dict[str, Any]]]| 속성 | 타입 | 설명 |
|---|---|---|
name | str | 도구의 고유 식별자 |
description | str | 사람이 읽을 수 있는 설명 |
input_schema | type[T] | dict[str, Any] | 입력 검증용 스키마 |
handler | Callable[[T], Awaitable[dict[str, Any]]] | 도구 실행을 처리하는 비동기 함수 |
ClaudeAgentOptions
ClaudeAgentOptionsClaude 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| 속성 | 타입 | 기본값 | 설명 |
|---|---|---|---|
allowed_tools | list[str] | [] | 허용된 도구 이름 목록 |
system_prompt | str | SystemPromptPreset | None | None | 시스템 프롬프트 구성. 사용자 정의 프롬프트의 경우 문자열을 전달하거나 Claude Code의 시스템 프롬프트의 경우 {"type": "preset", "preset": "claude_code"}를 사용합니다. 프리셋을 확장하려면 "append"를 추가합니다 |
mcp_servers | dict[str, McpServerConfig] | str | Path | {} | MCP 서버 구성 또는 구성 파일 경로 |
permission_mode | PermissionMode | None | None | 도구 사용을 위한 권한 모드 |
continue_conversation | bool | False | 가장 최근 대화 계속하기 |
resume | str | None | None | 재개할 세션 ID |
max_turns | int | None | None | 최대 대화 턴 |
disallowed_tools | list[str] | [] | 허용되지 않는 도구 이름 목록 |
model | str | None | None | 사용할 Claude 모델 |
permission_prompt_tool_name | str | None | None | 권한 프롬프트용 MCP 도구 이름 |
cwd | str | Path | None | None | 현재 작업 디렉토리 |
settings | str | None | None | 설정 파일 경로 |
add_dirs | list[str | Path] | [] | Claude가 액세스할 수 있는 추가 디렉토리 |
env | dict[str, str] | {} | 환경 변수 |
extra_args | dict[str, str | None] | {} | CLI에 직접 전달할 추가 CLI 인수 |
max_buffer_size | int | None | None | CLI stdout 버퍼링 시 최대 바이트 |
debug_stderr | Any | sys.stderr | 더 이상 사용되지 않음 - 디버그 출력용 파일 유사 객체. 대신 stderr 콜백 사용 |
stderr | Callable[[str], None] | None | None | CLI의 stderr 출력을 위한 콜백 함수 |
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 | resume으로 재개할 때 원본 세션을 계속하는 대신 새 세션 ID로 포크 |
agents | dict[str, AgentDefinition] | None | None | 프로그래밍 방식으로 정의된 서브에이전트 |
plugins | list[SdkPluginConfig] | [] | 로컬 경로에서 사용자 정의 플러그인 로드. 자세한 내용은 플러그인을 참조하세요 |
setting_sources | list[SettingSource] | None | None (설정 없음) | 로드할 파일 시스템 설정을 제어합니다. 생략하면 설정이 로드되지 않습니다. 참고: CLAUDE.md 파일을 로드하려면 "project"를 포함해야 합니다 |
SystemPromptPreset
SystemPromptPreset선택적 추가 사항과 함께 Claude Code의 프리셋 시스템 프롬프트 사용을 위한 구성입니다.
class SystemPromptPreset(TypedDict):
type: Literal["preset"]
preset: Literal["claude_code"]
append: NotRequired[str]| 필드 | 필수 | 설명 |
|---|---|---|
type | 예 | 프리셋 시스템 프롬프트를 사용하려면 "preset"이어야 합니다 |
preset | 예 | Claude Code의 시스템 프롬프트를 사용하려면 "claude_code"이어야 합니다 |
append | 아니오 | 프리셋 시스템 프롬프트에 추가할 추가 지침 |
SettingSource
SettingSourceSDK가 로드하는 파일 시스템 기반 구성 소스를 제어합니다.
SettingSource = Literal["user", "project", "local"]| 값 | 설명 | 위치 |
|---|---|---|
"user" | 전역 사용자 설정 | ~/.claude/settings.json |
"project" | 공유 프로젝트 설정 (버전 제어됨) | .claude/settings.json |
"local" | 로컬 프로젝트 설정 (gitignored) | .claude/settings.local.json |
기본 동작
setting_sources가 생략되거나 **None**일 때 SDK는 파일 시스템 설정을 로드하지 않습니다. 이는 SDK 애플리케이션에 격리를 제공합니다.
setting_sources를 사용하는 이유는 무엇입니까?
모든 파일 시스템 설정 로드 (레거시 동작):
# 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)특정 설정 소스만 로드:
# 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)테스트 및 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)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)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)설정 우선순위
여러 소스가 로드될 때 설정은 이 우선순위(높음에서 낮음)로 병합됩니다:
- 로컬 설정 (
.claude/settings.local.json) - 프로젝트 설정 (
.claude/settings.json) - 사용자 설정 (
~/.claude/settings.json)
프로그래밍 방식의 옵션 (예: agents, allowed_tools)은 항상 파일 시스템 설정을 재정의합니다.
AgentDefinition
AgentDefinition프로그래밍 방식으로 정의된 서브에이전트의 구성입니다.
@dataclass
class AgentDefinition:
description: str
prompt: str
tools: list[str] | None = None
model: Literal["sonnet", "opus", "haiku", "inherit"] | None = None| 필드 | 필수 | 설명 |
|---|---|---|
description | 예 | 이 에이전트를 사용할 시기에 대한 자연어 설명 |
tools | 아니오 | 허용된 도구 이름 배열. 생략하면 모든 도구를 상속합니다 |
prompt | 예 | 에이전트의 시스템 프롬프트 |
model | 아니오 | 이 에이전트의 모델 재정의. 생략하면 주 모델을 사용합니다 |
PermissionMode
PermissionMode도구 실행을 제어하기 위한 권한 모드입니다.
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
McpSdkServerConfigcreate_sdk_mcp_server()로 생성된 SDK MCP 서버의 구성입니다.
class McpSdkServerConfig(TypedDict):
type: Literal["sdk"]
name: str
instance: Any # MCP Server instanceMcpServerConfig
McpServerConfigMCP 서버 구성의 합집합 타입입니다.
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
SdkPluginConfigSDK에서 플러그인을 로드하기 위한 구성입니다.
class SdkPluginConfig(TypedDict):
type: Literal["local"]
path: str| 필드 | 타입 | 설명 |
|---|---|---|
type | Literal["local"] | "local"이어야 합니다 (현재 로컬 플러그인만 지원됨) |
path | str | 플러그인 디렉토리의 절대 또는 상대 경로 |
예제:
plugins=[
{"type": "local", "path": "./my-plugin"},
{"type": "local", "path": "/absolute/path/to/plugin"}
]플러그인 생성 및 사용에 대한 완전한 정보는 플러그인을 참조하세요.
메시지 타입
Message
Message모든 가능한 메시지의 합집합 타입입니다.
Message = UserMessage | AssistantMessage | SystemMessage | ResultMessageUserMessage
UserMessage사용자 입력 메시지입니다.
@dataclass
class UserMessage:
content: str | list[ContentBlock]AssistantMessage
AssistantMessage콘텐츠 블록이 있는 어시스턴트 응답 메시지입니다.
@dataclass
class AssistantMessage:
content: list[ContentBlock]
model: strSystemMessage
SystemMessage메타데이터가 있는 시스템 메시지입니다.
@dataclass
class SystemMessage:
subtype: str
data: dict[str, Any]ResultMessage
ResultMessage비용 및 사용 정보가 있는 최종 결과 메시지입니다.
@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 = None콘텐츠 블록 타입
ContentBlock
ContentBlock모든 콘텐츠 블록의 합집합 타입입니다.
ContentBlock = TextBlock | ThinkingBlock | ToolUseBlock | ToolResultBlockTextBlock
TextBlock텍스트 콘텐츠 블록입니다.
@dataclass
class TextBlock:
text: strThinkingBlock
ThinkingBlock사고 콘텐츠 블록입니다 (사고 능력이 있는 모델용).
@dataclass
class ThinkingBlock:
thinking: str
signature: strToolUseBlock
ToolUseBlock도구 사용 요청 블록입니다.
@dataclass
class ToolUseBlock:
id: str
name: str
input: dict[str, Any]ToolResultBlock
ToolResultBlock도구 실행 결과 블록입니다.
@dataclass
class ToolResultBlock:
tool_use_id: str
content: str | list[dict[str, Any]] | None = None
is_error: bool | None = None오류 타입
ClaudeSDKError
ClaudeSDKError모든 SDK 오류의 기본 예외 클래스입니다.
class ClaudeSDKError(Exception):
"""Base error for Claude SDK."""CLINotFoundError
CLINotFoundErrorClaude Code CLI가 설치되지 않았거나 찾을 수 없을 때 발생합니다.
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
CLIConnectionErrorClaude Code 연결이 실패할 때 발생합니다.
class CLIConnectionError(ClaudeSDKError):
"""Failed to connect to Claude Code."""ProcessError
ProcessErrorClaude Code 프로세스가 실패할 때 발생합니다.
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
CLIJSONDecodeErrorJSON 파싱이 실패할 때 발생합니다.
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훅 타입
HookEvent
HookEvent지원되는 훅 이벤트 타입입니다. 설정 제한으로 인해 Python SDK는 SessionStart, SessionEnd 및 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
HookCallback훅 콜백 함수의 타입 정의입니다.
HookCallback = Callable[
[dict[str, Any], str | None, HookContext],
Awaitable[dict[str, Any]]
]매개변수:
input_data: 훅 특정 입력 데이터 (훅 문서 참조)tool_use_id: 선택적 도구 사용 식별자 (도구 관련 훅용)context: 추가 정보가 있는 훅 컨텍스트
다음을 포함할 수 있는 딕셔너리를 반환합니다:
decision: 작업을 차단하려면"block"systemMessage: 트랜스크립트에 추가할 시스템 메시지hookSpecificOutput: 훅 특정 출력 데이터
HookContext
HookContext훅 콜백에 전달되는 컨텍스트 정보입니다.
@dataclass
class HookContext:
signal: Any | None = None # Future: abort signal supportHookMatcher
HookMatcher특정 이벤트 또는 도구에 훅을 일치시키기 위한 구성입니다.
@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훅 사용 예제
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)도구 입력/출력 타입
모든 기본 제공 Claude Code 도구의 입력/출력 스키마 문서입니다. Python SDK는 이를 타입으로 내보내지 않지만 메시지의 도구 입력 및 출력 구조를 나타냅니다.
Task
도구 이름: Task
입력:
{
"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
}출력:
{
"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
도구 이름: Bash
입력:
{
"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": 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
도구 이름: Edit
입력:
{
"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)
}출력:
{
"message": str, # Confirmation message
"replacements": int, # Number of replacements made
"file_path": str # File path that was edited
}Read
도구 이름: Read
입력:
{
"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
}출력 (텍스트 파일):
{
"content": str, # File contents with line numbers
"total_lines": int, # Total number of lines in file
"lines_returned": int # Lines actually returned
}출력 (이미지):
{
"image": str, # Base64 encoded image data
"mime_type": str, # Image MIME type
"file_size": int # File size in bytes
}Write
도구 이름: Write
입력:
{
"file_path": str, # The absolute path to the file to write
"content": str # The content to write to the file
}출력:
{
"message": str, # Success message
"bytes_written": int, # Number of bytes written
"file_path": str # File path that was written
}Glob
도구 이름: Glob
입력:
{
"pattern": str, # The glob pattern to match files against
"path": str | None # The directory to search in (defaults to cwd)
}출력:
{
"matches": list[str], # Array of matching file paths
"count": int, # Number of matches found
"search_path": str # Search directory used
}Grep
도구 이름: Grep
입력:
{
"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
}출력 (content 모드):
{
"matches": [
{
"file": str,
"line_number": int | None,
"line": str,
"before_context": list[str] | None,
"after_context": list[str] | None
}
],
"total_matches": int
}출력 (files_with_matches 모드):
{
"files": list[str], # Files containing matches
"count": int # Number of files with matches
}NotebookEdit
도구 이름: NotebookEdit
입력:
{
"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
}출력:
{
"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
도구 이름: WebFetch
입력:
{
"url": str, # The URL to fetch content from
"prompt": str # The prompt to run on the fetched content
}출력:
{
"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
도구 이름: WebSearch
입력:
{
"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
}출력:
{
"results": [
{
"title": str,
"url": str,
"snippet": str,
"metadata": dict | None
}
],
"total_results": int,
"query": str
}TodoWrite
도구 이름: TodoWrite
입력:
{
"todos": [
{
"content": str, # The task description
"status": "pending" | "in_progress" | "completed", # Task status
"activeForm": str # Active form of the description
}
]
}출력:
{
"message": str, # Success message
"stats": {
"total": int,
"pending": int,
"in_progress": int,
"completed": int
}
}BashOutput
도구 이름: BashOutput
입력:
{
"bash_id": str, # The ID of the background shell
"filter": str | None # Optional regex to filter output lines
}출력:
{
"output": str, # New output since last check
"status": "running" | "completed" | "failed", # Current shell status
"exitCode": int | None # Exit code when completed
}KillBash
도구 이름: KillBash
입력:
{
"shell_id": str # The ID of the background shell to kill
}출력:
{
"message": str, # Success message
"shell_id": str # ID of the killed shell
}ExitPlanMode
도구 이름: ExitPlanMode
입력:
{
"plan": str # The plan to run by the user for approval
}출력:
{
"message": str, # Confirmation message
"approved": bool | None # Whether user approved the plan
}ListMcpResources
도구 이름: ListMcpResources
입력:
{
"server": str | None # Optional server name to filter resources by
}출력:
{
"resources": [
{
"uri": str,
"name": str,
"description": str | None,
"mimeType": str | None,
"server": str
}
],
"total": int
}ReadMcpResource
도구 이름: ReadMcpResource
입력:
{
"server": str, # The MCP server name
"uri": str # The resource URI to read
}출력:
{
"contents": [
{
"uri": str,
"mimeType": str | None,
"text": str | None,
"blob": str | None
}
],
"server": str
}ClaudeSDKClient를 사용한 고급 기능
지속적인 대화 인터페이스 구축
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())동작 수정을 위해 훅 사용
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())실시간 진행 상황 모니터링
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())사용 예제
기본 파일 작업 (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())오류 처리
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}")클라이언트를 사용한 스트리밍 모드
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())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())참고 항목
- Python SDK 가이드 - 튜토리얼 및 예제
- SDK 개요 - 일반 SDK 개념
- TypeScript SDK 참조 - TypeScript SDK 문서
- CLI 참조 - 명령줄 인터페이스
- 일반적인 워크플로우 - 단계별 가이드