Conceptos

Qué son las MCP Apps

MCP Apps es la extensión oficial de MCP que permite que una tool responda con una interfaz interactiva en lugar de con un bloque de texto. Aquí tienes cómo funciona de principio a fin y qué necesitas para crear una.

11 min de lectura

¿Cómo va el lanzamiento de la beta? Enséñame el tablero antes del daily.

Ha usado show_kanban

Un widget de MCP Apps de verdad, pintado como lo pinta un host. Hecho con Widgetry.

Qué son las MCP Apps, en un párrafo

Las MCP Apps son interfaces interactivas que un servidor MCP acompaña a sus tools y que el host de IA (ChatGPT, Claude, VS Code, Microsoft 365 Copilot, Cursor, Goose y otros) pinta dentro de la conversación cuando el modelo llama a esas tools. El servidor registra un documento HTML como recurso con una dirección ui://, la tool apunta a ese recurso, y el host carga el documento en un iframe aislado y le pasa los datos de la tool por un pequeño protocolo JSON-RPC. Lo que ve el usuario es un tablero, una gráfica, un mapa o un formulario en el que puede hacer clic, justo donde habría ido la respuesta en texto. MCP Apps es la primera extensión oficial del Model Context Protocol: se propuso como SEP-1865 en noviembre de 2025 y se declaró estable el 26 de enero de 2026 (blog de MCP).

El tablero de arriba es una MCP App. En una conversación real, el modelo llamaría a una tool show_board con las columnas y las tarjetas como argumentos, y el host pintaría esta vista con esos datos.

Por qué las tools de MCP necesitaban una interfaz

Una tool de MCP normal devuelve texto, o JSON estructurado que el modelo convierte en texto. Para un dato suelto basta. Para algo que una persona quiere mirar o tocar, no: cuarenta filas de ventas, el estado de un proyecto, varios vuelos que comparar, una configuración con campos que dependen unos de otros.

En esos casos la conversación se vuelve un bucle lento. El modelo describe los datos, tú pides un cambio con palabras, el modelo vuelve a llamar a la tool y describe los datos nuevos. El blog de MCP cita los casos en los que más se nota: paneles para explorar datos, asistentes de configuración con campos dependientes, revisión de documentos con resaltados y paneles de monitorización que se actualizan solos (artículo de la propuesta).

MCP Apps lo resuelve sin salir de la conversación. La tool sigue devolviendo datos para el modelo, y además el host enseña una vista de esos datos que el usuario puede ordenar, filtrar, abrir y pulsar. Cuando lo que quiere hacer necesita lenguaje (una pregunta, un cambio de planes), la vista se lo devuelve al chat.

Cómo funcionan las MCP Apps

Una MCP App tiene dos mitades: lo que declara el servidor y lo que pasa dentro del iframe mientras se usa.

Lo que declara el servidor

El servidor registra un recurso de interfaz: un documento HTML con una URI ui:// y el tipo MIME text/html;profile=mcp-app. Después enlaza una tool con ese recurso mediante _meta.ui.resourceUri. Esto es lo que ve un host en tools/list:

json
{
  "name": "show_board",
  "description": "Shows a project board with columns and cards in the conversation.",
  "inputSchema": {
    "type": "object",
    "properties": {
      "title": { "type": "string" },
      "columns": { "type": "array" }
    }
  },
  "_meta": { "ui": { "resourceUri": "ui://widgets/board.html" } }
}

Y esto es lo que recibe al leer el recurso con resources/read:

json
{
  "contents": [
    {
      "uri": "ui://widgets/board.html",
      "mimeType": "text/html;profile=mcp-app",
      "text": "<!doctype html><html>...</html>",
      "_meta": { "ui": { "csp": { "resourceDomains": ["https://cdn.jsdelivr.net"] } } }
    }
  ]
}

Los hosts que soportan la extensión lo anuncian al conectarse, en capabilities.extensions["io.modelcontextprotocol/ui"], con text/html;profile=mcp-app entre los tipos MIME que saben pintar (especificación). La clave plana antigua, _meta["ui/resourceUri"], está obsoleta; el SDK oficial sigue escribiendo las dos para que los hosts antiguos no fallen.

