$npx -y skills add yctimlin/mcp_excalidraw --skill excalidraw-skillExcalidraw canvas toolkit for creating, editing, and refining diagrams on a live canvas. Use when an agent needs to (1) draw or lay out diagrams, (2) iteratively refine them by describing the scene and screenshotting its own work, (3) export/import .excalidraw files or PNG/SVG im
| 1 | # Excalidraw Skill |
| 2 | |
| 3 | ## Step 0: Pick an Interface |
| 4 | |
| 5 | Three interfaces drive the same live canvas. Pick the first one that applies: |
| 6 | |
| 7 | 1. **MCP tools** — if `excalidraw/*` tools (e.g. `batch_create_elements`) are in your tool list, prefer them: results land directly in your context, and screenshots come back as images without touching disk. |
| 8 | 2. **CLI** (default when no MCP tools are present): |
| 9 | ```bash |
| 10 | npx -y mcp-excalidraw-server <command> |
| 11 | ``` |
| 12 | No setup needed — any canvas-touching command **auto-starts the canvas server** on `http://127.0.0.1:3000` (first `npx` run downloads the package). If the CLI is installed globally (`npm i -g mcp-excalidraw-server`), the shorter alias `excalidraw-canvas <command>` works too. |
| 13 | 3. **REST API** (last resort, e.g. from application code): HTTP endpoints on `http://127.0.0.1:3000` — see `references/cheatsheet.md` for payloads. The server must already be running. |
| 14 | |
| 15 | The canvas URL comes from `EXPRESS_SERVER_URL` (default `http://127.0.0.1:3000`). Remind the user to open that URL in a browser — screenshots, image export, mermaid conversion, and viewport control need an open tab (CLI exits with code 4 when it's missing). |
| 16 | |
| 17 | ### CLI Quick Reference |
| 18 | |
| 19 | Results are JSON on stdout — except `describe` (plain text) and raw-content output when `--out` is omitted (`export` scene JSON, `screenshot --format svg`). Diagnostics on stderr. Exit codes: 0 ok, 1 error, 2 usage, 3 canvas unreachable, 4 browser tab required. |
| 20 | |
| 21 | | Task | Command | |
| 22 | |------|---------| |
| 23 | | Start / stop / inspect server | `start`, `stop`, `status` | |
| 24 | | Create elements (batch) | `add elements.json` or `echo '[...]' \| add` or `add --one '{...}'` | |
| 25 | | Multi-op patch in one call | `apply patch.json` — `{"create":[...],"update":[{"id":"a","set":{...}}],"delete":[...]}` | |
| 26 | | Read one / query many | `get <id>`, `query [--type t] [--bbox x0,y0,x1,y1] [--filter k=v] [--filter-json '{...}']` | |
| 27 | | Update / delete | `update <id> --set '{...}'`, `delete <id> [...]` | |
| 28 | | Understand the scene | `describe` (plain-text summary: ids, positions, labels, connections) | |
| 29 | | See the scene | `screenshot [--out f.png]` (PNG without `--out` → temp file path in JSON; SVG without `--out` → raw SVG) | |
| 30 | | Layout operations | `arrange align\|distribute\|group\|ungroup\|lock\|unlock\|duplicate --ids a,b,c [--to left\|horizontal\|...]` | |
| 31 | | Scene files | `export [--out scene.excalidraw]`, `import [scene.excalidraw|-] [--replace]` — a `.excalidraw.md` out path writes Obsidian's format (see File I/O) | |
| 32 | | Mermaid → canvas | `mermaid [diagram.mmd|-]` (or stdin) | |
| 33 | | Snapshots | `snapshot save\|list\|restore <name>` | |
| 34 | | Share link | `share` (encrypted upload → excalidraw.com URL) | |
| 35 | | Wipe canvas | `clear --yes` | |
| 36 | | Install / upgrade this skill | `install-skill --dir <skills-root>` (agent chooses project/global root) | |
| 37 | |
| 38 | ### Element Format (CLI and MCP) |
| 39 | |
| 40 | The CLI and MCP tools accept the same agent-friendly format and normalize it automatically: |
| 41 | |
| 42 | - **Labels**: put `"text": "My Label"` on any shape — converted to Excalidraw's bound-label format for you. |
| 43 | - **Arrow binding**: `"startElementId": "a"` / `"endElementId": "b"` — arrows auto-route to element edges. |
| 44 | - **fontFamily**: pass a string name (`"helvetica"`, `"cascadia"`, `"excalifont"`, ...) or string number `"1"`–`"8"`. |
| 45 | - **points**: both `[[x,y], ...]` tuples and `[{"x":..,"y":..}]` objects are accepted. |
| 46 | - **Patch updates**: in `apply`, update entries can use either direct fields (`{"id":"a","x":120}`) or a `set` object (`{"id":"a","set":{"x":120}}`). Do not mix both forms in one update entry. |
| 47 | |
| 48 | **Raw REST is stricter**: labels must be `"label": {"text": "..."}`, bindings must be `"start": {"id": "..."}` / `"end": {"id": "..."}`. Only worry about this when POSTing to the API directly. |
| 49 | |
| 50 | --- |
| 51 | |
| 52 | ## Coordinate System |
| 53 | |
| 54 | The canvas uses a 2D coordinate grid: **(0, 0) is the origin**, **x increases rightward**, **y increases downward**. Plan your layout before writing any JSON. |
| 55 | |
| 56 | **General spacing guidelines:** |
| 57 | - Vertical spacing between tiers: 80–120px (enough that arrows don't crowd labels) |
| 58 | - Horizontal spacing between siblings: 40–60px minimum; give labeled arrows 120px+ |
| 59 | - Shape width: `max(160, labelCharCount * 12)` to keep the label on one line |
| 60 | - Shape height: 60px single-line, 80px two-line labels |
| 61 | - Background/zone padding: 50px on all sides around contained elements |
| 62 | |
| 63 | * |