excalidraw-mcp

Use when creating hand-drawn style Excalidraw diagrams via the Excalidraw MCP at https://mcp.excalidraw.com/mcp. Use for flow diagrams, architecture diagrams, slide visuals, and any time a sketchy/hand-drawn diagram is needed as a PNG file.

9 stars

Best use case

excalidraw-mcp is best used when you need a repeatable AI agent workflow instead of a one-off prompt.

Use when creating hand-drawn style Excalidraw diagrams via the Excalidraw MCP at https://mcp.excalidraw.com/mcp. Use for flow diagrams, architecture diagrams, slide visuals, and any time a sketchy/hand-drawn diagram is needed as a PNG file.

Teams using excalidraw-mcp should expect a more consistent output, faster repeated execution, less prompt rewriting.

When to use this skill

  • You want a reusable workflow that can be run more than once with consistent structure.

When not to use this skill

  • You only need a quick one-off answer and do not need a reusable workflow.
  • You cannot install or maintain the underlying files, dependencies, or repository context.

Installation

Claude Code / Cursor / Codex

$curl -o ~/.claude/skills/excalidraw/SKILL.md --create-dirs "https://raw.githubusercontent.com/exiao/skills/main/design/excalidraw/SKILL.md"

Manual Installation

  1. Download SKILL.md from GitHub
  2. Place it in .claude/skills/excalidraw/SKILL.md inside your project
  3. Restart your AI agent — it will auto-discover the skill

How excalidraw-mcp Compares

Feature / Agentexcalidraw-mcpStandard Approach
Platform SupportNot specifiedLimited / Varies
Context Awareness High Baseline
Installation ComplexityUnknownN/A

Frequently Asked Questions

What does this skill do?

Use when creating hand-drawn style Excalidraw diagrams via the Excalidraw MCP at https://mcp.excalidraw.com/mcp. Use for flow diagrams, architecture diagrams, slide visuals, and any time a sketchy/hand-drawn diagram is needed as a PNG file.

Where can I find the source code?

You can find the source code on GitHub using the link provided at the top of the page.

SKILL.md Source

# Excalidraw MCP

Streams hand-drawn Excalidraw diagrams via MCP. Remote endpoint: `https://mcp.excalidraw.com/mcp`

## Connecting via mcporter

**Critical:** The `--server` flag auto-corrects to configured server names. Always use `--http-url` for the Excalidraw endpoint:

```bash
# List tools
mcporter list --http-url https://mcp.excalidraw.com/mcp

# Call a tool
mcporter call 'https://mcp.excalidraw.com/mcp.create_view' --args '{"elements": "[...]"}'
```

## Tools

| Tool | Purpose |
|------|---------|
| `read_me` | Returns element format reference. Call once per conversation. |
| `create_view` | Renders diagram in OpenClaw canvas panel (visible to user in chat). |
| `export_to_excalidraw` | Uploads to excalidraw.com, returns shareable URL you can screenshot. |
| `save_checkpoint` | Save user-edited state by ID. |
| `read_checkpoint` | Read checkpoint state for restore. |

## Getting a PNG File

`create_view` renders into a canvas panel — not a file. To get an embeddable image:

1. Use `export_to_excalidraw` → get shareable URL (e.g. `https://excalidraw.com/#json=...`)
2. Open URL in browser (`browser action=open profile=clawd`)
3. **Dismiss the "Load from link" dialog** — it always appears. Take a snapshot to get the button ref, then click "Replace my content"
4. Wait ~1.5s for diagram to render
5. Screenshot
6. Crop with ffmpeg: `ffmpeg -y -i input.jpg -vf 'crop=in_w:HEIGHT:0:0' -update 1 -frames:v 1 out.png`

```python
# Step 3 automation — snapshot then click
snapshot = browser(action="snapshot", targetId=tid)
# Find button "Replace my content" in snapshot, note its ref (e.g. e218)
browser(action="act", request={"kind":"click","ref":"e218"}, targetId=tid)
browser(action="act", request={"kind":"wait","timeMs":1500}, targetId=tid)
```

## Element Format

Elements are passed as a **JSON string** (double-encoded), not an array:

```python
import json, subprocess

elements = [...]  # list of element dicts
args = {"elements": json.dumps(elements)}  # elements MUST be a string

result = subprocess.run([
    "mcporter", "call",
    "https://mcp.excalidraw.com/mcp.create_view",
    "--args", json.dumps(args),
    "--output", "raw"
], capture_output=True, text=True)
```

## Element Reference

### Rectangle / Box + Bound Text (CORRECT for export_to_excalidraw)

The `label` shorthand only works in `create_view`. For `export_to_excalidraw`, use a separate bound text element with `containerId`:

```json
[
  {
    "type": "rectangle",
    "id": "r1",
    "x": 40, "y": 60, "width": 160, "height": 80,
    "angle": 0,
    "strokeColor": "#1e1e1e",
    "backgroundColor": "#e8f5e9",
    "fillStyle": "solid",
    "strokeWidth": 2, "strokeStyle": "solid",
    "roughness": 1, "opacity": 100,
    "roundness": {"type": 3}
  },
  {
    "type": "text",
    "id": "t1",
    "x": 40, "y": 60, "width": 160, "height": 80,
    "angle": 0,
    "strokeColor": "#1e1e1e",
    "backgroundColor": "transparent",
    "fillStyle": "solid",
    "strokeWidth": 1, "strokeStyle": "solid",
    "roughness": 1, "opacity": 100,
    "text": "My Label",
    "fontSize": 22,
    "fontFamily": 1,
    "textAlign": "center",
    "verticalAlign": "middle",
    "containerId": "r1",
    "originalText": "My Label"
  }
]
```

**fontFamily values:** 1=Virgil (hand-drawn) ← use this for the Excalidraw look, 2=Helvetica, 3=Cascadia (monospace)

Text element x/y/width/height must match the containing rectangle exactly.

### Arrow
```json
{
  "type": "arrow",
  "id": "a1",
  "x": 182, "y": 105,
  "width": 60, "height": 0,
  "angle": 0,
  "strokeColor": "#1e1e1e",
  "backgroundColor": "transparent",
  "fillStyle": "solid",
  "strokeWidth": 2,
  "strokeStyle": "solid",
  "roughness": 1,
  "opacity": 100,
  "points": [[0, 0], [60, 0]],
  "startArrowhead": null,
  "endArrowhead": "arrow"
}
```

### Text
```json
{
  "type": "text",
  "id": "t1",
  "x": 50, "y": 80,
  "width": 120, "height": 25,
  "text": "Hello",
  "fontSize": 20,
  "fontFamily": 1,
  "textAlign": "center",
  "verticalAlign": "middle",
  "strokeColor": "#1e1e1e"
}
```

### Export JSON wrapper
```python
excalidraw_json = json.dumps({
    "type": "excalidraw",
    "version": 2,
    "source": "https://excalidraw.com",
    "elements": elements,
    "appState": {"viewBackgroundColor": "#fffce8", "gridSize": None}
})
args = {"json": excalidraw_json}
```

## Color Palette (from read_me)

| Name | Hex | Use |
|------|-----|-----|
| Blue | `#4a9eed` | Primary actions |
| Amber | `#f59e0b` | Warnings, highlights |
| Green | `#22c55e` | Success |
| Red | `#ef4444` | Errors |
| Purple | `#8b5cf6` | Accents |

Background fills: `#e8f5e9` (green pastel), `#fff3e0` (orange pastel), `#ffd6d6` (red pastel), `#fffce8` (cream/default canvas)

## Layouts

### Left-to-right flow (4 boxes)
Spacing: box width ~155px, gap ~60px for arrows. Start x=20, y=60.
- Box 1: x=20
- Arrow: x=177 (box1.x + width + 2)
- Box 2: x=237 (arrow.x + 60)
- Arrow: x=394
- Box 3: x=454
- etc.

### Fan-in (3 sources → 1 center)
Use SVG `<path>` curves for the converging lines — Excalidraw arrows can't curve natively in the JSON format. Use `export_to_excalidraw` then screenshot.

## Common Mistakes

- **Passing elements as array** — `create_view` and `export_to_excalidraw` expect `elements` as a JSON *string*, not an array. Double-encode: `json.dumps(elements)`
- **Using `--server` flag** — it auto-corrects to a configured server. Use `--http-url https://mcp.excalidraw.com/mcp` instead
- **Expecting `create_view` to return an image** — it renders in the canvas panel only. Use `export_to_excalidraw` + screenshot for PNG files
- **Inline SVG marker ID conflicts** — when embedding multiple SVGs in one HTML doc, each `<marker id="...">` must be unique (ah-d1, ah-d2, ah-d3)

Related Skills

writer

9
from exiao/skills

Write content in Eric's voice — articles, blog posts, tweets, social media posts, marketing copy, newsletter drafts. Loads WRITING-STYLE.md and enforces kill phrases.

positioning-angles

9
from exiao/skills

Use when defining product positioning, choosing strategic angles, crafting value propositions, competitive positioning, product messaging, differentiation strategy, or go-to-market angles. Also use for 'how should I position my app', 'what angle should I use', 'painkiller vs vitamin', or 'market positioning'.

outline-generator

9
from exiao/skills

Use when generating outlines, article structures, content outlines, blog outlines, planning article sections, structuring posts, breaking down topics into sections, or organizing ideas for long-form content. Also use for 'outline this', 'structure this article', or 'plan the sections'.

last30days-open

9
from exiao/skills

Use only when the user explicitly asks for the open variant of last30days, including watchlists, briefings, and history queries. Sources: Reddit, X, YouTube, web.

last30days

9
from exiao/skills

Use when researching what happened in the last 30 days on a topic. Also triggered by 'last30'. Sources: Reddit, X, YouTube, web. Produces expert-level summary with copy-paste-ready prompts.

hooks

9
from exiao/skills

Use when generating hooks, headlines, titles, and scroll-stopping openers for content. Also use when analyzing viral posts, Reels, TikToks, YouTube Shorts, or successful social examples to extract reusable hook patterns and improve hook guidance.

evaluate-content

9
from exiao/skills

Use when judging content quality OR editing/improving existing copy: shareability, readability, voice, cuttability, angle, copy sweeps.

editor-in-chief

9
from exiao/skills

Use when a first draft is complete and all Phase 1 gates are done: topic selected (seo-research), title approved (hooks), outline approved (outline-generator), draft written (writer). Runs autonomous diagnosis-prescribe-rewrite loop before Substack.

copywriting

9
from exiao/skills

Write or improve marketing copy for any surface: pages, ads, app stores, landing pages, TikTok/Meta scripts, push notifications, UGC. Combines page copy frameworks with direct response principles.

content-strategy

9
from exiao/skills

Use when building content strategy: hooks, angles, and ideas from what's trending now. Covers organic and paid creative across TikTok, X, YouTube, Meta, LinkedIn.

content-pipeline

9
from exiao/skills

Orchestrator for the 3-article content pipeline — runs research phase, spawns parallel article sub-agents, creates Typefully drafts. Use when running the full content pipeline (usually via cron at 3am).

yt-dlp

9
from exiao/skills

Download audio/video from YouTube and other sites using yt-dlp. Use when the user asks to download music, songs, albums, podcasts, or video from YouTube or similar platforms. Triggers on 'download song', 'get mp3', 'yt-dlp', 'youtube download', 'rip audio'.