Instalação

pip install claude-agent-sdk

Escolhendo Entre query() e ClaudeSDKClient

O Python SDK fornece duas maneiras de interagir com o Claude Code:

Comparação Rápida

Recursoquery()ClaudeSDKClient
SessãoCria nova sessão a cada vezReutiliza a mesma sessão
ConversaTroca únicaMúltiplas trocas no mesmo contexto
ConexãoGerenciada automaticamenteControle manual
Entrada de 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 a cada vez✅ Mantém a conversa
Caso de UsoTarefas pontuaisConversas contínuas

Quando Usar query() (Nova Sessão a Cada Vez)

Melhor para:
  • Perguntas pontuais onde você não precisa do histórico da conversa
  • Tarefas independentes que não requerem contexto de trocas anteriores
  • Scripts de automação simples
  • Quando você quer um novo começo a cada vez

Quando Usar ClaudeSDKClient (Conversa Contínua)

Melhor para:
  • Continuar conversas - Quando você precisa que o Claude lembre do contexto
  • Perguntas de acompanhamento - Construindo sobre respostas anteriores
  • Aplicações interativas - Interfaces de chat, REPLs
  • Lógica orientada por resposta - Quando a próxima ação depende da resposta do Claude
  • Controle de sessão - Gerenciando o ciclo de vida da conversa explicitamente

Funções

query()

Cria uma nova sessão para cada interação com o Claude Code. Retorna um iterador assíncrono que produz mensagens conforme elas 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âmetros

ParâmetroTipoDescrição
promptstr | AsyncIterable[dict]O prompt de entrada como string ou iterável assíncrono para modo streaming
optionsClaudeAgentOptions | NoneObjeto de configuração opcional (padrão para ClaudeAgentOptions() se None)

Retorna

Retorna um AsyncIterator[Message] que produz mensagens da conversa.

Exemplo - Com opções


import asyncio
from claude_agent_sdk import query, ClaudeAgentOptions

async def main():
    options = ClaudeAgentOptions(
        system_prompt="Você é um desenvolvedor Python especialista",
        permission_mode='acceptEdits',
        cwd="/home/user/project"
    )

    async for message in query(
        prompt="Crie um servidor web Python",
        options=options
    ):
        print(message)


asyncio.run(main())

tool()

Decorador para definir ferramentas MCP com segurança de tipos.
def tool(
    name: str,
    description: str,
    input_schema: type | dict[str, Any]
) -> Callable[[Callable[[Any], Awaitable[dict[str, Any]]]], SdkMcpTool[Any]]

Parâmetros

ParâmetroTipoDescrição
namestrIdentificador único para a ferramenta
descriptionstrDescrição legível do que a ferramenta faz
input_schematype | dict[str, Any]Esquema definindo os parâmetros de entrada da ferramenta (veja abaixo)

Opções de Esquema de Entrada

  1. Mapeamento de tipo simples (recomendado):
    {"text": str, "count": int, "enabled": bool}
    
  2. Formato de esquema JSON (para validação complexa):
    {
        "type": "object",
        "properties": {
            "text": {"type": "string"},
            "count": {"type": "integer", "minimum": 0}
        },
        "required": ["text"]
    }
    

Retorna

Uma função decoradora que envolve a implementação da ferramenta e retorna uma instância SdkMcpTool.

Exemplo

from claude_agent_sdk import tool
from typing import Any

@tool("greet", "Cumprimentar um usuário", {"name": str})
async def greet(args: dict[str, Any]) -> dict[str, Any]:
    return {
        "content": [{
            "type": "text",
            "text": f"Olá, {args['name']}!"
        }]
    }

create_sdk_mcp_server()

Criar um servidor MCP em processo que executa dentro da sua aplicação Python.
def create_sdk_mcp_server(
    name: str,
    version: str = "1.0.0",
    tools: list[SdkMcpTool[Any]] | None = None
) -> McpSdkServerConfig

Parâmetros

ParâmetroTipoPadrãoDescrição
namestr-Identificador único para o servidor
versionstr"1.0.0"String de versão do servidor
toolslist[SdkMcpTool[Any]] | NoneNoneLista de funções de ferramenta criadas com o decorador @tool

Retorna

Retorna um objeto McpSdkServerConfig que pode ser passado para ClaudeAgentOptions.mcp_servers.

Exemplo

from claude_agent_sdk import tool, create_sdk_mcp_server

@tool("add", "Somar dois números", {"a": float, "b": float})
async def add(args):
    return {
        "content": [{
            "type": "text",
            "text": f"Soma: {args['a'] + args['b']}"
        }]
    }

@tool("multiply", "Multiplicar dois números", {"a": float, "b": float})
async def multiply(args):
    return {
        "content": [{
            "type": "text",
            "text": f"Produto: {args['a'] * args['b']}"
        }]
    }

calculator = create_sdk_mcp_server(
    name="calculator",
    version="2.0.0",
    tools=[add, multiply]  # Passar funções decoradas
)

