Skip to main content
Glama

drawspec

Built with claude-arsenal

drawspec MCP server – quality and maintenance score on Glama

Declarative diagram spec → clean, themeable SVG. Writable by an LLM with no coordinates to get wrong.

For anyone — person or language model — who puts diagrams in docs, specs and READMEs and is tired of arrows that miss their boxes:

pip install drawspec && drawspec render diagram.json -o diagram.svg

See every kind drawn in the gallery →

Status: it draws. All thirty-six kinds render — 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 and clock — and each of the eleven failure families below has a test asserting it cannot be expressed. The vocabulary opened from nine to thirteen on evidence: see docs/kinds-wanted.md, which sorts all 89 hand-drawn originals by the kind that would have to draw them. scatter is the fourteenth, added on a different kind of evidence: a real consumer's need for two continuous, ticked axes, which quadrant refuses. files is the fifteenth: a directory listing drawn from its paths, as pictures or as the tree-style text a code block wants. states is the sixteenth: a state machine, with its initial and final dots, labelled transitions, self-loops, and a refusal for any state nothing reaches. sequence is the seventeenth: messages between participants, in order, with activation bars inferred from each call and its reply. lanes is the eighteenth: a flow document whose actors are drawn as swimlanes, so every hand-off between parties is an arrow crossing a lane. entities is the nineteenth: ER models, database schemas and class diagrams as one kind — boxes of fields, relations that can land on one row, crow's feet or multiplicities at the ends, and inheritance with a hollow triangle. gantt is the twentieth: a plan, one row per task, with phases, milestones, finish-to-start dependencies and marks such as today — on a numbered axis in a unit you name, never dates. board is the twenty-first: cards sorted into columns — a kanban board with work-in-progress limits drawspec counts, a retrospective, or a story map whose releases are slices across the columns with a heavy cut under the first. fishbone is the twenty-second: one effect and the causes investigated, grouped by category on bones slanting into a spine — the root cause drawn heavy. venn is the twenty-third: two or three equal circles (four equal ellipses), each region's words named by the sets it is in and placed inside it — no coordinates, no sizes. journey is the twenty-fourth: one person's feeling, a named level per stage, drawn as a line through every stage over what they do there — no scores. treemap is the twenty-fifth: the parts of one known total, each a rectangle by its area with its label inside it — a few proportions stated, not explored. sankey is the twenty-sixth: where a known quantity goes, each flow a band as thick as its amount, columns derived from the flows — a few flows stated, not explored. numberline is the twenty-seventh: values and ranges on one scale, each end of a range in or out of it — an inequality, the pH scale, a spectrum; not when, which is timeline. barmodel is the twenty-eighth: a word problem as bars of parts in proportion, braced with their checked totals — the bar model, or tape diagram, of primary maths. waterfall is the twenty-ninth: how one total became another, step by step, every subtotal checked against the running sum — the bridge chart of a budget paper. pie is the thirtieth: the shares of one known total as labelled sectors — a few proportions stated, as on a treemap, with a donut as a theme's choice. mindmap is the thirty-first: a topic in the middle and its branches round it as peers, nested three deep and balanced on two sides — where a tree would rank them. argument is the thirty-second: a claim and the points for and against it, tagged and hatched by stance, co-premises under one bracket — the argument map of a law or critical-thinking course. familytree is the thirty-third: people, the couples they form and their children, a generation to a row and numbered for you (II-3), in the standard's symbols — the pedigree of a genetics clinic, and the family tree of a history lesson. meter is the thirty-fourth: one amount against its scale — a progress bar, a fundraising thermometer, a dial or a bullet graph, alone or a few to a panel, every band named in words. rag is the thirty-fifth: workstreams or risks rated red, amber or green — a highlight report, a risk register — each rating a word and a shape, never colour alone, with its trend written in words. clock is the thirty-sixth: analogue clock faces showing a time, for teaching it — the hour hand moving on with the minutes, the time in words under each face or hidden for a worksheet, and how long since an earlier time shaded round the face. A flow also draws a causal loop diagram: a sign on every arrow, delay marks, and named loops marked R or B, the type derived from the signs, never written.

The problem

Language models draw diagrams by emitting SVG with absolute x/y coordinates, and they fail at the same things every time: arrows with a head but no shaft, text that escapes its box, lines that stop short of the shape they point at, a different font size in every element.

These are not failures of judgement. In a corpus of 87 LLM-written diagrams reviewed by a human, 63 drew a complaint, and every one was about placement, not content — nobody disputed what the diagram said, only where things ended up. See docs/brief.md for the measured breakdown.

