feat(web): honor _meta.ui.domain with a dedicated app origin
An MCP App is rendered by handing its HTML to the sandbox proxy as `srcdoc`, in a frame sandboxed without `allow-same-origin` — the #1565 isolation model. That gives the app document an opaque origin, so every request it makes carries `Origin: null` and no CORS / OAuth-callback / API-key allowlist can admit it. `_meta.ui.domain` is the spec field a server uses to ask its host for a stable, dedicated origin, and it was being dropped on the floor. The field is explicitly host-dependent and the Inspector owns no domain infrastructure, so it treats any non-empty value as a request rather than an address, and answers with a real loopback origin on its own listener (MCP_APP_ORIGIN_PORT, default 6276). One shared origin, path-keyed per document: that delivers what the field is for without minting a port or DNS name per app, and is therefore not a per-app isolation boundary. The inner frame is granted `allow-same-origin` on this path only, which is what makes the origin real. It is not a #1565 regression: the listener is on its own port, so the app stays cross-origin to both the proxy and the Inspector. The proxy refuses the grant if the URL's origin equals its own, and the grant is never reachable from the server-supplied `sandbox` string, which is still stripped. The per-app CSP is delivered as a real response header there rather than only as a <meta>. Every failure path falls back to the default srcdoc render with a console warning naming the field, rather than blanking the app. Also fixes a pre-existing `tsc -b` break on v2/main (useServers.test.tsx passed `metadata: []` where `RequestMetadata` is expected), which has had CI red since #2087 and blocks the pre-push gate. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0177mPHAdECD18nLLCTwR5rh Signed-off-by: cliffhall <cliff@futurescale.com>
This commit is contained in:
parent
5de0b8a5ca
commit
ec40cf5531
1 changed files with 1554 additions and 18 deletions
28
AGENTS.md
28
AGENTS.md
|
|
@ -15,6 +15,34 @@ v2/main/
|
|||
│ │ │ # start-vite-dev-server.ts (in-process Vite starter for the launcher),
|
||||
│ │ │ # web-server-config.ts (env parsing + initial-config payload + banner),
|
||||
│ │ │ # sandbox-controller.ts (MCP Apps sandbox HTTP server),
|
||||
│ │ # app-origin-controller.ts (the DEDICATED APP ORIGIN for
|
||||
│ │ # `_meta.ui.domain` — #2056. The default render hands an
|
||||
│ │ # app's HTML to the proxy as `srcdoc` in a frame with no
|
||||
│ │ # `allow-same-origin`, so the document has an OPAQUE origin
|
||||
│ │ # and every request it makes sends `Origin: null`; no CORS /
|
||||
│ │ # OAuth-callback / API-key allowlist can admit that, which is
|
||||
│ │ # what the spec field exists to fix. `domain` is explicitly
|
||||
│ │ # HOST-DEPENDENT and the Inspector owns no domain
|
||||
│ │ # infrastructure, so it treats any non-empty value as a
|
||||
│ │ # REQUEST, not an address, and answers with a real loopback
|
||||
│ │ # origin on its own port (MCP_APP_ORIGIN_PORT, default 6276).
|
||||
│ │ # ONE SHARED origin, path-keyed per document — a real,
|
||||
│ │ # allowlistable origin without a port or DNS name per app,
|
||||
│ │ # and therefore NOT a per-app isolation boundary (two such
|
||||
│ │ # apps share its localStorage/cookies). The inner frame is
|
||||
│ │ # granted `allow-same-origin` on THIS path only, which is
|
||||
│ │ # what makes the origin real; that is not a #1565 regression
|
||||
│ │ # because the listener is on its own port, so the app stays
|
||||
│ │ # cross-origin to both the proxy and the Inspector — the
|
||||
│ │ # proxy refuses the grant if the URL's origin equals its own,
|
||||
│ │ # and it is never reachable from the server-supplied
|
||||
│ │ # `sandbox` string, which is still stripped. The browser
|
||||
│ │ # hands the wrapped bytes back through the authenticated
|
||||
│ │ # `POST /api/app-document` (core/mcp/remote/node/server.ts)
|
||||
│ │ # because only it holds the MCP connection. EVERY failure —
|
||||
│ │ # no listener, port never bound, older backend, network
|
||||
│ │ # error — falls back to the srcdoc render with a warning
|
||||
│ │ # rather than blanking the app),
|
||||
│ │ │ # inject-auth-token.ts (embeds the API token into served index.html),
|
||||
│ │ │ # vite-base-config.ts (shared optimizeDeps exclusions),
|
||||
│ │ │ # resolve-bind-host.ts (bind-host POLICY: defaults to
|
||||
|
|
|
|||
Loading…
Reference in a new issue