excalidraw-room-mcp
excalidraw-room-mcp
An MCP server that joins a live Excalidraw collaboration room as a headless participant, so an AI agent can read what you draw and draw back, in the same canvas, in real time.
You draw on excalidraw.com as normal. The agent sees your boxes, arrows, text and freehand strokes, and its edits appear on your screen as it makes them. No credits, no export step, no browser automation.
Why
The official excalidraw-mcp renders a drawing inside the chat window. That is one-way: the agent draws, you look. This server is the other direction. It attaches to a drawing you already have open and keeps both sides in sync.
It works because Excalidraw's collaboration protocol is open. The relay server (excalidraw-room) forwards encrypted blobs between participants and never sees plaintext. The collaboration link carries the room id and the AES key, so anyone holding the link can join, decrypt, and take part. This server does exactly that.
Install
Requires Node 22 or newer.
git clone https://github.com/bjcoombs/excalidraw-room-mcp.git
cd excalidraw-room-mcp
npm install
npm run buildRegister with Claude Code:
claude mcp add excalidraw-room -- node /absolute/path/to/excalidraw-room-mcp/dist/index.jsOr with any other MCP client that speaks stdio, using the same command.
Use
On excalidraw.com, click Live collaboration, then Start session, and copy the link. It looks like
https://excalidraw.com/#room=<id>,<key>.Ask the agent to join it: "join this excalidraw room: ".
Draw. Ask the agent what it sees, or ask it to add things.
Or the other way round: ask the agent to create a room, and open the link it gives you.
Tools
Tool | What it does |
| Make a new empty room, join it, return the link to open. |
| Join a room from its link. Loads the scene from a peer, or from the persisted copy if nobody else is there. |
| Connection state, peers, element counts. |
| The drawing as one line per element (default), or the full element JSON. Freehand strokes come back as a sampled path so a scribble is legible. |
| Add shapes, text, arrows, lines and freehand strokes from compact specs. Arrows bind to element ids; edge points are computed. |
| Add complete Excalidraw elements verbatim, for example from an |
| Patch elements by id. Versions are bumped so peers accept the change. |
| Soft-delete by id. |
| Disconnect. |
Example
add_elements:
- {type: rectangle, id: api, x: 0, y: 0, width: 160, height: 80, label: "API"}
- {type: ellipse, id: db, x: 320, y: 0, width: 160, height: 80, label: "Postgres"}
- {type: arrow, start: api, end: db, label: "query"}read_scene afterwards:
api rectangle @(0,0) 160x80 "API"
db ellipse @(320,0) 160x80 "Postgres"
Kp3... arrow 2 pts: (156,40) -> (324,40) from api to db "query"How it works
Transport: socket.io to
https://oss-collab.excalidraw.com, the relay excalidraw.com uses. The public relay rejects handshakes that do not carry anOrigin: https://excalidraw.comheader, so the client sends one. OverrideserverUrlandoriginonjoin_roomto point at a self-hostedexcalidraw-room.Encryption: AES-128-GCM with the key from the link, matching
packages/excalidraw/data/encryption.tsupstream. Implemented on Node's WebCrypto so the server does not depend on the browser-oriented@excalidraw/excalidrawpackage.Merging: Excalidraw's reconcile rule, per element id: higher
versionwins, ties go to the lowerversionNonce. Local edits bump both, exactly as the web app does, so peers accept them.Z-order: fractional indices via the same
fractional-indexinglibrary upstream uses. New elements go on top.Persistence: excalidraw.com keeps each room's encrypted scene in a public Firestore document. On joining an empty room the server reads it. After every write it saves the reconciled scene back, conditional on the document's update time, so a stale copy never overwrites a newer one. On a conflict it reloads, reconciles, and retries once. If the save still fails, the change has already reached connected peers and their browsers persist it on their normal schedule.
Limits
Images and other file attachments are out of scope. They travel by a separate path and are not needed for diagrams.
Text is measured by approximation, not a real font. Labels may be slightly wider or narrower than the web app would make them; the app re-measures on the next edit.
The public relay is not a documented API for third parties. The protocol is open source and stable in practice, but nobody has promised to keep it that way.
One room per server process. Run a second instance for a second room.
Security
The room key is the only secret, and it is in the link. The server uses it locally to encrypt and decrypt; it is never sent anywhere. Treat collaboration links as you would a password to that drawing.
Development
npm test # build + unit tests
EXCALIDRAW_ROOM_DEBUG=1 node dist/index.js # run with diagnostics on stderrLicense
MIT. Excalidraw itself is MIT, and the protocol details here are derived from its source.
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/bjcoombs/excalidraw-room-mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server