Pular para o conteúdo principal
Ao trabalhar em uma tarefa, Claude às vezes precisa verificar com os usuários. Pode precisar de permissão antes de excluir arquivos ou precisar perguntar qual banco de dados usar para um novo projeto. Seu aplicativo precisa apresentar essas solicitações aos usuários para que Claude possa continuar com sua entrada. Claude solicita entrada do usuário em duas situações: quando precisa de permissão para usar uma ferramenta (como excluir arquivos ou executar comandos) e quando tem perguntas de esclarecimento (por meio da ferramenta AskUserQuestion). Ambas acionam seu callback canUseTool, que pausa a execução até que você retorne uma resposta. Isso é diferente dos turnos de conversa normais, onde Claude termina e aguarda sua próxima mensagem. Para perguntas de esclarecimento, Claude gera as perguntas e opções. Seu papel é apresentá-las aos usuários e retornar suas seleções. Você não pode adicionar suas próprias perguntas a este fluxo; se precisar perguntar algo aos usuários, faça isso separadamente na lógica do seu aplicativo. O callback pode permanecer pendente indefinidamente. A execução permanece pausada até que seu callback retorne, e o SDK apenas cancela a espera quando a própria consulta é cancelada. Se um usuário puder levar mais tempo para responder do que seu processo pode razoavelmente permanecer em execução, retorne a decisão do hook defer, que permite que o processo saia e retome mais tarde a partir da sessão persistida. Este guia mostra como detectar cada tipo de solicitação e responder apropriadamente.

Detectar quando Claude precisa de entrada

Passe um callback canUseTool nas opções de sua consulta. O callback é acionado sempre que Claude precisa de entrada do usuário, recebendo o nome da ferramenta e a entrada como argumentos:
async def handle_tool_request(tool_name, input_data, context):
    # Solicite ao usuário e retorne permitir ou negar
    ...


options = ClaudeAgentOptions(can_use_tool=handle_tool_request)
O callback é acionado em dois casos:
  1. Ferramenta precisa de aprovação: Claude quer usar uma ferramenta que não é aprovada automaticamente por regras de permissão ou modos. Verifique tool_name para a ferramenta (por exemplo, "Bash", "Write").
  2. Claude faz uma pergunta: Claude chama a ferramenta AskUserQuestion. Verifique se tool_name == "AskUserQuestion" para tratá-la diferentemente. Se você especificar um array tools, inclua AskUserQuestion para que isso funcione. Veja Lidar com perguntas de esclarecimento para detalhes.
Para permitir ou negar automaticamente ferramentas sem solicitar aos usuários, use hooks em vez disso. Os hooks são executados antes de canUseTool e podem permitir, negar ou modificar solicitações com base em sua própria lógica. Você também pode usar o hook PermissionRequest para enviar notificações externas (Slack, email, push) quando Claude está aguardando aprovação.

Lidar com solicitações de aprovação de ferramentas

Depois de passar um callback canUseTool nas opções de sua consulta, ele é acionado quando Claude quer usar uma ferramenta que não é aprovada automaticamente. Seu callback recebe três argumentos:
ArgumentoDescrição
toolNameO nome da ferramenta que Claude quer usar (por exemplo, "Bash", "Write", "Edit")
inputOs parâmetros que Claude está passando para a ferramenta. O conteúdo varia por ferramenta.
options (TS) / context (Python)Contexto adicional incluindo suggestions opcional (entradas PermissionUpdate propostas para evitar re-solicitação) e um sinal de cancelamento. Em TypeScript, signal é um AbortSignal; em Python, o campo de sinal é reservado para uso futuro. Veja ToolPermissionContext para Python.
O objeto input contém parâmetros específicos da ferramenta. Exemplos comuns:
FerramentaCampos de entrada
Bashcommand, description, timeout
Writefile_path, content
Editfile_path, old_string, new_string
Readfile_path, offset, limit
Veja a referência do SDK para esquemas de entrada completos: Python | TypeScript. Você pode exibir essas informações ao usuário para que ele possa decidir se permite ou rejeita a ação, e então retornar a resposta apropriada. O exemplo a seguir pede ao Claude para criar e excluir um arquivo de teste. Quando Claude tenta cada operação, o callback imprime a solicitação de ferramenta no terminal e solicita aprovação s/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:
    # Exiba a solicitação de ferramenta
    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}")

    # Obtenha aprovação do usuário
    response = input("Allow this action? (y/n): ")

    # Retorne permitir ou negar com base na resposta do usuário
    if response.lower() == "y":
        # Permitir: ferramenta executa com a entrada original (ou modificada)
        return PermissionResultAllow(updated_input=input_data)
    else:
        # Negar: ferramenta não executa, Claude vê a mensagem
        return PermissionResultDeny(message="User denied this action")