# Usar com Claude
options = ClaudeAgentOptions(
    mcp_servers={"calc": calculator},
    allowed_tools=["mcp__calc__add", "mcp__calc__multiply"]
)

Classes

ClaudeSDKClient

Mantém uma sessão de conversa através de múltiplas trocas. Este é o equivalente Python de como a função query() do TypeScript SDK funciona internamente - ela cria um objeto cliente que pode continuar conversas.

Recursos Principais

  • Continuidade de Sessão: Mantém o contexto da conversa através de múltiplas chamadas query()
  • Mesma Conversa: Claude lembra de mensagens anteriores na sessão
  • Suporte a Interrupção: Pode parar o Claude no meio da execução
  • Ciclo de Vida Explícito: Você controla quando a sessão inicia e termina
  • Fluxo Orientado por Resposta: Pode reagir a respostas e enviar acompanhamentos
  • Ferramentas e Hooks Personalizados: Suporta ferramentas personalizadas (criadas com o decorador @tool) e hooks
class ClaudeSDKClient:
    def __init__(self, options: ClaudeAgentOptions | None = None)
    async def connect(self, prompt: str | AsyncIterable[dict] | None = None) -> None
    async def query(self, prompt: str | AsyncIterable[dict], session_id: str = "default") -> None
    async def receive_messages(self) -> AsyncIterator[Message]
    async def receive_response(self) -> AsyncIterator[Message]
    async def interrupt(self) -> None
    async def disconnect(self) -> None

Métodos

MétodoDescrição
__init__(options)Inicializar o cliente com configuração opcional
connect(prompt)Conectar ao Claude com um prompt inicial opcional ou fluxo de mensagem
query(prompt, session_id)Enviar uma nova solicitação em modo streaming
receive_messages()Receber todas as mensagens do Claude como um iterador assíncrono
receive_response()Receber mensagens até e incluindo uma ResultMessage
interrupt()Enviar sinal de interrupção (funciona apenas em modo streaming)
disconnect()Desconectar do Claude

Suporte a Gerenciador de Contexto

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("Olá Claude")
    async for message in client.receive_response():
        print(message)
Importante: Ao iterar sobre mensagens, evite usar break para sair cedo, pois isso pode causar problemas de limpeza do asyncio. Em vez disso, deixe a iteração completar naturalmente ou use flags para rastrear quando você encontrou o que precisa.

Exemplo - Continuando uma conversa

import asyncio
from claude_agent_sdk import ClaudeSDKClient, AssistantMessage, TextBlock, ResultMessage

async def main():
    async with ClaudeSDKClient() as client:
        # Primeira pergunta
        await client.query("Qual é a capital da França?")
        
        # Processar resposta
        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}")
        
        # Pergunta de acompanhamento - Claude lembra do contexto anterior
        await client.query("Qual é a população dessa cidade?")
        
        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}")
        
        # Outro acompanhamento - ainda na mesma conversa
        await client.query("Quais são alguns marcos famosos lá?")
        
        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())

Exemplo - Entrada de streaming com ClaudeSDKClient

import asyncio
from claude_agent_sdk import ClaudeSDKClient

async def message_stream():
    """Gerar mensagens dinamicamente."""
    yield {"type": "text", "text": "Analise os seguintes dados:"}
    await asyncio.sleep(0.5)
    yield {"type": "text", "text": "Temperatura: 25°C"}
    await asyncio.sleep(0.5)
    yield {"type": "text", "text": "Umidade: 60%"}
    await asyncio.sleep(0.5)
    yield {"type": "text", "text": "Que padrões você vê?"}

async def main():
    async with ClaudeSDKClient() as client:
        # Transmitir entrada para Claude
        await client.query(message_stream())
        
        # Processar resposta
        async for message in client.receive_response():
            print(message)
        
        # Acompanhamento na mesma sessão
        await client.query("Devemos nos preocupar com essas leituras?")
        
        async for message in client.receive_response():
            print(message)

asyncio.run(main())

Exemplo - Usando interrupções

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:
        # Iniciar uma tarefa de longa duração
        await client.query("Conte de 1 a 100 lentamente")
        
        # Deixar executar por um tempo
        await asyncio.sleep(2)
        
        # Interromper a tarefa
        await client.interrupt()
        print("Tarefa interrompida!")
        
        # Enviar um novo comando
        await client.query("Apenas diga olá em vez disso")
        
        async for message in client.receive_response():
            # Processar a nova resposta
            pass

asyncio.run(interruptible_task())

Exemplo - Controle avançado de permissões

from claude_agent_sdk import (
    ClaudeSDKClient,
    ClaudeAgentOptions
)

async def custom_permission_handler(
    tool_name: str,
    input_data: dict,
    context: dict
):
    """Lógica personalizada para permissões de ferramentas."""

    # Bloquear escritas em diretórios do sistema
    if tool_name == "Write" and input_data.get("file_path", "").startswith("/system/"):
        return {
            "behavior": "deny",
            "message": "Escrita em diretório do sistema não permitida",
            "interrupt": True
        }

    # Redirecionar operações de arquivo sensíveis
    if tool_name in ["Write", "Edit"] and "config" in input_data.get("file_path", ""):
        safe_path = f"./sandbox/{input_data['file_path']}"
        return {
            "behavior": "allow",
            "updatedInput": {**input_data, "file_path": safe_path}
        }

    # Permitir todo o resto
    return {
        "behavior": "allow",
        "updatedInput": input_data
    }

