@modelcontextprotocol/inspector 2.14.0 → 2.15.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 +4906 -2205
- package/clients/mcpdo/build/mcp-bin.js +891 -180
- package/clients/mcpdo/build/mcpdod.js +2 -3
- package/clients/mcpdo/build/{server-Dv339Ejx.js → server-B-CUVIBj.js} +2750 -1039
- package/clients/tui/build/index.js +1354 -533
- package/clients/web/build/index.js +239 -159
- package/clients/web/dist/assets/index-BZ94R6fh.js +1599 -0
- package/clients/web/dist/assets/{index-Dval2a7I.css → index-tBehH5iF.css} +1 -1
- package/clients/web/dist/assets/{prism-light-CoVRM9R2.js → prism-light-Crps--E8.js} +1 -1
- package/clients/web/dist/assets/worker-json-DEGsizKa.js +7859 -0
- package/clients/web/dist/index.html +2 -2
- package/package.json +4 -1
- package/skills/mcpdo/SKILL.md +50 -3
- package/clients/web/dist/assets/index-BCud2uMC.js +0 -1594
- package/clients/web/dist/assets/worker-json-8CDXFfik.js +0 -1631
|
@@ -37,20 +37,18 @@ import { createServer as createServer$2 } from "node:https";
|
|
|
37
37
|
import { serve } from "@hono/node-server";
|
|
38
38
|
import { serveStatic } from "@hono/node-server/serve-static";
|
|
39
39
|
import { spawnSync } from "node:child_process";
|
|
40
|
-
|
|
40
|
+
// ../../core/storage/store-id.ts
|
|
41
41
|
function validateStoreId(storeId) {
|
|
42
42
|
return /^[a-zA-Z0-9_-]+$/.test(storeId) && storeId !== "__proto__";
|
|
43
43
|
}
|
|
44
|
-
|
|
45
|
-
//#region ../../core/storage/store-serialize.ts
|
|
44
|
+
// ../../core/storage/store-serialize.ts
|
|
46
45
|
function serializeStore(data) {
|
|
47
46
|
return JSON.stringify(data, null, 2);
|
|
48
47
|
}
|
|
49
48
|
function parseStore(raw) {
|
|
50
49
|
return JSON.parse(raw);
|
|
51
50
|
}
|
|
52
|
-
|
|
53
|
-
//#region ../../core/storage/store-io.ts
|
|
51
|
+
// ../../core/storage/store-io.ts
|
|
54
52
|
function getDefaultStorageDir() {
|
|
55
53
|
const homeDir = process.env.HOME || process.env.USERPROFILE || ".";
|
|
56
54
|
return path$1.join(homeDir, ".mcp-inspector", "storage");
|
|
@@ -96,8 +94,7 @@ async function deleteStoreFile(filePath) {
|
|
|
96
94
|
if (error.code !== "ENOENT") throw error;
|
|
97
95
|
}
|
|
98
96
|
}
|
|
99
|
-
|
|
100
|
-
//#region ../../core/mcp/types.ts
|
|
97
|
+
// ../../core/mcp/types.ts
|
|
101
98
|
const DEFAULT_TASK_TTL_MS = 6e4;
|
|
102
99
|
const DEFAULT_MAX_FETCH_REQUESTS = 1e3;
|
|
103
100
|
const MODERN_LOG_LEVELS = [
|
|
@@ -114,8 +111,7 @@ const MODERN_LOG_LEVELS = [
|
|
|
114
111
|
function isModernLogLevel(value) {
|
|
115
112
|
return typeof value === "string" && MODERN_LOG_LEVELS.includes(value);
|
|
116
113
|
}
|
|
117
|
-
|
|
118
|
-
//#region ../../core/extension/skills/schemas.ts
|
|
114
|
+
// ../../core/extension/skills/schemas.ts
|
|
119
115
|
const CACHE_FIELDS = {
|
|
120
116
|
ttlMs: ReadResourceDirectoryResultSchema.shape.ttlMs,
|
|
121
117
|
cacheScope: SkillCacheScopeSchema
|
|
@@ -227,13 +223,11 @@ SKILL_LIMITS.bytes;
|
|
|
227
223
|
function isSkillCatalogLimit(value) {
|
|
228
224
|
return typeof value === "number" && Number.isSafeInteger(value) && value > 0;
|
|
229
225
|
}
|
|
230
|
-
|
|
231
|
-
//#region ../../core/auth/secret-fields.ts
|
|
226
|
+
// ../../core/auth/secret-fields.ts
|
|
232
227
|
const SECRET_FIELD_OAUTH_CLIENT_SECRET = "oauth-client-secret";
|
|
233
228
|
const SECRET_FIELD_IDP_CLIENT_SECRET = "idp-client-secret";
|
|
234
229
|
const envSecretField = (envKey) => `env:${envKey}`;
|
|
235
|
-
|
|
236
|
-
//#region ../../core/json/jsonUtils.ts
|
|
230
|
+
// ../../core/json/jsonUtils.ts
|
|
237
231
|
function isSerializableJson(value) {
|
|
238
232
|
if (value === null) return true;
|
|
239
233
|
switch (typeof value) {
|
|
@@ -247,8 +241,7 @@ function isSerializableJson(value) {
|
|
|
247
241
|
function toRecord(value) {
|
|
248
242
|
return value;
|
|
249
243
|
}
|
|
250
|
-
|
|
251
|
-
//#region ../../core/mcp/serverList.ts
|
|
244
|
+
// ../../core/mcp/serverList.ts
|
|
252
245
|
const VALID_SERVER_TYPES = /* @__PURE__ */ new Set([
|
|
253
246
|
"stdio",
|
|
254
247
|
"sse",
|
|
@@ -587,8 +580,7 @@ function defaultSeedConfig(tempDir) {
|
|
|
587
580
|
}
|
|
588
581
|
} };
|
|
589
582
|
}
|
|
590
|
-
|
|
591
|
-
//#region ../../core/storage/own-entry.ts
|
|
583
|
+
// ../../core/storage/own-entry.ts
|
|
592
584
|
function setOwnEntry(map, key, value) {
|
|
593
585
|
Object.defineProperty(map, key, {
|
|
594
586
|
value,
|
|
@@ -600,8 +592,7 @@ function setOwnEntry(map, key, value) {
|
|
|
600
592
|
function getOwnEntry(map, key) {
|
|
601
593
|
return map !== void 0 && Object.hasOwn(map, key) ? map[key] : void 0;
|
|
602
594
|
}
|
|
603
|
-
|
|
604
|
-
//#region ../../core/mcp/node/config.ts
|
|
595
|
+
// ../../core/mcp/node/config.ts
|
|
605
596
|
function parseKeyValuePair(value, previous = {}) {
|
|
606
597
|
const parts = value.split("=");
|
|
607
598
|
const key = parts[0] ?? "";
|
|
@@ -770,8 +761,7 @@ function resolveServerConfigs(options, mode) {
|
|
|
770
761
|
}
|
|
771
762
|
const API_SERVER_ENV_VARS = { AUTH_TOKEN: "MCP_INSPECTOR_API_TOKEN" };
|
|
772
763
|
const INSPECTOR_API_TOKEN_GLOBAL = "__INSPECTOR_API_TOKEN__";
|
|
773
|
-
|
|
774
|
-
//#region ../../core/auth/secret-storage-info.ts
|
|
764
|
+
// ../../core/auth/secret-storage-info.ts
|
|
775
765
|
function secretStorageLabel(info) {
|
|
776
766
|
switch (info.kind) {
|
|
777
767
|
case "keyring": return "OS keychain";
|
|
@@ -798,8 +788,7 @@ function secretStorageSummary(info) {
|
|
|
798
788
|
if (info.reason === "fallback") parts.push("— fell back from the OS keychain");
|
|
799
789
|
return parts.join(" ");
|
|
800
790
|
}
|
|
801
|
-
|
|
802
|
-
//#region ../../core/node/version.ts
|
|
791
|
+
// ../../core/node/version.ts
|
|
803
792
|
const ROOT_PACKAGE_NAME = "@modelcontextprotocol/inspector";
|
|
804
793
|
function readInspectorVersion(callerUrl) {
|
|
805
794
|
const startDir = dirname(fileURLToPath(callerUrl));
|
|
@@ -823,8 +812,7 @@ function readInspectorVersionSafe(callerUrl) {
|
|
|
823
812
|
return;
|
|
824
813
|
}
|
|
825
814
|
}
|
|
826
|
-
|
|
827
|
-
//#region ../../core/node/openUrl.ts
|
|
815
|
+
// ../../core/node/openUrl.ts
|
|
828
816
|
const OPEN_URL_TIMEOUT_MS = 5e3;
|
|
829
817
|
const OPENABLE_PROTOCOLS = /* @__PURE__ */ new Set(["http:", "https:"]);
|
|
830
818
|
function toOpenableHref(href) {
|
|
@@ -852,8 +840,7 @@ async function openUrl(url, timeoutMs = OPEN_URL_TIMEOUT_MS) {
|
|
|
852
840
|
clearTimeout(timer);
|
|
853
841
|
}
|
|
854
842
|
}
|
|
855
|
-
|
|
856
|
-
//#region ../../core/node/hostUrl.ts
|
|
843
|
+
// ../../core/node/hostUrl.ts
|
|
857
844
|
function stripBrackets(host) {
|
|
858
845
|
return host.replace(/^\[(.*)\]$/, "$1");
|
|
859
846
|
}
|
|
@@ -906,8 +893,7 @@ function isLoopbackHost(host) {
|
|
|
906
893
|
const h = canonicalUrlHost(host).replace(/\.$/, "");
|
|
907
894
|
return h === "localhost" || h === "[::1]" || /^127(\.(25[0-5]|2[0-4]\d|1?\d?\d)){3}$/.test(h);
|
|
908
895
|
}
|
|
909
|
-
|
|
910
|
-
//#region server/resolve-bind-host.ts
|
|
896
|
+
// server/resolve-bind-host.ts
|
|
911
897
|
const BIND_ALL_INTERFACES_ENV = "DANGEROUSLY_BIND_ALL_INTERFACES";
|
|
912
898
|
function isEnvFlagEnabled(value) {
|
|
913
899
|
const v = value?.trim().toLowerCase();
|
|
@@ -923,8 +909,7 @@ function resolveBindHostname(env = process.env) {
|
|
|
923
909
|
}
|
|
924
910
|
return stripBrackets(host);
|
|
925
911
|
}
|
|
926
|
-
|
|
927
|
-
//#region server/host-allow-list.ts
|
|
912
|
+
// server/host-allow-list.ts
|
|
928
913
|
const ALLOWED_HOSTS_ENV = "MCP_INSPECTOR_ALLOWED_HOSTS";
|
|
929
914
|
const HOSTNAME_RE = /^[a-z0-9._-]+$/;
|
|
930
915
|
function normalizeHostname(hostname) {
|
|
@@ -1006,8 +991,7 @@ function hostAllowListMiddleware(options) {
|
|
|
1006
991
|
await next();
|
|
1007
992
|
};
|
|
1008
993
|
}
|
|
1009
|
-
|
|
1010
|
-
//#region server/sandbox-controller.ts
|
|
994
|
+
// server/sandbox-controller.ts
|
|
1011
995
|
const __dirname$3 = dirname(fileURLToPath(import.meta.url));
|
|
1012
996
|
function isSandboxPath(url) {
|
|
1013
997
|
const path = (url ?? "").split("?", 1)[0];
|
|
@@ -1328,8 +1312,7 @@ function createAppOriginController(options) {
|
|
|
1328
1312
|
}
|
|
1329
1313
|
};
|
|
1330
1314
|
}
|
|
1331
|
-
|
|
1332
|
-
//#region server/public-address.ts
|
|
1315
|
+
// server/public-address.ts
|
|
1333
1316
|
const SANDBOX_FULL_ADDRESS_ENV = "MCP_SANDBOX_FULL_ADDRESS";
|
|
1334
1317
|
const APP_ORIGIN_FULL_ADDRESS_ENV = "MCP_APP_ORIGIN_FULL_ADDRESS";
|
|
1335
1318
|
const SANDBOX_PATH = "/sandbox";
|
|
@@ -1398,8 +1381,7 @@ function resolveAppOriginPublicOrigin(raw, allowedOrigins, sandboxUrl) {
|
|
|
1398
1381
|
warnMixedContent(APP_ORIGIN_FULL_ADDRESS_ENV, url, allowedOrigins);
|
|
1399
1382
|
return url.origin;
|
|
1400
1383
|
}
|
|
1401
|
-
|
|
1402
|
-
//#region server/tls.ts
|
|
1384
|
+
// server/tls.ts
|
|
1403
1385
|
const TLS_KEY_PATH_ENV = "INSPECTOR_TLS_KEY_PATH";
|
|
1404
1386
|
const TLS_CERT_PATH_ENV = "INSPECTOR_TLS_CERT_PATH";
|
|
1405
1387
|
function webScheme(tls) {
|
|
@@ -1435,8 +1417,7 @@ function resolveTlsConfig(env = process.env) {
|
|
|
1435
1417
|
cert
|
|
1436
1418
|
};
|
|
1437
1419
|
}
|
|
1438
|
-
|
|
1439
|
-
//#region server/web-server-config.ts
|
|
1420
|
+
// server/web-server-config.ts
|
|
1440
1421
|
const inspectorVersion = readInspectorVersionSafe(import.meta.url);
|
|
1441
1422
|
function defaultEnvironmentFromProcess(extra) {
|
|
1442
1423
|
const keys = process.platform === "win32" ? [
|
|
@@ -1625,8 +1606,7 @@ function resolveAutoOpen() {
|
|
|
1625
1606
|
if (flag === "false") return false;
|
|
1626
1607
|
return !process.env.VITEST;
|
|
1627
1608
|
}
|
|
1628
|
-
|
|
1629
|
-
//#region ../../core/mcp/config.ts
|
|
1609
|
+
// ../../core/mcp/config.ts
|
|
1630
1610
|
function getServerType(config) {
|
|
1631
1611
|
if (!("type" in config) || config.type === void 0) return "stdio";
|
|
1632
1612
|
const type = config.type;
|
|
@@ -1635,8 +1615,7 @@ function getServerType(config) {
|
|
|
1635
1615
|
if (type === "streamable-http") return "streamable-http";
|
|
1636
1616
|
throw new Error(`Invalid server type: ${type}. Valid types are: stdio, sse, streamable-http`);
|
|
1637
1617
|
}
|
|
1638
|
-
|
|
1639
|
-
//#region ../../core/mcp/fetchTracking.ts
|
|
1618
|
+
// ../../core/mcp/fetchTracking.ts
|
|
1640
1619
|
const SENSITIVE_HEADERS = /* @__PURE__ */ new Set([
|
|
1641
1620
|
"authorization",
|
|
1642
1621
|
"cookie",
|
|
@@ -1745,14 +1724,15 @@ function isLongLivedStreamResponse(method, contentType) {
|
|
|
1745
1724
|
}
|
|
1746
1725
|
const CHALLENGE_BODY_MAX_BYTES = 65536;
|
|
1747
1726
|
const CHALLENGE_BODY_READ_MS = 2e3;
|
|
1727
|
+
const TRACKED_BODY_MAX_BYTES = 1048576;
|
|
1748
1728
|
const TRUNCATED_BODY_SUFFIX = "\n[truncated]";
|
|
1749
1729
|
const WITHHELD_TRUNCATED_JSON_BODY = "[truncated JSON body withheld: an incomplete body cannot be redacted]";
|
|
1750
|
-
function
|
|
1730
|
+
function recordableBody(read, contentType) {
|
|
1751
1731
|
if (!read.truncated) return redactBody(read.text, contentType);
|
|
1752
1732
|
if ((contentType ?? "").toLowerCase().includes("json") || /^\s*[{[]/.test(read.text)) return WITHHELD_TRUNCATED_JSON_BODY;
|
|
1753
1733
|
return redactBody(read.text, contentType) + TRUNCATED_BODY_SUFFIX;
|
|
1754
1734
|
}
|
|
1755
|
-
async function
|
|
1735
|
+
async function readCappedBody(response, maxBytes, timeoutMs) {
|
|
1756
1736
|
let reader;
|
|
1757
1737
|
try {
|
|
1758
1738
|
const body = response.clone().body;
|
|
@@ -1766,20 +1746,23 @@ async function readChallengeBody(response) {
|
|
|
1766
1746
|
let text = "";
|
|
1767
1747
|
let bytes = 0;
|
|
1768
1748
|
let timer;
|
|
1769
|
-
const deadline = new Promise((resolve) => {
|
|
1770
|
-
timer = setTimeout(() => resolve("timeout"),
|
|
1749
|
+
const deadline = timeoutMs === void 0 ? void 0 : new Promise((resolve) => {
|
|
1750
|
+
timer = setTimeout(() => resolve("timeout"), timeoutMs);
|
|
1771
1751
|
});
|
|
1772
1752
|
try {
|
|
1773
|
-
|
|
1774
|
-
const next = await Promise.race([reader.read(), deadline]);
|
|
1753
|
+
for (;;) {
|
|
1754
|
+
const next = await (deadline ? Promise.race([reader.read(), deadline]) : reader.read());
|
|
1775
1755
|
if (next === "timeout") break;
|
|
1776
1756
|
if (next.done) return {
|
|
1777
1757
|
text: text + decoder.decode(),
|
|
1778
1758
|
truncated: false
|
|
1779
1759
|
};
|
|
1780
|
-
|
|
1760
|
+
if (next.value.byteLength === 0) continue;
|
|
1761
|
+
if (bytes >= maxBytes) break;
|
|
1762
|
+
const chunk = next.value.subarray(0, maxBytes - bytes);
|
|
1781
1763
|
bytes += chunk.byteLength;
|
|
1782
1764
|
text += decoder.decode(chunk, { stream: true });
|
|
1765
|
+
if (chunk.byteLength < next.value.byteLength) break;
|
|
1783
1766
|
}
|
|
1784
1767
|
reader.cancel().catch(() => {});
|
|
1785
1768
|
return {
|
|
@@ -1792,6 +1775,9 @@ async function readChallengeBody(response) {
|
|
|
1792
1775
|
clearTimeout(timer);
|
|
1793
1776
|
}
|
|
1794
1777
|
}
|
|
1778
|
+
function readChallengeBody(response) {
|
|
1779
|
+
return readCappedBody(response, CHALLENGE_BODY_MAX_BYTES, CHALLENGE_BODY_READ_MS);
|
|
1780
|
+
}
|
|
1795
1781
|
function longLivedStreamFraming(contentType) {
|
|
1796
1782
|
return contentType?.toLowerCase().includes("application/x-ndjson") ? "ndjson" : "sse";
|
|
1797
1783
|
}
|
|
@@ -1957,7 +1943,7 @@ function createFetchTracker(baseFetch, callbacks) {
|
|
|
1957
1943
|
};
|
|
1958
1944
|
if ((responseStatus === 401 || responseStatus === 403) && response.body && !response.bodyUsed) {
|
|
1959
1945
|
const read = await readChallengeBody(response);
|
|
1960
|
-
entry.responseBody = read === void 0 ? void 0 :
|
|
1946
|
+
entry.responseBody = read === void 0 ? void 0 : recordableBody(read, response.headers.get("content-type"));
|
|
1961
1947
|
callbacks.trackRequest?.(entry);
|
|
1962
1948
|
return response;
|
|
1963
1949
|
}
|
|
@@ -1965,11 +1951,12 @@ function createFetchTracker(baseFetch, callbacks) {
|
|
|
1965
1951
|
if (isLongLivedStream && response.body && !response.bodyUsed) watchLongLivedStream(response, id, longLivedStreamFraming(response.headers.get("content-type")), callbacks);
|
|
1966
1952
|
if (!isLongLivedStream && response.body && !response.bodyUsed) {
|
|
1967
1953
|
const responseContentType = response.headers.get("content-type");
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
|
|
1954
|
+
readCappedBody(response, TRACKED_BODY_MAX_BYTES).then((read) => {
|
|
1955
|
+
if (read === void 0) return;
|
|
1956
|
+
try {
|
|
1957
|
+
callbacks.updateResponseBody?.(id, recordableBody(read, responseContentType));
|
|
1958
|
+
} catch {}
|
|
1959
|
+
});
|
|
1973
1960
|
}
|
|
1974
1961
|
return response;
|
|
1975
1962
|
};
|
|
@@ -2035,8 +2022,7 @@ function parseAuthChallengeFromResponse(response, context) {
|
|
|
2035
2022
|
}
|
|
2036
2023
|
};
|
|
2037
2024
|
}
|
|
2038
|
-
|
|
2039
|
-
//#region ../../core/mcp/node/authChallengeFetch.ts
|
|
2025
|
+
// ../../core/mcp/node/authChallengeFetch.ts
|
|
2040
2026
|
function createAuthChallengeInterceptFetch(baseFetch) {
|
|
2041
2027
|
return async (input, init) => {
|
|
2042
2028
|
const response = await baseFetch(input, init);
|
|
@@ -2057,8 +2043,7 @@ function createAuthChallengeObserverFetch(baseFetch, onChallenge) {
|
|
|
2057
2043
|
return response;
|
|
2058
2044
|
};
|
|
2059
2045
|
}
|
|
2060
|
-
|
|
2061
|
-
//#region ../../core/mcp/node/proxyFetch.ts
|
|
2046
|
+
// ../../core/mcp/node/proxyFetch.ts
|
|
2062
2047
|
function asUndiciInit(init) {
|
|
2063
2048
|
return init;
|
|
2064
2049
|
}
|
|
@@ -2148,8 +2133,7 @@ function createProxyFetch() {
|
|
|
2148
2133
|
return (await loadedProxiedFetch)(input, init);
|
|
2149
2134
|
};
|
|
2150
2135
|
}
|
|
2151
|
-
|
|
2152
|
-
//#region ../../core/auth/requestTimeout.ts
|
|
2136
|
+
// ../../core/auth/requestTimeout.ts
|
|
2153
2137
|
const OAUTH_TIMEOUT_WIRE_CODE = "oauth_request_timeout";
|
|
2154
2138
|
function readNegotiatesEndpoint(hint) {
|
|
2155
2139
|
if (!hint) return true;
|
|
@@ -2196,11 +2180,23 @@ function callerSignalOf(input, init) {
|
|
|
2196
2180
|
if (explicit !== void 0) return explicit ?? void 0;
|
|
2197
2181
|
if (typeof input !== "string" && !(input instanceof URL)) return input.signal;
|
|
2198
2182
|
}
|
|
2199
|
-
|
|
2200
|
-
//#region ../../core/mcp/node/fetchGuard.ts
|
|
2183
|
+
// ../../core/auth/fetchRefusal.ts
|
|
2201
2184
|
var FetchTargetError = class extends Error {
|
|
2202
2185
|
name = "FetchTargetError";
|
|
2186
|
+
url;
|
|
2187
|
+
constructor(message, url) {
|
|
2188
|
+
super(message);
|
|
2189
|
+
if (url !== void 0) this.url = url;
|
|
2190
|
+
}
|
|
2203
2191
|
};
|
|
2192
|
+
function findFetchTargetError(err) {
|
|
2193
|
+
let current = err;
|
|
2194
|
+
for (let depth = 0; depth < 8 && current instanceof Error; depth++) {
|
|
2195
|
+
if (current instanceof FetchTargetError) return current;
|
|
2196
|
+
current = current.cause;
|
|
2197
|
+
}
|
|
2198
|
+
}
|
|
2199
|
+
const FETCH_TARGET_REFUSED_WIRE_CODE = "fetch_target_refused";
|
|
2204
2200
|
const REDIRECT_STATUSES = /* @__PURE__ */ new Set([
|
|
2205
2201
|
301,
|
|
2206
2202
|
302,
|
|
@@ -2209,11 +2205,12 @@ const REDIRECT_STATUSES = /* @__PURE__ */ new Set([
|
|
|
2209
2205
|
308
|
|
2210
2206
|
]);
|
|
2211
2207
|
const resolveWithSystemDns = async (hostname) => (await lookup(hostname, { all: true })).map((r) => r.address);
|
|
2208
|
+
const METADATA_IPV4_ADDRESSES = /* @__PURE__ */ new Set(["100.100.100.200", "192.0.0.192"]);
|
|
2212
2209
|
function isBlockedAddress(ip) {
|
|
2213
2210
|
const kind = isIP(ip);
|
|
2214
2211
|
if (kind === 4) {
|
|
2215
2212
|
const [a, b] = ip.split(".").map(Number);
|
|
2216
|
-
return a === 169 && b === 254;
|
|
2213
|
+
return a === 169 && b === 254 || METADATA_IPV4_ADDRESSES.has(ip);
|
|
2217
2214
|
}
|
|
2218
2215
|
if (kind !== 6) return false;
|
|
2219
2216
|
const addr = ip.toLowerCase();
|
|
@@ -2376,27 +2373,35 @@ async function guardedFetch(url, init, options = {}) {
|
|
|
2376
2373
|
headers: init.headers
|
|
2377
2374
|
};
|
|
2378
2375
|
for (let hop = 0; hop <= 5; hop++) {
|
|
2379
|
-
|
|
2380
|
-
|
|
2381
|
-
|
|
2382
|
-
|
|
2383
|
-
|
|
2384
|
-
|
|
2385
|
-
|
|
2386
|
-
|
|
2376
|
+
let res;
|
|
2377
|
+
try {
|
|
2378
|
+
await assertAllowedTarget(request.url, resolveHost, viaProxy, init.signal);
|
|
2379
|
+
res = await fetchFn(request.url.href, {
|
|
2380
|
+
method: request.method,
|
|
2381
|
+
headers: new Headers(request.headers),
|
|
2382
|
+
body: request.body,
|
|
2383
|
+
signal: init.signal,
|
|
2384
|
+
redirect: "manual"
|
|
2385
|
+
});
|
|
2386
|
+
} catch (err) {
|
|
2387
|
+
stampRefusedUrl(err, request.url);
|
|
2388
|
+
throw err;
|
|
2389
|
+
}
|
|
2387
2390
|
const location = res.headers.get("location");
|
|
2388
2391
|
if (!REDIRECT_STATUSES.has(res.status) || location === null) return res;
|
|
2389
2392
|
res.body?.cancel().catch(() => {});
|
|
2390
|
-
|
|
2393
|
+
try {
|
|
2394
|
+
request = nextRedirectRequest(request, res.status, location);
|
|
2395
|
+
} catch (err) {
|
|
2396
|
+
stampRefusedUrl(err, request.url);
|
|
2397
|
+
throw err;
|
|
2398
|
+
}
|
|
2391
2399
|
}
|
|
2392
|
-
throw new FetchTargetError(`Refusing to follow more than 5 redirects
|
|
2400
|
+
throw new FetchTargetError(`Refusing to follow more than 5 redirects`, request.url.href);
|
|
2393
2401
|
}
|
|
2394
|
-
function
|
|
2395
|
-
|
|
2396
|
-
|
|
2397
|
-
if (current instanceof FetchTargetError) return current;
|
|
2398
|
-
current = current.cause;
|
|
2399
|
-
}
|
|
2402
|
+
function stampRefusedUrl(err, url) {
|
|
2403
|
+
const refused = findFetchTargetError(err);
|
|
2404
|
+
if (refused && refused.url === void 0) refused.url = url.href;
|
|
2400
2405
|
}
|
|
2401
2406
|
async function bodyText(input, init) {
|
|
2402
2407
|
const body = init?.body !== void 0 ? init.body : input instanceof Request && input.body !== null ? await input.clone().text() : void 0;
|
|
@@ -2423,8 +2428,7 @@ function createGuardedFetch(options = {}) {
|
|
|
2423
2428
|
}
|
|
2424
2429
|
};
|
|
2425
2430
|
}
|
|
2426
|
-
|
|
2427
|
-
//#region ../../core/auth/routeOAuthRequests.ts
|
|
2431
|
+
// ../../core/auth/routeOAuthRequests.ts
|
|
2428
2432
|
function isJsonRpcMessage(value) {
|
|
2429
2433
|
return typeof value === "object" && value !== null && value.jsonrpc === "2.0";
|
|
2430
2434
|
}
|
|
@@ -2448,8 +2452,7 @@ function isMcpShapedRequest(input, init) {
|
|
|
2448
2452
|
function routeOAuthRequests(mcpFetch, oauthFetch, isMcpEndpoint) {
|
|
2449
2453
|
return (input, init) => isMcpShapedRequest(input, init) && isMcpEndpoint(requestUrlOf(input), requestHeadersOf(input, init)) ? mcpFetch(input, init) : oauthFetch(input, init);
|
|
2450
2454
|
}
|
|
2451
|
-
|
|
2452
|
-
//#region ../../core/mcp/node/notificationHeadersFetch.ts
|
|
2455
|
+
// ../../core/mcp/node/notificationHeadersFetch.ts
|
|
2453
2456
|
function createNotificationHeadersFetch(baseFetch) {
|
|
2454
2457
|
return (input, init) => {
|
|
2455
2458
|
const method = modernNotificationMethod(init);
|
|
@@ -2485,8 +2488,7 @@ function modernNotificationMethod(init) {
|
|
|
2485
2488
|
function isRecord$2(value) {
|
|
2486
2489
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
2487
2490
|
}
|
|
2488
|
-
|
|
2489
|
-
//#region ../../core/mcp/node/suppressNotificationStreamFetch.ts
|
|
2491
|
+
// ../../core/mcp/node/suppressNotificationStreamFetch.ts
|
|
2490
2492
|
function createSuppressNotificationStreamFetch(baseFetch, endpoint) {
|
|
2491
2493
|
return async (input, init) => {
|
|
2492
2494
|
if (isStandaloneStreamRequest(input, init, endpoint)) return new Response(null, {
|
|
@@ -2512,8 +2514,7 @@ function isStandaloneStreamRequest(input, init, endpoint) {
|
|
|
2512
2514
|
const headers = new Headers(init?.headers ?? request?.headers);
|
|
2513
2515
|
return (headers.get("accept")?.toLowerCase() ?? "").includes("text/event-stream") && !headers.has("last-event-id");
|
|
2514
2516
|
}
|
|
2515
|
-
|
|
2516
|
-
//#region ../../core/mcp/node/transport.ts
|
|
2517
|
+
// ../../core/mcp/node/transport.ts
|
|
2517
2518
|
function headersFromSettings(settings) {
|
|
2518
2519
|
if (!settings || settings.headers.length === 0) return void 0;
|
|
2519
2520
|
const out = {};
|
|
@@ -2595,8 +2596,7 @@ function createTransportNode(config, options = {}) {
|
|
|
2595
2596
|
}) };
|
|
2596
2597
|
}
|
|
2597
2598
|
}
|
|
2598
|
-
|
|
2599
|
-
//#region ../../core/mcp/remote/node/cappedBody.ts
|
|
2599
|
+
// ../../core/mcp/remote/node/cappedBody.ts
|
|
2600
2600
|
async function readCappedText(body, maxBytes) {
|
|
2601
2601
|
const reader = body.getReader();
|
|
2602
2602
|
const decoder = new TextDecoder();
|
|
@@ -2665,6 +2665,24 @@ var RateLimitLedger = class {
|
|
|
2665
2665
|
function isRateLimitError(err) {
|
|
2666
2666
|
return SdkHttpError.isInstance(err) && err.status === 429;
|
|
2667
2667
|
}
|
|
2668
|
+
// ../../core/mcp/sessionTermination.ts
|
|
2669
|
+
function canTerminateSession(transport) {
|
|
2670
|
+
return typeof transport === "object" && transport !== null && "terminateSession" in transport && typeof transport.terminateSession === "function";
|
|
2671
|
+
}
|
|
2672
|
+
const SESSION_TERMINATE_TIMEOUT_MS = 3e3;
|
|
2673
|
+
async function terminateSessionBestEffort(transport, timeoutMs = SESSION_TERMINATE_TIMEOUT_MS) {
|
|
2674
|
+
if (!canTerminateSession(transport) || !transport.sessionId) return false;
|
|
2675
|
+
let timer;
|
|
2676
|
+
const timedOut = new Promise((resolve) => {
|
|
2677
|
+
timer = setTimeout(() => resolve(false), timeoutMs);
|
|
2678
|
+
});
|
|
2679
|
+
const terminated = new Promise((resolve) => resolve(transport.terminateSession())).then(() => true, () => false);
|
|
2680
|
+
try {
|
|
2681
|
+
return await Promise.race([terminated, timedOut]);
|
|
2682
|
+
} finally {
|
|
2683
|
+
clearTimeout(timer);
|
|
2684
|
+
}
|
|
2685
|
+
}
|
|
2668
2686
|
function mergeOAuthSections(disk, snapshot, sections) {
|
|
2669
2687
|
const merged = {
|
|
2670
2688
|
servers: { ...disk?.servers },
|
|
@@ -2781,8 +2799,7 @@ function parseOAuthStoreWriteBody(body) {
|
|
|
2781
2799
|
if (!snapshot || !snapshotHasValidSecretPayloads(snapshot)) return null;
|
|
2782
2800
|
return { snapshot };
|
|
2783
2801
|
}
|
|
2784
|
-
|
|
2785
|
-
//#region ../../core/auth/node/secret-store.ts
|
|
2802
|
+
// ../../core/auth/node/secret-store.ts
|
|
2786
2803
|
const SERVICE_NAME = "mcp-inspector";
|
|
2787
2804
|
let keyringLoad;
|
|
2788
2805
|
var KeyringModuleShapeError = class extends Error {
|
|
@@ -3002,8 +3019,7 @@ var SessionSecretStore = class extends InMemorySecretStore {
|
|
|
3002
3019
|
return false;
|
|
3003
3020
|
}
|
|
3004
3021
|
};
|
|
3005
|
-
|
|
3006
|
-
//#region ../../core/auth/node/file-lock.ts
|
|
3022
|
+
// ../../core/auth/node/file-lock.ts
|
|
3007
3023
|
const STALE_MS = 1e4;
|
|
3008
3024
|
const RETRY = {
|
|
3009
3025
|
retries: 20,
|
|
@@ -3135,8 +3151,7 @@ async function withSecretFileLock(filePath, fn) {
|
|
|
3135
3151
|
await release();
|
|
3136
3152
|
}
|
|
3137
3153
|
}
|
|
3138
|
-
|
|
3139
|
-
//#region ../../core/auth/node/file-secret-store.ts
|
|
3154
|
+
// ../../core/auth/node/file-secret-store.ts
|
|
3140
3155
|
const scrypt = (passphrase, salt, keylen, options) => new Promise((resolve, reject) => {
|
|
3141
3156
|
crypto$1.scrypt(passphrase, salt, keylen, options, (err, key) => {
|
|
3142
3157
|
/* v8 ignore next -- @preserve: node validates the cost parameters
|
|
@@ -3544,8 +3559,7 @@ async function readSecretFilePermissions(filePath) {
|
|
|
3544
3559
|
};
|
|
3545
3560
|
}
|
|
3546
3561
|
}
|
|
3547
|
-
|
|
3548
|
-
//#region ../../core/auth/node/secret-store-selection.ts
|
|
3562
|
+
// ../../core/auth/node/secret-store-selection.ts
|
|
3549
3563
|
const SECRET_STORE_ENV = "MCP_INSPECTOR_SECRET_STORE";
|
|
3550
3564
|
const SECRET_FILE_ENV = "MCP_INSPECTOR_SECRET_FILE";
|
|
3551
3565
|
const STORAGE_DIR_ENV = "MCP_STORAGE_DIR";
|
|
@@ -3857,8 +3871,7 @@ function defaultSecretStore() {
|
|
|
3857
3871
|
async function resolveConcreteSecretStore(store) {
|
|
3858
3872
|
return store instanceof DeferredSecretStore ? (await resolveSecretStore()).store : store;
|
|
3859
3873
|
}
|
|
3860
|
-
|
|
3861
|
-
//#region ../../core/auth/node/oauth-secrets.ts
|
|
3874
|
+
// ../../core/auth/node/oauth-secrets.ts
|
|
3862
3875
|
const PERSIST_TOKENS_ENV = "MCP_INSPECTOR_PERSIST_TOKENS";
|
|
3863
3876
|
const warnedPolicyValues = /* @__PURE__ */ new Set();
|
|
3864
3877
|
function getPersistTokensPolicy(env = process.env) {
|
|
@@ -4128,8 +4141,7 @@ function snapshotHasPlaintextSecrets(snapshot) {
|
|
|
4128
4141
|
for (const session of Object.values(snapshot.idpSessions)) if (session.idToken !== void 0 || session.refreshToken !== void 0) return true;
|
|
4129
4142
|
return false;
|
|
4130
4143
|
}
|
|
4131
|
-
|
|
4132
|
-
//#region ../../core/auth/node/oauth-namespace-ledger.ts
|
|
4144
|
+
// ../../core/auth/node/oauth-namespace-ledger.ts
|
|
4133
4145
|
const namespaceLedgerPath = (stateFilePath) => `${stateFilePath}.namespaces.json`;
|
|
4134
4146
|
const KEYRING_LOCATION = "keyring";
|
|
4135
4147
|
const FILE_LOCATION_PREFIX = "file:";
|
|
@@ -4387,8 +4399,7 @@ async function purgeSupersededNamespaces(stateFilePath, secretStore, current, ow
|
|
|
4387
4399
|
warnLedgerFailure("update", error);
|
|
4388
4400
|
}
|
|
4389
4401
|
}
|
|
4390
|
-
|
|
4391
|
-
//#region ../../core/auth/node/oauth-persist-file.ts
|
|
4402
|
+
// ../../core/auth/node/oauth-persist-file.ts
|
|
4392
4403
|
const warnedStoreFailures = /* @__PURE__ */ new Set();
|
|
4393
4404
|
function warnStoreWriteFailure(error) {
|
|
4394
4405
|
const reason = error instanceof Error ? error.message : String(error);
|
|
@@ -4920,8 +4931,7 @@ async function removeOAuthStore(filePath, secretStore = defaultSecretStore()) {
|
|
|
4920
4931
|
if (locked) await purgeSupersededNamespaces(filePath, secretStore, void 0);
|
|
4921
4932
|
});
|
|
4922
4933
|
}
|
|
4923
|
-
|
|
4924
|
-
//#region ../../core/mcp/import/clientConfig.ts
|
|
4934
|
+
// ../../core/mcp/import/clientConfig.ts
|
|
4925
4935
|
function parseJsonObject(raw) {
|
|
4926
4936
|
let data;
|
|
4927
4937
|
try {
|
|
@@ -4951,8 +4961,7 @@ function parseVsCodeConfig(raw) {
|
|
|
4951
4961
|
if (!servers || typeof servers !== "object" || Array.isArray(servers)) throw new Error("No 'servers' object found in VS Code config file");
|
|
4952
4962
|
return { mcpServers: normalizeServerMap(servers) };
|
|
4953
4963
|
}
|
|
4954
|
-
|
|
4955
|
-
//#region ../../core/mcp/import/strategies.ts
|
|
4964
|
+
// ../../core/mcp/import/strategies.ts
|
|
4956
4965
|
function join$1(...segments) {
|
|
4957
4966
|
return segments.map((s, i) => i === 0 ? s.replace(/\/+$/, "") : s.replace(/^\/+|\/+$/g, "")).filter((s) => s.length > 0).join("/");
|
|
4958
4967
|
}
|
|
@@ -4993,8 +5002,7 @@ const IMPORT_STRATEGIES = Object.fromEntries([
|
|
|
4993
5002
|
function getImportStrategy(id) {
|
|
4994
5003
|
return IMPORT_STRATEGIES[id];
|
|
4995
5004
|
}
|
|
4996
|
-
|
|
4997
|
-
//#region ../../core/mcp/import/resolveSource.ts
|
|
5005
|
+
// ../../core/mcp/import/resolveSource.ts
|
|
4998
5006
|
function resolveImportSource(type, platform, homeDir, readFile) {
|
|
4999
5007
|
const strategy = getImportStrategy(type);
|
|
5000
5008
|
if (!strategy) return null;
|
|
@@ -5037,8 +5045,7 @@ function resolveImportSource(type, platform, homeDir, readFile) {
|
|
|
5037
5045
|
searched
|
|
5038
5046
|
};
|
|
5039
5047
|
}
|
|
5040
|
-
|
|
5041
|
-
//#region ../../core/mcp/remote/progressToken.ts
|
|
5048
|
+
// ../../core/mcp/remote/progressToken.ts
|
|
5042
5049
|
function progressTokenOf(message) {
|
|
5043
5050
|
if ("method" in message && message.method === "notifications/progress" && "params" in message && typeof message.params === "object" && message.params !== null) {
|
|
5044
5051
|
const parsed = ProgressTokenSchema.safeParse(message.params.progressToken);
|
|
@@ -5051,13 +5058,17 @@ function waitForProgressToken(waits, token) {
|
|
|
5051
5058
|
const numeric = Number(token);
|
|
5052
5059
|
return Number.isSafeInteger(numeric) ? waits.get(numeric) : void 0;
|
|
5053
5060
|
}
|
|
5054
|
-
|
|
5055
|
-
//#region ../../core/mcp/remote/node/remote-session.ts
|
|
5061
|
+
// ../../core/mcp/remote/node/remote-session.ts
|
|
5056
5062
|
var RemoteSession = class RemoteSession {
|
|
5057
5063
|
static DEFAULT_DETACH_GRACE_MS = 6e4;
|
|
5058
5064
|
static MAX_QUEUED_EVENTS = 1e3;
|
|
5059
5065
|
sessionId;
|
|
5066
|
+
serverName;
|
|
5067
|
+
transportType;
|
|
5068
|
+
createdAt;
|
|
5069
|
+
lastConsumerSeenAt = null;
|
|
5060
5070
|
transport;
|
|
5071
|
+
terminated = new AbortController();
|
|
5061
5072
|
eventQueue = [];
|
|
5062
5073
|
droppedEventCount = 0;
|
|
5063
5074
|
detachTimer;
|
|
@@ -5072,8 +5083,31 @@ var RemoteSession = class RemoteSession {
|
|
|
5072
5083
|
appliedProtocolVersion;
|
|
5073
5084
|
rateLimits = new RateLimitLedger();
|
|
5074
5085
|
static PROTOCOL_VERSION_PATTERN = /^[A-Za-z0-9._-]{1,64}$/;
|
|
5075
|
-
constructor(sessionId) {
|
|
5086
|
+
constructor(sessionId, descriptor = {}) {
|
|
5076
5087
|
this.sessionId = sessionId;
|
|
5088
|
+
this.serverName = descriptor.serverName;
|
|
5089
|
+
this.transportType = descriptor.transport ?? "stdio";
|
|
5090
|
+
this.createdAt = /* @__PURE__ */ new Date();
|
|
5091
|
+
}
|
|
5092
|
+
get terminatedSignal() {
|
|
5093
|
+
return this.terminated.signal;
|
|
5094
|
+
}
|
|
5095
|
+
terminate() {
|
|
5096
|
+
this.terminated.abort();
|
|
5097
|
+
}
|
|
5098
|
+
describe() {
|
|
5099
|
+
const attached = this.eventConsumer !== null;
|
|
5100
|
+
const lastSeen = attached ? /* @__PURE__ */ new Date() : this.lastConsumerSeenAt;
|
|
5101
|
+
return {
|
|
5102
|
+
sessionId: this.sessionId,
|
|
5103
|
+
...this.serverName !== void 0 && { serverName: this.serverName },
|
|
5104
|
+
transport: this.transportType,
|
|
5105
|
+
createdAt: this.createdAt.toISOString(),
|
|
5106
|
+
consumerAttached: attached,
|
|
5107
|
+
lastConsumerSeenAt: lastSeen ? lastSeen.toISOString() : null,
|
|
5108
|
+
queuedEvents: this.eventQueue.length,
|
|
5109
|
+
transportDead: this.transportDead
|
|
5110
|
+
};
|
|
5077
5111
|
}
|
|
5078
5112
|
setAuthProviderHandle(handle) {
|
|
5079
5113
|
this.authProviderHandle = handle;
|
|
@@ -5093,6 +5127,7 @@ var RemoteSession = class RemoteSession {
|
|
|
5093
5127
|
setEventConsumer(consumer) {
|
|
5094
5128
|
this.cancelDetachGrace();
|
|
5095
5129
|
this.eventConsumer = consumer;
|
|
5130
|
+
this.lastConsumerSeenAt = /* @__PURE__ */ new Date();
|
|
5096
5131
|
if (this.droppedEventCount > 0) {
|
|
5097
5132
|
const dropped = this.droppedEventCount;
|
|
5098
5133
|
this.droppedEventCount = 0;
|
|
@@ -5107,12 +5142,14 @@ var RemoteSession = class RemoteSession {
|
|
|
5107
5142
|
while (this.eventQueue.length > 0) consumer(this.eventQueue.shift());
|
|
5108
5143
|
}
|
|
5109
5144
|
clearEventConsumer() {
|
|
5145
|
+
if (this.eventConsumer !== null) this.lastConsumerSeenAt = /* @__PURE__ */ new Date();
|
|
5110
5146
|
this.eventConsumer = null;
|
|
5111
5147
|
return this.transportDead;
|
|
5112
5148
|
}
|
|
5113
5149
|
detachEventConsumer(consumer) {
|
|
5114
5150
|
if (this.eventConsumer !== consumer) return false;
|
|
5115
5151
|
this.eventConsumer = null;
|
|
5152
|
+
this.lastConsumerSeenAt = /* @__PURE__ */ new Date();
|
|
5116
5153
|
return true;
|
|
5117
5154
|
}
|
|
5118
5155
|
armDetachGrace(graceMs, onExpire) {
|
|
@@ -5301,8 +5338,7 @@ var RemoteSession = class RemoteSession {
|
|
|
5301
5338
|
});
|
|
5302
5339
|
}
|
|
5303
5340
|
};
|
|
5304
|
-
|
|
5305
|
-
//#region ../../core/mcp/remote/node/tokenAuthProvider.ts
|
|
5341
|
+
// ../../core/mcp/remote/node/tokenAuthProvider.ts
|
|
5306
5342
|
const REMOTE_OAUTH_STUB_METADATA = {
|
|
5307
5343
|
redirect_uris: [],
|
|
5308
5344
|
scope: ""
|
|
@@ -5367,8 +5403,7 @@ function createRemoteAuthProvider(initialAuthState) {
|
|
|
5367
5403
|
}
|
|
5368
5404
|
};
|
|
5369
5405
|
}
|
|
5370
|
-
|
|
5371
|
-
//#region ../../core/client/config-parse.ts
|
|
5406
|
+
// ../../core/client/config-parse.ts
|
|
5372
5407
|
function isAbsoluteHttpUrl(value) {
|
|
5373
5408
|
let url;
|
|
5374
5409
|
try {
|
|
@@ -5460,8 +5495,7 @@ function formatClientConfigLoadError(error) {
|
|
|
5460
5495
|
if (error instanceof Error) return error.message;
|
|
5461
5496
|
return String(error);
|
|
5462
5497
|
}
|
|
5463
|
-
|
|
5464
|
-
//#region ../../core/client/secrets.ts
|
|
5498
|
+
// ../../core/client/secrets.ts
|
|
5465
5499
|
const CLIENT_KEYCHAIN_ID = "client";
|
|
5466
5500
|
function hasClientPlaintextSecret(config) {
|
|
5467
5501
|
const secret = config.enterpriseManagedAuth?.idp?.clientSecret;
|
|
@@ -5502,8 +5536,7 @@ function mergeSecretsIntoClientConfig(config, secrets) {
|
|
|
5502
5536
|
}
|
|
5503
5537
|
};
|
|
5504
5538
|
}
|
|
5505
|
-
|
|
5506
|
-
//#region ../../core/client/node-persistence.ts
|
|
5539
|
+
// ../../core/client/node-persistence.ts
|
|
5507
5540
|
async function withClientConfigLock(filePath, action, body) {
|
|
5508
5541
|
let entered = false;
|
|
5509
5542
|
try {
|
|
@@ -5595,25 +5628,44 @@ async function deleteClientConfigStore(filePath, secretStore) {
|
|
|
5595
5628
|
}
|
|
5596
5629
|
});
|
|
5597
5630
|
}
|
|
5598
|
-
|
|
5599
|
-
//#region ../../core/mcp/remote/node/server.ts
|
|
5631
|
+
// ../../core/mcp/remote/node/server.ts
|
|
5600
5632
|
const SSE_PRIMING_COMMENT = ":\n\n";
|
|
5601
5633
|
function closeAbortedStream(stream) {
|
|
5602
5634
|
stream.close().catch(() => {});
|
|
5603
5635
|
}
|
|
5604
|
-
async function primeAndHoldSseStream(stream, cleanup) {
|
|
5636
|
+
async function primeAndHoldSseStream(stream, cleanup, end) {
|
|
5605
5637
|
let done = false;
|
|
5606
|
-
const
|
|
5607
|
-
|
|
5608
|
-
|
|
5609
|
-
|
|
5610
|
-
|
|
5638
|
+
const finish = () => {
|
|
5639
|
+
if (done) return false;
|
|
5640
|
+
done = true;
|
|
5641
|
+
cleanup();
|
|
5642
|
+
return true;
|
|
5643
|
+
};
|
|
5644
|
+
const released = new Promise((resolve) => {
|
|
5645
|
+
let closed = false;
|
|
5646
|
+
const release = () => {
|
|
5647
|
+
if (closed) return;
|
|
5648
|
+
closed = true;
|
|
5611
5649
|
closeAbortedStream(stream);
|
|
5612
5650
|
resolve();
|
|
5651
|
+
};
|
|
5652
|
+
let detachEnd = () => {};
|
|
5653
|
+
stream.onAbort(() => {
|
|
5654
|
+
if (finish()) detachEnd();
|
|
5655
|
+
release();
|
|
5613
5656
|
});
|
|
5657
|
+
if (!end) return;
|
|
5658
|
+
const { signal, flush } = end;
|
|
5659
|
+
const onEnd = () => {
|
|
5660
|
+
if (!finish()) return;
|
|
5661
|
+
flush().catch(() => {}).then(release);
|
|
5662
|
+
};
|
|
5663
|
+
detachEnd = () => signal.removeEventListener("abort", onEnd);
|
|
5664
|
+
if (signal.aborted) onEnd();
|
|
5665
|
+
else signal.addEventListener("abort", onEnd, { once: true });
|
|
5614
5666
|
});
|
|
5615
5667
|
await stream.write(SSE_PRIMING_COMMENT);
|
|
5616
|
-
await
|
|
5668
|
+
await released;
|
|
5617
5669
|
}
|
|
5618
5670
|
const MAX_APP_DOCUMENT_CHARS = 8388608;
|
|
5619
5671
|
const MAX_APP_DOCUMENT_BODY_BYTES = 25165824;
|
|
@@ -5720,6 +5772,13 @@ function mcpParamHeadersOnly(headers) {
|
|
|
5720
5772
|
for (const [key, value] of Object.entries(headers)) if (typeof value === "string" && key.toLowerCase().startsWith("Mcp-Param-".toLowerCase())) out[key] = value;
|
|
5721
5773
|
return Object.keys(out).length > 0 ? out : void 0;
|
|
5722
5774
|
}
|
|
5775
|
+
const SESSION_TERMINATED_MESSAGE = "Session terminated from the Inspector's active session list";
|
|
5776
|
+
const MAX_SESSION_SERVER_NAME_CHARS = 256;
|
|
5777
|
+
function sessionServerName(value) {
|
|
5778
|
+
if (typeof value !== "string") return void 0;
|
|
5779
|
+
const name = value.trim();
|
|
5780
|
+
return name === "" ? void 0 : name.slice(0, MAX_SESSION_SERVER_NAME_CHARS);
|
|
5781
|
+
}
|
|
5723
5782
|
function createRemoteApp(options) {
|
|
5724
5783
|
const dangerouslyOmitAuth = !!options.dangerouslyOmitAuth;
|
|
5725
5784
|
const authToken = dangerouslyOmitAuth ? "" : options.authToken || process.env[API_SERVER_ENV_VARS.AUTH_TOKEN] || randomBytes(32).toString("hex");
|
|
@@ -5730,7 +5789,8 @@ function createRemoteApp(options) {
|
|
|
5730
5789
|
session.armDetachGrace(sessionDetachGraceMs, () => {
|
|
5731
5790
|
sessions.delete(session.sessionId);
|
|
5732
5791
|
session.rejectActiveRequestWaits(/* @__PURE__ */ new Error("Session closed: no client attached"));
|
|
5733
|
-
session.transport
|
|
5792
|
+
const transport = session.transport;
|
|
5793
|
+
terminateSessionBestEffort(transport).then(() => transport.close().catch(() => {}));
|
|
5734
5794
|
});
|
|
5735
5795
|
};
|
|
5736
5796
|
const { logger: fileLogger, allowedOrigins } = options;
|
|
@@ -5846,7 +5906,10 @@ function createRemoteApp(options) {
|
|
|
5846
5906
|
const config = body.config;
|
|
5847
5907
|
if (!config) return c.json({ error: "Missing config" }, 400);
|
|
5848
5908
|
const sessionId = crypto.randomUUID();
|
|
5849
|
-
const session = new RemoteSession(sessionId
|
|
5909
|
+
const session = new RemoteSession(sessionId, {
|
|
5910
|
+
serverName: sessionServerName(body.serverName),
|
|
5911
|
+
transport: config.type ?? "stdio"
|
|
5912
|
+
});
|
|
5850
5913
|
let transport;
|
|
5851
5914
|
let authHandle;
|
|
5852
5915
|
try {
|
|
@@ -6010,12 +6073,13 @@ function createRemoteApp(options) {
|
|
|
6010
6073
|
const session = sessions.get(sessionId);
|
|
6011
6074
|
if (!session) return c.json({ error: "Session not found" }, 404);
|
|
6012
6075
|
return streamSSE(c, async (stream) => {
|
|
6076
|
+
let lastWrite = Promise.resolve();
|
|
6013
6077
|
const consumer = (event) => {
|
|
6014
6078
|
const data = JSON.stringify(event);
|
|
6015
|
-
stream.writeSSE({
|
|
6079
|
+
lastWrite = stream.writeSSE({
|
|
6016
6080
|
event: event.type,
|
|
6017
6081
|
data
|
|
6018
|
-
});
|
|
6082
|
+
}).catch(() => {});
|
|
6019
6083
|
};
|
|
6020
6084
|
session.setEventConsumer(consumer);
|
|
6021
6085
|
if (session.isTransportDead()) {
|
|
@@ -6031,6 +6095,9 @@ function createRemoteApp(options) {
|
|
|
6031
6095
|
return;
|
|
6032
6096
|
}
|
|
6033
6097
|
armSessionReap(session);
|
|
6098
|
+
}, {
|
|
6099
|
+
signal: session.terminatedSignal,
|
|
6100
|
+
flush: () => lastWrite
|
|
6034
6101
|
});
|
|
6035
6102
|
});
|
|
6036
6103
|
});
|
|
@@ -6047,11 +6114,28 @@ function createRemoteApp(options) {
|
|
|
6047
6114
|
if (session) {
|
|
6048
6115
|
session.cancelDetachGrace();
|
|
6049
6116
|
session.clearEventConsumer();
|
|
6117
|
+
await terminateSessionBestEffort(session.transport);
|
|
6050
6118
|
await session.transport.close();
|
|
6051
6119
|
sessions.delete(sessionId);
|
|
6052
6120
|
}
|
|
6053
6121
|
return c.json({ ok: true });
|
|
6054
6122
|
});
|
|
6123
|
+
app.get("/api/mcp/sessions", (c) => {
|
|
6124
|
+
const list = { sessions: [...sessions.values()].map((session) => session.describe()).sort((a, b) => a.createdAt.localeCompare(b.createdAt)) };
|
|
6125
|
+
return c.json(list);
|
|
6126
|
+
});
|
|
6127
|
+
app.delete("/api/mcp/sessions/:sessionId", async (c) => {
|
|
6128
|
+
const sessionId = c.req.param("sessionId");
|
|
6129
|
+
const session = sessions.get(sessionId);
|
|
6130
|
+
if (!session) return c.json({ error: "Session not found" }, 404);
|
|
6131
|
+
sessions.delete(sessionId);
|
|
6132
|
+
session.cancelDetachGrace();
|
|
6133
|
+
session.markTransportDead(SESSION_TERMINATED_MESSAGE);
|
|
6134
|
+
session.terminate();
|
|
6135
|
+
session.rejectActiveRequestWaits(/* @__PURE__ */ new Error(SESSION_TERMINATED_MESSAGE));
|
|
6136
|
+
await session.transport.close().catch(() => {});
|
|
6137
|
+
return c.json({ ok: true });
|
|
6138
|
+
});
|
|
6055
6139
|
app.post("/api/mcp/auth-state", async (c) => {
|
|
6056
6140
|
let body;
|
|
6057
6141
|
try {
|
|
@@ -6143,7 +6227,14 @@ function createRemoteApp(options) {
|
|
|
6143
6227
|
timeoutMs: deadlineMs
|
|
6144
6228
|
}, 504);
|
|
6145
6229
|
const refused = findFetchTargetError(err);
|
|
6146
|
-
if (refused)
|
|
6230
|
+
if (refused) {
|
|
6231
|
+
const body = {
|
|
6232
|
+
error: refused.message,
|
|
6233
|
+
code: FETCH_TARGET_REFUSED_WIRE_CODE,
|
|
6234
|
+
url: redactUrlQuery(refused.url ?? url)
|
|
6235
|
+
};
|
|
6236
|
+
return c.json(body, 403);
|
|
6237
|
+
}
|
|
6147
6238
|
return c.json({ error: msg }, 500);
|
|
6148
6239
|
} finally {
|
|
6149
6240
|
if (timer !== void 0) clearTimeout(timer);
|
|
@@ -6989,8 +7080,7 @@ function createRemoteApp(options) {
|
|
|
6989
7080
|
}
|
|
6990
7081
|
};
|
|
6991
7082
|
}
|
|
6992
|
-
|
|
6993
|
-
//#region server/inject-auth-token.ts
|
|
7083
|
+
// server/inject-auth-token.ts
|
|
6994
7084
|
function serializeTokenForScript(token) {
|
|
6995
7085
|
return JSON.stringify(token).replace(/</g, "\\u003c");
|
|
6996
7086
|
}
|
|
@@ -7003,8 +7093,7 @@ function injectAuthToken(html, token) {
|
|
|
7003
7093
|
if (bodyClose !== -1) return html.slice(0, bodyClose) + script + html.slice(bodyClose);
|
|
7004
7094
|
return script + html;
|
|
7005
7095
|
}
|
|
7006
|
-
|
|
7007
|
-
//#region server/health.ts
|
|
7096
|
+
// server/health.ts
|
|
7008
7097
|
const HEALTH_PATH = "/healthz";
|
|
7009
7098
|
const HEALTH_BODY = Object.freeze({ status: "ok" });
|
|
7010
7099
|
const HEALTH_HEADERS = Object.freeze({
|
|
@@ -7034,8 +7123,7 @@ function handleNodeHealthRequest(req, res) {
|
|
|
7034
7123
|
else res.end(body);
|
|
7035
7124
|
return true;
|
|
7036
7125
|
}
|
|
7037
|
-
|
|
7038
|
-
//#region server/node-request-bridge.ts
|
|
7126
|
+
// server/node-request-bridge.ts
|
|
7039
7127
|
function fetchUrlForNodeRequest(headers, pathname, scheme) {
|
|
7040
7128
|
return `${scheme}://${headers.host ?? headers[":authority"]}${pathname}`;
|
|
7041
7129
|
}
|
|
@@ -7057,8 +7145,7 @@ const CONNECTION_SPECIFIC_HEADERS = /* @__PURE__ */ new Set([
|
|
|
7057
7145
|
function isResponseHeaderAllowed(name, httpVersionMajor) {
|
|
7058
7146
|
return httpVersionMajor < 2 || !CONNECTION_SPECIFIC_HEADERS.has(name.toLowerCase());
|
|
7059
7147
|
}
|
|
7060
|
-
|
|
7061
|
-
//#region server/vite-hono-plugin.ts
|
|
7148
|
+
// server/vite-hono-plugin.ts
|
|
7062
7149
|
function honoMiddlewarePlugin(config) {
|
|
7063
7150
|
let resolvedAuthToken = "";
|
|
7064
7151
|
return {
|
|
@@ -7200,8 +7287,7 @@ function honoMiddlewarePlugin(config) {
|
|
|
7200
7287
|
}
|
|
7201
7288
|
};
|
|
7202
7289
|
}
|
|
7203
|
-
|
|
7204
|
-
//#region server/vite-base-config.ts
|
|
7290
|
+
// server/vite-base-config.ts
|
|
7205
7291
|
const NODE_ONLY_OPTIMIZE_DEPS_EXCLUDE = [
|
|
7206
7292
|
"@modelcontextprotocol/client/stdio",
|
|
7207
7293
|
"atomically",
|
|
@@ -7344,8 +7430,7 @@ function vitestSharedPaths(clientDir) {
|
|
|
7344
7430
|
}
|
|
7345
7431
|
};
|
|
7346
7432
|
}
|
|
7347
|
-
|
|
7348
|
-
//#region server/start-vite-dev-server.ts
|
|
7433
|
+
// server/start-vite-dev-server.ts
|
|
7349
7434
|
const __dirname$2 = dirname(fileURLToPath(import.meta.url));
|
|
7350
7435
|
async function startViteDevServer(config) {
|
|
7351
7436
|
const root = resolve(join(__dirname$2, ".."));
|
|
@@ -7378,8 +7463,7 @@ async function startViteDevServer(config) {
|
|
|
7378
7463
|
await server.close();
|
|
7379
7464
|
} };
|
|
7380
7465
|
}
|
|
7381
|
-
|
|
7382
|
-
//#region server/server.ts
|
|
7466
|
+
// server/server.ts
|
|
7383
7467
|
function isApiPath(path) {
|
|
7384
7468
|
return path === "/api" || path.startsWith("/api/");
|
|
7385
7469
|
}
|
|
@@ -7481,8 +7565,7 @@ async function startHonoServer(config) {
|
|
|
7481
7565
|
});
|
|
7482
7566
|
} };
|
|
7483
7567
|
}
|
|
7484
|
-
|
|
7485
|
-
//#region server/ensure-web-build.ts
|
|
7568
|
+
// server/ensure-web-build.ts
|
|
7486
7569
|
function runViteBuild(webRoot, log) {
|
|
7487
7570
|
const result = spawnSync("npm", ["run", "build:client"], {
|
|
7488
7571
|
cwd: webRoot,
|
|
@@ -7501,8 +7584,7 @@ function ensureWebBuild(webRoot, distRoot, deps = {}) {
|
|
|
7501
7584
|
log("No production web build found at clients/web/dist. Building the web UI now (first run only; this can take a minute)...");
|
|
7502
7585
|
if (build(webRoot, log) !== 0 || !exists(indexHtml)) throw new Error("Could not build the web UI automatically. Build it manually with `npm run build` (run from clients/web), or launch with `--dev` to use the Vite dev server.");
|
|
7503
7586
|
}
|
|
7504
|
-
|
|
7505
|
-
//#region server/run-web.ts
|
|
7587
|
+
// server/run-web.ts
|
|
7506
7588
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
7507
7589
|
function ensureStdioCwd(config) {
|
|
7508
7590
|
if ((config.type === "stdio" || config.type === void 0) && config.command && !config.cwd) return {
|
|
@@ -7631,12 +7713,10 @@ async function runWeb(argv) {
|
|
|
7631
7713
|
process.on("SIGTERM", shutdown);
|
|
7632
7714
|
return new Promise(() => {});
|
|
7633
7715
|
}
|
|
7634
|
-
|
|
7635
|
-
//#region src/index.ts
|
|
7716
|
+
// src/index.ts
|
|
7636
7717
|
const __filename = fileURLToPath(import.meta.url);
|
|
7637
7718
|
if (process.argv[1] !== void 0 && resolve(process.argv[1]) === resolve(__filename)) runWeb(process.argv).then((code) => process.exit(code ?? 0)).catch((e) => {
|
|
7638
7719
|
console.error(e);
|
|
7639
7720
|
process.exit(1);
|
|
7640
7721
|
});
|
|
7641
|
-
//#endregion
|
|
7642
7722
|
export { runWeb };
|