Skip to main content
Glama

Visual MCP

모델 컨텍스트 프로토콜 서버로, LLM에 구조화된 시각적 계층을 제공합니다:
무엇이 존재하는지 설명하면, 깔끔하고 정확한 SVG 다이어그램을 반환합니다 — ASCII 아트 대신에.


Visual MCP란 무엇인가요?

LLM에게 "아키텍처를 그려줘"라고 요청하면 이런 결과를 얻습니다:

+----------+      +---------+      +------------+
|  React   |----->| NestJS  |----->| PostgreSQL |
+----------+      +---------+      +------------+
                       |
                       +-----> Redis?

상자 그리기 문자는 공간 정보를 전달하기에 좋지 않은 매체입니다. 정렬이 깨지고, 화살표가 제대로 닿지 않으며, 이후에 수정할 수 없고, 모델이 문자 개수 세기에 추론 능력을 소모합니다.

명백한 해결책 — "모델이 SVG를 작성하게 하자" — 는 더 나쁩니다. 그러면 모델이 viewBox, 경로 데이터, 화살촉 다각형, 텍스트 기준선, 테두리 교차점을 모두 손으로, 피드백 없이 계산해야 하며, 사용자가 하나의 작은 변경을 요청할 때마다 처음부터 다시 계산해야 합니다.

Visual MCP는 모델의 작업에서 기하학을 제거합니다. 모델은 *씬 그래프(scene graph)*를 사용합니다:

