terminal-excalidraw
v0.1.1
Published
Excalidraw in a terminal pane via terminal-browser, with an MCP server for agents to draw on the live scene
Downloads
298
Maintainers
Readme
terminal-excalidraw
The full Excalidraw whiteboard running in your terminal, rendered by terminal-browser, with an MCP server so an agent can read and draw on the same scene you are looking at.
- You draw with the mouse in a terminal pane (kitty graphics protocol).
- An agent connects over MCP and adds/edits/deletes elements, exports PNGs/SVGs, and takes scene screenshots — live, while you watch.
- Everything autosaves (debounced 500 ms) to a standard
.excalidrawfile you can open anywhere.

Run it
One prereq — terminal-browser:
curl -fsSL https://terminal-browser.sh/install | bashThen, in any kitty-graphics terminal (Node ≥ 22):
npx terminal-excalidraw # opens ./drawing.excalidraw (created if missing)
npx terminal-excalidraw board.excalidraw # or a specific fileThe package ships fully bundled (zero runtime dependencies, ~2.7 MB tarball), so npx starts fast. The launcher starts a loopback-only app server on a random port with a random bridge token, then opens the page with terminal-browser open --app-mode. Closing the browser (Ctrl+Q) flushes the scene to the file and shuts everything down.
From source instead: pnpm install && pnpm build && ./bin/terminal-excalidraw demo.excalidraw.
Usage
terminal-excalidraw [file.excalidraw] # default: ./drawing.excalidraw, created if missing
terminal-excalidraw --help- Draw with the mouse like normal Excalidraw: pick tools from the toolbar (or keys
1–0,rrectangle,oellipse,aarrow,ttext), drag to draw,Ctrl+Z/Ctrl+Shift+Zundo/redo, scroll/pinch to pan/zoom. - Autosave: every change lands in the
.excalidrawfile after a 500 ms debounce;Ctrl+Qflushes and quits. The file is plain Excalidraw JSON — it opens on excalidraw.com too. - Let an agent draw: hook up the MCP server below, then ask your agent things like "add a flowchart of the deploy pipeline to my whiteboard" or "screenshot the scene and describe it". Its edits appear live in your pane, and your
Ctrl+Znever undoes the agent's work — only your own strokes.
Example agent tool calls:
// add_elements — skeletons; label creates bound text, start/end bind arrows
{ "elements": [
{ "type": "rectangle", "id": "api", "x": 100, "y": 100, "width": 240, "height": 100, "label": { "text": "API" } },
{ "type": "ellipse", "id": "db", "x": 500, "y": 300, "label": { "text": "DB" } },
{ "type": "arrow", "x": 340, "y": 200, "label": { "text": "reads" }, "start": { "id": "api" }, "end": { "id": "db" } }
] }
// update_elements
{ "patches": [{ "id": "<element-id>", "backgroundColor": "#ffd43b", "fillStyle": "solid" }] }
// export_png
{ "path": "./diagram.png" }MCP
Point your MCP client at the bundled stdio server:
{
"mcpServers": {
"terminal-excalidraw": {
"command": "npx",
"args": ["-y", "--package=terminal-excalidraw", "terminal-excalidraw-mcp"]
}
}
}(From a source checkout: "command": "node", "args": ["/abs/path/dist/mcp.cjs"].)
Tools:
| Tool | Live session | No session (file mode) |
| --- | --- | --- |
| read_scene | live scene JSON | reads the .excalidraw file |
| add_elements | skeletons via convertToExcalidrawElements (labels, arrow bindings) | minimal converter: rectangle/ellipse/diamond/text/line/arrow, no labels/bindings |
| update_elements | patch by id | patch by id in the file |
| delete_elements | by id, bound text follows its container | by id |
| export_png / export_svg | rendered in the editor, written to a path | clean error |
| screenshot | current scene as an MCP image block (exportToBlob) | clean error |
Live mode is detected by the unix socket at $XDG_RUNTIME_DIR/terminal-excalidraw.sock. In file mode, pass the file as an argument (node dist/mcp.cjs path/to/file.excalidraw) or set TERMINAL_EXCALIDRAW_FILE.
Keybindings
All of Excalidraw's own shortcuts work (the browser runs with --no-shortcuts, so keys go to the page). Added:
| Key | Action |
| --- | --- |
| Ctrl+Q | Quit: flush scene to file, close the pane, stop the server |
Theme sync
The preload reads the terminal palette (terminalBrowser.theme() / onTheme) and posts it to the page. Background luminance picks Excalidraw's dark/light theme, and the canvas color is set so the visible canvas exactly matches your terminal background — in dark mode that means storing the preimage of the background under Excalidraw's invert(93%) hue-rotate(180deg) canvas filter. Change your terminal theme mid-session and the canvas follows live.
Remote: draw locally, serve remotely
terminal-browser's SSH mode runs the page locally and proxies its network through the remote host, so the app server can live on another machine:
# one-time: put a built checkout on the remote (needs node >= 22 there)
rsync -a --exclude node_modules . user@host:~/terminal-excalidraw && ssh user@host 'cd terminal-excalidraw && pnpm install --prod=false && pnpm build'
# from your local terminal: run the server remotely, render locally
ssh user@host 'cd terminal-excalidraw && node dist/cli.cjs board.excalidraw' & # prints nothing; server owns the file remotely
terminal-browser open --ssh user@host --app-mode "http://127.0.0.1:<port>/?token=<token>"For a self-contained install-and-run flow, --ssh-bundle <dir> copies a bundle to the remote (default ${XDG_DATA_HOME:-~/.local/share}/terminal-browser/bundles, override with --ssh-bundle-dir) and executes it there, pairing with --app-mode + --ssh. Note: the MCP server must then also run on the remote host (the unix socket lives where the app server runs).
Known limits
screenshot/export_*render the scene content bounds, not a literal viewport crop; an empty scene screenshots as a flat background-color canvas.- File-mode
add_elementsrejectslabel/start/endskeleton features — those need the live editor. - One live session per machine (single unix socket path; a stale socket from a crash is reclaimed on next launch).
- The terminal-background canvas color is written into the
.excalidrawfile'sviewBackgroundColoron theme sync, replacing whatever the file had. - The SSH recipe above is written from terminal-browser's documented flags; it has not been exercised end-to-end in this repo's proof runs.
Attribution & license
MIT (see LICENSE).
- Excalidraw — MIT © Excalidraw contributors. This project embeds the unmodified
@excalidraw/excalidrawpackage. - terminal-browser — MIT © zenbu-labs. This project drives it purely through its public CLI and preload API.
