Build This Now
Build This Now
キーボードショートカットステータスラインガイド
フックガイドClaude Code クロスプラットフォームフックClaude Code セットアップフックストップフック自己検証する Claude Code エージェントClaude Code セッションフックClaude Code コンテキストバックアップフックスキルアクティベーションフックClaude Code パーミッションフックClaude CodeのMCPツールフック
speedy_devvkoen_salo
Blog/Toolkit/Hooks/MCP Tool Hooks in Claude Code

Claude CodeのMCPツールフック

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

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

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

Published 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

  • 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 チェック。スキップフラグ不要。
  • 自己検証する Claude Code エージェント
    自己検証する Claude Code エージェント: PostToolUse リントフック、Stop フック、読み取り専用レビュアーのサブエージェントをエージェント定義に組み込み、不良な出力が出荷されないようにする。

More from Toolkit

  • キーボードショートカット
    Claude Codeのkeybindings.jsonを設定する: 17のコンテキスト、キーストローク構文、コードシーケンス、修飾キーの組み合わせ、デフォルトショートカットを即座に無効化する方法。
  • ステータスラインガイド
    Claude Code のステータスラインにモデル名、gitブランチ、セッションコスト、コンテキスト使用量を表示する方法。settings.json の設定、JSON入力、bash、Python、Node.js スクリプトを解説。
  • AIによるSEOとGEO最適化
    Generative Engine Optimizationの解説: Googleで上位表示されるだけでなく、ChatGPT、Claude、Perplexityの回答内でコンテンツが引用されるようにする方法。
  • 2026年版 Claude Code と Cursor の比較
    2026年の Claude Code と Cursor を並べて比較します。エージェントモデル、コンテキストウィンドウ、料金プラン、そして各ツールが異なる開発ワークフローにどう適合するかを解説します。

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

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

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

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

MCPの基本

Model Context Protocolの基本:サーバープロセスがツール、API、サービスをClaude Codeに共有ワイヤーフォーマットで公開する仕組み。設定、トランスポート、最初のサーバー構築。

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ビルダーテンプレート。