IA

WebMCP: qué es, para qué sirve y cómo lo añadí a mi web

· 12 min de lectura

WebMCP deja que tu web ofrezca acciones a los agentes de IA sin que "clicando" el DOM. Qué es, las dos APIs, requisitos de Chrome y cómo lo monté en esta web

WebMCP: tu web ofrece acciones a los agentes de IA en vez de que "clicando" el DOM

1. Qué es WebMCP

El problema que resuelve

Hoy, cuando un agente de IA usa tu web, hace lo que en la spec llaman actuación: simula un usuario. Captura pantalla, parsea el DOM, decide "¿este es el botón de buscar?", escribe en el input, clica, vuelve a capturar pantalla para ver qué pasó. Cada uno de esos pasos gasta tokens, es lento y se rompe en cuanto cambias una clase CSS o el orden de dos divs.

WebMCP le da la vuelta: tu web declara herramientas (tools) — funciones con nombre, descripción, un esquema JSON de entrada y un execute — y el agente las llama directamente. Ni screenshots ni adivinar. Es la misma idea del Model Context Protocol, pero en lugar de vivir en un servidor, vive en la pestaña del navegador.

Comparación: sin WebMCP el agente captura pantalla y adivina el DOM; con WebMCP llama a una tool declarada

2. WebMCP vs MCP "de servidor"

Si vienes de la serie de MCP, la diferencia clave es dónde corre el tool y con qué identidad:

MCP de servidor

WebMCP

Dónde vive

Proceso local (stdio) o servidor remoto (HTTP)

En el JavaScript de tu página, en la pestaña

Quién lo arranca

El usuario lo configura en Claude Desktop / Cursor

Nadie: se registra solo al cargar la web

Identidad / sesión

La que le des al server (API keys, OAuth propio)

La sesión del navegador del usuario (sus cookies, su login)

Descubrimiento

El cliente ya sabe que el server existe

El agente descubre las tools al visitar la página

Estado de la página

No lo tiene

Acceso total al DOM, al store, a lo que el usuario está viendo

Caso típico

"Conecta mi CRM / mi base de datos / mi filesystem"

"Deja que el agente use ESTA web como la usaría el usuario"

No compiten: resuelven momentos distintos. MCP de servidor es para darle a un agente una capacidad nueva. WebMCP es para que la web que el usuario ya tiene abierta sea operable por un agente sin fricción. Para la comparación con APIs y function calling, tienes el post dedicado.

MCP de servidor: el tool corre en un proceso/servidor con su propia identidad. WebMCP: el tool corre en la pestaña con la sesión del usuario

3. Cómo funciona: las dos APIs

WebMCP tiene dos formas de declarar tools. Puedes mezclarlas.

3.1. API imperativa (JavaScript)

La base es document.modelContext.registerTool():

// Detecta soporte primero. La API se movió de navigator.modelContext
// a document.modelContext a mediados de 2026: comprueba las dos.
const modelContext = ('modelContext' in document && document.modelContext)
  || ('modelContext' in navigator && navigator.modelContext);

if (modelContext) {
  const controller = new AbortController();

  await modelContext.registerTool(
    {
      name: 'search_products',
      description:
        'Busca productos del catálogo por texto libre. Devuelve nombre, precio y URL.',
      inputSchema: {
        type: 'object',
        properties: {
          query: { type: 'string', description: 'Palabras clave, p. ej. "zapatillas trail talla 43"' },
          maxResults: { type: 'number', description: 'Máximo de resultados (por defecto 5)' },
        },
        required: ['query'],
        additionalProperties: false,
      },
      execute: async ({ query, maxResults }, { signal }) => {
        const res = await fetch(`/api/search?q=${encodeURIComponent(query)}`, { signal });
        const data = await res.json();
        return {
          content: [
            { type: 'text', text: JSON.stringify(data.slice(0, maxResults ?? 5), null, 2) },
          ],
        };
      },
    },
    { signal: controller.signal }, // controller.abort() desregistra la tool
  );
}

