メインコンテンツへスキップ
モジュール 3:実アーキテクチャ 3 / 6
上級 Session 15 Hooks Events

フックシステム

PreToolUse、PostToolUseなどのライフサイクルイベント用のイベントサブスクライバーを構築する。

2026年3月20日 18 分で読む

学ぶこと

Claude Codeにはビルトインのパーミッションがありますが、組織にはそれ以上のものが必要です。セキュリティチームは本番設定ファイルへのすべての書き込みをブロックしたいかもしれません。コンプライアンスチームは実行されたすべてのコマンドの監査ログが必要かもしれません。プラットフォームチームはAIが変更したファイルに自動タグ付けしたいかもしれません。フックシステムがこのすべてを可能にします。

このセッションを終えると、以下がわかるようになります:

  • フックとは何か、いつ発火するか
  • 4つのフックイベントタイプとその目的
  • settings.jsonでフックを設定する方法
  • マッチャーパターンがどのツールがフックをトリガーするかをフィルタリングする方法
  • 入出力コントラクト(JSON stdin、終了コード)
  • セキュリティゲートと監査システムの構築方法

課題

ビルトインのパーミッションシステムはバイナリです:許可か拒否か。しかし、現実のポリシーはニュアンスに富んでいます:

  • 「Bashを許可、ただしrm -rfを含むコマンドはブロック」
  • 「ファイル編集を許可、ただしconfig/production/内のファイルは不可」
  • 「すべて許可、ただしすべてのアクションを監査ファイルにログ」
  • 「gitコミットを許可、ただしメッセージにチケット番号を要求」

これらのルールは標準のパーミッションUIでは表現できません。プログラム可能なポリシー適用 — ツール実行ライフサイクルの特定のポイントで実行され、アクションを検査、変更、またはブロックできるコード — が必要です。

Without hooks:
  AI calls tool → Permission check (allow/deny) → Execute

With hooks:
  AI calls tool → Permission check → PreToolUse hook

                                    inspect, validate,
                                    block if needed


                                    Execute tool


                                    PostToolUse hook

                                    log, audit,
                                    side effects

仕組み

フックタイプ

Claude Codeは4つのライフサイクルイベントでフックを発火させます:

┌──────────────────────────────────────────────────┐
│              Hook Lifecycle                       │
│                                                   │
│  ┌─────────────┐                                  │
│  │ Notification │  AIが通知を生成したときに発火     │
│  │    Hook      │  (ステータスメッセージ)          │
│  └─────────────┘                                  │
│                                                   │
│  ┌─────────────┐                                  │
│  │ PreToolUse  │  ツール実行の前に発火             │
│  │    Hook     │  ツール呼び出しをブロック可能       │
│  └──────┬──────┘                                  │
│         │                                         │
│         ▼                                         │
│  ┌─────────────┐                                  │
│  │ Tool        │  実際のツール実行                  │
│  │ Execution   │                                   │
│  └──────┬──────┘                                  │
│         │                                         │
│         ▼                                         │
│  ┌─────────────┐                                  │
│  │ PostToolUse │  ツール実行の後に発火             │
│  │    Hook     │  ログと副作用用                    │
│  └─────────────┘                                  │
│                                                   │
│  ┌─────────────┐                                  │
│  │   Stop      │  エージェントループがターンを      │
│  │   Hook      │  終了しようとするときに発火         │
│  └─────────────┘                                  │
│                                                   │
└──────────────────────────────────────────────────┘
フックタイミングブロック可能?一般的な用途
PreToolUseツール実行前はいセキュリティゲート、バリデーション
PostToolUseツール実行後いいえログ、監査、副作用
Notificationステータスメッセージ時いいえカスタム通知
Stopターン終了前はい最終バリデーション、クリーンアップ

設定

フックは.claude/settings.json(プロジェクトレベル)または~/.claude/settings.json(ユーザーレベル)で定義します:

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "command": "/path/to/validate-command.sh"
      },
      {
        "matcher": "Edit",
        "command": "/path/to/check-file-path.sh"
      }
    ],
    "PostToolUse": [
      {
        "matcher": "*",
        "command": "/path/to/audit-log.sh"
      }
    ]
  }
}

