pi-tldraw 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (74) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +222 -0
  3. package/bridge/app-bridge-entry.js +6 -0
  4. package/mcp-app/LICENSE.md +9 -0
  5. package/mcp-app/PI_TLDRAW_PROVENANCE.json +32 -0
  6. package/mcp-app/README.md +129 -0
  7. package/mcp-app/dev-tunnel.sh +51 -0
  8. package/mcp-app/dist/editor-api.json +8493 -0
  9. package/mcp-app/dist/mcp-app.html +643 -0
  10. package/mcp-app/dist/method-map.json +915 -0
  11. package/mcp-app/package.json +42 -0
  12. package/mcp-app/plugins/tldraw-mcp/.cursor-plugin/plugin.json +10 -0
  13. package/mcp-app/plugins/tldraw-mcp/assets/logo.svg +3 -0
  14. package/mcp-app/plugins/tldraw-mcp/mcp.json +8 -0
  15. package/mcp-app/scripts/extract-editor-api.ts +1374 -0
  16. package/mcp-app/server.json +21 -0
  17. package/mcp-app/src/logger.ts +45 -0
  18. package/mcp-app/src/register-tools.ts +368 -0
  19. package/mcp-app/src/shared/generated-data.ts +160 -0
  20. package/mcp-app/src/shared/pending-requests.ts +69 -0
  21. package/mcp-app/src/shared/types.ts +76 -0
  22. package/mcp-app/src/shared/utils.ts +132 -0
  23. package/mcp-app/src/tools/exec.ts +120 -0
  24. package/mcp-app/src/tools/loadCachedCanvasWidgetHtml.ts +16 -0
  25. package/mcp-app/src/tools/search.ts +150 -0
  26. package/mcp-app/src/widget/app-context.tsx +29 -0
  27. package/mcp-app/src/widget/dev-log.tsx +70 -0
  28. package/mcp-app/src/widget/exec-helpers.ts +232 -0
  29. package/mcp-app/src/widget/export-tldr.ts +35 -0
  30. package/mcp-app/src/widget/focused/defaults.ts +141 -0
  31. package/mcp-app/src/widget/focused/focused-editor-proxy.ts +434 -0
  32. package/mcp-app/src/widget/focused/format.ts +366 -0
  33. package/mcp-app/src/widget/focused/to-focused.ts +258 -0
  34. package/mcp-app/src/widget/focused/to-tldraw.ts +570 -0
  35. package/mcp-app/src/widget/image-guard.tsx +106 -0
  36. package/mcp-app/src/widget/index.html +33 -0
  37. package/mcp-app/src/widget/mcp-app.css +113 -0
  38. package/mcp-app/src/widget/mcp-app.tsx +857 -0
  39. package/mcp-app/src/widget/persistence.ts +337 -0
  40. package/mcp-app/src/widget/snapshot.ts +157 -0
  41. package/mcp-app/src/worker.ts +305 -0
  42. package/mcp-app/tsconfig.json +23 -0
  43. package/mcp-app/vite.config.ts +13 -0
  44. package/mcp-app/wrangler.toml +45 -0
  45. package/mcp-app-source.json +36 -0
  46. package/package.json +90 -0
  47. package/patches/tldraw-mcp-app/001-pi-runtime.patch +35 -0
  48. package/scripts/assemble-mcp-app.mjs +193 -0
  49. package/scripts/build-bridge.mjs +74 -0
  50. package/scripts/e2e-mcp.mjs +69 -0
  51. package/scripts/e2e-packaged-mcp-app.mjs +79 -0
  52. package/scripts/run-mcp-app-dev.mjs +44 -0
  53. package/scripts/verify-bundle.mjs +41 -0
  54. package/scripts/verify-mcp-app-source.mjs +51 -0
  55. package/scripts/verify-mcp-app.mjs +38 -0
  56. package/scripts/verify-package-files.mjs +50 -0
  57. package/src/canvas/export.ts +164 -0
  58. package/src/canvas/state.ts +117 -0
  59. package/src/canvas/workflow.ts +105 -0
  60. package/src/commands/tldraw-command.ts +48 -0
  61. package/src/diagram/guidance.ts +44 -0
  62. package/src/host/local-host.ts +289 -0
  63. package/src/index.ts +762 -0
  64. package/src/mcp/client.ts +126 -0
  65. package/src/mcp/response.ts +74 -0
  66. package/src/semantic/layer.ts +309 -0
  67. package/src/server/server-manager.ts +153 -0
  68. package/src/store/export-store.ts +33 -0
  69. package/src/store/project-store.ts +251 -0
  70. package/src/ui/tldraw-status.ts +88 -0
  71. package/static/app-bridge-bundle.js +18114 -0
  72. package/static/app-bridge-bundle.meta.json +164 -0
  73. package/static/host.html +390 -0
  74. package/tsconfig.json +13 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Zbigniew Siwiec
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,222 @@
1
+ # pi-tldraw
2
+
3
+ A Pi extension that opens a local tldraw MCP canvas, lets the agent inspect/edit it, and persists canvas snapshots per project folder.
4
+
5
+ ## Install
6
+
7
+ From npm:
8
+
9
+ ```bash
10
+ pi install npm:pi-tldraw
11
+ ```
12
+
13
+ Pinned:
14
+
15
+ ```bash
16
+ pi install npm:pi-tldraw@0.1.0
17
+ ```
18
+
19
+ Before the npm package is published, you can install from GitHub after a repo/tag exists:
20
+
21
+ ```bash
22
+ pi install git:github.com/<github-owner>/pi-tldraw@v0.1.0
23
+ ```
24
+
25
+ Or quick-run a local checkout without installing:
26
+
27
+ ```bash
28
+ pi -e .
29
+ ```
30
+
31
+ ## Runtime requirement: tldraw MCP app
32
+
33
+ The extension talks to a tldraw MCP server at:
34
+
35
+ ```text
36
+ http://127.0.0.1:8787/mcp
37
+ ```
38
+
39
+ If that endpoint is not reachable and the endpoint is local, the extension tries to auto-start a local tldraw MCP app. Auto-start uses:
40
+
41
+ 1. `TLDRAW_MCP_APP_DIR`, if set.
42
+ 2. `./mcp-app` at the package root, if present in the installed package.
43
+
44
+ For a development checkout of tldraw:
45
+
46
+ ```bash
47
+ export TLDRAW_MCP_APP_DIR=/path/to/tldraw/apps/mcp-app
48
+ ```
49
+
50
+ If you prefer to run the MCP server yourself, start it separately and disable auto-start:
51
+
52
+ ```bash
53
+ export TLDRAW_MCP_AUTO_START=false
54
+ ```
55
+
56
+ The browser-side MCP Apps bridge is different: it is built from source in this repository and shipped as `static/app-bridge-bundle.js`.
57
+
58
+ ## Extension shape
59
+
60
+ This is a testable Pi extension package:
61
+
62
+ ```text
63
+ pi-tldraw/
64
+ ├── src/
65
+ │ ├── index.ts # Pi extension entry point / registration shell
66
+ │ ├── canvas/ # pure canvas state helpers + workflows
67
+ │ ├── commands/ # pure slash-command parsing
68
+ │ ├── host/ # local browser host adapter
69
+ │ ├── mcp/ # MCP HTTP client + response parsing
70
+ │ ├── semantic/ # pure canvas semantic rendering layer
71
+ │ ├── server/ # local MCP server lifecycle adapter
72
+ │ ├── store/ # project snapshot persistence
73
+ │ └── ui/ # Pi status widget adapter
74
+ ├── test/ # pure helper/workflow/unit tests
75
+ ├── bridge/ # source for the browser-side MCP Apps bridge bundle
76
+ ├── scripts/ # reproducible build / verification / local e2e helpers
77
+ ├── static/app-bridge-bundle.js
78
+ ├── static/app-bridge-bundle.meta.json
79
+ ├── static/host.html
80
+ ├── mcp-app/ # packaged tldraw MCP app runtime when release assembly provides it
81
+ ├── package.json # Pi package metadata
82
+ └── tsconfig.json
83
+ ```
84
+
85
+ Pi discovers the extension via:
86
+
87
+ ```json
88
+ {
89
+ "pi": {
90
+ "extensions": ["./src/index.ts"]
91
+ }
92
+ }
93
+ ```
94
+
95
+ ## Build and release contract
96
+
97
+ - Users installing from npm should receive prebuilt browser assets; they should not compile the bridge during `pi install`.
98
+ - Maintainers build the bridge in CI from the source under `bridge/` before publishing.
99
+ - The npm tarball includes both source (`bridge/`, `scripts/`) and the generated artifact (`static/app-bridge-bundle.js`, `static/app-bridge-bundle.meta.json`).
100
+ - Git installs work from tagged commits because the generated static artifact is committed and verified by CI.
101
+ - The runtime contract remains the same: the extension auto-starts a local `mcp-app/` when the release package contains it, or uses `TLDRAW_MCP_APP_DIR` / a manually started server.
102
+
103
+ ## Normal UX
104
+
105
+ Start or restore the current project canvas:
106
+
107
+ ```text
108
+ /tldraw open
109
+ ```
110
+
111
+ Then work in the browser canvas. Edits are autosaved into the current project folder:
112
+
113
+ ```text
114
+ .pi/tldraw-canvases/index.json
115
+ .pi/tldraw-canvases/<canvasId>.json
116
+ ```
117
+
118
+ You should not need a manual save during normal use.
119
+
120
+ ## Useful commands
121
+
122
+ ```text
123
+ /tldraw open [canvasId] # Open/restore canvas; creates one if needed
124
+ /tldraw host # Show host/server/autosave diagnostics
125
+ /tldraw canvases # List saved project canvases
126
+ /tldraw current # Show current project canvas id
127
+ /tldraw restart # Restart local MCP app server
128
+ /tldraw save # Manual checkpoint; refuses unsafe blank overwrite
129
+ /tldraw save! # Force manual empty overwrite
130
+ /tldraw reset # Reset MCP HTTP session
131
+ ```
132
+
133
+ ## Agent tools
134
+
135
+ - `tldraw_canvas_open` — open/restore the local browser canvas host.
136
+ - `tldraw_diagram_tips` — get minimalist drawing guidance before creating or revising a diagram.
137
+ - `tldraw_canvas_export` — export the current selection or whole canvas as a PNG/SVG image for visual feedback.
138
+ - `tldraw_canvas_scene` — compact semantic canvas view; falls back to saved snapshots.
139
+ - `tldraw_canvas_state` — inspect live canvas state and optionally save it.
140
+ - `tldraw_canvas_exec` — execute JavaScript against the live tldraw editor.
141
+ - `tldraw_search` — inspect the tldraw Editor API via the MCP search tool.
142
+ - `tldraw_status` — check MCP server tools/resources.
143
+
144
+ ## Persistence model
145
+
146
+ The durable source of truth is project-local JSON:
147
+
148
+ ```text
149
+ <project>/.pi/tldraw-canvases/<canvasId>.json
150
+ ```
151
+
152
+ The MCP server checkpoint store is treated as transient runtime state. If the browser port, MCP session, or Wrangler state changes, `/tldraw open` restores from the project snapshot.
153
+
154
+ Safety rules:
155
+
156
+ - Autosave and manual save refuse to overwrite a non-empty saved snapshot with an empty live canvas.
157
+ - Forced empty overwrite is available with `/tldraw save!`.
158
+ - Previous snapshots are copied to `.pi/tldraw-canvases/history/<canvasId>/` before overwrite.
159
+
160
+ ## Development
161
+
162
+ ```bash
163
+ npm install
164
+ npm run build # builds static/app-bridge-bundle.js from bridge/app-bridge-entry.js
165
+ npm run check
166
+ npm run pack:dry
167
+ ```
168
+
169
+ The bridge bundle is intentionally prebuilt for users, but maintainers build it from source with `npm run build:bridge`. CI rebuilds it and fails if the committed artifact drifts from source.
170
+
171
+ The packaged MCP app is assembled from a built tldraw checkout and records OSS provenance in `mcp-app/PI_TLDRAW_PROVENANCE.json`:
172
+
173
+ ```bash
174
+ # source checkout must already have apps/mcp-app/dist/* built
175
+ export TLDRAW_MCP_APP_SOURCE_DIR=/path/to/tldraw/apps/mcp-app
176
+ npm run assemble:mcp-app
177
+ npm run verify:mcp-app
178
+ npm run verify:mcp-app-source
179
+ ```
180
+
181
+ The source contract is declared in `mcp-app-source.json`: upstream tldraw repo, pinned commit, app path, build commands, and patch files under `patches/tldraw-mcp-app/`. The current packaged app is traceable to that pinned source plus `patches/tldraw-mcp-app/001-pi-runtime.patch`.
182
+
183
+ The packaged `mcp-app/` keeps the same runtime contract as before (`cd mcp-app && yarn dev`) but uses prebuilt `dist/` assets so users do not rebuild tldraw during install/startup.
184
+
185
+ For maintainers who want to rebuild from the pinned upstream source directly:
186
+
187
+ ```bash
188
+ npm run assemble:mcp-app:pinned
189
+ ```
190
+
191
+ This clones the pinned tldraw commit, applies declared patches, runs the manifest build commands, and assembles `mcp-app/`.
192
+
193
+ Release checklist:
194
+
195
+ ```bash
196
+ npm ci
197
+ export TLDRAW_MCP_APP_SOURCE_DIR=/path/to/tldraw/apps/mcp-app
198
+ npm run assemble:mcp-app
199
+ npm run verify:mcp-app-source
200
+ npm run build
201
+ npm run check
202
+ npm run e2e:packaged-mcp-app
203
+ npm run pack:dry
204
+ ```
205
+
206
+ GitHub Actions runs the same build/check path before publishing with npm provenance.
207
+
208
+ To smoke-test against a running tldraw MCP server:
209
+
210
+ ```bash
211
+ # optional: defaults to http://127.0.0.1:8787/mcp
212
+ export TLDRAW_MCP_URL=http://127.0.0.1:8787/mcp
213
+ npm run e2e:mcp
214
+ ```
215
+
216
+ To smoke-test the packaged MCP app itself on a temporary port:
217
+
218
+ ```bash
219
+ npm run e2e:packaged-mcp-app
220
+ ```
221
+
222
+ Keep logic that does not need Pi APIs in separate modules, such as `src/store/project-store.ts`, so it can be unit-tested without mocking Pi.
@@ -0,0 +1,6 @@
1
+ import { AppBridge, PostMessageTransport } from '@modelcontextprotocol/ext-apps/app-bridge'
2
+
3
+ // Browser host entry point for Pi's local tldraw iframe host.
4
+ // Keep this intentionally small: the build step owns bundling MCP Apps bridge
5
+ // dependencies into static/app-bridge-bundle.js for zero-build installs.
6
+ globalThis.McpAppsBridge = { AppBridge, PostMessageTransport }
@@ -0,0 +1,9 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 tldraw Inc.
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
6
+
7
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
8
+
9
+ THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,32 @@
1
+ {
2
+ "artifact": "mcp-app/",
3
+ "builder": "scripts/assemble-mcp-app.mjs",
4
+ "source": {
5
+ "mode": "local",
6
+ "repo": "https://github.com/tldraw/tldraw.git",
7
+ "commit": "4b0cfc539e074217e1e248461afa596fc7d02040",
8
+ "appPath": "apps/mcp-app",
9
+ "patches": [
10
+ {
11
+ "path": "patches/tldraw-mcp-app/001-pi-runtime.patch",
12
+ "sha256": "6bb0b7b7e9a2b9379bfe13800e4407c346b307e3e241d3c4ffe2f1876d1af894"
13
+ }
14
+ ],
15
+ "build": [
16
+ {
17
+ "cwd": ".",
18
+ "command": "yarn install --immutable"
19
+ },
20
+ {
21
+ "cwd": "apps/mcp-app",
22
+ "command": "yarn build"
23
+ }
24
+ ],
25
+ "sourceGitCommit": "4b0cfc539e074217e1e248461afa596fc7d02040"
26
+ },
27
+ "dist": {
28
+ "mcp-app.html": "24af4cfaef84460f172dec501803960bf161f2faeab9c5eff8c6b0b3ace83c67",
29
+ "editor-api.json": "7298c0069c700f4b5db94a31ab3fbf742a64dc97442cf93f63616ab029153620",
30
+ "method-map.json": "e060b2860d245d07bddd9f2ad41398dd733328eba48234058854ac03f1bb828e"
31
+ }
32
+ }
@@ -0,0 +1,129 @@
1
+ # tldraw MCP app
2
+
3
+ This is the tldraw MCP app. It exposes an interactive tldraw canvas to AI agents via the [Model Context Protocol app specification](https://github.com/modelcontextprotocol/ext-apps/), so you can work in tldraw with agents in any MCP client that supports the MCP app spec.
4
+
5
+ ## Architecture
6
+
7
+ The app has two parts: a **server** and a **widget**.
8
+
9
+ ### Server
10
+
11
+ The server runs in Cloudflare Workers via `src/worker.ts`, using a Durable Object (`TldrawMCP`) backed by SQLite for persistent checkpoint storage.
12
+
13
+ It exposes:
14
+
15
+ - `search` — query the extracted Editor API spec in a sandboxed dynamic worker
16
+ - `exec` — execute JavaScript against the live editor in the widget via a pending-request callback bridge
17
+ - `_exec_callback` — app-only tool the widget calls to resolve a pending `exec` request
18
+ - `save_checkpoint` / `read_checkpoint` — app-only tools used by the widget for checkpoint persistence
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.
23
+
24
+ When the AI calls `exec`, the server creates a pending request and the widget picks it up, runs the code through a focused editor proxy (`src/widget/focused/`) that translates between an AI-friendly shape format (simple string IDs, flat `_type` shapes) and tldraw's internal `TLShape`/`TLShapeId` types, then calls `_exec_callback` to resolve the pending request with the result. Canvas state is checkpointed to the Durable Object's SQLite database and to the browser's local storage.
25
+
26
+ ## Developing
27
+
28
+ ### Prerequisites
29
+
30
+ The widget build depends on generated files (`editor-api.json`, `method-map.json`) that are extracted from the editor's TypeScript declarations. Before you can develop or build the mcp-app, you need to build the core packages first:
31
+
32
+ ```bash
33
+ # from the repo root
34
+ yarn build
35
+ ```
36
+
37
+ This produces the `.tsbuild/` output that `yarn extract-api` reads from. The `build` and `dev` scripts run `extract-api` automatically, so you don't need to call it separately.
38
+
39
+ ### Package scripts
40
+
41
+ Run all commands from `apps/mcp-app`.
42
+
43
+ | Command | What it does |
44
+ | ----------------- | -------------------------------------------------------------------------------------------------- |
45
+ | `yarn build` | Build the widget HTML |
46
+ | `yarn dev` | Build widget + start local Cloudflare worker (HTTP MCP on `localhost:8787`) |
47
+ | `yarn dev:tunnel` | Build widget + start a Cloudflare tunnel + local worker with `WORKER_ORIGIN` set to the tunnel URL |
48
+ | `yarn deploy` | Build widget + deploy the Cloudflare worker to production |
49
+
50
+ `yarn dev:tunnel` requires the `cloudflared` CLI to be installed on your machine.
51
+
52
+ The worker defaults to production-safe behavior in `wrangler.toml`, including setting `MCP_IS_DEV="false"`. Local HTTP dev scripts override that with `MCP_IS_DEV=true` so local Claude/ChatGPT connectors suppress `ui.domain` while production deployments keep it enabled.
53
+
54
+ ### Cursor setup
55
+
56
+ Add these two servers in `~/.cursor/mcp.json`:
57
+
58
+ ```json
59
+ {
60
+ "mcpServers": {
61
+ "tldraw": {
62
+ "transport": "http",
63
+ "url": "https://tldraw-mcp-app.tldraw.workers.dev/mcp"
64
+ },
65
+ "tldraw-local": {
66
+ "command": "npx",
67
+ "args": ["-y", "mcp-remote", "http://127.0.0.1:8787/mcp"]
68
+ }
69
+ }
70
+ }
71
+ ```
72
+
73
+ ### Claude Desktop local setup
74
+
75
+ For local Claude Desktop development, use `claude_desktop_config.json` with the local HTTP server:
76
+
77
+ ```json
78
+ {
79
+ "mcpServers": {
80
+ "tldraw-local": {
81
+ "command": "npx",
82
+ "args": ["-y", "mcp-remote", "http://127.0.0.1:8787/mcp"]
83
+ }
84
+ }
85
+ }
86
+ ```
87
+
88
+ ### Claude Desktop remote setup
89
+
90
+ If you'd like to try the remote MCP server in Claude Desktop, use the in-app connector flow rather than adding the production URL to `claude_desktop_config.json`.
91
+
92
+ 1. Open Claude Desktop
93
+ 2. In the sidebar, go to **Customize**
94
+ 3. Open **Connectors**
95
+ 4. Click the button to add a connector, then choose **Add custom connector**
96
+ 5. Give it a name such as `tldraw`
97
+ 6. Paste `https://tldraw-mcp-app.tldraw.workers.dev/mcp` as the server URL
98
+
99
+ The **Add custom connector** option is not available on the free plan, so you may need Max or another paid plan.
100
+
101
+ If you need Notion access in Claude Desktop, use the Notion MCP connector for that separately.
102
+
103
+ ### ChatGPT local dev
104
+
105
+ ChatGPT requires an HTTPS origin, so you need a Cloudflare tunnel. You must be an admin of your OpenAI org/workspace to do local dev.
106
+
107
+ 1. Run `yarn dev:tunnel` in `apps/mcp-app`
108
+ 2. It prints a `https://...trycloudflare.com` tunnel URL
109
+ 3. In ChatGPT web (not the desktop app), go to **Apps** and add your app using that tunnel URL
110
+ 4. You can then test in both ChatGPT web and the desktop or mobile apps
111
+
112
+ `dev:tunnel` automatically wires `WORKER_ORIGIN` to the tunnel URL and sets `MCP_IS_DEV=true` for the local worker.
113
+
114
+ ### Iteration loop
115
+
116
+ 1. Make code changes in `apps/mcp-app`
117
+ 2. Run the relevant script (`dev` or `dev:tunnel`)
118
+ 3. Disconnect and reconnect the MCP server in your client (or reload the page/app)
119
+ 4. When making widget changes, make sure to rebuild, either by running `yarn build` or rerunning any of the dev scripts.
120
+
121
+ Reconnecting the server after changes is the most reliable way to pick up new code, especially when the widget HTML changes.
122
+
123
+ ## Contact
124
+
125
+ Find us on Twitter/X at [@tldraw](https://twitter.com/tldraw).
126
+
127
+ ## Community
128
+
129
+ 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,0 +1,51 @@
1
+ #!/usr/bin/env bash
2
+ # Starts a cloudflared tunnel + wrangler dev with the tunnel URL as WORKER_ORIGIN.
3
+ # Usage: ./dev-tunnel.sh
4
+ set -euo pipefail
5
+
6
+ PORT="${PORT:-8787}"
7
+ LOGFILE=$(mktemp)
8
+
9
+ # Build widget first
10
+ echo "Building widget..."
11
+ yarn build:widget
12
+
13
+ # Start cloudflared in background, capture the tunnel URL from stderr
14
+ cloudflared tunnel --url "http://localhost:$PORT" 2>"$LOGFILE" &
15
+ CF_PID=$!
16
+
17
+ cleanup() {
18
+ echo "Stopping cloudflared (pid $CF_PID)..."
19
+ kill "$CF_PID" 2>/dev/null || true
20
+ rm -f "$LOGFILE"
21
+ }
22
+ trap cleanup EXIT
23
+
24
+ # Wait for cloudflared to print the tunnel URL
25
+ echo "Waiting for tunnel URL..."
26
+ TUNNEL_URL=""
27
+ for i in $(seq 1 30); do
28
+ TUNNEL_URL=$(grep -o 'https://[a-z0-9-]*\.trycloudflare\.com' "$LOGFILE" | head -1 || true)
29
+ if [ -n "$TUNNEL_URL" ]; then
30
+ break
31
+ fi
32
+ sleep 1
33
+ done
34
+
35
+ if [ -z "$TUNNEL_URL" ]; then
36
+ echo "ERROR: Failed to get tunnel URL after 30s. cloudflared log:"
37
+ cat "$LOGFILE"
38
+ exit 1
39
+ fi
40
+
41
+ echo ""
42
+ echo "==================================="
43
+ echo "Tunnel URL: $TUNNEL_URL"
44
+ echo "==================================="
45
+ echo ""
46
+ echo "Update claude_desktop_config.json to use:"
47
+ echo " \"args\": [\"-y\", \"mcp-remote\", \"$TUNNEL_URL/mcp\"]"
48
+ echo ""
49
+
50
+ # Start wrangler with the tunnel URL as WORKER_ORIGIN in dev mode
51
+ exec wrangler dev --port "$PORT" --var "WORKER_ORIGIN:$TUNNEL_URL" --var "MCP_IS_DEV:true"