Visual MCP
Visual MCP
一个模型上下文协议服务器,为LLM提供结构化的视觉层:它描述存在什么,并返回一个干净、精确的SVG图表——而不是ASCII艺术。
什么是Visual MCP?
让任何LLM“画出架构图”,你会得到这样的结果:
+----------+ +---------+ +------------+
| React |----->| NestJS |----->| PostgreSQL |
+----------+ +---------+ +------------+
|
+-----> Redis?框线绘制字符对于空间信息来说是一种糟糕的媒介。对齐会断裂,箭头无法到达,之后无法编辑,而且模型会把推理能力浪费在字符计数上。
显而易见的修复方案——“让模型编写SVG”——更糟糕。那样它必须手动计算viewBox、路径数据、箭头多边形、文本基线和边框交点,全部没有反馈,而且一旦用户要求做一个小改动,又得从头再来。
Visual MCP将几何计算从模型的任务中移除。 模型处理的是一个场景图:
{
"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" }
]
}注意没有什么:没有坐标,没有尺寸,没有线条端点,没有箭头,没有SVG。服务器计算所有内容——根据标签计算节点大小,根据连接图计算位置,计算与边框相交的边、箭头标记、文本换行,以及一个不会裁剪的viewBox。
而且因为场景是一个带有稳定ID的图,对话的下一轮就是一行编辑,而不是重绘:
“把Redis放在后端上面,PostgreSQL放在它下面。” →
update_element× 2,其他一切不变。
“现在把所有基础设施放在一个叫AWS的框里。” →
group_elements,并且没有元素移动。
Related MCP server: Mermaid MCP Server
架构
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 UI五个理念支撑着这一切
1. 场景图是产物,SVG只是一种输出格式。
模型发送的所有内容都被验证为Scene并存储。渲染是该场景的一个纯函数,因此同一个图表可以在以后以不同的主题或由不同的后端重新渲染,而无需模型参与。
2. 语义元素编译为基本图元。
database变成一条路径、一个椭圆和两个文本块。connection变成一条带标记的路径。渲染器只看到这十个基本图元——这使其保持小巧,并且意味着以后添加neuron、decisionTree或functionPlot只需要在src/semantic/中增加一个扩展文件,而无需更改模式联合、布局引擎或渲染器。
3. 一个几何管线,两个后端。
渲染器的实际输出是一个SvgNode树,而不是字符串。serialize()将其转换为MCP工具的标记;<SceneRenderer>将其映射为UI的React元素。没有第二个实现会导致漂移,项目中也没有dangerouslySetInnerHTML。
4. 错误是为模型编写的,而不是为日志文件。
{
"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."
}
}一个用于分支的代码,一个指出问题的句子,以及一个包含答案的提示。绝无堆栈跟踪。
5. 每个突变都是原子的。 被拒绝的编辑会使存储的场景保持字节完全不变。没有这一点,一次错误的调用就会破坏对话剩余部分的图表。
与最初草拟布局的偏差
src/layout/是一个独立的模块,与src/semantic/分开。定位和含义到形状的扩展是不同的任务,这种分离使得以后替换为Dagre或ELK只需要更改一个文件(src/layout/flow.ts)——它的FlowItemin / centres out接口特意设计成这些库所暴露的形状。SvgNode位于渲染器及其输出之间(上面的理念3),这使得React视图可以在没有第二个渲染器且没有不安全的HTML注入的情况下存在。src/mcp/widget.ts是一个无依赖的纯查看器,与src/ui/中的React应用分开。ChatGPT的iframe资源必须是一个自包含的HTML字符串,没有可能过时或运行时缺失的构建步骤;React应用是本地游乐场。它们共享相同的行为(缩放/平移/适应/复制/导出)和相同的允许列表。自动适应默认开启,因此
width/height作为提示而不是硬画布。这消除了最常见的失败模式——模型选择了一个太小的画布并裁剪了自己的图表。
安装
git clone <this repo>
cd visual-mcp
npm install需要Node 20+(在Node 22/26上开发)。
开发
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.html针对运行中的服务器进行快速端到端检查:
npm run dev:http &
npx tsx scripts/smoke-mcp.ts它重放整个目标对话——构建一个没有坐标的图表,检查它,移动两个节点,将所有内容包裹在一个框中——并在每一步断言结果。
可用的MCP工具
工具 | 功能 | 模型应该何时使用它 |
| 在一次调用中构建并渲染整个场景,返回一个 | 任何要求绘制、可视化、图表、说明或视觉解释的请求。默认入口点。 |
| 重新渲染一个已存储的场景。 | 在一批编辑之后,显示结果。 |
| 返回场景以及每个元素的计算框。 | 在编辑之前——特别是对于相对更改(“稍微向右”)。 |
| 添加一个元素,可选地放在一个组内。 | “添加一个负载均衡器”,“从A到B画一个箭头”。 |
| 只更改给定的字段; | 每个“更改那个”的请求。永远不要为此重绘。 |
| 删除一个元素,级联删除其连接和标签。 | “移除缓存”。 |
| 将顶层元素包裹在一个带标签的框中,不移动任何元素。 | “把所有这些放在AWS内”,“把这些分组到一个VPC中”。 |
| 创建一个空画布。 | 仅在逐步组装大型图表时使用。 |
| 清空一个场景,保留其画布/主题/标题。 | “放弃那个,我们重新开始”。 |
| 返回可用的示例场景和类型目录。 | 当不确定如何表达某些内容时——复制并调整。 |
每个工具描述都说明了它的功能、何时使用、何时不使用以及每个属性的含义,因为另一个模型会读取这些描述并自行决定。只读工具带有readOnlyHint: true,破坏性工具带有destructiveHint: true,宿主使用这些来决定哪些需要确认。
场景模式
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[];
}每个元素都有id和type。ID是稳定的,是对话式编辑的工作方式。
基本图元——渲染器可以绘制的内容
circle · ellipse · rectangle · line · arrow · text · polygon · polyline · path ·
group
语义元素——模型实际应该使用的内容
类型 | 用途 |
| 带标签的框。十种形状( |
| 按ID链接: |
| 带标签的容器,拥有自己的布局。边界如“AWS”、“VLAN 10”。 |
| 一个坐标系和一个数据框。 |
| 当给定 |
| 可以通过ID附加到另一个元素并跟随它的标题。 |
| 领域预设——一个已经选择了正确形状和符号的 |
布局
layout: "auto"(默认)在有连接时从连接图构建一个分层流,否则构建一行。具有显式x/y的元素永远不会被移动,因此模型可以微调一个节点而不干扰其他节点。direction控制流的增长方向。
数据框
一个axis声明从数据单位到像素的映射;任何带有frame: "<axis id>"的内容都放置在数据坐标中,y向上增长,正如它应该的那样:
{ "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 }主题
四个内置主题(dark、light、blueprint、paper),每个都是一个完整的调色板加上一个不需要外部字体的字体堆栈。元素引用令牌——primary、surface、muted、danger——而不是硬编码的颜色,因此整个图表可以在不触及几何的情况下重新样式化。themeOverrides更改任何令牌。
本地运行
作为库,完全没有MCP
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" },
],
});作为HTTP服务器
npm run dev:http路由 | |
| MCP Streamable HTTP 端点 |
| 健康检查 |
| 存储的场景 |
| 渲染的场景 |
| 交互式查看器,独立运行 |
服务器是无状态的:每个请求获得自己的McpServer和传输,场景存储是唯一共享的状态。这就是为什么它在负载均衡器后面或服务器无服务器平台上安全,因为一个对话的两轮可能不会到达同一个进程。
作为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"]
}
}
}连接到ChatGPT
ChatGPT通过公共HTTPS端点的Streamable HTTP访问远程MCP服务器,因此服务器需要可以从互联网访问。两种方式:
A. 使用隧道快速测试
npm run dev:http # http://localhost:3333/mcp
npx localtunnel --port 3333 # or: ngrok http 3333, or cloudflared tunnelB. 在VPS上使用Docker部署
docker-compose.yml 运行两个容器:MCP 服务器在 4000 端口(不向互联网发布)和 Caddy,后者在其前端终止 TLS 并自动续期证书。
该 VPS 的 80 和 443 端口上已有其他服务,因此:
端口 | 原因 | |
HTTPS / MCP 端点 | 500 | 443 已被占用 |
ACME HTTP-01 验证 | 90 | 80 已被占用 |
MCP 服务器 | 4000 | 仅内部使用,绝不对外发布 |
难点: Let's Encrypt 始终连接 80 端口 进行 HTTP-01 验证——这是 RFC 8555 规定的,不可配置——TLS-ALPN 同样固定为 443。Caddy 可以监听 90 端口,但必须有东西将请求转发过去。因此,任何已占用 80 端口的程序都必须将验证路径转发给 Caddy。
1. 选择一个公共主机名。 ChatGPT 需要 HTTPS,而裸 IP 无法获得证书。如果您没有自己的域名,请使用 sslip.io——它会将 <ip>.sslip.io 解析为该 IP,无需注册,并且 Let's Encrypt 会为其颁发证书:
curl -4 ifconfig.me # on the VPS -> e.g. 203.0.113.45
# hostname becomes: 203.0.113.45.sslip.io2. 从占用 80 端口的服务器转发 ACME 验证。 先确定它是哪个:
sudo ss -lptn 'sport = :80'nginx —— 在 server { listen 80; } 块内:
location /.well-known/acme-challenge/ {
proxy_pass http://127.0.0.1:90;
proxy_set_header Host $host;
}Apache —— 在 <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 —— 在提供 80 端口服务的站点块内:
handle /.well-known/acme-challenge/* {
reverse_proxy 127.0.0.1:90
}使用真正的代理,而不是 301 重定向:Let's Encrypt 仅遵循到 80 和 443 端口的重定向,因此重定向到 :90 会失败。
3. 配置并启动:
cp .env.example .env
# MCP_DOMAIN=203.0.113.45.sslip.io
docker compose up -d --build如果占用 80 端口的服务器运行在自己的容器内而非主机上,那么 127.0.0.1:90 对其不可达——请在 .env 中设置 MCP_HTTP_BIND=0.0.0.0,并将代理指向 VPS 的内部 IP(或者将两个容器放在同一个 Docker 网络中)。
4. 验证(首次请求可能需要几秒钟,因为证书正在颁发):
curl https://$MCP_DOMAIN:500/health # {"status":"ok",...}
docker compose logs caddy | grep -i "certificate obtained"MCP URL 为 https://<MCP_DOMAIN>:500/mcp,并且是永久性的:restart: unless-stopped 可在重启后保持运行,证书保存在 caddy_data 卷中,因此续期在 docker compose down/up 后仍然存在。只有 docker compose down -v 才会清除它们。请保持验证转发——每 ~60 天的续期与首次颁发同样需要它。
PUBLIC_URL 和 ALLOWED_HOSTS 由 Compose 从 MCP_DOMAIN 和 MCP_HTTPS_PORT 派生。两者都必须包含端口:PUBLIC_URL 是因为 svgUrl 链接否则会指向 443 端口;ALLOWED_HOSTS 是因为 SDK 会将原始 Host 头(在非标准端口上读取 <domain>:500)作为精确字符串进行比较。
如果您无法修改占用 80 端口的服务器,则 HTTP-01 不可用。可行的方案是 DNS-01 验证(需要一个在受支持的 DNS 提供商上的真实域名——sslip.io 没有 API),或者 Cloudflare Tunnel,后者完全不需要入站端口。
不使用 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-mcp然后将任何反向代理指向 http://127.0.0.1:4000。容器以非 root 用户 node 身份运行,带有 /health 健康检查,并且仅包含生产依赖项。托管平台(Fly.io、Railway、Render、Cloud Run)同样适用——它们会注入自己的 PORT,服务器会遵循该端口。
然后在 ChatGPT 中
启用开发者模式。 ChatGPT Business、Enterprise 和 Edu 的网页版可用。管理员在 工作区设置 → 权限与角色 → 连接数据 → 开发者模式 / 创建自定义 MCP 连接器 中启用。
设置 → 连接器 → 创建 / 高级 → 开发者模式 → 添加自定义连接器。
填写:
名称:
Visual MCPMCP 服务器 URL:
https://<your-host>/mcp认证:
无认证(此服务器不附带认证——请参阅安全)
保存。ChatGPT 会立即调用
tools/list;您应该会看到列出的十个工具。在新建对话中,启用连接器并请求图表。
该服务器还注册了一个 MCP Apps UI 资源(ui://visual-mcp/scene.html,text/html;profile=mcp-app),通过 _meta.ui.resourceUri 和 ChatGPT 别名 _meta["openai/outputTemplate"] 附加到渲染工具。在支持的地方,图表会显示在带有缩放、平移、适应、复制和导出功能的交互式框架中;在其他地方,工具仍会在 structuredContent 中返回 SVG,因此服务器可以优雅地降级。
示例
npm run examples 将所有五个示例渲染到 examples/out/index.html,而 list_examples 将其提供给模型。
1. 网络图——examples/out/network.svg
域预设和自动从左到右布局。computer → switch → router → server,链接上带有 VLAN 标题。源场景中没有任何坐标。
2. LDA——examples/out/lda.svg
一个 axis 数据框架,两个带种子的 cluster 和软凸包,一条虚线决策边界以及 LDA 方向——全部使用数据坐标,两条线都裁剪到绘图区域。
3. 回归——examples/out/regression.svg
坐标轴,一个 scatter 系列和一条带有 extend: true 的拟合 plotLine,使用浅色主题。
4. 软件架构——examples/out/architecture.svg
React → REST API → { Redis, PostgreSQL },两个存储位于一个带标签的 group("数据层")内,连接路由进入其中。
5. 二叉树——examples/out/tree.svg
七个圆形节点和六条连接;down 方向的层次布局生成了树。
可在 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].安全
威胁模型很简单:服务器渲染的所有内容都来自语言模型,而该输出本身可能包含用户从其他地方粘贴的文本。因此,任何源自模型的内容都永远不会被视为代码。
封闭模式。 只解析已知的二十四种元素类型。颜色必须匹配十六进制/rgb/hsl/关键字/令牌语法——
url(javascript:…)在验证时会被拒绝。路径数据必须匹配 SVG 路径命令和数字,不能有其他内容。允许列表输出。 渲染器只能发出来自
src/renderer/svgNode.ts中两个显式列表的标签和属性。没有on*,没有href,没有style,没有class,没有<foreignObject>,没有<script>——模型无法表达它们,序列化器也会丢弃它们。转义。 文本内容和属性值在输出时会进行 XML 转义。
项目中没有任何地方使用
dangerouslySetInnerHTML、eval、new Function。React 视图从SvgNode树构建元素;ChatGPT 小部件解析 SVG 并逐个节点针对相同的允许列表重新构建它,因此即使服务器被攻破,也无法将脚本注入到那个框架中。有界输入。 元素数量、字符串长度、点数量、路径长度和存储的场景都有上限;场景按最旧优先驱逐。
不向模型泄露堆栈跟踪。 每个处理程序都被包装;任何意外情况都会变成带有简短消息的
INTERNAL_ERROR。
tests/security.test.tsx 中的测试对每一项都进行了断言。
按设计不包含的内容: 身份验证。该服务器不暴露任何秘密,也不接触任何外部系统,但公开部署就是一个公开的场景存储。在向除您之外的任何人暴露之前,请将其放在您平台的身份验证之后,或者通过 SDK 的身份验证辅助程序添加 OAuth。设置 ALLOWED_HOSTS 以在可从浏览器访问时启用 DNS 重新绑定保护。
当前限制
文本度量是估算的,而非实际测量——服务器上没有字体引擎。对于捆绑的无衬线字体栈,宽度在几个百分点以内,这对于盒子和换行来说足够了,但非常用字体或大量 CJK 字符会导致尺寸略微偏松。
布局引擎故意设计得很小。 最长路径分层加居中对齐。没有交叉最小化和重叠解决,因此密集图(大约 25 个以上节点且有许多交叉链接)会产生真实引擎会避免的交叉。接口之所以设计成 Dagre 风格,正是出于这个原因。
树形布局不是子节点居中的;父节点位于其层级的中心,而不是其子节点中点的上方。
render_diagram的 JSON Schema 约为 50 KB(约 12k 个 token),因为它包含了整个元素词汇表。其他九个工具总计约 5 KB。这是一个刻意的权衡:模型获得每个字段的文档,很少需要修复往返。在具有自动布局的组内,子节点的显式
x/y会被忽略——布局优先。使用组上的layout: "manual"来自己放置子节点。存储是内存中的。 场景不会在重启后保留,并且有多个副本时,场景只存在于创建它的实例上。
SceneStore接口的存在正是为了替换这个类。静态输出。 目前没有动画、3D 或数学表达式解析器——请参阅下文。
路线图
下一步
持久化
SceneStore(首先是 SQLite)——接口已经就位。撤销/重做。每个变更已记录为
SceneMutation;这只是存储逆操作的问题。在
src/layout/flow.ts后端使用 Dagre 或 ELK 处理密集图,以小引擎作为默认选项。子节点居中的树形布局。
数学——axis 数据框架是基础;以下每一项都是 src/semantic/ 中的一个扩展器,无需更改渲染器:
functionPlot ({ "expression": "x^2", "domain": [-5, 5] })、vector、matrix、plane、distribution、projection、decisionBoundary 和 regressionLine 作为 plotLine 的命名别名。
图表词汇——neuron、neuralNetwork、decisionTree、sequenceDiagram、stateMachine、gantt、swimlane。
动画——在连接上使用 { "animation": { "type": "flow", "duration": 1200 } },以 SVG SMIL 或 CSS 形式发出,使其保持声明式且无需运行时。数据包沿链接移动,请求穿过管道,算法在结构中逐步执行。
其他渲染器——解析管道最终生成与后端无关的树。具有 kind: "3d" 和 { "type": "sphere", "position": [0, 1, 0] } 的 Scene 会选择 Three.js 后端而不是 SVG 后端;Canvas 后端可用于非常大的散点图。MCP 层无需更改。
许可
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,236 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).13 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,232 npmMIT