Prerrequisitos

  • Python 3.10+
  • claude-code-sdk de PyPI
  • Node.js 18+
  • @anthropic-ai/claude-code de NPM

Para ver el código fuente del SDK de Python, consulta el repositorio claude-code-sdk.

Para desarrollo interactivo, usa IPython: pip install ipython

Instalación

Instala claude-code-sdk de PyPI y @anthropic-ai/claude-code de NPM:

pip install claude-code-sdk
npm install -g @anthropic-ai/claude-code  # Dependencia requerida

(Opcional) Instala IPython para desarrollo interactivo:

pip install ipython

Inicio rápido

Crea tu primer agente:

# legal-agent.py
import asyncio
from claude_code_sdk import ClaudeSDKClient, ClaudeCodeOptions

async def main():
    async with ClaudeSDKClient(
        options=ClaudeCodeOptions(
            system_prompt="Eres un asistente legal. Identifica riesgos y sugiere mejoras.",
            max_turns=2
        )
    ) as client:
        # Envía la consulta
        await client.query(
            "Revisa esta cláusula del contrato en busca de posibles problemas: 'La parte acepta responsabilidad ilimitada...'"
        )

        # Transmite la respuesta
        async for message in client.receive_response():
            if hasattr(message, 'content'):
                # Imprime contenido en streaming mientras llega
                for block in message.content:
                    if hasattr(block, 'text'):
                        print(block.text, end='', flush=True)

if __name__ == "__main__":
    asyncio.run(main())

Guarda el código anterior como legal-agent.py, luego ejecuta:

python legal-agent.py

Para notebooks de IPython/Jupyter, puedes ejecutar el código directamente en una celda:

await main()

Los ejemplos de Python en esta página usan asyncio, pero también puedes usar anyio.

Uso básico

El SDK de Python proporciona dos interfaces principales:

1. La clase ClaudeSDKClient (recomendada)

Mejor para respuestas en streaming, conversaciones multi-turno y aplicaciones interactivas:

import asyncio
from claude_code_sdk import ClaudeSDKClient, ClaudeCodeOptions

async def main():
    async with ClaudeSDKClient(
        options=ClaudeCodeOptions(
            system_prompt="Eres un ingeniero de rendimiento",
            allowed_tools=["Bash", "Read", "WebSearch"],
            max_turns=5
        )
    ) as client:
        await client.query("Analiza el rendimiento del sistema")

        # Transmite respuestas
        async for message in client.receive_response():
            if hasattr(message, 'content'):
                for block in message.content:
                    if hasattr(block, 'text'):
                        print(block.text, end='', flush=True)

# Ejecutar como script
asyncio.run(main())

# O en IPython/Jupyter: await main()

2. La función query

Para consultas simples de una sola vez:

from claude_code_sdk import query, ClaudeCodeOptions

async for message in query(
    prompt="Analiza el rendimiento del sistema",
    options=ClaudeCodeOptions(system_prompt="Eres un ingeniero de rendimiento")
):
    if type(message).__name__ == "ResultMessage":
        print(message.result)

Opciones de configuración

El SDK de Python acepta todos los argumentos soportados por la línea de comandos a través de la clase ClaudeCodeOptions.

Parámetros de ClaudeCodeOptions

from claude_code_sdk import ClaudeCodeOptions

options = ClaudeCodeOptions(
    # Configuración principal
    system_prompt="Eres un asistente útil",
    append_system_prompt="Instrucciones adicionales del sistema",
    max_turns=5,
    model="claude-3-5-sonnet-20241022",
    max_thinking_tokens=8000,
    
    # Gestión de herramientas
    allowed_tools=["Bash", "Read", "Write"],
    disallowed_tools=["WebSearch"],
    
    # Gestión de sesiones
    continue_conversation=False,
    resume="session-uuid",
    
    # Entorno
    cwd="/ruta/al/directorio/de/trabajo",
    add_dirs=["/directorio/contexto/adicional"],
    settings="/ruta/a/settings.json",
    
    # Permisos
    permission_mode="acceptEdits",  # "default", "acceptEdits", "plan", "bypassPermissions"
    permission_prompt_tool_name="mcp__approval_tool",
    
    # Integración MCP
    mcp_servers={
        "my_server": {
            "command": "npx",
            "args": ["-y", "@modelcontextprotocol/server-example"],
            "env": {"API_KEY": "tu-clave"}
        }
    },
    
    # Avanzado
    extra_args={"--verbose": None, "--custom-flag": "value"}
)

