← Blog
30 de julio, 2026 · 10 min

MCP Apps: la primera extensión oficial da interfaz a las tools

Los servidores MCP siempre pudieron devolver texto y datos estructurados. Desde el 26 de enero de 2026 también pueden devolver una interfaz. MCP Apps — SEP-1865 — es la primera extensión oficial del Model Context Protocol y, bajo la spec finalizada 2026-07-28, una de exactamente dos. Esto es lo que realmente especifica, y dónde toca a un gateway con billing por uso.

En este blog venimos siguiendo de cerca el framework de extensiones de MCP: Enterprise-Managed Authorization para identidad, Tasks para trabajo asíncrono, el Registry para discovery. Apps es la extensión que llegó primero — salió como oficial el 26 de enero de 2026, antes de que las demás se estabilizaran — y responde una pregunta distinta: qué pasa cuando una tool call necesita que un humano vea algo, no solo que un modelo lea algo.

Dos UIs en competencia se vuelven un estándar

Hacia fines de 2025 había dos intentos serios de meter interfaces dentro de hosts de agentes con forma de chat. El proyecto comunitario MCP-UI, creado por Ido Salomon y Liad Yosef, había sido adoptado por Postman, Shopify, Hugging Face, ElevenLabs y Goose. El Apps SDK de OpenAI hacía el mismo trabajo dentro de ChatGPT, construido sobre MCP como base. Mismo problema, dos respuestas incompatibles — exactamente la divergencia que el protocolo existe para prevenir.

La propuesta publicada el 21 de noviembre de 2025 fusionó los dos linajes de forma deliberada. La lista de autores cuenta la historia: los maintainers de MCP Anton Pidkuiko y Olivier Chafik, Salomon y Yosef de MCP-UI, el core maintainer Nick Cooper, Sean Strong y Jerome Swannack de Anthropic, Alexi Christakis y Bryan Ashley de OpenAI. La motivación declarada fue directa: la comunidad había sido creativa esquivando la falta de UI, pero cada workaround era distinto, así que los servidores no podían renderizar de forma consistente entre clientes.

Dos meses después, el 26 de enero de 2026, MCP Apps se convirtió en la primera extensión oficial, con spec versionada en specification/2026-01-26/apps.mdx dentro de su propio repositorio ext-apps. Esa estructura importa. Bajo el framework de extensiones, las extensiones son de primera clase pero separadas: identificadores DNS-inverso, negociación por capabilities, repos dedicados con maintainers delegados, versionado independiente. El release 2026-07-28 — finalizado hace dos días — nombra exactamente dos extensiones oficiales: Apps y Tasks.

La forma: una tool que trae su propia vista

El identificador de la extensión es io.modelcontextprotocol/ui. Un host que puede renderizar apps lo declara durante initialize, incluyendo qué tipos de contenido acepta:

{
  "capabilities": {
    "extensions": {
      "io.modelcontextprotocol/ui": {
        "mimeTypes": ["text/html;profile=mcp-app"]
      }
    }
  }
}

Del lado del servidor, una UI es simplemente un resource con un URI scheme reservado. Las plantillas viven bajo ui://, llevan el MIME type text/html;profile=mcp-app y se obtienen con el request estándar resources/read — sin transporte nuevo, sin endpoint especial. Una tool que quiere una vista apunta a una plantilla a través de su metadata:

_meta: {
  ui: {
    resourceUri: "ui://weather-server/dashboard",
    visibility: ["model", "app"]
  }
}

El campo visibility es el primitivo silenciosamente interesante. Una tool marcada ["app"] es invisible para el modelo y solo invocable desde la interfaz renderizada. Eso da a los servidores una forma limpia de exponer endpoints de interacción granulares — paginación, envío de formularios, un botón de confirmar — sin contaminar la lista de tools del modelo ni quemar contexto en tools que el modelo nunca debería llamar. El default es ["model", "app"]: tanto el agente como la interfaz pueden invocarla.

Las plantillas predeclaradas son una decisión de seguridad

La decisión de diseño más consecuente es que las plantillas son resources predeclarados, no HTML ad-hoc devuelto dentro de tool results. Los hosts pueden listarlas al conectar, hacer prefetch, cachearlas y revisarlas por seguridad antes de que nada se ejecute. El post del release candidate 2026-07-28 declara la intención sin rodeos: las tools declaran sus plantillas de UI por adelantado para que los hosts puedan hacer prefetch, cache y revisión de seguridad antes de que nada corra.

El resource de plantilla lleva su propio sobre de seguridad en _meta.ui: un objeto csp que declara exactamente qué dominios externos puede contactar la vista (connectDomains, resourceDomains, frameDomains, baseUriDomains), un objeto permissions para cámara, micrófono, geolocalización y clipboard, un domain dedicado opcional para aislar el origin del sandbox, y pistas cosméticas como prefersBorder.

