@modelcontextprotocol/inspector 2.4.0 → 2.6.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.
@@ -348,6 +348,9 @@ function storedFieldsToInspectorSettings(stored) {
348
348
  if (stored.oauth?.requestRefreshToken === false) {
349
349
  settings.oauthRequestRefreshToken = false;
350
350
  }
351
+ if (stored.oauth?.revokeOnClear === false) {
352
+ settings.oauthRevokeOnClear = false;
353
+ }
351
354
  if (stored.cwd) settings.cwd = stored.cwd;
352
355
  return settings;
353
356
  }
@@ -417,6 +420,9 @@ function inspectorSettingsToStoredFields(settings) {
417
420
  if (settings.oauthRequestRefreshToken === false) {
418
421
  oauthFields.requestRefreshToken = false;
419
422
  }
423
+ if (settings.oauthRevokeOnClear === false) {
424
+ oauthFields.revokeOnClear = false;
425
+ }
420
426
  if (Object.keys(oauthFields).length > 0) {
421
427
  out.oauth = oauthFields;
422
428
  }
@@ -511,6 +517,7 @@ function expectedSecretFields(stored) {
511
517
  }
512
518
  return fields;
513
519
  }
520
+ var EXAMPLE_SERVER_URL = "https://example-server.modelcontextprotocol.io/mcp";
514
521
  var DEFAULT_SEED_CONFIG = {
515
522
  mcpServers: {
516
523
  "filesystem-server-default": {
@@ -522,6 +529,10 @@ var DEFAULT_SEED_CONFIG = {
522
529
  type: "stdio",
523
530
  command: "npx",
524
531
  args: ["-y", "@modelcontextprotocol/server-everything"]
532
+ },
533
+ "example-server-default": {
534
+ type: "streamable-http",
535
+ url: EXAMPLE_SERVER_URL
525
536
  }
526
537
  }
527
538
  };
@@ -4489,11 +4500,27 @@ function createRemoteApp(options) {
4489
4500
  void responseWait.catch(() => {
4490
4501
  });
4491
4502
  }
4503
+ const upstreamAbort = new AbortController();
4504
+ const clientSignal = c.req.raw.signal;
4505
+ const onClientDisconnect = () => {
4506
+ upstreamAbort.abort(clientSignal.reason);
4507
+ if (requestId !== void 0) {
4508
+ session.cancelRequestWait(requestId);
4509
+ }
4510
+ };
4511
+ if (clientSignal.aborted) {
4512
+ onClientDisconnect();
4513
+ } else {
4514
+ clientSignal.addEventListener("abort", onClientDisconnect, {
4515
+ once: true
4516
+ });
4517
+ }
4492
4518
  try {
4493
4519
  const paramHeaders = mcpParamHeadersOnly(headers);
4494
4520
  await session.transport.send(message, {
4495
4521
  relatedRequestId,
4496
- ...paramHeaders && { headers: paramHeaders }
4522
+ ...paramHeaders && { headers: paramHeaders },
4523
+ requestSignal: upstreamAbort.signal
4497
4524
  });
4498
4525
  if (responseWait) {
4499
4526
  await responseWait;
@@ -4514,6 +4541,7 @@ function createRemoteApp(options) {
4514
4541
  const msg = err instanceof Error ? err.message : String(err);
4515
4542
  return c.json({ ok: false, kind: "transport_error", error: msg });
4516
4543
  } finally {
4544
+ clientSignal.removeEventListener("abort", onClientDisconnect);
4517
4545
  session.endSend();
4518
4546
  }
4519
4547
  });
@@ -4755,6 +4783,9 @@ function createRemoteApp(options) {
4755
4783
  if (o.requestRefreshToken !== void 0 && typeof o.requestRefreshToken !== "boolean") {
4756
4784
  return false;
4757
4785
  }
4786
+ if (o.revokeOnClear !== void 0 && typeof o.revokeOnClear !== "boolean") {
4787
+ return false;
4788
+ }
4758
4789
  return true;
4759
4790
  };
4760
4791
  const isRootArray = (v) => {
@@ -4826,7 +4857,7 @@ function createRemoteApp(options) {
4826
4857
  if ("oauth" in valObj && !isOauthObject(valObj.oauth)) {
4827
4858
  logWarn(
4828
4859
  { route: "/api/servers", id, droppedKey: "oauth" },
4829
- "Dropping malformed `oauth` field \u2014 expected `{ clientId?, clientSecret?, scopes?, authorizationParams?, authorizationUrl?, tokenUrl?, enterpriseManaged?, onInsufficientScope?, requestRefreshToken? }`."
4860
+ "Dropping malformed `oauth` field \u2014 expected `{ clientId?, clientSecret?, scopes?, authorizationParams?, authorizationUrl?, tokenUrl?, enterpriseManaged?, onInsufficientScope?, requestRefreshToken?, revokeOnClear? }`."
4830
4861
  );
4831
4862
  delete valObj.oauth;
4832
4863
  }
@@ -5010,6 +5041,12 @@ function createRemoteApp(options) {
5010
5041
  error: "settings.oauthRequestRefreshToken must be a boolean"
5011
5042
  };
5012
5043
  }
5044
+ if (obj.oauthRevokeOnClear !== void 0 && typeof obj.oauthRevokeOnClear !== "boolean") {
5045
+ return {
5046
+ ok: false,
5047
+ error: "settings.oauthRevokeOnClear must be a boolean"
5048
+ };
5049
+ }
5013
5050
  if (obj.oauthOnInsufficientScope !== void 0 && obj.oauthOnInsufficientScope !== "reauthorize" && obj.oauthOnInsufficientScope !== "throw") {
5014
5051
  return {
5015
5052
  ok: false,
@@ -5092,6 +5129,9 @@ function createRemoteApp(options) {
5092
5129
  if (obj.oauthRequestRefreshToken === false) {
5093
5130
  value.oauthRequestRefreshToken = false;
5094
5131
  }
5132
+ if (obj.oauthRevokeOnClear === false) {
5133
+ value.oauthRevokeOnClear = false;
5134
+ }
5095
5135
  if (obj.oauthOnInsufficientScope === "reauthorize" || obj.oauthOnInsufficientScope === "throw") {
5096
5136
  value.oauthOnInsufficientScope = obj.oauthOnInsufficientScope;
5097
5137
  }
@@ -5864,7 +5904,11 @@ function vitestSharedPaths(clientDir) {
5864
5904
  "react",
5865
5905
  "react-dom",
5866
5906
  "@modelcontextprotocol/client",
5867
- "@modelcontextprotocol/core"
5907
+ "@modelcontextprotocol/core",
5908
+ // Every SDK schema is a zod type, so a second copy breaks `instanceof`
5909
+ // across the whole surface. The alias below picks the install; this keeps
5910
+ // it to one copy within it.
5911
+ "zod"
5868
5912
  ];
5869
5913
  const nodeModulesAliases = [
5870
5914
  {
@@ -5890,67 +5934,123 @@ function vitestSharedPaths(clientDir) {
5890
5934
  find: /^react-dom\/client$/,
5891
5935
  replacement: path5.resolve(dirname10, "node_modules/react-dom/client.js")
5892
5936
  },
5893
- { find: /^pino$/, replacement: path5.resolve(dirname10, "node_modules/pino") },
5937
+ // Everything below is **root-owned** and resolves from the repo root,
5938
+ // unlike the `react` / `react-dom` pins above. What they have in common is
5939
+ // that no client declares them, so the root install is the only place a
5940
+ // client's resolution chain is guaranteed to find one — not that they are
5941
+ // all root `dependencies`: `express` is test-only and sits in the root
5942
+ // `devDependencies`. Nor is this the complete root runtime set; a package
5943
+ // no client install carries a copy of (`ajv`, `commander`, `undici`)
5944
+ // resolves to the root on its own and needs no pin. Absence from this list
5945
+ // is a statement about the installed tree, not about the manifests — check
5946
+ // the tree before adding or removing an entry.
5947
+ //
5948
+ // `express` is reached only through `test-servers/src` (the http/oauth
5949
+ // servers), which is root-owned code with no manifest of its own.
5950
+ //
5951
+ // `yaml` was too — `load-config.ts` — but is now also a `core/` runtime
5952
+ // import: `core/mcp/skillFile.ts` parses a served SKILL.md's frontmatter
5953
+ // for the SEP-2640 cross-check (#2248). That matters to anyone revisiting
5954
+ // this pin: it is no longer removable by retiring a test-server path, and
5955
+ // as a dependency `core/` imports it is additionally named in all three
5956
+ // bundler `external` lists.
5957
+ //
5958
+ // Pointing these at `<client>/node_modules` is what broke when the MCP
5959
+ // packages moved to the root (#1970): express was never declared by a client
5960
+ // at all, it arrived in `clients/cli` as a peer of `express-rate-limit`
5961
+ // under `@modelcontextprotocol/server-legacy`, so removing that manifest
5962
+ // entry took express with it and every cli test that spawns a test server
5963
+ // failed to resolve it.
5964
+ {
5965
+ find: /^express$/,
5966
+ replacement: path5.resolve(repoRoot, "node_modules/express")
5967
+ },
5968
+ {
5969
+ find: /^yaml$/,
5970
+ replacement: path5.resolve(repoRoot, "node_modules/yaml")
5971
+ },
5972
+ // Same reasoning, one layer in: `proper-lockfile` is reached only through
5973
+ // `core/` (the secrets file's cross-process lock, #2082), which is the
5974
+ // other root-owned tree with no manifest of its own. Resolution finds the
5975
+ // root copy on its own today — nothing declares it in a client — and this
5976
+ // pin is what keeps that from depending on nothing ever arriving as some
5977
+ // client's transitive dependency, which would otherwise give a test two
5978
+ // copies of a module whose whole job is a single registry of held locks.
5979
+ {
5980
+ find: /^proper-lockfile$/,
5981
+ replacement: path5.resolve(repoRoot, "node_modules/proper-lockfile")
5982
+ },
5983
+ // The rest of the root-owned aliases, consolidated into the root manifest
5984
+ // by #2195. Mostly `core/`'s runtime dependencies, but not only —
5985
+ // `@hono/node-server` is reached from web client code alone and is here for
5986
+ // the same resolution reason rather than because `core/` imports it. Each
5987
+ // used to be declared by the clients that reached it and was pinned to
5988
+ // `<client>/node_modules` accordingly; once the declarations went away
5989
+ // those paths stopped existing, so the pin has to follow the package to
5990
+ // the root. Left un-repointed they would resolve
5991
+ // to a directory that is not there — or, worse, to a transitive copy some
5992
+ // unrelated dependency happened to drag in, which is the duplicate this
5993
+ // whole pin list exists to prevent.
5994
+ {
5995
+ find: /^pino$/,
5996
+ replacement: path5.resolve(repoRoot, "node_modules/pino")
5997
+ },
5894
5998
  {
5895
5999
  find: /^pino\/browser\.js$/,
5896
- replacement: path5.resolve(dirname10, "node_modules/pino/browser.js")
6000
+ replacement: path5.resolve(repoRoot, "node_modules/pino/browser.js")
5897
6001
  },
5898
6002
  {
5899
6003
  find: /^hono$/,
5900
- replacement: path5.resolve(dirname10, "node_modules/hono/dist/index.js")
6004
+ replacement: path5.resolve(repoRoot, "node_modules/hono/dist/index.js")
5901
6005
  },
5902
6006
  {
5903
6007
  find: /^hono\/streaming$/,
5904
6008
  replacement: path5.resolve(
5905
- dirname10,
6009
+ repoRoot,
5906
6010
  "node_modules/hono/dist/helper/streaming/index.js"
5907
6011
  )
5908
6012
  },
5909
6013
  {
5910
6014
  find: /^@hono\/node-server$/,
5911
- replacement: path5.resolve(dirname10, "node_modules/@hono/node-server")
6015
+ replacement: path5.resolve(repoRoot, "node_modules/@hono/node-server")
5912
6016
  },
5913
6017
  {
5914
6018
  find: /^atomically$/,
5915
- replacement: path5.resolve(dirname10, "node_modules/atomically")
6019
+ replacement: path5.resolve(repoRoot, "node_modules/atomically")
5916
6020
  },
5917
6021
  {
5918
6022
  find: /^chokidar$/,
5919
- replacement: path5.resolve(dirname10, "node_modules/chokidar")
6023
+ replacement: path5.resolve(repoRoot, "node_modules/chokidar")
5920
6024
  },
5921
6025
  {
5922
6026
  find: /^@napi-rs\/keyring$/,
5923
- replacement: path5.resolve(dirname10, "node_modules/@napi-rs/keyring")
5924
- },
5925
- // `express` and `yaml` resolve from the **repo root**, unlike every pin
5926
- // above. Both are reached only through `test-servers/src` — express by the
5927
- // http/oauth servers, yaml by `load-config.ts` — which is root-owned code
5928
- // with no manifest of its own, so the root is where they are declared and
5929
- // the only place a client's resolution chain is guaranteed to find them.
5930
- // Pointing these at `<client>/node_modules` is what broke when the MCP
5931
- // packages moved to the root (#1970): express was never declared by a client
5932
- // at all, it arrived in `clients/cli` as a peer of `express-rate-limit`
5933
- // under `@modelcontextprotocol/server-legacy`, so removing that manifest
5934
- // entry took express with it and every cli test that spawns a test server
5935
- // failed to resolve it.
5936
- {
5937
- find: /^express$/,
5938
- replacement: path5.resolve(repoRoot, "node_modules/express")
6027
+ replacement: path5.resolve(repoRoot, "node_modules/@napi-rs/keyring")
5939
6028
  },
6029
+ // `zod` and `open` are pinned for a different reason from everything above:
6030
+ // they resolve *somewhere* without help, and the somewhere is wrong. Both
6031
+ // still sit at the top level of `clients/web/node_modules` as transitive
6032
+ // copies — zod under `eslint-plugin-react-hooks`, open under Storybook —
6033
+ // so an unpinned bare import from a web test resolves the client copy while
6034
+ // `core/` and the SDK packages resolve the root's (Copilot).
6035
+ //
6036
+ // For `open` that is merely wasteful. For `zod` it is the hazard this file
6037
+ // exists for: two copies in one process means a schema built by one and an
6038
+ // `instanceof` check made by the other, across the entire
6039
+ // `@modelcontextprotocol/*` surface. The versions are identical today and
6040
+ // `verify:dep-lockstep` is what keeps them that way, but identical is not
6041
+ // the same as single, and only a pin makes it single.
6042
+ //
6043
+ // `zod/v4` needs its own entry — first-party code imports both specifiers,
6044
+ // and pinning only the bare one would split the package across two installs
6045
+ // rather than collapse it.
6046
+ { find: /^zod$/, replacement: path5.resolve(repoRoot, "node_modules/zod") },
5940
6047
  {
5941
- find: /^yaml$/,
5942
- replacement: path5.resolve(repoRoot, "node_modules/yaml")
6048
+ find: /^zod\/v4$/,
6049
+ replacement: path5.resolve(repoRoot, "node_modules/zod/v4/index.js")
5943
6050
  },
5944
- // Same reasoning, one layer in: `proper-lockfile` is reached only through
5945
- // `core/` (the secrets file's cross-process lock, #2082), which is the
5946
- // other root-owned tree with no manifest of its own. Resolution finds the
5947
- // root copy on its own today — nothing declares it in a client — and this
5948
- // pin is what keeps that from depending on nothing ever arriving as some
5949
- // client's transitive dependency, which would otherwise give a test two
5950
- // copies of a module whose whole job is a single registry of held locks.
5951
6051
  {
5952
- find: /^proper-lockfile$/,
5953
- replacement: path5.resolve(repoRoot, "node_modules/proper-lockfile")
6052
+ find: /^open$/,
6053
+ replacement: path5.resolve(repoRoot, "node_modules/open")
5954
6054
  }
5955
6055
  ];
5956
6056
  const projectResolve = {