You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
fix(mcp-app): host name resolution, tool annotations, and widget improvements (tldraw#8168)
In order to improve MCP client identity tracking and fix several widget
UX issues, this PR refactors the MCP app with a clean commit history.
Clean reimplementation of
[`max/mcp-fixes`](https://github.com/tldraw/tldraw/tree/max/mcp-fixes).
**Key changes:**
- **Rename `cloudflare-worker.ts` → `worker.ts`** for clarity
- **Fix build scripts** so dev:stdio, dev:tunnel, and deploy always
build the widget first
- **Centralize server metadata** — use shared constants (name, version,
title, etc.) across server and widget; bump version to 0.1.0; extract
`injectBootstrapData()` helper
- **Add client host name resolution** — resolve connecting client
identity (cursor, vscode, claude, chatgpt) from MCP client version
string; thread through bootstrap data; use for domain resolution instead
of raw client strings; persist in worker SQLite
- **Extract image guard and app context** into separate modules; rename
context to `McpAppContext`
- **Improve widget UI** — show "Build it" only in code editors; reorder
share panel buttons; use download icon; simplify state; use typed host
context; query host capabilities for fullscreen/download
- **Add explicit tool annotations** — `readOnlyHint`/`destructiveHint`
on all tools so MCP clients can make better auto-approval decisions
- **Add README** with architecture overview, setup instructions, and dev
workflow
### Change type
- [x] `improvement`
### Test plan
1. Connect via Cursor — verify "Build it" button appears, download and
fullscreen buttons work
2. Connect via Claude Desktop — verify "Build it" is hidden, download
and fullscreen work
3. Connect via ChatGPT — verify resource domain resolves correctly
4. Run `yarn dev:stdio` — verify widget builds before server starts
- [ ] Unit tests
- [ ] End to end tests
<!-- CURSOR_SUMMARY -->
---
> [!NOTE]
> **Medium Risk**
> Moderate risk because it changes MCP server metadata/bootstrap
injection and client-based domain resolution across both Node and Worker
entry points, which could affect connectivity or resource loading in
different hosts.
>
> **Overview**
> **Client/host identity is now normalized and propagated end-to-end.**
The server/worker resolve a canonical host name
(`cursor`/`vscode`/`claude`/`chatgpt`) from the MCP client string,
persist it in the Durable Object, and embed it into the widget bootstrap
so the UI and HTTP `domain` selection use the same identity.
>
> **Server/tool registration is tightened and standardized.** Server
metadata is centralized via shared constants (including a version bump
to `0.1.0`), bootstrap HTML injection is extracted to a helper, and all
tools now include explicit `readOnlyHint`/`destructiveHint` annotations
(plus annotations for the app-only `event` tool).
>
> **Widget UX and host integration are refined.** Host context handling
is typed/safer, fullscreen/download buttons are gated by host
capabilities, the Share panel is reordered with an icon download button,
and the "Build it" action is shown only for code-editor hosts;
image/asset/embed inputs are proactively blocked via extracted
`image-guard` overrides.
>
> **Dev/deploy ergonomics and docs.** `dev:stdio`, `dev:http`,
`dev:tunnel`, and `deploy` now always build the widget first, the
Cloudflare entry point is set to `src/worker.ts`, and a new `README.md`
documents architecture and setup.
>
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
5d15524. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
This is the tldraw MCP app. It exposes an interactive tldraw canvas to AI agents via the [Model Context Protocol](https://modelcontextprotocol.io/), so agents in Cursor, Claude Desktop, ChatGPT, and VS Code can draw diagrams and shapes on a canvas during a conversation.
4
+
5
+
## Architecture
6
+
7
+
The app has two parts: a **server** and a **widget**.
8
+
9
+
### Server
10
+
11
+
The server registers MCP tools (`create_shapes`, `update_shapes`, `delete_shapes`, `diagram_drawing_read_me`) and serves the widget HTML as an MCP App resource.
12
+
13
+
There are two entry points:
14
+
15
+
-`main.ts` — Node.js stdio transport, for local clients like Claude Desktop and Cursor
16
+
-`src/worker.ts` — Cloudflare Workers with a Durable Object (`TldrawMCP`) backed by SQLite for persistent checkpoint storage
17
+
18
+
Both entry points share tool registration logic in `src/register-tools.ts`.
19
+
20
+
### Widget
21
+
22
+
The widget is a React app (`src/widget/mcp-app.tsx`) that renders a full tldraw canvas inside the MCP host's iframe. Vite bundles it into a single HTML file (`dist/mcp-app.html`) using `vite-plugin-singlefile`, which the server injects bootstrap data into before serving.
23
+
24
+
The widget handles streaming previews (shapes appear as the model streams tool arguments), checkpoint persistence to `localStorage`, and syncing state back to the server.
ChatGPT requires an HTTPS origin, so you need a Cloudflare tunnel. You must be a workspace admin.
109
+
110
+
1. Run `yarn dev:tunnel` in `apps/mcp-app`
111
+
2. It prints a `https://...trycloudflare.com` tunnel URL
112
+
3. In ChatGPT web (not the desktop app), go to **Apps** and add your app using that tunnel URL
113
+
4. You can then test in both ChatGPT web and the desktop app
114
+
115
+
`dev:tunnel` automatically wires `WORKER_ORIGIN` to the tunnel URL.
116
+
117
+
### Iteration loop
118
+
119
+
1. Make code changes in `apps/mcp-app`
120
+
2. Run the relevant script (`dev`, `dev:stdio`, or `dev:tunnel`)
121
+
3. Disconnect and reconnect the MCP server in your client (or reload the page/app)
122
+
4. When making widget changes, make sure to rebuild — `yarn dev` does this automatically
123
+
124
+
Reconnecting the server after changes is the most reliable way to pick up new code, especially when the widget HTML changes.
125
+
126
+
## License
127
+
128
+
The code in this folder is Copyright (c) 2024-present tldraw Inc. The tldraw SDK is provided under the [tldraw license](https://github.com/tldraw/tldraw/blob/main/LICENSE.md).
129
+
130
+
## Trademarks
131
+
132
+
Copyright (c) 2024-present tldraw Inc. The tldraw name and logo are trademarks of tldraw. Please see our [trademark guidelines](https://github.com/tldraw/tldraw/blob/main/TRADEMARKS.md) for info on acceptable usage.
133
+
134
+
## Contact
135
+
136
+
Find us on Twitter/X at [@tldraw](https://twitter.com/tldraw).
137
+
138
+
## Community
139
+
140
+
Have questions, comments or feedback? [Join our discord](https://discord.tldraw.com/?utm_source=github&utm_medium=readme&utm_campaign=sociallink). For the latest news and release notes, visit [tldraw.dev](https://tldraw.dev).
0 commit comments