Inicio / Extender: habilidades, MCP, subagentes, hooks, plugins
Los eventos de hook, del disparo al mensaje
PreToolUse puede impedir que una llamada se produzca, PostToolUse ya solo puede reaccionar después de los hechos, y la decisión de un hook pasa por campos JSON precisos, nunca por una frase escrita en la salida estándar.
Un hook PreToolUse se ejecuta antes de que la herramienta se active: su decisión puede impedir que la llamada se produzca. Un hook PostToolUse se ejecuta después, una vez que la herramienta ya se ha lanzado: su decisión ya no puede impedir nada, solo reaccionar. Esta diferencia de momento determina lo que un hook puede razonablemente hacer en cada evento, y explica por qué un mismo mecanismo de bloqueo no se comporta de la misma manera según el evento que lo porte.
Una treintena de eventos, del arranque al fin de sesión
La documentación oficial enumera una treintena de eventos del ciclo de vida, desde SessionStart y UserPromptSubmit hasta PreCompact, WorktreeCreate o SessionEnd. Cada uno cubre un momento preciso: un archivo que cambia, un cambio de modelo, un subagente que arranca o termina, una instrucción en curso de expansión. Un automatismo deseado, presentado en la lección anterior como la traducción mecánica de una regla que se esperaba ver seguida, siempre se declara sobre uno de esos eventos precisos, nunca sobre una intención general.
El plazo de expiración depende del tipo de hook, no solo del evento
Un hook de tipo command, http o mcp_tool dispone por defecto de seiscientos segundos para responder. Tres eventos rebajan ese plazo a treinta segundos, UserPromptSubmit, PreModelSwitch y PostModelSwitch, y uno solo lo reduce a diez segundos, MessageDisplay. Un hook de tipo prompt conserva un plazo fijo de treinta segundos, un hook de tipo agent un plazo fijo de sesenta segundos, sea cual sea el evento que lo dispare. SessionEnd funciona de otra manera: el conjunto de sus hooks comparte un presupuesto de un segundo y medio, ampliable hasta sesenta segundos solo si un hook declara explícitamente un plazo más largo.
Lo que realmente llega al modelo: campos JSON, no texto libre
Un hook no comunica su decisión escribiendo una frase en la salida estándar. Devuelve un objeto JSON cuyo campo hookSpecificOutput.permissionDecision lleva el valor allow o deny, acompañado de un permissionDecisionReason que explica la elección, y el campo hookEventName retoma el nombre exacto del evento que lo disparó. Para añadir información sin bloquear nada, hookSpecificOutput.additionalContext inserta texto que el modelo leerá como si viniera de otra parte de la conversación.
{
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"permissionDecision": "deny",
"permissionDecisionReason": "Commande destructive detectee dans le motif teste"
}
}
El código de salida del script también cuenta, con independencia del JSON: un código de salida 2 bloquea la acción incluso si el cuerpo JSON afirmaba allow. Esta regla sufre excepciones nombradas, PermissionRequest, StopFailure fuera de una secuencia terminal, PermissionDenied, y sobre todo PostToolUse y PostToolUseFailure, donde el código 2 se vuelve no bloqueante y se limita a mostrarse a Claude vía el error estándar: lógico, ya que la herramienta ya ha terminado de ejecutarse cuando se dispara ese hook.
Diferir en lugar de decidir
El campo permissionDecision documenta dos valores, allow y deny. Para devolver la decisión al flujo de permisos habitual en lugar de forzarla, un hook no escribe ningún tercer valor en ese campo: sale con el código 0 sin reportar decisión, y la llamada sigue su camino normal de aprobación, como si ese hook concreto no hubiera tenido nada que decir al respecto.
Plazo de expiración según el tipo de hook y el evento
| Plazo de expiración por defecto | Tipo de hook implicado | Plazo por defecto | Particularidad |
|---|---|---|---|
| Hook command, http o mcp_tool ordinario | command, http, mcp_tool | 600 segundos | Se aplica a la mayoría de los eventos del ciclo de vida |
| UserPromptSubmit, PreModelSwitch, PostModelSwitch | command, http, mcp_tool | 30 segundos | Estos tres eventos rebajan el plazo normalmente fijado en 600 segundos |
| MessageDisplay | command, http, mcp_tool | 10 segundos | El plazo más corto de todos los eventos documentados |
| SessionEnd | Todos los tipos combinados | 1,5 segundos para el conjunto de los hooks del evento | Ampliable hasta 60 segundos si un hook declara explícitamente un plazo más largo |
| Hook de tipo prompt | prompt | 30 segundos | Plazo propio del tipo, independiente del evento que lo dispara |
| Hook de tipo agent | agent | 60 segundos | Plazo propio del tipo, independiente del evento que lo dispara |
Del evento a la decisión transmitida al modelo
Un desarrollador configura un hook PostToolUse que devuelve un código de salida 2 después de cada llamada de la herramienta Bash. Después lanza un comando Bash desde Claude Code y observa que el comando se ejecuta hasta el final.
Escribe en una frase lo que esta situación establece, y en una frase lo que no establece.
Lo que esto establece: Esta observación establece que, para el evento PostToolUse, un código de salida 2 no bloquea la acción y se trata solo como un mensaje mostrado a Claude vía el error estándar.
Lo que esto no establece: No establece que el código de salida 2 se comporte de la misma manera en todos los eventos de hook, ya que PreToolUse respeta ese mismo código bloqueando la llamada antes de que se produzca.
Los tres calibrados falsos más frecuentes
- Demasiado amplio Esta observación prueba que el código de salida 2 nunca bloquea nada, sea cual sea el evento de hook implicado.
- Demasiado estrecho Esta observación no prueba nada sobre el comportamiento del hook, ya que solo se probó una llamada de la herramienta Bash.
- Fuera de tema Esta observación muestra que el hook tardó más en ejecutarse que el propio comando Bash.
- PreToolUse puede impedir que se produzca una llamada de herramienta, PostToolUse ya solo puede reaccionar una vez que la herramienta ya se ha lanzado.
- El plazo por defecto de seiscientos segundos para un hook command, http o mcp_tool baja a treinta segundos en tres eventos y a diez segundos en uno solo.
- La decisión de un hook pasa por el campo hookSpecificOutput.permissionDecision, con los valores confirmados allow y deny, nunca por una frase escrita en la salida estándar.
- Un código de salida 2 bloquea la acción incluso cuando el JSON afirma allow, salvo en una lista de eventos nombrados donde se convierte en un simple mensaje mostrado a Claude.
- Para diferir al flujo de permisos habitual en lugar de forzar allow o deny, un hook sale con el código 0 sin reportar decisión, no existe un tercer valor escrito en permissionDecision.
Escribe un hook PreToolUse mínimo que salga con el código 0 sin escribir nada en su salida estándar, decláralo sobre un evento anodino, y confirma que la llamada sigue su camino normal de aprobación en lugar de ser forzada.
Cada afirmación datable de esta lección remite aquí al texto público que la respalda. Una fuente que no se abre no prueba nada.
- Claude Code, hooks, valores de permissionDecision y código de salida 2 según el evento consultée le 2026-09-02