async def main():
    options = ClaudeAgentOptions(
        can_use_tool=custom_permission_handler,
        allowed_tools=["Read", "Write", "Edit"]
    )
    
    async with ClaudeSDKClient(options=options) as client:
        await client.query("Atualize o arquivo de configuração do sistema")
        
        async for message in client.receive_response():
            # Usará o caminho da sandbox em vez disso
            print(message)

asyncio.run(main())

Tipos

SdkMcpTool

Definiçã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]]]
PropriedadeTipoDescrição
namestrIdentificador único para a ferramenta
descriptionstrDescrição legível
input_schematype[T] | dict[str, Any]Esquema para validação de entrada
handlerCallable[[T], Awaitable[dict[str, Any]]]Função assíncrona que lida com a execução da ferramenta

ClaudeAgentOptions

Dataclass de configuração para consultas do Claude Code.
@dataclass
class ClaudeAgentOptions:
    allowed_tools: list[str] = field(default_factory=list)
    max_thinking_tokens: int = 8000
    system_prompt: str | 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
    fork_session: bool = False
    max_turns: int | None = None
    disallowed_tools: list[str] = field(default_factory=list)
    model: str | None = None
    permission_prompt_tool_name: str | None = None
    cwd: str | Path | None = None
    settings: str | None = None
    add_dirs: list[str | Path] = field(default_factory=list)
    env: dict[str, str] = field(default_factory=dict)
    extra_args: dict[str, str | None] = field(default_factory=dict)
PropriedadeTipoPadrãoDescrição
allowed_toolslist[str][]Lista de nomes de ferramentas permitidas
max_thinking_tokensint8000Tokens máximos para processo de pensamento
system_promptstr | NoneNoneConfiguração do prompt do sistema. Passe uma string para prompt personalizado, ou use formato predefinido para o prompt do sistema do Claude Code
mcp_serversdict[str, McpServerConfig] | str | Path{}Configurações de servidor MCP ou caminho para arquivo de configuração
permission_modePermissionMode | NoneNoneModo de permissão para uso de ferramentas
continue_conversationboolFalseContinuar a conversa mais recente
resumestr | NoneNoneID da sessão para retomar
fork_sessionboolFalseAo retomar com resume, bifurcar para um novo ID de sessão em vez de continuar a sessão original
max_turnsint | NoneNoneTurnos máximos de conversa
disallowed_toolslist[str][]Lista de nomes de ferramentas não permitidas
modelstr | NoneNoneModelo Claude a usar
permission_prompt_tool_namestr | NoneNoneNome da ferramenta MCP para prompts de permissão
cwdstr | Path | NoneNoneDiretório de trabalho atual
settingsstr | NoneNoneCaminho para arquivo de configurações
add_dirslist[str | Path][]Diretórios adicionais que Claude pode acessar
extra_argsdict[str, str | None]{}Argumentos CLI adicionais para passar diretamente para o CLI
can_use_toolCanUseTool | NoneNoneFunção de callback de permissão de ferramenta
hooksdict[HookEvent, list[HookMatcher]] | NoneNoneConfigurações de hook para interceptar eventos
agentsdict[str, AgentDefinition] | NoneNoneSubagentes definidos programaticamente
setting_sourceslist[SettingSource] | NoneNone (sem configurações)Controla quais configurações do sistema de arquivos carregar. Quando omitido, nenhuma configuração é carregada

SettingSource

Controla quais fontes de configuração baseadas no sistema de arquivos o SDK carrega configurações.
SettingSource = Literal["user", "project", "local"]
ValorDescriçãoLocalização
"user"Configurações globais do usuário~/.claude/settings.json
"project"Configurações compartilhadas do projeto (controladas por versão).claude/settings.json
"local"Configurações locais do projeto (gitignored).claude/settings.local.json

Comportamento padrão

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.

Por que usar setting_sources?

Carregar todas as configurações do sistema de arquivos (comportamento legado):
# Carregar todas as configurações como o SDK v0.0.x fazia
from claude_agent_sdk import query, ClaudeAgentOptions

async for message in query(
    prompt="Analise este código",
    options=ClaudeAgentOptions(
        setting_sources=["user", "project", "local"]  # Carregar todas as configurações
    )
):
    print(message)
Carregar apenas fontes de configuração específicas:
# Carregar apenas configurações do projeto, ignorar usuário e local
async for message in query(
    prompt="Executar verificações de CI",
    options=ClaudeAgentOptions(
        setting_sources=["project"]  # Apenas .claude/settings.json
    )
):
    print(message)
