@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.
- package/README.md +51 -579
- package/clients/cli/build/index.js +2805 -161
- package/clients/tui/build/index.js +4282 -1453
- package/clients/web/build/index.js +137 -37
- package/clients/web/dist/assets/index-30pI79Cf.js +1595 -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-9SaQe5zv.js} +1 -1
- package/clients/web/dist/index.html +2 -2
- package/package.json +25 -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
|
}
|
|
@@ -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
|
-
|
|
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(
|
|
6000
|
+
replacement: path5.resolve(repoRoot, "node_modules/pino/browser.js")
|
|
5897
6001
|
},
|
|
5898
6002
|
{
|
|
5899
6003
|
find: /^hono$/,
|
|
5900
|
-
replacement: path5.resolve(
|
|
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
|
-
|
|
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(
|
|
6015
|
+
replacement: path5.resolve(repoRoot, "node_modules/@hono/node-server")
|
|
5912
6016
|
},
|
|
5913
6017
|
{
|
|
5914
6018
|
find: /^atomically$/,
|
|
5915
|
-
replacement: path5.resolve(
|
|
6019
|
+
replacement: path5.resolve(repoRoot, "node_modules/atomically")
|
|
5916
6020
|
},
|
|
5917
6021
|
{
|
|
5918
6022
|
find: /^chokidar$/,
|
|
5919
|
-
replacement: path5.resolve(
|
|
6023
|
+
replacement: path5.resolve(repoRoot, "node_modules/chokidar")
|
|
5920
6024
|
},
|
|
5921
6025
|
{
|
|
5922
6026
|
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")
|
|
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: /^
|
|
5942
|
-
replacement: path5.resolve(repoRoot, "node_modules/
|
|
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: /^
|
|
5953
|
-
replacement: path5.resolve(repoRoot, "node_modules/
|
|
6052
|
+
find: /^open$/,
|
|
6053
|
+
replacement: path5.resolve(repoRoot, "node_modules/open")
|
|
5954
6054
|
}
|
|
5955
6055
|
];
|
|
5956
6056
|
const projectResolve = {
|