Visual MCP
Visual MCP
Un servidor del Protocolo de Contexto de Modelo (MCP) que proporciona a un LLM una capa visual estructurada: describe lo que existe y recibe a cambio un diagrama SVG limpio y preciso — en lugar de arte ASCII.
¿Qué es Visual MCP?
Pídele a cualquier LLM que "dibuje la arquitectura" y obtienes esto:
+----------+ +---------+ +------------+
| React |----->| NestJS |----->| PostgreSQL |
+----------+ +---------+ +------------+
|
+-----> Redis?Los caracteres de dibujo de recuadros son un mal medio para la información espacial. La alineación se rompe, las flechas no llegan, nada se puede editar después, y el modelo consume razonamiento contando caracteres.
La solución obvia — "dejar que el modelo escriba SVG" — es peor. Entonces tiene que calcular viewBoxes, datos de trayectoria, polígonos de punta de flecha, líneas base de texto e intersecciones de bordes, todo a mano, todo sin retroalimentación, y todo de nuevo desde cero en cuanto el usuario pide un pequeño cambio.
Visual MCP elimina la geometría del trabajo del modelo. El modelo trabaja con un grafo de escena:
{
"title": "Service architecture",
"elements": [
{ "id": "frontend", "type": "node", "label": "React" },
{ "id": "backend", "type": "node", "label": "NestJS" },
{ "id": "db", "type": "database", "label": "PostgreSQL" },
{ "id": "cache", "type": "database", "label": "Redis" },
{ "id": "c1", "type": "connection", "from": "frontend", "to": "backend" },
{ "id": "c2", "type": "connection", "from": "backend", "to": "db" },
{ "id": "c3", "type": "connection", "from": "backend", "to": "cache" }
]
}Nota lo que no está: sin coordenadas, sin tamaños, sin puntos finales de línea, sin puntas de flecha, sin SVG. El servidor lo calcula todo — tamaños de los nodos a partir de las etiquetas, posiciones a partir del grafo de conexiones, bordes que encuentran los límites, marcadores de punta de flecha, ajuste de texto, y un viewBox que no puede recortar.
Y como la escena es un grafo con identificadores estables, el siguiente turno de la conversación es una edición de una línea en lugar de un redibujado:
"Pon Redis encima del backend y PostgreSQL debajo de él." →
update_element× 2, todo lo demás intacto.
"Ahora pon toda la infraestructura dentro de una caja llamada AWS." →
group_elements, y nada se mueve.
Related MCP server: Mermaid MCP Server
Arquitectura
ChatGPT
│ tool call: render_diagram / update_element / …
▼
MCP server src/mcp/ (transport, tools, error shaping)
│
▼
Scene graph src/scene/ (Zod schemas, validation, store, mutations)
│
├─▶ layout src/layout/ (sizes and positions for elements with no coordinates)
├─▶ semantic src/semantic/ (node/connection/axis/… ▸ primitives)
│
▼
SvgNode tree src/renderer/ (closed, allow-listed representation of an SVG document)
│
├─▶ toSvgString() ─────────────────▶ SVG returned by the MCP tools
└─▶ <SceneRenderer> ────────────────▶ React, for the interactive UICinco ideas lo sostienen
1. El grafo de escena es el artefacto, SVG es solo un formato de salida.
Todo lo que el modelo envía se valida en una Scene y se almacena. El renderizado es una función pura de
esa escena, por lo que el mismo diagrama se puede volver a renderizar más tarde con un tema diferente, o con un backend
diferente, sin que el modelo intervenga.
2. Los elementos semánticos se compilan en primitivas.
database se convierte en un trazado, una elipse y dos bloques de texto. connection se convierte en un trazado con un
marcador. El renderizador solo ve las diez primitivas — lo que lo mantiene pequeño, y significa que añadir
neuron, decisionTree o functionPlot más tarde es un archivo de expansión en src/semantic/, sin
cambiar la unión del esquema, el motor de diseño o el renderizador.
3. Un pipeline de geometría, dos backends.
La salida real del renderizador es un árbol SvgNode, no una cadena. serialize() lo convierte en marcado
para las herramientas MCP; <SceneRenderer> lo mapea a elementos React para la interfaz de usuario. No hay
una segunda implementación que pueda desviarse, y no hay dangerouslySetInnerHTML en ningún lugar del proyecto.
4. Los errores están escritos para un modelo, no para un archivo de registro.
{
"success": false,
"error": {
"code": "ELEMENT_NOT_FOUND",
"message": "Connection 'c1' points to 'router-2' (to), which does not exist in the scene.",
"path": "c1.to",
"hint": "Existing elements you can connect: pc, switch, router-1, server."
}
}Un código para bifurcar, una frase que nombra el problema, y una pista que contiene la respuesta. Nunca un rastreo de pila.
5. Cada mutación es atómica. Una edición rechazada deja la escena almacenada byte por byte como estaba. Sin eso, una mala llamada corrompería el diagrama para el resto de la conversación.
Desviaciones del diseño originalmente esbozado
src/layout/es su propio módulo, separado desrc/semantic/. El posicionamiento y la expansión de significado a forma son problemas diferentes, y la división es lo que permite intercambiar Dagre o ELK más tarde con un cambio en un solo archivo (src/layout/flow.ts) — su interfazFlowItemin / centres out es deliberadamente la forma que exponen esas bibliotecas.SvgNodese sitúa entre el renderizador y su salida (idea 3 anterior), que es lo que permite que la vista React exista sin un segundo renderizador y sin inyección insegura de HTML.src/mcp/widget.tses un visor vanilla sin dependencias, separado de la aplicación React ensrc/ui/. El recurso de iframe de ChatGPT debe ser una única cadena HTML autocontenida sin paso de compilación que podría estar desactualizada o faltante en tiempo de ejecución; la aplicación React es el playground local. Comparten el mismo comportamiento (zoom/pan/ajustar/copiar/exportar) y la misma lista de permitidos.El ajuste automático está activado por defecto, por lo que
width/heightactúan como sugerencias en lugar de un lienzo fijo. Esto elimina el modo de fallo más común: un modelo eligiendo un lienzo demasiado pequeño y recortando su propio diagrama.
Instalación
git clone <this repo>
cd visual-mcp
npm installRequiere Node 20+ (desarrollado en Node 22/26).
Desarrollo
npm run dev:http # MCP server over Streamable HTTP on http://localhost:3333/mcp
npm run dev:stdio # MCP server over stdio (Claude Desktop, MCP Inspector, tunnels)
npm run dev:ui # React playground on http://localhost:5180
npm test # 128 tests
npm run typecheck
npm run build # server → dist/
npm run build:ui # playground → dist-ui/
npm run examples # render the reference scenes → examples/out/index.htmlComprobación rápida extremo a extremo contra un servidor en ejecución:
npm run dev:http &
npx tsx scripts/smoke-mcp.tsReproduce toda la conversación objetivo: construir un diagrama sin coordenadas, inspeccionarlo, mover dos nodos, envolver todo en una caja — y verifica el resultado en cada paso.
Herramientas MCP disponibles
Herramienta | Qué hace | Cuándo el modelo debe usarla |
| Construye y renderiza una escena completa en una llamada, devuelve un | Cualquier solicitud de dibujar, visualizar, diagramar, ilustrar o explicar visualmente. El punto de entrada por defecto. |
| Vuelve a renderizar una escena almacenada. | Después de un lote de ediciones, para mostrar el resultado. |
| Devuelve la escena más la caja calculada de cada elemento. | Antes de editar — especialmente para cambios relativos ("un poco a la derecha"). |
| Añade un elemento, opcionalmente dentro de un grupo. | "Añadir un balanceador de carga", "dibujar una flecha de A a B". |
| Cambia solo los campos dados; | Cada solicitud de "cambia eso". Nunca redibujar para esto. |
| Elimina un elemento, eliminando en cascada sus conexiones y etiquetas. | "Eliminar la caché". |
| Envuelve elementos de nivel superior en una caja etiquetada, sin mover nada. | "Poner todo esto dentro de AWS", "agrupar estos en una VPC". |
| Crea un lienzo vacío. | Solo cuando se ensambla un diagrama grande de forma incremental. |
| Vacía una escena, manteniendo su lienzo/tema/título. | "Desecha eso, empecemos de nuevo". |
| Devuelve escenas de ejemplo funcionales y el catálogo de tipos. | Cuando no se está seguro de cómo expresar algo — copiar y adaptar. |
Cada descripción de herramienta indica qué hace, cuándo usarla, cuándo no, y qué significa cada propiedad,
porque otro modelo lee estas y decide por sí mismo. Las herramientas de solo lectura llevan
readOnlyHint: true y las destructivas destructiveHint: true, que los anfitriones usan para decidir qué
necesita confirmación.
Esquema de la escena
interface Scene {
id?: string;
title?: string;
subtitle?: string;
width?: number; // hint; autoFit grows the canvas so nothing is clipped
height?: number;
autoFit?: boolean; // default true
background?: string;
theme?: "dark" | "light" | "blueprint" | "paper";
themeOverrides?: Partial<Theme>;
layout?: "auto" | "layered" | "horizontal" | "vertical" | "grid" | "manual";
direction?: "right" | "down" | "left" | "up";
gap?: number;
padding?: number;
legend?: boolean;
elements: VisualElement[];
}Cada elemento tiene id y type. Los ids son estables y son como funciona la edición conversacional.
Primitivas — lo que el renderizador puede dibujar
circle · ellipse · rectangle · line · arrow · text · polygon · polyline · path ·
group
Elementos semánticos — lo que el modelo debería usar realmente
Tipo | Propósito |
| Caja etiquetada. Diez formas ( |
| Enlace por id: |
| Contenedor etiquetado con su propio diseño. Límites como "AWS", "VLAN 10". |
| Un sistema de coordenadas y un marco de datos. |
| Marcadores y líneas en coordenadas de datos cuando se les da |
| Leyenda que se puede adjuntar a otro elemento por id y lo sigue. |
| Preajustes de dominio — un |
Diseño
layout: "auto" (el valor por defecto) construye un flujo en capas a partir del grafo de conexiones cuando hay alguna,
de lo contrario una fila. Los elementos con x/y explícitos nunca se mueven, por lo que el modelo puede mover un nodo
sin perturbar el resto. direction controla en qué dirección crece el flujo.
Marcos de datos
Un axis declara un mapeo de unidades de datos a píxeles; cualquier cosa con frame: "<axis id>" se
coloca en coordenadas de datos, con y creciendo hacia arriba como debería:
{ "id": "plot", "type": "axis", "x": 90, "y": 70, "width": 620, "height": 420,
"xRange": [0, 10], "yRange": [0, 10], "xLabel": "Feature 1", "yLabel": "Feature 2" },
{ "id": "class-a", "type": "cluster", "frame": "plot", "x": 3.4, "y": 6.6,
"count": 40, "spread": 0.8, "label": "Class A", "hull": true, "seed": 7 }Temas
Cuatro temas integrados (dark, light, blueprint, paper), cada uno con una paleta completa más una pila de fuentes
que no requiere fuentes externas. Los elementos se refieren a tokens — primary, surface, muted,
danger — en lugar de colores fijos, por lo que todo un diagrama se reestiliza sin tocar su geometría. themeOverrides cambia cualquier token.
Ejecución local
Como biblioteca, sin MCP en absoluto
import { renderScene } from "visual-mcp";
const svg = renderScene({
title: "Request flow",
elements: [
{ id: "client", type: "computer", label: "Client" },
{ id: "api", type: "server", label: "API" },
{ id: "c", type: "connection", from: "client", to: "api", label: "HTTPS" },
],
});Como servidor HTTP
npm run dev:httpRuta | |
| Endpoint MCP HTTP Streamable |
| verificación de salud |
| escenas almacenadas |
| una escena renderizada |
| el visor interactivo, independiente |
El servidor es sin estado: cada solicitud obtiene su propio McpServer y transporte, y el almacén
de escenas es el único estado compartido. Eso es lo que lo hace seguro detrás de un balanceador de carga o en una
plataforma serverless, donde dos turnos de una conversación pueden no llegar al mismo proceso.
Como servidor stdio (MCP Inspector, Claude Desktop, Cursor)
npx @modelcontextprotocol/inspector npx tsx src/mcp/stdio.ts{
"mcpServers": {
"visual-mcp": {
"command": "node",
"args": ["/absolute/path/to/visual-mcp/dist/mcp/stdio.js"]
}
}
}Conexión a ChatGPT
ChatGPT se conecta a servidores MCP remotos a través de HTTP Streamable en un endpoint HTTPS público, por lo que el servidor debe ser accesible desde internet. Dos formas:
A. Prueba rápida con un túnel
npm run dev:http # http://localhost:3333/mcp
npx localtunnel --port 3333 # or: ngrok http 3333, or cloudflared tunnelB. Desplegar con Docker en un VPS
docker-compose.yml ejecuta dos contenedores: el servidor MCP en el puerto 4000 (no publicado en
internet) y Caddy, que termina TLS al frente y renueva el certificado
automáticamente.
Este VPS ya tiene otros servicios en los puertos 80 y 443, por lo tanto:
Puerto | Motivo | |
HTTPS / Endpoint MCP | 500 | 443 está ocupado |
Desafío ACME HTTP-01 | 90 | 80 está ocupado |
Servidor MCP | 4000 | solo interno, nunca publicado |
El problema: Let's Encrypt siempre se conecta al puerto 80 para HTTP-01 — eso está fijado por RFC 8555 y no se puede configurar — y TLS-ALPN también está fijado al 443. Caddy puede escuchar en el 90, pero algo debe llevar la solicitud hasta allí. Así que lo que ya posea el puerto 80 debe reenviar la ruta del desafío a Caddy.
1. Elige un nombre de host público. ChatGPT requiere HTTPS, y una IP desnuda no puede tener un
certificado. Sin un dominio propio, usa sslip.io — resuelve
<ip>.sslip.io a esa IP sin registro, y Let's Encrypt emite certificados para ello:
curl -4 ifconfig.me # on the VPS -> e.g. 203.0.113.45
# hostname becomes: 203.0.113.45.sslip.io2. Reenvía el desafío ACME desde el servidor del puerto 80. Identifícalo primero:
sudo ss -lptn 'sport = :80'nginx — dentro del bloque server { listen 80; }:
location /.well-known/acme-challenge/ {
proxy_pass http://127.0.0.1:90;
proxy_set_header Host $host;
}Apache — dentro del <VirtualHost *:80>:
ProxyPreserveHost On
ProxyPass /.well-known/acme-challenge/ http://127.0.0.1:90/.well-known/acme-challenge/
ProxyPassReverse /.well-known/acme-challenge/ http://127.0.0.1:90/.well-known/acme-challenge/Caddy — dentro del bloque del sitio que sirve el puerto 80:
handle /.well-known/acme-challenge/* {
reverse_proxy 127.0.0.1:90
}Usa un proxy real, no una redirección 301: Let's Encrypt sigue redirecciones solo a los puertos 80 y
443, por lo que una redirección a :90 fallaría.
3. Configura e inicia:
cp .env.example .env
# MCP_DOMAIN=203.0.113.45.sslip.io
docker compose up -d --buildSi el servidor del puerto 80 se ejecuta dentro de su propio contenedor en lugar de en el host,
127.0.0.1:90 no es accesible desde él — establece MCP_HTTP_BIND=0.0.0.0 en .env y apunta el
proxy a la IP interna del VPS (o coloca ambos contenedores en una misma red de Docker).
4. Verifica (la primera solicitud puede tardar unos segundos mientras se emite el certificado):
curl https://$MCP_DOMAIN:500/health # {"status":"ok",...}
docker compose logs caddy | grep -i "certificate obtained"La URL MCP es entonces https://<MCP_DOMAIN>:500/mcp, y es permanente: restart: unless-stopped sobrevive a reinicios, y los certificados viven en el volumen caddy_data, por lo que
las renovaciones persisten a través de docker compose down/up. Solo docker compose down -v los
elimina. Mantén el reenvío del desafío activo — las renovaciones cada ~60 días lo necesitan tanto como
la primera emisión.
PUBLIC_URL y ALLOWED_HOSTS se derivan de MCP_DOMAIN y MCP_HTTPS_PORT mediante
Compose. Ambos deben llevar el puerto: PUBLIC_URL porque los enlaces svgUrl apuntarían
al 443 de lo contrario, y ALLOWED_HOSTS porque el SDK compara el encabezado Host sin procesar
— que lee <domain>:500 en un puerto no estándar — como una cadena exacta.
Si no puedes tocar el servidor del puerto 80, HTTP-01 no está disponible en absoluto. Las opciones son un desafío DNS-01 (necesita un dominio real en un proveedor de DNS compatible — sslip.io no tiene API), o un Cloudflare Tunnel, que no necesita puertos de entrada.
Sin Compose
docker build -t visual-mcp .
docker run -d --name visual-mcp --restart unless-stopped -p 127.0.0.1:4000:4000 \
-e PUBLIC_URL=https://your-host -e ALLOWED_HOSTS=your-host visual-mcpLuego apunta cualquier proxy inverso a http://127.0.0.1:4000. El contenedor se ejecuta como el usuario
no root node, incluye un HEALTHCHECK /health y solo lleva dependencias de producción.
Las plataformas gestionadas (Fly.io, Railway, Render, Cloud Run) también funcionan — inyectan su propio
PORT, que el servidor respeta.
Luego, en ChatGPT
Activa el modo desarrollador. Está disponible en ChatGPT Business, Enterprise y Edu en la web. Un administrador lo activa en Configuración del espacio de trabajo → Permisos y roles → Datos conectados → Modo desarrollador / Crear conectores MCP personalizados.
Configuración → Conectores → Crear / Avanzado → Modo desarrollador → Añadir conector personalizado.
Rellena:
Nombre:
Visual MCPURL del servidor MCP:
https://<tu-host>/mcpAutenticación:
Sin autenticación(este servidor se envía sin autenticación — ver Seguridad)
Guarda. ChatGPT llama a
tools/listinmediatamente; deberías ver los diez herramientas listadas.En un nuevo chat, activa el conector y pide un diagrama.
El servidor también registra un recurso MCP Apps UI (ui://visual-mcp/scene.html,
text/html;profile=mcp-app), adjunto a las herramientas de renderizado a través de
_meta.ui.resourceUri y el alias de ChatGPT _meta["openai/outputTemplate"]. Donde sea
compatible, el diagrama aparece en un marco interactivo con zoom, desplazamiento, ajuste, copia y
exportación; en otros lugares, las herramientas aún devuelven el SVG en structuredContent, por lo que el
servidor degrada con elegancia.
Ejemplos
npm run examples renderiza los cinco en examples/out/index.html, y list_examples los sirve al
modelo.
1. Red — examples/out/network.svg
Preajustes de dominio y diseño automático de izquierda a derecha. computer → switch → router → server,
con subtítulos de VLAN en los enlaces. Sin coordenadas en ninguna parte de la escena fuente.
2. LDA — examples/out/lda.svg
Un marco de datos axis, dos clusters sembrados con contornos suaves, un límite de decisión
discontinuo y la dirección LDA — todo en coordenadas de datos, ambas líneas recortadas al gráfico.
3. Regresión — examples/out/regression.svg
Eje, una serie scatter y un plotLine ajustado con extend: true, en el tema claro.
4. Arquitectura de software — examples/out/architecture.svg
React → REST API → { Redis, PostgreSQL }, con los dos almacenes dentro de un group etiquetado
("Capa de datos") al que las conexiones se dirigen.
5. Árbol binario — examples/out/tree.svg
Siete nodos circulares y seis conexiones; el diseño en capas que fluye down produce el árbol.
Indicaciones para probar en ChatGPT
Draw the architecture where React talks to NestJS, NestJS uses PostgreSQL and also queries Redis.
Now put Redis above the backend and the database below it.
Now put all the infrastructure inside a box called AWS.
Make PostgreSQL bigger and give it a purple border.
Explain Linear Discriminant Analysis visually.
Show me graphically how linear regression works.
Draw a network where a PC in VLAN 20 reaches a server in VLAN 10 through a switch and a router.
Draw a balanced binary tree with 7 nodes.
Explain the TCP three-way handshake as a diagram.
Diagram merge sort on [5, 2, 9, 1].Seguridad
El modelo de amenazas es simple: todo lo que el servidor renderiza se originó en un modelo de lenguaje, y esa salida puede contener texto que el usuario pegó de otro lugar. Por lo tanto, nada derivado del modelo se trata nunca como código.
Esquema cerrado. Solo se analizan los veinticuatro tipos de elementos conocidos. Los colores deben coincidir con una gramática de hex/rgb/hsl/keyword/token —
url(javascript:…)se rechaza en la validación. Los datos de ruta deben coincidir con comandos y números de ruta SVG, nada más.Lista blanca de salida. El renderizador solo puede emitir etiquetas y atributos de dos listas explícitas en
src/renderer/svgNode.ts. No hayon*, no hayhref, no haystyle, no hayclass, no hay<foreignObject>, no hay<script>— el modelo no puede expresarlos, y el serializador los descartaría de todos modos.Escapado. El contenido de texto y los valores de atributos se escapan en XML a la salida.
Sin
dangerouslySetInnerHTML, sineval, sinnew Functionen ningún lugar del proyecto. La vista React construye elementos a partir del árbolSvgNode; el widget de ChatGPT analiza el SVG y lo reconstruye nodo por nodo contra las mismas listas blancas, por lo que incluso un servidor comprometido no puede introducir scripts en ese marco.Entrada acotada. Los recuentos de elementos, longitudes de cadenas, recuentos de puntos, longitud de ruta y escenas almacenadas están todos limitados; las escenas se eliminan las más antiguas primero.
Sin trazas de pila para el modelo. Cada manejador está envuelto; cualquier cosa inesperada se convierte en
INTERNAL_ERRORcon un mensaje corto.
Las pruebas en tests/security.test.tsx afirman cada uno de estos.
No incluido, por diseño: autenticación. El servidor no expone secretos ni toca ningún sistema
externo, pero una implementación pública es un almacén de escenas público. Ponlo detrás de la autenticación de tu plataforma, o añade
OAuth a través de los ayudantes de autenticación del SDK, antes de exponerlo a nadie más que a ti mismo. Establece ALLOWED_HOSTS para
activar la protección contra el rebote de DNS cuando sea accesible desde un navegador.
Limitaciones actuales
Las métricas de texto se estiman, no se miden — no hay un motor de fuentes en el servidor. Los anchos están dentro de unos pocos por ciento para las pilas sans-serif incluidas, lo que es suficiente para cuadros y ajuste de línea, pero una fuente inusual o mucho CJK se ajustarán ligeramente sueltos.
El motor de diseño es intencionalmente pequeño. Capas de ruta más larga con centrado de rango. No tiene minimización de cruces ni resolución de superposición, por lo que un gráfico denso (aproximadamente 25+ nodos con muchos enlaces cruzados) producirá cruces que un motor real evitaría. La interfaz tiene forma de Dagre precisamente por esta razón.
Los diseños de árbol no están centrados en los hijos; un padre se sienta en el centro de su rango, no sobre el punto medio de sus hijos.
El esquema JSON de
render_diagramtiene aproximadamente 50 KB (~12k tokens) porque enseña todo el vocabulario de elementos. Las otras nueve herramientas suman ~5 KB. Eso es una compensación deliberada: el modelo obtiene documentación por campo y rara vez necesita un viaje de ida y vuelta de reparación.Dentro de un grupo con diseño automático, las coordenadas
x/yexplícitas de los hijos se ignoran — el diseño gana. Usalayout: "manual"en el grupo para posicionar a los hijos tú mismo.El almacenamiento está en memoria. Las escenas no sobreviven a un reinicio, y con múltiples réplicas una escena vive en la instancia que la creó. La interfaz
SceneStoreexiste para que esta sea una clase a reemplazar.Salida estática. Sin animación, sin 3D, sin analizador de expresiones matemáticas todavía — ver más abajo.
Hoja de ruta
Próximo
SceneStorepersistente (SQLite primero) — la interfaz ya está en su lugar.Deshacer/rehacer. Cada mutación ya se registra como una
SceneMutation; esto es cuestión de almacenar la inversa.Dagre o ELK detrás de
src/layout/flow.tspara gráficos densos, con el motor pequeño como predeterminado.Diseño de árbol centrado en los hijos.
Matemáticas — el marco de datos axis es la base; cada uno de estos es un expansor en
src/semantic/, sin cambio en el renderizador:
functionPlot ({ "expression": "x^2", "domain": [-5, 5] }), vector, matrix, plane,
distribution, projection, decisionBoundary y regressionLine como alias nombrados de
plotLine.
Vocabulario de diagramas — neuron, neuralNetwork, decisionTree, sequenceDiagram, stateMachine,
gantt, swimlane.
Animación — { "animation": { "type": "flow", "duration": 1200 } } en una conexión, emitido como
SVG SMIL o CSS para que siga siendo declarativo y no necesite tiempo de ejecución. Paquetes moviéndose a lo largo de un enlace, una solicitud
viajando a través de una tubería, un algoritmo avanzando a través de una estructura.
Otros renderizadores — la tubería de resolución ya termina en un árbol agnóstico del backend. Una Scene con
kind: "3d" y { "type": "sphere", "position": [0, 1, 0] } seleccionaría un backend Three.js
en lugar del SVG; un backend Canvas serviría gráficos de dispersión muy grandes. Sin cambio en la capa
MCP.
Licencia
MIT
Available Tools
10 toolsadd_elementAdd an elementA
Add one element to a scene that already exists.
USE THIS when the user wants something new in a diagram you already drew: "add a load balancer", "put a Redis cache next to the API", "draw an arrow from A to B".
Call get_scene first if you are not certain which ids exist. To link the new element to an existing one, make a second call with an element of type 'connection' referencing the two ids - the geometry is computed for you.
Omit x/y and the layout engine places it. Set parentId to put it inside a group.
This does not display anything. Call render_scene once your edits are done.
| Name | Required | Description | Default |
|---|---|---|---|
| element | Yes | The element to add: an object with a unique `id`, a `type`, and the properties of that type - exactly the same shape as the entries of `elements` in render_diagram. Types: node, connection, group, axis, point, scatter, cluster, plotLine, label, server, database, router, switch, computer, cloud, circle, ellipse, rectangle, line, arrow, text, polygon, polyline, path. Example: { "id": "cache", "type": "database", "label": "Redis" }. | |
| sceneId | Yes | Id of the scene to work on, as returned by render_diagram or create_scene. | |
| parentId | No | Id of a 'group' element to nest this inside. |
Output Schema
| Name | Required | Description |
|---|---|---|
| error | No | Present only when success is false. |
| sceneId | No | |
| success | Yes | |
| elementId | No | |
| elementCount | No | Top-level elements in the scene after the add. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Beyond the annotations (which are all false and give no safety hints), the description richly discloses behavior: 'Omit x/y and the layout engine places it', 'the geometry is computed for you', and 'This does not display anything. Call render_scene once your edits are done.' It also explains that parentId nests the element inside a group. These are meaningful behavioral traits not present in annotations or schema descriptions.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is well-structured and front-loaded: a one-sentence summary, a bold 'USE THIS' callout, then prerequisites, special cases, and rendering note. Every sentence adds unique value—no fluff or repetition of schema content. It is compact despite covering multiple scenarios (first-time adds, connections, layout, grouping, rendering).
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
The description is remarkably complete for a tool with nested objects and 3 parameters. It covers prerequisites (get_scene), the exact workflow for linking elements (connection type), layout behavior (omit x/y), grouping (parentId), and the follow-up step (render_scene). Since an output schema exists, the lack of return-value explanation is acceptable. The tool feels self-contained for an agent to invoke successfully.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so parameters are well-documented in the schema. The tool description nevertheless adds significant value: it explains that the element object is 'exactly the same shape as the entries of elements in render_diagram', provides a concrete example, and clarifies behavior of x/y (omit for auto-layout). This goes beyond the schema, though the core parameter meanings are already covered, so a 4 is appropriate rather than 5.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a clear, specific statement: 'Add one element to a scene that already exists.' This uses a strong verb ('Add'), defines the resource ('element'), and the target ('scene'). It distinguishes itself from sibling tools like update_element, remove_element, and group_elements by focusing solely on adding new elements. Concrete usage examples ('add a load balancer', 'draw an arrow from A to B') further clarify the purpose.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicit 'USE THIS' guidance tells the agent exactly when to apply the tool: 'when the user wants something new in a diagram you already drew.' It also provides alternative and prerequisite actions: 'Call get_scene first if you are not certain which ids exist' and 'Call render_scene once your edits are done.' The connection workflow is explained with 'make a second call with an element of type 'connection'', giving clear usage boundaries.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
clear_sceneEmpty a sceneADestructiveIdempotent
Remove every element from a scene while keeping its id, canvas, theme and title.
USE THIS when the user wants to restart the drawing but keep talking about the same diagram: "scrap that, let's do it differently".
DO NOT use it for corrections - update_element and remove_element exist for that, and they preserve everything the user already approved.
| Name | Required | Description | Default |
|---|---|---|---|
| sceneId | Yes | Id of the scene to work on, as returned by render_diagram or create_scene. |
Output Schema
| Name | Required | Description |
|---|---|---|
| error | No | Present only when success is false. |
| sceneId | No | |
| success | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The description adds significant behavioral context beyond what annotations provide. Annotations already indicate destructiveHint=true, so the destructive nature is known. But the description clarifies exactly what is destroyed (every element) and what is preserved (id, canvas, theme, title). It also notes that this is idempotent (idempotentHint=true is already in annotations). The only missing detail is whether undo is possible or what the output schema returns, but the output schema exists to specify that.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is three sentences with zero wasted words. The first sentence defines purpose and scope concisely. The second and third sentences provide usage guidelines and sibling differentiation in a clear, front-loaded manner. The 'USE THIS when' and 'DO NOT use it' structure is exceptionally clear.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the simple tool with only one parameter, clear annotations, and an output schema, the description is complete. It covers purpose, scope, preservation details, usage context, and exclusions. There is no missing critical information for an AI agent to correctly select and invoke this tool.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100% for the single parameter, and the schema already documents that sceneId must match a specific pattern and length, and gives a helpful description. The description adds context by referring to 'as returned by render_diagram or create_scene', which helps in understanding how to obtain the correct ID. This surpasses the baseline of 3.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the tool removes every element from a scene while preserving its id, canvas, theme, and title. It specifies the verb 'Remove every element' and the resource 'scene', and distinguishes itself from siblings like update_element and remove_element by describing its unique behavior of clearing all elements.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description explicitly tells when to use the tool ('when the user wants to restart the drawing but keep talking about the same diagram') and provides a concrete user utterance ('scrap that, let's do it differently'). It also explicitly tells when NOT to use it ('DO NOT use it for corrections') and names the appropriate alternatives (update_element and remove_element).
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
create_sceneCreate an empty sceneA
Create a new, empty scene and get back its id.
USE THIS when you want to build a diagram incrementally - create the canvas, then add elements one at a time with add_element, then call render_scene when it is complete.
DO NOT use this when you already know the whole picture: render_diagram does the same job in one round trip and is almost always the better choice. Incremental building is only worth it for large diagrams you are assembling as the conversation goes.
Nothing is shown to the user until you call render_scene.
| Name | Required | Description | Default |
|---|---|---|---|
| id | No | Stable, meaningful id (e.g. 'backend'). Reuse it later to update or remove this element. | |
| gap | No | Spacing used by the automatic layout. Default 90. | |
| theme | No | Visual theme. 'dark' (default) is a modern technical look, 'light' is for documents, 'blueprint' is a blue schematic, 'paper' is warm and printable. | |
| title | No | Diagram title, drawn at the top. Keep it short - it is a caption, not a sentence. | |
| width | No | Canvas width in pixels. Default 960. Use 1200+ for wide flows. | |
| height | No | Canvas height in pixels. Default 600. | |
| layout | No | How elements without explicit x/y are placed. 'auto' (default) builds a layered flow from the connections when there are any, otherwise a row. 'layered' forces the flow layout, 'horizontal'/'vertical'/'grid' force a simple arrangement, 'manual' means you provide every x/y yourself. | |
| legend | No | Show a legend built from the `label` of scatter/cluster series. Default true. | |
| autoFit | No | Grow the canvas so nothing is clipped. Default true - leave it on and stop worrying about exact sizes. | |
| padding | No | Margin around the drawing. Default 48. | |
| subtitle | No | Optional second line under the title. | |
| direction | No | Direction the layered flow grows in. Default 'right'. | |
| background | No | Canvas background. Defaults to the theme background. | |
| themeOverrides | No | Optional palette overrides. Only set what you actually want to change. |
Output Schema
| Name | Required | Description |
|---|---|---|
| message | Yes | |
| sceneId | Yes | |
| success | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=false and destructiveHint=false. The description adds that the scene is empty and that user sees nothing until render_scene is called. This context about the multi-step workflow goes beyond the annotations without contradicting them.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is extremely concise: three short paragraphs. The first sentence states purpose, the second gives usage direction, the third provides an important behavioral note. Every sentence earns its place with zero padding.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the tool has 14 parameters (fully documented in the schema) and an output schema, the description does not need to cover those. It provides the essential workflow context—that this is part of a multi-step process culminating in render_scene—and explains the trade-off with render_diagram, which is sufficient for an agent to decide when to invoke.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%—every parameter has a detailed description in the schema. The tool description does not add new parameter-level information but also does not repeat what the schema provides, so baseline 3 is appropriate.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with 'Create a new, empty scene and get back its id,' which is a specific verb+resource statement. It clearly distinguishes from siblings like 'render_diagram' (alternative for known pictures) and 'add_element' (incremental step).
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicit 'USE THIS' section describes when to use (incremental building) and 'DO NOT use this' section points to the sibling 'render_diagram' as the better choice for one-shot creation. Also clarifies that nothing is shown until 'render_scene' is called.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_sceneInspect a sceneARead-onlyIdempotent
Return the current structured description of a scene: every element with its id, type and properties, plus the position and size each one actually ended up with.
USE THIS BEFORE EDITING whenever you are not sure of the current state - which ids exist, what a node is called, where it sits, what is already connected. Reading first is what makes small edits possible instead of redrawing the diagram from scratch.
The layout field gives the computed box (x, y, width, height) of every element,
including ones you never gave coordinates to. Those are the numbers to use when the user
asks for something relative: "a bit to the right", "above the backend", "same width as X".
This does not display anything.
| Name | Required | Description | Default |
|---|---|---|---|
| sceneId | Yes | Id of the scene to work on, as returned by render_diagram or create_scene. | |
| elementId | No | Return just this one element instead of the whole scene. | |
| includeLayout | No | Include the computed box of every element. Default true. |
Output Schema
| Name | Required | Description |
|---|---|---|
| ids | No | Every element id in the scene. |
| error | No | Present only when success is false. |
| scene | No | The full scene, ready to be edited or re-sent. |
| layout | No | Map of element id to { x, y, width, height }. |
| element | No | |
| history | No | The last few mutations applied to this scene. |
| sceneId | No | |
| success | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true, idempotentHint=true, and destructiveHint=false. The description adds valuable behavioral context: the layout field includes computed boxes for elements never given coordinates, and it warns that the tool does not display anything. This goes beyond the safety profile provided by annotations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is compact and front-loaded: it states the main function in the first sentence, then provides targeted usage guidance and a caveat. Every sentence earns its place, and the bolded 'USE THIS BEFORE EDITING' plus the final 'This does not display anything' are high-signal phrases with no filler.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With full annotations, a complete input schema (100% coverage), and an output schema, the description supplies all necessary operational context: when to use, what to expect, and a key caveat about non-display. No critical gaps remain for a read-only inspection tool.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, with clear descriptions for all three parameters, giving a baseline of 3. The description adds extra meaning by explaining the layout field operationally—'the computed box (x, y, width, height) of every element, including ones you never gave coordinates to'—which clarifies how to use parameters for relative positioning.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description uses a specific verb and resource: 'Return the current structured description of a scene' and enumerates the exact contents (id, type, properties, position, size). It also clearly distinguishes itself from rendering siblings with the explicit note 'This does not display anything.'
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description explicitly states when to use it: 'USE THIS BEFORE EDITING whenever you are not sure of the current state.' It also implies exclusions (not for display) but does not explicitly name alternative tools like render_diagram, so it stops short of a full 5.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
group_elementsGroup elements into a labelled boxA
Wrap existing top-level elements in a labelled container.
USE THIS for "put all of this inside a box called AWS", "group these services into a VPC", "draw a boundary around the data layer", "show which parts are in VLAN 10".
The members keep their ids, and every connection to or from them keeps working - including connections that cross the boundary.
Set layout to re-arrange the members inside the box ('vertical' stacks them, 'horizontal'
puts them in a row, 'grid' wraps them). Leave it out to keep their current arrangement.
Only top-level elements can be grouped. To nest a group inside another group, create the inner one first, then group it together with its siblings.
| Name | Required | Description | Default |
|---|---|---|---|
| fill | No | Background colour. | |
| label | No | Text drawn on the box, e.g. 'AWS'. | |
| layout | No | Re-arrange the members. Default 'manual' (keep their current positions). | |
| stroke | No | Border colour. | |
| groupId | Yes | Id for the new group, e.g. 'aws' or 'vlan-10'. | |
| sceneId | Yes | Id of the scene to work on, as returned by render_diagram or create_scene. | |
| elementIds | Yes | Ids of the top-level elements to move inside the box. |
Output Schema
| Name | Required | Description |
|---|---|---|
| error | No | Present only when success is false. |
| groupId | No | |
| members | No | |
| sceneId | No | |
| success | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The description goes well beyond the annotations (which only mark non-read-only/non-destructive) by detailing behavioral traits: 'The members keep their ids, and every connection to or from them keeps working - including connections that cross the boundary.' It also explains how the layout parameter affects arrangement and the default behavior ('manual' keeps current positions). This provides rich behavioral context the agent needs for safe invocation.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is concise yet comprehensive, with only 9 sentences. It front-loads the core purpose, then provides usage examples, behavioral guarantees, parameter guidance, and a crucial limitation for nesting. Every sentence adds unique value without redundancy.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the tool's complexity (7 parameters, 3 required, 100% schema coverage, output schema present), the description is complete. It explains the grouping behavior, parameter effects, constraints ('Only top-level elements can be grouped'), and nesting strategy. There are no gaps in understanding what the tool does or how to use it correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
With 100% schema description coverage, the schema already documents all parameters thoroughly. The description adds value by explaining the layout parameter options in natural language ('vertical stacks them, horizontal puts them in a row, grid wraps them') and clarifying the scope of elementIds ('top-level elements'). However, it doesn't add meaning to all 7 parameters (e.g., fill, stroke, sceneId are not elaborated beyond schema).
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a specific verb+resource ('Wrap existing top-level elements in a labelled container'), immediately clarifying what the tool does. It is easily distinguishable from siblings like 'add_element' (which adds individual elements, not grouping) and 'render_diagram' (which visualizes the whole scene). The examples concretely illustrate its scope and differentiate it from other tools.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description provides explicit usage examples ('put all of this inside a box called AWS'), including when to use it ('group these services into a VPC') and a clear limitation ('Only top-level elements can be grouped') with guidance on how to handle nesting ('create the inner one first, then group it together with its siblings'). This effectively tells the agent when and how to use this tool over alternatives.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_examplesShow example scenesARead-onlyIdempotent
Return complete, working example scenes for common kinds of diagram.
USE THIS when you are unsure how to express something with this server: which element type fits, how data frames work, how to nest a group. Copy the closest example and adapt it - that is faster and more reliable than guessing at the schema.
Call it with no arguments for the catalogue, or with name for one full scene you can
pass straight to render_diagram.
Available: network, lda, regression, architecture, tree.
| Name | Required | Description | Default |
|---|---|---|---|
| name | No | Return the full scene for this example. Omit for the catalogue. |
Output Schema
| Name | Required | Description |
|---|---|---|
| scene | No | |
| success | Yes | |
| examples | No | |
| elementTypes | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true, idempotentHint=true, and destructiveHint=false, so the safety profile is clear. The description adds the behavioral insight that calling with 'name' returns a full renderable scene, which goes beyond the schema. However, it does not detail what the output looks like or that it should be passed to render_diagram, though the token 'render_diagram' hints at integration. With annotations covering safety, a score of 3 is appropriate.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is concise with no wasted words. It front-loads the core purpose, immediately follows with usage guidance, and ends with a clear list of available examples. Every sentence contributes meaning.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the tool's low complexity (1 parameter, no required ones, enums documented, output schema exists), the description is largely complete. It explains the tool's role, how to invoke it, and the available options. A minor gap is not explicitly stating that the output scene is meant for render_diagram, but the sibling context and the phrase 'pass straight to render_diagram' cover this adequately.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the schema already documents the single parameter 'name' with enum values. The description adds value by explaining the two usage modes (no args vs with name) and lists the available examples in the enum. However, it repeats what the enum provides, offering only incremental context. Baseline 3 is correct.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly defines the tool's purpose with a specific verb-resource pair: 'Return complete, working example scenes.' It distinguishes the tool from siblings by stating these are examples to help when unsure about schema usage, unlike render_diagram or get_scene which serve different functions.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description explicitly states when to use this tool ('when you are unsure how to express something'), advises against alternatives ('guessing at the schema'), and provides a behavioral tip ('Copy the closest example and adapt it - that is faster and more reliable'). It also clarifies the two calling modes: no arguments for catalogue, or with name for a full scene.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
remove_elementRemove an elementADestructiveIdempotent
Delete one element from a scene.
USE THIS for "remove the cache", "delete that arrow", "drop the second database".
Connections pointing at the element, and labels attached to it, are deleted with it by
default - otherwise the scene would keep dangling references. Set cascade to false only
if you plan to repair those references yourself in the same turn.
Removing a 'group' also removes everything inside it. To keep the children, update the
group instead and set frame to false.
| Name | Required | Description | Default |
|---|---|---|---|
| cascade | No | Also remove connections and labels attached to it. Default true. | |
| sceneId | Yes | Id of the scene to work on, as returned by render_diagram or create_scene. | |
| elementId | Yes | Id of the element to remove. |
Output Schema
| Name | Required | Description |
|---|---|---|
| error | No | Present only when success is false. |
| removed | No | Every id that was deleted, including cascaded ones. |
| sceneId | No | |
| success | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Beyond annotations (destructiveHint=true), the description details the cascade behavior, deletion of connections and labels, and implications for groups. It warns about dangling references and when to set cascade=false, providing rich behavioral insight.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is well-structured: opening purpose statement, usage examples, behavioral detail, and a specific note on groups. Every sentence adds value, no fluff, and it's appropriately compact for the complexity.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the tool's destructive nature and edge cases (dependencies, groups), the description covers all necessary aspects. It explains when to use, side effects, parameter nuances, and alternative actions. With an output schema present, no return-value explanation is needed.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema covers all parameters at 100%, so baseline is 3. The description adds value by explaining cascade semantics in depth (when to use false) and the group removal behavior, which goes beyond the schema's simple descriptions.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states 'Delete one element from a scene' with a specific verb and resource. It distinguishes from siblings by giving usage examples like 'remove the cache' and contrasts with add_element/update_element. The purpose is unambiguous and well-scoped.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly tells the agent when to use it with 'USE THIS for...' and provides examples. It also gives guidance on when not to use cascade and advises updating the group instead to keep children. This is excellent context for selecting the tool over alternatives.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
render_diagramRender a diagramA
Create and render a structured visual diagram as SVG, in a single call.
USE THIS whenever the user asks to draw, sketch, visualise, diagram, illustrate, map out, show graphically, explain visually, or represent something spatially: architectures, network topologies, flows, pipelines, data structures, algorithms, state machines, relationships, plots, distributions, classifiers, or any concept where position and connection carry meaning.
ALWAYS PREFER THIS OVER ASCII ART, box-drawing characters, Markdown tables used as layout, or hand-written SVG/Mermaid. Those are unreliable and hard to read; this tool produces a precise, styled picture and the user sees it directly.
HOW TO USE IT WELL:
Describe WHAT exists, not WHERE it goes. Give elements ids and labels and omit x/y: the layout engine positions them from the connections. Only set x/y when the user asks for a specific arrangement, or for plots built on an
axis.Link things with
{ type: 'connection', from: '<id>', to: '<id>' }. Never compute x1/y1/x2/y2 for a link between elements, and never draw arrowheads by hand.Use semantic types (
node,database,server,router,switch,computer,cloud,group,axis,cluster,scatter,plotLine,label) before reaching for raw primitives (circle,rectangle,line,arrow,text,path, ...).Canvas size is optional: the drawing is auto-fitted so nothing is ever clipped.
DO NOT use this tool for: plain prose answers, code, tables of numbers, or when the user explicitly asked for text only.
Returns a sceneId. Keep it: later edits go through update_element / add_element /
remove_element on that id instead of rebuilding the whole scene.
| Name | Required | Description | Default |
|---|---|---|---|
| gap | No | Spacing used by the automatic layout. Default 90. | |
| theme | No | Visual theme. 'dark' (default) is a modern technical look, 'light' is for documents, 'blueprint' is a blue schematic, 'paper' is warm and printable. | |
| title | No | Diagram title, drawn at the top. Keep it short - it is a caption, not a sentence. | |
| width | No | Canvas width in pixels. Default 960. Use 1200+ for wide flows. | |
| height | No | Canvas height in pixels. Default 600. | |
| layout | No | How elements without explicit x/y are placed. 'auto' (default) builds a layered flow from the connections when there are any, otherwise a row. 'layered' forces the flow layout, 'horizontal'/'vertical'/'grid' force a simple arrangement, 'manual' means you provide every x/y yourself. | |
| legend | No | Show a legend built from the `label` of scatter/cluster series. Default true. | |
| autoFit | No | Grow the canvas so nothing is clipped. Default true - leave it on and stop worrying about exact sizes. | |
| padding | No | Margin around the drawing. Default 48. | |
| elements | Yes | Everything in the picture. Order matters: later elements are drawn on top. | |
| subtitle | No | Optional second line under the title. | |
| direction | No | Direction the layered flow grows in. Default 'right'. | |
| background | No | Canvas background. Defaults to the theme background. | |
| themeOverrides | No | Optional palette overrides. Only set what you actually want to change. |
Output Schema
| Name | Required | Description |
|---|---|---|
| svg | No | The rendered SVG markup. |
| title | No | |
| width | Yes | |
| format | Yes | |
| height | Yes | |
| svgUrl | No | Direct link to the rendered SVG, when deployed. |
| sceneId | Yes | Use this id with get_scene / update_element / add_element. |
| success | Yes | |
| elementCount | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations declare readOnlyHint=false, destructiveHint=false, idempotentHint=false, openWorldHint=false. The description goes beyond these by disclosing specific behavioral traits: 'the layout engine positions them from the connections', 'Canvas size is optional: the drawing is auto-fitted', 'Order matters: later elements are drawn on top', and 'Returns a sceneId. Keep it: later edits go through update_element / add_element / remove_element on that id.' This is rich context beyond just the boolean hints.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is front-loaded with the essential purpose in the first sentence, then branches into use cases, usage rules, best practices, and exclusions. It is quite long but every section earns its place—the 'HOW TO USE IT WELL' section is especially valuable for correct tool invocation. One minor redundancy: 'DO NOT use this tool for: plain prose answers, code, tables of numbers, or when the user explicitly asked for text only' could be slightly tighter, but overall it's well-structured and efficient for the complexity.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the high complexity (14 params, nested objects, 21 element types), the description is remarkably complete. It covers creation, layout guidance, element semantics, positioning philosophy, return value ('Returns a sceneId'), and lifecycle ('later edits go through update_element / add_element / remove_element'). An output schema exists, so return value details don't need to be in the description. The description fully equips an agent to invoke this tool correctly across a wide range of diagram types.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so baseline is 3. The description adds significant meaning beyond the schema: it explains layout philosophy ('Describe WHAT exists, not WHERE it goes', 'omit x/y: the layout engine positions them'), provides best-practice usage for connections ('Link things with { type: 'connection', from: '<id>', to: '<id>' }'), lists semantic element types to prefer, and gives theme descriptions (''dark' (default) is a modern technical look'). This goes well above the baseline.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description explicitly states 'Create and render a structured visual diagram as SVG, in a single call.' It lists specific use cases like architectures, network topologies, flows, plots, etc., and distinguishes this tool from alternatives like ASCII art, Mermaid, or manual SVG by asserting 'ALWAYS PREFER THIS OVER...'. The verb+resource combo is very specific, and the guidance on when to use it versus parents like add_element or create_scene is clear.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description includes a 'USE THIS whenever the user asks...' block listing numerous contexts (visualize, map out, diagram, etc.) and a 'DO NOT use this tool for' block excluding plain prose, code, or pure-text requests. It also advises 'ALWAYS PREFER THIS OVER ASCII ART, box-drawing characters...' and mentions alternatives ('later edits go through update_element / add_element / remove_element'). This provides comprehensive when-to-use and when-not-to-use guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
render_sceneRender an existing sceneARead-onlyIdempotent
Render a stored scene and show it to the user.
USE THIS after a batch of add_element / update_element / remove_element / group_elements calls, to display the updated diagram. It is the last step of every edit.
This never changes the scene - it only draws what is currently in it. For a brand new diagram use render_diagram instead, which builds and shows it in one call.
| Name | Required | Description | Default |
|---|---|---|---|
| sceneId | Yes | Id of the scene to work on, as returned by render_diagram or create_scene. |
Output Schema
| Name | Required | Description |
|---|---|---|
| svg | No | The rendered SVG markup. |
| error | No | Present only when success is false. |
| title | No | |
| width | No | |
| format | No | |
| height | No | |
| svgUrl | No | Stable link to the rendered SVG, when deployed. |
| sceneId | No | |
| success | Yes | |
| elementCount | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already provide readOnlyHint=true, idempotentHint=true, destructiveHint=false. The description adds value by explaining the tool 'never changes the scene - it only draws what is currently in it', which reinforces the behavioral contract beyond the annotations. A slight deduction is that it doesn't mention any potential rendering delays or failure modes.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is extremely concise with only three sentences, all front-loaded with the core purpose. Every sentence adds value: purpose, usage guidelines, and exclusion of alternatives. No wasted words.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the tool has 1 parameter with full schema coverage, an output schema, and comprehensive annotations, the description is complete. It provides all necessary context for an agent to select and invoke this tool correctly: purpose, usage flow, behavioral safety, parameter source, and sibling differentiation.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the baseline is 3. The description adds some value by mentioning the sceneId is 'as returned by render_diagram or create_scene', providing context on how to obtain valid IDs. However, it doesn't add new meaning about the parameter format beyond the schema's pattern.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states this tool renders a stored scene and shows it to the user. It uses specific verbs ('render', 'show', 'display') and identifies the resource ('stored scene', 'updated diagram'), distinguishing it from siblings like render_diagram which builds a new diagram.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description explicitly tells when to use this tool: after a batch of editing calls (add_element, update_element, etc.) as the last step of every edit. It also clearly states when NOT to use it: for brand new diagrams, use render_diagram instead. This provides excellent context for an AI agent to decide.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
update_elementUpdate an elementAIdempotent
Change properties of one element. Only the fields you send are touched; everything else in the scene stays exactly as it is.
USE THIS for every 'change that' request: move it, resize it, recolour it, rename its label, make a link dashed, add a caption to a connection. For a relative move like "a bit to the right", read the current position with get_scene and send the new value.
DO NOT call render_diagram again to change one thing. That throws away the scene id, the layout and everything the user already accepted.
id and type cannot be changed - remove and re-add the element if you truly need that.
Send null as a value to clear an optional property; for example { "x": null, "y": null }
hands the element back to the automatic layout.
Nothing is displayed until you call render_scene.
| Name | Required | Description | Default |
|---|---|---|---|
| changes | Yes | Properties to set. Examples: { "x": 420 } to move, { "label": "PostgreSQL 16" } to rename, { "fill": "primary", "emphasis": "strong" } to highlight, { "dash": "dashed" } on a connection, { "width": 240, "height": 120 } to resize. null clears an optional property. | |
| sceneId | Yes | Id of the scene to work on, as returned by render_diagram or create_scene. | |
| elementId | Yes | Id of the element to change. |
Output Schema
| Name | Required | Description |
|---|---|---|
| error | No | Present only when success is false. |
| element | No | The element after the change. |
| sceneId | No | |
| success | Yes | |
| elementId | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The annotations already mark destructiveHint=false and readOnlyHint=false, so mutation is expected. The description goes far beyond by explaining partial update semantics ('Only the fields you send are touched'), immutability of id and type, how to clear properties with null, and the critical fact that nothing is displayed until render_scene is called. No contradiction with annotations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is four paragraphs, each focused on a distinct aspect (what the tool does, use-cases, anti-patterns, edge cases/immutability/clearing, and rendering dependency). Every sentence serves a purpose; no wasted words.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the tool's complexity (3 params, all required, one being a nested object), the description fully covers usage, limitations, behavioral nuances, and integration with sibling tools. An output schema exists, so return values are not needed in the description.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, but the description adds enormous value: it explains how to use the `changes` parameter with concrete examples, clarifies that null clears optional properties, and eliminates ambiguity around partial updates. The schema itself is well-described, but the description contextualizes the schema's static definitions into actionable guidance.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description uses the specific verb 'Change properties' targeting 'one element', clearly distinguishing it from sibling tools like add_element or remove_element. It also explicitly calls out what it does and does not do, making its purpose unmistakable.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description provides excellent usage guidance: it tells when to use this tool ('USE THIS for every change that request'), provides examples of specific changes, and explicitly tells when NOT to use alternatives ('DO NOT call render_diagram again to change one thing'). It also gives a relative-move workaround using get_scene.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
10 tool updates
v0.1.0- First observed
add_element - First observed
clear_scene - First observed
create_scene - First observed
get_scene - First observed
group_elements - First observed
list_examples - First observed
remove_element - First observed
render_diagram - First observed
render_scene - First observed
update_element
TDQS
Scored across 10 tools
Each tool targets a distinct phase of the diagram lifecycle: create-and-render, render-existing, inspect, mutate, group, clear, and example lookup. The descriptions clearly separate render_diagram from create_scene/render_scene and get_scene from render_scene, so an agent should not misselect.
All tool names follow a consistent verb_noun pattern: render_diagram, get_scene, add_element, update_element, remove_element, group_elements, clear_scene. The convention is uniform and predictable across the entire set.
10 tools is well-scoped for a diagramming server: one call for whole diagrams, plus granular create/read/update/delete/render/group operations. Each tool serves a clear purpose without redundancy or bloat.
The lifecycle is well covered: create, render, inspect, add, update, remove, group, clear, and example guidance. The main gap is the lack of a dedicated ungroup operation, and there is no scene deletion/list tool, though clear_scene mitigates restart scenarios.
Maintenance
Related MCP Connectors
A comprehensive Model Context Protocol (MCP) server that enables AI assistants to interact with yo…
A Model Context Protocol server for Wix AI tools
Polished system design diagrams (SVG/PNG) from a tiny YAML spec, ~18x fewer tokens than raw SVG.
Create and edit architecture diagrams from your AI agent; get an SVG and a live editable canvas.
Related MCP Servers
- AlicenseBqualityAmaintenanceA Model Context Protocol server that enables LLMs to create, modify, and manipulate Excalidraw diagrams through a structured API.111,303 npm2,546MIT
- AlicenseNot gradedqualityNot gradedmaintenanceA server that implements the Model Context Protocol (MCP), providing an interface for LLM applications to generate mermaid.js visualizations and diagrams.MIT
- AlicenseAqualityDmaintenanceA Model Context Protocol (MCP) server designed to easily dump your codebase context into Large Language Models (LLMs).16 npm3Apache 2.0
- AlicenseBqualityDmaintenanceA Model Context Protocol server that enables LLMs to create, modify and manipulate Excalidraw diagrams through a structured API, supporting element creation, styling, organization, and scene management.122,276 npmMIT