Puntos que importan:

  • inputSchema es JSON Schema. Cuanto más preciso (tipos, enum, required, description por campo), menos alucina el agente al rellenarlo.

  • execute devuelve { content: [{ type: 'text', text }] } — el mismo formato de resultado que un tool de MCP.

  • AbortSignal para el ciclo de vida. Pasas un signal y, cuando llamas a controller.abort(), la tool desaparece. Útil en SPAs: registras tools de una vista y las quitas al salir.

  • Descubrir y ejecutar a mano: document.modelContext.getTools() lista lo registrado y executeTool(tool, argsJson) lo dispara. El evento toolchange te avisa cuando la lista cambia. (Esto es justo lo que uso en mi playground local, más abajo.)

3.2. API declarativa (HTML)

Para formularios que ya existen, no hace falta JS: anotas el <form>.

<form toolname="supportRequest"
      tooldescription="Envía una solicitud de soporte al equipo correcto."
      action="/support/submit"
      method="post">

  <label for="firstName">Nombre</label>
  <input type="text" name="firstName" id="firstName" required>

  <label for="message">Mensaje</label>
  <textarea name="message" id="message"
            toolparamdescription="Descripción del problema del usuario." required></textarea>

  <select name="team" required
          toolparamdescription="Determina a qué equipo se enruta: devoluciones, envíos o soporte web.">
    <option value="returns">Quiero devolver una compra</option>
    <option value="shipping">Dónde está mi pedido</option>
    <option value="web">Ayuda con la web</option>
  </select>

  <button type="submit">Enviar</button>
</form>
  • toolname / tooldescription: nombre y para qué sirve (esto es lo que lee el agente para decidir si la usa).

  • toolparamdescription: describe cada campo dentro del schema que el navegador genera solo a partir del form.

  • toolautosubmit: si lo pones, el agente puede enviar el formulario sin confirmación humana. Úsalo solo en acciones reversibles (una búsqueda, un filtro). Nunca en "pagar" o "borrar cuenta".

  • Para el feedback visual tienes las pseudoclases CSS :tool-form-active (en el form mientras el agente lo usa) y :tool-submit-active (en el botón de submit).


4. Requisitos del navegador

Esto es lo que me llevó más tiempo entender, porque si falla, document.modelContext sencillamente no existe y no hay error que te lo diga.

  1. Documento aislado por origen. WebMCP solo está disponible en documentos origin-isolated. En la práctica: tu servidor tiene que mandar la cabecera de respuesta

    Origin-Agent-Cluster: ?1
    

    en el HTML. Sin eso, la API no se expone.

  2. Permissions Policy tools. Por defecto es self (tu propio origen puede registrar tools). Un <iframe> de otro origen necesita allow="tools" explícito para poder hacerlo.

  3. Un Chrome que lo traiga. A día de hoy:

    • Apareció en Chrome 146 Canary (feb 2026) tras el flag chrome://flags/#enable-webmcp-testing.

    • Hay prueba de origen de Chrome 149 a 156 — la anunciaron en Google I/O 2026. Eso te deja usarlo en producción para usuarios reales sin pedirles que toquen flags.

    • Chrome 150 marcó navigator.modelContext como deprecated en favor de document.modelContext (por eso el feature-detect de arriba comprueba los dos).

    • No está activado por defecto en estable todavía. El despliegue general se espera hacia finales de 2026.

    • Es solo Chromium (Chrome y, previsiblemente, Edge). Firefox y Safari están en la conversación del estándar, sin fecha.

  4. Hoy casi ningún agente consume estas tools. La spec está publicada, Chrome la sirve, empresas grandes (Booking, Shopify, Etsy, Instacart, Target…) están en la prueba de origen — pero el consumidor natural, Gemini dentro de Chrome, aún no lo explota de forma masiva. Es un problema de huevo y gallina que Google controla por los dos lados.


5. Cómo lo he añadido a omarjs.com

Este blog es Angular con SSR. WebMCP ya está montado. Estas son las piezas.

