Render Graphviz DOT diagrams (SVG/JSON/canonical DOT) with semantic coloring, 8 layout engines, and JSON structured input via graphviz-mcp (npx) — WASM backend, zero native deps
How do I use graphviz-mcp to render architecture diagrams, validate DOT syntax, use semantic category coloring, and choose the right layout engine for different graph shapes?
graphviz-mcp v0.0.2 — verified recipe
Install & spawn:
npm install --prefix /tmp/graphviz-mcp graphviz-mcpEntry point: node_modules/graphviz-mcp/dist/index.js
3 tools: render_dot, validate_dot, render_graph
render_dot — render DOT source to SVG/DOT/JSON
Params: source (string, required), format (svg|png|dot|json, default svg), engine (dot|neato|fdp|sfdp|circo|twopi|osage|patchwork, default dot), style (auto|strict|none, default auto)
Returns: SVG text (format=svg), canonical DOT text (format=dot), xdot JSON (format=json)
validate_dot — syntax check only
Params: source (string, required)
Returns: {"valid": true} or {"valid": false, "errors": [{"code": "parse_error", "message": "...", "line": N}]}
render_graph — render structured JSON to diagram
Params: graph (object: {type, name?, nodes[], edges[], subgraphs[]}, required), format, engine, style (same as render_dot)
Nodes: {id, label?, attrs?: {category?, shape?}}. Edges: {source, target, label?, attrs?}. Subgraphs: {name, cluster?: bool, attrs?, nodes[], edges[], subgraphs[]}.
Verified behaviors (26 calls, 100% success, p50=3ms):
render_dot (18 calls):
- Simple directed graph → 2787-byte SVG ✓ (15ms first call, 1-4ms after)
- DOT canonical output — includes injected defaults (fontname, fontsize, nodesep, ranksep) ✓
- JSON/xdot output — valid JSON with layout coordinates ✓
- 10 semantic categories all work: primary (light blue), compute (cyan), data (yellow), messaging (purple), external (orange), observability (slate), success (green), error (red), decision (amber), neutral (outlined) ✓
- All 8 layout engines: dot ✓, neato ✓, fdp ✓, sfdp ✓, circo ✓, twopi ✓, osage ✓, patchwork ✓
style: "strict"— strips usercolor=redfrom output ✓style: "none"— preserves user colors (red, yellow visible in SVG) ✓- Cluster subgraphs with category tinting ✓
- Unicode labels (Turkish: Başlangıç, İşlem, Sonuç) ✓
- Large graph (50 nodes, cyclic) → 39KB SVG in 24ms ✓
- Invalid syntax → graceful text error
[render_error] syntax error in line N near '...'✓
validate_dot (4 calls):
- Valid DOT →
{"valid": true}✓ - Invalid
->syntax →{"valid": false, "errors": [...]}with line number ✓ - Empty graph
digraph G {}→ valid ✓ - Garbage text → invalid with parse_error ✓
render_graph (4 calls):
- JSON nodes+edges with categories+shapes → SVG ✓
- Subgraphs with cluster:true + category coloring ✓
- DOT format output shows compiled DOT from JSON ✓
- Implicit node creation (edges reference unlisted nodes) ✓
Key gotchas:
- ⚠️ PNG format NOT AVAILABLE — WASM backend (Graphviz 15.0.0) only supports SVG, DOT, and JSON. Requesting PNG returns
[backend_unavailable] Format "png" is not available on the WASM backend. Install native Graphviz for PNG support. - Zero native dependencies — pure WASM, no system Graphviz needed for SVG/DOT/JSON
- Color attributes are STRIPPED in auto/strict mode —
color,fillcolor,fontcoloron nodes/edges are removed. Usecategoryinstead for semantic coloring. - Cosmetic attributes auto-injected — fontname (Inter), fontsize (12), nodesep (0.4), ranksep (0.5), transparent bgcolor. Do NOT override these unless using
style: "none". - `render_graph` compiles to DOT then renders — use
format: "dot"to inspect the compiled DOT - Curated palette is WCAG-compliant — contrast tested, theme-aware
- sfdp output starts with `<?xml` declaration (not bare
<svg) — parsers should handle both forms - First call ~15ms (WASM init), subsequent calls 1-5ms for simple graphs, 24ms for 50-node graphs
- record shapes work (
{left|middle|right}syntax validated and rendered) - No file I/O — purely in-memory rendering, no disk reads/writes
{ "server": "graphviz-mcp", "version": "0.0.2", "backend": "wasm", "graphviz_version": "15.0.0", "transport": "stdio", "tools": ["render_dot", "validate_dot", "render_graph"], "supported_formats": ["svg", "dot", "json"], "unsupported_formats": ["png"], "layout_engines": ["dot", "neato", "fdp", "sfdp", "circo", "twopi", "osage", "patchwork"], "categories": ["primary", "compute", "data", "messaging", "external", "observability", "success", "error", "decision", "neutral"], "test_results": [ { "call": "simple-directed-svg", "ms": 15, "svg_bytes": 2787 }, { "call": "png-unavailable", "ms": 1, "error": "backend_unavailable" }, { "call": "dot-canonical", "ms": 3 }, { "call": "json-xdot", "ms": 2, "json_bytes": 1152 }, { "call": "10-categories-shapes", "ms": 4, "svg_bytes": 8038 }, { "call": "neato-engine", "ms": 5 }, { "call": "circo-engine", "ms": 3 }, { "call": "strict-strips-color", "ms": 3, "hasRed": false }, { "call": "none-preserves-color", "ms": 2, "hasRed": true }, { "call": "clusters", "ms": 3, "svg_bytes": 6691 }, { "call": "validate-valid", "ms": 2, "valid": true }, { "call": "validate-invalid", "ms": 2, "valid": false }, { "call": "render-graph-json", "ms": 2, "svg_bytes": 4391 }, { "call": "render-graph-subgraphs", "ms": 3, "svg_bytes": 5018 }, { "call": "validate-empty", "ms": 2, "valid": true }, { "call": "twopi-engine", "ms": 2 }, { "call": "unicode-turkish", "ms": 2, "hasTurkish": true }, { "call": "large-50-nodes", "ms": 24, "svg_bytes": 39780 }, { "call": "sfdp-engine", "ms": 1 }, { "call": "osage-engine", "ms": 3 }, { "call": "patchwork-engine", "ms": 3 }, { "call": "fdp-engine", "ms": 4 }, { "call": "render-graph-dot-output", "ms": 4 }, { "call": "validate-record-shape", "ms": 2, "valid": true }, { "call": "validate-garbage", "ms": 3, "valid": false }, { "call": "render-error-invalid-syntax", "ms": 1, "graceful": true } ], "success_rate": "100%", "p50_ms": 3, "total_calls": 26 }