pip install claude-agent-sdkquery() e ClaudeSDKClientO SDK Python fornece duas maneiras de interagir com Claude Code:
| Recurso | query() | ClaudeSDKClient |
|---|---|---|
| Sessão | Cria nova sessão cada vez | Reutiliza a mesma sessão |
| Conversa | Troca única | Múltiplas trocas no mesmo contexto |
| Conexão | Gerenciada automaticamente | Controle manual |
| Entrada em Streaming | ✅ Suportado | ✅ Suportado |
| Interrupções | ❌ Não suportado | ✅ Suportado |
| Hooks | ❌ Não suportado | ✅ Suportado |
| Ferramentas Personalizadas | ❌ Não suportado | ✅ Suportado |
| Continuar Chat | ❌ Nova sessão cada vez | ✅ Mantém conversa |
| Caso de Uso | Tarefas únicas | Conversas contínuas |
query() (Nova Sessão Cada Vez)Melhor para:
ClaudeSDKClient (Conversa Contínua)Melhor para:
query()Cria uma nova sessão para cada interação com Claude Code. Retorna um iterador assíncrono que produz mensagens conforme chegam. Cada chamada para query() começa do zero sem memória de interações anteriores.
async def query(
*,
prompt: str | AsyncIterable[dict[str, Any]],
options: ClaudeAgentOptions | None = None
) -> AsyncIterator[Message]| Parâmetro | Tipo | Descrição |
|---|---|---|
prompt | str | AsyncIterable[dict] | O prompt de entrada como uma string ou iterável assíncrono para modo de streaming |
options | ClaudeAgentOptions | None | Objeto de configuração opcional (padrão para ClaudeAgentOptions() se None) |
Retorna um AsyncIterator[Message] que produz mensagens da conversa.
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()Decorador para definir ferramentas MCP com segurança de tipo.
def tool(
name: str,
description: str,
input_schema: type | dict[str, Any]
) -> Callable[[Callable[[Any], Awaitable[dict[str, Any]]]], SdkMcpTool[Any]]| Parâmetro | Tipo | Descrição |
|---|---|---|
name | str | Identificador único para a ferramenta |
description | str | Descrição legível por humanos do que a ferramenta faz |
input_schema | type | dict[str, Any] | Schema definindo os parâmetros de entrada da ferramenta (veja abaixo) |
Mapeamento de tipo simples (recomendado):
{"text": str, "count": int, "enabled": bool}Formato JSON Schema (para validação complexa):
{
"type": "object",
"properties": {
"text": {"type": "string"},
"count": {"type": "integer", "minimum": 0}
},
"required": ["text"]
}Uma função decoradora que envolve a implementação da ferramenta e retorna uma instância 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()Cria um servidor MCP em processo que é executado dentro de sua aplicação Python.
def create_sdk_mcp_server(
name: str,
version: str = "1.0.0",
tools: list[SdkMcpTool[Any]] | None = None
) -> McpSdkServerConfig| Parâmetro | Tipo | Padrão | Descrição |
|---|---|---|---|
name | str | - | Identificador único para o servidor |
version | str | "1.0.0" | String de versão do servidor |
tools | list[SdkMcpTool[Any]] | None | None | Lista de funções de ferramenta criadas com decorador @tool |
Retorna um objeto McpSdkServerConfig que pode ser passado para ClaudeAgentOptions.mcp_servers.
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"]
)ClaudeSDKClientMantém uma sessão de conversa em múltiplas trocas. Este é o equivalente Python de como a função query() do SDK TypeScript funciona internamente - ela cria um objeto cliente que pode continuar conversas.
query()@tool) e hooksclass 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| Método | Descrição |
|---|---|
__init__(options) | Inicializa o cliente com configuração opcional |
connect(prompt) | Conecta a Claude com um prompt inicial opcional ou fluxo de mensagens |
query(prompt, session_id) | Envia uma nova solicitação em modo de streaming |
receive_messages() | Recebe todas as mensagens de Claude como um iterador assíncrono |
receive_response() | Recebe mensagens até e incluindo uma ResultMessage |
interrupt() | Envia sinal de interrupção (funciona apenas em modo de streaming) |
rewind_files(user_message_uuid) | Restaura arquivos para seu estado na mensagem de usuário especificada. Requer enable_file_checkpointing=True. Veja File checkpointing |
disconnect() | Desconecta de Claude |
O cliente pode ser usado como um gerenciador de contexto assíncrono para gerenciamento automático de conexão:
async with ClaudeSDKClient() as client:
await client.query("Hello Claude")
async for message in client.receive_response():
print(message)Importante: Ao iterar sobre mensagens, evite usar
breakpara sair cedo, pois isso pode causar problemas de limpeza do asyncio. Em vez disso, deixe a iteração ser concluída naturalmente ou use sinalizadores para rastrear quando você encontrou o que precisa.
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())SdkMcpToolDefinição para uma ferramenta SDK MCP criada com o decorador @tool.
@dataclass
class SdkMcpTool(Generic[T]):
name: str
description: str
input_schema: type[T] | dict[str, Any]
handler: Callable[[T], Awaitable[dict[str, Any]]]| Propriedade | Tipo | Descrição |
|---|---|---|
name | str | Identificador único para a ferramenta |
description | str | Descrição legível por humanos |
input_schema | type[T] | dict[str, Any] | Schema para validação de entrada |
handler | Callable[[T], Awaitable[dict[str, Any]]] | Função assíncrona que manipula a execução da ferramenta |
ClaudeAgentOptionsDataclass de configuração para consultas Claude 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| Propriedade | Tipo | Padrão | Descrição |
|---|---|---|---|
tools | list[str] | ToolsPreset | None | None | Configuração de ferramentas. Use {"type": "preset", "preset": "claude_code"} para as ferramentas padrão do Claude Code |
allowed_tools | list[str] | [] | Lista de nomes de ferramentas permitidas |
system_prompt | str | SystemPromptPreset | None | None | Configuração de prompt do sistema. Passe uma string para prompt personalizado, ou use {"type": "preset", "preset": "claude_code"} para o prompt do sistema do Claude Code. Adicione "append" para estender o preset |
mcp_servers | dict[str, McpServerConfig] | str | Path | {} | Configurações de servidor MCP ou caminho para arquivo de configuração |
permission_mode | PermissionMode | None | None | Modo de permissão para uso de ferramentas |
continue_conversation | bool | False | Continuar a conversa mais recente |
resume | str | None | None | ID de sessão para retomar |
max_turns | int | None | None | Número máximo de turnos de conversa |
max_budget_usd | float | None | None | Orçamento máximo em USD para a sessão |
disallowed_tools | list[str] | [] | Lista de nomes de ferramentas não permitidas |
enable_file_checkpointing | bool | False | Habilitar rastreamento de mudanças de arquivo para retrocesso. Veja File checkpointing |
model | str | None | None | Modelo Claude a usar |
fallback_model | str | None | None | Modelo alternativo a usar se o modelo principal falhar |
betas | list[SdkBeta] | [] | Recursos beta a habilitar. Veja SdkBeta para opções disponíveis |
output_format | OutputFormat | None | None | Definir formato de saída para resultados do agente. Veja Structured outputs para detalhes |
permission_prompt_tool_name | str | None | None | Nome da ferramenta MCP para prompts de permissão |
cwd | str | Path | None | None | Diretório de trabalho atual |
cli_path | str | Path | None | None | Caminho personalizado para o executável Claude Code CLI |
settings | str | None | None | Caminho para arquivo de configurações |
add_dirs | list[str | Path] | [] | Diretórios adicionais que Claude pode acessar |
env | dict[str, str] | {} | Variáveis de ambiente |
extra_args | dict[str, str | None] | {} | Argumentos CLI adicionais a passar diretamente para a CLI |
max_buffer_size | int | None | None | Bytes máximos ao armazenar em buffer a saída stdout da CLI |
debug_stderr | Any | sys.stderr | Deprecated - Objeto semelhante a arquivo para saída de depuração. Use callback stderr em vez disso |
stderr | Callable[[str], None] | None | None | Função de callback para saída stderr da CLI |
can_use_tool | CanUseTool | None | None | Função de callback de permissão de ferramenta. Veja Permission types para detalhes |
hooks | dict[HookEvent, list[HookMatcher]] | None | None | Configurações de hook para interceptar eventos |
user | str | None | None | Identificador de usuário |
include_partial_messages | bool | False | Incluir eventos de streaming de mensagens parciais. Quando habilitado, mensagens StreamEvent são produzidas |
fork_session | bool | False | Ao retomar com resume, fazer fork para um novo ID de sessão em vez de continuar a sessão original |
agents | dict[str, AgentDefinition] | None | None | Subagentes definidos programaticamente |
plugins | list[SdkPluginConfig] | [] | Carregar plugins personalizados de caminhos locais. Veja Plugins para detalhes |
sandbox | SandboxSettings | None | None | Configurar comportamento de sandbox programaticamente. Veja Sandbox settings para detalhes |
setting_sources | list[SettingSource] | None | None (sem configurações) | Controlar quais configurações do sistema de arquivos carregar. Quando omitido, nenhuma configuração é carregada. Nota: Deve incluir "project" para carregar arquivos CLAUDE.md |
max_thinking_tokens | int | None | None | Tokens máximos para blocos de pensamento |
OutputFormatConfiguração para validação de saída estruturada.
class OutputFormat(TypedDict):
type: Literal["json_schema"]
schema: dict[str, Any]| Campo | Obrigatório | Descrição |
|---|---|---|
type | Sim | Deve ser "json_schema" para validação JSON Schema |
schema | Sim | Definição JSON Schema para validação de saída |
SystemPromptPresetConfiguração para usar o preset de prompt do sistema do Claude Code com adições opcionais.
class SystemPromptPreset(TypedDict):
type: Literal["preset"]
preset: Literal["claude_code"]
append: NotRequired[str]| Campo | Obrigatório | Descrição |
|---|---|---|
type | Sim | Deve ser "preset" para usar um preset de prompt do sistema |
preset | Sim | Deve ser "claude_code" para usar o prompt do sistema do Claude Code |
append | Não | Instruções adicionais a anexar ao preset de prompt do sistema |
SettingSourceControla quais fontes de configuração baseadas em sistema de arquivos o SDK carrega configurações.
SettingSource = Literal["user", "project", "local"]| Valor | Descrição | Localização |
|---|---|---|
"user" | Configurações globais do usuário | ~/.claude/settings.json |
"project" | Configurações de projeto compartilhadas (controladas por versão) | .claude/settings.json |
"local" | Configurações de projeto local (gitignored) | .claude/settings.local.json |
Quando setting_sources é omitido ou None, o SDK não carrega nenhuma configuração do sistema de arquivos. Isso fornece isolamento para aplicações SDK.
Carregar todas as configurações do sistema de arquivos (comportamento legado):
# 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)Carregar apenas fontes de configuração específicas:
# 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)Ambientes de teste e 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)Aplicações apenas 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)Carregando instruções de projeto 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)Quando múltiplas fontes são carregadas, as configurações são mescladas com esta precedência (maior para menor):
.claude/settings.local.json).claude/settings.json)~/.claude/settings.json)Opções programáticas (como agents, allowed_tools) sempre substituem configurações do sistema de arquivos.
AgentDefinitionConfiguração para um subagente definido programaticamente.
@dataclass
class AgentDefinition:
description: str
prompt: str
tools: list[str] | None = None
model: Literal["sonnet", "opus", "haiku", "inherit"] | None = None| Campo | Obrigatório | Descrição |
|---|---|---|
description | Sim | Descrição em linguagem natural de quando usar este agente |
tools | Não | Array de nomes de ferramentas permitidas. Se omitido, herda todas as ferramentas |
prompt | Sim | O prompt do sistema do agente |
model | Não | Substituição de modelo para este agente. Se omitido, usa o modelo principal |
PermissionModeModos de permissão para controlar a execução de ferramentas.
PermissionMode = Literal[
"default", # Standard permission behavior
"acceptEdits", # Auto-accept file edits
"plan", # Planning mode - no execution
"bypassPermissions" # Bypass all permission checks (use with caution)
]CanUseToolAlias de tipo para funções de callback de permissão de ferramenta.
CanUseTool = Callable[
[str, dict[str, Any], ToolPermissionContext],
Awaitable[PermissionResult]
]O callback recebe:
tool_name: Nome da ferramenta sendo chamadainput_data: Os parâmetros de entrada da ferramentacontext: Um ToolPermissionContext com informações adicionaisRetorna um PermissionResult (seja PermissionResultAllow ou PermissionResultDeny).
ToolPermissionContextInformações de contexto passadas para callbacks de permissão de ferramenta.
@dataclass
class ToolPermissionContext:
signal: Any | None = None # Future: abort signal support
suggestions: list[PermissionUpdate] = field(default_factory=list)| Campo | Tipo | Descrição |
|---|---|---|
signal | Any | None | Reservado para suporte futuro de sinal de interrupção |
suggestions | list[PermissionUpdate] | Sugestões de atualização de permissão da CLI |
PermissionResultTipo union para resultados de callback de permissão.
PermissionResult = PermissionResultAllow | PermissionResultDenyPermissionResultAllowResultado indicando que a chamada de ferramenta deve ser permitida.
@dataclass
class PermissionResultAllow:
behavior: Literal["allow"] = "allow"
updated_input: dict[str, Any] | None = None
updated_permissions: list[PermissionUpdate] | None = None| Campo | Tipo | Padrão | Descrição |
|---|---|---|---|
behavior | Literal["allow"] | "allow" | Deve ser "allow" |
updated_input | dict[str, Any] | None | None | Entrada modificada para usar em vez da original |
updated_permissions | list[PermissionUpdate] | None | None | Atualizações de permissão a aplicar |
PermissionResultDenyResultado indicando que a chamada de ferramenta deve ser negada.
@dataclass
class PermissionResultDeny:
behavior: Literal["deny"] = "deny"
message: str = ""
interrupt: bool = False| Campo | Tipo | Padrão | Descrição |
|---|---|---|---|
behavior | Literal["deny"] | "deny" | Deve ser "deny" |
message | str | "" | Mensagem explicando por que a ferramenta foi negada |
interrupt | bool | False | Se deve interromper a execução atual |
PermissionUpdateConfiguração para atualizar permissões programaticamente.
@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| Campo | Tipo | Descrição |
|---|---|---|
type | Literal[...] | O tipo de operação de atualização de permissão |
rules | list[PermissionRuleValue] | None | Regras para operações de adicionar/substituir/remover |
behavior | Literal["allow", "deny", "ask"] | None | Comportamento para operações baseadas em regras |
mode | PermissionMode | None | Modo para operação setMode |
directories | list[str] | None | Diretórios para operações de adicionar/remover diretório |
destination | Literal[...] | None | Onde aplicar a atualização de permissão |
SdkBetaTipo literal para recursos beta do SDK.
SdkBeta = Literal["context-1m-2025-08-07"]Use com o campo betas em ClaudeAgentOptions para ativar recursos beta.
McpSdkServerConfigConfiguração para servidores MCP do SDK criados com create_sdk_mcp_server().
class McpSdkServerConfig(TypedDict):
type: Literal["sdk"]
name: str
instance: Any # MCP Server instanceMcpServerConfigTipo union para configurações de servidor MCP.
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]]SdkPluginConfigConfiguração para carregar plugins no SDK.
class SdkPluginConfig(TypedDict):
type: Literal["local"]
path: str| Campo | Tipo | Descrição |
|---|---|---|
type | Literal["local"] | Deve ser "local" (apenas plugins locais são suportados atualmente) |
path | str | Caminho absoluto ou relativo para o diretório do plugin |
Exemplo:
plugins=[
{"type": "local", "path": "./my-plugin"},
{"type": "local", "path": "/absolute/path/to/plugin"}
]Para informações completas sobre como criar e usar plugins, consulte Plugins.
MessageTipo union de todas as mensagens possíveis.
Message = UserMessage | AssistantMessage | SystemMessage | ResultMessage | StreamEventUserMessageMensagem de entrada do usuário.
@dataclass
class UserMessage:
content: str | list[ContentBlock]AssistantMessageMensagem de resposta do assistente com blocos de conteúdo.
@dataclass
class AssistantMessage:
content: list[ContentBlock]
model: strSystemMessageMensagem do sistema com metadados.
@dataclass
class SystemMessage:
subtype: str
data: dict[str, Any]ResultMessageMensagem de resultado final com informações de custo e uso.
@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 = NoneStreamEventEvento de stream para atualizações parciais de mensagens durante streaming. Recebido apenas quando include_partial_messages=True em ClaudeAgentOptions.
@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| Campo | Tipo | Descrição |
|---|---|---|
uuid | str | Identificador único para este evento |
session_id | str | Identificador de sessão |
event | dict[str, Any] | Os dados brutos do evento de stream da API Anthropic |
parent_tool_use_id | str | None | ID de uso de ferramenta pai se este evento for de um subaagente |
ContentBlockTipo union de todos os blocos de conteúdo.
ContentBlock = TextBlock | ThinkingBlock | ToolUseBlock | ToolResultBlockTextBlockBloco de conteúdo de texto.
@dataclass
class TextBlock:
text: strThinkingBlockBloco de conteúdo de pensamento (para modelos com capacidade de pensamento).
@dataclass
class ThinkingBlock:
thinking: str
signature: strToolUseBlockBloco de solicitação de uso de ferramenta.
@dataclass
class ToolUseBlock:
id: str
name: str
input: dict[str, Any]ToolResultBlockBloco de resultado de execução de ferramenta.
@dataclass
class ToolResultBlock:
tool_use_id: str
content: str | list[dict[str, Any]] | None = None
is_error: bool | None = NoneClaudeSDKErrorClasse de exceção base para todos os erros do SDK.
class ClaudeSDKError(Exception):
"""Base error for Claude SDK."""CLINotFoundErrorLevantado quando Claude Code CLI não está instalado ou não é encontrado.
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
"""CLIConnectionErrorLevantado quando a conexão com Claude Code falha.
class CLIConnectionError(ClaudeSDKError):
"""Failed to connect to Claude Code."""ProcessErrorLevantado quando o processo Claude Code falha.
class ProcessError(ClaudeSDKError):
def __init__(self, message: str, exit_code: int | None = None, stderr: str | None = None):
self.exit_code = exit_code
self.stderr = stderrCLIJSONDecodeErrorLevantado quando a análise JSON falha.
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_errorPara um guia abrangente sobre como usar hooks com exemplos e padrões comuns, consulte o guia de Hooks.
HookEventTipos de eventos de hook suportados. Observe que devido a limitações de configuração, o SDK Python não suporta hooks SessionStart, SessionEnd e 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
]HookCallbackDefinição de tipo para funções de callback de hook.
HookCallback = Callable[
[dict[str, Any], str | None, HookContext],
Awaitable[dict[str, Any]]
]Parâmetros:
input_data: Dados de entrada específicos do hook (consulte guia de Hooks)tool_use_id: Identificador de uso de ferramenta opcional (para hooks relacionados a ferramentas)context: Contexto de hook com informações adicionaisRetorna um dicionário que pode conter:
decision: "block" para bloquear a açãosystemMessage: Mensagem do sistema a adicionar à transcriçãohookSpecificOutput: Dados de saída específicos do hookHookContextInformações de contexto passadas para callbacks de hook.
@dataclass
class HookContext:
signal: Any | None = None # Future: abort signal supportHookMatcherConfiguração para corresponder hooks a eventos ou ferramentas específicas.
@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)HookInputTipo union de todos os tipos de entrada de hook. O tipo real depende do campo hook_event_name.
HookInput = (
PreToolUseHookInput
| PostToolUseHookInput
| UserPromptSubmitHookInput
| StopHookInput
| SubagentStopHookInput
| PreCompactHookInput
)BaseHookInputCampos base presentes em todos os tipos de entrada de hook.
class BaseHookInput(TypedDict):
session_id: str
transcript_path: str
cwd: str
permission_mode: NotRequired[str]| Campo | Tipo | Descrição |
|---|---|---|
session_id | str | Identificador de sessão atual |
transcript_path | str | Caminho para o arquivo de transcrição da sessão |
cwd | str | Diretório de trabalho atual |
permission_mode | str (opcional) | Modo de permissão atual |
PreToolUseHookInputDados de entrada para eventos de hook PreToolUse.
class PreToolUseHookInput(BaseHookInput):
hook_event_name: Literal["PreToolUse"]
tool_name: str
tool_input: dict[str, Any]| Campo | Tipo | Descrição |
|---|---|---|
hook_event_name | Literal["PreToolUse"] | Sempre "PreToolUse" |
tool_name | str | Nome da ferramenta prestes a ser executada |
tool_input | dict[str, Any] | Parâmetros de entrada para a ferramenta |
PostToolUseHookInputDados de entrada para eventos de hook PostToolUse.
class PostToolUseHookInput(BaseHookInput):
hook_event_name: Literal["PostToolUse"]
tool_name: str
tool_input: dict[str, Any]
tool_response: Any| Campo | Tipo | Descrição |
|---|---|---|
hook_event_name | Literal["PostToolUse"] | Sempre "PostToolUse" |
tool_name | str | Nome da ferramenta que foi executada |
tool_input | dict[str, Any] | Parâmetros de entrada que foram usados |
tool_response | Any | Resposta da execução da ferramenta |
UserPromptSubmitHookInputDados de entrada para eventos de hook UserPromptSubmit.
class UserPromptSubmitHookInput(BaseHookInput):
hook_event_name: Literal["UserPromptSubmit"]
prompt: str| Campo | Tipo | Descrição |
|---|---|---|
hook_event_name | Literal["UserPromptSubmit"] | Sempre "UserPromptSubmit" |
prompt | str | O prompt enviado pelo usuário |
StopHookInputDados de entrada para eventos de hook Stop.
class StopHookInput(BaseHookInput):
hook_event_name: Literal["Stop"]
stop_hook_active: bool| Campo | Tipo | Descrição |
|---|---|---|
hook_event_name | Literal["Stop"] | Sempre "Stop" |
stop_hook_active | bool | Se o hook de parada está ativo |
SubagentStopHookInputDados de entrada para eventos de hook SubagentStop.
class SubagentStopHookInput(BaseHookInput):
hook_event_name: Literal["SubagentStop"]
stop_hook_active: bool| Campo | Tipo | Descrição |
|---|---|---|
hook_event_name | Literal["SubagentStop"] | Sempre "SubagentStop" |
stop_hook_active | bool | Se o hook de parada está ativo |
PreCompactHookInputDados de entrada para eventos de hook PreCompact.
class PreCompactHookInput(BaseHookInput):
hook_event_name: Literal["PreCompact"]
trigger: Literal["manual", "auto"]
custom_instructions: str | None| Campo | Tipo | Descrição |
|---|---|---|
hook_event_name | Literal["PreCompact"] | Sempre "PreCompact" |
trigger | Literal["manual", "auto"] | O que acionou a compactação |
custom_instructions | str | None | Instruções personalizadas para compactação |
HookJSONOutputTipo union para valores de retorno de callback de hook.
HookJSONOutput = AsyncHookJSONOutput | SyncHookJSONOutputSyncHookJSONOutputSaída de hook síncrona com campos de controle e decisão.
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]]Use continue_ (com underscore) no código Python. É automaticamente convertido para continue quando enviado para a CLI.
AsyncHookJSONOutputSaída de hook assíncrona que adia a execução do hook.
class AsyncHookJSONOutput(TypedDict):
async_: Literal[True] # Set to True to defer execution
asyncTimeout: NotRequired[int] # Timeout in millisecondsUse async_ (com underscore) no código Python. É automaticamente convertido para async quando enviado para a CLI.
Este exemplo registra dois hooks: um que bloqueia comandos bash perigosos como rm -rf /, e outro que registra todo o uso de ferramentas para auditoria. O hook de segurança é executado apenas em comandos Bash (via matcher), enquanto o hook de logging é executado em todas as ferramentas.
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)Documentação de esquemas de entrada/saída para todas as ferramentas Claude Code integradas. Embora o SDK Python não exporte esses como tipos, eles representam a estrutura de entradas e saídas de ferramentas em mensagens.
Nome da ferramenta: Task
Entrada:
{
"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
}Saída:
{
"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
}Nome da ferramenta: AskUserQuestion
Faz perguntas de esclarecimento ao usuário durante a execução. Consulte Lidar com aprovações e entrada do usuário para detalhes de uso.
Entrada:
{
"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
}Saída:
{
"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
}Nome da ferramenta: Bash
Entrada:
{
"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
}Saída:
{
"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
}Nome da ferramenta: Edit
Entrada:
{
"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)
}Saída:
{
"message": str, # Confirmation message
"replacements": int, # Number of replacements made
"file_path": str # File path that was edited
}Nome da ferramenta: Read
Entrada:
{
"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
}Saída (Arquivos de texto):
{
"content": str, # File contents with line numbers
"total_lines": int, # Total number of lines in file
"lines_returned": int # Lines actually returned
}Saída (Imagens):
{
"image": str, # Base64 encoded image data
"mime_type": str, # Image MIME type
"file_size": int # File size in bytes
}Nome da ferramenta: Write
Entrada:
{
"file_path": str, # The absolute path to the file to write
"content": str # The content to write to the file
}Saída:
{
"message": str, # Success message
"bytes_written": int, # Number of bytes written
"file_path": str # File path that was written
}Nome da ferramenta: Glob
Entrada:
{
"pattern": str, # The glob pattern to match files against
"path": str | None # The directory to search in (defaults to cwd)
}Saída:
{
"matches": list[str], # Array of matching file paths
"count": int, # Number of matches found
"search_path": str # Search directory used
}Nome da ferramenta: Grep
Entrada:
{
"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
}Saída (modo content):
{
"matches": [
{
"file": str,
"line_number": int | None,
"line": str,
"before_context": list[str] | None,
"after_context": list[str] | None
}
],
"total_matches": int
}Saída (modo files_with_matches):
{
"files": list[str], # Files containing matches
"count": int # Number of files with matches
}Nome da ferramenta: NotebookEdit
Entrada:
{
"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
}Saída:
{
"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
}Nome da ferramenta: WebFetch
Entrada:
{
"url": str, # The URL to fetch content from
"prompt": str # The prompt to run on the fetched content
}Saída:
{
"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
}Nome da ferramenta: WebSearch
Entrada:
{
"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
}Saída:
{
"results": [
{
"title": str,
"url": str,
"snippet": str,
"metadata": dict | None
}
],
"total_results": int,
"query": str
}Nome da ferramenta: TodoWrite
Entrada:
{
"todos": [
{
"content": str, # The task description
"status": "pending" | "in_progress" | "completed", # Task status
"activeForm": str # Active form of the description
}
]
}Saída:
{
"message": str, # Success message
"stats": {
"total": int,
"pending": int,
"in_progress": int,
"completed": int
}
}Nome da ferramenta: BashOutput
Entrada:
{
"bash_id": str, # The ID of the background shell
"filter": str | None # Optional regex to filter output lines
}Saída:
{
"output": str, # New output since last check
"status": "running" | "completed" | "failed", # Current shell status
"exitCode": int | None # Exit code when completed
}Nome da ferramenta: KillBash
Entrada:
{
"shell_id": str # The ID of the background shell to kill
}Saída:
{
"message": str, # Success message
"shell_id": str # ID of the killed shell
}Nome da ferramenta: ExitPlanMode
Entrada:
{
"plan": str # The plan to run by the user for approval
}Saída:
{
"message": str, # Confirmation message
"approved": bool | None # Whether user approved the plan
}Nome da ferramenta: ListMcpResources
Entrada:
{
"server": str | None # Optional server name to filter resources by
}Saída:
{
"resources": [
{
"uri": str,
"name": str,
"description": str | None,
"mimeType": str | None,
"server": str
}
],
"total": int
}Nome da ferramenta: ReadMcpResource
Entrada:
{
"server": str, # The MCP server name
"uri": str # The resource URI to read
}Saída:
{
"contents": [
{
"uri": str,
"mimeType": str | None,
"text": str | None,
"blob": str | None
}
],
"server": str
}from claude_agent_sdk import ClaudeSDKClient, ClaudeAgentOptions, AssistantMessage, TextBlock
import asyncio
class ConversationSession:
"""Maintains a single conversation session with Claude."""
def __init__(self, options: ClaudeAgentOptions = None):
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())SandboxSettingsConfiguração para comportamento de sandbox. Use isso para habilitar sandboxing de comando e configurar restrições de rede programaticamente.
class SandboxSettings(TypedDict, total=False):
enabled: bool
autoAllowBashIfSandboxed: bool
excludedCommands: list[str]
allowUnsandboxedCommands: bool
network: SandboxNetworkConfig
ignoreViolations: SandboxIgnoreViolations
enableWeakerNestedSandbox: bool| Property | Type | Default | Description |
|---|---|---|---|
enabled | bool | False | Habilitar modo sandbox para execução de comando |
autoAllowBashIfSandboxed | bool | False | Aprovar automaticamente comandos bash quando sandbox está habilitado |
excludedCommands | list[str] | [] | Comandos que sempre contornam restrições de sandbox (por exemplo, ["docker"]). Estes são executados sem sandbox automaticamente sem envolvimento do modelo |
allowUnsandboxedCommands | bool | False | Permitir que o modelo solicite executar comandos fora do sandbox. Quando True, o modelo pode definir dangerouslyDisableSandbox na entrada da ferramenta, que retorna ao sistema de permissões |
network | SandboxNetworkConfig | None | Configuração de sandbox específica de rede |
ignoreViolations | SandboxIgnoreViolations | None | Configurar quais violações de sandbox ignorar |
enableWeakerNestedSandbox | bool | False | Habilitar um sandbox aninhado mais fraco para compatibilidade |
Restrições de acesso ao sistema de arquivos e rede NÃO são configuradas via configurações de sandbox. Em vez disso, elas são derivadas de regras de permissão:
Use configurações de sandbox para sandboxing de execução de comando e regras de permissão para controle de acesso ao sistema de arquivos e rede.
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)Segurança de socket Unix: A opção allowUnixSockets pode conceder acesso a serviços de sistema poderosos. Por exemplo, permitir /var/run/docker.sock efetivamente concede acesso completo ao sistema host através da API Docker, contornando isolamento de sandbox. Apenas permita sockets Unix que são estritamente necessários e compreenda as implicações de segurança de cada um.
SandboxNetworkConfigConfiguração específica de rede para modo sandbox.
class SandboxNetworkConfig(TypedDict, total=False):
allowLocalBinding: bool
allowUnixSockets: list[str]
allowAllUnixSockets: bool
httpProxyPort: int
socksProxyPort: int| Property | Type | Default | Description |
|---|---|---|---|
allowLocalBinding | bool | False | Permitir que processos se vinculem a portas locais (por exemplo, para servidores de desenvolvimento) |
allowUnixSockets | list[str] | [] | Caminhos de socket Unix que processos podem acessar (por exemplo, socket Docker) |
allowAllUnixSockets | bool | False | Permitir acesso a todos os sockets Unix |
httpProxyPort | int | None | Porta de proxy HTTP para requisições de rede |
socksProxyPort | int | None | Porta de proxy SOCKS para requisições de rede |
SandboxIgnoreViolationsConfiguração para ignorar violações específicas de sandbox.
class SandboxIgnoreViolations(TypedDict, total=False):
file: list[str]
network: list[str]| Property | Type | Default | Description |
|---|---|---|---|
file | list[str] | [] | Padrões de caminho de arquivo para ignorar violações |
network | list[str] | [] | Padrões de rede para ignorar violações |
Quando allowUnsandboxedCommands está habilitado, o modelo pode solicitar executar comandos fora do sandbox definindo dangerouslyDisableSandbox: True na entrada da ferramenta. Essas solicitações retornam ao sistema de permissões existente, significando que seu manipulador can_use_tool será invocado, permitindo que você implemente lógica de autorização personalizada.
excludedCommands vs allowUnsandboxedCommands:
excludedCommands: Uma lista estática de comandos que sempre contornam o sandbox automaticamente (por exemplo, ["docker"]). O modelo não tem controle sobre isso.allowUnsandboxedCommands: Permite que o modelo decida em tempo de execução se deve solicitar execução sem sandbox definindo dangerouslyDisableSandbox: True na entrada da ferramenta.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)Este padrão permite que você:
Comandos em execução com dangerouslyDisableSandbox: True têm acesso completo ao sistema. Certifique-se de que seu manipulador can_use_tool valida essas solicitações cuidadosamente.
Se permission_mode estiver definido como bypassPermissions e allow_unsandboxed_commands estiver habilitado, o modelo pode executar autonomamente comandos fora do sandbox sem nenhum prompt de aprovação. Esta combinação efetivamente permite que o modelo escape do isolamento de sandbox silenciosamente.
Was this page helpful?