@modelcontextprotocol/inspector 1.0.0 → 2.0.0-rc.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. package/README.md +199 -351
  2. package/clients/cli/build/index.js +13218 -0
  3. package/clients/launcher/build/index.js +90 -0
  4. package/clients/launcher/build/parse-launcher-argv.js +29 -0
  5. package/clients/tui/build/chunk-MCKGQKYU.js +15 -0
  6. package/clients/tui/build/index.js +14573 -0
  7. package/clients/tui/build/undici-HXPKCIY3.js +25789 -0
  8. package/clients/web/build/chunk-MCKGQKYU.js +15 -0
  9. package/clients/web/build/index.js +4322 -0
  10. package/clients/web/build/undici-HXPKCIY3.js +25789 -0
  11. package/clients/web/dist/assets/css-DfBGKsUM.js +1 -0
  12. package/clients/web/dist/assets/index-Cbtmmw0V.js +300 -0
  13. package/clients/web/dist/assets/index-CiUpWldH.css +1 -0
  14. package/clients/web/dist/assets/json-rfaH9Mx5.js +1 -0
  15. package/clients/web/dist/assets/markdown-PVXc4EJ0.js +1 -0
  16. package/clients/web/dist/assets/markup-Bk4keGDK.js +1 -0
  17. package/clients/web/dist/assets/markup-DPsXihXF.js +1 -0
  18. package/clients/web/dist/assets/prism-light-B8tIwqDr.js +6 -0
  19. package/clients/web/dist/assets/tomorrow-COrD2Qnh.js +1 -0
  20. package/clients/web/dist/assets/yaml-B78V9E0m.js +1 -0
  21. package/clients/web/dist/icons.svg +24 -0
  22. package/clients/web/dist/index.html +17 -0
  23. package/package.json +96 -80
  24. package/scripts/install-clients.mjs +65 -0
  25. package/LICENSE +0 -216
  26. package/cli/build/cli.js +0 -277
  27. package/cli/build/client/connection.js +0 -38
  28. package/cli/build/client/index.js +0 -6
  29. package/cli/build/client/prompts.js +0 -43
  30. package/cli/build/client/resources.js +0 -36
  31. package/cli/build/client/tools.js +0 -87
  32. package/cli/build/client/types.js +0 -1
  33. package/cli/build/error-handler.js +0 -18
  34. package/cli/build/index.js +0 -257
  35. package/cli/build/transport.js +0 -65
  36. package/cli/build/utils/awaitable-log.js +0 -7
  37. package/client/bin/client.js +0 -62
  38. package/client/bin/start.js +0 -350
  39. package/client/dist/assets/OAuthCallback-D56s1eS_.js +0 -55
  40. package/client/dist/assets/OAuthDebugCallback-CdGJkhXe.js +0 -64
  41. package/client/dist/assets/index-BlUSNvoM.css +0 -2738
  42. package/client/dist/assets/index-DjSLdS7f.js +0 -66698
  43. package/client/dist/index.html +0 -14
  44. package/server/build/index.js +0 -886
  45. package/server/build/mcpProxy.js +0 -121
  46. package/server/build/static/sandbox_proxy.html +0 -197
  47. package/server/static/sandbox_proxy.html +0 -197
  48. /package/{client/dist/mcp.svg → clients/web/dist/assets/MCP-CWGQJYyH.svg} +0 -0
package/README.md CHANGED
@@ -1,479 +1,327 @@
1
1
  # MCP Inspector
2
2
 
