mcpField
H
Hook Development

Hook Development

github.com/davila7エージェントワークフロー33.0k301 回インストール今日更新
リポジトリ

Hook Development について

Hook Development は、Claude Code プラグインの Hook を開発する人向けのガイドです。プロンプト型とコマンド型の Hook、イベントのライフサイクル、セキュリティ対策、デバッグ方法を扱います。

主な機能

  1. 01プロンプト型とコマンド型 Hook の設定
  2. 02ツールやセッションのイベントへの対応
  3. 03ツール名による Hook のマッチング
  4. 04入力検証とセキュリティリスクへの対処
  5. 05Hook のデバッグとテスト

活用シーン

  1. 01ツール実行前の操作の検証やブロック
  2. 02セッション開始時のプロジェクトコンテキスト読み込み
  3. 03エージェント停止前のタスク完了確認
  4. 04ツール結果の記録やセッション終了時のクリーンアップ

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/davila7/claude-code-templates --skill hook-development

インストール後、Claude Code で/hook-developmentを入力して読み込まれたか確認します。

同梱ファイル

SKILL.md と一緒にインストールされる参考資料やスクリプト。

examples/
load-context.sh
validate-write.sh
validate-bash.sh
references/
migration.md
advanced.md
patterns.md
scripts/
hook-linter.sh
test-hook.sh
validate-hook-schema.sh
README.md

詳細

Claude Code プラグインのフック開発

概要

フックは、Claude Code のイベントに応じて実行されるイベント駆動型の自動化スクリプトです。フックを使うと、操作の検証、ポリシーの適用、コンテキストの追加、外部ツールのワークフローへの統合ができます。

主な機能:

  • 実行前にツール呼び出しを検証(PreToolUse)
  • ツールの結果に対応(PostToolUse)
  • 完了基準を適用(Stop、SubagentStop)
  • プロジェクトのコンテキストを読み込む(SessionStart)
  • 開発ライフサイクル全体のワークフローを自動化

フックの種類

プロンプトベースのフック(推奨)

LLM による意思決定を使って、コンテキストを考慮した検証を行います。

{
  "type": "prompt",
  "prompt": "Evaluate if this tool use is appropriate: $TOOL_INPUT",
  "timeout": 30
}

対応イベント: Stop、SubagentStop、UserPromptSubmit、PreToolUse

メリット:

  • 自然言語による推論に基づき、コンテキストを考慮した判断ができます
  • bash スクリプトを使わずに、柔軟な評価ロジックを実現できます
  • エッジケースへの対応が向上します
  • 保守や拡張が容易です
コマンドフック

決定的なチェックを行うために、bash コマンドを実行します。

{
  "type": "command",
  "command": "bash ${CLAUDE_PLUGIN_ROOT}/scripts/validate.sh",
  "timeout": 60
}

用途:

  • 高速で決定的な検証
  • ファイルシステム操作
  • 外部ツールとの統合
  • パフォーマンスが重要なチェック

フック設定形式

プラグインの hooks.json 形式

hooks/hooks.json 内のプラグインフックでは、ラッパー形式を使います。

{
  "description": "Brief explanation of hooks (optional)",
  "hooks": {
    "PreToolUse": [...],
    "Stop": [...],
    "SessionStart": [...]
  }
}

要点:

  • description フィールドは任意です
  • hooks フィールドは必須のラッパーで、実際のフックイベントを含みます
  • これはプラグイン固有の形式です

例:

{
  "description": "Validation hooks for code quality",
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Write",
        "hooks": [
          {
            "type": "command",
            "command": "${CLAUDE_PLUGIN_ROOT}/hooks/validate.sh"
          }
        ]
      }
    ]
  }
}
設定形式(直接指定)

.claude/settings.json 内のユーザー設定では、直接指定する形式を使います。

{
  "PreToolUse": [...],
  "Stop": [...],
  "SessionStart": [...]
}

要点:

  • ラッパーはなく、イベントをトップレベルに直接記述します
  • description フィールドはありません
  • これは設定形式です

重要: 以下の例は、どちらの形式にも含められるフックイベントの構造を示しています。プラグインの hooks.json では、これらを {"hooks": {...}} で囲みます。

フックイベント

PreToolUse

ツールの実行前に実行されます。ツール呼び出しの承認、拒否、変更に使います。

例(プロンプトベース):

{
  "PreToolUse": [
    {
      "matcher": "Write|Edit",
      "hooks": [
        {
          "type": "prompt",
          "prompt": "Validate file write safety. Check: system paths, credentials, path traversal, sensitive content. Return 'approve' or 'deny'."
        }
      ]
    }
  ]
}

PreToolUse の出力:

{
  "hookSpecificOutput": {
    "permissionDecision": "allow|deny|ask",
    "updatedInput": {"field": "modified_value"}
  },
  "systemMessage": "Explanation for Claude"
}
PostToolUse

ツールの完了後に実行されます。結果への対応、フィードバックの提供、ログ記録に使います。

例:

{
  "PostToolUse": [
    {
      "matcher": "Edit",
      "hooks": [
        {
          "type": "prompt",
          "prompt": "Analyze edit result for potential issues: syntax errors, security vulnerabilities, breaking changes. Provide feedback."
        }
      ]
    }
  ]
}

出力の動作:

  • Exit 0:stdout がトランスクリプトに表示されます
  • Exit 2:stderr が Claude にフィードバックされます
  • systemMessage がコンテキストに含まれます
Stop

メインエージェントが停止を検討するときに実行されます。完了状態の検証に使います。

例:

{
  "Stop": [
    {
      "matcher": "*",
      "hooks": [
        {
          "type": "prompt",
          "prompt": "Verify task completion: tests run, build succeeded, questions answered. Return 'approve' to stop or 'block' with reason to continue."
        }
      ]
    }
  ]
}

判断の出力:

{
  "decision": "approve|block",
  "reason": "Explanation",
  "systemMessage": "Additional context"
}
SubagentStop

サブエージェントが停止を検討するときに実行されます。サブエージェントがタスクを完了したことを確認するために使います。

Stop フックと似ていますが、サブエージェント向けです。

UserPromptSubmit

ユーザーがプロンプトを送信したときに実行されます。コンテキストの追加、検証、プロンプトのブロックに使います。

例:

{
  "UserPromptSubmit": [
    {
      "matcher": "*",
      "hooks": [
        {
          "type": "prompt",
          "prompt": "Check if prompt requires security guidance. If discussing auth, permissions, or API security, return relevant warnings."
        }
      ]
    }
  ]
}
SessionStart

Claude Code のセッション開始時に実行されます。コンテキストの読み込みや環境の設定に使います。

例:

{
  "SessionStart": [
    {
      "matcher": "*",
      "hooks": [
        {
          "type": "command",
          "command": "bash ${CLAUDE_PLUGIN_ROOT}/scripts/load-context.sh"
        }
      ]
    }
  ]
}

特別な機能: $CLAUDE_ENV_FILE を使って環境変数を保持できます。

echo "export PROJECT_TYPE=nodejs" >> "$CLAUDE_ENV_FILE"

完全な例は examples/load-context.sh を参照してください。

SessionEnd

セッション終了時に実行されます。クリーンアップ、ログ記録、状態の保持に使います。

PreCompact

コンテキストの圧縮前に実行されます。保持する必要のある重要な情報を追加するために使います。

Notification

Claude が通知を送信したときに実行されます。ユーザーへの通知に対応するために使います。

フックの出力形式

標準出力(すべてのフック)
{
  "continue": true,
  "suppressOutput": false,
  "systemMessage": "Message for Claude"
}
  • continue:false の場合、処理を停止します(デフォルトは true)
  • suppressOutput:出力をトランスクリプトに表示しません(デフォルトは false)
  • systemMessage:Claude に表示されるメッセージ
終了コード
  • 0 - 成功(stdout がトランスクリプトに表示されます)
  • 2 - ブロッキングエラー(stderr が Claude にフィードバックされます)
  • その他 - 非ブロッキングエラー

フックの入力形式

すべてのフックは、共通フィールドを含む JSON を stdin 経由で受け取ります。

{
  "session_id": "abc123",
  "transcript_path": "/path/to/transcript.txt",
  "cwd": "/current/working/dir",
  "permission_mode": "ask|allow",
  "hook_event_name": "PreToolUse"
}

イベント固有のフィールド:

  • PreToolUse/PostToolUse: tool_name、tool_input、tool_result
  • UserPromptSubmit: user_prompt
  • Stop/SubagentStop: reason

プロンプト内で $TOOL_INPUT、$TOOL_RESULT、$USER_PROMPT などを使ってフィールドにアクセスします。

環境変数

すべてのコマンドフックで使用できます。

  • $CLAUDE_PROJECT_DIR - プロジェクトのルートパス
  • $CLAUDE_PLUGIN_ROOT - プラグインディレクトリ(移植可能なパスに使用)
  • $CLAUDE_ENV_FILE - SessionStart のみ:環境変数をここに保存します
  • $CLAUDE_CODE_REMOTE - リモートコンテキストで実行されている場合に設定されます

移植性を確保するため、フックコマンドでは常に ${CLAUDE_PLUGIN_ROOT} を使用してください。

{
  "type": "command",
  "command": "bash ${CLAUDE_PLUGIN_ROOT}/scripts/validate.sh"
}

プラグインのフック設定

プラグインでは、hooks/hooks.json でフックを定義します。

{
  "PreToolUse": [
    {
      "matcher": "Write|Edit",
      "hooks": [
        {
          "type": "prompt",
          "prompt": "Validate file write safety"
        }
      ]
    }
  ],
  "Stop": [
    {
      "matcher": "*",
      "hooks": [
        {
          "type": "prompt",
          "prompt": "Verify task completion"
        }
      ]
    }
  ],
  "SessionStart": [
    {
      "matcher": "*",
      "hooks": [
        {
          "type": "command",
          "command": "bash ${CLAUDE_PLUGIN_ROOT}/scripts/load-context.sh",
          "timeout": 10
        }
      ]
    }
  ]
}

プラグインのフックはユーザーのフックと統合され、並列で実行されます。

マッチャー

ツール名の照合

完全一致:

"matcher": "Write"

複数のツール:

"matcher": "Read|Write|Edit"

ワイルドカード(すべてのツール):

"matcher": "*"

正規表現パターン:

"matcher": "mcp__.*__delete.*"  // All MCP delete tools

注: マッチャーでは大文字と小文字が区別されます。

よく使われるパターン
// All MCP tools
"matcher": "mcp__.*"

// Specific plugin's MCP tools
"matcher": "mcp__plugin_asana_.*"

// All file operations
"matcher": "Read|Write|Edit"

// Bash commands only
"matcher": "Bash"

セキュリティのベストプラクティス

入力の検証

コマンドフックでは、必ず入力を検証してください。

#!/bin/bash
set -euo pipefail

input=$(cat)
tool_name=$(echo "$input" | jq -r '.tool_name')

# Validate tool name format
if [[ ! "$tool_name" =~ ^[a-zA-Z0-9_]+$ ]]; then
  echo '{"decision": "deny", "reason": "Invalid tool name"}' >&2
  exit 2
fi
パスの安全性

パストラバーサルや機密ファイルがないか確認してください。

file_path=$(echo "$input" | jq -r '.tool_input.file_path')

# Deny path traversal
if [[ "$file_path" == *".."* ]]; then
  echo '{"decision": "deny", "reason": "Path traversal detected"}' >&2
  exit 2
fi

# Deny sensitive files
if [[ "$file_path" == *".env"* ]]; then
  echo '{"decision": "deny", "reason": "Sensitive file"}' >&2
  exit 2
fi

