excalidraw-it
A diagram here is a scene in the operator's Excalidraw+ workspace. You write it through the MCP, and the operator's browser shows each write as soon as the server saves it. The operator edits the scene by hand only to correct something, and tells you when they do: read back only the elements they name, never the whole scene.
[!SETUP]:
- Call the format guide that matches the task once per session:
read_diagram_format,read_freeform_formatorread_presentation_format. The server instructions ask for it before the first write, although the server accepts writes without it, and it costs about 10k tokens. - Use
create_sceneor an existing scene id, and give the operator the linkhttps://app.excalidraw.com/s/<workspace>/<sceneId>. Both ids come in thecreate_sceneresponse.
[!TYPES]:
python3 <base_directory>/scripts/draw.py --help lists the diagram types that have a generator: a script that draws the whole diagram, title and note included, from its data in YAML. For one of them, draw.py <type> --help shows the YAML it takes, and it is all you need to read: write the data and run python3 <base_directory>/scripts/draw.py <type> data.yaml | python3 <base_directory>/scripts/write.py <sceneId>.
To change a generated diagram, change its data and draw it again: first send {"delete": [...]} with the addedElementIds of its write answer, and their labels go with them. Edit elements by hand only for what the data cannot say.
For a type with no generator, read its entry in ledger/types.yaml: which route draws it (create_diagram, Mermaid through mermaid-to-excalidraw, or by hand with edit_scene_content) and why the others are vetoed. A type with no entry: draw it by hand from the entry of the closest type, and propose an entry for it to the operator.
[!EVERY-WRITE]:
- Before you write JSON by hand, check the plan against the sins and use every piece from the ledger that fits; a generator already does both.
- Write through
python3 <base_directory>/scripts/write.py <sceneId>, with the payload on stdin: anaddarray, or an object withdelete,updateandadd. It callsedit_scene_contentover HTTP and prints one line; the full answer goes to$TMPDIR/excalidraw-it/. - After the write, run
python3 <base_directory>/scripts/lint.py <sceneId> --fix. It applies the fix of every finding and lints the scene again. - Once the lint is clean, call
take_screenshotbefore you show the result. Render per frame when the scene is larger than one screen, because a full-scene render is scaled down to 1920x1080 and small defects stop being visible.