各フックエントリには以下があります:

  • matcher — どのツールがこのフックをトリガーするか。"Bash"のようなツール名か、すべてのツールに対して"*"を使用。
  • command — 実行するシェルコマンド。これがフックスクリプトです。

マッチャーパターン

マッチャーはどのツール呼び出しがフックをトリガーするかを決定します:

Matcher      Matches
────────     ────────────────────────────
"Bash"       Bashツールの呼び出しのみ
"Edit"       Editツールの呼び出しのみ
"Read"       Readツールの呼び出しのみ
"mcp__*"     すべてのMCPツール呼び出し
"*"          すべてのツール呼び出し

複数のフックが同じツール呼び出しにマッチできます。順番に実行されます。PreToolUseフックのいずれかがブロックすると、ツール呼び出しは停止されます。

入出力コントラクト

フックが発火すると、Claude CodeはコンテキストをstdinのJSONとして渡し、終了コードから判断を読み取ります:

入力(stdin):

{
  "hook_type": "PreToolUse",
  "tool_name": "Bash",
  "tool_input": {
    "command": "rm -rf /tmp/build-cache"
  },
  "session_id": "abc-123",
  "message_id": "msg-456"
}

出力(終了コード):

終了コード意味効果
0許可ツール呼び出しが通常通り続行
2ブロックツール呼び出しがブロックされ、AIに理由が伝達
その他エラー許可として扱う(フェイルオープン)

Stdout(オプション、終了コード2の場合):

ブロック時に、フックは理由をstdoutに書き込めます。この理由はAIに表示され、調整できます:

BLOCKED: Cannot delete directories outside of project root.

AIはこれをツールエラーとして受け取り、別のアプローチを試すことができます。

実行フロー(詳細)

PreToolUseフックが設定されている場合の完全なフローです:

AIが実行したい: Bash("npm install express")


  ┌──────────────────┐
  │ Permission Check  │   標準の許可/拒否
  └────────┬─────────┘
           │ (allowed)

  ┌──────────────────┐
  │ Matcher Check     │   "Bash"にマッチするフックがあるか?
  └────────┬─────────┘
           │ (yes, matched)

  ┌──────────────────┐
  │ Run Hook Script   │   プロセスを起動、JSONをstdinにパイプ
  │                   │
  │ stdin: {          │
  │   "tool_name":    │
  │     "Bash",       │
  │   "tool_input": { │
  │     "command":     │
  │     "npm install"  │
  │   }               │
  │ }                 │
  └────────┬─────────┘

     ┌─────┴─────┐
     │           │
  exit 0      exit 2
  (allow)     (block)
     │           │
     ▼           ▼
  Execute     Return error
  tool        to AI:
              "BLOCKED: ..."

重要なポイント

フックは組織ポリシーの拡張ポイントです。 AIの指示を変更せずに、AIが従うべきルールを適用できます。

この区別は重要です。CLAUDE.mdに「本番ファイルを決して削除しない」と書けば、AIは通常従うでしょう。しかし、指示はオーバーライドされたり、圧縮後に忘れられたり、エッジケースで無視されたりする可能性があります。ファイルパスをチェックするPreToolUseフックは決定論的です — AIが何を決定しようと、バイパスされることはありません。

次のように考えてください:

レイヤーメカニズム保証
CLAUDE.mdの指示AIが読んで従うベストエフォート(AIの判断)
パーミッションシステムツールごとの許可/拒否バイナリ、ニュアンスなし
フック呼び出しごとのカスタムコード決定論的、プログラム可能

フックは「AIに丁寧にお願いする」と「Claude Codeにハードコードする」のギャップを埋めます。ポリシー適用レイヤーです。

ハンズオン例

例1:危険なコマンドのブロック

破壊的なシェルコマンドを防ぐPreToolUseフック:

#!/bin/bash
# hooks/block-dangerous.sh
# Bash tool用のPreToolUseフック

# stdinからJSON入力を読み取り
INPUT=$(cat)

# コマンドを抽出
COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // empty')

# ブロックパターンを定義
BLOCKED_PATTERNS=(
  "rm -rf /"
  "rm -rf ~"
  "DROP DATABASE"
  "DROP TABLE"
  "mkfs"
  "> /dev/sda"
  "chmod -R 777 /"
)

