- Blokir operasi berbahaya sebelum dieksekusi, seperti perintah shell yang merusak atau akses file yang tidak sah
- Log dan audit setiap pemanggilan tool untuk kepatuhan, debugging, atau analitik
- Transform input dan output untuk membersihkan data, menyuntikkan kredensial, atau mengalihkan jalur file
- Memerlukan persetujuan manusia untuk tindakan sensitif seperti penulisan database atau panggilan API
- Track lifecycle sesi untuk mengelola state, membersihkan resources, atau mengirim notifikasi
Cara kerja hooks
1
Sebuah event terjadi
Sesuatu terjadi selama eksekusi agent dan SDK menembakkan event: tool akan dipanggil (
PreToolUse), tool mengembalikan hasil (PostToolUse), subagent dimulai atau berhenti, agent idle, atau eksekusi selesai. Lihat daftar lengkap events.2
SDK mengumpulkan hooks terdaftar
SDK memeriksa hooks yang terdaftar untuk tipe event tersebut. Ini termasuk callback hooks yang Anda berikan di
options.hooks dan shell command hooks dari file pengaturan ketika settingSources atau setting_sources entry yang sesuai diaktifkan, yang mana untuk opsi query() default.3
Matchers memfilter hooks mana yang berjalan
Jika hook memiliki pola
matcher (seperti "Write|Edit"), SDK mengujinya terhadap target event (misalnya, nama tool). Hooks tanpa matcher berjalan untuk setiap event dari tipe tersebut.4
Fungsi callback dieksekusi
Setiap fungsi callback hook yang cocok menerima input tentang apa yang terjadi: nama tool, argumennya, ID sesi, dan detail spesifik event lainnya.
5
Callback Anda mengembalikan keputusan
Setelah melakukan operasi apa pun (logging, panggilan API, validasi), callback Anda mengembalikan output object yang memberi tahu agent apa yang harus dilakukan: izinkan operasi, blokir, modifikasi input, atau suntikkan konteks ke dalam percakapan.
PreToolUse (langkah 1) dengan matcher "Write|Edit" (langkah 3) sehingga callback hanya terjadi untuk tools penulisan file. Ketika dipicu, callback menerima input tool (langkah 4), memeriksa apakah jalur file menargetkan file .env, dan mengembalikan permissionDecision: "deny" untuk memblokir operasi (langkah 5):
.env dan hook menolak panggilan tool tersebut.
Available hooks
SDK menyediakan hooks untuk tahap berbeda dari eksekusi agent. Beberapa hooks tersedia di kedua SDK, sementara yang lain hanya TypeScript.Konfigurasi hooks
Untuk mengonfigurasi hook, berikan di fieldhooks dari opsi agent Anda (ClaudeAgentOptions di Python, object options di TypeScript). Snippet ini mengasumsikan Anda telah mendefinisikan callback hook, seperti protect_env_files di Python atau protectEnvFiles di TypeScript dari contoh di atas:
hooks adalah dictionary di Python atau object di TypeScript, di mana:
- Keys: nama hook event seperti
'PreToolUse','PostToolUse', dan'Stop' - Values: array dari matchers, masing-masing berisi pola filter opsional dan fungsi callback Anda
Matchers
Gunakan matchers untuk memfilter kapan callbacks Anda terjadi. Fieldmatcher cocok dengan nilai berbeda tergantung pada tipe hook event. Misalnya, tool-based hooks cocok dengan nama tool, sementara hooks Notification cocok dengan tipe notifikasi.
SDK matchers mengikuti aturan yang sama dengan matchers dalam file settings. Bagian tersebut mendokumentasikan jalur evaluasi string yang tepat dan regular expression, persyaratan versi mereka, dan nilai matcher untuk setiap tipe event.
Gunakan pola
matcher untuk menargetkan tools spesifik kapan pun memungkinkan. Matcher dengan 'Bash' hanya berjalan untuk perintah Bash, sementara menghilangkan pola menjalankan callbacks Anda untuk setiap kemunculan event. Hilangkan dengan sengaja untuk log setiap pemanggilan tool yang dibuat sesi Anda.
Callback functions
Inputs
Setiap hook callback menerima tiga argumen:- Input data: object yang diketik berisi detail event. Setiap tipe hook memiliki bentuk input sendiri. Misalnya,
PreToolUseHookInputmencakuptool_namedantool_input, sementaraNotificationHookInputmencakupmessage. Lihat definisi tipe lengkap di referensi SDK TypeScript dan Python.- Semua hook inputs berbagi
session_id,cwd, danhook_event_name. agent_iddanagent_typediisi ketika hook terjadi di dalam subagent. Di TypeScript, ini berada di base hook input dan tersedia untuk semua tipe hook. Di Python, ini adalah field opsional padaPreToolUse,PostToolUse,PostToolUseFailure, danPermissionRequest, dan field yang diperlukan padaSubagentStartdanSubagentStop.
- Semua hook inputs berbagi
- Tool use ID (
str | None/string | undefined): mengkorelasikan eventsPreToolUsedanPostToolUseuntuk pemanggilan tool yang sama. - Context: di TypeScript, berisi property
signal(AbortSignal) untuk pembatalan. Di Python, argumen ini dicadangkan untuk penggunaan di masa depan.
Outputs
Callback Anda mengembalikan object dengan dua kategori fields:- Top-level fields diterima pada setiap event:
systemMessagemenampilkan pesan kepada pengguna, dancontinue(continue_di Python) menentukan apakah agent terus berjalan setelah hook ini. Beberapa events membuangnya atau mengirimnya ke tempat lain. Setiap bagian event di halaman hooks mengatakan di mana mereka mendarat. hookSpecificOutputmengontrol operasi saat ini. Fields yang Anda tetapkan di dalamnya tergantung pada tipe hook event:- Untuk hooks
PreToolUse, di sinilah Anda menetapkanpermissionDecision("allow","deny","ask", atau"defer"),permissionDecisionReason, danupdatedInput. Jika Anda mengembalikan"defer", giliran berakhir dengan pesan hasil yangstop_reason-nya adalah"tool_deferred", sehingga Anda dapat melanjutkan panggilan tersebut nanti. - Untuk hooks
PostToolUse, Anda dapat menetapkanadditionalContextuntuk menambahkan informasi ke hasil tool. Untuk mengganti output tool sebelum Claude melihatnya, tetapkanupdatedToolOutput, yang bekerja untuk tool apa pun di kedua SDK. FieldupdatedMCPToolOutputyang lebih lama hanya mengganti output MCP tool. - Di TypeScript SDK, callback
PostToolUsejuga dapat mengembalikanclassifierContext, catatan singkat tentang hasil pemanggilan tool untuk auto mode permission classifier. Karena callback Anda berjalan dalam proses aplikasi Anda sendiri, classifier dapat menimbang pernyataan pengguna yang Anda sampaikan dalam catatan sebagai intent pengguna. Field memerlukan TypeScript Agent SDK v0.3.236 atau lebih baru. Annotate a result for the auto mode classifier mencakup batas panjang, aturan synchronous-only, dan apa yang tidak boleh dimasukkan dalam catatan.
- Untuk hooks
{} untuk mengizinkan operasi tanpa perubahan. SDK callback hooks menggunakan format output JSON yang sama dengan Claude Code shell command hooks, yang mendokumentasikan setiap field dan opsi spesifik event. Untuk definisi tipe SDK, lihat referensi SDK TypeScript dan Python.
Ketika multiple hooks atau permission rules berlaku,
deny mengambil prioritas atas defer, yang mengambil prioritas atas ask, yang mengambil prioritas atas allow. Jika hook apa pun mengembalikan deny, operasi diblokir terlepas dari hooks lainnya.Asynchronous output
Secara default, agent menunggu hook Anda kembali sebelum melanjutkan. Jika hook Anda melakukan side effect, seperti logging atau mengirim webhook, dan tidak perlu mempengaruhi perilaku agent, Anda dapat mengembalikan async output sebagai gantinya. Ini memberi tahu agent untuk melanjutkan segera tanpa menunggu hook selesai. Dalam snippet ini,send_to_logging_service di Python dan sendToLoggingService di TypeScript berdiri untuk fungsi logging apa pun yang Anda definisikan:
Async outputs tidak dapat memblokir, memodifikasi, atau menyuntikkan konteks ke dalam operasi karena agent telah melanjutkan. Gunakan hanya untuk side effects seperti logging, metrics, atau notifikasi.
Contoh
Beberapa contoh di bagian ini menunjukkan hanya fungsi callback. Untuk menjalankan satu, daftarkan callback di bawah event yang cocok di fieldhooks dari opsi Anda, seperti yang ditunjukkan di Konfigurasi hooks.
Modifikasi input tool
Contoh ini mengintersepsi pemanggilan tool Write dan menulis ulang argumenfile_path untuk menambahkan /sandbox, mengalihkan semua penulisan file ke direktori sandboxed. Callback mengembalikan updatedInput dengan jalur yang dimodifikasi dan permissionDecision: 'allow' untuk auto-approve operasi yang ditulis ulang:
Pasangkan
updatedInput dengan permissionDecision: 'allow' untuk auto-approve input yang dimodifikasi, atau permissionDecision: 'ask' untuk menampilkannya kepada pengguna. Jika Anda menghilangkan permissionDecision, input yang dimodifikasi masih berlaku dan mengalir melalui evaluasi permission normal. Dengan 'defer', updatedInput diabaikan. Selalu kembalikan object baru daripada mutating tool_input asli../sandbox atau /tmp/sandbox (macOS tidak memungkinkan pembuatan direktori root-level /sandbox), kemudian minta agent untuk menulis file: hasil tool Write di message stream menamai path dengan prefix sandbox Anda daripada yang diminta Claude.
Tambahkan konteks dan blokir tool
Contoh ini memblokir penulisan ke direktori/etc dan menjelaskan alasannya kepada model dan pengguna:
permissionDecision: 'deny'menghentikan pemanggilan tool.permissionDecisionReasonmemberitahu model mengapa, sehingga menghindari percobaan ulang.systemMessagemenunjukkan kepada pengguna apa yang terjadi.
PreToolUse dengan matcher Write|Edit dan minta agent untuk membuat file di bawah /etc: hasil tool Write di message stream berisi Writing to /etc is not allowed, dan tidak ada file yang dibuat.
Auto-approve tools spesifik
Secara default, agent dapat meminta permission sebelum menggunakan tools tertentu. Contoh ini melakukan auto-approve pada tool filesystem read-only (Read, Glob, Grep) dengan mengembalikanpermissionDecision: 'allow', membiarkannya berjalan tanpa konfirmasi pengguna, kecuali pembacaan dari jalur jaringan, sambil tetap membiarkan semua tool lainnya tunduk pada pemeriksaan izin normal:
Daftarkan multiple hooks
Ketika event terjadi, semua hooks yang cocok berjalan secara paralel. Untuk keputusan permission, hasil yang paling ketat menang: satudeny memblokir pemanggilan tool terlepas dari apa yang dikembalikan hooks lainnya. Karena urutan penyelesaian tidak dapat diprediksi, tulis setiap hook untuk bertindak secara independen daripada mengandalkan hook lain yang telah berjalan terlebih dahulu.
Contoh di bawah ini mendaftarkan tiga pemeriksaan independen untuk setiap pemanggilan tool. Nama hook di dalamnya, seperti audit_logger di Python atau auditLogger di TypeScript, berdiri untuk callbacks yang Anda tentukan:
Filter dengan multi-tool matchers
Gunakan multi-tool matchers untuk berbagi satu callback di seluruh tools terkait. Contoh ini mendaftarkan tiga matchers dengan scope berbeda, dan setiap hook yang dinamainya berdiri untuk callback yang Anda tentukan:- Daftar exact yang dipisahkan pipe (
Write|Edit|NotebookEdit) memicufile_security_hookhanya untuk file modification tools. - Regex (
^mcp__) memicumcp_audit_hookuntuk tool MCP apa pun yang namanya dimulai denganmcp__. - Matcher yang dihilangkan memicu
global_loggeruntuk setiap pemanggilan tool terlepas dari nama.
Lacak aktivitas subagent
Gunakan hooksSubagentStop untuk memantau ketika subagents menyelesaikan pekerjaan mereka. Lihat tipe input lengkap di referensi SDK TypeScript dan Python. Contoh ini mencatat ringkasan setiap kali subagent selesai:
[SUBAGENT] Completed: dengan ID subagent dan jalur transcript.
Buat HTTP requests dari hooks
Hooks dapat melakukan operasi asynchronous seperti HTTP requests. Tangkap errors di dalam hook Anda daripada membiarkan mereka menyebar. Contoh ini mengirim webhook setelah setiap tool selesai, mencatat tool mana yang berjalan dan kapan. Hook menangkap errors dari webhook yang gagal:Teruskan notifikasi ke Slack
Gunakan hooksNotification untuk menerima notifikasi sistem dari agent dan meneruskannya ke layanan eksternal. Dalam sesi SDK, Claude Code menjalankan hook ini untuk tipe notifikasi berikut:
permission_promptsekali permintaan permission telah menunggu sekitar enam detik di callbackcanUseToolAnda. Memerlukan TypeScript Agent SDK v0.3.233 atau lebih baru, atau Python Agent SDK v0.2.139 atau lebih baruelicitation_completedanelicitation_responseuntuk alur elicitation user-prompt
idle_prompt, auth_success, dan elicitation_dialog, dari UI interaktif yang sesi SDK tidak jalankan.
Setiap notifikasi mencakup field message dengan deskripsi yang dapat dibaca manusia dan secara opsional title.
Contoh ini meneruskan setiap notifikasi ke channel Slack. Ini memerlukan Slack incoming webhook URL, yang Anda buat dengan menambahkan app ke workspace Slack Anda dan mengaktifkan incoming webhooks:
Notification terjadi, hook memposting message notifikasi, dengan awalan Agent status:, ke channel yang ditargetkan webhook Anda.
Perbaiki masalah umum
Hook not firing
- Verifikasi nama hook event benar dan case-sensitive (
PreToolUse, bukanpreToolUse) - Periksa bahwa pola matcher Anda cocok dengan nama tool dengan tepat
- Pastikan hook berada di bawah tipe event yang benar di
options.hooks - Untuk non-tool hooks yang mendukung matchers, seperti
NotificationdanSubagentStop, matchers cocok dengan fields berbeda, danStopmengabaikan matchers sepenuhnya (lihat matcher patterns) - Hooks mungkin tidak terjadi ketika agent mencapai batas
max_turnskarena sesi berakhir sebelum hooks dapat dieksekusi
Matcher not filtering as expected
Matchers hanya cocok dengan nama tool, bukan jalur file atau argumen lainnya. Untuk memfilter berdasarkan jalur file, periksatool_input.file_path di dalam hook Anda:
Hook timeout
Claude Code menjalankan setiap callback dengan timeout, yang Anda atur dalam hitungan detik dengan fieldtimeout pada HookMatcher-nya. Ketika Anda tidak mengaturnya, Claude Code menggunakan default event: 600 detik untuk sebagian besar events, 30 detik untuk UserPromptSubmit, PreModelSwitch, dan PostModelSwitch, dan 10 detik untuk MessageDisplay. Claude Code menjalankan callbacks SessionEnd selama shutdown di bawah SessionEnd timeout budget yang lebih pendek, 1,5 detik secara default.
Ketika callback melampaui timeout-nya, Claude Code membatalkannya dan membuang output-nya, dan sesi berlanjut daripada hang. Apa yang terjadi selanjutnya tergantung pada event:
PreToolUse: Claude Code tidak menjalankan panggilan tool, Claude menerima hasil tool yang menyatakan hook tidak merespons sebelum timeout-nya, dan giliran berlanjut. Jika hookPreToolUselain mengembalikan explicit deny, Claude menerima denial itu sebagai gantinya dari error timeout. Sebelum v2.1.210, Claude Code melaporkan timeout ke Claude sebagai user rejection, yang membuat unattended sessions berhenti dan menunggu input.PostToolUsedanPostToolUseFailure: Claude Code menyimpan hasil tool dan giliran berlanjut.UserPromptSubmitdanUserPromptExpansion: Claude Code memblokir prompt dengan pesan yang menyebutkan hook dan timeout, dan sesi berlanjut. Karena callback pada events ini dapat bertindak sebagai policy gate, Claude Code tidak pernah membiarkan prompt yang timed-out melewati tanpa screening. Sebelum v2.1.208, Claude Code mengakhiri query denganerror_during_executionketika callback pada events ini timed out.StopdanSubagentStop: callback yang timed-out dihitung sebagai tidak mengembalikan keputusan. Agent atau subagent berhenti seolah-olah callback itu telah mengizinkannya, dan keputusan dari hooks lain Anda pada event masih berlaku. Sebelum Claude Code v2.1.273, callbackStopatauSubagentStopyang timed-out dihitung sebagai failed hook run, dan Claude Code membuang keputusan hooks lain Anda pada event.SessionStart: callback yang timed-out dihitung sebagai tidak mengembalikan output, dan sesi berlanjut dengan output dari hooksSessionStartlain Anda.PreModelSwitch: Claude Code memblokir model switch. Hook yang tidak menjawab belum menyetujui switch.- Events lainnya, seperti
Notification,PreCompact, danPostModelSwitch: Claude Code mencatat kegagalan dan berlanjut.
Stop atau SessionStart timed out di sesi utama, Claude Code juga menambahkan SDKInformationalMessage ke message stream mengatakan app yang menjalankan sesi tidak merespons. Timeouts kemudian tidak mengulangi pesan itu saat app Anda tetap tidak responsif.
Jika Anda mengganggu query saat callback tertunda, Claude Code membatalkan panggilan tool yang tertunda. Sebelum v2.1.208, panggilan tool masih bisa dilanjutkan jika Anda mengganggu selama callback PreToolUse yang tertunda.
Jika callback Anda membutuhkan lebih banyak waktu, atur timeout yang lebih tinggi pada HookMatcher-nya. Di TypeScript, gunakan AbortSignal dari argumen callback ketiga untuk menangani pembatalan dengan baik ketika timeout terjadi.
Tool blocked unexpectedly
- Periksa semua hooks
PreToolUseuntuk returnspermissionDecision: 'deny' - Tambahkan logging ke hooks Anda untuk melihat apa
permissionDecisionReasonyang mereka kembalikan - Verifikasi pola matcher tidak terlalu luas: matcher kosong cocok dengan semua tools
Modified input not applied
-
Pastikan
updatedInputberada di dalamhookSpecificOutput, bukan di top level: -
Jangan pasangkan
updatedInputdenganpermissionDecision: 'defer', yang menghilangkan input yang dimodifikasi. MenghilangkanpermissionDecisiontidak masalah: input yang dimodifikasi masih berlaku melalui evaluasi izin normal. Anda juga dapat mengembalikan'allow'untuk auto-approve input yang dimodifikasi atau'ask'untuk menampilkannya kepada pengguna untuk persetujuan -
Sertakan
hookEventNamedihookSpecificOutputuntuk mengidentifikasi tipe hook mana output-nya
Session hooks not available in Python
SessionStart dan SessionEnd dapat didaftarkan sebagai SDK callback hooks di TypeScript, tetapi tidak tersedia di Python SDK karena tipe HookEvent-nya menghilangkannya. Di Python, mereka hanya tersedia sebagai shell command hooks yang didefinisikan dalam file pengaturan seperti .claude/settings.json. File pengaturan mana yang dimuat oleh aplikasi SDK Anda bergantung pada setting_sources atau settingSources. Jika Anda mengatur opsi tersebut, sertakan sumber yang berisi hook:
client.receive_response() sebagai trigger Anda.
Subagent permission prompts multiplying
Ketika menjalankan beberapa subagent, masing-masing mungkin meminta izin secara terpisah untuk panggilan tool-nya sendiri. Untuk menghindari permintaan izin berulang, gunakan hooksPreToolUse untuk auto-approve tool spesifik, atau konfigurasikan aturan izin, yang diwarisi subagent dari percakapan induk.
Recursive hook loops with subagents
HookUserPromptSubmit yang menjalankan subagent dapat membuat infinite loops jika subagent tersebut memicu hook yang sama. Untuk mencegah ini:
- Gunakan shared variable atau session state untuk melacak apakah Anda sudah berada di dalam subagent
- Batasi hook agar hanya berjalan untuk sesi agent tingkat atas
systemMessage not appearing in output
FieldsystemMessage menampilkan pesan kepada pengguna, bukan model. Pada Claude Code v2.1.227 atau lebih baru, systemMessage hook dapat muncul di message stream sebagai SDKInformationalMessage. Apakah itu terjadi tergantung pada event. Setiap section event di halaman hooks mengatakan bagaimana output muncul. Untuk meneruskan konteks ke model sebagai gantinya, kembalikan additionalContext.
Sebelum v2.1.227, SDK menampilkan hook output di message stream hanya untuk hooks SessionStart dan Setup. Untuk event lainnya, output muncul hanya di lifecycle events yang includeHookEvents (include_hook_events di Python) tambahkan. Entry opsi itu mencakup lifecycle events mana yang setiap hook event hasilkan.
Jika Anda perlu menampilkan keputusan hook ke aplikasi Anda dengan andal, catat keputusan tersebut secara terpisah atau gunakan channel output khusus.
Related resources
- Claude Code hooks reference: full JSON input/output schemas, event documentation, dan matcher patterns
- Claude Code hooks guide: shell command hook examples dan walkthroughs
- TypeScript SDK reference: hook types, input/output definitions, dan configuration options
- Python SDK reference: hook types, input/output definitions, dan configuration options
- Permissions: kontrol apa yang dapat dilakukan agent Anda
- Custom tools: build tools untuk extend agent capabilities