5.1. La cabecera

En el servidor mando la cabecera en cada respuesta HTML:

res.setHeader('Origin-Agent-Cluster', '?1');
{
  "headers": [
    { "source": "/(.*)", "headers": [
      { "key": "Origin-Agent-Cluster", "value": "?1" }
    ]}
  ]
}

Sin esto, todo lo demás es inútil: document.modelContext no aparece.

5.2. Las tools, con la API de Angular 22

Angular 22 trae provideExperimentalWebMcpTools(). Registras las tools como un provider y corren en el injection context, así que puedes inject() tus servicios dentro del execute. Lo tengo en un provider aparte y lo enchufo en app.config.ts:

// src/app/app.config.ts
import { provideBlogWebMcpTools } from './core/webmcp/blog-webmcp.providers';

export const appConfig: ApplicationConfig = {
  providers: [
    // ...resto de providers
    provideBlogWebMcpTools(),
  ],
};
// src/app/core/webmcp/blog-webmcp.providers.ts (recortado)
import { inject, provideExperimentalWebMcpTools } from '@angular/core';
import { Router } from '@angular/router';
import { ArticleService } from '../articles/article.service';
import { searchArticles } from '../../shared/utils/article-search';

export function provideBlogWebMcpTools() {
  return provideExperimentalWebMcpTools([
    {
      name: 'search_articles',
      description:
        'Search published blog articles by keywords in title, excerpt, or body. ' +
        'Returns compact matches with paths.',
      inputSchema: {
        type: 'object',
        properties: {
          query: { type: 'string', description: 'Search keywords (e.g. "Angular signals").' },
          maxResults: { type: 'number', description: 'Maximum number of results (default 5).' },
        },
        required: ['query'],
        additionalProperties: false,
      },
      execute: async ({ query, maxResults }) => {
        const articlesApi = inject(ArticleService);
        const matches = searchArticles(await articlesApi.getPublishedArticles(), query)
          .slice(0, maxResults ?? 5)
          .map((a) => ({ title: a.title, path: `/${a.category}/${a.slug}`, excerpt: a.excerpt }));

        return { content: [{ type: 'text', text: JSON.stringify(matches, null, 2) }] };
      },
    },
    // list_articles  -> lista los últimos posts, con filtro opcional por categoría
    // open_article   -> navega el navegador a /categoria/slug con el Router de Angular
  ]);
}

Tres tools, y cada una hace algo que un agente querría de verdad en un blog:

Tool

Qué hace

search_articles

Busca en título, extracto y cuerpo. Devuelve rutas, no HTML.

list_articles

Últimos posts publicados, con filtro opcional por categoría (ia, frontend, css…).

open_article

Navega la pestaña a un artículo (/ia/webmcp) usando el Router. Acción, no solo lectura.

Fíjate en que search_articles y list_articles son de solo lectura y reversibles (candidatas a auto-ejecución sin molestar al usuario), mientras que open_article cambia lo que el usuario ve: ahí sí quieres que el agente lo haga de forma visible.

5.3. Un playground para probarlas sin esperar a Chrome

Como el soporte nativo aún va por flags y prueba de origen, me hice una ruta solo local (/webmcp-lab, con noindex) que:

  • Detecta si document.modelContext es nativo o si hace falta un shim.

  • En desarrollo instala un shim mínimo de document.modelContext (registerTool / getTools / executeTool / evento toolchange) para no depender del flag.

  • Lista las tools registradas y te deja ejecutarlas a mano con un JSON de entrada, que es exactamente lo que haría un agente.

El shim nunca pisa una implementación nativa real: si el documento está origin-isolated y el navegador ya expone la API, se aparta. Así el mismo código me sirve para aprender en local hoy y para producción cuando Chrome lo active para todos.


6. Qué función tiene esto en esta web

