Skip to content

fix(mcp): restore the Inspector's missing app sandbox document - #348

Merged
dsklyar merged 2 commits into
mainfrom
danielsklyar/zel-8113-inspector-sandbox-proxy
Aug 6, 2026
Merged

dsklyar merged 2 commits into
mainfrom
danielsklyar/zel-8113-inspector-sandbox-proxy

Conversation

@dsklyar

@dsklyar dsklyar commented Aug 2, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Works around inspector#1859: the published v2 tarball's files list covers clients/web/build and clients/web/dist but not clients/web/static, so the one document the Apps tab needs is absent. The web server reads it at startup, swallows the ENOENT, and substitutes its own error page — which then renders inside the app frame as Sandbox not loaded: ENOENT ... sandbox_proxy.html. Every other tab works, so it reads as a broken view rather than a missing file. Upstream shipped and fixed the same omission once before in v1 (#1113, for server/static).

ensureInspectorSandboxProxy locates the install and copies the document in if it is missing, carrying a TODO naming the issue and the exact condition for deleting all of it.

Ticket: ZEL-8113. Eighth of eleven PRs splitting the MCP Apps branch, and the first of three building the Inspector dev loop. Nothing calls this yet — pnpm mcp:inspect arrives in the next PR — so this lands as an inert, independently reviewable fix.

Why restore upstream's file instead of substituting our own

The proxy is the security boundary for app rendering: it holds the untrusted view at an opaque origin, strips allow-same-origin from anything a server asks for, and relays bridge messages between host and view. A replacement of our own making would make this loop diverge from real hosts on exactly the axis the Inspector exists to check. So the vendored copy is upstream's file verbatim, down to the bytes — which is why oxfmt is told to skip it and a test pins its SHA-256.

Two other choices worth a look:

  • The install directory is derived from the child's own PATH, not by globbing ~/.npm/_npx. npm decides where that cache lives; it moves with npm_config_cache and sandboxes relocate it wholesale. Running the probe under the same spec we are about to launch is what guarantees we patch the install that will actually be used.
  • Every failure path warns rather than throws. This works around someone else's packaging bug, and the Inspector is still useful for tools, resources, and the handshake even when the Apps tab cannot paint. Refusing to launch would be a worse outcome than a rendered error that a warning explains.

