Build This Now
Build This Now
キーボードショートカットステータスラインガイド
フックガイドClaude Code クロスプラットフォームフックClaude Code セットアップフックストップフック自己検証する Claude Code エージェントClaude Code セッションフックClaude Code コンテキストバックアップフックスキルアクティベーションフックClaude Code パーミッションフックClaude CodeのMCPツールフックCommands to Skills
Skills, Subagents, HooksSubagent Sweet SpotCLAUDE.md Best PracticesFix Context Limit
speedy_devvkoen_salo
Blog/Toolkit/Hooks/MCP Tool Hooks

Claude CodeのMCPツールフック

type: mcp_toolを使ってClaude CodeのフックからMCPサーバーのツールを直接呼び出す方法。スキーマ、変数展開の構文、ユースケース、本番パターンまで解説します。

設定をやめて、構築を始めよう。

AIオーケストレーション付きSaaSビルダーテンプレート。

企業向けに構築している実績を見る →
speedy_devvWritten by speedy_devvPublished Apr 24, 20269 min readToolkit hubHooks index

問題: フックはシェルスクリプトで動いています。MCPサーバーを呼び出すたびにサブプロセスを立ち上げ、トランスポートの配線、認証処理、レスポンスのパース、JSONのstdout出力と、毎回同じ作業が発生します。ファイル書き込みのたびに動くフォーマッターやセキュリティチェックだと、そのオーバーヘッドが積み重なります。

解決策: v2.1.118からフックに新しいtypeが追加されました。MCPツールをサブプロセスなしで直接呼び出せます。.claude/settings.jsonにこれを追加するだけで、ファイル書き込みのたびにセキュリティスキャンが走ります:

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Write|Edit",
        "hooks": [
          {
            "type": "mcp_tool",
            "server": "semgrep",
            "tool": "scan_file",
            "input": { "path": "${tool_input.file_path}" }
          }
        ]
      }
    ]
  }
}

MCPサーバーはすでに起動しています。フックはシェルをスキップして、サーバーのRPC接続に直接呼び出しをかけます。ツールのテキスト出力は、commandフックと同じJSONデシジョンパーサーに渡されます。

type: "mcp_tool" とは何か

v2.1.118より前、フックにはcommand、http、prompt、agentの4つのハンドラータイプがありました。今は5つです:

Type何が実行されるか
commandシェルのサブプロセス(stdin/stdout)
httpURLエンドポイントへのPOST
mcp_tool接続済みMCPサーバーへの直接RPC呼び出し
promptシングルターンのLLM評価(デフォルトはHaiku)
agentRead/Grep/Globアクセスを持つマルチターンのサブエージェント

mcp_toolタイプはcommandやhttpと同様に、すべてのフックイベントに対応しています。注意点は一つ:SessionStartとSetupはサーバー接続中に発火するため、初回実行時に「サーバー未接続」エラーが出ることがあります。2回目以降は問題ありません。

フルスキーマ

mcp_toolフック固有のフィールドは3つです。残りはすべてのフックタイプで共通です:

{
  "type": "mcp_tool",
  "server": "my-mcp-server",
  "tool": "tool_name",
  "input": {
    "arg1": "${tool_input.file_path}",
    "arg2": "${session_id}"
  },
  "timeout": 30,
  "statusMessage": "Checking...",
  "if": "Edit(*.ts|*.tsx)"
}
フィールド必須説明
serverYESsettingsで設定したMCPサーバーの正確な名前
toolYESそのサーバー上のツール名
inputnoツールに渡す引数。${path}変数展開に対応
timeoutnoフックがキャンセルされるまでの秒数
statusMessagenoフック実行中に表示されるスピナーのテキスト
ifnoパーミッションルール構文のフィルター。完全な呼び出しが一致したときだけフックが発火

重要: serverはMCP設定のサーバー名と完全一致している必要があります。1文字でも違うと、フックはエラーなく静かに失敗します。

変数展開(Input Substitution)

inputの文字列値は${field.path}というドット記法でフックのイベントJSONを参照できます。Write呼び出しのPostToolUseフックでは、イベントJSONはこうなります:

{
  "session_id": "abc123",
  "cwd": "/your/project",
  "hook_event_name": "PostToolUse",
  "tool_name": "Write",
  "tool_use_id": "toolu_01...",
  "tool_input": {
    "file_path": "/your/project/src/api.ts",
    "content": "..."
  },
  "tool_response": { "filePath": "/your/project/src/api.ts", "success": true },
  "duration_ms": 142
}

なので"${tool_input.file_path}"は/your/project/src/api.tsに解決されます。このオブジェクト内のフィールドはどれでも参照できます。duration_msフィールドはmcp_toolの1リリース後、v2.1.119で追加されました。