Detalles de parámetros

  • system_prompt: str | None - Prompt del sistema personalizado que define el rol del agente
  • append_system_prompt: str | None - Texto adicional agregado al prompt del sistema
  • max_turns: int | None - Máximo de turnos de conversación (ilimitado si es None)
  • model: str | None - Modelo específico de Claude a usar
  • max_thinking_tokens: int - Máximo de tokens para el proceso de pensamiento de Claude (por defecto: 8000)
  • allowed_tools: list[str] - Herramientas específicamente permitidas para usar
  • disallowed_tools: list[str] - Herramientas que no deben usarse
  • continue_conversation: bool - Continuar la conversación más reciente (por defecto: False)
  • resume: str | None - UUID de sesión para reanudar conversación específica
  • cwd: str | Path | None - Directorio de trabajo para la sesión
  • add_dirs: list[str | Path] - Directorios adicionales para incluir en el contexto
  • settings: str | None - Ruta al archivo de configuración o cadena JSON de configuración
  • permission_mode: str | None - Modo de manejo de permisos
  • permission_prompt_tool_name: str | None - Nombre personalizado de la herramienta de prompt de permisos
  • mcp_servers: dict | str | Path - Configuraciones del servidor MCP
  • extra_args: dict[str, str | None] - Pasar flags CLI arbitrarios al CLI subyacente de Claude Code

Modos de permisos

  • "default": CLI solicita confirmación para herramientas peligrosas (comportamiento por defecto)
  • "acceptEdits": Acepta automáticamente ediciones de archivos sin solicitar confirmación
  • "plan": Modo Plan - analiza sin hacer cambios
  • "bypassPermissions": Permite todas las herramientas sin solicitar confirmación (usar con precaución)

Ejemplo de configuración avanzada

import asyncio
from claude_code_sdk import ClaudeSDKClient, ClaudeCodeOptions

async def advanced_agent():
    """Ejemplo que muestra opciones de configuración avanzadas"""
    
    async with ClaudeSDKClient(
        options=ClaudeCodeOptions(
            # Directorio de trabajo personalizado y contexto adicional
            cwd="/raiz/proyecto",
            add_dirs=["/libs/compartidas", "/utils/comunes"],
            
            # Configuración de modelo y pensamiento
            model="claude-3-5-sonnet-20241022",
            max_thinking_tokens=12000,
            
            # Control avanzado de herramientas
            allowed_tools=["Read", "Write", "Bash", "Grep"],
            disallowed_tools=["WebSearch", "Bash(rm*)"],
            
            # Configuraciones personalizadas y argumentos CLI
            settings='{"editor": "vim", "theme": "dark"}',
            extra_args={
                "--verbose": None,
                "--timeout": "300"
            }
        )
    ) as client:
        await client.query("Analiza la estructura del código base")
        
        async for message in client.receive_response():
            if hasattr(message, 'content'):
                for block in message.content:
                    if hasattr(block, 'text'):
                        print(block.text, end='', flush=True)

asyncio.run(advanced_agent())

Mensajes estructurados y entradas de imagen

El SDK soporta pasar mensajes estructurados y entradas de imagen:

from claude_code_sdk import ClaudeSDKClient, ClaudeCodeOptions

async with ClaudeSDKClient() as client:
    # Mensaje de texto
    await client.query("Analiza este código en busca de problemas de seguridad")

    # Mensaje con referencia de imagen (la imagen será leída por la herramienta Read de Claude)
    await client.query("Explica lo que se muestra en screenshot.png")

    # Múltiples mensajes en secuencia
    messages = [
        "Primero, analiza el diagrama de arquitectura en diagram.png",
        "Ahora sugiere mejoras basadas en el diagrama",
        "Finalmente, genera código de implementación"
    ]

    for msg in messages:
        await client.query(msg)
        async for response in client.receive_response():
            # Procesa cada respuesta
            pass

