Exemplo de elicitation MCP: suporte ao cliente e configuração no Claude Code

Um exemplo funcional de elicitation MCP para suporte ao cliente: um servidor em TypeScript que pausa a escalada de um chamado, pede ao agente a prioridade, a área do produto e o status da interrupção por meio de um formulário e depois retoma o processo. Inclui a configuração do Claude Code, o tratamento de recusa e cancelamento e uma lista de testes.

Exemplo de elicitation MCP: suporte ao cliente e configuração no Claude Code
Cristian Da Conceicao
Fundador do Picasso IA

Seu agente de suporte digita "escalate SUP-1042" no Claude Code e a ferramenta começa a rodar. Aí ela trava, porque ninguém informou a prioridade, a área do produto nem se os clientes estão sem acesso. Uma configuração fraca adivinha e aciona a equipe errada. Uma configuração melhor pergunta. Essa pergunta, enviada do servidor de volta para a pessoa no terminal, é o que a elicitation MCP faz. Este exemplo de elicitation MCP para uma central de suporte ao cliente mostra o ciclo completo: o código do servidor, o schema do formulário, a configuração do Claude Code e o que fazer quando o agente disser não.

Tudo abaixo segue a revisão 2025-11-25 do Model Context Protocol e o SDK para TypeScript. O servidor é pequeno o bastante para ser lido de uma vez, e cada parte dele corresponde a uma regra da especificação.

O que a elicitation MCP realmente faz

Normalmente, um cliente MCP chama uma ferramenta, o servidor trabalha e um resultado volta. A elicitation adiciona uma etapa no meio. Enquanto a ferramenta está rodando, o servidor envia uma requisição elicitation/create ao cliente. O cliente mostra uma caixa de diálogo para a pessoa, coleta a resposta e a devolve. Daí a ferramenta continua com dados reais, em vez de um palpite.

Mãos segurando um formulário de papel e uma caneta-tinteiro sobre uma mesa de carvalho

Isso torna a elicitation uma primitiva de humano no loop. O servidor continua no comando do que precisa saber. O cliente continua no comando de como a pergunta aparece, quais servidores podem perguntar e se a pessoa tem permissão para recusar.

Modo formulário e modo URL

A especificação define dois modos:

  • Modo formulário coleta dados estruturados dentro do próprio protocolo. O servidor envia uma mensagem curta com um requestedSchema, e o cliente monta um formulário a partir dele.
  • Modo URL envia a pessoa para um endereço externo em casos sensíveis, como um login ou um pagamento. Os dados nunca passam pelo cliente. A revisão 2025-11-25 introduziu esse modo.

Os schemas de formulário são propositalmente simples. São objetos planos, com apenas propriedades primitivas:

Tipo no schemaOpções úteisUso típico
stringminLength, maxLength, pattern, format (email, uri, date, date-time)E-mail de contato, nota curta
number ou integerminimum, maximum, defaultClientes afetados
booleandefault"Isto é uma interrupção?"
enum de seleção únicaenum, ou oneOf com títulosPrioridade, área do produto
enum de seleção múltiplaarray com minItems e maxItemsPlataformas afetadas

Objetos aninhados e arrays de objetos ficam de fora de propósito, para que qualquer cliente consiga desenhar o formulário sem adivinhar.

Três respostas possíveis

Toda resposta traz uma action:

  1. accept: a pessoa enviou o formulário, e content contém os valores.
  2. decline: a pessoa disse não de propósito.
  3. cancel: a pessoa fechou a caixa de diálogo sem escolher nada.

Seu servidor precisa tratar as três como resultados normais. A maioria dos bugs de elicitation vem de tratar apenas o primeiro.

O cenário de suporte ao cliente

Imagine uma equipe de suporte com uma central de chamados cheia e uma pequena escala de plantão. Os agentes trabalham dentro do Claude Code, e um servidor MCP chamado support-desk dá ao modelo uma única ação de escrita: escalate_ticket. Ela recebe um ID de chamado e entrega o caso aos engenheiros certos.

Agente de atendimento ao cliente com headset em uma mesa com dois monitores

Por que chutar falha aqui

O modelo consegue ler um chamado e inferir uma prioridade. Muitas vezes vai acertar. Quando erra, uma dúvida de cobrança cai no canal de incidentes, ou uma interrupção de login fica numa fila lenta durante a noite. Você poderia ampliar o schema de entrada da ferramenta e torcer para o modelo preencher todos os campos corretamente, mas uma chamada com valores inventados parece idêntica a uma com valores reais.