出力の処理方法

MCPツールのテキストコンテンツは、commandフックのstdoutとまったく同じように扱われます。有効なJSONとしてパースできればClaudeはデシジョンフィールドに従って動作し、そうでなければテキストはClaudeへのコンテキストになります。

デシジョンフィールドは他のフックと同じです:

{
  "decision": "block",
  "reason": "Security issue found in src/api.ts: SQL injection risk on line 42."
}

PostToolUseのMCPツールフックでこれを返すと、Claudeはメッセージを受け取ってファイルを修正します。ツールはすでに実行済みなので、これは予防ではなくアドバイスです。ツール実行前にブロックしたい場合はPreToolUseを使い、permissionDecision: "deny"を返してください。

mcp_toolフック(PostToolUse限定)に固有のフィールドが一つあります:updatedMCPToolOutputです。これにより、Claudeが会話に読み込む前に、別のツールの出力結果を書き換えられます。起動中のMCPサーバーが、他のツールの結果をClaude読み込み前に後処理できるということです。

シェルコマンドフックとの違い

スピードだけじゃありません。2つの具体的な違いがあります。

ステートフルなサーバー。 シェルのサブプロセスは毎回ゼロから起動します。MCPサーバーは独自の状態を持つ生きたプロセスです:ロード済みの設定、オープンな接続、キャッシュ、蓄積されたセッションコンテキスト。起動時にtsconfig.jsonをパース済みのリンティングMCPは、ファイル書き込みのたびに再パースしません。commandフックは毎回します。

シェル環境への依存がない。 commandフックはPATHが間違っていると、jqがインストールされていないと、~/.zshrcが非インタラクティブシェルで何かをstdoutに出力すると、静かに失敗します。MCPツールフックはそれをすべて回避します。呼び出しはClaude Codeから既存のRPC接続経由でサーバーに直接届きます。

ifフィールド:フックのスコープを絞る

ifがないと、フックはmatcherに合致するすべてのイベントで発火します。ifがあると、完全なツール呼び出し(名前と引数)がパーミッションルール構文に一致した場合だけフックが起動します:

{
  "type": "mcp_tool",
  "server": "semgrep",
  "tool": "scan_file",
  "if": "Edit(*.py|*.ts|*.js)",
  "input": { "path": "${tool_input.file_path}" }
}

このフックは.mdや.jsonファイルでは絶対に実行されません。ドキュメント編集が多いプロジェクトでは、体感できるパフォーマンス差が出ます。

パターン1:書き込みのたびにセキュリティスキャン

ファイルパスを受け取って検出結果を返すセキュリティMCPサーバー。何か見つかったらClaudeをブロックします:

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Write|Edit",
        "hooks": [
          {
            "type": "mcp_tool",
            "server": "semgrep",
            "tool": "scan_file",
            "if": "Write(*.ts|*.py|*.js|*.go)",
            "input": { "path": "${tool_input.file_path}" },
            "statusMessage": "Scanning..."
          }
        ]
      }
    ]
  }
}

MCPツールが検出結果を返す場合、レスポンスをこう組み立てます:

{
  "decision": "block",
  "reason": "Semgrep finding: [description of issue at line N]"
}

Claudeはブロックメッセージを受け取り、ファイルを修正します。スキャンはサーバーのキャッシュ済みルールセットで実行されるため、毎回サブプロセスをパースする必要がありません。

パターン2:外部検証を使ったStopフック

Claudeが完了を宣言する前に、関連チケットが本当にクローズされているか確認するStopフック。LinearやJiraのMCPを呼び出します:

{
  "hooks": {
    "Stop": [
      {
        "hooks": [
          {
            "type": "mcp_tool",
            "server": "linear",
            "tool": "get_issue_status",
            "input": { "issue_id": "${tool_input.issue_id}" }
          }
        ]
      }
    ]
  }
}

MCPツールがチケットのステータスを返します。In Progressだった場合、レスポンスJSONにdecision: "block"と理由を含めます。Claudeは作業を続けます。

Stopフックのロジックでは必ずstop_hook_activeを確認してください。 イベントJSONには、Claudeが前回のStopフック発火から継続中のときに"true"がセットされるこのフィールドが含まれています。チェックしないサーバーは無限ループを作ります。MCPツール側にガードを組み込んでください:inputのstop_hook_activeが"true"なら、空の出力を返してきれいに終了する、という処理です。

パターン3:停止前に本番エラーをチェック

Claudeが機能を作り終えたあと、セッション完了とマークする前にステージングで何か壊れていないか確認します。SentryのMCPが調べてくれます:

{
  "hooks": {
    "Stop": [
      {
        "hooks": [
          {
            "type": "mcp_tool",
            "server": "sentry",
            "tool": "get_new_errors_since",
            "input": { "minutes": "5", "skip_if_active": "${stop_hook_active}" }
          }
        ]
      }
    ]
  }
}

