This document explains the architecture of the mcp-inspector application entrypoints and configuration processing.
Previously, the project suffered from split config logic and an unnecessary process boundary for the web server:
- Two processes for web and CLI: The main
mcp-inspectorentrypoint would parse arguments and thenspawn()a child process for the actual web server (either Vite for dev, ornode dist/server.jsfor prod). - Config via environment variables: To pass config (like server command, transport, auth token, etc.) from the launcher to the child process, the runner serialized everything into an unwieldy list of environment variables (
MCP_INITIAL_*,MCP_ENV_VARS, etc.). - Doesn't scale: Multi-server or complex config required more environment variables and ad-hoc encoding (e.g. JSON in env). This was fragile and easy to get out of sync.
- Config logic split: Config parsing was duplicated across the web runner, the CLI runner, and the TUI runner.
- Direct launch was inconsistent: Running
npm run devor calling a client's binary directly skipped parts of the launcher logic, leading to inconsistent behavior.
The architecture is now consolidated into a single-process model with a shared configuration processor.
A dedicated package under clients/launcher/ (src/index.ts → build/index.js) serves as the global mcp-inspector binary.
- Responsibility: Its only job is to choose which app to run (
--web,--cli, or--tui) and to forwardprocess.argv. - No spawn: It dynamically imports the chosen app's runner and calls it in-process.
All configuration parsing and merging rules live in core (core/mcp/node/config.ts).
- Input: Parsed argument options (file path, server name, env vars, transport, headers) and a mode (
singleormulti). - Output: A list of
MCPServerConfigobjects. - Benefits: Web, CLI, and TUI all share the exact same rules for loading config files, applying command-line overrides, and resolving environment variables.
Each app (Web, CLI, TUI) exposes a runner function (runWeb(argv), runCli(argv), runTui(argv)).
- The runner uses Commander to parse the arguments.
- It calls the shared core config processor with the relevant server-config subset.
- It receives the config list and starts the application logic.
- Direct launch (e.g.
node clients/cli/build/index.js, or a client's ownnpm run dev) just imports the runner and passesprocess.argv. This guarantees identical behavior whether invoked via the launcher or directly. v2 ships a singlemcp-inspectorbin; there are no longer per-client binaries.
The web app no longer uses spawn() or environment variable handoffs. The runner process is the web server.
- WebServerConfig Object: The runner builds a typed
WebServerConfigobject (containing port, initial MCP config, auth, etc.) and passes it directly to the server. - Vite Dev:
startViteDevServer(config)uses Vite's Node API (createServerfromvite) in the same process. It passes the config directly to the Hono Vite plugin viahonoMiddlewarePlugin(config). - Hono Prod:
startHonoServer(config)starts the production server in the same process. - Benefits:
- Simpler & Scalable: No env var encoding/decoding. Multi-server config is easily passed as a standard JS object.
- Easier debugging: A single Node process means no spawn/kill plumbing and clear shutdown logic.
| Component | Responsibility |
|---|---|
| Launcher | Detects --web/--cli/--tui and calls the app runner in-process. |
| Runner | Parses argv using Commander and calls the core config processor. |
| Core Config | Applies file/cli merging rules, returns MCPServerConfig object(s). |
| App Execution | Runner uses the config object to directly start Vite API (dev), Hono API (prod), CLI method, or TUI app. |
This document was written for v1.5 and describes an architecture that still
holds on v2/main; only the paths moved. Two things worth knowing when reading
it against the current tree:
- The clients live under
clients/*(clients/web,clients/cli,clients/tui,clients/launcher). v2 is not an npm workspace — each client keeps its ownpackage.jsonandnode_modules, and shared code incore/is consumed through the@inspector/corebuild-time alias rather than as a dependency. See the root README. - The shared config processor gained the
--catalog(writable) vs.--config(read-only) split, which the launcher's argument tables and MCP server configuration describe.