@modelcontextprotocol/inspector 1.0.1 → 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 -361
  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 -290
  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 -355
  39. package/client/dist/assets/OAuthCallback-DKaW5Tk8.js +0 -55
  40. package/client/dist/assets/OAuthDebugCallback-Bx3X7Zkh.js +0 -64
  41. package/client/dist/assets/index-CH-qD91V.js +0 -67487
  42. package/client/dist/assets/index-DXy0twoH.css +0 -2786
  43. package/client/dist/index.html +0 -14
  44. package/server/build/index.js +0 -889
  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,489 +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
- > # ⚠️ Deprecated
6
- >
7
- > **`@modelcontextprotocol/inspector` v1 is deprecated.**
8
- >
9
- > ```
10
- > npx @modelcontextprotocol/inspector@latest
11
- > ```
12
- >
13
- > v1 receives **security fixes only**. All new development happens in v2.
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).
14
8
 
15
- ![MCP Inspector Screenshot](https://raw.githubusercontent.com/modelcontextprotocol/inspector/v1/main/mcp-inspector.png)
9
+ All three run through one global `mcp-inspector` binary:
16
10
 
17
- ## Architecture Overview
18
-
19
- The MCP Inspector consists of two main components that work together:
20
-
21
- - **MCP Inspector Client (MCPI)**: A React-based web UI that provides an interactive interface for testing and debugging MCP servers
22
- - **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)
23
-
24
- 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.
25
-
26
- ## Running the Inspector
27
-
28
- ### Requirements
11
+ ```bash
12
+ npx @modelcontextprotocol/inspector # web UI (default)
13
+ npx @modelcontextprotocol/inspector --cli # CLI
14
+ npx @modelcontextprotocol/inspector --tui # TUI
15
+ ```
29
16
 
30
- - 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.
31
18
 
32
- ### Quick Start (UI mode)
19
+ ## Project layout
33
20
 
34
- 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)).
35
22
 
36
- ```bash
37
- 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
38
43
  ```
39
44
 
40
- The server will start up and the UI will be accessible at `http://localhost:6274`.
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).
41
47
 
42
- ### Docker Container
48
+ Task-oriented guides live under [`docs/`](./docs):
43
49
 
44
- You can also start it in a Docker container with the following command:
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.
45
53
 
46
- ```bash
47
- docker run --rm \
48
- -p 127.0.0.1:6274:6274 \
49
- -p 127.0.0.1:6277:6277 \
50
- -e HOST=0.0.0.0 \
51
- -e MCP_AUTO_OPEN_ENABLED=false \
52
- ghcr.io/modelcontextprotocol/inspector:latest
53
- ```
54
+ ## Setup
54
55
 
55
- ### From an MCP server repository
56
-
57
- 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`.
58
57
 
59
58
  ```bash
60
- npx @modelcontextprotocol/inspector node build/index.js
59
+ npm install # root install; postinstall cascades into every client
61
60
  ```
62
61
 
63
- 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.
64
64
 
65
- ```bash
66
- # Pass arguments only
67
- 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.
68
66
 
69
- # Pass environment variables only
70
- npx @modelcontextprotocol/inspector -e key=value -e key2=$VALUE2 node build/index.js
67
+ ## Running during development
71
68
 
72
- # Pass both environment variables and arguments
73
- 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):
74
70
 
75
- # Use -- to separate inspector flags from server arguments
76
- npx @modelcontextprotocol/inspector -e key=$VALUE -- node build/index.js -e server-flag
71
+ ```bash
72
+ cd clients/web && npm run dev
77
73
  ```
78
74
 
79
- 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`):
80
76
 
81
77
  ```bash
82
- 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)
83
80
  ```
84
81
 
85
- 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).
82
+ ## The `@inspector/core` shared package
86
83
 
87
- ### Servers File Export
84
+ ![Shared code architecture: the four clients over the @inspector/core shared package](specification/diagrams/shared-code-architecture.png)
88
85
 
89
- 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`.
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.
90
87
 
91
- - **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.
88
+ `core/` intentionally has **no `package.json`** — it is not published on its own. Each client bundles it in via a `@inspector/core` alias:
92
89
 
93
- **STDIO transport example:**
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.
94
92
 
95
- ```json
96
- {
97
- "command": "node",
98
- "args": ["build/index.js", "--debug"],
99
- "env": {
100
- "API_KEY": "your-api-key",
101
- "DEBUG": "true"
102
- }
103
- }
104
- ```
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).
105
94
 
106
- **SSE transport example:**
95
+ ## Web client: "dumb components" + Storybook
107
96
 
108
- ```json
109
- {
110
- "type": "sse",
111
- "url": "http://localhost:3000/events",
112
- "note": "For SSE connections, add this URL directly in Client"
113
- }
114
- ```
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.
115
98
 
