excalidrop
v0.2.15
Published
Remote GitHub-backed Excalidraw canvas + MCP server — no local server, one-command setup
Maintainers
Readme
Excalidraw MCP Server & Agent Skill
Run a live Excalidraw canvas and control it from AI agents. This repo provides:
- MCP Server: Connect via Model Context Protocol (Claude Desktop, Cursor, Codex CLI, etc.)
- Agent Skill: Portable skill for Claude Code, Codex CLI, and other skill-enabled agents
Keywords: Excalidraw agent skill, Excalidraw MCP server, AI diagramming, Claude Code skill, Codex CLI skill, Claude Desktop MCP, Cursor MCP, Mermaid to Excalidraw.
Demo

To make iframes work use Ignore X-Frames extension chrome://extensions/?id=gleekbfjekiniecknbkamfmkohkpodhe
Table of Contents
- Demo
- What It Is
- How We Differ from the Official Excalidraw MCP
- What's New
- Quick Start (Local)
- Configure MCP Clients
- Agent Skill (Optional)
- MCP Tools (26 Total)
- Testing
- Troubleshooting
- Known Issues / TODO
- Development
What It Is
Remote-only: GitHub is the canvas. No local server, no ports.
- Viewer: static Excalidraw app on GitHub Pages (public repos) or Cloudflare Pages (private repos), reads
canvas.excalidrawfrom theexcalidropbranch - MCP server: remote-only stdio (
npx --prefix ~/.excalidrop-npx -y excalidrop@latest mcp --repo owner/repo— the--prefixkeeps npx from resolving a same-named local checkout instead of the registry); draws via GitHub Contents API, screenshots via shared relay when a viewer tab is open
How We Differ from the Official Excalidraw MCP
Excalidraw now has an official MCP — it's great for quick, prompt-to-diagram generation rendered inline in chat. We solve a different problem.
| | Official Excalidraw MCP | This Project |
|---|---|---|
| Approach | Prompt in, diagram out (one-shot) | Programmatic element-level control (26 tools) |
| State | Stateless — each call is independent | Persistent live canvas with real-time sync |
| Element CRUD | No | Full create / read / update / delete per element |
| AI sees the canvas | No | describe_scene (structured text) + get_canvas_screenshot (image) |
| Iterative refinement | No — regenerate the whole diagram | Draw → look → adjust → look again, element by element |
| Layout tools | No | align_elements, distribute_elements, group / ungroup |
| File I/O | No | export_scene / import_scene (.excalidraw JSON) |
| Snapshot & rollback | No | snapshot_scene / restore_snapshot |
| Mermaid conversion | No | create_from_mermaid |
| Shareable URLs | Yes | Yes — export_to_excalidraw_url |
| Design guide | read_me cheat sheet | read_diagram_guide (colors, sizing, layout, anti-patterns) |
| Viewport control | Camera animations | set_viewport (zoom-to-fit, center on element, manual zoom) |
| Live canvas UI | Rendered inline in chat | Standalone Excalidraw app synced via WebSocket |
| Multi-agent | Single user | Multiple agents can draw on the same canvas concurrently |
| Works without MCP | No | Yes — REST API fallback via agent skill |
TL;DR — The official MCP generates diagrams. We give AI agents a full canvas toolkit to build, inspect, and iteratively refine diagrams — including the ability to see what they drew.
Quick Start
npx excalidrop # interactive TUI: repo → auth → host → editor
# or one-shot:
npx excalidrop setup owner/repo [--target pages|cloudflare]setup walks you through the whole flow on any repo you own or can access:
- Checks
gh auth(log in withgh auth loginfirst — 2FA via GitHub, ornpx excalidrop logindevice flow). - Picks a host (recommended from visibility: public→GitHub Pages, private→Cloudflare Pages; override with
--target). Publishes the viewer once — later saves never redeploy. - Repo access == canvas access: collaborators with write can edit, readers get view-only, everyone else gets a login wall.
- Writes
.mcp.json+ remembers the repo in.excalidrop.json, so the MCP preloads it and subsequent sessions draw directly. Commits land on GitHub; the viewer updates itself.
No install needed — editors run the MCP straight from npm:
claude mcp add excalidrop --scope project -- npx --prefix ~/.excalidrop-npx -y excalidrop@latest mcp --repo owner/repo
codex mcp add excalidrop -- npx --prefix ~/.excalidrop-npx -y excalidrop@latest mcp --repo owner/repoScreenshots / viewport / mermaid need one viewer tab open (shared relay at excalidrop.wtf403.workers.dev); drawing works headless.
Migrating public↔private
Flipping repo visibility doesn't move canvas data (it stays on the excalidrop branch) — only the viewer host changes. GitHub Pages serves private repos only on paid plans, so private repos use Cloudflare Pages. The TUI detects the flip (stored target in .excalidrop.json vs current visibility) and pre-selects the right host.
Public → private:
gh repo edit owner/repo --visibility private(or repo Settings).npx excalidrop setup owner/repo --target cloudflare(needswrangler loginonce). New URL:https://excalidrop-owner-repo.pages.dev/(repo slug is baked into the viewer build at deploy time, no?repo=needed).- Anonymous viewing ends: private scenes need a token, so every viewer must log in and the Excalidrop app must be installed on the repo.
- No OAuth callback registration needed: Cloudflare viewers bounce login through the central host (
https://wtf403.github.io/excalidrop/, overridable viaVITE_CENTRAL_LOGIN_HOST), which hands the token back via the address hash. (If you self-host withVITE_CENTRAL_LOGIN_HOST=off, registerhttps://<project>.pages.dev/as an exact callback URL instead.) - The old
owner.github.io/repoURL 404s — optionally disable Pages (Settings → Pages) to avoid confusion.
Private → public:
gh repo edit owner/repo --visibility public.- Either stay on Cloudflare (keeps working; anonymous reads start working once public) or move back to zero-config:
npx excalidrop setup owner/repo --target pages. - If you move back, optionally delete the Cloudflare project (
npx -y wrangler@4 pages project delete <project>) so two live URLs don't drift.
Configure MCP Clients
The MCP server runs over stdio (remote-only) and can be configured with any MCP-compatible client. The recommended path is npx excalidrop setup owner/repo (writes .mcp.json + auto-installs), which replaces the manual setups below.
Environment Variables
| Variable | Description | Default |
|----------|-------------|---------|
| EXCALIDROP_REPO | Repo canvas (owner/repo), alternative to --repo= | git remote / .excalidrop.json |
| GITHUB_TOKEN | GitHub token (else gh auth token / stored device token) | — |
| EXCALIDROP_RELAY_URL | Screenshot/viewport relay | https://excalidrop.wtf403.workers.dev |
| EXCALIDROP_NO_RELAY | Set 1 to use GitHub command-queue instead of relay | unset |
Claude Desktop
Config location:
- macOS:
~/Library/Application Support/Claude/claude_desktop_config.json - Windows:
%APPDATA%\Claude\claude_desktop_config.json - Linux:
~/.config/Claude/claude_desktop_config.json
{
"mcpServers": {
"excalidrop": {
"command": "npx",
"args": ["-y", "excalidrop@latest", "mcp", "--repo", "owner/repo"]
}
}
}Claude Code
# Project-level (shared via .mcp.json — written automatically by setup):
claude mcp add excalidrop --scope project -- npx --prefix ~/.excalidrop-npx -y excalidrop@latest mcp --repo owner/repo
# User-level (all projects):
claude mcp add excalidrop --scope user -- npx --prefix ~/.excalidrop-npx -y excalidrop@latest mcp --repo owner/repoManage servers:
claude mcp list # List configured servers
claude mcp remove excalidrop # Remove a serverCursor
Config location: .cursor/mcp.json in your project root (or ~/.cursor/mcp.json for global config)
{
"mcpServers": {
"excalidrop": {
"command": "npx",
"args": ["-y", "excalidrop@latest", "mcp", "--repo", "owner/repo"]
}
}
}Codex CLI
codex mcp add excalidrop -- npx --prefix ~/.excalidrop-npx -y excalidrop@latest mcp --repo owner/repoManage servers:
codex mcp list # List configured servers
codex mcp remove excalidrop # Remove a serverOpenCode
Config location: ~/.config/opencode/opencode.json or project-level opencode.json
{
"$schema": "https://opencode.ai/config.json",
"mcp": {
"excalidrop": {
"type": "local",
"command": ["npx", "-y", "excalidrop@latest", "mcp", "--repo", "owner/repo"],
"enabled": true
}
}
}Antigravity (Google)
Config location: ~/.gemini/antigravity/mcp_config.json
{
"mcpServers": {
"excalidrop": {
"command": "npx",
"args": ["-y", "excalidrop@latest", "mcp", "--repo", "owner/repo"]
}
}
}Notes
- No install, no local server: replace
owner/repowith your canvas repo. Auth viaGITHUB_TOKEN,gh auth, ornpx excalidrop login. - Scene storage:
canvas.excalidrawon the repo'sexcalidropbranch (+snapshots/*.json). Nothing lives in memory to lose. - Screenshots (
get_canvas_screenshot,export_to_image,set_viewport) need one viewer tab open (relay); everything else works headless.
Agent Skill (Optional)
This repo includes a skill at skills/excalidraw-skill/ that provides:
- Workflow playbook (
SKILL.md): step-by-step guidance for drawing, refining, and exporting diagrams - Cheatsheet (
references/cheatsheet.md): MCP tool and REST API reference - Helper scripts (
scripts/*.cjs): export, import, clear, healthcheck, CRUD operations
The skill complements the MCP server by giving your AI agent structured workflows to follow.
Install The Skill (Codex CLI example)
mkdir -p ~/.codex/skills
cp -R skills/excalidraw-skill ~/.codex/skills/excalidraw-skillTo update an existing installation, remove the old folder first (rm -rf ~/.codex/skills/excalidraw-skill) then re-copy.
Install The Skill (Claude Code)
User-level (available across all your projects):
mkdir -p ~/.claude/skills
cp -R skills/excalidraw-skill ~/.claude/skills/excalidraw-skillProject-level (scoped to a specific project, can be committed to the repo):
mkdir -p /path/to/your/project/.claude/skills
cp -R skills/excalidraw-skill /path/to/your/project/.claude/skills/excalidraw-skillThen invoke the skill in Claude Code with /excalidraw-skill.
To update an existing installation, remove the old folder first then re-copy.
Use The Skill Scripts
Skill scripts talk to the remote canvas via gh auth (no local server):
node skills/excalidraw-skill/scripts/healthcheck.cjs --repo owner/repo
node skills/excalidraw-skill/scripts/export-elements.cjs --repo owner/repo --out diagram.elements.json
node skills/excalidraw-skill/scripts/import-elements.cjs --repo owner/repo --in diagram.elements.json --mode batchWhen The Skill Is Useful
- Repository workflow: export elements as JSON, commit it, and re-import later.
- Reliable refactors: clear + re-import in
syncmode to make canvas match a file. - Automated smoke tests: create/update/delete a known element to validate a deployment.
- Repeatable diagrams: keep a library of element JSON snippets and import them.
See skills/excalidraw-skill/SKILL.md and skills/excalidraw-skill/references/cheatsheet.md.
MCP Tools (31 Total, remote-only)
| Category | Tools |
|---|---|
| Element CRUD | create_element, get_element, update_element, delete_element, query_elements, batch_create_elements, duplicate_elements |
| Layout | align_elements, distribute_elements, group_elements, ungroup_elements, lock_elements, unlock_elements |
| Scene Awareness | describe_scene, get_canvas_screenshot |
| File I/O | export_scene, import_scene, export_to_image, export_to_excalidraw_url, create_from_mermaid |
| State Management | clear_canvas, snapshot_scene, restore_snapshot |
| Viewport | set_viewport |
| Design Guide | read_diagram_guide |
| Resources | get_resource |
Full schemas are discoverable via tools/list or in skills/excalidraw-skill/references/cheatsheet.md.
Remote MCP (ChatGPT, Claude.ai — no install)
One endpoint serves every canvas (stateless, spec 2026-07-28):
https://excalidrop.wtf403.workers.dev/mcp- In your chat client, add a custom MCP connector with the URL above.
- Approve GitHub OAuth (
reposcope — the relay only forwards your token, per request, to the GitHub API). - Call any tool with
{ "repo": "owner/name" }— e.g.describe_scene,create_element,get_canvas_screenshot(viewer tab must be open for screenshots).
Notes:
- No
?repo=in the URL: the repo travels per tool call.switch_remotesets a per-token default so later calls may omit it;list_canvasesshows repos you can access. - Shared relay quota: 60 calls/min per token, daily budget shared across users (honest
429with reset time). Heavy use?npx excalidrop setup owner/repo --relay=selfdeploys the relay into your own Cloudflare account (free 100k/day) — then register its/mcpURL instead. - OAuth discovery for clients:
https://excalidrop.wtf403.workers.dev/.well-known/oauth-authorization-server. - Browser login is bounce-free: hosts outside the OAuth callback list redirect through the central login host straight to GitHub (target allowlist-checked, no confirm modal).
One-click OAuth for chat clients (DCR)
The worker is its own OAuth Authorization Server (spec 2026-07-28, RFC 7591 + PKCE):
POST /registermints a client (excc_*/excs_*);GET /authorizebounces to GitHub;/oauth/callbackswaps the code server-side (app secret never leaves the worker);POST /tokenreturns the GitHub user token asaccess_token.GET /.well-known/oauth-protected-resource+WWW-Authenticatechallenge on/mcp401s drive automatic discovery.- Contract tests:
node worker/test/oauth.test.mjs(30 checks, stubbed KV + GitHub). - The demo OAuth App is third-party: for full self-ownership, register your own GitHub OAuth App with callback
https://<your-worker>/oauth/callback, then setMCP_GITHUB_CLIENT_ID(wrangler.toml) +wrangler secret put MCP_GITHUB_CLIENT_SECRETand redeploy. Until then the shared id works for login but chat-client flows stop at GitHub's "Invalid Redirect URI" page. - Org repos with SAML SSO: authorize the OAuth App under the org's SSO settings, or API calls 404 despite a valid token.
Testing
Status
npx excalidrop status # repo + auth + scene element countMCP Smoke Test (MCP Inspector)
List tools:
npx @modelcontextprotocol/inspector --cli -- node dist/mcp.js --repo owner/repo --method tools/listCreate a rectangle (commits to the repo's excalidrop branch):
npx @modelcontextprotocol/inspector --cli -- node dist/mcp.js --repo owner/repo \
--method tools/call --tool-name create_element \
--tool-arg type=rectangle --tool-arg x=100 --tool-arg y=100 \
--tool-arg width=300 --tool-arg height=200Viewer screenshots
Open the canvas URL once, then call get_canvas_screenshot — it renders via the shared relay (EXCALIDROP_RELAY_URL, --no-relay falls back to the GitHub command queue).
Troubleshooting
No repo selected: pass--repo=owner/repo, setEXCALIDROP_REPO, or runnpx excalidrop setup owner/repo.No GitHub token: rungh auth loginornpx excalidrop login.- Screenshot
503 no viewer connected: open the canvas URL in a browser first. - Updates/deletes fail after batch creation: ensure you are on a build that includes the batch id preservation fix (merged via PR #34).
Known Issues / TODO
All previously listed bugs have been fixed in v2.0. Remaining items:
- [ ] Persistent storage: Elements are stored in-memory — restarting the server clears everything. Use
export_scene/ snapshots as a workaround. - [ ] Image export requires a browser:
export_to_imageandget_canvas_screenshotrely on the frontend doing the actual rendering. The canvas UI must be open in a browser.
Contributions welcome!
Development
npm run type-check
npm run build