@modelcontextprotocol/inspector 2.1.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.
- package/README.md +69 -16
- package/clients/cli/build/index.js +304 -38
- package/clients/tui/build/index.js +2737 -656
- package/clients/web/build/index.js +112 -9
- package/clients/web/dist/assets/index-BrCIxo3Q.js +242 -0
- package/clients/web/dist/assets/{prism-light-CCFFzO_n.js → prism-light-CWdjx3zH.js} +1 -1
- package/clients/web/dist/index.html +1 -1
- package/clients/web/static/sandbox_proxy.html +237 -0
- package/package.json +13 -14
- package/clients/web/dist/assets/index-CyPh2pEE.js +0 -300
|
@@ -1680,6 +1680,9 @@ var RemoteSession = class _RemoteSession {
|
|
|
1680
1680
|
static AUTH_HTTP_ECHO_SUPPRESS_MS = 3e4;
|
|
1681
1681
|
requestWaits = /* @__PURE__ */ new Map();
|
|
1682
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}$/;
|
|
1683
1686
|
constructor(sessionId) {
|
|
1684
1687
|
this.sessionId = sessionId;
|
|
1685
1688
|
}
|
|
@@ -1695,6 +1698,27 @@ var RemoteSession = class _RemoteSession {
|
|
|
1695
1698
|
setTransport(transport) {
|
|
1696
1699
|
this.transport = transport;
|
|
1697
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
|
+
}
|
|
1698
1722
|
setEventConsumer(consumer) {
|
|
1699
1723
|
this.eventConsumer = consumer;
|
|
1700
1724
|
while (this.eventQueue.length > 0) {
|
|
@@ -1953,8 +1977,42 @@ function createRemoteAuthProvider(initialAuthState) {
|
|
|
1953
1977
|
}
|
|
1954
1978
|
|
|
1955
1979
|
// ../../core/auth/node/secret-store.ts
|
|
1956
|
-
import { AsyncEntry, findCredentialsAsync } from "@napi-rs/keyring";
|
|
1957
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
|
+
};
|
|
1958
2016
|
function parseAccount(account) {
|
|
1959
2017
|
const idx = account.indexOf(":");
|
|
1960
2018
|
if (idx <= 0 || idx === account.length - 1) return null;
|
|
@@ -1966,16 +2024,31 @@ function parseAccount(account) {
|
|
|
1966
2024
|
var buildAccount = (serverId, field) => `${serverId}:${field}`;
|
|
1967
2025
|
var KeychainUnavailableError = class extends Error {
|
|
1968
2026
|
constructor(cause) {
|
|
2027
|
+
const message = cause instanceof Error ? cause.message : String(cause);
|
|
1969
2028
|
super(
|
|
1970
|
-
`OS keychain is not available.
|
|
2029
|
+
`OS keychain is not available. ${hintFor(cause, message)} Underlying error: ${message}`
|
|
1971
2030
|
);
|
|
1972
2031
|
this.name = "KeychainUnavailableError";
|
|
1973
2032
|
}
|
|
1974
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
|
+
};
|
|
1975
2043
|
var KeyringSecretStore = class {
|
|
1976
2044
|
async get(serverId, field) {
|
|
1977
|
-
const entry = new AsyncEntry(SERVICE_NAME, buildAccount(serverId, field));
|
|
1978
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
|
+
);
|
|
1979
2052
|
const v = await entry.getPassword();
|
|
1980
2053
|
return v ?? null;
|
|
1981
2054
|
} catch {
|
|
@@ -1983,16 +2056,27 @@ var KeyringSecretStore = class {
|
|
|
1983
2056
|
}
|
|
1984
2057
|
}
|
|
1985
2058
|
async set(serverId, field, value) {
|
|
1986
|
-
const entry = new AsyncEntry(SERVICE_NAME, buildAccount(serverId, field));
|
|
1987
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
|
+
);
|
|
1988
2066
|
await entry.setPassword(value);
|
|
1989
2067
|
} catch (err) {
|
|
2068
|
+
if (err instanceof KeychainUnavailableError) throw err;
|
|
1990
2069
|
throw new KeychainUnavailableError(err);
|
|
1991
2070
|
}
|
|
1992
2071
|
}
|
|
1993
2072
|
async delete(serverId, field) {
|
|
1994
|
-
const entry = new AsyncEntry(SERVICE_NAME, buildAccount(serverId, field));
|
|
1995
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
|
+
);
|
|
1996
2080
|
await entry.deleteCredential();
|
|
1997
2081
|
} catch {
|
|
1998
2082
|
}
|
|
@@ -2000,7 +2084,9 @@ var KeyringSecretStore = class {
|
|
|
2000
2084
|
async deleteAllForServer(serverId) {
|
|
2001
2085
|
let creds;
|
|
2002
2086
|
try {
|
|
2003
|
-
|
|
2087
|
+
const keyring = await loadKeyring();
|
|
2088
|
+
if (!keyring.ok) return;
|
|
2089
|
+
creds = await keyring.mod.findCredentialsAsync(SERVICE_NAME);
|
|
2004
2090
|
} catch {
|
|
2005
2091
|
return;
|
|
2006
2092
|
}
|
|
@@ -2232,6 +2318,7 @@ async function deleteClientConfigStore(filePath, secretStore) {
|
|
|
2232
2318
|
|
|
2233
2319
|
// ../../core/mcp/remote/node/server.ts
|
|
2234
2320
|
import { ZodError as ZodError2 } from "zod";
|
|
2321
|
+
var SSE_PRIMING_COMMENT = ":\n\n";
|
|
2235
2322
|
function createOriginMiddleware(allowedOrigins) {
|
|
2236
2323
|
return async (c, next) => {
|
|
2237
2324
|
if (!allowedOrigins || allowedOrigins.length === 0) {
|
|
@@ -2571,7 +2658,7 @@ function createRemoteApp(options) {
|
|
|
2571
2658
|
} catch {
|
|
2572
2659
|
return c.json({ error: "Invalid JSON body" }, 400);
|
|
2573
2660
|
}
|
|
2574
|
-
const { sessionId, message, relatedRequestId, headers } = body;
|
|
2661
|
+
const { sessionId, message, relatedRequestId, headers, protocolVersion } = body;
|
|
2575
2662
|
if (!sessionId || !message) {
|
|
2576
2663
|
return c.json({ error: "Missing sessionId or message" }, 400);
|
|
2577
2664
|
}
|
|
@@ -2583,6 +2670,7 @@ function createRemoteApp(options) {
|
|
|
2583
2670
|
const errorMsg = session.getTransportError() || "Transport closed";
|
|
2584
2671
|
return c.json({ ok: false, kind: "transport_error", error: errorMsg });
|
|
2585
2672
|
}
|
|
2673
|
+
session.applyProtocolVersion(protocolVersion);
|
|
2586
2674
|
session.beginSend();
|
|
2587
2675
|
const requestId = requestIdForSendWait(message);
|
|
2588
2676
|
let responseWait;
|
|
@@ -2642,6 +2730,7 @@ function createRemoteApp(options) {
|
|
|
2642
2730
|
sessions.delete(sessionId);
|
|
2643
2731
|
return;
|
|
2644
2732
|
}
|
|
2733
|
+
await stream.write(SSE_PRIMING_COMMENT);
|
|
2645
2734
|
stream.onAbort(() => {
|
|
2646
2735
|
const shouldCleanup = session.clearEventConsumer();
|
|
2647
2736
|
stream.close();
|
|
@@ -3612,6 +3701,7 @@ function createRemoteApp(options) {
|
|
|
3612
3701
|
serverEventSubscribers.add(send);
|
|
3613
3702
|
ensureWatcher();
|
|
3614
3703
|
}
|
|
3704
|
+
await stream.write(SSE_PRIMING_COMMENT);
|
|
3615
3705
|
stream.onAbort(() => {
|
|
3616
3706
|
serverEventSubscribers.delete(send);
|
|
3617
3707
|
void maybeStopWatcher();
|
|
@@ -3852,7 +3942,9 @@ var NODE_ONLY_OPTIMIZE_DEPS_EXCLUDE = [
|
|
|
3852
3942
|
"which",
|
|
3853
3943
|
// `@napi-rs/keyring` is loaded only inside
|
|
3854
3944
|
// `core/auth/node/secret-store.ts` from the Hono `/api/servers`
|
|
3855
|
-
// handlers
|
|
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
|
|
3856
3948
|
// excluding it keeps Vite's dep scanner from chasing into the
|
|
3857
3949
|
// platform-specific binaries during dev startup.
|
|
3858
3950
|
"@napi-rs/keyring"
|
|
@@ -3947,9 +4039,20 @@ function vitestSharedPaths(clientDir) {
|
|
|
3947
4039
|
find: /^@napi-rs\/keyring$/,
|
|
3948
4040
|
replacement: path2.resolve(dirname8, "node_modules/@napi-rs/keyring")
|
|
3949
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.
|
|
3950
4053
|
{
|
|
3951
4054
|
find: /^express$/,
|
|
3952
|
-
replacement: path2.resolve(
|
|
4055
|
+
replacement: path2.resolve(repoRoot, "node_modules/express")
|
|
3953
4056
|
},
|
|
3954
4057
|
{
|
|
3955
4058
|
find: /^yaml$/,
|