116
- **Streamable HTTP transport example:**
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).
117
100
 
118
- ```json
119
- {
120
- "type": "streamable-http",
121
- "url": "http://localhost:3000/mcp",
122
- "note": "For Streamable HTTP connections, add this URL directly in your MCP Client"
123
- }
124
- ```
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/`.
125
102
 
126
- - **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`.
103
+ ## Test servers
127
104
 
128
- **STDIO transport example:**
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:
129
106
 
130
- ```json
131
- {
132
- "mcpServers": {
133
- "default-server": {
134
- "command": "node",
135
- "args": ["build/index.js", "--debug"],
136
- "env": {
137
- "API_KEY": "your-api-key",
138
- "DEBUG": "true"
139
- }
140
- }
141
- }
142
- }
143
- ```
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).
144
109
 
145
- **SSE transport example:**
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:
146
111
 
147
- ```json
148
- {
149
- "mcpServers": {
150
- "default-server": {
151
- "type": "sse",
152
- "url": "http://localhost:3000/events",
153
- "note": "For SSE connections, add this URL directly in Client"
154
- }
155
- }
156
- }
157
- ```
112
+ ```bash
113
+ npm run test-servers:build # (from clients/web) → tsc -p test-servers, emits test-servers/build/
114
+ ```
158
115
 
159
- **Streamable HTTP transport example:**
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.
160
117
 
161
- ```json
162
- {
163
- "mcpServers": {
164
- "default-server": {
165
- "type": "streamable-http",
166
- "url": "http://localhost:3000/mcp",
167
- "note": "For Streamable HTTP connections, add this URL directly in your MCP Client"
168
- }
169
- }
170
- }
171
- ```
118
+ ### Serving the modern protocol era
172
119
 
173
- These buttons appear in the Inspector UI after you've configured your server settings, making it easy to save and reuse your configurations.
120
+ A streamable-HTTP server can also serve the **modern (2026-07-28) protocol era** via the SDK's `createMcpHandler`:
174
121
 
175
- 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.
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`.
176
124
 
177
- 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.
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`.
178
126
 
179
- ### Authentication
127
+ ### Showcase configs
180
128
 
181
- 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.
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**.
182
130
 
183
- ### Security Considerations
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) |
184
142
 
185
- 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.
143
+ #### MRTR
186
144
 
187
- #### Authentication
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`.
188
146
 
189
- The MCP Inspector proxy server requires authentication by default. When starting the server, a random session token is generated and printed to the console:
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.
190
148
 
191
- ```
192
- 🔑 Session token: 3a1c267fad21f7150b7d624c160b7f09b0b8c4f623c7107bbf13378f051538d4
149
+ `mrtr-showcase-http.json` bundles every MRTR preset in one server:
193
150
 
194
- 🔗 Open inspector with token pre-filled:
195
- http://localhost:6274/?MCP_PROXY_AUTH_TOKEN=3a1c267fad21f7150b7d624c160b7f09b0b8c4f623c7107bbf13378f051538d4
196
- ```
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 |
197
159
 
198
- 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.
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.
199
161
 
200
- **Automatic browser opening** - The inspector now automatically opens your browser with the token pre-filled in the URL when authentication is enabled.
162
+ #### Network tab — standardized headers and error taxonomy
201
163
 
202
- **Alternative: Manual configuration** - If you already have the inspector open:
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`.
203
165
 
204
- 1. Click the "Configuration" button in the sidebar
205
- 2. Find "Proxy Session Token" and enter the token displayed in the proxy console
206
- 3. Click "Save" to apply the configuration
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:
207
167
 
208
- The token will be saved in your browser's local storage for future use.
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` |
209
174
 
210
- If you need to disable authentication (NOT RECOMMENDED), you can set the `DANGEROUSLY_OMIT_AUTH` environment variable:
175
+ Open the Network tab to see the mirrored `Mcp-*` headers highlighted, sentinel values decoded, and each error rendered distinctly.
211
176
 
212
- ```bash
213
- DANGEROUSLY_OMIT_AUTH=true npm start
214
- ```
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.
215
178
 
216
- ---
179
+ #### `x-mcp-header` in the Tools tab
217
180
 
218
- **🚨 WARNING 🚨**
181
+ `xmcpheader-modern-http.json` serves:
219
182
 
220
- 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.
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.
221
187
 
222
- 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)
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_.
223
189
 
224
- ---
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`).
225
191
 
226
- You can also set the token via the `MCP_PROXY_AUTH_TOKEN` environment variable when starting the server:
192
+ #### Page-by-page fetching
227
193
 
228
- ```bash
229
- MCP_PROXY_AUTH_TOKEN=$(openssl rand -hex 32) npm start
230
- ```
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.
231
195
 
232
- #### Local-only Binding
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.
233
197
 
234
- 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:
198
+ #### Advertised extensions
235
199
 
236
- ```bash
237
- HOST=0.0.0.0 npm start
238
- ```
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`.
239
201
 
240
- **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.
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`.
241
205
 
242
- #### DNS Rebinding Protection
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`.
243
207
 
244
- 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):
208
+ #### Logging, both eras
245
209
 
246
- ```bash
247
- ALLOWED_ORIGINS=http://localhost:6274,http://localhost:8000 npm start
248
- ```
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`.
249
211
 
250
- ### Configuration
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.
251
214
 
252
- The MCP Inspector supports the following configuration settings. To change them, click on the `Configuration` button in the MCP Inspector UI:
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.
253
216
 
254
- | Setting | Description | Default |
255
- | --------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------- | ------- |
256
- | `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 |
257
- | `MCP_REQUEST_TIMEOUT_RESET_ON_PROGRESS` | Reset timeout on progress notifications | true |
258
- | `MCP_REQUEST_MAX_TOTAL_TIMEOUT` | Maximum total timeout for requests sent to the MCP server (ms) (Use with progress notifications) | 60000 |
259
- | `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 | "" |
260
- | `MCP_AUTO_OPEN_ENABLED` | Enable automatic browser opening when inspector starts (works with authentication enabled). Only as environment var, not configurable in browser. | true |
217
+ #### Resource subscriptions, both eras
261
218
 
262
- **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.
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`.
263
220
 
264
- These settings can be adjusted in real-time through the UI and will persist across sessions.
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.
265
223
 
266
- The inspector also supports configuration files to store settings for different MCP servers. This is useful when working with multiple servers or complex configurations:
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.
267
225
 
268
- ```bash
269
- npx @modelcontextprotocol/inspector --config path/to/config.json --server everything
270
- ```
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.
271
227
 
272
- Example server configuration file:
273
-
274
- ```json
275
- {
276
- "mcpServers": {
277
- "everything": {
278
- "command": "npx",
279
- "args": ["@modelcontextprotocol/server-everything"],
280
- "env": {
281
- "hello": "Hello MCP!"
282
- }
283
- },
284
- "my-server": {
285
- "command": "node",
286
- "args": ["build/index.js", "arg1", "arg2"],
287
- "env": {
288
- "key": "value",
289
- "key2": "value2"
290
- }
291
- }
292
- }
293
- }
294
- ```
295
-
296
- #### Transport Types in Config Files
228
+ #### Tasks, both eras
297
229
 
298
- The inspector automatically detects the transport type from your config file. You can specify different transport types:
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`.
299
231
 
300
- **STDIO (default):**
301
-
302
- ```json
303
- {
304
- "mcpServers": {
305
- "my-stdio-server": {
306
- "type": "stdio",
307
- "command": "npx",
308
- "args": ["@modelcontextprotocol/server-everything"]
309
- }
310
- }
311
- }
312
- ```
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`.
313
233
 
314
- **SSE (Server-Sent Events):**
315
-
316
- ```json
317
- {
318
- "mcpServers": {
319
- "my-sse-server": {
320
- "type": "sse",
321
- "url": "http://localhost:3000/sse"
322
- }
323
- }
324
- }
325
- ```
326
-
327
- **Streamable HTTP:**
328
-
329
- ```json
330
- {
331
- "mcpServers": {
332
- "my-http-server": {
333
- "type": "streamable-http",
334
- "url": "http://localhost:3000/mcp"
335
- }
336
- }
337
- }
338
- ```
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.
339
236
 
340
- #### 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`.
341
238
 
342
- 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.
343
240
 
344
- 1. **A single server** - automatically selected:
241
+ ## Building
345
242
 
346
243
  ```bash
347
- # Automatically uses "my-server" if it's the only one
348
- npx @modelcontextprotocol/inspector --config mcp.json
244
+ npm run build # builds all clients: web → cli → tui → launcher
349
245
  ```
350
246
 
351
- 2. **A server named "default-server"** - automatically selected:
352
-
353
- ```json
354
- {
355
- "mcpServers": {
356
- "default-server": {
357
- "command": "npx",
358
- "args": ["@modelcontextprotocol/server-everything"]
359
- },
360
- "other-server": {
361
- "command": "node",
362
- "args": ["other.js"]
363
- }
364
- }
365
- }
366
- ```
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).
367
248
 
368
- > **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
369
250
 
370
- 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).
371
252
 
372
- ```
373
- http://localhost:6274/?transport=sse&serverUrl=http://localhost:8787/sse
374
- http://localhost:6274/?transport=streamable-http&serverUrl=http://localhost:8787/mcp
375
- http://localhost:6274/?transport=stdio&serverCommand=npx&serverArgs=arg1%20arg2
376
- ```
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). |
377
264
 
