MCP Hub
Back to servers

tldraw-mcp

Minimal MCP server for editing tldraw .tldr files via JSON manipulation. Headless, no browser needed.

glama
Stars
1
Updated
May 2, 2026
Validated
May 11, 2026

tldraw-mcp

Minimal MCP server for editing tldraw .tldr files via JSON manipulation. Headless, no browser needed.

Status

Working skeleton. Schema validation is wired (@tldraw/tlschema validators run before every write), fractional indexing uses @tldraw/utils, file writes are guarded by proper-lockfile. Output verified end-to-end against the real tldraw runtime via Store.loadStoreSnapshot() in the contract test layer.

Quick Start

Requires Node ≥ 20.

1. Install (user scope — available in every project):

claude mcp remove tldraw-m9810223 -s user 2>/dev/null; rm -rf ~/.npm/_npx
claude mcp add -s user tldraw-m9810223 -- npx -y github:m9810223/tldraw-mcp

Restart Claude Code. /mcp should now list tldraw-m9810223.

2. Try the demo prompt in Claude Code:

Draw the Shai-Hulud supply-chain attack flow described in this article with MCP `tldraw-m9810223`, save to `./demo.tldr`:
https://semgrep.dev/blog/2026/malicious-dependency-in-pytorch-lightning-used-for-ai-training/

- Match the actors and how data/control flows between them
- Polish the result at the end

3. View the result — drop ./demo.tldr onto tldraw.com, or use the tldraw VS Code extension for live preview.

Shai-Hulud supply-chain attack flow recreated by tldraw-mcp

Source file: docs/demo.tldr

Tools

File / page lifecycle

ToolWhat it does
create_empty_fileCreate a fresh .tldr with a default page
create_pageAdd a new page
list_pagesList pages with id, name, ordering index
move_to_pageMove shapes; bindings: 'error' | 'pull' | 'cut' controls binding handling

Shapes

ToolWhat it does
create_rectCreate a rectangle (geo shape)
create_textCreate a text shape
create_groupGroup shapes by reparenting them
ungroupDissolve a group, reparenting its children to the group's parent
connectArrow + bindings between two same-page shapes
list_shapesList shapes — id, type, x, y, label only
get_shapeFull record of one shape by id
update_shapeShallow-merge patch (use nested { "props": {...} } for prop edits)
delete_shapeDelete by id; cascade: true (default) also removes attached arrows + bindings

Layout / text fitting

ToolWhat it does
fit_to_textResize a geo/text shape to fit its current text content
alignAlign shapes on an axis (left/right/top/bottom/center-x/center-y)
distributeEven-space shapes between the outermost two
auto_layoutLay shapes out in a horizontal/vertical chain
graph_layoutDagre layout for arrow-connected shapes (best for non-chain topologies)
measure_arrow_labelsReport label sizes + endpoint distances for labeled arrows
bend_overlapping_arrowsBend parallel arrows (same shape pair) symmetrically; priority[] keeps important arrows straight. graph_layout calls this automatically.
polish_layoutOne-shot finisher: fit_to_text every node + graph_layout (auto-bends arrows). Use as the last step after building a fresh diagram.

Discovery & escape hatch

ToolWhat it doesToken cost
search_apiList supported shape types + curated required props. Pass {type, verbose:true} to dump live prop names from @tldraw/tlschema for any type (including ones not in the curated list)low / medium
exec_jqRun a jq filter against the file. write=true persists (auto-checkpoint first)varies

Checkpoints (safety)

ToolWhat it doesToken cost
save_checkpointCopy .tldr to a timestamped backuplow
list_checkpointsList backups, newest firstlow
restore_checkpointRestore a backup (most recent if checkpoint omitted)low

The token-saving design: tools take primitive args, return ids or ok. The full JSON only enters context when you call get_shape deliberately.

Install / Update / Remove

jq only needed for exec_jq (brew install jq / apt-get install jq).

# Install — user scope: every project on this machine (recommended)
claude mcp remove tldraw-m9810223 -s user 2>/dev/null; rm -rf ~/.npm/_npx
claude mcp add -s user tldraw-m9810223 -- npx -y github:m9810223/tldraw-mcp

# Install — local scope (default): only the current project directory
claude mcp remove tldraw-m9810223 2>/dev/null; rm -rf ~/.npm/_npx
claude mcp add tldraw-m9810223 -- npx -y github:m9810223/tldraw-mcp

The first arg (tldraw-m9810223) is the local server name — pick whatever you like, then refer to it the same way in subsequent commands. Restart Claude Code, then /mcp lists it with 26 tools.

Wire up to other MCP clients

Same JSON shape, different config file location:

ClientConfig path
Claude Desktop~/Library/Application Support/Claude/claude_desktop_config.json (macOS)
Cursor~/.cursor/mcp.json
VS Code.vscode/mcp.json

Bootstrapping a .tldr file

Use the create_empty_file tool, or save an empty canvas from tldraw.com and point tools at the absolute path.

create_empty_file({ file: "/tmp/demo.tldr" })

Viewing / editing the output

ToolNotes
tldraw.comDrag the .tldr file onto the page
tldraw VS Code extensionNative .tldr preview + edit inside VS Code; survives file changes from the MCP server
Tldraw Desktop / official editorDrop the file in

The VS Code extension is the smoothest dev loop — keep code path/to/file.tldr open in a tab while the MCP edits it; the editor refreshes on disk change.

Design comparison vs official tldraw-mcp-app

The Cloudflare-hosted official MCP exposes only search + exec (run any JS in a live tldraw Editor). This skeleton goes the opposite way — typed JSON edits over .tldr files — and borrows the discovery pattern (search_api) and escape hatch (exec_jq) so an LLM can fall through when typed tools don't cover an operation.

Official tldraw-mcp-appThis skeleton
Transportstreamable-http + sse (Cloudflare)stdio (works in Claude Code directly)
RuntimeReal tldraw Editor in widget iframePure Node, edits raw JSON
Tools2 (search, exec) + checkpoints26: file/page (4) + shape (9) + layout (8) + discovery (2) + ckpt (3)
Live previewYes (widget iframe)No (open the file in tldraw to view)
CoverageWhole Editor APIGeo / text / arrow + jq escape hatch

Known gaps

  • index (z-order) only supports appending above the current max — no insert-between
  • No image / video / asset support
  • Schema version pinning is informational only — opening a file in a newer tldraw may trigger migrations
  • search_api curated list is hand-maintained alongside the live @tldraw/tlschema reflection

Architecture

src/
  index.ts       MCP server entry, tool registration (stdio transport)
  tools.ts       Tool handlers + zod input schemas
  shapes.ts      tldraw record factories (geo/text/arrow/group/binding)
  store.ts       Load/save .tldr + withFileLock; helpers (id gen, indexing, find, page-of-shape, bindings-for-shape)
  template.ts    Empty .tldr generator using @tldraw/tlschema serialize()
  validate.ts    validateShape / validateBinding using createShapeValidator + createBindingValidator
  checkpoint.ts  Timestamped backups under .tldraw-mcp-checkpoints/
  jq.ts          Shell-out to jq for the exec_jq escape hatch
  text-metrics.ts Text size heuristics for autoFit + label measurement
  graph-layout.ts Dagre wrapper for graph_layout
  arrow-bending.ts Detect parallel-edge overlap + assign symmetric bend values

test/
  unit/          store + validate + text-metrics + arrow-bending (34 tests)
  integration/   tools end-to-end on tmp .tldr (39 tests)
  contract/      loadStoreSnapshot against real @tldraw/store (4 tests)
  boundary/      N-1 / N / N+1 limits + concurrent writes (89 tests)

Pure JSON manipulation — no @tldraw/store, no DOM, no React.

Reviews

No reviews yet

Sign in to write a review