Placement failures cannot be reviewed away: a cheap mechanical check over finished SVG catches 17 of those 63 and misses 46. They have to be made impossible to express.

Related MCP server: diagram-mcp

The approach

The author writes what the diagram means — nodes, edges, labels, and a semantic role for each. Everything that can be got wrong without seeing the result is not in the format at all:

The author writes

The tool decides

Nodes, edges, labels

Coordinates, box sizes, canvas size

A semantic role per node

Font family and size, colour, stroke weight

Grouping and diagram type

Edge routing, anchor points, arrowheads

An optional size budget

Overlap resolution, label placement

Output is SVG that can be pasted inline into a Markdown document: colour inherited from the surrounding page, no global <style>, no ids that collide when two diagrams share a page, and no information carried by colour alone — so it survives dark mode and greyscale printing. For anywhere an SVG cannot go — a chat, a slide, an attachment — the same drawing comes out as a PNG on a light, dark or transparent background (pip install 'drawspec[png]', one self-contained wheel; no PDF, no JPEG). And for where no picture draws — a terminal, a commit message, a code block — twelve kinds, flows and state machines among them, come out as plain text, box-drawing or ASCII: 80 columns, 100 for swimlanes and sequences, or whatever --width you give.

A small sequence, sequence-terminal.json, as drawspec render docs/reference/sequence-terminal.json --format text draws it, and with --charset ascii added:

┌───────┐     ┏━━━━━━━━━━┓
│ Shell │     ┃ drawspec ┃
└───┬───┘     ┗━━━━┯━━━━━┛
    │ flow.json    │
    ├─────────────▶┃
    │ the drawing  ┃
    │◁┄┄┄┄┄┄┄┄┄┄┄┄┄┤
    │              │
+-------+     #==========#
| Shell |     # drawspec #
+---+---+     #====#=====#
    | flow.json    |
    +------------->#
    | the drawing  #
    |<<............+
    |              |

What it looks like

Three documents and the drawings they produced. Nothing was positioned by hand, and every one of these is a file in docs/reference/ that make gallery renders — so the picture is always the render of the spec beside it.

A flow

{
  "version": 1,
  "kind": "flow",
  "title": "Triaging a report",
  "nodes": [
    {"id": "arrive", "text": "A report arrives", "role": "start"},
    {"id": "repro", "text": "Does it reproduce?", "role": "decision"},
    {"id": "fix", "text": "Schedule a fix", "role": "step"},
    {"id": "ask", "text": "Ask for the document", "role": "terminal"}
  ],
  "edges": [
    {"from": "arrive", "to": "repro"},
    {"from": "repro", "to": "fix", "label": "yes"},
    {"from": "repro", "to": "ask", "label": "no"}
  ]
}

The role on each node is the only appearance decision in the file, and it is a semantic one: start and terminal came out as pills and decision as a diamond because the theme says those roles look like that. Change the theme and every diagram in your project changes with it.

A timeline

The thirty-six kinds are the part people do not expect, so here is one that is not a box-and-arrow diagram at all:

{
  "version": 1,
  "kind": "timeline",
  "title": "How the work went",
  "items": [
    {"text": "Brief", "note": "March"},
    {"text": "Specification", "note": "April"},
    {"text": "Plan", "note": "May"},
    {"text": "Foundation", "note": "June to July"},
    {"text": "Kinds", "note": "August"}
  ]
}

Above the line is what happened; below it is when. That is what note means on a timeline, and it is the only kind that draws one.

A chart

{
  "version": 1,
  "kind": "chart",
  "title": "Where the tickets went",
  "axes": {
    "horizontal": { "label": "Quarter", "min": 0, "max": 5 },
    "vertical": { "label": "Incidents" }
  },
  "series": [
    { "name": "Platform", "mark": "bar", "data": [[1, 34], [2, 41], [3, 28], [4, 22]] },
    { "name": "Service desk", "mark": "bar", "data": [[1, 52], [2, 47], [3, 55], [4, 38]] },
    { "name": "Still open", "mark": "line", "data": [[1, 12], [2, 18], [3, 9], [4, 6]] }
  ]
}

No colours are named anywhere in that file. The series are told apart by fill pattern as well as by hue, which is what lets the same drawing survive greyscale printing — a theme is checked against that invariant by drawspec theme check.

