WebMCP on your site|INEMA.CLUB-PRO

In this lesson you will

  1. 1The fields of a tool and the rules for name and description
  2. 2inputSchema in JSON Schema, closed and described
  3. 3executeTool: an object goes in, JSON text comes back
  4. 4Your first tool in extras.mjs

Goal: register a tool with a name, description, inputSchema, and execute, and call it the way an agent would, seeing both the result and the screen.

01From definition to result

Click each step.

Click each block in the diagram to see what it does.

Spec from 10/16/2026 and Chromium 156.0.8078.4. If it changes, lesson 1 will say so: check the date first.

02The kit's search tool

Excerpt from kit/pagina/ferramentas.mjs. The same agenda.buscar serves both the form and the tool.

Excerpt · kit/pagina/ferramentas.mjs
{
  name: 'buscar_oficinas',
  title: 'Buscar oficinas',
  description: 'Lista as oficinas da agenda por tema. Só lê: não reserva nada. Use apenasComVaga=true para ver só as que ainda têm vaga.',
  inputSchema: {
    type: 'object',
    properties: {
      tema: { type: 'string', enum: TEMAS, description: 'Tema da oficina.' },
      apenasComVaga: { type: 'boolean', description: 'true = só oficinas com vaga.' }
    },
    additionalProperties: false
  },
  annotations: { readOnlyHint: true },
  execute: comErroUtil(async input => {
    const lista = agenda.buscar(input);
    ui.mostrarResultados(lista, input);   // the person sees the same thing the agent received
    return { total: lista.length, oficinas: lista };
  })
}
Excerpt · registering with detection (ferramentas.mjs)
const mc = document.modelContext;   // current name (10/16/2026)
if (!mc || typeof mc.registerTool !== 'function') {
  aviso?.('Este navegador não expõe WebMCP. Tudo funciona pelo formulário.');
  return { modo: 'sem', registradas: 0 };
}
const pagina = new AbortController();
addEventListener('pagehide', () => pagina.abort(), { once: true });
await registrar(mc, defs, pagina.signal);

03Calling it the way an agent would

listar.mjs runs getTools; executar.mjs runs executeTool with the object you pass and shows the screen afterward.

Terminal · list and run
cd ~/projetos/formacao-webmcp-site-v72/kit
node scripts/listar.mjs nativo
node scripts/executar.mjs nativo buscar_oficinas '{"tema":"webmcp","apenasComVaga":true}'; echo "saída: $?"
Example from the repository
== Chromium 156.0.8078.4 (Playwright) · modo: nativo
buscar_oficinas      readOnlyHint
listar_comentarios   readOnlyHint, untrustedContentHint
minhas_reservas      readOnlyHint
reservar_vaga        consequentialHint
4 ferramentas registradas
== Chromium 156.0.8078.4 (Playwright) · modo: nativo
resultado (string): {"total":1,"oficinas":[{"id":"of-301","titulo":"Primeira ferramenta WebMCP","tema":"webmcp","data":"2026-11-18","vagas":4}]}
tela · resultados: 1 · reservas: Nenhuma reserva.
saída: 0

"resultado (string)": the browser returns JSON text, as the spec says. The list on the screen also ended up with 1 workshop.

04Your first tool

The block copies the page to a sandbox, writes an extras.mjs with contar_vagas, and checks it. To make it stick in the kit, paste the same list into kit/pagina/extras.mjs.

Terminal · sandbox with contar_vagas
cd ~/projetos/formacao-webmcp-site-v72/kit
E=${TMPDIR:-/tmp}/ensaio-webmcp-site/a3; rm -rf $E; mkdir -p $E; cp -r pagina/. $E/
cat > $E/extras.mjs <<'FIM'
export function extras(agenda) {
  return [{
    name: 'contar_vagas',
    description: 'Soma as vagas livres das oficinas de um tema. Só lê: não reserva nada.',
    inputSchema: {
      type: 'object',
      properties: { tema: { type: 'string', enum: ['html', 'javascript', 'webmcp', 'mcp'], description: 'Tema da oficina.' } },
      required: ['tema'],
      additionalProperties: false
    },
    annotations: { readOnlyHint: true },
    async execute({ tema }) {
      const lista = agenda.buscar({ tema });
      return { tema, vagas: lista.reduce((soma, o) => soma + o.vagas, 0) };
    }
  }];
}
FIM
PAGINA=$E node scripts/listar.mjs nativo | tail -1
PAGINA=$E node scripts/executar.mjs nativo contar_vagas '{"tema":"html"}'; echo "saída: $?"
Example from the repository
5 ferramentas registradas
== Chromium 156.0.8078.4 (Playwright) · modo: nativo
resultado (string): {"tema":"html","vagas":3}
tela · resultados: 6 · reservas: Nenhuma reserva.
saída: 0

Before extras.mjs, the count was 4. The HTML total: 3 spots in of-101 and 0 in of-102.

05Practice

Native mode; switch to polyfill if you want to compare. Each exercise ends with a done when.

E1 · Call the search

5 min

Run listar and executar. Change the topic to html and then to cobol and compare.

Done when: node scripts/executar.mjs nativo buscar_oficinas '{"tema":"webmcp","apenasComVaga":true}' → resultado (string): {"total":1, and saída: 0; with cobol → {"erro":"tema inválido: cobol"}.

E2 · contar_vagas in the kit

10 min

Paste the contar_vagas list into kit/pagina/extras.mjs (inside the return [ ]).

Done when: node scripts/listar.mjs nativo | tail -1 → 5 ferramentas registradas (before: 4).

E3 · A tool of your own

15 min

Write another read-only tool in extras.mjs, for example proxima_oficina (the one with the nearest date). Name with _, a description that says what it doesn't do, closed schema.

Done when: node scripts/executar.mjs nativo [name] '{}' → resultado (string): with the expected JSON and saída: 0.

06Lesson quiz

The explanation appears after your answer.

Question 1 of 4

What does executeTool return in the October 16, 2026 spec?

07In three sentences

DefinitionA name with a rule, a clear description, a closed inputSchema, and an execute that returns an object.
CallgetTools lists; executeTool takes an object and returns JSON text.
On screenThe execute uses the same rule as the form and shows the person what the agent received.

To go further

Previous lessonNext lesson