Skip to main content
Glama

Visual MCP

Ein Model Context Protocol Server, der einer KI eine strukturierte visuelle Ebene bereitstellt: Er beschreibt, was existiert, und erhält ein sauberes, präzises SVG-Diagramm zurück – anstelle von ASCII-Art.


Was ist Visual MCP?

Bittet man eine KI, "die Architektur zu zeichnen", erhält man dies:

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

Box-Zeichen sind ein schlechtes Medium für räumliche Informationen. Ausrichtung bricht, Pfeile erreichen ihr Ziel nicht, nichts lässt sich nachträglich bearbeiten, und das Modell verbraucht Rechenleistung für das Zählen von Zeichen.

Die naheliegende Lösung – "die KI SVG schreiben lassen" – ist noch schlimmer. Dann muss sie viewBoxes, Pfaddaten, Pfeilspitzen-Polygone, Textbaselines und Randüberschneidungen berechnen, alles von Hand, alles ohne Rückmeldung, und alles wieder von Grund auf, sobald der Benutzer eine kleine Änderung wünscht.

Visual MCP entfernt die Geometrie aus der Aufgabe des Modells. Das Modell arbeitet mit einem Szenengraphen:

{
  "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" }
  ]
}

Beachten Sie, was nicht da ist: keine Koordinaten, keine Größen, keine Linienendpunkte, keine Pfeilspitzen, kein SVG. Der Server berechnet alles – Knotengrößen aus den Beschriftungen, Positionen aus dem Verbindungsgraphen, Kanten, die die Ränder treffen, Pfeilspitzenmarkierungen, Textumbruch und eine viewBox, die nichts abschneiden kann.

Und weil die Szene ein Graph mit stabilen IDs ist, ist der nächste Gesprächsschritt eine einzeilige Bearbeitung statt einer Neuzeichnung:

"Setze Redis oberhalb des Backends und PostgreSQL unterhalb." → update_element × 2, alles andere bleibt unberührt.

"Stecke nun die gesamte Infrastruktur in eine Box namens AWS." → group_elements, und nichts bewegt sich.


Related MCP server: Mermaid MCP Server

Architektur

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

Fünf Ideen halten das Ganze zusammen

1. Der Szenengraph ist das Artefakt, SVG ist nur ein Ausgabeformat. Alles, was das Modell sendet, wird zu einer Scene validiert und gespeichert. Das Rendern ist eine reine Funktion dieser Szene, sodass dasselbe Diagramm später in einem anderen Thema oder durch ein anderes Backend neu gerendert werden kann, ohne dass das Modell involviert ist.

2. Semantische Elemente werden zu Primitiven kompiliert. database wird zu einem Pfad, einer Ellipse und zwei Textblöcken. connection wird zu einem Pfad mit einer Markierung. Der Renderer sieht nur die zehn Primitiven – das hält ihn klein, und das Hinzufügen von neuron, decisionTree oder functionPlot später ist eine Erweiterungsdatei in src/semantic/, ohne Änderung der Schema-Union, der Layout-Engine oder des Renderers.

3. Eine Geometrie-Pipeline, zwei Backends. Die eigentliche Ausgabe des Renderers ist ein SvgNode-Baum, kein String. serialize() wandelt ihn in Markup für die MCP-Tools um; <SceneRenderer> bildet ihn auf React-Elemente für die UI ab. Es gibt keine zweite Implementierung, die abweichen könnte, und kein dangerouslySetInnerHTML irgendwo im Projekt.

4. Fehler werden für ein Modell geschrieben, nicht für eine Logdatei.

{
  "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."
  }
}

Ein Code, nach dem verzweigt werden kann, ein Satz, der das Problem benennt, und ein Hinweis, der die Antwort enthält. Niemals ein Stacktrace.

5. Jede Mutation ist atomar. Eine abgelehnte Bearbeitung hinterlässt die gespeicherte Szene bytegenau so, wie sie war. Ohne das würde ein einziger fehlerhafter Aufruf das Diagramm für den Rest des Gesprächs zerstören.

