tldraw-mcp-app
Provides tools for interacting with tldraw canvases, including creating views, patching shapes, managing canvas lifecycle, and editing diagrams.
Click on "Install Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@tldraw-mcp-appCreate a new canvas and add a sticky note"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
Standalone tldraw MCP App
This repository is an independently runnable MCP Server and MCP App
distribution for tldraw. Clients connect to it over strict MCP
2026-07-28; they do not embed this repository's editor or persistence code.
The server does not import OpenChamber or OpenCode. Any compatible client that
supports the MCP Apps UI extension can connect to its Streamable HTTP
endpoint.
The current distribution does not provide a stdio transport or legacy SSE. Run one HTTP service and configure clients with its URL.
Documentation:
The repository intentionally keeps the existing interoperability contract:
standalone distribution version:
1.3.0bundled upstream editor version:
tldraw v5.0.2MCP endpoint:
http://127.0.0.1:39512/mcphealth endpoint:
http://127.0.0.1:39512/healthApp resource:
ui://openchamber/interop-tldraw-contract-v5.0.2model-only contract Tool:
tldraw_read_mepreferred model/App Tools:
tldraw_create_viewandtldraw_patch_shapes(semantic protocol v2)compatible model/App Tools:
tldraw_open_canvasandtldraw_patch_diagrammodel-only catalog/lifecycle Tools:
tldraw_list_canvases,tldraw_archive_canvas,tldraw_unarchive_canvas,tldraw_request_delete_canvas, andtldraw_delete_canvasApp-only Tools:
tldraw_get_canvas_state,tldraw_apply_operations,tldraw_save_canvas,tldraw_export_snapshot,tldraw_list_canvas_revisions,tldraw_restore_canvas_revision, and thetldraw_asset_{begin_upload,append_chunk,commit_upload,read_chunk,abort_upload}content-addressed asset protocolpersistent, revision-fenced, multi-canvas state with bounded exact history
The openchamber segment in the resource URI is retained only as a stable
protocol identity so existing sessions and pins continue to resolve. It is not
a runtime dependency.
This public source repository is provided for inspection, evaluation, and interoperability testing under its all-rights-reserved LICENSE. Public visibility does not make it open source or grant redistribution or production-use rights. Review LICENSE.tldraw.md and THIRD_PARTY_NOTICES.md before any distribution or production use.
Quick start
Requirements: Node.js 22.18 or newer.
npm ci
npm run build
npm startIn another terminal:
npm run status
npm run probe
npm stopnpm start runs a managed background process. Logs, PID metadata, and canvas
state default to ~/.local/share/tldraw-mcp-app on macOS/Linux and
%LOCALAPPDATA%\\tldraw-mcp-app on Windows. It refuses to stop a process it
does not own.
Use an isolated runtime and port for tests:
TLDRAW_MCP_RUNTIME_DIR=/tmp/tldraw-mcp-test \
TLDRAW_MCP_PORT=39513 \
npm start
TLDRAW_MCP_URL=http://127.0.0.1:39513/mcp npm run probe
TLDRAW_MCP_RUNTIME_DIR=/tmp/tldraw-mcp-test \
TLDRAW_MCP_PORT=39513 \
npm stopRelated MCP server: tldraw-mcp
Client connection
The server is transport-level independent of OpenChamber. A compatible client only needs the MCP URL and MCP Apps support. An OpenCode-shaped example is in examples/opencode.json:
{
"mcp": {
"interop-tldraw-2026": {
"type": "remote",
"url": "http://127.0.0.1:39512/mcp",
"oauth": false,
"timeout": 30000,
"enabled": true
}
}
}Existing OpenChamber/OpenCode installations should keep the
interop-tldraw-2026 connection key shown above. Changing only the service URL
is safe; renaming the key can break restoration of historical MCP App parts.
Clients without MCP Apps negotiation may still use the model-facing Tools.
They receive the same validated structuredContent canvas state plus a text
fallback instead of the interactive ui:// surface. App-only Tools are marked
with _meta.ui.visibility: ["app"] and must never be exposed to the model tool
picker.
The package is named @zunbaran/tldraw-mcp-app. It remains marked
private: true to prevent accidental npm publication; that flag is independent
of this GitHub repository's public visibility. The process, data directory,
Docker image, client configuration, and release artifact are standalone.
Transport and capability contract
Capability | Support |
MCP protocol | strict |
Transport | Streamable HTTP at |
MCP Apps |
|
stdio | not implemented |
legacy SSE / legacy initialize | rejected |
OAuth | not implemented by this private loopback service |
The protocol and extension versions are independent. "MCP 2026-07-28"
in this repository refers to the Core protocol and Streamable HTTP wire
contract. "MCP Apps" refers to the separately versioned
io.modelcontextprotocol/ui stable specification dated 2026-01-26.
The bundled View pins @modelcontextprotocol/ext-apps 1.7.5; capabilities
that exist only in the Apps draft are described as draft SDK capabilities,
not as part of Core 2026-07-28.
In particular, ui/download-file is an optional, capability-negotiated API
implemented by the pinned ext-apps SDK. It is not guaranteed by the MCP Apps
stable 2026-01-26 contract. Hosts that do not advertise it still get a fully
usable canvas, but SVG/PNG saving is reported as unavailable rather than being
silently simulated. The same rule applies to the SDK's model-context modality
capability fields: the App filters to advertised modalities, while remaining
compatible with a stable Host that exposes only the base updateModelContext
operation.
/health proves process and artifact identity only. It does not prove that a
client negotiated Apps, filtered App-only Tools, loaded the sandboxed UI, or
connected AppBridge.
Model diagram workflow
tldraw_read_me is a read-only discovery Tool. With a canvasId, it returns
the current canvas and revision when present, plus the exact patch limits and
revision rules.
For new work, use tldraw_create_view with semantic elements protocol v2.
It supports geo shapes, text, notes, arrows, lines, frames, groups, hierarchy,
bindings, style and ordering with stable IDs. Use tldraw_patch_shapes for
later create, update, delete, bind, and reorder transactions. Every
patch carries exact expectedRevision and unique transactionId fences; the
whole final graph commits once or not at all. See
the protocol guide and the checked examples:
tldraw_open_canvas keeps its existing behavior for compatible node/edge
clients: it opens a known canvas, creates one only when createIfMissing is
true, and can seed a new canvas with a bounded initialDiagram.
Use tldraw_patch_diagram for subsequent model-authored edits. Every call must
include the canvas's exact expectedRevision. The nodes and edges objects
each accept upsert, update, and remove arrays. Upserts are complete
items; updates contain an existing id plus only the changed fields. A patch
is applied transactionally, with a maximum of 100 final nodes, 200 final
edges, 100 node operations, and 200 edge operations. Edge endpoints are
validated against the final graph, so a patch can remove a node and its edges,
or add nodes and their connecting edges, in one revision.
Failed validation and stale revisions change nothing. Successful note and diagram operations advance only their target canvas and retain any saved real tldraw editor snapshot.
Semantic and legacy operations share the same canvas revision. The server maintains a compatibility projection instead of creating two independent canvases.
Canvas catalog/lifecycle operations are scoped to the server-configured
workspace. Archive is revision fenced and recoverable. Deletion requires an
already archived canvas plus a separate request that returns the confirmation
token consumed by tldraw_delete_canvas; the record is moved to private
recoverable trash rather than erased. The configured default canvas is
protected from archive and delete.
Canvas identity and persistence
tldraw_open_canvas returns a stable canvasId and revision. Every mutation
must send the same identity plus expectedRevision. Stale or cross-canvas
writes are rejected. Save persists the same canvas; it does not invent a new
canvas ID. tldraw_save_canvas also requires an idempotencyKey, so retrying a
lost response with the same input returns the original committed result rather
than creating another revision. Diagram and note mutations never erase a saved
editor snapshot. The state file is atomically replaced and written with
owner-only permissions.
Inline mode uses lightweight SVG/DOM and never mounts the tldraw editor.
ontoolinputpartial incrementally draws complete, valid items from streaming
Tool arguments; incomplete nodes and dangling edges are withheld until they
are safe to render. Edit requests fullscreen, the only mode that mounts the
verified official tldraw editor. Model-authored changes use the revision-bound
diagram patch Tool; fullscreen Save submits canonical diagram changes,
App-only note operations, and the lossless editor snapshot together in one
atomic tldraw_save_canvas request without changing canvasId.
The editor supports up to 16 lossless pages. Exactly one page carries the
openchamberSemanticPrimaryPage marker: semantic v2 and legacy model Tools
read and patch only that primary page, while Save persists every page, page
record, shape, binding, and document setting. Existing unmarked snapshots are
migrated by preferring the page that already contains canonical semantic
shapes, then the snapshot's active page. Inline mode previews only the primary
page, so pages are never visually overlaid. Fullscreen restores the normal
tldraw page menu; SVG/PNG exports include only the currently selected page and
include its page-name slug in the filename.
Save retry and in-flight edit detection fence the complete snapshot document, not only primary-page shapes. Session camera, selection, and current-page state are excluded from that document fingerprint, and a page switch made while Save is in flight is restored after the accepted snapshot loads. A snapshotless new canvas resets the prior document, page records, assets, and view state before canonical reconciliation. Server-originated resets and reconciliation bypass shape locks and do not enter the user's Undo history.
Each successful revision archives the superseded full canvas state. The Server
retains at most 32 historical revisions per canvas within a 4 MiB aggregate
history budget. tldraw_get_canvas_state can read an exact retained revision,
tldraw_list_canvas_revisions identifies retained and current entries, and
tldraw_restore_canvas_revision copies a retained revision into a new,
monotonically increasing current revision. Restore requires the current
expectedRevision and an idempotencyKey; it never rewinds revision numbers
or merges concurrent work.
A historical Tool result or pin stays on its exact retained revision. It is
labelled read-only even when a newer current revision exists; Refresh latest
is the explicit action that leaves historical review, while Restore creates
the new current revision described above. Separate App instances refresh the
same canvasId on focus, return to visibility, HostContext changes, and a
fixed five-second poll, so chat and pin/workbench copies do not remain silently
stale.
If newer Server state arrives while the editor is dirty, the App preserves the
local draft and queues the authoritative state. There is no automatic merge.
Save is available only for a conflict-free dirty draft; after a concurrent
update it is disabled while Review server shows the semantic delta and
Discard explicitly loads the authoritative revision. Fullscreen exit is
blocked while edits remain open. SVG and PNG use tldraw's real renderers,
record an App-only export receipt, and then call the official
ui/download-file flow. A compatible Host must confirm and perform the
download; a receipt alone is not a downloaded file.
App-only asset protocol
The Server exposes a private, App-only chunk protocol for PNG, JPEG, and WebP
bytes. tldraw_asset_begin_upload binds a declared byte length, MIME type, and
SHA-256 digest to one existing canvasId and returns an upload id plus a
short-lived bearer token. Only that begin result contains the token, and its
human-readable content never repeats it. Append, commit, read, abort, and all
errors return allowlisted fields without filesystem paths.
tldraw_asset_append_chunk accepts canonical base64 and an exact byte offset;
decoded chunks are limited to the asset store's advertised chunkSize
(currently 192 KiB). Commit publishes content only after byte length, digest,
and detected media type all match. Reads return bounded base64 chunks from a
content-addressed mcp-asset:<sha256> object. Asset data lives under the same
configured store root and scope/workspace namespace as canvases and survives a
Server restart.
tldraw_save_canvas accepts an optional, strict assetManifest containing
sha256, mimeType, and byteLength. The Server extracts only asset records
from snapshot.document.store, permits only image records whose props.src is
exactly mcp-asset:<sha256>, and requires the manifest digest set to match those
references exactly without duplicates. Every manifest item must already exist
in the workspace asset store with identical metadata. Omission is compatible
with older clients only when the snapshot has no asset records.
The validated allowlist is stored in current canvas state and every retained
revision, so historical exact reads and restores keep the asset binding that
was valid at that revision. The bundled App accepts PNG, JPEG, and WebP images
from its file picker, drag-and-drop, and clipboard paths. Each image is limited
to 12 MiB, uploaded through bounded 192 KiB App-only chunks, and replaced with
its verified mcp-asset:<sha256> URI before it may be saved. URL images, SVG,
video, embeds, and other open media paths remain disabled. The Server never
deletes assets during Save. Instead, after the canvas store is completely
restored at process startup, revision-aware garbage
collection scans every active and archived canvas, its current state, and all
retained revisions before computing one exact keep-set. Unreferenced committed
blobs remain protected for at least 24 hours, so a newly uploaded image that
has not yet been saved cannot be removed by the next restart. The startup pass
can delete, audit in dry-run mode, or be disabled; /health reports only
aggregate counts and byte totals, never asset paths or upload authority.
Compatible host requirements
Server health alone does not mean a model or App can use this integration. A compatible host must complete all of these independent steps:
negotiate strict MCP
2026-07-28and carry the Apps extension capability on the requests that resolve the Tool result;expose only
model/model + appTools to the model while keeping App-only Tools behind a session/message/server/resource-bound AppBridge;load the verified offline
ui://resource in an isolated document and provideHostContextupdates for inline, fullscreen, and workbench sizes;implement the pinned Apps SDK's optional
ui/download-filecapability if SVG/PNG downloads are advertised, including a user confirmation before writing; this API is capability-gated and is not required by Apps stable2026-01-26;preserve the exact retained revision represented by historical Tool results and pins, expose an explicit latest-state refresh, and refresh clean live instances from the same
canvasIdon lifecycle signals;advertise only the
updateModelContextmodalities it supports. The App emits only advertised text and/orstructuredContent, never a snapshot or an unadvertised modality. Its bounded semantic summary preservescanvasIdandrevisionand reports added, removed, moved, resized, and renamed items plus added, removed, repointed, and renamed edges.
The verified App HTML is multi-megabyte. Raising the MCP resource limit was
necessary but not sufficient: directly encoding the 4 MB+ App into a data:
URL also failed in the reference Host. Do not infer a universal browser URL threshold;
keep large App markup out of bootstrap URLs.
OpenChamber's reference path is:
Host-owned raw App HTML with injected resource CSP
→ small opaque data: Loader
→ source/nonce/identity-authenticated chunk transfer
→ sandboxed about:srcdoc AppThe Host sends the raw HTML through nonce- and identity-bound messages. The
Loader validates the complete transfer, drops the raw chunks, and the Host
commits the final document through iframe.srcdoc. The final App stays in an
opaque sandbox origin and remains cross-origin with the Host.
tldraw's StyleEmbedder creates an internal about:blank frame while exporting
so it can measure isolated browser-default styles. Because the final App iframe
uses sandbox="allow-scripts" without allow-same-origin, its active sandbox
origin flag also applies when it creates a nested browsing context. The helper
frame receives a new opaque origin, so frame.contentDocument is unavailable;
adding about: to CSP does not make the two opaque origins same-origin.
The reviewed pinned-source patch handles that expected condition by treating
isolated default styles as unavailable and using NO_STYLES for default-style
elision. It may inline more filtered computed CSS, but it keeps tldraw's normal
style, font, media, pseudo-element, and SVG-to-PNG pipeline intact. Do not add
allow-same-origin: that would weaken the Host/App security boundary rather
than repair the exporter. Loader navigation, nonce, source, session, message,
server, resource, and Tool checks remain fail-closed.
The MCP resource uses verified data: URLs for embedded assets and currently
also records about: as a non-network frame source. The latter is neither an
origin grant nor a requirement of the exporter fallback, and a Host may reject
or normalize unsupported scheme sources. connectDomains stays empty. Hosts
must not interpret these declarations as permission to reach any network
origin.
The full editor keeps bundled icon/font data and the pinned English, Simplified
Chinese, and Traditional Chinese translations on verified data: URLs. Test
the actual disconnected editor—source-string or artifact-hash checks cannot
prove that toolbar and minimap icons render or that HostContext locale changes
select the intended language.
Three exact, hash-pinned source patches are part of this distribution: the
opaque-sandbox style exporter fallback, offline translation decoding for
verified JSON data: URLs, and export handling that preserves already-resolved
media data: URLs instead of trying to fetch them under connect-src 'none'.
Their patch bytes plus pristine and patched target hashes are recorded in
config/upstream-lock.json; an unknown preimage fails the build closed.
Export acceptance must also run in a real browser with the exact
sandbox="allow-scripts" topology, production CSP, and a canvas containing a
rich-text label that exercises foreignObject style embedding. Trigger both
SVG and PNG, assert that frame must have a document is absent and the bridge
remains usable, and inspect the decoded SVG/PNG rather than only matching the
patch source. Automated renderer/receipt/download-policy checks still do not
prove a file was saved; manual acceptance must choose a path and inspect both
resulting files.
Environment contract
Primary variables:
Variable | Default | Purpose |
|
| Listen address |
|
| Listen port |
| platform data directory | Managed process files |
| next to state path | Namespaced catalog, canvas records, history, recoverable trash, and content-addressed assets |
|
| Stable deployment/user namespace identity |
|
| Stable workspace namespace identity |
| resolved store root | Stable installation identity seed used to partition App caches |
| runtime state file | Legacy monolithic state import source and rollback evidence |
|
| Initial/default canvas |
| loopback hostnames | HTTP Host allowlist; required for non-loopback bind |
| loopback hostnames | HTTP Origin-host allowlist; required for non-loopback bind |
| sibling | Verified App bundle |
| sibling provenance | App integrity metadata |
|
| Startup-only revision-aware asset maintenance: |
| loopback MCP URL | Probe target |
For migration, corresponding OPENCHAMBER_TLDRAW_MCP_* variables are accepted
as lower-priority aliases. New deployments should use TLDRAW_MCP_*.
Scope and workspace are server configuration, not model Tool arguments. Keep them stable across restarts and upgrades: changing either intentionally selects a different logical canvas catalog. Literal namespace values are validated and hashed before storage paths are derived.
Builds and verification
The checked-in dist/app.html is the verified offline tldraw v5.0.2 App. It
embeds its icons, fonts, translations, CSS, and JavaScript. Normal server
development does not rebuild it:
npm test
npm run build:server
npm run verify:distFor the complete standalone gate, including an isolated managed service and real protocol probe, run:
npm run acceptThe acceptance runner executes tests, build, verification, start, probe, and stop serially to keep memory bounded. It prints the path to a private JSON report. Real sandbox/editor/download acceptance remains a separate Host-level gate described in docs/ACCEPTANCE.md.
npm run build:app reuses an already verified upstream checkout in .cache.
npm run bootstrap:app explicitly downloads and prepares the pinned upstream
source. Bootstrap is intentionally heavyweight and is never run implicitly by
npm ci, tests, the normal server build, or Docker.
npm run probe checks the strict protocol and Apps negotiation, semantic v2
create/patch/idempotent replay, catalog archive/unarchive/double-confirmation
recoverable delete, App-only history/restore visibility, the required Save
idempotencyKey, exact historical reads, revision listing, snapshot retention,
and the verified App resource/hash. The protocol and App source-contract tests additionally cover
multi-instance stale-revision recovery, bounded history and restore semantics,
streaming partial rendering, the lightweight inline/fullscreen boundary,
conflict choices, model-context modalities/deltas, and export ordering.
See docs/DEVELOPMENT.md for repository maintenance and config/upstream-lock.json for the pinned source and artifact hashes.
Docker
docker compose up --build -d
docker compose ps
TLDRAW_MCP_URL=http://127.0.0.1:39512/mcp npm run probe
docker compose downThe Compose service binds only to loopback, uses a named volume for canvas
state, and does not rebuild the tldraw App. The process binds 0.0.0.0 inside
the container, so Compose explicitly supplies loopback Host and Origin
allowlists. If a reviewed reverse proxy is added, set both allowlists to the
exact proxy-facing hostnames (hostname only, without scheme, port, path, or
wildcard) before starting.
This image and Compose file are for private development/interoperability only. They do not confer a tldraw production license and must not be published as a public production service.
This server cannot be installed
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Related MCP Servers
- AlicenseAqualityBmaintenanceA Model Context Protocol server that enables creating, modifying, and validating infinite canvas data structures according to the JSON Canvas 1.0 specification.Last updated714MIT
- AlicenseAqualityDmaintenanceA minimal MCP server for AI-driven canvas manipulation and visualization using tldraw. It enables AI clients to programmatically create, update, and manage shapes, flowcharts, and frames on a live interactive canvas.Last updated91MIT
- AlicenseAqualityBmaintenanceShared versioned state for multi-agent AI workflows. An MCP server.Last updated52MIT
- FlicenseAqualityBmaintenanceMinimal MCP server for editing tldraw .tldr files via JSON manipulation. Headless, no browser needed.Last updated181
Related MCP Connectors
MCP (Model Context Protocol) server for Appwrite
Cloud-hosted MCP server for durable AI memory
Agent-native MCP server over the public saagarpatel.dev corpus. Read-only, stateless.
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/ZunbaRan/tldraw-mcp-app'
If you have feedback or need assistance with the MCP directory API, please join our Discord server