fix: ship clients/web/static so the MCP Apps sandbox proxy loads

`clients/web/static/sandbox_proxy.html` is a committed source file, not a
build artifact, and it was never listed in the root package.json "files"
allowlist. `sandbox-controller.ts` reads it at runtime as
`<runner dir>/../static/sandbox_proxy.html`, so it resolved fine in-repo but
was absent from every published tarball — the read threw and the controller
served its "Sandbox not loaded" fallback page, breaking the Apps tab for
anyone running `npx @modelcontextprotocol/inspector`.

v1 shipped the equivalent as `server/static` in both the root and the server
workspace "files" lists; that entry has no counterpart in v2.

Add `clients/web/static` to the allowlist, and assert it in pack:verify twice
over — once in the tarball packlist and once on disk after install, since what
the runtime needs is the path *relative to* clients/web/build rather than mere
presence in the tarball.

No .npmignore change is needed: clients/web/.gitignore does not list `static`,
so the nested-gitignore packlist hazard that hid `build/` does not apply here.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XAR2Nr9kXbrywFNUVoTe9F
This commit is contained in:
cliffhall 2026-08-05 17:46:10 -04:00
commit 22da48c652
3 changed files with 22 additions and 11 deletions

View file

@ -278,6 +278,7 @@ The root `package.json` `"files"` allowlist is the source of truth for the tarba
- **No source maps.** The client bundlers set `sourcemap: false` (`clients/{cli,tui}/tsup.config.ts`, `clients/web/tsup.runner.config.ts`); Vite and the launcher's `tsc` already emit none. Maps are ~half the unpacked size and aren't needed at runtime — debug via `npm run dev` on the source.
- **`clients/web/build` ships via `clients/web/.npmignore`.** `clients/web/.gitignore` lists `build/`, and npm's packlist honors that nested `.gitignore` over the root `"files"` allowlist — so the prod web-server runner was silently missing from the tarball while `clients/web/dist` slipped through (its `.gitignore` only lists `dist-ssr`). `clients/web/.npmignore` overrides the `.gitignore` for publishing so both `build/` (runner) and `dist/` (SPA) ship. The other clients don't need this — none ship a nested `.gitignore`.
- **`clients/web/static` ships the MCP Apps sandbox proxy.** `clients/web/static/sandbox_proxy.html` is a committed source file (not a build artifact), read from disk at runtime by `clients/web/server/sandbox-controller.ts` as `<runner dir>/../static/sandbox_proxy.html`. It was missing from the root `"files"` allowlist entirely, so every published build failed the Apps tab with **"Sandbox not loaded"** ([#1859](https://github.com/modelcontextprotocol/inspector/issues/1859)) while working fine in the repo. Because the path is resolved *relative to* `clients/web/build`, the directory must ship at that exact location — `pack:verify` asserts both the tarball entry and the installed-on-disk path.
- **A single version number, read from the root `package.json`.** The Inspector ships as one package with one version, so only the **root** `package.json` carries a `version` — the four `clients/*/package.json`s deliberately have none. Every Node client (CLI, TUI, and the web backend) resolves the version through the shared `readInspectorVersion()` reader in `core/node/version.ts`, which walks up to the root manifest (always present in the tarball). No client `package.json` is read at runtime, so none needs to ship. The web **browser** can't read the filesystem; it gets its version from the backend via `GET /api/config` (see [#1639](https://github.com/modelcontextprotocol/inspector/issues/1639)).
### `npm run pack:verify` — publish smoke against the real tarball

View file

@ -24,6 +24,7 @@
"clients/launcher/build",
"clients/web/build",
"clients/web/dist",
"clients/web/static",
"clients/cli/build",
"clients/tui/build",
"scripts/install-clients.mjs"

View file

@ -22,8 +22,8 @@
*
* 1. builds every client (`npm run build`);
* 2. packs the publishable tarball (`npm pack`) and inspects its file list —
* asserting NO source maps ship and that `clients/web/{build,dist}` are
* both present (the two packaging fixes this work landed);
* asserting NO source maps ship and that `clients/web/{build,dist,static}`
* are all present (the packaging fixes this work landed);
* 3. installs that tarball into a fresh temp dir (real `npm install <tgz>`,
* which runs the package's `postinstall`);
* 4. runs the installed `mcp-inspector` bin: `--help`, `--cli`/`--tui` help
@ -164,26 +164,31 @@ if (maps.length > 0) {
}
// 2b. Runtime files that are easy to omit from the packlist and only fail once
// installed: both web artifacts — the prod server runner (build) AND the SPA
// (dist). `clients/web/build` was previously dropped by the nested
// .gitignore. (The version the CLI/TUI report is read from the root
// package.json — always shipped — via readInspectorVersion(), so no client
// package.json needs to ship; that read is exercised by driving the bin in
// step 4.)
// installed: the web artifacts — the prod server runner (build), the SPA
// (dist), and the MCP Apps sandbox proxy page (static). `clients/web/build`
// was previously dropped by the nested .gitignore; `clients/web/static` was
// never listed in the root "files" allowlist at all, so the Apps tab failed
// with "Sandbox not loaded" on every published build (#1859). None of these
// are checked-in-tree failures — only an installed tarball reveals them.
// (The version the CLI/TUI report is read from the root package.json —
// always shipped — via readInspectorVersion(), so no client package.json
// needs to ship; that read is exercised by driving the bin in step 4.)
for (const required of [
"clients/web/build/index.js",
"clients/web/dist/index.html",
"clients/web/static/sandbox_proxy.html",
]) {
if (!tarredPaths.includes(required)) {
fail(
`expected \`${required}\` in the published tarball but it is missing — ` +
`check the "files" field in clients/web/package.json`,
`check the "files" field in the root package.json (and that ` +
`clients/web/.npmignore does not exclude it)`,
);
}
}
console.log(
`pack:verify — tarball OK: ${tarredPaths.length} files, no source maps, ` +
`clients/web/{build,dist} present (${(packInfo.unpackedSize / 1048576).toFixed(2)} MB unpacked)`,
`clients/web/{build,dist,static} present (${(packInfo.unpackedSize / 1048576).toFixed(2)} MB unpacked)`,
);
// ---------------------------------------------------------------------------
@ -229,10 +234,14 @@ try {
if (!existsSync(bin)) {
fail(`installed \`mcp-inspector\` bin not found at ${bin}`);
}
// Confirm the two packaging fixes survived install onto disk.
// Confirm the packaging fixes survived install onto disk. The sandbox proxy
// is resolved at runtime as `<build>/../static/sandbox_proxy.html`, so its
// position *relative to* clients/web/build is what matters, not just presence
// in the tarball (#1859).
for (const required of [
join(installedPkg, "clients", "web", "build", "index.js"),
join(installedPkg, "clients", "web", "dist", "index.html"),
join(installedPkg, "clients", "web", "static", "sandbox_proxy.html"),
join(installedPkg, "clients", "launcher", "build", "index.js"),
]) {
if (!existsSync(required)) {