Abweichungen vom ursprünglich skizzierten Layout

  • src/layout/ ist ein eigenes Modul, getrennt von src/semantic/. Positionierung und Expansion von Bedeutung zu Form sind unterschiedliche Probleme, und die Aufteilung ist es, die das Austauschen von Dagre oder ELK später zu einer Änderung in einer Datei (src/layout/flow.ts) macht – deren FlowItem-in-/out-Schnittstelle bewusst die Form dieser Bibliotheken widerspiegelt.

  • SvgNode sitzt zwischen dem Renderer und seiner Ausgabe (Idee 3 oben), was es ermöglicht, dass die React-Ansicht ohne einen zweiten Renderer und ohne unsichere HTML-Injektion existiert.

  • src/mcp/widget.ts ist ein abhängigkeitsfreier Vanilla-Viewer, getrennt von der React-App in src/ui/. Die ChatGPT-iframe-Ressource muss ein einzelner, in sich geschlossener HTML-String sein, ohne Build-Schritt, der zur Laufzeit veraltet oder fehlen könnte; die React-App ist der lokale Spielplatz. Sie teilen dasselbe Verhalten (Zoom/Pan/Fit/Kopieren/Exportieren) und dieselbe Erlaubnisliste.

  • Auto-Fit ist standardmäßig aktiviert, sodass width/height als Hinweise und nicht als harte Leinwand fungieren. Dies beseitigt den häufigsten Fehlermodus – ein Modell wählt eine zu kleine Leinwand und schneidet sein eigenes Diagramm ab.


Installation

git clone <this repo>
cd visual-mcp
npm install

Erfordert Node 20+ (entwickelt auf Node 22/26).


Entwicklung

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

Schneller End-to-End-Check gegen einen laufenden Server:

npm run dev:http &
npx tsx scripts/smoke-mcp.ts

Es spielt die gesamte Zielkonversation nach – baue ein Diagramm ohne Koordinaten, inspiziere es, verschiebe zwei Knoten, verpacke alles in eine Box – und prüft das Ergebnis bei jedem Schritt.


Verfügbare MCP-Tools

Tool

Was es tut

Wann das Modell danach greifen sollte

render_diagram

Erstellt und rendert eine ganze Szene in einem Aufruf, gibt eine sceneId zurück.

Jede Aufforderung, etwas zu zeichnen, visualisieren, diagrammieren, illustrieren oder visuell zu erklären. Der Standard-Einstiegspunkt.

render_scene

Rendert eine gespeicherte Szene erneut.

Nach einer Reihe von Bearbeitungen, um das Ergebnis zu zeigen.

get_scene

Gibt die Szene plus die berechneten Boxen jedes Elements zurück.

Vor dem Bearbeiten – insbesondere für relative Änderungen ("etwas nach rechts").

add_element

Fügt ein Element hinzu, optional innerhalb einer Gruppe.

"Füge einen Load Balancer hinzu", "zeichne einen Pfeil von A nach B".

update_element

Ändert nur die angegebenen Felder; null löscht eines.

Jede "ändere das"-Anfrage. Niemals dafür neu zeichnen.

remove_element

Löscht ein Element, kaskadierend seine Verbindungen und Beschriftungen.

"Entferne den Cache".

group_elements

Fasst Elemente der obersten Ebene in einer beschrifteten Box zusammen, ohne etwas zu verschieben.

"Stecke das alles in AWS", "gruppiere diese in eine VPC".

create_scene

Erstellt eine leere Leinwand.

Nur beim schrittweisen Aufbau eines großen Diagramms.

clear_scene

Leert eine Szene, behält ihre Leinwand/Thema/Überschrift.

"Weg damit, fangen wir von vorne an".

list_examples

Gibt funktionierende Beispielszenen und den Typenkatalog zurück.

Wenn man unsicher ist, wie man etwas ausdrückt – kopieren und anpassen.

Jede Toolbeschreibung sagt, was es tut, wann man es verwendet, wann nicht, und was jede Eigenschaft bedeutet, weil ein anderes Modell diese liest und selbstständig entscheidet. Schreibgeschützte Tools tragen readOnlyHint: true und destruktive destructiveHint: true, die Hosts verwenden, um zu entscheiden, was eine Bestätigung erfordert.


Szenen-Schema

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[];
}

Jedes Element hat id und type. IDs sind stabil und ermöglichen die konversationelle Bearbeitung.

Primitive – was der Renderer zeichnen kann

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

Semantische Elemente – was das Modell tatsächlich verwenden sollte

Typ

Zweck

node

Beschriftete Box. Zehn Formen (rounded, circle, diamond, hexagon, cylinder, cloud, stack, screen, pill, rect). Größe aus Beschriftung; Position aus Verbindungen.

