pip install claude-agent-sdkquery()와 ClaudeSDKClient 중 선택하기Python SDK는 Claude Code와 상호작용하는 두 가지 방법을 제공합니다:
| 기능 | query() | ClaudeSDKClient |
|---|---|---|
| 세션 | 매번 새 세션 생성 | 동일 세션 재사용 |
| 대화 | 단일 교환 | 동일 컨텍스트에서 다중 교환 |
| 연결 | 자동 관리 | 수동 제어 |
| 스트리밍 입력 | ✅ 지원 | ✅ 지원 |
| 인터럽트 | ❌ 미지원 | ✅ 지원 |
| 훅 | ❌ 미지원 | ✅ 지원 |
| 커스텀 도구 | ❌ 미지원 | ✅ 지원 |
| 대화 계속 | ❌ 매번 새 세션 | ✅ 대화 유지 |
| 사용 사례 | 일회성 작업 | 지속적 대화 |
query() 사용 시기 (매번 새 세션)적합한 경우:
ClaudeSDKClient 사용 시기 (지속적 대화)적합한 경우:
query()Claude Code와의 각 상호작용마다 새 세션을 생성합니다. 메시지가 도착할 때마다 yield하는 비동기 이터레이터를 반환합니다. 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()가 기본값) |
대화에서 메시지를 yield하는 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()타입 안전성을 갖춘 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 Schema 형식 (복잡한 유효성 검사용):
{
"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()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여러 교환에 걸쳐 대화 세션을 유지합니다. 이것은 TypeScript SDK의 query() 함수가 내부적으로 작동하는 방식의 Python 동등물입니다 - 대화를 계속할 수 있는 클라이언트 객체를 생성합니다.
query() 호출에 걸쳐 대화 컨텍스트를 유지@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 rewind_files(self, user_message_uuid: str) -> None
async def disconnect(self) -> None| 메서드 | 설명 |
|---|---|
__init__(options) | 선택적 구성으로 클라이언트 초기화 |
connect(prompt) | 선택적 초기 프롬프트 또는 메시지 스트림으로 Claude에 연결 |
query(prompt, session_id) | 스트리밍 모드에서 새 요청 전송 |
receive_messages() | Claude로부터 모든 메시지를 비동기 이터레이터로 수신 |
receive_response() | ResultMessage를 포함하여 그때까지의 메시지 수신 |
interrupt() | 인터럽트 신호 전송 (스트리밍 모드에서만 작동) |
rewind_files(user_message_uuid) | 지정된 사용자 메시지 시점의 파일 상태로 복원. enable_file_checkpointing=True가 필요합니다. 파일 체크포인팅 참조 |
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())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
)
from claude_agent_sdk.types import PermissionResultAllow, PermissionResultDeny
async def custom_permission_handler(
tool_name: str,
input_data: dict,
context: dict
) -> 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():
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@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]]] | 도구 실행을 처리하는 비동기 함수 |
ClaudeAgentOptionsClaude Code 쿼리를 위한 구성 데이터클래스입니다.
@dataclass
class ClaudeAgentOptions:
tools: list[str] | ToolsPreset | None = None
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
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: OutputFormat | 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
fork_session: bool = False
agents: dict[str, AgentDefinition] | None = None
setting_sources: list[SettingSource] | None = None
max_thinking_tokens: int | None = None| 속성 | 타입 | 기본값 | 설명 |
|---|---|---|---|
tools | list[str] | ToolsPreset | None | None | 도구 구성. Claude Code의 기본 도구를 사용하려면 {"type": "preset", "preset": "claude_code"}를 사용 |
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 | 최대 대화 턴 수 |
max_budget_usd | float | None | None | 세션의 최대 예산 (USD) |
disallowed_tools | list[str] | [] | 허용되지 않는 도구 이름 목록 |
enable_file_checkpointing | bool | False | 되감기를 위한 파일 변경 추적 활성화. 파일 체크포인팅 참조 |
model | str | None | None | 사용할 Claude 모델 |
fallback_model | str | None | None | 기본 모델이 실패할 경우 사용할 대체 모델 |
betas | list[SdkBeta] | [] | 활성화할 베타 기능. 사용 가능한 옵션은 SdkBeta 참조 |
output_format | OutputFormat | None | None | 에이전트 결과의 출력 형식 정의. 자세한 내용은 구조화된 출력 참조 |
permission_prompt_tool_name | str | None | None | 권한 프롬프트를 위한 MCP 도구 이름 |
cwd | str | Path | None | None | 현재 작업 디렉토리 |
cli_path | str | Path | None | None | Claude Code CLI 실행 파일의 커스텀 경로 |
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 | 부분 메시지 스트리밍 이벤트 포함. 활성화하면 StreamEvent 메시지가 yield됨 |
fork_session | bool | False | resume으로 재개할 때 원래 세션을 계속하는 대신 새 세션 ID로 포크 |
agents | dict[str, AgentDefinition] | None | None | 프로그래밍 방식으로 정의된 서브에이전트 |
plugins | list[SdkPluginConfig] | [] | 로컬 경로에서 커스텀 플러그인 로드. 자세한 내용은 플러그인 참조 |
sandbox | SandboxSettings | None | None | 프로그래밍 방식으로 샌드박스 동작 구성. 자세한 내용은 샌드박스 설정 참조 |
setting_sources | list[SettingSource] | None | None (설정 없음) | 로드할 파일 시스템 설정 제어. 생략하면 설정이 로드되지 않음. 참고: CLAUDE.md 파일을 로드하려면 "project"를 포함해야 함 |
max_thinking_tokens | int | None | None | 사고 블록의 최대 토큰 수 |
OutputFormat구조화된 출력 유효성 검사를 위한 구성입니다.
class OutputFormat(TypedDict):
type: Literal["json_schema"]
schema: dict[str, Any]| 필드 | 필수 | 설명 |
|---|---|---|
type | 예 | JSON Schema 유효성 검사를 위해 "json_schema"여야 함 |
schema | 예 | 출력 유효성 검사를 위한 JSON Schema 정의 |
SystemPromptPreset선택적 추가 사항과 함께 Claude Code의 프리셋 시스템 프롬프트를 사용하기 위한 구성입니다.
class SystemPromptPreset(TypedDict):
type: Literal["preset"]
preset: Literal["claude_code"]
append: NotRequired[str]| 필드 | 필수 | 설명 |
|---|---|---|
type | 예 | 프리셋 시스템 프롬프트를 사용하려면 "preset"이어야 함 |
preset | 예 | Claude Code의 시스템 프롬프트를 사용하려면 "claude_code"여야 함 |
append | 아니오 | 프리셋 시스템 프롬프트에 추가할 추가 지침 |
SettingSourceSDK가 설정을 로드하는 파일 시스템 기반 구성 소스를 제어합니다.
SettingSource = Literal["user", "project", "local"]| 값 | 설명 | 위치 |
|---|---|---|
"user" | 전역 사용자 설정 | ~/.claude/settings.json |
"project" | 공유 프로젝트 설정 (버전 관리됨) | .claude/settings.json |
"local" | 로컬 프로젝트 설정 (gitignore됨) | .claude/settings.local.json |
setting_sources가 생략되거나 **None**이면 SDK는 파일 시스템 설정을 로드하지 않습니다. 이는 SDK 애플리케이션에 대한 격리를 제공합니다.
모든 파일 시스템 설정 로드 (레거시 동작):
# 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프로그래밍 방식으로 정의된 서브에이전트의 구성입니다.
@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 = Literal[
"default", # Standard permission behavior
"acceptEdits", # Auto-accept file edits
"plan", # Planning mode - no execution
"bypassPermissions" # Bypass all permission checks (use with caution)
]CanUseTool도구 권한 콜백 함수를 위한 타입 별칭입니다.
CanUseTool = Callable[
[str, dict[str, Any], ToolPermissionContext],
Awaitable[PermissionResult]
]콜백은 다음을 수신합니다:
tool_name: 호출되는 도구의 이름input_data: 도구의 입력 매개변수context: 추가 정보가 포함된 ToolPermissionContextPermissionResult(PermissionResultAllow 또는 PermissionResultDeny)를 반환합니다.
ToolPermissionContext도구 권한 콜백에 전달되는 컨텍스트 정보입니다.
@dataclass
class ToolPermissionContext:
signal: Any | None = None # Future: abort signal support
suggestions: list[PermissionUpdate] = field(default_factory=list)| 필드 | 타입 | 설명 |
|---|---|---|
signal | Any | None | 향후 중단 신호 지원을 위해 예약됨 |
suggestions | list[PermissionUpdate] | CLI에서 제공하는 권한 업데이트 제안 |
PermissionResult권한 콜백 결과를 위한 유니온 타입입니다.
PermissionResult = PermissionResultAllow | PermissionResultDenyPermissionResultAllow도구 호출이 허용되어야 함을 나타내는 결과입니다.
@dataclass
class PermissionResultAllow:
behavior: Literal["allow"] = "allow"
updated_input: dict[str, Any] | None = None
updated_permissions: list[PermissionUpdate] | None = None| 필드 | 타입 | 기본값 | 설명 |
|---|---|---|---|
behavior | Literal["allow"] | "allow" | 반드시 "allow"여야 함 |
updated_input | dict[str, Any] | None | None | 원본 대신 사용할 수정된 입력 |
updated_permissions | list[PermissionUpdate] | None | None | 적용할 권한 업데이트 |
PermissionResultDeny도구 호출이 거부되어야 함을 나타내는 결과입니다.
@dataclass
class PermissionResultDeny:
behavior: Literal["deny"] = "deny"
message: str = ""
interrupt: bool = False| 필드 | 타입 | 기본값 | 설명 |
|---|---|---|---|
behavior | Literal["deny"] | "deny" | 반드시 "deny"여야 함 |
message | str | "" | 도구가 거부된 이유를 설명하는 메시지 |
interrupt | bool | False | 현재 실행을 중단할지 여부 |
PermissionUpdate프로그래밍 방식으로 권한을 업데이트하기 위한 구성입니다.
@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| 필드 | 타입 | 설명 |
|---|---|---|
type | Literal[...] | 권한 업데이트 작업의 유형 |
rules | list[PermissionRuleValue] | None | 추가/교체/제거 작업을 위한 규칙 |
behavior | Literal["allow", "deny", "ask"] | None | 규칙 기반 작업의 동작 |
mode | PermissionMode | None | setMode 작업을 위한 모드 |
directories | list[str] | None | 디렉토리 추가/제거 작업을 위한 디렉토리 |
destination | Literal[...] | None | 권한 업데이트를 적용할 위치 |
SdkBetaSDK 베타 기능을 위한 리터럴 타입입니다.
SdkBeta = Literal["context-1m-2025-08-07"]베타 기능을 활성화하려면 ClaudeAgentOptions의 betas 필드와 함께 사용하세요.
McpSdkServerConfigcreate_sdk_mcp_server()로 생성된 SDK MCP 서버를 위한 구성입니다.
class McpSdkServerConfig(TypedDict):
type: Literal["sdk"]
name: str
instance: Any # MCP Server instanceMcpServerConfigMCP 서버 구성을 위한 유니온 타입입니다.
McpServerConfig = McpStdioServerConfig | McpSSEServerConfig | McpHttpServerConfig | McpSdkServerConfigMcpStdioServerConfigclass McpStdioServerConfig(TypedDict):
type: NotRequired[Literal["stdio"]] # Optional for backwards compatibility
command: str
args: NotRequired[list[str]]
env: NotRequired[dict[str, str]]McpSSEServerConfigclass McpSSEServerConfig(TypedDict):
type: Literal["sse"]
url: str
headers: NotRequired[dict[str, str]]McpHttpServerConfigclass McpHttpServerConfig(TypedDict):
type: Literal["http"]
url: str
headers: NotRequired[dict[str, str]]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 = UserMessage | AssistantMessage | SystemMessage | ResultMessage | StreamEventUserMessage사용자 입력 메시지입니다.
@dataclass
class UserMessage:
content: str | list[ContentBlock]AssistantMessage콘텐츠 블록이 포함된 어시스턴트 응답 메시지입니다.
@dataclass
class AssistantMessage:
content: list[ContentBlock]
model: strSystemMessage메타데이터가 포함된 시스템 메시지입니다.
@dataclass
class SystemMessage:
subtype: str
data: dict[str, Any]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
structured_output: Any = NoneStreamEvent스트리밍 중 부분 메시지 업데이트를 위한 스트림 이벤트입니다. ClaudeAgentOptions에서 include_partial_messages=True인 경우에만 수신됩니다.
@dataclass
class StreamEvent:
uuid: str
session_id: str
event: dict[str, Any] # The raw Anthropic API stream event
parent_tool_use_id: str | None = None| 필드 | 타입 | 설명 |
|---|---|---|
uuid | str | 이 이벤트의 고유 식별자 |
session_id | str | 세션 식별자 |
event | dict[str, Any] | 원시 Anthropic API 스트림 이벤트 데이터 |
parent_tool_use_id | str | None | 이 이벤트가 서브에이전트에서 온 경우 부모 도구 사용 ID |
ContentBlock모든 콘텐츠 블록의 유니온 타입입니다.
ContentBlock = TextBlock | ThinkingBlock | ToolUseBlock | ToolResultBlockTextBlock텍스트 콘텐츠 블록입니다.
@dataclass
class TextBlock:
text: strThinkingBlock사고 콘텐츠 블록입니다 (사고 기능이 있는 모델용).
@dataclass
class ThinkingBlock:
thinking: str
signature: strToolUseBlock도구 사용 요청 블록입니다.
@dataclass
class ToolUseBlock:
id: str
name: str
input: dict[str, Any]ToolResultBlock도구 실행 결과 블록입니다.
@dataclass
class ToolResultBlock:
tool_use_id: str
content: str | list[dict[str, Any]] | None = None
is_error: bool | None = NoneClaudeSDKError모든 SDK 오류의 기본 예외 클래스입니다.
class ClaudeSDKError(Exception):
"""Base error for Claude SDK."""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
"""CLIConnectionErrorClaude Code 연결에 실패했을 때 발생합니다.
class CLIConnectionError(ClaudeSDKError):
"""Failed to connect to Claude Code."""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 = stderrCLIJSONDecodeErrorJSON 파싱에 실패했을 때 발생합니다.
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지원되는 훅 이벤트 타입입니다. 설정 제한으로 인해 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 = Callable[
[dict[str, Any], str | None, HookContext],
Awaitable[dict[str, Any]]
]매개변수:
input_data: 훅별 입력 데이터 (훅 가이드 참조)tool_use_id: 선택적 도구 사용 식별자 (도구 관련 훅용)context: 추가 정보가 포함된 훅 컨텍스트다음을 포함할 수 있는 딕셔너리를 반환합니다:
decision: 작업을 차단하려면 "block"systemMessage: 트랜스크립트에 추가할 시스템 메시지hookSpecificOutput: 훅별 출력 데이터HookContext훅 콜백에 전달되는 컨텍스트 정보입니다.
@dataclass
class HookContext:
signal: Any | None = None # Future: abort signal supportHookMatcher특정 이벤트 또는 도구에 훅을 매칭하기 위한 구성입니다.
@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 for all hooks in this matcher (default: 60)HookInput모든 훅 입력 타입의 유니온 타입입니다. 실제 타입은 hook_event_name 필드에 따라 달라집니다.
HookInput = (
PreToolUseHookInput
| PostToolUseHookInput
| UserPromptSubmitHookInput
| StopHookInput
| SubagentStopHookInput
| PreCompactHookInput
)BaseHookInput모든 훅 입력 타입에 존재하는 기본 필드입니다.
class BaseHookInput(TypedDict):
session_id: str
transcript_path: str
cwd: str
permission_mode: NotRequired[str]| 필드 | 타입 | 설명 |
|---|---|---|
session_id | str | 현재 세션 식별자 |
transcript_path | str | 세션 트랜스크립트 파일 경로 |
cwd | str | 현재 작업 디렉토리 |
permission_mode | str (선택적) | 현재 권한 모드 |
PreToolUseHookInputPreToolUse 훅 이벤트의 입력 데이터입니다.
class PreToolUseHookInput(BaseHookInput):
hook_event_name: Literal["PreToolUse"]
tool_name: str
tool_input: dict[str, Any]| 필드 | 타입 | 설명 |
|---|---|---|
hook_event_name | Literal["PreToolUse"] | 항상 "PreToolUse" |
tool_name | str | 실행될 도구의 이름 |
tool_input | dict[str, Any] | 도구의 입력 매개변수 |
PostToolUseHookInputPostToolUse 훅 이벤트의 입력 데이터입니다.
class PostToolUseHookInput(BaseHookInput):
hook_event_name: Literal["PostToolUse"]
tool_name: str
tool_input: dict[str, Any]
tool_response: Any| 필드 | 타입 | 설명 |
|---|---|---|
hook_event_name | Literal["PostToolUse"] | 항상 "PostToolUse" |
tool_name | str | 실행된 도구의 이름 |
tool_input | dict[str, Any] | 사용된 입력 매개변수 |
tool_response | Any | 도구 실행의 응답 |
UserPromptSubmitHookInputUserPromptSubmit 훅 이벤트의 입력 데이터입니다.
class UserPromptSubmitHookInput(BaseHookInput):
hook_event_name: Literal["UserPromptSubmit"]
prompt: str| 필드 | 타입 | 설명 |
|---|---|---|
hook_event_name | Literal["UserPromptSubmit"] | 항상 "UserPromptSubmit" |
prompt | str | 사용자가 제출한 프롬프트 |
StopHookInputStop 훅 이벤트의 입력 데이터입니다.
class StopHookInput(BaseHookInput):
hook_event_name: Literal["Stop"]
stop_hook_active: bool| 필드 | 타입 | 설명 |
|---|---|---|
hook_event_name | Literal["Stop"] | 항상 "Stop" |
stop_hook_active | bool | 중지 훅이 활성 상태인지 여부 |
SubagentStopHookInputSubagentStop 훅 이벤트의 입력 데이터입니다.
class SubagentStopHookInput(BaseHookInput):
hook_event_name: Literal["SubagentStop"]
stop_hook_active: bool| 필드 | 타입 | 설명 |
|---|---|---|
hook_event_name | Literal["SubagentStop"] | 항상 "SubagentStop" |
stop_hook_active | bool | 중지 훅이 활성 상태인지 여부 |
PreCompactHookInputPreCompact 훅 이벤트의 입력 데이터입니다.
class PreCompactHookInput(BaseHookInput):
hook_event_name: Literal["PreCompact"]
trigger: Literal["manual", "auto"]
custom_instructions: str | None| 필드 | 타입 | 설명 |
|---|---|---|
hook_event_name | Literal["PreCompact"] | 항상 "PreCompact" |
trigger | Literal["manual", "auto"] | 압축을 트리거한 원인 |
custom_instructions | str | None | 압축을 위한 사용자 정의 지침 |
HookJSONOutput훅 콜백 반환 값을 위한 유니온 타입입니다.
HookJSONOutput = AsyncHookJSONOutput | SyncHookJSONOutputSyncHookJSONOutput제어 및 결정 필드가 포함된 동기 훅 출력입니다.
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[dict[str, Any]]Python 코드에서는 continue_(밑줄 포함)를 사용하세요. CLI로 전송될 때 자동으로 continue로 변환됩니다.
AsyncHookJSONOutput훅 실행을 지연시키는 비동기 훅 출력입니다.
class AsyncHookJSONOutput(TypedDict):
async_: Literal[True] # Set to True to defer execution
asyncTimeout: NotRequired[int] # Timeout in millisecondsPython 코드에서는 async_(밑줄 포함)를 사용하세요. CLI로 전송될 때 자동으로 async로 변환됩니다.
이 예시는 두 개의 훅을 등록합니다: rm -rf /와 같은 위험한 bash 명령을 차단하는 훅과 감사를 위해 모든 도구 사용을 기록하는 훅입니다. 보안 훅은 Bash 명령에서만 실행되고(matcher를 통해), 로깅 훅은 모든 도구에서 실행됩니다.
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 (default 60s timeout)
],
'PostToolUse': [
HookMatcher(hooks=[log_tool_use])
]
}
)
async for message in query(
prompt="Analyze this codebase",
options=options
):
print(message)모든 내장 Claude Code 도구의 입력/출력 스키마 문서입니다. Python SDK는 이를 타입으로 내보내지 않지만, 메시지에서 도구 입력 및 출력의 구조를 나타냅니다.
도구 이름: 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
}도구 이름: AskUserQuestion
실행 중 사용자에게 명확한 질문을 합니다. 사용 세부 정보는 승인 및 사용자 입력 처리를 참조하세요.
입력:
{
"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
}
],
"multiSelect": bool # Set to true to allow multiple selections
}
],
"answers": dict | None # User answers populated by the permission system
}출력:
{
"questions": [ # The questions that were asked
{
"question": str,
"header": str,
"options": [{"label": str, "description": str}],
"multiSelect": bool
}
],
"answers": dict[str, str] # Maps question text to answer string
# Multi-select answers are comma-separated
}도구 이름: 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
입력:
{
"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
입력:
{
"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
입력:
{
"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
입력:
{
"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
입력:
{
"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
입력:
{
"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
입력:
{
"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
입력:
{
"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
입력:
{
"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
입력:
{
"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
입력:
{
"shell_id": str # The ID of the background shell to kill
}출력:
{
"message": str, # Success message
"shell_id": str # ID of the killed shell
}도구 이름: 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
입력:
{
"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
입력:
{
"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
}from claude_agent_sdk import ClaudeSDKClient, ClaudeAgentOptions, AssistantMessage, TextBlock
import asyncio
class ConversationSession:
"""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())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())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())SandboxSettings샌드박스 동작을 위한 구성입니다. 명령어 샌드박싱을 활성화하고 네트워크 제한을 프로그래밍 방식으로 구성하려면 이것을 사용하세요.
class SandboxSettings(TypedDict, total=False):
enabled: bool
autoAllowBashIfSandboxed: bool
excludedCommands: list[str]
allowUnsandboxedCommands: bool
network: SandboxNetworkConfig
ignoreViolations: SandboxIgnoreViolations
enableWeakerNestedSandbox: bool| 속성 | 타입 | 기본값 | 설명 |
|---|---|---|---|
enabled | bool | False | 명령어 실행을 위한 샌드박스 모드 활성화 |
autoAllowBashIfSandboxed | bool | False | 샌드박스가 활성화되었을 때 bash 명령어 자동 승인 |
excludedCommands | list[str] | [] | 항상 샌드박스 제한을 우회하는 명령어 (예: ["docker"]). 이러한 명령어는 모델의 개입 없이 자동으로 샌드박스 외부에서 실행됩니다 |
allowUnsandboxedCommands | bool | False | 모델이 샌드박스 외부에서 명령어 실행을 요청할 수 있도록 허용합니다. True일 때 모델은 도구 입력에서 dangerouslyDisableSandbox를 설정할 수 있으며, 이는 권한 시스템으로 폴백됩니다 |
network | SandboxNetworkConfig | None | 네트워크 관련 샌드박스 구성 |
ignoreViolations | SandboxIgnoreViolations | None | 무시할 샌드박스 위반 구성 |
enableWeakerNestedSandbox | bool | False | 호환성을 위한 약한 중첩 샌드박스 활성화 |
파일 시스템 및 네트워크 접근 제한은 샌드박스 설정을 통해 구성되지 않습니다. 대신 권한 규칙에서 파생됩니다:
명령어 실행 샌드박싱에는 샌드박스 설정을 사용하고, 파일 시스템 및 네트워크 접근 제어에는 권한 규칙을 사용하세요.
from claude_agent_sdk import query, ClaudeAgentOptions, SandboxSettings
sandbox_settings: SandboxSettings = {
"enabled": True,
"autoAllowBashIfSandboxed": True,
"network": {
"allowLocalBinding": True
}
}
async for message in query(
prompt="Build and test my project",
options=ClaudeAgentOptions(sandbox=sandbox_settings)
):
print(message)Unix 소켓 보안: allowUnixSockets 옵션은 강력한 시스템 서비스에 대한 접근 권한을 부여할 수 있습니다. 예를 들어, /var/run/docker.sock을 허용하면 Docker API를 통해 전체 호스트 시스템 접근 권한을 효과적으로 부여하여 샌드박스 격리를 우회합니다. 반드시 필요한 Unix 소켓만 허용하고 각각의 보안 영향을 이해하세요.
SandboxNetworkConfig샌드박스 모드를 위한 네트워크 관련 구성입니다.
class SandboxNetworkConfig(TypedDict, total=False):
allowLocalBinding: bool
allowUnixSockets: list[str]
allowAllUnixSockets: bool
httpProxyPort: int
socksProxyPort: int| 속성 | 타입 | 기본값 | 설명 |
|---|---|---|---|
allowLocalBinding | bool | False | 프로세스가 로컬 포트에 바인딩할 수 있도록 허용 (예: 개발 서버용) |
allowUnixSockets | list[str] | [] | 프로세스가 접근할 수 있는 Unix 소켓 경로 (예: Docker 소켓) |
allowAllUnixSockets | bool | False | 모든 Unix 소켓에 대한 접근 허용 |
httpProxyPort | int | None | 네트워크 요청을 위한 HTTP 프록시 포트 |
socksProxyPort | int | None | 네트워크 요청을 위한 SOCKS 프록시 포트 |
SandboxIgnoreViolations특정 샌드박스 위반을 무시하기 위한 구성입니다.
class SandboxIgnoreViolations(TypedDict, total=False):
file: list[str]
network: list[str]| 속성 | 타입 | 기본값 | 설명 |
|---|---|---|---|
file | list[str] | [] | 위반을 무시할 파일 경로 패턴 |
network | list[str] | [] | 위반을 무시할 네트워크 패턴 |
allowUnsandboxedCommands가 활성화되면, 모델은 도구 입력에서 dangerouslyDisableSandbox: True를 설정하여 샌드박스 외부에서 명령어를 실행하도록 요청할 수 있습니다. 이러한 요청은 기존 권한 시스템으로 폴백되며, 이는 can_use_tool 핸들러가 호출되어 커스텀 인증 로직을 구현할 수 있음을 의미합니다.
excludedCommands vs allowUnsandboxedCommands:
excludedCommands: 항상 자동으로 샌드박스를 우회하는 정적 명령어 목록입니다 (예: ["docker"]). 모델은 이를 제어할 수 없습니다.allowUnsandboxedCommands: 모델이 도구 입력에서 dangerouslyDisableSandbox: True를 설정하여 런타임에 샌드박스 외부 실행을 요청할지 결정할 수 있게 합니다.from claude_agent_sdk import query, ClaudeAgentOptions
async def can_use_tool(tool: str, input: dict) -> bool:
# Check if the model is requesting to bypass the sandbox
if tool == "Bash" and input.get("dangerouslyDisableSandbox"):
# The model wants to run this command outside the sandbox
print(f"Unsandboxed command requested: {input.get('command')}")
# Return True to allow, False to deny
return is_command_authorized(input.get("command"))
return True
async def main():
async for message in query(
prompt="Deploy my application",
options=ClaudeAgentOptions(
sandbox={
"enabled": True,
"allowUnsandboxedCommands": True # Model can request unsandboxed execution
},
permission_mode="default",
can_use_tool=can_use_tool
)
):
print(message)이 패턴을 통해 다음을 수행할 수 있습니다:
dangerouslyDisableSandbox: True로 실행되는 명령어는 전체 시스템 접근 권한을 가집니다. can_use_tool 핸들러가 이러한 요청을 신중하게 검증하도록 하세요.
permission_mode가 bypassPermissions로 설정되고 allow_unsandboxed_commands가 활성화된 경우, 모델은 승인 프롬프트 없이 자율적으로 샌드박스 외부에서 명령어를 실행할 수 있습니다. 이 조합은 사실상 모델이 샌드박스 격리를 조용히 탈출할 수 있게 합니다.
Was this page helpful?