@sanjibdevnath/mcp-excalidraw-local
v1.6.2
Published
Fully local MCP server for Excalidraw with SQLite persistence, multi-tenancy, auto-sync, real-time canvas, and 32 tools
Maintainers
Readme
MCP Excalidraw Local
A fully local, self-hosted Excalidraw MCP server with SQLite persistence, multi-tenancy, and auto-sync — designed to run entirely on your machine without depending on excalidraw.com.
Run a live Excalidraw canvas and control it from any AI agent. This repo provides:
- MCP Server: 32 tools over stdio — works with any MCP-compatible client
- Agent Skill: Portable skill with workflow playbooks, cheatsheets, and helper scripts
- Live Canvas: Real-time Excalidraw UI synced via WebSocket
- SQLite Persistence: Elements survive restarts, with versioning and search
- Multi-Tenancy: Isolated canvases per workspace, auto-detected
Fork notice: This project is forked from yctimlin/mcp_excalidraw and extends it with persistence, multi-workspace support, and numerous UX improvements. Full credit to the original author for the excellent foundation. See What Changed From Upstream for details.
Keywords: Excalidraw MCP server, AI diagramming, local Excalidraw, self-hosted, SQLite persistence, multi-tenant, Mermaid to Excalidraw.
Screenshots
Canvas UI
The live Excalidraw canvas with toolbar, connection status, sync controls, and workspace badge:

Workspace Switcher
Click the workspace badge to switch between isolated canvases — each workspace has its own set of diagrams:

For a demo of the upstream project (before persistence/multi-tenancy), see the original video by @yctimlin.
Table of Contents
- Screenshots
- Prerequisites
- Quick Start
- Configuration
- Verify Installation
- Updating
- How We Differ from the Official Excalidraw MCP
- What Changed From Upstream
- Architecture
- Environment Variables
- Multi-Tenancy (Workspaces)
- Agent Skill (Optional)
- MCP Tools (32 Total)
- Testing
- Troubleshooting
- Known Issues / TODO
- Development
- Credits
Prerequisites
| Requirement | Why | Check |
|---|---|---|
| Node.js >= 20 (LTS 20 or 22 recommended) | Runtime | node --version |
| C++ build tools | better-sqlite3 compiles native bindings | See below |
| npm (bundled with Node.js) | Package manager | npm --version |
C++ build tools by platform
macOS:
xcode-select --installUbuntu / Debian:
sudo apt install build-essential python3Windows: Install "Desktop development with C++" from Visual Studio Build Tools.
better-sqlite3ships prebuilt binaries for most Node LTS versions. The build tools are only needed when a prebuilt binary isn't available for your platform/Node combination.
Quick Start
Path A: Interactive Setup (recommended for first-time users)
The setup wizard checks your environment, optionally installs the agent skill, and configures MCP clients — all interactively. Every step is skippable.
npx @sanjibdevnath/mcp-excalidraw-local setup$ npx @sanjibdevnath/mcp-excalidraw-local setup
Excalidraw MCP — Setup
[1/3] Environment
✔ Node.js v22.12.0 .................. OK
✔ better-sqlite3 bindings ........... OK
✔ Frontend build .................... OK
[2/3] Agent Skill
Install the Excalidraw agent skill? [Y/n]: Y
Detected agents:
[1] Cursor (~/.cursor)
[2] Claude Code (~/.claude)
Which agents? (comma-separated, 'all', or 'skip'): all
Cursor — scope? [G]lobal / [l]ocal: G
✔ Installed to ~/.cursor/skills/excalidraw-skill/
Claude Code — scope? [G]lobal / [l]ocal: G
✔ Installed to ~/.claude/skills/excalidraw-skill/
[3/3] MCP Configuration
Add MCP server to agent configs automatically? [Y/n]: Y
Cursor — add to ~/.cursor/mcp.json? [Y/n]: Y
✔ Added 'excalidraw-canvas' to ~/.cursor/mcp.json
Claude Code — register via CLI? [Y/n]: Y
✔ Registered 'excalidraw-canvas' via Claude Code CLI
Done! Open http://localhost:3000 to verify the canvas.The setup is fully optional. If you prefer to configure everything manually, skip to Path B or C below.
Path B: From Source
git clone https://github.com/sanjibdevnathlabs/mcp-excalidraw-local.git
cd mcp-excalidraw-local
npm install
npm run buildThen configure your MCP client — see Configuration.
To run manually (outside an MCP client):
node dist/index.jsOpen http://localhost:3000 in your browser.
Path C: Docker
Canvas server:
docker run -d -p 3000:3000 --name mcp-excalidraw-canvas sanjibdevnath/mcp-excalidraw-local-canvas:latestMCP server (stdio) is typically launched by your MCP client:
{
"mcpServers": {
"excalidraw-canvas": {
"command": "docker",
"args": [
"run", "-i", "--rm",
"-e", "CANVAS_PORT=3000",
"sanjibdevnath/mcp-excalidraw-local:latest"
]
}
}
}Note: For Docker on Linux, add
--add-host=host.docker.internal:host-gateway.
Configuration
This is a standard MCP server communicating over stdio. It works with any MCP-compatible client.
Cursor
Add to ~/.cursor/mcp.json (global) or .cursor/mcp.json (per-project):
{
"mcpServers": {
"excalidraw-canvas": {
"command": "npx",
"args": ["-y", "@sanjibdevnath/mcp-excalidraw-local"],
"env": {
"CANVAS_PORT": "3000"
}
}
}
}Or, if installed from source:
{
"mcpServers": {
"excalidraw-canvas": {
"command": "node",
"args": ["/absolute/path/to/mcp-excalidraw-local/dist/index.js"],
"env": {
"CANVAS_PORT": "3000"
}
}
}
}Claude Desktop
Add to claude_desktop_config.json:
{
"mcpServers": {
"excalidraw-canvas": {
"command": "npx",
"args": ["-y", "@sanjibdevnath/mcp-excalidraw-local"],
"env": {
"CANVAS_PORT": "3000"
}
}
}
}Claude Code
claude mcp add excalidraw-canvas --scope user \
-e CANVAS_PORT=3000 \
-- npx -y @sanjibdevnath/mcp-excalidraw-localCodex CLI
Add to ~/.codex/mcp.json:
{
"mcpServers": {
"excalidraw-canvas": {
"command": "npx",
"args": ["-y", "@sanjibdevnath/mcp-excalidraw-local"],
"env": {
"CANVAS_PORT": "3000"
}
}
}
}Key points
- Single process — The canvas server is embedded. No separate terminal or process needed.
- Browser required for screenshots —
export_to_imageandget_canvas_screenshotrely on the frontend. Openhttp://localhost:3000in a browser.
Verify Installation
After configuring your MCP client, verify everything works:
# 1. Check the canvas server is running
curl http://localhost:3000/health
# 2. Open the canvas in your browser
open http://localhost:3000
# 3. In your AI agent, ask it to:
# "Create a blue rectangle labeled 'Hello World' on the Excalidraw canvas"If the health check fails, see Troubleshooting.
Updating
Already installed a previous version? The interactive update wizard is the easiest way to update everything — MCP server and agent skills — in one go.
Interactive Update (recommended)
npx @sanjibdevnath/mcp-excalidraw-local@latest update$ npx @sanjibdevnath/mcp-excalidraw-local@latest update
Excalidraw MCP — Update v1.2.0
[1/2] Skill Update
Found 2 existing skill installation(s):
[1] Cursor (global) — ~/.cursor/skills/excalidraw-skill
[2] Claude Code (global) — ~/.claude/skills/excalidraw-skill
Update all 2 installation(s) to v1.2.0? [Y/n]: Y
✔ Updated Cursor (global) — ~/.cursor/skills/excalidraw-skill
✔ Updated Claude Code (global) — ~/.claude/skills/excalidraw-skill
2/2 skill(s) updated.
[2/2] MCP Configuration
Re-apply MCP server config? (overwrites existing entry) [y/N]: N
MCP config unchanged.
Update complete! Restart your MCP client to pick up changes.The update wizard:
- Finds all existing skill installations across Cursor, Claude Code, and Codex CLI (both global and local scopes)
- Updates them in-place with the latest skill files (SKILL.md, cheatsheet, geometric-thinking reference, helper scripts)
- Offers to install the skill for any detected agent that doesn't have it yet
- Optionally re-applies MCP config if needed
Why this matters: The agent skill contains workflow guidance, sizing rules, color palettes, and anti-patterns that evolve alongside the MCP tools. Updating the MCP server without updating the skill means your AI agent is working with stale instructions.
Manual Update by Installation Method
If you prefer to update manually, follow the steps for your installation method, then restart your MCP client.
npx users
If your MCP config uses npx -y @sanjibdevnath/mcp-excalidraw-local, npx caches the package locally and won't automatically fetch new versions.
Option A — Clear the cache (one-time):
npm cache clean --forceThen restart your MCP client. npx will download the latest version on next launch.
Option B — Pin to @latest in your MCP config (permanent fix):
Update the args in your MCP config to include @latest:
{
"mcpServers": {
"excalidraw-canvas": {
"command": "npx",
"args": ["-y", "@sanjibdevnath/mcp-excalidraw-local@latest"],
"env": { "CANVAS_PORT": "3000" }
}
}
}This ensures npx always checks for the newest published version.
From-source users
cd mcp-excalidraw-local
git pull origin main
npm install
npm run buildDocker users
docker pull sanjibdevnath/mcp-excalidraw-local:latest
docker pull sanjibdevnath/mcp-excalidraw-local-canvas:latestThen recreate your containers (docker compose up -d or docker run again).
Updating the agent skill manually
If you skipped the interactive update, copy the skill files yourself:
cp -R skills/excalidraw-skill ~/.cursor/skills/excalidraw-skill
cp -R skills/excalidraw-skill ~/.claude/skills/excalidraw-skillVerify the update
# Check the running version
curl -s http://localhost:3000/health
# Or check the installed package version
npx @sanjibdevnath/mcp-excalidraw-local --versionHow 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 (32 tools) |
| State | Stateless — each call is independent | Persistent live canvas with real-time sync |
| Storage | None | SQLite with WAL mode, versioning, element history |
| Multi-tenancy | No | Workspace-based isolation, auto-detected |
| 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 |
| Search | No | search_elements — full-text search across labels |
| 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 |
What Changed From Upstream
This fork extends yctimlin/mcp_excalidraw with the following enhancements:
| Area | Upstream | This Fork |
|---|---|---|
| Storage | In-memory (lost on restart) | SQLite with WAL mode, versioning, element history |
| Multi-tenancy | None | Workspace-based tenant isolation (auto-detected via server.listRoots()) |
| Canvas lifecycle | Separate process (2 terminals) | Embedded in MCP process (single node dist/index.js) |
| Auto-sync | Manual "Sync to Backend" button | Debounced auto-sync (3s idle) with manual override |
| Canvas port | Hardcoded 3000 | Configurable via CANVAS_PORT env var |
| MCP tools | 26 | 32 (added search, history, tenants, projects) |
| Workspace switcher | None | Dropdown with search in canvas UI |
| Sync normalization | Bound text breaks on reload | Elements normalized to MCP format before storage |
Architecture

