@modelcontextprotocol/inspector 2.0.0-rc.3 → 2.1.0
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 +18 -14
- package/clients/cli/build/index.js +134 -36
- package/clients/tui/build/index.js +129 -33
- package/clients/web/build/index.js +24 -5
- package/clients/web/dist/assets/index-CyPh2pEE.js +300 -0
- package/clients/web/dist/assets/prism-light-CCFFzO_n.js +6 -0
- package/clients/web/dist/index.html +1 -1
- package/package.json +3 -3
- package/clients/web/dist/assets/index-wIMJi5MN.js +0 -300
- package/clients/web/dist/assets/prism-light-CVa8c8_p.js +0 -6
package/README.md
CHANGED
|
@@ -14,7 +14,7 @@ npx @modelcontextprotocol/inspector --cli # CLI
|
|
|
14
14
|
npx @modelcontextprotocol/inspector --tui # TUI
|
|
15
15
|
```
|
|
16
16
|
|
|
17
|
-
> **Repo status.** This is the **v2** line of the Inspector (branch
|
|
17
|
+
> **Repo status.** This is the **v2** line of the Inspector. Active development happens on **`v2/main`** (the develop branch — all v2 PRs target it), which is merged into **`main`** at milestone releases; `main` is the default branch and holds the latest released v2, published to the npm `latest` tag. The legacy **v1** line lives on **`v1/main`** — security fixes only, published straight from that branch to the npm `v1-latest` tag (`npx @modelcontextprotocol/inspector@v1-latest`). See [`AGENTS.md`](./AGENTS.md) for branch/board conventions.
|
|
18
18
|
|
|
19
19
|
## Project layout
|
|
20
20
|
|
|
@@ -29,6 +29,7 @@ inspector/
|
|
|
29
29
|
│ └── launcher/ # Shared launcher — provides the `mcp-inspector` bin, dispatches to web/cli/tui
|
|
30
30
|
├── core/ # Shared code consumed via the `@inspector/core` alias (no package.json)
|
|
31
31
|
│ ├── auth/ # OAuth: providers, discovery, storage, mid-session recovery (browser/node/remote backends)
|
|
32
|
+
│ ├── client/ # Install-level client config (`client.json`): browser-safe parse/validate + Node load/save, remote backend, secrets
|
|
32
33
|
│ ├── json/ # JSON + parameter/argument conversion utilities
|
|
33
34
|
│ ├── logging/ # Silent pino logger singleton
|
|
34
35
|
│ ├── mcp/ # InspectorClient runtime, state stores, transports, config import
|
|
@@ -37,6 +38,7 @@ inspector/
|
|
|
37
38
|
│ └── storage/ # File I/O helpers for the OAuth persist backends
|
|
38
39
|
├── test-servers/ # Composable MCP test servers + fixtures used by integration tests
|
|
39
40
|
├── scripts/ # Root build/verify tooling (install cascade, smokes, verify-build-gate, verify-format-coverage, pack:verify)
|
|
41
|
+
├── docs/ # Task-oriented guides (server configuration, MCP App review, launcher/config plan)
|
|
40
42
|
├── specification/ # Design/build specifications
|
|
41
43
|
├── AGENTS.md # Contribution rules for agents AND humans (see below)
|
|
42
44
|
└── README.md # You are here
|
|
@@ -174,7 +176,7 @@ It also serves four `trigger_*` tools that the modern leg's spec-error injector
|
|
|
174
176
|
|
|
175
177
|
Open the Network tab to see the mirrored `Mcp-*` headers highlighted, sentinel values decoded, and each error rendered distinctly.
|
|
176
178
|
|
|
177
|
-
> **`Mcp-Param-*` mirroring is
|
|
179
|
+
> **`Mcp-Param-*` mirroring is built by the Inspector, not the SDK.** The SDK only mirrors inside `client.callTool()`, and skips it in the browser (`detectProbeEnvironment() !== "browser"`). The Inspector routes `tools/call` through `client.request()` to drive MRTR manually, so it builds the mirrored headers itself ([#1846](https://github.com/modelcontextprotocol/inspector/issues/1846)) — on **every** client, web included, since the web client's upstream request is issued by the Node backend rather than the browser. So `get_weather` is callable from web, CLI, and TUI alike, in both the plain and "Run as task" forms.
|
|
178
180
|
|
|
179
181
|
#### `x-mcp-header` in the Tools tab
|
|
180
182
|
|
|
@@ -250,17 +252,17 @@ Individual clients: `build:web`, `build:cli`, `build:tui`, `build:launcher`. The
|
|
|
250
252
|
|
|
251
253
|
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).
|
|
252
254
|
|
|
253
|
-
| Script
|
|
254
|
-
|
|
|
255
|
-
| `npm run validate`
|
|
256
|
-
| `npm run coverage`
|
|
257
|
-
| `npm run smoke`
|
|
258
|
-
| `npm run verify:build-gate`
|
|
259
|
-
| `npm run verify:format-coverage`
|
|
260
|
-
| `npm run test:scripts`
|
|
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`
|
|
263
|
-
| `npm run pack:verify`
|
|
255
|
+
| Script | What it does |
|
|
256
|
+
| --------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
257
|
+
| `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. |
|
|
258
|
+
| `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`). |
|
|
259
|
+
| `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). |
|
|
260
|
+
| `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`. |
|
|
261
|
+
| `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`. |
|
|
262
|
+
| `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. |
|
|
263
|
+
| `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`. |
|
|
264
|
+
| `npm run ci` | **Mandatory pre-push command.** `validate` → `coverage` → `verify:build-gate` → `smoke` → Storybook. A true superset of GitHub CI. |
|
|
265
|
+
| `npm run pack:verify` | Publish smoke — see [Publishing](#publishing). |
|
|
264
266
|
|
|
265
267
|
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.
|
|
266
268
|
|
|
@@ -286,9 +288,11 @@ The `smoke:*` scripts run against the in-repo build tree, which is **not** the p
|
|
|
286
288
|
|
|
287
289
|
Publishing is automated by two release-gated jobs in [`.github/workflows/main.yml`](.github/workflows/main.yml) (`github.event_name == 'release'`, both `needs: build`):
|
|
288
290
|
|
|
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
|
|
291
|
+
- **`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`, `NPM_TOKEN`).
|
|
290
292
|
- **`publish-github-container-registry`** — the container image (see [Docker](#docker)).
|
|
291
293
|
|
|
294
|
+
A v2 release is cut from **`main`**, after the milestone's work has been merged there from `v2/main` — not from `v2/main` itself. (The v1 line releases independently from `v1/main` to the `v1-latest` tag and never touches `main`; see [Repo status](#mcp-inspector).)
|
|
295
|
+
|
|
292
296
|
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:
|
|
293
297
|
|
|
294
298
|
```bash
|
|
@@ -536,9 +536,13 @@ function loadMcpServersConfig(configPath, writable) {
|
|
|
536
536
|
return { ...config, mcpServers: normalizedServers };
|
|
537
537
|
} catch (error) {
|
|
538
538
|
if (error instanceof Error) {
|
|
539
|
-
throw new Error(`Error loading configuration: ${error.message}
|
|
539
|
+
throw new Error(`Error loading configuration: ${error.message}`, {
|
|
540
|
+
cause: error
|
|
541
|
+
});
|
|
540
542
|
}
|
|
541
|
-
throw new Error("Error loading configuration: Unknown error"
|
|
543
|
+
throw new Error("Error loading configuration: Unknown error", {
|
|
544
|
+
cause: error
|
|
545
|
+
});
|
|
542
546
|
}
|
|
543
547
|
}
|
|
544
548
|
function loadServerFromConfig(configPath, serverName, writable) {
|
|
@@ -1067,7 +1071,9 @@ function parseHttpUrl(value, label) {
|
|
|
1067
1071
|
return new URL(trimmed);
|
|
1068
1072
|
} catch (err) {
|
|
1069
1073
|
const detail = err instanceof Error ? err.message : String(err);
|
|
1070
|
-
throw new Error(`Invalid ${label}: "${trimmed}" (${detail})
|
|
1074
|
+
throw new Error(`Invalid ${label}: "${trimmed}" (${detail})`, {
|
|
1075
|
+
cause: err
|
|
1076
|
+
});
|
|
1071
1077
|
}
|
|
1072
1078
|
}
|
|
1073
1079
|
var parseOAuthCallbackParams = (location) => {
|
|
@@ -2100,6 +2106,7 @@ import { Client } from "@modelcontextprotocol/client";
|
|
|
2100
2106
|
|
|
2101
2107
|
// ../../core/json/xMcpHeader.ts
|
|
2102
2108
|
var X_MCP_HEADER_KEY = "x-mcp-header";
|
|
2109
|
+
var MCP_PARAM_HEADER_PREFIX = "Mcp-Param-";
|
|
2103
2110
|
var RFC9110_TOKEN = /^[!#$%&'*+\-.^_`|~0-9A-Za-z]+$/;
|
|
2104
2111
|
var PERMITTED_X_MCP_HEADER_TYPES = /* @__PURE__ */ new Set([
|
|
2105
2112
|
"string",
|
|
@@ -2189,6 +2196,66 @@ function scanXMcpHeaderDeclarations(inputSchema) {
|
|
|
2189
2196
|
const fault = visit(inputSchema, [], true);
|
|
2190
2197
|
return fault === void 0 ? { valid: true, declarations } : { valid: false, reason: fault };
|
|
2191
2198
|
}
|
|
2199
|
+
var BASE64_SENTINEL_PREFIX = "=?base64?";
|
|
2200
|
+
var BASE64_SENTINEL_SUFFIX = "?=";
|
|
2201
|
+
function mcpParamPrimitiveToString(value) {
|
|
2202
|
+
if (typeof value === "string") return value;
|
|
2203
|
+
if (typeof value === "boolean") return value ? "true" : "false";
|
|
2204
|
+
if (typeof value === "number") {
|
|
2205
|
+
if (!Number.isFinite(value)) return void 0;
|
|
2206
|
+
if (Number.isInteger(value) && !Number.isSafeInteger(value)) {
|
|
2207
|
+
return void 0;
|
|
2208
|
+
}
|
|
2209
|
+
return String(value);
|
|
2210
|
+
}
|
|
2211
|
+
return void 0;
|
|
2212
|
+
}
|
|
2213
|
+
function needsBase64(s2) {
|
|
2214
|
+
if (s2.length === 0) return true;
|
|
2215
|
+
if (s2.startsWith(BASE64_SENTINEL_PREFIX) && s2.endsWith(BASE64_SENTINEL_SUFFIX)) {
|
|
2216
|
+
return true;
|
|
2217
|
+
}
|
|
2218
|
+
if (s2 !== s2.trim()) return true;
|
|
2219
|
+
for (let i2 = 0; i2 < s2.length; i2++) {
|
|
2220
|
+
const c = s2.codePointAt(i2);
|
|
2221
|
+
if (c === 9 || c >= 32 && c <= 126) continue;
|
|
2222
|
+
return true;
|
|
2223
|
+
}
|
|
2224
|
+
return false;
|
|
2225
|
+
}
|
|
2226
|
+
function utf8ToBase64(s2) {
|
|
2227
|
+
const bytes = new TextEncoder().encode(s2);
|
|
2228
|
+
let bin = "";
|
|
2229
|
+
for (const b2 of bytes) bin += String.fromCodePoint(b2);
|
|
2230
|
+
return btoa(bin);
|
|
2231
|
+
}
|
|
2232
|
+
function encodeMcpParamValue(value) {
|
|
2233
|
+
return needsBase64(value) ? `${BASE64_SENTINEL_PREFIX}${utf8ToBase64(value)}${BASE64_SENTINEL_SUFFIX}` : value;
|
|
2234
|
+
}
|
|
2235
|
+
function valueAtPath(root, path2) {
|
|
2236
|
+
let node = root;
|
|
2237
|
+
for (const key of path2) {
|
|
2238
|
+
if (node === null || typeof node !== "object") return void 0;
|
|
2239
|
+
node = node[key];
|
|
2240
|
+
}
|
|
2241
|
+
return node;
|
|
2242
|
+
}
|
|
2243
|
+
function buildMcpParamHeaders(declarations, args) {
|
|
2244
|
+
const out = {};
|
|
2245
|
+
for (const decl of declarations) {
|
|
2246
|
+
const raw = valueAtPath(args, decl.path);
|
|
2247
|
+
if (raw === void 0 || raw === null) continue;
|
|
2248
|
+
const stringValue = mcpParamPrimitiveToString(raw);
|
|
2249
|
+
if (stringValue === void 0) continue;
|
|
2250
|
+
out[`${MCP_PARAM_HEADER_PREFIX}${decl.headerName}`] = encodeMcpParamValue(stringValue);
|
|
2251
|
+
}
|
|
2252
|
+
return out;
|
|
2253
|
+
}
|
|
2254
|
+
function mcpParamHeadersForTool(tool, args) {
|
|
2255
|
+
const scan = scanXMcpHeaderDeclarations(tool.inputSchema);
|
|
2256
|
+
if (!scan.valid || scan.declarations.length === 0) return {};
|
|
2257
|
+
return buildMcpParamHeaders(scan.declarations, args);
|
|
2258
|
+
}
|
|
2192
2259
|
|
|
2193
2260
|
// ../../core/mcp/messageTrackingTransport.ts
|
|
2194
2261
|
var MessageTrackingTransport = class {
|
|
@@ -3285,7 +3352,9 @@ function getAuthorizationServerUrl(serverUrl, resourceMetadata) {
|
|
|
3285
3352
|
return new URL("/", serverUrl);
|
|
3286
3353
|
} catch (err) {
|
|
3287
3354
|
const detail = err instanceof Error ? err.message : String(err);
|
|
3288
|
-
throw new Error(`Invalid MCP server URL: "${serverUrl}" (${detail})
|
|
3355
|
+
throw new Error(`Invalid MCP server URL: "${serverUrl}" (${detail})`, {
|
|
3356
|
+
cause: err
|
|
3357
|
+
});
|
|
3289
3358
|
}
|
|
3290
3359
|
}
|
|
3291
3360
|
|
|
@@ -4948,7 +5017,10 @@ var RemoteClientTransport = class {
|
|
|
4948
5017
|
message,
|
|
4949
5018
|
...options?.relatedRequestId != null && {
|
|
4950
5019
|
relatedRequestId: options.relatedRequestId
|
|
4951
|
-
}
|
|
5020
|
+
},
|
|
5021
|
+
// Forward per-send `Mcp-Param-*` headers (SEP-2243) for the backend to
|
|
5022
|
+
// apply to the upstream send; the browser can't set them cross-origin.
|
|
5023
|
+
...options?.headers != null && { headers: options.headers }
|
|
4952
5024
|
};
|
|
4953
5025
|
const res = await this.fetchFn(`${this.baseUrl}/api/mcp/send`, {
|
|
4954
5026
|
method: "POST",
|
|
@@ -7198,6 +7270,43 @@ var InspectorClient = class _InspectorClient extends InspectorClientEventTarget
|
|
|
7198
7270
|
}
|
|
7199
7271
|
}
|
|
7200
7272
|
}
|
|
7273
|
+
/**
|
|
7274
|
+
* Coerce the string-valued entries of a tool's arguments to the types its
|
|
7275
|
+
* `inputSchema` declares (the Tools form hands everything over as text).
|
|
7276
|
+
* Shared by the two `tools/call` entry points — {@link attemptToolCall} and
|
|
7277
|
+
* {@link callToolStream} — so both put the SAME arguments on the wire.
|
|
7278
|
+
*/
|
|
7279
|
+
convertStringToolArgs(tool, args) {
|
|
7280
|
+
const stringArgs = {};
|
|
7281
|
+
for (const [key, value] of Object.entries(args)) {
|
|
7282
|
+
if (typeof value === "string") {
|
|
7283
|
+
stringArgs[key] = value;
|
|
7284
|
+
}
|
|
7285
|
+
}
|
|
7286
|
+
if (Object.keys(stringArgs).length === 0) return args;
|
|
7287
|
+
return { ...args, ...convertToolParameters(tool, stringArgs) };
|
|
7288
|
+
}
|
|
7289
|
+
/**
|
|
7290
|
+
* SEP-2243: mirror `x-mcp-header`-annotated arguments into `Mcp-Param-*`
|
|
7291
|
+
* headers on a modern connection. The SDK only does this inside
|
|
7292
|
+
* `client.callTool()` (and skips it in the browser), but we route
|
|
7293
|
+
* `tools/call` through `client.request()` for manual MRTR driving (#1704), so
|
|
7294
|
+
* we mirror ourselves. `Protocol.request` forwards `headers` (preserved
|
|
7295
|
+
* across MRTR retry legs) to the transport, and the remote transport relays
|
|
7296
|
+
* them to the backend's upstream send — issued server-side, where the browser
|
|
7297
|
+
* skip doesn't apply. No-op on legacy/stdio (no annotations).
|
|
7298
|
+
*
|
|
7299
|
+
* Applied by BOTH `tools/call` entry points: a plain call
|
|
7300
|
+
* ({@link attemptToolCall}) and a task-augmented one
|
|
7301
|
+
* ({@link callToolStream}) — a strict modern server rejects either with
|
|
7302
|
+
* `-32020` when the mirrored header is missing.
|
|
7303
|
+
*/
|
|
7304
|
+
applyMirroredParamHeaders(tool, convertedArgs, requestOptions) {
|
|
7305
|
+
if (this.protocolEra !== "modern") return;
|
|
7306
|
+
const paramHeaders = mcpParamHeadersForTool(tool, convertedArgs);
|
|
7307
|
+
if (Object.keys(paramHeaders).length === 0) return;
|
|
7308
|
+
requestOptions.headers = { ...requestOptions.headers, ...paramHeaders };
|
|
7309
|
+
}
|
|
7201
7310
|
/**
|
|
7202
7311
|
* Run a single tools/call attempt: convert args, issue the request, validate,
|
|
7203
7312
|
* and return a successful {@link ToolCallInvocation}. Throws on any error
|
|
@@ -7217,17 +7326,7 @@ var InspectorClient = class _InspectorClient extends InspectorClientEventTarget
|
|
|
7217
7326
|
if (!client) {
|
|
7218
7327
|
throw new Error("Client is not connected");
|
|
7219
7328
|
}
|
|
7220
|
-
|
|
7221
|
-
const stringArgs = {};
|
|
7222
|
-
for (const [key, value] of Object.entries(args)) {
|
|
7223
|
-
if (typeof value === "string") {
|
|
7224
|
-
stringArgs[key] = value;
|
|
7225
|
-
}
|
|
7226
|
-
}
|
|
7227
|
-
if (Object.keys(stringArgs).length > 0) {
|
|
7228
|
-
const convertedStringArgs = convertToolParameters(tool, stringArgs);
|
|
7229
|
-
convertedArgs = { ...args, ...convertedStringArgs };
|
|
7230
|
-
}
|
|
7329
|
+
const convertedArgs = this.convertStringToolArgs(tool, args);
|
|
7231
7330
|
const callMetadata = generalMetadata || toolSpecificMetadata ? { ...generalMetadata || {}, ...toolSpecificMetadata || {} } : void 0;
|
|
7232
7331
|
const timestamp = /* @__PURE__ */ new Date();
|
|
7233
7332
|
const metadata = this.mergeMeta(callMetadata);
|
|
@@ -7243,6 +7342,7 @@ var InspectorClient = class _InspectorClient extends InspectorClientEventTarget
|
|
|
7243
7342
|
metadata?.progressToken,
|
|
7244
7343
|
signal
|
|
7245
7344
|
);
|
|
7345
|
+
this.applyMirroredParamHeaders(tool, convertedArgs, requestOptions);
|
|
7246
7346
|
const rawResult = await this.invokeMcpClient(
|
|
7247
7347
|
() => this.requestWithInputRequired(
|
|
7248
7348
|
"tools/call",
|
|
@@ -7646,17 +7746,7 @@ var InspectorClient = class _InspectorClient extends InspectorClientEventTarget
|
|
|
7646
7746
|
throw new Error("Client is not connected");
|
|
7647
7747
|
}
|
|
7648
7748
|
try {
|
|
7649
|
-
|
|
7650
|
-
const stringArgs = {};
|
|
7651
|
-
for (const [key, value] of Object.entries(args)) {
|
|
7652
|
-
if (typeof value === "string") {
|
|
7653
|
-
stringArgs[key] = value;
|
|
7654
|
-
}
|
|
7655
|
-
}
|
|
7656
|
-
if (Object.keys(stringArgs).length > 0) {
|
|
7657
|
-
const convertedStringArgs = convertToolParameters(tool, stringArgs);
|
|
7658
|
-
convertedArgs = { ...args, ...convertedStringArgs };
|
|
7659
|
-
}
|
|
7749
|
+
const convertedArgs = this.convertStringToolArgs(tool, args);
|
|
7660
7750
|
const callMetadata = generalMetadata || toolSpecificMetadata ? { ...generalMetadata || {}, ...toolSpecificMetadata || {} } : void 0;
|
|
7661
7751
|
const timestamp = /* @__PURE__ */ new Date();
|
|
7662
7752
|
const metadata = this.mergeMeta(callMetadata);
|
|
@@ -7674,6 +7764,7 @@ var InspectorClient = class _InspectorClient extends InspectorClientEventTarget
|
|
|
7674
7764
|
let taskId;
|
|
7675
7765
|
let error;
|
|
7676
7766
|
const requestOptions = this.getRequestOptions(metadata?.progressToken);
|
|
7767
|
+
this.applyMirroredParamHeaders(tool, convertedArgs, requestOptions);
|
|
7677
7768
|
if (this.progress) {
|
|
7678
7769
|
const innerOnProgress = requestOptions.onprogress;
|
|
7679
7770
|
requestOptions.onprogress = (progress) => {
|
|
@@ -7779,7 +7870,8 @@ var InspectorClient = class _InspectorClient extends InspectorClientEventTarget
|
|
|
7779
7870
|
);
|
|
7780
7871
|
} catch (resultError) {
|
|
7781
7872
|
throw new Error(
|
|
7782
|
-
`Tool call did not return a result: ${resultError instanceof Error ? resultError.message : String(resultError)}
|
|
7873
|
+
`Tool call did not return a result: ${resultError instanceof Error ? resultError.message : String(resultError)}`,
|
|
7874
|
+
{ cause: resultError }
|
|
7783
7875
|
);
|
|
7784
7876
|
}
|
|
7785
7877
|
}
|
|
@@ -7922,7 +8014,8 @@ var InspectorClient = class _InspectorClient extends InspectorClientEventTarget
|
|
|
7922
8014
|
expandedUri = uriTemplate2.expand(params);
|
|
7923
8015
|
} catch (error) {
|
|
7924
8016
|
throw new Error(
|
|
7925
|
-
`Failed to expand URI template "${uriTemplate}": ${error instanceof Error ? error.message : String(error)}
|
|
8017
|
+
`Failed to expand URI template "${uriTemplate}": ${error instanceof Error ? error.message : String(error)}`,
|
|
8018
|
+
{ cause: error }
|
|
7926
8019
|
);
|
|
7927
8020
|
}
|
|
7928
8021
|
const readInvocation = await this.readResource(expandedUri, metadata);
|
|
@@ -8120,7 +8213,8 @@ var InspectorClient = class _InspectorClient extends InspectorClientEventTarget
|
|
|
8120
8213
|
return { values: [] };
|
|
8121
8214
|
}
|
|
8122
8215
|
throw new Error(
|
|
8123
|
-
`Failed to get completions: ${error instanceof Error ? error.message : String(error)}
|
|
8216
|
+
`Failed to get completions: ${error instanceof Error ? error.message : String(error)}`,
|
|
8217
|
+
{ cause: error }
|
|
8124
8218
|
);
|
|
8125
8219
|
}
|
|
8126
8220
|
}
|
|
@@ -8522,7 +8616,8 @@ var InspectorClient = class _InspectorClient extends InspectorClientEventTarget
|
|
|
8522
8616
|
this.dispatchSubscriptionsChange();
|
|
8523
8617
|
} catch (error) {
|
|
8524
8618
|
throw new Error(
|
|
8525
|
-
`Failed to subscribe to resource: ${error instanceof Error ? error.message : String(error)}
|
|
8619
|
+
`Failed to subscribe to resource: ${error instanceof Error ? error.message : String(error)}`,
|
|
8620
|
+
{ cause: error }
|
|
8526
8621
|
);
|
|
8527
8622
|
}
|
|
8528
8623
|
}
|
|
@@ -8566,7 +8661,8 @@ var InspectorClient = class _InspectorClient extends InspectorClientEventTarget
|
|
|
8566
8661
|
}
|
|
8567
8662
|
} catch (error) {
|
|
8568
8663
|
throw new Error(
|
|
8569
|
-
`Failed to unsubscribe from resource: ${error instanceof Error ? error.message : String(error)}
|
|
8664
|
+
`Failed to unsubscribe from resource: ${error instanceof Error ? error.message : String(error)}`,
|
|
8665
|
+
{ cause: error }
|
|
8570
8666
|
);
|
|
8571
8667
|
}
|
|
8572
8668
|
}
|
|
@@ -11567,7 +11663,8 @@ async function runMethod(inspectorClient, args) {
|
|
|
11567
11663
|
roots = parsed;
|
|
11568
11664
|
} catch (e2) {
|
|
11569
11665
|
throw new Error(
|
|
11570
|
-
`--roots-json is invalid: ${e2 instanceof Error ? e2.message : String(e2)}
|
|
11666
|
+
`--roots-json is invalid: ${e2 instanceof Error ? e2.message : String(e2)}`,
|
|
11667
|
+
{ cause: e2 }
|
|
11571
11668
|
);
|
|
11572
11669
|
}
|
|
11573
11670
|
await inspectorClient.setRoots(roots);
|
|
@@ -11846,7 +11943,7 @@ function parseRunnerOAuthCallbackUrl(cliCallbackUrl) {
|
|
|
11846
11943
|
url = new URL(raw);
|
|
11847
11944
|
} catch (err) {
|
|
11848
11945
|
const reason = err?.message ?? String(err);
|
|
11849
|
-
throw new Error(`Invalid OAuth callback URL: ${reason}
|
|
11946
|
+
throw new Error(`Invalid OAuth callback URL: ${reason}`, { cause: err });
|
|
11850
11947
|
}
|
|
11851
11948
|
if (url.protocol !== "http:") {
|
|
11852
11949
|
throw new Error("OAuth callback URL must use http scheme");
|
|
@@ -12810,7 +12907,7 @@ async function parseArgs(argv) {
|
|
|
12810
12907
|
const scriptArgs = rawArgs.slice(2);
|
|
12811
12908
|
const dashDashIndex = scriptArgs.indexOf("--");
|
|
12812
12909
|
let targetArgs = [];
|
|
12813
|
-
let optionArgs
|
|
12910
|
+
let optionArgs;
|
|
12814
12911
|
if (dashDashIndex >= 0) {
|
|
12815
12912
|
targetArgs = scriptArgs.slice(0, dashDashIndex);
|
|
12816
12913
|
optionArgs = scriptArgs.slice(dashDashIndex + 1);
|
|
@@ -13120,7 +13217,8 @@ async function parseArgs(argv) {
|
|
|
13120
13217
|
parsed = JSON.parse(options.toolArgsJson);
|
|
13121
13218
|
} catch (e2) {
|
|
13122
13219
|
throw new Error(
|
|
13123
|
-
`--tool-args-json is not valid JSON: ${e2.message}
|
|
13220
|
+
`--tool-args-json is not valid JSON: ${e2 instanceof Error ? e2.message : String(e2)}`,
|
|
13221
|
+
{ cause: e2 }
|
|
13124
13222
|
);
|
|
13125
13223
|
}
|
|
13126
13224
|
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
|