Pedir dado ao usuário no meio da chamada: MRTR (InputRequiredResult)
Reservar, pagar, apagar: ação com consequência pede um sim de uma pessoa. Na revisão 2026-07-28 o servidor não abre mais um pedido próprio para perguntar. Ele devolve input_required, o cliente pergunta ao usuário e repete a chamada com a resposta. Nesta aula você vê as duas voltas e o caminho do não.
1As duas voltas: input_required e a chamada repetida com a resposta
2acceptedContent e inputResponse: ler o sim, o não e a ausência
3O cliente precisa declarar que sabe perguntar (elicitation)
4requestState para fluxo em várias etapas, assinado
Objetivo: fazer a ferramenta com consequência pedir confirmação no meio da chamada com input_required e só agir depois do sim.
01 · Duas voltas, uma reserva
Clique em cada etapa. O seu handler roda nas duas voltas.
Clique em cada bloco do diagrama para ver o que ele faz.
Antes (2025): o servidor iniciava elicitation/create, sampling e roots por conta própria. Na 2026-07-28 isso virou MRTR, e Sampling e Roots estão descontinuados.
02 · O handler de reservar_vaga
Trecho do kit/servidor.mjs. Ele é escrito para rodar nas duas voltas: lê o que já chegou e pede só o que falta.
O script mostra a 1ª volta crua (sem o cliente completar sozinho) e depois o fluxo normal.
Terminal · usuário diz sim
cd ~/projetos/formacao-webmcp-mcp-v72/kit
node teste-confirmacao.mjs sim; echo "saída: $?"
Terminal · usuário diz não
cd ~/projetos/formacao-webmcp-mcp-v72/kit
node teste-confirmacao.mjs nao; echo "saída: $?"
Arquivo · kit/teste-confirmacao.mjs
// teste-confirmacao.mjs — mostra o MRTR (revisão 2026-07-28): a ferramenta reservar_vaga
// devolve resultType "input_required"; o cliente pergunta ao usuário e repete a chamada.
// Uso: node teste-confirmacao.mjs sim (o "usuário" aceita)
// node teste-confirmacao.mjs nao (o "usuário" recusa)
import { Client, StreamableHTTPClientTransport } from '@modelcontextprotocol/client';
import { createMcpHandler } from '@modelcontextprotocol/server';
import { createServer } from './servidor.mjs';
const resposta = process.argv[2] === 'nao' ? 'nao' : 'sim';
const handler = createMcpHandler(createServer);
const ligar = async extra => {
const c = new Client({ name: 'teste-confirmacao', version: '1.0.0' },
{ versionNegotiation: { mode: 'auto' }, capabilities: { elicitation: { form: {} } }, ...extra });
await c.connect(new StreamableHTTPClientTransport(new URL('http://teste.local/mcp'),
{ fetch: (url, init) => handler.fetch(new Request(url, init)) }));
return c;
};
const args = { turma_id: 'html-01', email: 'aluno@exemplo.com' };
// 1ª volta "na mão": o que chega do servidor antes de o usuário responder.
const cru = await ligar({ inputRequired: { autoFulfill: false } });
const volta1 = await cru.request({ method: 'tools/call', params: { name: 'reservar_vaga', arguments: args } }, { allowInputRequired: true });
console.log(`1ª volta: resultType=${volta1.resultType} · pede: ${Object.keys(volta1.inputRequests ?? {}).join(', ')}`);
await cru.close();
// Fluxo normal: o SDK do cliente chama o seu handler de elicitation e repete a chamada sozinho.
const cliente = await ligar({});
cliente.setRequestHandler('elicitation/create', async req => {
console.log(`[cliente] pergunta ao usuário: ${req.params.message} → ${resposta}`);
return resposta === 'sim' ? { action: 'accept', content: { confirmar: true } } : { action: 'decline' };
});
const fim = await cliente.callTool({ name: 'reservar_vaga', arguments: args });
console.log(`resultado: ${fim.content[0].text} · isError=${fim.isError === true}`);
await cliente.close();
await handler.close();
process.exitCode = fim.isError ? 1 : 0;
Exemplo do repositório
1ª volta: resultType=input_required · pede: confirmar
[cliente] pergunta ao usuário: Reservar 1 vaga em "HTML semântico para agentes" para aluno@exemplo.com? → sim
resultado: reservado: html-01 para aluno@exemplo.com (restam 2) · isError=false
saída: 0
1ª volta: resultType=input_required · pede: confirmar
[cliente] pergunta ao usuário: Reservar 1 vaga em "HTML semântico para agentes" para aluno@exemplo.com? → nao
resultado: reserva cancelada pelo usuário · isError=true
saída: 1
Recusa vira isError e saída 1: a vaga continua lá.
04 · Erro comum: cliente que não sabe perguntar
Sem capabilities.elicitation, o servidor não tem como pedir a confirmação.
Terminal · cliente sem capacidade
cd ~/projetos/formacao-webmcp-mcp-v72/kit
node teste-sem-capacidade.mjs; echo "saída: $?"
Exemplo do repositório
recusado: -32021 Cannot request input 'confirmar' (elicitation/create): the request's client capabilities do not declare the re
saída: 1
A mensagem sai cortada em 110 caracteres pelo próprio script (slice). O SDK recusa com -32021 antes de mandar o pedido. Num host real, isso significa que aquele cliente não pode usar a ferramenta com confirmação: avise na descrição ou ofereça outro caminho.
05 · Três cuidados
1. Resposta não é confiável
inputResponses vem do cliente. Valide com esquema (acceptedContent faz isso) antes de agir.
2. Nada sobrevive entre voltas
O servidor é sem estado: a 2ª volta é outro pedido. Fluxo em várias etapas guarda o que já foi provado em requestState, assinado com createRequestStateCodec (HMAC). É assinado, não cifrado: nada de segredo dentro.
3. Cliente antigo
Numa conexão 2025, o SDK do servidor converte o input_required num elicitation/create à moda antiga (shim ligado por padrão). O mesmo handler serve as duas eras.
06 · Prática
O sim, o não e uma confirmação sua.
E12 · Sim e não
5 min
Rode os dois blocos.
Pronto quando:node teste-confirmacao.mjs nao → reserva cancelada pelo usuário · isError=true e saída: 1; com sim → reservado: html-01 e saída: 0. Nos dois, a 1ª linha é resultType=input_required.
E13 · Sem capacidade
3 min
Rode o cliente sem capacidade.
Pronto quando:recusado: -32021 e saída: 1.
E14 · Confirmação na sua ferramenta
20 min
Na ferramenta com consequência do seu serviço (aula 3), peça confirmação com inputRequired.elicit e trate o não. Copie o teste-confirmacao.mjs trocando nome e argumentos.
Pronto quando: o seu teste com nao termina em saída: 1 sem mudar nada; com sim, em saída: 0.
07 · Teste da aula
Pergunta 1 de 4
Na revisão 2026-07-28, como o servidor pede uma confirmação ao usuário?competência
Correto
O servidor não inicia mais pedidos. Ele responde input_required e o cliente pergunta e repete a chamada (MRTR).
Não é essa
O servidor não inicia mais pedidos. Ele responde input_required e o cliente pergunta e repete a chamada (MRTR).Rever: Duas voltas, uma reserva
Pergunta 2 de 4
O usuário recusou. O que o handler do kit faz?competência
Correto
inputResponse mostra action diferente de accept; o handler devolve isError e a vaga continua lá.
Um fluxo pede confirmação e depois o escopo. Onde guardar que a confirmação já aconteceu?
Correto
Sem estado, o servidor não lembra da volta anterior. requestState volta idêntico do cliente; assine com createRequestStateCodec e grave só o que já foi provado.
Não é essa
Sem estado, o servidor não lembra da volta anterior. requestState volta idêntico do cliente; assine com createRequestStateCodec e grave só o que já foi provado.Rever: Três cuidados
Pergunta 4 de 4
O cliente não declarou capabilities.elicitation e chama reservar_vaga. Resultado?
Correto
Cada pedido embutido é conferido contra as capacidades do cliente. Sem elicitation, recusa com -32021.
Não é essa
Cada pedido embutido é conferido contra as capacidades do cliente. Sem elicitation, recusa com -32021.Rever: O handler de reservar_vaga
Teste concluído
Você acertou {a} de {n}. Acertando todas, a aula é marcada como concluída.
Escolha uma alternativa. A explicação aparece depois da sua tentativa.
08 · Em três frases
MRTRinput_required na 1ª volta; o cliente pergunta e repete.
HandlerRoda nas duas voltas; age só com o sim validado.