connection

Verknüpfung nach ID: { from: "a", to: "b" }. Findet beide Ränder, fügt Pfeilspitze hinzu, bleibt korrekt, wenn sich eines der Enden bewegt. Routen: straight, curved, orthogonal.

group

Beschrifteter Container mit eigenem Layout. Begrenzungen wie "AWS", "VLAN 10".

axis

Ein Koordinatensystem und ein Datenrahmen.

point / scatter / cluster / plotLine

Markierungen und Linien in Daten-Koordinaten, wenn frame: "<axis id>" angegeben ist. cluster erzeugt einen deterministischen, gesäten Punktesatz.

label

Beschriftung, die nach ID an ein anderes Element angehängt werden kann und ihm folgt.

server database router switch computer cloud

Domänenvorgaben – ein node mit bereits ausgewählter richtiger Form und Glyphe.

Layout

layout: "auto" (Standard) erstellt einen geschichteten Fluss aus dem Verbindungsgraphen, falls vorhanden, ansonsten eine Zeile. Elemente mit explizitem x/y werden nie bewegt, sodass das Modell einen Knoten verschieben kann, ohne die anderen zu stören. direction steuert, in welche Richtung der Fluss wächst.

Datenrahmen

Ein axis deklariert eine Abbildung von Dateneinheiten zu Pixeln; alles mit frame: "<axis id>" wird in Datenkoordinaten platziert, mit y, das nach oben wächst, wie es sein sollte:

{ "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 }

Themen

Vier eingebaute Themen (dark, light, blueprint, paper), jedes eine vollständige Palette plus einen Schriftstapel, der keine externen Schriftarten benötigt. Elemente beziehen sich auf Token – primary, surface, muted, danger – anstatt auf hartcodierte Farben, sodass ein ganzes Diagramm umgestaltet wird, ohne seine Geometrie zu berühren. themeOverrides ändert jeden Token.


Lokales Ausführen

Als Bibliothek, ganz ohne 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" },
  ],
});

Als HTTP-Server

npm run dev:http

Route

POST /mcp

MCP Streamable HTTP-Endpunkt

GET /health

Health-Check

GET /scenes

Gespeicherte Szenen

GET /scenes/:id.svg

Eine gerenderte Szene

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

Der interaktive Viewer, eigenständig

Der Server ist zustandslos: Jede Anfrage erhält einen eigenen McpServer und Transport, und der Szenenspeicher ist der einzige gemeinsame Zustand. Das macht ihn sicher hinter einem Load Balancer oder auf einer serverlosen Plattform, wo zwei Schritte einer Konversation möglicherweise nicht denselben Prozess erreichen.

Als stdio-Server (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"]
    }
  }
}

Verbindung zu ChatGPT

ChatGPT erreicht entfernte MCP-Server über Streamable HTTP an einem öffentlichen HTTPS-Endpunkt, daher muss der Server vom Internet aus erreichbar sein. Zwei Möglichkeiten:

A. Schneller Test mit einem Tunnel

npm run dev:http                  # http://localhost:3333/mcp
npx localtunnel --port 3333       # or: ngrok http 3333, or cloudflared tunnel

B. Bereitstellung mit Docker auf einem VPS

docker-compose.yml startet zwei Container: den MCP-Server auf Port 4000 (nicht ins Internet veröffentlicht) und Caddy, der davor TLS terminiert und das Zertifikat automatisch erneuert.

Dieser VPS hat bereits andere Dienste auf den Ports 80 und 443, daher:

Port

Grund

HTTPS / MCP-Endpunkt

500

443 ist belegt

ACME HTTP-01-Challenge

90

80 ist belegt

MCP-Server

4000

nur intern, nie veröffentlicht

Der Haken: Let's Encrypt verbindet sich für HTTP-01 immer mit Port 80 – das ist durch RFC 8555 festgelegt und kann nicht konfiguriert werden – und TLS-ALPN ist ebenfalls auf 443 festgelegt. Caddy kann auf 90 hören, aber irgendetwas muss die Anfrage dorthin leiten. Also muss der Dienst, der bereits Port 80 belegt, den Challenge-Pfad an Caddy weiterleiten.