Ambientes de teste e CI:
# Garantir comportamento consistente em CI excluindo configurações locais
async for message in query(
    prompt="Executar testes",
    options=ClaudeAgentOptions(
        setting_sources=["project"],  # Apenas configurações compartilhadas da equipe
        permission_mode="bypassPermissions"
    )
):
    print(message)
Aplicações apenas SDK:
# Definir tudo programaticamente (comportamento padrão)
# Sem dependências do sistema de arquivos - setting_sources padrão para None
async for message in query(
    prompt="Revisar este PR",
    options=ClaudeAgentOptions(
        # setting_sources=None é o padrão, não precisa especificar
        agents={ /* ... */ },
        mcp_servers={ /* ... */ },
        allowed_tools=["Read", "Grep", "Glob"]
    )
):
    print(message)

Precedência de configurações

Quando múltiplas fontes são carregadas, as configurações são mescladas com esta precedência (maior para menor):
  1. Configurações locais (.claude/settings.local.json)
  2. Configurações do projeto (.claude/settings.json)
  3. Configurações do usuário (~/.claude/settings.json)
Opções programáticas (como agents, allowed_tools) sempre sobrescrevem configurações do sistema de arquivos.

AgentDefinition

Configuraçã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
CampoObrigatórioDescrição
descriptionSimDescrição em linguagem natural de quando usar este agente
toolsNãoArray de nomes de ferramentas permitidas. Se omitido, herda todas as ferramentas
promptSimO prompt do sistema do agente
modelNãoSubstituição de modelo para este agente. Se omitido, usa o modelo principal

PermissionMode

Modos de permissão para controlar a execução de ferramentas.
PermissionMode = Literal[
    "default",           # Comportamento padrão de permissão
    "acceptEdits",       # Auto-aceitar edições de arquivo
    "plan",              # Modo de planejamento - sem execução
    "bypassPermissions"  # Ignorar todas as verificações de permissão (use com cuidado)
]

McpSdkServerConfig

Configuração para servidores SDK MCP criados com create_sdk_mcp_server().
class McpSdkServerConfig(TypedDict):
    type: Literal["sdk"]
    name: str
    instance: Any  # Instância do servidor MCP

McpServerConfig

Tipo união para configurações de servidor MCP.
McpServerConfig = McpStdioServerConfig | McpSSEServerConfig | McpHttpServerConfig | McpSdkServerConfig

McpStdioServerConfig

class McpStdioServerConfig(TypedDict):
    type: NotRequired[Literal["stdio"]]  # Opcional para compatibilidade com versões anteriores
    command: str
    args: NotRequired[list[str]]
    env: NotRequired[dict[str, str]]

McpSSEServerConfig

class McpSSEServerConfig(TypedDict):
    type: Literal["sse"]
    url: str
    headers: NotRequired[dict[str, str]]

McpHttpServerConfig

class McpHttpServerConfig(TypedDict):
    type: Literal["http"]
    url: str
    headers: NotRequired[dict[str, str]]

Tipos de Mensagem

Message

Tipo união de todas as mensagens possíveis.
Message = UserMessage | AssistantMessage | SystemMessage | ResultMessage

UserMessage

Mensagem de entrada do usuário.
@dataclass
class UserMessage:
    content: str | list[ContentBlock]

AssistantMessage

Mensagem de resposta do assistente com blocos de conteúdo.
@dataclass
class AssistantMessage:
    content: list[ContentBlock]
    model: str

SystemMessage

Mensagem do sistema com metadados.
@dataclass
class SystemMessage:
    subtype: str
    data: dict[str, Any]

ResultMessage

Mensagem 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

Tipos de Bloco de Conteúdo

ContentBlock

Tipo união de todos os blocos de conteúdo.
ContentBlock = TextBlock | ThinkingBlock | ToolUseBlock | ToolResultBlock

TextBlock

Bloco de conteúdo de texto.
@dataclass
class TextBlock:
    text: str

ThinkingBlock

Bloco de conteúdo de pensamento (para modelos com capacidade de pensamento).
@dataclass
class ThinkingBlock:
    thinking: str
    signature: str

ToolUseBlock

Bloco de solicitação de uso de ferramenta.
@dataclass
class ToolUseBlock:
    id: str
    name: str
    input: dict[str, Any]

ToolResultBlock

Bloco 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 = None

Tipos de Erro

ClaudeSDKError

Classe de exceção base para todos os erros do SDK.
class ClaudeSDKError(Exception):
    """Erro base para Claude SDK."""

CLINotFoundError

Levantado quando o CLI do Claude Code não está instalado ou não foi encontrado.
class CLINotFoundError(CLIConnectionError):
    def __init__(self, message: str = "Claude Code não encontrado", cli_path: str | None = None):
        """
        Args:
            message: Mensagem de erro (padrão: "Claude Code não encontrado")
            cli_path: Caminho opcional para o CLI que não foi encontrado
        """

CLIConnectionError

Levantado quando a conexão com o Claude Code falha.
class CLIConnectionError(ClaudeSDKError):
    """Falha ao conectar com o Claude Code."""

ProcessError

Levantado quando o processo do 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 = stderr

