drawspec
Drawspec's MCP server lets a model discover, validate and render coordinate-free diagrams from JSON documents, locally and deterministically.
List diagram kinds (
list_kinds): returns all 36kindvalues with a one-line purpose each, in a fixed order, so you can pick the right diagram type first.Get a starter document (
get_example): returns the smallest valid JSON document of one kind (under 1 KB), ready to edit and already passing validation.Inspect a kind's schema (
get_schema): returns the JSON Schema (draft 2020-12) for one kind — fields, types, limits, descriptions — cut from the full schema (3–13 KB).Validate a document (
validate_document): checks whether rendering would refuse the document, reporting every violation at once as RFC 6901 JSON pointers plus a message; optionally under a named or file-based theme.Render a document (
render_document): draws it as self-contained inline-safe SVG (default), base64 PNG, or plain text (unicode/ASCII) for supported kinds — with options for width/height, scale, background, theme, and SVG theme variants (e.g. dark).Errors are actionable: refusals come back as structured JSON (
ok,error,message,violationswithpointer) so the model can fix and retry.Safe and local: all tools are read-only, idempotent, closed-world, deterministic, do no I/O or writes, and answer in milliseconds (except PNG, which needs the
pngextra).
drawspec
Diagrams from a JSON description — no coordinates, so nothing to misplace. You say what the diagram means: the boxes, the arrows, the labels, and a role for each. drawspec decides every position, size, route and colour, and draws a clean SVG, PNG or plain-text diagram.
It is built for language models first. An LLM that writes SVG by hand gets the
content right and the placement wrong — arrows that miss their boxes, text that
spills out, lines that stop short. A drawspec document has no x, y or font
size to get wrong: those fields are refused, and every mistake comes back as an
error pointing at the exact field, so the model can fix it and try again.
pip install drawspec
drawspec render diagram.json -o diagram.svgBrowse the gallery: more than 30 kinds, drawn →
Example
You write this:
{
"$schema": "https://nuncaeslupus.github.io/drawspec/schema/drawspec-v1.schema.json",
"version": 1,
"kind": "flow",
"title": "The nightly import",
"description": "The vendor's export is fetched, checked and loaded into staging before it replaces the live table; a timeout and a deadlock are each retried in place.",
"nodes": [
{"id": "fetch", "text": "Fetch the vendor's export", "role": "start"},
{"id": "check", "text": "Do the row counts match?", "role": "decision"},
{"id": "load", "text": "Load into staging"},
{"id": "page", "text": "Page the data owner", "role": "terminal"},
{"id": "swap", "text": "Swap staging into live", "role": "terminal"}
],
"edges": [
{"from": "fetch", "to": "fetch", "label": "timed out: retry"},
{"from": "fetch", "to": "check"},
{"from": "check", "to": "load", "label": "yes"},
{"from": "check", "to": "page", "label": "no"},
{"from": "load", "to": "load", "label": "deadlock: retry"},
{"from": "load", "to": "swap"}
]
}drawspec draws this:
The same document as text, for a terminal, a commit message or a code block
(--format text --charset ascii):
*---------------------------*
| Fetch the vendor's export +-+ timed out: retry
| |<+
*-------------+-------------*
|
v
+--------------------------+
< Do the row counts match? >
++-------------------+-----+
| yes | no
+----+ |
v v
+-------------------+ .---------------------.
| Load into staging +-+ deadlock: retry ( Page the data owner )
| |<+ '---------------------'
+---------+---------+
|
v
.------------------------.
( Swap staging into live )
'------------------------'A sequence diagram works the same way — participants and messages in order; activation bars, arrows and spacing are worked out for you:
{
"version": 1,
"kind": "sequence",
"title": "Resolving a name the first time",
"caption": "Nothing is cached yet, so the resolver walks the hierarchy from the top.",
"participants": [
{"id": "laptop", "text": "Laptop"},
{"id": "resolver", "text": "Recursive resolver", "role": "emphasis"},
{"id": "root", "text": "Root server"},
{"id": "tld", "text": "`.org` server"},
{"id": "auth", "text": "Authoritative server"}
],
"messages": [
{"from": "laptop", "to": "resolver", "text": "where is `wiki.example.org`?"},
{"from": "resolver", "to": "root", "text": "where is `wiki.example.org`?"},
{"from": "root", "to": "resolver", "text": "ask the `.org` servers", "role": "weak"},
{"from": "resolver", "to": "tld", "text": "where is `wiki.example.org`?"},
{"from": "tld", "to": "resolver", "text": "ask `ns1.example.org`", "role": "weak"},
{"from": "resolver", "to": "auth", "text": "where is `wiki.example.org`?"},
{"from": "auth", "to": "resolver", "text": "`203.0.113.7`", "role": "weak"},
{"from": "resolver", "to": "resolver", "text": "cache it for the TTL", "note": "the next lookup stops here"},
{"from": "resolver", "to": "laptop", "text": "`203.0.113.7`", "role": "weak"}
]
}Related MCP server: diagram-mcp
More than 30 kinds of diagram
Flow charts, sequence diagrams, state machines, ER and class diagrams, Gantt charts, swimlanes, mind maps, timelines, Venn, Sankey, treemaps, pie and bar charts, kanban boards, fishbone, family trees, file trees, timing diagrams, network diagrams, tournament brackets, cladograms and more — one format for all of them. Click a drawing to open it full size, or a kind's name for its source — the code is all here.
The full list: flow · tree · cycle · stack · timeline · columns ·
matrix · pyramid · rings · funnel · chart · quadrant · scatter ·
curve · files · states · sequence · lanes · entities · gantt ·
board · fishbone · venn · journey · treemap · sankey · numberline ·
barmodel · waterfall · pie · mindmap · argument · familytree ·
meter · rag · clock · sunburst · timing · gitgraph · network · railroad · usecase ·
wardley · packet · pert · automaton · neuralnet. Each has an example in docs/reference,
and the gallery
(docs/gallery/index.html) draws every one.
Using it from an LLM
MCP server —
pip install 'drawspec[mcp]'addsdrawspec-mcp, with the toolslist_kinds,get_example,get_schema,validate_documentandrender_document. A mistake comes back as data the model can act on:{"pointer": "/edges/0/to", "message": …}. See the guide.AGENTS.md— the whole format on one page, written for a model to read in one pass: which kind fits, the size limits, the roles, worked examples, and what each error means.llms.txtindexes it.Agent Skill — copy
skills/drawspec/into your skills directory.JSON Schema — start a document with
"$schema": "https://nuncaeslupus.github.io/drawspec/schema/drawspec-v1.schema.json"for completion and errors as you type, in an editor or a structured-output call.
A typical loop: the model picks a kind, writes the JSON, calls
validate_document, fixes what is reported, and renders.
What you get
You write | drawspec decides |
Nodes, edges, labels | Coordinates, box sizes, canvas size |
A semantic role per node ( | Shape, font, colour, stroke weight |
The kind of diagram | Layout, edge routing, arrowheads, label placement |
SVG that pastes inline into Markdown or HTML: it takes the page's text colour, carries no global styles or colliding ids, and never relies on colour alone, so it works in dark mode and in greyscale print.
--variant dark=<theme>adds a dark version that followsprefers-color-scheme.PNG on a light, dark or transparent background (
pip install 'drawspec[png]').Plain text (box-drawing or ASCII) for many kinds, including flows, state machines, sequences, Gantt charts, syntax diagrams, file trees and packet headers (as an RFC draws them).
Themes: change one TOML file and every diagram in a project follows.
drawspec theme from site.cssbuilds one from your CSS or design tokens.Readable by design: each kind has a size budget; past it, drawspec asks you to split the diagram rather than draw a dense one.
Command line
drawspec render diagram.json -o diagram.svg # or to stdout
drawspec render diagram.json -o diagram.png # needs drawspec[png]; --background dark, --scale 3
drawspec render diagram.json --theme blueprint -o sheet.png
drawspec render diagram.json --format text # 80 columns; --charset ascii
drawspec validate diagram.json # every error, by JSON pointer
drawspec theme from site.css -o house.toml # a theme from your CSS
drawspec schema --out drawspec-v1.schema.json # for editor completionWhat it is not
Not a graph-layout engine. For a hundred-node graph, use Graphviz.
Not a plotting library.
chartstates a few numbers you already know; it has no statistics, dates, log scales or interactivity.Not a drawing program. Anything whose point is where things are — a floor plan, a map — has no place in a format without coordinates.
Documentation
Page | What it covers |
Install, a first diagram, choosing a kind, embedding, validating in CI, themes | |
Every field of every kind, and every refused field with the reason | |
Every command, option and exit code | |
Every theme key and its default | |
The format on one page, for language models |
The references are generated from the code (make docs), and a test fails if they
drift.
Development
make sync # install with dev extras
make lint # ruff + strict mypy
make test # pytest
make gallery # render every reference document into docs/gallery
make readme # redraw the README's example images in docs/readme
make docs # regenerate the format, CLI and theme references
make schema # regenerate the published JSON SchemaThe library is in src/drawspec/, the examples in docs/reference/, the tests in
tests/. docs/brief.md and corpus/ hold the original design brief and the
evidence it was based on. .claude/ and claude-arsenal/ are development tooling
and do not affect the package.
Related projects
Built with claude-arsenal, a spec-driven Claude Code workflow whose skills draw their diagrams with drawspec. By the same author:
integral-job-search — a candidate-centred job search run as a conversation in Claude Code.
literary-critic — lets an LLM index, analyse and advise on a full novel manuscript the way a professional editor would.
doc-control — a deterministic tool for AI agents to edit Word documents as tracked changes (in development).
License
MIT — see LICENSE.
Available Tools
5 toolsget_exampleGet an example document of one kindARead-onlyIdempotent
Get the smallest valid document of one kind, as JSON ready to edit: the fastest way to the right field names. kind is one exact value from list_kinds, e.g. flow. Use it after list_kinds has settled the kind, and get_schema only for a field the example leaves out. Static and local: the document drawspec example <kind> prints, under 1 KB, identical every call; it passes validate_document as is. An unknown kind is an error result naming list_kinds.
| Name | Required | Description | Default |
|---|---|---|---|
| kind | Yes | A diagram kind, exactly as `list_kinds` names it, e.g. `flow`. |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already cover read-only, idempotent, non-destructive, closed-world, but the description adds substantive traits beyond them: static/local behavior, sub-1KB size, byte-identical output on every call, guaranteed validity against `validate_document`, and the failure mode where an unknown kind returns an error naming `list_kinds`.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with the core purpose, then the routing rule, then the behavioral guarantees and error case. Every clause carries distinct information; nothing is restated from the schema or annotations.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With an output schema present, return values need no explanation, and the description covers the remaining gaps an agent needs: where the valid input values come from, when to prefer this tool over get_schema, determinism, and unknown-kind error handling.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100% and the enum documents all 36 values, so the baseline is 3. The description still adds meaning beyond the schema by tying the value to `list_kinds` as the authoritative source and stating that an unknown kind is an error result rather than a silent fallback.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('Get the smallest valid document of one kind, as JSON ready to edit') and immediately frames its value ('the fastest way to the right field names'). It is clearly distinguishable from get_schema, list_kinds, and render_document without opening any schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicit routing: 'Use it after `list_kinds` has settled the kind, and `get_schema` only for a field the example leaves out.' It also names the precondition that `kind` must be an exact value from `list_kinds`, so the agent knows the call order across siblings.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_schemaGet the JSON Schema for one kindARead-onlyIdempotent
Get the JSON Schema (draft 2020-12) for one kind's documents: every field it accepts, with types, limits and descriptions, plus only the shared $defs it refers to. kind is one value from list_kinds, e.g. sequence. Use it when get_example does not show a field you need. Local and deterministic: 3 to 13 KB per kind, cut from the ~150 KB published schema. Referential rules (unique ids, edges that land on real nodes) are beyond JSON Schema — validate_document checks those.
| Name | Required | Description | Default |
|---|---|---|---|
| kind | Yes | A diagram kind, exactly as `list_kinds` names it, e.g. `flow`. |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint, idempotentHint, non-destructive and closed-world, so the safety profile is covered. The description adds genuinely new context: local and deterministic execution, and a concrete payload size range (3–13 KB per kind versus the ~150 KB published schema), which helps an agent budget context. It omits nothing material, though it does not describe the response envelope itself.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with the operation and its scope, then routing guidance, then cost and limits. Three dense sentences with no filler; each clause carries distinct operational information.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
An output schema exists, so return values need no prose. The description covers everything else an agent needs: what is returned, what is deliberately excluded (referential rules), cost, determinism, and which sibling to consult for the parameter value.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100% and the enum already enumerates every valid value, so the baseline is 3. The description adds a useful cross-reference — that values must match `list_kinds` naming, with `sequence` as a worked example — which tells the agent how to discover valid input rather than guessing.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource: returns the JSON Schema (draft 2020-12) for one kind's documents, enumerating fields, types, limits and descriptions. It also scopes the payload precisely ('plus only the shared `$defs` it refers to'), which distinguishes it from both `get_example` and the full published schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives explicit selection criteria: use it when `get_example` does not show a field you need, with `kind` drawn from `list_kinds`. It also names the boundary condition — referential rules belong to `validate_document`, not here — so the agent knows when this tool is the wrong choice.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_kindsList the diagram kindsARead-onlyIdempotent
List the vocabulary: all 36 values kind may take, each with a one-line purpose. Call it first, before choosing a kind — several kinds are not box-and-arrow diagrams and are easy to miss — then get_example for a starting document. No arguments. Static and local: no I/O, about 4 KB, the same answer every call, in a fixed order that keeps related kinds (graphs, grids, shapes, charts) adjacent.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
Output Schema
| Name | Required | Description |
|---|---|---|
| kinds | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare read-only, idempotent, closed-world, non-destructive, but the description adds traits the annotations cannot express: no arguments, static and local with no I/O, ~4 KB payload, identical answer every call, and a fixed ordering that groups related kinds. This is meaningful operational context for an agent sizing up the call.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three tight sentences, front-loaded with what the tool returns and immediately followed by the ordering instruction. The parenthetical grouping detail and size estimate are compact and each sentence earns its place.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
An output schema exists, so return-structure explanation is unnecessary; the description still characterizes the payload (36 values, one-line purposes). For a zero-argument, deterministic lookup tool, an agent has everything needed to call it correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Zero parameters, so the baseline is 4. The description confirms 'No arguments,' which is a small but useful reinforcement of the empty schema rather than redundant filler.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource: it enumerates the complete set of 36 `kind` values with a one-line purpose each. It is clearly distinguishable from siblings like get_example, which it names as the follow-up step rather than a competitor.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly prescribes when to call it ('Call it first, before choosing a kind') and why ('several kinds are not box-and-arrow diagrams and are easy to miss'), then names the next tool in the workflow ('then `get_example` for a starting document'). No inference required.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
render_documentRender a drawspec document to SVG, PNG or textARead-onlyIdempotent
Draw the document. Use it when it is ready to show; while still fixing errors, validate_document returns just the verdict. Returns one content block by format: svg (default) self-contained SVG text, safe inline in Markdown; png a base64 image/png block (needs the server's drawspec[png] extra, slowest); text plain text for a code block, only for kinds with a text form. width/height override the document's size; with text, width is cells per line and height and theme are ignored. Local, deterministic, writes nothing. Refusals are error results with JSON text {"ok": false, "error": "DocumentError", "message": "...", "violations": [{"pointer": "/edges/0/to", "message": "..."}]}: error is e.g. DocumentError or FitError, pointer is RFC 6901 (empty for the whole document), and violations is never empty.
| Name | Required | Description | Default |
|---|---|---|---|
| scale | No | png only: pixels per SVG unit (default 2). | |
| theme | No | A bundled theme name (accent, blueprint, compartment, default, wheel) or a path to a `.toml` theme file on the server's disk, e.g. `blueprint`. Omit for `default`. | |
| width | No | Override the document's width, e.g. `640`. With format `text`, the most terminal cells a line may take (default 80, or 100 for `lanes` and `sequence`). | |
| format | No | svg (default), png, or text: the diagram as plain text for a code block or a terminal, for the kinds files, tree, sequence, gantt, timeline, board, stack, rag, meter, flow, states, lanes. | |
| height | No | Override the document's height (ignored with format `text`). | |
| charset | No | text only: box-drawing characters, or ASCII only (default unicode). | |
| document | Yes | A drawspec document as a JSON object (not a string or a path), e.g. `{"version": 1, "kind": "flow", "nodes": [...], "edges": [...]}`. `list_kinds` names the 36 values `kind` may take, `get_example` returns the smallest valid document of one, and `get_schema` returns its fields (the whole format is published at https://nuncaeslupus.github.io/drawspec/schema/drawspec-v1.schema.json). | |
| variants | No | svg only: more themes for the same drawing, by the name a page selects each with, e.g. `{"dark": "./dark.toml"}`. The SVG switches on an ancestor's `data-theme`, and `dark` also follows `prefers-color-scheme`; `theme` is the base and answers to `light`. A variant may change colours only. | |
| background | No | png only: the page the drawing is on (default light). |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already cover read-only/idempotent/no-write, and the description still adds determinism, a performance note for png, a required server extra (`drawspec[png]`), and the exact refusal envelope with `DocumentError`/`FitError`, RFC 6901 pointers, and non-empty `violations`. That is substantive behavior beyond the structured fields.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with the decision to render versus validate, then returns, then parameter effects, then errors — a sensible order with no filler. Some sentences are long semicolon-chained compounds, but every clause carries operative information.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Nine parameters, a nested document object, and no output schema — yet the description still explains the return block per format and the JSON error shape. An agent has everything needed to call it and interpret both success and refusal.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is 3; the description earns above that by explaining cross-parameter interactions (with `text`, `width` is cells per line while `height` and `theme` are ignored) and by describing per-`format` return shapes. A fair amount still repeats schema text, which keeps it from a 5.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('Draw the document') and the title adds the concrete outputs (SVG, PNG, text). It explicitly routes away from the sibling `validate_document`, so an agent can distinguish it without opening any schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives a clear trigger ('when it is ready to show') and names the alternative for the opposite case ('while still fixing errors, `validate_document` returns just the verdict'). It also steers format choice with per-format guidance (svg default/safe inline, png slowest, text only for kinds with a text form).
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
validate_documentValidate a drawspec documentARead-onlyIdempotent
Check whether render_document would refuse this document, and exactly where. It renders with the same theme and discards the drawing, so it passes only what draws; every violation comes back at once, so one pass is one edit. Call it while writing or editing; skip it when about to render anyway, since render_document refuses in the same shape. Local, deterministic, milliseconds. Refusals are error results with JSON text {"ok": false, "error": "DocumentError", "message": "...", "violations": [{"pointer": "/edges/0/to", "message": "..."}]}: error is e.g. DocumentError or FitError, pointer is RFC 6901 (empty for the whole document), and violations is never empty.
| Name | Required | Description | Default |
|---|---|---|---|
| theme | No | A bundled theme name (accent, blueprint, compartment, default, wheel) or a path to a `.toml` theme file on the server's disk, e.g. `blueprint`. Omit for `default`. | |
| document | Yes | A drawspec document as a JSON object (not a string or a path), e.g. `{"version": 1, "kind": "flow", "nodes": [...], "edges": [...]}`. `list_kinds` names the 36 values `kind` may take, `get_example` returns the smallest valid document of one, and `get_schema` returns its fields (the whole format is published at https://nuncaeslupus.github.io/drawspec/schema/drawspec-v1.schema.json). |
Output Schema
| Name | Required | Description |
|---|---|---|
| ok | Yes | |
| kind | Yes | |
| message | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already establish read-only, idempotent, non-destructive, local behavior, and the description adds real substance on top: it renders with the same `theme` and discards the drawing, all violations return at once (one pass = one edit), and it is local, deterministic, and millisecond-scale. It even discloses the exact error payload shape and semantics of `error`, `pointer`, and `violations`.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Information-dense and front-loaded, leading with purpose, then when/when-not, then cost, then error shape. A few clauses ('it passes only what draws', 'one pass is one edit') are terse enough to require a re-read, but nothing is filler.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Even though an output schema exists, the description still explains the refusal shape and the meaning of each error field, and covers cost, determinism, and exact validation semantics. Nothing an agent needs to invoke it correctly or interpret a failure is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is 3, but the description adds meaning the schema does not: `theme` is applied the same way `render_document` applies it, which tells the agent why the theme parameter exists here. No additional syntax for `document` is offered beyond what the schema already says.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a precise verb and resource and goes further by defining the semantics: it checks whether `render_document` would refuse the document and pinpoints where. The distinction from the sibling `render_document` is explicit, so an agent can route between them without opening either schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives both directions: call it while writing or editing, and skip it when about to render anyway because `render_document` refuses in the same shape. The alternative and the condition that selects it are named outright.
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 tool update
v0.4.3- Changed
render_document1 field changed- added
Input schema / properties / variantsAdded value: +{ + "additionalProperties": { + "type": "string" + }, + "description": "svg only: more themes for the same drawing, by the name a page selects each with, e.g. `{\"dark\": \"./dark.toml\"}`. The SVG switches on an ancestor's `data-theme`, and `dark` also follows `prefers-color-scheme`; `theme` is the base and answers to `light`. A variant may change colours only.", + "type": "object" +}
4 tool updates
v0.3.4- Changed
get_example1 field changed- changed
Input schema / properties / kind / descriptionPrevious value: -"A diagram kind, as `list_kinds` names them."New value: +"A diagram kind, exactly as `list_kinds` names it, e.g. `flow`."
- Changed
get_schema1 field changed- changed
Input schema / properties / kind / descriptionPrevious value: -"A diagram kind, as `list_kinds` names them."New value: +"A diagram kind, exactly as `list_kinds` names it, e.g. `flow`."
- Changed
render_document4 fields changed- changed
Input schema / properties / document / descriptionPrevious value: -"A drawspec document, as JSON. `list_kinds` names the 36 values `kind` may take, `get_example` returns the smallest valid document of one, and `get_schema` returns its fields (the whole format is published at https://nuncaeslupus.github.io/drawspec/schema/drawspec-v1.schema.json)."New value: +"A drawspec document as a JSON object (not a string or a path), e.g. `{\"version\": 1, \"kind\": \"flow\", \"nodes\": [...], \"edges\": [...]}`. `list_kinds` names the 36 values `kind` may take, `get_example` returns the smallest valid document of one, and `get_schema` returns its fields (the whole format is published at https://nuncaeslupus.github.io/drawspec/schema/drawspec-v1.schema.json)." - changed
Input schema / properties / height / descriptionPrevious value: -"Override the document's height."New value: +"Override the document's height (ignored with format `text`)." - changed
Input schema / properties / theme / descriptionPrevious value: -"A bundled theme name or a path to a theme file. Omit for the default."New value: +"A bundled theme name (accent, blueprint, compartment, default, wheel) or a path to a `.toml` theme file on the server's disk, e.g. `blueprint`. Omit for `default`." - changed
Input schema / properties / width / descriptionPrevious value: -"Override the document's width. With format `text`, the most terminal cells a line may take (default 80, or 100 for `lanes` and `sequence`)."New value: +"Override the document's width, e.g. `640`. With format `text`, the most terminal cells a line may take (default 80, or 100 for `lanes` and `sequence`)."
- Changed
validate_document2 fields changed- changed
Input schema / properties / document / descriptionPrevious value: -"A drawspec document, as JSON. `list_kinds` names the 36 values `kind` may take, `get_example` returns the smallest valid document of one, and `get_schema` returns its fields (the whole format is published at https://nuncaeslupus.github.io/drawspec/schema/drawspec-v1.schema.json)."New value: +"A drawspec document as a JSON object (not a string or a path), e.g. `{\"version\": 1, \"kind\": \"flow\", \"nodes\": [...], \"edges\": [...]}`. `list_kinds` names the 36 values `kind` may take, `get_example` returns the smallest valid document of one, and `get_schema` returns its fields (the whole format is published at https://nuncaeslupus.github.io/drawspec/schema/drawspec-v1.schema.json)." - changed
Input schema / properties / theme / descriptionPrevious value: -"A bundled theme name or a path to a theme file. Omit for the default."New value: +"A bundled theme name (accent, blueprint, compartment, default, wheel) or a path to a `.toml` theme file on the server's disk, e.g. `blueprint`. Omit for `default`."
8 tool updates
v0.3.3- Added
get_example - Added
get_schema - Removed
kinds - Added
list_kinds - Removed
render - Added
render_document - Removed
validate - Added
validate_document
3 tool updates
v0.1.0- First observed
kinds - First observed
render - First observed
validate
TDQS
Scored across 5 tools
Each tool occupies a distinct stage of the authoring pipeline: discover kinds, fetch an example, fetch a schema, validate, render. list_kinds/get_example/get_schema are clearly separated by output (vocabulary vs. document vs. schema), and validate vs. render is explicitly differentiated by verdict-only vs. drawing. No two tools could reasonably be confused.
All five names follow a strict verb_noun snake_case pattern (list_kinds, get_example, get_schema, validate_document, render_document). Verbs are meaningful and non-redundant, and the resource nouns map cleanly onto the domain objects.
Five tools is well-scoped for a document-rendering server, with each tool earning its place as one step in a discover→example→schema→validate→render flow. Nothing is redundant and nothing feels artificially split.
The surface covers the full authoring lifecycle from kind discovery through validation and rendering, with outputs in multiple formats. Minor gaps: no way to persist/export rendered output to a file or to enumerate theme options, though returning content blocks and accepting a theme argument largely mitigates this.
Maintenance
Related MCP Connectors
Render, validate, encode/decode PlantUML diagram-as-code; 22 diagram types. Free, no auth.
Create, validate, edit, export (markdown/svg/png/mermaid), and search JSON Canvas files.
Immutable agent diagrams: create, edit, validate and render versioned diagrams over MCP.
Render, verify, describe, and safely edit Mermaid diagrams through MCP.
Related MCP Servers
- AlicenseNot gradedqualityDmaintenanceEnables creating and iterating on animated SVG diagrams from text input, photos of sketches, and YAML specifications, with support for shapes, connections, SMIL animations, and file output.1MIT
- AlicenseAqualityCmaintenanceRenders diagram-as-code to PNG files on your disk, and also provides text-art previews and syntax validation, all locally without a browser or network.49 npmMIT
- AlicenseNot gradedqualityBmaintenanceTurns structured JSON content into resolved, audited infographic plans and renders them as SVG or executable Figma Plugin API scripts, exposing theme packs, grammars, planning, auditing and export tools over stdio. Also lets hosts inspect theme provenance and layout decisions so different tools produce identical frames.MIT
- AlicenseAqualityAmaintenanceEnables converting ASCII and Unicode text diagrams into crisp, animated SVG with 1:1 verification, including repair of misaligned diagrams.2MIT