From cdb49d16bdf46a07d14621b9992fcd2724c0e94c Mon Sep 17 00:00:00 2001 From: Derrick Hammer Date: Sat, 22 Aug 2026 09:41:58 +0000 Subject: [PATCH 1/2] fix(mcp): bundle @uppy into self-contained MCP app bundles The upload MCP apps (upload_file, vault_put_file) depend on @uppy/core and @uppy/xhr-upload, but these were not in the tsdown alwaysBundle list, so they were externalized as bare module specifiers (e.g. import ... from "@uppy/core") that the host's sandboxed app iframe cannot resolve. This caused a 'Failed to resolve module specifier "@uppy/core"' crash when rendering the apps in Claude. Fix by forcing @uppy/* into the self-contained bundle, and add regression coverage at both layers: - Go: mcpapp_test TestAppModuleJSEmbedded now scans all embedded app bundles for bare module specifiers (previously only a 7-app subset that missed the upload apps), plus a self-check for the detector. - Browser: sunpeak inspector tests render upload_file and vault_put_file in real Chromium across ChatGPT and Claude hosts, asserting the bundle module actually executed via the window.__PINNER_CLI_VERSION__ global (which is only set after all static imports resolve). --- internal/mcpapp/mcpapp_test.go | 111 +++++++++++++++++++-- packages/apps/scripts/build-apps.mjs | 17 +++- tests/sunpeak/tests/inspector-apps.test.ts | 61 +++++++++++ 3 files changed, 181 insertions(+), 8 deletions(-) diff --git a/internal/mcpapp/mcpapp_test.go b/internal/mcpapp/mcpapp_test.go index 08d52dd6..78e9315d 100644 --- a/internal/mcpapp/mcpapp_test.go +++ b/internal/mcpapp/mcpapp_test.go @@ -1,6 +1,8 @@ package mcpapp import ( + "regexp" + "sort" "strings" "testing" ) @@ -42,18 +44,113 @@ func TestMcpAppThemeCSSEmbedded(t *testing.T) { } } -// TestAppModuleJSEmbedded pins that each app's self-contained bundle is -// embedded and inlines into the served document. A missing/empty bundle (JS -// not built before Go) panics, so a passing test also proves `pnpm build` ran. +// fromSpecifierRe matches the module-specifier string in `import ... from +// "spec"` and side-effect `import "spec"` statements. Minified bundles drop the +// space around the keyword/specifier, so both `from "x"` and `from"x"` (and +// `import"x"`) must match. +var fromSpecifierRe = regexp.MustCompile(`from\s*["']([^"']+)["']|(?:^|[;)\]}])import\s*["']([^"']+)["']`) + +// bareModuleSpecifiers returns any module specifiers in an inline-ready bundle +// that the browser cannot resolve on its own: bare package specifiers (e.g. +// "@uppy/core") that do NOT start with ".", "/", or a URL scheme. The sandboxed +// ui:// iframe serves each app as a single inline