CLIJSONDecodeError

Levantado quando a análise JSON falha.
class CLIJSONDecodeError(ClaudeSDKError):
    def __init__(self, line: str, original_error: Exception):
        """
        Args:
            line: A linha que falhou ao analisar
            original_error: A exceção original de decodificação JSON
        """
        self.line = line
        self.original_error = original_error

Tipos de Hook

HookEvent

Tipos de evento de hook suportados. Note que devido a limitações de configuração, o Python SDK não suporta hooks SessionStart, SessionEnd e Notification.
HookEvent = Literal[
    "PreToolUse",      # Chamado antes da execução da ferramenta
    "PostToolUse",     # Chamado após a execução da ferramenta
    "UserPromptSubmit", # Chamado quando o usuário envia um prompt
    "Stop",            # Chamado ao parar a execução
    "SubagentStop",    # Chamado quando um subagente para
    "PreCompact"       # Chamado antes da compactação de mensagem
]

HookCallback

Definiçã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 (veja documentação de hook)
  • tool_use_id: Identificador opcional de uso de ferramenta (para hooks relacionados a ferramentas)
  • context: Contexto do hook com informações adicionais
Retorna um dicionário que pode conter:
  • decision: "block" para bloquear a ação
  • systemMessage: Mensagem do sistema para adicionar à transcrição
  • hookSpecificOutput: Dados de saída específicos do hook

HookContext

Informações de contexto passadas para callbacks de hook.
@dataclass
class HookContext:
    signal: Any | None = None  # Futuro: suporte a sinal de aborto

HookMatcher

Configuração para corresponder hooks a eventos ou ferramentas específicas.
@dataclass
class HookMatcher:
    matcher: str | None = None        # Nome da ferramenta ou padrão para corresponder (ex: "Bash", "Write|Edit")
    hooks: list[HookCallback] = field(default_factory=list)  # Lista de callbacks para executar

Exemplo de Uso de Hook

from claude_agent_sdk import query, ClaudeAgentOptions, HookMatcher, HookContext
from typing import Any

async def validate_bash_command(
    input_data: dict[str, Any],
    tool_use_id: str | None,
    context: HookContext
) -> dict[str, Any]:
    """Validar e potencialmente bloquear comandos bash perigosos."""
    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': 'Comando perigoso bloqueado'
                }
            }
    return {}

async def log_tool_use(
    input_data: dict[str, Any],
    tool_use_id: str | None,
    context: HookContext
) -> dict[str, Any]:
    """Registrar todo uso de ferramenta para auditoria."""
    print(f"Ferramenta usada: {input_data.get('tool_name')}")
    return {}

options = ClaudeAgentOptions(
    hooks={
        'PreToolUse': [
            HookMatcher(matcher='Bash', hooks=[validate_bash_command]),
            HookMatcher(hooks=[log_tool_use])  # Aplica a todas as ferramentas
        ],
        'PostToolUse': [
            HookMatcher(hooks=[log_tool_use])
        ]
    }
)

async for message in query(
    prompt="Analise esta base de código",
    options=options
):
    print(message)

Tipos de Entrada/Saída de Ferramenta

Documentação de esquemas de entrada/saída para todas as ferramentas integradas do Claude Code. Embora o Python SDK não exporte estes como tipos, eles representam a estrutura de entradas e saídas de ferramentas em mensagens.

Task

Nome da ferramenta: Task Entrada:
{
    "description": str,      # Uma descrição curta (3-5 palavras) da tarefa
    "prompt": str,           # A tarefa para o agente executar
    "subagent_type": str     # O tipo de agente especializado a usar
}
Saída:
{
    "result": str,                    # Resultado final do subagente
    "usage": dict | None,             # Estatísticas de uso de token
    "total_cost_usd": float | None,  # Custo total em USD
    "duration_ms": int | None         # Duração da execução em milissegundos
}

Bash

Nome da ferramenta: Bash Entrada:
{
    "command": str,                  # O comando a executar
    "timeout": int | None,           # Timeout opcional em milissegundos (máx 600000)
    "description": str | None,       # Descrição clara e concisa (5-10 palavras)
    "run_in_background": bool | None # Definir como true para executar em segundo plano
}
Saída:
{
    "output": str,              # Saída combinada de stdout e stderr
    "exitCode": int,            # Código de saída do comando
    "killed": bool | None,      # Se o comando foi morto devido ao timeout
    "shellId": str | None       # ID do shell para processos em segundo plano
}

Edit

Nome da ferramenta: Edit Entrada:
{
    "file_path": str,           # O caminho absoluto para o arquivo a modificar
    "old_string": str,          # O texto a substituir
    "new_string": str,          # O texto para substituí-lo
    "replace_all": bool | None  # Substituir todas as ocorrências (padrão False)
}
Saída:
{
    "message": str,      # Mensagem de confirmação
    "replacements": int, # Número de substituições feitas
    "file_path": str     # Caminho do arquivo que foi editado
}

MultiEdit

