Claude Codeの仕組み講座(講師のためのエンジニア水準)

区分3 拡張の仕組み★★★

No.10 フック(イベントと受け渡しの中身)

フックは「決まった瞬間に、AIの判断と関係なく必ず動く仕掛け」です。受講生の「CLAUDE.md に書いたのに守らない」への、いちばん確かな答えになります。イベント・渡される情報・返し方まで説明できるように、この単元はしっかりやります。数字は2026年10月時点の公式ドキュメントのものです。
目次
  1. 第0章 前提知識
  2. 第1章 イベントの種類
  3. 第2章 どこに、どう書くか
  4. 第3章 フックに渡る情報と、返し方
  5. 確認問題

第0章 前提知識

① フック :決まった瞬間に必ず動く仕掛け

Claude Code は、仕事の途中にいくつもの「節目」を通ります。道具を使う前、道具を使った後、返事を終えた時、などです。この節目を、イベントと呼びます。

フックは、イベントが起きた時に、あなたが指定した命令を自動で動かす仕掛けです。動かすのはAIではなく、Claude Code 本体です。

事務所でいえば、入口の「入退室のたびに鳴るチャイム」です。誰がどんな気分で通っても、通れば必ず鳴ります。スキル(No.09)は新人が判断して読むマニュアルですが、フックは新人の判断を待ちません。

② 言葉の整理

この単元では、これらが何度も出てきます。

第1章 イベントの種類

公式には、30を超えるイベントがあります。「いつ動くか」と「止められるか」の2点で、よく使う物を押さえます。

① よく使うイベント 必ず覚える

イベント 動く瞬間 止められるか
SessionStart 会話が始まった・再開した時 止められない
UserPromptSubmit あなたが依頼を送った直後、AIが読む前 止められる
PreToolUse 道具を使う直前 止められる
PermissionRequest 許可の確認が出る時 確認に答えられる
PostToolUse 道具を使い終えた直後(成功時) 止められない(結果を知らせることはできる)
Notification Claude Code が通知を出す時 止められない
Stop Claude が返事を終えようとした時 止められる(続けさせる)
SubagentStop サブエージェント(No.12)が終わる時 止められる
PreCompact 会話を要約する直前 止められる
SessionEnd 会話が終わる時 止められない

Pre は「前」、Post は「後」、ToolUse は「道具を使うこと」です。名前を読めば、いつ動くかが分かります。

② 「止める」の意味 必ず覚える

PreToolUse で止める=道具を使う前に止めて、AIに理由を返す、です。Stop で止める=返事を終わらせず、続けさせる、です。

PostToolUse は、道具がもう実行された後なので、巻き戻しはできません。結果を見て「こう直して」とAIに伝える使い方になります。

第2章 どこに、どう書くか

① 書く場所 必ず覚える

フックは、設定ファイルの hooks という欄に書きます。

場所 効く範囲
~/.claude/settings.json 自分の全作業場
.claude/settings.json その作業場(共有できる)
.claude/settings.local.json その作業場・自分だけ
プラグインの hooks/hooks.json プラグインを有効にした所(No.12)

「作業場」は、いま開いているフォルダ(公式では「プロジェクト」)のことです。複数の場所に書いたフックは、上書きされず、全部が合わさって動きます。

② 3段の入れ子と matcher 必ず覚える

書き方は、「イベント → 条件の組 → 実行する物」の3段です。

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          { "type": "command", "command": "~/.claude/hooks/check.sh" }
        ]
      }
    ]
  }
}

matcher(絞り込みの条件)は、「どの道具の時に動かすか」を決めます。決まりは3つです。

MCP(No.11)の道具は mcp__サーバー名__道具名 という名前になります。サーバー全部に当てるなら、mcp__memory__.* のように末尾に .* を付けます。matcher に対応しないイベントでは、matcher は黙って無視されます。

③ 動かせる物は5種類 さらっと

type に書く値で、動かす物を選べます。

動かす時間の上限は、命令・http・mcp_tool で600秒が初期値です。ただし UserPromptSubmit では30秒です。依頼を送るたびに動くので、短く設定されています。

第3章 フックに渡る情報と、返し方

フックは、情報を受け取って、返事を返す小さなプログラムです。

① 渡される情報(標準入力のJSON) 必ず覚える

JSON(ジェイソン)は、項目と値を { } で書く、プログラム同士が使う書式です。フックには、いまの状況がJSONで標準入力に流し込まれます。

共通で入る項目は次の通りです。

PreToolUse なら、さらにこう入ります。

{
  "hook_event_name": "PreToolUse",
  "tool_name": "Bash",
  "tool_input": { "command": "npm test" },
  "tool_use_id": "toolu_01..."
}

つまりフックは、「どの道具を、どんな引数(道具に渡す値。ここでは実行する命令)で使おうとしているか」を、実行の直前に見られます。これが、止めたり、書き換えたりできる理由です。

② 返し方は2通り 必ず覚える

返事は、終了コードか、JSONのどちらか1つで返します。混ぜません。

終了コードで返す