{
  "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. 씬 그래프는 결과물(artefact)이고, SVG는 단지 출력 형식일 뿐입니다. 모델이 보내는 모든 것은 검증되어 Scene으로 저장됩니다. 렌더링은 그 씬의 순수 함수이므로, 동일한 다이어그램을 나중에 다른 테마나 다른 백엔드에서 다시 렌더링할 수 있으며, 모델이 관여할 필요가 없습니다.

2. 의미 요소(semantic element)는 기본 요소(primitive)로 컴파일됩니다. database는 경로(path), 타원(ellipse), 두 개의 텍스트 블록이 됩니다. connection은 마커가 있는 경로가 됩니다. 렌더러는 열 개의 기본 요소만 보게 되므로 작게 유지되며, 나중에 neuron, decisionTree, functionPlot을 추가하는 것은 src/semantic/에 하나의 확장 파일만 추가하면 되며, 스키마 유니온, 레이아웃 엔진, 렌더러를 변경할 필요가 없습니다.

3. 하나의 지오메트리 파이프라인, 두 개의 백엔드. 렌더러의 실제 출력은 문자열이 아닌 SvgNode 트리입니다. serialize()는 이를 MCP 도구용 마크업으로 변환하고, <SceneRenderer>는 React 요소로 매핑하여 UI에 표시합니다. 두 번째 구현이 따로 존재하지 않아 표류할 일이 없고, 프로젝트 어디에도 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. 모든 변경(mutation)은 원자적(atomic)입니다. 거부된 편집은 저장된 씬을 바이트 단위로 그대로 유지합니다. 이것이 없으면 한 번의 잘못된 호출이 나머지 대화의 다이어그램을 손상시킬 수 있습니다.

원래 스케치된 레이아웃과의 차이점

  • src/layout/는 자체 모듈로, src/semantic/과 분리되어 있습니다. 위치 지정과 의미-모양 변환은 서로 다른 문제이며, 이 분리가 나중에 Dagre나 ELK를 교체하는 것을 src/layout/flow.ts 하나의 파일 변경으로 가능하게 합니다 — 그 FlowItem in / centres out 인터페이스는 의도적으로 해당 라이브러리들이 노출하는 형태입니다.

  • SvgNode는 렌더러와 그 출력 사이에 위치하며 (위의 아이디어 3), React 뷰가 두 번째 렌더러나 안전하지 않은 HTML 주입 없이 존재할 수 있게 합니다.

  • src/mcp/widget.ts는 종속성 없는 바닐라 뷰어로, src/ui/의 React 앱과 분리되어 있습니다. ChatGPT iframe 리소스는 빌드 단계 없이 단일 자체 포함 HTML 문자열이어야 하며, 런타임에 오래되거나 누락될 수 없습니다. React 앱은 로컬 플레이그라운드입니다. 이들은 동일한 동작(확대/축소/이동/맞춤/복사/내보내기)과 동일한 허용 목록을 공유합니다.

  • 자동 맞춤(auto-fit)이 기본으로 켜져 있어, 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 도구

도구

하는 일

모델이 사용해야 할 때

render_diagram

전체 씬을 한 번에 빌드 하고 렌더링하여 sceneId를 반환합니다.

그리기, 시각화, 다이어그램, 설명 또는 시각적 설명을 요청하는 모든 경우. 기본 진입점입니다.

render_scene

저장된 씬을 다시 렌더링합니다.

편집 배치 후 결과를 보여주기 위해.

get_scene

씬과 모든 요소의 계산된 박스를 반환합니다.

편집 전 — 특히 상대적 변경("오른쪽으로 조금")을 위해.

add_element

하나의 요소를 추가합니다. 선택적으로 그룹 내부에.

"로드 밸런서 추가", "A에서 B로 화살표 그리기".

update_element

주어진 필드만 변경합니다. null은 필드를 지웁니다.

모든 "이것을 변경해줘" 요청. 이를 위해 다시 그리지 마세요.

remove_element

요소를 삭제하고, 연결된 연결선과 레이블도 함께 삭제합니다.

"캐시 제거".

group_elements

최상위 요소들을 레이블이 있는 상자에 넣고, 아무것도 움직이지 않습니다.

"이 모든 것을 AWS 안에 넣어줘", "이것들을 VPC로 그룹화".

create_scene

빈 캔버스를 만듭니다.

큰 다이어그램을 점진적으로 조립할 때만.

clear_scene

씬을 비우고, 캔버스/테마/제목은 유지합니다.

"그거 버리고, 새로 시작하자".

list_examples

작동하는 예제 씬과 유형 카탈로그를 반환합니다.

무언가를 어떻게 표현할지 확신이 없을 때 — 복사해서 적응하세요.

모든 도구 설명은 무엇을 하는지, 언제 사용하는지, 언제 사용하지 말아야 하는지, 각 속성이 무엇을 의미하는지 명시합니다. 다른 모델이 이를 읽고 스스로 결정하기 때문입니다. 읽기 전용 도구는 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는 안정적이며 대화형 편집이 작동하는 방식입니다.

기본 요소(Primitives) — 렌더러가 그릴 수 있는 것

circle · ellipse · rectangle · line · arrow · text · polygon · polyline · path · group

의미 요소(Semantic elements) — 모델이 실제로 사용해야 하는 것

유형

목적

node

레이블이 있는 상자. 열 가지 모양(rounded, circle, diamond, hexagon, cylinder, cloud, stack, screen, pill, rect). 레이블에서 크기를 스스로 결정합니다. 연결에서 위치를 결정합니다.

connection

ID로 연결: { from: "a", to: "b" }. 두 테두리를 찾고 화살촉을 추가하며, 한쪽 끝이 이동해도 올바르게 유지됩니다. 라우팅: straight, curved, orthogonal.

group

자체 레이아웃이 있는 레이블이 있는 컨테이너. "AWS", "VLAN 10"과 같은 경계.

axis

좌표계 이자 데이터 프레임.

point / scatter / cluster / plotLine

frame: "<axis id>"가 주어지면 데이터 좌표의 마커와 선. cluster는 결정론적이고 시드된 점 덩어리를 생성합니다.

label

ID로 다른 요소에 첨부될 수 있고 따라다니는 캡션.

server database router switch computer cloud

도메인 프리셋 — 이미 올바른 모양과 글리프가 선택된 node.

레이아웃

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

라우트

POST /mcp

MCP Streamable HTTP 엔드포인트

GET /health

헬스 체크

GET /scenes

저장된 씬 목록

GET /scenes/:id.svg

렌더링된 씬

GET /viewer?svg=/scenes/:id.svg

대화형 뷰어, 독립 실행형

서버는 상태 비저장(stateless) 입니다: 각 요청은 자체 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 tunnel

B. 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는 HTTP-01을 위해 항상 포트 80에 연결합니다. 이는 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.io

2. 포트 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으로 지정하세요. 컨테이너는 루트가 아닌 node 사용자로 실행되며, /health HEALTHCHECK를 제공하고 프로덕션 종속성만 포함합니다. 관리형 플랫폼(Fly.io, Railway, Render, Cloud Run)에서도 작동합니다 — 자체 PORT를 주입하면 서버가 이를 따릅니다.

그런 다음, ChatGPT에서

  1. 개발자 모드를 활성화하세요. ChatGPT Business, Enterprise 및 Edu(웹)에서 사용할 수 있습니다. 관리자가 작업 공간 설정 → 권한 및 역할 → 연결된 데이터 → 개발자 모드 / 사용자 지정 MCP 커넥터 만들기에서 활성화합니다.

  2. 설정 → 커넥터 → 만들기 / 고급 → 개발자 모드 → 사용자 지정 커넥터 추가.

  3. 다음을 입력하세요:

    • 이름: Visual MCP

    • MCP 서버 URL: https://<your-host>/mcp

    • 인증: 인증 없음 (이 서버는 인증 없이 제공됨 — 보안 참조)

  4. 저장합니다. ChatGPT는 즉시 tools/list를 호출하며, 10개의 도구가 나열된 것을 볼 수 있습니다.

  5. 새 채팅에서 커넥터를 활성화하고 다이어그램을 요청하세요.

서버는 또한 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

7개의 원형 노드와 6개의 연결; 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].