# El SDK maneja archivos de imagen a través de la herramienta Read integrada de Claude
# Formatos soportados: PNG, JPG, PDF y otros formatos comunes

Conversaciones multi-turno

Método 1: Usando ClaudeSDKClient para conversaciones persistentes

import asyncio
from claude_code_sdk import ClaudeSDKClient, ClaudeCodeOptions, query

# Método 1: Usando ClaudeSDKClient para conversaciones persistentes
async def multi_turn_conversation():
    async with ClaudeSDKClient() as client:
        # Primera consulta
        await client.query("Refactoricemos el módulo de pagos")
        async for msg in client.receive_response():
            # Procesa primera respuesta
            pass

        # Continúa en la misma sesión
        await client.query("Ahora agrega manejo de errores integral")
        async for msg in client.receive_response():
            # Procesa continuación
            pass

        # El contexto de la conversación se mantiene durante toda la sesión

# Método 2: Usando función query con gestión de sesiones
async def resume_session():
    # Continúa la conversación más reciente
    async for message in query(
        prompt="Ahora refactoriza esto para mejor rendimiento",
        options=ClaudeCodeOptions(continue_conversation=True)
    ):
        if type(message).__name__ == "ResultMessage":
            print(message.result)

    # Reanuda sesión específica
    async for message in query(
        prompt="Actualiza las pruebas",
        options=ClaudeCodeOptions(
            resume="550e8400-e29b-41d4-a716-446655440000",
            max_turns=3
        )
    ):
        if type(message).__name__ == "ResultMessage":
            print(message.result)

# Ejecuta los ejemplos
asyncio.run(multi_turn_conversation())

Prompts del sistema personalizados

Los prompts del sistema definen el rol, experiencia y comportamiento de tu agente:

import asyncio
from claude_code_sdk import ClaudeSDKClient, ClaudeCodeOptions

async def specialized_agents():
    # Agente de respuesta a incidentes SRE con streaming
    async with ClaudeSDKClient(
        options=ClaudeCodeOptions(
            system_prompt="Eres un experto en SRE. Diagnostica problemas sistemáticamente y proporciona soluciones accionables.",
            max_turns=3
        )
    ) as sre_agent:
        await sre_agent.query("La API está caída, investiga")

        # Transmite el proceso de diagnóstico
        async for message in sre_agent.receive_response():
            if hasattr(message, 'content'):
                for block in message.content:
                    if hasattr(block, 'text'):
                        print(block.text, end='', flush=True)

    # Agente de revisión legal con prompt personalizado
    async with ClaudeSDKClient(
        options=ClaudeCodeOptions(
            append_system_prompt="Siempre incluye manejo integral de errores y pruebas unitarias.",
            max_turns=2
        )
    ) as dev_agent:
        await dev_agent.query("Refactoriza esta función")

        # Recopila respuesta completa
        full_response = []
        async for message in dev_agent.receive_response():
            if type(message).__name__ == "ResultMessage":
                print(message.result)

asyncio.run(specialized_agents())

Herramientas personalizadas vía MCP

El Protocolo de Contexto de Modelo (MCP) te permite dar a tus agentes herramientas y capacidades personalizadas:

import asyncio
from claude_code_sdk import ClaudeSDKClient, ClaudeCodeOptions

