@modelcontextprotocol/inspector 2.0.0 → 2.2.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.
@@ -453,9 +453,13 @@ function loadMcpServersConfig(configPath, writable) {
453
453
  return { ...config, mcpServers: normalizedServers };
454
454
  } catch (error) {
455
455
  if (error instanceof Error) {
456
- throw new Error(`Error loading configuration: ${error.message}`);
456
+ throw new Error(`Error loading configuration: ${error.message}`, {
457
+ cause: error
458
+ });
457
459
  }
458
- throw new Error("Error loading configuration: Unknown error");
460
+ throw new Error("Error loading configuration: Unknown error", {
461
+ cause: error
462
+ });
459
463
  }
460
464
  }
461
465
  function loadServerFromConfig(configPath, serverName, writable) {
@@ -1507,6 +1511,9 @@ function createTransportNode(config, options = {}) {
1507
1511
  }
1508
1512
  }
1509
1513
 
1514
+ // ../../core/json/xMcpHeader.ts
1515
+ var MCP_PARAM_HEADER_PREFIX = "Mcp-Param-";
1516
+
1510
1517
  // ../../core/mcp/import/clientConfig.ts
1511
1518
  function parseJsonObject(raw) {
1512
1519
  let data;
@@ -1514,7 +1521,7 @@ function parseJsonObject(raw) {
1514
1521
  data = JSON.parse(raw);
1515
1522
  } catch (err) {
1516
1523
  const detail = err instanceof Error ? err.message : String(err);
1517
- throw new Error(`Invalid JSON: ${detail}`);
1524
+ throw new Error(`Invalid JSON: ${detail}`, { cause: err });
1518
1525
  }
1519
1526
  if (!data || typeof data !== "object" || Array.isArray(data)) {
1520
1527
  throw new Error("Expected a JSON object at the top level");
@@ -1673,6 +1680,9 @@ var RemoteSession = class _RemoteSession {
1673
1680
  static AUTH_HTTP_ECHO_SUPPRESS_MS = 3e4;
1674
1681
  requestWaits = /* @__PURE__ */ new Map();
1675
1682
  authProviderHandle = null;
1683
+ appliedProtocolVersion;
1684
+ /** A protocol version is a dated token (`2025-11-25`, `2026-07-28`, …). */
1685
+ static PROTOCOL_VERSION_PATTERN = /^[A-Za-z0-9._-]{1,64}$/;
1676
1686
  constructor(sessionId) {
1677
1687
  this.sessionId = sessionId;
1678
1688
  }
@@ -1688,6 +1698,27 @@ var RemoteSession = class _RemoteSession {
1688
1698
  setTransport(transport) {
1689
1699
  this.transport = transport;
1690
1700
  }
1701
+ /**
1702
+ * Apply the client's negotiated protocol version to the upstream transport
1703
+ * (#1935). The browser's SDK Client owns the `initialize` handshake, so the
1704
+ * `setProtocolVersion` call it makes lands on the *remote* transport; without
1705
+ * this the backend's real HTTP transport never learns the version and drops
1706
+ * `Mcp-Protocol-Version` from every subsequent request.
1707
+ *
1708
+ * Ignores anything that isn't a plausible version token so a client can't
1709
+ * push arbitrary bytes into an upstream header, and re-applies only on
1710
+ * change (the value rides every `/api/mcp/send`). The value arrives from an
1711
+ * unvalidated JSON body, so the type is checked rather than assumed —
1712
+ * `RegExp.test` would otherwise coerce a `123` or `true` into a "matching"
1713
+ * string and hand the wrong type to the transport.
1714
+ */
1715
+ applyProtocolVersion(version) {
1716
+ if (typeof version !== "string" || version === this.appliedProtocolVersion || !_RemoteSession.PROTOCOL_VERSION_PATTERN.test(version)) {
1717
+ return;
1718
+ }
1719
+ this.appliedProtocolVersion = version;
1720
+ this.transport.setProtocolVersion?.(version);
1721
+ }
1691
1722
  setEventConsumer(consumer) {
1692
1723
  this.eventConsumer = consumer;
1693
1724
  while (this.eventQueue.length > 0) {
@@ -1946,8 +1977,42 @@ function createRemoteAuthProvider(initialAuthState) {
1946
1977
  }
1947
1978
 
1948
1979
  // ../../core/auth/node/secret-store.ts
1949
- import { AsyncEntry, findCredentialsAsync } from "@napi-rs/keyring";
1950
1980
  var SERVICE_NAME = "mcp-inspector";
1981
+ var keyringLoad;
1982
+ var KeyringModuleShapeError = class extends Error {
1983
+ constructor(detail, options) {
1984
+ super(`@napi-rs/keyring loaded but ${detail}`, options);
1985
+ this.name = "KeyringModuleShapeError";
1986
+ }
1987
+ };
1988
+ var checkKeyringShape = (mod) => {
1989
+ try {
1990
+ if (typeof mod.AsyncEntry === "function" && typeof mod.findCredentialsAsync === "function") {
1991
+ return { ok: true, mod };
1992
+ }
1993
+ return {
1994
+ ok: false,
1995
+ err: new KeyringModuleShapeError(
1996
+ "did not expose AsyncEntry / findCredentialsAsync"
1997
+ )
1998
+ };
1999
+ } catch (err) {
2000
+ return {
2001
+ ok: false,
2002
+ err: new KeyringModuleShapeError(
2003
+ `its exports could not be read: ${err instanceof Error ? err.message : String(err)}`,
2004
+ { cause: err }
2005
+ )
2006
+ };
2007
+ }
2008
+ };
2009
+ var loadKeyring = () => {
2010
+ keyringLoad ??= import("@napi-rs/keyring").then(
2011
+ checkKeyringShape,
2012
+ (err) => ({ ok: false, err })
2013
+ );
2014
+ return keyringLoad;
2015
+ };
1951
2016
  function parseAccount(account) {
1952
2017
  const idx = account.indexOf(":");
1953
2018
  if (idx <= 0 || idx === account.length - 1) return null;
@@ -1959,16 +2024,31 @@ function parseAccount(account) {
1959
2024
  var buildAccount = (serverId, field) => `${serverId}:${field}`;
1960
2025
  var KeychainUnavailableError = class extends Error {
1961
2026
  constructor(cause) {
2027
+ const message = cause instanceof Error ? cause.message : String(cause);
1962
2028
  super(
1963
- `OS keychain is not available. On Linux, install libsecret / gnome-keyring. Underlying error: ${cause instanceof Error ? cause.message : String(cause)}`
2029
+ `OS keychain is not available. ${hintFor(cause, message)} Underlying error: ${message}`
1964
2030
  );
1965
2031
  this.name = "KeychainUnavailableError";
1966
2032
  }
1967
2033
  };
2034
+ var hintFor = (cause, message) => {
2035
+ if (cause instanceof KeyringModuleShapeError) {
2036
+ return `The @napi-rs/keyring package loaded but does not expose the API this build expects \u2014 most likely a version or packaging mismatch; reinstall the Inspector, and report this if it persists.`;
2037
+ }
2038
+ if (message.includes("Cannot find native binding")) {
2039
+ return `The @napi-rs/keyring platform package for this OS is missing or unavailable \u2014 reinstall the Inspector (for npx, clear the npx cache under your npm cache directory first).`;
2040
+ }
2041
+ return `On Linux, install libsecret / gnome-keyring.`;
2042
+ };
1968
2043
  var KeyringSecretStore = class {
1969
2044
  async get(serverId, field) {
1970
- const entry = new AsyncEntry(SERVICE_NAME, buildAccount(serverId, field));
1971
2045
  try {
2046
+ const keyring = await loadKeyring();
2047
+ if (!keyring.ok) return null;
2048
+ const entry = new keyring.mod.AsyncEntry(
2049
+ SERVICE_NAME,
2050
+ buildAccount(serverId, field)
2051
+ );
1972
2052
  const v = await entry.getPassword();
1973
2053
  return v ?? null;
1974
2054
  } catch {
@@ -1976,16 +2056,27 @@ var KeyringSecretStore = class {
1976
2056
  }
1977
2057
  }
1978
2058
  async set(serverId, field, value) {
1979
- const entry = new AsyncEntry(SERVICE_NAME, buildAccount(serverId, field));
1980
2059
  try {
2060
+ const keyring = await loadKeyring();
2061
+ if (!keyring.ok) throw new KeychainUnavailableError(keyring.err);
2062
+ const entry = new keyring.mod.AsyncEntry(
2063
+ SERVICE_NAME,
2064
+ buildAccount(serverId, field)
2065
+ );
1981
2066
  await entry.setPassword(value);
1982
2067
  } catch (err) {
2068
+ if (err instanceof KeychainUnavailableError) throw err;
1983
2069
  throw new KeychainUnavailableError(err);
1984
2070
  }
1985
2071
  }
1986
2072
  async delete(serverId, field) {
1987
- const entry = new AsyncEntry(SERVICE_NAME, buildAccount(serverId, field));
1988
2073
  try {
2074
+ const keyring = await loadKeyring();
2075
+ if (!keyring.ok) return;
2076
+ const entry = new keyring.mod.AsyncEntry(
2077
+ SERVICE_NAME,
2078
+ buildAccount(serverId, field)
2079
+ );
1989
2080
  await entry.deleteCredential();
1990
2081
  } catch {
1991
2082
  }
@@ -1993,7 +2084,9 @@ var KeyringSecretStore = class {
1993
2084
  async deleteAllForServer(serverId) {
1994
2085
  let creds;
1995
2086
  try {
1996
- creds = await findCredentialsAsync(SERVICE_NAME);
2087
+ const keyring = await loadKeyring();
2088
+ if (!keyring.ok) return;
2089
+ creds = await keyring.mod.findCredentialsAsync(SERVICE_NAME);
1997
2090
  } catch {
1998
2091
  return;
1999
2092
  }
@@ -2225,6 +2318,7 @@ async function deleteClientConfigStore(filePath, secretStore) {
2225
2318
 
2226
2319
  // ../../core/mcp/remote/node/server.ts
2227
2320
  import { ZodError as ZodError2 } from "zod";
2321
+ var SSE_PRIMING_COMMENT = ":\n\n";
2228
2322
  function createOriginMiddleware(allowedOrigins) {
2229
2323
  return async (c, next) => {
2230
2324
  if (!allowedOrigins || allowedOrigins.length === 0) {
@@ -2347,6 +2441,16 @@ function requestIdForSendWait(message) {
2347
2441
  }
2348
2442
  return void 0;
2349
2443
  }
2444
+ function mcpParamHeadersOnly(headers) {
2445
+ if (headers === null || typeof headers !== "object") return void 0;
2446
+ const out = {};
2447
+ for (const [key, value] of Object.entries(headers)) {
2448
+ if (typeof value === "string" && key.toLowerCase().startsWith(MCP_PARAM_HEADER_PREFIX.toLowerCase())) {
2449
+ out[key] = value;
2450
+ }
2451
+ }
2452
+ return Object.keys(out).length > 0 ? out : void 0;
2453
+ }
2350
2454
  function createRemoteApp(options) {
2351
2455
  const dangerouslyOmitAuth = !!options.dangerouslyOmitAuth;
2352
2456
  const authToken = dangerouslyOmitAuth ? "" : options.authToken || process.env[API_SERVER_ENV_VARS.AUTH_TOKEN] || randomBytes(32).toString("hex");
@@ -2554,7 +2658,7 @@ function createRemoteApp(options) {
2554
2658
  } catch {
2555
2659
  return c.json({ error: "Invalid JSON body" }, 400);
2556
2660
  }
2557
- const { sessionId, message, relatedRequestId } = body;
2661
+ const { sessionId, message, relatedRequestId, headers, protocolVersion } = body;
2558
2662
  if (!sessionId || !message) {
2559
2663
  return c.json({ error: "Missing sessionId or message" }, 400);
2560
2664
  }
@@ -2566,6 +2670,7 @@ function createRemoteApp(options) {
2566
2670
  const errorMsg = session.getTransportError() || "Transport closed";
2567
2671
  return c.json({ ok: false, kind: "transport_error", error: errorMsg });
2568
2672
  }
2673
+ session.applyProtocolVersion(protocolVersion);
2569
2674
  session.beginSend();
2570
2675
  const requestId = requestIdForSendWait(message);
2571
2676
  let responseWait;
@@ -2575,8 +2680,10 @@ function createRemoteApp(options) {
2575
2680
  });
2576
2681
  }
2577
2682
  try {
2683
+ const paramHeaders = mcpParamHeadersOnly(headers);
2578
2684
  await session.transport.send(message, {
2579
- relatedRequestId
2685
+ relatedRequestId,
2686
+ ...paramHeaders && { headers: paramHeaders }
2580
2687
  });
2581
2688
  if (responseWait) {
2582
2689
  await responseWait;
@@ -2623,6 +2730,7 @@ function createRemoteApp(options) {
2623
2730
  sessions.delete(sessionId);
2624
2731
  return;
2625
2732
  }
2733
+ await stream.write(SSE_PRIMING_COMMENT);
2626
2734
  stream.onAbort(() => {
2627
2735
  const shouldCleanup = session.clearEventConsumer();
2628
2736
  stream.close();
@@ -3593,6 +3701,7 @@ function createRemoteApp(options) {
3593
3701
  serverEventSubscribers.add(send);
3594
3702
  ensureWatcher();
3595
3703
  }
3704
+ await stream.write(SSE_PRIMING_COMMENT);
3596
3705
  stream.onAbort(() => {
3597
3706
  serverEventSubscribers.delete(send);
3598
3707
  void maybeStopWatcher();
@@ -3833,7 +3942,9 @@ var NODE_ONLY_OPTIMIZE_DEPS_EXCLUDE = [
3833
3942
  "which",
3834
3943
  // `@napi-rs/keyring` is loaded only inside
3835
3944
  // `core/auth/node/secret-store.ts` from the Hono `/api/servers`
3836
- // handlers. It's a native-binding package (no browser code path) so
3945
+ // handlers — and lazily even there, via a cached dynamic import, so
3946
+ // an unsupported platform degrades instead of crashing at startup
3947
+ // (#1905). It's a native-binding package (no browser code path) so
3837
3948
  // excluding it keeps Vite's dep scanner from chasing into the
3838
3949
  // platform-specific binaries during dev startup.
3839
3950
  "@napi-rs/keyring"
@@ -3928,9 +4039,20 @@ function vitestSharedPaths(clientDir) {
3928
4039
  find: /^@napi-rs\/keyring$/,
3929
4040
  replacement: path2.resolve(dirname8, "node_modules/@napi-rs/keyring")
3930
4041
  },
4042
+ // `express` and `yaml` resolve from the **repo root**, unlike every pin
4043
+ // above. Both are reached only through `test-servers/src` — express by the
4044
+ // http/oauth servers, yaml by `load-config.ts` — which is root-owned code
4045
+ // with no manifest of its own, so the root is where they are declared and
4046
+ // the only place a client's resolution chain is guaranteed to find them.
4047
+ // Pointing these at `<client>/node_modules` is what broke when the MCP
4048
+ // packages moved to the root (#1970): express was never declared by a client
4049
+ // at all, it arrived in `clients/cli` as a peer of `express-rate-limit`
4050
+ // under `@modelcontextprotocol/server-legacy`, so removing that manifest
4051
+ // entry took express with it and every cli test that spawns a test server
4052
+ // failed to resolve it.
3931
4053
  {
3932
4054
  find: /^express$/,
3933
- replacement: path2.resolve(dirname8, "node_modules/express")
4055
+ replacement: path2.resolve(repoRoot, "node_modules/express")
3934
4056
  },
3935
4057
  {
3936
4058
  find: /^yaml$/,