@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.
- package/README.md +199 -361
- package/clients/cli/build/index.js +13218 -0
- package/clients/launcher/build/index.js +90 -0
- package/clients/launcher/build/parse-launcher-argv.js +29 -0
- package/clients/tui/build/chunk-MCKGQKYU.js +15 -0
- package/clients/tui/build/index.js +14573 -0
- package/clients/tui/build/undici-HXPKCIY3.js +25789 -0
- package/clients/web/build/chunk-MCKGQKYU.js +15 -0
- package/clients/web/build/index.js +4322 -0
- package/clients/web/build/undici-HXPKCIY3.js +25789 -0
- package/clients/web/dist/assets/css-DfBGKsUM.js +1 -0
- package/clients/web/dist/assets/index-Cbtmmw0V.js +300 -0
- package/clients/web/dist/assets/index-CiUpWldH.css +1 -0
- package/clients/web/dist/assets/json-rfaH9Mx5.js +1 -0
- package/clients/web/dist/assets/markdown-PVXc4EJ0.js +1 -0
- package/clients/web/dist/assets/markup-Bk4keGDK.js +1 -0
- package/clients/web/dist/assets/markup-DPsXihXF.js +1 -0
- package/clients/web/dist/assets/prism-light-B8tIwqDr.js +6 -0
- package/clients/web/dist/assets/tomorrow-COrD2Qnh.js +1 -0
- package/clients/web/dist/assets/yaml-B78V9E0m.js +1 -0
- package/clients/web/dist/icons.svg +24 -0
- package/clients/web/dist/index.html +17 -0
- package/package.json +96 -80
- package/scripts/install-clients.mjs +65 -0
- package/LICENSE +0 -216
- package/cli/build/cli.js +0 -290
- package/cli/build/client/connection.js +0 -38
- package/cli/build/client/index.js +0 -6
- package/cli/build/client/prompts.js +0 -43
- package/cli/build/client/resources.js +0 -36
- package/cli/build/client/tools.js +0 -87
- package/cli/build/client/types.js +0 -1
- package/cli/build/error-handler.js +0 -18
- package/cli/build/index.js +0 -257
- package/cli/build/transport.js +0 -65
- package/cli/build/utils/awaitable-log.js +0 -7
- package/client/bin/client.js +0 -62
- package/client/bin/start.js +0 -355
- package/client/dist/assets/OAuthCallback-DKaW5Tk8.js +0 -55
- package/client/dist/assets/OAuthDebugCallback-Bx3X7Zkh.js +0 -64
- package/client/dist/assets/index-CH-qD91V.js +0 -67487
- package/client/dist/assets/index-DXy0twoH.css +0 -2786
- package/client/dist/index.html +0 -14
- package/server/build/index.js +0 -889
- package/server/build/mcpProxy.js +0 -121
- package/server/build/static/sandbox_proxy.html +0 -197
- package/server/static/sandbox_proxy.html +0 -197
- /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
|
-
|
|
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
|
-
|
|
6
|
-
|
|
7
|
-
|
|
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
|
-
|
|
9
|
+
All three run through one global `mcp-inspector` binary:
|
|
16
10
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
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
|
-
|
|
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
|
-
|
|
19
|
+
## Project layout
|
|
33
20
|
|
|
34
|
-
|
|
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
|
-
```
|
|
37
|
-
|
|
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
|
-
|
|
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
|
-
|
|
48
|
+
Task-oriented guides live under [`docs/`](./docs):
|
|
43
49
|
|
|
44
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
59
|
+
npm install # root install; postinstall cascades into every client
|
|
61
60
|
```
|
|
62
61
|
|
|
63
|
-
|
|
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
|
-
|
|
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
|
-
|
|
70
|
-
npx @modelcontextprotocol/inspector -e key=value -e key2=$VALUE2 node build/index.js
|
|
67
|
+
## Running during development
|
|
71
68
|
|
|
72
|
-
|
|
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
|
-
|
|
76
|
-
|
|
71
|
+
```bash
|
|
72
|
+
cd clients/web && npm run dev
|
|
77
73
|
```
|
|
78
74
|
|
|
79
|
-
The
|
|
75
|
+
The launcher-driven scripts below run the **built** launcher, so build first (`npm run build`):
|
|
80
76
|
|
|
81
77
|
```bash
|
|
82
|
-
|
|
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
|
-
|
|
82
|
+
## The `@inspector/core` shared package
|
|
86
83
|
|
|
87
|
-
|
|
84
|
+

|
|
88
85
|
|
|
89
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
95
|
+
## Web client: "dumb components" + Storybook
|
|
107
96
|
|
|
108
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
103
|
+
## Test servers
|
|
127
104
|
|
|
128
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
148
|
-
|
|
149
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
120
|
+
A streamable-HTTP server can also serve the **modern (2026-07-28) protocol era** via the SDK's `createMcpHandler`:
|
|
174
121
|
|
|
175
|
-
|
|
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
|
-
|
|
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
|
-
###
|
|
127
|
+
### Showcase configs
|
|
180
128
|
|
|
181
|
-
|
|
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
|
-
|
|
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
|
-
|
|
143
|
+
#### MRTR
|
|
186
144
|
|
|
187
|
-
|
|
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
|
|
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
|
-
|
|
195
|
-
|
|
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
|
-
|
|
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
|
-
|
|
162
|
+
#### Network tab — standardized headers and error taxonomy
|
|
201
163
|
|
|
202
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
175
|
+
Open the Network tab to see the mirrored `Mcp-*` headers highlighted, sentinel values decoded, and each error rendered distinctly.
|
|
211
176
|
|
|
212
|
-
|
|
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
|
-
|
|
181
|
+
`xmcpheader-modern-http.json` serves:
|
|
219
182
|
|
|
220
|
-
|
|
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
|
-
|
|
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
|
-
|
|
192
|
+
#### Page-by-page fetching
|
|
227
193
|
|
|
228
|
-
|
|
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
|
-
|
|
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
|
-
|
|
198
|
+
#### Advertised extensions
|
|
235
199
|
|
|
236
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
208
|
+
#### Logging, both eras
|
|
245
209
|
|
|
246
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
**
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
241
|
+
## Building
|
|
345
242
|
|
|
346
243
|
```bash
|
|
347
|
-
#
|
|
348
|
-
npx @modelcontextprotocol/inspector --config mcp.json
|
|
244
|
+
npm run build # builds all clients: web → cli → tui → launcher
|
|
349
245
|
```
|
|
350
246
|
|
|
351
|
-
|
|
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
|
-
|
|
249
|
+
## Testing & the quality gate
|
|
369
250
|
|
|
370
|
-
|
|
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
|
-
|
|
374
|
-
|
|
375
|
-
|
|
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
|
-
|
|
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
|
-
|
|
269
|
+
## Publishing
|
|
385
270
|
|
|
386
|
-
|
|
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
|
-
|
|
273
|
+
### What ships, and the packaging invariants
|
|
389
274
|
|
|
390
|
-
|
|
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
|
-
|
|
393
|
-
npm
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
285
|
+
### Cutting a release
|
|
410
286
|
|
|
411
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
446
|
-
|
|
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
|
-
#
|
|
461
|
-
|
|
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
|
-
|
|
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
|
-
##
|
|
317
|
+
## Contributing — `AGENTS.md` and `CLAUDE.md`
|
|
477
318
|
|
|
478
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
327
|
+
MIT.
|