@modelcontextprotocol/inspector 2.10.1 → 2.11.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/clients/cli/build/index.js +3087 -2771
- package/clients/launcher/build/index.js +4 -1
- package/clients/mcpdo/build/{chunk-2E5EZYNU.js → chunk-O6RN6WMD.js} +362 -78
- package/clients/mcpdo/build/mcp-bin.js +7 -21
- package/clients/mcpdo/build/mcpdod.js +1 -1
- package/clients/tui/build/index.js +421 -119
- package/clients/web/build/index.js +148 -43
- package/clients/web/dist/assets/index-B79AqubU.js +1587 -0
- package/clients/web/dist/assets/{markdown-CZCm46w1.js → markdown-BlYcOtJg.js} +1 -1
- package/clients/web/dist/assets/{prism-light-B0A727v7.js → prism-light-J_CKjs7N.js} +2 -2
- package/clients/web/dist/index.html +1 -1
- package/package.json +14 -9
- package/scripts/install-clients.mjs +68 -28
- package/clients/web/dist/assets/index-DgDEc2gL.js +0 -1589
|
@@ -821,16 +821,30 @@ function buildConfigFromOptions(options) {
|
|
|
821
821
|
const target = options.target ?? [];
|
|
822
822
|
const first = target[0];
|
|
823
823
|
const rest = target.slice(1);
|
|
824
|
+
if (first && options.serverUrl) {
|
|
825
|
+
throw new Error(
|
|
826
|
+
`Cannot combine a positional target (${first}) with --server-url (${options.serverUrl}). Pass one or the other.`
|
|
827
|
+
);
|
|
828
|
+
}
|
|
824
829
|
const urlFromTarget = first && (first.startsWith("http://") || first.startsWith("https://")) ? first : null;
|
|
825
830
|
const url = urlFromTarget ?? options.serverUrl ?? null;
|
|
826
831
|
if (url) {
|
|
827
|
-
if (rest.length > 0
|
|
832
|
+
if (rest.length > 0) {
|
|
828
833
|
throw new Error("Arguments cannot be passed to a URL-based MCP server.");
|
|
829
834
|
}
|
|
835
|
+
if (options.transport === "stdio") {
|
|
836
|
+
throw new Error(
|
|
837
|
+
`stdio transport cannot be used with a URL (${url}). Use --transport http or --transport sse, or pass a local command instead.`
|
|
838
|
+
);
|
|
839
|
+
}
|
|
830
840
|
let transportType;
|
|
831
841
|
const t = options.transport === "http" ? "streamable-http" : options.transport;
|
|
832
842
|
if (t === "sse" || t === "streamable-http") {
|
|
833
843
|
transportType = t;
|
|
844
|
+
} else if (t) {
|
|
845
|
+
throw new Error(
|
|
846
|
+
`Invalid transport type: ${t}. Valid types are: sse, http, stdio`
|
|
847
|
+
);
|
|
834
848
|
} else {
|
|
835
849
|
const u = new URL(url);
|
|
836
850
|
if (u.pathname.endsWith("/mcp")) {
|
|
@@ -1683,7 +1697,10 @@ MCP Inspector Web is up and running at:
|
|
|
1683
1697
|
}
|
|
1684
1698
|
function openBrowser(url) {
|
|
1685
1699
|
openUrl(url).catch((err) => {
|
|
1686
|
-
|
|
1700
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
1701
|
+
console.warn(
|
|
1702
|
+
`Could not open a browser automatically (${reason}). Open the URL above manually.`
|
|
1703
|
+
);
|
|
1687
1704
|
});
|
|
1688
1705
|
}
|
|
1689
1706
|
var LOOPBACK_HOSTNAMES = /* @__PURE__ */ new Set(["localhost", "127.0.0.1", "[::1]"]);
|
|
@@ -2291,7 +2308,10 @@ function parseAuthChallengeFromResponse(response, context) {
|
|
|
2291
2308
|
if (status !== 401 && status !== 403) {
|
|
2292
2309
|
return void 0;
|
|
2293
2310
|
}
|
|
2294
|
-
const wwwAuthenticate = response.headers.get("WWW-Authenticate")
|
|
2311
|
+
const wwwAuthenticate = response.headers.get("WWW-Authenticate") || void 0;
|
|
2312
|
+
if (status === 403 && !wwwAuthenticate) {
|
|
2313
|
+
return void 0;
|
|
2314
|
+
}
|
|
2295
2315
|
const bearer = wwwAuthenticate ? parseWwwAuthenticateBearer(wwwAuthenticate) : {};
|
|
2296
2316
|
const requiredScopes = parseScopeString(bearer.scope);
|
|
2297
2317
|
return {
|
|
@@ -2623,6 +2643,9 @@ function createTransportNode(config, options = {}) {
|
|
|
2623
2643
|
// ../../core/auth/requestTimeout.ts
|
|
2624
2644
|
var OAUTH_TIMEOUT_WIRE_CODE = "oauth_request_timeout";
|
|
2625
2645
|
|
|
2646
|
+
// ../../core/mcp/remote/node/server.ts
|
|
2647
|
+
import { SdkHttpError } from "@modelcontextprotocol/client";
|
|
2648
|
+
|
|
2626
2649
|
// ../../core/auth/oauth-persist.ts
|
|
2627
2650
|
import { OAuthTokensSchema } from "@modelcontextprotocol/core";
|
|
2628
2651
|
var OAUTH_PERSIST_STORE_ID = "oauth";
|
|
@@ -4723,6 +4746,17 @@ async function recordNamespaceKeys(stateFilePath, secretStore, namespace, server
|
|
|
4723
4746
|
warnLedgerFailure("update", error);
|
|
4724
4747
|
}
|
|
4725
4748
|
}
|
|
4749
|
+
async function recordedNamespaces(stateFilePath) {
|
|
4750
|
+
try {
|
|
4751
|
+
const ledger = parseLedger(
|
|
4752
|
+
await readStoreFile(namespaceLedgerPath(stateFilePath))
|
|
4753
|
+
);
|
|
4754
|
+
return [...ledger.keys()].reverse();
|
|
4755
|
+
} catch (error) {
|
|
4756
|
+
warnLedgerFailure("read", error);
|
|
4757
|
+
return [];
|
|
4758
|
+
}
|
|
4759
|
+
}
|
|
4726
4760
|
async function purgeSupersededNamespaces(stateFilePath, secretStore, current) {
|
|
4727
4761
|
let ledger;
|
|
4728
4762
|
let location;
|
|
@@ -4886,24 +4920,13 @@ async function adoptSecretsNamespace(filePath, secretStore, locked) {
|
|
|
4886
4920
|
if (raw !== null && snapshot === null) {
|
|
4887
4921
|
throw new OAuthStateFileUnrecognizedError(filePath, "save");
|
|
4888
4922
|
}
|
|
4889
|
-
if (locked) {
|
|
4890
|
-
await purgeSupersededNamespaces(filePath, secretStore, void 0);
|
|
4891
|
-
}
|
|
4892
4923
|
const namespace = newSecretsNamespace();
|
|
4893
|
-
if (snapshot === null)
|
|
4894
|
-
|
|
4895
|
-
|
|
4896
|
-
|
|
4897
|
-
|
|
4898
|
-
|
|
4899
|
-
})),
|
|
4900
|
-
...Object.keys(snapshot.idpSessions).map((issuer) => ({
|
|
4901
|
-
legacyId: oauthIdpSecretServerId(issuer),
|
|
4902
|
-
scopedId: oauthIdpSecretServerId(issuer, namespace),
|
|
4903
|
-
fields: [IDP_SESSION_FIELD]
|
|
4904
|
-
}))
|
|
4905
|
-
];
|
|
4906
|
-
if (moves.length === 0) return namespace;
|
|
4924
|
+
if (snapshot === null || Object.keys(snapshot.servers).length === 0 && Object.keys(snapshot.idpSessions).length === 0) {
|
|
4925
|
+
if (locked) {
|
|
4926
|
+
await purgeSupersededNamespaces(filePath, secretStore, void 0);
|
|
4927
|
+
}
|
|
4928
|
+
return namespace;
|
|
4929
|
+
}
|
|
4907
4930
|
if (!locked) {
|
|
4908
4931
|
throw new SecretStoreUnavailableError(
|
|
4909
4932
|
`Could not save OAuth state: ${filePath} predates per-state-file secret namespaces, and migrating its secret-store entries needs the file lock, which is unavailable here (see the lock warning above). Migrating without it could lose credentials if two processes migrate at once. Nothing was changed; make the lock directory writable and retry \u2014 or, if you cannot, clear this file's stored OAuth state and re-authorize (clearing does not migrate, and the fresh state file mints its namespace without the lock).`
|
|
@@ -4918,10 +4941,24 @@ async function adoptSecretsNamespace(filePath, secretStore, locked) {
|
|
|
4918
4941
|
);
|
|
4919
4942
|
const touched = [];
|
|
4920
4943
|
try {
|
|
4921
|
-
|
|
4922
|
-
|
|
4923
|
-
|
|
4924
|
-
|
|
4944
|
+
const values = await readEntrySecrets(
|
|
4945
|
+
snapshot,
|
|
4946
|
+
secretStore,
|
|
4947
|
+
void 0,
|
|
4948
|
+
await recordedNamespaces(filePath)
|
|
4949
|
+
);
|
|
4950
|
+
const copies = [
|
|
4951
|
+
...[...values.servers].map(([url, secrets]) => ({
|
|
4952
|
+
scopedId: oauthSecretServerId(url, namespace),
|
|
4953
|
+
secrets
|
|
4954
|
+
})),
|
|
4955
|
+
...[...values.idpSessions].map(([issuer, secrets]) => ({
|
|
4956
|
+
scopedId: oauthIdpSecretServerId(issuer, namespace),
|
|
4957
|
+
secrets
|
|
4958
|
+
}))
|
|
4959
|
+
];
|
|
4960
|
+
for (const { scopedId, secrets } of copies) {
|
|
4961
|
+
for (const [field, value] of Object.entries(secrets)) {
|
|
4925
4962
|
touched.push({ serverId: scopedId, field, value: null });
|
|
4926
4963
|
await secretStore.set(scopedId, field, value);
|
|
4927
4964
|
}
|
|
@@ -4931,7 +4968,14 @@ async function adoptSecretsNamespace(filePath, secretStore, locked) {
|
|
|
4931
4968
|
await restoreSecretFields(secretStore, touched, warnRestoreFailure);
|
|
4932
4969
|
throw error;
|
|
4933
4970
|
}
|
|
4934
|
-
|
|
4971
|
+
await purgeSupersededNamespaces(filePath, secretStore, namespace);
|
|
4972
|
+
const legacyIds = [
|
|
4973
|
+
...Object.keys(snapshot.servers).map((url) => oauthSecretServerId(url)),
|
|
4974
|
+
...Object.keys(snapshot.idpSessions).map(
|
|
4975
|
+
(issuer) => oauthIdpSecretServerId(issuer)
|
|
4976
|
+
)
|
|
4977
|
+
];
|
|
4978
|
+
for (const legacyId of legacyIds) {
|
|
4935
4979
|
try {
|
|
4936
4980
|
await secretStore.deleteAllForServer(legacyId);
|
|
4937
4981
|
} catch (error) {
|
|
@@ -4984,6 +5028,14 @@ function preserveNonDurableSecrets(diskSecrets, secrets) {
|
|
|
4984
5028
|
return keep;
|
|
4985
5029
|
}
|
|
4986
5030
|
async function writeOAuthSections(filePath, snapshot, sections, secretStore = defaultSecretStore()) {
|
|
5031
|
+
await writeOAuthSectionsFrom(
|
|
5032
|
+
filePath,
|
|
5033
|
+
async () => snapshot,
|
|
5034
|
+
sections,
|
|
5035
|
+
secretStore
|
|
5036
|
+
);
|
|
5037
|
+
}
|
|
5038
|
+
async function writeOAuthSectionsFrom(filePath, resolveSnapshot, sections, secretStore) {
|
|
4987
5039
|
const policy = getPersistTokensPolicy();
|
|
4988
5040
|
const durable = await secretStoreIsDurable(secretStore);
|
|
4989
5041
|
await withOAuthStateLock(filePath, "save", async (locked) => {
|
|
@@ -5067,6 +5119,7 @@ async function writeOAuthSections(filePath, snapshot, sections, secretStore = de
|
|
|
5067
5119
|
unconfirmed = null;
|
|
5068
5120
|
namespace = diskNamespace;
|
|
5069
5121
|
}
|
|
5122
|
+
const snapshot = await resolveSnapshot(disk, namespace);
|
|
5070
5123
|
const effective = sections ? {
|
|
5071
5124
|
servers: sections.servers && [...new Set(sections.servers)],
|
|
5072
5125
|
idpSessions: sections.idpSessions && [
|
|
@@ -5100,7 +5153,8 @@ async function writeOAuthSections(filePath, snapshot, sections, secretStore = de
|
|
|
5100
5153
|
const candidates = [
|
|
5101
5154
|
.../* @__PURE__ */ new Set([
|
|
5102
5155
|
...serverSecretFields(getOwnEntry(disk?.servers, url)),
|
|
5103
|
-
...serverSecretFields(next)
|
|
5156
|
+
...serverSecretFields(next),
|
|
5157
|
+
...[...restoreBaseline.values()].filter((prior) => prior.serverId === serverId).map(({ field }) => field)
|
|
5104
5158
|
])
|
|
5105
5159
|
];
|
|
5106
5160
|
const entryPrior = await snapshotSecretFields(
|
|
@@ -5109,8 +5163,12 @@ async function writeOAuthSections(filePath, snapshot, sections, secretStore = de
|
|
|
5109
5163
|
candidates
|
|
5110
5164
|
);
|
|
5111
5165
|
priorSecrets.push(...entryPrior);
|
|
5166
|
+
const entryPriorValue = new Map(
|
|
5167
|
+
entryPrior.map(({ field, value }) => [field, value])
|
|
5168
|
+
);
|
|
5112
5169
|
if (next === void 0) {
|
|
5113
5170
|
for (const field of candidates) {
|
|
5171
|
+
if (entryPriorValue.get(field) === null) continue;
|
|
5114
5172
|
ourWrites.set(priorKey({ serverId, field }), null);
|
|
5115
5173
|
}
|
|
5116
5174
|
await secretStore.deleteAllForServer(serverId);
|
|
@@ -5125,10 +5183,9 @@ async function writeOAuthSections(filePath, snapshot, sections, secretStore = de
|
|
|
5125
5183
|
}
|
|
5126
5184
|
const { residue, secrets } = splitServerOAuthState(next, policy);
|
|
5127
5185
|
for (const field of candidates) {
|
|
5128
|
-
|
|
5129
|
-
|
|
5130
|
-
|
|
5131
|
-
);
|
|
5186
|
+
const value = secrets[field] ?? null;
|
|
5187
|
+
if (value === entryPriorValue.get(field)) continue;
|
|
5188
|
+
ourWrites.set(priorKey({ serverId, field }), value);
|
|
5132
5189
|
}
|
|
5133
5190
|
setOwnEntry(merged.servers, url, residue);
|
|
5134
5191
|
if (!durable) {
|
|
@@ -5271,26 +5328,52 @@ function secretRequestsFor(snapshot, namespace) {
|
|
|
5271
5328
|
}
|
|
5272
5329
|
return requests;
|
|
5273
5330
|
}
|
|
5274
|
-
|
|
5275
|
-
const
|
|
5276
|
-
|
|
5331
|
+
function fillMissingSecrets(target, source) {
|
|
5332
|
+
for (const [field, value] of Object.entries(source ?? {})) {
|
|
5333
|
+
if (!Object.hasOwn(target, field)) setOwnEntry(target, field, value);
|
|
5334
|
+
}
|
|
5335
|
+
}
|
|
5336
|
+
async function readEntrySecrets(snapshot, secretStore, namespace, fallbacks) {
|
|
5337
|
+
const servers = /* @__PURE__ */ new Map();
|
|
5338
|
+
const idpSessions = /* @__PURE__ */ new Map();
|
|
5339
|
+
for (const url of Object.keys(snapshot.servers)) servers.set(url, {});
|
|
5340
|
+
for (const issuer of Object.keys(snapshot.idpSessions)) {
|
|
5341
|
+
idpSessions.set(issuer, {});
|
|
5342
|
+
}
|
|
5343
|
+
for (const source of [namespace, ...fallbacks]) {
|
|
5344
|
+
const requests = secretRequestsFor(snapshot, source);
|
|
5345
|
+
if (requests.length === 0) break;
|
|
5346
|
+
const values = await secretStoreGetManyStrict(secretStore, requests);
|
|
5347
|
+
for (const [url, target] of servers) {
|
|
5348
|
+
fillMissingSecrets(target, values[oauthSecretServerId(url, source)]);
|
|
5349
|
+
}
|
|
5350
|
+
for (const [issuer, target] of idpSessions) {
|
|
5351
|
+
fillMissingSecrets(
|
|
5352
|
+
target,
|
|
5353
|
+
values[oauthIdpSecretServerId(issuer, source)]
|
|
5354
|
+
);
|
|
5355
|
+
}
|
|
5356
|
+
}
|
|
5357
|
+
return { servers, idpSessions };
|
|
5358
|
+
}
|
|
5359
|
+
async function joinSnapshot(snapshot, secretStore, namespace, fallbacks) {
|
|
5360
|
+
const values = await readEntrySecrets(
|
|
5361
|
+
snapshot,
|
|
5362
|
+
secretStore,
|
|
5363
|
+
namespace,
|
|
5364
|
+
fallbacks
|
|
5365
|
+
);
|
|
5277
5366
|
return {
|
|
5278
5367
|
servers: Object.fromEntries(
|
|
5279
5368
|
Object.entries(snapshot.servers).map(([url, state]) => [
|
|
5280
5369
|
url,
|
|
5281
|
-
joinServerOAuthState(
|
|
5282
|
-
state,
|
|
5283
|
-
values[oauthSecretServerId(url, namespace)] ?? {}
|
|
5284
|
-
)
|
|
5370
|
+
joinServerOAuthState(state, values.servers.get(url) ?? {})
|
|
5285
5371
|
])
|
|
5286
5372
|
),
|
|
5287
5373
|
idpSessions: Object.fromEntries(
|
|
5288
5374
|
Object.entries(snapshot.idpSessions).map(([issuer, session]) => [
|
|
5289
5375
|
issuer,
|
|
5290
|
-
joinIdpSession(
|
|
5291
|
-
session,
|
|
5292
|
-
values[oauthIdpSecretServerId(issuer, namespace)] ?? {}
|
|
5293
|
-
)
|
|
5376
|
+
joinIdpSession(session, values.idpSessions.get(issuer) ?? {})
|
|
5294
5377
|
])
|
|
5295
5378
|
)
|
|
5296
5379
|
};
|
|
@@ -5357,7 +5440,9 @@ async function readOAuthStore(filePath, secretStore = defaultSecretStore()) {
|
|
|
5357
5440
|
warnMigrationFailure(error);
|
|
5358
5441
|
}
|
|
5359
5442
|
}
|
|
5360
|
-
|
|
5443
|
+
const namespace = parseSecretsNamespace(raw);
|
|
5444
|
+
const fallbacks = namespace === void 0 ? await recordedNamespaces(filePath) : [];
|
|
5445
|
+
return joinSnapshot(snapshot, secretStore, namespace, fallbacks);
|
|
5361
5446
|
});
|
|
5362
5447
|
}
|
|
5363
5448
|
async function removeOAuthStore(filePath, secretStore = defaultSecretStore()) {
|
|
@@ -6714,6 +6799,22 @@ function createRemoteApp(options) {
|
|
|
6714
6799
|
});
|
|
6715
6800
|
}
|
|
6716
6801
|
const msg = err instanceof Error ? err.message : String(err);
|
|
6802
|
+
if (err instanceof SdkHttpError) {
|
|
6803
|
+
const text = err.data.text;
|
|
6804
|
+
return c.json({
|
|
6805
|
+
ok: false,
|
|
6806
|
+
kind: "transport_error",
|
|
6807
|
+
error: msg,
|
|
6808
|
+
http: {
|
|
6809
|
+
code: err.code,
|
|
6810
|
+
status: err.status,
|
|
6811
|
+
...err.statusText !== void 0 && {
|
|
6812
|
+
statusText: err.statusText
|
|
6813
|
+
},
|
|
6814
|
+
...typeof text === "string" && { text }
|
|
6815
|
+
}
|
|
6816
|
+
});
|
|
6817
|
+
}
|
|
6717
6818
|
return c.json({ ok: false, kind: "transport_error", error: msg });
|
|
6718
6819
|
} finally {
|
|
6719
6820
|
clientSignal.removeEventListener("abort", onClientDisconnect);
|
|
@@ -8768,7 +8869,11 @@ async function runWeb(argv) {
|
|
|
8768
8869
|
preArgs = argv.slice(0, argSeparatorIndex);
|
|
8769
8870
|
postArgs = argv.slice(argSeparatorIndex + 1);
|
|
8770
8871
|
}
|
|
8771
|
-
program.name("mcp-inspector-web").description("Web UI for MCP Inspector").
|
|
8872
|
+
program.name("mcp-inspector-web").description("Web UI for MCP Inspector").version(
|
|
8873
|
+
readInspectorVersionSafe(import.meta.url) ?? "unknown",
|
|
8874
|
+
"-V, --version",
|
|
8875
|
+
"Print the Inspector version"
|
|
8876
|
+
).allowExcessArguments().allowUnknownOption().option(
|
|
8772
8877
|
"-e <env>",
|
|
8773
8878
|
"environment variables in KEY=VALUE format",
|
|
8774
8879
|
parseKeyValuePair,
|