This PR also carries the one-line switch of test:root to vitest run --dir scripts, since it is the first to add a scripts/*.test.ts beyond the three that were named explicitly.

Where this sits in the stack

Eleven layers, each targeting the one below it, registered as a GitHub stack — so this diff is only its own commits, and merging a lower layer rebases and retargets everything above it automatically.

  1. fix(design-tokens): resolve tokens.css from source inside the monorepo #341 — design-tokens: resolve tokens.css from source inside the monorepo
  2. feat(mcp-server-base): negotiate client capabilities from the handshake #342 — negotiate client capabilities from the handshake
  3. feat(mcp-server-base): attribute stdio sessions to the detected host #343 — attribute stdio sessions to the detected host
  4. feat(mcp): serve ui:// views and per-capability tool variants #344 — serve ui:// resources and per-capability tool variants
  5. feat(mcp-server-base): add a browser-only ./ui subpath with useMcpApp #345 — browser-only ./ui subpath with useMcpApp
  6. feat(mcp-server-base): publish the Tailwind view theme #346 — publish the Tailwind view theme
  7. feat(mcp): build MCP App views with React and Vite #347 — build views into one self-contained document with Vite
  8. fix(mcp): restore the Inspector's missing app sandbox document #348 — restore the Inspector's missing app sandbox document ← this PR
  9. feat(dev): add mcp-server-examples with the reference view and form flows #349 — dev/mcp-server-examples, the first consumer of all of it
  10. feat(mcp): add pnpm mcp:inspect with per-request dev view HTML #350 — pnpm mcp:inspect with per-request dev view HTML
  11. feat(mcp): scaffold a tool, an app, or a form with pnpm mcp:new #351 — pnpm mcp:new-view scaffolder

Depends on nothing below it: its position is ordering alone, so it can be read and approved without the layers underneath.

Test plan

  • format, lint, typecheck, typecheck:root, and the full test run are green; root test files go from 3 to 4
  • Unit tests cover all three outcomes: written when absent, left alone when a release ships its own, and nothing created when the directory is not an Inspector install
  • Two tests guard the vendored document itself — one asserting it is a participant in the bridge protocol that denies allow-same-origin, one pinning its hash. The hash test passing after pnpm run format is what proves the oxfmt ignore actually holds.
  • Verified against a real install: npx @modelcontextprotocol/inspector@2 reproduces the Sandbox not loaded error in the app frame, and rendering succeeds once the document is restored

Made with Cursor

@linear-code

linear-code Bot commented Aug 2, 2026 •

Copy link
Copy Markdown

ZEL-8113

ZEL-8153

@pkg-pr-new

pkg-pr-new Bot commented Aug 2, 2026 •

Copy link
Copy Markdown

Open in StackBlitz

@transcend-io/airgap.js-types

pnpm add https://pkg.pr.new/@transcend-io/airgap.js-types@348
yarn add https://pkg.pr.new/@transcend-io/airgap.js-types@348.tgz

@transcend-io/cli

pnpm add https://pkg.pr.new/@transcend-io/cli@348
yarn add https://pkg.pr.new/@transcend-io/cli@348.tgz

@transcend-io/design-tokens

pnpm add https://pkg.pr.new/@transcend-io/design-tokens@348
yarn add https://pkg.pr.new/@transcend-io/design-tokens@348.tgz

@transcend-io/internationalization

pnpm add https://pkg.pr.new/@transcend-io/internationalization@348
yarn add https://pkg.pr.new/@transcend-io/internationalization@348.tgz

@transcend-io/privacy-types

pnpm add https://pkg.pr.new/@transcend-io/privacy-types@348
yarn add https://pkg.pr.new/@transcend-io/privacy-types@348.tgz

@transcend-io/sdk

pnpm add https://pkg.pr.new/@transcend-io/sdk@348
yarn add https://pkg.pr.new/@transcend-io/sdk@348.tgz

@transcend-io/type-utils

pnpm add https://pkg.pr.new/@transcend-io/type-utils@348
yarn add https://pkg.pr.new/@transcend-io/type-utils@348.tgz

@transcend-io/utils

pnpm add https://pkg.pr.new/@transcend-io/utils@348
yarn add https://pkg.pr.new/@transcend-io/utils@348.tgz

@transcend-io/mcp

pnpm add https://pkg.pr.new/@transcend-io/mcp@348
yarn add https://pkg.pr.new/@transcend-io/mcp@348.tgz

@transcend-io/mcp-server-admin

pnpm add https://pkg.pr.new/@transcend-io/mcp-server-admin@348
yarn add https://pkg.pr.new/@transcend-io/mcp-server-admin@348.tgz

@transcend-io/mcp-server-assessment

pnpm add https://pkg.pr.new/@transcend-io/mcp-server-assessment@348
yarn add https://pkg.pr.new/@transcend-io/mcp-server-assessment@348.tgz

@transcend-io/mcp-server-base

pnpm add https://pkg.pr.new/@transcend-io/mcp-server-base@348
yarn add https://pkg.pr.new/@transcend-io/mcp-server-base@348.tgz

@transcend-io/mcp-server-consent

pnpm add https://pkg.pr.new/@transcend-io/mcp-server-consent@348
yarn add https://pkg.pr.new/@transcend-io/mcp-server-consent@348.tgz

@transcend-io/mcp-server-discovery

pnpm add https://pkg.pr.new/@transcend-io/mcp-server-discovery@348
yarn add https://pkg.pr.new/@transcend-io/mcp-server-discovery@348.tgz

@transcend-io/mcp-server-docs

pnpm add https://pkg.pr.new/@transcend-io/mcp-server-docs@348
yarn add https://pkg.pr.new/@transcend-io/mcp-server-docs@348.tgz

@transcend-io/mcp-server-dsr

pnpm add https://pkg.pr.new/@transcend-io/mcp-server-dsr@348
yarn add https://pkg.pr.new/@transcend-io/mcp-server-dsr@348.tgz

@transcend-io/mcp-server-inventory

pnpm add https://pkg.pr.new/@transcend-io/mcp-server-inventory@348
yarn add https://pkg.pr.new/@transcend-io/mcp-server-inventory@348.tgz

@transcend-io/mcp-server-preferences

pnpm add https://pkg.pr.new/@transcend-io/mcp-server-preferences@348
yarn add https://pkg.pr.new/@transcend-io/mcp-server-preferences@348.tgz

@transcend-io/mcp-server-workflows

pnpm add https://pkg.pr.new/@transcend-io/mcp-server-workflows@348
yarn add https://pkg.pr.new/@transcend-io/mcp-server-workflows@348.tgz

commit: 394eada

Vendors upstream sandbox_proxy.html and restores it into npx Inspector
installs that omit clients/web/static. Points the removal TODO at ZEL-8153.
@dsklyar
dsklyar force-pushed the danielsklyar/zel-8113-inspector-sandbox-proxy branch from ada3a80 to 3849525 Compare August 5, 2026 22:30

@dawson-turechek-transcend dawson-turechek-transcend left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good!

const VENDORED_SANDBOX_PROXY = join(scriptsLibDir, 'inspector-sandbox-proxy.html');

/** What {@link restoreSandboxProxy} did, for logging and tests. */
export type SandboxProxyOutcome = 'present' | 'written' | 'unrecognized';

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: I think we should use an enum and derive the type, if we're going to use these string values in the code.

@dsklyar dsklyar Aug 6, 2026 •

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Going to add as follow up point for this ticket.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in #397

@dsklyar
dsklyar requested a review from a team as a code owner August 6, 2026 17:21
@dsklyar
dsklyar added this pull request to the merge queue Aug 6, 2026
@dsklyar
dsklyar removed this pull request from the merge queue due to a manual request Aug 6, 2026
@dsklyar
dsklyar added this pull request to the merge queue Aug 6, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to no response for status checks Aug 6, 2026
@dsklyar
dsklyar added this pull request to the merge queue Aug 6, 2026
Merged via the queue into main with commit cfda9d7 Aug 6, 2026
4 of 10 checks passed
@dsklyar
dsklyar deleted the danielsklyar/zel-8113-inspector-sandbox-proxy branch August 6, 2026 22:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants