Loading...
    • 開発者ガイド
    • APIリファレンス
    • MCP
    • リソース
    • リリースノート
    Search...
    ⌘K
    最初のステップ
    Claudeの紹介クイックスタート
    モデルと価格
    モデル概要モデルの選択Claude 4.5の新機能Claude 4.5への移行モデルの廃止予定価格
    Claudeで構築
    機能概要Messages APIの使用コンテキストウィンドウプロンプトのベストプラクティス
    機能
    プロンプトキャッシングコンテキスト編集拡張思考エフォートストリーミングメッセージバッチ処理引用多言語対応トークンカウント埋め込みビジョンPDF対応Files API検索結果構造化出力
    ツール
    概要ツール使用の実装方法細粒度ツールストリーミングBashツールコード実行ツールプログラマティックツール呼び出しコンピュータ使用ツールテキストエディタツールWebフェッチツールWeb検索ツールメモリツールツール検索ツール
    エージェントスキル
    概要クイックスタートベストプラクティスAPIでスキルを使用
    エージェントSDK
    概要クイックスタートTypeScript SDKTypeScript V2(プレビュー)Python SDK移行ガイド
    ストリーミング入力権限の処理フックで実行を制御セッション管理SDKの構造化出力エージェントSDKのホスティングAIエージェントの安全なデプロイシステムプロンプトの変更SDKのMCPカスタムツールSDKのサブエージェントSDKのスラッシュコマンドSDKのエージェントスキルコストと使用状況の追跡TodoリストSDKのプラグイン
    APIのMCP
    MCPコネクタリモートMCPサーバー
    サードパーティプラットフォームのClaude
    Amazon BedrockMicrosoft FoundryVertex AI
    プロンプトエンジニアリング
    概要プロンプトジェネレータプロンプトテンプレートの使用プロンプト改善ツール明確で直接的に例を使用(マルチショットプロンプティング)Claudeに考えさせる(CoT)XMLタグを使用Claudeに役割を与える(システムプロンプト)Claudeの応答を事前入力複雑なプロンプトをチェーン長いコンテキストのヒント拡張思考のヒント
    テストと評価
    成功基準の定義テストケースの開発評価ツールの使用レイテンシの削減
    ガードレールの強化
    幻覚の削減出力の一貫性を向上ジェイルブレイクの軽減ストリーミング拒否プロンプト漏洩の削減Claudeをキャラクターのままに
    管理と監視
    Admin API概要使用状況とコストAPIClaude Code Analytics API
    Console
    Log in
    Loading...
    Loading...
    Loading...
    Loading...
    Loading...
    Loading...
    Loading...
    Loading...
    Loading...
    Loading...
    Loading...
    Loading...
    Loading...
    Loading...
    Loading...
    Loading...

    Solutions

    • AI agents
    • Code modernization
    • Coding
    • Customer support
    • Education
    • Financial services
    • Government
    • Life sciences

    Partners

    • Amazon Bedrock
    • Google Cloud's Vertex AI

    Learn

    • Blog
    • Catalog
    • Courses
    • Use cases
    • Connectors
    • Customer stories
    • Engineering at Anthropic
    • Events
    • Powered by Claude
    • Service partners
    • Startups program

    Company

    • Anthropic
    • Careers
    • Economic Futures
    • Research
    • News
    • Responsible Scaling Policy
    • Security and compliance
    • Transparency

    Learn

    • Blog
    • Catalog
    • Courses
    • Use cases
    • Connectors
    • Customer stories
    • Engineering at Anthropic
    • Events
    • Powered by Claude
    • Service partners
    • Startups program

    Help and security

    • Availability
    • Status
    • Support
    • Discord

    Terms and policies

    • Privacy policy
    • Responsible disclosure policy
    • Terms of service: Commercial
    • Terms of service: Consumer
    • Usage policy
    ガイド

    フックを使用してエージェントの動作をインターセプトして制御する

    フックを使用して、実行の重要なポイントでエージェントの動作をインターセプトしてカスタマイズする

    フックを使用すると、エージェント実行の重要なポイントでインターセプトして、検証、ログ記録、セキュリティ制御、またはカスタムロジックを追加できます。フックを使用すると、以下のことができます:

    • 危険な操作をブロックする:破壊的なシェルコマンドや不正なファイルアクセスなど、実行前に危険な操作をブロックします
    • ログと監査:コンプライアンス、デバッグ、またはアナリティクスのために、すべてのツール呼び出しをログして監査します
    • 入力と出力を変換する:データをサニタイズしたり、認証情報を注入したり、ファイルパスをリダイレクトします
    • 人間の承認を要求する:データベース書き込みやAPI呼び出しなどの機密アクションに対して人間の承認を要求します
    • セッションライフサイクルを追跡する:状態を管理し、リソースをクリーンアップし、通知を送信します

    フックには2つの部分があります:

    1. コールバック関数:フックが発火したときに実行されるロジック
    2. フック設定:SDKにどのイベントをフックするか(例:PreToolUse)、どのツールをマッチするかを指示します

    次の例は、エージェントが.envファイルを変更するのをブロックします。まず、ファイルパスをチェックするコールバックを定義し、次にそれをquery()に渡して、Write または Edit ツール呼び出しの前に実行します:

    import asyncio
    from claude_agent_sdk import query, ClaudeAgentOptions, HookMatcher
    
    # ツール呼び出しの詳細を受け取るフックコールバックを定義する
    async def protect_env_files(input_data, tool_use_id, context):
        # ツールの入力引数からファイルパスを抽出する
        file_path = input_data['tool_input'].get('file_path', '')
        file_name = file_path.split('/')[-1]
    
        # .envファイルをターゲットにしている場合、操作をブロックする
        if file_name == '.env':
            return {
                'hookSpecificOutput': {
                    'hookEventName': input_data['hook_event_name'],
                    'permissionDecision': 'deny',
                    'permissionDecisionReason': 'Cannot modify .env files'
                }
            }
    
        # 空のオブジェクトを返して操作を許可する
        return {}
    
    async def main():
        async for message in query(
            prompt="Update the database configuration",
            options=ClaudeAgentOptions(
                hooks={
                    # PreToolUseイベントのフックを登録する
                    # マッチャーはWrite と Edit ツール呼び出しのみにフィルタリングする
                    'PreToolUse': [HookMatcher(matcher='Write|Edit', hooks=[protect_env_files])]
                }
            )
        ):
            print(message)
    
    asyncio.run(main())

    これはPreToolUseフックです。ツール実行前に実行され、ロジックに基づいて操作をブロックまたは許可できます。このガイドの残りの部分では、利用可能なすべてのフック、その設定オプション、および一般的なユースケースのパターンについて説明します。

    利用可能なフック

    SDKは、エージェント実行のさまざまなステージのフックを提供します。一部のフックは両方のSDKで利用可能ですが、他のフックはTypeScript専用です。これはPython SDKがそれらをサポートしていないためです。

    フックイベントPython SDKTypeScript SDKトリガーされるもの使用例
    PreToolUseはいはいツール呼び出しリクエスト(ブロックまたは変更可能)危険なシェルコマンドをブロック
    PostToolUseはいはいツール実行結果すべてのファイル変更を監査証跡にログ
    PostToolUseFailureいいえはいツール実行失敗ツールエラーを処理またはログ
    UserPromptSubmitはいはいユーザープロンプト送信プロンプトに追加コンテキストを注入
    Stop

    一般的なユースケース

    フックは多くの異なるシナリオを処理するのに十分な柔軟性があります。以下は、カテゴリ別に整理された最も一般的なパターンの一部です。

    フックを設定する

    エージェントのフックを設定するには、query()を呼び出すときにoptions.hooksパラメータでフックを渡します:

    async for message in query(
        prompt="Your prompt",
        options=ClaudeAgentOptions(
            hooks={
                'PreToolUse': [HookMatcher(matcher='Bash', hooks=[my_callback])]
            }
        )
    ):
        print(message)

    hooksオプションは辞書(Python)またはオブジェクト(TypeScript)です。ここで:

    • キーはフックイベント名(例:'PreToolUse'、'PostToolUse'、'Stop')です
    • 値はマッチャーの配列で、各マッチャーはオプションのフィルタパターンとコールバック関数を含みます

    フックコールバック関数は、イベントについての入力データを受け取り、レスポンスを返して、エージェントに操作を許可、ブロック、または変更するよう指示します。

    マッチャー

    マッチャーを使用して、どのツールがコールバックをトリガーするかをフィルタリングします:

    オプション型デフォルト説明
    matcherstringundefinedツール名をマッチするための正規表現パターン。組み込みツールにはBash、Read、Write、Edit、Glob、Grep、WebFetch、Taskなどが含まれます。MCPツールはパターンmcp__<server>__<action>を使用します。
    hooksHookCallback[]-必須。パターンがマッチしたときに実行するコールバック関数の配列

    可能な限りmatcherパターンを使用して特定のツールをターゲットにします。'Bash'のマッチャーはBashコマンドのみで実行され、パターンを省略するとすべてのツール呼び出しに対してコールバックが実行されます。マッチャーはツール名でのみフィルタリングすることに注意してください。ファイルパスまたは他の引数でフィルタリングするには、コールバック内でtool_input.file_pathをチェックします。

    マッチャーはツールベースのフック(PreToolUse、PostToolUse、PostToolUseFailure、PermissionRequest)にのみ適用されます。Stop、SessionStart、Notificationなどのライフサイクルフックの場合、マッチャーは無視され、そのタイプのすべてのイベントに対してフックが発火します。

    **ツール名の発見:**セッション開始時の初期システムメッセージのtools配列をチェックするか、マッチャーなしでフックを追加してすべてのツール呼び出しをログします。

    MCPツール命名: MCPツールは常にmcp__で始まり、その後にサーバー名とアクション:mcp__<server>__<action>が続きます。例えば、playwrightという名前のサーバーを設定する場合、そのツールはmcp__playwright__browser_screenshot、mcp__playwright__browser_clickなどという名前になります。サーバー名はmcpServers設定で使用するキーから来ます。

    この例は、マッチャーを使用してPreToolUseイベントが発火したときにファイル変更ツールのみでフックを実行します:

    options = ClaudeAgentOptions(
        hooks={
            'PreToolUse': [
                HookMatcher(matcher='Write|Edit', hooks=[validate_file_path])
            ]
        }
    )

    コールバック関数の入力

    すべてのフックコールバックは3つの引数を受け取ります:

    1. 入力データ(dict / HookInput):イベント詳細。フィールドについては入力データを参照してください
    2. ツール使用ID(str | None / string | null):PreToolUseとPostToolUseイベントを相関させます
    3. コンテキスト(HookContext):TypeScriptでは、キャンセル用のsignalプロパティ(AbortSignal)を含みます。fetch()などの非同期操作にこれを渡して、フックがタイムアウトした場合に自動的にキャンセルされるようにします。Pythonでは、この引数は将来の使用のために予約されています。

    入力データ

    フックコールバックの最初の引数には、イベントに関する情報が含まれています。フィールド名はSDK全体で同じです(両方ともsnake_caseを使用)。

    すべてのフックタイプに存在する共通フィールド:

    フィールド型説明
    hook_event_namestringフックタイプ(PreToolUse、PostToolUseなど)
    session_idstring現在のセッション識別子
    transcript_pathstring会話トランスクリプトへのパス
    cwdstring現在の作業ディレクトリ

    フック固有フィールドはフックタイプによって異なります。TSでマークされた項目はTypeScript SDKでのみ利用可能です:

    フィールド型説明フック
    tool_namestring呼び出されるツールの名前PreToolUse、PostToolUse、PostToolUseFailureTS、PermissionRequestTS
    tool_inputobjectツールに渡される引数PreToolUse、PostToolUse、PostToolUseFailureTS、PermissionRequestTS
    tool_responseanyツール実行から返された結果PostToolUse
    errorstringツール実行失敗からのエラーメッセージ

    以下のコードは、tool_nameとtool_inputを使用して各ツール呼び出しの詳細をログするフックコールバックを定義しています:

    コールバック出力

    コールバック関数は、SDKに続行方法を指示するオブジェクトを返します。操作を変更せずに許可するには、空のオブジェクト{}を返します。操作をブロック、変更、またはコンテキストを追加するには、hookSpecificOutputフィールドを含む決定を含むオブジェクトを返します。

    トップレベルフィールド(hookSpecificOutputの外):

    フィールド型説明
    continuebooleanこのフック後にエージェントが続行するかどうか(デフォルト:true)
    stopReasonstringcontinueがfalseの場合に表示されるメッセージ
    suppressOutputbooleanトランスクリプトからstdoutを非表示にする(デフォルト:false)
    systemMessagestringClaudeが見るための会話に注入されるメッセージ

    hookSpecificOutput内のフィールド:

    フィールド型フック説明
    hookEventNamestringすべて必須。現在のイベントをマッチするためにinput.hook_event_nameを使用します
    permissionDecision'allow' | 'deny' | 'ask'PreToolUseツールが実行されるかどうかを制御します
    permissionDecisionReasonstringPreToolUse決定についてClaudeに表示される説明
    updatedInput

    この例は/etcディレクトリへの書き込み操作をブロックしながら、Claudeに安全なファイルプラクティスについて思い出させるシステムメッセージを注入します:

    パーミッション決定フロー

    複数のフックまたはパーミッションルールが適用される場合、SDKはこの順序で評価します:

    1. Denyルールが最初にチェックされます(いずれかがマッチ = 即座に拒否)。
    2. Askルールが2番目にチェックされます。
    3. Allowルールが3番目にチェックされます。
    4. デフォルトはAskです(何もマッチしない場合)。

    フックがdenyを返す場合、操作はブロックされます。allowを返す他のフックはそれをオーバーライドしません。

    ツールをブロックする

    deny決定を返してツール実行を防止します:

    ツール入力を変更する

    更新された入力を返してツールが受け取るものを変更します:

    updatedInputを使用する場合、permissionDecisionも含める必要があります。常に新しいオブジェクトを返し、元のtool_inputを変更しないでください。

    システムメッセージを追加する

    会話にコンテキストを注入します:

    async def add_security_reminder(input_data, tool_use_id, context):
        return {
            'systemMessage': 'Remember to follow security best practices.'
        }

    特定のツールを自動承認する

    信頼できるツールのパーミッションプロンプトをバイパスします。これは、特定の操作がユーザー確認なしで実行されるようにしたい場合に便利です:

    permissionDecisionフィールドは3つの値を受け入れます:'allow'(自動承認)、'deny'(ブロック)、または'ask'(確認を促す)。

    高度なシナリオを処理する

    これらのパターンは、複雑なユースケースのためのより洗練されたフックシステムを構築するのに役立ちます。

    複数のフックをチェーンする

    フックは配列に表示される順序で実行されます。各フックを単一の責任に焦点を当てて、複雑なロジックのために複数のフックをチェーンします。この例は、すべてのツール呼び出しに対して4つのフックすべてを実行します(マッチャーが指定されていません):

    ツール固有のマッチャーと正規表現

    正規表現パターンを使用して複数のツールをマッチします:

    マッチャーはツール名のみをマッチします。ファイルパスまたは他の引数でフィルタリングするには、フックコールバック内でtool_input.file_pathをチェックします。

    サブエージェント活動を追跡する

    SubagentStopフックを使用してサブエージェント完了を監視します。tool_use_idは親エージェント呼び出しとそのサブエージェントを相関させるのに役立ちます:

    フック内の非同期操作

    フックはHTTPリクエストなどの非同期操作を実行できます。例外をスローする代わりに例外をキャッチして、エラーを適切に処理します。TypeScriptでは、signalをfetch()に渡して、フックがタイムアウトした場合にリクエストがキャンセルされるようにします:

    通知を送信する(TypeScript のみ)

    Notificationフックを使用してエージェントからステータス更新を受け取り、Slackやモニタリングダッシュボードなどの外部サービスに転送します:

    TypeScript
    import { query, HookCallback, NotificationHookInput } from "@anthropic-ai/claude-agent-sdk";
    
    const notificationHandler: HookCallback = async (input, toolUseID, { signal }) => {
      const notification = input as NotificationHookInput;
    
      await fetch('https://hooks.slack.com/services/YOUR/WEBHOOK/URL', {
        method: 'POST',
        body: JSON.stringify({
          text: `Agent status: ${notification.message}`
        }),
        signal
      });
    
      return {};
    };
    
    for await (const message of query({
      prompt: "Analyze this codebase",
      options: {
        hooks: {
          Notification: [{ hooks: [notificationHandler] }]
        }
      }
    })) {
      console.log(message);
    }

    一般的な問題を修正する

    このセクションでは、一般的な問題とその解決方法について説明します。

    フックが発火しない

    • フックイベント名が正しく、大文字と小文字が区別されていることを確認してください(preToolUseではなくPreToolUse)
    • マッチャーパターンがツール名と正確にマッチしていることを確認してください
    • フックがoptions.hooksの正しいイベントタイプの下にあることを確認してください
    • SubagentStop、Stop、SessionStart、SessionEnd、Notificationフックの場合、マッチャーは無視されます。これらのフックはそのタイプのすべてのイベントに対して発火します。
    • エージェントがmax_turns制限に達した場合、フックが実行される前にセッションが終了するため、フックが発火しない可能性があります

    マッチャーが期待通りにフィルタリングしない

    マッチャーはツール名のみをマッチします。ファイルパスまたは他の引数でフィルタリングするには、フック内でtool_input.file_pathをチェックします:

    const myHook: HookCallback = async (input, toolUseID, { signal }) => {
      const preInput = input as PreToolUseHookInput;
      const filePath = preInput.tool_input?.file_path as string;
      if (!filePath?.endsWith('.md')) return {};  // マークダウンファイル以外をスキップ
      // マークダウンファイルを処理...
    };

    フックタイムアウト

    • HookMatcher設定でtimeout値を増加させます
    • 3番目のコールバック引数からAbortSignalを使用して、TypeScriptでキャンセルを適切に処理します

    ツールが予期せずブロックされた

    • すべてのPreToolUseフックでpermissionDecision: 'deny'の戻り値をチェックします
    • フックにログを追加して、返されているpermissionDecisionReasonを確認します
    • マッチャーパターンが広すぎないことを確認します(空のマッチャーはすべてのツールをマッチします)

    変更された入力が適用されない

    • updatedInputがhookSpecificOutput内にあり、トップレベルにないことを確認します:

      return {
        hookSpecificOutput: {
          hookEventName: input.hook_event_name,
          permissionDecision: 'allow',
          updatedInput: { command: 'new command' }
        }
      };
    • 入力変更が有効になるには、permissionDecision: 'allow'も返す必要があります

    セッションフックが利用できない

    SessionStart、SessionEnd、NotificationフックはTypeScript SDKでのみ利用可能です。Python SDKはセットアップの制限により、これらのイベントをサポートしていません。

    サブエージェントパーミッションプロンプトが増加する

    複数のサブエージェントを生成する場合、各サブエージェントは個別にパーミッションをリクエストする可能性があります。サブエージェントは親エージェントのパーミッションを自動的に継承しません。繰り返されるプロンプトを避けるには、PreToolUseフックを使用して特定のツールを自動承認するか、サブエージェントセッションに適用されるパーミッションルールを設定します。

    サブエージェントとの再帰的フックループ

    サブエージェントを生成するUserPromptSubmitフックは、それらのサブエージェントが同じフックをトリガーする場合、無限ループを作成できます。これを防ぐには:

    • フック入力でサブエージェント指標をチェックしてからサブエージェントを生成します
    • parent_tool_use_idフィールドを使用して、既にサブエージェントコンテキストにいるかどうかを検出します
    • フックをトップレベルエージェントセッションのみで実行するようにスコープします

    systemMessageが出力に表示されない

    systemMessageフィールドはモデルが見るための会話にコンテキストを追加しますが、すべてのSDK出力モードに表示されない場合があります。フック決定をアプリケーションに表示する必要がある場合は、別途ログするか、専用の出力チャネルを使用します。

    さらに詳しく

    • パーミッション:エージェントが何ができるかを制御する
    • カスタムツール:エージェント機能を拡張するツールを構築する
    • TypeScript SDK リファレンス
    • Python SDK リファレンス
    • 通知を送信する(TypeScript のみ)
    • systemMessageが出力に表示されない
    はい
    はい
    エージェント実行停止
    終了前にセッション状態を保存
    SubagentStartいいえはいサブエージェント初期化並列タスク生成を追跡
    SubagentStopはいはいサブエージェント完了並列タスクの結果を集約
    PreCompactはいはい会話圧縮リクエスト要約前に完全なトランスクリプトをアーカイブ
    PermissionRequestいいえはいパーミッションダイアログが表示されるカスタムパーミッション処理
    SessionStartいいえはいセッション初期化ログとテレメトリを初期化
    SessionEndいいえはいセッション終了一時的なリソースをクリーンアップ
    Notificationいいえはいエージェントステータスメッセージエージェントステータス更新をSlackまたはPagerDutyに送信
    timeoutnumber60タイムアウト(秒単位)。外部APIを呼び出すフックの場合は増加させます
    PostToolUseFailureTS
    is_interruptboolean失敗が割り込みによって引き起こされたかどうかPostToolUseFailureTS
    promptstringユーザーのプロンプトテキストUserPromptSubmit
    stop_hook_activebooleanストップフックが現在処理中かどうかStop、SubagentStop
    agent_idstringサブエージェントの一意の識別子SubagentStartTS、SubagentStopTS
    agent_typestringサブエージェントのタイプ/ロールSubagentStartTS
    agent_transcript_pathstringサブエージェントの会話トランスクリプトへのパスSubagentStopTS
    triggerstring圧縮をトリガーしたもの:manualまたはautoPreCompact
    custom_instructionsstring圧縮用に提供されたカスタム指示PreCompact
    permission_suggestionsarrayツールの推奨パーミッション更新PermissionRequestTS
    sourcestringセッションの開始方法:startup、resume、clear、またはcompactSessionStartTS
    reasonstringセッションが終了した理由:clear、logout、prompt_input_exit、bypass_permissions_disabled、またはotherSessionEndTS
    messagestringエージェントからのステータスメッセージNotificationTS
    notification_typestring通知のタイプ:permission_prompt、idle_prompt、auth_success、またはelicitation_dialogNotificationTS
    titlestringエージェントによって設定されたオプションのタイトルNotificationTS
    async def log_tool_calls(input_data, tool_use_id, context):
        if input_data['hook_event_name'] == 'PreToolUse':
            print(f"Tool: {input_data['tool_name']}")
            print(f"Input: {input_data['tool_input']}")
        return {}
    object
    PreToolUse
    変更されたツール入力(permissionDecision: 'allow'が必要)
    additionalContextstringPostToolUse、UserPromptSubmit、SessionStartTS、SubagentStartTS会話に追加されるコンテキスト
    async def block_etc_writes(input_data, tool_use_id, context):
        file_path = input_data['tool_input'].get('file_path', '')
    
        if file_path.startswith('/etc'):
            return {
                # トップレベルフィールド:会話にガイダンスを注入する
                'systemMessage': 'Remember: system directories like /etc are protected.',
                # hookSpecificOutput:操作をブロックする
                'hookSpecificOutput': {
                    'hookEventName': input_data['hook_event_name'],
                    'permissionDecision': 'deny',
                    'permissionDecisionReason': 'Writing to /etc is not allowed'
                }
            }
        return {}
    async def block_dangerous_commands(input_data, tool_use_id, context):
        if input_data['hook_event_name'] != 'PreToolUse':
            return {}
    
        command = input_data['tool_input'].get('command', '')
    
        if 'rm -rf /' in command:
            return {
                'hookSpecificOutput': {
                    'hookEventName': input_data['hook_event_name'],
                    'permissionDecision': 'deny',
                    'permissionDecisionReason': 'Dangerous command blocked: rm -rf /'
                }
            }
        return {}
    async def redirect_to_sandbox(input_data, tool_use_id, context):
        if input_data['hook_event_name'] != 'PreToolUse':
            return {}
    
        if input_data['tool_name'] == 'Write':
            original_path = input_data['tool_input'].get('file_path', '')
            return {
                'hookSpecificOutput': {
                    'hookEventName': input_data['hook_event_name'],
                    'permissionDecision': 'allow',
                    'updatedInput': {
                        **input_data['tool_input'],
                        'file_path': f'/sandbox{original_path}'
                    }
                }
            }
        return {}
    async def auto_approve_read_only(input_data, tool_use_id, context):
        if input_data['hook_event_name'] != 'PreToolUse':
            return {}
    
        read_only_tools = ['Read', 'Glob', 'Grep', 'LS']
        if input_data['tool_name'] in read_only_tools:
            return {
                'hookSpecificOutput': {
                    'hookEventName': input_data['hook_event_name'],
                    'permissionDecision': 'allow',
                    'permissionDecisionReason': 'Read-only tool auto-approved'
                }
            }
        return {}
    options = ClaudeAgentOptions(
        hooks={
            'PreToolUse': [
                HookMatcher(hooks=[rate_limiter]),        # 最初:レート制限をチェック
                HookMatcher(hooks=[authorization_check]), # 2番目:パーミッションを確認
                HookMatcher(hooks=[input_sanitizer]),     # 3番目:入力をサニタイズ
                HookMatcher(hooks=[audit_logger])         # 最後:アクションをログ
            ]
        }
    )
    options = ClaudeAgentOptions(
        hooks={
            'PreToolUse': [
                # ファイル変更ツールをマッチ
                HookMatcher(matcher='Write|Edit|Delete', hooks=[file_security_hook]),
    
                # すべてのMCPツールをマッチ
                HookMatcher(matcher='^mcp__', hooks=[mcp_audit_hook]),
    
                # すべてをマッチ(マッチャーなし)
                HookMatcher(hooks=[global_logger])
            ]
        }
    )
    async def subagent_tracker(input_data, tool_use_id, context):
        if input_data['hook_event_name'] == 'SubagentStop':
            print(f"[SUBAGENT] Completed")
            print(f"  Tool use ID: {tool_use_id}")
            print(f"  Stop hook active: {input_data.get('stop_hook_active')}")
        return {}
    
    options = ClaudeAgentOptions(
        hooks={
            'SubagentStop': [HookMatcher(hooks=[subagent_tracker])]
        }
    )
    import aiohttp
    from datetime import datetime
    
    async def webhook_notifier(input_data, tool_use_id, context):
        if input_data['hook_event_name'] != 'PostToolUse':
            return {}
    
        try:
            async with aiohttp.ClientSession() as session:
                await session.post(
                    'https://api.example.com/webhook',
                    json={
                        'tool': input_data['tool_name'],
                        'timestamp': datetime.now().isoformat()
                    }
                )
        except Exception as e:
            print(f'Webhook request failed: {e}')
    
        return {}

    どのフックタイプの出力かを識別するために、hookSpecificOutputにhookEventNameを含めます