Claude Code Hooks 使い方|PreToolUse/PostToolUse/Stop/SubagentStop 完全ガイド

この記事のポイント
Claude Code Hooks の設定方法を、主要4イベント(PreToolUse/PostToolUse/Stop/SubagentStop)の比較表・コピペ用レシピ・exit code の正しい使い方とあわせて解説します。2026年10月時点(v2.1.301)の33イベント、onFailure、mods との違い、企業導入時のセキュリティ統制まで整理しました。
Claude Code Hooks は、rm -rf のような危ないコマンドを実行前に止めたり、ファイル編集のたびに Prettier や型チェックを走らせたりする処理を、Claude の判断に任せず設定ファイルで必ず実行させる仕組みです。2026年10月時点の公式リファレンス(Claude Code v2.1.301)では、フックを掛けられるイベントが33種類、処理の種類(ハンドラー)が5つあります。
この記事では、次の内容を解説します。
- Claude Code Hooks の全体像と5つのハンドラータイプの使い分け
- 主要4イベント(PreToolUse / PostToolUse / Stop / SubagentStop)の違いを1枚にまとめた比較表と設定例
- 33イベントの一覧と、それぞれ止められるかどうか
exit 1では止まらない、JSON の置き場所を間違えるとエラーも出ずに無視される、Stop が止まらなくなる、といったつまずきやすい点と対処法- Hooks / Permissions / CLAUDE.md / Skills / mods の使い分け
- 2026年夏〜秋の主な仕様変更(
onFailure: "block"、UserPromptSubmit の実行順など) - 企業で使うときのセキュリティ統制(managed settings、
-p実行時の注意)
対象は、Claude Code をすでに使っていて自動化と安全策をもう一段強めたい開発者と、チームに Claude Code を配る前にルールを整えたい技術リーダーです。
Claude Code Hooks とは|30秒で全体像を掴む
Claude Code Hooks は、Claude Code の動作の決まったタイミング(ツール実行の直前・直後、応答の終了時など)に、自分で用意したシェルコマンドや HTTP リクエストを差し込む機能です。公式ガイド(hooks でワークフローを自動化する)は、LLM が実行を選ぶかどうかに頼らず、特定の処理が毎回確実に起きるようにするための仕組みだと説明しています。
CLAUDE.md に「.env は編集しないで」と書くのはお願いで、Claude が守るかどうかはその場の判断次第です。Hooks で .env への Edit をブロックすれば、Claude がどう判断しても編集は通りません。この「お願い」と「強制」の差が、Hooks を使う理由です。
Hooks の主な用途は、安全・品質・運用の3つに分かれます。
- 安全:
rm -rf、git push --force、本番 DB への書き込みなどを設定側で止める - 品質: ファイル編集のたびに Prettier・ESLint・型チェック・テストを走らせる
- 運用: セッション開始時に必要な情報を渡す、作業ログを残す、入力待ちを通知する
できることの代表例
カテゴリ | 代表的な使い方 | 主に使うイベント |
|---|---|---|
事前ブロック |
| PreToolUse |
品質チェックの自動化 | Prettier / ESLint / 型チェック / テストを編集後に実行 | PostToolUse |
情報の追加 | ブランチ名や | UserPromptSubmit / SessionStart |
監査・記録 | ツール呼び出しを外部サーバーへ送る、設定変更を記録する | PostToolUse / ConfigChange |
完了チェック | テストが通るまで Claude に作業を続けさせる | Stop |
サブエージェントの検証 | サブエージェントの成果物を検査し、足りなければやり直させる | SubagentStop |
通知 | 権限確認待ち・入力待ちをデスクトップや Slack に通知 | Notification |
Hooks の提供状況(2026年10月時点)
- 提供元: Anthropic。Claude Code に標準で入っている機能で、Hooks 単体の追加料金はありません
- 動く環境: ターミナル版 CLI、Desktop アプリの Code タブ、VS Code 拡張、
claude -p/ Agent SDK など、設定ファイルを読み込む環境。Windows 向けには PowerShell の指定も公式に用意されています - 最新バージョン: v2.1.301(2026年10月15日リリース)。更新が速いため、手元のバージョンは
claude --versionで確認してください - 確認方法: Claude Code の中で
/hooksを実行すると、設定済みの hooks をイベントごとに一覧できます(閲覧専用)

出典: Anthropic 公式サイト
設定ファイルの配置場所とスコープ

