- Bloccare operazioni pericolose prima che vengano eseguite, come comandi shell distruttivi o accesso a file non autorizzato
- Registrare e controllare le chiamate agli strumenti per conformità, debug o analitiche
- Trasformare input e output per sanitizzare i dati, iniettare credenziali o reindirizzare i percorsi dei file
- Richiedere approvazione umana per azioni sensibili come scritture su database o chiamate API
- Tracciare il ciclo di vita della sessione per gestire lo stato, pulire le risorse o inviare notifiche
Come funzionano gli hook
1
Un evento si attiva
Qualcosa accade durante l’esecuzione dell’agente e l’SDK attiva un evento: uno strumento sta per essere chiamato (
PreToolUse), uno strumento ha restituito un risultato (PostToolUse), un subagent è stato avviato o interrotto, l’agente è inattivo o l’esecuzione è terminata. Consulta l’elenco completo degli eventi.2
L'SDK raccoglie gli hook registrati
L’SDK verifica la presenza di hook registrati per quel tipo di evento. Questo include gli hook di callback che passi in
options.hooks e gli hook dei comandi shell dai file di impostazioni quando la voce settingSources o setting_sources corrispondente è abilitata, come avviene per le opzioni predefinite di query().3
I matcher filtrano quali hook vengono eseguiti
Se un hook ha un modello
matcher (come "Write|Edit"), l’SDK lo testa rispetto al target dell’evento (ad esempio, il nome dello strumento). Gli hook senza un matcher vengono eseguiti per ogni evento di quel tipo.4
Le funzioni di callback vengono eseguite
La funzione di callback di ogni hook corrispondente riceve input su ciò che sta accadendo: il nome dello strumento, i suoi argomenti, l’ID della sessione e altri dettagli specifici dell’evento.
5
Il tuo callback restituisce una decisione
Dopo aver eseguito qualsiasi operazione (log, chiamate API, convalida), il tuo callback restituisce un oggetto di output che dice all’agente cosa fare: consentire l’operazione, bloccarla, modificare l’input o iniettare contesto nella conversazione.
PreToolUse (passaggio 1) con un matcher "Write|Edit" (passaggio 3) in modo che il callback si attivi per gli strumenti Write e Edit. Quando attivato, il callback riceve l’input dello strumento (passaggio 4), verifica se il percorso del file è destinato a un file .env e restituisce permissionDecision: "deny" per bloccare l’operazione (passaggio 5):
file_path termina con /.env, indipendentemente da maiuscole e minuscole. Su Windows, file_path usa le barre rovesciate, come in C:\project\.env, e questo hook consente una chiamata a Write o Edit verso C:\project\.env.
Hook disponibili
L’SDK fornisce hooks per diverse fasi dell’esecuzione dell’agente. Alcuni hooks sono disponibili in entrambi gli SDK, mentre altri sono solo per TypeScript.Configurare gli hook
Per configurare un hook, passalo nel campohooks delle opzioni dell’agente (ClaudeAgentOptions in Python, l’oggetto options in TypeScript). Questo snippet presuppone che tu abbia già definito un callback hook, come protect_env_files in Python o protectEnvFiles in TypeScript dall’esempio precedente:
hooks è un dizionario in Python o un oggetto in TypeScript, dove:
- Le chiavi: nomi degli eventi hook come
'PreToolUse','PostToolUse'e'Stop' - I valori: array di matcher, ognuno contenente un modello di filtro opzionale e le tue funzioni di callback
Matcher
Utilizza i matcher per filtrare quando i tuoi callback si attivano. Il campomatcher corrisponde a un valore diverso a seconda del tipo di evento hook. Ad esempio, gli hook basati su strumenti corrispondono al nome dello strumento, mentre gli hook Notification corrispondono al tipo di notifica.
I matcher SDK seguono le stesse regole dei matcher nei file di impostazioni. Quella sezione documenta i percorsi di valutazione di stringa esatta e espressione regolare, i loro requisiti di versione e i valori di matcher per ogni tipo di evento.
Utilizza il modello
matcher per indirizzare strumenti specifici quando possibile. Un matcher con 'Bash' viene eseguito solo per i comandi Bash, mentre omettere il modello esegue i tuoi callback per ogni occorrenza dell’evento.
Funzioni di callback
Input
Ogni callback hook riceve tre argomenti:- Dati di input: un oggetto tipizzato contenente i dettagli dell’evento. Ogni tipo di hook ha la sua forma di input. Ad esempio,
PreToolUseHookInputincludetool_nameetool_input, mentreNotificationHookInputincludemessage. Consulta le definizioni di tipo complete nei riferimenti SDK TypeScript e Python.- Tutti gli input hook condividono
session_id,cwdehook_event_name. agent_ideagent_typevengono popolati quando l’hook si attiva all’interno di un subagent. In TypeScript, questi si trovano sull’input hook di base e sono disponibili per tutti i tipi di hook. In Python, sono campi opzionali suPreToolUse,PostToolUse,PostToolUseFailureePermissionRequest, e campi obbligatori suSubagentStarteSubagentStop.
- Tutti gli input hook condividono
- ID di utilizzo dello strumento (
str | None/string | undefined): correla gli eventiPreToolUseePostToolUseper la stessa chiamata a uno strumento. - Contesto: in TypeScript, contiene una proprietà
signal(AbortSignal) per l’annullamento. In Python, questo argomento è riservato per uso futuro.
Output
Il tuo callback restituisce un oggetto con due categorie di campi:- Campi di livello superiore sono accettati su ogni evento:
systemMessagemostra un messaggio all’utente, econtinue(continue_in Python) determina se l’agente continua a funzionare dopo questo hook. Alcuni eventi li scartano o li consegnano altrove. La sezione di ogni evento sulla pagina degli hook dice dove finiscono. hookSpecificOutputcontrolla l’operazione corrente. I campi che imposti all’interno dipendono dal tipo di evento hook:- Per gli hook
PreToolUse, è qui che impostipermissionDecision("allow","deny","ask"o"defer"),permissionDecisionReasoneupdatedInput. Se restituisci"defer", il turno termina con un messaggio di risultato il cuistop_reasonè"tool_deferred", in modo da poter riprendere la chiamata in seguito. - Per gli hook
PostToolUse, puoi impostareadditionalContextper aggiungere informazioni al risultato dello strumento. Per sostituire l’output dello strumento prima che Claude lo veda, impostaupdatedToolOutput, che funziona per qualsiasi strumento in entrambi gli SDK. Il campo più vecchioupdatedMCPToolOutputsostituisce solo l’output dello strumento MCP. - Nel TypeScript SDK, un callback
PostToolUsepuò anche restituireclassifierContext, una breve nota sul risultato della chiamata allo strumento per il classificatore dei permessi della modalità auto. Poiché il tuo callback viene eseguito nel processo della tua applicazione, il classificatore può pesare una dichiarazione dell’utente che inoltri nella nota come intenzione dell’utente. Il campo richiede TypeScript Agent SDK v0.3.236 o successivo. Annotare un risultato per il classificatore della modalità auto copre il limite di lunghezza, la regola solo sincrona e cosa non mettere nella nota.
- Per gli hook
{} per consentire l’operazione senza modifiche. Gli hook di callback SDK utilizzano lo stesso formato di output JSON degli hook dei comandi shell di Claude Code, che documenta ogni campo e opzione specifica dell’evento. Per le definizioni di tipo SDK, consulta i riferimenti SDK TypeScript e Python.
Quando più hook
PreToolUse restituiscono decisioni diverse, deny ha priorità su defer, che ha priorità su ask, che ha priorità su allow.Output asincrono
Per impostazione predefinita, l’agente attende che il tuo hook restituisca prima di procedere. Se il tuo hook esegue un effetto collaterale, come la registrazione di log o l’invio di un webhook, e non ha bisogno di influenzare il comportamento dell’agente, puoi restituire un output asincrono. Questo dice all’agente di continuare immediatamente senza attendere il completamento dell’hook. In questo snippet,send_to_logging_service in Python e sendToLoggingService in TypeScript rappresentano qualsiasi funzione di logging che definisci:
Gli output asincroni non possono bloccare, modificare o iniettare contesto nell’operazione poiché l’agente ha già proseguito. Utilizzali solo per effetti collaterali come logging, metriche o notifiche.
Esempi
Diversi esempi in questa sezione mostrano solo la funzione di callback. Per eseguirne uno, registrate il callback sotto l’evento corrispondente nel campohooks delle vostre opzioni, come mostrato in Configurare gli hooks.
Modificare l’input dello strumento
Questo esempio intercetta le chiamate allo strumento Write e riscrive l’argomentofile_path per anteporre /sandbox. Il callback restituisce updatedInput con il percorso modificato e permissionDecision: 'allow' per approvare automaticamente l’operazione riscritta:
Abbinate
updatedInput con permissionDecision: 'allow' per approvare automaticamente l’input modificato, oppure permissionDecision: 'ask' per mostrarlo all’utente. Se omettete permissionDecision, l’input modificato si applica comunque e scorre attraverso la valutazione normale dei permessi. Con 'defer', updatedInput viene ignorato. Restituite sempre un nuovo oggetto piuttosto che mutare l’originale tool_input../sandbox o /tmp/sandbox (macOS non consente di creare una directory /sandbox a livello di root), quindi chiedete all’agente di scrivere un file: il risultato dello strumento Write nel flusso di messaggi nomina il percorso con il vostro prefisso sandbox piuttosto che quello che Claude ha richiesto.
Aggiungere contesto e bloccare uno strumento
Questo esempio nega le chiamate agli strumenti il cuifile_path inizia con /etc e spiega il motivo sia al modello sia all’utente:
permissionDecision: 'deny'interrompe la chiamata dello strumento.permissionDecisionReasoncomunica al modello il motivo.systemMessagemostra all’utente cosa è accaduto.
PreToolUse con un matcher Write|Edit e chiedete all’agente di creare un file sotto /etc: il risultato dello strumento Write nel flusso di messaggi contiene Writing to /etc is not allowed, e nessun file viene creato.
Approvare automaticamente strumenti specifici
Per impostazione predefinita, l’agente potrebbe chiedere il permesso prima di utilizzare determinati strumenti. Questo esempio approva automaticamente gli strumenti del file system di sola lettura (Read, Glob, Grep) restituendopermissionDecision: 'allow', consentendo loro di essere eseguiti senza conferma dell’utente, fatta eccezione per le letture da percorsi di rete, mentre lascia tutti gli altri strumenti soggetti ai normali controlli dei permessi:
Registrare più hooks
Quando un evento si attiva, gli hook corrispondenti vengono eseguiti in parallelo. Per le decisioni sui permessi, si applica il risultato più restrittivo. Ildeny di un singolo hook blocca la chiamata allo strumento. Poiché l’ordine di completamento è non deterministico, scrivi ogni hook in modo che agisca in modo indipendente, invece di fare affidamento sul fatto che un altro hook sia stato eseguito per primo.
L’esempio seguente registra tre controlli indipendenti, ciascuno con un matcher omesso. I nomi degli hook al suo interno, come audit_logger in Python o auditLogger in TypeScript, stanno al posto di callback che definisci tu:
Filtrare con matcher multi-strumento
Utilizzate matcher multi-strumento per condividere un callback tra strumenti correlati. Questo esempio registra tre matcher con ambiti diversi, e ogni hook che nomina sta al posto di un callback che voi definite:- Un elenco esatto separato da pipe (
Write|Edit|NotebookEdit) attivafile_security_hookper gli strumenti Write, Edit e NotebookEdit. - Un’espressione regolare (
^mcp__) attivamcp_audit_hookper qualsiasi strumento MCP il cui nome inizia conmcp__. - Un matcher omesso attiva
global_loggersenza filtrare per nome dello strumento.
Tracciare l’attività dei subagenti
Utilizzate gli hooksSubagentStop per monitorare quando i subagenti completano il loro lavoro. Consultate il tipo di input completo nei riferimenti SDK TypeScript e Python. Questo esempio registra un riepilogo ogni volta che un subagente si completa:
[SUBAGENT] Completed: con l’ID del subagente e il percorso della trascrizione.
Effettuare richieste HTTP dagli hooks
Gli hooks possono eseguire operazioni asincrone come richieste HTTP. Catturate gli errori all’interno del vostro hook invece di lasciarli propagare. Questo esempio invia un webhook dopo il completamento di ogni strumento, registrando quale strumento è stato eseguito e quando. L’hook cattura gli errori da un webhook non riuscito:Inoltrare le notifiche a Slack
Utilizzate gli hooksNotification per ricevere notifiche di sistema dall’agente e inoltrarle a servizi esterni. Nelle sessioni SDK, Claude Code esegue questo hook per i seguenti tipi di notifica:
permission_promptuna volta che una richiesta di autorizzazione ha atteso circa sei secondi sul vostro callbackcanUseTool. Richiede TypeScript Agent SDK v0.3.233 o successivo, oppure Python Agent SDK v0.2.139 o successivoelicitation_completeeelicitation_responseper i flussi di elicitazione dell’input dell’utente
idle_prompt, auth_success e elicitation_dialog, dall’interfaccia utente interattiva che le sessioni SDK non eseguono.
Ogni notifica include un campo message con una descrizione leggibile dall’uomo e facoltativamente un title.
Questo esempio inoltra ogni notifica a un canale Slack. Richiede un URL webhook in arrivo di Slack, che create aggiungendo un’app al vostro spazio di lavoro Slack e abilitando i webhook in arrivo:
Notification si attiva, l’hook pubblica il message della notifica, con il prefisso Agent status:, al canale a cui il vostro webhook è destinato.
Risolvere i problemi comuni
Hook non si attiva
- Verifica che il nome dell’evento hook sia corretto e sensibile alle maiuscole (
PreToolUse, nonpreToolUse) - Controlla che il tuo pattern di matcher corrisponda esattamente al nome dello strumento
- Assicurati che l’hook sia sotto il tipo di evento corretto in
options.hooks - Per gli hook non basati su strumenti che supportano matcher, come
NotificationeSubagentStop, i matcher corrispondono a campi diversi, eStopignora completamente i matcher (consulta pattern di matcher) - Gli hook potrebbero non attivarsi quando l’agente raggiunge il limite
max_turnsperché la sessione termina prima che gli hook possano essere eseguiti
Matcher non filtra come previsto
I matcher corrispondono solo ai nomi degli strumenti, non ai percorsi dei file o ad altri argomenti. Per filtrare per percorso di file, controllatool_input.file_path all’interno del tuo hook:
Timeout dell’hook
Claude Code esegue ogni callback con un timeout, che imposti in secondi con il campotimeout sul suo HookMatcher. Quando non ne imposti uno, Claude Code utilizza il valore predefinito dell’evento: 600 secondi per la maggior parte degli eventi, 30 secondi per UserPromptSubmit, PreModelSwitch e PostModelSwitch, e 10 secondi per MessageDisplay. Claude Code esegue i callback SessionEnd durante l’arresto con il budget di timeout di SessionEnd più breve, 1,5 secondi per impostazione predefinita.
Quando un callback supera il suo timeout, Claude Code lo annulla e scarta il suo output, e la sessione continua anziché bloccarsi. Quello che accade dopo dipende dall’evento:
PreToolUse: Claude Code blocca la chiamata allo strumento. Al suo posto, Claude riceve un risultato dello strumento che indica che l’hook non ha risposto prima del suo timeout, e il turno continua. Prima della v2.1.210, Claude Code segnalava il timeout a Claude come un rifiuto dell’utente, il che faceva fermare le sessioni incustodite in attesa di input.PostToolUseePostToolUseFailure: Claude Code mantiene il risultato dello strumento e il turno continua.UserPromptSubmiteUserPromptExpansion: Claude Code blocca il prompt con un messaggio che nomina l’hook e il timeout, e la sessione continua. Prima della v2.1.208, Claude Code terminava la query conerror_during_executionquando un callback su questi eventi scadeva.StopeSubagentStop: il callback scaduto conta come se non avesse restituito alcuna decisione. L’agente o il subagent si ferma come se quel callback lo avesse consentito, e una decisione dei tuoi altri hook sull’evento si applica comunque. Prima di Claude Code v2.1.273, un callbackStopoSubagentStopscaduto contava come un’esecuzione di hook non riuscita, e Claude Code scartava le decisioni dei tuoi altri hook sull’evento.SessionStart: il callback scaduto conta come se non avesse restituito alcun output, e la sessione continua con l’output dei tuoi altri hookSessionStart.PreModelSwitch: Claude Code blocca il cambio di modello. Un hook che non risponde non ha approvato il cambio.- Altri eventi, come
Notification,PreCompactePostModelSwitch: Claude Code registra l’errore nei log e continua.
Stop o SessionStart scade nella sessione principale, Claude Code aggiunge anche un SDKInformationalMessage al flusso dei messaggi indicando che l’app che guida la sessione non ha risposto. I timeout successivi non ripetono quel messaggio finché la tua app rimane non responsiva.
Se interrompi la query mentre un callback è in sospeso, Claude Code annulla la chiamata allo strumento in sospeso. Prima della v2.1.208, la chiamata allo strumento poteva ancora procedere se interrompevi durante un callback PreToolUse in sospeso.
Se il tuo callback ha bisogno di più tempo, imposta un timeout più alto sul suo HookMatcher. In TypeScript, utilizza l’AbortSignal dal terzo argomento del callback per gestire l’annullamento in modo pulito quando scatta il timeout.
Strumento bloccato inaspettatamente
- Controlla tutti gli hook
PreToolUseper i ritornipermissionDecision: 'deny' - Aggiungi dei log ai tuoi hook per vedere quale
permissionDecisionReasonstanno restituendo - Verifica che i pattern di matcher non siano troppo ampi: un matcher vuoto corrisponde a tutti gli strumenti
Input modificato non applicato
-
Assicurati che
updatedInputsia all’interno dihookSpecificOutput, non al livello superiore: -
Non abbinare
updatedInputapermissionDecision: 'defer', che scarta l’input modificato. OmetterepermissionDecisionva bene: l’input modificato si applica comunque attraverso la normale valutazione dei permessi. Puoi anche restituire'allow'per approvare automaticamente l’input modificato o'ask'per mostrarlo all’utente per l’approvazione -
Includi
hookEventNameinhookSpecificOutputper identificare a quale tipo di hook si riferisce l’output
Hook di sessione non disponibili in Python
SessionStart e SessionEnd possono essere registrati come hook di callback SDK in TypeScript, ma non sono disponibili nell’SDK Python perché il suo tipo HookEvent li omette. In Python, sono disponibili solo come hook di comandi shell definiti in file di impostazioni come .claude/settings.json. Quali file di impostazioni carica la tua applicazione SDK dipende da setting_sources o settingSources. Se imposti quell’opzione, includi la fonte che contiene gli hook:
client.receive_response() come trigger.
Le richieste di permesso dei subagent si moltiplicano
Quando avvii più subagent, ognuno potrebbe richiedere permessi separatamente per le proprie chiamate agli strumenti. Per evitare richieste ripetute, utilizza gli hookPreToolUse per approvare automaticamente strumenti specifici, oppure configura regole di permesso, che i subagent ereditano dalla conversazione principale.
Loop ricorsivi di hook con subagent
Un hookUserPromptSubmit che avvia subagent può creare loop infiniti se quei subagent attivano lo stesso hook. Per prevenire questo:
- Utilizza una variabile condivisa o lo stato della sessione per tenere traccia del fatto che ti trovi già all’interno di un subagent
- Limita gli hook in modo che vengano eseguiti solo per la sessione dell’agente di livello superiore
systemMessage non appare nell’output
Il camposystemMessage mostra un messaggio all’utente, non al modello. Su Claude Code v2.1.227 o successivo, il systemMessage di un hook può emergere nel flusso dei messaggi come un SDKInformationalMessage. Che ciò avvenga dipende dall’evento. La sezione di ogni evento nella pagina degli hook indica come emerge l’output. Per passare invece il contesto al modello, restituisci additionalContext.
Prima della v2.1.227, l’SDK faceva emergere l’output degli hook nel flusso dei messaggi solo per gli hook SessionStart e Setup. Per qualsiasi altro evento, l’output appariva solo negli eventi del ciclo di vita che includeHookEvents (include_hook_events in Python) aggiunge. La voce di quell’opzione descrive quali eventi del ciclo di vita produce ogni evento hook.
Se hai bisogno di far emergere in modo affidabile le decisioni degli hook nella tua applicazione, registrale separatamente nei log o utilizza un canale di output dedicato.
Risorse correlate
- Riferimento degli hooks di Claude Code: schemi JSON di input/output completi, documentazione degli eventi e modelli di matcher
- Guida agli hooks di Claude Code: esempi di hook dei comandi shell e procedure dettagliate
- Riferimento SDK TypeScript: tipi di hook, definizioni di input/output e opzioni di configurazione
- Riferimento SDK Python: tipi di hook, definizioni di input/output e opzioni di configurazione
- Autorizzazioni: controllare cosa può fare il vostro agente
- Strumenti personalizzati: creare strumenti per estendere le capacità dell’agente