1> ## Documentation Index
2> Fetch the complete documentation index at: https://code.claude.com/docs/llms.txt
3> Use this file to discover all available pages before exploring further.
4
5# Lidar com aprovações e entrada do usuário
6
7> Apresente as solicitações de aprovação e perguntas de esclarecimento do Claude aos usuários e retorne suas decisões ao SDK.
8
9Ao 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.
10
11Claude 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.
12
13Para 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.
14
15O 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, o SDK TypeScript suporta o [hook `defer`](/pt/hooks#defer-a-tool-call-for-later), que permite que o processo saia e retome mais tarde a partir da sessão persistida; esta opção não está disponível no SDK Python.
16
17Este guia mostra como detectar cada tipo de solicitação e responder apropriadamente.
18
19## Detectar quando Claude precisa de entrada
20
21Passe 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:
22
23<CodeGroup>
24 ```python Python theme={null}
25 async def handle_tool_request(tool_name, input_data, context):
26 # Solicite ao usuário e retorne permitir ou negar
27 ...
28
29
30 options = ClaudeAgentOptions(can_use_tool=handle_tool_request)
31 ```
32
33 ```typescript TypeScript theme={null}
34 async function handleToolRequest(toolName, input, options) {
35 // options inclui { signal: AbortSignal, suggestions?: PermissionUpdate[] }
36 // Solicite ao usuário e retorne permitir ou negar
37 }
38
39 const options = { canUseTool: handleToolRequest };
40 ```
41</CodeGroup>
42
43O callback é acionado em dois casos:
44
451. **Ferramenta precisa de aprovação**: Claude quer usar uma ferramenta que não é aprovada automaticamente por [regras de permissão](/pt/agent-sdk/permissions) ou modos. Verifique `tool_name` para a ferramenta (por exemplo, `"Bash"`, `"Write"`).
462. **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](#handle-clarifying-questions) para detalhes.
47
48<Note>
49 Para permitir ou negar automaticamente ferramentas sem solicitar aos usuários, use [hooks](/pt/agent-sdk/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`](/pt/agent-sdk/hooks#available-hooks) para enviar notificações externas (Slack, email, push) quando Claude está aguardando aprovação.
50</Note>
51
52## Lidar com solicitações de aprovação de ferramentas
53
54Depois 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:
55
56| Argumento | Descrição |
57| ----------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
58| `toolName` | O nome da ferramenta que Claude quer usar (por exemplo, `"Bash"`, `"Write"`, `"Edit"`) |
59| `input` | Os parâmetros que Claude está passando para a ferramenta. O conteúdo varia por ferramenta. |
60| `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`](/pt/agent-sdk/python#toolpermissioncontext) para Python. |
61
62O objeto `input` contém parâmetros específicos da ferramenta. Exemplos comuns:
63
64| Ferramenta | Campos de entrada |
65| ---------- | --------------------------------------- |
66| `Bash` | `command`, `description`, `timeout` |
67| `Write` | `file_path`, `content` |
68| `Edit` | `file_path`, `old_string`, `new_string` |
69| `Read` | `file_path`, `offset`, `limit` |
70
71Veja a referência do SDK para esquemas de entrada completos: [Python](/pt/agent-sdk/python#tool-input%2Foutput-types) | [TypeScript](/pt/agent-sdk/typescript#tool-input-types).
72
73Você 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.
74
75O 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.
76
77<CodeGroup>
78 ```python Python theme={null}
79 import asyncio
80
81 from claude_agent_sdk import ClaudeAgentOptions, ResultMessage, query
82 from claude_agent_sdk.types import (
83 HookMatcher,
84 PermissionResultAllow,
85 PermissionResultDeny,
86 ToolPermissionContext,
87 )
88
89
90 async def can_use_tool(
91 tool_name: str, input_data: dict, context: ToolPermissionContext
92 ) -> PermissionResultAllow | PermissionResultDeny:
93 # Exiba a solicitação de ferramenta
94 print(f"\nTool: {tool_name}")
95 if tool_name == "Bash":
96 print(f"Command: {input_data.get('command')}")
97 if input_data.get("description"):
98 print(f"Description: {input_data.get('description')}")
99 else:
100 print(f"Input: {input_data}")
101
102 # Obtenha aprovação do usuário
103 response = input("Allow this action? (y/n): ")
104
105 # Retorne permitir ou negar com base na resposta do usuário
106 if response.lower() == "y":
107 # Permitir: ferramenta executa com a entrada original (ou modificada)
108 return PermissionResultAllow(updated_input=input_data)
109 else:
110 # Negar: ferramenta não executa, Claude vê a mensagem
111 return PermissionResultDeny(message="User denied this action")
112
113
114 # Solução alternativa necessária: hook fictício mantém o fluxo aberto para can_use_tool
115 async def dummy_hook(input_data, tool_use_id, context):
116 return {"continue_": True}
117
118
119 async def prompt_stream():
120 yield {
121 "type": "user",
122 "message": {
123 "role": "user",
124 "content": "Create a test file in /tmp and then delete it",
125 },
126 }
127
128
129 async def main():
130 async for message in query(
131 prompt=prompt_stream(),
132 options=ClaudeAgentOptions(
133 can_use_tool=can_use_tool,
134 hooks={"PreToolUse": [HookMatcher(matcher=None, hooks=[dummy_hook])]},
135 ),
136 ):
137 if isinstance(message, ResultMessage) and message.subtype == "success":
138 print(message.result)
139
140
141 asyncio.run(main())
142 ```
143
144 ```typescript TypeScript theme={null}
145 import { query } from "@anthropic-ai/claude-agent-sdk";
146 import * as readline from "readline";
147
148 // Auxiliar para solicitar entrada do usuário no terminal
149 function prompt(question: string): Promise<string> {
150 const rl = readline.createInterface({
151 input: process.stdin,
152 output: process.stdout
153 });
154 return new Promise((resolve) =>
155 rl.question(question, (answer) => {
156 rl.close();
157 resolve(answer);
158 })
159 );
160 }
161
162 for await (const message of query({
163 prompt: "Create a test file in /tmp and then delete it",
164 options: {
165 canUseTool: async (toolName, input) => {
166 // Exiba a solicitação de ferramenta
167 console.log(`\nTool: ${toolName}`);
168 if (toolName === "Bash") {
169 console.log(`Command: ${input.command}`);
170 if (input.description) console.log(`Description: ${input.description}`);
171 } else {
172 console.log(`Input: ${JSON.stringify(input, null, 2)}`);
173 }
174
175 // Obtenha aprovação do usuário
176 const response = await prompt("Allow this action? (y/n): ");
177
178 // Retorne permitir ou negar com base na resposta do usuário
179 if (response.toLowerCase() === "y") {
180 // Permitir: ferramenta executa com a entrada original (ou modificada)
181 return { behavior: "allow", updatedInput: input };
182 } else {
183 // Negar: ferramenta não executa, Claude vê a mensagem
184 return { behavior: "deny", message: "User denied this action" };
185 }
186 }
187 }
188 })) {
189 if ("result" in message) console.log(message.result);
190 }
191 ```
192</CodeGroup>
193
194<Note>
195 Em Python, `can_use_tool` requer [modo de streaming](/pt/agent-sdk/streaming-vs-single-mode) 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.
196</Note>
197
198Este 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](#respond-to-tool-requests) para todas as maneiras que você pode responder.
199
200### Responder a solicitações de ferramentas
201
202Seu callback retorna um de dois tipos de resposta:
203
204| Resposta | Python | TypeScript |
205| ------------ | ------------------------------------------ | ------------------------------------- |
206| **Permitir** | `PermissionResultAllow(updated_input=...)` | `{ behavior: "allow", updatedInput }` |
207| **Negar** | `PermissionResultDeny(message=...)` | `{ behavior: "deny", message }` |
208
209Ao 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.
210
211<CodeGroup>
212 ```python Python theme={null}
213 from claude_agent_sdk.types import PermissionResultAllow, PermissionResultDeny
214
215 # Permita que a ferramenta execute
216 return PermissionResultAllow(updated_input=input_data)
217
218 # Bloqueie a ferramenta
219 return PermissionResultDeny(message="User rejected this action")
220 ```
221
222 ```typescript TypeScript theme={null}
223 // Permita que a ferramenta execute
224 return { behavior: "allow", updatedInput: input };
225
226 // Bloqueie a ferramenta
227 return { behavior: "deny", message: "User rejected this action" };
228 ```
229</CodeGroup>
230
231Além de permitir ou negar, você pode modificar a entrada da ferramenta ou fornecer contexto que ajude Claude a ajustar sua abordagem:
232
233* **Aprovar**: deixe a ferramenta executar conforme Claude solicitou
234* **Aprovar com alterações**: modifique a entrada antes da execução (por exemplo, sanitize caminhos, adicione restrições)
235* **Rejeitar**: bloqueie a ferramenta e diga ao Claude por quê
236* **Sugerir alternativa**: bloqueie mas guie Claude para o que o usuário quer em vez disso
237* **Redirecionar completamente**: use [entrada de streaming](/pt/agent-sdk/streaming-vs-single-mode) para enviar ao Claude uma instrução completamente nova
238
239<Tabs>
240 <Tab title="Aprovar">
241 O usuário aprova a ação como está. Passe a `input` do seu callback inalterada e a ferramenta executa exatamente como Claude solicitou.
242
243 <CodeGroup>
244 ```python Python theme={null}
245 async def can_use_tool(tool_name, input_data, context):
246 print(f"Claude wants to use {tool_name}")
247 approved = await ask_user("Allow this action?")
248
249 if approved:
250 return PermissionResultAllow(updated_input=input_data)
251 return PermissionResultDeny(message="User declined")
252 ```
253
254 ```typescript TypeScript theme={null}
255 canUseTool: async (toolName, input) => {
256 console.log(`Claude wants to use ${toolName}`);
257 const approved = await askUser("Allow this action?");
258
259 if (approved) {
260 return { behavior: "allow", updatedInput: input };
261 }
262 return { behavior: "deny", message: "User declined" };
263 };
264 ```
265 </CodeGroup>
266 </Tab>
267
268 <Tab title="Aprovar com alterações">
269 O usuário aprova mas quer modificar a solicitação primeiro. Você pode alterar a entrada antes da ferramenta executar. Claude vê o resultado mas não é informado de que você alterou nada. Útil para sanitizar parâmetros, adicionar restrições ou escopar acesso.
270
271 <CodeGroup>
272 ```python Python theme={null}
273 async def can_use_tool(tool_name, input_data, context):
274 if tool_name == "Bash":
275 # Usuário aprovou, mas escope todos os comandos para sandbox
276 sandboxed_input = {**input_data}
277 sandboxed_input["command"] = input_data["command"].replace(
278 "/tmp", "/tmp/sandbox"
279 )
280 return PermissionResultAllow(updated_input=sandboxed_input)
281 return PermissionResultAllow(updated_input=input_data)
282 ```
283
284 ```typescript TypeScript theme={null}
285 canUseTool: async (toolName, input) => {
286 if (toolName === "Bash") {
287 // Usuário aprovou, mas escope todos os comandos para sandbox
288 const sandboxedInput = {
289 ...input,
290 command: input.command.replace("/tmp", "/tmp/sandbox")
291 };
292 return { behavior: "allow", updatedInput: sandboxedInput };
293 }
294 return { behavior: "allow", updatedInput: input };
295 };
296 ```
297 </CodeGroup>
298 </Tab>
299
300 <Tab title="Rejeitar">
301 O usuário não quer que esta ação aconteça. Bloqueie a ferramenta e forneça uma mensagem explicando por quê. Claude vê esta mensagem e pode tentar uma abordagem diferente.
302
303 <CodeGroup>
304 ```python Python theme={null}
305 async def can_use_tool(tool_name, input_data, context):
306 approved = await ask_user(f"Allow {tool_name}?")
307
308 if not approved:
309 return PermissionResultDeny(message="User rejected this action")
310 return PermissionResultAllow(updated_input=input_data)
311 ```
312
313 ```typescript TypeScript theme={null}
314 canUseTool: async (toolName, input) => {
315 const approved = await askUser(`Allow ${toolName}?`);
316
317 if (!approved) {
318 return {
319 behavior: "deny",
320 message: "User rejected this action"
321 };
322 }
323 return { behavior: "allow", updatedInput: input };
324 };
325 ```
326 </CodeGroup>
327 </Tab>
328
329 <Tab title="Sugerir alternativa">
330 O usuário não quer esta ação específica, mas tem uma ideia diferente. Bloqueie a ferramenta e inclua orientação em sua mensagem. Claude lerá isso e decidirá como proceder com base em seu feedback.
331
332 <CodeGroup>
333 ```python Python theme={null}
334 async def can_use_tool(tool_name, input_data, context):
335 if tool_name == "Bash" and "rm" in input_data.get("command", ""):
336 # Usuário não quer deletar, sugira arquivar em vez disso
337 return PermissionResultDeny(
338 message="User doesn't want to delete files. They asked if you could compress them into an archive instead."
339 )
340 return PermissionResultAllow(updated_input=input_data)
341 ```
342
343 ```typescript TypeScript theme={null}
344 canUseTool: async (toolName, input) => {
345 if (toolName === "Bash" && input.command.includes("rm")) {
346 // Usuário não quer deletar, sugira arquivar em vez disso
347 return {
348 behavior: "deny",
349 message:
350 "User doesn't want to delete files. They asked if you could compress them into an archive instead."
351 };
352 }
353 return { behavior: "allow", updatedInput: input };
354 };
355 ```
356 </CodeGroup>
357 </Tab>
358
359 <Tab title="Redirecionar completamente">
360 Para uma mudança completa de direção (não apenas um empurrão), use [entrada de streaming](/pt/agent-sdk/streaming-vs-single-mode) para enviar ao Claude uma nova instrução diretamente. Isso ignora a solicitação de ferramenta atual e dá ao Claude instruções completamente novas para seguir.
361 </Tab>
362</Tabs>
363
364## Lidar com perguntas de esclarecimento
365
366Quando 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.
367
368<Tip>
369 Perguntas de esclarecimento são especialmente comuns no [modo `plan`](/pt/agent-sdk/permissions#plan-mode-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.
370</Tip>
371
372Os passos a seguir mostram como lidar com perguntas de esclarecimento:
373
374<Steps>
375 <Step title="Passe um callback canUseTool">
376 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:
377
378 <CodeGroup>
379 ```python Python theme={null}
380 async for message in query(
381 prompt="Analyze this codebase",
382 options=ClaudeAgentOptions(
383 # Inclua AskUserQuestion em sua lista de ferramentas
384 tools=["Read", "Glob", "Grep", "AskUserQuestion"],
385 can_use_tool=can_use_tool,
386 ),
387 ):
388 print(message)
389 ```
390
391 ```typescript TypeScript theme={null}
392 for await (const message of query({
393 prompt: "Analyze this codebase",
394 options: {
395 // Inclua AskUserQuestion em sua lista de ferramentas
396 tools: ["Read", "Glob", "Grep", "AskUserQuestion"],
397 canUseTool: async (toolName, input) => {
398 // Lidar com perguntas de esclarecimento aqui
399 }
400 }
401 })) {
402 console.log(message);
403 }
404 ```
405 </CodeGroup>
406 </Step>
407
408 <Step title="Detecte AskUserQuestion">
409 Em seu callback, verifique se `toolName` é igual a `AskUserQuestion` para tratá-lo diferentemente de outras ferramentas:
410
411 <CodeGroup>
412 ```python Python theme={null}
413 async def can_use_tool(tool_name: str, input_data: dict, context):
414 if tool_name == "AskUserQuestion":
415 # Sua implementação para coletar respostas do usuário
416 return await handle_clarifying_questions(input_data)
417 # Lidar com outras ferramentas normalmente
418 return await prompt_for_approval(tool_name, input_data)
419 ```
420
421 ```typescript TypeScript theme={null}
422 canUseTool: async (toolName, input) => {
423 if (toolName === "AskUserQuestion") {
424 // Sua implementação para coletar respostas do usuário
425 return handleClarifyingQuestions(input);
426 }
427 // Lidar com outras ferramentas normalmente
428 return promptForApproval(toolName, input);
429 };
430 ```
431 </CodeGroup>
432 </Step>
433
434 <Step title="Analise a entrada da pergunta">
435 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):
436
437 ```json theme={null}
438 {
439 "questions": [
440 {
441 "question": "How should I format the output?",
442 "header": "Format",
443 "options": [
444 { "label": "Summary", "description": "Brief overview" },
445 { "label": "Detailed", "description": "Full explanation" }
446 ],
447 "multiSelect": false
448 },
449 {
450 "question": "Which sections should I include?",
451 "header": "Sections",
452 "options": [
453 { "label": "Introduction", "description": "Opening context" },
454 { "label": "Conclusion", "description": "Final summary" }
455 ],
456 "multiSelect": true
457 }
458 ]
459 }
460 ```
461
462 Veja [Formato de pergunta](#question-format) para descrições completas de campos.
463 </Step>
464
465 <Step title="Colete respostas do usuário">
466 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.
467 </Step>
468
469 <Step title="Retorne respostas ao Claude">
470 Construa o objeto `answers` como um registro onde cada chave é o texto `question` e cada valor é o `label` da opção selecionada:
471
472 | Do objeto de pergunta | Use como |
473 | ------------------------------------------------------------------- | -------- |
474 | Campo `question` (por exemplo, `"How should I format the output?"`) | Chave |
475 | Campo `label` da opção selecionada (por exemplo, `"Summary"`) | Valor |
476
477 Para perguntas de seleção múltipla, passe um array de labels ou junte-os com `", "`. Se você [suportar entrada de texto livre](#support-free-text-input), use o texto personalizado do usuário como o valor.
478
479 <CodeGroup>
480 ```python Python theme={null}
481 return PermissionResultAllow(
482 updated_input={
483 "questions": input_data.get("questions", []),
484 "answers": {
485 "How should I format the output?": "Summary",
486 "Which sections should I include?": ["Introduction", "Conclusion"],
487 },
488 }
489 )
490 ```
491
492 ```typescript TypeScript theme={null}
493 return {
494 behavior: "allow",
495 updatedInput: {
496 questions: input.questions,
497 answers: {
498 "How should I format the output?": "Summary",
499 "Which sections should I include?": "Introduction, Conclusion"
500 }
501 }
502 };
503 ```
504 </CodeGroup>
505 </Step>
506</Steps>
507
508### Formato de pergunta
509
510A entrada contém as perguntas geradas pelo Claude em um array `questions`. Cada pergunta tem estes campos:
511
512| Campo | Descrição |
513| ------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- |
514| `question` | O texto completo da pergunta a exibir |
515| `header` | Rótulo curto para a pergunta (máximo 12 caracteres) |
516| `options` | Array de 2-4 escolhas, cada uma com `label` e `description`. TypeScript: opcionalmente `preview` (veja [abaixo](#option-previews-type-script)) |
517| `multiSelect` | Se `true`, os usuários podem selecionar múltiplas opções |
518
519A estrutura que seu callback recebe:
520
521```json theme={null}
522{
523 "questions": [
524 {
525 "question": "How should I format the output?",
526 "header": "Format",
527 "options": [
528 { "label": "Summary", "description": "Brief overview of key points" },
529 { "label": "Detailed", "description": "Full explanation with examples" }
530 ],
531 "multiSelect": false
532 }
533 ]
534}
535```
536
537#### Visualizações de opção (TypeScript)
538
539`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.
540
541| `previewFormat` | `preview` contém |
542| :-------------------- | :------------------------------------------------------------------------------------------------------------------------- |
543| não definido (padrão) | Campo está ausente. Claude não gera visualizações. |
544| `"markdown"` | Arte ASCII e blocos de código cercados |
545| `"html"` | Um fragmento `<div>` estilizado (o SDK rejeita `<script>`, `<style>` e `<!DOCTYPE>` antes que seu callback seja executado) |
546
547O 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.
548
549```typescript theme={null}
550import { query } from "@anthropic-ai/claude-agent-sdk";
551
552for await (const message of query({
553 prompt: "Help me choose a card layout",
554 options: {
555 toolConfig: {
556 askUserQuestion: { previewFormat: "html" }
557 },
558 canUseTool: async (toolName, input) => {
559 // input.questions[].options[].preview é uma string HTML ou undefined
560 return { behavior: "allow", updatedInput: input };
561 }
562 }
563})) {
564 // ...
565}
566```
567
568Uma opção com uma visualização HTML:
569
570```json theme={null}
571{
572 "label": "Compact",
573 "description": "Title and metric value only",
574 "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>"
575}
576```
577
578### Formato de resposta
579
580Retorne um objeto `answers` mapeando cada campo `question` da pergunta para o `label` da opção selecionada:
581
582| Campo | Descrição |
583| ----------- | ----------------------------------------------------------------------------------- |
584| `questions` | Passe o array de perguntas original (obrigatório para processamento de ferramentas) |
585| `answers` | Objeto onde as chaves são texto de pergunta e os valores são labels selecionados |
586
587Para perguntas de seleção múltipla, passe um array de labels ou junte-os com `", "`. Para entrada de texto livre, use o texto personalizado do usuário diretamente.
588
589```json theme={null}
590{
591 "questions": [
592 // ...
593 ],
594 "answers": {
595 "How should I format the output?": "Summary",
596 "Which sections should I include?": ["Introduction", "Conclusion"]
597 }
598}
599```
600
601#### Suporte para entrada de texto livre
602
603As 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:
604
605* Exiba uma escolha "Outro" adicional após as opções do Claude que aceita entrada de texto
606* Use o texto personalizado do usuário como o valor da resposta (não a palavra "Outro")
607
608Veja o [exemplo completo](#complete-example) abaixo para uma implementação completa.
609
610### Exemplo completo
611
612Claude 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.
613
614Este exemplo lida com essas perguntas em um aplicativo de terminal. Aqui está o que acontece em cada etapa:
615
6161. **Rotear a solicitação**: O callback `canUseTool` verifica se o nome da ferramenta é `"AskUserQuestion"` e roteia para um manipulador dedicado
6172. **Exibir perguntas**: O manipulador percorre o array `questions` e imprime cada pergunta com opções numeradas
6183. **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")
6194. **Mapear respostas**: O código verifica se a entrada é numérica (usa o label da opção) ou texto livre (usa o texto diretamente)
6205. **Retornar ao Claude**: A resposta inclui tanto o array `questions` original quanto o mapeamento `answers`
621
622<CodeGroup>
623 ```python Python theme={null}
624 import asyncio
625
626 from claude_agent_sdk import ClaudeAgentOptions, ResultMessage, query
627 from claude_agent_sdk.types import HookMatcher, PermissionResultAllow
628
629
630 def parse_response(response: str, options: list) -> str:
631 """Analise a entrada do usuário como número(s) de opção ou texto livre."""
632 try:
633 indices = [int(s.strip()) - 1 for s in response.split(",")]
634 labels = [options[i]["label"] for i in indices if 0 <= i < len(options)]
635 return ", ".join(labels) if labels else response
636 except ValueError:
637 return response
638
639
640 async def handle_ask_user_question(input_data: dict) -> PermissionResultAllow:
641 """Exiba as perguntas do Claude e colete respostas do usuário."""
642 answers = {}
643
644 for q in input_data.get("questions", []):
645 print(f"\n{q['header']}: {q['question']}")
646
647 options = q["options"]
648 for i, opt in enumerate(options):
649 print(f" {i + 1}. {opt['label']} - {opt['description']}")
650 if q.get("multiSelect"):
651 print(" (Enter numbers separated by commas, or type your own answer)")
652 else:
653 print(" (Enter a number, or type your own answer)")
654
655 response = input("Your choice: ").strip()
656 answers[q["question"]] = parse_response(response, options)
657
658 return PermissionResultAllow(
659 updated_input={
660 "questions": input_data.get("questions", []),
661 "answers": answers,
662 }
663 )
664
665
666 async def can_use_tool(
667 tool_name: str, input_data: dict, context
668 ) -> PermissionResultAllow:
669 # Rotear AskUserQuestion para nosso manipulador de perguntas
670 if tool_name == "AskUserQuestion":
671 return await handle_ask_user_question(input_data)
672 # Auto-aprovar outras ferramentas para este exemplo
673 return PermissionResultAllow(updated_input=input_data)
674
675
676 async def prompt_stream():
677 yield {
678 "type": "user",
679 "message": {
680 "role": "user",
681 "content": "Help me decide on the tech stack for a new mobile app",
682 },
683 }
684
685
686 # Solução alternativa necessária: hook fictício mantém o fluxo aberto para can_use_tool
687 async def dummy_hook(input_data, tool_use_id, context):
688 return {"continue_": True}
689
690
691 async def main():
692 async for message in query(
693 prompt=prompt_stream(),
694 options=ClaudeAgentOptions(
695 can_use_tool=can_use_tool,
696 hooks={"PreToolUse": [HookMatcher(matcher=None, hooks=[dummy_hook])]},
697 ),
698 ):
699 if isinstance(message, ResultMessage) and message.subtype == "success":
700 print(message.result)
701
702
703 asyncio.run(main())
704 ```
705
706 ```typescript TypeScript theme={null}
707 import { query } from "@anthropic-ai/claude-agent-sdk";
708 import * as readline from "readline/promises";
709
710 // Auxiliar para solicitar entrada do usuário no terminal
711 async function prompt(question: string): Promise<string> {
712 const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
713 const answer = await rl.question(question);
714 rl.close();
715 return answer;
716 }
717
718 // Analise a entrada do usuário como número(s) de opção ou texto livre
719 function parseResponse(response: string, options: any[]): string {
720 const indices = response.split(",").map((s) => parseInt(s.trim()) - 1);
721 const labels = indices
722 .filter((i) => !isNaN(i) && i >= 0 && i < options.length)
723 .map((i) => options[i].label);
724 return labels.length > 0 ? labels.join(", ") : response;
725 }
726
727 // Exiba as perguntas do Claude e colete respostas do usuário
728 async function handleAskUserQuestion(input: any) {
729 const answers: Record<string, string> = {};
730
731 for (const q of input.questions) {
732 console.log(`\n${q.header}: ${q.question}`);
733
734 const options = q.options;
735 options.forEach((opt: any, i: number) => {
736 console.log(` ${i + 1}. ${opt.label} - ${opt.description}`);
737 });
738 if (q.multiSelect) {
739 console.log(" (Enter numbers separated by commas, or type your own answer)");
740 } else {
741 console.log(" (Enter a number, or type your own answer)");
742 }
743
744 const response = (await prompt("Your choice: ")).trim();
745 answers[q.question] = parseResponse(response, options);
746 }
747
748 // Retorne as respostas ao Claude (deve incluir perguntas originais)
749 return {
750 behavior: "allow",
751 updatedInput: { questions: input.questions, answers }
752 };
753 }
754
755 async function main() {
756 for await (const message of query({
757 prompt: "Help me decide on the tech stack for a new mobile app",
758 options: {
759 canUseTool: async (toolName, input) => {
760 // Rotear AskUserQuestion para nosso manipulador de perguntas
761 if (toolName === "AskUserQuestion") {
762 return handleAskUserQuestion(input);
763 }
764 // Auto-aprovar outras ferramentas para este exemplo
765 return { behavior: "allow", updatedInput: input };
766 }
767 }
768 })) {
769 if ("result" in message) console.log(message.result);
770 }
771 }
772
773 main();
774 ```
775</CodeGroup>
776
777## Limitações
778
779* **Subagentes**: `AskUserQuestion` não está disponível em subagentes gerados por meio da ferramenta Agent
780* **Limites de perguntas**: cada chamada `AskUserQuestion` suporta 1-4 perguntas com 2-4 opções cada
781
782## Outras maneiras de obter entrada do usuário
783
784O 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:
785
786### Entrada de streaming
787
788Use [entrada de streaming](/pt/agent-sdk/streaming-vs-single-mode) quando você precisar:
789
790* **Interromper o agente no meio da tarefa**: enviar um sinal de cancelamento ou mudar de direção enquanto Claude está trabalhando
791* **Fornecer contexto adicional**: adicionar informações que Claude precisa sem esperar que ele pergunte
792* **Construir interfaces de chat**: permitir que os usuários enviem mensagens de acompanhamento durante operações de longa duração
793
794A 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.
795
796### Ferramentas personalizadas
797
798Use [ferramentas personalizadas](/pt/agent-sdk/custom-tools) quando você precisar:
799
800* **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`
801* **Integrar sistemas de aprovação externos**: conectar a plataformas de ticketing, fluxo de trabalho ou aprovação existentes
802* **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
803
804As 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.
805
806## Recursos relacionados
807
808* [Configurar permissões](/pt/agent-sdk/permissions): configurar modos e regras de permissão
809* [Controlar execução com hooks](/pt/agent-sdk/hooks): executar código personalizado em pontos-chave do ciclo de vida do agente
810* [Referência do SDK TypeScript](/pt/agent-sdk/typescript#canusetool): documentação completa da API canUseTool