---
title: "WebMCP: la API que deja a tu web ofrecer herramientas a los agentes"
excerpt: "WebMCP es la propuesta de Google y Microsoft para que una página registre tools que un agente del navegador puede llamar. Qué es, en qué estado está y cómo se escribe, con la API imperativa y la declarativa."
date: "2026-08-26T11:00:00.000Z"
category: "Inteligencia Artificial"
tech_article: true
author:
  name: "angel cruz"
  picture: "/images/me/angel-cruz.png"
ogImage:
  url: "/images/open-graph/mcp-opengraph-image.png"
seo_title: "WebMCP: qué es y cómo funciona la API de tools en el navegador"
seo_description: "Guía de WebMCP: document.modelContext, registerTool, la API declarativa en formularios, seguridad y el origin trial de Chrome 149. Con código real."
---

Hoy, cuando un agente de IA usa tu web, adivina. Mira el DOM, decide que ese `<button>` probablemente envía el formulario, hace clic y espera que salga bien. **WebMCP** propone lo contrario: que la propia página registre herramientas con nombre, descripción y esquema de entrada, y que el agente llame a esas funciones en vez de simular clics.

Es una propuesta de estándar web que Google y Microsoft están incubando en el **Web Machine Learning Community Group** del W3C, y desde Chrome 149 se puede probar en un origin trial. Aquí va lo que dicen las fuentes originales: la especificación, el explainer y la documentación de Chrome.

## Qué es WebMCP exactamente

Una sola cosa nueva en el DOM: `document.modelContext`. Desde ahí registras *tools*, que son funciones JavaScript con un JSON Schema de entrada y una descripción en lenguaje natural. Un agente que esté operando en esa pestaña las descubre y las invoca.

El vocabulario viene de [MCP](/post/introduccion-a-mcp-model-context-protocol): tools, schemas, parámetros. Pero el explainer es explícito en que **WebMCP no reemplaza a MCP**. MCP conecta un modelo con servidores; WebMCP vive dentro de una página, con las reglas del navegador (orígenes, permissions policy, DOM) y reutilizando la lógica de cliente que ya escribiste.

Los objetivos declarados en el explainer son estos, y el tercero es el más interesante para quien tiene un producto web:

- Flujos con humano en el bucle, donde el usuario ve la página y mantiene el control.
- Integración de agentes sin automatización frágil de UI.
- **Evitar la desintermediación**: que tu frontend se adapte a los agentes en lugar de que lo salteen con una integración de backend.
- Reutilizar la lógica de cliente que ya tienes.
- Accesibilidad: un agente como intermediario capaz para quien usa tecnología asistiva.

Y los no objetivos importan igual: no está pensado para navegación headless, ni para flujos totalmente autónomos sin humano, ni para reemplazar la interfaz humana. Las tools se suman a la UI, no la sustituyen.

## En qué estado está de verdad

Aquí es donde conviene ser preciso, porque circula mucho artículo que lo presenta como si ya fuera estándar.

- La especificación es un **Draft Community Group Report**, con fecha del 26 de agosto de 2026. No es un estándar del W3C. Los editores son Brandon Walderman (Microsoft), Khushal Sagar y Dominic Farolino (Google).
- En Chrome hubo developer trial desde la 146 y el **origin trial va de Chrome 149 a Chrome 156**. El estado de la feature en Chrome Platform Status sigue siendo "Proposed", y la review del TAG está pendiente: la piden antes de shippear.
- Firefox y Safari: **sin señal**. Ninguno de los dos ha publicado una posición.

O sea: es real, es probable, es escribible hoy, y no está garantizado. Si lo adoptas ahora, lo haces como progressive enhancement detrás de un `if ('modelContext' in document)`, no como camino crítico.

## La API imperativa

El caso normal. Registras una tool con `registerTool`:

```js
await document.modelContext.registerTool({
  name: 'toggle_layer',
  description: 'Control pizza layers (sauce, cheese). Use "add", "remove", or "toggle".',
  inputSchema: {
    type: 'object',
    properties: {
      layer: { type: 'string', enum: ['sauce-layer', 'cheese-layer'] },
      action: { type: 'string', enum: ['add', 'remove', 'toggle'] },
    },
    required: ['layer'],
  },
  execute: async ({ layer, action }) => {
    await toggleLayer(layer, action);
    return `Performed ${action || 'toggle'} on layer: ${layer}`;
  },
});
```

Cuatro piezas: `name`, `description`, `inputSchema` en JSON Schema y `execute`. La especificación limita el nombre a 128 caracteres alfanuméricos ASCII con guiones, guiones bajos y puntos, y exige que la descripción no esté vacía.

Fíjate en el `return`: la tool devuelve una frase, no un objeto opaco. Ese texto es lo que el modelo lee para saber si funcionó, así que escríbelo pensando en que lo va a leer un modelo.

`execute` recibe además un `AbortSignal` como segundo argumento, que es lo que te permite cancelar trabajo largo cuando el agente abandona:

```js
execute: async ({ url, priority }, { signal }) => {
  const response = await fetch(url, { priority, signal });
  const stream = response.body.pipeThrough(new TextDecoderStream());
  for await (const chunk of stream) {
    document.querySelector('pre').textContent += chunk;
  }
  return 'Success';
}
```

Para desregistrar una tool, otro `AbortController`, esta vez en las opciones del registro:

```js
const controller = new AbortController();
await document.modelContext.registerTool(addTodoTool, { signal: controller.signal });
controller.abort(); // adiós tool
```

Y el resto de la superficie: `getTools()` lista lo registrado en el documento y sus descendientes, `executeTool()` ejecuta una tool en su documento de origen, y el evento `toolchange` avisa cuando algo se registra o se va.

```js
document.modelContext.addEventListener('toolchange', () => {
  // la lista de tools cambió
});
```

Si trabajas con tipos, existe el paquete `webmcp-types` en npm. Para React hay hooks experimentales en `usewebmcp`, y Angular tiene soporte experimental propio.

## La API declarativa: formularios anotados

La segunda vía no lleva JavaScript. Anotas un formulario que ya existe y se convierte en una tool:

```html
<form toolname="supportRequestTool"
      tooldescription="Submit a request for support."
      action="/submit">
  <label for="firstName">First Name</label>
  <input type=text name=firstName>

  <select name="select" required
    toolparamdescription="Determines what team this request is routed to.">
    <option value="Customer happiness team">Return my purchase.</option>
    <option value="Distribution team">Check where my package is.</option>
  </select>

  <button type=submit>Submit</button>
</form>
```

`toolname` y `tooldescription` sobre el `<form>`, `toolparamdescription` sobre cada campo que necesite explicación. Los `name` de los inputs se convierten en las propiedades del schema.

Con `toolautosubmit` el formulario se envía solo cuando lo invoca un agente, y el `SubmitEvent` trae dos cosas nuevas para que distingas quién lo disparó y devuelvas un resultado:

```html
<form toolautosubmit toolname="search_tool"
      tooldescription="Search the web" action="/search">
  <input type=text name=query>
</form>

<script>
  document.querySelector("form").addEventListener("submit", (e) => {
    e.preventDefault();
    if (!myFormIsValid()) {
      if (e.agentInvoked) { e.respondWith(myFormValidationErrorPromise); }
      return;
    }
    if (e.agentInvoked) { e.respondWith(Promise.resolve("Search is done!")); }
  });
</script>
```

`e.agentInvoked` es booleano y `e.respondWith(promise)` devuelve el resultado al modelo. Para un formulario de contacto o una búsqueda, esto es todo el trabajo.

## Seguridad: lo que hay que leer antes de registrar nada

WebMCP amplía la superficie de ataque de tu web de una forma nueva, y la especificación lo asume por escrito. Documenta tres vectores de prompt injection: por metadatos (nombre y descripción de la tool), por la salida de la tool y por la implementación misma.

Las defensas que trae la plataforma:

- **Aislamiento de origen obligatorio.** WebMCP solo funciona en documentos con origin isolation. Si usas `document.domain` o mandas `Origin-Agent-Cluster: ?0`, queda deshabilitado.
- **Permissions Policy.** Está detrás de la policy `tools`, con allowlist por defecto `['self']`. Un iframe cross-origin necesita `allow="tools"` explícito.
- **`exposedTo`.** Al registrar puedes limitar qué orígenes ven esa tool: `{ exposedTo: ['https://example.com'] }`.
- **Anotaciones.** `readOnlyHint` marca una tool que no muta nada y `untrustedContentHint` marca que lo que devuelve viene de contenido no confiable, para que el agente lo trate como datos y no como instrucciones.

La regla mental es la de siempre con agentes: la tool es una API pública más. Lo que no expondrías en un endpoint sin autorización, no lo expongas como tool. Si te interesa el lado feo de esto, escribí sobre [servidores MCP maliciosos](/post/servidores-mcp-maliciosos-ghostsplice) y sobre [OAuth para agentes](/post/aap-oauth-agentes-de-ia).

## Buenas prácticas, según Chrome

La documentación de Chrome tiene una guía de best practices y hay cuatro ideas que valen más que el resto:

**Nombra distinguiendo ejecutar de iniciar.** `create-event` para lo que crea el evento; `start-event-creation-process` si lo que haces es llevar al usuario a un formulario. El agente decide por el verbo.

**Describe en positivo.** En vez de "no uses esta tool para el clima", escribe "esta tool crea un evento de calendario para una fecha y hora concretas". Las limitaciones se deducen de una buena descripción.

