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
|
|
@ -148,6 +148,7 @@ import {
|
|||
import { clearScrollMemory } from "./hooks/useScrollMemory";
|
||||
import type { AppRendererHandle } from "./components/elements/AppRenderer/AppRenderer";
|
||||
import { createAppBridgeFactory } from "./components/elements/AppRenderer/createAppBridgeFactory";
|
||||
import { publishAppDocument } from "./lib/publishAppDocument";
|
||||
import { AppElicitationHost } from "./components/elements/AppElicitation/AppElicitationHost";
|
||||
import {
|
||||
AppElicitationController,
|
||||
|
|
@ -808,9 +809,23 @@ function App() {
|
|||
[],
|
||||
);
|
||||
|
||||
// `_meta.ui.domain` support (#2056): hand a wrapped app document to the
|
||||
// backend so it can serve it from a dedicated origin, giving the app's
|
||||
// requests a real `Origin`. Resolves `null` on any backend that can't, and
|
||||
// the factory then renders the app the default (opaque-origin) way.
|
||||
const publishDocument = useCallback(
|
||||
(doc: { html: string; csp?: string }) =>
|
||||
publishAppDocument(doc, {
|
||||
baseUrl: configBaseUrl,
|
||||
authToken: getAuthToken(),
|
||||
}),
|
||||
[configBaseUrl],
|
||||
);
|
||||
|
||||
const sandboxBridgeFactory = useMemo(
|
||||
() =>
|
||||
createAppBridgeFactory({
|
||||
publishAppDocument: publishDocument,
|
||||
getClient: () => inspectorClient?.getAppRendererClient() ?? null,
|
||||
getListedResourceMeta,
|
||||
readResource: async (uri) => {
|
||||
|
|
@ -832,7 +847,7 @@ function App() {
|
|||
});
|
||||
},
|
||||
}),
|
||||
[inspectorClient, getListedResourceMeta],
|
||||
[inspectorClient, getListedResourceMeta, publishDocument],
|
||||
);
|
||||
|
||||
// App-rendered form elicitations (#1854). The controller is created once and
|
||||
|
|
@ -885,6 +900,7 @@ function App() {
|
|||
() =>
|
||||
createAppBridgeFactory({
|
||||
advertiseElicitation: true,
|
||||
publishAppDocument: publishDocument,
|
||||
getClient: () => inspectorClient?.getAppRendererClient() ?? null,
|
||||
getListedResourceMeta,
|
||||
readResource: async (uri) => {
|
||||
|
|
@ -903,7 +919,7 @@ function App() {
|
|||
});
|
||||
},
|
||||
}),
|
||||
[inspectorClient, getListedResourceMeta],
|
||||
[inspectorClient, getListedResourceMeta, publishDocument],
|
||||
);
|
||||
/**
|
||||
* Close the previous client's session and open one for the client being
|
||||
|
|
|
|||
Loading…
Reference in a new issue