mcpField
H
hook-developer

hook-developer

github.com/parcadei開発3.9k506 回インストール39 週間前に更新
リポジトリ

hook-developer について

Claude Code のフックを作成、トラブルシューティングする開発者向けのリファレンススキルです。イベントのスキーマ、処理のブロック、登録、フック種別、テスト、実装パターンを解説します。

主な機能

  1. 01フックイベントの入出力スキーマ
  2. 02処理のブロックとコンテキスト注入
  3. 03フック登録とマッチャーパターン
  4. 04コマンド型とプロンプト型のフック
  5. 05テストとデバッグの手引き
  6. 06一般的なフック実装パターン

活用シーン

  1. 01正しいイベントスキーマでフックを作成
  2. 02フックで処理をブロックできるか確認
  3. 03`settings.json` でイベントマッチャーを設定
  4. 04フックの出力や登録の問題を調査

MCP 対応

Claude Code のフックで MCP ツールを識別、マッチさせる方法と、MCP サーバーツール名のパターンを説明します。

インストール

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

$npx skills add https://github.com/parcadei/continuous-claude-v3 --skill hook-developer

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

詳細

フック開発者

Claude Code フック開発の完全なリファレンスです。正しい入出力スキーマでフックを作成する際にお使いください。

使用する場面

  • 新しいフックを作成するとき
  • フックの入出力形式をデバッグするとき
  • 使用可能なフィールドを確認するとき
  • settings.json でフックの登録を設定するとき
  • フックでブロックできる処理とコンテキストを挿入できる処理を確認するとき

クイックリファレンス

フック実行されるタイミングブロック可能?主な用途
PreToolUseツールの実行前はいツール呼び出しのブロック/変更
PostToolUseツールの完了後一部可能ツールの結果への対応
UserPromptSubmitユーザーがプロンプトを送信したときはい検証/コンテキストの挿入
PermissionRequest権限ダイアログの表示時はい自動で許可/拒否
SessionStartセッションの開始時いいえコンテキストの読み込み、環境変数の設定
SessionEndセッションの終了時いいえクリーンアップ/状態の保存
Stopエージェントの処理終了時はい継続の強制
SubagentStartサブエージェントの起動時いいえパターンの連携
SubagentStopサブエージェントの処理終了時はい継続の強制
PreCompactコンテキストの圧縮前いいえ状態の保存
Notification通知の送信時いいえカスタムアラート

フックの種類のオプション: type: "command"(bash)または type: "prompt"(LLM による評価)


フックの入出力スキーマ

PreToolUse

目的: ツールの実行前に、その実行をブロックまたは変更します。

入力:

{
  "session_id": "string",
  "transcript_path": "string",
  "cwd": "string",
  "permission_mode": "default|plan|acceptEdits|bypassPermissions",
  "hook_event_name": "PreToolUse",
  "tool_name": "string",
  "tool_input": {
    "file_path": "string",
    "command": "string"
  },
  "tool_use_id": "string"
}

出力(JSON):

{
  "hookSpecificOutput": {
    "hookEventName": "PreToolUse",
    "permissionDecision": "allow|deny|ask",
    "permissionDecisionReason": "string",
    "updatedInput": {}
  },
  "continue": true,
  "stopReason": "string",
  "systemMessage": "string",
  "suppressOutput": true
}

終了コード 2: ツールをブロックし、stderr を Claude に表示します。

一般的なマッチャー: Bash、Edit|Write、Read、Task、mcp__.*


PostToolUse

目的: ツール実行の結果に対応し、Claude にフィードバックを提供します。

入力:

{
  "session_id": "string",
  "transcript_path": "string",
  "cwd": "string",
  "permission_mode": "string",
  "hook_event_name": "PostToolUse",
  "tool_name": "string",
  "tool_input": {},
  "tool_response": {
    "filePath": "string",
    "success": true,
    "output": "string",
    "exitCode": 0
  },
  "tool_use_id": "string"
}

重要: 応答フィールドは tool_result ではなく、tool_response です。

出力(JSON):

{
  "decision": "block",
  "reason": "string",
  "hookSpecificOutput": {
    "hookEventName": "PostToolUse",
    "additionalContext": "string"
  },
  "continue": true,
  "stopReason": "string",
  "suppressOutput": true
}

