Skip to content

fix(dev-agent): render dev views directly against co-located desktop sandbox#4174

Open
viktormarinho wants to merge 1 commit into
mainfrom
viktormarinho/dev-views-desktop-direct
Open

fix(dev-agent): render dev views directly against co-located desktop sandbox#4174
viktormarinho wants to merge 1 commit into
mainfrom
viktormarinho/dev-views-desktop-direct

Conversation

@viktormarinho

@viktormarinho viktormarinho commented Jun 26, 2026

Copy link
Copy Markdown
Contributor

Problem

The dev/live feature (#4138) renders an MCP-app agent's views against its sandbox dev server via the synthetic dev_<id> connection. For a user-desktop sandbox that dev server runs on a loopback previewUrl (http://<handle>.localhost:<port>, bound to 127.0.0.1 only).

But dev_<id> is resolved and fetched in the cloud pod (resolveDevConnectionservesMcp runs server-side, and the frontend's MCP client hits /api/:org/mcp/dev_<id> on the studio origin). The pod cannot reach the user's loopbackservesMcp fails → resolveDevConnection returns null → the view 503s ("Dev server not running").

This is the default path, not an edge case: SANDBOX_START defaults to user-desktop whenever the acting user's link daemon is online. (agent-sandbox works today because its previewUrl is public.)

Empirically confirmed

Against a live desktop sandbox (...localhost:50235), running the exact servesMcp probe:

Vantage result
Host (co-located, = local dev) 200, ok:true
Off-loopback (= the cloud pod) Couldn't connectfalse → null → 503

Root cause: the desktop ingress binds 127.0.0.1 only (user-desktop-provider.ts), and <handle>.localhost resolves to loopback — so it's physically unreachable from any host but the user's. Local dev masks the bug because the pod and the sandbox share one loopback.

Fix (clean: co-locate the fetch)

The invariant: a loopback previewUrl may only be fetched by a process on the user's machine. The browser/webview is co-located with the daemon, so for user-desktop sandboxes it talks to the dev server's previewUrl directly, bypassing the cloud proxy. The deco dev server's CORS is *, so the cross-origin MCP calls are allowed. This mirrors the existing website preview, which already points its <iframe src> straight at the loopback previewUrl.

  • @decocms/mesh-sdkcreateMCPClient / useMCPClient / useMCPClientOptional accept an optional absolute mcpUrl override (connect directly to that URL instead of the /api/:org/mcp/:connectionId route; never the self/REST client).
  • use-main-panel-tabs — dev-view auto-detection points at ${previewUrl}/api/mcp for user-desktop.
  • project-app-view (AppViewContent) — the dev view renders against the same direct client.

Gated on a dev_<id> connection id + sandboxProviderKind === "user-desktop", so agent-sandbox dev views keep the existing public-URL cloud route and regular connection UIs are untouched. Purely additive — no regression.

Not in this PR (documented follow-up)

Dev tools in the desktop harness still resolve in the cloud (handleVirtualMcpRequest serves the daemon's mcp.url). Surfacing them for a desktop sandbox needs the cluster to pass the loopback dev URL to the co-located daemon, which then opens the MCP client locally — a typed HarnessStreamInput change across @decocms/harness. The cloud path already returns null gracefully for desktop sandboxes today, so this is additive, not a regression.

Also: browser-direct works for authless dev servers (the default). A token-gated dev app (MCP_AUTH_TOKEN) would need the token surfaced to the browser — same deferral as the header-auth follow-up noted in dev-connection.ts.

Testing

  • bun run --cwd packages/mesh-sdk check ✅, bun run --cwd apps/mesh check ✅ (0 errors)
  • bun run lint ✅ (0 errors; 3 pre-existing warnings in untouched files)
  • bun run fmt
  • Topology reproduction above run against a live desktop sandbox.
  • ⚠️ The cross-origin render itself needs verification on the desktop app / staging (can't be exercised in single-host local dev, where loopback is always reachable).

🤖 Generated with Claude Code


Summary by cubic

Render dev MCP views for desktop sandboxes by connecting the browser directly to the sandbox’s loopback preview URL, bypassing the cloud proxy. Fixes the 503 error when the pod cannot reach http://<handle>.localhost.

  • Bug Fixes
    • Added mcpUrl override to createMCPClient, useMCPClient, and useMCPClientOptional in @decocms/mesh-sdk to allow direct MCP connections.
    • In use-main-panel-tabs, point dev views at ${previewUrl}/api/mcp when sandboxProviderKind is "user-desktop".
    • In project-app-view (AppViewContent), use the same direct client for dev_<id> connections.
    • Guarded to affect only desktop sandboxes; agent-sandbox and regular/self connections keep existing routes.

Written for commit d83d8e3. Summary will update on new commits.

Review in cubic

…sandbox

A `user-desktop` sandbox serves its dev server on a loopback previewUrl
(`http://<handle>.localhost:<port>`), bound to `127.0.0.1` only. The dev views
were fetched through the `dev_<id>` connection, which `resolveDevConnection`
resolves and fetches in the cloud pod — and the pod cannot reach the user's
loopback. So `servesMcp` fails, `resolveDevConnection` returns null, and the
view 503s ("Dev server not running"). This is the default path: SANDBOX_START
defaults to `user-desktop` whenever the link daemon is online.

Empirically confirmed against a live desktop sandbox: the previewUrl
(`...localhost:50235`) answers MCP from the host (co-located) but is a
"Couldn't connect" from any off-loopback vantage (the pod's perspective),
because the ingress binds `127.0.0.1` only.

Fix (clean): the browser is co-located with the daemon, so for user-desktop
sandboxes it talks to the dev server's previewUrl directly instead of routing
through the cloud proxy (deco dev server CORS is `*`). Agent-sandbox keeps the
public previewUrl via the existing `dev_<id>` cloud route, and regular
connection UIs are untouched (gated on a `dev_<id>` connection id).

- mesh-sdk: `createMCPClient`/`useMCPClient`/`useMCPClientOptional` accept an
  optional absolute `mcpUrl` override (never the self/REST client).
- use-main-panel-tabs: dev-view auto-detection points at `${previewUrl}/api/mcp`
  for user-desktop.
- project-app-view (AppViewContent): the dev view renders against the same
  direct client.

Follow-up (not in this PR): dev TOOLS in the desktop harness still resolve in
the cloud (handleVirtualMcpRequest serves the daemon's mcp.url). Surfacing them
for a desktop sandbox needs the cluster to pass the loopback dev URL to the
co-located daemon so it opens the MCP client locally — a typed
HarnessStreamInput change across @decocms/harness.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant