メインコンテンツへスキップ
タスクに取り組んでいる間、Claude はユーザーに確認を取る必要がある場合があります。ファイルを削除する前に許可が必要な場合もあれば、新しいプロジェクト用にどのデータベースを使用するかを尋ねる必要がある場合もあります。アプリケーションはこれらのリクエストをユーザーに表示して、Claude がユーザーの入力で続行できるようにする必要があります。 Claude がユーザー入力をリクエストするのは 2 つの状況です。ツールを使用する許可が必要な場合(ファイルの削除やコマンドの実行など)と、確認質問がある場合AskUserQuestion ツール経由)です。どちらも canUseTool コールバックをトリガーし、応答を返すまで実行を一時停止します。これは Claude が終了して次のメッセージを待つ通常の会話ターンとは異なります。 確認質問については、Claude が質問とオプションを生成します。あなたの役割は、それらをユーザーに提示して、ユーザーの選択を返すことです。このフローに独自の質問を追加することはできません。ユーザーに何か尋ねる必要がある場合は、アプリケーションロジックで別途実行してください。 コールバックは無期限に保留中のままにすることができます。実行はコールバックが返されるまで一時停止したままであり、SDK はクエリ自体がキャンセルされた場合にのみ待機をキャンセルします。ユーザーがプロセスが合理的に実行し続けることができるより長く応答するのに時間がかかる可能性がある場合、defer フック決定を返します。これにより、プロセスを終了して、後で永続化されたセッションから再開できます。 このガイドでは、各タイプのリクエストを検出し、適切に応答する方法を示します。

Claude が入力を必要とする場合を検出する

クエリオプションで canUseTool コールバックを渡します。Claude がユーザー入力を必要とするたびにコールバックが発火し、ツール名と入力を引数として受け取ります。
async def handle_tool_request(tool_name, input_data, context):
    # ユーザーにプロンプトを表示して、許可または拒否を返す
    ...


options = ClaudeAgentOptions(can_use_tool=handle_tool_request)
コールバックは 2 つのケースで発火します。
  1. ツールが承認を必要とする場合:Claude が 許可ルールまたはモードによって自動承認されていないツールを使用したい場合。tool_name でツール(例:"Bash""Write")を確認します。
  2. Claude が質問をする場合:Claude が AskUserQuestion ツールを呼び出します。tool_name == "AskUserQuestion" をチェックして、異なる方法で処理します。tools 配列を指定する場合は、これが機能するように AskUserQuestion を含めます。詳細は 確認質問を処理するを参照してください。
ユーザーにプロンプトを表示せずにツールを自動的に許可または拒否するには、代わりに フックを使用します。フックは canUseTool の前に実行され、独自のロジックに基づいてリクエストを許可、拒否、または変更できます。また、PermissionRequest フックを使用して、Claude が承認を待っているときに外部通知(Slack、メール、プッシュ)を送信することもできます。

ツール承認リクエストを処理する