ブロック: "decision": "block" と "reason" を指定すると、Claude に問題への対処を促します。

一般的なマッチャー: Edit|Write、Bash


UserPromptSubmit

目的: ユーザープロンプトを検証し、Claude が処理する前にコンテキストを挿入します。

入力:

{
  "session_id": "string",
  "transcript_path": "string",
  "cwd": "string",
  "permission_mode": "string",
  "hook_event_name": "UserPromptSubmit",
  "prompt": "string"
}

出力(プレーンテキスト):

Any stdout text is added to context for Claude.

出力(JSON):

{
  "decision": "block",
  "reason": "string",
  "hookSpecificOutput": {
    "hookEventName": "UserPromptSubmit",
    "additionalContext": "string"
  }
}

ブロック: "decision": "block" を指定すると、プロンプトを消去し、"reason" をユーザーにのみ表示します(Claude には表示されません)。

終了コード 2: プロンプトをブロックし、stderr をユーザーにのみ表示します。


PermissionRequest

目的: 権限ダイアログの判断を自動化します。

入力:

{
  "session_id": "string",
  "transcript_path": "string",
  "cwd": "string",
  "permission_mode": "string",
  "hook_event_name": "PermissionRequest",
  "tool_name": "string",
  "tool_input": {}
}

出力:

{
  "hookSpecificOutput": {
    "hookEventName": "PermissionRequest",
    "decision": {
      "behavior": "allow|deny",
      "updatedInput": {},
      "message": "string",
      "interrupt": false
    }
  }
}

SessionStart

目的: セッションを初期化し、コンテキストを読み込み、環境変数を設定します。

入力:

{
  "session_id": "string",
  "transcript_path": "string",
  "cwd": "string",
  "permission_mode": "string",
  "hook_event_name": "SessionStart",
  "source": "startup|resume|clear|compact"
}

環境変数: CLAUDE_ENV_FILE — export VAR=value を書き込むと、環境変数を保持できます。

出力(プレーンテキストまたは JSON):

{
  "hookSpecificOutput": {
    "hookEventName": "SessionStart",
    "additionalContext": "string"
  },
  "suppressOutput": true
}

プレーンテキストの stdout はコンテキストとして追加されます。


SessionEnd

目的: クリーンアップ、状態の保存、セッションの記録を行います。

入力:

{
  "session_id": "string",
  "transcript_path": "string",
  "cwd": "string",
  "permission_mode": "string",
  "hook_event_name": "SessionEnd",
  "reason": "clear|logout|prompt_input_exit|other"
}

出力: セッションはすでに終了処理中のため、影響を与えることはできません。クリーンアップにのみ使用してください。


Stop

目的: Claude が停止するタイミングを制御し、継続を強制します。

入力:

{
  "session_id": "string",
  "transcript_path": "string",
  "cwd": "string",
  "permission_mode": "string",
  "hook_event_name": "Stop",
  "stop_hook_active": false
}

重要: 無限ループを防ぐため、stop_hook_active: true を確認してください!

出力:

{
  "decision": "block",
  "reason": "string"
}

ブロック: "decision": "block" を指定すると、"reason" をプロンプトとして Claude の処理継続を強制します。


SubagentStart

目的: サブエージェント(Task ツール)の起動時に実行します。

入力:

{
  "session_id": "string",
  "transcript_path": "string",
  "cwd": "string",
  "permission_mode": "string",
  "hook_event_name": "SubagentStart",
  "agent_id": "string"
}

出力: コンテキストの挿入のみ(ブロックはできません)。


SubagentStop

目的: サブエージェント(Task ツール)が停止するタイミングを制御します。

入力:

{
  "session_id": "string",
  "transcript_path": "string",
  "cwd": "string",
  "permission_mode": "string",
  "hook_event_name": "SubagentStop",
  "stop_hook_active": false
}

出力: Stop と同じです。


PreCompact

目的: コンテキストの圧縮前に状態を保存します。

入力:

{
  "session_id": "string",
  "transcript_path": "string",
  "cwd": "string",
  "permission_mode": "string",
  "hook_event_name": "PreCompact",
  "trigger": "manual|auto",
  "custom_instructions": "string"
}

マッチャー: manual、auto

出力:

{
  "continue": true,
  "systemMessage": "string"
}

Notification

目的: 通知をカスタム処理します。

入力:

{
  "session_id": "string",
  "transcript_path": "string",
  "cwd": "string",
  "permission_mode": "string",
  "hook_event_name": "Notification",
  "message": "string",
  "notification_type": "permission_prompt|idle_prompt|auth_success|elicitation_dialog"
}

マッチャー: permission_prompt、idle_prompt、auth_success、elicitation_dialog、*

出力:

{
  "continue": true,
  "suppressOutput": true,
  "systemMessage": "string"
}

settings.json での登録

標準的な構造
{
  "hooks": {
    "EventName": [
      {
        "matcher": "ToolPattern",
        "hooks": [
          {
            "type": "command",
            "command": "$CLAUDE_PROJECT_DIR/.claude/hooks/my-hook.sh",
            "timeout": 60
          }
        ]
      }
    ]
  }
}
マッチャーパターン
パターン一致するもの
BashBash ツールのみ
Edit|WriteEdit または Write
Read.*正規表現:Read*
mcp__.*__write.*MCP の書き込みツール
*すべてのツール

大文字と小文字は区別されます: Bash ≠ bash

マッチャーが必要なイベント
  • PreToolUse - はい(必須)
  • PostToolUse - はい(必須)
  • PermissionRequest - はい(必須)
  • Notification - はい(任意)
  • SessionStart - はい(startup|resume|clear|compact)
  • PreCompact - はい(manual|auto)
マッチャーが不要なイベント
{
  "hooks": {
    "UserPromptSubmit": [
      {
        "hooks": [{ "type": "command", "command": "/path/to/hook.sh" }]
      }
    ]
  }
}

フックの種類

コマンドフック(type: "command")

デフォルトの種類です。bash コマンドまたはスクリプトを実行します。

{
  "type": "command",
  "command": "$CLAUDE_PROJECT_DIR/.claude/hooks/my-hook.sh",
  "timeout": 60
}
プロンプトベースのフック(type: "prompt")

LLM(Haiku)を使用して、コンテキストを考慮した判断を行います。Stop/SubagentStop に最適です。

{
  "type": "prompt",
  "prompt": "Evaluate if Claude should stop. Context: $ARGUMENTS. Check if all tasks are complete.",
  "timeout": 30
}

応答スキーマ:

{
  "decision": "approve" | "block",
  "reason": "Explanation",
  "continue": false,
  "stopReason": "Message to user",
  "systemMessage": "Warning"
}

MCP ツールの命名規則

MCP ツールは mcp__<server>__<tool> のパターンを使用します:

パターン一致するもの
mcp__memory__.*memory サーバーのすべてのツール
mcp__.*__write.*MCP のすべての書き込みツール
mcp__github__.*GitHub のすべてのツール

環境変数

すべてのフックで利用可能
変数説明
CLAUDE_PROJECT_DIRプロジェクトルートの絶対パス
CLAUDE_CODE_REMOTEリモート/Web の場合は "true"、ローカルの CLI の場合は空
SessionStart のみ
変数説明
CLAUDE_ENV_FILEexport VAR=value の行を書き込むパス
プラグインフックのみ
変数説明
CLAUDE_PLUGIN_ROOTプラグインディレクトリの絶対パス

終了コード

終了コード動作stdoutstderr
0成功JSON を処理無視
2ブロッキングエラー無視エラーメッセージ
その他非ブロッキングエラー無視詳細モード
フック別の終了コード 2 の動作
フック効果
PreToolUseツールをブロックし、stderr を Claude に送信
PostToolUsestderr を Claude に送信(ツールはすでに実行済み)
UserPromptSubmitプロンプトをブロックし、stderr をユーザーのみに送信
Stop停止をブロックし、stderr を Claude に送信

シェルラッパーパターン

#!/bin/bash
set -e
cd "$CLAUDE_PROJECT_DIR/.claude/hooks"
cat | npx tsx src/my-hook.ts

バンドル済みの場合は、次のようにします:

#!/bin/bash
set -e
cd "$HOME/.claude/hooks"
cat | node dist/my-hook.mjs

TypeScript ハンドラーパターン

import { readFileSync } from 'fs';

interface HookInput {
  session_id: string;
  hook_event_name: string;
  tool_name?: string;
  tool_input?: Record<string, unknown>;
  tool_response?: Record<string, unknown>;
  // ... other fields per hook type
}