A elicitation transfere a decisão para a pessoa que é responsável por ela. O modelo fornece o ID do chamado. O ser humano fornece o julgamento.

Os campos que o servidor pede

Cinco campos bastam. Mais do que isso e os agentes começam a fechar o diálogo sem responder.

CampoTipoPor que está aqui
priorityenum de seleção únicaEncaminha para a fila de plantão certa
areaenum de seleção únicaEscolhe a equipe responsável
affectedCustomersinteger, de 1 a 10000Separa um usuário de um incidente amplo
customerEmailstring, format emailPermite que o engenheiro faça o acompanhamento
outagebooleanAbre o canal de incidentes

Vista de cima de uma mesa com notebook, setas em um caderno e notas adesivas em sequência

Somente priority e area são obrigatórios. Os outros têm valores padrão, então um agente com pressa pode aceitar e seguir em frente.

💡 Mantenha o formulário curto. Cada campo extra é um motivo para o agente pressionar cancelar.

Crie o servidor de suporte

O servidor é um único arquivo TypeScript, um transporte stdio e duas pequenas dependências.

Desenvolvedor de software digitando em um notebook em um loft silencioso

Arquivos do projeto e dependências

mkdir support-desk-mcp && cd support-desk-mcp
npm init -y
npm pkg set type=module
npm install @modelcontextprotocol/sdk zod
npm install -D typescript tsx @types/node
mkdir src

Definir type como module permite que o arquivo use await no nível superior e imports ES.

A ferramenta de escalada

Salve isto como src/server.ts:

import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";

const server = new McpServer({ name: "support-desk", version: "1.0.0" });

const EscalationForm = z.object({
  priority: z.enum(["p1", "p2", "p3"]),
  area: z.enum(["billing", "login", "api", "mobile-app"]),
  affectedCustomers: z.number().int().min(1).max(10000).default(1),
  customerEmail: z.string().email().optional(),
  outage: z.boolean().default(false),
});

// Stub: swap in your helpdesk API call.
async function createEscalation(ticketId: string, data: z.infer<typeof EscalationForm>) {
  return `ESC-${Date.now().toString(36).toUpperCase()}`;
}

const reply = (text: string, isError = false) => ({
  content: [{ type: "text" as const, text }],
  isError,
});

server.registerTool(
  "escalate_ticket",
  {
    description: "Escalate a support ticket to the on-call team. Asks the agent for missing details.",
    inputSchema: { ticketId: z.string().describe("Ticket ID, for example SUP-1042") },
  },
  async ({ ticketId }) => {
    if (!server.server.getClientCapabilities()?.elicitation) {
      return reply("This client cannot show forms. Ask the agent for priority and area, then retry.", true);
    }

    const answer = await server.server.elicitInput({
      message: `Ticket ${ticketId} needs a few details before it reaches the on-call team.`,
      requestedSchema: {
        type: "object",
        properties: {
          priority: {
            type: "string",
            title: "Priority",
            oneOf: [
              { const: "p1", title: "P1 Service down" },
              { const: "p2", title: "P2 Major feature broken" },
              { const: "p3", title: "P3 Minor issue" },
            ],
          },
          area: {
            type: "string",
            title: "Product area",
            enum: ["billing", "login", "api", "mobile-app"],
          },
          affectedCustomers: {
            type: "integer",
            title: "Customers affected",
            minimum: 1,
            maximum: 10000,
            default: 1,
          },
          customerEmail: { type: "string", format: "email", title: "Customer email" },
          outage: { type: "boolean", title: "Is this an outage?", default: false },
        },
        required: ["priority", "area"],
      },
    });

    if (answer.action === "decline") {
      return reply(`The agent declined to escalate ${ticketId}. The ticket is unchanged.`);
    }
    if (answer.action === "cancel") {
      return reply(`Escalation of ${ticketId} was cancelled. Nothing was changed.`);
    }

    const parsed = EscalationForm.safeParse(answer.content);
    if (!parsed.success) {
      return reply("The form answers were invalid. Ask again.", true);
    }

    const escalationId = await createEscalation(ticketId, parsed.data);
    return reply(`Escalated ${ticketId} as ${parsed.data.priority} in ${parsed.data.area}. Reference ${escalationId}.`);
  }
);

await server.connect(new StdioServerTransport());

Repare que mode não aparece na chamada de elicitInput. O modo formulário é o padrão, o que mantém a requisição legível para clientes mais antigos.

Verifique primeiro as capacidades do cliente