- Single process: The MCP server embeds the canvas server. Starting the MCP starts both; stopping it stops both.
- SQLite: Stored at
~/.excalidraw-mcp/excalidraw.dbby default. WAL mode +busy_timeoutfor multi-process safety. - Multi-tenancy: Each workspace gets an isolated tenant (SHA-256 hash of workspace path). The UI shows a workspace switcher dropdown with search.
Environment Variables
| Variable | Description | Default |
|----------|-------------|---------|
| CANVAS_PORT | Port for the embedded canvas server | 3000 |
| EXCALIDRAW_DB_PATH | Path to the SQLite database file | ~/.excalidraw-mcp/excalidraw.db |
| EXCALIDRAW_EXPORT_DIR | Allowed directory for file exports | process.cwd() |
| EXPRESS_SERVER_URL | Canvas server URL (only if running canvas separately) | http://localhost:3000 |
Multi-Tenancy (Workspaces)
Each workspace (codebase) gets an isolated canvas. The tenant is identified by a SHA-256 hash of the workspace path.
How it works
- Auto-detection: When the MCP starts, it calls
server.listRoots()to get the actual workspace path from the MCP client. This is hashed to create a unique tenant ID. - Per-request scoping: Every HTTP request includes an
X-Tenant-Idheader. The canvas server uses this to scope all CRUD operations to the correct tenant. - UI switcher: The canvas UI shows a "Workspace: <name>" badge. Click it to open a dropdown with all known workspaces, complete with search.
- Multi-instance safe: SQLite WAL mode with
busy_timeout = 5000mshandles concurrent access from multiple client instances.
Projects within a tenant
Each tenant can have multiple projects (collections of elements). Use the list_projects and switch_project MCP tools, or manage via the REST API.
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 — including an iterative write-check-review cycle, sizing rules, color palettes, and anti-patterns - Cheatsheet (
references/cheatsheet.md): MCP tool and REST API reference for all 32 tools - Helper scripts (
scripts/*.cjs): export, import, clear, healthcheck, CRUD operations
Install via Setup Wizard
The easiest way to install the skill:
npx @sanjibdevnath/mcp-excalidraw-local setupThe wizard detects your installed agents and lets you choose which ones get the skill.
Install Manually
Copy the skill folder to your agent's skill directory:
# Cursor
cp -R skills/excalidraw-skill ~/.cursor/skills/excalidraw-skill
# Claude Code
cp -R skills/excalidraw-skill ~/.claude/skills/excalidraw-skill
# Codex CLI
cp -R skills/excalidraw-skill ~/.codex/skills/excalidraw-skillMCP Tools (32 Total)
| 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 |
| Search & History | search_elements, element_history |
| Multi-Tenancy | list_tenants, switch_tenant |
| Projects | list_projects, switch_project |
Full schemas are discoverable via tools/list or in skills/excalidraw-skill/references/cheatsheet.md.
Testing
Health check
curl http://localhost:3000/healthMCP Inspector
List tools:
npx @modelcontextprotocol/inspector --cli \
-e CANVAS_PORT=3000 -- \
node dist/index.js --method tools/listCreate a rectangle:
npx @modelcontextprotocol/inspector --cli \
-e CANVAS_PORT=3000 -- \
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=200Troubleshooting
better-sqlite3 compilation failure
This is the most common installation issue. better-sqlite3 is a native Node.js module that requires C++ build tools.
Symptoms:
npm installfails withgyp ERR!orprebuild-installerrorsnpxcommand fails during installation- Error:
Cannot find module 'better-sqlite3'at runtime
Fix:
- Install build tools for your platform (see Prerequisites)
- Rebuild the module:
npm rebuild better-sqlite3 - Or run the setup wizard which handles this automatically:
npx @sanjibdevnath/mcp-excalidraw-local setup
EADDRINUSE (port already in use)
Symptom: Error listen EADDRINUSE: address already in use :::3000
Fix:
# Find what's using the port
lsof -i :3000
# Either kill the process or use a different port
CANVAS_PORT=3001 node dist/index.jsThe MCP server automatically detects and reuses an existing healthy canvas server on the same port, so this error is rare.
Canvas not loading / "Frontend not found"
Symptom: Browser shows "Frontend not found" or blank page at http://localhost:3000
Fix:
npm run build # builds both frontend and server
node dist/index.js # restartNVM / path issues with npx
Symptom: npx @sanjibdevnath/mcp-excalidraw-local hangs or uses the wrong Node version.
Fix:
# Ensure you're using a supported Node version
nvm use 20 # or 22
# Clear npm cache if npx is stale
npm cache clean --force
# Try with explicit node path in your MCP config
which node # copy this pathThen use the full path in your MCP config:
{
"mcpServers": {
"excalidraw-canvas": {
"command": "/Users/you/.nvm/versions/node/v22.12.0/bin/npx",
"args": ["-y", "@sanjibdevnath/mcp-excalidraw-local"],
"env": { "CANVAS_PORT": "3000" }
}
}
}Canvas not updating / elements not syncing
Fix:
- Confirm the MCP process is running and the browser is connected (check the green status dot in the header)
- Click the "Sync" button in the canvas header for a manual sync
Wrong workspace shown
The MCP uses server.listRoots() to detect the workspace. Restart your MCP client if the workspace changed.
Known Issues / TODO
- [ ] Image export requires a browser:
export_to_imageandget_canvas_screenshotrely on the frontend rendering. The canvas UI must be open in a browser. - [ ]
export_to_excalidraw_urlblocked: Organizations that blockexcalidraw.comcannot use shareable URL export. Useexport_scenefor local.excalidrawfiles instead.
Contributions welcome!
Development
# Type check
npm run type-check
# Full build (frontend + server)
npm run build
# Dev mode (watch)
npm run devDatabase
SQLite database: ~/.excalidraw-mcp/excalidraw.db
Override with EXCALIDRAW_DB_PATH environment variable.
REST API
The canvas server exposes a REST API alongside the WebSocket interface:
| Method | Endpoint | Description |
|--------|----------|-------------|
| GET | /health | Health check |
| GET | /api/elements | List all elements |
| POST | /api/elements | Create an element |
| PUT | /api/elements/:id | Update an element |
| DELETE | /api/elements/:id | Delete an element |
| DELETE | /api/elements/clear | Clear all elements |
| POST | /api/elements/sync | Sync all elements (bulk upsert) |
| GET | /api/tenants | List all tenants |
| GET | /api/tenant/active | Get the active tenant |
| PUT | /api/tenant/active | Set the active tenant |
| GET | /api/settings/:key | Read a setting |
| PUT | /api/settings/:key | Write a setting |
All endpoints accept an X-Tenant-Id header for per-request tenant scoping.
Credits
This project is forked from yctimlin/mcp_excalidraw — an excellent Excalidraw MCP server with a live canvas, 26 tools, real-time WebSocket sync, Mermaid conversion, and a comprehensive agent skill. Full credit to @yctimlin for the original design and implementation.
This fork adds SQLite persistence, multi-tenancy, auto-sync, embedded canvas lifecycle, and workspace management on top of that foundation.
Licensed under MIT.