There are no coordinates in any of them, and none may be written. x, font_size, stroke and twenty-odd other fields are refused by name, with the JSON pointer of the place they were written. A document may size the whole canvas — width and height at the top level — and may not size one box in it.

Try it

pip install drawspec               # or drawspec[png] for PNG export

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  # on a drafting sheet, like the banner
drawspec render diagram.json --format text      # plain text, 80 columns; --charset ascii
drawspec validate diagram.json                # every violation, by JSON pointer
drawspec theme check default                  # including the greyscale invariant
drawspec theme from site.css -o house.toml    # a theme from your brand's CSS or design tokens
drawspec schema --out drawspec-v1.schema.json # for editor completion

Put "$schema": "https://nuncaeslupus.github.io/drawspec/schema/drawspec-v1.schema.json" at the top of a document and any editor with JSON Schema support completes the fields and flags a refused one while you type it, rather than after a render.

Every kind, drawn, on one page →

One document per kind lives in docs/reference, and make gallery renders every one of them into docs/gallery/index.html, which is what that link publishes. It is the fastest way to see the whole vocabulary, and the fastest way to see what a change did — looking at it is how four rendering defects were found that no gate had caught.

It is committed as well as published, so a diff shows what a change did to the pictures.

Every kind is drawn to the theme's one canvas width, and a diagram narrower than it is centred in it rather than cropped to its own ink. That is what makes the three above the same type size at the same column width — and it is the whole reason the width belongs to the theme. Set [canvas] width_mode = "ink" for a diagram meant to be sized on its own.

Every kind is also framed alike: [canvas] margin is the blank between the drawing and the edge of the figure, one number applied in one place. Before it existed each family invented its own, so a timeline above a flow chart bled to the edge beside the flow's quarter-inch of white.

What it is not

drawspec draws simple, declarative diagrams well, and it is the wrong tool for several jobs it might look like it does.

  • It is not a Graphviz replacement. Graphviz is a graph-layout engine with decades of work behind its layered and force-directed placement, and it will lay out a hundred-node graph that drawspec refuses. drawspec's layout exists to serve thirty-six named diagram shapes at the sizes a document reads at; past each kind's readability budget (fifteen boxes on a flow) it will tell you to split the diagram into an overview and details rather than produce something dense and unreadable, which is deliberate but is not what a graph tool does.

  • It is not charting software. chart has lines, bars, areas, stacks, point labels and a legend. It has no statistics, no interactivity, no dates, no log scales, no error bars, and no second axis. If you are exploring data, use a plotting library; if you are stating a small number you already know, this draws it in the same ink and at the same type size as every other diagram on the page, which a plotting library will not. treemap and sankey are held to the same line: ten parts and one level of groups at most, every part labelled inside itself; fourteen nodes in four columns, every flow balanced and thick enough to see — a few proportions and flows you already know, not a disk-usage explorer or a flow analyser.

  • It is not a drawing program. A document has no coordinates in it, on purpose — see The approach above. Anything whose whole point is where things are (a floor plan, a map, a picture of two overlapping Wi-Fi cells) has no declarative description, and drawspec should decline it rather than pretend.

  • It is not a rich-text system. Text carries **bold** and `code` spans and nothing else.

What it is good at is the long tail of ordinary explanatory diagrams — the kind that fills a set of course notes, a design document or a runbook — drawn consistently, reviewably, and without an author ever choosing a coordinate.

Documentation

The guide is the way in, and it is meant to be enough on its own — install, a first diagram, choosing a kind, embedding the output, validating in a build, and writing a theme. Under it sit three references, each generated from the code it describes, so none of them can quietly go out of date:

Page

What it answers

The document format

Every field a document may carry, by kind — and every field that is refused, with the reason

The command line

Every command, argument and exit code

The theme reference

Every key a theme may set, and what the default sets it to

make docs regenerates all three; a test fails if what is committed is not what the code would write, which is the same arrangement the published JSON Schema has.

If you are an agent, or pointing one here

AGENTS.md is the whole format on one page — whether to draw at all, which kind fits what the reader must understand, the readability budget, the roles, worked examples, and what each refusal is telling you to do. It is written to be read in a single pass rather than navigated, which is what the guide and the references are for. llms.txt is the index that points at it.

And if your client speaks MCP, pip install 'drawspec[mcp]' gives you drawspec-mcp: list_kinds, get_example, get_schema, validate_document and render_document as tools, so a refusal arrives as {"pointer": "/edges/0/to", "message": …} in the model's own loop instead of as prose on stderr it has to read. Same checks, same messages, no shell. See the guide.