As primeiras linhas do handler importam mais do que parecem. Um cliente que suporta elicitation declara uma capacidade elicitation durante a inicialização. Um objeto elicitation vazio conta apenas como modo formulário, e um servidor nunca deve enviar um modo que o cliente não declarou.

Se a capacidade estiver ausente, o servidor devolve um erro em texto simples em vez de travar. O modelo lê essa mensagem e pergunta ao agente no chat. Uma ferramenta que falha com educação vale mais do que uma que congela.

💡 Em um servidor stdio, nunca imprima na saída padrão. Linhas soltas de console.log corrompem o fluxo do protocolo. Envie a saída de depuração com console.error.

Configuração do Claude Code passo a passo

Com o servidor escrito, o Claude Code precisa saber que ele existe.

Mãos digitando em um notebook com uma janela de terminal na tela

Registre o servidor

Em qualquer pasta, adicione-o com a CLI. Tudo depois dos dois traços é o comando que inicia o servidor:

claude mcp add support-desk -- npx -y tsx /absolute/path/to/support-desk-mcp/src/server.ts

Para compartilhar a configuração com a equipe, adicione --scope project. Então o Claude Code grava um arquivo .mcp.json na raiz do repositório:

{
  "mcpServers": {
    "support-desk": {
      "type": "stdio",
      "command": "npx",
      "args": ["-y", "tsx", "/absolute/path/to/support-desk-mcp/src/server.ts"]
    }
  }
}
EscopoCarregado emCompartilhado com a equipeArmazenado em
local (padrão)Somente o projeto atualNão~/.claude.json
projectSomente o projeto atualSim, pelo controle de versão.mcp.json
userTodos os seus projetosNão~/.claude.json

💡 No Windows nativo, envolva o launcher: claude mcp add support-desk -- cmd /c npx -y tsx C:\path\to\src\server.ts.

Confirme a conexão

Duas verificações mostram que o servidor está ativo:

claude --version
claude mcp list

Dentro de uma sessão, /mcp abre o painel de servidores com o status da conexão. Você deve ver support-desk listado como conectado.

Se um servidor parar de conectar após uma atualização, confira o changelog do Claude Code. As notas da versão 2.1.287, que incluiu prompts de URL vindos de servidores no protocolo 2025-11-25, recomendam adicionar "bareElicitationCapability": true à entrada de configuração desse servidor quando ele deixar de conectar.

Execute o fluxo de escalada

Inicie uma sessão no seu projeto e digite um pedido simples:

Escalate ticket SUP-1042 using the support-desk tool.

O Claude chama mcp__support-desk__escalate_ticket. A ferramenta pausa, o Claude Code mostra o formulário, e o agente preenche prioridade, área, clientes afetados, e-mail e o indicador de interrupção.

Tela de notebook mostrando uma caixa de diálogo simples com um dedo acima do trackpad

Depois que o agente envia, a ferramenta retoma e devolve algo como Escalated SUP-1042 as p1 in login. Reference ESC-LQ3F9A2. O modelo pode citar essa referência diretamente na resposta.

O Claude Code também expõe os hooks Elicitation e ElicitationResult, filtrados pelo nome do servidor. Um script pode responder a um formulário conhecido por conta própria, ou registrar cada resposta antes de ela chegar ao servidor. Use isso em formulários de baixo risco na automação, e mantenha uma pessoa supervisionando qualquer coisa que afete clientes.

Trate recusa, cancelamento e entrada inválida

Demonstrações do caminho feliz escondem a parte que decide se os agentes confiam na ferramenta. As pessoas fecham diálogos, mudam de ideia e digitam valores errados.

Dedo pairando sobre a tecla Esc de um notebook

O que cada ação deve disparar

AçãoO que a pessoa fezO que a ferramenta deve fazer
acceptEnviou o formulárioValidar de novo e criar a escalada
declineDisse não de propósitoDeixar o chamado como está, registrar e oferecer um caminho manual
cancelFechou o diálogoNão alterar nada, permitir nova tentativa depois

Repare na chamada safeParse do servidor. Os clientes devem validar as respostas contra o schema, e os servidores devem fazer isso de novo. Valores padrão e formatos são dicas para a interface, não garantias sobre os dados que chegam.

Devolva um resultado isError para entrada inválida, em vez de lançar uma exceção. O modelo vê a mensagem e pode pedir ao agente que execute a ferramenta novamente.

Erros que quebram a elicitation

A maioria das falhas vem de uma pequena lista de hábitos.

Dados sensíveis em formulários

Cadeado de latão em uma porta de madeira verde desgastada

A especificação é direta: servidores não devem pedir senhas, tokens de API ou credenciais de pagamento pelo modo formulário. As respostas do formulário passam pelo cliente, então podem acabar em logs e transcrições. Qualquer coisa secreta pertence ao modo URL, em que a pessoa digita a informação em uma página que o cliente não consegue ler.

Um nome ou um endereço de e-mail é diferente. O servidor pode pedir esses dados, e a pessoa pode revisar e recusar.

Schemas aninhados

Um requestedSchema com um objeto aninhado ou uma lista de objetos será rejeitado ou renderizado mal. Achate a estrutura. Se você precisar de uma lista de itens, faça várias elicitations pequenas ou aceite um enum de seleção múltipla.

Outros hábitos que causam problemas:

  • Tratar cancel como decline, para que um diálogo fechado pareça uma recusa.
  • Confiar em uma identidade digitada em um formulário. Identifique os usuários por autorização, não por um campo de texto.
  • Pedir os mesmos dados duas vezes em uma sessão.
  • Esquecer que um servidor remoto precisa vincular o estado ao usuário, e não apenas a um ID de sessão.

Uma lista curta de testes

Engenheiro de garantia de qualidade com uma prancheta de checklist e dois notebooks

Execute estes cinco casos antes que chamados reais toquem na ferramenta:

  • Aceite só com os valores padrão e confirme que affectedCustomers vira 1.
  • Aceite com um e-mail que tenha erro de digitação e confirme que o servidor o rejeita.
  • Recuse e confirme que o chamado não mudou.
  • Cancele com a tecla Esc e confirme que nada foi gravado.
  • Conecte a partir de um cliente sem elicitation e confirme que aparece o fallback em texto simples.

Você também pode executar o servidor no MCP Inspector, a partir da pasta do projeto, com npx @modelcontextprotocol/inspector npx tsx src/server.ts para acompanhar as mensagens elicitation/create brutas passando.

Rascunhe respostas com o Claude Sonnet 5

Depois que a escalada devolve uma referência, o agente ainda deve uma resposta ao cliente. O Claude Sonnet 5 no PicassoIA cuida dessa etapa de redação, e ele também lê capturas de tela, o que ajuda quando um cliente anexa uma imagem de erro.

  1. Abra a página do Claude Sonnet 5 e cole o resumo do chamado mais a referência da escalada em Prompt.
  2. Defina System Prompt uma vez: "Você escreve respostas de suporte curtas e calmas. Nunca prometa um prazo de correção."
  3. Deixe Effort em low para rascunhos rápidos. Aumente para medium ou high quando o chamado exigir raciocínio de verdade. Segundo a página do modelo, low desativa o pensamento para as respostas mais rápidas e baratas.
  4. Reduza Max Tokens do padrão 8192 para cerca de 600, para que as respostas fiquem curtas.
  5. Anexe uma captura de tela em Image se o cliente tiver enviado uma. Max Image Resolution tem como padrão 0,5 megapixel, o que basta para uma caixa de diálogo de erro.
ParâmetroPadrãoDica para respostas de suporte
EffortlowAumente só para bugs complicados
Max Tokens8192Defina perto de 600
System PromptvazioFixe o tom e os limites uma vez
Max Image Resolution0,5 MPMantenha como está para capturas de tela

Para raciocínios mais difíceis, o Claude Opus 4.7 está na mesma coleção. Comece com o Sonnet 5 e suba de modelo só quando um rascunho não acertar o ponto.

Crie suas próprias imagens com o Picasso IA

Documentações de suporte e artigos de central de ajuda ficam melhores com imagens reais. As mesmas cenas de mesa que você viu acima, um headset sobre uma mesa ou uma prancheta sob luz de janela, levam um único prompt para serem criadas.

Experimente o PicassoIA Image para uma primeira versão rápida, ou o Flux 2 Pro quando quiser uma textura mais fina. Um prompt que funciona bem:

A support engineer at a wooden desk, 35mm lens, soft window light from the left, shallow depth of field, Kodak Portra 400 film grain, no text.

Escolha um modelo, cole o prompt, mude um detalhe por execução e compare os resultados. Dez minutos de experimentos vão ensinar mais sobre a redação de prompts do que qualquer lista de regras. Abra o Picasso IA, crie sua primeira imagem de cabeçalho e coloque-a no seu próximo artigo de suporte.

Compartilhe este artigo

Escolha seu idioma