@modelcontextprotocol/inspector 2.7.0 → 2.8.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/LICENSE +216 -0
- package/README.md +7 -2
- package/clients/cli/build/index.js +353 -35
- package/clients/tui/build/index.js +360 -41
- package/clients/web/build/index.js +177 -14
- package/clients/web/dist/assets/{index-DZkZ6KYt.js → index-9ZWu1ACP.js} +114 -114
- package/clients/web/dist/assets/{prism-light-CoQfbOXL.js → prism-light-CGBvpchP.js} +1 -1
- package/clients/web/dist/index.html +1 -1
- package/package.json +2 -2
|
@@ -109,6 +109,7 @@ var MODERN_LOG_LEVELS = [
|
|
|
109
109
|
function isModernLogLevel(value) {
|
|
110
110
|
return typeof value === "string" && MODERN_LOG_LEVELS.includes(value);
|
|
111
111
|
}
|
|
112
|
+
var MODERN_PROTOCOL_VERSION = "2026-07-28";
|
|
112
113
|
|
|
113
114
|
// ../../core/mcp/skillsSchemas.ts
|
|
114
115
|
import { z } from "zod/v4";
|
|
@@ -424,7 +425,7 @@ function cleanEndpointOverride(value, field) {
|
|
|
424
425
|
return value.trim() || void 0;
|
|
425
426
|
}
|
|
426
427
|
function storedFieldsToInspectorSettings(stored) {
|
|
427
|
-
const hasAny = stored.headers !== void 0 || stored.metadata !== void 0 || stored.connectionTimeout !== void 0 || stored.requestTimeout !== void 0 || stored.taskTtl !== void 0 || stored.autoRefreshOnListChanged !== void 0 || stored.paginatedLists !== void 0 || stored.advertisedExtensions !== void 0 || stored.maxFetchRequests !== void 0 || stored.skillCatalogMaxSkills !== void 0 || stored.skillCatalogMaxBytes !== void 0 || stored.oauth !== void 0 || stored.roots !== void 0 || stored.protocolEra !== void 0 || stored.modernLogLevel !== void 0 || stored.env !== void 0 || stored.cwd !== void 0;
|
|
428
|
+
const hasAny = stored.headers !== void 0 || stored.metadata !== void 0 || stored.connectionTimeout !== void 0 || stored.requestTimeout !== void 0 || stored.taskTtl !== void 0 || stored.autoRefreshOnListChanged !== void 0 || stored.paginatedLists !== void 0 || stored.suppressNotificationStream !== void 0 || stored.advertisedExtensions !== void 0 || stored.maxFetchRequests !== void 0 || stored.skillCatalogMaxSkills !== void 0 || stored.skillCatalogMaxBytes !== void 0 || stored.oauth !== void 0 || stored.roots !== void 0 || stored.protocolEra !== void 0 || stored.modernLogLevel !== void 0 || stored.env !== void 0 || stored.cwd !== void 0;
|
|
428
429
|
if (!hasAny) return void 0;
|
|
429
430
|
const headersPairs = stored.headers ? Object.entries(stored.headers).map(([key, value]) => ({ key, value })) : [];
|
|
430
431
|
const settings = {
|
|
@@ -456,6 +457,9 @@ function storedFieldsToInspectorSettings(stored) {
|
|
|
456
457
|
if (isSkillCatalogLimit(stored.skillCatalogMaxBytes)) {
|
|
457
458
|
settings.skillCatalogMaxBytes = stored.skillCatalogMaxBytes;
|
|
458
459
|
}
|
|
460
|
+
if (stored.suppressNotificationStream === true) {
|
|
461
|
+
settings.suppressNotificationStream = true;
|
|
462
|
+
}
|
|
459
463
|
if (isProtocolEra(stored.protocolEra)) {
|
|
460
464
|
settings.protocolEra = stored.protocolEra;
|
|
461
465
|
}
|
|
@@ -525,6 +529,9 @@ function inspectorSettingsToStoredFields(settings) {
|
|
|
525
529
|
if (settings.paginatedLists) {
|
|
526
530
|
out.paginatedLists = true;
|
|
527
531
|
}
|
|
532
|
+
if (settings.suppressNotificationStream) {
|
|
533
|
+
out.suppressNotificationStream = true;
|
|
534
|
+
}
|
|
528
535
|
if (settings.advertisedExtensions && Object.keys(settings.advertisedExtensions).length > 0) {
|
|
529
536
|
out.advertisedExtensions = { ...settings.advertisedExtensions };
|
|
530
537
|
}
|
|
@@ -591,6 +598,7 @@ var INSPECTOR_FIELD_KEY_MAP = {
|
|
|
591
598
|
taskTtl: true,
|
|
592
599
|
autoRefreshOnListChanged: true,
|
|
593
600
|
paginatedLists: true,
|
|
601
|
+
suppressNotificationStream: true,
|
|
594
602
|
advertisedExtensions: true,
|
|
595
603
|
maxFetchRequests: true,
|
|
596
604
|
skillCatalogMaxSkills: true,
|
|
@@ -954,7 +962,7 @@ function secretStorageCaveat(info) {
|
|
|
954
962
|
return `The secrets file could not be read (${info.encryptionUnknown}). Saving a secret will fail rather than overwrite it.`;
|
|
955
963
|
}
|
|
956
964
|
if (info.plaintext) {
|
|
957
|
-
return info.pendingEncryption ? "Existing secrets in this file are still unencrypted (file mode 0600). They are re-encrypted the next time a secret is saved." : "Secrets are stored unencrypted (file mode 0600). Set MCP_INSPECTOR_SECRET_KEY to encrypt them.";
|
|
965
|
+
return info.pendingEncryption ? "Existing secrets in this file are still unencrypted (file mode 0600). They are re-encrypted the next time a secret is saved." : "Secrets are stored unencrypted (file mode 0600). Set MCP_INSPECTOR_SECRET_KEY or MCP_INSPECTOR_SECRET_KEY_FILE to encrypt them.";
|
|
958
966
|
}
|
|
959
967
|
return void 0;
|
|
960
968
|
}
|
|
@@ -1058,14 +1066,14 @@ function isAllInterfacesHost(host) {
|
|
|
1058
1066
|
|
|
1059
1067
|
// server/resolve-bind-host.ts
|
|
1060
1068
|
var BIND_ALL_INTERFACES_ENV = "DANGEROUSLY_BIND_ALL_INTERFACES";
|
|
1061
|
-
function
|
|
1069
|
+
function isEnvFlagEnabled(value) {
|
|
1062
1070
|
const v = value?.trim().toLowerCase();
|
|
1063
1071
|
return v === "true" || v === "1";
|
|
1064
1072
|
}
|
|
1065
1073
|
var DEFAULT_BIND_HOST = "127.0.0.1";
|
|
1066
1074
|
function resolveBindHostname(env = process.env) {
|
|
1067
1075
|
const host = (env.HOST ?? DEFAULT_BIND_HOST).trim();
|
|
1068
|
-
if (isAllInterfacesHost(host) && !
|
|
1076
|
+
if (isAllInterfacesHost(host) && !isEnvFlagEnabled(env[BIND_ALL_INTERFACES_ENV])) {
|
|
1069
1077
|
const resolved2 = canonicalUrlHost(host);
|
|
1070
1078
|
const shown = stripBrackets(resolved2) === stripBrackets(host.toLowerCase()) ? `HOST="${host}"` : `HOST="${host}" (resolves to ${resolved2})`;
|
|
1071
1079
|
throw new Error(
|
|
@@ -1658,7 +1666,9 @@ function buildWebServerConfig(options = {}) {
|
|
|
1658
1666
|
);
|
|
1659
1667
|
}
|
|
1660
1668
|
const hostname = resolveBindHostname();
|
|
1661
|
-
const dangerouslyOmitAuth =
|
|
1669
|
+
const dangerouslyOmitAuth = isEnvFlagEnabled(
|
|
1670
|
+
process.env.DANGEROUSLY_OMIT_AUTH
|
|
1671
|
+
);
|
|
1662
1672
|
const authToken = dangerouslyOmitAuth ? "" : process.env[API_SERVER_ENV_VARS.AUTH_TOKEN] ?? process.env[LEGACY_AUTH_TOKEN_ENV] ?? "";
|
|
1663
1673
|
const sandboxPort = resolveSandboxPort();
|
|
1664
1674
|
const requestedAppOriginPort = resolveAppOriginPort();
|
|
@@ -1736,7 +1746,7 @@ import react from "@vitejs/plugin-react";
|
|
|
1736
1746
|
|
|
1737
1747
|
// ../../core/mcp/remote/node/server.ts
|
|
1738
1748
|
import { randomBytes as randomBytes3, timingSafeEqual } from "crypto";
|
|
1739
|
-
import { existsSync as existsSync3, readFileSync as
|
|
1749
|
+
import { existsSync as existsSync3, readFileSync as readFileSync6 } from "fs";
|
|
1740
1750
|
import { stat as fsStat } from "fs/promises";
|
|
1741
1751
|
import { homedir } from "os";
|
|
1742
1752
|
import { Hono } from "hono";
|
|
@@ -2359,6 +2369,73 @@ function createProxyFetch() {
|
|
|
2359
2369
|
};
|
|
2360
2370
|
}
|
|
2361
2371
|
|
|
2372
|
+
// ../../core/mcp/node/notificationHeadersFetch.ts
|
|
2373
|
+
import { PROTOCOL_VERSION_META_KEY } from "@modelcontextprotocol/client";
|
|
2374
|
+
function createNotificationHeadersFetch(baseFetch) {
|
|
2375
|
+
return (input, init) => {
|
|
2376
|
+
const method = modernNotificationMethod(init);
|
|
2377
|
+
if (method === void 0) return baseFetch(input, init);
|
|
2378
|
+
const headers = new Headers(init?.headers);
|
|
2379
|
+
headers.set("mcp-protocol-version", method.version);
|
|
2380
|
+
headers.set("mcp-method", method.method);
|
|
2381
|
+
return baseFetch(input, { ...init, headers });
|
|
2382
|
+
};
|
|
2383
|
+
}
|
|
2384
|
+
function modernNotificationMethod(init) {
|
|
2385
|
+
if (init?.method?.toUpperCase() !== "POST") return void 0;
|
|
2386
|
+
if (typeof init.body !== "string") return void 0;
|
|
2387
|
+
let message;
|
|
2388
|
+
try {
|
|
2389
|
+
message = JSON.parse(init.body);
|
|
2390
|
+
} catch {
|
|
2391
|
+
return void 0;
|
|
2392
|
+
}
|
|
2393
|
+
if (!isRecord(message) || "id" in message) return void 0;
|
|
2394
|
+
if (typeof message.method !== "string") return void 0;
|
|
2395
|
+
const meta = isRecord(message.params) ? message.params._meta : void 0;
|
|
2396
|
+
const version = isRecord(meta) ? meta[PROTOCOL_VERSION_META_KEY] : void 0;
|
|
2397
|
+
if (typeof version !== "string" || version < MODERN_PROTOCOL_VERSION) {
|
|
2398
|
+
return void 0;
|
|
2399
|
+
}
|
|
2400
|
+
return { method: message.method, version };
|
|
2401
|
+
}
|
|
2402
|
+
function isRecord(value) {
|
|
2403
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
2404
|
+
}
|
|
2405
|
+
|
|
2406
|
+
// ../../core/mcp/node/suppressNotificationStreamFetch.ts
|
|
2407
|
+
function createSuppressNotificationStreamFetch(baseFetch, endpoint) {
|
|
2408
|
+
return async (input, init) => {
|
|
2409
|
+
if (isStandaloneStreamRequest(input, init, endpoint)) {
|
|
2410
|
+
return new Response(null, {
|
|
2411
|
+
status: 405,
|
|
2412
|
+
statusText: "Method Not Allowed"
|
|
2413
|
+
});
|
|
2414
|
+
}
|
|
2415
|
+
return baseFetch(input, init);
|
|
2416
|
+
};
|
|
2417
|
+
}
|
|
2418
|
+
function requestUrl(input) {
|
|
2419
|
+
const raw = input instanceof Request ? input.url : input instanceof URL ? input.href : input;
|
|
2420
|
+
try {
|
|
2421
|
+
return new URL(raw);
|
|
2422
|
+
} catch {
|
|
2423
|
+
return void 0;
|
|
2424
|
+
}
|
|
2425
|
+
}
|
|
2426
|
+
function isStandaloneStreamRequest(input, init, endpoint) {
|
|
2427
|
+
const request = input instanceof Request ? input : void 0;
|
|
2428
|
+
const method = (init?.method ?? request?.method ?? "GET").toUpperCase();
|
|
2429
|
+
if (method !== "GET") return false;
|
|
2430
|
+
const url = requestUrl(input);
|
|
2431
|
+
if (!url || url.origin !== endpoint.origin || url.pathname !== endpoint.pathname || url.search !== endpoint.search) {
|
|
2432
|
+
return false;
|
|
2433
|
+
}
|
|
2434
|
+
const headers = new Headers(init?.headers ?? request?.headers);
|
|
2435
|
+
const accept = headers.get("accept")?.toLowerCase() ?? "";
|
|
2436
|
+
return accept.includes("text/event-stream") && !headers.has("last-event-id");
|
|
2437
|
+
}
|
|
2438
|
+
|
|
2362
2439
|
// ../../core/mcp/node/transport.ts
|
|
2363
2440
|
function headersFromSettings(settings) {
|
|
2364
2441
|
if (!settings || settings.headers.length === 0) return void 0;
|
|
@@ -2446,10 +2523,14 @@ function createTransportNode(config, options = {}) {
|
|
|
2446
2523
|
...httpConfig.requestInit,
|
|
2447
2524
|
...headers && { headers }
|
|
2448
2525
|
};
|
|
2526
|
+
const stampedFetch = createNotificationHeadersFetch(
|
|
2527
|
+
fetchWithOptionalAuthIntercept
|
|
2528
|
+
);
|
|
2529
|
+
const httpFetch = settings?.suppressNotificationStream === true ? createSuppressNotificationStreamFetch(stampedFetch, url) : stampedFetch;
|
|
2449
2530
|
const transport = new StreamableHTTPClientTransport(url, {
|
|
2450
2531
|
authProvider,
|
|
2451
2532
|
requestInit,
|
|
2452
|
-
fetch:
|
|
2533
|
+
fetch: httpFetch,
|
|
2453
2534
|
// SEP-2350: how the transport reacts to a `403 insufficient_scope`
|
|
2454
2535
|
// challenge. Defaults to the SDK's `reauthorize` when unset.
|
|
2455
2536
|
...settings?.oauthOnInsufficientScope && {
|
|
@@ -3214,6 +3295,7 @@ import * as path4 from "path";
|
|
|
3214
3295
|
|
|
3215
3296
|
// ../../core/auth/node/file-secret-store.ts
|
|
3216
3297
|
import * as crypto2 from "crypto";
|
|
3298
|
+
import { readFileSync as readFileSync4, statSync as statSync2 } from "fs";
|
|
3217
3299
|
import * as fs3 from "fs/promises";
|
|
3218
3300
|
import * as path3 from "path";
|
|
3219
3301
|
|
|
@@ -3416,6 +3498,55 @@ var SCRYPT_N = 16384;
|
|
|
3416
3498
|
var SCRYPT_R = 8;
|
|
3417
3499
|
var SCRYPT_P = 1;
|
|
3418
3500
|
var SECRET_KEY_ENV = "MCP_INSPECTOR_SECRET_KEY";
|
|
3501
|
+
var SECRET_KEY_FILE_ENV = "MCP_INSPECTOR_SECRET_KEY_FILE";
|
|
3502
|
+
function isSameFile(a, b) {
|
|
3503
|
+
if (path3.resolve(a) === path3.resolve(b)) return true;
|
|
3504
|
+
try {
|
|
3505
|
+
const sa = statSync2(a);
|
|
3506
|
+
const sb = statSync2(b);
|
|
3507
|
+
return sa.dev === sb.dev && sa.ino === sb.ino;
|
|
3508
|
+
} catch {
|
|
3509
|
+
return false;
|
|
3510
|
+
}
|
|
3511
|
+
}
|
|
3512
|
+
function resolveSecretPassphrase(env = process.env, secretFilePath) {
|
|
3513
|
+
const direct = env[SECRET_KEY_ENV];
|
|
3514
|
+
const hasDirect = direct !== void 0 && direct.trim() !== "";
|
|
3515
|
+
const rawKeyFile = env[SECRET_KEY_FILE_ENV];
|
|
3516
|
+
const wantsKeyFile = rawKeyFile !== void 0;
|
|
3517
|
+
const keyFile = rawKeyFile?.trim();
|
|
3518
|
+
if (hasDirect && wantsKeyFile) {
|
|
3519
|
+
return {
|
|
3520
|
+
problem: `both ${SECRET_KEY_ENV} and ${SECRET_KEY_FILE_ENV} are set; set only one`
|
|
3521
|
+
};
|
|
3522
|
+
}
|
|
3523
|
+
if (hasDirect) return { passphrase: direct };
|
|
3524
|
+
if (!wantsKeyFile) return {};
|
|
3525
|
+
if (!keyFile) {
|
|
3526
|
+
return { problem: `${SECRET_KEY_FILE_ENV} is set but empty` };
|
|
3527
|
+
}
|
|
3528
|
+
const resolved2 = path3.resolve(keyFile);
|
|
3529
|
+
if (secretFilePath !== void 0 && isSameFile(resolved2, secretFilePath)) {
|
|
3530
|
+
return {
|
|
3531
|
+
problem: `${SECRET_KEY_FILE_ENV} (${resolved2}) is the secrets file itself; point it at a separate key file`
|
|
3532
|
+
};
|
|
3533
|
+
}
|
|
3534
|
+
let contents;
|
|
3535
|
+
try {
|
|
3536
|
+
contents = readFileSync4(resolved2, "utf-8");
|
|
3537
|
+
} catch (err) {
|
|
3538
|
+
return {
|
|
3539
|
+
// `String(err)` rather than `.message`: `readFileSync` only ever throws
|
|
3540
|
+
// an `Error`, so an `instanceof` guard would be a branch no test can take.
|
|
3541
|
+
problem: `${SECRET_KEY_FILE_ENV} (${resolved2}) could not be read: ${String(err)}`
|
|
3542
|
+
};
|
|
3543
|
+
}
|
|
3544
|
+
const passphrase = contents.replace(/[\r\n]+$/, "");
|
|
3545
|
+
if (passphrase.trim() === "") {
|
|
3546
|
+
return { problem: `${SECRET_KEY_FILE_ENV} (${resolved2}) is empty` };
|
|
3547
|
+
}
|
|
3548
|
+
return { passphrase };
|
|
3549
|
+
}
|
|
3419
3550
|
var buildAccount2 = (serverId, field) => `${serverId}:${field}`;
|
|
3420
3551
|
function asSecretMap(value, filePath) {
|
|
3421
3552
|
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
@@ -3435,7 +3566,7 @@ function asSecretMap(value, filePath) {
|
|
|
3435
3566
|
var SecretFileKeyMismatchError = class extends SecretStoreUnavailableError {
|
|
3436
3567
|
constructor(filePath, hasKey) {
|
|
3437
3568
|
super(
|
|
3438
|
-
hasKey ? `The secrets file at ${filePath} could not be decrypted with the current ${SECRET_KEY_ENV}. Refusing to write, which would overwrite the existing secrets. Restore the original passphrase, or delete the file to start over.` : `The secrets file at ${filePath} is encrypted but ${SECRET_KEY_ENV} is not set. Refusing to write, which would overwrite the existing secrets. Set the passphrase this file was written with, or delete the file to start over.`
|
|
3569
|
+
hasKey ? `The secrets file at ${filePath} could not be decrypted with the current ${SECRET_KEY_ENV} (or ${SECRET_KEY_FILE_ENV}). Refusing to write, which would overwrite the existing secrets. Restore the original passphrase, or delete the file to start over.` : `The secrets file at ${filePath} is encrypted but ${SECRET_KEY_ENV} is not set (nor ${SECRET_KEY_FILE_ENV}). Refusing to write, which would overwrite the existing secrets. Set the passphrase this file was written with, or delete the file to start over.`
|
|
3439
3570
|
);
|
|
3440
3571
|
this.name = "SecretFileKeyMismatchError";
|
|
3441
3572
|
}
|
|
@@ -3493,10 +3624,17 @@ function encryptedEnvelopeProblem(parsed) {
|
|
|
3493
3624
|
var FileSecretStore = class {
|
|
3494
3625
|
filePath;
|
|
3495
3626
|
passphrase;
|
|
3627
|
+
/**
|
|
3628
|
+
* Why a configured key could not be obtained. While set, every read throws
|
|
3629
|
+
* and every write refuses — see {@link SecretPassphrase}.
|
|
3630
|
+
*/
|
|
3631
|
+
keyProblem;
|
|
3496
3632
|
constructor(options) {
|
|
3497
3633
|
this.filePath = options.filePath;
|
|
3498
|
-
const
|
|
3634
|
+
const resolved2 = options.passphrase !== void 0 ? { passphrase: options.passphrase } : resolveSecretPassphrase(process.env, this.filePath);
|
|
3635
|
+
const raw = resolved2.passphrase;
|
|
3499
3636
|
this.passphrase = raw && raw.trim() ? raw : void 0;
|
|
3637
|
+
this.keyProblem = resolved2.problem;
|
|
3500
3638
|
}
|
|
3501
3639
|
/**
|
|
3502
3640
|
* True when the *next write* will encrypt. This is the configured policy,
|
|
@@ -3511,7 +3649,8 @@ var FileSecretStore = class {
|
|
|
3511
3649
|
* no file yet, or when it cannot be read or parsed.
|
|
3512
3650
|
*
|
|
3513
3651
|
* Separate from {@link encrypted} because the two genuinely disagree for a
|
|
3514
|
-
* whole session: adding `MCP_INSPECTOR_SECRET_KEY`
|
|
3652
|
+
* whole session: adding a passphrase (`MCP_INSPECTOR_SECRET_KEY` or
|
|
3653
|
+
* `MCP_INSPECTOR_SECRET_KEY_FILE`) to an install that
|
|
3515
3654
|
* already has a plaintext file flips `encrypted` to true immediately,
|
|
3516
3655
|
* while the bytes stay readable until the next `set`. A descriptor built
|
|
3517
3656
|
* from the policy would tell that user "File (encrypted)" while their
|
|
@@ -3535,6 +3674,9 @@ var FileSecretStore = class {
|
|
|
3535
3674
|
* `set` is in fact about to refuse.
|
|
3536
3675
|
*/
|
|
3537
3676
|
async readOnDiskEncryption() {
|
|
3677
|
+
if (this.keyProblem !== void 0) {
|
|
3678
|
+
return { state: "unreadable", detail: this.keyProblem };
|
|
3679
|
+
}
|
|
3538
3680
|
let raw;
|
|
3539
3681
|
try {
|
|
3540
3682
|
raw = await readStoreFile(this.filePath);
|
|
@@ -3572,7 +3714,7 @@ var FileSecretStore = class {
|
|
|
3572
3714
|
} catch (err) {
|
|
3573
3715
|
return {
|
|
3574
3716
|
state: "unreadable",
|
|
3575
|
-
detail: err instanceof SecretFileKeyMismatchError ? `it cannot be decrypted with the current ${SECRET_KEY_ENV}` : err instanceof Error ? err.message : String(err)
|
|
3717
|
+
detail: err instanceof SecretFileKeyMismatchError ? `it cannot be decrypted with the current ${SECRET_KEY_ENV} (or ${SECRET_KEY_FILE_ENV})` : err instanceof Error ? err.message : String(err)
|
|
3576
3718
|
};
|
|
3577
3719
|
}
|
|
3578
3720
|
return { state: "encrypted" };
|
|
@@ -3623,6 +3765,11 @@ var FileSecretStore = class {
|
|
|
3623
3765
|
return this.serialize(() => this.readMap());
|
|
3624
3766
|
}
|
|
3625
3767
|
async readMap() {
|
|
3768
|
+
if (this.keyProblem !== void 0) {
|
|
3769
|
+
throw new SecretStoreUnavailableError(
|
|
3770
|
+
`The secrets file at ${this.filePath} cannot be used: ${this.keyProblem}. Refusing to read or write it rather than store secrets unencrypted.`
|
|
3771
|
+
);
|
|
3772
|
+
}
|
|
3626
3773
|
const raw = await readStoreFile(this.filePath);
|
|
3627
3774
|
if (raw === null) return null;
|
|
3628
3775
|
let parsed;
|
|
@@ -3978,6 +4125,7 @@ async function readSecretFilePermissions(filePath) {
|
|
|
3978
4125
|
var SECRET_STORE_ENV = "MCP_INSPECTOR_SECRET_STORE";
|
|
3979
4126
|
var SECRET_FILE_ENV = "MCP_INSPECTOR_SECRET_FILE";
|
|
3980
4127
|
var STORAGE_DIR_ENV = "MCP_STORAGE_DIR";
|
|
4128
|
+
var SECRET_STORAGE_DOCS_URL = "https://github.com/modelcontextprotocol/inspector/blob/main/docs/secret-storage.md";
|
|
3981
4129
|
var KINDS = ["keyring", "file", "memory"];
|
|
3982
4130
|
function parseSecretStoreEnv(raw) {
|
|
3983
4131
|
const value = raw?.trim().toLowerCase();
|
|
@@ -4120,6 +4268,11 @@ function warnAboutSecretStorage(info) {
|
|
|
4120
4268
|
}
|
|
4121
4269
|
const caveat = secretStorageCaveat(info);
|
|
4122
4270
|
if (caveat) console.warn(`[mcp-inspector] ${caveat}`);
|
|
4271
|
+
if (info.reason === "fallback" || caveat) {
|
|
4272
|
+
console.warn(
|
|
4273
|
+
`[mcp-inspector] How the secret store is chosen, and how to secure it: ${SECRET_STORAGE_DOCS_URL}`
|
|
4274
|
+
);
|
|
4275
|
+
}
|
|
4123
4276
|
}
|
|
4124
4277
|
var resolved;
|
|
4125
4278
|
function resolveSecretStore() {
|
|
@@ -5584,6 +5737,12 @@ function createRemoteApp(options) {
|
|
|
5584
5737
|
error: "settings.paginatedLists must be a boolean"
|
|
5585
5738
|
};
|
|
5586
5739
|
}
|
|
5740
|
+
if (obj.suppressNotificationStream !== void 0 && typeof obj.suppressNotificationStream !== "boolean") {
|
|
5741
|
+
return {
|
|
5742
|
+
ok: false,
|
|
5743
|
+
error: "settings.suppressNotificationStream must be a boolean"
|
|
5744
|
+
};
|
|
5745
|
+
}
|
|
5587
5746
|
if (obj.maxFetchRequests !== void 0 && (typeof obj.maxFetchRequests !== "number" || obj.maxFetchRequests < 0)) {
|
|
5588
5747
|
return {
|
|
5589
5748
|
ok: false,
|
|
@@ -5686,6 +5845,10 @@ function createRemoteApp(options) {
|
|
|
5686
5845
|
autoRefreshOnListChanged: obj.autoRefreshOnListChanged === true,
|
|
5687
5846
|
// Absent → false, matching the read side (omit-on-false on the write side).
|
|
5688
5847
|
paginatedLists: obj.paginatedLists === true,
|
|
5848
|
+
// Absent → unset (off); only an explicit true is carried (#2317).
|
|
5849
|
+
...obj.suppressNotificationStream === true && {
|
|
5850
|
+
suppressNotificationStream: true
|
|
5851
|
+
},
|
|
5689
5852
|
// Absent → product default, matching the read side. The default is the
|
|
5690
5853
|
// omit-sentinel in inspectorSettingsToStoredFields, so a client that
|
|
5691
5854
|
// didn't send one writes no spurious maxFetchRequests to disk.
|
|
@@ -5936,7 +6099,7 @@ function createRemoteApp(options) {
|
|
|
5936
6099
|
type,
|
|
5937
6100
|
process.platform,
|
|
5938
6101
|
homedir(),
|
|
5939
|
-
(path6) => existsSync3(path6) ?
|
|
6102
|
+
(path6) => existsSync3(path6) ? readFileSync6(path6, "utf-8") : null
|
|
5940
6103
|
);
|
|
5941
6104
|
if (!result) {
|
|
5942
6105
|
return c.json(
|
|
@@ -6745,7 +6908,7 @@ async function startViteDevServer(config) {
|
|
|
6745
6908
|
}
|
|
6746
6909
|
|
|
6747
6910
|
// server/server.ts
|
|
6748
|
-
import { readFileSync as
|
|
6911
|
+
import { readFileSync as readFileSync7 } from "fs";
|
|
6749
6912
|
import { join as join8, dirname as dirname8 } from "path";
|
|
6750
6913
|
import { fileURLToPath as fileURLToPath5 } from "url";
|
|
6751
6914
|
import { randomBytes as randomBytes4 } from "crypto";
|
|
@@ -6799,7 +6962,7 @@ async function startHonoServer(config) {
|
|
|
6799
6962
|
});
|
|
6800
6963
|
const serveIndexHtml = (c) => {
|
|
6801
6964
|
const indexPath = join8(rootPath, "index.html");
|
|
6802
|
-
const html =
|
|
6965
|
+
const html = readFileSync7(indexPath, "utf-8");
|
|
6803
6966
|
c.header("Cache-Control", "no-store");
|
|
6804
6967
|
return c.html(injectAuthToken(html, resolvedAuthToken));
|
|
6805
6968
|
};
|