3
- The MCP inspector is a developer tool for testing and debugging MCP servers.
3
+ A developer tool for inspecting [Model Context Protocol](https://modelcontextprotocol.io) (MCP) servers. It ships as a single package, `@modelcontextprotocol/inspector`, that provides three ways to inspect a server:
4
4
 
5
- ![MCP Inspector Screenshot](https://raw.githubusercontent.com/modelcontextprotocol/inspector/main/mcp-inspector.png)
5
+ - **Web** — a Vite + React + [Mantine](https://mantine.dev) single-page app with a Node backend.
6
+ - **CLI** — a scriptable command-line client for automation, CI, and fast agent feedback loops.
7
+ - **TUI** — an interactive terminal UI built with [Ink](https://github.com/vadimdemedes/ink).
6
8
 
7
- ## Architecture Overview
9
+ All three run through one global `mcp-inspector` binary:
8
10
 
9
- The MCP Inspector consists of two main components that work together:
10
-
11
- - **MCP Inspector Client (MCPI)**: A React-based web UI that provides an interactive interface for testing and debugging MCP servers
12
- - **MCP Proxy (MCPP)**: A Node.js server that acts as a protocol bridge, connecting the web UI to MCP servers via various transport methods (stdio, SSE, streamable-http)
13
-
14
- Note that the proxy is not a network proxy for intercepting traffic. Instead, it functions as both an MCP client (connecting to your MCP server) and an HTTP server (serving the web UI), enabling browser-based interaction with MCP servers that use different transport protocols.
15
-
16
- ## Running the Inspector
17
-
18
- ### Requirements
11
+ ```bash
12
+ npx @modelcontextprotocol/inspector # web UI (default)
13
+ npx @modelcontextprotocol/inspector --cli # CLI
14
+ npx @modelcontextprotocol/inspector --tui # TUI
15
+ ```
19
16
 
20
- - Node.js: ^22.7.5
17
+ > **Repo status.** This is the **v2** line of the Inspector (branch `v2/main`). The `main` branch is the legacy v1 implementation (bug fixes only). v2 will eventually replace `main`. See [`AGENTS.md`](./AGENTS.md) for branch/board conventions.
21
18
 
22
- ### Quick Start (UI mode)
19
+ ## Project layout
23
20
 
24
- To get up and running right away with the UI, just execute the following:
21
+ v2 is **not** an npm workspace. Each client under `clients/*` keeps its own `package.json` and `node_modules`; shared code lives in `core/` and is consumed via a `@inspector/core` build-time alias (no `package.json` of its own). A single `npm install` at the root cascades installs into every client (see [Setup](#setup)).
25
22
 
26
- ```bash
27
- npx @modelcontextprotocol/inspector
23
+ ```
24
+ inspector/
25
+ ├── clients/
26
+ │ ├── web/ # Web client (Vite + React + Mantine). src/ = browser app; server/ = Node dev/prod backend
27
+ │ ├── cli/ # CLI client (tsup bundle, @inspector/core alias)
28
+ │ ├── tui/ # TUI client (Ink + React, tsup bundle)
29
+ │ └── launcher/ # Shared launcher — provides the `mcp-inspector` bin, dispatches to web/cli/tui
30
+ ├── core/ # Shared code consumed via the `@inspector/core` alias (no package.json)
31
+ │ ├── auth/ # OAuth: providers, discovery, storage, mid-session recovery (browser/node/remote backends)
32
+ │ ├── json/ # JSON + parameter/argument conversion utilities
33
+ │ ├── logging/ # Silent pino logger singleton
34
+ │ ├── mcp/ # InspectorClient runtime, state stores, transports, config import
35
+ │ ├── node/ # Node-only shared helpers: version reader, hostUrl (host normalize/canonicalize + all-interfaces/loopback detection)
36
+ │ ├── react/ # React hooks over the state stores
37
+ │ └── storage/ # File I/O helpers for the OAuth persist backends
38
+ ├── test-servers/ # Composable MCP test servers + fixtures used by integration tests
39
+ ├── scripts/ # Root build/verify tooling (install cascade, smokes, verify-build-gate, verify-format-coverage, pack:verify)
40
+ ├── specification/ # Design/build specifications
41
+ ├── AGENTS.md # Contribution rules for agents AND humans (see below)
42
+ └── README.md # You are here
28
43
  ```
29
44
 
30
- The server will start up and the UI will be accessible at `http://localhost:6274`.
31
-
32
- ### Docker Container
45
+ Each client has its own README with client-specific detail:
46
+ [web](./clients/web/README.md) · [cli](./clients/cli/README.md) · [tui](./clients/tui/README.md) · [launcher](./clients/launcher/README.md).
33
47
 
34
- You can also start it in a Docker container with the following command:
48
+ Task-oriented guides live under [`docs/`](./docs):
35
49
 
36
- ```bash
37
- docker run --rm \
38
- -p 127.0.0.1:6274:6274 \
39
- -p 127.0.0.1:6277:6277 \
40
- -e HOST=0.0.0.0 \
41
- -e MCP_AUTO_OPEN_ENABLED=false \
42
- ghcr.io/modelcontextprotocol/inspector:latest
43
- ```
50
+ - [MCP server configuration](./docs/mcp-server-configuration.md) — which server(s) the Inspector connects to: `--catalog` vs. `--config`, ad-hoc targets, the `--` separator, the file format and its Inspector-specific per-server fields. Shared by all three clients; the cli and tui READMEs delegate their server-options sections to it.
51
+ - [Reviewing an MCP App](./docs/mcp-app-review.md) — the CLI-first → one-shot-web recipe for automated App-tool review: `--app-info` probe → deep-link navigate → rendered widget, plus OAuth handoff and proxy support.
52
+ - [Launcher and config consolidation](./docs/launcher-config-consolidation-plan.md) — why the launcher runs a client in-process rather than spawning it, and how the shared config processor fits in.
44
53
 
45
- ### From an MCP server repository
54
+ ## Setup
46
55
 
47
- To inspect an MCP server implementation, there's no need to clone this repo. Instead, use `npx`. For example, if your server is built at `build/index.js`:
56
+ Requires Node `>=22.19.0`.
48
57
 
49
58
  ```bash
50
- npx @modelcontextprotocol/inspector node build/index.js
59
+ npm install # root install; postinstall cascades into every client
51
60
  ```
52
61
 
53
- You can pass both arguments and environment variables to your MCP server. Arguments are passed directly to your server, while environment variables can be set using the `-e` flag:
62
+ - **Fresh clone:** run `npm install` at the repo root.
63
+ - **After a pull that changes a client's dependencies:** re-run `npm install` at the root to re-sync every client.
54
64
 
55
- ```bash
56
- # Pass arguments only
57
- npx @modelcontextprotocol/inspector node build/index.js arg1 arg2
65
+ The cascade (`scripts/install-clients.mjs`) is dev-only — it exits early when the package is installed as a dependency, and the published tarball ships only each client's `build/`, so end users are unaffected. Set `INSPECTOR_SKIP_CLIENT_INSTALL=1` to skip it.
58
66
 
59
- # Pass environment variables only
60
- npx @modelcontextprotocol/inspector -e key=value -e key2=$VALUE2 node build/index.js
67
+ ## Running during development
61
68
 
62
- # Pass both environment variables and arguments
63
- npx @modelcontextprotocol/inspector -e key=value -e key2=$VALUE2 node build/index.js arg1 arg2
69
+ For day-to-day web iteration, run Vite directly from the web client (fast HMR, no launcher build needed):
64
70
 
65
- # Use -- to separate inspector flags from server arguments
66
- npx @modelcontextprotocol/inspector -e key=$VALUE -- node build/index.js -e server-flag
71
+ ```bash
72
+ cd clients/web && npm run dev
67
73
  ```
68
74
 
69
- The inspector runs both an MCP Inspector (MCPI) client UI (default port 6274) and an MCP Proxy (MCPP) server (default port 6277). Open the MCPI client UI in your browser to use the inspector. (These ports are derived from the T9 dialpad mapping of MCPI and MCPP respectively, as a mnemonic). You can customize the ports if needed:
75
+ The launcher-driven scripts below run the **built** launcher, so build first (`npm run build`):
70
76
 
71
77
  ```bash
72
- CLIENT_PORT=8080 SERVER_PORT=9000 npx @modelcontextprotocol/inspector node build/index.js
78
+ npm run web # prod web launcher against clients/web/dist
79
+ npm run web:dev # web launcher in --dev mode (Vite)
73
80
  ```
74
81
 
75
- For more details on ways to use the inspector, see the [Inspector section of the MCP docs site](https://modelcontextprotocol.io/docs/tools/inspector). For help with debugging, see the [Debugging guide](https://modelcontextprotocol.io/docs/tools/debugging).
76
-
77
- ### Servers File Export
82
+ ## The `@inspector/core` shared package
78
83
 
79
- The MCP Inspector provides convenient buttons to export server launch configurations for use in clients such as Cursor, Claude Code, or the Inspector's CLI. The file is usually called `mcp.json`.
84
+ ![Shared code architecture: the four clients over the @inspector/core shared package](specification/diagrams/shared-code-architecture.png)
80
85
 
81
- - **Server Entry** - Copies a single server configuration entry to your clipboard. This can be added to your `mcp.json` file inside the `mcpServers` object with your preferred server name.
86
+ `core/` holds the logic shared by all three clients so that web, CLI, and TUI behave identically. Its entry point is the **`InspectorClient`** class (`core/mcp/`), which owns the connection to an MCP server, the request/response lifecycle, and a set of state stores; `core/react/` exposes React hooks over those stores that both the web and TUI (Ink) React trees consume. OAuth (`core/auth/`) is factored into isomorphic logic plus browser/node/remote backends so the same flows work in the browser, in Node, and against a remote backend.
82
87
 
83
- **STDIO transport example:**
88
+ `core/` intentionally has **no `package.json`** — it is not published on its own. Each client bundles it in via a `@inspector/core` alias:
84
89
 
85
- ```json
86
- {
87
- "command": "node",
88
- "args": ["build/index.js", "--debug"],
89
- "env": {
90
- "API_KEY": "your-api-key",
91
- "DEBUG": "true"
92
- }
93
- }
94
- ```
90
+ - **CLI / TUI:** `esbuildOptions.alias` in their `tsup.config.ts` maps `@inspector/core` → the repo `core/` directory, and `noExternal: [/^@inspector\/core/]` inlines it into the bundle.
91
+ - **Web:** the same alias in `clients/web/vite.config.ts` for the browser app and the Node backend runner.
95
92
 
96
- **SSE transport example:**
93
+ Publishing `core/` as its own package (e.g. for third parties to build on) is deliberately deferred — see issue [#1636](https://github.com/modelcontextprotocol/inspector/issues/1636).
97
94
 
98
- ```json
99
- {
100
- "type": "sse",
101
- "url": "http://localhost:3000/events",
102
- "note": "For SSE connections, add this URL directly in Client"
103
- }
104
- ```
95
+ ## Web client: "dumb components" + Storybook
105
96
 
106
- **Streamable HTTP transport example:**
97
+ The v2 web client is built from **presentational ("dumb") components** — they accept data and callbacks as props and contain only display logic, with no direct data fetching or client state. State comes from the `@inspector/core` hooks, wired in near the top of the tree. This keeps components isolated, testable, and documentable.
107
98
 
108
- ```json
109
- {
110
- "type": "streamable-http",
111
- "url": "http://localhost:3000/mcp",
112
- "note": "For Streamable HTTP connections, add this URL directly in your MCP Client"
113
- }
114
- ```
99
+ That approach is what makes **Storybook** first-class here: every screen and element component has a `*.stories.tsx` file (96+ stories) that renders it against fixture props. Storybook **play functions** double as interaction tests, run headless in CI (`npm run ci:storybook`, Chromium via Playwright).
115
100
 
116
- - **Servers File** - Copies a complete MCP configuration file structure to your clipboard, with your current server configuration added as `default-server`. This can be saved directly as `mcp.json`.
101
+ Styling follows a strict Mantine-first convention (theme variants and component props over CSS classes, `--inspector-*` CSS custom properties over raw color literals). The full rules live in [`AGENTS.md`](./AGENTS.md) under **React instructions** — read them before touching web UI. Element components live in `clients/web/src/components/elements/`; theme variants in `clients/web/src/theme/`.
117
102
 
118
- **STDIO transport example:**
103
+ ## Test servers
119
104
 
120
- ```json
121
- {
122
- "mcpServers": {
123
- "default-server": {
124
- "command": "node",
125
- "args": ["build/index.js", "--debug"],
126
- "env": {
127
- "API_KEY": "your-api-key",
128
- "DEBUG": "true"
129
- }
130
- }
131
- }
132
- }
133
- ```
105
+ `test-servers/` provides **composable MCP servers** used by the integration and smoke suites, so tests exercise a real server over a real transport instead of mocks. A server is assembled from **presets** (fixture factories in `test-servers/src/preset-registry.ts` — tools, resources, prompts, tasks, elicitation, sampling, OAuth, …) and can be driven two ways:
134
106
 
135
- **SSE transport example:**
107
+ - **In-process** — import the factories (`createTestServerHttp`, `createEchoTool`, …) and run the server inside the test's event loop (used by the HTTP integration paths).
108
+ - **As a subprocess** — `test-servers/build/test-server-stdio.js` is spawned as a real stdio child (used by the CLI smoke and stdio integration tests).
136
109
 
137
- ```json
138
- {
139
- "mcpServers": {
140
- "default-server": {
141
- "type": "sse",
142
- "url": "http://localhost:3000/events",
143
- "note": "For SSE connections, add this URL directly in Client"
144
- }
145
- }
146
- }
147
- ```
110
+ Configure a server declaratively with a JSON config (see `test-servers/configs/*.json`) selecting presets, then load it via `--config`. Because the servers are spawned as real subprocesses, the build output must exist first:
148
111
 
149
- **Streamable HTTP transport example:**
112
+ ```bash
113
+ npm run test-servers:build # (from clients/web) → tsc -p test-servers, emits test-servers/build/
114
+ ```
150
115
 
151
- ```json
152
- {
153
- "mcpServers": {
154
- "default-server": {
155
- "type": "streamable-http",
156
- "url": "http://localhost:3000/mcp",
157
- "note": "For Streamable HTTP connections, add this URL directly in your MCP Client"
158
- }
159
- }
160
- }
161
- ```
116
+ The Vite alias `@modelcontextprotocol/inspector-test-server` (in `clients/web/vite.config.ts`) points at `test-servers/build/index.js` so `getTestMcpServerPath()` resolves to a real `.js` path.
162
117
 
163
- These buttons appear in the Inspector UI after you've configured your server settings, making it easy to save and reuse your configurations.
118
+ ### Serving the modern protocol era
164
119
 
165
- For SSE and Streamable HTTP transport connections, the Inspector provides similar functionality for both buttons. The "Server Entry" button copies the configuration that can be added to your existing configuration file, while the "Servers File" button creates a complete configuration file containing the URL for direct use in clients.
120
+ A streamable-HTTP server can also serve the **modern (2026-07-28) protocol era** via the SDK's `createMcpHandler`:
166
121
 
167
- You can paste the Server Entry into your existing `mcp.json` file under your chosen server name, or use the complete Servers File payload to create a new configuration file.
122
+ - Set `transport.modern` in the JSON config — `true` for dual-era stateless serving, or `{ "legacy": "reject" }` for modern-only strict.
123
+ - Or pass `modern` on the `ServerConfig` for an in-process `createTestServerHttp`.
168
124
 
169
- ### Authentication
125
+ This is what lets an Inspector connection negotiating `protocolEra: "auto" | "modern"` reach the modern leg (populated `server/discover`, sessionless). See `test-servers/configs/modern-http.json`.
170
126
 
171
- The inspector supports bearer token authentication for SSE connections. Enter your token in the UI when connecting to an MCP server, and it will be sent in the Authorization header. You can override the header name using the input field in the sidebar.
127
+ ### Showcase configs
172
128
 
173
- ### Security Considerations
129
+ Each config below is a ready-made server for exercising one feature by hand. Load one with `--config`, and unless noted, connect with **Protocol Era = Modern**.
174
130
 
175
- The MCP Inspector includes a proxy server that can run and communicate with local MCP processes. The proxy server should not be exposed to untrusted networks as it has permissions to spawn local processes and can connect to any specified MCP server.
131
+ | Config | Demonstrates | Issue |
132
+ | ----------------------------------------- | -------------------------------------------------- | ---------------------------------------------------------------------- |
133
+ | `modern-mrtr-http.json` | A single MRTR round-trip | — |
134
+ | `mrtr-showcase-http.json` | Every MRTR preset in one server | — |
135
+ | `modern-network-http.json` | Network tab: `Mcp-*` headers + error taxonomy | [#1628](https://github.com/modelcontextprotocol/inspector/issues/1628) |
136
+ | `xmcpheader-modern-http.json` | Tools tab: `x-mcp-header` mirroring and exclusions | [#1632](https://github.com/modelcontextprotocol/inspector/issues/1632) |
137
+ | `pagination-http.json` | Page-by-page list fetching | [#1721](https://github.com/modelcontextprotocol/inspector/issues/1721) |
138
+ | `advertised-extensions-http.json` | Tool registration gated on advertised extensions | [#1739](https://github.com/modelcontextprotocol/inspector/issues/1739) |
139
+ | `logging-{legacy,modern}-http.json` | Logging, both eras | [#1629](https://github.com/modelcontextprotocol/inspector/issues/1629) |
140
+ | `subscriptions-{legacy,modern}-http.json` | Resource subscriptions, both eras | [#1630](https://github.com/modelcontextprotocol/inspector/issues/1630) |
141
+ | `tasks-{legacy,modern}-http.json` | Tasks, both eras | [#1631](https://github.com/modelcontextprotocol/inspector/issues/1631) |
176
142
 
177
- #### Authentication
143
+ #### MRTR
178
144
 
179
- The MCP Inspector proxy server requires authentication by default. When starting the server, a random session token is generated and printed to the console:
145
+ `modern-mrtr-http.json` serves the `mrtr_confirm` tool (preset `mrtr_confirm`, `createMrtrTool`) over the modern leg. Its handler returns `inputRequired(...)` embedding a form elicitation, so invoking it produces a real round-trip: `input_required` → the client fulfils the embedded elicitation and retries with a new id → `complete`.
180
146
 
181
- ```
182
- 🔑 Session token: 3a1c267fad21f7150b7d624c160b7f09b0b8c4f623c7107bbf13378f051538d4
147
+ The Inspector drives MRTR manually (`inputRequired: { autoFulfill: false }`), so the embedded elicitation pauses at the pending-request modal (tagged "input_required") for you to answer, then the retry completes. Useful for eyeballing both that pending-request UX and the Protocol view's MRTR conversation grouping.
183
148
 
184
- 🔗 Open inspector with token pre-filled:
185
- http://localhost:6274/?MCP_PROXY_AUTH_TOKEN=3a1c267fad21f7150b7d624c160b7f09b0b8c4f623c7107bbf13378f051538d4
186
- ```
149
+ `mrtr-showcase-http.json` bundles every MRTR preset in one server:
187
150
 
188
- This token must be included as a Bearer token in the Authorization header for all requests to the server. The inspector will automatically open your browser with the token pre-filled in the URL.
151
+ | Preset | Behavior |
152
+ | --------------- | ------------------------------------------------------------------------------ |
153
+ | `mrtr_confirm` | Single round |
154
+ | `mrtr_two_step` | Two elicitation rounds via `requestState` |
155
+ | `mrtr_sample` | Embedded sampling → the Sampling panel |
156
+ | `mrtr_roots` | Embedded `roots/list`, auto-answered silently from configured roots (no modal) |
157
+ | `mrtr_edge` | An `inputRequests`-only round, then a `requestState`-only round |
158
+ | `mrtr_loop` | Never completes → trips the `MRTR_MAX_ROUNDS` bound |
189
159
 
190
- **Automatic browser opening** - The inspector now automatically opens your browser with the token pre-filled in the URL when authentication is enabled.
160
+ > The legacy `collect_elicitation` preset calls `server.elicitInput`, which errors on the 2026-07-28 leg — server→client requests aren't allowed there. MRTR is the modern replacement.
191
161
 
192
- **Alternative: Manual configuration** - If you already have the inspector open:
162
+ #### Network tab — standardized headers and error taxonomy
193
163
 
194
- 1. Click the "Configuration" button in the sidebar
195
- 2. Find "Proxy Session Token" and enter the token displayed in the proxy console
196
- 3. Click "Save" to apply the configuration
164
+ `modern-network-http.json` covers SEP-2243 / SEP-2575. It serves a `get_weather` tool whose `city` argument carries an `x-mcp-header: "City"` annotation, so a modern client mirrors it to `Mcp-Param-City`.
197
165
 
198
- The token will be saved in your browser's local storage for future use.
166
+ It also serves four `trigger_*` tools that the modern leg's spec-error injector (`transport.modern.injectSpecErrors: true`) answers with a real HTTP status plus JSON-RPC error body:
199
167
 
200
- If you need to disable authentication (NOT RECOMMENDED), you can set the `DANGEROUSLY_OMIT_AUTH` environment variable:
168
+ | Tool | Response |
169
+ | ----------------------------- | ---------------------------------------- |
170
+ | `trigger_header_mismatch` | `400` / `-32020` |
171
+ | `trigger_missing_capability` | `400` / `-32021` |
172
+ | `trigger_unsupported_version` | `400` / `-32022` (with `data.supported`) |
173
+ | `trigger_method_not_found` | `404` / `-32601` |
201
174
 
202
- ```bash
203
- DANGEROUSLY_OMIT_AUTH=true npm start
204
- ```
175
+ Open the Network tab to see the mirrored `Mcp-*` headers highlighted, sentinel values decoded, and each error rendered distinctly.
205
176
 
206
- ---
177
+ > **`Mcp-Param-*` mirroring is skipped by the SDK in the browser** (`detectProbeEnvironment() !== "browser"`). So calling `get_weather` from the **web** client omits `Mcp-Param-City` and the strict server answers `-32020`. The same tool is callable from the Node CLI/TUI, where mirroring is active.
207
178
 
208
- **🚨 WARNING 🚨**
179
+ #### `x-mcp-header` in the Tools tab
209
180
 
210
- Disabling authentication with `DANGEROUSLY_OMIT_AUTH` is incredibly dangerous! Disabling auth leaves your machine open to attack not just when exposed to the public internet, but also **via your web browser**. Meaning, visiting a malicious website OR viewing a malicious advertizement could allow an attacker to remotely compromise your computer. Do not disable this feature unless you truly understand the risks.
181
+ `xmcpheader-modern-http.json` serves:
211
182
 
212
- Read more about the risks of this vulnerability on Oligo's blog: [Critical RCE Vulnerability in Anthropic MCP Inspector - CVE-2025-49596](https://www.oligo.security/blog/critical-rce-vulnerability-in-anthropic-mcp-inspector-cve-2025-49596)
183
+ - `echo` — plain tool.
184
+ - `get_weather` — a **valid** `x-mcp-header: "City"` annotation on its `city` argument.
185
+ - `invalid_header_tool` — an annotation using the header name `"Bad Header"`. The space makes it an invalid RFC 9110 token, so the whole tool definition is invalid.
186
+ - `trigger_invalid_params` — answered with a real `-32602 Invalid params` error whose message is _not_ about a missing tool.
213
187
 
214
- ---
188
+ Open the Tools tab: `get_weather`'s detail panel shows a **"Mirrored request headers (SEP-2243)"** section (`city → Mcp-Param-City`), and `invalid_header_tool` appears struck-through under an **"Excluded (SEP-2243)"** divider with the reason on hover. A conforming Streamable HTTP client MUST drop it from `tools/list`; the Inspector surfaces _why_.
215
189
 
216
- You can also set the token via the `MCP_PROXY_AUTH_TOKEN` environment variable when starting the server:
190
+ Under SDK v2 a `tools/call` rejecting with `-32602` renders as a distinct error panel rather than an `isError` result — headed **"Unknown Tool"** when the message names a missing tool, or **"Invalid Parameters"** otherwise (run `trigger_invalid_params`).
217
191
 
218
- ```bash
219
- MCP_PROXY_AUTH_TOKEN=$(openssl rand -hex 32) npm start
220
- ```
221
-
222
- #### Local-only Binding
223
-
224
- By default, both the MCP Inspector proxy server and client bind only to `localhost` to prevent network access. This ensures they are not accessible from other devices on the network. If you need to bind to all interfaces for development purposes, you can override this with the `HOST` environment variable:
192
+ #### Page-by-page fetching
225
193
 
226
- ```bash
227
- HOST=0.0.0.0 npm start
228
- ```
194
+ `pagination-http.json` serves 12 tools, 12 resources, and 12 prompts (presets `numbered_tools` / `numbered_resources` / `numbered_prompts`, `count: 12`) with a `maxPageSize` of 4 each, so every list paginates into three pages.
229
195
 
230
- **Warning:** Only bind to all interfaces in trusted network environments, as this exposes the proxy server's ability to execute local processes and both services to network access.
196
+ Turn on **"Fetch Lists One Page at a Time"** (Server Settings — the `paginatedLists` setting, or the **Paginated** switch in a list sidebar) and the lists load page 1 only (4 items) with a **Load next page** control and an _N pages loaded_ status. Each click fetches the next 4 and appends them; Refresh resets to page 1. With the switch off (the default), the same lists auto-aggregate all three pages on connect.
231
197
 
232
- #### DNS Rebinding Protection
198
+ #### Advertised extensions
233
199
 
234
- To prevent DNS rebinding attacks, the MCP Inspector validates the `Origin` header on incoming requests. By default, only requests from the client origin are allowed (respects `CLIENT_PORT` if set, defaulting to port 6274). You can configure additional allowed origins by setting the `ALLOWED_ORIGINS` environment variable (comma-separated list):
200
+ `advertised-extensions-http.json` serves `echo` (always) and a `get_weather` tool **gated on the `io.modelcontextprotocol/tasks` extension** (`extensionGatedTools`): the tool is registered but starts disabled, and the server enables it on `notifications/initialized` only when the client declared that extension in its `capabilities.extensions`.
235
201
 
236
- ```bash
237
- ALLOWED_ORIGINS=http://localhost:6274,http://localhost:8000 npm start
238
- ```
202
+ 1. Connect — the Inspector advertises the Tasks extension by default, so the Tools list shows both `echo` and `get_weather`.
203
+ 2. Open **Server Settings → Advertised Extensions**, uncheck **Tasks (io.modelcontextprotocol/tasks)**, and reconnect.
204
+ 3. The client now advertises no extensions, the server never enables `get_weather`, and the Tools list shows only `echo`.
239
205
 
240
- ### Configuration
206
+ This is the debugging knob for a server legitimately changing tool registration based on what the client advertises. Legacy stateful leg only — the modern per-request leg has no persistent `oninitialized`.
241
207
 
242
- The MCP Inspector supports the following configuration settings. To change them, click on the `Configuration` button in the MCP Inspector UI:
208
+ #### Logging, both eras
243
209
 
244
- | Setting | Description | Default |
245
- | --------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------- | ------- |
246
- | `MCP_SERVER_REQUEST_TIMEOUT` | Client-side timeout (ms) - Inspector will cancel the request if no response is received within this time. Note: servers may have their own timeouts | 300000 |
247
- | `MCP_REQUEST_TIMEOUT_RESET_ON_PROGRESS` | Reset timeout on progress notifications | true |
248
- | `MCP_REQUEST_MAX_TOTAL_TIMEOUT` | Maximum total timeout for requests sent to the MCP server (ms) (Use with progress notifications) | 60000 |
249
- | `MCP_PROXY_FULL_ADDRESS` | Set this if you are running the MCP Inspector Proxy on a non-default address. Example: http://10.1.1.22:5577 | "" |
250
- | `MCP_AUTO_OPEN_ENABLED` | Enable automatic browser opening when inspector starts (works with authentication enabled). Only as environment var, not configurable in browser. | true |
210
+ `logging-legacy-http.json` and `logging-modern-http.json` both serve `logging: true` plus a `send_notification` tool that emits a `notifications/message` at a chosen level. The legacy one is a plain streamable-HTTP server; the modern one sets `transport.modern: true`.
251
211
 
252
- **Note on Timeouts:** The timeout settings above control when the Inspector (as an MCP client) will cancel requests. These are independent of any server-side timeouts. For example, if a server tool has a 10-minute timeout but the Inspector's timeout is set to 30 seconds, the Inspector will cancel the request after 30 seconds. Conversely, if the Inspector's timeout is 10 minutes but the server times out after 30 seconds, you'll receive the server's timeout error. For tools that require user interaction (like elicitation) or long-running operations, ensure the Inspector's timeout is set appropriately.
212
+ - **Legacy** — the **Logs** tab gives a session-scoped **Set Active Level** selector + **Set** button. Calling `send_notification` streams the log into the panel.
213
+ - **Modern** — the same tab instead shows **Log Level per Request**. Pick a level to opt in and the client stamps `_meta["io.modelcontextprotocol/logLevel"]` on every subsequent request (verify in the Network tab's request body). Calling `send_notification` streams the log over the request's SSE response. Set it back to **Off** and the same call is silently gated — the request omits the `logLevel` key, so the log never arrives.
253
214
 
254
- These settings can be adjusted in real-time through the UI and will persist across sessions.
215
+ That gating is faithful to the spec ("a server MUST NOT emit `notifications/message` for a request that didn't opt in") because `send_notification` emits through the SDK's request-scoped, threshold-aware `extra.log` (`ctx.mcpReq.log`). On the modern leg it reads the per-request `logLevel` opt-in from the request envelope and drops the message when the client didn't opt in or the level is below the requested severity; on legacy it honors the session level from `logging/setLevel`. Because it emits through the request's `notify`, the modern response upgrades to SSE and the log rides the originating request's stream.
255
216
 
256
- The inspector also supports configuration files to store settings for different MCP servers. This is useful when working with multiple servers or complex configurations:
217
+ #### Resource subscriptions, both eras
257
218
 
258
- ```bash
259
- npx @modelcontextprotocol/inspector --config path/to/config.json --server everything
260
- ```
219
+ `subscriptions-legacy-http.json` and `subscriptions-modern-http.json` both serve three `numbered_resources` with `subscriptions: true`. The legacy one also serves an `update_resource` tool; the modern one sets `transport.modern: true`.
261
220
 
262
- Example server configuration file:
263
-
264
- ```json
265
- {
266
- "mcpServers": {
267
- "everything": {
268
- "command": "npx",
269
- "args": ["@modelcontextprotocol/server-everything"],
270
- "env": {
271
- "hello": "Hello MCP!"
272
- }
273
- },
274
- "my-server": {
275
- "command": "node",
276
- "args": ["build/index.js", "arg1", "arg2"],
277
- "env": {
278
- "key": "value",
279
- "key2": "value2"
280
- }
281
- }
282
- }
283
- }
284
- ```
221
+ - **Legacy** — open a resource in the **Resources** tab and click **Subscribe**. The client sends `resources/subscribe` and the Subscriptions section lists the URI with no stream chrome. Call `update_resource` with that URI and the server updates the content and emits `notifications/resources/updated`, stamping the subscribed tile's last-updated time.
222
+ - **Modern** — the same Subscribe instead sends **`subscriptions/listen`** (its filter carries `resourceSubscriptions` plus the `resourcesListChanged` opt-in) and resolves on `notifications/subscriptions/acknowledged`. The Subscriptions section then shows a stream-status badge (`Connecting…` → `Listening`) in its header, and reconnects by re-listing if the long-lived stream drops.
285
223
 
286
- #### Transport Types in Config Files
224
+ The modern config deliberately **omits** `update_resource`. The SDK's modern leg is stateless/per-request (`createMcpHandler(() => createMcpServer(config))`), so the tool would run against a throwaway server instance — the content change wouldn't persist for the next `resources/read`, and its `resources/updated` wouldn't reach the separate listen stream. More confusing than useful.
287
225
 
288
- The inspector automatically detects the transport type from your config file. You can specify different transport types:
226
+ So the live update-notification round-trip is demonstrated on the legacy (stateful-session) server, and the modern server is for the subscribe/listen/badge behavior. The Inspector's _receive_ path is era-transparent, so a real stateful modern server that routes `resources/updated` onto the listen stream drives the subscribed tile the same way.
289
227
 
290
- **STDIO (default):**
228
+ #### Tasks, both eras
291
229
 
292
- ```json
293
- {
294
- "mcpServers": {
295
- "my-stdio-server": {
296
- "type": "stdio",
297
- "command": "npx",
298
- "args": ["@modelcontextprotocol/server-everything"]
299
- }
300
- }
301
- }
302
- ```
230
+ **Legacy** (`tasks-legacy-http.json`) advertises `capabilities.tasks` (`tasks: { list, cancel }`) with the `simple_task` / `progress_task` / `elicitation_task` presets. Run one of those tools with **Run as task** on, and the **Tasks** tab lists it (populated via `tasks/list`), polls `tasks/get`, fetches the payload with the blocking `tasks/result`, and cancels with `tasks/cancel`.
303
231
 
304
- **SSE (Server-Sent Events):**
305
-
306
- ```json
307
- {
308
- "mcpServers": {
309
- "my-sse-server": {
310
- "type": "sse",
311
- "url": "http://localhost:3000/sse"
312
- }
313
- }
314
- }
315
- ```
232
+ **Modern** (`tasks-modern-http.json`) sets `transport.modern: true` and `tasksExtension: true`, advertising the `io.modelcontextprotocol/tasks` extension (SEP-2663) and serving `modern_task` / `modern_input_task`. The **Tasks** tab is gated on the negotiated extension, not `capabilities.tasks`.
316
233
 
317
- **Streamable HTTP:**
318
-
319
- ```json
320
- {
321
- "mcpServers": {
322
- "my-http-server": {
323
- "type": "streamable-http",
324
- "url": "http://localhost:3000/mcp"
325
- }
326
- }
327
- }
328
- ```
234
+ - Run `modern_task` as a task — the `tools/call` returns a `CreateTaskResult` (`resultType: "task"`, visible in the Protocol/Network tabs), the client polls **`tasks/get`** (no `tasks/list`), and the completed task inlines its result (no blocking `tasks/result`).
235
+ - Run `modern_input_task` — the task moves to `input_required`, surfacing an embedded elicitation through the pending-request modal. Answering it sends **`tasks/update`** with the `inputResponses`, and the next poll completes.
329
236
 
330
- #### Default Server Selection
237
+ SDK v2 removed all tasks support **and** era-gates the `tasks/*` spec methods out of the modern era on both sides. So the Inspector drives the extension itself — the `resultType: "task"` frame is rewritten at the transport into a `CallToolResult` carrying the handle, and `tasks/get` / `update` / `cancel` ride a raw-wire request channel with the full modern envelope. The test server serves `tasks/*` from an Express interceptor ahead of the SDK handler, since the SDK's modern leg would answer them `-32601`.
331
238
 
332
- You can launch the inspector without specifying a server name if your config has:
239
+ The Tasks tab's **Refresh** re-polls the handles already known to the client — modern has no server-side task list.
333
240
 
334
- 1. **A single server** - automatically selected:
241
+ ## Building
335
242
 
336
243
  ```bash
337
- # Automatically uses "my-server" if it's the only one
338
- npx @modelcontextprotocol/inspector --config mcp.json
244
+ npm run build # builds all clients: web → cli → tui → launcher
339
245
  ```
340
246
 
341
- 2. **A server named "default-server"** - automatically selected:
342
-
343
- ```json
344
- {
345
- "mcpServers": {
346
- "default-server": {
347
- "command": "npx",
348
- "args": ["@modelcontextprotocol/server-everything"]
349
- },
350
- "other-server": {
351
- "command": "node",
352
- "args": ["other.js"]
353
- }
354
- }
355
- }
356
- ```
247
+ Individual clients: `build:web`, `build:cli`, `build:tui`, `build:launcher`. The web build produces both the browser SPA (`clients/web/dist`, Vite) and the Node prod-server runner (`clients/web/build`, tsup).
357
248
 
358
- > **Tip:** You can easily generate this configuration format using the **Server Entry** and **Servers File** buttons in the Inspector UI, as described in the Servers File Export section above.
249
+ ## Testing & the quality gate
359
250
 
360
- You can also set the initial `transport` type, `serverUrl`, `serverCommand`, and `serverArgs` via query params, for example:
251
+ Each client self-validates from its own folder; the root scripts chain them. There is **no** aggregate root `test` script — use `validate` (fast) or `coverage` (the gate).
361
252
 
362
- ```
363
- http://localhost:6274/?transport=sse&serverUrl=http://localhost:8787/sse
364
- http://localhost:6274/?transport=streamable-http&serverUrl=http://localhost:8787/mcp
365
- http://localhost:6274/?transport=stdio&serverCommand=npx&serverArgs=arg1%20arg2
366
- ```
253
+ | Script | What it does |
254
+ | ----------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
255
+ | `npm run validate` | Runs `verify:format-coverage` (asserts every tracked source file is format-gated) first, then `validate:core` (the shared `core/` `format:check` + `lint` gate), then per client: `format:check` + `lint` + **`typecheck`** (cli/tui only) + `build` + fast unit tests. The quick inner-loop check. |
256
+ | `npm run coverage` | The **per-file ≥90% gate** (lines/statements/functions/branches) under v8 instrumentation, per client. CI-enforced. For web this also runs the integration project and covers the shared `core/` runtime (including `core/json` and `core/client`). |
257
+ | `npm run smoke` | End-to-end smokes through the built launcher (`--help` dispatch + prod cli/tui/web), plus a headless-Chromium boot smoke that runs the prod web bundle and asserts a clean first render (no uncaught error — sync exception or unhandled rejection, how a Node built-in reaching the browser bundle manifests). |
258
+ | `npm run verify:build-gate` | Runs a real `vite build` with a Node built-in forced into the browser graph and asserts the build **fails** via the #1769 gate (which turns Vite's browser-externalization warning into a hard error). Guards against the warning phrasing drifting in a Vite bump and silently disabling the gate. Part of `npm run ci`. |
259
+ | `npm run verify:format-coverage` | Parses the `format:check` globs out of every `package.json` (only those reachable from `validate`), enumerates all tracked source files, and **fails** listing any not covered by a glob — the durable guard for the "every first-party source file is format-gated" invariant (#1792). Runs first in `validate`. |
260
+ | `npm run test:scripts` | Table-driven unit tests (`node --test`) for the guard's own pure parsers (`scripts/lib/npm-scripts.mjs` + the exported helpers of `verify-typecheck-coverage.mjs`), one case per rule they encode. Runs in `validate` — and `verify:typecheck-coverage` guards _this_ gate in turn (reachable from `validate`, non-empty test set, every test file matched by the `test:scripts` glob), since `node --test` silently skips a file its glob misses and still exits 0. |
261
+ | `npm run verify:typecheck-coverage` | The typecheck-coverage analog of the above (#1791): for each Node client (auto-discovered from disk — enrolled via its `typecheck` script's projects, or for a `tsc -b` client like `clients/web` via its `tsconfig.json` `references`) it runs those projects with `tsc --listFilesOnly`, unions them, and **fails** listing any tracked `.ts`/`.tsx`/`.mts`/`.cts` under the client that lands in no project (so a new top-level config/helper can't silently go untypechecked). It also requires, deny-by-default, the first-party TS no client owns (`test-servers/src`, the root `vitest.shared.mts`, all of `core/`, and any new top-level location) to land in some client project's tsc pass — so a `core` `*.tsx` web's projects don't reach is caught too. Also asserts the gate is wired (each client's typecheck pass — its `typecheck` script, or web's `tsc -b` — is reachable from its `validate`, and the root chain runs each client's `validate`). Runs in `validate`. |
262
+ | `npm run ci` | **Mandatory pre-push command.** `validate` → `coverage` → `verify:build-gate` → `smoke` → Storybook. A true superset of GitHub CI. |
263
+ | `npm run pack:verify` | Publish smoke — see [Publishing](#publishing). |
367
264
 
368
- You can also set initial config settings via query params, for example:
265
+ Per-client scripts exist too (`validate:web`, `coverage:cli`, `smoke:tui`, …), plus root `validate:core` / `format:core` for the shared `core/` package, `format:scripts` for the root `scripts/` tooling, and `format:shared` / `lint:shared` for the root "shared" surface (`test-servers/src/**`, `vitest.shared.mts`, the root `eslint.config.js`). Run `npm run format` before committing — the root `format` fixes `core/`, the root `scripts/`, the shared surface, and every client; `validate` runs the non-fixing `format:check` and fails CI on any unformatted file.
369
266
 
370
- ```
371
- http://localhost:6274/?MCP_SERVER_REQUEST_TIMEOUT=60000&MCP_REQUEST_TIMEOUT_RESET_ON_PROGRESS=false&MCP_PROXY_FULL_ADDRESS=http://10.1.1.22:5577
372
- ```
267
+ For the full testing rules — the ≥90% per-file gate, where test files live, the unit vs. integration vs. storybook projects, and the `v8 ignore` policy — see [`AGENTS.md`](./AGENTS.md).
373
268
 
374
- Note that if both the query param and the corresponding localStorage item are set, the query param will take precedence.
269
+ ## Publishing
375
270
 
376
- ### From this repository
271
+ 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.
377
272
 
378
- If you're working on the inspector itself:
273
+ ### What ships, and the packaging invariants
379
274
 
380
- Development mode:
275
+ 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`:
381
276
 
382
- ```bash
383
- npm run dev
277
+ - **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.
278
+ - **`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`.
279
+ - **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.json`s 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](https://github.com/modelcontextprotocol/inspector/issues/1639)).
384
280
 
385
- # To co-develop with the typescript-sdk package (assuming it's cloned in ../typescript-sdk; set MCP_SDK otherwise):
386
- npm run dev:sdk "cd sdk && npm run examples:simple-server:w"
387
- # then open http://localhost:3000/mcp as SHTTP in the inspector.
388
- # To go back to the deployed SDK version:
389
- # npm run unlink:sdk && npm i
390
- ```
281
+ ### `npm run pack:verify` — publish smoke against the real tarball
391
282
 
392
- > **Note for Windows users:**
393
- > On Windows, use the following command instead:
394
- >
395
- > ```bash
396
- > npm run dev:windows
397
- > ```
283
+ 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 pack`s 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, and a prod `--web` boot that must serve `/` from the shipped `dist`. 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`/`ci` loop.
398
284
 
399
- Production mode:
285
+ ### Cutting a release
400
286
 
401
- ```bash
402
- npm run build
403
- npm start
404
- ```
287
+ Publishing is automated by two release-gated jobs in [`.github/workflows/main.yml`](.github/workflows/main.yml) (`github.event_name == 'release'`, both `needs: build`):
405
288
 
406
- ### CLI Mode
289
+ - **`publish`** — the npm package. Runs `npm run pack:verify` as the pre-publish gate, asserts the release tag matches the root `package.json` version, then `npm publish --access public --provenance` — a single `npm publish` (v2 is not an npm workspace, so there is no v1-style `publish-all`/`--workspaces`), with a signed provenance attestation via GitHub OIDC (`id-token: write`, `environment: release`). Authentication is npm **trusted publishing** — there is no `NPM_TOKEN` secret, and adding one would break the publish (an empty `NODE_AUTH_TOKEN` makes npm fail `ENEEDAUTH` before OIDC is attempted). The job upgrades the npm CLI to `>=11.5.1` first, since trusted publishing requires it and Node 22 bundles npm 10.x.
290
+ - **`publish-github-container-registry`** — the container image (see [Docker](#docker)).
407
291
 
408
- CLI mode enables programmatic interaction with MCP servers from the command line, ideal for scripting, automation, and integration with coding assistants. This creates an efficient feedback loop for MCP server development.
292
+ Because there is **one version number** (only the root `package.json` has one — the clients carry none, so there is nothing to keep in sync and no `check-version` step), the release flow is just:
409
293
 
410
294
  ```bash
411
- npx @modelcontextprotocol/inspector --cli node build/index.js
295
+ npm version <major|minor|patch> # bumps the root package.json + tags
296
+ git push --follow-tags
297
+ # then draft & publish a GitHub Release for that tag → triggers `publish`
412
298
  ```
413
299
 
414
- The CLI mode supports most operations across tools, resources, and prompts. A few examples:
415
-
416
- ```bash
417
- # Basic usage
418
- npx @modelcontextprotocol/inspector --cli node build/index.js
419
-
420
- # With config file
421
- npx @modelcontextprotocol/inspector --cli --config path/to/config.json --server myserver
422
-
423
- # List available tools
424
- npx @modelcontextprotocol/inspector --cli node build/index.js --method tools/list
425
-
426
- # Call a specific tool
427
- npx @modelcontextprotocol/inspector --cli node build/index.js --method tools/call --tool-name mytool --tool-arg key=value --tool-arg another=value2
300
+ The release's target commit selects which workflow runs, so this only publishes when a release is cut from a commit carrying this (v2) workflow.
428
301
 
429
- # Call a tool with JSON arguments
430
- npx @modelcontextprotocol/inspector --cli node build/index.js --method tools/call --tool-name mytool --tool-arg 'options={"format": "json", "max_tokens": 100}'
302
+ ### Docker
431
303
 
432
- # List available resources
433
- npx @modelcontextprotocol/inspector --cli node build/index.js --method resources/list
304
+ A container image is published to GHCR (`ghcr.io/modelcontextprotocol/inspector`, `linux/amd64` + `linux/arm64`) by the release workflow. The [`Dockerfile`](Dockerfile) is a two-stage build: the first stage installs and `npm pack`s the publishable tarball; the second stage `npm install -g`s that tarball, so the image ships the exact same artifact as npm, with a clean `mcp-inspector` bin.
434
305
 
435
- # List available prompts
436
- npx @modelcontextprotocol/inspector --cli node build/index.js --method prompts/list
437
-
438
- # Connect to a remote MCP server (default is SSE transport)
439
- npx @modelcontextprotocol/inspector --cli https://my-mcp-server.example.com
440
-
441
- # Connect to a remote MCP server (with Streamable HTTP transport)
442
- npx @modelcontextprotocol/inspector --cli https://my-mcp-server.example.com --transport http --method tools/list
443
-
444
- # Connect to a remote MCP server (with custom headers)
445
- npx @modelcontextprotocol/inspector --cli https://my-mcp-server.example.com --transport http --method tools/list --header "X-API-Key: your-api-key"
446
-
447
- # Call a tool on a remote server
448
- npx @modelcontextprotocol/inspector --cli https://my-mcp-server.example.com --method tools/call --tool-name remotetool --tool-arg param=value
306
+ ```bash
307
+ # run the web UI (reads the auth token from the container logs)
308
+ docker run --rm -p 6274:6274 ghcr.io/modelcontextprotocol/inspector
449
309
 
450
- # List resources from a remote server
451
- npx @modelcontextprotocol/inspector --cli https://my-mcp-server.example.com --method resources/list
310
+ # or build the image locally
311
+ docker build -t mcp-inspector .
312
+ docker run --rm -p 6274:6274 mcp-inspector
452
313
  ```
453
314
 
454
- ### UI Mode vs CLI Mode: When to Use Each
455
-
456
- | Use Case | UI Mode | CLI Mode |
457
- | ------------------------ | ------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
458
- | **Server Development** | Visual interface for interactive testing and debugging during development | Scriptable commands for quick testing and continuous integration; creates feedback loops with AI coding assistants like Cursor for rapid development |
459
- | **Resource Exploration** | Interactive browser with hierarchical navigation and JSON visualization | Programmatic listing and reading for automation and scripting |
460
- | **Tool Testing** | Form-based parameter input with real-time response visualization | Command-line tool execution with JSON output for scripting |
461
- | **Prompt Engineering** | Interactive sampling with streaming responses and visual comparison | Batch processing of prompts with machine-readable output |
462
- | **Debugging** | Request history, visualized errors, and real-time notifications | Direct JSON output for log analysis and integration with other tools |
463
- | **Automation** | N/A | Ideal for CI/CD pipelines, batch processing, and integration with coding assistants |
464
- | **Learning MCP** | Rich visual interface helps new users understand server capabilities | Simplified commands for focused learning of specific endpoints |
315
+ The image defaults to `--web` bound to `0.0.0.0:6274` with browser auto-open disabled; override the args to run another mode (`docker run --rm ghcr.io/modelcontextprotocol/inspector --cli …`). Pass `-e MCP_INSPECTOR_API_TOKEN=…` to set a known token (otherwise one is generated and printed in the logs), or `-e DANGEROUSLY_OMIT_AUTH=true` to disable auth. Binding `0.0.0.0` (all network interfaces) is refused by default outside a container — it exposes the process-spawning backend to the local network — so the image opts in explicitly with `DANGEROUSLY_BIND_ALL_INTERFACES=true` (already set in the `Dockerfile`); a bare `HOST=0.0.0.0` without that flag exits with an error. If you **remap the published port** (`-p 8080:6274`), the browser's origin (`http://localhost:8080`) no longer matches the in-container port, so set `-e ALLOWED_ORIGINS=http://localhost:8080,http://127.0.0.1:8080` (or run `-e CLIENT_PORT=8080 -p 8080:8080`) or connects will 403. `ALLOWED_ORIGINS` **replaces** the default list rather than merging, so list every loopback form you'll browse from (see the [web README](./clients/web/README.md#host-binding--the-origin-allow-list)). The image runs as the non-root `node` user and has a `HEALTHCHECK` that probes the web UI — it assumes the default `--web` mode, so add `--no-healthcheck` when running `--cli`/`--tui` (which have no web server).
465
316
 
466
- ## Tool Input Validation Guidelines
317
+ ## Contributing — `AGENTS.md` and `CLAUDE.md`
467
318
 
468
- When implementing or modifying tool input parameter handling in the Inspector:
319
+ **[`AGENTS.md`](./AGENTS.md) is the contract for changing this codebase, and it applies to humans and AI agents alike.** It is not agent-only boilerplate — it holds the project's real conventions: the issue-and-board workflow, branch/label rules, the TypeScript and Mantine/React standards, the testing and coverage requirements, and the mandatory pre-push gate. Read it before making changes, and keep it up to date when you change structure, tooling, or rules.
469
320
 
470
- - **Omit optional fields with empty values** - When processing form inputs, omit empty strings or null values for optional parameters, UNLESS the field has an explicit default value in the schema that matches the current value
471
- - **Preserve explicit default values** - If a field schema contains an explicit default (e.g., `default: null`), and the current value matches that default, include it in the request. This is a meaningful value the tool expects
472
- - **Always include required fields** - Preserve required field values even when empty, allowing the MCP server to validate and return appropriate error messages
473
- - **Defer deep validation to the server** - Implement basic field presence checking in the Inspector client, but rely on the MCP server for parameter validation according to its schema
321
+ `CLAUDE.md` is the entry point the [Claude Code](https://claude.com/claude-code) agent loads automatically; it simply includes `AGENTS.md` and this README, so both agents and humans work from the same source of truth. If you use a different agent that reads `AGENTS.md`, you get the same rules.
474
322
 
475
- These guidelines maintain clean parameter passing and proper separation of concerns between the Inspector client and MCP servers.
323
+ A key rule worth surfacing here: **all work is issue-driven.** Before starting, find or create a tracking issue on the v2 project board; open PRs against `v2/main` with `Closes #<issue>`. The exact recipes (labels, board IDs, statuses) are in `AGENTS.md`.
476
324
 
477
325
  ## License
478
326
 
479
- This project is licensed under the MIT License—see the [LICENSE](LICENSE) file for details.
327
+ MIT.