# Solução alternativa necessária: hook fictício mantém o fluxo aberto para can_use_tool
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())
Em Python, can_use_tool requer modo de streaming e um hook PreToolUse que retorna {"continue_": True} para manter o fluxo aberto. Sem este hook, o fluxo fecha antes que o callback de permissão possa ser invocado.
Este exemplo usa um fluxo s/n onde qualquer entrada diferente de s é tratada como uma negação. Na prática, você pode construir uma interface de usuário mais rica que permite aos usuários modificar a solicitação, fornecer feedback ou redirecionar Claude completamente. Veja Responder a solicitações de ferramentas para todas as maneiras que você pode responder.

Responder a solicitações de ferramentas

Seu callback retorna um de dois tipos de resposta:
RespostaPythonTypeScript
PermitirPermissionResultAllow(updated_input=...){ behavior: "allow", updatedInput }
NegarPermissionResultDeny(message=...){ behavior: "deny", message }
Ao permitir, passe a entrada da ferramenta (original ou modificada). Ao negar, forneça uma mensagem explicando por quê. Claude vê esta mensagem e pode ajustar sua abordagem.
from claude_agent_sdk.types import PermissionResultAllow, PermissionResultDeny

# Permita que a ferramenta execute
return PermissionResultAllow(updated_input=input_data)

# Bloqueie a ferramenta
return PermissionResultDeny(message="User rejected this action")
Além de permitir ou negar, você pode modificar a entrada da ferramenta ou fornecer contexto que ajude Claude a ajustar sua abordagem:
  • Aprovar: deixe a ferramenta executar conforme Claude solicitou
  • Aprovar com alterações: modifique a entrada antes da execução (por exemplo, sanitize caminhos, adicione restrições)
  • Aprovar e lembrar: repita uma regra de permissão sugerida para que chamadas correspondentes ignorem o prompt na próxima vez
  • Rejeitar: bloqueie a ferramenta e diga ao Claude por quê
  • Sugerir alternativa: bloqueie mas guie Claude para o que o usuário quer em vez disso
  • Redirecionar completamente: use entrada de streaming para enviar ao Claude uma instrução completamente nova
O usuário aprova a ação como está. Passe a input do seu callback inalterada e a ferramenta executa exatamente como Claude solicitou.
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")

Lidar com perguntas de esclarecimento

Quando Claude precisa de mais direção em uma tarefa com múltiplas abordagens válidas, ele chama a ferramenta AskUserQuestion. Isso aciona seu callback canUseTool com toolName definido como AskUserQuestion. A entrada contém as perguntas do Claude como opções de múltipla escolha, que você exibe ao usuário e retorna suas seleções.
Perguntas de esclarecimento são especialmente comuns no modo plan, onde Claude explora a base de código e faz perguntas antes de propor um plano. Isso torna o modo plan ideal para fluxos de trabalho interativos onde você quer que Claude reúna requisitos antes de fazer alterações.
Os passos a seguir mostram como lidar com perguntas de esclarecimento:
1

Passe um callback canUseTool

Passe um callback canUseTool nas opções de sua consulta. Por padrão, AskUserQuestion está disponível. Se você especificar um array tools para restringir as capacidades do Claude (por exemplo, um agente somente leitura com apenas Read, Glob e Grep), inclua AskUserQuestion nesse array. Caso contrário, Claude não será capaz de fazer perguntas de esclarecimento:
async for message in query(
    prompt="Analyze this codebase",
    options=ClaudeAgentOptions(
        # Inclua AskUserQuestion em sua lista de ferramentas
        tools=["Read", "Glob", "Grep", "AskUserQuestion"],
        can_use_tool=can_use_tool,
    ),
):
    print(message)
