History

Serve the MCP-Apps shell from static assets on Workers hosts

The deployed cloud Worker has no filesystem, so loadMcpAppsShellHtml's fs.readFile always failed and the ui://executor/shell.html resource silently served a 'Shell not built' placeholder — an inert document that never starts the MCP-Apps handshake, so every artifact widget in every MCP client spun on its loading state forever with no error anywhere. - Workers hosts (cloud, host-cloudflare) now fetch the built shell through the ASSETS binding: mcpAppsShellAsset emits a stable-named copy alongside the content-hashed one, and makeAssetsShellHtmlLoader (new @executor-js/mcp-apps-shell/worker entry) reads and verifies it. Under vite dev the document travels inline via a dev-only virtual module, since no assets are emitted yet. - The shell document carries an identity marker the loaders verify, so an SPA not-found fallback (index.html with a 200) can never be served as the shell. - The self-host image reads the same emitted asset from its SPA dist; the runtime image has no packages/ tree for the old candidates. - The silent placeholder is gone: a host that cannot produce the shell fails the resource read with an actionable error. - App builds now fail when the shell asset is missing or wrong (apps/cloud scripts/build.mjs, assert-shell-asset.mjs on both hosts).

Rhys Sullivan committed 3 months ago

Commit 1e50ff31 parent 6a801f223 files · +451 −58Browse files

Showing the changes to apps/cloud/scripts (1 of 23 files). Show all

Unified · Split

apps/cloud/scripts/build.mjs+20−1
@@ -9,7 +9,7 @@ · 8 unmodified lines
9 9
10import { spawnSync } from "node:child_process";10import { spawnSync } from "node:child_process";
11import { randomBytes } from "node:crypto";11import { randomBytes } from "node:crypto";
12import { rmSync } from "node:fs";12import { existsSync, readFileSync, rmSync } from "node:fs";
13 13
14if (!process.env.VITE_PUBLIC_ANALYTICS_PATH) {14if (!process.env.VITE_PUBLIC_ANALYTICS_PATH) {
15 process.env.VITE_PUBLIC_ANALYTICS_PATH = randomBytes(4).toString("hex");15 process.env.VITE_PUBLIC_ANALYTICS_PATH = randomBytes(4).toString("hex");
@@ -32,3 +32,22 @@ for (const step of steps) { · 16 unmodified lines
32 process.exit(result.status ?? 1);32 process.exit(result.status ?? 1);
33 }33 }
34}34}
35
36// The deployed Worker serves the MCP-Apps shell (`ui://executor/shell.html`)
37// by fetching this stable-named asset through the ASSETS binding — it has no
38// filesystem to read the shell from. A build that failed to emit it would
39// deploy a Worker whose artifact resource reads all fail, so make the miss a
40// build failure here instead of a production incident. Same shape as the
41// worker-bundler asset assertion from #1378.
42const shellAsset = new URL(
43 "../dist/client/assets/executor-mcp-apps-shell-stable.html",
44 import.meta.url,
45);
46if (!existsSync(shellAsset)) {
47 console.error(`[build] missing MCP-Apps shell asset at ${shellAsset.pathname}`);
48 process.exit(1);
49}
50if (!readFileSync(shellAsset, "utf8").includes('name="executor-mcp-apps-shell"')) {
51 console.error(`[build] ${shellAsset.pathname} is not the MCP-Apps shell document`);
52 process.exit(1);
53}

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t