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
99
1010 import { spawnSync } from "node:child_process";
1111 import { randomBytes } from "node:crypto";
12−import { rmSync } from "node:fs";
12+import { existsSync, readFileSync, rmSync } from "node:fs";
1313
1414 if (!process.env.VITE_PUBLIC_ANALYTICS_PATH) {
1515 process.env.VITE_PUBLIC_ANALYTICS_PATH = randomBytes(4).toString("hex");
@@ -32,3 +32,22 @@ for (const step of steps) { · 16 unmodified lines
3232 process.exit(result.status ?? 1);
3333 }
3434 }
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.
42+const shellAsset = new URL(
43+ "../dist/client/assets/executor-mcp-apps-shell-stable.html",
44+ import.meta.url,
45+);
46+if (!existsSync(shellAsset)) {
47+ console.error(`[build] missing MCP-Apps shell asset at ${shellAsset.pathname}`);
48+ process.exit(1);
49+}
50+if (!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