pip install claude-agent-sdkquery() y ClaudeSDKClientEl SDK de Python proporciona dos formas de interactuar con Claude Code:
| Característica | query() | ClaudeSDKClient |
|---|---|---|
| Sesión | Crea una nueva sesión cada vez | Reutiliza la misma sesión |
| Conversación | Intercambio único | Múltiples intercambios en el mismo contexto |
| Conexión | Gestionada automáticamente | Control manual |
| Entrada de streaming | ✅ Compatible | ✅ Compatible |
| Interrupciones | ❌ No compatible | ✅ Compatible |
| Hooks | ❌ No compatible | ✅ Compatible |
| Herramientas personalizadas | ❌ No compatible | ✅ Compatible |
| Continuar chat | ❌ Nueva sesión cada vez | ✅ Mantiene la conversación |
| Caso de uso | Tareas puntuales | Conversaciones continuas |
query() (Nueva sesión cada vez)Mejor para:
ClaudeSDKClient (Conversación continua)Mejor para:
query()Crea una nueva sesión para cada interacción con Claude Code. Devuelve un iterador asincrónico que produce mensajes a medida que llegan. Cada llamada a query() comienza de nuevo sin memoria de interacciones anteriores.
async def query(
*,
prompt: str | AsyncIterable[dict[str, Any]],
options: ClaudeAgentOptions | None = None
) -> AsyncIterator[Message]| Parámetro | Tipo | Descripción |
|---|---|---|
prompt | str | AsyncIterable[dict] | El prompt de entrada como una cadena o iterable asincrónico para modo de streaming |
options | ClaudeAgentOptions | None | Objeto de configuración opcional (por defecto ClaudeAgentOptions() si es None) |
Devuelve un AsyncIterator[Message] que produce mensajes de la conversación.
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 herramientas MCP con seguridad de tipos.
def tool(
name: str,
description: str,
input_schema: type | dict[str, Any]
) -> Callable[[Callable[[Any], Awaitable[dict[str, Any]]]], SdkMcpTool[Any]]| Parámetro | Tipo | Descripción |
|---|---|---|
name | str | Identificador único para la herramienta |
description | str | Descripción legible por humanos de lo que hace la herramienta |
input_schema | type | dict[str, Any] | Esquema que define los parámetros de entrada de la herramienta (ver abajo) |
Mapeo de tipo simple (recomendado):
{"text": str, "count": int, "enabled": bool}Formato JSON Schema (para validación compleja):
{
"type": "object",
"properties": {
"text": {"type": "string"},
"count": {"type": "integer", "minimum": 0}
},
"required": ["text"]
}Una función decoradora que envuelve la implementación de la herramienta y devuelve una instancia de 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()Crea un servidor MCP en proceso que se ejecuta dentro de tu aplicación Python.
def create_sdk_mcp_server(
name: str,
version: str = "1.0.0",
tools: list[SdkMcpTool[Any]] | None = None
) -> McpSdkServerConfig| Parámetro | Tipo | Por defecto | Descripción |
|---|---|---|---|
name | str | - | Identificador único para el servidor |
version | str | "1.0.0" | Cadena de versión del servidor |
tools | list[SdkMcpTool[Any]] | None | None | Lista de funciones de herramientas creadas con el decorador @tool |
Devuelve un objeto McpSdkServerConfig que puede pasarse a 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"]
)ClaudeSDKClientMantiene una sesión de conversación en múltiples intercambios. Este es el equivalente en Python de cómo funciona internamente la función query() del SDK de TypeScript - crea un objeto cliente que puede continuar conversaciones.
query()@tool) y 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 | Descripción |
|---|---|
__init__(options) | Inicializa el cliente con configuración opcional |
connect(prompt) | Conecta a Claude con un prompt inicial opcional o flujo de mensajes |
query(prompt, session_id) | Envía una nueva solicitud en modo de streaming |
receive_messages() | Recibe todos los mensajes de Claude como un iterador asincrónico |
receive_response() | Recibe mensajes hasta e incluyendo un ResultMessage |
interrupt() | Envía señal de interrupción (solo funciona en modo de streaming) |
rewind_files(user_message_uuid) | Restaura archivos a su estado en el mensaje de usuario especificado. Requiere enable_file_checkpointing=True. Ver Punto de control de archivos |
disconnect() | Desconecta de Claude |
El cliente puede usarse como un gestor de contexto asincrónico para gestión automática de conexiones:
async with ClaudeSDKClient() as client:
await client.query("Hello Claude")
async for message in client.receive_response():
print(message)Importante: Al iterar sobre mensajes, evita usar
breakpara salir temprano ya que esto puede causar problemas de limpieza de asyncio. En su lugar, deja que la iteración se complete naturalmente o usa banderas para rastrear cuándo has encontrado lo que necesitas.
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())SdkMcpToolDefinición para una herramienta MCP del SDK creada con el 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]]]| Propiedad | Tipo | Descripción |
|---|---|---|
name | str | Identificador único para la herramienta |
description | str | Descripción legible por humanos |
input_schema | type[T] | dict[str, Any] | Esquema para validación de entrada |
handler | Callable[[T], Awaitable[dict[str, Any]]] | Función asincrónica que maneja la ejecución de la herramienta |
ClaudeAgentOptionsClase de datos de configuración para consultas de 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| Propiedad | Tipo | Por defecto | Descripción |
|---|---|---|---|
tools | list[str] | ToolsPreset | None | None | Configuración de herramientas. Usa {"type": "preset", "preset": "claude_code"} para las herramientas predeterminadas de Claude Code |
allowed_tools | list[str] | [] | Lista de nombres de herramientas permitidas |
system_prompt | str | SystemPromptPreset | None | None | Configuración del prompt del sistema. Pasa una cadena para un prompt personalizado, o usa {"type": "preset", "preset": "claude_code"} para el prompt del sistema de Claude Code. Añade "append" para extender el preset |
mcp_servers | dict[str, McpServerConfig] | str | Path | {} | Configuraciones de servidor MCP o ruta al archivo de configuración |
permission_mode | PermissionMode | None | None | Modo de permiso para el uso de herramientas |
continue_conversation | bool | False | Continuar la conversación más reciente |
resume | str | None | None | ID de sesión a reanudar |
max_turns | int | None | None | Máximo de turnos de conversación |
max_budget_usd | float | None | None | Presupuesto máximo en USD para la sesión |
disallowed_tools | list[str] | [] | Lista de nombres de herramientas no permitidas |
enable_file_checkpointing | bool | False | Habilitar seguimiento de cambios de archivos para rebobinar. Ver Punto de control de archivos |
model | str | None | None | Modelo Claude a usar |
fallback_model | str | None | None | Modelo de respaldo a usar si el modelo principal falla |
betas | list[SdkBeta] | [] | Características beta a habilitar. Ver SdkBeta para opciones disponibles |
output_format | OutputFormat | None | None | Definir formato de salida para resultados del agente. Ver Salidas estructuradas para detalles |
permission_prompt_tool_name | str | None | None | Nombre de herramienta MCP para prompts de permiso |
cwd | str | Path | None | None | Directorio de trabajo actual |
cli_path | str | Path | None | None | Ruta personalizada al ejecutable de la CLI de Claude Code |
settings | str | None | None | Ruta al archivo de configuración |
add_dirs | list[str | Path] | [] | Directorios adicionales a los que Claude puede acceder |
env | dict[str, str] | {} | Variables de entorno |
extra_args | dict[str, str | None] | {} | Argumentos CLI adicionales a pasar directamente a la CLI |
max_buffer_size | int | None | None | Máximo de bytes al almacenar en búfer la salida estándar de la CLI |
debug_stderr | Any | sys.stderr | Deprecado - Objeto similar a un archivo para salida de depuración. Usa el callback stderr en su lugar |
stderr | Callable[[str], None] | None | None | Función de callback para salida stderr de la CLI |
can_use_tool | CanUseTool | None | None | Función de callback de permiso de herramienta. Ver Tipos de permiso para detalles |
hooks | dict[HookEvent, list[HookMatcher]] | None | None | Configuraciones de hook para interceptar eventos |
user | str | None | None | Identificador de usuario |
include_partial_messages | bool | False | Incluir eventos de streaming de mensajes parciales. Cuando está habilitado, se producen mensajes StreamEvent |
fork_session | bool | False | Al reanudar con resume, bifurcar a un nuevo ID de sesión en lugar de continuar la sesión original |
agents | dict[str, AgentDefinition] | None | None | Subagentes definidos programáticamente |
plugins | list[SdkPluginConfig] | [] | Cargar plugins personalizados desde rutas locales. Ver Plugins para detalles |
sandbox | SandboxSettings | None | None | Configurar comportamiento de sandbox programáticamente. Ver Configuración de sandbox para detalles |
setting_sources | list[SettingSource] | None | None (sin configuración) | Controlar qué configuraciones del sistema de archivos cargar. Cuando se omite, no se carga ninguna configuración. Nota: Debe incluir "project" para cargar archivos CLAUDE.md |
max_thinking_tokens | int | None | None | Máximo de tokens para bloques de pensamiento |
OutputFormatConfiguración para validación de salida estructurada.
class OutputFormat(TypedDict):
type: Literal["json_schema"]
schema: dict[str, Any]| Campo | Requerido | Descripción |
|---|---|---|
type | Sí | Debe ser "json_schema" para validación de JSON Schema |
schema | Sí | Definición de JSON Schema para validación de salida |
SystemPromptPresetConfiguración para usar el prompt del sistema preestablecido de Claude Code con adiciones opcionales.
class SystemPromptPreset(TypedDict):
type: Literal["preset"]
preset: Literal["claude_code"]
append: NotRequired[str]| Campo | Requerido | Descripción |
|---|---|---|
type | Sí | Debe ser "preset" para usar un prompt del sistema preestablecido |
preset | Sí | Debe ser "claude_code" para usar el prompt del sistema de Claude Code |
append | No | Instrucciones adicionales a añadir al prompt del sistema preestablecido |
SettingSourceControla qué fuentes de configuración basadas en el sistema de archivos carga el SDK.
SettingSource = Literal["user", "project", "local"]| Valor | Descripción | Ubicación |
|---|---|---|
"user" | Configuración global del usuario | ~/.claude/settings.json |
"project" | Configuración compartida del proyecto (controlada por versión) | .claude/settings.json |
"local" | Configuración local del proyecto (ignorada por git) | .claude/settings.local.json |
Cuando setting_sources está omitido o es None, el SDK no carga ninguna configuración del sistema de archivos. Esto proporciona aislamiento para aplicaciones SDK.
Cargar todas las configuraciones del sistema de archivos (comportamiento heredado):
# 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)Cargar solo fuentes de configuración 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)Entornos de prueba e integración continua:
# 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)Aplicaciones solo 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)Cargar instrucciones del proyecto 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)Cuando se cargan múltiples fuentes, las configuraciones se fusionan con esta precedencia (mayor a menor):
.claude/settings.local.json).claude/settings.json)~/.claude/settings.json)Las opciones programáticas (como agents, allowed_tools) siempre anulan las configuraciones del sistema de archivos.
AgentDefinitionConfiguración para un subagente definido programáticamente.
@dataclass
class AgentDefinition:
description: str
prompt: str
tools: list[str] | None = None
model: Literal["sonnet", "opus", "haiku", "inherit"] | None = None| Campo | Requerido | Descripción |
|---|---|---|
description | Sí | Descripción en lenguaje natural de cuándo usar este agente |
tools | No | Array de nombres de herramientas permitidas. Si se omite, hereda todas las herramientas |
prompt | Sí | El prompt del sistema del agente |
model | No | Anulación de modelo para este agente. Si se omite, usa el modelo principal |
PermissionModeModos de permiso para controlar la ejecución de herramientas.
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 funciones de devolución de llamada de permisos de herramientas.
CanUseTool = Callable[
[str, dict[str, Any], ToolPermissionContext],
Awaitable[PermissionResult]
]La devolución de llamada recibe:
tool_name: Nombre de la herramienta que se está llamandoinput_data: Los parámetros de entrada de la herramientacontext: Un ToolPermissionContext con información adicionalDevuelve un PermissionResult (ya sea PermissionResultAllow o PermissionResultDeny).
ToolPermissionContextInformación de contexto pasada a las devoluciones de llamada de permisos de herramientas.
@dataclass
class ToolPermissionContext:
signal: Any | None = None # Future: abort signal support
suggestions: list[PermissionUpdate] = field(default_factory=list)| Campo | Tipo | Descripción |
|---|---|---|
signal | Any | None | Reservado para soporte futuro de señal de aborto |
suggestions | list[PermissionUpdate] | Sugerencias de actualización de permisos de la CLI |
PermissionResultTipo de unión para resultados de devoluciones de llamada de permisos.
PermissionResult = PermissionResultAllow | PermissionResultDenyPermissionResultAllowResultado que indica que la llamada a la herramienta debe permitirse.
@dataclass
class PermissionResultAllow:
behavior: Literal["allow"] = "allow"
updated_input: dict[str, Any] | None = None
updated_permissions: list[PermissionUpdate] | None = None| Campo | Tipo | Predeterminado | Descripción |
|---|---|---|---|
behavior | Literal["allow"] | "allow" | Debe ser "allow" |
updated_input | dict[str, Any] | None | None | Entrada modificada para usar en lugar de la original |
updated_permissions | list[PermissionUpdate] | None | None | Actualizaciones de permisos a aplicar |
PermissionResultDenyResultado que indica que la llamada a la herramienta debe denegarse.
@dataclass
class PermissionResultDeny:
behavior: Literal["deny"] = "deny"
message: str = ""
interrupt: bool = False| Campo | Tipo | Predeterminado | Descripción |
|---|---|---|---|
behavior | Literal["deny"] | "deny" | Debe ser "deny" |
message | str | "" | Mensaje explicando por qué se denegó la herramienta |
interrupt | bool | False | Si se debe interrumpir la ejecución actual |
PermissionUpdateConfiguración para actualizar permisos mediante programación.
@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 | Descripción |
|---|---|---|
type | Literal[...] | El tipo de operación de actualización de permisos |
rules | list[PermissionRuleValue] | None | Reglas para operaciones de agregar/reemplazar/eliminar |
behavior | Literal["allow", "deny", "ask"] | None | Comportamiento para operaciones basadas en reglas |
mode | PermissionMode | None | Modo para operación setMode |
directories | list[str] | None | Directorios para operaciones de agregar/eliminar directorio |
destination | Literal[...] | None | Dónde aplicar la actualización de permisos |
SdkBetaTipo literal para características beta del SDK.
SdkBeta = Literal["context-1m-2025-08-07"]Usar con el campo betas en ClaudeAgentOptions para habilitar características beta.
McpSdkServerConfigConfiguración para servidores MCP del SDK creados con create_sdk_mcp_server().
class McpSdkServerConfig(TypedDict):
type: Literal["sdk"]
name: str
instance: Any # MCP Server instanceMcpServerConfigTipo de unión para configuraciones 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]]SdkPluginConfigConfiguración para cargar complementos en el SDK.
class SdkPluginConfig(TypedDict):
type: Literal["local"]
path: str| Campo | Tipo | Descripción |
|---|---|---|
type | Literal["local"] | Debe ser "local" (actualmente solo se admiten complementos locales) |
path | str | Ruta absoluta o relativa al directorio del complemento |
Ejemplo:
plugins=[
{"type": "local", "path": "./my-plugin"},
{"type": "local", "path": "/absolute/path/to/plugin"}
]Para obtener información completa sobre cómo crear y usar complementos, consulte Complementos.
MessageTipo de unión de todos los mensajes posibles.
Message = UserMessage | AssistantMessage | SystemMessage | ResultMessage | StreamEventUserMessageMensaje de entrada del usuario.
@dataclass
class UserMessage:
content: str | list[ContentBlock]AssistantMessageMensaje de respuesta del asistente con bloques de contenido.
@dataclass
class AssistantMessage:
content: list[ContentBlock]
model: strSystemMessageMensaje del sistema con metadatos.
@dataclass
class SystemMessage:
subtype: str
data: dict[str, Any]ResultMessageMensaje de resultado final con información de costo y 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 transmisión para actualizaciones de mensajes parciales durante la transmisión. Solo se recibe cuando include_partial_messages=True en 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 | Descripción |
|---|---|---|
uuid | str | Identificador único para este evento |
session_id | str | Identificador de sesión |
event | dict[str, Any] | Los datos del evento de transmisión de la API de Anthropic sin procesar |
parent_tool_use_id | str | None | ID de uso de herramienta principal si este evento es de un subaagente |
ContentBlockTipo de unión de todos los bloques de contenido.
ContentBlock = TextBlock | ThinkingBlock | ToolUseBlock | ToolResultBlockTextBlockBloque de contenido de texto.
@dataclass
class TextBlock:
text: strThinkingBlockBloque de contenido de pensamiento (para modelos con capacidad de pensamiento).
@dataclass
class ThinkingBlock:
thinking: str
signature: strToolUseBlockBloque de solicitud de uso de herramienta.
@dataclass
class ToolUseBlock:
id: str
name: str
input: dict[str, Any]ToolResultBlockBloque de resultado de ejecución de herramienta.
@dataclass
class ToolResultBlock:
tool_use_id: str
content: str | list[dict[str, Any]] | None = None
is_error: bool | None = NoneClaudeSDKErrorClase de excepción base para todos los errores del SDK.
class ClaudeSDKError(Exception):
"""Base error for Claude SDK."""CLINotFoundErrorSe genera cuando Claude Code CLI no está instalado o no se encuentra.
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
"""CLIConnectionErrorSe genera cuando la conexión a Claude Code falla.
class CLIConnectionError(ClaudeSDKError):
"""Failed to connect to Claude Code."""ProcessErrorSe genera cuando el proceso de Claude Code falla.
class ProcessError(ClaudeSDKError):
def __init__(self, message: str, exit_code: int | None = None, stderr: str | None = None):
self.exit_code = exit_code
self.stderr = stderrCLIJSONDecodeErrorSe genera cuando el análisis de JSON falla.
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 una guía completa sobre el uso de ganchos con ejemplos y patrones comunes, consulte la guía de Ganchos.
HookEventTipos de eventos de gancho admitidos. Tenga en cuenta que debido a limitaciones de configuración, el SDK de Python no admite ganchos SessionStart, SessionEnd y 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
]HookCallbackDefinición de tipo para funciones de devolución de llamada de gancho.
HookCallback = Callable[
[dict[str, Any], str | None, HookContext],
Awaitable[dict[str, Any]]
]Parámetros:
input_data: Datos de entrada específicos del gancho (consulte guía de Ganchos)tool_use_id: Identificador de uso de herramienta opcional (para ganchos relacionados con herramientas)context: Contexto de gancho con información adicionalDevuelve un diccionario que puede contener:
decision: "block" para bloquear la acciónsystemMessage: Mensaje del sistema para agregar a la transcripciónhookSpecificOutput: Datos de salida específicos del ganchoHookContextInformación de contexto pasada a las devoluciones de llamada de gancho.
@dataclass
class HookContext:
signal: Any | None = None # Future: abort signal supportHookMatcherConfiguración para hacer coincidir ganchos con eventos o herramientas 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 de unión de todos los tipos de entrada de gancho. El tipo real depende del campo hook_event_name.
HookInput = (
PreToolUseHookInput
| PostToolUseHookInput
| UserPromptSubmitHookInput
| StopHookInput
| SubagentStopHookInput
| PreCompactHookInput
)BaseHookInputCampos base presentes en todos los tipos de entrada de gancho.
class BaseHookInput(TypedDict):
session_id: str
transcript_path: str
cwd: str
permission_mode: NotRequired[str]| Campo | Tipo | Descripción |
|---|---|---|
session_id | str | Identificador de sesión actual |
transcript_path | str | Ruta al archivo de transcripción de sesión |
cwd | str | Directorio de trabajo actual |
permission_mode | str (opcional) | Modo de permiso actual |
PreToolUseHookInputDatos de entrada para eventos de gancho PreToolUse.
class PreToolUseHookInput(BaseHookInput):
hook_event_name: Literal["PreToolUse"]
tool_name: str
tool_input: dict[str, Any]| Campo | Tipo | Descripción |
|---|---|---|
hook_event_name | Literal["PreToolUse"] | Siempre "PreToolUse" |
tool_name | str | Nombre de la herramienta a punto de ejecutarse |
tool_input | dict[str, Any] | Parámetros de entrada para la herramienta |
PostToolUseHookInputDatos de entrada para eventos de gancho PostToolUse.
class PostToolUseHookInput(BaseHookInput):
hook_event_name: Literal["PostToolUse"]
tool_name: str
tool_input: dict[str, Any]
tool_response: Any| Campo | Tipo | Descripción |
|---|---|---|
hook_event_name | Literal["PostToolUse"] | Siempre "PostToolUse" |
tool_name | str | Nombre de la herramienta que se ejecutó |
tool_input | dict[str, Any] | Parámetros de entrada que se utilizaron |
tool_response | Any | Respuesta de la ejecución de la herramienta |
UserPromptSubmitHookInputDatos de entrada para eventos de gancho UserPromptSubmit.
class UserPromptSubmitHookInput(BaseHookInput):
hook_event_name: Literal["UserPromptSubmit"]
prompt: str| Campo | Tipo | Descripción |
|---|---|---|
hook_event_name | Literal["UserPromptSubmit"] | Siempre "UserPromptSubmit" |
prompt | str | El mensaje enviado por el usuario |
StopHookInputDatos de entrada para eventos de gancho Stop.
class StopHookInput(BaseHookInput):
hook_event_name: Literal["Stop"]
stop_hook_active: bool| Campo | Tipo | Descripción |
|---|---|---|
hook_event_name | Literal["Stop"] | Siempre "Stop" |
stop_hook_active | bool | Si el gancho de parada está activo |
SubagentStopHookInputDatos de entrada para eventos de gancho SubagentStop.
class SubagentStopHookInput(BaseHookInput):
hook_event_name: Literal["SubagentStop"]
stop_hook_active: bool| Campo | Tipo | Descripción |
|---|---|---|
hook_event_name | Literal["SubagentStop"] | Siempre "SubagentStop" |
stop_hook_active | bool | Si el gancho de parada está activo |
PreCompactHookInputDatos de entrada para eventos de gancho PreCompact.
class PreCompactHookInput(BaseHookInput):
hook_event_name: Literal["PreCompact"]
trigger: Literal["manual", "auto"]
custom_instructions: str | None| Campo | Tipo | Descripción |
|---|---|---|
hook_event_name | Literal["PreCompact"] | Siempre "PreCompact" |
trigger | Literal["manual", "auto"] | Qué desencadenó la compactación |
custom_instructions | str | None | Instrucciones personalizadas para compactación |
HookJSONOutputTipo de unión para valores de retorno de devoluciones de llamada de gancho.
HookJSONOutput = AsyncHookJSONOutput | SyncHookJSONOutputSyncHookJSONOutputSalida de gancho síncrono con campos de control y decisión.
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_ (with underscore) in Python code. It is automatically converted to continue when sent to the CLI.
AsyncHookJSONOutputSalida de gancho asíncrono que difiere la ejecución del gancho.
class AsyncHookJSONOutput(TypedDict):
async_: Literal[True] # Set to True to defer execution
asyncTimeout: NotRequired[int] # Timeout in millisecondsUse async_ (with underscore) in Python code. It is automatically converted to async when sent to the CLI.
Este ejemplo registra dos ganchos: uno que bloquea comandos bash peligrosos como rm -rf /, y otro que registra todo el uso de herramientas para auditoría. El gancho de seguridad solo se ejecuta en comandos Bash (a través del matcher), mientras que el gancho de registro se ejecuta en todas las herramientas.
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)Documentación de esquemas de entrada/salida para todas las herramientas integradas de Claude Code. Aunque el SDK de Python no exporta estos como tipos, representan la estructura de entradas y salidas de herramientas en mensajes.
Nombre de herramienta: 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
}Salida:
{
"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
}Nombre de herramienta: AskUserQuestion
Hace preguntas aclaratorias al usuario durante la ejecución. Consulte Manejar aprobaciones e entrada del usuario para obtener detalles 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
}Salida:
{
"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
}Nombre de herramienta: 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
}Salida:
{
"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
}Nombre de herramienta: 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)
}Salida:
{
"message": str, # Confirmation message
"replacements": int, # Number of replacements made
"file_path": str # File path that was edited
}Nombre de herramienta: 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
}Salida (Archivos de texto):
{
"content": str, # File contents with line numbers
"total_lines": int, # Total number of lines in file
"lines_returned": int # Lines actually returned
}Salida (Imágenes):
{
"image": str, # Base64 encoded image data
"mime_type": str, # Image MIME type
"file_size": int # File size in bytes
}Nombre de herramienta: Write
Entrada:
{
"file_path": str, # The absolute path to the file to write
"content": str # The content to write to the file
}Salida:
{
"message": str, # Success message
"bytes_written": int, # Number of bytes written
"file_path": str # File path that was written
}Nombre de herramienta: Glob
Entrada:
{
"pattern": str, # The glob pattern to match files against
"path": str | None # The directory to search in (defaults to cwd)
}Salida:
{
"matches": list[str], # Array of matching file paths
"count": int, # Number of matches found
"search_path": str # Search directory used
}Nombre de herramienta: 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
}Salida (modo content):
{
"matches": [
{
"file": str,
"line_number": int | None,
"line": str,
"before_context": list[str] | None,
"after_context": list[str] | None
}
],
"total_matches": int
}Salida (modo files_with_matches):
{
"files": list[str], # Files containing matches
"count": int # Number of files with matches
}Nombre de herramienta: 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
}Salida:
{
"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
}Nombre de herramienta: WebFetch
Entrada:
{
"url": str, # The URL to fetch content from
"prompt": str # The prompt to run on the fetched content
}Salida:
{
"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
}Nombre de herramienta: 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
}Salida:
{
"results": [
{
"title": str,
"url": str,
"snippet": str,
"metadata": dict | None
}
],
"total_results": int,
"query": str
}Nombre de herramienta: TodoWrite
Entrada:
{
"todos": [
{
"content": str, # The task description
"status": "pending" | "in_progress" | "completed", # Task status
"activeForm": str # Active form of the description
}
]
}Salida:
{
"message": str, # Success message
"stats": {
"total": int,
"pending": int,
"in_progress": int,
"completed": int
}
}Nombre de herramienta: BashOutput
Entrada:
{
"bash_id": str, # The ID of the background shell
"filter": str | None # Optional regex to filter output lines
}Salida:
{
"output": str, # New output since last check
"status": "running" | "completed" | "failed", # Current shell status
"exitCode": int | None # Exit code when completed
}Nombre de herramienta: KillBash
Entrada:
{
"shell_id": str # The ID of the background shell to kill
}Salida:
{
"message": str, # Success message
"shell_id": str # ID of the killed shell
}Nombre de herramienta: ExitPlanMode
Entrada:
{
"plan": str # The plan to run by the user for approval
}Salida:
{
"message": str, # Confirmation message
"approved": bool | None # Whether user approved the plan
}Nombre de herramienta: ListMcpResources
Entrada:
{
"server": str | None # Optional server name to filter resources by
}Salida:
{
"resources": [
{
"uri": str,
"name": str,
"description": str | None,
"mimeType": str | None,
"server": str
}
],
"total": int
}Nombre de herramienta: ReadMcpResource
Entrada:
{
"server": str, # The MCP server name
"uri": str # The resource URI to read
}Salida:
{
"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())SandboxSettingsConfiguración para el comportamiento del sandbox. Utiliza esto para habilitar el sandboxing de comandos y configurar restricciones de red mediante programación.
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 la ejecución de comandos |
autoAllowBashIfSandboxed | bool | False | Aprobar automáticamente comandos bash cuando el sandbox está habilitado |
excludedCommands | list[str] | [] | Comandos que siempre omiten restricciones de sandbox (por ejemplo, ["docker"]). Estos se ejecutan sin sandbox automáticamente sin intervención del modelo |
allowUnsandboxedCommands | bool | False | Permitir que el modelo solicite ejecutar comandos fuera del sandbox. Cuando es True, el modelo puede establecer dangerouslyDisableSandbox en la entrada de la herramienta, que se retrocede al sistema de permisos |
network | SandboxNetworkConfig | None | Configuración de sandbox específica de red |
ignoreViolations | SandboxIgnoreViolations | None | Configurar qué violaciones de sandbox ignorar |
enableWeakerNestedSandbox | bool | False | Habilitar un sandbox anidado más débil para compatibilidad |
Las restricciones de acceso al sistema de archivos y red NO se configuran a través de la configuración de sandbox. En su lugar, se derivan de reglas de permisos:
Utiliza la configuración de sandbox para el sandboxing de ejecución de comandos, y reglas de permisos para el control de acceso al sistema de archivos y red.
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)Seguridad de socket Unix: La opción allowUnixSockets puede otorgar acceso a servicios del sistema poderosos. Por ejemplo, permitir /var/run/docker.sock efectivamente otorga acceso completo al sistema host a través de la API de Docker, omitiendo el aislamiento de sandbox. Solo permite sockets Unix que sean estrictamente necesarios y comprende las implicaciones de seguridad de cada uno.
SandboxNetworkConfigConfiguración específica de red 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 los procesos se vinculen a puertos locales (por ejemplo, para servidores de desarrollo) |
allowUnixSockets | list[str] | [] | Rutas de socket Unix que los procesos pueden acceder (por ejemplo, socket de Docker) |
allowAllUnixSockets | bool | False | Permitir acceso a todos los sockets Unix |
httpProxyPort | int | None | Puerto proxy HTTP para solicitudes de red |
socksProxyPort | int | None | Puerto proxy SOCKS para solicitudes de red |
SandboxIgnoreViolationsConfiguración para ignorar violaciones específicas de sandbox.
class SandboxIgnoreViolations(TypedDict, total=False):
file: list[str]
network: list[str]| Property | Type | Default | Description |
|---|---|---|---|
file | list[str] | [] | Patrones de ruta de archivo para ignorar violaciones |
network | list[str] | [] | Patrones de red para ignorar violaciones |
Cuando allowUnsandboxedCommands está habilitado, el modelo puede solicitar ejecutar comandos fuera del sandbox estableciendo dangerouslyDisableSandbox: True en la entrada de la herramienta. Estas solicitudes se retroceden al sistema de permisos existente, lo que significa que se invocará tu controlador can_use_tool, permitiéndote implementar lógica de autorización personalizada.
excludedCommands vs allowUnsandboxedCommands:
excludedCommands: Una lista estática de comandos que siempre omiten el sandbox automáticamente (por ejemplo, ["docker"]). El modelo no tiene control sobre esto.allowUnsandboxedCommands: Permite que el modelo decida en tiempo de ejecución si solicitar ejecución sin sandbox estableciendo dangerouslyDisableSandbox: True en la entrada de la herramienta.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 patrón te permite:
Los comandos que se ejecutan con dangerouslyDisableSandbox: True tienen acceso completo al sistema. Asegúrate de que tu controlador can_use_tool valide estas solicitudes cuidadosamente.
Si permission_mode se establece en bypassPermissions y allow_unsandboxed_commands está habilitado, el modelo puede ejecutar autónomamente comandos fuera del sandbox sin ningún aviso de aprobación. Esta combinación efectivamente permite que el modelo escape del aislamiento de sandbox silenciosamente.
Was this page helpful?