Skip to content

Latest commit

 

History

History
35 lines (23 loc) · 7.2 KB

File metadata and controls

35 lines (23 loc) · 7.2 KB

Publishing

The root @modelcontextprotocol/inspector package ships as one tarball with a single version number — no separate -web / -cli / -tui / -core packages. npm run build builds every client, then prepack runs before npm publish. Runtime dependencies are declared on the root package.json; client builds bundle @inspector/core and externalize npm packages resolved from the root install.

What ships, and the packaging invariants

The root package.json "files" allowlist is the source of truth for the tarball. A few non-obvious entries exist because they are read at runtime or were silently dropped by npm's packlist — do not remove them without re-running npm run pack:verify:

  • 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) 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 the tarball entry, the installed-on-disk path, and (since #2003) that a widget actually loads through it on the installed bin. Presence and reachability are different properties: a rename with a stale reader ships a file that is there and unusable.
  • A dependency that renders React is bundled, not externalized. An externalized package resolves its own react from wherever npm placed it in the consumer's tree, which is not necessarily where the bundle resolves ours — npm places a package beside a React satisfying its peer range, and those ranges are looser than ours. ink-form and ink-scroll-view declare ">=18", so a project holding React 18 satisfies them and gets them hoisted while the Inspector's React 19 nests underneath: two React copies, and the TUI dies with TypeError: Cannot read properties of null (reading 'useState') the moment a tool test form or a scroll view mounts (#1952). Both are therefore inlined by clients/tui/tsup.config.ts and are not root dependencies: the tarball ships their code inside clients/tui/build/index.js rather than having consumers install them. Bundling also pins their transitive deps to what this repo's install resolved (notably ink-select-input@6 via overrides, which npm ignores for a package installed as a dependency). ink is the one exception, on cost: bundling it works but adds ~1.4 MB (react-reconciler and yoga-layout come along, plus a createRequire banner for the inlined CJS), so it stays external — not because its ">=19" peer makes it safe, which it does not. What keeps that tolerable is the root react range: "^19.0.0" is deliberately open to the whole major so npm can dedupe our React with whatever React 19 a consumer pins, leaving an external ink on the same copy the bundle uses. Narrowing that range reopens the bug for the renderer itselfclients/tui/__tests__/tsupConfig.test.ts pins it to ink's peer floor, and guards the rest of the split; see the TUI README.
  • 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.jsons 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).

npm run pack:verify — publish smoke against the real tarball

The smoke:* scripts run against the in-repo build tree, which is not the published package. npm run pack:verify (scripts/pack-and-verify.mjs) closes that gap: it builds, npm packs the publishable tarball (asserting no source maps ship and that the runtime-required files are present), installs the tarball into a clean throwaway consumer — a fresh temp directory where it runs a real npm install <tgz> (pulls runtime deps, runs postinstall), exactly as npx @modelcontextprotocol/inspector would — and drives the installed mcp-inspector bin end to end: --help dispatch, a real --cli tools/list over stdio, a prod --web boot that must serve / from the shipped dist, and — riding that same boot — an MCP App rendered in headless Chromium through the shipped sandbox proxy, connect → open app → data-app-status="ready" (#2003). That last step shares its flow with smoke:web:app via scripts/lib/mcp-app-flow.mjs rather than copying it, since the deep-link shape is the part that rots silently; the client comes from the install while the App test server stays a repo fixture. It catches "works in --dev, breaks under npx …" path/packaging failures. It requires network access (the install pulls deps), so it is a local / release check, not part of the fast validate loop or of local:gate.

Cutting a release

The step-by-step procedure — bump on v2/main first, merge the milestone into main, tag origin/main with a bare x.y.z, then draft the GitHub Release — lives in the release skill, so there is exactly one copy of it. Invoke it as /release, or just read the file.

The short version: publishing is automated by two release-gated jobs in .github/workflows/main.yml, both needs: [build, coverage], so a release cannot publish with either the build job or the coverage gate red.

Docker

The container image and everything about running it — ports, volumes, where secrets go — is in Running the Inspector in Docker.