**Una función por tool, y registro estático por defecto.** Nada de tools que se solapan. El registro dinámico existe, pero úsalo con moderación.

**Valida estricto en el código, laxo en el schema.** El schema no está garantizado; lo que sí funciona es devolver un error descriptivo que el modelo pueda leer y corregir. Y acepta la entrada cruda del usuario en lugar de pedirle al modelo que haga cálculos previos.

Sobre pruebas, la recomendación es eval-driven: defines un baseline, un resultado ideal y evalúas cualitativamente, en lugar de tests unitarios rígidos.

## Cómo probarlo hoy

Para desarrollo local basta el flag `chrome://flags/#enable-webmcp-testing`. Para servirlo a usuarios reales necesitas registrarte en el origin trial desde `developer.chrome.com/origintrials`, que corre hasta Chrome 156.

Para inspeccionar hay una extensión, el Model Context Tool Inspector, que lista las tools registradas, te deja ejecutarlas a mano, valida el JSON Schema y permite probar con lenguaje natural. Y en `GoogleChromeLabs/webmcp-tools` están las demos de referencia: un Pizza Maker y un viaje en React con la API imperativa, y Le Petit Bistro con la declarativa.

## Lo que todavía no está decidido

El explainer mantiene una lista abierta que conviene mirar antes de construir encima: soporte multimodal (entradas y salidas binarias), qué pasa con la respuesta de una tool si la página navega, qué ven por defecto los agentes integrados del navegador, streaming de entradas y salidas grandes, validación de schemas, agrupación de tools en "skills", reporte de progreso en tareas largas e integración con service workers para descubrir tools sin abrir el sitio.

Esa última es la que más cambia el juego: hoy la única forma de descubrir tus tools es que alguien visite tu página. Mientras eso siga así, WebMCP mejora la sesión de un agente que ya llegó, pero no te trae agentes nuevos. Para lo otro están las vías de descubrimiento que ya existen, como [content negotiation](/post/content-negotiation-agentes-ia) o [DNS](/post/dns-aid-descubrimiento-agentes-ia-dns).

## Preguntas frecuentes

### ¿WebMCP reemplaza a MCP?

No, y el explainer lo dice como no objetivo explícito. MCP conecta un modelo con servidores externos; WebMCP expone tools dentro de una página, con las reglas del navegador. Un producto puede tener los dos: un [servidor MCP](/post/como-crear-un-servidor-mcp) para integraciones de backend y tools WebMCP para la sesión del usuario en el navegador.

### ¿Funciona ya en producción?

Solo en Chrome y solo dentro del origin trial, que va de la versión 149 a la 156. Firefox y Safari no han dado señal. Regístralo detrás de una comprobación de capacidad y trata la UI humana como el camino principal.

### ¿Necesito escribir JavaScript para usar WebMCP?

No siempre. Si lo que quieres exponer es un formulario, la API declarativa te lo resuelve con `toolname`, `tooldescription` y `toolparamdescription` en el HTML. El JavaScript hace falta cuando la tool no es un envío de formulario o cuando quieres controlar el resultado que ve el modelo.

### ¿Cualquier página puede ver mis tools?

No. La policy `tools` tiene allowlist `['self']` por defecto, así que un iframe cross-origin necesita `allow="tools"`, y al registrar puedes acotar con `exposedTo`. Además el documento tiene que estar aislado por origen: con `document.domain` o `Origin-Agent-Cluster: ?0` la API queda deshabilitada.

### ¿WebMCP es un estándar del W3C?

Todavía no. Es un Draft Community Group Report del Web Machine Learning Community Group, que es la fase de incubación. Chrome lo tiene como "Proposed" y la review del TAG está pendiente.

### ¿Sirve para agentes headless o scraping?

No es su caso de uso. El explainer excluye explícitamente la navegación headless y los flujos totalmente autónomos: está diseñado para un navegador local con un humano supervisando.

## Fuentes

- [WebMCP, Draft Community Group Report](https://webmachinelearning.github.io/webmcp/) (W3C Web Machine Learning CG)
- [Explainer y repositorio de la propuesta](https://github.com/webmachinelearning/webmcp)
- [WebMCP en Chrome for Developers](https://developer.chrome.com/docs/ai/webmcp)
- [Join the WebMCP origin trial](https://developer.chrome.com/blog/ai-webmcp-origin-trial)
- [Ficha de la feature en Chrome Platform Status](https://chromestatus.com/feature/5117755740913664)

---

## Sitemap

Índice completo del sitio: [/sitemap.md](https://www.angelcruz.dev/sitemap.md)

Canónico HTML: [https://www.angelcruz.dev/post/webmcp-api-navegador-agentes-ia](https://www.angelcruz.dev/post/webmcp-api-navegador-agentes-ia)
