- JavaScript 48.9%
- TypeScript 47.2%
- HTML 3.1%
- Dockerfile 0.8%
|
|
||
|---|---|---|
| .forgejo/workflows | ||
| frontend | ||
| skills/excalidraw-skill | ||
| src | ||
| .dockerignore | ||
| .gitignore | ||
| claude_desktop_config.json | ||
| demo.gif | ||
| docker-compose.yml | ||
| Dockerfile | ||
| Dockerfile.canvas | ||
| LICENSE | ||
| package-lock.json | ||
| package.json | ||
| README.md | ||
| tsconfig.json | ||
| vite.config.js | ||
Excalidraw MCP Server
A self-hosted Excalidraw canvas with MCP (Model Context Protocol) integration, OIDC authentication, and real-time collaboration. Fork of yctimlin/mcp_excalidraw with significant additions for production deployment.
What This Fork Adds
- OIDC Authentication — Login via Authentik (or any OIDC provider) with PKCE, signed session cookies
- API Token Auth —
MCP_AUTH_TOKENbearer token for MCP agent access to write APIs - View-Only Public Access — Unauthenticated visitors can watch the canvas in real-time (pan/zoom only)
- Dark Mode — Default dark theme with persistence across reloads
- Server-Side Libraries — Mount
.excalidrawlibfiles and they auto-load for all users - Library Persistence — Library items saved to localStorage, survive page reloads
- Production Docker Setup — Healthcheck, reverse proxy support, hairpin NAT workarounds
Quick Start (Docker)
# compose.yaml
services:
excalidraw-canvas:
image: git.bros.ninja/mike/mcp_excalidraw/canvas:latest
ports:
- 8942:3000
environment:
- NODE_ENV=production
- PORT=3000
- HOST=0.0.0.0
- LIBRARIES_DIR=/libraries
- MCP_AUTH_TOKEN=${MCP_AUTH_TOKEN}
# OIDC (optional — omit to disable)
- OIDC_ISSUER_URL=${OIDC_ISSUER_URL:-}
- OIDC_CLIENT_ID=${OIDC_CLIENT_ID:-}
- OIDC_SECRET=${OIDC_SECRET:-}
- OIDC_BASE_URL=${OIDC_BASE_URL:-}
- OIDC_COOKIE_SECRET=${OIDC_COOKIE_SECRET:-}
volumes:
- ./libraries:/libraries:ro
restart: unless-stopped
healthcheck:
test:
[
"CMD-SHELL",
'node -e "fetch(''http://localhost:3000/health'').then(r=>{if(!r.ok)process.exit(1)}).catch(()=>process.exit(1))"',
]
interval: 30s
timeout: 10s
retries: 3
# Generate an auth token
openssl rand -hex 32 > .env
echo "MCP_AUTH_TOKEN=$(cat .env)" > .env
docker compose up -d
Open http://localhost:8942.
Quick Start (Local)
npm ci
npm run build
# Terminal 1: canvas server
PORT=3000 npm run canvas
# Terminal 2: MCP server (stdio)
EXPRESS_SERVER_URL=http://localhost:3000 node dist/index.js
Authentication
How It Works
The canvas has three access levels:
| Level | Access | How |
|---|---|---|
| Public | View canvas, pan/zoom, WebSocket updates | No auth needed |
| MCP Agent | Full API read/write | Authorization: Bearer <MCP_AUTH_TOKEN> header |
| OIDC User | Full UI read/write | Login via /auth/login |
All write API routes (POST/PUT/DELETE) require either a valid bearer token or an OIDC session. Read routes (GET, WebSocket) are public.
MCP Agent Token
Set MCP_AUTH_TOKEN in both the canvas container and your MCP client config:
{
"mcpServers": {
"excalidraw": {
"command": "node",
"args": ["/path/to/mcp_excalidraw/dist/index.js"],
"env": {
"EXPRESS_SERVER_URL": "http://localhost:8942",
"ENABLE_CANVAS_SYNC": "true",
"MCP_AUTH_TOKEN": "your-token-here"
}
}
}
}
OIDC (Authentik)
- Create an OAuth2/OIDC Provider in Authentik
- Set redirect URI:
https://your-domain.com/auth/callback - Scopes:
openid profile email - Add to
.env:
OIDC_ISSUER_URL=https://auth.example.com/application/o/excalidraw/
OIDC_CLIENT_ID=your-client-id
OIDC_SECRET=your-client-secret
OIDC_BASE_URL=https://your-domain.com
OIDC_COOKIE_SECRET=$(openssl rand -hex 32)
Works with any standard OIDC provider — not Authentik-specific.
Libraries
Drop .excalidrawlib files into the libraries/ directory (mounted at /libraries in the container). They're automatically loaded for all users on page load and merged with any locally-saved libraries.
Environment Variables
| Variable | Description | Default |
|---|---|---|
PORT |
Canvas server port | 3000 |
HOST |
Bind address | localhost |
EXPRESS_SERVER_URL |
Canvas URL (MCP client) | http://localhost:3000 |
ENABLE_CANVAS_SYNC |
Real-time canvas sync | true |
MCP_AUTH_TOKEN |
Bearer token for MCP agent auth | (none — auth disabled) |
LIBRARIES_DIR |
Path to .excalidrawlib files |
/libraries |
OIDC_ISSUER_URL |
OIDC provider discovery URL | (none — OIDC disabled) |
OIDC_CLIENT_ID |
OIDC client ID | |
OIDC_SECRET |
OIDC client secret | |
OIDC_BASE_URL |
Public URL for redirect URI | |
OIDC_COOKIE_SECRET |
Session cookie encryption key |
MCP Tools (26 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 |
Reverse Proxy Notes
When running behind nginx/caddy with TLS termination:
- The server has
trust proxyenabled —X-Forwarded-Protois respected for cookie security - If your OIDC provider resolves to a public IP that hairpins through your router, add
extra_hoststo point it at the LAN IP:
extra_hosts:
- "auth.example.com:192.168.1.100"
Upstream
Forked from yctimlin/mcp_excalidraw. The upstream provides the core MCP server, canvas UI, WebSocket sync, and 26 MCP tools. This fork adds authentication, dark mode, library management, and production deployment features.
License
MIT