fix(dev-agent): render dev views directly against co-located desktop sandbox#4174
Open
viktormarinho wants to merge 1 commit into
Open
fix(dev-agent): render dev views directly against co-located desktop sandbox#4174viktormarinho wants to merge 1 commit into
viktormarinho wants to merge 1 commit into
Conversation
…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>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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 auser-desktopsandbox that dev server runs on a loopback previewUrl (http://<handle>.localhost:<port>, bound to127.0.0.1only).But
dev_<id>is resolved and fetched in the cloud pod (resolveDevConnection→servesMcpruns 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 loopback →servesMcpfails →resolveDevConnectionreturnsnull→ the view 503s ("Dev server not running").This is the default path, not an edge case:
SANDBOX_STARTdefaults touser-desktopwhenever the acting user's link daemon is online. (agent-sandboxworks today because its previewUrl is public.)Empirically confirmed
Against a live desktop sandbox (
...localhost:50235), running the exactservesMcpprobe:200,ok:trueCouldn't connect→false→ null → 503Root cause: the desktop ingress binds
127.0.0.1only (user-desktop-provider.ts), and<handle>.localhostresolves 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-desktopsandboxes 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-sdk—createMCPClient/useMCPClient/useMCPClientOptionalaccept an optional absolutemcpUrloverride (connect directly to that URL instead of the/api/:org/mcp/:connectionIdroute; never the self/REST client).use-main-panel-tabs— dev-view auto-detection points at${previewUrl}/api/mcpforuser-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 (
handleVirtualMcpRequestserves the daemon'smcp.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 typedHarnessStreamInputchange 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 indev-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✅🤖 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.mcpUrloverride tocreateMCPClient,useMCPClient, anduseMCPClientOptionalin@decocms/mesh-sdkto allow direct MCP connections.use-main-panel-tabs, point dev views at${previewUrl}/api/mcpwhensandboxProviderKindis"user-desktop".project-app-view(AppViewContent), use the same direct client fordev_<id>connections.agent-sandboxand regular/self connections keep existing routes.Written for commit d83d8e3. Summary will update on new commits.