出典: Claude Code Docs(Settings)
Hooks は複数の設定ファイルに書けます。各ファイルの hooks は上書きではなくマージ(合算)されるため、ユーザー設定とプロジェクト設定の両方に書いた hook はどちらも動きます。まったく同じハンドラーが複数のファイルにある場合は1回だけ実行されます。
配置場所 | 効く範囲 | Git で共有 | 主な用途 |
|---|---|---|---|
| 自分の全プロジェクト | しない(自分の PC のみ) | 個人で常に入れておきたいガード |
| 1つのプロジェクト | する(コミット可) | チーム共通のガード・品質チェック |
| 1つのプロジェクト | しない(gitignore) | 個人の試験的な hook |
管理ポリシー設定(managed settings) | 組織全体 | 管理者が配布 | 企業としての統制 |
プラグインの | プラグインを有効にしている間 | プラグインに同梱 | 再利用できる hook の配布 |
Skill の frontmatter | そのスキルを呼んだ後、セッションの残り | スキルに同梱 | 特定作業のときだけ効く hook |
サブエージェントの frontmatter | そのサブエージェントの実行中 | エージェント定義に同梱 | サブエージェント専用の検査(frontmatter に書いた |
運用のコツ: チームで守りたい最低限のガード(rm -rf の防止、.env の保護など)は .claude/settings.json にコミットし、個人的に試している hook は .claude/settings.local.json に置くのが基本です。
設定ファイルを編集すると、通常はファイル監視で自動的に読み込み直されます。反映されないときはセッションを再起動してください。JSON に末尾のカンマやコメントを入れると読み込めないので注意してください。
設定ファイルの基本構造|3階層ネストを理解する
Hooks の設定は「イベント名 → matcher(対象の絞り込み)→ 実行するハンドラー」の3階層で書きます。
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"if": "Bash(rm *)",
"command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/block-rm.sh",
"timeout": 30
}
]
}
]
}
}各階層の役割
- イベント名(
PreToolUseなど): いつ動かすか - matcher: どのツール・どの種類のときに動かすか
- hooks: 実際に実行するハンドラー(
command/http/mcp_tool/prompt/agentの5タイプ)
ハンドラー共通で使える主なフィールドは、type、if、timeout(秒)、statusMessage(実行中にスピナーに出す文言)です。command 型には args を付けるとシェルを通さずに実行する形式(exec 形式)になり、shell で bash か powershell を選べます。
matcher 構文の評価ルール
matcher は、書いた文字列の中身によって解釈が変わります。
書き方 | 解釈 | 例 |
|---|---|---|
| すべてに一致 | すべてのツール |
英数字・ | 完全一致。 |
|
それ以外の文字を含む | JavaScript の正規表現(前後のアンカーなし) |
|
押さえておきたい点は次のとおりです。
- 大文字と小文字は区別されます。
bashと書くとBashツールには一致しません - 正規表現は部分一致です。全体で一致させたいときは
^...$で囲みます - MCP ツールは
mcp__<サーバー名>__<ツール名>という名前です。サーバー全体を対象にするならmcp__memory__.*と書きます(mcp__memoryだけでは何にも一致しません) - UserPromptSubmit、PostToolBatch、Stop、TaskCreated、TaskCompleted、TeammateIdle、WorktreeCreate、WorktreeRemove、MessageDisplay、CwdChanged は matcher に対応していません。書いてもエラーは出ず、そのまま無視されます
if フィールドでツール引数レベルのフィルタ
if を使うと、ツール名に加えて引数の中身で hook を動かすかどうかを決められます。書き方は権限ルールと同じです。
{ "if": "Bash(git push --force*)" }
{ "if": "Edit(*.ts)" }
{ "if": "Bash(rm *)" }書き方のポイントは次のとおりです。
matcherでツール名を絞り、ifで引数を絞るのが基本の組み合わせですifに書けるルールは1つだけです。&&や||でつなげないので、*.tsと*.tsxの両方を対象にしたいときはハンドラーを2つに分けますnpm test && git pushのような複合コマンドは、サブコマンドごとに判定されます。$()の中も対象です。判定できないコマンドは、パターンにかかわらず hook が動きますifが効くのはツール系イベント(PreToolUse / PostToolUse / PostToolUseFailure / PermissionRequest / PermissionDenied)だけです。それ以外のイベントにifを付けると、その hook 自体が動かなくなります- 公式は
ifを「ベストエフォート」と位置付けています。絶対に許可・拒否したいものは permissions の allow / deny ルールで設定してください
5つのハンドラータイプの使い分け
Hook で実行する処理は5種類から選べます。確実さを求めるなら command、外部サービスに送るなら http、判断に文脈の理解が必要なら prompt という分け方が基本です。
タイプ | 中身 | モデルの使用 | 既定のタイムアウト | 向いている用途 |
|---|---|---|---|---|
| シェルコマンドを実行。stdin でイベントの JSON を受け取る | なし | 600秒 | ブロック、整形、テスト、ログ記録など大半の用途 |
| イベントの JSON を指定 URL へ POST | なし | 600秒 | 監査サーバーへの送信、社内システムとの連携 |
| 設定済み MCP サーバーのツールを呼ぶ | なし | 600秒 | 既存の MCP 連携を hook から使う |
| Claude モデルに1回だけ判定させ、 | あり | 30秒 | 「タスクは全部終わったか」など、条件をコードで書きにくい判定 |
| ツールを使えるサブエージェントに検証させる(最大50ツールターン) | あり | 60秒 | テストを実際に走らせて確かめる検証。公式が「実験的」と明記 |
タイムアウトの例外として、UserPromptSubmit と PreModelSwitch / PostModelSwitch では command / http / mcp_tool も30秒、MessageDisplay は10秒です。SessionEnd はすべての hook で合計1.5秒しかありません(hook の timeout を長くすると最大60秒まで延びます)。
まずは command から始めるのが安全です。 prompt と agent はモデルを呼ぶぶん、プランの使用量(API 利用なら API 料金)を消費し、判定結果にも揺れが出ます。必ず止めたいガードには向きません。agent 型は公式が実験的と明記しており、本番で使う場合も command を勧めています。
prompt 型は、ok: false を返したときの動きがイベントで異なります。
- Stop / SubagentStop: 理由を Claude に渡して作業を続けさせます。
"impossible": trueを返すと停止を許可します - PreToolUse: 既定ではツールを拒否してターンを終えます。
continueOnBlock: trueを付けると、理由をツールのエラーとして返して作業を続けさせます(v2.1.210 で既定の動きが変わりました) - PostToolUse: 既定ではターンを終えます。
continueOnBlock: trueで作業継続
なお v2.1.294 で、「〜するコマンドをブロックせよ」のように命令形で書いた prompt / agent 型の hook が、ブロックすべきものを通してしまう不具合が修正されています。古いバージョンで prompt 型のガードを使っている場合は更新してください。
mcp_tool 型は、プロジェクトですでに MCP サーバーを使っているときに便利です。server / tool / input を指定し、input の中では ${tool_input.file_path} のようにイベントの値を差し込めます。
関連記事: Claude Code MCPの使い方
Exit Code と制御出力|いちばん間違えやすいところ
Hook の結果は終了コード(exit code)か stdout の JSON で Claude Code に伝えます。ここを取り違えると、ガードを書いたつもりで何も止まっていない状態になります。
Exit Code の動作
Exit Code | 動作 |
|---|---|
| 成功。stdout が JSON オブジェクト1つだけなら、制御用の JSON として読まれます。JSON 以外のテキストはデバッグログに残るだけです(例外として UserPromptSubmit / UserPromptExpansion / SessionStart / PostModelSwitch では、そのテキストが Claude への情報として追加されます)。exit 0 のときの stderr は Claude には見えません |
| ブロック。stderr の内容が理由として扱われます。届く先はイベントで異なり、PreToolUse なら Claude にフィードバックされ、ブロックできないイベントではユーザーに表示されるだけです。JSON で許可を返していても exit 2 ならブロックが優先されます |
その他( | ブロックしないエラー。処理はそのまま続き、stderr の1行目が通知として表示されるだけです |
PreToolUse で exit 0 を返すのは「許可」ではなく「意見なし」です。その後は通常の権限確認の流れに進みます。
最大の落とし穴: exit 1 では止まらない
一般的なシェルの感覚で exit 1 を「失敗=中止」として使うと、警告が出るだけでコマンドは実行されます。止めたいときは必ず exit 2 を使ってください。 日本語の解説記事にも「exit 1 でキャンセルされる」と書かれたものがあるので注意が必要です。
# ❌ 悪い例: 警告は出るが rm -rf は実行される
if echo "$COMMAND" | grep -q "rm -rf"; then
echo "危険なコマンドです" >&2
exit 1 # ← これでは止まらない
fi
# ✅ 正しい例: 実行がブロックされ、理由が Claude に返る
if echo "$COMMAND" | grep -q "rm -rf"; then
echo "rm -rf は禁止されています" >&2
exit 2
fiガード用の hook が動かないと操作が通ってしまう問題と onFailure
もうひとつ見落とされやすいのが、hook 自体が失敗したときの扱いです。スクリプトのパスが間違っている、jq が入っていない、タイムアウトした、といった場合、既定では「その他の終了コード」と同じ扱いになり、ツールはそのまま実行されます。
v2.1.295 で追加された onFailure: "block" を付けると、hook が起動できない・0と2以外で終了した・HTTP エラー・タイムアウト・出力が不正、のいずれでも操作をブロックします(command と http 型が対象。既定値は "continue")。
{
"type": "command",
"command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/guard-bash.sh",
"onFailure": "block"
}ただし Stop / SubagentStop / TaskCompleted / TeammateIdle と、非同期(async)の hook には効きません。
構造化 JSON 出力(exit 0 時)
細かく制御したいときは、exit 0 で stdout に JSON を返します。どの階層に書くかが重要です。
{
"continue": true,
"stopReason": "continue が false のときにユーザーに表示する理由",
"systemMessage": "ユーザーへの警告メッセージ",
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"permissionDecision": "deny",
"permissionDecisionReason": "本番環境への接続は事前承認が必要です",
"additionalContext": "Claude に渡す補足情報"
}
}JSON の書き方で気をつけたい点は次のとおりです。
permissionDecisionやadditionalContextはhookSpecificOutputの中に書きます(hookEventNameも必須)。トップレベルに置くと、エラーも出ずに無視されます。claude --debugで起動するとHook JSON output had unrecognized keysと出るので、効かないときはここを確認します- トップレベルの
decision: "block"+reasonが使えるのは、UserPromptSubmit / UserPromptExpansion / PostToolUse / PostToolUseFailure / PostToolBatch / Stop / SubagentStop / ConfigChange / PreCompact / TaskCreated です。PreToolUse の旧形式(decision: "approve"/"block")は非推奨です continue: falseは Claude の処理自体を止め、イベント固有の判断より優先されますsuppressOutputは現行リファレンスでは「受け付けるが効果なし」とされていますadditionalContextは1フィールド10,000文字が上限です。命令口調ではなく事実として書くのが公式の推奨です(プロンプトインジェクション対策に誤検知されにくくなります)- 公式は、hook ごとに「exit 2 と stderr」か「exit 0 と JSON」のどちらかに決めて使うことを勧めています
主要4イベントの違いを比較|PreToolUse / PostToolUse / Stop / SubagentStop

出典: Claude Code Docs(Hooks reference)
主要4イベントの違いを1枚で押さえておくと、どのイベントに何を書くか迷わなくなります。操作を止めたいなら PreToolUse、結果を検査して直させたいなら PostToolUse、終わり方を検査したいなら Stop / SubagentStop です。
項目 | PreToolUse | PostToolUse | Stop | SubagentStop |
|---|---|---|---|---|
発火タイミング | ツール実行の直前 | ツールが成功した直後 | Claude が応答を終えるたび | サブエージェントが終了したとき |
matcher の対象 | ツール名 | ツール名 | なし(書いても無視) | エージェントの種類( |
操作を止められるか | 止められる | 止められない(実行済み) | 停止を取り消して作業を続けさせられる | サブエージェントに作業を続けさせられる |
主な入力 |
|
|
|
|
返し方 | exit 2、または |
| exit 2、または | Stop と同じ |
典型的な用途 | 危険コマンドの阻止、機密ファイルの保護、引数の書き換え | 整形、Lint、型チェック、ログ記録 | テスト未通過なら終わらせない、作業ログ | サブエージェントの成果物チェック |
注意点 |
| Bash 経由のファイル変更は |
| 親セッションに情報を返したいなら |
全イベント一覧|ライフサイクル上で何が発火するか(33種類)
2026年10月時点の公式リファレンスでは、hook を掛けられるイベントは33種類です。5月時点から、PreModelSwitch / PostModelSwitch、MessageDisplay、DirectoryAdded などが加わっています。
イベント名 | 発火タイミング | matcher の値 | 止められるか |
|---|---|---|---|
SessionStart | セッションの開始・再開 |
| 不可 |
Setup |
|
| 不可 |
UserPromptSubmit | プロンプト送信直後、処理の前(Claude Code が自分で始めるターンでも発火) | なし | 可 |
UserPromptExpansion | 入力したコマンドがプロンプトに展開されるとき | コマンド名 | 可 |
PreToolUse | ツール実行の直前 | ツール名 | 可 |
PermissionRequest | 権限の確認が必要になったとき | ツール名 |
|
PermissionDenied | auto mode がツール呼び出しを拒否したとき | ツール名 | 不可( |
PostToolUse | ツールが成功した直後 | ツール名 | 不可(フィードバックは可) |
PostToolUseFailure | ツールが失敗した直後 | ツール名 | 不可 |
PostToolBatch | 並列のツール呼び出しがすべて終わり、次のモデル呼び出しの前 | なし | 可 |
Notification | 通知を送るとき |
| 不可 |
MessageDisplay | Claude のテキストを表示するとき | なし | 不可(表示のみ) |
SubagentStart | サブエージェントの生成時 | エージェントの種類 | 不可 |
SubagentStop | サブエージェントの終了時 | エージェントの種類 | 可 |
TaskCreated | TaskCreate でタスクを作ったとき | なし | 可 |
TaskCompleted | タスクが完了扱いになったとき | なし | 可 |
Stop | Claude が応答を終えたとき | なし | 可(作業を続けさせる) |
StopFailure | API エラーでターンが終わったとき |
| 不可 |
TeammateIdle | Agent Teams のチームメイトがアイドルになる前 | なし | 可 |
InstructionsLoaded | CLAUDE.md や |
| 不可 |
ConfigChange | セッション中に設定ファイルが変わったとき |
| 可( |
CwdChanged | 作業ディレクトリが変わったとき | なし | 不可 |
DirectoryAdded |
|
| 不可 |
FileChanged | 監視対象のファイルがディスク上で変わったとき | ファイル名(例: | 不可 |
WorktreeCreate | worktree を作るとき(既定の git の動作を置き換える) | なし | 可(0以外で終了すると作成失敗) |
WorktreeRemove | worktree を削除するとき | なし | 可 |
PreCompact | コンテキスト圧縮の前 |
| 可 |
PostCompact | コンテキスト圧縮の後 |
| 不可 |
PreModelSwitch | モデルの切り替えが適用される前 | 切り替え先のモデル名 | 可 |
PostModelSwitch | モデルが切り替わった後 | 切り替え後のモデル名 | 不可 |
Elicitation | MCP サーバーがユーザーに入力を求めたとき | MCP サーバー名 | 可(辞退) |
ElicitationResult | ユーザーが答えた後、サーバーに返す前 | MCP サーバー名 | 可(辞退) |
SessionEnd | セッションの終了時 |
| 不可 |
環境変数を後続のコマンドに渡す方法: SessionStart / CwdChanged / FileChanged の hook で
$CLAUDE_ENV_FILEが指すファイルに書き込むと、その後の Bash コマンドの実行前に読み込まれます。公式ガイドには direnv と組み合わせる例が載っています。
PreToolUse|ツール実行前に挟むガードレール(最重要)

PreToolUse は、操作を実行前に止められる唯一の主要イベントで、安全策の中心になります。Claude がツール呼び出しの引数を決めた後、ツールが実際に動く前に発火します。
いつ発火するか
- Claude がツール呼び出し(Bash / Edit / Write / Read / Agent / WebFetch / MCP ツールなど)を決めた後
- ツールが実際に動く前
- すべての権限モード(
dontAskを含む)で、権限チェックより前に発火します
matcher の対象
ツール名で絞り込みます。
- 組み込みツール:
Bash/PowerShell/Edit/Write/Read/Glob/Grep/Agent/WebFetch/WebSearch/ExitPlanModeなど - MCP ツール:
mcp__<server>__<tool>形式(例:mcp__github__create_issue)。v2.1.274 からは入力にmcp_serverも入ります
入力スキーマ
stdin には次のような JSON が渡されます。
{
"session_id": "abc123",
"transcript_path": "/path/to/transcript.jsonl",
"cwd": "/current/directory",
"permission_mode": "default",
"hook_event_name": "PreToolUse",
"tool_name": "Bash",
"tool_input": { "command": "npm test" },
"tool_use_id": "toolu_01ABC123..."
}Write / Edit / Read の tool_input.file_path は常に絶対パスです。
制御出力(2通り)
方式A: exit 2 と stderr のメッセージ
#!/bin/bash
INPUT=$(cat)
COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // empty')
if echo "$COMMAND" | grep -qE '(rm -rf|git push --force|drop table)'; then
echo "破壊的なコマンドのためブロックしました: $COMMAND" >&2
exit 2
fi
exit 0方式B: JSON で permissionDecision を返す
{
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"permissionDecision": "deny",
"permissionDecisionReason": "本番環境への接続は事前承認が必要です"
}
}permissionDecision の値は4種類です。
値 | 動き |
|---|---|
| 権限確認のプロンプトを省略する(ただし settings の deny / ask ルールは越えられない) |
| 実行を中止し、理由を Claude に伝える。 |
| ユーザーに確認を求める。理由はユーザーに表示される。auto mode でも必ずプロンプトが出る |
|
|
複数の hook が別々の判断を返した場合は、最も厳しいもの(deny > defer > ask > allow)が採用されます。ただし hook は並列に実行されるので、1つが deny しても他の hook は止まらずに動きます(ログの書き込みなどの副作用は起きます)。
引数を書き換える updatedInput
hookSpecificOutput.updatedInput を返すと、ツールの引数を書き換えられます。注意点は3つです。
- 引数は丸ごと置き換えです。変えない項目も含めて返します
- 権限ルールは書き換え後の引数で判定され、v2.1.300 からは Claude 自身の入力と同じ検証を受けます
- 複数の hook が
updatedInputを返すと、最後に終わったものが採用されます。並列実行で順序は決まらないため、1つのツールにつき書き換える hook は1つにしてください
設定例: rm -rf と git push --force を全プロジェクトでブロック
~/.claude/settings.json:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "\"$HOME\"/.claude/hooks/guard-bash.sh",
"timeout": 10,
"onFailure": "block"
}
]
}
]
}
}~/.claude/hooks/guard-bash.sh:
#!/bin/bash
INPUT=$(cat)
COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // empty')
if echo "$COMMAND" | grep -qE '(^|[;&| ])rm -rf( |$)'; then
echo "rm -rf は禁止されています。対象を確認してから個別に削除してください。" >&2
exit 2
fi
if echo "$COMMAND" | grep -qE 'git push.*--force'; then
echo "force push は禁止です。--force-with-lease か PR を使ってください。" >&2
exit 2
fi
exit 0onFailure: "block" を付けているので、jq が入っていない環境やスクリプトが見つからない場合も、Bash の実行は止まります。
補足: 許可モードとの優先関係
PreToolUse の hook は権限チェックより前に動くため、ユーザーが bypassPermissions モードや --dangerously-skip-permissions を使っていても、hook が deny を返せば必ずブロックされます。
逆に、hook が allow を返しても settings の deny / ask ルールは越えられません。公式の表現を借りると、hooks は制限を厳しくすることはできても、権限ルール以上に緩めることはできません。
ひとつ例外として、2026年秋に追加された mods(Claude Code のプロセス内で動く JS/TS の拡張)が tool.check を処理すると、PreToolUse の hook がブロックした呼び出しを承認できる場合があります。managed settings に書いた PreToolUse hook はこの上書きを受けません。mods を入れる場合は、インストール前に claude plugin validate で中身を確認しておくと安心です。
PostToolUse|実行後の品質ゲート