直近5分間に新しいエラーが発生していれば、MCPツールはそれをdecision: "block"とともに返します。Claudeはエラーの詳細を読んで、停止前にリグレッションを修正します。

パターン4:プロンプトのたびにドキュメントを自動注入

UserPromptSubmitフックにContext7のMCPを組み合わせて、プロンプトで言及されているライブラリのドキュメントをClaudeが処理する前に取得します:

{
  "hooks": {
    "UserPromptSubmit": [
      {
        "hooks": [
          {
            "type": "mcp_tool",
            "server": "context7",
            "tool": "get_library_docs",
            "input": { "prompt": "${prompt}" },
            "timeout": 15
          }
        ]
      }
    ]
  }
}

以前はClaudeが明示的にMCPツールを呼び出す必要がありました。今は毎回のプロンプトで自動的に行われます。Claudeは学習データではなく、最新のドキュメントからスタートします。

パターン5:エージェントチームへのポリシー適用

マルチエージェントワークフローで、共有ポリシーMCPサーバーを使ってどのエージェントがどのディレクトリに書き込めるかを管理できます。CLAUDE_AGENT_NAME環境変数で現在のエージェントを識別します。PreToolUseフックがポリシーサーバーを呼び出します:

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Write|Edit",
        "hooks": [
          {
            "type": "mcp_tool",
            "server": "policy-server",
            "tool": "check_write_permission",
            "input": {
              "agent": "${agent_name}",
              "path": "${tool_input.file_path}"
            }
          }
        ]
      }
    ]
  }
}

ポリシーサーバーが完全な認可マップを持っています。サーバーを一度更新すれば、すべてのプロジェクトのすべてのエージェントが新しいルールを引き継ぎます。settings.jsonを一つも触らずに。

パターン6:エージェントフロントマターのMCPツールフック

フックはsettings.jsonに置く必要はありません。エージェントのYAMLフロントマターに書いて、そのエージェントのライフサイクルにスコープを絞ることもできます:

---
name: backend-developer
description: Builds API endpoints and database logic
hooks:
  PostToolUse:
    - matcher: "Write"
      hooks:
        - type: mcp_tool
          server: semgrep
          tool: scan_file
          input: { "path": "${tool_input.file_path}" }
  Stop:
    - hooks:
        - type: agent
          prompt: "Verify all API endpoints have corresponding tests. Block if any are missing."
---

オーケストレーションされたチームの各専門エージェントが、自分のバリデーションロジックを持ちます。バックエンドエージェントはセキュリティ問題をスキャンし、フロントエンドエージェントはアクセシビリティをチェックします。全員に適用されるグローバルフックは不要です。

Elicitation制御

Elicitationイベントは、MCPサーバーがタスク中にユーザー入力を求めると発火します。mcp_toolフックでシークレットマネージャーを呼び出して、既知のプロンプトに自動応答できます:

{
  "hooks": {
    "Elicitation": [
      {
        "matcher": "my-db-server",
        "hooks": [
          {
            "type": "mcp_tool",
            "server": "secrets-manager",
            "tool": "get_credential",
            "input": { "key": "${elicitation.field_name}" }
          }
        ]
      }
    ]
  }
}

予測可能なクレデンシャルの問い合わせは自動で解決されます。タスクは中断なく進みます。

フックと相性のいいMCPサーバー

すべてのMCPサーバーがフックのターゲットとして有効なわけではありません。ユーザー介入なしに特定のイベントで発火させたいツールが、最も相性がいいです:

サーバーイベント何をするか
SemgrepPostToolUse: Write書き込みのたびにセキュリティスキャン
SentryStop完了前にステージングの新しいエラーを確認
Linear / JiraStop, TaskCompletedチケットステータスの確認、完了時に更新
Context7UserPromptSubmit言及されたライブラリのドキュメントを自動取得
ElevenLabsStop, Notificationタスク完了時にTTSで音声通知
SlackNotification, Stopcurlボイラープレートなしでチームにアラート
E2BStop完了マーク前に生成されたスクリプトをサンドボックスで実行
claude-memPostCompact, SessionStartコンパクション後にセッションコンテキストを復元
n8nTaskCompleted完了時に外部ワークフローをトリガー

既知の問題:PostToolUse + MCPイベント + additionalContext