完全な例については、examples/validate-write.sh と examples/validate-bash.sh を参照してください。

すべての変数を引用符で囲む
# GOOD: Quoted
echo "$file_path"
cd "$CLAUDE_PROJECT_DIR"

# BAD: Unquoted (injection risk)
echo $file_path
cd $CLAUDE_PROJECT_DIR
適切なタイムアウトを設定する
{
  "type": "command",
  "command": "bash script.sh",
  "timeout": 10
}

デフォルト: コマンドフック(60s)、プロンプトフック(30s)

パフォーマンスに関する考慮事項

並列実行

一致するフックはすべて並列で実行されます。

{
  "PreToolUse": [
    {
      "matcher": "Write",
      "hooks": [
        {"type": "command", "command": "check1.sh"},  // Parallel
        {"type": "command", "command": "check2.sh"},  // Parallel
        {"type": "prompt", "prompt": "Validate..."}   // Parallel
      ]
    }
  ]
}

設計上の留意点:

  • フックは互いの出力を参照できません
  • 実行順序は一定ではありません
  • 独立して動作するように設計してください
最適化
  1. すばやく確定的なチェックにはコマンドフックを使用します
  2. 複雑な推論にはプロンプトフックを使用します
  3. 検証結果を一時ファイルにキャッシュします
  4. 頻繁に実行される処理では I/O を最小限に抑えます

一時的に有効化するフック

フラグファイルまたは設定を確認し、条件に応じて有効化するフックを作成します。

パターン:フラグファイルによる有効化

#!/bin/bash
# Only active when flag file exists
FLAG_FILE="$CLAUDE_PROJECT_DIR/.enable-strict-validation"

if [ ! -f "$FLAG_FILE" ]; then
  # Flag not present, skip validation
  exit 0
fi

# Flag present, run validation
input=$(cat)
# ... validation logic ...

パターン:設定による有効化

#!/bin/bash
# Check configuration for activation
CONFIG_FILE="$CLAUDE_PROJECT_DIR/.claude/plugin-config.json"

if [ -f "$CONFIG_FILE" ]; then
  enabled=$(jq -r '.strictMode // false' "$CONFIG_FILE")
  if [ "$enabled" != "true" ]; then
    exit 0  # Not enabled, skip
  fi
fi

# Enabled, run hook logic
input=$(cat)
# ... hook logic ...

使用例:

  • 必要な場合にのみ厳密な検証を有効にする
  • 一時的なデバッグ用フック
  • プロジェクト固有のフック動作
  • フック用の機能フラグ

ベストプラクティス: 一時的なフックを有効/無効にする方法がユーザーにわかるよう、プラグインの README に有効化の仕組みを記載してください。

フックのライフサイクルと制限事項

フックはセッション開始時に読み込まれる

重要: フックは Claude Code のセッション開始時に読み込まれます。フック設定を変更するには、Claude Code を再起動する必要があります。

フックはホットスワップできません:

  • hooks/hooks.json を編集しても、現在のセッションには反映されません
  • 新しいフックスクリプトを追加しても認識されません
  • フックコマンドやプロンプトを変更しても更新されません
  • Claude Code を再起動する必要があります。終了してから、再度 claude を実行してください

フックの変更をテストするには:

  1. フックの設定またはスクリプトを編集します
  2. Claude Code のセッションを終了します
  3. claude または cc で再起動します
  4. 新しいフック設定が読み込まれます
  5. claude --debug でフックをテストします
起動時のフック検証

Claude Code の起動時にフックが検証されます。

  • hooks.json の JSON が無効な場合、読み込みに失敗します
  • スクリプトが見つからない場合、警告が表示されます
  • 構文エラーはデバッグモードで報告されます

/hooks コマンドを使用して、現在のセッションで読み込まれているフックを確認してください。

フックのデバッグ

デバッグモードを有効にする
claude --debug

フックの登録、実行ログ、入出力の JSON、タイミング情報を確認してください。

フックスクリプトをテストする

コマンドフックを直接テストします。

