- Bloquear operaciones peligrosas antes de que se ejecuten, como comandos de shell destructivos o acceso a archivos no autorizado
- Registrar y auditar las llamadas a herramientas para cumplimiento, depuración o análisis
- Transformar entradas y salidas para sanitizar datos, inyectar credenciales o redirigir rutas de archivos
- Requerir aprobación humana para acciones sensibles como escrituras en bases de datos o llamadas a API
- Rastrear el ciclo de vida de la sesión para gestionar estado, limpiar recursos o enviar notificaciones
Cómo funcionan los hooks
1
Se dispara un evento
Algo sucede durante la ejecución del agente y el SDK dispara un evento: una herramienta está a punto de ser llamada (
PreToolUse), una herramienta devolvió un resultado (PostToolUse), un subagente se inició o se detuvo, el agente está inactivo, o la ejecución finalizó. Consulta la lista completa de eventos.2
El SDK recopila hooks registrados
El SDK verifica si hay hooks registrados para ese tipo de evento. Esto incluye hooks de devolución de llamada que pasas en
options.hooks y hooks de comandos de shell de archivos de configuración cuando la entrada settingSources o setting_sources correspondiente está habilitada, lo cual lo está para las opciones predeterminadas de query().3
Los matchers filtran qué hooks se ejecutan
Si un hook tiene un patrón
matcher (como "Write|Edit"), el SDK lo prueba contra el objetivo del evento (por ejemplo, el nombre de la herramienta). Los hooks sin un matcher se ejecutan para cada evento de ese tipo.4
Se ejecutan las funciones de devolución de llamada
La función de devolución de llamada de cada hook coincidente recibe información sobre lo que está sucediendo: el nombre de la herramienta, sus argumentos, el ID de sesión y otros detalles específicos del evento.
5
Tu devolución de llamada devuelve una decisión
Después de realizar cualquier operación (registro, llamadas a API, validación), tu devolución de llamada devuelve un objeto de salida que le dice al agente qué hacer: permitir la operación, bloquearla, modificar la entrada o inyectar contexto en la conversación.
PreToolUse (paso 1) con un matcher "Write|Edit" (paso 3) para que la devolución de llamada se dispare para las herramientas Write y Edit. Cuando se activa, la devolución de llamada recibe la entrada de la herramienta (paso 4), verifica si la ruta del archivo apunta a un archivo .env, y devuelve permissionDecision: "deny" para bloquear la operación (paso 5):
file_path termina en /.env, sin importar mayúsculas o minúsculas. En Windows, file_path usa barras invertidas, como C:\project\.env, y este hook permite una llamada a Write o Edit sobre C:\project\.env.
Hooks disponibles
El SDK proporciona hooks para diferentes etapas de la ejecución del agente. Algunos hooks están disponibles en ambos SDK, mientras que otros son solo para TypeScript.Configurar hooks
Para configurar un hook, pásalo en el campohooks de tus opciones de agente (ClaudeAgentOptions en Python, el objeto options en TypeScript). Este fragmento asume que ya has definido una devolución de llamada de hook, como protect_env_files en Python o protectEnvFiles en TypeScript del ejemplo anterior:
hooks es un diccionario en Python u objeto en TypeScript, donde:
- Las claves: nombres de eventos de hook como
'PreToolUse','PostToolUse'y'Stop' - Los valores: matrices de matchers, cada una conteniendo un patrón de filtro opcional y tus funciones de devolución de llamada
Matchers
Usa matchers para filtrar cuándo se disparan tus devoluciones de llamada. El campomatcher coincide con un valor diferente dependiendo del tipo de evento de hook. Por ejemplo, los hooks basados en herramientas coinciden con el nombre de la herramienta, mientras que los hooks Notification coinciden con el tipo de notificación.
Los matchers del SDK siguen las mismas reglas que matchers en archivos de configuración. Esa sección documenta las rutas de evaluación de cadena exacta y expresión regular, sus requisitos de versión, y los valores de matcher para cada tipo de evento.
Usa el patrón
matcher para dirigirte a herramientas específicas siempre que sea posible. Un matcher con 'Bash' solo se ejecuta para comandos Bash, mientras que omitir el patrón ejecuta tus devoluciones de llamada para cada ocurrencia del evento.
Funciones de devolución de llamada
Entradas
Cada devolución de llamada de hook recibe tres argumentos:- Datos de entrada: un objeto tipado que contiene detalles del evento. Cada tipo de hook tiene su propia forma de entrada. Por ejemplo,
PreToolUseHookInputincluyetool_nameytool_input, mientras queNotificationHookInputincluyemessage. Consulta las definiciones de tipo completas en las referencias del SDK de TypeScript y Python.- Todas las entradas de hook comparten
session_id,cwdyhook_event_name. agent_idyagent_typese rellenan cuando el hook se dispara dentro de un subagente. En TypeScript, estos están en la entrada de hook base y disponibles para todos los tipos de hook. En Python, son campos opcionales enPreToolUse,PostToolUse,PostToolUseFailureyPermissionRequest, y campos requeridos enSubagentStartySubagentStop.
- Todas las entradas de hook comparten
- ID de uso de herramienta (
str | None/string | undefined): correlaciona eventosPreToolUseyPostToolUsepara la misma llamada a herramienta. - Contexto: en TypeScript, contiene una propiedad
signal(AbortSignal) para cancelación. En Python, este argumento está reservado para uso futuro.
Salidas
Tu devolución de llamada devuelve un objeto con dos categorías de campos:- Campos de nivel superior se aceptan en cada evento:
systemMessagemuestra un mensaje al usuario, ycontinue(continue_en Python) determina si el agente sigue ejecutándose después de este hook. Algunos eventos los descartan o los entregan en otro lugar. Cada sección del evento en la página de hooks dice dónde llegan. hookSpecificOutputcontrola la operación actual. Los campos que estableces dentro dependen del tipo de evento de hook:- Para hooks
PreToolUse, aquí es donde establecespermissionDecision("allow","deny","ask"o"defer"),permissionDecisionReasoneupdatedInput. Si devuelves"defer", el turno termina con un mensaje de resultado cuyostop_reasones"tool_deferred", para que puedas reanudar la llamada más tarde. - Para hooks
PostToolUse, puedes estableceradditionalContextpara agregar información al resultado de la herramienta. Para reemplazar la salida de la herramienta antes de que Claude la vea, estableceupdatedToolOutput, que funciona para cualquier herramienta en ambos SDK. El campo anteriorupdatedMCPToolOutputreemplaza solo la salida de herramientas MCP. - En el SDK de TypeScript, una devolución de llamada
PostToolUsetambién puede devolverclassifierContext, una nota breve sobre el resultado de la llamada a herramienta para el clasificador de permisos del modo automático. Debido a que tu devolución de llamada se ejecuta en el proceso propio de tu aplicación, el clasificador puede considerar una declaración de usuario que retransmitas en la nota como intención del usuario. El campo requiere TypeScript Agent SDK v0.3.236 o posterior. Anotar un resultado para el clasificador del modo automático cubre el límite de longitud, la regla de solo sincrónico, y qué no poner en la nota.
- Para hooks
{} para permitir la operación sin cambios. Los hooks de devolución de llamada del SDK usan el mismo formato de salida JSON que hooks de comandos de shell de Claude Code, que documenta cada campo y opción específica del evento. Para las definiciones de tipo del SDK, consulta las referencias del SDK de TypeScript y Python.
Cuando varios hooks
PreToolUse devuelven decisiones diferentes, deny tiene prioridad sobre defer, que tiene prioridad sobre ask, que tiene prioridad sobre allow.Salida asincrónica
De forma predeterminada, el agente espera a que tu hook devuelva antes de continuar. Si tu hook realiza un efecto secundario, como registro o envío de webhook, y no necesita influir en el comportamiento del agente, puedes devolver una salida asincrónica en su lugar. Esto le dice al agente que continúe inmediatamente sin esperar a que el hook termine. En este fragmento,send_to_logging_service en Python y sendToLoggingService en TypeScript representan cualquier función de registro que definas:
Las salidas asincrónicas no pueden bloquear, modificar o inyectar contexto en la operación ya que el agente ya ha avanzado. Úsalas solo para efectos secundarios como registro, métricas o notificaciones.
Ejemplos
Varios ejemplos en esta sección muestran solo la función de devolución de llamada. Para ejecutar uno, registre la devolución de llamada bajo el evento coincidente en el campohooks de sus opciones, como se muestra en Configurar hooks.
Modificar entrada de herramienta
Este ejemplo intercepta llamadas a la herramienta Write y reescribe el argumentofile_path para anteponer /sandbox. La devolución de llamada devuelve updatedInput con la ruta modificada y permissionDecision: 'allow' para aprobar automáticamente la operación reescrita:
Empareje
updatedInput con permissionDecision: 'allow' para aprobar automáticamente la entrada modificada, o permissionDecision: 'ask' para mostrársela al usuario. Si omite permissionDecision, la entrada modificada aún se aplica y fluye a través de la evaluación de permiso normal. Con 'defer', updatedInput se ignora. Siempre devuelva un nuevo objeto en lugar de mutar el tool_input original../sandbox o /tmp/sandbox (macOS no permite crear un directorio /sandbox a nivel raíz), luego pida al agente que escriba un archivo: el resultado de la herramienta Write en el flujo de mensajes nombra la ruta con su prefijo de sandbox en lugar del que Claude solicitó.
Agregar contexto y bloquear una herramienta
Este ejemplo deniega las llamadas a herramientas cuyofile_path comienza con /etc y explica por qué tanto al modelo como al usuario:
permissionDecision: 'deny'detiene la llamada de herramienta.permissionDecisionReasonle dice al modelo por qué.systemMessagemuestra al usuario qué sucedió.
PreToolUse con un matcher Write|Edit y pida al agente que cree un archivo bajo /etc: el resultado de la herramienta Write en el flujo de mensajes contiene Writing to /etc is not allowed, y no se crea ningún archivo.
Aprobar automáticamente herramientas específicas
De forma predeterminada, el agente puede solicitar permiso antes de usar ciertas herramientas. Este ejemplo aprueba automáticamente herramientas del sistema de archivos de solo lectura (Read, Glob, Grep) devolviendopermissionDecision: 'allow', permitiéndoles ejecutarse sin confirmación del usuario, salvo las lecturas desde rutas de red, mientras deja todas las otras herramientas sujetas a verificaciones de permisos normales:
Registrar múltiples hooks
Cuando se dispara un evento, los hooks correspondientes se ejecutan en paralelo. Para las decisiones de permisos, se aplica el resultado más restrictivo. Undeny de un hook bloquea la llamada a herramienta. Debido a que el orden de finalización es no determinista, escribe cada hook para que actúe de forma independiente en lugar de depender de que otro hook se haya ejecutado primero.
El ejemplo a continuación registra tres verificaciones independientes, cada una con un matcher omitido. Los nombres de hook en él, como audit_logger en Python o auditLogger en TypeScript, representan devoluciones de llamada que tú defines:
Filtrar con matchers de múltiples herramientas
Use matchers de múltiples herramientas para compartir una devolución de llamada entre herramientas relacionadas. Este ejemplo registra tres matchers con diferentes alcances, y cada hook que nombra representa una devolución de llamada que usted define:- Una lista exacta separada por barras verticales (
Write|Edit|NotebookEdit) disparafile_security_hookpara las herramientas Write, Edit y NotebookEdit. - Una expresión regular (
^mcp__) disparamcp_audit_hookpara cualquier herramienta MCP cuyo nombre comience conmcp__. - Un matcher omitido dispara
global_loggersin filtrar por nombre de herramienta.
Rastrear actividad de subagente
Use hooksSubagentStop para monitorear cuándo los subagentes terminan su trabajo. Vea el tipo de entrada completo en las referencias del SDK de TypeScript y Python. Este ejemplo registra un resumen cada vez que un subagente se completa:
[SUBAGENT] Completed: con el ID del subagente y la ruta de la transcripción.
Realizar solicitudes HTTP desde hooks
Los hooks pueden realizar operaciones asincrónicas como solicitudes HTTP. Capture errores dentro de su hook en lugar de dejarlos propagarse. Este ejemplo envía un webhook después de que cada herramienta se completa, registrando qué herramienta se ejecutó y cuándo. El hook captura errores de un webhook fallido:Reenviar notificaciones a Slack
Use hooksNotification para recibir notificaciones del sistema del agente y reenviarlas a servicios externos. En sesiones del SDK, Claude Code ejecuta este hook para los siguientes tipos de notificación:
permission_promptuna vez que una solicitud de permiso ha esperado aproximadamente seis segundos en su devolución de llamadacanUseTool. Requiere TypeScript Agent SDK v0.3.233 o posterior, o Python Agent SDK v0.2.139 o posteriorelicitation_completeyelicitation_responsepara flujos de elicitación de solicitud del usuario
idle_prompt, auth_success y elicitation_dialog, desde la interfaz de usuario interactiva que las sesiones del SDK no ejecutan.
Cada notificación incluye un campo message con una descripción legible por humanos y opcionalmente un title.
Este ejemplo reenvía cada notificación a un canal de Slack. Requiere una URL de webhook entrante de Slack, que crea agregando una aplicación a su espacio de trabajo de Slack y habilitando webhooks entrantes:
Notification, el hook publica el message de la notificación, con el prefijo Agent status:, al canal que apunta su webhook.
Solucionar problemas comunes
Hook no se dispara
- Verifica que el nombre del evento de hook sea correcto y sensible a mayúsculas (
PreToolUse, nopreToolUse) - Verifica que tu patrón de matcher coincida exactamente con el nombre de la herramienta
- Asegúrate de que el hook esté bajo el tipo de evento correcto en
options.hooks - Para hooks que no son de herramientas que admiten matchers, como
NotificationySubagentStop, los matchers coinciden contra campos diferentes, yStopignora los matchers por completo (consulta patrones de matcher) - Los hooks pueden no dispararse cuando el agente alcanza el límite
max_turnsporque la sesión termina antes de que los hooks puedan ejecutarse
Matcher no filtra como se esperaba
Los matchers solo coinciden con nombres de herramientas, no con rutas de archivo u otros argumentos. Para filtrar por ruta de archivo, verificatool_input.file_path dentro de tu hook:
Tiempo de espera del hook
Claude Code ejecuta cada devolución de llamada con un tiempo de espera, que estableces en segundos con el campotimeout en su HookMatcher. Cuando no estableces uno, Claude Code utiliza el valor predeterminado del evento: 600 segundos para la mayoría de eventos, 30 segundos para UserPromptSubmit, PreModelSwitch y PostModelSwitch, y 10 segundos para MessageDisplay. Claude Code ejecuta devoluciones de llamada SessionEnd durante el apagado bajo el presupuesto de tiempo de espera SessionEnd más corto, 1,5 segundos de forma predeterminada.
Cuando una devolución de llamada excede su tiempo de espera, Claude Code la cancela y descarta su salida, y la sesión continúa en lugar de colgarse. Lo que sucede a continuación depende del evento:
PreToolUse: Claude Code bloquea la llamada a herramienta. En su lugar, Claude recibe un resultado de herramienta indicando que el hook no respondió antes de su tiempo de espera, y el turno continúa. Antes de v2.1.210, Claude Code reportaba el tiempo de espera a Claude como un rechazo del usuario, lo que hacía que las sesiones desatendidas se detuvieran y esperaran entrada.PostToolUseyPostToolUseFailure: Claude Code mantiene el resultado de la herramienta y el turno continúa.UserPromptSubmityUserPromptExpansion: Claude Code bloquea el prompt con un mensaje que nombra el hook y el tiempo de espera, y la sesión continúa. Antes de v2.1.208, Claude Code terminaba la consulta conerror_during_executioncuando se agotaba el tiempo de espera de una devolución de llamada en estos eventos.StopySubagentStop: la devolución de llamada con tiempo de espera agotado cuenta como no devolver ninguna decisión. El agente o subagente se detiene como si esa devolución de llamada lo hubiera permitido, y una decisión de tus otros hooks en el evento aún se aplica. Antes de Claude Code v2.1.273, una devolución de llamadaStopoSubagentStopcon tiempo de espera agotado contaba como una ejecución de hook fallida, y Claude Code descartaba las decisiones de tus otros hooks en el evento.SessionStart: la devolución de llamada con tiempo de espera agotado cuenta como no devolver ninguna salida, y la sesión continúa con la salida de tus otros hooksSessionStart.PreModelSwitch: Claude Code bloquea el cambio de modelo. Un hook que no responde no ha aprobado el cambio.- Otros eventos, como
Notification,PreCompactyPostModelSwitch: Claude Code registra el fallo y continúa.
Stop o SessionStart en la sesión principal, Claude Code también agrega un SDKInformationalMessage al flujo de mensajes diciendo que la aplicación que impulsa la sesión no respondió. Los tiempos de espera posteriores no repiten ese mensaje mientras tu aplicación permanece sin responder.
Si interrumpes la consulta mientras una devolución de llamada está pendiente, Claude Code cancela la llamada a herramienta pendiente. Antes de v2.1.208, la llamada a herramienta podía proceder si interrumpías durante una devolución de llamada PreToolUse pendiente.
Si tu devolución de llamada necesita más tiempo, establece un timeout más alto en su HookMatcher. En TypeScript, usa el AbortSignal del tercer argumento de la devolución de llamada para manejar la cancelación correctamente cuando se agote el tiempo de espera.
Herramienta bloqueada inesperadamente
- Verifica todos los hooks
PreToolUseen busca de devoluciones depermissionDecision: 'deny' - Agrega registro a tus hooks para ver qué
permissionDecisionReasonestán devolviendo - Verifica que los patrones de matcher no sean demasiado amplios: un matcher vacío coincide con todas las herramientas
Entrada modificada no aplicada
-
Asegúrate de que
updatedInputesté dentro dehookSpecificOutput, no en el nivel superior: -
No emparejes
updatedInputconpermissionDecision: 'defer', que descarta la entrada modificada. OmitirpermissionDecisionestá bien: la entrada modificada aún se aplica a través de la evaluación de permisos normal. También puedes devolver'allow'para aprobar automáticamente la entrada modificada u'ask'para mostrarla al usuario para su aprobación -
Incluye
hookEventNameenhookSpecificOutputpara identificar para qué tipo de hook es la salida
Hooks de sesión no disponibles en Python
SessionStart y SessionEnd pueden registrarse como hooks de devolución de llamada del SDK en TypeScript, pero no están disponibles en el SDK de Python porque su tipo HookEvent los omite. En Python, solo están disponibles como hooks de comandos de shell definidos en archivos de configuración como .claude/settings.json. Los archivos de configuración que carga tu aplicación SDK dependen de setting_sources o settingSources. Si estableces esa opción, incluye la fuente que contiene los hooks:
client.receive_response() como tu disparador.
Solicitudes de permiso de subagentes multiplicándose
Al generar múltiples subagentes, cada uno puede solicitar permisos por separado para sus propias llamadas a herramientas. Para evitar solicitudes repetidas, usa hooksPreToolUse para aprobar automáticamente herramientas específicas, o configura reglas de permisos, que los subagentes heredan de la conversación padre.
Bucles recursivos de hook con subagentes
Un hookUserPromptSubmit que genera subagentes puede crear bucles infinitos si esos subagentes disparan el mismo hook. Para prevenir esto:
- Usa una variable compartida o estado de sesión para rastrear si ya estás dentro de un subagente
- Limita los hooks para que solo se ejecuten en la sesión del agente de nivel superior
systemMessage no aparece en la salida
El camposystemMessage muestra un mensaje al usuario, no al modelo. En Claude Code v2.1.227 o posterior, el systemMessage de un hook puede aparecer en el flujo de mensajes como un SDKInformationalMessage. Que aparezca o no depende del evento. Cada sección del evento en la página de hooks indica cómo aparece la salida. Para pasar contexto al modelo en su lugar, devuelve additionalContext.
Antes de v2.1.227, el SDK exponía la salida de hooks en el flujo de mensajes solo para hooks SessionStart y Setup. Para cualquier otro evento, la salida aparecía solo en los eventos del ciclo de vida que includeHookEvents (include_hook_events en Python) agrega. La entrada de esa opción cubre qué eventos del ciclo de vida produce cada evento de hook.
Si necesitas exponer decisiones de hook a tu aplicación de manera confiable, regístralas por separado o usa un canal de salida dedicado.
Recursos relacionados
- Referencia de hooks de Claude Code: esquemas completos de entrada/salida JSON, documentación de eventos y patrones de matcher
- Guía de hooks de Claude Code: ejemplos de hooks de comandos de shell y tutoriales
- Referencia del SDK de TypeScript: tipos de hook, definiciones de entrada/salida y opciones de configuración
- Referencia del SDK de Python: tipos de hook, definiciones de entrada/salida y opciones de configuración
- Permisos: controlar qué puede hacer su agente
- Herramientas personalizadas: crear herramientas para extender las capacidades del agente