function readStdin(): string {
  return readFileSync(0, 'utf-8');
}

async function main() {
  const input: HookInput = JSON.parse(readStdin());

  // Process input

  const output = {
    decision: 'block',  // or undefined to allow
    reason: 'Why blocking'
  };

  console.log(JSON.stringify(output));
}

main().catch(console.error);

フックのテスト

手動テストコマンド
# PostToolUse (Write)
echo '{"tool_name":"Write","tool_input":{"file_path":"test.md"},"tool_response":{"success":true},"session_id":"test"}' | \
  .claude/hooks/my-hook.sh

# PreToolUse (Bash)
echo '{"tool_name":"Bash","tool_input":{"command":"ls"},"session_id":"test"}' | \
  .claude/hooks/my-hook.sh

# SessionStart
echo '{"hook_event_name":"SessionStart","source":"startup","session_id":"test"}' | \
  .claude/hooks/session-start.sh

# SessionEnd
echo '{"hook_event_name":"SessionEnd","reason":"clear","session_id":"test"}' | \
  .claude/hooks/session-end.sh

# UserPromptSubmit
echo '{"prompt":"test prompt","session_id":"test"}' | \
  .claude/hooks/prompt-submit.sh
TypeScript の編集後に再ビルド
cd .claude/hooks
npx esbuild src/my-hook.ts \
  --bundle --platform=node --format=esm \
  --outfile=dist/my-hook.mjs

よくあるパターン

危険なファイルをブロックする(PreToolUse)
#!/usr/bin/env python3
import json, sys

data = json.load(sys.stdin)
path = data.get('tool_input', {}).get('file_path', '')

BLOCKED = ['.env', 'secrets.json', '.git/']
if any(b in path for b in BLOCKED):
    print(json.dumps({
        "hookSpecificOutput": {
            "hookEventName": "PreToolUse",
            "permissionDecision": "deny",
            "permissionDecisionReason": f"Blocked: {path} is protected"
        }
    }))
else:
    print('{}')
ファイルを自動フォーマットする(PostToolUse)
#!/bin/bash
INPUT=$(cat)
FILE=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty')

if [[ "$FILE" == *.ts ]] || [[ "$FILE" == *.tsx ]]; then
  npx prettier --write "$FILE" 2>/dev/null
fi

echo '{}'
Git のコンテキストを注入する(UserPromptSubmit)
#!/bin/bash
echo "Git status:"
git status --short 2>/dev/null || echo "(not a git repo)"
echo ""
echo "Recent commits:"
git log --oneline -5 2>/dev/null || echo "(no commits)"
テストの検証を必須にする(Stop)
#!/usr/bin/env python3
import json, sys, subprocess

data = json.load(sys.stdin)

# Prevent infinite loops
if data.get('stop_hook_active'):
    print('{}')
    sys.exit(0)

# Check if tests pass
result = subprocess.run(['npm', 'test'], capture_output=True)
if result.returncode != 0:
    print(json.dumps({
        "decision": "block",
        "reason": "Tests are failing. Please fix before stopping."
    }))
else:
    print('{}')

デバッグチェックリスト

  • settings.json にフックが登録されていますか?
  • シェルスクリプトに +x 権限がありますか?
  • TS の変更後にバンドルを再ビルドしましたか?
  • tool_result ではなく tool_response を使用していますか?
  • 出力は有効な JSON(またはプレーンテキスト)ですか?
  • Stop フックで stop_hook_active を確認していますか?
  • パスに $CLAUDE_PROJECT_DIR を使用していますか?

過去のセッションから得た重要な知見

  1. フィールド名が重要です - tool_result ではなく tool_response
  2. 出力形式 - ブロックする場合は decision: "block" と reason
  3. 終了コード 2 - stderr は Claude/ユーザーに送信され、stdout は無視されます
  4. バンドルを再ビルドします - TypeScript ソースの編集は自動では反映されません
  5. 手動でテストします - フックに依存する前に echo '{}' | ./hook.sh を実行します
  6. まず出力を確認します - コードを編集する前に ls .claude/cache/ を実行します
  7. デタッチされた spawn はエラーを隠します - デバッグ用にログ出力を追加します

関連項目

  • /debug-hooks - 体系的なデバッグワークフロー
  • .claude/rules/hooks.md - フック開発ルール

コメント