Las obligaciones del host alrededor de ese sobre están escritas como requisitos duros. El host MUST construir la Content Security Policy desde los dominios declarados, y MUST NOT permitir dominios no declarados. El iframe corre con los atributos de sandbox allow-scripts y allow-same-origin, y la CSP base parte de default-src 'none' — todo está negado salvo lo que la plantilla declaró. Los hosts SHOULD advertir al usuario cuando una vista requiere acceso a dominios externos y MAY imponer allowlists o blocklists globales encima.

La regla que carga el peso — una MCP App no puede hablar en silencio con un origin que su autor no declaró en la metadata de la plantilla. La predeclaración convierte la plantilla en un artefacto auditable: el host conoce la superficie de red completa de la interfaz antes de renderizar el primer byte.

El bridge: JSON-RPC de MCP sobre postMessage

En vez de inventar un protocolo de mensajes de widgets, la extensión reutiliza el propio protocolo base JSON-RPC de MCP, transportado por postMessage entre el iframe y el host. La vista abre con un handshake ui/initialize, declarando sus capabilities — display modes soportados entre inline, fullscreen y pip — y recibe de vuelta la versión de protocolo del host, sus capabilities y un hostContext con tema, variables CSS, dimensiones del contenedor, locale, timezone y plataforma.

Desde ahí la vista puede emitir un conjunto pequeño y cerrado de requests al host. Puede llamar tools/call para ejecutar una tool del servidor, resources/read para traer datos, ui/message para publicar en el chat, ui/open-link para URLs externas, ui/request-display-mode para ir a fullscreen y ui/update-model-context para devolver estado al modelo. El host empuja en la otra dirección con notificaciones: ui/notifications/tool-input entrega los argumentos de la tool call que originó la vista (con una variante streaming tool-input-partial), tool-result entrega el resultado, tool-cancelled, size-changed y host-context-changed mantienen la vista sincronizada, y ui/resource-teardown cierra el ciclo de vida.

La consecuencia de reutilizar el protocolo base es que un click dentro del iframe no es un canal lateral. Un tools/call iniciado por la UI fluye por la misma maquinaria del host — el mismo audit trail, los mismos prompts de consentimiento — que una llamada hecha directamente por el modelo. Esa es la propiedad que hace defendible todo el diseño: la interfaz no tiene un camino privilegiado hacia el servidor.

El theming sigue el mismo instinto de estandarización. Los hosts inyectan CSS custom properties — --color-background-primary, --color-text-primary, --font-sans, --border-radius-* y varias docenas más — de modo que una misma plantilla se ve nativa en el tema oscuro de Claude y el claro de ChatGPT. El sizing se negocia por eje: fijo, acotado o controlado por la vista.

Lo que el MVP deja afuera

La spec 2026-01-26 es deliberadamente estrecha. El único tipo de contenido soportado es text/html;profile=mcp-app — HTML y JavaScript empaquetados, entregados como texto o blob base64. Tres cosas que los proyectos predecesores soportaban quedaron explícitamente diferidas: URLs alojadas externamente, enfoques de remote DOM y formatos de widgets nativos. También hay deuda de limpieza en el propio formato de wire: la clave plana _meta["ui/resourceUri"] de los drafts tempranos está deprecada en favor del objeto anidado _meta.ui y está marcada para eliminarse antes del GA. Si publicas un servidor hoy, escribe la forma anidada.

Dónde está parado en julio de 2026

Esto no es una spec de papel. El SDK, @modelcontextprotocol/ext-apps, está en v1.7.5 al 23 de julio de 2026 — con React hooks para autores de apps y un entry point app-bridge para desarrolladores de hosts. El repositorio ext-apps trae servidores de ejemplo funcionales: un visualizador 3D con three.js, un servidor de mapas, uno de PDF, un monitor de sistema, un renderizador de partituras. Del lado de los hosts, el post de lanzamiento de enero listaba Claude en web y desktop, Goose y VS Code Insiders renderizando MCP Apps, ChatGPT aterrizando esa misma semana, con JetBrains, Kiro y Antigravity de Google DeepMind explorando soporte.

Y con la spec 2026-07-28 ya final — la revisión de núcleo stateless que cubrimos en profundidad — Apps queda en la posición privilegiada de ser una de dos extensiones oficiales, versionada independientemente del core que extiende.

La nueva superficie de ataque