async def mcp_enabled_agent():
    # Agente legal con acceso a documentos y streaming
    # Nota: Configura tus servidores MCP según sea necesario
    mcp_servers = {
        # Configuración de ejemplo - descomenta y configura según sea necesario:
        # "docusign": {
        #     "command": "npx",
        #     "args": ["-y", "@modelcontextprotocol/server-docusign"],
        #     "env": {"API_KEY": "tu-clave"}
        # }
    }

    async with ClaudeSDKClient(
        options=ClaudeCodeOptions(
            mcp_servers=mcp_servers,
            allowed_tools=["mcp__docusign", "mcp__compliance_db"],
            system_prompt="Eres un abogado corporativo especializado en revisión de contratos.",
            max_turns=4
        )
    ) as client:
        await client.query("Revisa este contrato en busca de riesgos de cumplimiento")

        # Monitorea uso de herramientas y respuestas
        async for message in client.receive_response():
            if hasattr(message, 'content'):
                for block in message.content:
                    if hasattr(block, 'type'):
                        if block.type == 'tool_use':
                            print(f"\n[Usando herramienta: {block.name}]\n")
                        elif hasattr(block, 'text'):
                            print(block.text, end='', flush=True)
                    elif hasattr(block, 'text'):
                        print(block.text, end='', flush=True)

            if type(message).__name__ == "ResultMessage":
                print(f"\n\nRevisión completa. Costo total: ${message.total_cost_usd:.4f}")

asyncio.run(mcp_enabled_agent())

Herramienta de prompt de permisos personalizada

Implementa manejo de permisos personalizado para llamadas de herramientas:

import asyncio
from claude_code_sdk import ClaudeSDKClient, ClaudeCodeOptions

async def use_permission_prompt():
    """Ejemplo usando herramienta de prompt de permisos personalizada"""

    # Configuración del servidor MCP
    mcp_servers = {
        # Configuración de ejemplo - descomenta y configura según sea necesario:
        # "security": {
        #     "command": "npx",
        #     "args": ["-y", "@modelcontextprotocol/server-security"],
        #     "env": {"API_KEY": "tu-clave"}
        # }
    }

    async with ClaudeSDKClient(
        options=ClaudeCodeOptions(
            permission_prompt_tool_name="mcp__security__approval_prompt",  # Cambiado de permission_prompt_tool
            mcp_servers=mcp_servers,
            allowed_tools=["Read", "Grep"],
            disallowed_tools=["Bash(rm*)", "Write"],
            system_prompt="Eres un auditor de seguridad"
        )
    ) as client:
        await client.query("Analiza y corrige los problemas de seguridad")

        # Monitorea uso de herramientas y permisos
        async for message in client.receive_response():
            if hasattr(message, 'content'):
                for block in message.content:
                    if hasattr(block, 'type'):  # Agregada verificación del atributo 'type'
                        if block.type == 'tool_use':
                            print(f"[Herramienta: {block.name}] ", end='')
                    if hasattr(block, 'text'):
                        print(block.text, end='', flush=True)

            # Verifica denegaciones de permisos en mensajes de error
            if type(message).__name__ == "ErrorMessage":
                if hasattr(message, 'error') and "Permission denied" in str(message.error):
                    print(f"\n⚠️ Permiso denegado: {message.error}")

# Ejemplo de implementación del servidor MCP (Python)
# Esto estaría en tu código del servidor MCP
async def approval_prompt(tool_name: str, input: dict, tool_use_id: str = None):
    """Manejador de prompt de permisos personalizado"""
    # Tu lógica personalizada aquí
    if "allow" in str(input):
        return json.dumps({
            "behavior": "allow",
            "updatedInput": input
        })
    else:
        return json.dumps({
            "behavior": "deny",
            "message": f"Permiso denegado para {tool_name}"
        })

asyncio.run(use_permission_prompt())

Formatos de salida

Salida de texto con streaming

# Salida de texto por defecto con streaming
async with ClaudeSDKClient() as client:
    await client.query("Explica el archivo src/components/Header.tsx")

    # Transmite texto mientras llega
    async for message in client.receive_response():
        if hasattr(message, 'content'):
            for block in message.content:
                if hasattr(block, 'text'):
                    print(block.text, end='', flush=True)
                    # La salida se transmite en tiempo real: Este es un componente React que muestra...

Salida JSON con metadatos

# Recopila todos los mensajes con metadatos
async with ClaudeSDKClient() as client:
    await client.query("¿Cómo funciona la capa de datos?")

    messages = []
    result_data = None

    async for message in client.receive_messages():
        messages.append(message)

        # Captura mensaje de resultado con metadatos
        if type(message).__name__ == "ResultMessage":
            result_data = {
                "result": message.result,
                "cost": message.total_cost_usd,
                "duration": message.duration_ms,
                "num_turns": message.num_turns,
                "session_id": message.session_id
            }
            break

    print(result_data)