終了コード 意味
0 成功。何も言わなければ「判断なし」で、通常の流れが続く
2 止めてほしい。標準エラー出力(エラー用の出力口。stderr とも書く)の文章が、理由としてAIに渡る
それ以外 失敗だが、止めない。エラーの知らせが出るだけで処理は続く

大事なのは、1 では止まらないという点です。うっかり exit 1 で終わるフックは、止める役を果たしません。

原則は「フックの失敗は通す側に倒れる(fail-open)」です。時間切れになった時も、終了コード1などで終わった時も、道具はそのまま実行されます(公式:「On most events, when a hook fails or times out, Claude Code still carries out the action」)。確実に止めたいフックは、止める時に必ず終了コード2か拒否のJSONで終わらせます。

JSONで返す

標準出力(普通の出力口)に、JSONを1つだけ出します。たとえば PreToolUse で拒否するなら、こう返します。

{
  "hookSpecificOutput": {
    "hookEventName": "PreToolUse",
    "permissionDecision": "deny",
    "permissionDecisionReason": "データベースへの書き込みは禁止"
  }
}

permissionDecision には、allow(許可)・deny(拒否)・ask(本人に確認)などを入れます。updatedInput で、道具に渡す引数を書き換えることもできます。additionalContext に文章を入れると、AIへの補足として机に置かれます(上限は1万文字)。

ここで注意したいのは、フックの allow は許可の確認を省くだけで、設定の拒否ルールを越えられないことです。公式には「Deny and ask rules are still evaluated」とあり、拒否ルールに当たる道具は、フックが allow を返しても止まります。ルールの判定順は No.13 で扱います。

③ 止める仕組みの実例 必ず覚える

公式の例は、rm -rf(フォルダを中身ごと消す命令)を含む命令を止めるフックです。シェルスクリプト(命令を並べたファイル)で書きます。

#!/bin/bash   # 「このファイルはbashで動かす」という宣言
# jq は、JSONから項目を取り出す道具。echo は、文字をそのまま出す命令。grep は、文字を探す道具
COMMAND=$(jq -r '.tool_input.command')
if echo "$COMMAND" | grep -q 'rm -rf'; then
  jq -n '{hookSpecificOutput:{hookEventName:"PreToolUse",
    permissionDecision:"deny",
    permissionDecisionReason:"破壊的な命令は止めました"}}'
else
  exit 0
fi

流れは3歩です。標準入力のJSONから命令を取り出す → 危ない文字が入っていたら拒否のJSONを出す → そうでなければ何もせず 0 で終わる、です。

時間切れ(タイムアウト)になった PreToolUse のフックも、道具をそのまま実行させます。止める役のフックが固まると、止まらずに通ってしまうので、講義で伝える価値があります。なお公式には、設定に "onFailure": "block" を足すと失敗時も止められる、とあります(v2.1.295以降)。

④ 手元の実物 さらっと

あなたの環境では、~/.claude/settings.json に4組のフックが入っています。

どれも、イベントの瞬間にだけ動く小さなスクリプトです。中身を開いて、読み取っている項目を探してみると、仕組みがよく分かります。

フックの強さの順番は、フックの deny は他より優先、allow は拒否ルールを越えられない、という形です。機能ごとの比較は No.15 で扱います。

○ 受講生によく聞かれる質問:「CLAUDE.md に『このファイルは触るな』と書いたのに、触られました」
→ 答え方:CLAUDE.md は読み物で、強制ではありません。確実に止めたいなら PreToolUse のフックで、終了コード2か拒否のJSONを返します。フックはAIの判断より前に、本体が動かします
○ 受講生によく聞かれる質問:「フックを入れたら、会話が遅くなりました」
→ 答え方:UserPromptSubmit などは、依頼を送るたびに待ちが発生します。重い処理を入れていないか、動く時間の上限が長すぎないかを見ます
○ 講義で使える図のたとえ:入口のチャイムと警備員です。チャイム(PostToolUse)は鳴らすだけ。警備員(PreToolUse)は、通す前に身分証を見て、止められます。
○ 関係する単元:No.09 スキル(判断して動く側)、No.13 許可の判定順

フック=イベントの瞬間に本体が動かす。状況はJSONが標準入力で渡る。止めるのは終了コード2か、拒否のJSON。終了コード1では止まらない。

確認問題

問1 「危険な命令は、実行される前に必ず止めたい」。使うイベントは?

問2 止めるつもりのフックが、命令を止めてくれませんでした。原因の候補で正しいのは?

問3 「Edit と Write の2つの道具の時だけ動かす」matcher の書き方として正しいのは?

問4 フックに渡される情報と、その使い方で正しいのは?

出典:Claude Code 公式ドキュメント「Hooks reference」(https://code.claude.com/docs/en/hooks)/「Automate actions with hooks」(https://code.claude.com/docs/en/hooks-guide)/「Extend Claude Code」(https://code.claude.com/docs/en/features-overview)/手元の ~/.claude/settings.json の hooks の実物(命令の中身は載せない)。数字は上記の公式による