PostToolUse はツールが成功した直後に発火します。編集したファイルの整形、Lint、型チェックなど、品質チェックの中心になるイベントです。
発火タイミングと入力
- ツールが成功したときに発火します(失敗時は PostToolUseFailure)
- stdin には
tool_inputに加えて、ツールの結果tool_response、所要時間duration_ms(権限確認と PreToolUse の時間は含まない)が入ります - Bash の場合、変更したファイルの情報が
tool_response.bashEditDiffに入ることがあります(v2.1.269〜)
{
"hook_event_name": "PostToolUse",
"tool_name": "Write",
"tool_input": { "file_path": "/path/to/file.ts", "content": "..." },
"tool_response": { "filePath": "/path/to/file.ts", "success": true },
"tool_use_id": "toolu_01ABC123..."
}制御出力とその限界
重要: PostToolUse は実行済みの操作を取り消せません。 できるのは、結果について Claude に伝えて次の行動で直させることです。
出力 | 効果 |
|---|---|
| ツールの結果に理由を添えて Claude に渡す。元の出力も Claude に見える |
| 補足情報を Claude に渡す |
| Claude に見せるツールの出力を差し替える(形式が合わないと組み込みツールでは無視される。実行した処理は戻らない) |
| auto mode の分類器向けのメモ(2,000文字まで) |
Bash でのファイル変更は Edit|Write では拾えない
matcher を Edit|Write にしていても、Claude が sed や echo > など Bash 経由でファイルを書き換えた場合は発火しません。公式ガイドは、Bash|PowerShell も対象にして git status --porcelain で変更を確かめる、Stop で作業ツリーを1回まとめて検査する、FileChanged を使う、といった方法を挙げています。
代表ユースケース
- 型チェック:
*.ts/*.tsxの編集後にtsc --noEmitを走らせ、エラーを Claude に返す - ESLint の自動修正:
eslint --fixを走らせる - Prettier での整形: 編集したファイルをすぐ整形する
- テストの実行: 小さなプロジェクトなら編集のたびに pytest / vitest
- 変更の記録: 監査用に
git diffを保存する - サブエージェントの結果を親に渡す:
Agentツールの PostToolUse で、サブエージェントの結果に補足を付けて親セッションに返す
設定例: TypeScript 編集後の型チェック
if には1ルールしか書けないため、拡張子ごとにハンドラーを分けます。
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"if": "Edit(*.ts)",
"command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/tsc-check.sh",
"timeout": 120
},
{
"type": "command",
"if": "Edit(*.tsx)",
"command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/tsc-check.sh",
"timeout": 120
}
]
}
]
}
}Write で新規作成したファイルも対象にしたい場合は、Write(*.ts) と Write(*.tsx) のハンドラーを同じように追加します。
.claude/hooks/tsc-check.sh:
#!/bin/bash
cd "$CLAUDE_PROJECT_DIR" || exit 0
OUTPUT=$(npx tsc --noEmit 2>&1)
if [ $? -ne 0 ]; then
jq -n --arg reason "$OUTPUT" '{
decision: "block",
reason: ("型エラーがあります。修正してください:\n" + $reason)
}'
fi
exit 0運用のコツは次の3つです。
matcherとifで対象を必ず絞ります。すべてのツール呼び出しで型チェックを走らせると、そのたびに待ち時間が積み重なります- 大きなプロジェクトでは変更したファイルだけを検査する、重いテストは Stop にまとめる、といった工夫をします
- 結果を待つ必要がない処理は
async: trueで裏で動かせます。この場合は Claude の行動を止められず、結果は次のターンで Claude に渡ります
Stop|Claude の応答終了時に割り込む

Stop は Claude が応答を終えたときに発火します。テストが通っていなければ作業を続けさせる、最後に作業ログを残す、といった「終わる前の確認」に使います。
発火タイミングの注意
- タスクの完了時だけでなく、応答を終えるたびに毎回発火します
- ユーザーが中断した場合は発火しません。API エラーで終わった場合は StopFailure が発火します
入力と制御出力
- 入力:
stop_hook_active(すでに Stop hook によって続けさせられている最中ならtrue)、last_assistant_message(最後の応答)、background_tasks、session_crons - 最後の応答の内容を見たいときは、transcript ファイルではなく
last_assistant_messageを使います。Stop の時点では transcript に最後のメッセージが書き込まれている保証がないためです - exit 2、または JSON で
decision: "block"+reasonを返すと、Claude は止まらずに作業を続け、理由が次の指示として渡ります hookSpecificOutput.additionalContextを返すと、エラー表示を出さずに会話を続けさせられます
最重要の注意点: 無限ループ防止と8回上限
Stop hook が条件を見ずに毎回 block を返すと、次のように終わらなくなります。
Claude の応答終了 → Stop hook → block → Claude が作業を続ける
→ また応答終了 → Stop hook → block → …Claude Code には歯止めがあり、Stop hook がツール呼び出しを挟まずに8回続けて作業を続けさせると、その次の block は無視されてターンが終わります。ツール呼び出しがあれば回数はリセットされ、上限は環境変数 CLAUDE_CODE_STOP_HOOK_BLOCK_CAP で変えられます。ただし上限に頼るのではなく、スクリプト側で stop_hook_active を確認するのが基本です。
#!/bin/bash
INPUT=$(cat)
# 無限ループ対策: Stop hook による継続中なら、停止を許可する
if [ "$(echo "$INPUT" | jq -r '.stop_hook_active')" = "true" ]; then
exit 0
fi
# 本来の判定(例: テストが通らなければ終わらせない)
cd "$CLAUDE_PROJECT_DIR" || exit 0
if ! npm test --silent > /dev/null 2>&1; then
echo "テストが失敗しています。テストが通るまで修正を続けてください。" >&2
exit 2
fi
exit 0この書き方だと、テストが通らないとき Claude に1回だけ作業を続けさせ、次の終了時は止まることを許します。何度か再挑戦させたい場合は、回数を一時ファイルに記録して判定します。
prompt 型で「全部終わったか」を判定する
条件をコードで書きにくいときは、prompt 型で判定させる方法もあります。
{
"hooks": {
"Stop": [
{
"hooks": [
{
"type": "prompt",
"prompt": "Claude の最後の応答を確認し、ユーザーが依頼した作業がすべて終わっているかを判定してください。終わっていれば ok を true、残っていれば false にして、残っている作業を reason に書いてください。"
}
]
}
]
}
}v2.1.294 で Stop / SubagentStop での prompt 型の判定が改善され、早すぎる停止が起きにくくなっています。ただし判定のたびにモデルを呼ぶため、使用量を消費する点は意識しておいてください。
代表ユースケース
- 完了ゲート: テストが通らなければ終わらせない
- 作業ログ: 応答ごとの記録を残す
- 未コミットの変更の警告:
git statusで残りを見つけて伝える - Bash 経由の変更の検査: 作業ツリー全体を1回だけまとめて Lint する
SubagentStop|サブエージェント終了時のフック

出典: Claude Code Docs(Subagents)
SubagentStop は、Agent ツールで起動したサブエージェントが終了したときに発火します。Stop がメインの Claude の終了を扱うのに対し、SubagentStop はサブエージェントごとの終了を扱います。
matcher
エージェントの種類(agent_type)で絞り込みます。
- 組み込み:
general-purpose/Explore/Plan - カスタムエージェント:
.claude/agents/などで定義したエージェント名 - プラグインのエージェントは、
^my-plugin:reviewer$のようにアンカーを付けて指定するのが推奨です
入力
フィールド | 内容 |
|---|---|
| Stop と同じ。ループ防止に使う |
| どのサブエージェントか |
| サブエージェント自身の会話記録( |
| メインセッションの会話記録 |
| サブエージェントの最後の応答 |
制御出力
- Stop と同じく、exit 2 または
decision: "block"+reasonでサブエージェントに作業を続けさせられます。理由が次の指示になります stop_hook_activeのチェックも同じく必要です- 親セッションに情報を渡したいときは、SubagentStop ではなく
Agentツールの PostToolUse を使うのが公式の案内です。SubagentStop の出力はサブエージェント側に返ります
関連するイベントとして SubagentStart があります。生成を止めることはできませんが、additionalContext でサブエージェントの会話の冒頭に情報を入れられます。
代表ユースケース
- 成果物の検証: レビュー用エージェントの出力に必須の項目がそろっているか確認し、足りなければ続けさせる
- 後片付け: サブエージェント用の一時ファイルを削除する
- 実行記録: どのエージェントがいつ終わったかを記録する
設定例: カスタムエージェントの出力を検証
{
"hooks": {
"SubagentStop": [
{
"matcher": "^code-reviewer$",
"hooks": [
{
"type": "command",
"command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/validate-review.sh",
"timeout": 30
}
]
}
]
}
}.claude/hooks/validate-review.sh:
#!/bin/bash
INPUT=$(cat)
[ "$(echo "$INPUT" | jq -r '.stop_hook_active')" = "true" ] && exit 0
MESSAGE=$(echo "$INPUT" | jq -r '.last_assistant_message // empty')
if ! echo "$MESSAGE" | grep -q "## 指摘事項"; then
jq -n '{decision: "block", reason: "出力に「## 指摘事項」の見出しがありません。指摘を一覧にしてから終了してください。"}'
fi
exit 0関連記事: Claude Code サブエージェントの使い方
Notification|デスクトップ通知・外部サービス連携
Notification は、Claude Code が通知を出すとき(権限確認待ち、入力待ちなど)に発火します。操作は止められませんが、長い作業の途中で席を外していても、確認が必要になった時点で気付けるようになります。
matcher の値
現行の公式リファレンスでは12種類あります。よく使うのは次のとおりです。
matcher の値 | 発火するとき |
|---|---|
| ツールの承認待ちで入力を待っているとき |
| 作業が終わり、次のプロンプトを待っているとき |
| 認証が完了したとき |
| MCP の入力要求に関する通知 |
| エージェントが入力を必要とした・完了したとき |
| 使用量の上限に関する自動再開の通知(v2.1.234〜) |
設定例: macOS の通知と Slack への送信
{
"hooks": {
"Notification": [
{
"matcher": "idle_prompt",
"hooks": [
{
"type": "command",
"command": "osascript -e 'display notification \"Claude Code の作業が終わりました\" with title \"Claude Code\"'"
}
]
},
{
"matcher": "permission_prompt",
"hooks": [
{
"type": "command",
"command": "curl -s -X POST -H 'Content-Type: application/json' -d '{\"text\":\"Claude Code: ツールの承認が必要です\"}' \"$SLACK_WEBHOOK_URL\"",
"async": true
}
]
}
]
}
}Linux なら notify-send、Windows なら PowerShell の通知で同じことができます(公式ガイドに3 OS分の例があります)。http 型はイベントの JSON をそのまま POST するため、Slack の Incoming Webhook のように決まった形式を求める宛先には、この例のように command 型から curl で送るほうが確実です。
注意点は2つです。
- Notification は止められません。exit 2 を返してもユーザーに表示されるだけです
- 通知処理で待たせたくない場合は
async: trueを付けます
補足イベント|知っておくと差がつく4つ
UserPromptSubmit|ユーザー入力直後
プロンプトを送信した直後、Claude が処理を始める前に発火します。情報を追加したり、入力内容を検査してブロックしたりできます。
v2.1.299 から、スキルやカスタムスラッシュコマンドが展開される前に実行されるようになりました。展開後の内容を検査したい場合は UserPromptExpansion を使います。既定のタイムアウトは30秒です。
代表的な使い方は次の2つです。
- ブランチ名や
git statusを毎回渡す - 機密情報らしき文字列を含むプロンプトを止める
#!/bin/bash
BRANCH=$(git branch --show-current 2>/dev/null || echo "no-git")
STATUS=$(git status --short 2>/dev/null)
jq -n --arg branch "$BRANCH" --arg status "$STATUS" '{
hookSpecificOutput: {
hookEventName: "UserPromptSubmit",
additionalContext: ("現在のブランチ: " + $branch + "\n未コミットの変更:\n" + $status)
}
}'SessionStart|セッション開始・再開時
matcher で発火の条件を絞れます。
startup: 新しいセッションresume:--resume/--continue//resumeclear:/clearの後compact: コンテキスト圧縮の後fork: セッションを分岐したとき
代表的な使い方: プロジェクトの前提情報を渡す、圧縮で失われがちなルールを compact のときに渡し直す(公式ガイドの例)、$CLAUDE_ENV_FILE で環境変数を設定する。
PostToolUseFailure|ツール失敗時
ツールが失敗したときに発火します。失敗の記録、外部へのアラート、additionalContext で別のやり方を提案する、といった用途に使えます。実行は失敗しているので止める必要はなく、ブロックもできません。
SessionEnd|セッション終了時
終了の理由(clear / resume / logout / prompt_input_exit / other)で絞り込めます。一時ファイルの削除やログの保存に使いますが、持ち時間はすべての hook 合わせて1.5秒です。重い処理は入れないか、timeout を明示して延ばします。
Hooks / Permissions / CLAUDE.md の使い分け
「何を Hooks で書き、何を Permissions で設定し、何を CLAUDE.md に書くか」は迷いやすいところです。単純な許可・拒否は Permissions、必ず起こしたい処理は Hooks、望ましい振る舞いの指示は CLAUDE.md と考えると整理しやすくなります。
手段 | 性格 | 向いている用途 | 向いていない用途 |
|---|---|---|---|
Permissions | 宣言的なルール | ツール名やパスでの単純な許可・拒否 | 条件分岐、後処理、外部連携 |
CLAUDE.md | Claude が読む指示 | コーディング規約、プロジェクトの前提、好ましい進め方 | 毎回必ず起きてほしい処理 |
Hooks | イベントで必ず動く処理 | Permissions で書けない条件のブロック、整形、検査、記録、通知 | 単純な許可・拒否(Permissions で足りる) |
Skills | 必要なときに読み込む手順書 | 特定作業のやり方を Claude に教える | 強制したいルール |
mods | Claude Code の中で動く JS/TS の拡張 | 権限判定やツールの挙動を細かく組み込む | 手軽な自動化(Hooks で足りる) |
おすすめの組み合わせ: 基本的な拒否は Permissions の deny ルール、Permissions で書けない条件や後処理・通知は Hooks、チームの書き方や進め方は CLAUDE.md、作業手順のまとまりは Skills に置きます。
関連記事: Claude Code の Skills と Hooks の違い / Claude Code スラッシュコマンドの使い方
バージョン別機能追加履歴

出典: GitHub(anthropics/claude-code)
Hooks はリリースのたびに細かく変わっています。2026年夏〜秋(v2.1.210〜v2.1.301)の公式 changelog から、使い方に関わる主な変更をまとめました。
バージョン | 主な変更 |
|---|---|
v2.1.301 | プラグインやスキルの hook が |
v2.1.300 | PreToolUse / PermissionRequest の |
v2.1.299 | UserPromptSubmit がスキル・カスタムスラッシュコマンドの展開前に実行されるよう変更 |
v2.1.296 |
|
v2.1.295 |
|
v2.1.294 | 命令形で書いた prompt / agent 型 hook がブロックすべきものを通してしまう不具合を修正。Stop / SubagentStop の prompt 型の判定を改善 |
v2.1.292 | hook の出力に含まれる |
v2.1.287 | ターミナル版で mods が既定で有効に |
v2.1.274 | PreToolUse の入力に |
v2.1.269 | PostToolUse の Bash の結果に |
v2.1.257 | 共通の入力に |
v2.1.236 | PostToolUse で |
v2.1.218 | プロジェクトのサブエージェント frontmatter の hooks は、フォルダの信頼を承認した後だけ実行 |
v2.1.214 |
|
v2.1.210 | prompt 型の PreToolUse で |
注意: この表は公式 changelog で確認できた範囲です。それより前の変更や各機能の最初のバージョンは載せていません。手元のバージョンは
claude --versionで確認してください。
実務で即使える 10 のレシピ

公式ガイドの例をもとに、すぐ使える形にまとめました。スクリプトは chmod +x で実行権限を付けてから使ってください。
1. rm -rf / force push のブロック
最初に入れておきたい hook です。~/.claude/settings.json の PreToolUse に matcher Bash で guard-bash.sh を登録し、onFailure: "block" を付けておけば全プロジェクトで効きます。スクリプトは tool_input.command を jq で取り出し、rm -rf や git push --force に一致したら stderr に理由を書いて exit 2 で終わる形にします。正規表現で検出する方式なので、変数展開や別名を使ったコマンドまでは防げません。permissions の deny ルールと併用してください。
2. .env / 秘密鍵の読み取り・編集防止
if は1ルールずつなので、守りたいファイルの種類ごとにハンドラーを並べます。
{
"hooks": {
"PreToolUse": [
{
"matcher": "Read|Edit|Write",
"hooks": [
{ "type": "command", "if": "Read(*.env)", "command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/deny-secret.sh" },
{ "type": "command", "if": "Edit(*.env)", "command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/deny-secret.sh" },
{ "type": "command", "if": "Write(*.env)", "command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/deny-secret.sh" },
{ "type": "command", "if": "Read(*.pem)", "command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/deny-secret.sh" }
]
}
]
}
}.claude/hooks/deny-secret.sh:
#!/bin/bash
FILE=$(jq -r '.tool_input.file_path // empty')
echo "機密ファイル ($FILE) へのアクセスはブロックされています。" >&2
exit 2公式ガイドには、.env・package-lock.json・.git/ を守るスクリプトの例も載っています。ファイルの読み取りを確実に禁止したいなら、permissions の deny ルールにも同じパターンを入れておくのが安全です。
3. TypeScript 型チェック自動実行
PostToolUse に tsc-check.sh(tsc --noEmit を実行し、エラーがあれば decision: "block" で Claude に返すスクリプト)を登録し、if で Edit(*.ts) と Edit(*.tsx) を別々のハンドラーとして指定します。プロジェクト全体の型チェックが重い場合は、編集のたびではなく Stop で1回だけ実行する形に変えると待ち時間を抑えられます。
4. ESLint 自動修正
#!/bin/bash
FILE=$(jq -r '.tool_input.file_path // empty')
case "$FILE" in
*.ts|*.tsx|*.js|*.jsx) npx eslint --fix "$FILE" >/dev/null 2>&1 ;;
esac
exit 05. Prettier 自動整形
公式ガイドの例です。PostToolUse の matcher を Edit|Write にして、次のコマンドを command に書きます。
{
"type": "command",
"command": "jq -r '.tool_input.file_path' | xargs npx prettier --write"
}6. ブランチ名・git status の自動注入(UserPromptSubmit)
UserPromptSubmit に、git branch --show-current と git status --short の結果を hookSpecificOutput.additionalContext として返すスクリプトを登録します。毎回、今どのブランチで何が変わっているかが Claude に伝わるので、ブランチを取り違えた作業を減らせます。v2.1.299 以降はスキルやスラッシュコマンドの展開前に実行されるため、/ で始まるコマンドを送ったときも同じように情報が付きます。
7. 作業ログの自動記録(Stop)
#!/bin/bash
INPUT=$(cat)
[ "$(echo "$INPUT" | jq -r '.stop_hook_active')" = "true" ] && exit 0
echo "$INPUT" | jq -c '{time: (now | todate), session_id, last_message: .last_assistant_message}' \
>> "$CLAUDE_PROJECT_DIR/.claude/work-log.jsonl"
exit 08. 未コミット変更の警告(Stop)
#!/bin/bash
INPUT=$(cat)
[ "$(echo "$INPUT" | jq -r '.stop_hook_active')" = "true" ] && exit 0
cd "$CLAUDE_PROJECT_DIR" || exit 0
if [ -n "$(git status --porcelain)" ]; then
echo "未コミットの変更があります。コミットするか、残す理由をユーザーに説明してください。" >&2
exit 2
fi
exit 09. パッケージ追加の事前確認(PreToolUse)
依存関係を勝手に増やされないよう、npm install / pnpm add / yarn add には ask を返してユーザーに確認させます。
#!/bin/bash
COMMAND=$(jq -r '.tool_input.command // empty')
if echo "$COMMAND" | grep -qE '(^|[;&| ])(npm install|npm i|pnpm add|yarn add) '; then
jq -n '{
hookSpecificOutput: {
hookEventName: "PreToolUse",
permissionDecision: "ask",
permissionDecisionReason: "パッケージの追加は確認が必要です"
}
}'
fi
exit 010. テストを裏で回して結果を次のターンに渡す(async)
async: true を付けた command 型は、Claude を待たせずに裏で動きます。結果の additionalContext は次のターンで Claude に渡ります。
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/run-tests-async.sh",
"async": true
}
]
}
]
}
}非同期の hook はブロックも判断もできません。テストが落ちたときにすぐ Claude を起こしたいなら、asyncRewake を使って exit 2 で知らせる方法があります。-p 実行では、プロセスの終了時にまだ動いている非同期 hook は止められます。
よくある5つの落とし穴と回避策
1. exit 1 でブロックしたつもりになっている
症状: ガードを書いたのに危険なコマンドが実行される。警告だけ出て止まらない。
原因: exit 0 と 2 以外はブロックしないエラーとして扱われ、処理が続く。
対処: 止めたいときは必ず exit 2。hook の起動失敗でも止めたいなら onFailure: "block" を付ける。
2. JSON を返しているのに効かない
症状: permissionDecision: "deny" を返しているのに止まらない。additionalContext が Claude に届いていない。
原因: よくあるのは次の3つです。
permissionDecisionやadditionalContextをhookSpecificOutputの外(トップレベル)に書いている.bashrcなどのシェルの設定ファイルにechoがあり、その出力が JSON の前に混ざって JSON として読めなくなっている(Git Bash やBASH_ENVを設定している環境で起きやすい)- exit 0 以外で終了している(JSON は exit 0 のときだけ読まれる)
対処: claude --debug で起動し、Hook JSON output had unrecognized keys などのメッセージを確認する。stdout には JSON 以外を出さない。
3. パスやコマンドが見つからずに動いていない
症状: hook が動いているはずなのに何も起きない。チームの他の人の環境でだけ動かない。
原因: 相対パスでスクリプトを指定している、jq が入っていない、実行権限がない、など。
対処: スクリプトは "$CLAUDE_PROJECT_DIR"/.claude/hooks/... のように絶対パスで指定する(シェル形式ではダブルクォートで囲む。args を使う exec 形式ならクォートは不要)。ガード用の hook には onFailure: "block" を付け、動かなかったときに操作がそのまま通らないようにする。/hooks で登録内容を確認する。
4. matcher や if を書いたのに無視される
症状: Stop に matcher を書いたのに全部で動く。SessionStart に if を付けたら動かなくなった。
原因: matcher に対応していないイベント(Stop、UserPromptSubmit など)では matcher が無視され、ツール系以外のイベントに if を付けるとその hook が動かなくなる。matcher の大文字・小文字の違いも原因になる。
対処: matcher と if が使えるイベントを確認する。bash ではなく Bash のように正確な名前で書く。
5. 処理が重くて作業全体が遅くなる
症状: ファイルを1つ直すたびに待たされ、作業が進まない。
原因: PostToolUse で、すべての編集に対して重い型チェックやテストを走らせている。prompt 型の hook が多く、毎回モデルの判定を待っている。
対処は次のとおりです。
matcherとifで対象を絞る- 変更したファイルだけを検査する
- 重い検査は Stop にまとめるか、
async: trueで裏で動かす timeoutを明示し、待ち時間の上限を決めておく
企業導入時のセキュリティ統制

チームや組織で Claude Code を使う場合、個人任せの hook 設定では統制が効きません。hooks は「設定ファイルで配られる実行コード」だと考えて、誰が書いたものが、どの環境で動くのかを管理します。
hooks はあなたの権限で何でも実行できる
公式ドキュメントは、command 型の hook はユーザーの権限すべてでシェルコマンドを実行し、アカウントが触れるファイルはすべて変更・削除・参照できると警告しています。追加する前に中身を確認し、テストしてから使うことが前提です。
他人のリポジトリと -p 実行の注意
- 対話モードでは、フォルダの信頼を承認するまで、
~/.claude/settings.jsonを含むすべての設定ファイルの hooks は実行されません claude -pや Agent SDK では信頼の確認画面が出ず、フォルダは信頼済みとして扱われます。つまり、他人が作ったリポジトリにclaude -pをかけると、そのリポジトリの.claude/settings.jsonに書かれた hooks がそのまま動きます- 自分で書いていないリポジトリに
-pを使う前に.claude/の中身を確認する、--bareで起動する、--settings '{"disableAllHooks": true}'で hooks を止める、のいずれかで対策します
実際に、セキュリティ企業 Check Point Research は2026年2月、「.claude/settings.json の hooks が、新しいディレクトリで起動したときに確認なしで実行されてしまう」問題(CVSS 8.7)を公表しています(Check Point Research)。この問題は v1.0.87 で修正済みで、公表前に対応が終わっていましたが、リポジトリを開くだけで hooks が動きうることを示した例です。
管理者向けの制御
設定 | 動作 |
|---|---|
| ユーザー・プロジェクト・ローカル・プラグインの hooks を止め、管理者が配った hooks だけを動かす。 |
| 管理者が強制的に有効にしたプラグインの hooks は、 |
| hooks を止める。ただしユーザー・プロジェクト・ローカルの設定で指定しても、managed settings の hooks は止まらない。設定の優先順位が適用されるため、プロジェクトの |
| ガード用の hook が失敗したときに操作を止める |
| HTTP hook の送信先 URL と、ヘッダーに差し込める環境変数を制限する |
managed settings に置いた PreToolUse hook は、mods による承認でも上書きされません。組織として必ず止めたいものは managed settings に置くのが確実です。
関連記事: Claude Code のチーム導入ガイド
mods と settings の hooks の違い
mods は、Claude Code のプロセスの中で動く JS/TS のイベントハンドラーで、v2.1.287 からターミナル版で既定で有効です。settings に書く hooks と比べて、次の点に注意します。
- mods はユーザーの権限で動き、サンドボックスの外で実行されます
- mod が PreToolUse の hook でブロックされた呼び出しや、ask ルールの対象を承認できる場合があります
- インストール前に
claude plugin validateで、含まれる hooks や呼び出しを確認できます disableAllHooksを設定すると、利用者が入れた mods も止まります(組み込みの mods は止まりません)
mods は新しい機能で、細かい仕様が変わる可能性があります。使う場合は公式の mods の概要 を確認してください。
HTTP hook の環境変数補間
監査サーバーにツールの呼び出しを送る場合、HTTP hook のヘッダーで $VAR / ${VAR} 形式の環境変数を使えます。ただし、allowedEnvVars に書いた変数だけが展開され、それ以外は空になります。認証トークンが意図せず送られるのを防ぐための仕組みです。
{
"type": "http",
"url": "https://audit.example.com/hooks",
"headers": {
"Authorization": "Bearer ${AUDIT_TOKEN}"
},
"allowedEnvVars": ["AUDIT_TOKEN"]
}スクリプトを書くときの注意
公式のベストプラクティスから、特に大事なものを挙げます。
- 入力を検証する: stdin の JSON は
jqなどで値として取り出し、シェルの文字列にそのまま埋め込まない。変数は"$VAR"とクォートする - パスを確かめる:
..を含むパスで想定外の場所を触られないようにする - 機密ファイルを避ける:
.env、.git/、鍵ファイルを hook の処理対象にしない additionalContextに命令を書かない: 事実として書く。外部から取ってきた文字列をそのまま流し込まない- 裏で動かすプロセスの出力を切り離す: hook の中でバックグラウンドのプロセスを起動するなら、判断の出力を先に済ませ、そのプロセスの stdout / stderr はリダイレクトしておく
社内の業務システムとつなぐとき
Hooks で監査ログを社内サーバーに送ったり、MCP 経由で社内のチケット管理につないだりし始めると、個人の設定ファイルの範囲では管理しきれなくなります。試作した仕組みを社内の実データや既存システムにつないで業務で使う段階では、権限の設計、ログの保管、障害時の対応、保守を誰が引き受けるかを決めておく必要があります。その進め方はAIで作ったアプリを開発会社へ引き継ぐにはで整理しています。
関連記事: Claude Codeのセキュリティと安全な使い方 / Claude Code の auto mode とプロンプトインジェクション対策
Hooks 導入ロードマップ|どこから始めるか
いきなり多くのイベントに hook を入れると、どれが原因で動きがおかしいのか分からなくなります。次の順番で少しずつ増やすのが安全です。
Step 1: 安全ネット(初日)
- PreToolUse で
rm -rf/git push --force/.envの編集を止める exit 2とonFailure: "block"の使い方に慣れる/hooksで登録内容を確認する
Step 2: 情報の追加(1週間以内)
- UserPromptSubmit でブランチ名と
git statusを渡す - SessionStart の
compactで、圧縮後に必要なルールを渡し直す
Step 3: 品質チェック(2週間以内)
- PostToolUse で Prettier・ESLint・型チェックを走らせる
matcherとifで対象を絞り、待ち時間が気にならない範囲に収める
Step 4: 完了チェック(必要に応じて)
- Stop でテスト未通過・未コミットの変更を検知する
stop_hook_activeの確認を必ず入れる
Step 5: チームへの展開
.claude/settings.jsonをコミットしてチーム共通の hooks を配る- 組織として統制が必要なら、managed settings と
allowManagedHooksOnlyを検討する -pや CI で使うリポジトリの.claude/を確認する運用を決める- Notification で Slack に通知し、長い作業の確認待ちを見逃さないようにする
Claude Code Hooks がおすすめの人・そうでない人
こんな人におすすめ
- Claude に任せた作業での事故が心配な開発者: 危険な操作を設定で確実に止めたい
- チームで Claude Code を使う技術リーダー: メンバー全員に同じ品質チェックと安全策を効かせたい
- 既存の Lint・型チェック・テストを AI での開発に組み込みたい人: PostToolUse でそのままつなげられる
- 監査ログが必要な企業: ツールの呼び出しや設定変更を外部に記録したい
- 長い作業を任せることが多い人: 確認待ちを通知で受け取り、完了の条件を Stop で決めておきたい
おすすめしないケース
- Claude Code を使い始めたばかりの段階: まず通常の使い方と permissions に慣れてから足すほうが、問題の切り分けがしやすい
- 小さな個人スクリプトを書くだけの用途: 設定の手間に見合う効果が出にくい
- シェルスクリプトに慣れていない人: JSON の扱いや終了コードでつまずきやすい。まずは公式ガイドの例をそのまま使うところから始める
- 確実に止めたいガードを
prompt/agent型だけで作りたい場合: 判定に揺れがあり、agent 型は公式が実験的としている。command 型か permissions と組み合わせる
よくある質問(FAQ)
Q1. Hooks の利用に追加料金はかかりますか?
Hooks の機能自体に追加料金はありません。ただし Claude Code を使うには Pro(月払い $20)以上の有料プランか API の利用が必要で(2026年10月時点の公式料金ページ)、prompt 型と agent 型の hook はモデルを呼ぶため、そのぶんプランの使用量や API 料金を消費します。command / http / mcp_tool 型はモデルを呼びません。
Q2. 個別の hook だけを一時的に無効にできますか?
個別の hook を無効にするスイッチはありません。止めたい hook のエントリを設定ファイルから削除するか、すべてを止めるなら disableAllHooks: true を使います。ただし managed settings の hooks は、ユーザーやプロジェクトの設定では止まりません。
Q3. Windows でも使えますか?
使えます。command 型で "shell": "powershell" を指定すると PowerShell で実行され、公式ガイドには Windows 向けの通知の例もあります。Bash 向けに書いたスクリプトをそのまま使う場合は、Git Bash などの環境で、シェルの設定ファイルの出力が JSON に混ざらないかを確認してください。
Q4. Hook の中から Claude に追加の指示を出したいのですが、どう書けばいいですか?
additionalContext を使います。命令口調で書くより「テストが3件失敗している」「このファイルは自動生成されている」のように事実として書くほうが、公式の推奨に沿っており、プロンプトインジェクション対策に引っかかりにくくなります。1フィールドの上限は10,000文字で、超えた分はファイルに保存され、2,000文字のプレビューだけが渡ります。
Q5. 複数の hook の実行順は決められますか?
決められません。同じイベントに一致した hook は並列で実行されます。「Aで書き換えてからBで検査する」のような順番に頼る設計は成り立たないので、順番が必要な処理は1つのスクリプトにまとめてください。
Q6. Hook から / コマンドやツールを直接実行できますか?
できません。command 型の hook が Claude に影響を与えられるのは stdout・stderr・終了コードだけです(http 型はレスポンスの本文)。別の作業をしてほしいときは、additionalContext や Stop の reason で Claude に伝え、Claude 自身に実行させます。MCP サーバーのツールを呼ぶだけなら mcp_tool 型が使えます。
Q7. MCP ツールに対する hook は書けますか?
書けます。matcher に mcp__<server>__<tool> の形で指定し、サーバー全体なら mcp__github__.* のように書きます。プラグインに入っている MCP サーバーは mcp__plugin_<プラグイン名>_<サーバー名>__<ツール名> という名前になる点に注意してください。
関連記事: Claude Code MCP連携ガイド
Q8. 非同期(async)の hook ではどこまでできますか?
async: true を付けた command 型の hook は裏で動き、Claude を待たせません。その代わり、ブロックや許可の判断は一切できず、additionalContext などの結果は次のターンで Claude に渡ります。Claude が入力待ちの間は、次の操作があるまで届きません。テストの失敗などですぐ Claude を動かしたい場合は asyncRewake を使い、exit 2 で知らせます。
Q9. agent タイプのハンドラーは本番で使えますか?
2026年10月時点では、公式ドキュメントが agent 型を「実験的」としており、本番では command 型を勧めています。使う場合は検証用途に限定し、ブロックしたい処理は command 型や permissions で別に確実に止まるようにしてください。
Q10. GitHub Actions などの CI 環境でも Hooks は動きますか?
動きます。CI で claude -p を実行する場合も、リポジトリの .claude/settings.json の hooks が読み込まれます。注意したいのは、-p ではフォルダの信頼確認が出ないため、外部からのプルリクエストなどで書き換えられた hooks もそのまま動く点です。外部の変更を扱うワークフローでは --settings '{"disableAllHooks": true}' を付けるか、hooks の変更を必ずレビューする運用にしてください。
まとめ|Hooks は「LLM 任せにしない」ための基盤
Claude Code Hooks は、Claude Code に必ず動く安全策と自動化を組み込む仕組みです。Claude が正しく判断してくれることを期待するのではなく、「この操作は実行させない」「編集したら必ず整形する」を設定ファイルで決められます。
- 2026年10月時点(v2.1.301)で、33種類のイベントと5タイプのハンドラーが使える
- 主要4イベントは、止めるなら PreToolUse、結果を検査するなら PostToolUse、終わり方を確かめるなら Stop / SubagentStop
- 止めたいときは必ず
exit 2。exit 1では止まらない。ガード用の hook にはonFailure: "block"を付ける - JSON の
permissionDecisionやadditionalContextはhookSpecificOutputの中に書く - Stop / SubagentStop では
stop_hook_activeを必ず確認する - hooks は制限を厳しくはできても、権限ルール以上に緩めることはできない
-pや SDK では他人のリポジトリの hooks も確認なしで動くため、チームでは managed settings と運用ルールで管理するagent型は実験的。確実に止めたいものは command 型と permissions で
まずは「rm -rf のブロック」と「UserPromptSubmit でのブランチ情報の追加」の2つから始め、慣れてきたら品質チェックと完了チェックを足していくのがおすすめです。
合わせて読みたい関連記事
- Claude Code とは|料金・使い方・特徴を徹底解説
- Claude Code 使い方 完全ガイド
- Claude Code の Skills と Hooks の違い
- Claude Code サブエージェントの使い方
- Claude Codeのセキュリティと安全な使い方
- Claude Code MCPの使い方
- Claude Code GitHub Actionsの使い方
- Claude Code料金(日本円換算)
公式リソース
このツールを自社のシステムや業務にどう組み込むか、ご提案します
この記事の著者

AI革命
編集部
AI革命株式会社の編集部です。最新のAI技術動向から実践的な導入事例まで、企業のデジタル変革に役立つ情報をお届けしています。豊富な経験と専門知識を活かし、読者の皆様にとって価値のあるコンテンツを制作しています。
最新記事

Grok 4.5とは?API料金$2/$6・V9基盤・「Opus級性能」の実態とGrok 4.6/4.7との違い【2026年10月】
2026/07/09

OpenAI Daybreakとは?GPT-5.6-Cyber・Blue/Red・Codex Security・Patch the Planetを解説【2026年10月最新】
2026/05/15

Microsoft 365 Copilot エージェントとは?Word/Excel/PowerPointの自律実行機能・料金・制限を解説
2026/04/29

OpenAI Codex Labs とは|Accenture・PwC・Cognizant・CGI・Infosys・TCS・Capgemini 7大コンサル提携エンタープライズ導入ガイド【2026年10月】
2026/04/27

DeepSeek DeepInferとは?できること・動作環境・公開範囲・自社で使うべきかを整理【2026年10月】
2026/10/20

Windsurf 2.0 とは?Devin Desktop統合・Agent Command Center・料金・使い方を徹底解説【2026年10月最新】
2026/04/20


