Claude Code Hooks(フック)は、Claude Codeがファイルを編集する直前・直後、入力待ちになった時、セッションを開始した時などの決まった時点で、あらかじめ登録したシェルコマンドやHTTPリクエストを自動で実行させる仕組みです。CLAUDE.mdに書いたルールはClaudeへの「お願い」にとどまりますが、フックは条件に合うイベントで自動実行され、整形・通知・操作のブロックに使えます。ただし、PreToolUseのcommand・http・mcp_toolフックはタイムアウトすると操作をブロックしないため、起動と制御の成功は区別する必要があります。この記事では、フックがほかの拡張機能とどう違うのか、何に使うと効くのか、導入前に知っておきたい落とし穴を整理します。イベント一覧やsettings.jsonの細かな書式はClaude Code Hooksの設定方法|全33イベント一覧とsettings.jsonの書き方で扱っています。
まとめ:Claude Code Hooksの要点
- フックは、Claude Codeのライフサイクル上の決まった時点(イベント)で必ず起動する処理です。2026年10月時点のイベントは33種類あります。
- CLAUDE.mdやSkillsはClaudeが読んで従う「指示」で、守られる保証はありません。毎回必ず守らせたいルールはフックに移します。
- 処理の種類は command(シェル)・http・mcp_tool・prompt(LLMの1回判定)・agent(ツールを使う検証)の5つです。
- 設定はYAMLではなくsettings.jsonの
hooksキーにJSONで書きます。/hooksは登録内容を確認する閲覧用の画面です。 - PreToolUseでは終了コード2でツール実行を止められます。JSON判定を返さない実測では、終了コード1だけでは止まりませんでした。
- フックはあなたのユーザー権限でコマンドを実行します。
claude -pでは他人のリポジトリのフックも確認なしで走るため、実行前に.claude/settings.jsonを確認します。
Claude Code Hooksの仕組み:決まった時点で必ず走る処理
公式ドキュメント(hooks でアクションを自動化する)はフックを「Claude Codeのライフサイクルの特定の時点で実行される、ユーザー定義のシェルコマンド」と説明し、その価値を決定論的な制御(LLMの判断に任せず、特定の処理を必ず起こすこと)に置いています。Claudeがツールを使うたびに、ある処理を挟み込めるのが要点です。
イベントは発火の頻度で3つに分かれます。
- セッションごと:
SessionStart(開始・再開時)、SessionEnd(終了時) - ターンごと:
UserPromptSubmit(プロンプト送信時)、Stop(Claudeが応答を終えた時)、StopFailure(APIエラーで終わった時) - ツール呼び出しごと:
PreToolUse(実行前)、PostToolUse(成功後)
このほか、通知を出す時の Notification、コンテキスト圧縮の前後に起きる PreCompact・PostCompact、設定ファイルが変わった時の ConfigChange などがあり、合計33種類です。全イベントの発火条件はClaude Code Hooksの設定方法|全33イベント一覧とsettings.jsonの書き方にまとめています。
イベント・matcher・処理の3点で動く流れ
フックは「どのイベントで」「どの条件に一致したとき(matcher)」「何を実行するか(ハンドラ)」で設定します。matcherの対象はイベントによって異なり、ツール名、セッションの開始理由、通知の種類などです。matcherを使わないイベントもあります。イベントが起きてmatcherが一致すると、Claude Codeはイベントの内容をJSONにしてハンドラへ渡します。commandハンドラなら標準入力に届きます。
実際に PreToolUse のフックが受け取ったJSONは次のとおりです(Claude Code 2.1.284で記録・IDは省略)。tool_name と tool_input を見れば、Claudeがこれから何をしようとしているかが分かります。
{
"hook_event_name": "PreToolUse",
"permission_mode": "default",
"cwd": "/private/tmp/hooktest",
"tool_name": "Bash",
"tool_input": {
"command": "echo hi > /tmp/hooktest/out1.txt",
"description": "Write \"hi\" to file in /tmp/hooktest"
}
}
ハンドラは終了コードかJSON出力で結果を返します。PreToolUseのcommandハンドラでは、判定JSONを返さない終了コード0は「判定なし」、終了コード2はツール実行のブロックを意味し、標準エラーで理由を伝えられます。終了コード0でも、有効なJSONを返せば拒否などの判定が適用されます。
React Hooks・Gitフックなど一般の「フック」との違い
「フック(hook)」は、処理の流れの決まった位置に別の処理を差し込む仕組みを指す一般的なプログラミング用語です。Gitのpre-commitフックや、Zshの precmd フック(add-zsh-hookでZsh Hookを登録する方法)も同じ考え方です。一方、ReactのHooksは useState などコンポーネントに状態を持たせる関数群の名前で、差し込みの仕組みとは別物です(Reactフック(Hooks)とは?全18種の一覧とReact Compiler時代の使い分け)。Claude Code Hooksは前者の系統で、差し込む先がAIエージェントの行動です。
CLAUDE.md・Skills・MCPとHooksの使い分け
公式の機能概要ページは、拡張手段の役割を次のように分けています。違いは「誰が動かすか」と「確実に起きるか」です。
| 機能 | 動くきっかけ | 確実に起きるか | コンテキストの消費 | 向く用途 |
|---|---|---|---|---|
| CLAUDE.md | 毎回の会話で読み込み | 指示として読むだけ | 常に消費 | 規約・前提の共有 |
| Skill | /名前の入力か説明文との一致 | Claudeの解釈次第 | 説明文は毎回、本文は使用時 | 手順書・参照資料 |
| サブエージェント | Claudeからの委任 | Claudeの判断次第 | 別コンテキストで消費 | 調査の隔離・並列化 |
| MCP | Claudeがツールを選ぶ | Claudeの判断次第 | ツール名は起動時に読む | 外部サービスとの接続 |
| フック | ライフサイクルのイベント | 対応イベント・設定条件に従い起動 | 主会話への追加は出力次第。LLM型は別途消費 | 整形・ブロック・通知・記録 |
プラグインは上の機能を束ねて配布する入れ物で、フックも hooks/hooks.json としてプラグインに同梱できます(Claude Codeプラグインの作り方と社内配布)。SkillsとCLAUDE.mdの書き方はClaude Codeカスタムコマンドの作り方|Skills統合後の書き方と引数とClaude Codeの使い方|導入から権限とCLAUDE.md設定まで実務手順で扱っています。
CLAUDE.mdからフックへ移すルールの判断基準
公式ドキュメントは「CLAUDE.mdやSkillに書いた『.envを編集しない』という指示は要望であって保証ではない。PreToolUse フックで編集をブロックするのが強制だ」と明言しています。判断の目安は次のとおりです。
- 毎回同じ手順で、Claudeが考える必要のない処理(保存時の整形、
rm -rfの拒否、終了時のSlack通知)はフックにします。 - 状況に応じてClaudeが手順を選ぶべきもの(リリース手順、APIの設計規約、デバッグの進め方)はSkillかCLAUDE.mdに書きます。
- 両方を組み合わせるのが実務の形です。たとえばリンターの実行は
PostToolUseフックで確実に起こし、出たエラーの直し方はSkillに書いておきます。PostToolUseの通常の標準出力は、そのままではClaudeに渡りません。エラーを伝えるには、標準エラーへ書いて終了コード2を返すか、JSONのadditionalContextで渡します。
同じ前提をセッション開始時に毎回伝えたいだけなら、フックではなくCLAUDE.mdで足ります。公式も、セッション開始のたびに文脈を入れるならCLAUDE.mdを検討するよう案内しており、SessionStart フックの例はコンテキスト圧縮の直後に前提を入れ直す用途に絞っています。
フックで権限を広げることはできない:権限ルールとの関係
フックと権限設定(allow・denyルール、パーミッションモード)は役割が非対称です。PreToolUse フックはどの権限モードよりも先に発火し、denyを返せば bypassPermissions モードや --dangerously-skip-permissions でもツールを止められます。逆に、フックがallowを返しても、settingsのdenyルールは覆せません。公式の表現では、settingsやプラグインのフックは「制限を厳しくできるが、権限ルールが許す範囲より緩めることはできない」ものです。
ただし、フックの条件指定(if フィールド)はベストエフォートで、公式は確実な許可・拒否には権限システムを使うよう求めています。外部からの攻撃を防ぐ最後の壁としてはフックに頼らず、denyルールやサンドボックスと重ねて使うのが前提です。
Hooksでできること:目的別の代表例
公式ガイドが例として挙げている使い道を、使うイベントと合わせて整理します。最初に入れるなら、効果がすぐ見える通知と自動整形が向いています。
| 目的 | イベント | 処理の中身 |
|---|---|---|
| 入力待ち・許可待ちの通知 | Notification | デスクトップ通知・Slack投稿 |
| 編集後の自動整形 | PostToolUse(Edit|Write) | Prettierなどを実行 |
| 保護ファイルの編集禁止 | PreToolUse(Edit|Write) | パスを見て終了コード2 |
| 圧縮後の前提の再注入 | SessionStart(compact) | 標準出力に前提を書く |
| 設定変更の監査 | ConfigChange | 変更をログに追記 |
| 環境変数の再読み込み | CwdChanged・FileChanged | direnvなどを再実行 |
| 特定の確認だけ自動承認 | PermissionRequest | ExitPlanModeに限定して承認 |
| 終了前のテスト通過確認 | Stop | agentハンドラでテストを実行 |
最初の1本:入力待ちのデスクトップ通知
~/.claude/settings.json に次を追加すると、macOSではNotificationイベントの発生時に通知が出ます。この例はmatcherが空なので、入力待ち以外の通知にも反応します。
{
"hooks": {
"Notification": [
{
"matcher": "",
"hooks": [
{
"type": "command",
"command": "osascript -e 'display notification \"Claude Code needs your attention\" with title \"Claude Code\"'"
}
]
}
]
}
}
通知が出ない場合は、macOSの「システム設定 > 通知」でスクリプトエディタの通知が許可されているかを確認します。osascript はスクリプトエディタ経由で通知を出すため、許可がないと何も表示されずに失敗します。Slackに送るなら、commandの中でIncoming WebhookのURLにcurlでPOSTします(Slack Incoming Webhookの実装手順)。
保護ファイルの編集をPythonでブロックする例
公式ガイドの例はbashとjqで書かれていますが、標準入力のJSONを読めれば言語は問いません。次のPythonスクリプトはjqを使いません。パスの部分一致で判定するため、.env.exampleやpackage-lock.json.bakもブロックします。.claude/hooks/protect_files.py に保存し、chmod +x で実行権限を付けます。
#!/usr/bin/env python3
import json
import sys
PROTECTED = (".env", ".git/", "package-lock.json")
data = json.load(sys.stdin)
path = data.get("tool_input", {}).get("file_path", "").replace("\\", "/")
if any(p in path for p in PROTECTED):
print(f"保護対象のため編集できません: {path}", file=sys.stderr)
sys.exit(2)
sys.exit(0)
この例はEdit・Writeによる編集だけを対象にし、BashやMCPツール経由の書き込みは防ぎません。また、入力されたパス文字列だけを調べ、シンボリックリンクの参照先は検査しないため、保護対象への別名パスまで防ぐ実装ではありません。登録はプロジェクトの .claude/settings.json に書きます。$CLAUDE_PROJECT_DIR はプロジェクトのルートに置き換わります。
{
"hooks": {
"PreToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/protect_files.py"
}
]
}
]
}
}
Claude Code 2.1.284(Python 3.14.6)で、Claudeに「.env を編集し、次に notes.txt を作成する」よう指示して確かめました。.env の編集はブロックされ、Claudeは標準エラーの「保護対象のため編集できません: …/.env」をそのまま受け取って報告しました。.env の中身は変わらず、notes.txt の作成は通常どおり成功しています。
5種類のハンドラ:シェル処理とLLM判定の使い分け
ハンドラ(実行する処理)は5種類です。決まったルールで判定できるならcommand、文脈を読む判断が要るならpromptかagentを選びます。
| type | 実行するもの | 通常の既定タイムアウト(イベント別の例外あり) | 追加された版 |
|---|---|---|---|
| command | シェルコマンド | 10分 | 1.0.38 |
| prompt | Claudeモデルによる1回の判定 | 30秒 | 2.0.30(当初はStopのみ) |
| agent | ツールを使えるサブエージェント | 60秒 | 実験的機能 |
| http | URLへのPOST | 10分 | 2.1.63(2026-02-28) |
| mcp_tool | 接続済みMCPサーバーのツール | 10分 | 2.1.118 |
promptとagentのハンドラは、{"ok": false, "reason": "…"} のようなJSONで判定を返します。Stop で ok: false が返ると、通常は理由を受け取って作業を続けます。ただし、promptハンドラが達成不能を示す impossible: true も返した場合は終了します。「タスクが全部終わったか」「テストが通ったか」をLLMに確認させてから終了させる、といった使い方です。promptハンドラはClaude Codeがバックグラウンド処理に使うモデルを既定で呼ぶため、その分の利用量を消費します。agentハンドラは公式が実験的機能と明記しており、本番の運用ではcommandを優先するよう案内されています。
httpハンドラはチーム共通の監査サーバーにツール利用を集約する用途向けで、ブロックするにはステータスコードではなく2xxの応答本文でJSONの判定を返します。
Hooks導入時の制約と誤設定による失敗
挙動を誤解したまま入れると「止めたつもりで止まっていない」状態になります。公式の制約事項と実測から、つまずきやすい点を挙げます。
JSON判定を返さない場合の終了コード1と2の違い
多くのツールでは終了コード1が「失敗」を意味するため、ブロックのつもりで exit 1 を書きがちです。PreToolUseでは終了コード2でツール実行をブロックできます。終了コード1でも、有効なJSONで拒否判定を返せばブロックされるため、以下の実測は標準出力にJSONを返さない場合の結果です。Claude Code 2.1.284で、Bashの実行前に標準エラーへメッセージを書いて終了するフックを用意し、ファイルを書き出すコマンドを実行させて確かめました。
| フックの終了コード | Bashコマンド | Claudeへの伝わり方 |
|---|---|---|
| exit 1 | 実行された(ファイル作成) | メッセージは伝わらない |
| exit 2 | ブロックされた | 標準エラーの理由を受け取った |
JSONで permissionDecision: "allow" を返しても、終了コード2のブロックは覆りません。また、終了コード0で何も出力しない場合は「許可」ではなく「判定なし」で、通常の権限確認がそのまま行われます。
PostToolUseの事後処理とStopの発火タイミング
PostToolUse はツールが実行された後に起動するため、変更を元に戻すことはできません。止めたい操作は必ず PreToolUse で判定します。Stop は「タスクが完了した時」ではなく「Claudeが応答を終えるたび」に起動し、ユーザーが中断した時には起動しません。Stop フックで作業を続けさせる設計では、既定ではStopフックによる継続が連続8回に達すると次のブロックが無効になるため、入力JSONの stop_hook_active を見て2回目以降は抜ける処理を入れておきます。
複数フックの並列実行と判定の優先順位
一致したフックはすべて並列に実行され、1つがdenyを返しても他のフックは最後まで動きます。PreToolUse の判定が割れたときは deny・defer・ask・allow の順に優先されます。ただし、deferは非対話モードの単一ツール呼び出しでのみ有効で、対話セッションや複数ツールの同時呼び出しでは無視されます。たとえば「ログに記録するフック」と「危険なコマンドを止めるフック」を並べると、止められたコマンドもログには残ります。ツールの引数を書き換える updatedInput を複数のフックが返した場合は、最後に終わったものが勝ち、その順序は決まりません。同じツールの入力を書き換えるフックは1つに絞ります。
claude -pでのリポジトリ内フックの自動実行
対話モードのClaude Codeは、フォルダの信頼を確認するダイアログに同意するまで、自分の ~/.claude/settings.json を含むすべての設定ファイルのフックを保留します。一方、-p オプションやAgent SDKで起動した場合はダイアログが出ず、フォルダを信頼済みとして扱います。つまり、clone しただけのリポジトリの .claude/settings.json に書かれたフックが、あなたの権限で実行されます。他人が書いたリポジトリに対してCIやスクリプトで claude -p を回す前に、.claude/ 配下を確認するか、--settings '{"disableAllHooks": true}' でその回だけフックを止めます。commandハンドラはあなたのユーザー権限でシェルを動かすので、権限上は任意のファイルを読み書き・削除できます。
他のAIコーディングエージェントのフックとの比較
「LLMのフック」「AIフック」と呼ばれる仕組みは、Claude Codeが2025年6月に公開した後、主要なコーディングエージェントに相次いで実装されました。設定ファイルの場所とイベント名は製品ごとに違いますが、ツール実行前のイベントで終了コード2か拒否の判定を返すと止まる、という基本の形はほぼ共通です。
| 製品 | 設定ファイル | ツール実行前のイベント | 導入 |
|---|---|---|---|
| Claude Code | .claude/settings.json ほか | PreToolUse | 1.0.38(2025年6月) |
| Cursor | .cursor/hooks.json | preToolUse・beforeShellExecution | 1.7(ベータ) |
| GitHub Copilot CLI | .github/hooks/*.json | preToolUse | 0.0.396(2026-01-27) |
| Gemini CLI | .gemini/settings.json | BeforeTool | v0.26.0(2026-01) |
| OpenAI Codex | .codex/hooks.json か config.toml | PreToolUse | 0.114.0(2026-03-11、当初はSessionStart・Stopのみ) |
Codexはイベント名もClaude Codeとほぼ同じ(PreToolUse・PostToolUse・Stopなど12種類)で、終了コード2と標準エラーでブロックする方式も共通です。複数のエージェントを併用するチームでは、判定ロジックを1本のスクリプトにまとめ、各製品の設定ファイルからそれを呼ぶ形にすると管理が楽になります。ただし、フックに渡る入力JSONの形式は製品ごとに定義されているので、共通化する前に各製品のリファレンス(Cursor・GitHub Copilot・Gemini CLI・Codex)で項目名を確認します。ClineのHooksはClineの設定ファイルとインストール完全ガイド|MCP設定・OS別パス・Hooksを整理、エージェントの制御部品としてのフックの位置づけはAIハーネスとは?LLMエージェントの制御基盤を構成する6部品とSDK別の実装で扱っています。
Hooks機能の経緯と、2025年の解説との違い
フックはClaude Code 1.0.38のCHANGELOGに「Released hooks.」と記載されて正式に加わりました。その後、1.0.54でプロンプト送信時の UserPromptSubmit、2.0.30でpromptハンドラ(当初は Stop 向け)、2.1.63でhttpハンドラ、2.1.118でmcp_toolハンドラが追加され、2026年10月時点の最新版2.1.287でイベントは33種類になっています。
2025年の解説には現行と合わない記述が残っています。代表的なのは、フックを hooks.yaml というYAMLファイルに書くという説明、trigger・retries といった設定項目、/hooks でフックを追加・編集するという手順です。現行のフックはsettings.jsonなどにJSONで書き、/hooks は登録済みのフックを出どころ(ユーザー設定・プロジェクト設定・プラグインなど)つきで一覧する閲覧用の画面です。誤った設定例や旧来の操作手順と現行仕様の対照はClaude Code Hooksの設定方法|全33イベント一覧とsettings.jsonの書き方の対照表で確認できます。
よくある質問
HooksとCLAUDE.mdはどちらを使えばよいですか?
毎回守らせたいルールや定型処理はフック、状況に応じてClaudeに判断してほしい規約や前提はCLAUDE.mdに書きます。CLAUDE.mdの記述は指示であって、守られる保証はありません。
Claude Code Hooksの設定はどこに書きますか?
~/.claude/settings.json(自分の全プロジェクト)、.claude/settings.json(プロジェクト共有)、.claude/settings.local.json(自分だけ)のいずれかの hooks キーにJSONで書きます。ほかに管理者の管理設定、プラグインの hooks/hooks.json、Skillとサブエージェントのフロントマターにも置けます。
Claude Code HooksはPythonで書けますか?
書けます。commandハンドラは標準入力でJSONを受け取り、終了コードか標準出力のJSONで結果を返す実行ファイルであれば、PythonでもNode.jsでも構いません。上の保護ファイルの例はPython 3.14.6で動作を確認しています。
Hooksを使うと料金やトークンは増えますか?
commandやhttpのハンドラはClaude Codeの外で動くため、出力を返さない限りコンテキストを消費しません。promptとagentのハンドラはClaudeモデルを呼び出すので、その分の利用量がかかります。
LLMやAIエージェントの「フック」とは何ですか?
AIエージェントがツールを使う前後などの決まった時点で、人間が用意した処理を必ず差し込む仕組みです。LLMの判断に任せると漏れる整形やブロックを、プログラムで確実に実行させる目的で使われます。Claude Code Hooksはその代表例です。