2

Detecte AskUserQuestion

Em seu callback, verifique se toolName é igual a AskUserQuestion para tratá-lo diferentemente de outras ferramentas:
async def can_use_tool(tool_name: str, input_data: dict, context):
    if tool_name == "AskUserQuestion":
        # Sua implementação para coletar respostas do usuário
        return await handle_clarifying_questions(input_data)
    # Lidar com outras ferramentas normalmente
    return await prompt_for_approval(tool_name, input_data)
3

Analise a entrada da pergunta

A entrada contém as perguntas do Claude em um array questions. Cada pergunta tem uma question (o texto a exibir), options (as escolhas) e multiSelect (se múltiplas seleções são permitidas):
{
  "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
    }
  ]
}
Veja Formato de pergunta para descrições completas de campos.
4

Colete respostas do usuário

Apresente as perguntas ao usuário e colete suas seleções. Como você faz isso depende de seu aplicativo: um prompt de terminal, um formulário web, um diálogo móvel, etc.
5

Retorne respostas ao Claude

Construa o objeto answers como um registro onde cada chave é o texto question e cada valor é o label da opção selecionada:
Do objeto de perguntaUse como
Campo question (por exemplo, "How should I format the output?")Chave
Campo label da opção selecionada (por exemplo, "Summary")Valor
Para perguntas de seleção múltipla, passe um array de labels ou junte-os com ", ". Se você suportar entrada de texto livre, use o texto personalizado do usuário como o valor.
return PermissionResultAllow(
    updated_input={
        "questions": input_data.get("questions", []),
        "answers": {
            "How should I format the output?": "Summary",
            "Which sections should I include?": ["Introduction", "Conclusion"],
        },
    }
)

Formato de pergunta

A entrada contém as perguntas geradas pelo Claude em um array questions. Cada pergunta tem estes campos:
CampoDescrição
questionO texto completo da pergunta a exibir
headerRótulo curto para a pergunta (máximo 12 caracteres)
optionsArray de 2-4 escolhas, cada uma com label e description. TypeScript: opcionalmente preview (veja abaixo)
multiSelectSe true, os usuários podem selecionar múltiplas opções
A estrutura que seu callback recebe:
{
  "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
    }
  ]
}

Visualizações de opção (TypeScript)

toolConfig.askUserQuestion.previewFormat adiciona um campo preview a cada opção para que seu aplicativo possa mostrar uma simulação visual ao lado do rótulo. Sem esta configuração, Claude não gera visualizações e o campo está ausente.
previewFormatpreview contém
não definido (padrão)Campo está ausente. Claude não gera visualizações.
"markdown"Arte ASCII e blocos de código cercados
"html"Um fragmento <div> estilizado (o SDK rejeita <script>, <style> e <!DOCTYPE> antes que seu callback seja executado)
O formato se aplica a todas as perguntas na sessão. Claude inclui preview em opções onde uma comparação visual ajuda (escolhas de layout, esquemas de cores) e a omite onde não ajudaria (confirmações sim/não, escolhas apenas de texto). Verifique se há undefined antes de renderizar.
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 é uma string HTML ou undefined
      return { behavior: "allow", updatedInput: input };
    }
  }
})) {
  // ...
}
Uma opção com uma visualização 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>"
}

Formato de resposta

Retorne um objeto answers mapeando cada campo question da pergunta para o label da opção selecionada:
CampoDescrição
questionsPasse o array de perguntas original (obrigatório para processamento de ferramentas)
answersObjeto onde as chaves são texto de pergunta e os valores são labels selecionados
responseResposta freeform opcional que o usuário digitou em vez de responder às perguntas estruturadas
Para perguntas de seleção múltipla, passe um array de labels ou junte-os com ", ". Para entrada de texto livre por pergunta, como uma opção “Outro”, coloque o texto do usuário em answers[question] conforme mostrado em Suporte para entrada de texto livre. Defina response apenas quando sua interface do usuário permitir que o usuário descarte o cartão de pergunta e digite uma resposta geral que não seja uma resposta a nenhuma pergunta específica. Quando response é definido, Claude recebe “O usuário respondeu: …” em vez da lista de resposta por pergunta.
{
  "questions": [
    // ...
  ],
  "answers": {
    "How should I format the output?": "Summary",
    "Which sections should I include?": ["Introduction", "Conclusion"]
  }
}