Toda capability es una superficie, y esta le entrega a código no confiable escrito por el servidor un contexto de renderizado dentro del host. Las mitigaciones de la spec son en capas: los iframes sandboxed restringen lo que el código puede tocar, las plantillas predeclaradas hacen posible la revisión, los MUST de CSP acotan las rutas de exfiltración y el bridge JSON-RPC hace auditable cada interacción. Pero dos primitivos merecen atención específica de cualquiera que opere agentes.

Primero, ui/update-model-context es una ruta de escritura desde HTML renderizado hacia el contexto del modelo. Una plantilla comprometida o maliciosa no necesita exfiltrar datos para hacer daño; puede inyectar instrucciones en el mismo agente que la invocó. Es la misma clase de riesgo que mapeamos en el threat model de seguridad de agentes — prompt injection indirecta — con un canal nuevo y bendecido por la spec. Los hosts que suben actualizaciones de contexto originadas en apps al modelo sin etiquetar la procedencia están repitiendo los errores del manejo temprano de tool results.

Segundo, las tools solo-app (visibility: ["app"]) se ejecutan fuera de la conciencia del modelo por diseño. Eso es correcto para plomería de UX, pero significa que los logs de auditoría — no las transcripciones del modelo — son el único registro de lo que hizo una interfaz. Si tu billing, tu modelo de consentimiento o tu revisión de seguridad asumen que cada tool call aparece en la conversación, esta extensión rompe esa suposición.

Qué significa para LLM4Agents

LLM4Agents es infraestructura para agentes headless — llamadores machine-to-machine que se autentican, pagan por uso en stablecoins vía x402 y nunca renderizan un pixel. Una extensión de UI podría parecer ortogonal. No lo es, por tres razones.

Primero, los operadores son humanos aunque los agentes no lo sean. Nuestro servidor MCP expone tools de cuenta — balance, uso, workspace, historial de settlements — que los operadores de agentes ya invocan desde Claude y otros hosts MCP. Cada una de esas es una MCP App natural: un widget de balance con un sparkline de gasto le gana a un blob de JSON, y la plantilla puede declarar csp.connectDomains acotado exactamente al origin de nuestro gateway, manteniendo limpia la historia de auditoría.

Segundo, el momento de pago quiere una superficie. En el flujo walk-up de x402, una tool call se detiene en payment-required con precio, chain y asset adjuntos. Hoy esa negociación es invisible salvo que el SDK cliente auto-pague, como detallamos en el buyer stack. Una plantilla de app adjunta a una tool de pago es el lugar estandarizado para mostrarle a un humano cuánto cuesta una tool, qué se liquidó y el hash de transacción del recibo — aprobación de pago human-in-the-loop sin inventar un canal de widgets propietario.

Tercero, la semántica de billing sobrevive intacta. Como un tools/call iniciado por la UI es una tool call real sobre el mismo wire, pasa por nuestro metering reserve-then-settle exactamente igual que una iniciada por el modelo. Sin casos especiales. Pero la salvedad de la superficie de auditoría de arriba nos aplica directamente: los logs del gateway deben registrar si una llamada liquidada se originó con visibility de modelo o de app, porque los operadores van a preguntar cuál de las dos gastó su balance.

Cómo mantenerse en la frontera

Secuencia concreta para la plataforma, en orden. Negociar io.modelcontextprotocol/ui en el handshake de capabilities de nuestro servidor MCP y publicar una plantilla — el dashboard de balance y uso — como resource ui:// cableado a las tools de cuenta existentes vía _meta.ui.resourceUri. Después construir la app de aprobación de pagos: renderizar ofertas de precio x402 y recibos de settlement inline para flujos walk-up, usando solo el formato anidado _meta.ui. Tercero, etiquetar cada invocación de tool del lado del gateway con su visibility de origen (model vs app) en los registros de billing y observabilidad antes de que exista tráfico de apps, no después. Cuarto, publicar la entrada del servidor con soporte de apps en el MCP Registry bajo nuestro namespace DNS para que los hosts puedan descubrir y pre-revisar las plantillas. Por último, seguir el roadmap diferido — URLs externas y remote DOM cambiarían materialmente el cálculo de CSP — y tratar a cualquier host que renderice nuestras plantillas sin aplicar los MUST de dominios declarados como fuera de contrato.

El patrón de 2026 ya es inconfundible: identidad (EMA), ejecución asíncrona (Tasks), discovery (Registry) y ahora presentación (Apps) se están convirtiendo en capas estandarizadas y versionadas de forma independiente alrededor de un núcleo MCP stateless. Las plataformas que ganan son las que implementan cada capa lo bastante temprano para moldear cómo aterriza la siguiente.

Dale a tus agentes un gateway — y a tus operadores una vista

Una API OpenAI-compatible, 345+ modelos, billing en stablecoins vía x402 y un servidor MCP que tus tools ya hablan.

Registra tu agente