Formatos de entrada

import asyncio
from claude_code_sdk import ClaudeSDKClient

async def process_inputs():
    async with ClaudeSDKClient() as client:
        # Entrada de texto
        await client.query("Explica este código")
        async for message in client.receive_response():
            # Procesa respuesta en streaming
            pass

        # Entrada de imagen (Claude usará la herramienta Read automáticamente)
        await client.query("¿Qué hay en este diagrama? screenshot.png")
        async for message in client.receive_response():
            # Procesa análisis de imagen
            pass

        # Múltiples entradas con contenido mixto
        inputs = [
            "Analiza la arquitectura en diagram.png",
            "Compárala con las mejores prácticas",
            "Genera versión mejorada"
        ]

        for prompt in inputs:
            await client.query(prompt)
            async for message in client.receive_response():
                # Procesa cada respuesta
                pass

asyncio.run(process_inputs())

Ejemplos de integración de agentes

Agente de respuesta a incidentes SRE

import asyncio
from claude_code_sdk import ClaudeSDKClient, ClaudeCodeOptions

async def investigate_incident(incident_description: str, severity: str = "medium"):
    """Agente de respuesta a incidentes automatizado con streaming en tiempo real"""

    # Configuración del servidor MCP para herramientas de monitoreo
    mcp_servers = {
        # Configuración de ejemplo - descomenta y configura según sea necesario:
        # "datadog": {
        #     "command": "npx",
        #     "args": ["-y", "@modelcontextprotocol/server-datadog"],
        #     "env": {"API_KEY": "tu-clave-datadog", "APP_KEY": "tu-clave-app"}
        # }
    }

    async with ClaudeSDKClient(
        options=ClaudeCodeOptions(
            system_prompt="Eres un experto en SRE. Diagnostica problemas sistemáticamente y proporciona soluciones accionables.",
            max_turns=6,
            allowed_tools=["Bash", "Read", "WebSearch", "mcp__datadog"],
            mcp_servers=mcp_servers
        )
    ) as client:
        # Envía los detalles del incidente
        prompt = f"Incidente: {incident_description} (Severidad: {severity})"
        print(f"🚨 Investigando: {prompt}\n")
        await client.query(prompt)

        # Transmite el proceso de investigación
        investigation_log = []
        async for message in client.receive_response():
            if hasattr(message, 'content'):
                for block in message.content:
                    if hasattr(block, 'type'):
                        if block.type == 'tool_use':
                            print(f"[{block.name}] ", end='')
                    if hasattr(block, 'text'):
                        text = block.text
                        print(text, end='', flush=True)
                        investigation_log.append(text)

            # Captura resultado final
            if type(message).__name__ == "ResultMessage":
                return {
                    'analysis': ''.join(investigation_log),
                    'cost': message.total_cost_usd,
                    'duration_ms': message.duration_ms
                }

# Uso
result = await investigate_incident("API de pagos devolviendo errores 500", "high")
print(f"\n\nInvestigación completa. Costo: ${result['cost']:.4f}")

Revisión de seguridad automatizada

import subprocess
import asyncio
import json
from claude_code_sdk import ClaudeSDKClient, ClaudeCodeOptions

async def audit_pr(pr_number: int):
    """Agente de auditoría de seguridad para pull requests con retroalimentación en streaming"""
    # Obtiene diff del PR
    pr_diff = subprocess.check_output(
        ["gh", "pr", "diff", str(pr_number)],
        text=True
    )

    async with ClaudeSDKClient(
        options=ClaudeCodeOptions(
            system_prompt="Eres un ingeniero de seguridad. Revisa este PR en busca de vulnerabilidades, patrones inseguros y problemas de cumplimiento.",
            max_turns=3,
            allowed_tools=["Read", "Grep", "WebSearch"]
        )
    ) as client:
        print(f"🔍 Auditando PR #{pr_number}\n")
        await client.query(pr_diff)

        findings = []
        async for message in client.receive_response():
            if hasattr(message, 'content'):
                for block in message.content:
                    if hasattr(block, 'text'):
                        # Transmite hallazgos mientras se descubren
                        print(block.text, end='', flush=True)
                        findings.append(block.text)

            if type(message).__name__ == "ResultMessage":
                return {
                    'pr_number': pr_number,
                    'findings': ''.join(findings),
                    'metadata': {
                        'cost': message.total_cost_usd,
                        'duration': message.duration_ms,
                        'severity': 'high' if 'vulnerability' in ''.join(findings).lower() else 'medium'
                    }
                }