Voy a ser directo: hoy, el retorno inmediato de WebMCP en un blog personal es casi cero. Ningún agente masivo lee mis tres tools todavía. Entonces, ¿por qué lo he puesto?

  1. Cuesta muy poco. Una cabecera, un provider y tres funciones que reutilizan servicios que ya tenía. No es deuda técnica: si la spec cambia, toco un archivo.

  2. Se aprende haciéndolo, no leyéndolo. Todo este post sale de tropezar con Origin-Agent-Cluster, con la migración navigatordocument, con el ciclo de vida por AbortSignal.

Donde la historia cambia por completo es en e-commerce, reservas, banca, SaaS transaccional: ahí "buscar producto", "añadir al carrito", "elegir fecha", "reprogramar cita" son acciones con valor directo, y una web que las expone bien es operable por un agente sin que el agente se pelee con tu DOM. Para esos casos, WebMCP no es un experimento simpático: es infraestructura de canal.


7. Seguridad

Las tools de WebMCP se ejecutan con la sesión del usuario: sus cookies, su login, sus permisos. Eso abre la puerta si no tienes cuidado:

  • Inyección de prompt: un agente manipulado llama a tus tools legítimas con intención maliciosa.

  • Confused deputy: contenido de otro origen consigue que tu tool haga algo que el usuario no pidió.

  • Descripciones engañosas: una description mal escrita hace que el agente use la tool cuando no debe.

Reglas que sigo:

  1. Tools estrechas. Una acción concreta por tool, no un do_everything({ action }).

  2. Human-in-the-loop para efectos secundarios. Nada de toolautosubmit en acciones que cuesten dinero, borren datos o manden mensajes. Reservado para búsquedas y filtros.

  3. No te fíes de la entrada. Valida los argumentos del execute como validarías un body de API. En mis tools, cada campo pasa por un assert antes de tocar nada.

  4. Registra las llamadas. Si algo va raro, quieres saber qué tool se llamó y con qué.

  5. Nada de headless. WebMCP está pensado para flujos locales con un humano delante, no para automatización masiva sin supervisión.


FAQ

¿WebMCP reemplaza a los MCP de servidor?

No. Un MCP de servidor le da a un agente una capacidad nueva (tu CRM, tu base de datos) desde fuera del navegador. WebMCP hace que la web que el usuario ya tiene abierta sea operable por un agente, con la sesión del usuario. Conviven.

¿Necesito reescribir mi web para soportar WebMCP?

Para lo básico, no: puedes anotar formularios existentes con la API declarativa, o registrar unas pocas tools imperativas que llamen a tus endpoints actuales. Para exponerlo bien de verdad sí ayuda tener la lógica separada de los componentes de UI.

¿Funciona ya en el Chrome de mis usuarios?

Depende. Está en prueba de origen (Chrome 149–156): si te registras en la prueba y mandas la cabecera, funciona para usuarios reales sin flags. Activado por defecto para todo el mundo, se espera hacia finales de 2026. Fuera de Chromium (Firefox, Safari), aún no.

¿webmcp.dev y WebMCP son lo mismo?

No. webmcp.dev es una librería JS independiente que conecta tu página a clientes MCP externos (Claude Desktop) con un token. WebMCP "el estándar" es la API nativa document.modelContext de Chrome. Este post va del estándar.

¿Y esto qué tiene que ver con SEO?

Directamente, nada de ranking clásico. Indirectamente: si los agentes se vuelven una vía real de tráfico y conversión, estar o no estar disponible como tool será el equivalente a estar o no indexado.

¿Qué necesito como mínimo para probarlo hoy?

Chrome con chrome://flags/#enable-webmcp-testing, tu web sirviendo Origin-Agent-Cluster: ?1, y una tool registrada con document.modelContext.registerTool(...). Con eso, document.modelContext.getTools() en la consola ya te la lista.

Siguiente paso

Si os interesa, el próximo paso lógico es un tutorial paso a paso — "añade WebMCP a una web Angular desde cero" — con el repo de este blog como base y el /webmcp-lab para probar sin esperar a Chrome. Lo dejo anotado.

Enlaces