378
- 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.
379
266
 
380
- ```
381
- 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
382
- ```
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).
383
268
 
384
- Note that if both the query param and the corresponding localStorage item are set, the query param will take precedence.
269
+ ## Publishing
385
270
 
386
- ### 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.
387
272
 
388
- If you're working on the inspector itself:
273
+ ### What ships, and the packaging invariants
389
274
 
390
- 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`:
391
276
 
392
- ```bash
393
- 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)).
394
280
 
395
- # To co-develop with the typescript-sdk package (assuming it's cloned in ../typescript-sdk; set MCP_SDK otherwise):
396
- npm run dev:sdk "cd sdk && npm run examples:simple-server:w"
397
- # then open http://localhost:3000/mcp as SHTTP in the inspector.
398
- # To go back to the deployed SDK version:
399
- # npm run unlink:sdk && npm i
400
- ```
281
+ ### `npm run pack:verify` — publish smoke against the real tarball
401
282
 
402
- > **Note for Windows users:**
403
- > On Windows, use the following command instead:
404
- >
405
- > ```bash
406
- > npm run dev:windows
407
- > ```
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.
408
284
 
409
- Production mode:
285
+ ### Cutting a release
410
286
 
411
- ```bash
412
- npm run build
413
- npm start
414
- ```
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`):
415
288
 
416
- ### 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)).
417
291
 
418
- 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:
419
293
 
420
294
  ```bash
421
- 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`
422
298
  ```
423
299
 
424
- The CLI mode supports most operations across tools, resources, and prompts. A few examples:
425
-
426
- ```bash
427
- # Basic usage
428
- npx @modelcontextprotocol/inspector --cli node build/index.js
429
-
430
- # With config file
431
- npx @modelcontextprotocol/inspector --cli --config path/to/config.json --server myserver
432
-
433
- # List available tools
434
- npx @modelcontextprotocol/inspector --cli node build/index.js --method tools/list
435
-
436
- # Call a specific tool
437
- 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.
438
301
 
439
- # Call a tool with JSON arguments
440
- npx @modelcontextprotocol/inspector --cli node build/index.js --method tools/call --tool-name mytool --tool-arg 'options={"format": "json", "max_tokens": 100}'
302
+ ### Docker
441
303
 
442
- # List available resources
443
- 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.
444
305
 
445
- # List available prompts
446
- npx @modelcontextprotocol/inspector --cli node build/index.js --method prompts/list
447
-
448
- # Connect to a remote MCP server (default is SSE transport)
449
- npx @modelcontextprotocol/inspector --cli https://my-mcp-server.example.com
450
-
451
- # Connect to a remote MCP server (with Streamable HTTP transport)
452
- npx @modelcontextprotocol/inspector --cli https://my-mcp-server.example.com --transport http --method tools/list
453
-
454
- # Connect to a remote MCP server (with custom headers)
455
- npx @modelcontextprotocol/inspector --cli https://my-mcp-server.example.com --transport http --method tools/list --header "X-API-Key: your-api-key"
456
-
457
- # Call a tool on a remote server
458
- 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
459
309
 
460
- # List resources from a remote server
461
- 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
462
313
  ```
463
314
 
464
- ### UI Mode vs CLI Mode: When to Use Each
465
-
466
- | Use Case | UI Mode | CLI Mode |
467
- | ------------------------ | ------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
468
- | **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 |
469
- | **Resource Exploration** | Interactive browser with hierarchical navigation and JSON visualization | Programmatic listing and reading for automation and scripting |
470
- | **Tool Testing** | Form-based parameter input with real-time response visualization | Command-line tool execution with JSON output for scripting |
471
- | **Prompt Engineering** | Interactive sampling with streaming responses and visual comparison | Batch processing of prompts with machine-readable output |
472
- | **Debugging** | Request history, visualized errors, and real-time notifications | Direct JSON output for log analysis and integration with other tools |
473
- | **Automation** | N/A | Ideal for CI/CD pipelines, batch processing, and integration with coding assistants |
474
- | **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).
475
316
 
476
- ## Tool Input Validation Guidelines
317
+ ## Contributing — `AGENTS.md` and `CLAUDE.md`
477
318
 
478
- 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.
479
320
 
480
- - **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
481
- - **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
482
- - **Always include required fields** - Preserve required field values even when empty, allowing the MCP server to validate and return appropriate error messages
483
- - **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.
484
322
 
485
- 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`.
486
324
 
487
325
  ## License
488
326
 
489
- This project is licensed under the MIT License—see the [LICENSE](LICENSE) file for details.
327
+ MIT.