WebMCP em produção|INEMA.CLUB-PRO

Nesta aula você vai

  1. 1O que uma linha de registro precisa ter
  2. 2O embrulho observado() nas três portas
  3. 3Erro, recusa, cancelamento e o pedido do MRTR
  4. 4Por que o log guarda o nome do campo e não o valor

Objetivo: registrar cada chamada nas três superfícies com resultado e tempo, sem dado pessoal, e ler os desfechos possíveis no log.

01Do pedido à linha no log

O registro fica na fronteira, não dentro da regra. Clique em cada etapa.

Clique em cada bloco do diagrama para ver o que ele faz.

02Os desfechos que importam

Cada um pede uma ação diferente de quem opera o site.

1. erro

Entrada que a regra não aceita: tema inválido, oficina sem vaga.

Muitos erros na mesma ferramenta = descrição ou schema confusos para o agente.

Kitmotivo: tema inválido: cobol

03O embrulho

O mesmo arquivo serve a página e o servidor. Na página, em produção, a linha iria para o seu backend (sendBeacon, por exemplo); no kit fica em memória.

Arquivo · kit/pagina/registro.mjs
// registro.mjs — observabilidade nas fronteiras: uma linha por chamada de ferramenta, em qualquer superfície.
// Roda no navegador (página) e no Node (servidor MCP). Não guarda dado pessoal: só o NOME dos campos de entrada.
// Resultado: ok | erro (regra recusou) | recusado (a pessoa disse não) | cancelado (AbortSignal) | excecao
//            | pediu-pessoa (MCP: a 1ª volta do MRTR devolve input_required e o cliente pergunta ao usuário).

const linhas = [];
let destino = null;   // função que recebe cada linha (no servidor: grava JSONL; na página: sendBeacon em produção)

export function aoRegistrar(fn) { destino = fn; }
export function linhasRegistradas() { return linhas.map(l => ({ ...l })); }

export function classificar(r) {
  if (r && typeof r === 'object') {
    if (r.resultType === 'input_required') return 'pediu-pessoa';
    if ('erro' in r) return 'erro';
    if (r.reservado === false) return 'recusado';
  }
  return 'ok';
}

export function anotar(linha) {
  const l = { t: new Date().toISOString(), ...linha };
  linhas.push(l);
  globalThis.__registro = linhas;    // os testes leem daqui
  destino?.(l);
  return l;
}

// Embrulha o execute de uma ferramenta: mede o tempo e anota o resultado, sem mudar o que ela devolve.
export function observado(superficie, ferramenta, fn) {
  return async (input = {}, opcoes) => {
    const inicio = performance.now();
    const base = { superficie, ferramenta, campos: Object.keys(input).sort() };
    try {
      const r = await fn(input, opcoes);
      anotar({ ...base, resultado: classificar(r), ms: Math.round(performance.now() - inicio), ...(r?.erro ? { motivo: r.erro } : {}) });
      return r;
    } catch (e) {
      anotar({ ...base, resultado: e.name === 'AbortError' ? 'cancelado' : 'excecao', ms: Math.round(performance.now() - inicio), motivo: e.name });
      throw e;
    }
  };
}

04Provocar os desfechos e ler o log

O script busca, erra o tema, recusa, cancela e reserva pelo MCP com recusa. Grava logs/chamadas.jsonl.

Terminal · registro de chamadas
cd ~/projetos/formacao-webmcp-expert-v72/kit
node scripts/observar.mjs nativo; echo "saída: $?"
tail -1 logs/chamadas.jsonl
Exemplo do repositório
== Chromium 156.0.8078.4 (Playwright) · modo: nativo
html   buscar_oficinas  ok              0 ms
webmcp buscar_oficinas  ok              0 ms
webmcp buscar_oficinas  erro            3 ms · tema inválido: cobol
webmcp reservar_vaga    recusado      184 ms
webmcp reservar_vaga    cancelado     319 ms · AbortError
mcp    buscar_oficinas  ok              0 ms
mcp    reservar_vaga    pediu-pessoa   26 ms
mcp    reservar_vaga    recusado        2 ms
REGISTRO: 8 chamadas · ok 3 · erro 1 · recusado 2 · cancelado 1 · pediu-pessoa 1 · dado pessoal no registro: não
gravado em logs/chamadas.jsonl
saída: 0
{"t":"2026-10-19T20:03:13.159Z","superficie":"mcp","ferramenta":"reservar_vaga","campos":["id","nome"],"resultado":"recusado","ms":2}

A última linha é uma linha real do arquivo: campos id e nome aparecem pelo nome, nunca pelo valor. Os milissegundos variam a cada rodada.

05O ensaio do vazamento

Numa cópia descartável do kit.

Terminal · log com dado pessoal
cd ~/projetos/formacao-webmcp-expert-v72/kit
E=$(bash scripts/copia.sh a3)
# o erro comum: registrar a entrada inteira "para depurar"
sed -i 's/campos: Object.keys(input).sort()/entrada: input/' $E/pagina/registro.mjs
cd $E && node scripts/observar.mjs nativo | grep "REGISTRO"; echo "saída: ${PIPESTATUS[0]}"

06Prática

Ler o log, ver o vazamento ser pego e decidir o que o seu site registra. Cada exercício termina num pronto quando.

E1 · Os desfechos no log

5 min

Rode o bloco do registro e conte as linhas por desfecho.

Pronto quando: node scripts/observar.mjs nativo; echo "saída: $?" → REGISTRO: 8 chamadas · ok 3 · erro 1 · recusado 2 · cancelado 1 · pediu-pessoa 1 · dado pessoal no registro: não e saída: 0.

E2 · Pegar o vazamento

3 min

Rode o ensaio do log com a entrada inteira.

Pronto quando: O bloco imprime dado pessoal no registro: SIM e saída: 1.

E3 · O registro do seu site

15 min

Para as ferramentas do seu site, escreva quais campos entram no log (só o nome), para onde a linha vai em produção e quem olha o resumo por semana.

Pronto quando: O arquivo registro-[seu site].md tem uma linha por ferramenta, o destino do log e o responsável, e nenhum campo com valor de entrada.

07Teste da aula

A explicação aparece depois da resposta.

Pergunta 1 de 4

A reserva pelo MCP com confirmação deixou duas linhas no log. Por quê?

08Em três frases

LinhaSuperfície, ferramenta, nomes dos campos, desfecho e tempo: uma por chamada.
DesfechoErro, recusa, cancelamento e pedido do MRTR contam histórias diferentes.
PrivacidadeO valor da entrada fica fora do log; o script pega quem esquecer.

Para ir além

Aula anteriorPróxima aula