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
refactor(mcp-app): replace per-host domain config with isDev flag (tldraw#8184)
In order to simplify widget domain configuration and make local
development reliable, this PR replaces the per-host `MCP_DOMAIN_OPENAI`
/ `MCP_DOMAIN_CLAUDE` environment variables with a single `MCP_IS_DEV`
flag and a `getWidgetDomain()` function that resolves the correct domain
per host.
Reimplemented from
[`max/mcp-app-approvals-fixes`](https://github.com/tldraw/tldraw/tree/max/mcp-app-approvals-fixes)
with a clean commit history.
### Change type
- [x] `improvement`
### Test plan
1. Run `yarn dev` in `apps/mcp-app` — verify local worker starts with
`MCP_IS_DEV=true` and no `ui.domain` is set on the canvas resource
2. Run `yarn dev:tunnel` — verify tunnel mode also passes
`MCP_IS_DEV=true`
3. Deploy to production — verify `MCP_IS_DEV=false` (default in
wrangler.toml) causes `getWidgetDomain()` to return the correct domain
for ChatGPT and Claude hosts
4. Hit `/.well-known/openai-apps-challenge` — verify it returns the
verification string
### Release notes
- Replace per-host domain env vars with `MCP_IS_DEV` flag for simpler
widget domain configuration
- Add `/.well-known/openai-apps-challenge` endpoint for OpenAI domain
verification
- Add `enabled` flag to Logger to suppress logs in production
<!-- CURSOR_SUMMARY -->
---
> [!NOTE]
> **Medium Risk**
> Changes how `ui.domain` is computed for ChatGPT/Claude and introduces
a new unauthenticated verification route, which could affect production
widget rendering or deployment validation if misconfigured.
>
> **Overview**
> **Widget domain handling is refactored** to drop
`MCP_DOMAIN_OPENAI`/`MCP_DOMAIN_CLAUDE` and instead use `MCP_IS_DEV` + a
new `getWidgetDomain()` to set `_meta.ui.domain` only in production
(ChatGPT uses `https://tldraw.com`; Claude hashes the deployed `/mcp`
URL from `WORKER_ORIGIN`).
>
> **Dev/prod behavior is now explicit**: local `wrangler dev` and tunnel
scripts pass `MCP_IS_DEV:true`, while `wrangler.toml` defaults
`MCP_IS_DEV="false"`; docs are updated accordingly. The worker also adds
`/.well-known/openai-apps-challenge` (no auth) and gates structured
logging behind a new `Logger(enabled)` flag (enabled only in dev).
>
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
50deff3. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
Copy file name to clipboardExpand all lines: apps/mcp-app/README.md
+10-1Lines changed: 10 additions & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -39,6 +39,8 @@ Run all commands from `apps/mcp-app`.
39
39
40
40
`yarn dev:tunnel` requires the `cloudflared` CLI to be installed on your machine.
41
41
42
+
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.
43
+
42
44
### Cursor setup
43
45
44
46
Add up to three servers in `~/.cursor/mcp.json`:
@@ -112,7 +114,14 @@ ChatGPT requires an HTTPS origin, so you need a Cloudflare tunnel. You must be a
112
114
3. In ChatGPT web (not the desktop app), go to **Apps** and add your app using that tunnel URL
113
115
4. You can then test in both ChatGPT web and the desktop app
114
116
115
-
`dev:tunnel` automatically wires `WORKER_ORIGIN` to the tunnel URL.
117
+
`dev:tunnel` automatically wires `WORKER_ORIGIN` to the tunnel URL and sets `MCP_IS_DEV=true` for the local worker.
118
+
119
+
### Auth and environment flags
120
+
121
+
-`MCP_AUTH_TOKEN` controls bearer auth for the HTTP worker. If it is unset, the worker accepts unauthenticated local requests.
122
+
-`MCP_IS_DEV` controls local-only widget behavior, such as suppressing `ui.domain` for local HTTP/tunnel connectors.
123
+
124
+
These flags are intentionally separate so auth configuration does not change widget-domain behavior.
0 commit comments