Nome da ferramenta: MultiEdit Entrada:
{
    "file_path": str,     # O caminho absoluto para o arquivo a modificar
    "edits": [            # Array de operações de edição
        {
            "old_string": str,          # O texto a substituir
            "new_string": str,          # O texto para substituí-lo
            "replace_all": bool | None  # Substituir todas as ocorrências
        }
    ]
}
Saída:
{
    "message": str,       # Mensagem de sucesso
    "edits_applied": int, # Número total de edições aplicadas
    "file_path": str      # Caminho do arquivo que foi editado
}

Read

Nome da ferramenta: Read Entrada:
{
    "file_path": str,       # O caminho absoluto para o arquivo a ler
    "offset": int | None,   # O número da linha para começar a ler
    "limit": int | None     # O número de linhas a ler
}
Saída (Arquivos de texto):
{
    "content": str,         # Conteúdo do arquivo com números de linha
    "total_lines": int,     # Número total de linhas no arquivo
    "lines_returned": int   # Linhas realmente retornadas
}
Saída (Imagens):
{
    "image": str,       # Dados de imagem codificados em base64
    "mime_type": str,   # Tipo MIME da imagem
    "file_size": int    # Tamanho do arquivo em bytes
}

Write

Nome da ferramenta: Write Entrada:
{
    "file_path": str,  # O caminho absoluto para o arquivo a escrever
    "content": str     # O conteúdo a escrever no arquivo
}
Saída:
{
    "message": str,        # Mensagem de sucesso
    "bytes_written": int,  # Número de bytes escritos
    "file_path": str       # Caminho do arquivo que foi escrito
}

Glob

Nome da ferramenta: Glob Entrada:
{
    "pattern": str,       # O padrão glob para corresponder arquivos
    "path": str | None    # O diretório para pesquisar (padrão para cwd)
}
Saída:
{
    "matches": list[str],  # Array de caminhos de arquivo correspondentes
    "count": int,          # Número de correspondências encontradas
    "search_path": str     # Diretório de pesquisa usado
}

Grep

Nome da ferramenta: Grep Entrada:
{
    "pattern": str,                    # O padrão de expressão regular
    "path": str | None,                # Arquivo ou diretório para pesquisar
    "glob": str | None,                # Padrão glob para filtrar arquivos
    "type": str | None,                # Tipo de arquivo para pesquisar
    "output_mode": str | None,         # "content", "files_with_matches", ou "count"
    "-i": bool | None,                 # Pesquisa insensível a maiúsculas
    "-n": bool | None,                 # Mostrar números de linha
    "-B": int | None,                  # Linhas para mostrar antes de cada correspondência
    "-A": int | None,                  # Linhas para mostrar após cada correspondência
    "-C": int | None,                  # Linhas para mostrar antes e depois
    "head_limit": int | None,          # Limitar saída às primeiras N linhas/entradas
    "multiline": bool | None           # Habilitar modo multilinha
}
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],  # Arquivos contendo correspondências
    "count": int         # Número de arquivos com correspondências
}

NotebookEdit

Nome da ferramenta: NotebookEdit Entrada:
{
    "notebook_path": str,                     # Caminho absoluto para o notebook Jupyter
    "cell_id": str | None,                    # O ID da célula a editar
    "new_source": str,                        # A nova fonte para a célula
    "cell_type": "code" | "markdown" | None,  # O tipo da célula
    "edit_mode": "replace" | "insert" | "delete" | None  # Tipo de operação de edição
}
Saída:
{
    "message": str, # Mensagem de sucesso
    "edit_type": "replaced" | "inserted" | "deleted",  # Tipo de edição executada
    "cell_id": str | None,                       # ID da célula que foi afetada
    "total_cells": int                           # Total de células no notebook após edição
}

WebFetch

Nome da ferramenta: WebFetch Entrada:
{
    "url": str,     # A URL para buscar conteúdo
    "prompt": str   # O prompt para executar no conteúdo buscado
}
Saída:
{
    "response": str,           # Resposta do modelo de IA ao prompt
    "url": str,                # URL que foi buscada
    "final_url": str | None,   # URL final após redirecionamentos
    "status_code": int | None  # Código de status HTTP
}

WebSearch

Nome da ferramenta: WebSearch Entrada:
{
    "query": str,                        # A consulta de pesquisa a usar
    "allowed_domains": list[str] | None, # Incluir apenas resultados destes domínios
    "blocked_domains": list[str] | None  # Nunca incluir resultados destes domínios
}
Saída:
{
    "results": [
        {
            "title": str,
            "url": str,
            "snippet": str,
            "metadata": dict | None
        }
    ],
    "total_results": int,
    "query": str
}

TodoWrite

Nome da ferramenta: TodoWrite Entrada:
{
    "todos": [
        {
            "content": str, # A descrição da tarefa
            "status": "pending" | "in_progress" | "completed",  # Status da tarefa
            "activeForm": str                            # Forma ativa da descrição
        }
    ]
}
Saída:
{
    "message": str,  # Mensagem de sucesso
    "stats": {
        "total": int,
        "pending": int,
        "in_progress": int,
        "completed": int
    }
}