# Uso
report = await audit_pr(123)
print(f"\n\nAuditoría completa. Severidad: {report['metadata']['severity']}")
print(json.dumps(report, indent=2))
import asyncio
from claude_code_sdk import ClaudeSDKClient, ClaudeCodeOptions

async def legal_review():
    """Revisión de documentos legales con sesión persistente y streaming"""

    async with ClaudeSDKClient(
        options=ClaudeCodeOptions(
            system_prompt="Eres un abogado corporativo. Proporciona análisis legal detallado.",
            max_turns=2
        )
    ) as client:
        # Revisión multi-paso en la misma sesión
        steps = [
            "Revisa contract.pdf en busca de cláusulas de responsabilidad",
            "Verifica cumplimiento con requisitos GDPR",
            "Genera resumen ejecutivo de riesgos"
        ]

        review_results = []

        for step in steps:
            print(f"\n📋 {step}\n")
            await client.query(step)

            step_result = []
            async for message in client.receive_response():
                if hasattr(message, 'content'):
                    for block in message.content:
                        if hasattr(block, 'text'):
                            text = block.text
                            print(text, end='', flush=True)
                            step_result.append(text)

                if type(message).__name__ == "ResultMessage":
                    review_results.append({
                        'step': step,
                        'analysis': ''.join(step_result),
                        'cost': message.total_cost_usd
                    })

        # Resumen
        total_cost = sum(r['cost'] for r in review_results)
        print(f"\n\n✅ Revisión legal completa. Costo total: ${total_cost:.4f}")
        return review_results

# Uso
results = await legal_review()

Mejores prácticas específicas de Python

Patrones clave

import asyncio
from claude_code_sdk import ClaudeSDKClient, ClaudeCodeOptions

# Siempre usa gestores de contexto
async with ClaudeSDKClient() as client:
    await client.query("Analiza este código")
    async for msg in client.receive_response():
        # Procesa mensajes en streaming
        pass

# Ejecuta múltiples agentes concurrentemente
async with ClaudeSDKClient() as reviewer, ClaudeSDKClient() as tester:
    await asyncio.gather(
        reviewer.query("Revisa main.py"),
        tester.query("Escribe pruebas para main.py")
    )

# Manejo de errores
from claude_code_sdk import CLINotFoundError, ProcessError

try:
    async with ClaudeSDKClient() as client:
        # Tu código aquí
        pass
except CLINotFoundError:
    print("Instala CLI: npm install -g @anthropic-ai/claude-code")
except ProcessError as e:
    print(f"Error de proceso: {e}")

# Recopila respuesta completa con metadatos
async def get_response(client, prompt):
    await client.query(prompt)
    text = []
    async for msg in client.receive_response():
        if hasattr(msg, 'content'):
            for block in msg.content:
                if hasattr(block, 'text'):
                    text.append(block.text)
        if type(msg).__name__ == "ResultMessage":
            return {'text': ''.join(text), 'cost': msg.total_cost_usd}

Consejos para IPython/Jupyter

# En Jupyter, usa await directamente en las celdas
client = ClaudeSDKClient()
await client.connect()
await client.query("Analiza data.csv")
async for msg in client.receive_response():
    print(msg)
await client.disconnect()

# Crea funciones auxiliares reutilizables
async def stream_print(client, prompt):
    await client.query(prompt)
    async for msg in client.receive_response():
        if hasattr(msg, 'content'):
            for block in msg.content:
                if hasattr(block, 'text'):
                    print(block.text, end='', flush=True)

Recursos relacionados