# 各パターンをチェック
for pattern in "${BLOCKED_PATTERNS[@]}"; do
  if echo "$COMMAND" | grep -qi "$pattern"; then
    echo "BLOCKED: Command contains dangerous pattern: $pattern"
    exit 2
  fi
done

# コマンドを許可
exit 0

例2:監査ログ

すべてのツール実行をログに記録するPostToolUseフック:

#!/bin/bash
# hooks/audit-log.sh
# すべてのツール用のPostToolUseフック

INPUT=$(cat)

# フィールドを抽出
TOOL=$(echo "$INPUT" | jq -r '.tool_name')
TIMESTAMP=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
SESSION=$(echo "$INPUT" | jq -r '.session_id')

# ログエントリを構築
LOG_ENTRY=$(jq -n \
  --arg ts "$TIMESTAMP" \
  --arg tool "$TOOL" \
  --arg session "$SESSION" \
  --argjson input "$(echo "$INPUT" | jq '.tool_input')" \
  '{timestamp: $ts, tool: $tool, session: $session, input: $input}')

# 監査ログに追記
echo "$LOG_ENTRY" >> ~/.claude/audit.jsonl

# PostToolUseフックは常にexit 0(実行後はブロックできない)
exit 0

これにより、JSONL監査証跡が生成されます:

{"timestamp":"2026-03-14T10:00:01Z","tool":"Bash","session":"abc","input":{"command":"npm test"}}
{"timestamp":"2026-03-14T10:00:03Z","tool":"Edit","session":"abc","input":{"file_path":"src/auth.ts",...}}
{"timestamp":"2026-03-14T10:00:05Z","tool":"Read","session":"abc","input":{"file_path":"package.json"}}

例3:本番ファイルの保護

本番設定への編集を防ぐPreToolUseフック:

#!/bin/bash
# hooks/protect-prod-config.sh
# EditとWriteツール用のPreToolUseフック

INPUT=$(cat)

FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty')

# 保護されたパスを定義
PROTECTED=(
  "config/production"
  ".env.production"
  "docker-compose.prod"
  "k8s/production"
)

for protected in "${PROTECTED[@]}"; do
  if echo "$FILE_PATH" | grep -q "$protected"; then
    echo "BLOCKED: Cannot modify production file: $FILE_PATH. Use a staging config instead."
    exit 2
  fi
done

exit 0

すべてをまとめる

3つすべてのフックを含む完全なsettings.jsonです:

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "command": "/project/.claude/hooks/block-dangerous.sh"
      },
      {
        "matcher": "Edit",
        "command": "/project/.claude/hooks/protect-prod-config.sh"
      },
      {
        "matcher": "Write",
        "command": "/project/.claude/hooks/protect-prod-config.sh"
      }
    ],
    "PostToolUse": [
      {
        "matcher": "*",
        "command": "/project/.claude/hooks/audit-log.sh"
      }
    ]
  }
}

この設定により:

  1. すべてのBashコマンドは実行前にブロックリストに対してチェックされる
  2. すべてのファイル編集は実行前に保護されたパスに対してチェックされる
  3. すべてのツール実行(すべてのツール)は実行後に監査ファイルにログ記録される

AIはフックの存在を認識していません。通常通りツールを呼び出します。フックがブロックした場合、AIはエラーメッセージを受け取って適応します — 通常はより安全な代替手段を選択します。

何が変わったか

フックなしフックあり
ポリシーは指示(ベストエフォート)ポリシーはコード(決定論的)
監査証跡なしすべてのアクションがログ記録
保護はAIのコンプライアンスに依存保護はランタイムで適用
すべてのプロジェクトで同じルールプロジェクトごとのフック設定
外部システムとの統合方法なしフックは任意の外部APIを呼び出し可能
セキュリティは信頼ベースセキュリティは検証してから信頼

次のセッション

これでモジュール3:実アーキテクチャが完了です。Claude Codeを拡張可能にする3つの柱を理解しました:コントロールプロトコル(CLIとSDKの通信方法)、MCP(外部ツールのプラグイン方法)、フック(ポリシーの適用方法)。

セッション16モジュール4:設定とカスタマイズを開始し、セッションストレージ — Claude Codeがセッション間で状態を永続化し、継続性とメモリを実現する方法 — を扱います。