Dry-run em operações de escrita

Tools de escrita expostas a um LLM são uma superfície de risco diferente de uma API consumida por humanos. O modelo pode invocar uma ferramenta por inferência incorreta (hallucination), interpretar mal o contexto de uma conversa ou simplesmente confundir parâmetros. Quando essa tool tem efeito colateral real — enviar um WhatsApp, criar um lembrete, baixar um certificado — um único disparo equivocado custa dinheiro e quebra confiança.

O padrão dry-run by default transforma toda chamada de escrita em duas etapas:

  1. Primeira chamada — o LLM monta os parâmetros e a tool devolve apenas um preview em markdown do que aconteceria. Nada muda no sistema.
  2. Segunda chamada — o usuário humano lê o preview, valida no chat e o LLM repete a chamada com confirmar: true. Só agora a mutação acontece.

Esse duplo passo combina com o comportamento do Claude Desktop, que pinta um escudo amarelo em qualquer tool marcada como destrutiva e exige clique humano antes de cada invocação — então o operador vê duas vezes antes de qualquer efeito real.

Padrão Zod

Toda tool de escrita declara o campo confirmar no schema, com default false:

const inputSchema = z.object({
  // ... outros campos específicos da tool
  confirmar: z
    .boolean()
    .default(false)
    .describe(
      "Se `false` (default), apenas mostra preview (dry-run). Se `true`, executa de verdade.",
    ),
});

O describe() em PT-BR é deliberado — essa string é o que o LLM consome para entender o contrato.

Comportamento

Valor de confirmarO que a tool fazEstado do sistema
false (default)Monta um markdown com os campos resolvidos e retorna como textinalterado
trueChama a WebApi, propaga o resultado real (id criado, status, agendamento)mutado

O preview é desenhado para que o humano consiga conferir cada campo em segundos: identificadores entre crases, mensagens em bloco de código, data formatada em PT-BR.

Annotations MCP

Além do dry-run no schema, registramos a tool com destructiveHint: true:

server.registerTool("criar_lembrete", {
  // ...
  annotations: {
    destructiveHint: true,
    openWorldHint: true,
  },
});

O Claude Desktop usa essa annotation para renderizar o escudo amarelo ao lado do nome da tool e exigir confirmação manual a cada chamada. Combinado com o confirmar, são duas barreiras independentes: uma protege contra cliques acidentais no cliente, a outra protege contra parâmetros mal montados pelo modelo.

Exemplo no criar_lembrete

O fluxo típico de envio de um lembrete WhatsApp pelo Claude:

  1. Usuário: "agenda um lembrete pro cliente 123 amanhã às 9h".
  2. Claude monta a chamada criar_lembrete({cliente_id: 123, contato: "(11) 9...", mensagem: "...", data_hora_envio: "..."}) sem confirmar.
  3. Tool devolve um bloco markdown com cliente_id, contato, data de envio formatada em PT-BR e a mensagem renderizada — sinalizando que nada foi enviado.
  4. Usuário lê, valida o telefone e responde "confirma".
  5. Claude repete a chamada com confirmar: true. A WebApi cria o lembrete e devolve lembrete_id, status e timestamp real.

Dupla confirmação é responsabilidade humana

O confirmar=true no schema e o escudo amarelo do Claude Desktop são camadas técnicas — mas a decisão final é sempre do operador humano. Antes de confirmar uma escrita, revise destinatário, valores e horário. Mensagens WhatsApp Business têm custo e não voltam atrás.