Ir al contenido
Mastering Claude

Inicio / Extender: habilidades, MCP, subagentes, hooks, plugins

Extender: habilidades, MCP, subagentes, hooks, plugins11 minApplication

Los eventos de hook, del disparo al mensaje

PreToolUse se ejecuta antes de la herramienta y puede impedir la llamada; PostToolUse se ejecuta después y solo puede reaccionar, incluso sustituyendo el resultado de la herramienta. Un hook comunica su decisión mediante un objeto JSON con campos precisos y mediante su código de salida, nunca con 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, incluso sustituyendo el resultado de la herramienta con el campo updatedToolOutput.

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. Prolonga la lección anterior: un automatismo siempre se declara sobre uno de esos eventos, nunca sobre una intención general.

El plazo de expiración depende del tipo de hook, no solo del evento

Un hook command, http o mcp_tool dispone por defecto de seiscientos segundos, tres eventos lo rebajan a treinta segundos y MessageDisplay a diez. Un hook prompt conserva treinta segundos, un hook agent sesenta, sea cual sea el evento.

Lo que 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ó.

{
  "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 admite cuatro valores: allow omite la petición de permiso, con excepciones documentadas, deny impide la llamada, ask pide confirmación al usuario, y defer termina el turno para retomar la herramienta más tarde, solo en modo no interactivo con -p. Para devolver la decisión al flujo de permisos habitual en lugar de forzarla, un hook no escribe ninguno de esos valores: sale con el código 0 sin reportar decisión, y la llamada sigue su camino normal de aprobación.

Probar un hook por su entrada estándar

Un error de hook deja pasar la acción y solo se anuncia con un aviso en la transcripción, fácil de pasar por alto: una prueba mal formada devuelve un silencio que parece «el hook no reacciona». El hook lee un JSON en su entrada estándar, así que puedes ejecutarlo a mano, con un caso que debe rechazar y un contratestigo que debe dejar pasar. El script siguiente y sus dos llamadas fabrican sus propios datos:

cat > garde.sh << 'EOF'
#!/bin/sh
ENTREE=$(cat)
case "$ENTREE" in
  *"npm publish"*) echo "Rechazado: lance primero npm publish --dry-run." >&2; exit 2 ;;
esac
exit 0
EOF
printf '%s' '{"tool_name":"Bash","tool_input":{"command":"npm publish"}}' | sh garde.sh; echo "code=$?"
printf '%s' '{"tool_name":"Bash","tool_input":{"command":"npm test"}}' | sh garde.sh; echo "code=$?"
Rechazado: lance primero npm publish --dry-run.
code=2
code=0

La primera llamada prueba que el guardia rechaza, la segunda que su silencio significa «autorizado» y no «roto». Escribe el JSON con printf '%s' entre comillas simples: un shell que reduce una barra invertida rompe el JSON sin error visible.

¿Un guardia averiado deja pasar?

Desde octubre de 2026, un hook PreToolUse o PermissionRequest cuya correspondencia falla, o cuya entrada no se serializa, bloquea la llamada en lugar de ser ignorado. En Windows, un guardia sin shell declarado también bloquea cuando falta Git for Windows. Un hook lanzado por PowerShell que salía con el código 2 no bloqueaba antes de la corrección de octubre: relee los guardias escritos antes. Para que un guardia de seguridad bloquee en todos los casos de fallo, declara "onFailure": "block".

Figure 1

Plazo de expiración según el tipo de hook y el evento

Plazo de expiración por defectoTipo de hook implicadoPlazo por defectoParticularidad
Hook command, http o mcp_tool ordinariocommand, http, mcp_tool600 segundosSe aplica a la mayoría de los eventos del ciclo de vida
UserPromptSubmit, PreModelSwitch, PostModelSwitchcommand, http, mcp_tool30 segundosEstos tres eventos rebajan el plazo normalmente fijado en 600 segundos
MessageDisplaycommand, http, mcp_tool10 segundosEl plazo más corto de todos los eventos documentados
SessionEndTodos los tipos combinados1,5 segundos para el conjunto de los hooks del eventoAmpliable hasta 60 segundos si un hook declara explícitamente un plazo más largo
Hook de tipo promptprompt30 segundosPlazo propio del tipo, independiente del evento que lo dispara
Hook de tipo agentagent60 segundosPlazo propio del tipo, independiente del evento que lo dispara
La tabla cruza el tipo de hook con su plazo por defecto, y aísla los eventos que rebajan ese plazo o que siguen una regla aparte como SessionEnd.
Figure 2

Del evento a la decisión transmitida al modelo

01
Evento disparado
Una herramienta está a punto de ejecutarse, o acaba de hacerlo, según el evento registrado.
02
Hook ejecutado
El arnés lanza el comando, la llamada HTTP, la herramienta MCP, la instrucción o el agente declarado para ese evento.
03
Decisión emitida en JSON
hookSpecificOutput.permissionDecision lleva allow o deny, acompañado de permissionDecisionReason, y hookEventName retoma el nombre del evento.
04
Contexto añadido o acción bloqueada
additionalContext enriquece lo que ve el modelo, o la llamada se cancela antes de producirse si el evento es PreToolUse.
05
Código de salida 2 como red de seguridad
Bloquea la acción incluso si el JSON dice allow, salvo para los eventos que no lo respetan, como PostToolUse donde solo se muestra a Claude vía stderr.
La secuencia sitúa el momento en que PreToolUse todavía puede impedir la llamada, y aquel en que PostToolUse ya solo puede constatar lo que acaba de ocurrir.
Calíbralo tú mismo

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 hay que recordar
  • PreToolUse puede impedir que se produzca una llamada de herramienta, PostToolUse no puede impedir nada pero puede sustituir el resultado una vez lanzada la herramienta.
  • Un hook se prueba a mano enviándole un JSON por su entrada estándar, con un caso que rechazar y un contratestigo que dejar pasar.
  • La decisión de un hook pasa por el campo hookSpecificOutput.permissionDecision, con los valores allow, deny, ask o defer, nunca por una frase en la salida estándar.
  • Un código de salida 2 bloquea la acción incluso cuando el JSON afirma allow, salvo en eventos nombrados donde se convierte en un simple mensaje mostrado a Claude.
  • Para diferir al flujo de permisos habitual, un hook sale con el código 0 sin reportar decisión, y un guardia de seguridad declara onFailure: "block" para bloquear también ante un fallo.
Hazlo ahora

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.

Verificar en la fuente

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.