For agents that load Agent Skills, copy skills/drawspec/ into your skills directory: a short pointer that installs drawspec, reads AGENTS.md, and validates before rendering.

It cannot drift either: a test asserts that every kind in the vocabulary is named in it, that every role the theme declares appears, and that every example in it is a document that still validates. An agent copying from a stale brief is the failure that file exists to prevent, so it is held to the same standard as the generated references.

Repository layout

Everything you would look in, and the two directories you can ignore.

Path

What

src/

The library and CLI — src/drawspec/ is the whole of the shipped package

docs/

Everything written. The four entries below are the ones worth knowing apart

docs/guide.md

Using drawspec in another project: install, first diagram, embedding, theming

docs/format.md, docs/cli.md, docs/theme.md

The three generated references. make docs writes them; tools/docs.py is the generator

docs/reference/

One document per kind — the examples, and what the gallery and the acceptance suite both render

docs/gallery/

The rendered gallery: make gallery writes it, and it is committed so a diff shows what a change did to the pictures

docs/brief.md

The originating brief: the problem, the measured failure taxonomy, the acceptance tests. theme-requirements.md, kinds-wanted.md and the plan-round-*.md files sit beside it — the evidence each round was decided on

schema/

The published JSON Schema, versioned and addressable. Copied into the wheel from here, so there is only ever one of it

corpus/

87 anonymized LLM-written diagrams and their measurements, as evidence

skills/

An Agent Skill that points an agent at drawspec — install, read AGENTS.md, validate, render. A test holds every command it names to the CLI

tests/

The suite. test_acceptance.py and test_failure_families.py are the two that encode the promises rather than the mechanics

tools/

Development scripts, not shipped: the docs generator, the gallery builder, and the checkers — collisions.py (is the word readable), clipping.py (is the word on the page) and frames.py (does an edge cut through a frame it has no end in)

status/

The origin documents: the specification this was built from, and its plan. Kept because docs/brief.md is the summary and this is the source, and because the session protocol reads status/plan.md

.claude/, claude-arsenal/

Development machinery for the agent workflow this is built with — task queue, session protocol, vendored skills. Nothing here affects the package; skip both if you are reading the project rather than working on it

Development

make sync     # install with dev extras
make lint     # ruff + strict mypy
make test     # pytest
make gallery  # render every reference document and look at it
make docs     # regenerate the format, CLI and theme references
make schema   # regenerate the published JSON Schema

Built with claude-arsenal, the spec-driven Claude Code workflow and gated task queue this repo is developed on — whose spec and design skills draw their diagrams as drawspec documents. Same author:

  • claude-arsenal — a Claude Code skill and plugin marketplace: specs before code, failing tests first, gates with numbers.

  • 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 tools
get_exampleGet an example document of one kindA
Read-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.

ParametersJSON Schema
NameRequiredDescriptionDefault
kindYesA diagram kind, exactly as `list_kinds` names it, e.g. `flow`.

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.9/5.0
Behavior5/5

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.

Conciseness5/5

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.

Completeness5/5

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.

Parameters4/5

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.

Purpose5/5

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.

Usage Guidelines5/5

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

ParametersJSON Schema
NameRequiredDescriptionDefault
kindYesA diagram kind, exactly as `list_kinds` names it, e.g. `flow`.

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.7/5.0
Behavior4/5

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.

Conciseness5/5

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.

Completeness5/5

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.

Parameters4/5

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.

Purpose5/5

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.

Usage Guidelines5/5

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

ParametersJSON Schema
NameRequiredDescriptionDefault

No parameters

Output Schema

ParametersJSON Schema
NameRequiredDescription
kindsYes

TDQS

A4.9/5.0
Behavior5/5

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.

Conciseness5/5

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.

Completeness5/5

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.

Parameters4/5

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.

Purpose5/5

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.

Usage Guidelines5/5

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

ParametersJSON Schema
NameRequiredDescriptionDefault
scaleNopng only: pixels per SVG unit (default 2).
themeNoA 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`.
widthNoOverride 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`).
formatNosvg (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.
heightNoOverride the document's height (ignored with format `text`).
charsetNotext only: box-drawing characters, or ASCII only (default unicode).
documentYesA 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).
backgroundNopng only: the page the drawing is on (default light).

TDQS

A4.8/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already cover read-only/idempotent/non-destructive, but the description adds substantial operational context: per-format return types, that PNG needs a server extra and is slowest, that text-only applies to certain kinds, that width becomes cells-per-line with text while height/theme are ignored, and a full JSON shape for refusal errors including RFC 6901 pointers. This goes well beyond the annotations.

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?

Purpose and the alternative are front-loaded in the first sentence, followed by dense but non-redundant detail. It is long for a single paragraph and could be split, though each clause carries distinct information.

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 no output schema, the description fully covers what comes back per format and how errors are surfaced, and it points to `list_kinds`/`get_example`/`get_schema` for document construction. Nothing an agent needs to call it correctly is missing.

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 coverage is 100%, so the baseline is 3, but the description adds cross-parameter interactions the schema does not state — that `height` and `theme` are ignored under `text`, and that `width` means cells-per-line in that mode. It doesn't add much beyond that, so it stops short of 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?

States a concrete verb+resource ("Draw the document") and immediately distinguishes itself from the sibling `validate_document`, noting the latter "returns just the verdict" while this renders output. An agent can tell the two apart 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.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly names the condition for use ("when it is ready to show") and the alternative to use in the other case (`validate_document` while still fixing errors). This is a clear when/when-not/alternative routing statement.

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

ParametersJSON Schema
NameRequiredDescriptionDefault
themeNoA 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`.
documentYesA 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

ParametersJSON Schema
NameRequiredDescription
okYes
kindYes
messageYes

TDQS

A4.8/5.0
Behavior5/5

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.

Conciseness4/5

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.

Completeness5/5

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.

Parameters4/5

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.

Purpose5/5

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.

Usage Guidelines5/5

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. 4 tool updatesv0.3.4
    • Changedget_example1 field changed
      • changedInput schema / properties / kind / description
        Previous value: -"A diagram kind, as `list_kinds` names them."New value: +"A diagram kind, exactly as `list_kinds` names it, e.g. `flow`."
    • Changedget_schema1 field changed
      • changedInput schema / properties / kind / description
        Previous value: -"A diagram kind, as `list_kinds` names them."New value: +"A diagram kind, exactly as `list_kinds` names it, e.g. `flow`."
    • Changedrender_document4 fields changed
      • changedInput schema / properties / document / description
        Previous 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)."
      • changedInput schema / properties / height / description
        Previous value: -"Override the document's height."New value: +"Override the document's height (ignored with format `text`)."
      • changedInput schema / properties / theme / description
        Previous 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`."
      • changedInput schema / properties / width / description
        Previous 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`)."
    • Changedvalidate_document2 fields changed
      • changedInput schema / properties / document / description
        Previous 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)."
      • changedInput schema / properties / theme / description
        Previous 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`."
  2. 8 tool updatesv0.3.3
    • Addedget_example
    • Addedget_schema
    • Removedkinds
    • Addedlist_kinds
    • Removedrender
    • Addedrender_document
    • Removedvalidate
    • Addedvalidate_document
  3. 3 tool updatesv0.1.0
    • First observedkinds
    • First observedrender
    • First observedvalidate

TDQS

A4.8/5.0

Scored across 5 tools

Disambiguation5/5

Each tool has a distinct role in a clear pipeline: list_kinds (vocabulary), get_example (sample), get_schema (full schema), validate_document (check), render_document (draw). The only potential overlap — get_example vs get_schema — is explicitly resolved in the descriptions ('use schema only for a field the example leaves out').

Naming Consistency5/5

All five tools follow a consistent verb_noun snake_case pattern: list_kinds, get_example, get_schema, validate_document, render_document. Verb choice is predictable and matches the action semantics.

Tool Count5/5

Five tools is well-scoped for a document-spec server: one discovery tool, two reference tools, one validator, one renderer. Each earns its place with no redundancy or conspicuous padding.

Completeness4/5

The surface covers the full authoring lifecycle: discover kinds, get an example, consult the schema, validate (including referential rules JSON Schema can't express), and render. Minor gap is there is no persistence/export or alternate-output helper, but rendering returns SVG/PNG/text so core needs are met.

Maintenance

ActivityActive
ResponsivenessResponsive

Related MCP Connectors

Related MCP Servers

  • A
    license
    A
    quality
    C
    maintenance
    Renders 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.
    4
    8 npm
    MIT
  • F
    license
    B
    quality
    B
    maintenance
    Enables creating and maintaining technical diagrams from .dot sources, with validation, rendering to SVG/PNG/PDF, and publishing into Markdown while automatically partitioning oversized graphs.
    6
    -
  • A
    license
    Not graded
    quality
    B
    maintenance
    Turns 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