WebMCP: qué es, para qué sirve y cómo lo añadí a mi web
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

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.

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.

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:
inputSchemaes JSON Schema. Cuanto más preciso (tipos,enum,required,descriptionpor campo), menos alucina el agente al rellenarlo.executedevuelve{ content: [{ type: 'text', text }] }— el mismo formato de resultado que un tool de MCP.AbortSignalpara el ciclo de vida. Pasas unsignaly, cuando llamas acontroller.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 yexecuteTool(tool, argsJson)lo dispara. El eventotoolchangete 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.
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: ?1en el HTML. Sin eso, la API no se expone.
Permissions Policy
tools. Por defecto esself(tu propio origen puede registrar tools). Un<iframe>de otro origen necesitaallow="tools"explícito para poder hacerlo.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.modelContextcomo deprecated en favor dedocument.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.
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 |
|---|---|
| Busca en título, extracto y cuerpo. Devuelve rutas, no HTML. |
| Últimos posts publicados, con filtro opcional por categoría ( |
| Navega la pestaña a un artículo ( |
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.modelContextes nativo o si hace falta un shim.En desarrollo instala un shim mínimo de
document.modelContext(registerTool/getTools/executeTool/ eventotoolchange) 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?
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.
Se aprende haciéndolo, no leyéndolo. Todo este post sale de tropezar con
Origin-Agent-Cluster, con la migraciónnavigator→document, con el ciclo de vida porAbortSignal.
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
descriptionmal escrita hace que el agente use la tool cuando no debe.
Reglas que sigo:
Tools estrechas. Una acción concreta por tool, no un
do_everything({ action }).Human-in-the-loop para efectos secundarios. Nada de
toolautosubmiten acciones que cuesten dinero, borren datos o manden mensajes. Reservado para búsquedas y filtros.No te fíes de la entrada. Valida los argumentos del
executecomo validarías un body de API. En mis tools, cada campo pasa por unassertantes de tocar nada.Registra las llamadas. Si algo va raro, quieres saber qué tool se llamó y con qué.
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
Empieza por los cimientos: Qué es un MCP
Monta uno de servidor: Cómo crear un MCP
Interfaz dentro del chat: App visual en ChatGPT y MCP Apps vs A2UI
Cuándo cada capa: MCP vs API vs function calling
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.