Tutoriales
Crear widgets de MCP Apps con un agente de IA
Casi todas las formas de crear una MCP App con Claude Code generan código en tu repo. Aquí tienes la otra: darle a tu agente tools para crear, diseñar y publicar widgets, con lo mismo que puedes hacer tú en la web.
8 min de lectura
¿Qué tal le fue a la tienda online en septiembre comparado con agosto?
Ha usado show_metrics
#La respuesta corta
Para crear widgets con un agente de IA, conéctalo a un servidor MCP cuyas tools crean y editan widgets. El endpoint de Widgetry, https://widgets.gonzaloverdugo.com/mcp, es uno de ellos: conectas Claude, Claude Code o cualquier cliente MCP con permiso de edición, y el agente puede listar plantillas, crear un widget a partir de una, cambiar su esquema de datos, sus datos de ejemplo, el markup y los estilos, hacer un diseño, publicar el widget y darte el código para enlazarlo desde tu propio servidor. Hace lo mismo que una persona en la web, con 15 tools widgetry_*.
No es lo mismo que pedirle a un agente que escriba una MCP App desde cero en tu código. Las dos cosas sirven; la última sección las compara.
#Por qué darle a un agente un servidor MCP para crear interfaces
Un widget de MCP Apps es una vista HTML que un host como ChatGPT o Claude pinta dentro de la conversación cuando se ejecuta una tool (lo tienes en qué son las MCP Apps). Escribirlo a mano supone empaquetar la vista, hablar el puente de postMessage, fijar una Content Security Policy y montar un servidor que registre el recurso y la tool.
Si el agente trabaja con tools, no toca nada de eso. Edita un widget que ya funciona: una plantilla Liquid, CSS que lee los tokens del diseño, un JSON Schema para los datos y unos datos de ejemplo que lo cumplen. El servidor comprueba cada cambio, y uno inválido vuelve con lo que hay que arreglar. El agente itera sobre lo que importa (qué enseña el widget y cómo se ve) y tú abres el resultado en el editor mientras trabaja.
#Cómo conectar tu agente
La URL del MCP es la misma para todos los clientes: https://widgets.gonzaloverdugo.com/mcp. Lo que cambia es cómo se identifica cada uno. En los dos casos la credencial apunta a una organización, y el agente solo ve y cambia los widgets y los diseños de esa organización.
#Claude y Claude Desktop: OAuth
Añade la URL como conector personalizado en claude.ai o en Claude Desktop. El cliente te lleva a Widgetry, entras con Google y eliges la organización en la pantalla de consentimiento. Esa pantalla también dice si el cliente pide solo leer o también escribir:
widgets:read: el agente ve la organización y puede enseñar sus widgets publicados.widgets:write: además puede crear, editar, publicar y borrar widgets y diseños, si tu papel en esa organización lo permite.
El mismo flujo vale para otros clientes que conectan servidores MCP remotos con OAuth.
#Claude Code, Codex y otros agentes: una clave de API
Los agentes de código y los scripts usan una clave de API que empieza por wgt_, enviada como token Bearer. La crea un propietario de la organización en Ajustes, con permiso de lectura o de lectura y escritura. Solo se enseña una vez, así que guárdala en cuanto aparezca.
En Claude Code, el comando es este:
claude mcp add --transport http widgetry https://widgets.gonzaloverdugo.com/mcp \
--header "Authorization: Bearer $WIDGETRY_API_KEY"Cualquier otro cliente que admita servidores MCP por HTTP con cabeceras propias funciona igual: la URL más Authorization: Bearer wgt_.... Una clave de lectura le da al agente las tools de lectura; una de lectura y escritura, todas.
#Las tools que recibe tu agente
Toda credencial recibe ocho tools de lectura. Solo una credencial que puede escribir recibe las otras siete. El servidor manda además al agente una guía corta en sus instrucciones (el contexto de Liquid, los tokens --wg-*, los scripts, las librerías y los dominios de imágenes), así que un modelo capaz puede empezar sin leer documentación.
#Lectura (cualquier credencial)
| Tool | Qué hace |
|---|---|
widgetry_get_organization | Dónde trabaja la credencial, si puede escribir, cuántos widgets y diseños hay, y las URLs |
widgetry_list_starters | Los tipos de widget de partida y las librerías de script disponibles (three, gsap, d3, chartjs) |
widgetry_get_starter | Una plantilla completa: markup, estilos, script, esquema de datos y datos de ejemplo |
widgetry_list_widgets | Los widgets de la organización, borradores incluidos |
widgetry_get_widget | Un widget completo, con sus dominios de imágenes y sus librerías |
widgetry_export_widget | El manifiesto (tool y recurso con su CSP), la URL viva para enlazar y el TypeScript para registrarlo |
widgetry_list_designs | Los diseños de serie y los de la organización |
widgetry_get_design | Los tokens claros y oscuros de un diseño, su CSS extra y todos los tokens que un diseño puede fijar |
#Escritura (clave de escritura, u OAuth con widgets:write de un editor o propietario)
| Tool | Qué hace |
|---|---|
widgetry_create_widget | Crea un borrador copiando una plantilla, con slug fijo, diseño e idioma de los datos de ejemplo opcionales |
widgetry_update_widget | Cambia solo los campos que mandas: nombre, descripción, diseño, markup, estilos, script, librerías, dominios de imágenes, esquema de datos, datos de ejemplo |
widgetry_set_published | Publica o despublica un widget |
widgetry_delete_widget | Borra un widget para siempre |
widgetry_create_design | Crea un diseño a partir de cualquier otro, con tokens, tokens oscuros y CSS |
widgetry_update_design | Edita un diseño de la organización |
widgetry_delete_design | Borra un diseño que no lleve ningún widget |
Cada widget que devuelven las tools trae un editorUrl y un previewUrl, que abres en el navegador con tu sesión. Y cada widget publicado se convierte en su propia tool en el mismo endpoint, show_<slug>, así que el agente lo puede pintar en la conversación en el momento.
#Un ejemplo completo
Una sesión con datos inventados. Le pides a Claude Code: "Hazme un widget con las cifras semanales del equipo de soporte, con un diseño verde azulado, y dame el código para mi servidor MCP".
1. Ver dónde está. El agente llama a widgetry_get_organization y ve canWrite: true.
2. Elegir plantilla. widgetry_list_starters devuelve los 13 tipos. Una fila de cifras con su variación encaja con la plantilla de indicadores, así que el agente la lee con widgetry_get_starter para conocer su esquema.
3. Crear el widget. Un slug fijo mantiene estable el nombre de la tool, y locale: "es" arranca con los datos de ejemplo en castellano:
{
"starterId": "metrics",
"name": "Soporte semanal",
"slug": "soporte-semanal",
"locale": "es"
}La respuesta trae "toolName": "show_soporte_semanal", el editorUrl y el previewUrl.
4. Dar forma a los datos. El agente llama a widgetry_update_widget con una descripción que leerá el modelo, un esquema que ahora exige el periodo y datos de ejemplo:
{
"slug": "soporte-semanal",
"description": "Muestra las cifras semanales del equipo de soporte: tickets, tiempo de respuesta y satisfacción, cada una con su variación frente a la semana anterior.",
"dataSchema": {
"type": "object",
"required": ["title", "period", "metrics"],
"properties": {
"title": { "type": "string" },
"period": { "type": "string" },
"metrics": {
"type": "array",
"items": {
"type": "object",
"required": ["label", "value"],
"properties": {
"label": { "type": "string" },
"value": { "type": "number" },
"unit": { "type": "string" },
"change": { "type": "number" },
"lower_is_better": { "type": "boolean" }
}
}
}
}
},
"sampleData": {
"title": "Soporte esta semana",
"period": "Semana 40",
"metrics": [
{ "label": "Tickets resueltos", "value": 1284, "change": 6.2 },
{ "label": "Primera respuesta", "value": 38, "unit": "min", "change": -12.5, "lower_is_better": true },
{ "label": "Satisfacción", "value": 94.1, "unit": "%", "change": 1.3 }
]
}
}Si los datos no cumplieran el esquema, la tool respondería con el error y lo que hay que arreglar, y el agente volvería a intentarlo.
5. Hacer un diseño. Ningún diseño de serie es verde azulado, así que el agente crea uno a partir de nordic:
{
"name": "Puerto",
"from": "nordic",
"tokens": { "--wg-accent": "#0f766e" },
"darkTokens": { "--wg-accent": "#2dd4bf" }
}El diseño recibe la clave puerto, y otra llamada a widgetry_update_widget le pone "design": "puerto" al widget.
6. Publicar. widgetry_set_published con "published": true. Para publicar hacen falta una descripción y unos datos de ejemplo que cumplan el esquema, y los dos ya están. El widget es ahora la tool show_soporte_semanal del endpoint.
7. Entregar el código. widgetry_export_widget devuelve el manifiesto, la URL viva y dos snippets de TypeScript: uno que enlaza el widget publicado con una clave de solo lectura (lo que publiques después llega a tu servidor en menos de un minuto, sin desplegar) y otro que registra los ficheros descargados. El agente pega el enlazado en tu servidor. Añadir una interfaz a tu servidor MCP explica los dos.
Un aviso si el agente es Claude Code: la terminal no pinta MCP Apps, llama a la tool como texto. Abre el previewUrl o el editor para ver el widget o, con Widgetry conectado en Claude en la web o en el escritorio, pídele que llame a show_soporte_semanal.
#Límites: lo que el agente no puede hacer
Dar permiso de escritura a un agente es más seguro cuando los límites están en el servidor y no en el prompt:
- Una credencial de lectura se queda en lectura. Una clave de lectura, o un token OAuth sin
widgets:write, solo recibe las tools de lectura. Un token sin ese scope actúa como lector aunque la persona sea propietaria. - Los papeles siguen contando. Un lector de la organización tampoco escribe a través de un agente. Para escribir hace falta un editor o un propietario detrás del token, o una clave de escritura.
- Ni miembros, ni invitaciones, ni claves. Se quedan en la web a propósito: una clave creada por un agente acabaría escrita en un chat.
- Una organización por credencial. Cada widget y cada diseño pertenecen a una organización, y la credencial decide cuál. Pedir algo de otra organización devuelve "not found".
- Las tools vivas están protegidas. Un widget publicado rechaza un cambio que rompería su tool; el agente tiene que despublicarlo antes o mantener el esquema compatible.
- Borrar está marcado como destructivo, y la tool le dice al agente que te lo confirme antes. No se puede deshacer.
#Frente a las skills que generan código
Hay buenas skills que enseñan a un agente de código a escribir MCP Apps. Resuelven otro problema.
| Opción | Qué hace | Mejor para |
|---|---|---|
Agent Skills oficiales de ext-apps (create-mcp-app, add-app-to-server, convert-web-app, migrate-oai-app) y el plugin mcp-apps de Claude Code | Generan y migran código de MCP Apps en tu repo (ext-apps) | Interfaces a medida con tu framework, control de cada línea, migrar una app de OpenAI |
La skill build-mcp-app de Anthropic, en el plugin mcp-server-dev | Añade interfaz (formularios, selectores, diálogos de confirmación) mientras construyes un servidor MCP (plugin) | Pasos interactivos dentro de un servidor que estás escribiendo |
La skill generate-mcp-app-ui de Microsoft Power Apps (en preview) | Genera widgets HTML autocontenidos con Fluent UI a partir del JSON de una tool (docs) | Equipos que ya están en Power Apps y quieren widgets con Fluent UI |
| Las tools MCP de Widgetry | Crean, diseñan y publican widgets como datos en un servicio alojado | Mostrar datos (tableros, indicadores, gráficos, tablas) sin código de interfaz en tu repo, diseños compartidos y cambios que llegan a tu servidor sin desplegar |
Generar código gana cuando la interfaz es poco común, cuando tiene que pedir sus propios datos o cuando no quieres depender de un servicio. Un widget de Widgetry es una plantilla Liquid con un script opcional limitado a un catálogo cerrado de librerías, y enseña los datos que le pasa el modelo: no pide datos por su cuenta. Si eso te encaja, el agente tiene un widget que funciona en cada paso en lugar de código que depurar. Para ver más opciones, lee herramientas para crear MCP Apps comparadas.
#Pruébalo
Entra en widgets.gonzaloverdugo.com, crea una clave de escritura en Ajustes (o conecta Claude con OAuth) y pídele un widget a tu agente. Verás cada cambio llegar al editor, podrás pulirlo tú y dejar que el agente lo publique cuando te guste.