Suporte para entrada de texto livre

As opções predefinidas do Claude nem sempre cobrirão o que os usuários querem. Para permitir que os usuários digitem sua própria resposta:
  • Exiba uma escolha “Outro” adicional após as opções do Claude que aceita entrada de texto
  • Use o texto personalizado do usuário como o valor da resposta (não a palavra “Outro”)
Veja o exemplo completo abaixo para uma implementação completa.

Exemplo completo

Claude faz perguntas de esclarecimento quando precisa de entrada do usuário para prosseguir. Por exemplo, quando solicitado a ajudar a decidir sobre uma pilha de tecnologia para um aplicativo móvel, Claude pode perguntar sobre cross-platform vs nativo, preferências de backend ou plataformas alvo. Essas perguntas ajudam Claude a tomar decisões que correspondem às preferências do usuário em vez de adivinhar. Este exemplo lida com essas perguntas em um aplicativo de terminal. Aqui está o que acontece em cada etapa:
  1. Rotear a solicitação: O callback canUseTool verifica se o nome da ferramenta é "AskUserQuestion" e roteia para um manipulador dedicado
  2. Exibir perguntas: O manipulador percorre o array questions e imprime cada pergunta com opções numeradas
  3. Coletar entrada: O usuário pode inserir um número para selecionar uma opção ou digitar texto livre diretamente (por exemplo, “jquery”, “não sei”)
  4. Mapear respostas: O código verifica se a entrada é numérica (usa o label da opção) ou texto livre (usa o texto diretamente)
  5. Retornar ao Claude: A resposta inclui tanto o array questions original quanto o mapeamento 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:
    """Analise a entrada do usuário como número(s) de opção ou texto livre."""
    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:
    """Exiba as perguntas do Claude e colete respostas do usuário."""
    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:
    # Rotear AskUserQuestion para nosso manipulador de perguntas
    if tool_name == "AskUserQuestion":
        return await handle_ask_user_question(input_data)
    # Auto-aprovar outras ferramentas para este exemplo
    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",
        },
    }


# Solução alternativa necessária: hook fictício mantém o fluxo aberto para can_use_tool
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())

Limitações

  • Subagentes: AskUserQuestion não está disponível em subagentes gerados por meio da ferramenta Agent
  • Limites de perguntas: cada chamada AskUserQuestion suporta 1-4 perguntas com 2-4 opções cada

Outras maneiras de obter entrada do usuário

O callback canUseTool e a ferramenta AskUserQuestion cobrem a maioria dos cenários de aprovação e esclarecimento, mas o SDK oferece outras maneiras de obter entrada dos usuários:

Entrada de streaming

Use entrada de streaming quando você precisar:
  • Interromper o agente no meio da tarefa: enviar um sinal de cancelamento ou mudar de direção enquanto Claude está trabalhando
  • Fornecer contexto adicional: adicionar informações que Claude precisa sem esperar que ele pergunte
  • Construir interfaces de chat: permitir que os usuários enviem mensagens de acompanhamento durante operações de longa duração
A entrada de streaming é ideal para interfaces conversacionais onde os usuários interagem com o agente durante toda a execução, não apenas em pontos de aprovação.

Ferramentas personalizadas

Use ferramentas personalizadas quando você precisar:
  • Coletar entrada estruturada: construir formulários, assistentes ou fluxos de trabalho de várias etapas que vão além do formato de múltipla escolha do AskUserQuestion
  • Integrar sistemas de aprovação externos: conectar a plataformas de ticketing, fluxo de trabalho ou aprovação existentes
  • Implementar interações específicas do domínio: criar ferramentas adaptadas às necessidades do seu aplicativo, como interfaces de revisão de código ou listas de verificação de implantação
As ferramentas personalizadas lhe dão controle total sobre a interação, mas requerem mais trabalho de implementação do que usar o callback canUseTool integrado.

Recursos relacionados