MCPツール呼び出しがトリガーとなったイベントでadditionalContextが静かに破棄されるバグがあります(GitHub issue #24788)。これはmcp_toolフック自体ではなく、MCPツールイベントに反応するtype: "command"フックに影響します。

区別が重要です:MCPの呼び出しであるフックは正常に動作します。MCPツール呼び出しに反応してadditionalContextを返すフックは動作しません。回避策は、MCPツール呼び出しをターゲットにしたPostToolUseフックでの重要なメッセージに、exit 2とstderrを使うことです。ブロッキングパターンは動作します。アドバイザリーな注入は動作しません。

MCPツールフックはフックシステムの最後のピース

以前、フックはセーフティネットでした。危険なものをブロックしたり、フォーマッターを実行したりするシェルコマンド。ステートレスで、プロセスローカルで、MCPサーバーが持つ情報とは切り離されていました。

今は違います。フックは決定論的なオーケストレーション層です。どんなイベントにも、どんなMCPツールにも対応し、完全なデシジョン制御が可能で、呼び出しをまたいで状態が維持され、サブプロセスのオーバーヘッドもありません。

パイプラインはこれで完成しました。PreToolUseが検証し、PostToolUseがフォーマットとスキャンを行い、PostToolBatchがテストを実行し、Stopが実際の外部データで検証します。すべてのステップをMCPツールの呼び出しにできる。シェルスクリプトはもう必要ありません。

Continue in Hooks

  • Slash Commands Are Now Skills: Migrating Commands to Skills
    Claude Code slash commands and skills now do the same thing. Here is how to migrate .claude/commands to .claude/skills and why it pays off.
  • Claude Code セットアップフック
    スクリプト、エージェント、ドキュメントをClaude Codeのセットアップフックに組み合わせる方法。1つのコマンドで決定論的スクリプトを実行し、診断エージェントに出力を渡し、自動更新されるドキュメントを記録する。
  • Claude Code コンテキストバックアップフック
    StatusLineを活用したClaude Codeのコンテキストバックアップフック。10Kトークンごとに構造化されたスナップショットを書き込み、自動圧縮によってエラー文字列・関数シグネチャ・判断内容が失われるのを防ぐ。
  • Claude Code クロスプラットフォームフック
    Claude Codeのクロスプラットフォームフック: .cmd・.sh・.ps1のラッパーを捨て、nodeを直接呼び出すことで、1つの.mjsファイルがmacOS・Linux・Windowsで動く方法。
  • フックガイド
    Claude Code フックの基礎から実践まで: 終了コード、JSON出力、非同期コマンド、HTTPエンドポイント、PreToolUseとPostToolUseのマッチャー、本番環境パターン。
  • Claude Code パーミッションフック
    3階層の Claude Code パーミッションフックをインストールする: 安全な呼び出しは即時許可、危険な呼び出しは即時拒否、グレーゾーンは LLM チェック。スキップフラグ不要。

More from Toolkit

  • CLAUDE.md, Skills, Subagents, Hooks: When to Use Which
    Claude Code skills vs subagents vs hooks vs CLAUDE.md: a plain mental model for picking the right primitive, with token costs and examples.
  • Claude Code Subagents: The 3 to 5 Agent Sweet Spot
    Claude code subagents work best at 3-5 concurrent agents. Here is why that ceiling exists, how to set them up, and what to use past it.
  • CLAUDE.md Best Practices: The File That Makes Claude Code Reliable
    CLAUDE.md best practices: keep it under 200 lines, write it by hand, and use hooks when you need real enforcement, not advice.
  • How to Fix Claude Code Running Out of Context
    Claude Code running out of context is a session design problem. Fix it with /compact, lean CLAUDE.md, skills, and subagents, not a bigger window.

設定をやめて、構築を始めよう。

AIオーケストレーション付きSaaSビルダーテンプレート。

企業向けに構築している実績を見る →

Claude Code パーミッションフック

3階層の Claude Code パーミッションフックをインストールする: 安全な呼び出しは即時許可、危険な呼び出しは即時拒否、グレーゾーンは LLM チェック。スキップフラグ不要。

Commands to Skills

Claude Code slash commands and skills now do the same thing. Here is how to migrate .claude/commands to .claude/skills and why it pays off.

On this page

type: "mcp_tool" とは何か
フルスキーマ
変数展開(Input Substitution)
出力の処理方法
シェルコマンドフックとの違い
ifフィールド:フックのスコープを絞る
パターン1:書き込みのたびにセキュリティスキャン
パターン2:外部検証を使ったStopフック
パターン3:停止前に本番エラーをチェック
パターン4:プロンプトのたびにドキュメントを自動注入
パターン5:エージェントチームへのポリシー適用
パターン6:エージェントフロントマターのMCPツールフック
Elicitation制御
フックと相性のいいMCPサーバー
既知の問題:PostToolUse + MCPイベント + additionalContext
MCPツールフックはフックシステムの最後のピース

設定をやめて、構築を始めよう。

AIオーケストレーション付きSaaSビルダーテンプレート。

企業向けに構築している実績を見る →