WebMCP en producción|INEMA.CLUB-PRO

En esta clase vas a

  1. 1Qué necesita tener una línea de registro
  2. 2El envoltorio observado() en las tres puertas
  3. 3Error, rechazo, cancelación y el pedido del MRTR
  4. 4Por qué el log guarda el nombre del campo y no el valor

Objetivo: registrar cada llamada en las tres superficies con resultado y tiempo, sin datos personales, y leer los desenlaces posibles en el log.

01Del pedido a la línea en el log

El registro queda en la frontera, no dentro de la regla. Haz clic en cada etapa.

Haz clic en cada bloque del diagrama para ver qué hace.

02Los desenlaces que importan

Cada uno pide una acción distinta de quien opera el sitio.

1. erro

Una entrada que la regla no acepta: tema inválido, taller sin cupo.

Muchos errores en la misma herramienta = descripción o schema confusos para el agente.

Kitmotivo: tema inválido: cobol

03El envoltorio

El mismo archivo sirve a la página y al servidor. En la página, en producción, la línea iría a tu backend (sendBeacon, por ejemplo); en el kit queda en memoria.

Archivo · 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 los desenlaces y leer el log

El script busca, se equivoca de tema, rechaza, cancela y reserva por MCP con rechazo. Graba logs/chamadas.jsonl.

Terminal · registro de llamadas
cd ~/projetos/formacao-webmcp-expert-v72/kit
node scripts/observar.mjs nativo; echo "saída: $?"
tail -1 logs/chamadas.jsonl
Ejemplo del repositorio
== 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}

La última línea es una línea real del archivo: los campos id y nome aparecen por su nombre, nunca por su valor. Los milisegundos varían en cada ronda.

05El ensayo de la fuga

En una copia desechable del kit.

Terminal · log con datos personales
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áctica

Leer el log, ver cómo se detecta la fuga y decidir qué registra tu sitio. Cada ejercicio termina en un listo cuando.

E1 · Los desenlaces en el log

5 min

Corre el bloque del registro y cuenta las líneas por desenlace.

Listo cuando: 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 y saída: 0.

E2 · Detectar la fuga

3 min

Corre el ensayo del log con la entrada completa.

Listo cuando: El bloque imprime dado pessoal no registro: SIM y saída: 1.

E3 · El registro de tu sitio

15 min

Para las herramientas de tu sitio, escribe qué campos entran en el log (solo el nombre), a dónde va la línea en producción y quién revisa el resumen cada semana.

Listo cuando: El archivo registro-[tu sitio].md tiene una línea por herramienta, el destino del log y la persona responsable, y ningún campo con valor de entrada.

07Prueba de la clase

La explicación aparece después de la respuesta.

Pregunta 1 de 4

La reserva por MCP con confirmación dejó dos líneas en el log. ¿Por qué?

08En tres frases

LíneaSuperficie, herramienta, nombres de los campos, desenlace y tiempo: una por llamada.
DesenlaceError, rechazo, cancelamiento y pedido del MRTR cuentan historias diferentes.
PrivacidadEl valor de la entrada queda fuera del log; el script atrapa a quien se olvide.

Para ir más allá

Clase anteriorPróxima clase