Files
excalidraw-mcp-sentinel/README.md
T
7143b5e41e feat: add SQLite persistence, multi-tenancy, auto-sync, and CI/Docker improvements (#1)
Replace in-memory storage with SQLite (WAL mode), add workspace-based
multi-tenancy with auto-detection via server.listRoots(), and embed the
canvas server into the MCP process for single-process operation.

🔧 Core enhancements:
- SQLite persistence with versioning, element history, and search
- Multi-tenancy: isolated canvases per workspace (SHA-256 tenant IDs)
- Embedded canvas lifecycle (single node process starts MCP + canvas)
- Auto-sync with 3s debounce and manual override toggle
- Configurable canvas port via CANVAS_PORT env var
- 6 new MCP tools (search, history, tenants, projects)
- Workspace switcher UI with dropdown search
- Sync normalization to prevent bound-text breakage on reload

🐳 Docker & CI improvements:
- BuildKit cache mounts for faster npm installs across builds
- Skip native compilation in frontend-builder stage (--ignore-scripts)
- Build only linux/amd64 on PRs, multi-arch on push to main
- Docker Hub registry with proper build tools for better-sqlite3
- CI and Docker status check gates (github/ci-status-check, github/docker-build-check)

📦 Package & publishing:
- Renamed to @sanjibdevnath/mcp-excalidraw-local (v3.0.0)
- Updated npm-publish workflow for scoped package
- Updated bin entry, keywords, and files list

📝 Documentation:
- README with UI screenshots, architecture diagram, and full feature docs
- Updated agent skill with 32-tool cheatsheet and workflow playbooks
- Fork attribution and upstream comparison table

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-19 10:17:29 +05:30

22 KiB

MCP Excalidraw Local

CI Docker Build & Push License

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:

Canvas UI

Workspace Switcher

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

Workspace Switcher

For a demo of the upstream project (before persistence/multi-tenancy), see the original video by @yctimlin.

Table of Contents

What It Is

This MCP server gives AI agents a full canvas toolkit to build, inspect, and iteratively refine Excalidraw diagrams — including the ability to see what they drew.

The repo contains a single Node.js process that runs:

  • MCP server (stdio): 32 tools for element CRUD, layout, scene awareness, file I/O, snapshots, search, multi-tenancy, and more
  • Canvas server (embedded): web UI + REST API + WebSocket updates at http://localhost:<CANVAS_PORT>
  • SQLite database: persistent storage at ~/.excalidraw-mcp/excalidraw.db

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 (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

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.

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
Projects None Multiple projects per tenant
Element history None Full version history per element
Search None Full-text search across elements

New MCP Tools (6 added)

Tool Description
search_elements Full-text search across element labels and text
element_history View version history for any element
list_projects List projects within the active tenant
switch_project Switch between projects
list_tenants List all workspace tenants
switch_tenant Switch the active workspace tenant

What's New

v3.0 — This Fork (Persistence & Multi-Tenancy)

  • SQLite persistence: Elements, projects, tenants, snapshots, and element versions stored in ~/.excalidraw-mcp/excalidraw.db with WAL mode and busy_timeout for multi-process safety
  • Multi-tenancy: Each workspace gets an isolated canvas. Tenant auto-detected from workspace path via server.listRoots(). UI dropdown with search for switching workspaces
  • Embedded canvas: Canvas server runs inside the MCP process — single node dist/index.js starts everything, stops together
  • Auto-sync with debounce: Canvas changes are automatically persisted after 3s of inactivity. Manual sync button as fallback. Toggle auto-sync on/off
  • Configurable port: CANVAS_PORT env var (default 3000)
  • Sync normalization: Excalidraw's internal bound-text representation is normalized to MCP format before storage, preventing text overflow/detachment on reload
  • 6 new MCP tools: search_elements, element_history, list_projects, switch_project, list_tenants, switch_tenant
  • Updated agent skill: Comprehensive workflow playbook with iterative write-check-review cycle, sizing rules, anti-patterns, and quality checklist
  • Workspace switcher UI: Click "Workspace: ..." badge to search and switch between workspaces

v2.0 — Canvas Toolkit (upstream)

  • 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_screenshot returns an image) — enabling iterative refinement
  • Design guide: read_diagram_guide returns best-practice color palettes, sizing rules, layout patterns, and anti-patterns
  • Viewport control: set_viewport with scrollToContent, scrollToElementId, or manual zoom/offset
  • File I/O: export/import full .excalidraw JSON files
  • Snapshots: save and restore named canvas states
  • Skill fallback: Agent skill auto-detects MCP vs REST API mode
  • Fixed all previously known issues: align_elements / distribute_elements fully implemented, points type normalization, removed invalid label type, ungroup_elements now errors on failure

v1.x (upstream)

  • 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
  • Bugfixes: batch create now preserves element ids (fixes update/delete after batch); frontend entrypoint fixed

Architecture

┌─────────────────────────────────────────────────┐
│  MCP Process (single node process)              │
│                                                 │
│  ┌──────────────┐     ┌──────────────────────┐  │
│  │  MCP Server   │────▶│  Canvas Server        │  │
│  │  (stdio)      │     │  (Express + WS)       │  │
│  │  32 tools     │     │  http://localhost:PORT │  │
│  └──────────────┘     └──────────┬───────────┘  │
│                                  │               │
│                       ┌──────────▼───────────┐  │
│                       │  SQLite Database      │  │
│                       │  ~/.excalidraw-mcp/   │  │
│                       │  excalidraw.db        │  │
│                       └──────────────────────┘  │
└─────────────────────────────────────────────────┘
         ▲                         ▲
         │ stdio                   │ HTTP/WS
    ┌────┴─────┐           ┌───────┴──────┐
    │ Any MCP   │           │   Browser     │
    │ Client    │           │  :3000        │
    │           │           │  (Excalidraw) │
    └──────────┘           └──────────────┘
  • Single process: The MCP server embeds the canvas server. Starting the MCP starts both; stopping it stops both.
  • SQLite: Stored at ~/.excalidraw-mcp/excalidraw.db by default. WAL mode + busy_timeout for 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.

Quick Start

npx @sanjibdevnath/mcp-excalidraw-local

Or install globally:

npm install -g @sanjibdevnath/mcp-excalidraw-local
mcp-excalidraw-local

Option B: From source

Prerequisites: Node >= 18, npm or pnpm

git clone https://github.com/sanjibdevnathlabs/mcp-excalidraw-local.git
cd mcp-excalidraw-local

# Install dependencies (pnpm or npm)
pnpm install
pnpm rebuild better-sqlite3 esbuild

# Build frontend + server
pnpm run build

The MCP server is typically started by your MCP client — see Configuration. To run manually:

node dist/index.js

This starts the MCP server (stdio) and the canvas server. Open http://localhost:3000 in your browser.

Quick Start (Docker)

Canvas server:

docker run -d -p 3000:3000 --name mcp-excalidraw-canvas sanjibdevnath/mcp-excalidraw-local-canvas:latest

MCP server (stdio) is typically launched by your MCP client. If you want a local container, use sanjibdevnath/mcp-excalidraw-local:latest.

Configuration

This is a standard MCP server communicating over stdio. It works with any MCP-compatible client (Cursor, Claude Desktop, Claude Code, Codex CLI, OpenCode, Gemini, or any other agent that supports the Model Context Protocol).

JSON config (most clients)

Add this to your client's MCP configuration file:

{
  "mcpServers": {
    "excalidraw-canvas": {
      "command": "node",
      "args": ["/absolute/path/to/mcp-excalidraw-local/dist/index.js"],
      "env": {
        "CANVAS_PORT": "3000"
      }
    }
  }
}

Replace /absolute/path/to/mcp-excalidraw-local with the actual path where you cloned and built the repo.

CLI-based registration

# Example for Claude Code
claude mcp add excalidraw-canvas --scope user \
  -e CANVAS_PORT=3000 \
  -- node /absolute/path/to/mcp-excalidraw-local/dist/index.js

Docker

{
  "mcpServers": {
    "excalidraw-canvas": {
      "command": "docker",
      "args": [
        "run", "-i", "--rm",
        "-e", "CANVAS_PORT=3000",
        "sanjibdevnath/mcp-excalidraw-local:latest"
      ]
    }
  }
}

Note: For Docker on Linux, you may need --add-host=host.docker.internal:host-gateway.

Key points

  • Single process — The canvas server is embedded. No separate terminal or process needed.
  • Browser required for screenshotsexport_to_image and get_canvas_screenshot rely on the frontend. Open http://localhost:3000 in a browser.

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
ENABLE_CANVAS_SYNC Enable real-time canvas sync true

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

  1. 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.
  2. Per-request scoping: Every HTTP request includes an X-Tenant-Id header. The canvas server uses this to scope all CRUD operations to the correct tenant.
  3. UI switcher: The canvas UI shows a "Workspace: <name>" badge. Click it to open a dropdown with all known workspaces, complete with search.
  4. Multi-instance safe: SQLite WAL mode with busy_timeout = 5000ms handles 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

The skill complements the MCP server by giving your AI agent structured workflows to follow.

Install the Skill

Copy the skill folder to your agent's skill directory:

# Claude Code
mkdir -p ~/.claude/skills
cp -R skills/excalidraw-skill ~/.claude/skills/excalidraw-skill

# Cursor
mkdir -p ~/.cursor/skills
cp -R skills/excalidraw-skill ~/.cursor/skills/excalidraw-skill

# Codex CLI
mkdir -p ~/.codex/skills
cp -R skills/excalidraw-skill ~/.codex/skills/excalidraw-skill

# Or any agent that supports a skills directory
cp -R skills/excalidraw-skill /path/to/your/agent/skills/

To update an existing installation, remove the old folder first then re-copy.

Use the Skill Scripts

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

When the Skill Is Useful

  • Repository workflow: export elements as JSON, commit it, and re-import later
  • Reliable refactors: clear + re-import in sync mode 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 (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/health

MCP Inspector

List tools:

npx @modelcontextprotocol/inspector --cli \
  -e CANVAS_PORT=3000 -- \
  node dist/index.js --method tools/list

Create 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=200

Frontend Screenshots

If you use a browser automation tool for UI checks:

# Open the canvas and take a screenshot for verification
open http://127.0.0.1:3000
# Or use agent-browser, Playwright, Puppeteer, etc.

Troubleshooting

  • Canvas not loading: Ensure CANVAS_PORT isn't occupied by another process. Check lsof -i :3000.
  • Canvas not updating: Confirm the MCP process is running and the browser is connected (check the status dot in the header).
  • Wrong workspace shown: The MCP uses server.listRoots() to detect the workspace. Restart your MCP client if the workspace changed.
  • Elements missing after restart: Check ~/.excalidraw-mcp/excalidraw.db exists. If you previously ran the upstream (in-memory) version, data wasn't persisted.
  • Port conflict with multiple instances: Set different CANVAS_PORT values for each workspace, or rely on multi-tenancy (same port, different tenants).
  • Updates/deletes fail after batch creation: Ensure you are on a build that includes the batch id preservation fix.

Known Issues / TODO

All previously listed bugs from the upstream have been fixed. Remaining items:

  • Image export requires a browser: export_to_image and get_canvas_screenshot rely on the frontend rendering. The canvas UI must be open in a browser.
  • export_to_excalidraw_url blocked: Organizations that block excalidraw.com cannot use shareable URL export. Use export_scene for local .excalidraw files instead.

Contributions welcome!

Development

# Type check
pnpm run type-check

# Full build (frontend + server)
pnpm run build

# Dev mode (watch)
pnpm run dev

Database

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
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

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.