# Excalidraw MCP Sentinel
[](https://github.com/celstnblacc/excalidraw-mcp-sentinel/actions/workflows/ci.yml)
[](https://github.com/celstnblacc/excalidraw-mcp-sentinel/actions/workflows/release.yml)
[](LICENSE)
A **hardened**, fully local, self-hosted Excalidraw MCP server with **SQLite persistence**, **multi-tenancy**, **auto-sync**, and **production-grade security** — 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
- **Security Hardened**: Helmet, rate limiting, API key auth, prototype pollution guard, WS challenge-response
- **446 Tests**: Full test coverage across unit, API, WebSocket, and regression tests
## Why this fork?
Forked from [celstnblacc/excalidraw-mcp-sentinel](https://github.com/celstnblacc/excalidraw-mcp-sentinel) (itself a fork of [yctimlin/mcp_excalidraw](https://github.com/yctimlin/mcp_excalidraw)) with production hardening:
- **446 tests** (upstream has none) — unit, API, WebSocket, and regression
- **Security middleware** (`src/security.ts`): helmet, CORS allowlist, timing-safe API key auth, prototype pollution guard, input sanitization
- **3-tier rate limiting**: general, destructive, and write-burst ceilings
- **WebSocket challenge-response authentication**
- **Docker hardening**: non-root user, resource limits, hardened `.dockerignore`
- **Full gauntlet security audit pass**
Full credit to [@sanjibdevnathlabs](https://github.com/sanjibdevnathlabs) and [@yctimlin](https://github.com/yctimlin) for the excellent foundation. See [What Changed From Upstream](#what-changed-from-upstream) for the full diff.
Keywords: Excalidraw MCP server, AI diagramming, local Excalidraw, self-hosted, SQLite persistence, multi-tenant, Mermaid to Excalidraw, security hardened.
## 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](https://youtu.be/ufW78Amq5qA).
## Table of Contents
- [Screenshots](#screenshots)
- [Prerequisites](#prerequisites)
- [Quick Start](#quick-start)
- [Configuration](#configuration)
- [Verify Installation](#verify-installation)
- [Updating](#updating)
- [How We Differ from the Official Excalidraw MCP](#how-we-differ-from-the-official-excalidraw-mcp)
- [What Changed From Upstream](#what-changed-from-upstream)
- [Architecture](#architecture)
- [Environment Variables](#environment-variables)
- [Multi-Tenancy (Workspaces)](#multi-tenancy-workspaces)
- [Agent Skill (Optional)](#agent-skill-optional)
- [MCP Tools (32 Total)](#mcp-tools-32-total)
- [Testing](#testing)
- [Troubleshooting](#troubleshooting)
- [Known Issues / TODO](#known-issues--todo)
- [Development](#development)
- [Similar Project Scan](#similar-project-scan)
- [Credits](#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:**
```bash
xcode-select --install
```
**Ubuntu / Debian:**
```bash
sudo apt install build-essential python3
```
**Windows:**
Install "Desktop development with C++" from [Visual Studio Build Tools](https://visualstudio.microsoft.com/visual-cpp-build-tools/).
> `better-sqlite3` ships 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.
```bash
npx excalidraw-mcp-sentinel setup
```
Example session
```
$ npx excalidraw-mcp-sentinel 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
```bash
git clone https://github.com/celstnblacc/excalidraw-mcp-sentinel.git
cd excalidraw-mcp-sentinel
npm install
npm run build
```
Then configure your MCP client — see [Configuration](#configuration).
To run manually (outside an MCP client):
```bash
node dist/index.js
```
Open `http://localhost:3000` in your browser.
### Path C: Docker
Canvas server:
```bash
docker run -d -p 3000:3000 --name mcp-excalidraw-canvas celstnblacc/excalidraw-mcp-sentinel-canvas:latest
```
MCP server (stdio) is typically launched by your MCP client:
```json
{
"mcpServers": {
"excalidraw-canvas": {
"command": "docker",
"args": [
"run", "-i", "--rm",
"-e", "CANVAS_PORT=3000",
"celstnblacc/excalidraw-mcp-sentinel: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):
```json
{
"mcpServers": {
"excalidraw-canvas": {
"command": "npx",
"args": ["-y", "excalidraw-mcp-sentinel"],
"env": {
"CANVAS_PORT": "3000"
}
}
}
}
```
Or, if installed from source:
```json
{
"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`:
```json
{
"mcpServers": {
"excalidraw-canvas": {
"command": "npx",
"args": ["-y", "excalidraw-mcp-sentinel"],
"env": {
"CANVAS_PORT": "3000"
}
}
}
}
```
### Claude Code
```bash
claude mcp add excalidraw-canvas --scope user \
-e CANVAS_PORT=3000 \
-- npx -y excalidraw-mcp-sentinel
```
### Codex CLI
Add to `~/.codex/mcp.json`:
```json
{
"mcpServers": {
"excalidraw-canvas": {
"command": "npx",
"args": ["-y", "excalidraw-mcp-sentinel"],
"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_image` and `get_canvas_screenshot` rely on the frontend. Open `http://localhost:3000` in a browser.
## Verify Installation
After configuring your MCP client, verify everything works:
```bash
# 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](#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)
```bash
npx excalidraw-mcp-sentinel@latest update
```
Example session
```
$ npx excalidraw-mcp-sentinel@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:
1. **Finds all existing skill installations** across Cursor, Claude Code, and Codex CLI (both global and local scopes)
2. **Updates them in-place** with the latest skill files (SKILL.md, cheatsheet, geometric-thinking reference, helper scripts)
3. **Offers to install** the skill for any detected agent that doesn't have it yet
4. **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 excalidraw-mcp-sentinel`, npx caches the package locally and won't automatically fetch new versions.
**Option A — Clear the cache (one-time):**
```bash
npm cache clean --force
```
Then 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`:
```json
{
"mcpServers": {
"excalidraw-canvas": {
"command": "npx",
"args": ["-y", "excalidraw-mcp-sentinel@latest"],
"env": { "CANVAS_PORT": "3000" }
}
}
}
```
This ensures npx always checks for the newest published version.
#### From-source users
```bash
cd mcp-excalidraw-local
git pull origin main
npm install
npm run build
```
#### Docker users
```bash
docker pull celstnblacc/excalidraw-mcp-sentinel:latest
docker pull celstnblacc/excalidraw-mcp-sentinel-canvas:latest
```
Then 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:
```bash
cp -R skills/excalidraw-skill ~/.cursor/skills/excalidraw-skill
cp -R skills/excalidraw-skill ~/.claude/skills/excalidraw-skill
```
### Verify the update
```bash
# Check the running version
curl -s http://localhost:3000/health
# Or check the installed package version
npx excalidraw-mcp-sentinel --version
```
## How We Differ from the Official Excalidraw MCP
Excalidraw now has an [official MCP](https://github.com/excalidraw/excalidraw-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](https://github.com/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.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.
## 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` |
| `EXCALIDRAW_API_KEY` | Shared secret for API key auth on all `/api/*` routes. When unset, auth is disabled (dev mode). | _(unset — auth off)_ |
| `ALLOWED_ORIGINS` | Comma-separated list of allowed CORS + WebSocket origins | `http://localhost:3000,http://127.0.0.1:3000` |
| `EXCALIDRAW_RATE_LIMIT_GENERAL_MAX` | Override the general API rate-limit ceiling (requests per 15-minute window) | `100` |
| `EXCALIDRAW_RATE_LIMIT_DESTRUCTIVE_MAX` | Override the destructive-operation rate-limit ceiling (requests per 1-minute window) | `10` |
| `EXCALIDRAW_RATE_LIMIT_WRITE_BURST_MAX` | Override the sync write-burst rate-limit ceiling (requests per 1-minute window) | `10` |
### Security configuration
**Enabling API key protection** (recommended for any network-accessible deployment):
```bash
EXCALIDRAW_API_KEY=your-secret-here node dist/server.js
```
All requests to `/api/*` must then include the header `X-API-Key: your-secret-here`. The `/health` endpoint is always exempt.
When `EXCALIDRAW_API_KEY` is set, the browser canvas UI receives the key automatically: `GET /` injects `window.__EXCALIDRAW_API_KEY__` into the served HTML, so the browser's WebSocket `hello` message can include it without any manual configuration. The WebSocket handshake uses a challenge-response protocol: the server sends `{ type: "auth_required" }` immediately on connect, the client must respond with a `hello` message containing `{ apiKey: "" }` within 5 seconds, or the connection is closed (code 4001).
**Restricting CORS origins** (e.g. if your canvas UI is on a custom domain):
```bash
ALLOWED_ORIGINS=https://canvas.example.com,http://localhost:3000 node dist/server.js
```
This controls both REST CORS responses and WebSocket `Origin` verification. Requests with no `Origin` header (MCP stdio, curl, server-side tools) are always allowed.
## 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
### Install via Setup Wizard
The easiest way to install the skill:
```bash
npx excalidraw-mcp-sentinel setup
```
The 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:
```bash
# 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-skill
```
## 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`.
> ⚠️ **Requires open browser:** `get_canvas_screenshot` and `export_to_image` rely on the frontend rendering pipeline. The canvas UI must be open in a browser tab at `http://localhost:3000` for these tools to work. They return HTTP 503 if no browser is connected.
## Testing
### Health check
```bash
curl http://localhost:3000/health
```
### MCP Inspector
List tools:
```bash
npx @modelcontextprotocol/inspector --cli \
-e CANVAS_PORT=3000 -- \
node dist/index.js --method tools/list
```
Create a rectangle:
```bash
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
```
## Troubleshooting
### `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 install` fails with `gyp ERR!` or `prebuild-install` errors
- `npx` command fails during installation
- Error: `Cannot find module 'better-sqlite3'` at runtime
**Fix:**
1. Install build tools for your platform (see [Prerequisites](#prerequisites))
2. Rebuild the module:
```bash
npm rebuild better-sqlite3
```
3. Or run the setup wizard which handles this automatically:
```bash
npx excalidraw-mcp-sentinel setup
```
### EADDRINUSE (port already in use)
**Symptom:** Error `listen EADDRINUSE: address already in use :::3000`
**Fix:**
```bash
# Find what's using the port
lsof -i :3000
# Either kill the process or use a different port
CANVAS_PORT=3001 node dist/index.js
```
> The 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:**
```bash
npm run build # builds both frontend and server
node dist/index.js # restart
```
### NVM / path issues with npx
**Symptom:** `npx excalidraw-mcp-sentinel` hangs or uses the wrong Node version.
**Fix:**
```bash
# 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 path
```
Then use the full path in your MCP config:
```json
{
"mcpServers": {
"excalidraw-canvas": {
"command": "/Users/you/.nvm/versions/node/v22.12.0/bin/npx",
"args": ["-y", "excalidraw-mcp-sentinel"],
"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_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
```bash
# Type check
npm run type-check
# Full build (frontend + server)
npm run build
# Dev mode (watch)
npm run dev
```
### Similar Project Scan
Use the built-in scanner to look for repositories that are architecturally similar to this project. The scan is capability-based, not fork-based: it looks for Excalidraw plus MCP, backend sync, persistence, security, and self-hosting signals.
Basic run:
```bash
npm run scan:similar-projects
```
Broader competitor scan excluding this repo's direct lineage:
```bash
npm run scan:similar-projects -- \
--exclude-repo yctimlin/mcp_excalidraw \
--exclude-repo sanjibdevnathlabs/mcp-excalidraw-local \
--exclude-repo celstnblacc/excalidraw-mcp-sentinel
```
Outputs are written to `docs/generated/` as both JSON and Markdown reports. For higher GitHub API limits, set `GITHUB_TOKEN` before running the scan.
> Note: rerun the scan after significant product or architecture changes. The ranking is based on the current shape of this repo, so active work on MCP features, persistence, security, or backend topology can materially change which repos are the closest matches.
See also:
- [Top repo comparison](docs/COMPARISON-excalidraw-top-repos.md)
- [Search strategy](docs/GUIDE-excalidraw-similar-project-search.md)
### 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 (auth-exempt) |
| GET | `/api/elements` | List all elements |
| POST | `/api/elements` | Create an element |
| GET | `/api/elements/search` | Search elements (`?q=term` for FTS, `?type=rectangle` for filter) |
| GET | `/api/elements/:id` | Get element by ID |
| PUT | `/api/elements/:id` | Update an element |
| DELETE | `/api/elements/:id` | Delete an element |
| DELETE | `/api/elements/clear` | Clear all elements (requires `?confirm=true`) |
| POST | `/api/elements/batch` | Batch create elements |
| POST | `/api/elements/from-mermaid` | Convert Mermaid diagram and broadcast to canvas |
| POST | `/api/elements/sync` | Bulk-replace all elements (canvas → server) |
| POST | `/api/elements/sync/v2` | Delta sync (changes since `lastSyncVersion`) |
| GET | `/api/sync/version` | Current sync version for a project |
| GET | `/api/sync/status` | Sync status (element count, memory usage) |
| GET | `/api/files` | List image files (in-memory) |
| POST | `/api/files` | Add image files |
| DELETE | `/api/files/:id` | Delete an image file |
| POST | `/api/export/image` | Request image export (requires open browser tab) |
| POST | `/api/export/image/result` | Deliver export result from frontend |
| POST | `/api/viewport` | Set canvas viewport (requires open browser tab) |
| POST | `/api/viewport/result` | Deliver viewport result from frontend |
| POST | `/api/snapshots` | Save a named snapshot |
| GET | `/api/snapshots` | List snapshots |
| GET | `/api/snapshots/:name` | Get snapshot by name |
| GET | `/api/projects` | List projects for the active tenant |
| POST | `/api/projects` | Create a new project |
| PUT | `/api/project/active` | Switch the active project |
| DELETE | `/api/projects/:id` | Delete a project (cascades elements) |
| 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. When `EXCALIDRAW_API_KEY` is set, all `/api/*` endpoints require `X-API-Key: ` (see [Security configuration](#security-configuration)).
## Credits
This project is forked from [yctimlin/mcp_excalidraw](https://github.com/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](https://github.com/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](LICENSE).