Una tool también puede fijar _meta.ui.visibility. Por defecto vale ["model", "app"]. Con ["app"] el modelo no ve la tool, pero la vista sí puede llamarla: sirve para un botón de "actualizar" o "cargar más" que el modelo no debería usar por su cuenta.

Lo que pasa mientras se usa

Cuando el modelo llama a la tool, el host carga el HTML en un iframe aislado (sandbox). A partir de ahí, la vista y el host hablan JSON-RPC 2.0 por postMessage:

  1. Saludo. La vista envía ui/initialize. El host responde con su contexto de host y la vista confirma con ui/notifications/initialized. El host no puede mandar nada más antes de esa confirmación.
  2. Argumentos de la tool. El host envía ui/notifications/tool-input con los argumentos completos que ha escrito el modelo. Mientras el modelo aún los está generando, puede mandar antes ui/notifications/tool-input-partial, para que la vista enseñe un esqueleto de carga.
  3. Resultado. Cuando el servidor responde, el host envía ui/notifications/tool-result. La vista suele pintar a partir del structuredContent del resultado. Si la llamada se cancela, recibe ui/notifications/tool-cancelled.
  4. Cierre. Cuando la vista desaparece, el host envía ui/resource-teardown para que libere lo que tenga abierto.

El contexto de host le dice a la vista dónde está: theme (claro u oscuro), styles.variables (las variables CSS del host para colores, fuentes y radios), displayMode y availableDisplayModes (en línea, pantalla completa o imagen en imagen), containerDimensions, locale, timeZone, platform, safeAreaInsets y algunos campos más. Si algo cambia, por ejemplo el usuario pasa a modo oscuro, el host envía ui/notifications/host-context-changed. Así es como una MCP App bien hecha parece nativa en cada host: lee las variables del host en lugar de llevar una paleta fija. La guía de temas lo cuenta con detalle.

Acciones de vuelta al host

La vista no es una imagen pasiva. Puede pedirle cosas al host:

  • ui/message: publicar un mensaje en el chat como si lo hubiera escrito el usuario ("Cuéntame más de la tarjeta Vídeo de lanzamiento"). Así un clic se convierte en un turno nuevo para el modelo.
  • ui/open-link: pedir al host que abra una URL. El host decide si la abre y cómo.
  • tools/call: llamar a una tool del mismo servidor MCP con una petición MCP estándar. El resultado vuelve a la vista, no al chat.
  • ui/update-model-context: actualizar lo que el modelo sabe del estado de la vista, sin publicar un mensaje visible.
  • ui/request-display-mode: pedir pantalla completa o imagen en imagen.
  • ui/notifications/size-changed: avisar de que la altura del contenido ha cambiado, para que una tarjeta en línea crezca o encoja.

En la vista, el SDK oficial (@modelcontextprotocol/ext-apps) envuelve todo esto en una clase App. El núcleo de una vista son pocas líneas. Asigna los manejadores antes de connect(), o te puedes perder las primeras notificaciones:

ts
import { App } from '@modelcontextprotocol/ext-apps'

const app = new App({ name: 'Board', version: '1.0.0' })
app.ontoolresult = (result) => render(result.structuredContent)
await app.connect()

render es tu propia función. Métodos como app.callServerTool({ name, arguments }), app.sendMessage y app.openLink corresponden a las acciones de arriba.

¿Son seguras? El modelo de seguridad de MCP Apps

Ejecutar HTML de terceros dentro de un chat suena arriesgado, así que la extensión se diseñó por capas (artículo del lanzamiento):

  • Iframe aislado. La vista se ejecuta en un iframe con permisos recortados, separada de la página del host, de sus cookies y de su DOM. Los hosts web añaden además un proxy de sandbox (un marco intermedio en otro origen). La vista no puede leer la conversación; solo recibe lo que el host le manda.
  • Una CSP estricta por defecto. Si el recurso no declara nada, el host aplica una política que permite scripts y estilos en línea, imágenes data: y ninguna conexión de red (connect-src 'none'). Para cargar un script de una CDN, enseñar imágenes de tu almacenamiento o llamar a tu API, el recurso tiene que listar esos orígenes en _meta.ui.csp (resourceDomains, connectDomains, frameDomains, baseUriDomains). El host no puede permitir nada que no esté declarado. Es la causa más habitual de un widget en blanco; la guía de CSP explica cada campo y cómo depurarlo.
  • Declarada de antemano. La interfaz es un recurso que el servidor registra antes, no HTML generado dentro del resultado de una tool, así que un host puede descargarlo y revisarlo antes de pintarlo.
  • Mensajes auditables. Todo lo que se dicen la vista y el host es JSON-RPC, así que el host puede registrarlo, y puede pedir al usuario que apruebe las llamadas a tools que lance la vista.
  • Permisos explícitos. Cámara, micrófono, geolocalización y escritura en el portapapeles se piden en _meta.ui.permissions del recurso, y el host aun así puede negarlos.

