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.
AI agent creates a complete architecture diagram from a single prompt (4x speed). Watch full video on YouTube
- Demo
- What It Is
- How We Differ from the Official Excalidraw MCP
- What's New
- Quick Start (Local)
- Quick Start (Docker)
- Configure MCP Clients
- Agent Skill (Optional)
- MCP Tools (26 Total)
- Testing
- Troubleshooting
- Known Issues / TODO
- Development
This repo contains two separate processes:
- Canvas server: web UI + REST API + WebSocket updates (default
http://localhost:3000) - MCP server: exposes MCP tools over stdio; syncs to the canvas via
EXPRESS_SERVER_URL
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.
- 13 new MCP tools (26 total):
get_element,clear_canvas,export_scene,import_scene,export_to_image,duplicate_elements,snapshot_scene,restore_snapshot,describe_scene,get_canvas_screenshot,read_diagram_guide,export_to_excalidraw_url,set_viewport - Closed feedback loop: AI can now inspect the canvas (
describe_scene) and see it (get_canvas_screenshotreturns an image) β enabling iterative refinement - Design guide:
read_diagram_guidereturns best-practice color palettes, sizing rules, layout patterns, and anti-patterns β dramatically improves AI-generated diagram quality - Shareable URLs:
export_to_excalidraw_urlencrypts and uploads the scene to excalidraw.com, returns a shareable link anyone can open - Viewport control:
set_viewportwithscrollToContent,scrollToElementId, or manual zoom/offset β agents can auto-fit diagrams after creation - File I/O: export/import full
.excalidrawJSON files - Snapshots: save and restore named canvas states
- Skill fallback: Agent skill auto-detects MCP vs REST API mode, gracefully falls back to HTTP endpoints when MCP server isn't configured
- Fixed all previously known issues:
align_elements/distribute_elementsfully implemented, points type normalization, removed invalidlabeltype, removed HTTP transport dead code,ungroup_elementsnow errors on failure
- Agent skill:
skills/excalidraw-skill/(portable instructions + helper scripts for export/import and repeatable CRUD) - Better testing loop: MCP Inspector CLI examples + browser screenshot checks (
agent-browser) - Bugfixes: batch create now preserves element ids (fixes update/delete after batch); frontend entrypoint fixed (
main.tsx)
Prereqs: Node >= 18, npm
npm ci
npm run buildTerminal 1: start the canvas
HOST=0.0.0.0 PORT=3000 npm run canvasOpen http://localhost:3000.
Terminal 2: run the MCP server (stdio)
EXPRESS_SERVER_URL=http://localhost:3000 node dist/index.jsCanvas server:
docker run -d -p 3000:3000 --name mcp-excalidraw-canvas ghcr.io/yctimlin/mcp_excalidraw-canvas:latestMCP server (stdio) is typically launched by your MCP client (Claude Desktop/Cursor/etc.). If you want a local container for it, use the image ghcr.io/yctimlin/mcp_excalidraw:latest and set EXPRESS_SERVER_URL to point at the canvas.
The MCP server runs over stdio and can be configured with any MCP-compatible client. Below are configurations for both local (requires cloning and building) and Docker (pull-and-run) setups.
| Variable | Description | Default |
|---|---|---|
EXPRESS_SERVER_URL |
URL of the canvas server | http://localhost:3000 |
ENABLE_CANVAS_SYNC |
Enable real-time canvas sync | true |
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
Local (node)
{
"mcpServers": {
"excalidraw": {
"command": "node",
"args": ["/absolute/path/to/mcp_excalidraw/dist/index.js"],
"env": {
"EXPRESS_SERVER_URL": "http://localhost:3000",
"ENABLE_CANVAS_SYNC": "true"
}
}
}
}Docker
{
"mcpServers": {
"excalidraw": {
"command": "docker",
"args": [
"run", "-i", "--rm",
"-e", "EXPRESS_SERVER_URL=http://host.docker.internal:3000",
"-e", "ENABLE_CANVAS_SYNC=true",
"ghcr.io/yctimlin/mcp_excalidraw:latest"
]
}
}
}Use the claude mcp add command to register the MCP server.
Local (node) - User-level (available across all projects):
claude mcp add excalidraw --scope user \
-e EXPRESS_SERVER_URL=http://localhost:3000 \
-e ENABLE_CANVAS_SYNC=true \
-- node /absolute/path/to/mcp_excalidraw/dist/index.jsLocal (node) - Project-level (shared via .mcp.json):
claude mcp add excalidraw --scope project \
-e EXPRESS_SERVER_URL=http://localhost:3000 \
-e ENABLE_CANVAS_SYNC=true \
-- node /absolute/path/to/mcp_excalidraw/dist/index.jsDocker
claude mcp add excalidraw --scope user \
-- docker run -i --rm \
-e EXPRESS_SERVER_URL=http://host.docker.internal:3000 \
-e ENABLE_CANVAS_SYNC=true \
ghcr.io/yctimlin/mcp_excalidraw:latestManage servers:
claude mcp list # List configured servers
claude mcp remove excalidraw # Remove a serverConfig location: .cursor/mcp.json in your project root (or ~/.cursor/mcp.json for global config)
Local (node)
{
"mcpServers": {
"excalidraw": {
"command": "node",
"args": ["/absolute/path/to/mcp_excalidraw/dist/index.js"],
"env": {
"EXPRESS_SERVER_URL": "http://localhost:3000",
"ENABLE_CANVAS_SYNC": "true"
}
}
}
}Docker
{
"mcpServers": {
"excalidraw": {
"command": "docker",
"args": [
"run", "-i", "--rm",
"-e", "EXPRESS_SERVER_URL=http://host.docker.internal:3000",
"-e", "ENABLE_CANVAS_SYNC=true",
"ghcr.io/yctimlin/mcp_excalidraw:latest"
]
}
}
}Use the codex mcp add command to register the MCP server.
Local (node)
codex mcp add excalidraw \
--env EXPRESS_SERVER_URL=http://localhost:3000 \
--env ENABLE_CANVAS_SYNC=true \
-- node /absolute/path/to/mcp_excalidraw/dist/index.jsDocker
codex mcp add excalidraw \
-- docker run -i --rm \
-e EXPRESS_SERVER_URL=http://host.docker.internal:3000 \
-e ENABLE_CANVAS_SYNC=true \
ghcr.io/yctimlin/mcp_excalidraw:latestManage servers:
codex mcp list # List configured servers
codex mcp remove excalidraw # Remove a serverConfig location: ~/.config/opencode/opencode.json or project-level opencode.json
Local (node)
{
"$schema": "https://opencode.ai/config.json",
"mcp": {
"excalidraw": {
"type": "local",
"command": ["node", "/absolute/path/to/mcp_excalidraw/dist/index.js"],
"enabled": true,
"environment": {
"EXPRESS_SERVER_URL": "http://localhost:3000",
"ENABLE_CANVAS_SYNC": "true"
}
}
}
}Canvas is optional: The MCP server works without the canvas in API-only mode (for programmatic access only).
| Variable | Default | Description |
|---|---|---|
EXPRESS_SERVER_URL |
http://localhost:3000 |
Canvas server URL for MCP sync |
ENABLE_CANVAS_SYNC |
true |
Enable/disable canvas synchronization |
LOG_FILE_PATH |
excalidraw.log |
Path to the log file |
DEBUG |
false |
Enable debug logging |
PORT |
3000 |
Canvas server port |
HOST |
localhost |
Canvas server host |
The canvas server provides these REST endpoints:
| Method | Endpoint | Description |
|---|---|---|
GET |
/api/elements |
Get all elements |
POST |
/api/elements |
Create new element |
PUT |
/api/elements/:id |
Update element |
DELETE |
/api/elements/:id |
Delete element |
POST |
/api/elements/batch |
Create multiple elements |
GET |
/health |
Server health check |
create_element- Create any type of Excalidraw elementupdate_element- Modify existing elementsdelete_element- Remove elementsquery_elements- Search elements with filters
batch_create_elements- Create complex diagrams in one call
group_elements- Group multiple elementsungroup_elements- Ungroup element groupsalign_elements- Align elements (left, center, right, top, middle, bottom)distribute_elements- Distribute elements evenlylock_elements/unlock_elements- Lock/unlock elements
get_resource- Access scene, library, theme, or elements data
export_canvas- Export canvas to PNG or SVG with light/dark theme support
The export feature allows you to download your Excalidraw canvas in PNG or SVG format with customizable theme options.
// Export as PNG with light theme
{
"format": "png",
"theme": "light",
"filename": "my-diagram"
}
// Export as SVG with dark theme
{
"format": "svg",
"theme": "dark",
"filename": "my-dark-diagram"
}The canvas interface provides four quick export buttons:
- π· PNG Light - Export as PNG with light background
- π PNG Dark - Export as PNG with dark background
- π SVG Light - Export as SVG with light background
- π SVG Dark - Export as SVG with dark background
| Parameter | Type | Required | Description |
|---|---|---|---|
format |
'png' | 'svg' |
Yes | Export format |
theme |
'light' | 'dark' |
No | Theme mode (default: 'light') |
filename |
string |
No | Custom filename without extension |
- β High-quality PNG exports (95% quality)
- β Vector SVG exports for scalability
- β Automatic dark/light theme switching
- β Custom background colors based on theme
- β Automatic file download in browser
- β Preserves all element properties and styles
- React + TypeScript: Modern TSX components with full type safety
- Vite Build System: Fast development and optimized production builds
- Official Excalidraw:
@excalidraw/excalidrawpackage with TypeScript types - WebSocket Client: Type-safe real-time element synchronization
- Clean UI: Production-ready interface with proper TypeScript typing
- TypeScript + Express.js: Fully typed REST API + static file serving
- WebSocket: Type-safe real-time client communication
- Element Storage: In-memory with comprehensive type definitions
- CORS: Cross-origin support with proper typing
- TypeScript MCP Protocol: Type-safe Model Context Protocol implementation
- Canvas Sync: Strongly typed HTTP requests to canvas server
- Element Management: Full CRUD operations with comprehensive type checking
- Batch Support: Type-safe complex diagram creation
- Excalidraw Element Types: Complete type definitions for all element types
- API Response Types: Strongly typed REST API interfaces
- WebSocket Message Types: Type-safe real-time communication
- Server Element Types: Enhanced element types with metadata
- Ensure
npm run buildcompleted successfully - Check that
dist/index.htmlanddist/frontend/directory exist - Verify canvas server is running on port 3000
- Check if port 3000 is already in use:
lsof -i :3000(macOS/Linux) ornetstat -ano | findstr :3000(Windows)
- Confirm canvas server is running and accessible at http://localhost:3000
- Check
ENABLE_CANVAS_SYNC=truein MCP server environment configuration - Verify
EXPRESS_SERVER_URLpoints to correct canvas server URL - Check browser console for WebSocket connection errors
- For Docker: Ensure
--network hostflag is used
- Check browser console for WebSocket errors (F12 β Console tab)
- Ensure no firewall blocking WebSocket connections on port 3000
- Try refreshing the browser page
- Verify canvas server is running:
curl http://localhost:3000/health
Canvas Container:
- Check if container is running:
docker ps | grep canvas - View logs:
docker logs mcp-excalidraw-canvas - Ensure port 3000 is not already in use
MCP Container:
- For Docker MCP server, ensure
--network hostis used (required to access localhost:3000) - Verify
-iflag is present (required for MCP stdin/stdout protocol) - Check environment variables are properly set
- Delete
node_modulesanddist/directories, then runnpm install && npm run build - Check Node.js version (requires 16+):
node --version - Run
npm run type-checkto identify TypeScript issues - Verify
dist/directory contains bothindex.js,server.js, andfrontend/after build
- Status: NPM package is under development
- Recommendation: Use local or Docker installation methods for production use
mcp_excalidraw/
βββ frontend/
β βββ src/
β β βββ App.tsx # Main React component (TypeScript)
β β βββ main.tsx # React entry point (TypeScript)
β βββ index.html # HTML template
βββ src/ (TypeScript Source)
β βββ index.ts # MCP server (TypeScript)
β βββ server.ts # Canvas server (Express + WebSocket, TypeScript)
β βββ types.ts # Comprehensive type definitions
β βββ utils/
β βββ logger.ts # Logging utility (TypeScript)
βββ dist/ (Compiled Output)
β βββ index.js # Compiled MCP server
β βββ server.js # Compiled Canvas server
β βββ types.js # Compiled type definitions
β βββ utils/
β β βββ logger.js # Compiled logging utility
β βββ frontend/ # Built React frontend
βββ tsconfig.json # TypeScript configuration
βββ vite.config.js # Vite build configuration
βββ package.json # Dependencies and scripts
βββ README.md # This file
Docker
{
"$schema": "https://opencode.ai/config.json",
"mcp": {
"excalidraw": {
"type": "local",
"command": ["docker", "run", "-i", "--rm", "-e", "EXPRESS_SERVER_URL=http://host.docker.internal:3000", "-e", "ENABLE_CANVAS_SYNC=true", "ghcr.io/yctimlin/mcp_excalidraw:latest"],
"enabled": true
}
}
}Config location: ~/.gemini/antigravity/mcp_config.json
Local (node)
{
"mcpServers": {
"excalidraw": {
"command": "node",
"args": ["/absolute/path/to/mcp_excalidraw/dist/index.js"],
"env": {
"EXPRESS_SERVER_URL": "http://localhost:3000",
"ENABLE_CANVAS_SYNC": "true"
}
}
}
}Docker
{
"mcpServers": {
"excalidraw": {
"command": "docker",
"args": [
"run", "-i", "--rm",
"-e", "EXPRESS_SERVER_URL=http://host.docker.internal:3000",
"-e", "ENABLE_CANVAS_SYNC=true",
"ghcr.io/yctimlin/mcp_excalidraw:latest"
]
}
}
}- Docker networking: Use
host.docker.internalto reach the canvas server running on your host machine. On Linux, you may need--add-host=host.docker.internal:host-gatewayor use172.17.0.1. - Canvas server: Must be running before the MCP server connects. Start it with
npm run canvas(local) ordocker run -d -p 3000:3000 ghcr.io/yctimlin/mcp_excalidraw-canvas:latest(Docker). - Absolute paths: When using local node setup, replace
/absolute/path/to/mcp_excalidrawwith the actual path where you cloned and built the repo. - In-memory storage: The canvas server stores elements in memory. Restarting the server will clear all elements. Use the export/import scripts if you need persistence.
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.
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.
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.
All scripts respect EXPRESS_SERVER_URL (default http://localhost:3000) or accept --url.
EXPRESS_SERVER_URL=http://127.0.0.1:3000 node skills/excalidraw-skill/scripts/healthcheck.cjs
EXPRESS_SERVER_URL=http://127.0.0.1:3000 node skills/excalidraw-skill/scripts/export-elements.cjs --out diagram.elements.json
EXPRESS_SERVER_URL=http://127.0.0.1:3000 node skills/excalidraw-skill/scripts/import-elements.cjs --in diagram.elements.json --mode batch- 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.
| 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.
curl http://localhost:3000/healthList tools:
npx @modelcontextprotocol/inspector --cli \
-e EXPRESS_SERVER_URL=http://localhost:3000 \
-e ENABLE_CANVAS_SYNC=true -- \
node dist/index.js --method tools/listCreate a rectangle:
npx @modelcontextprotocol/inspector --cli \
-e EXPRESS_SERVER_URL=http://localhost:3000 \
-e ENABLE_CANVAS_SYNC=true -- \
node dist/index.js --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=200If you use agent-browser for UI checks:
agent-browser install
agent-browser open http://127.0.0.1:3000
agent-browser wait --load networkidle
agent-browser screenshot /tmp/canvas.png- Canvas not updating: confirm
EXPRESS_SERVER_URLpoints at the running canvas server. - Updates/deletes fail after batch creation: ensure you are on a build that includes the batch id preservation fix (merged via PR #34).
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!
npm run type-check
npm run build