보안

위협 모델은 간단합니다: 서버가 렌더링하는 모든 것은 언어 모델에서 비롯되었으며, 그 출력에는 사용자가 다른 곳에서 붙여넣은 텍스트가 포함될 수 있습니다. 따라서 모델에서 파생된 어떤 것도 코드로 취급되지 않습니다.

  • 폐쇄형 스키마. 24개의 알려진 요소 유형만 구문 분석됩니다. 색상은 hex/rgb/hsl/keyword/token 문법과 일치해야 합니다 — 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를 추가한 후 다른 사람에게 노출하세요. 브라우저에서 접근 가능할 때 DNS-리바인딩 보호를 활성화하려면 ALLOWED_HOSTS를 설정하세요.


현재 제한 사항

  • 텍스트 메트릭은 추정되며, 측정되지 않습니다. 서버에 글꼴 엔진이 없기 때문입니다. 번들된 산세리프 스택의 경우 너비가 몇 퍼센트 이내로 정확하므로 상자와 줄바꿈에 충분하지만, 특이한 글꼴이나 많은 CJK 문자가 있으면 약간 느슨하게 크기가 조정됩니다.

  • 레이아웃 엔진은 의도적으로 작습니다. 최장 경로 레이어링과 순위 중심 정렬. 교차 최소화나 중복 해결이 없으므로, 밀집된 그래프(약 25개 이상의 노드와 많은 교차 링크)는 실제 엔진이 피할 교차를 생성합니다. 인터페이스는 정확히 이 이유로 Dagre 형태입니다.

  • 트리 레이아웃은 자식 중심이 아닙니다. 부모는 자식의 중간점 위가 아닌, 자신의 순위 중심에 위치합니다.

  • render_diagram의 JSON 스키마는 약 50KB(약 12k 토큰) 입니다. 전체 요소 어휘를 가르치기 때문입니다. 다른 아홉 도구는 총 약 5KB입니다. 이는 의도적인 절충입니다: 모델은 필드별 문서를 얻고 수리 라운드트립이 거의 필요하지 않습니다.

  • 자동 레이아웃이 있는 그룹 내에서 자식의 명시적 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은 SVG 대신 Three.js 백엔드를 선택합니다. Canvas 백엔드는 매우 큰 산점도를 제공합니다. MCP 레이어 변경 없음.


라이선스

MIT

Available Tools

10 tools
add_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.

ParametersJSON Schema
NameRequiredDescriptionDefault
elementYesThe 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" }.
sceneIdYesId of the scene to work on, as returned by render_diagram or create_scene.
parentIdNoId of a 'group' element to nest this inside.

Output Schema

ParametersJSON Schema
NameRequiredDescription
errorNoPresent only when success is false.
sceneIdNo
successYes
elementIdNo
elementCountNoTop-level elements in the scene after the add.

TDQS

A4.9/5.0
Behavior5/5

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.

Conciseness5/5

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.

Completeness5/5

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.