Además, cada host pone cada app en su propio origen. El recurso puede proponer uno con _meta.ui.domain, cuyo formato depende del host: Claude, por ejemplo, usa un subdominio de claudemcpcontent.com y ChatGPT uno de oaiusercontent.com.

De dónde vienen las MCP Apps

MCP Apps no salió de la nada. Juntó dos líneas de trabajo.

MCP-UI. Un proyecto de la comunidad, creado por Ido Salomon y Liad Yosef, que demostró que una interfaz interactiva podía viajar por MCP como recurso. Lo adoptaron, entre otros, Postman, Shopify, Hugging Face, Goose y ElevenLabs (artículo de la propuesta).

El Apps SDK de OpenAI. OpenAI montó las apps de ChatGPT sobre MCP, con convenciones propias: un recurso text/html+skybridge, una clave openai/outputTemplate en la tool y un objeto window.openai dentro del iframe.

Las dos cosas funcionaban, pero de forma distinta, y cada servidor tenía que elegir. Así que Anthropic, OpenAI y los responsables de MCP-UI escribieron juntos el SEP-1865. Se propuso el 21 de noviembre de 2025, con text/html+mcp como tipo MIME, y se declaró estable el 26 de enero de 2026 como primera extensión oficial de MCP, ya con el tipo definitivo text/html;profile=mcp-app. Desde entonces:

  • ChatGPT se declaró "totalmente compatible con la especificación de MCP Apps" el 22 de febrero de 2026 (changelog de OpenAI). OpenAI recomienda ya los campos y el puente comunes para cualquier interfaz nueva, y window.openai solo para lo que el estándar no cubre.
  • MCP-UI se presenta como "estandarizado en MCP Apps", y su SDK emite ya el tipo MIME estándar (mcpui.dev).
  • @modelcontextprotocol/ext-apps 2.0 salió el 8 de septiembre de 2026 sobre los paquetes separados del SDK v2 de MCP, con el mismo protocolo que la 1.x.

La comparativa explica lo que todavía distingue a MCP Apps, MCP-UI y las extensiones de ChatGPT.

Qué hosts pintan MCP Apps

A octubre de 2026, la matriz de clientes, que mantiene la comunidad, incluye Claude (web y escritorio), ChatGPT, VS Code con GitHub Copilot, Microsoft 365 Copilot, Cursor, Goose, Postman, MCPJam, Archestra.AI y PostHog Code. Claude las pinta también en móvil, con algunos límites.

El soporte no es igual en todos. Cada host decide cuánto puede medir una tarjeta en línea, qué modos de visualización ofrece, qué campos de la CSP respeta (Claude restringe frameDomains, por ejemplo) y qué variables CSS envía. La lista completa, con fechas, fuentes y los límites de cada uno, está en Qué clientes soportan MCP Apps.

Qué pasa en los hosts que no soportan MCP Apps

No se rompe nada. La especificación dice que, si el host no soporta MCP Apps, la tool se comporta como una tool normal y se queda en texto. El host ignora _meta.ui, llama a la tool y le da el resultado al modelo.

Es lo que pasa con Claude Code y otros agentes de terminal: llaman a la tool y trabajan con lo que devuelve, pero no pintan nada. Por eso una buena tool de MCP App devuelve dos cosas:

  • structuredContent: los datos que pinta la vista.
  • content: un resumen corto en texto para el modelo y para los hosts que solo leen texto. "Tablero Lanzamiento de la beta: 2 tarjetas por hacer, 2 en curso, 1 hecha" sirve. "Mostrando widget" no.

La guía de clientes explica cómo escribir un buen texto de respaldo.

Cómo crear una MCP App

Hay tres caminos, de más código a menos.

Desde cero. Con el SDK oficial: registerAppResource y registerAppTool de @modelcontextprotocol/ext-apps/server en el servidor, y la clase App en la vista. El repositorio oficial trae plantillas de inicio para React, Vue, Svelte, Preact, Solid y JavaScript sin framework, y ejemplos de mapas, escenas 3D, PDF o mapas de calor (ext-apps en GitHub). La vista tiene que acabar siendo un único documento HTML, así que casi todos los proyectos la empaquetan (el quickstart oficial usa Vite con un plugin de fichero único). La guía Crear una MCP App en TypeScript lo recorre entero, con las diferencias entre ext-apps 1.x y 2.x.

Añadir una interfaz a un servidor que ya tienes. Si ya tienes un servidor MCP con tools que devuelven datos, te basta con el recurso, el enlace _meta.ui.resourceUri y structuredContent en el resultado. Cómo añadir una interfaz a tu servidor MCP enseña el camino más corto.

Partir de un widget hecho. Widgetry es un editor de widgets de MCP Apps que te ahorra el HTML y el protocolo. Eliges una de sus 13 plantillas (tablero, KPI, gráficas, una tabla, una cuenta atrás, un globo 3D y más), cambias los datos de ejemplo, el esquema de datos, el markup Liquid y los estilos, y eliges uno de sus 20 diseños o creas el tuyo. La vista previa del editor es un host MCP Apps de verdad, construido sobre el puente de host del SDK oficial, en claro y en oscuro. Después te lo llevas a tu servidor: o lo enlazas, y tu servidor lee el widget publicado en Widgetry con una clave de solo lectura y recibe cada cambio que publiques sin volver a desplegar, o lo descargas como documento HTML más un manifiesto y lo registras con unas líneas del SDK oficial de TypeScript. Si aún no tienes servidor, los widgets publicados también se sirven desde el endpoint MCP de Widgetry. Y si prefieres pedírselo a tu agente, puede crear los widgets por MCP.

Sea cual sea el camino, pruébala en más de un host: el mismo widget puede verse bien en uno y en blanco en otro por una CSP o un límite de tamaño.

Preguntas frecuentes sobre MCP Apps

¿Las MCP Apps son lo mismo que las apps de ChatGPT?

No exactamente. ChatGPT implementa el estándar abierto MCP Apps, así que una MCP App funciona en ChatGPT. Pero ChatGPT añade capacidades propias a través de window.openai (pago, subida de ficheros, modales, estado guardado del widget) que otros hosts no tienen. Desde julio de 2026 OpenAI distribuye las apps dentro de plugins; Interfaz de plugins de ChatGPT con MCP Apps explica el cambio y cómo migrar desde los campos antiguos del Apps SDK.

¿Necesito React para crear una MCP App?

No. El host solo recibe un documento HTML. Puedes escribirlo a mano, usar cualquier framework que empaquete en un único fichero o usar un editor. React es una elección habitual y el SDK trae hooks para él (useApp, useHostStyles), pero nada del protocolo depende de React.

¿Una MCP App puede pedir datos a mi API?

Sí, si el recurso declara el origen de la API en connectDomains. Lo más sencillo, de todos modos, es que la tool obtenga los datos en el servidor y se los pase a la vista en el resultado: el modelo ve los mismos datos, no abres nada más en la CSP y la vista funciona igual en todos los hosts. Si más tarde la vista necesita datos frescos, puede llamar a una tool del servidor con tools/call.

¿Claude Code pinta las MCP Apps?

No. A octubre de 2026, Claude Code llama a la tool y trabaja con su texto, sin pintar la interfaz (documentación de Claude). Claude en la web, en escritorio y en móvil sí la pinta. Por eso importa tanto el texto que devuelve la tool.

Tu primer widget, en el chat en unos minutos

Elige una plantilla, pon tus datos y míralo como lo enseñarán ChatGPT o Claude. Después enlázalo desde tu servidor MCP, o deja que tu agente haga el siguiente.