@modelcontextprotocol/inspector 2.4.0 → 2.5.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 +47 -577
- package/clients/cli/build/index.js +1286 -49
- package/clients/tui/build/index.js +1887 -759
- package/clients/web/build/index.js +126 -37
- package/clients/web/dist/assets/index-ZYqA4f7P.js +1486 -0
- package/clients/web/dist/assets/{index-DQVN73FG.css → index-dqp8Sd8W.css} +1 -1
- package/clients/web/dist/assets/{prism-light-HLcnHJHw.js → prism-light-_bvDBTQE.js} +1 -1
- package/clients/web/dist/index.html +2 -2
- package/package.json +24 -12
- package/clients/web/dist/assets/index-JCsLrWVa.js +0 -1484
- package/clients/web/dist/assets/json-BJ8dCynk.js +0 -1
|
@@ -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
|
}
|
|
@@ -4489,11 +4495,27 @@ function createRemoteApp(options) {
|
|
|
4489
4495
|
void responseWait.catch(() => {
|
|
4490
4496
|
});
|
|
4491
4497
|
}
|
|
4498
|
+
const upstreamAbort = new AbortController();
|
|
4499
|
+
const clientSignal = c.req.raw.signal;
|
|
4500
|
+
const onClientDisconnect = () => {
|
|
4501
|
+
upstreamAbort.abort(clientSignal.reason);
|
|
4502
|
+
if (requestId !== void 0) {
|
|
4503
|
+
session.cancelRequestWait(requestId);
|
|
4504
|
+
}
|
|
4505
|
+
};
|
|
4506
|
+
if (clientSignal.aborted) {
|
|
4507
|
+
onClientDisconnect();
|
|
4508
|
+
} else {
|
|
4509
|
+
clientSignal.addEventListener("abort", onClientDisconnect, {
|
|
4510
|
+
once: true
|
|
4511
|
+
});
|
|
4512
|
+
}
|
|
4492
4513
|
try {
|
|
4493
4514
|
const paramHeaders = mcpParamHeadersOnly(headers);
|
|
4494
4515
|
await session.transport.send(message, {
|
|
4495
4516
|
relatedRequestId,
|
|
4496
|
-
...paramHeaders && { headers: paramHeaders }
|
|
4517
|
+
...paramHeaders && { headers: paramHeaders },
|
|
4518
|
+
requestSignal: upstreamAbort.signal
|
|
4497
4519
|
});
|
|
4498
4520
|
if (responseWait) {
|
|
4499
4521
|
await responseWait;
|
|
@@ -4514,6 +4536,7 @@ function createRemoteApp(options) {
|
|
|
4514
4536
|
const msg = err instanceof Error ? err.message : String(err);
|
|
4515
4537
|
return c.json({ ok: false, kind: "transport_error", error: msg });
|
|
4516
4538
|
} finally {
|
|
4539
|
+
clientSignal.removeEventListener("abort", onClientDisconnect);
|
|
4517
4540
|
session.endSend();
|
|
4518
4541
|
}
|
|
4519
4542
|
});
|
|
@@ -4755,6 +4778,9 @@ function createRemoteApp(options) {
|
|
|
4755
4778
|
if (o.requestRefreshToken !== void 0 && typeof o.requestRefreshToken !== "boolean") {
|
|
4756
4779
|
return false;
|
|
4757
4780
|
}
|
|
4781
|
+
if (o.revokeOnClear !== void 0 && typeof o.revokeOnClear !== "boolean") {
|
|
4782
|
+
return false;
|
|
4783
|
+
}
|
|
4758
4784
|
return true;
|
|
4759
4785
|
};
|
|
4760
4786
|
const isRootArray = (v) => {
|
|
@@ -4826,7 +4852,7 @@ function createRemoteApp(options) {
|
|
|
4826
4852
|
if ("oauth" in valObj && !isOauthObject(valObj.oauth)) {
|
|
4827
4853
|
logWarn(
|
|
4828
4854
|
{ route: "/api/servers", id, droppedKey: "oauth" },
|
|
4829
|
-
"Dropping malformed `oauth` field \u2014 expected `{ clientId?, clientSecret?, scopes?, authorizationParams?, authorizationUrl?, tokenUrl?, enterpriseManaged?, onInsufficientScope?, requestRefreshToken? }`."
|
|
4855
|
+
"Dropping malformed `oauth` field \u2014 expected `{ clientId?, clientSecret?, scopes?, authorizationParams?, authorizationUrl?, tokenUrl?, enterpriseManaged?, onInsufficientScope?, requestRefreshToken?, revokeOnClear? }`."
|
|
4830
4856
|
);
|
|
4831
4857
|
delete valObj.oauth;
|
|
4832
4858
|
}
|
|
@@ -5010,6 +5036,12 @@ function createRemoteApp(options) {
|
|
|
5010
5036
|
error: "settings.oauthRequestRefreshToken must be a boolean"
|
|
5011
5037
|
};
|
|
5012
5038
|
}
|
|
5039
|
+
if (obj.oauthRevokeOnClear !== void 0 && typeof obj.oauthRevokeOnClear !== "boolean") {
|
|
5040
|
+
return {
|
|
5041
|
+
ok: false,
|
|
5042
|
+
error: "settings.oauthRevokeOnClear must be a boolean"
|
|
5043
|
+
};
|
|
5044
|
+
}
|
|
5013
5045
|
if (obj.oauthOnInsufficientScope !== void 0 && obj.oauthOnInsufficientScope !== "reauthorize" && obj.oauthOnInsufficientScope !== "throw") {
|
|
5014
5046
|
return {
|
|
5015
5047
|
ok: false,
|
|
@@ -5092,6 +5124,9 @@ function createRemoteApp(options) {
|
|
|
5092
5124
|
if (obj.oauthRequestRefreshToken === false) {
|
|
5093
5125
|
value.oauthRequestRefreshToken = false;
|
|
5094
5126
|
}
|
|
5127
|
+
if (obj.oauthRevokeOnClear === false) {
|
|
5128
|
+
value.oauthRevokeOnClear = false;
|
|
5129
|
+
}
|
|
5095
5130
|
if (obj.oauthOnInsufficientScope === "reauthorize" || obj.oauthOnInsufficientScope === "throw") {
|
|
5096
5131
|
value.oauthOnInsufficientScope = obj.oauthOnInsufficientScope;
|
|
5097
5132
|
}
|
|
@@ -5864,7 +5899,11 @@ function vitestSharedPaths(clientDir) {
|
|
|
5864
5899
|
"react",
|
|
5865
5900
|
"react-dom",
|
|
5866
5901
|
"@modelcontextprotocol/client",
|
|
5867
|
-
"@modelcontextprotocol/core"
|
|
5902
|
+
"@modelcontextprotocol/core",
|
|
5903
|
+
// Every SDK schema is a zod type, so a second copy breaks `instanceof`
|
|
5904
|
+
// across the whole surface. The alias below picks the install; this keeps
|
|
5905
|
+
// it to one copy within it.
|
|
5906
|
+
"zod"
|
|
5868
5907
|
];
|
|
5869
5908
|
const nodeModulesAliases = [
|
|
5870
5909
|
{
|
|
@@ -5890,67 +5929,117 @@ function vitestSharedPaths(clientDir) {
|
|
|
5890
5929
|
find: /^react-dom\/client$/,
|
|
5891
5930
|
replacement: path5.resolve(dirname10, "node_modules/react-dom/client.js")
|
|
5892
5931
|
},
|
|
5893
|
-
|
|
5932
|
+
// Everything below is **root-owned** and resolves from the repo root,
|
|
5933
|
+
// unlike the `react` / `react-dom` pins above. What they have in common is
|
|
5934
|
+
// that no client declares them, so the root install is the only place a
|
|
5935
|
+
// client's resolution chain is guaranteed to find one — not that they are
|
|
5936
|
+
// all root `dependencies`: `express` is test-only and sits in the root
|
|
5937
|
+
// `devDependencies`. Nor is this the complete root runtime set; a package
|
|
5938
|
+
// no client install carries a copy of (`ajv`, `commander`, `undici`)
|
|
5939
|
+
// resolves to the root on its own and needs no pin. Absence from this list
|
|
5940
|
+
// is a statement about the installed tree, not about the manifests — check
|
|
5941
|
+
// the tree before adding or removing an entry.
|
|
5942
|
+
//
|
|
5943
|
+
// `express` and `yaml` are reached only through `test-servers/src` —
|
|
5944
|
+
// express by the http/oauth servers, yaml by `load-config.ts` — which is
|
|
5945
|
+
// root-owned code with no manifest of its own.
|
|
5946
|
+
//
|
|
5947
|
+
// Pointing these at `<client>/node_modules` is what broke when the MCP
|
|
5948
|
+
// packages moved to the root (#1970): express was never declared by a client
|
|
5949
|
+
// at all, it arrived in `clients/cli` as a peer of `express-rate-limit`
|
|
5950
|
+
// under `@modelcontextprotocol/server-legacy`, so removing that manifest
|
|
5951
|
+
// entry took express with it and every cli test that spawns a test server
|
|
5952
|
+
// failed to resolve it.
|
|
5953
|
+
{
|
|
5954
|
+
find: /^express$/,
|
|
5955
|
+
replacement: path5.resolve(repoRoot, "node_modules/express")
|
|
5956
|
+
},
|
|
5957
|
+
{
|
|
5958
|
+
find: /^yaml$/,
|
|
5959
|
+
replacement: path5.resolve(repoRoot, "node_modules/yaml")
|
|
5960
|
+
},
|
|
5961
|
+
// Same reasoning, one layer in: `proper-lockfile` is reached only through
|
|
5962
|
+
// `core/` (the secrets file's cross-process lock, #2082), which is the
|
|
5963
|
+
// other root-owned tree with no manifest of its own. Resolution finds the
|
|
5964
|
+
// root copy on its own today — nothing declares it in a client — and this
|
|
5965
|
+
// pin is what keeps that from depending on nothing ever arriving as some
|
|
5966
|
+
// client's transitive dependency, which would otherwise give a test two
|
|
5967
|
+
// copies of a module whose whole job is a single registry of held locks.
|
|
5968
|
+
{
|
|
5969
|
+
find: /^proper-lockfile$/,
|
|
5970
|
+
replacement: path5.resolve(repoRoot, "node_modules/proper-lockfile")
|
|
5971
|
+
},
|
|
5972
|
+
// The rest of the root-owned aliases, consolidated into the root manifest
|
|
5973
|
+
// by #2195. Mostly `core/`'s runtime dependencies, but not only —
|
|
5974
|
+
// `@hono/node-server` is reached from web client code alone and is here for
|
|
5975
|
+
// the same resolution reason rather than because `core/` imports it. Each
|
|
5976
|
+
// used to be declared by the clients that reached it and was pinned to
|
|
5977
|
+
// `<client>/node_modules` accordingly; once the declarations went away
|
|
5978
|
+
// those paths stopped existing, so the pin has to follow the package to
|
|
5979
|
+
// the root. Left un-repointed they would resolve
|
|
5980
|
+
// to a directory that is not there — or, worse, to a transitive copy some
|
|
5981
|
+
// unrelated dependency happened to drag in, which is the duplicate this
|
|
5982
|
+
// whole pin list exists to prevent.
|
|
5983
|
+
{
|
|
5984
|
+
find: /^pino$/,
|
|
5985
|
+
replacement: path5.resolve(repoRoot, "node_modules/pino")
|
|
5986
|
+
},
|
|
5894
5987
|
{
|
|
5895
5988
|
find: /^pino\/browser\.js$/,
|
|
5896
|
-
replacement: path5.resolve(
|
|
5989
|
+
replacement: path5.resolve(repoRoot, "node_modules/pino/browser.js")
|
|
5897
5990
|
},
|
|
5898
5991
|
{
|
|
5899
5992
|
find: /^hono$/,
|
|
5900
|
-
replacement: path5.resolve(
|
|
5993
|
+
replacement: path5.resolve(repoRoot, "node_modules/hono/dist/index.js")
|
|
5901
5994
|
},
|
|
5902
5995
|
{
|
|
5903
5996
|
find: /^hono\/streaming$/,
|
|
5904
5997
|
replacement: path5.resolve(
|
|
5905
|
-
|
|
5998
|
+
repoRoot,
|
|
5906
5999
|
"node_modules/hono/dist/helper/streaming/index.js"
|
|
5907
6000
|
)
|
|
5908
6001
|
},
|
|
5909
6002
|
{
|
|
5910
6003
|
find: /^@hono\/node-server$/,
|
|
5911
|
-
replacement: path5.resolve(
|
|
6004
|
+
replacement: path5.resolve(repoRoot, "node_modules/@hono/node-server")
|
|
5912
6005
|
},
|
|
5913
6006
|
{
|
|
5914
6007
|
find: /^atomically$/,
|
|
5915
|
-
replacement: path5.resolve(
|
|
6008
|
+
replacement: path5.resolve(repoRoot, "node_modules/atomically")
|
|
5916
6009
|
},
|
|
5917
6010
|
{
|
|
5918
6011
|
find: /^chokidar$/,
|
|
5919
|
-
replacement: path5.resolve(
|
|
6012
|
+
replacement: path5.resolve(repoRoot, "node_modules/chokidar")
|
|
5920
6013
|
},
|
|
5921
6014
|
{
|
|
5922
6015
|
find: /^@napi-rs\/keyring$/,
|
|
5923
|
-
replacement: path5.resolve(
|
|
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")
|
|
6016
|
+
replacement: path5.resolve(repoRoot, "node_modules/@napi-rs/keyring")
|
|
5939
6017
|
},
|
|
6018
|
+
// `zod` and `open` are pinned for a different reason from everything above:
|
|
6019
|
+
// they resolve *somewhere* without help, and the somewhere is wrong. Both
|
|
6020
|
+
// still sit at the top level of `clients/web/node_modules` as transitive
|
|
6021
|
+
// copies — zod under `eslint-plugin-react-hooks`, open under Storybook —
|
|
6022
|
+
// so an unpinned bare import from a web test resolves the client copy while
|
|
6023
|
+
// `core/` and the SDK packages resolve the root's (Copilot).
|
|
6024
|
+
//
|
|
6025
|
+
// For `open` that is merely wasteful. For `zod` it is the hazard this file
|
|
6026
|
+
// exists for: two copies in one process means a schema built by one and an
|
|
6027
|
+
// `instanceof` check made by the other, across the entire
|
|
6028
|
+
// `@modelcontextprotocol/*` surface. The versions are identical today and
|
|
6029
|
+
// `verify:dep-lockstep` is what keeps them that way, but identical is not
|
|
6030
|
+
// the same as single, and only a pin makes it single.
|
|
6031
|
+
//
|
|
6032
|
+
// `zod/v4` needs its own entry — first-party code imports both specifiers,
|
|
6033
|
+
// and pinning only the bare one would split the package across two installs
|
|
6034
|
+
// rather than collapse it.
|
|
6035
|
+
{ find: /^zod$/, replacement: path5.resolve(repoRoot, "node_modules/zod") },
|
|
5940
6036
|
{
|
|
5941
|
-
find: /^
|
|
5942
|
-
replacement: path5.resolve(repoRoot, "node_modules/
|
|
6037
|
+
find: /^zod\/v4$/,
|
|
6038
|
+
replacement: path5.resolve(repoRoot, "node_modules/zod/v4/index.js")
|
|
5943
6039
|
},
|
|
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
6040
|
{
|
|
5952
|
-
find: /^
|
|
5953
|
-
replacement: path5.resolve(repoRoot, "node_modules/
|
|
6041
|
+
find: /^open$/,
|
|
6042
|
+
replacement: path5.resolve(repoRoot, "node_modules/open")
|
|
5954
6043
|
}
|
|
5955
6044
|
];
|
|
5956
6045
|
const projectResolve = {
|