| Filename | Latest commit message | Latest commit date |
|---|---|---|
* test(web): audit branch-coverage outliers, raise per-file gate to 70 (#1271) The web per-file coverage gate had drifted red: the recently-landed EMA auth feature (core/auth/ema/*) and ClientSettings components shipped undertested, and the prior 50% branch floor let business-logic branches go untested. CI does not run the coverage gate, so this accumulated. Add tests to bring every failing file above the gate and push branch coverage well past 70 across the board, then raise the per-file branches threshold 50 -> 70 in clients/web/vite.config.ts. - EMA core (emaFlow, resourceContext, jwt, wire): 50-88% -> 86-100% branch - EMA OIDC/transport (idpOidc, tokenEndpoint, transportProvider): -> 97-100% - oauthManager / discovery: 73-83% -> 88-92% - ClientSettingsModal/Form, ConnectionInfoContent: 0-88% -> 96-100% - useClientSettingsDraft / useEmaIdpLoginState hooks: 20% -> 100% - TaskStatusBadge / SortableServerCard outliers: 50% -> 100% Coverable branches reached by mocking useComputedColorScheme (dark theme), dnd-kit useSortable (isDragging), and fetch/OIDC paths. Remaining sub-100 branches are genuinely-unreachable defensive guards or happy-dom-inherent (portal / useMediaQuery / typeof window) and documented in test comments. No source files changed. Global web branch coverage 88.73% -> 90.63%. AGENTS.md updated; cli/tui/launcher stay at 50 (out of scope). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * test: raise per-file coverage gate to 90/90/90/90 across all clients Follows the #1271 web branch-coverage audit (50 -> 70) by raising the whole per-file gate to >=90 on all four dimensions — lines, statements, functions, branches — for web, cli, tui, and launcher (core/ is measured under the web suite). A codebase-wide audit added real tests for every outlier and brought global coverage up across the board. Config: all four vitest/vite coverage configs set to 90/90/90/90. Two pure-type modules (core/mcp/import/types.ts, clients/web/server/ types.ts) added to the web coverage exclude list, matching the existing type-only exclusions. Tests: ~50 test files extended/created across core (auth, ema, mcp, remote, state, storage, react), web components/screens/server, and the cli/tui clients. Highlights: full EMA OAuth flow + transport coverage, inspectorClient/remote server backfill, every Mantine control's handler branches, and the cli unsupported-method / argv-fallback paths. Genuinely-unreachable branches are annotated at the source with justified `/* v8 ignore -- <reason> */` comments rather than lowering the gate: happy-dom-inherent paths (Mantine portal, useMediaQuery, typeof window SSR guards), React StrictMode effect replay, and provably-dead defensive guards (?? fallbacks the types make impossible, Select.onChange values outside the allowed list). No runtime logic changed; the only structural edits are two behavior-preserving refactors (ResourceTemplatePanel hoists the field value to a local so the ignore applies in JSX; ToolsScreen swaps a ternary for an if) and prettier line-wrapping. Also fixed a flaky real-timer debounce assertion in PromptArgumentsForm (asserted an exact sibling-typing interleaving that raced the 300ms debounce; now asserts the load-bearing fact — a non-empty fire-time sibling capture). AGENTS.md updated to describe the uniform 90 gate and the v8-ignore policy. All four client coverage gates, root validate, storybook (370), and smoke pass. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * test(web): make connect-crash SSE reader stop on the terminal event `readSseEvents` looped until the server closed the stream (`done`). The server closes the events stream immediately after writing the terminal `transport_error`, but under the heavy parallel scheduler pressure of a full `coverage` run (v8 instrumentation across all workers) that close can lag far enough behind the event to trip the 30s test timeout — a hang, not a wrong result. This made the test intermittently fail under `npm run coverage` (it passed in isolation and under CI's lighter `test:integration`). Stop reading as soon as the terminal `transport_error` event arrives (cancelling the reader) instead of waiting only for `done`. That event is always the last one the session queue emits, so nothing is lost, and the test no longer depends on stream-close timing. The `done` break is kept for any stream that closes first. Verified green across two consecutive full `npm run test:coverage` runs (3094 tests) where it previously hung, plus isolation (~0.5s). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
| .. | ||
| __tests__ | ||
| scripts | ||
| src | ||
| eslint.config.js | ||
| package-lock.json | ||
| package.json | ||
| README.md | ||
| tsconfig.json | ||
| vitest.config.ts | ||
MCP Inspector Launcher
The launcher is the package that provides the global mcp-inspector binary (e.g. when users run npx @modelcontextprotocol/inspector). It is not a separate user-facing app—it is the single entrypoint that selects and runs one of the clients (web, CLI, or TUI).
Responsibility
- Parse mode from a leading prefix of
--web(default),--cli, or--tuiimmediately after the script name. - Forward all following arguments unchanged (including tokens that look like mode flags).
- Dynamically import that app’s runner from
clients/{web,cli,tui}/build/index.js(relative to the launcher build output) and call it in-process (nospawn()).
All configuration parsing, config-file loading, and server setup are handled by the app runners and by core; the launcher does not interpret config or env vars.
Web server-list flags (--web)
mcp-inspector --web chooses which server list the UI shows and whether it is
editable (see specification/v2_catalog_launch_config.md):
| Invocation | Server list | Editable in UI? |
|---|---|---|
mcp-inspector --web |
Default catalog ~/.mcp-inspector/mcp.json |
Yes |
mcp-inspector --web --catalog <path> (or MCP_CATALOG_PATH=<path>) |
That file as the active catalog (created/seeded if missing) | Yes |
mcp-inspector --web --config <path> |
That file as a read-only session — shown but never written, seeded, or migrated (safe for a foreign config) | No |
mcp-inspector --web --server-url <url> --transport http --header "Name: Value" (or a positional command) |
One ad-hoc server held in memory, connectable with the given --headers |
No |
Rules: --catalog and --config are mutually exclusive; neither combines with
an ad-hoc target or --header; --header requires an ad-hoc HTTP/SSE server
and is applied to that connection (it is no longer a warn-only no-op).
CLI and TUI server-list flags (--cli / --tui)
The CLI and TUI use the same --catalog / --config vocabulary as --web,
resolved by the shared core/mcp/node/config.ts helpers:
| Invocation | Server list source |
|---|---|
mcp-inspector --cli / --tui (no source flag, no ad-hoc target) |
Default writable catalog ~/.mcp-inspector/mcp.json (created/seeded empty if missing) |
--catalog <path> (or MCP_CATALOG_PATH=<path>) |
That file as a writable catalog — seeded empty if missing |
--config <path> |
That file as a read-only session — served as-is, never written or seeded; errors if absent |
positional command / --server-url <url> |
One ad-hoc server |
Rules (shared serverSourceConflict): --catalog and --config are mutually
exclusive, and neither combines with an ad-hoc command/URL target. The CLI/TUI
do not perform catalog CRUD yet — they are read consumers — so the
writable/read-only split currently surfaces only as seed-if-missing
(--catalog/default) vs error-if-missing (--config). Full writable
persistence is tracked in #1482 / #1432.
Production web build (--web, no --dev)
Prod --web serves static assets from clients/web/dist/, which only exists
after a build. In the published package dist/ always ships. In a fresh dev
checkout it is absent, so the runner builds it on demand the first time you
launch (vite build via npm run build:client, run in clients/web) instead
of serving a broken page (#1486). If that build can't run — e.g. dev
dependencies are missing — the launcher exits with an actionable error telling
you to run npm run build (from clients/web) or relaunch with --dev to use
the Vite dev server. --dev never needs dist/; it runs Vite directly.
CI and npm run validate (via the top-level smoke step) exercise this prod
path end-to-end with npm run smoke:web (scripts/smoke-web.mjs): it starts
mcp-inspector --web against the built dist/ and asserts GET / returns the
SPA (HTTP 200) with the injected __INSPECTOR_API_TOKEN__.
CLI and TUI smokes
The top-level smoke step also runs end-to-end smokes for the other two modes
through the built launcher artifact (beyond the --help checks in
smoke:launcher):
npm run smoke:cli(scripts/smoke-cli.mjs) — runsmcp-inspector --cliagainst the bundled stdio test server via a temp--catalogand assertstools/listreturns the server's tools, plus the--catalog/--configresolution paths (default-catalog seed-on-missing, read-only--configerror-without-seed,--catalog/--configconflict).npm run smoke:tui(scripts/smoke-tui.mjs) — launchesmcp-inspector --tui --catalog <temp>and asserts the Ink app renders its first frame within a timeout, then shuts it down (a shallow boot/render check, not full interaction).
Both build test-servers/build on demand if it is missing.
Development
Like the web client, the launcher self-validates from its own folder:
npm run validate # format:check && lint && build && test:coverage
This has no dependency on the other clients being built — it only checks the
launcher's own source. eslint.config.js is a Node-only flat config (the web
client's React/Storybook plugins stripped out), and the per-file coverage gate
covers parse-launcher-argv.ts (the pure arg-parsing logic); src/index.ts is
excluded as binary bootstrap and is instead exercised by the smokes above. The
repo-root validate:launcher simply delegates here (cd clients/launcher && npm run validate).
Publishing
The root @modelcontextprotocol/inspector package ships as one fat tarball: npm run build at the repo root builds all clients, 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.
Architecture
For how the launcher fits with the shared config processor and app runners, see the Launcher and config consolidation document in the repo root docs/ folder.