echo '{"tool_name": "Write", "tool_input": {"file_path": "/test"}}' | \
  bash ${CLAUDE_PLUGIN_ROOT}/scripts/validate.sh

echo "Exit code: $?"
JSON 出力を検証する

フックが有効な JSON を出力することを確認してください。

output=$(./your-hook.sh < test-input.json)
echo "$output" | jq .

クイックリファレンス

フックイベントの概要
イベントタイミング用途
PreToolUseツールの実行前検証、変更
PostToolUseツールの実行後フィードバック、ログ記録
UserPromptSubmitユーザー入力時コンテキスト、検証
Stopエージェントの停止時完全性の確認
SubagentStopサブエージェントの完了時タスクの検証
SessionStartセッション開始時コンテキストの読み込み
SessionEndセッション終了時クリーンアップ、ログ記録
PreCompactコンパクト化の前コンテキストの保持
Notificationユーザーへの通知時ログ記録、リアクション
ベストプラクティス

推奨事項:

  • ✅ 複雑なロジックには、プロンプトベースのフックを使用します
  • ✅ 移植性を確保するために ${CLAUDE_PLUGIN_ROOT} を使用します
  • ✅ コマンドフックでは、すべての入力を検証します
  • ✅ すべての bash 変数を引用符で囲みます
  • ✅ 適切なタイムアウトを設定します
  • ✅ 構造化された JSON 出力を返します
  • ✅ フックを十分にテストします

禁止事項:

  • ❌ ハードコードされたパスを使用しないでください
  • ❌ 検証せずにユーザー入力を信頼しないでください
  • ❌ 長時間実行されるフックを作成しないでください
  • ❌ フックの実行順序に依存しないでください
  • ❌ グローバル状態を予測不能な形で変更しないでください
  • ❌ 機密情報をログに記録しないでください

追加リソース

参考ファイル

詳細なパターンと高度なテクニックについては、以下を参照してください。

  • references/patterns.md - 一般的なフックのパターン(実証済みのパターンを8つ以上)
  • references/migration.md - 基本的なフックから高度なフックへの移行
  • references/advanced.md - 高度なユースケースとテクニック
フックスクリプトの例

examples/ に実用的な例があります。

  • validate-write.sh - ファイル書き込みの検証例
  • validate-bash.sh - Bash コマンドの検証例
  • load-context.sh - SessionStart でのコンテキスト読み込み例
ユーティリティスクリプト

[scripts/](scripts/) に開発ツールがあります。

  • validate-hook-schema.sh - hooks.json の構造と構文を検証します
  • test-hook.sh - デプロイ前にサンプル入力を使ってフックをテストします
  • hook-linter.sh - よくある問題やベストプラクティスに照らしてフックスクリプトを確認します
外部リソース
  • 公式ドキュメント:https://docs.claude.com/en/docs/claude-code/hooks
  • 例:マーケットプレイスの security-guidance プラグインを参照してください
  • テスト:詳細なログを確認するには claude --debug を使用します
  • 検証:フックの JSON 出力を検証するには jq を使用します

実装ワークフロー

プラグインにフックを実装するには、次の手順に従います。

  1. フックするイベント(PreToolUse、Stop、SessionStart など)を特定します
  2. プロンプトベース(柔軟)とコマンド(決定論的)のどちらのフックを使うか決めます
  3. hooks/hooks.json にフックの設定を記述します
  4. コマンドフックの場合は、フックスクリプトを作成します
  5. すべてのファイル参照に ${CLAUDE_PLUGIN_ROOT} を使用します
  6. scripts/validate-hook-schema.sh hooks/hooks.json で設定を検証します
  7. デプロイ前に scripts/test-hook.sh でフックをテストします
  8. claude --debug を使って Claude Code でテストします
  9. プラグインの README にフックを記載します

ほとんどのユースケースでは、プロンプトベースのフックを優先してください。コマンドフックは、パフォーマンスが重要なチェックや決定論的なチェックに限定してください。

コメント