BashOutput

Nome da ferramenta: BashOutput Entrada:
{
    "bash_id": str,       # O ID do shell em segundo plano
    "filter": str | None  # Regex opcional para filtrar linhas de saída
}
Saída:
{
    "output": str, # Nova saída desde a última verificação
    "status": "running" | "completed" | "failed",       # Status atual do shell
    "exitCode": int | None # Código de saída quando completado
}

KillBash

Nome da ferramenta: KillBash Entrada:
{
    "shell_id": str  # O ID do shell em segundo plano para matar
}
Saída:
{
    "message": str,  # Mensagem de sucesso
    "shell_id": str  # ID do shell morto
}

ExitPlanMode

Nome da ferramenta: ExitPlanMode Entrada:
{
    "plan": str  # O plano para executar pelo usuário para aprovação
}
Saída:
{
    "message": str,          # Mensagem de confirmação
    "approved": bool | None  # Se o usuário aprovou o plano
}

ListMcpResources

Nome da ferramenta: List McpResources Entrada:
{
    "server": str | None  # Nome opcional do servidor para filtrar recursos
}
Saída:
{
    "resources": [
        {
            "uri": str,
            "name": str,
            "description": str | None,
            "mimeType": str | None,
            "server": str
        }
    ],
    "total": int
}

ReadMcpResource

Nome da ferramenta: ReadMcpResource Entrada:
{
    "server": str,  # O nome do servidor MCP
    "uri": str      # A URI do recurso para ler
}
Saída:
{
    "contents": [
        {
            "uri": str,
            "mimeType": str | None,
            "text": str | None,
            "blob": str | None
        }
    ],
    "server": str
}

Recursos Avançados com ClaudeSDKClient

Construindo uma Interface de Conversa Contínua

from claude_agent_sdk import ClaudeSDKClient, ClaudeCodeOptions, AssistantMessage, TextBlock
import asyncio

class ConversationSession:
    """Mantém uma única sessão de conversa com Claude."""
    
    def __init__(self, options: ClaudeCodeOptions = None):
        self.client = ClaudeSDKClient(options)
        self.turn_count = 0
    
    async def start(self):
        await self.client.connect()
        print("Iniciando sessão de conversa. Claude lembrará do contexto.")
        print("Comandos: 'exit' para sair, 'interrupt' para parar tarefa atual, 'new' para nova sessão")
        
        while True:
            user_input = input(f"\n[Turno {self.turn_count + 1}] Você: ")
            
            if user_input.lower() == 'exit':
                break
            elif user_input.lower() == 'interrupt':
                await self.client.interrupt()
                print("Tarefa interrompida!")
                continue
            elif user_input.lower() == 'new':
                # Desconectar e reconectar para uma sessão nova
                await self.client.disconnect()
                await self.client.connect()
                self.turn_count = 0
                print("Iniciou nova sessão de conversa (contexto anterior limpo)")
                continue
            
            # Enviar mensagem - Claude lembra de todas as mensagens anteriores nesta sessão
            await self.client.query(user_input)
            self.turn_count += 1
            
            # Processar resposta
            print(f"[Turno {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()  # Nova linha após resposta
        
        await self.client.disconnect()
        print(f"Conversa terminou após {self.turn_count} turnos.")

async def main():
    options = ClaudeCodeOptions(
        allowed_tools=["Read", "Write", "Bash"],
        permission_mode="acceptEdits"
    )
    session = ConversationSession(options)
    await session.start()

# Exemplo de conversa:
# Turno 1 - Você: "Crie um arquivo chamado hello.py"
# Turno 1 - Claude: "Vou criar um arquivo hello.py para você..."
# Turno 2 - Você: "O que tem nesse arquivo?"  
# Turno 2 - Claude: "O arquivo hello.py que acabei de criar contém..." (lembra!)
# Turno 3 - Você: "Adicione uma função main a ele"
# Turno 3 - Claude: "Vou adicionar uma função main ao hello.py..." (sabe qual arquivo!)

asyncio.run(main())

Usando Hooks para Modificação de Comportamento

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]:
    """Registrar todo uso de ferramenta antes da execução."""
    tool_name = input_data.get('tool_name', 'unknown')
    print(f"[PRE-TOOL] Prestes a usar: {tool_name}")

    # Você pode modificar ou bloquear a execução da ferramenta aqui
    if tool_name == "Bash" and "rm -rf" in str(input_data.get('tool_input', {})):
        return {
            'hookSpecificOutput': {
                'hookEventName': 'PreToolUse',
                'permissionDecision': 'deny',
                'permissionDecisionReason': 'Comando perigoso bloqueado'
            }
        }
    return {}

async def post_tool_logger(
    input_data: dict[str, Any],
    tool_use_id: str | None,
    context: HookContext
) -> dict[str, Any]:
    """Registrar resultados após execução da ferramenta."""
    tool_name = input_data.get('tool_name', 'unknown')
    print(f"[POST-TOOL] Completado: {tool_name}")
    return {}