1. Wähle einen öffentlichen Hostnamen. ChatGPT benötigt HTTPS, und eine reine IP kann kein Zertifikat haben. Ohne eine eigene Domain verwende sslip.io – es löst <ip>.sslip.io ohne Registrierung zu dieser IP auf, und Let's Encrypt stellt Zertifikate dafür aus:

curl -4 ifconfig.me            # on the VPS -> e.g. 203.0.113.45
# hostname becomes: 203.0.113.45.sslip.io

2. Leite die ACME-Challenge vom Port-80-Server weiter. Identifiziere ihn zuerst:

sudo ss -lptn 'sport = :80'

nginx – innerhalb des server { listen 80; }-Blocks:

location /.well-known/acme-challenge/ {
    proxy_pass http://127.0.0.1:90;
    proxy_set_header Host $host;
}

Apache – innerhalb des <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 – innerhalb des Site-Blocks, der Port 80 bedient:

handle /.well-known/acme-challenge/* {
    reverse_proxy 127.0.0.1:90
}

Verwende einen echten Proxy, keine 301-Weiterleitung: Let's Encrypt folgt Weiterleitungen nur zu den Ports 80 und 443, daher würde eine Weiterleitung zu :90 fehlschlagen.

3. Konfiguriere und starte:

cp .env.example .env
# MCP_DOMAIN=203.0.113.45.sslip.io
docker compose up -d --build

Wenn der Port-80-Server in einem eigenen Container und nicht auf dem Host läuft, ist 127.0.0.1:90 von dort aus nicht erreichbar – setze MCP_HTTP_BIND=0.0.0.0 in .env und richte den Proxy auf die interne IP des VPS (oder platziere beide Container in einem Docker-Netzwerk).

4. Überprüfe (die erste Anfrage kann einige Sekunden dauern, während das Zertifikat ausgestellt wird):

curl https://$MCP_DOMAIN:500/health       # {"status":"ok",...}
docker compose logs caddy | grep -i "certificate obtained"

Die MCP-URL lautet dann https://<MCP_DOMAIN>:500/mcp und ist dauerhaft: restart: unless-stopped überlebt Neustarts, und die Zertifikate leben im caddy_data-Volume, sodass Erneuerungen über docker compose down/up hinweg bestehen bleiben. Nur docker compose down -v löscht sie. Behalte die Challenge-Weiterleitung bei – Erneuerungen alle ~60 Tage benötigen sie genauso wie die Erstausstellung.

PUBLIC_URL und ALLOWED_HOSTS werden von Compose aus MCP_DOMAIN und MCP_HTTPS_PORT abgeleitet. Beide müssen den Port enthalten: PUBLIC_URL, weil svgUrl-Links sonst auf 443 zeigen würden, und ALLOWED_HOSTS, weil das SDK den rohen Host-Header – der bei einem nicht standardmäßigen Port <domain>:500 lautet – als exakten String vergleicht.

Wenn du den Port-80-Server nicht anfassen kannst, ist HTTP-01 überhaupt nicht verfügbar. Die Optionen sind eine DNS-01-Challenge (benötigt eine echte Domain bei einem unterstützten DNS-Anbieter – sslip.io hat keine API) oder ein Cloudflare Tunnel, der überhaupt keine eingehenden Ports benötigt.

Ohne 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

Richte dann einen beliebigen Reverse-Proxy auf http://127.0.0.1:4000 aus. Der Container läuft als nicht-root node-Benutzer, hat einen /health-HEALTHCHECK und enthält nur Produktionsabhängigkeiten. Auch verwaltete Plattformen (Fly.io, Railway, Render, Cloud Run) funktionieren – sie injizieren ihren eigenen PORT, den der Server beachtet.

Dann in ChatGPT

  1. Aktiviere den Entwicklermodus. Er ist auf ChatGPT Business, Enterprise und Edu im Web verfügbar. Ein Admin aktiviert ihn unter Workspace-Einstellungen → Berechtigungen & Rollen → Verbundene Daten → Entwicklermodus / Benutzerdefinierte MCP-Connectors erstellen.

  2. Einstellungen → Connectors → Erstellen / Erweitert → Entwicklermodus → Benutzerdefinierten Connector hinzufügen.

  3. Fülle aus:

    • Name: Visual MCP

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

    • Authentifizierung: Keine Authentifizierung (dieser Server wird ohne Authentifizierung ausgeliefert – siehe Sicherheit)

  4. Speichern. ChatGPT ruft sofort tools/list auf; du solltest die zehn Tools aufgelistet sehen.

  5. Aktiviere in einem neuen Chat den Connector und bitte um ein Diagramm.

Der Server registriert auch eine MCP Apps UI-Ressource (ui://visual-mcp/scene.html, text/html;profile=mcp-app), die über _meta.ui.resourceUri und das ChatGPT-Alias _meta["openai/outputTemplate"] an die Rendering-Tools gebunden ist. Wo dies unterstützt wird, erscheint das Diagramm in einem interaktiven Frame mit Zoom, Schwenken, Anpassen, Kopieren und Exportieren; andernfalls geben die Tools das SVG weiterhin in structuredContent zurück, sodass der Server anmutig degradiert.


Beispiele

npm run examples rendert alle fünf nach examples/out/index.html, und list_examples serviert sie dem Modell.

1. Netzwerk – examples/out/network.svg

Domain-Voreinstellungen und automatisches Layout von links nach rechts. computer → switch → router → server, mit VLAN-Beschriftungen auf den Verbindungen. Keine Koordinaten irgendwo in der Quell-Szene.

2. LDA – examples/out/lda.svg

Ein axis-Datenrahmen, zwei gesäte cluster mit weichen Hüllen, eine gestrichelte Entscheidungsgrenze und die LDA-Richtung – alles in Datenkoordinaten, beide Linien auf den Plot beschnitten.

3. Regression – examples/out/regression.svg

Achse, eine scatter-Serie und eine angepasste plotLine mit extend: true, im hellen Thema.

4. Softwarearchitektur – examples/out/architecture.svg

React → REST API → { Redis, PostgreSQL }, mit den beiden Speichern innerhalb einer beschrifteten group ("Data layer"), in die die Verbindungen hineinführen.

5. Binärbaum – examples/out/tree.svg

Sieben kreisförmige Knoten und sechs Verbindungen; das geschichtete Layout mit Flussrichtung down erzeugt den Baum.

Auszuprobierende Prompts in 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].

Sicherheit

Das Bedrohungsmodell ist einfach: Alles, was der Server rendert, stammt von einem Sprachmodell, und diese Ausgabe kann selbst Text enthalten, den der Benutzer von woanders eingefügt hat. Daher wird niemals etwas, das vom Modell stammt, als Code behandelt.

  • Geschlossenes Schema. Nur die vierundzwanzig bekannten Elementtypen werden geparst. Farben müssen einer Hex-/RGB-/HSL-/Schlüsselwort-/Token-Grammatik entsprechen – url(javascript:…) wird bei der Validierung abgewiesen. Pfaddaten müssen SVG-Pfadbefehlen und Zahlen entsprechen, sonst nichts.

  • Positivliste für Ausgaben. Der Renderer kann nur Tags und Attribute aus zwei expliziten Listen in src/renderer/svgNode.ts ausgeben. Es gibt kein on*, kein href, kein style, kein class, kein <foreignObject>, kein <script> – das Modell kann sie nicht ausdrücken, und der Serialisierer würde sie ohnehin fallen lassen.

  • Escaping. Textinhalte und Attributwerte werden beim Verlassen XML-escaped.

  • Kein dangerouslySetInnerHTML, kein eval, kein new Function irgendwo im Projekt. Die React-Ansicht baut Elemente aus dem SvgNode-Baum; das ChatGPT-Widget parst das SVG und baut es Knoten für Knoten gegen dieselben Positivlisten neu auf, sodass selbst ein kompromittierter Server kein Skript in diesen Frame bringen kann.

  • Begrenzte Eingabe. Elementanzahlen, Zeichenkettenlängen, Punktanzahlen, Pfadlänge und gespeicherte Szenen sind alle begrenzt; Szenen werden nach dem FIFO-Prinzip verdrängt.

  • Keine Stack-Traces für das Modell. Jeder Handler ist umschlossen; alles Unerwartete wird zu INTERNAL_ERROR mit einer kurzen Nachricht.

Tests in tests/security.test.tsx bestätigen jeden dieser Punkte.

Nicht enthalten, absichtlich: Authentifizierung. Der Server gibt keine Geheimnisse preis und berührt kein externes System, aber eine öffentliche Bereitstellung ist ein öffentlicher Szenenspeicher. Setze ihn hinter die Authentifizierung deiner Plattform oder füge OAuth über die Auth-Helfer des SDKs hinzu, bevor du ihn anderen als dir selbst zugänglich machst. Setze ALLOWED_HOSTS, um den DNS-Rebinding-Schutz zu aktivieren, wenn er von einem Browser aus erreichbar ist.


Aktuelle Einschränkungen

  • Textmetriken werden geschätzt, nicht gemessen – es gibt keine Schriftart-Engine auf dem Server. Breiten liegen für die gebündelten serifenlosen Stapel innerhalb weniger Prozent, was für Boxen und Zeilenumbrüche ausreicht, aber eine ungewöhnliche Schriftart oder viel CJK wird etwas locker dimensioniert.

  • Die Layout-Engine ist bewusst klein. Längster-Pfad-Schichtung mit Rangzentrierung. Sie hat keine Kreuzungsminimierung und keine Überlappungsauflösung, daher erzeugt ein dichter Graph (etwa 25+ Knoten mit vielen Querverbindungen) Kreuzungen, die eine echte Engine vermeiden würde. Die Schnittstelle ist genau aus diesem Grund Dagre-förmig.

  • Baumlayouts sind nicht kindzentriert; ein Elternteil sitzt in der Mitte seines Ranges, nicht über dem Mittelpunkt seiner Kinder.

  • Das JSON-Schema von render_diagram ist etwa 50 KB (~12k Tokens), weil es das gesamte Elementvokabular lehrt. Die anderen neun Tools summieren sich auf ~5 KB. Das ist ein bewusster Kompromiss: Das Modell erhält feldweise Dokumentation und benötigt selten eine Reparaturrunde.

  • Innerhalb einer Gruppe mit automatischem Layout werden explizite x/y der Kinder ignoriert – das Layout gewinnt. Verwende layout: "manual" auf der Gruppe, um Kinder selbst zu positionieren.

  • Der Speicher ist im Arbeitsspeicher. Szenen überleben keinen Neustart, und bei mehreren Replikaten lebt eine Szene auf der Instanz, die sie erstellt hat. Das SceneStore-Interface existiert, sodass dies eine auszutauschende Klasse ist.

  • Statische Ausgabe. Noch keine Animation, kein 3D, kein Mathe-Ausdrucksparser – siehe unten.


Fahrplan

Als Nächstes

  • Persistenter SceneStore (zuerst SQLite) – das Interface ist bereits vorhanden.

  • Rückgängig/Wiederholen. Jede Mutation wird bereits als SceneMutation aufgezeichnet; es ist eine Frage der Speicherung der Umkehrung.

  • Dagre oder ELK hinter src/layout/flow.ts für dichte Graphen, mit der kleinen Engine als Standard.

  • Kindzentriertes Baumlayout.

Mathe – der axis-Datenrahmen ist die Grundlage; jeder davon ist ein Expander in src/semantic/, ohne Renderer-Änderung: functionPlot ({ "expression": "x^2", "domain": [-5, 5] }), vector, matrix, plane, distribution, projection, decisionBoundary und regressionLine als benannte Aliase von plotLine.

Diagrammvokabularneuron, neuralNetwork, decisionTree, sequenceDiagram, stateMachine, gantt, swimlane.

Animation{ "animation": { "type": "flow", "duration": 1200 } } auf einer Verbindung, ausgegeben als SVG SMIL oder CSS, sodass es deklarativ bleibt und keine Laufzeit benötigt. Pakete, die sich entlang einer Verbindung bewegen, eine Anfrage, die durch eine Pipeline reist, ein Algorithmus, der durch eine Struktur schreitet.

Andere Renderer – die Auflösungspipeline endet bereits in einem backend-agnostischen Baum. Eine Scene mit kind: "3d" und { "type": "sphere", "position": [0, 1, 0] } würde ein Three.js-Backend anstelle des SVG-Backends auswählen; ein Canvas-Backend würde sehr große Streudiagramme bedienen. Keine Änderung an der MCP-Schicht.


Lizenz

MIT

Install Server
F
license - not found
A
quality
C
maintenance

Maintenance

Maintainers
Response time
Release cycle
Releases (12mo)
Commit activity

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Related MCP Servers

View all related MCP servers

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

  • MCP (Model Context Protocol) server for Appwrite

View all MCP Connectors

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/daniel69zz/visual_draw_mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server