Parameters4/5

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.

Purpose5/5

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.

Usage Guidelines5/5

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 sceneA
DestructiveIdempotent

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.

ParametersJSON Schema
NameRequiredDescriptionDefault
sceneIdYesId of the scene to work on, as returned by render_diagram or create_scene.

Output Schema

ParametersJSON Schema
NameRequiredDescription
errorNoPresent only when success is false.
sceneIdNo
successYes

TDQS

A4.7/5.0
Behavior4/5

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.

Conciseness5/5

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.

Completeness5/5

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.

Parameters4/5

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.

Purpose5/5

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.

Usage Guidelines5/5

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.

ParametersJSON Schema
NameRequiredDescriptionDefault
idNoStable, meaningful id (e.g. 'backend'). Reuse it later to update or remove this element.
gapNoSpacing used by the automatic layout. Default 90.
themeNoVisual theme. 'dark' (default) is a modern technical look, 'light' is for documents, 'blueprint' is a blue schematic, 'paper' is warm and printable.
titleNoDiagram title, drawn at the top. Keep it short - it is a caption, not a sentence.
widthNoCanvas width in pixels. Default 960. Use 1200+ for wide flows.
heightNoCanvas height in pixels. Default 600.
layoutNoHow 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.
legendNoShow a legend built from the `label` of scatter/cluster series. Default true.
autoFitNoGrow the canvas so nothing is clipped. Default true - leave it on and stop worrying about exact sizes.
paddingNoMargin around the drawing. Default 48.
subtitleNoOptional second line under the title.
directionNoDirection the layered flow grows in. Default 'right'.
backgroundNoCanvas background. Defaults to the theme background.
themeOverridesNoOptional palette overrides. Only set what you actually want to change.

Output Schema

ParametersJSON Schema
NameRequiredDescription
messageYes
sceneIdYes
successYes

TDQS

A4.4/5.0
Behavior4/5

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.

Conciseness5/5

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.

Completeness4/5

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.

Parameters3/5

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.

Purpose5/5

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.

Usage Guidelines5/5

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 sceneA
Read-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.

ParametersJSON Schema
NameRequiredDescriptionDefault
sceneIdYesId of the scene to work on, as returned by render_diagram or create_scene.
elementIdNoReturn just this one element instead of the whole scene.
includeLayoutNoInclude the computed box of every element. Default true.

Output Schema

ParametersJSON Schema
NameRequiredDescription
idsNoEvery element id in the scene.
errorNoPresent only when success is false.
sceneNoThe full scene, ready to be edited or re-sent.
layoutNoMap of element id to { x, y, width, height }.
elementNo
historyNoThe last few mutations applied to this scene.
sceneIdNo
successYes

TDQS

A4.5/5.0
Behavior4/5

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.

Conciseness5/5

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.

Completeness5/5

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.

Parameters4/5

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.

Purpose5/5

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.

Usage Guidelines4/5

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.

ParametersJSON Schema
NameRequiredDescriptionDefault
fillNoBackground colour.
labelNoText drawn on the box, e.g. 'AWS'.
layoutNoRe-arrange the members. Default 'manual' (keep their current positions).
strokeNoBorder colour.
groupIdYesId for the new group, e.g. 'aws' or 'vlan-10'.
sceneIdYesId of the scene to work on, as returned by render_diagram or create_scene.
elementIdsYesIds of the top-level elements to move inside the box.

Output Schema

ParametersJSON Schema
NameRequiredDescription
errorNoPresent only when success is false.
groupIdNo
membersNo
sceneIdNo
successYes

TDQS

A4.9/5.0
Behavior5/5

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.

Conciseness5/5

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.

Completeness5/5

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.

Parameters4/5

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.

Purpose5/5

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.

Usage Guidelines5/5

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 scenesA
Read-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.

ParametersJSON Schema
NameRequiredDescriptionDefault
nameNoReturn the full scene for this example. Omit for the catalogue.

Output Schema

ParametersJSON Schema
NameRequiredDescription
sceneNo
successYes
examplesNo
elementTypesNo

TDQS

A4.2/5.0
Behavior3/5

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.

Conciseness5/5

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.

Completeness4/5

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.

Parameters3/5

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.

Purpose5/5

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.

Usage Guidelines5/5

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 elementA
DestructiveIdempotent

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.

ParametersJSON Schema
NameRequiredDescriptionDefault
cascadeNoAlso remove connections and labels attached to it. Default true.
sceneIdYesId of the scene to work on, as returned by render_diagram or create_scene.
elementIdYesId of the element to remove.

Output Schema

ParametersJSON Schema
NameRequiredDescription
errorNoPresent only when success is false.
removedNoEvery id that was deleted, including cascaded ones.
sceneIdNo
successYes

TDQS

A4.9/5.0
Behavior5/5

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.

Conciseness5/5

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.

Completeness5/5

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.

Parameters4/5

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.

Purpose5/5

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.

Usage Guidelines5/5

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.

ParametersJSON Schema
NameRequiredDescriptionDefault
gapNoSpacing used by the automatic layout. Default 90.
themeNoVisual theme. 'dark' (default) is a modern technical look, 'light' is for documents, 'blueprint' is a blue schematic, 'paper' is warm and printable.
titleNoDiagram title, drawn at the top. Keep it short - it is a caption, not a sentence.
widthNoCanvas width in pixels. Default 960. Use 1200+ for wide flows.
heightNoCanvas height in pixels. Default 600.
layoutNoHow 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.
legendNoShow a legend built from the `label` of scatter/cluster series. Default true.
autoFitNoGrow the canvas so nothing is clipped. Default true - leave it on and stop worrying about exact sizes.
paddingNoMargin around the drawing. Default 48.
elementsYesEverything in the picture. Order matters: later elements are drawn on top.
subtitleNoOptional second line under the title.
directionNoDirection the layered flow grows in. Default 'right'.
backgroundNoCanvas background. Defaults to the theme background.
themeOverridesNoOptional palette overrides. Only set what you actually want to change.

Output Schema

ParametersJSON Schema
NameRequiredDescription
svgNoThe rendered SVG markup.
titleNo
widthYes
formatYes
heightYes
svgUrlNoDirect link to the rendered SVG, when deployed.
sceneIdYesUse this id with get_scene / update_element / add_element.
successYes
elementCountYes

TDQS

A4.6/5.0
Behavior4/5

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.

Conciseness4/5

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.

Completeness5/5

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.

Parameters4/5

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.

Purpose5/5

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.

Usage Guidelines5/5

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 sceneA
Read-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.

ParametersJSON Schema
NameRequiredDescriptionDefault
sceneIdYesId of the scene to work on, as returned by render_diagram or create_scene.

Output Schema

ParametersJSON Schema
NameRequiredDescription
svgNoThe rendered SVG markup.
errorNoPresent only when success is false.
titleNo
widthNo
formatNo
heightNo
svgUrlNoStable link to the rendered SVG, when deployed.
sceneIdNo
successYes
elementCountNo

TDQS

A4.5/5.0
Behavior4/5

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.

Conciseness5/5

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.

Completeness5/5

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.

Parameters3/5

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.

Purpose5/5

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.

Usage Guidelines5/5

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 elementA
Idempotent

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.

ParametersJSON Schema
NameRequiredDescriptionDefault
changesYesProperties 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.
sceneIdYesId of the scene to work on, as returned by render_diagram or create_scene.
elementIdYesId of the element to change.

Output Schema

ParametersJSON Schema
NameRequiredDescription
errorNoPresent only when success is false.
elementNoThe element after the change.
sceneIdNo
successYes
elementIdNo

TDQS

A5/5.0
Behavior5/5

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.

Conciseness5/5

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.

Completeness5/5

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.

Parameters5/5

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.

Purpose5/5

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.

Usage Guidelines5/5

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.

  1. 10 tool updatesv0.1.0
    • First observedadd_element
    • First observedclear_scene
    • First observedcreate_scene
    • First observedget_scene
    • First observedgroup_elements
    • First observedlist_examples
    • First observedremove_element
    • First observedrender_diagram
    • First observedrender_scene
    • First observedupdate_element

TDQS

A4.6/5.0

Scored across 10 tools

Disambiguation5/5

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.

Naming Consistency5/5

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.

Tool Count5/5

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.

Completeness4/5

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

ActivityStale
ResponsivenessNo issues

Related MCP Connectors

Related MCP Servers