async def user_prompt_modifier(
    input_data: dict[str, Any],
    tool_use_id: str | None,
    context: HookContext
) -> dict[str, Any]:
    """Adicionar contexto aos prompts do usuário."""
    original_prompt = input_data.get('prompt', '')

    # Adicionar timestamp a todos os 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("Listar arquivos no diretório atual")
        
        async for message in client.receive_response():
            # Hooks registrarão automaticamente o uso de ferramentas
            pass

asyncio.run(main())

Monitoramento de Progresso em Tempo Real

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(
            "Crie 5 arquivos Python com diferentes algoritmos de ordenação"
        )
        
        # Monitorar progresso em tempo real
        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"🔨 Criando: {file_path}")
                    elif isinstance(block, ToolResultBlock):
                        print(f"✅ Execução de ferramenta completada")
                    elif isinstance(block, TextBlock):
                        print(f"💭 Claude diz: {block.text[:100]}...")
            
            # Verificar se recebemos o resultado final
            if hasattr(message, 'subtype') and message.subtype in ['success', 'error']:
                print(f"\n🎯 Tarefa completada!")
                break

asyncio.run(monitor_progress())

Exemplo de Uso

Operações básicas de arquivo (usando query)

from claude_agent_sdk import query, ClaudeAgentOptions, AssistantMessage, ToolUseBlock
import asyncio

async def create_project():
    options = ClaudeAgentOptions(
        allowed_tools=["Read", "Write", "Bash"],
        permission_mode='acceptEdits',
        cwd="/home/user/project"
    )
    
    async for message in query(
        prompt="Crie uma estrutura de projeto Python com setup.py",
        options=options
    ):
        if isinstance(message, AssistantMessage):
            for block in message.content:
                if isinstance(block, ToolUseBlock):
                    print(f"Usando ferramenta: {block.name}")

asyncio.run(create_project())

Tratamento de erros

from claude_agent_sdk import (
    query,
    CLINotFoundError,
    ProcessError,
    CLIJSONDecodeError
)

try:
    async for message in query(prompt="Olá"):
        print(message)
except CLINotFoundError:
    print("Por favor instale o Claude Code: npm install -g @anthropic-ai/claude-code")
except ProcessError as e:
    print(f"Processo falhou com código de saída: {e.exit_code}")
except CLIJSONDecodeError as e:
    print(f"Falha ao analisar resposta: {e}")

Modo streaming com cliente

from claude_agent_sdk import ClaudeSDKClient
import asyncio

async def interactive_session():
    async with ClaudeSDKClient() as client:
        # Enviar mensagem inicial
        await client.query("Como está o tempo?")
        
        # Processar respostas
        async for msg in client.receive_response():
            print(msg)
        
        # Enviar acompanhamento
        await client.query("Me conte mais sobre isso")
        
        # Processar resposta de acompanhamento
        async for msg in client.receive_response():
            print(msg)

asyncio.run(interactive_session())

Usando ferramentas personalizadas com ClaudeSDKClient

from claude_agent_sdk import (
    ClaudeSDKClient,
    ClaudeAgentOptions,
    tool,
    create_sdk_mcp_server,
    AssistantMessage,
    TextBlock
)
import asyncio
from typing import Any

# Definir ferramentas personalizadas com decorador @tool
@tool("calculate", "Executar cálculos matemáticos", {"expression": str})
async def calculate(args: dict[str, Any]) -> dict[str, Any]:
    try:
        result = eval(args["expression"], {"__builtins__": {}})
        return {
            "content": [{
                "type": "text",
                "text": f"Resultado: {result}"
            }]
        }
    except Exception as e:
        return {
            "content": [{
                "type": "text",
                "text": f"Erro: {str(e)}"
            }],
            "is_error": True
        }

@tool("get_time", "Obter hora atual", {})
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"Hora atual: {current_time}"
        }]
    }

async def main():
    # Criar servidor SDK MCP com ferramentas personalizadas
    my_server = create_sdk_mcp_server(
        name="utilities",
        version="1.0.0",
        tools=[calculate, get_time]
    )

    # Configurar opções com o servidor
    options = ClaudeAgentOptions(
        mcp_servers={"utils": my_server},
        allowed_tools=[
            "mcp__utils__calculate",
            "mcp__utils__get_time"
        ]
    )
    
    # Usar ClaudeSDKClient para uso interativo de ferramentas
    async with ClaudeSDKClient(options=options) as client:
        await client.query("Quanto é 123 * 456?")
        
        # Processar resposta de cálculo
        async for message in client.receive_response():
            if isinstance(message, AssistantMessage):
                for block in message.content:
                    if isinstance(block, TextBlock):
                        print(f"Cálculo: {block.text}")
        
        # Acompanhar com consulta de hora
        await client.query("Que horas são agora?")
        
        async for message in client.receive_response():
            if isinstance(message, AssistantMessage):
                for block in message.content:
                    if isinstance(block, TextBlock):
                        print(f"Hora: {block.text}")

asyncio.run(main())

Veja também