クエリオプションで canUseTool コールバックを渡すと、Claude が自動承認されていないツールを使用したい場合に発火します。コールバックは 3 つの引数を受け取ります。
引数説明
toolNameClaude が使用したいツールの名前(例:"Bash""Write""Edit"
inputClaude がツールに渡しているパラメーター。内容はツールによって異なります。
options(TS)/ context(Python)再度プロンプトを表示しないための提案された PermissionUpdate エントリを含むオプション suggestions とキャンセル信号を含む追加コンテキスト。TypeScript では、signalAbortSignal です。Python では、信号フィールドは将来の使用のために予約されています。Python については ToolPermissionContextを参照してください。
input オブジェクトにはツール固有のパラメーターが含まれます。一般的な例:
ツール入力フィールド
Bashcommanddescriptiontimeout
Writefile_pathcontent
Editfile_pathold_stringnew_string
Readfile_pathoffsetlimit
完全な入力スキーマについては SDK リファレンスを参照してください。Python | TypeScript この情報をユーザーに表示して、アクションを許可するか拒否するかを決定してから、適切な応答を返すことができます。 次の例では、Claude にテストファイルを作成して削除するよう要求します。Claude が各操作を試みるたびに、コールバックはツールリクエストをターミナルに出力し、y/n 承認を求めます。
import asyncio

from claude_agent_sdk import ClaudeAgentOptions, ResultMessage, query
from claude_agent_sdk.types import (
    HookMatcher,
    PermissionResultAllow,
    PermissionResultDeny,
    ToolPermissionContext,
)


async def can_use_tool(
    tool_name: str, input_data: dict, context: ToolPermissionContext
) -> PermissionResultAllow | PermissionResultDeny:
    # ツールリクエストを表示する
    print(f"\nTool: {tool_name}")
    if tool_name == "Bash":
        print(f"Command: {input_data.get('command')}")
        if input_data.get("description"):
            print(f"Description: {input_data.get('description')}")
    else:
        print(f"Input: {input_data}")

    # ユーザーの承認を取得する
    response = input("Allow this action? (y/n): ")

    # ユーザーの応答に基づいて許可または拒否を返す
    if response.lower() == "y":
        # 許可:ツールは元の(または変更された)入力で実行される
        return PermissionResultAllow(updated_input=input_data)
    else:
        # 拒否:ツールは実行されず、Claude はメッセージを見る
        return PermissionResultDeny(message="User denied this action")


# 必須の回避策:ダミーフックはストリームを canUseTool 用に開いたままにします
async def dummy_hook(input_data, tool_use_id, context):
    return {"continue_": True}


async def prompt_stream():
    yield {
        "type": "user",
        "message": {
            "role": "user",
            "content": "Create a test file in /tmp and then delete it",
        },
    }


async def main():
    async for message in query(
        prompt=prompt_stream(),
        options=ClaudeAgentOptions(
            can_use_tool=can_use_tool,
            hooks={"PreToolUse": [HookMatcher(matcher=None, hooks=[dummy_hook])]},
        ),
    ):
        if isinstance(message, ResultMessage) and message.subtype == "success":
            print(message.result)


asyncio.run(main())
Python では、can_use_toolストリーミングモード{"continue_": True} を返す PreToolUse フックが必要で、ストリームを開いたままにします。このフックがないと、許可コールバックが呼び出される前にストリームが閉じます。
この例では y/n フローを使用しており、y 以外の入力は拒否として扱われます。実際には、ユーザーがリクエストを変更したり、フィードバックを提供したり、Claude を完全にリダイレクトしたりできるより豊富な UI を構築する可能性があります。すべての応答方法については ツールリクエストに応答するを参照してください。

ツールリクエストに応答する

コールバックは 2 つの応答タイプのいずれかを返します。
応答PythonTypeScript
許可PermissionResultAllow(updated_input=...){ behavior: "allow", updatedInput }
拒否PermissionResultDeny(message=...){ behavior: "deny", message }
許可する場合、ツール入力(元の、または変更された)を渡します。拒否する場合、理由を説明するメッセージを提供します。Claude はこのメッセージを見て、アプローチを調整する可能性があります。
from claude_agent_sdk.types import PermissionResultAllow, PermissionResultDeny

# ツールの実行を許可する
return PermissionResultAllow(updated_input=input_data)

# ツールをブロックする
return PermissionResultDeny(message="User rejected this action")
許可または拒否を超えて、ツールの入力を変更したり、Claude がアプローチを調整するのに役立つコンテキストを提供したりできます。
  • 承認:ツールを Claude がリクエストしたとおりに実行させる
  • 変更を加えて承認:実行前に入力を変更する(例:パスをサニタイズ、制約を追加)
  • 承認して記憶:提案された許可ルールをエコーバックして、一致する呼び出しが次回プロンプトをスキップするようにする
  • 拒否:ツールをブロックして Claude に理由を伝える
  • 代替案を提案:ブロックするが、ユーザーが望むものに向かって Claude をガイドする
  • 完全にリダイレクトストリーミング入力を使用して Claude に完全に新しい指示を送信する
ユーザーはアクションをそのまま承認します。コールバックから input をそのまま渡し、ツールは Claude がリクエストしたとおりに実行されます。
async def can_use_tool(tool_name, input_data, context):
    print(f"Claude wants to use {tool_name}")
    approved = await ask_user("Allow this action?")

    if approved:
        return PermissionResultAllow(updated_input=input_data)
    return PermissionResultDeny(message="User declined")

確認質問を処理する

Claude が複数の有効なアプローチを持つタスクについてさらに方向性が必要な場合、AskUserQuestion ツールを呼び出します。これは toolNameAskUserQuestion に設定された canUseTool コールバックをトリガーします。入力には Claude の質問が複数選択肢として含まれており、これらをユーザーに表示して、ユーザーの選択を返します。
確認質問は特に plan モードで一般的です。Claude はコードベースを探索し、計画を提案する前に質問をします。これにより、プラン モードは Claude が変更を加える前に要件を収集したい対話的なワークフローに最適です。
次のステップは、確認質問を処理する方法を示しています。
1

canUseTool コールバックを渡す

クエリオプションで canUseTool コールバックを渡します。デフォルトでは、AskUserQuestion が利用可能です。Claude の機能を制限するために tools 配列を指定する場合(例:ReadGlobGrep のみを持つ読み取り専用エージェント)、その配列に AskUserQuestion を含めます。そうしないと、Claude は確認質問をすることができません。
async for message in query(
    prompt="Analyze this codebase",
    options=ClaudeAgentOptions(
        # ツールリストに AskUserQuestion を含める
        tools=["Read", "Glob", "Grep", "AskUserQuestion"],
        can_use_tool=can_use_tool,
    ),
):
    print(message)
2

AskUserQuestion を検出する

コールバックで、toolNameAskUserQuestion と等しいかどうかをチェックして、他のツールとは異なる方法で処理します。
async def can_use_tool(tool_name: str, input_data: dict, context):
    if tool_name == "AskUserQuestion":
        # ユーザーから回答を収集するための実装
        return await handle_clarifying_questions(input_data)
    # 他のツールを通常どおり処理する
    return await prompt_for_approval(tool_name, input_data)
3

質問入力を解析する

入力には Claude の質問が questions 配列に含まれています。各質問には question(表示するテキスト)、options(選択肢)、multiSelect(複数選択が許可されているかどうか)があります。
{
  "questions": [
    {
      "question": "How should I format the output?",
      "header": "Format",
      "options": [
        { "label": "Summary", "description": "Brief overview" },
        { "label": "Detailed", "description": "Full explanation" }
      ],
      "multiSelect": false
    },
    {
      "question": "Which sections should I include?",
      "header": "Sections",
      "options": [
        { "label": "Introduction", "description": "Opening context" },
        { "label": "Conclusion", "description": "Final summary" }
      ],
      "multiSelect": true
    }
  ]
}
完全なフィールド説明については 質問形式を参照してください。
4

ユーザーから回答を収集する

質問をユーザーに提示して、ユーザーの選択を収集します。これをどのように行うかは、アプリケーションによって異なります。ターミナルプロンプト、Web フォーム、モバイルダイアログなど。
5

Claude に回答を返す

answers オブジェクトをレコードとして構築します。各キーは question テキストで、各値は選択されたオプションの label です。
質問オブジェクトから使用方法
question フィールド(例:"How should I format the output?"キー
選択されたオプションの label フィールド(例:"Summary"
複数選択質問の場合、ラベルの配列を渡すか、", " で結合します。自由テキスト入力をサポートする場合は、ユーザーのカスタムテキストを値として使用します。
return PermissionResultAllow(
    updated_input={
        "questions": input_data.get("questions", []),
        "answers": {
            "How should I format the output?": "Summary",
            "Which sections should I include?": ["Introduction", "Conclusion"],
        },
    }
)

質問形式

入力には Claude が生成した質問が questions 配列に含まれています。各質問には以下のフィールドがあります。
フィールド説明
question表示する完全な質問テキスト
header質問の短いラベル(最大 12 文字)
options2~4 個の選択肢の配列。各選択肢には labeldescription があります。TypeScript:オプションで preview(下記の オプションプレビューを参照)
multiSelecttrue の場合、ユーザーは複数のオプションを選択できます
コールバックが受け取る構造:
{
  "questions": [
    {
      "question": "How should I format the output?",
      "header": "Format",
      "options": [
        { "label": "Summary", "description": "Brief overview of key points" },
        { "label": "Detailed", "description": "Full explanation with examples" }
      ],
      "multiSelect": false
    }
  ]
}

オプションプレビュー(TypeScript)

toolConfig.askUserQuestion.previewFormat は各オプションに preview フィールドを追加して、アプリがラベルと一緒に視覚的なモックアップを表示できるようにします。この設定がない場合、Claude はプレビューを生成せず、フィールドは存在しません。
previewFormatpreview に含まれるもの
未設定(デフォルト)フィールドは存在しません。Claude はプレビューを生成しません。
"markdown"ASCII アートとフェンスコードブロック
"html"スタイル付き <div> フラグメント(SDK はコールバックが実行される前に <script><style><!DOCTYPE> を拒否します)
形式はセッション内のすべての質問に適用されます。Claude は視覚的な比較が役立つオプション(レイアウト選択、配色)に preview を含め、役立たないオプション(はい/いいえの確認、テキストのみの選択)では省略します。レンダリング前に undefined をチェックしてください。
import { query } from "@anthropic-ai/claude-agent-sdk";

for await (const message of query({
  prompt: "Help me choose a card layout",
  options: {
    toolConfig: {
      askUserQuestion: { previewFormat: "html" }
    },
    canUseTool: async (toolName, input) => {
      // input.questions[].options[].preview は HTML 文字列または undefined です
      return { behavior: "allow", updatedInput: input };
    }
  }
})) {
  // ...
}
HTML プレビュー付きのオプション:
{
  "label": "Compact",
  "description": "Title and metric value only",
  "preview": "<div style=\"padding:12px;border:1px solid #ddd;border-radius:8px\"><div style=\"font-size:12px;color:#666\">Active users</div><div style=\"font-size:28px;font-weight:600\">1,284</div></div>"
}

応答形式

各質問の question フィールドを選択されたオプションの label にマップする answers オブジェクトを返します。
フィールド説明
questions元の質問配列をパススルーする(ツール処理に必須)
answersキーが質問テキストで、値が選択されたラベルであるオブジェクト
responseユーザーが構造化された質問に答える代わりに入力した、オプションの自由形式の返信
複数選択質問の場合、ラベルの配列を渡すか、", " で結合します。自由テキスト入力をサポートに示されているような質問ごとの自由テキスト(例:「その他」オプション)の場合は、ユーザーのテキストを answers[question] に入力します。response は、ユーザーが質問カードを閉じて、特定の質問への回答ではない一般的な返信を入力できる UI の場合にのみ設定します。response が設定されている場合、Claude は質問ごとの回答リストではなく「ユーザーが応答しました:…」を受け取ります。
{
  "questions": [
    // ...
  ],
  "answers": {
    "How should I format the output?": "Summary",
    "Which sections should I include?": ["Introduction", "Conclusion"]
  }
}

自由テキスト入力をサポートする

Claude の定義済みオプションがユーザーが望むものをカバーしていない場合があります。ユーザーが独自の回答を入力できるようにするには:
  • Claude のオプションの後に追加の「その他」選択肢を表示して、テキスト入力を受け入れます
  • ユーザーのカスタムテキストを回答値として使用します(「その他」という単語ではなく)
完全な実装については、下記の 完全な例を参照してください。

完全な例

Claude は、タスクを進めるためにユーザー入力が必要な場合に確認質問をします。たとえば、モバイルアプリのテックスタックを決定するのに役立つよう求められた場合、Claude はクロスプラットフォーム対応 vs ネイティブ、バックエンド設定、またはターゲットプラットフォームについて質問する可能性があります。これらの質問は、Claude がユーザーの設定に合致する決定を下すのに役立ちます。推測ではなく。 この例は、ターミナルアプリケーションでこれらの質問を処理します。各ステップで何が起こるかは以下の通りです。
  1. リクエストをルーティングするcanUseTool コールバックはツール名が "AskUserQuestion" であるかどうかをチェックし、専用ハンドラーにルーティングします
  2. 質問を表示する:ハンドラーは questions 配列をループして、各質問を番号付きオプションで出力します
  3. 入力を収集する:ユーザーはオプションを選択する番号を入力するか、自由テキストを直接入力できます(例:「jquery」、「i don’t know」)
  4. 回答をマップする:コードは入力が数値(オプションのラベルを使用)か自由テキスト(テキストを直接使用)かをチェックします
  5. Claude に返す:応答には元の questions 配列と answers マッピングの両方が含まれます
import asyncio

from claude_agent_sdk import ClaudeAgentOptions, ResultMessage, query
from claude_agent_sdk.types import HookMatcher, PermissionResultAllow


def parse_response(response: str, options: list) -> str:
    """ユーザー入力をオプション番号または自由テキストとして解析します。"""
    try:
        indices = [int(s.strip()) - 1 for s in response.split(",")]
        labels = [options[i]["label"] for i in indices if 0 <= i < len(options)]
        return ", ".join(labels) if labels else response
    except ValueError:
        return response


async def handle_ask_user_question(input_data: dict) -> PermissionResultAllow:
    """Claude の質問を表示してユーザーの回答を収集します。"""
    answers = {}

    for q in input_data.get("questions", []):
        print(f"\n{q['header']}: {q['question']}")

        options = q["options"]
        for i, opt in enumerate(options):
            print(f"  {i + 1}. {opt['label']} - {opt['description']}")
        if q.get("multiSelect"):
            print("  (Enter numbers separated by commas, or type your own answer)")
        else:
            print("  (Enter a number, or type your own answer)")

        response = input("Your choice: ").strip()
        answers[q["question"]] = parse_response(response, options)

    return PermissionResultAllow(
        updated_input={
            "questions": input_data.get("questions", []),
            "answers": answers,
        }
    )


async def can_use_tool(
    tool_name: str, input_data: dict, context
) -> PermissionResultAllow:
    # AskUserQuestion を質問ハンドラーにルーティングする
    if tool_name == "AskUserQuestion":
        return await handle_ask_user_question(input_data)
    # この例では他のツールを自動承認する
    return PermissionResultAllow(updated_input=input_data)


async def prompt_stream():
    yield {
        "type": "user",
        "message": {
            "role": "user",
            "content": "Help me decide on the tech stack for a new mobile app",
        },
    }


# 必須の回避策:ダミーフックはストリームを canUseTool 用に開いたままにします
async def dummy_hook(input_data, tool_use_id, context):
    return {"continue_": True}


async def main():
    async for message in query(
        prompt=prompt_stream(),
        options=ClaudeAgentOptions(
            can_use_tool=can_use_tool,
            hooks={"PreToolUse": [HookMatcher(matcher=None, hooks=[dummy_hook])]},
        ),
    ):
        if isinstance(message, ResultMessage) and message.subtype == "success":
            print(message.result)


asyncio.run(main())

制限事項

  • サブエージェントAskUserQuestion は現在、Agent ツール経由で生成されたサブエージェントでは利用できません
  • 質問の制限:各 AskUserQuestion 呼び出しは 1~4 個の質問と 2~4 個のオプションをサポートします

ユーザー入力を取得する他の方法

canUseTool コールバックと AskUserQuestion ツールはほとんどの承認と明確化のシナリオをカバーしていますが、SDK はユーザーから入力を取得する他の方法を提供しています。

ストリーミング入力

以下が必要な場合は ストリーミング入力を使用します。
  • エージェントをタスク途中で中断する:Claude が作業中にキャンセル信号を送信するか、方向を変更する
  • 追加コンテキストを提供する:Claude が尋ねるのを待たずに、Claude が必要とする情報を追加する
  • チャットインターフェースを構築する:長時間実行される操作中にユーザーがエージェントと対話できるようにする
ストリーミング入力は、承認チェックポイントだけでなく、実行全体を通じてユーザーがエージェントと対話する会話型 UI に最適です。

カスタムツール

以下が必要な場合は カスタムツールを使用します。
  • 構造化入力を収集するAskUserQuestion の複数選択形式を超えたフォーム、ウィザード、または複数ステップのワークフローを構築する
  • 外部承認システムを統合する:既存のチケット、ワークフロー、または承認プラットフォームに接続する
  • ドメイン固有の対話を実装する:コードレビューインターフェースやデプロイメントチェックリストなど、アプリケーションのニーズに合わせたツールを作成する
カスタムツールは対話を完全に制御できますが、組み込みの canUseTool コールバックを使用するよりも実装作業が必要です。

関連リソース