Obsidian Excalidraw MCP
by Razeefshaik
README.md
# Obsidian Excalidraw MCP
A small local [Model Context Protocol](https://modelcontextprotocol.io/) server for inspecting and making targeted edits to Excalidraw class diagrams in an Obsidian vault. It is designed for LLD practice. It needs Node.js, not an Obsidian plugin or database.
## Tools
| Tool | Action |
| --- | --- |
| `list_drawings` | Find `.excalidraw` and `.excalidraw.md` drawings under one configured folder. |
| `read_scene` | Return element IDs, text, geometry, bindings, points, and a revision hash. Embedded files and app state are excluded. |
| `summarize_scene` | Return class rectangles, labels, free text, and relationships for LLD review. |
| `edit_scene` | Add, update, move, rewire, resize, or mark elements deleted. Requires a recent revision and creates a backup. |
The edit operations are `add_text`, `update_text`, `add_rectangle`, `resize_rectangle`, `add_arrow`, `rewire_arrow`, `move`, and `delete`. New text can be attached to a rectangle with `containerId`; a new arrow can carry a label.
## Requirements and setup
1. Install [Node.js](https://nodejs.org/) 20 or newer and verify `node --version` and `npm --version` in PowerShell.
2. Clone this repository, open its directory, run `npm ci`, then `npm run build` and `npm test`.
3. Copy `config.example.json` to `config.json`. Set `vaultPath` to the absolute path of your Obsidian vault and `subfolder` to the smallest drawing folder you want the server to access. The folder must exist. `config.json` is ignored by Git.
4. Run `npm start` to start the stdio server. It waits for an MCP client on standard input and output; no web page opens.
Example private config:
```json
{
"vaultPath": "C:\\Users\\YOU\\Documents\\ObsidianVault",
"subfolder": "SystemDesign/LLD"
}
```
Use `subfolder: ""` only if you intentionally want to expose the whole vault. Only connect this server to an AI client you trust with the drawings in that folder.
## Connect a local MCP client
Configure the client to start `node` with the absolute path to `dist/server.js` and set `EXCALIDRAW_MCP_CONFIG` to the absolute path of your private `config.json`. For example, VS Code's `.vscode/mcp.json` can contain:
```json
{
"servers": {
"obsidian-excalidraw": {
"type": "stdio",
"command": "node",
"args": ["C:\\path\\to\\repo\\dist\\server.js"],
"env": {
"EXCALIDRAW_MCP_CONFIG": "C:\\path\\to\\repo\\config.json"
}
}
}
}
```
For Codex Desktop or another stdio MCP client, use the same command, argument, and environment variable in its MCP settings.
## Connect ChatGPT web on Windows
ChatGPT web needs an MCP connection reachable through its [Developer mode](https://developers.openai.com/api/docs/guides/developer-mode). [Secure MCP Tunnel](https://developers.openai.com/api/docs/guides/secure-mcp-tunnels) can connect this local stdio server without opening an inbound public port. Availability and setup screens may change; consult those official guides.
1. Enable Developer mode in ChatGPT and create a tunnel in [OpenAI Platform](https://platform.openai.com/settings/organization/tunnels). Copy your `tunnel_...` ID.
2. Create a restricted Platform runtime key with only Tunnels Read and Use permissions. Do not put it in this repository or paste it into chat. ChatGPT Plus and Platform API billing are separate; check current Platform terms before using a billable service.
3. Download the Windows `tunnel-client` from the [official OpenAI tunnel-client releases](https://github.com/openai/tunnel-client/releases). Verify the published checksum. Put `tunnel-client.exe` at `tools/tunnel-client/tunnel-client.exe`. This binary and its profile directory are ignored by Git.
4. From PowerShell in this repository, run `./start-chatgpt-tunnel.ps1 -TunnelId tunnel_YOUR_ID`. The launcher prompts for the key with hidden input, checks the tunnel, and runs it. Keep the PowerShell window open while using the app.
5. In ChatGPT, create a developer-mode app using the tunnel, review the four tools and their permissions, then enable that app in a chat. Try `list_drawings`, followed by `summarize_scene` on one drawing.
The launcher finds `node` on `PATH` and stores no key in the profile. Its health listener binds to `127.0.0.1` on a random port. The key exists in the tunnel process environment while it runs and is cleared from the launcher environment when it stops. Treat the tunnel ID and runtime key as private operational data.
## Example edit
After a summary or read, use its exact revision:
```json
{
"path": "parking-lot.excalidraw.md",
"expectedRevision": "<64-character hash from summary>",
"edits": [
{ "op": "add_rectangle", "x": 100, "y": 100, "width": 220, "height": 120 },
{ "op": "update_text", "id": "<existing text ID>", "text": "ParkingLot" }
]
}
```
`edit_scene` returns the IDs it changed, the new revision, and the backup path. Read the drawing again before another edit. Close the drawing in Obsidian while applying edits; an open Obsidian view may later save stale content over the server's changes. Review AI proposed edits before sending them to `edit_scene`.
## Supported formats and limits
Raw `.excalidraw` JSON and Obsidian `.excalidraw.md` files with exactly one fenced `## Drawing` block in `json` or `compressed-json` format are supported. The latter uses LZ-String base64, including line-wrapped blocks. The surrounding Markdown is preserved. Markdown text edits require the `## Text Elements` section to contain plain text matching the scene; links, formatted text, and custom block references are rejected before writing. Such files can still be read and reviewed.
Only files in the configured folder are accessible. Paths with traversal, alternate data streams, or links escaping the folder are rejected; file listing skips symlinks and junctions. Reads omit embedded assets and app state. Drawings have a 10 MiB file limit, decoded JSON has a 20 MiB limit, and edits have size and count limits. An edit checks the revision, creates a timestamped backup in `.excalidraw-mcp-backups`, and writes through a temporary file and rename. The server does not create drawings, embedded images, or network requests.
There is still a small race if another program saves a drawing between the final revision check and rename. Keep the Obsidian drawing closed during edits. Backups contain the full original drawing, including any embedded assets, and stay in the configured vault folder; handle them as private vault data. To restore, copy the appropriate `.bak` file over the drawing after closing it in Obsidian.
## Development and release
Run `npm test` for the local tests and `npm audit --omit=dev` for production dependency advisories. See [SECURITY.md](SECURITY.md) for security scope and reporting, [CONTRIBUTING.md](CONTRIBUTING.md) for contributions, and [LICENSE](LICENSE) for the license. Do not commit `config.json`, downloaded tunnel binaries, profiles, vault files, backups, or API keys.
This server cannot be deployed
Maintenance
ActivityMaintained
ResponsivenessNo issues