@frontmcp/sdk 1.8.6 → 1.8.7
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/agent/flows/call-agent.flow.d.ts.map +1 -1
- package/auth/instances/instance.local-primary-auth.d.ts.map +1 -1
- package/common/dynamic/dynamic.adapter.d.ts +1 -1
- package/common/dynamic/dynamic.adapter.d.ts.map +1 -1
- package/common/dynamic/dynamic.plugin.d.ts +7 -1
- package/common/dynamic/dynamic.plugin.d.ts.map +1 -1
- package/common/dynamic/dynamic.utils.d.ts +10 -1
- package/common/dynamic/dynamic.utils.d.ts.map +1 -1
- package/common/interfaces/execution-context.interface.d.ts +14 -0
- package/common/interfaces/execution-context.interface.d.ts.map +1 -1
- package/common/metadata/front-mcp.metadata.d.ts +127 -19
- package/common/metadata/front-mcp.metadata.d.ts.map +1 -1
- package/common/types/options/http/index.d.ts +1 -1
- package/common/types/options/http/index.d.ts.map +1 -1
- package/common/types/options/http/interfaces.d.ts +36 -1
- package/common/types/options/http/interfaces.d.ts.map +1 -1
- package/common/types/options/http/schema.d.ts +12 -0
- package/common/types/options/http/schema.d.ts.map +1 -1
- package/common/types/options/transport/schema.d.ts +2 -2
- package/context/frontmcp-context-storage.d.ts.map +1 -1
- package/context/frontmcp-context.d.ts +11 -0
- package/context/frontmcp-context.d.ts.map +1 -1
- package/elicitation/store/elicitation-store.factory.d.ts.map +1 -1
- package/errors/misconfiguration.d.ts +30 -0
- package/errors/misconfiguration.d.ts.map +1 -0
- package/esm/index.mjs +912 -198
- package/flows/flow.instance.d.ts.map +1 -1
- package/front-mcp/front-mcp.d.ts.map +1 -1
- package/ha/ha-headers.d.ts +16 -0
- package/ha/ha-headers.d.ts.map +1 -0
- package/health/health.probes.d.ts +1 -1
- package/health/health.probes.d.ts.map +1 -1
- package/index.js +1016 -298
- package/job/store/job-definition-store.factory.d.ts.map +1 -1
- package/job/store/job-state-store.factory.d.ts.map +1 -1
- package/notification/notification.service.d.ts +4 -1
- package/notification/notification.service.d.ts.map +1 -1
- package/package.json +10 -10
- package/remote-mcp/mcp-stateless-client.adapter.d.ts +9 -1
- package/remote-mcp/mcp-stateless-client.adapter.d.ts.map +1 -1
- package/remote-mcp/resilience/health-check.d.ts.map +1 -1
- package/scope/scope.instance.d.ts +8 -0
- package/scope/scope.instance.d.ts.map +1 -1
- package/server/adapters/express.host.adapter.d.ts +6 -1
- package/server/adapters/express.host.adapter.d.ts.map +1 -1
- package/server/middleware/csp.middleware.d.ts +12 -1
- package/server/middleware/csp.middleware.d.ts.map +1 -1
- package/server/security/security-audit.d.ts.map +1 -1
- package/server/server.instance.d.ts.map +1 -1
- package/skill/cache/skill-http-cache.factory.d.ts +0 -5
- package/skill/cache/skill-http-cache.factory.d.ts.map +1 -1
- package/task/store/index.d.ts +1 -1
- package/task/store/index.d.ts.map +1 -1
- package/task/store/task-store.factory.d.ts +6 -0
- package/task/store/task-store.factory.d.ts.map +1 -1
- package/tool/flows/call-tool.flow.d.ts.map +1 -1
- package/tool/ui/ui-option-warnings.d.ts +3 -0
- package/tool/ui/ui-option-warnings.d.ts.map +1 -0
- package/tool/ui/ui-resource.handler.d.ts.map +1 -1
- package/tool/ui/ui-shared.d.ts +14 -0
- package/tool/ui/ui-shared.d.ts.map +1 -1
- package/transport/event-stores/redis.event-store.d.ts.map +1 -1
- package/transport/flows/handle.mcp-20260728.flow.d.ts.map +1 -1
- package/transport/flows/handle.sse.flow.d.ts.map +1 -1
- package/transport/flows/handle.stateless-http.flow.d.ts +2 -1
- package/transport/flows/handle.stateless-http.flow.d.ts.map +1 -1
- package/transport/flows/handle.streamable-http.flow.d.ts +2 -1
- package/transport/flows/handle.streamable-http.flow.d.ts.map +1 -1
- package/transport/ipc-server.transport.d.ts +26 -0
- package/transport/ipc-server.transport.d.ts.map +1 -0
- package/transport/mcp-20260728/client/mcp-stateless.client.d.ts +2 -0
- package/transport/mcp-20260728/client/mcp-stateless.client.d.ts.map +1 -1
- package/transport/transport.registry.d.ts +34 -0
- package/transport/transport.registry.d.ts.map +1 -1
- package/transport/web-fetch-handler.d.ts.map +1 -1
package/index.js
CHANGED
|
@@ -1859,7 +1859,7 @@ var init_session = __esm({
|
|
|
1859
1859
|
});
|
|
1860
1860
|
|
|
1861
1861
|
// libs/sdk/src/common/types/options/http/schema.ts
|
|
1862
|
-
var import_lazy_zod8, HTTP_METHODS, httpRouteSchema, bodyParserLimitSchema, corsOptionsSchema, httpOptionsSchema;
|
|
1862
|
+
var import_lazy_zod8, HTTP_METHODS, httpRouteSchema, bodyParserLimitSchema, corsOptionsSchema, securityHeaderValueSchema, securityHeadersSchema, httpOptionsSchema;
|
|
1863
1863
|
var init_schema3 = __esm({
|
|
1864
1864
|
"libs/sdk/src/common/types/options/http/schema.ts"() {
|
|
1865
1865
|
"use strict";
|
|
@@ -1897,6 +1897,19 @@ var init_schema3 = __esm({
|
|
|
1897
1897
|
credentials: import_lazy_zod8.z.boolean().optional(),
|
|
1898
1898
|
maxAge: import_lazy_zod8.z.number().optional()
|
|
1899
1899
|
});
|
|
1900
|
+
securityHeaderValueSchema = import_lazy_zod8.z.union([import_lazy_zod8.z.string(), import_lazy_zod8.z.literal(false)]);
|
|
1901
|
+
securityHeadersSchema = import_lazy_zod8.z.object({
|
|
1902
|
+
hsts: securityHeaderValueSchema.optional(),
|
|
1903
|
+
contentTypeOptions: securityHeaderValueSchema.optional(),
|
|
1904
|
+
frameOptions: securityHeaderValueSchema.optional(),
|
|
1905
|
+
custom: import_lazy_zod8.z.record(import_lazy_zod8.z.string(), import_lazy_zod8.z.string()).optional(),
|
|
1906
|
+
csp: import_lazy_zod8.z.object({
|
|
1907
|
+
enabled: import_lazy_zod8.z.boolean().optional(),
|
|
1908
|
+
directives: import_lazy_zod8.z.record(import_lazy_zod8.z.string(), import_lazy_zod8.z.union([import_lazy_zod8.z.string(), import_lazy_zod8.z.array(import_lazy_zod8.z.string())])).optional(),
|
|
1909
|
+
reportUri: import_lazy_zod8.z.string().optional(),
|
|
1910
|
+
reportOnly: import_lazy_zod8.z.boolean().optional()
|
|
1911
|
+
}).optional()
|
|
1912
|
+
});
|
|
1900
1913
|
httpOptionsSchema = import_lazy_zod8.z.object({
|
|
1901
1914
|
port: import_lazy_zod8.z.number().optional().default(Number(process.env["PORT"]) || 3e3),
|
|
1902
1915
|
// `FRONTMCP_HTTP_ENTRY_PATH` lets `frontmcp dev` propagate the configured
|
|
@@ -1955,7 +1968,9 @@ var init_schema3 = __esm({
|
|
|
1955
1968
|
* app as the MCP endpoint; public by default, `auth: true` opts into the MCP
|
|
1956
1969
|
* `session:verify` flow. Reserved-path collisions are rejected at startup.
|
|
1957
1970
|
*/
|
|
1958
|
-
routes: import_lazy_zod8.z.array(httpRouteSchema).optional()
|
|
1971
|
+
routes: import_lazy_zod8.z.array(httpRouteSchema).optional(),
|
|
1972
|
+
/** Response security headers; see `SecurityHeadersOptions`. */
|
|
1973
|
+
securityHeaders: securityHeadersSchema.optional()
|
|
1959
1974
|
});
|
|
1960
1975
|
}
|
|
1961
1976
|
});
|
|
@@ -2357,7 +2372,7 @@ var init_schema7 = __esm({
|
|
|
2357
2372
|
persistenceConfigSchema = import_lazy_zod12.z.object({
|
|
2358
2373
|
redis: redisOptionsSchema.optional(),
|
|
2359
2374
|
sqlite: sqliteOptionsSchema.optional(),
|
|
2360
|
-
defaultTtlMs: import_lazy_zod12.z.number().int().positive().
|
|
2375
|
+
defaultTtlMs: import_lazy_zod12.z.number().int().positive().optional()
|
|
2361
2376
|
}).refine((cfg) => !(cfg.redis && cfg.sqlite), {
|
|
2362
2377
|
message: "Invalid persistence config: choose either redis or sqlite, not both",
|
|
2363
2378
|
path: ["sqlite"]
|
|
@@ -6442,6 +6457,7 @@ var require_dist = __commonJS({
|
|
|
6442
6457
|
var __toCommonJS2 = (mod) => __copyProps2(__defProp2({}, "__esModule", { value: true }), mod);
|
|
6443
6458
|
var index_exports2 = {};
|
|
6444
6459
|
__export2(index_exports2, {
|
|
6460
|
+
SqliteDecryptionError: () => SqliteDecryptionError,
|
|
6445
6461
|
SqliteElicitationStore: () => SqliteElicitationStore,
|
|
6446
6462
|
SqliteEventStore: () => SqliteEventStore,
|
|
6447
6463
|
SqliteKvStore: () => SqliteKvStore,
|
|
@@ -6453,34 +6469,47 @@ var require_dist = __commonJS({
|
|
|
6453
6469
|
encryptValue: () => encryptValue,
|
|
6454
6470
|
loadBetterSqlite3: () => loadBetterSqlite3,
|
|
6455
6471
|
openDatabase: () => openDatabase,
|
|
6456
|
-
sqliteStorageOptionsSchema: () => sqliteStorageOptionsSchema
|
|
6472
|
+
sqliteStorageOptionsSchema: () => sqliteStorageOptionsSchema,
|
|
6473
|
+
withBusyRetry: () => withBusyRetry
|
|
6457
6474
|
});
|
|
6458
6475
|
module2.exports = __toCommonJS2(index_exports2);
|
|
6459
|
-
var
|
|
6476
|
+
var import_utils119 = require("@frontmcp/utils");
|
|
6477
|
+
var SqliteDecryptionError = class extends Error {
|
|
6478
|
+
code = "SQLITE_DECRYPTION_FAILED";
|
|
6479
|
+
constructor() {
|
|
6480
|
+
super(
|
|
6481
|
+
"SqliteKvStore: failed to decrypt a stored value. The encryption secret differs from the one the database was written with (or the value is corrupted). Restore the original secret, or delete the database file to start empty."
|
|
6482
|
+
);
|
|
6483
|
+
this.name = "SqliteDecryptionError";
|
|
6484
|
+
}
|
|
6485
|
+
};
|
|
6460
6486
|
var HKDF_SALT = new TextEncoder().encode("frontmcp-sqlite-storage-v1");
|
|
6461
6487
|
var HKDF_INFO = new TextEncoder().encode("aes-256-gcm-value-encryption");
|
|
6462
6488
|
var KEY_LENGTH = 32;
|
|
6463
6489
|
function deriveEncryptionKey(secret) {
|
|
6464
6490
|
const ikm = new TextEncoder().encode(secret);
|
|
6465
|
-
return (0,
|
|
6491
|
+
return (0, import_utils119.hkdfSha256)(ikm, HKDF_SALT, HKDF_INFO, KEY_LENGTH);
|
|
6466
6492
|
}
|
|
6467
6493
|
var SEPARATOR = ":";
|
|
6468
6494
|
function encryptValue(key, plaintext) {
|
|
6469
|
-
const iv = (0,
|
|
6495
|
+
const iv = (0, import_utils119.randomBytes)(12);
|
|
6470
6496
|
const plaintextBytes = new TextEncoder().encode(plaintext);
|
|
6471
|
-
const { ciphertext, tag } = (0,
|
|
6472
|
-
return [(0,
|
|
6497
|
+
const { ciphertext, tag } = (0, import_utils119.encryptAesGcm)(key, plaintextBytes, iv);
|
|
6498
|
+
return [(0, import_utils119.base64urlEncode)(iv), (0, import_utils119.base64urlEncode)(tag), (0, import_utils119.base64urlEncode)(ciphertext)].join(SEPARATOR);
|
|
6473
6499
|
}
|
|
6474
6500
|
function decryptValue(key, encrypted) {
|
|
6475
6501
|
const parts = encrypted.split(SEPARATOR);
|
|
6476
6502
|
if (parts.length !== 3) {
|
|
6477
6503
|
throw new Error("Invalid encrypted value format");
|
|
6478
6504
|
}
|
|
6479
|
-
const iv = (0,
|
|
6480
|
-
const tag = (0,
|
|
6481
|
-
const ciphertext = (0,
|
|
6482
|
-
|
|
6483
|
-
|
|
6505
|
+
const iv = (0, import_utils119.base64urlDecode)(parts[0]);
|
|
6506
|
+
const tag = (0, import_utils119.base64urlDecode)(parts[1]);
|
|
6507
|
+
const ciphertext = (0, import_utils119.base64urlDecode)(parts[2]);
|
|
6508
|
+
try {
|
|
6509
|
+
return new TextDecoder().decode((0, import_utils119.decryptAesGcm)(key, ciphertext, iv, tag));
|
|
6510
|
+
} catch {
|
|
6511
|
+
throw new SqliteDecryptionError();
|
|
6512
|
+
}
|
|
6484
6513
|
}
|
|
6485
6514
|
var import_utils210 = require("@frontmcp/utils");
|
|
6486
6515
|
var cached = null;
|
|
@@ -6493,7 +6522,27 @@ var require_dist = __commonJS({
|
|
|
6493
6522
|
return mod;
|
|
6494
6523
|
}
|
|
6495
6524
|
var MEMORY_PATH = ":memory:";
|
|
6496
|
-
|
|
6525
|
+
var DEFAULT_BUSY_TIMEOUT_MS = 5e3;
|
|
6526
|
+
function isBusyError(err) {
|
|
6527
|
+
const code = err?.code;
|
|
6528
|
+
return code === "SQLITE_BUSY" || code === "SQLITE_BUSY_RECOVERY" || code === "SQLITE_BUSY_SNAPSHOT";
|
|
6529
|
+
}
|
|
6530
|
+
function sleepSync(ms) {
|
|
6531
|
+
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
|
|
6532
|
+
}
|
|
6533
|
+
function withBusyRetry(operation, options = {}) {
|
|
6534
|
+
const attempts = options.attempts ?? 5;
|
|
6535
|
+
const delayMs = options.delayMs ?? 50;
|
|
6536
|
+
for (let attempt = 1; ; attempt++) {
|
|
6537
|
+
try {
|
|
6538
|
+
return operation();
|
|
6539
|
+
} catch (err) {
|
|
6540
|
+
if (!isBusyError(err) || attempt >= attempts) throw err;
|
|
6541
|
+
sleepSync(delayMs * attempt);
|
|
6542
|
+
}
|
|
6543
|
+
}
|
|
6544
|
+
}
|
|
6545
|
+
function openDatabase(path3, storeName, options = {}) {
|
|
6497
6546
|
const BetterSqlite3 = loadBetterSqlite3();
|
|
6498
6547
|
if (path3 !== MEMORY_PATH && path3 !== "") {
|
|
6499
6548
|
const dir = (0, import_utils210.dirname)(path3);
|
|
@@ -6502,16 +6551,28 @@ var require_dist = __commonJS({
|
|
|
6502
6551
|
(0, import_utils210.ensureDirSync)(dir);
|
|
6503
6552
|
} catch (err) {
|
|
6504
6553
|
const message = err instanceof Error ? err.message : String(err);
|
|
6505
|
-
throw new Error(`${storeName}: failed to create directory "${dir}" for database "${path3}": ${message}
|
|
6554
|
+
throw new Error(`${storeName}: failed to create directory "${dir}" for database "${path3}": ${message}`, {
|
|
6555
|
+
cause: err
|
|
6556
|
+
});
|
|
6506
6557
|
}
|
|
6507
6558
|
}
|
|
6508
6559
|
}
|
|
6560
|
+
let db;
|
|
6561
|
+
try {
|
|
6562
|
+
db = new BetterSqlite3(path3);
|
|
6563
|
+
} catch (err) {
|
|
6564
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
6565
|
+
throw new Error(`${storeName}: failed to open database at "${path3}": ${message}`, { cause: err });
|
|
6566
|
+
}
|
|
6509
6567
|
try {
|
|
6510
|
-
|
|
6568
|
+
db.pragma(`busy_timeout = ${Math.max(0, Math.floor(options.busyTimeoutMs ?? DEFAULT_BUSY_TIMEOUT_MS))}`);
|
|
6569
|
+
if (options.walMode) withBusyRetry(() => db.pragma("journal_mode = WAL"));
|
|
6511
6570
|
} catch (err) {
|
|
6571
|
+
db.close();
|
|
6512
6572
|
const message = err instanceof Error ? err.message : String(err);
|
|
6513
|
-
throw new Error(`${storeName}: failed to
|
|
6573
|
+
throw new Error(`${storeName}: failed to configure database at "${path3}": ${message}`, { cause: err });
|
|
6514
6574
|
}
|
|
6575
|
+
return db;
|
|
6515
6576
|
}
|
|
6516
6577
|
var SqliteKvStore = class {
|
|
6517
6578
|
db;
|
|
@@ -6519,14 +6580,14 @@ var require_dist = __commonJS({
|
|
|
6519
6580
|
cleanupTimer = null;
|
|
6520
6581
|
stmts = null;
|
|
6521
6582
|
constructor(options) {
|
|
6522
|
-
this.db = openDatabase(options.path, "SqliteKvStore"
|
|
6523
|
-
|
|
6524
|
-
|
|
6525
|
-
}
|
|
6583
|
+
this.db = openDatabase(options.path, "SqliteKvStore", {
|
|
6584
|
+
busyTimeoutMs: options.busyTimeoutMs,
|
|
6585
|
+
walMode: options.walMode !== false
|
|
6586
|
+
});
|
|
6526
6587
|
if (options.encryption?.secret) {
|
|
6527
6588
|
this.encryptionKey = deriveEncryptionKey(options.encryption.secret);
|
|
6528
6589
|
}
|
|
6529
|
-
this.initSchema();
|
|
6590
|
+
withBusyRetry(() => this.initSchema());
|
|
6530
6591
|
this.prepareStatements();
|
|
6531
6592
|
const cleanupInterval = options.ttlCleanupIntervalMs ?? 6e4;
|
|
6532
6593
|
if (cleanupInterval > 0) {
|
|
@@ -6985,17 +7046,17 @@ var require_dist = __commonJS({
|
|
|
6985
7046
|
encryptionKey = null;
|
|
6986
7047
|
stmts = null;
|
|
6987
7048
|
constructor(options) {
|
|
6988
|
-
this.db = openDatabase(options.path, "SqliteTaskStore"
|
|
6989
|
-
|
|
6990
|
-
|
|
6991
|
-
}
|
|
7049
|
+
this.db = openDatabase(options.path, "SqliteTaskStore", {
|
|
7050
|
+
busyTimeoutMs: options.busyTimeoutMs,
|
|
7051
|
+
walMode: options.walMode !== false
|
|
7052
|
+
});
|
|
6992
7053
|
this.logger = options.logger;
|
|
6993
7054
|
this.liveness = options.livenessProbe ?? defaultLivenessProbe;
|
|
6994
7055
|
if (options.encryption?.secret) {
|
|
6995
7056
|
this.encryptionKey = deriveEncryptionKey(options.encryption.secret);
|
|
6996
7057
|
}
|
|
6997
7058
|
this.emitter.setMaxListeners(200);
|
|
6998
|
-
this.initSchema();
|
|
7059
|
+
withBusyRetry(() => this.initSchema());
|
|
6999
7060
|
this.prepareStatements();
|
|
7000
7061
|
const cleanupInterval = options.ttlCleanupIntervalMs ?? 6e4;
|
|
7001
7062
|
if (cleanupInterval > 0) {
|
|
@@ -7232,13 +7293,13 @@ var require_dist = __commonJS({
|
|
|
7232
7293
|
cleanupTimer = null;
|
|
7233
7294
|
stmts = null;
|
|
7234
7295
|
constructor(options) {
|
|
7235
|
-
this.db = openDatabase(options.path, "SqliteEventStore"
|
|
7236
|
-
|
|
7237
|
-
|
|
7238
|
-
}
|
|
7296
|
+
this.db = openDatabase(options.path, "SqliteEventStore", {
|
|
7297
|
+
busyTimeoutMs: options.busyTimeoutMs,
|
|
7298
|
+
walMode: options.walMode !== false
|
|
7299
|
+
});
|
|
7239
7300
|
this.maxEvents = options.maxEvents ?? 1e4;
|
|
7240
7301
|
this.ttlMs = options.ttlMs ?? 3e5;
|
|
7241
|
-
this.initSchema();
|
|
7302
|
+
withBusyRetry(() => this.initSchema());
|
|
7242
7303
|
this.prepareStatements();
|
|
7243
7304
|
const cleanupInterval = options.ttlCleanupIntervalMs ?? 6e4;
|
|
7244
7305
|
if (cleanupInterval > 0) {
|
|
@@ -7373,7 +7434,8 @@ var require_dist = __commonJS({
|
|
|
7373
7434
|
secret: import_lazy_zod113.z.string().min(1)
|
|
7374
7435
|
}).optional(),
|
|
7375
7436
|
ttlCleanupIntervalMs: import_lazy_zod113.z.number().int().nonnegative().optional().default(6e4),
|
|
7376
|
-
walMode: import_lazy_zod113.z.boolean().optional().default(true)
|
|
7437
|
+
walMode: import_lazy_zod113.z.boolean().optional().default(true),
|
|
7438
|
+
busyTimeoutMs: import_lazy_zod113.z.number().int().nonnegative().optional().default(5e3)
|
|
7377
7439
|
});
|
|
7378
7440
|
}
|
|
7379
7441
|
});
|
|
@@ -7391,6 +7453,14 @@ function detectStorageType(storage, configuredType) {
|
|
|
7391
7453
|
if ((0, import_utils15.getEnv)("REDIS_URL") || (0, import_utils15.getEnv)("REDIS_HOST")) return "redis";
|
|
7392
7454
|
return "memory";
|
|
7393
7455
|
}
|
|
7456
|
+
function isExplicitStorage(storage) {
|
|
7457
|
+
return Boolean(storage?.type && storage.type !== "auto");
|
|
7458
|
+
}
|
|
7459
|
+
function resolvesToVercelKvTaskBackend(redis, storage) {
|
|
7460
|
+
if (isExplicitStorage(storage)) return storage.type === "vercel-kv";
|
|
7461
|
+
if (redis) return "provider" in redis && redis.provider === "vercel-kv";
|
|
7462
|
+
return Boolean((0, import_utils15.getEnv)("KV_REST_API_URL"));
|
|
7463
|
+
}
|
|
7394
7464
|
async function createTaskStore(options = {}) {
|
|
7395
7465
|
const { storage: storageConfig, redis, keyPrefix = "mcp:task:", logger, isEdgeRuntime: isEdgeRuntime3 = false, sqlite } = options;
|
|
7396
7466
|
if (isEdgeRuntime3 && sqlite) {
|
|
@@ -7415,7 +7485,7 @@ async function createTaskStore(options = {}) {
|
|
|
7415
7485
|
return { store: store2, type: "sqlite" };
|
|
7416
7486
|
}
|
|
7417
7487
|
let finalStorageConfig = storageConfig;
|
|
7418
|
-
if (redis && !storageConfig) {
|
|
7488
|
+
if (redis && !isExplicitStorage(storageConfig)) {
|
|
7419
7489
|
if ("provider" in redis && redis.provider === "vercel-kv") {
|
|
7420
7490
|
throw new TaskStoreNotSupportedError(
|
|
7421
7491
|
"Vercel KV is not supported for task stores. Task result blocking and cancel signalling require pub/sub. Use Redis or Upstash instead."
|
|
@@ -7443,7 +7513,8 @@ async function createTaskStore(options = {}) {
|
|
|
7443
7513
|
};
|
|
7444
7514
|
}
|
|
7445
7515
|
}
|
|
7446
|
-
|
|
7516
|
+
const explicitBackend = isExplicitStorage(storageConfig) || Boolean(redis);
|
|
7517
|
+
if (finalStorageConfig?.type === "vercel-kv" || !explicitBackend && (0, import_utils15.getEnv)("KV_REST_API_URL")) {
|
|
7447
7518
|
throw new TaskStoreNotSupportedError(
|
|
7448
7519
|
"Vercel KV is not supported for task stores (pub/sub required). Use Redis or Upstash."
|
|
7449
7520
|
);
|
|
@@ -8684,11 +8755,8 @@ var init_get_prompt_request_handler = __esm({
|
|
|
8684
8755
|
|
|
8685
8756
|
// libs/sdk/src/notification/notification.service.ts
|
|
8686
8757
|
function hasMcpAppsExtension(capabilities) {
|
|
8687
|
-
|
|
8688
|
-
|
|
8689
|
-
}
|
|
8690
|
-
const uiExtension = capabilities.experimental[MCP_APPS_EXTENSION_KEY];
|
|
8691
|
-
return uiExtension !== void 0 && uiExtension !== null;
|
|
8758
|
+
const declared = capabilities?.experimental?.[MCP_APPS_EXTENSION_KEY] ?? capabilities?.extensions?.[MCP_APPS_EXTENSION_KEY];
|
|
8759
|
+
return declared !== void 0 && declared !== null;
|
|
8692
8760
|
}
|
|
8693
8761
|
function detectPlatformFromCapabilities(capabilities) {
|
|
8694
8762
|
if (hasMcpAppsExtension(capabilities)) {
|
|
@@ -11290,6 +11358,8 @@ var init_ui_shared = __esm({
|
|
|
11290
11358
|
widgets = /* @__PURE__ */ new Map();
|
|
11291
11359
|
manifests = /* @__PURE__ */ new Map();
|
|
11292
11360
|
resourceMeta = /* @__PURE__ */ new Map();
|
|
11361
|
+
uiTools = /* @__PURE__ */ new Set();
|
|
11362
|
+
customUris = /* @__PURE__ */ new Map();
|
|
11293
11363
|
resolver;
|
|
11294
11364
|
options;
|
|
11295
11365
|
constructor(resolver, options = {}) {
|
|
@@ -11304,6 +11374,28 @@ var init_ui_shared = __esm({
|
|
|
11304
11374
|
logger: this.options.logger
|
|
11305
11375
|
};
|
|
11306
11376
|
}
|
|
11377
|
+
/**
|
|
11378
|
+
* Record that `toolName` has a UI, and the custom `ui.resourceUri` it advertises (if any).
|
|
11379
|
+
* `tools/list` advertises that URI verbatim, so `resources/read` must resolve it back to the
|
|
11380
|
+
* tool; and only registered tools may be served a placeholder widget.
|
|
11381
|
+
*/
|
|
11382
|
+
registerTool(toolName, resourceUri) {
|
|
11383
|
+
this.uiTools.add(toolName);
|
|
11384
|
+
if (resourceUri) this.customUris.set(resourceUri, toolName);
|
|
11385
|
+
}
|
|
11386
|
+
/** The tool that advertised `uri` as its custom `ui.resourceUri`. */
|
|
11387
|
+
resolveCustomUri(uri) {
|
|
11388
|
+
return this.customUris.get(uri);
|
|
11389
|
+
}
|
|
11390
|
+
/**
|
|
11391
|
+
* Whether `name` (or its app-qualified form `app:name`) is a tool with a UI.
|
|
11392
|
+
*/
|
|
11393
|
+
hasTool(name44) {
|
|
11394
|
+
const known = (n) => this.uiTools.has(n) || this.widgets.has(n) || this.resourceMeta.has(n);
|
|
11395
|
+
if (known(name44)) return true;
|
|
11396
|
+
const sep = name44.indexOf(":");
|
|
11397
|
+
return sep > 0 && known(name44.slice(sep + 1));
|
|
11398
|
+
}
|
|
11307
11399
|
getStaticWidget(name44) {
|
|
11308
11400
|
return this.widgets.get(name44);
|
|
11309
11401
|
}
|
|
@@ -11332,8 +11424,9 @@ var init_ui_shared = __esm({
|
|
|
11332
11424
|
const output = options["output"] ?? {};
|
|
11333
11425
|
const uiConfig = options["uiConfig"];
|
|
11334
11426
|
const resourceMode = uiConfig?.["resourceMode"] ?? options["resourceMode"];
|
|
11335
|
-
if (!toolName
|
|
11427
|
+
if (!toolName) return;
|
|
11336
11428
|
this.updateResourceMetaFromConfig(toolName, uiConfig);
|
|
11429
|
+
if (!template) return;
|
|
11337
11430
|
const result = (0, import_adapters.renderToolTemplate)({
|
|
11338
11431
|
toolName,
|
|
11339
11432
|
input,
|
|
@@ -11342,6 +11435,7 @@ var init_ui_shared = __esm({
|
|
|
11342
11435
|
resolver: this.resolver,
|
|
11343
11436
|
resourceMode,
|
|
11344
11437
|
sizing: extractSizing(uiConfig),
|
|
11438
|
+
csp: uiConfig?.["csp"],
|
|
11345
11439
|
...this.stringResultOptions(uiConfig)
|
|
11346
11440
|
});
|
|
11347
11441
|
this.widgets.set(toolName, result.html);
|
|
@@ -11420,6 +11514,7 @@ var init_ui_shared = __esm({
|
|
|
11420
11514
|
resolver: this.resolver,
|
|
11421
11515
|
resourceMode,
|
|
11422
11516
|
sizing: extractSizing(uiConfig),
|
|
11517
|
+
csp: uiConfig?.["csp"],
|
|
11423
11518
|
...this.stringResultOptions(uiConfig)
|
|
11424
11519
|
});
|
|
11425
11520
|
return { meta: result.meta };
|
|
@@ -11472,26 +11567,14 @@ function handleUIResourceRead(uri, registry, platformType) {
|
|
|
11472
11567
|
return { handled: false };
|
|
11473
11568
|
}
|
|
11474
11569
|
const mimeType = getUIResourceMimeType(platformType);
|
|
11475
|
-
const
|
|
11570
|
+
const customTool = registry.resolveCustomUri(uri);
|
|
11571
|
+
const widgetParsed = customTool ? { toolName: customTool } : parseWidgetUri(uri);
|
|
11476
11572
|
if (widgetParsed) {
|
|
11477
|
-
|
|
11478
|
-
|
|
11479
|
-
if (cachedWidget) {
|
|
11480
|
-
return {
|
|
11481
|
-
handled: true,
|
|
11482
|
-
result: {
|
|
11483
|
-
contents: [
|
|
11484
|
-
{
|
|
11485
|
-
uri,
|
|
11486
|
-
mimeType,
|
|
11487
|
-
text: cachedWidget,
|
|
11488
|
-
...resourceMeta ? { _meta: resourceMeta } : {}
|
|
11489
|
-
}
|
|
11490
|
-
]
|
|
11491
|
-
}
|
|
11492
|
-
};
|
|
11573
|
+
if (!registry.hasTool(widgetParsed.toolName)) {
|
|
11574
|
+
return { handled: true, error: `No UI widget is registered for ${uri}` };
|
|
11493
11575
|
}
|
|
11494
|
-
const
|
|
11576
|
+
const resourceMeta = buildResourceMetaForWidget(registry, widgetParsed.toolName);
|
|
11577
|
+
const html = registry.getStaticWidget(widgetParsed.toolName) ?? generatePlaceholderWidget(widgetParsed.toolName);
|
|
11495
11578
|
return {
|
|
11496
11579
|
handled: true,
|
|
11497
11580
|
result: {
|
|
@@ -12234,19 +12317,34 @@ var init_direct_client = __esm({
|
|
|
12234
12317
|
// Resource Operations (raw format)
|
|
12235
12318
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
12236
12319
|
async listResources() {
|
|
12237
|
-
|
|
12320
|
+
const resources = await listAllPages("resources/list", async (cursor) => {
|
|
12321
|
+
const result = await this.mcpClient.listResources(cursor ? { cursor } : void 0);
|
|
12322
|
+
return { items: result.resources, nextCursor: result.nextCursor };
|
|
12323
|
+
});
|
|
12324
|
+
return { resources };
|
|
12238
12325
|
}
|
|
12239
12326
|
async readResource(uri) {
|
|
12240
12327
|
return this.mcpClient.readResource({ uri });
|
|
12241
12328
|
}
|
|
12242
12329
|
async listResourceTemplates() {
|
|
12243
|
-
|
|
12330
|
+
const resourceTemplates = await listAllPages(
|
|
12331
|
+
"resources/templates/list",
|
|
12332
|
+
async (cursor) => {
|
|
12333
|
+
const result = await this.mcpClient.listResourceTemplates(cursor ? { cursor } : void 0);
|
|
12334
|
+
return { items: result.resourceTemplates, nextCursor: result.nextCursor };
|
|
12335
|
+
}
|
|
12336
|
+
);
|
|
12337
|
+
return { resourceTemplates };
|
|
12244
12338
|
}
|
|
12245
12339
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
12246
12340
|
// Prompt Operations (raw format)
|
|
12247
12341
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
12248
12342
|
async listPrompts() {
|
|
12249
|
-
|
|
12343
|
+
const prompts = await listAllPages("prompts/list", async (cursor) => {
|
|
12344
|
+
const result = await this.mcpClient.listPrompts(cursor ? { cursor } : void 0);
|
|
12345
|
+
return { items: result.prompts, nextCursor: result.nextCursor };
|
|
12346
|
+
});
|
|
12347
|
+
return { prompts };
|
|
12250
12348
|
}
|
|
12251
12349
|
async getPrompt(name44, args) {
|
|
12252
12350
|
return this.mcpClient.getPrompt({
|
|
@@ -12974,8 +13072,8 @@ function createRemoteAppProbe(appId, provider) {
|
|
|
12974
13072
|
name: `remote:${appId}`,
|
|
12975
13073
|
async check() {
|
|
12976
13074
|
const result = provider.getHealthStatus(appId);
|
|
12977
|
-
if (!result) {
|
|
12978
|
-
return { status: "
|
|
13075
|
+
if (!result || result.status === "unknown" && result.consecutiveFailures === 0) {
|
|
13076
|
+
return { status: "degraded", details: { state: "unknown" } };
|
|
12979
13077
|
}
|
|
12980
13078
|
return {
|
|
12981
13079
|
status: result.status === "healthy" ? "healthy" : result.status === "degraded" ? "degraded" : "unhealthy",
|
|
@@ -14091,6 +14189,12 @@ var init_frontmcp_context = __esm({
|
|
|
14091
14189
|
// =====================
|
|
14092
14190
|
/** Request metadata (headers, user-agent, etc.) */
|
|
14093
14191
|
metadata;
|
|
14192
|
+
/**
|
|
14193
|
+
* The hosting platform's bindings object for this request: a Cloudflare Worker's `env`
|
|
14194
|
+
* (KV namespaces, D1 databases, R2 buckets, Durable Objects, `[vars]`, secrets).
|
|
14195
|
+
* `undefined` on Node, where bindings do not exist.
|
|
14196
|
+
*/
|
|
14197
|
+
platformEnv;
|
|
14094
14198
|
// =====================
|
|
14095
14199
|
// Auth (mutable, progressively populated)
|
|
14096
14200
|
// =====================
|
|
@@ -14126,6 +14230,7 @@ var init_frontmcp_context = __esm({
|
|
|
14126
14230
|
this.traceContext = args.traceContext ?? generateTraceContext();
|
|
14127
14231
|
this.timestamp = args.timestamp ?? Date.now();
|
|
14128
14232
|
this._authInfo = args.authInfo ?? {};
|
|
14233
|
+
this.platformEnv = args.platformEnv;
|
|
14129
14234
|
this.config = {
|
|
14130
14235
|
forwardCallerTokenTo: toOrigins(args.config?.forwardCallerTokenTo),
|
|
14131
14236
|
forwardCustomHeadersTo: toOrigins(args.config?.forwardCustomHeadersTo),
|
|
@@ -14828,6 +14933,7 @@ var init_frontmcp_context_storage = __esm({
|
|
|
14828
14933
|
import_utils39 = require("@frontmcp/utils");
|
|
14829
14934
|
init_decorators();
|
|
14830
14935
|
init_metadata();
|
|
14936
|
+
init_tokens();
|
|
14831
14937
|
init_mcp_error();
|
|
14832
14938
|
init_frontmcp_context();
|
|
14833
14939
|
init_metadata_utils();
|
|
@@ -14895,7 +15001,12 @@ var init_frontmcp_context_storage = __esm({
|
|
|
14895
15001
|
const headers = request.headers ?? {};
|
|
14896
15002
|
const headerSessionId = typeof headers["mcp-session-id"] === "string" ? headers["mcp-session-id"].trim() : "";
|
|
14897
15003
|
const sessionId = headerSessionId.length > 0 ? headerSessionId : `anon:${(0, import_utils39.randomUUID)()}`;
|
|
14898
|
-
|
|
15004
|
+
const platformEnv = request[ServerRequestTokens.webEnv];
|
|
15005
|
+
return this.runFromHeaders(
|
|
15006
|
+
headers,
|
|
15007
|
+
{ sessionId, scopeId, peerAddress: request.socket?.remoteAddress, platformEnv },
|
|
15008
|
+
fn
|
|
15009
|
+
);
|
|
14899
15010
|
}
|
|
14900
15011
|
withServerConfig(args) {
|
|
14901
15012
|
return { ...args, config: { ...this.contextConfig, ...args.config } };
|
|
@@ -21948,6 +22059,14 @@ var init_tool_credentials_gate = __esm({
|
|
|
21948
22059
|
});
|
|
21949
22060
|
|
|
21950
22061
|
// libs/sdk/src/tool/flows/call-tool.flow.ts
|
|
22062
|
+
function declaredOutput(tool, output) {
|
|
22063
|
+
if (!tool?.outputSchema || typeof output !== "object" || output === null || Array.isArray(output)) return output;
|
|
22064
|
+
if (Array.isArray(output["content"])) return output;
|
|
22065
|
+
const parsed = tool.safeParseOutput(output);
|
|
22066
|
+
if (!parsed.success) return output;
|
|
22067
|
+
const structured = parsed.data.structuredContent;
|
|
22068
|
+
return typeof structured === "object" && structured !== null && !Array.isArray(structured) ? structured : output;
|
|
22069
|
+
}
|
|
21951
22070
|
function toolNameCandidates(name44) {
|
|
21952
22071
|
if (!/[-_]/.test(name44)) return [name44];
|
|
21953
22072
|
return [name44, name44.includes("_") ? name44.replace(/_/g, "-") : name44.replace(/-/g, "_")];
|
|
@@ -22681,7 +22800,7 @@ ${JSON.stringify(error.schema, null, 2)}
|
|
|
22681
22800
|
this.logger.verbose("execute:done (elicitation fire-and-forget fallback)");
|
|
22682
22801
|
return;
|
|
22683
22802
|
}
|
|
22684
|
-
if (isClientFacingError(error)) throw error;
|
|
22803
|
+
if (isClientFacingError(error) || error instanceof InvalidOutputError) throw error;
|
|
22685
22804
|
throw new ToolExecutionError(
|
|
22686
22805
|
this.state.tool?.metadata.name || "unknown",
|
|
22687
22806
|
error instanceof Error ? error : void 0
|
|
@@ -22724,7 +22843,8 @@ ${JSON.stringify(error.schema, null, 2)}
|
|
|
22724
22843
|
}
|
|
22725
22844
|
async applyUI() {
|
|
22726
22845
|
this.logger.verbose("applyUI:start");
|
|
22727
|
-
const { tool, rawOutput, input } = this.state;
|
|
22846
|
+
const { tool, rawOutput: executeOutput, input } = this.state;
|
|
22847
|
+
const rawOutput = declaredOutput(tool, executeOutput);
|
|
22728
22848
|
const ctx = this.input.ctx;
|
|
22729
22849
|
if (ctx?._skipUI) {
|
|
22730
22850
|
this.logger.verbose("applyUI:skip (agent context - structured data only)");
|
|
@@ -24350,6 +24470,38 @@ var init_context = __esm({
|
|
|
24350
24470
|
}
|
|
24351
24471
|
});
|
|
24352
24472
|
|
|
24473
|
+
// libs/sdk/src/errors/misconfiguration.ts
|
|
24474
|
+
function findMisconfiguration(error) {
|
|
24475
|
+
let current = error;
|
|
24476
|
+
for (let depth = 0; current instanceof Error && depth < 5; depth++) {
|
|
24477
|
+
const code = current.code;
|
|
24478
|
+
if (typeof code === "string" && Object.prototype.hasOwnProperty.call(MISCONFIGURATION_REMEDIES, code)) {
|
|
24479
|
+
return { code, remedy: MISCONFIGURATION_REMEDIES[code] };
|
|
24480
|
+
}
|
|
24481
|
+
if (current.name === "ZodError") return { code: "CONFIG_INVALID", remedy: CONFIG_INVALID_REMEDY };
|
|
24482
|
+
current = current.cause;
|
|
24483
|
+
}
|
|
24484
|
+
return void 0;
|
|
24485
|
+
}
|
|
24486
|
+
function misconfigurationBody(m) {
|
|
24487
|
+
return { error: "server_misconfigured", code: m.code, message: m.remedy };
|
|
24488
|
+
}
|
|
24489
|
+
var MISCONFIGURATION_REMEDIES, CONFIG_INVALID_REMEDY;
|
|
24490
|
+
var init_misconfiguration = __esm({
|
|
24491
|
+
"libs/sdk/src/errors/misconfiguration.ts"() {
|
|
24492
|
+
"use strict";
|
|
24493
|
+
MISCONFIGURATION_REMEDIES = {
|
|
24494
|
+
SESSION_SECRET_REQUIRED: "Set MCP_SESSION_SECRET in the deployment environment (e.g. `wrangler secret put MCP_SESSION_SECRET` on Cloudflare, or the platform environment settings on Node, Vercel and Lambda). Session IDs are encrypted with it, and production refuses the development machine-id fallback.",
|
|
24495
|
+
JWT_SECRET_INVALID: "JWT_SECRET is present but too weak: HS256 requires at least 32 bytes (RFC 7518). Replace it with `openssl rand -hex 32`.",
|
|
24496
|
+
JWT_SECRET_REQUIRED: "Set JWT_SECRET in the deployment environment (e.g. `wrangler secret put JWT_SECRET`). Tokens are signed with it; production refuses the random per-process fallback because tokens would not survive a restart or verify across instances.",
|
|
24497
|
+
// The startup checks: a server whose entries ask for protection nothing gives them does not start.
|
|
24498
|
+
UNENFORCED_METADATA: "An entry declares a field only a plugin enforces (approval, featureFlag, ...) and no installed plugin that enforces it reaches the entry, so the server refuses to start. Install the plugin or remove the field; the server log names the entries.",
|
|
24499
|
+
AUTH_CONFIGURATION_ERROR: "The auth or authorities configuration is invalid (for example, entries declare authorities and the server has no authorities option, or a rule checks nothing), so the server refuses to start. The server log names the entries."
|
|
24500
|
+
};
|
|
24501
|
+
CONFIG_INVALID_REMEDY = "The FrontMCP configuration failed validation, so the server refuses to start. The server log names the invalid fields.";
|
|
24502
|
+
}
|
|
24503
|
+
});
|
|
24504
|
+
|
|
24353
24505
|
// libs/sdk/src/server/server.validation.ts
|
|
24354
24506
|
function applyHeaders(res, headers) {
|
|
24355
24507
|
if (!headers) return;
|
|
@@ -24662,6 +24814,7 @@ var init_flow_instance = __esm({
|
|
|
24662
24814
|
init_common();
|
|
24663
24815
|
init_context();
|
|
24664
24816
|
init_errors();
|
|
24817
|
+
init_misconfiguration();
|
|
24665
24818
|
init_server_validation();
|
|
24666
24819
|
init_flow_http_path();
|
|
24667
24820
|
init_flow_stages();
|
|
@@ -24721,6 +24874,15 @@ var init_flow_instance = __esm({
|
|
|
24721
24874
|
return writeHttpResponse(response, e.output);
|
|
24722
24875
|
}
|
|
24723
24876
|
}
|
|
24877
|
+
const misconfiguration = findMisconfiguration(e);
|
|
24878
|
+
if (misconfiguration) {
|
|
24879
|
+
this.logger.error("Server misconfigured", { flow: this.name, code: misconfiguration.code });
|
|
24880
|
+
return writeHttpResponse(response, {
|
|
24881
|
+
kind: "json",
|
|
24882
|
+
status: 500,
|
|
24883
|
+
body: misconfigurationBody(misconfiguration)
|
|
24884
|
+
});
|
|
24885
|
+
}
|
|
24724
24886
|
if (e instanceof PublicMcpError) {
|
|
24725
24887
|
const challenge = e.wwwAuthenticate;
|
|
24726
24888
|
this.logger.warn("Public error in flow", {
|
|
@@ -25433,14 +25595,39 @@ function collectDynamicProviders(klass, options) {
|
|
|
25433
25595
|
}
|
|
25434
25596
|
return out;
|
|
25435
25597
|
}
|
|
25598
|
+
function collectDynamicTools(klass, options) {
|
|
25599
|
+
const chain = [];
|
|
25600
|
+
for (let k = klass; k && k !== Function.prototype; k = Object.getPrototypeOf(k)) {
|
|
25601
|
+
chain.push(k);
|
|
25602
|
+
}
|
|
25603
|
+
const out = [];
|
|
25604
|
+
for (let i = chain.length - 1; i >= 0; i--) {
|
|
25605
|
+
const k = chain[i];
|
|
25606
|
+
if (Object.prototype.hasOwnProperty.call(k, "dynamicTools") && typeof k.dynamicTools === "function") {
|
|
25607
|
+
out.push(...k.dynamicTools(options) ?? []);
|
|
25608
|
+
}
|
|
25609
|
+
}
|
|
25610
|
+
return out;
|
|
25611
|
+
}
|
|
25612
|
+
function pluginMetadataFromOptions(options, dynamicTools) {
|
|
25613
|
+
const out = { ...options };
|
|
25614
|
+
for (const key of LIST_METADATA_KEYS) {
|
|
25615
|
+
if (key in out && !Array.isArray(out[key])) delete out[key];
|
|
25616
|
+
}
|
|
25617
|
+
const tools = [...out["tools"] ?? [], ...dynamicTools];
|
|
25618
|
+
if (tools.length > 0) out["tools"] = tools;
|
|
25619
|
+
return out;
|
|
25620
|
+
}
|
|
25436
25621
|
function dedupePluginProviders(providers) {
|
|
25437
25622
|
const map = /* @__PURE__ */ new Map();
|
|
25438
25623
|
for (const p of providers) map.set(p["provide"] ?? p, p);
|
|
25439
25624
|
return [...map.values()];
|
|
25440
25625
|
}
|
|
25626
|
+
var LIST_METADATA_KEYS;
|
|
25441
25627
|
var init_dynamic_utils = __esm({
|
|
25442
25628
|
"libs/sdk/src/common/dynamic/dynamic.utils.ts"() {
|
|
25443
25629
|
"use strict";
|
|
25630
|
+
LIST_METADATA_KEYS = ["exports", "plugins", "adapters", "tools", "resources", "prompts", "skills"];
|
|
25444
25631
|
}
|
|
25445
25632
|
});
|
|
25446
25633
|
|
|
@@ -25472,7 +25659,7 @@ var init_dynamic_plugin = __esm({
|
|
|
25472
25659
|
const typedOptions = options;
|
|
25473
25660
|
if ("useFactory" in options) {
|
|
25474
25661
|
return {
|
|
25475
|
-
...typedOptions,
|
|
25662
|
+
...pluginMetadataFromOptions(typedOptions, []),
|
|
25476
25663
|
provide: this,
|
|
25477
25664
|
inject: options.inject,
|
|
25478
25665
|
useFactory: options.useFactory,
|
|
@@ -25481,10 +25668,11 @@ var init_dynamic_plugin = __esm({
|
|
|
25481
25668
|
}
|
|
25482
25669
|
const dyn = collectDynamicProviders(this, typedOptions);
|
|
25483
25670
|
const mergedProviders = dedupePluginProviders([...dyn ?? [], ...extraProviders ?? []]);
|
|
25671
|
+
const dynamicTools = collectDynamicTools(this, typedOptions);
|
|
25484
25672
|
const instance = new this(options);
|
|
25485
25673
|
initOptionsByInstance.set(instance, options);
|
|
25486
25674
|
return {
|
|
25487
|
-
...typedOptions,
|
|
25675
|
+
...pluginMetadataFromOptions(typedOptions, dynamicTools),
|
|
25488
25676
|
provide: this,
|
|
25489
25677
|
useValue: instance,
|
|
25490
25678
|
providers: mergedProviders
|
|
@@ -25746,9 +25934,18 @@ var init_execution_context_interface = __esm({
|
|
|
25746
25934
|
if (this._authContext) return this._authContext;
|
|
25747
25935
|
const rawAuth = require("@frontmcp/auth");
|
|
25748
25936
|
const auth = rawAuth.default ?? rawAuth;
|
|
25749
|
-
this._authContext = auth.buildAuthContext(this.
|
|
25937
|
+
this._authContext = auth.buildAuthContext(this.resolveAuthSource(), this.scope.metadata.authorities?.claimsMapping);
|
|
25750
25938
|
return this._authContext;
|
|
25751
25939
|
}
|
|
25940
|
+
resolveAuthSource() {
|
|
25941
|
+
const fromContext = this.tryGetContext()?.authInfo;
|
|
25942
|
+
if (!fromContext) return this._authInfo;
|
|
25943
|
+
const merged = { ...this._authInfo };
|
|
25944
|
+
for (const [key, value] of Object.entries(fromContext)) {
|
|
25945
|
+
if (value !== void 0) merged[key] = value;
|
|
25946
|
+
}
|
|
25947
|
+
return merged;
|
|
25948
|
+
}
|
|
25752
25949
|
/**
|
|
25753
25950
|
* Try to get the context, returning undefined if not available.
|
|
25754
25951
|
*
|
|
@@ -25934,6 +26131,23 @@ var init_execution_context_interface = __esm({
|
|
|
25934
26131
|
get config() {
|
|
25935
26132
|
return this.providers.get(ConfigService);
|
|
25936
26133
|
}
|
|
26134
|
+
// ---- Platform bindings ----
|
|
26135
|
+
/**
|
|
26136
|
+
* The hosting platform's bindings for the current request — on a Cloudflare Worker, the `env`
|
|
26137
|
+
* object holding KV namespaces, D1 databases, R2 buckets, Durable Object namespaces, `[vars]`
|
|
26138
|
+
* and secrets. `undefined` on Node/Express, where no such object exists.
|
|
26139
|
+
*
|
|
26140
|
+
* Only string bindings are mirrored into `process.env`; use this for the rest.
|
|
26141
|
+
*
|
|
26142
|
+
* @example
|
|
26143
|
+
* ```typescript
|
|
26144
|
+
* const kv = this.workerEnv?.['MY_KV'] as KVNamespace | undefined;
|
|
26145
|
+
* ```
|
|
26146
|
+
*/
|
|
26147
|
+
get workerEnv() {
|
|
26148
|
+
const env = this.tryGetContext()?.platformEnv;
|
|
26149
|
+
return env !== null && typeof env === "object" ? env : void 0;
|
|
26150
|
+
}
|
|
25937
26151
|
// ---- Runtime context helpers ----
|
|
25938
26152
|
/**
|
|
25939
26153
|
* Get the current runtime context (platform, runtime, deployment, env).
|
|
@@ -30740,6 +30954,9 @@ var init_completion_events = __esm({
|
|
|
30740
30954
|
});
|
|
30741
30955
|
|
|
30742
30956
|
// libs/sdk/src/agent/flows/call-agent.flow.ts
|
|
30957
|
+
function invalidOutputFrom(outputError) {
|
|
30958
|
+
return outputError instanceof InvalidOutputError ? outputError : new InvalidOutputError();
|
|
30959
|
+
}
|
|
30743
30960
|
var import_auth26, import_guard6, import_lazy_zod60, import_protocol41, inputSchema15, outputSchema14, stateSchema14, plan14, name14, Stage14, CallAgentFlow;
|
|
30744
30961
|
var init_call_agent_flow = __esm({
|
|
30745
30962
|
"libs/sdk/src/agent/flows/call-agent.flow.ts"() {
|
|
@@ -31126,7 +31343,7 @@ var init_call_agent_flow = __esm({
|
|
|
31126
31343
|
const { agent, agentContext, executionStartedAt, executionError, outputError, authInfo } = this.state;
|
|
31127
31344
|
if (!agent || executionStartedAt === void 0) return;
|
|
31128
31345
|
if (executionError instanceof ElicitationFallbackRequired || executionError instanceof FlowControl) return;
|
|
31129
|
-
const failure = executionError ?? (outputError !== void 0 ?
|
|
31346
|
+
const failure = executionError ?? (outputError !== void 0 ? invalidOutputFrom(outputError) : void 0);
|
|
31130
31347
|
const failed = failure !== void 0;
|
|
31131
31348
|
const requestId = this.tryGetContext()?.requestId;
|
|
31132
31349
|
const sessionId = typeof authInfo?.sessionId === "string" && authInfo.sessionId ? authInfo.sessionId : void 0;
|
|
@@ -31156,7 +31373,7 @@ var init_call_agent_flow = __esm({
|
|
|
31156
31373
|
agent: agent.metadata.name,
|
|
31157
31374
|
errors: outputError
|
|
31158
31375
|
});
|
|
31159
|
-
throw
|
|
31376
|
+
throw invalidOutputFrom(outputError);
|
|
31160
31377
|
}
|
|
31161
31378
|
const result = parsedOutput;
|
|
31162
31379
|
if (executionMeta) {
|
|
@@ -32781,6 +32998,10 @@ var init_mcp_stateless_client = __esm({
|
|
|
32781
32998
|
async listResources(cursor) {
|
|
32782
32999
|
return this.request("resources/list", cursor ? { cursor } : {});
|
|
32783
33000
|
}
|
|
33001
|
+
/** One page of `resources/templates/list`; pass the previous page's `nextCursor` for the next. */
|
|
33002
|
+
async listResourceTemplates(cursor) {
|
|
33003
|
+
return this.request("resources/templates/list", cursor ? { cursor } : {});
|
|
33004
|
+
}
|
|
32784
33005
|
/** One page of `prompts/list`; pass the previous page's `nextCursor` for the next. */
|
|
32785
33006
|
async listPrompts(cursor) {
|
|
32786
33007
|
return this.request("prompts/list", cursor ? { cursor } : {});
|
|
@@ -33122,6 +33343,13 @@ var init_mcp_stateless_client_adapter = __esm({
|
|
|
33122
33343
|
...nextCursorOf(result)
|
|
33123
33344
|
};
|
|
33124
33345
|
}
|
|
33346
|
+
async listResourceTemplates(params) {
|
|
33347
|
+
const result = await this.client.listResourceTemplates(params?.cursor);
|
|
33348
|
+
return {
|
|
33349
|
+
resourceTemplates: Array.isArray(result["resourceTemplates"]) ? result["resourceTemplates"] : [],
|
|
33350
|
+
...nextCursorOf(result)
|
|
33351
|
+
};
|
|
33352
|
+
}
|
|
33125
33353
|
async readResource(params) {
|
|
33126
33354
|
return this.client.readResource(params.uri);
|
|
33127
33355
|
}
|
|
@@ -33489,7 +33717,7 @@ var init_health_check = __esm({
|
|
|
33489
33717
|
this.consecutiveSuccesses++;
|
|
33490
33718
|
this.consecutiveFailures = 0;
|
|
33491
33719
|
const previousStatus = this.status;
|
|
33492
|
-
if (this.consecutiveSuccesses >= this.options.healthyThreshold) {
|
|
33720
|
+
if (previousStatus === "unknown" || this.consecutiveSuccesses >= this.options.healthyThreshold) {
|
|
33493
33721
|
this.status = latencyMs > this.options.degradedLatencyMs ? "degraded" : "healthy";
|
|
33494
33722
|
}
|
|
33495
33723
|
this.lastResult = {
|
|
@@ -42711,9 +42939,11 @@ var init_instance_local_primary_auth = __esm({
|
|
|
42711
42939
|
} else if ((0, import_utils80.isProduction)() && (0, import_auth12.isOrchestratedMode)(options)) {
|
|
42712
42940
|
throw new import_auth14.JwtSecretRequiredError(options.mode);
|
|
42713
42941
|
} else {
|
|
42714
|
-
|
|
42715
|
-
|
|
42716
|
-
|
|
42942
|
+
if (!(0, import_auth12.isPublicMode)(options)) {
|
|
42943
|
+
this.logger.warn(
|
|
42944
|
+
"JWT_SECRET is not set; signing with a random per-process secret. Tokens will not survive a restart and will not verify across instances. Set JWT_SECRET for any deployment that mints tokens."
|
|
42945
|
+
);
|
|
42946
|
+
}
|
|
42717
42947
|
this.secret = getDefaultNoAuthSecret();
|
|
42718
42948
|
}
|
|
42719
42949
|
this.tokenStorage = this.readTokenStorage(options);
|
|
@@ -46682,7 +46912,14 @@ var init_encrypted_elicitation_store = __esm({
|
|
|
46682
46912
|
});
|
|
46683
46913
|
|
|
46684
46914
|
// libs/sdk/src/elicitation/store/elicitation-store.factory.ts
|
|
46685
|
-
function detectStorageType2(storage) {
|
|
46915
|
+
function detectStorageType2(storage, configuredType) {
|
|
46916
|
+
if (configuredType === "redis" || configuredType === "upstash" || configuredType === "memory") {
|
|
46917
|
+
return configuredType;
|
|
46918
|
+
}
|
|
46919
|
+
const backend = storage.type ?? storage.backendName;
|
|
46920
|
+
if (backend === "redis" || backend === "upstash" || backend === "memory") {
|
|
46921
|
+
return backend;
|
|
46922
|
+
}
|
|
46686
46923
|
if ((0, import_utils84.getEnv)("UPSTASH_REDIS_REST_URL")) {
|
|
46687
46924
|
return "upstash";
|
|
46688
46925
|
}
|
|
@@ -46772,7 +47009,8 @@ async function createElicitationStore(options = {}) {
|
|
|
46772
47009
|
});
|
|
46773
47010
|
}
|
|
46774
47011
|
}
|
|
46775
|
-
|
|
47012
|
+
const explicitBackend = Boolean(storageConfig || redis);
|
|
47013
|
+
if (finalStorageConfig?.type === "vercel-kv" || !explicitBackend && (0, import_utils84.getEnv)("KV_REST_API_URL")) {
|
|
46776
47014
|
throw new ElicitationNotSupportedError(
|
|
46777
47015
|
"Vercel KV is not supported for elicitation stores. Elicitation requires pub/sub for cross-node result routing, which Vercel KV does not support. Use Redis or Upstash instead."
|
|
46778
47016
|
);
|
|
@@ -46793,7 +47031,7 @@ async function createElicitationStore(options = {}) {
|
|
|
46793
47031
|
);
|
|
46794
47032
|
}
|
|
46795
47033
|
let store = new StorageElicitationStore(storage, logger);
|
|
46796
|
-
const type = detectStorageType2(storage);
|
|
47034
|
+
const type = detectStorageType2(storage, finalStorageConfig?.type);
|
|
46797
47035
|
const encryptionEnabled = encryption?.enabled ?? "auto";
|
|
46798
47036
|
const encryptionSecret = encryption?.secret;
|
|
46799
47037
|
const hasSecret = encryptionSecret || isElicitationEncryptionAvailable();
|
|
@@ -49620,6 +49858,7 @@ function createJobDefinitionStore(options, logger) {
|
|
|
49620
49858
|
host: options.redis.host ?? "localhost",
|
|
49621
49859
|
port: options.redis.port ?? 6379
|
|
49622
49860
|
});
|
|
49861
|
+
(0, import_utils88.attachRedisErrorListener)(client, { label: "JobDefinitionStore", logger: effectiveLogger });
|
|
49623
49862
|
return {
|
|
49624
49863
|
// Factory-created client lifetime is bound to the store, so the store
|
|
49625
49864
|
// must close it on dispose() — pass ownsClient: true.
|
|
@@ -49635,10 +49874,11 @@ function createJobDefinitionStore(options, logger) {
|
|
|
49635
49874
|
type: "memory"
|
|
49636
49875
|
};
|
|
49637
49876
|
}
|
|
49638
|
-
var noopFn, noopLogger2;
|
|
49877
|
+
var import_utils88, noopFn, noopLogger2;
|
|
49639
49878
|
var init_job_definition_store_factory = __esm({
|
|
49640
49879
|
"libs/sdk/src/job/store/job-definition-store.factory.ts"() {
|
|
49641
49880
|
"use strict";
|
|
49881
|
+
import_utils88 = require("@frontmcp/utils");
|
|
49642
49882
|
init_memory_job_definition_store();
|
|
49643
49883
|
noopFn = () => {
|
|
49644
49884
|
};
|
|
@@ -49831,6 +50071,7 @@ function createJobStateStore(options, logger) {
|
|
|
49831
50071
|
host: options.redis.host ?? "localhost",
|
|
49832
50072
|
port: options.redis.port ?? 6379
|
|
49833
50073
|
});
|
|
50074
|
+
(0, import_utils89.attachRedisErrorListener)(client, { label: "JobStateStore", logger });
|
|
49834
50075
|
return {
|
|
49835
50076
|
store: new RedisJobStateStore2(client, logger, keyPrefix, ttlSeconds),
|
|
49836
50077
|
type: "redis"
|
|
@@ -49844,9 +50085,11 @@ function createJobStateStore(options, logger) {
|
|
|
49844
50085
|
type: "memory"
|
|
49845
50086
|
};
|
|
49846
50087
|
}
|
|
50088
|
+
var import_utils89;
|
|
49847
50089
|
var init_job_state_store_factory = __esm({
|
|
49848
50090
|
"libs/sdk/src/job/store/job-state-store.factory.ts"() {
|
|
49849
50091
|
"use strict";
|
|
50092
|
+
import_utils89 = require("@frontmcp/utils");
|
|
49850
50093
|
init_memory_job_state_store();
|
|
49851
50094
|
}
|
|
49852
50095
|
});
|
|
@@ -50717,15 +50960,15 @@ var init_skill_session_types = __esm({
|
|
|
50717
50960
|
});
|
|
50718
50961
|
|
|
50719
50962
|
// libs/sdk/src/skill/session/skill-session.manager.ts
|
|
50720
|
-
var
|
|
50963
|
+
var import_utils90, SkillSessionManager;
|
|
50721
50964
|
var init_skill_session_manager = __esm({
|
|
50722
50965
|
"libs/sdk/src/skill/session/skill-session.manager.ts"() {
|
|
50723
50966
|
"use strict";
|
|
50724
|
-
|
|
50967
|
+
import_utils90 = require("@frontmcp/utils");
|
|
50725
50968
|
init_errors();
|
|
50726
50969
|
init_skill_session_types();
|
|
50727
|
-
SkillSessionManager = class extends
|
|
50728
|
-
storage = new
|
|
50970
|
+
SkillSessionManager = class extends import_utils90.EventEmitter {
|
|
50971
|
+
storage = new import_utils90.AsyncLocalStorage();
|
|
50729
50972
|
options;
|
|
50730
50973
|
logger;
|
|
50731
50974
|
store;
|
|
@@ -51924,11 +52167,11 @@ function createSkillHttpAuthValidator(skillsConfig, logger) {
|
|
|
51924
52167
|
logger
|
|
51925
52168
|
});
|
|
51926
52169
|
}
|
|
51927
|
-
var
|
|
52170
|
+
var import_utils91, SkillHttpAuthValidator;
|
|
51928
52171
|
var init_skill_http_auth = __esm({
|
|
51929
52172
|
"libs/sdk/src/skill/auth/skill-http-auth.ts"() {
|
|
51930
52173
|
"use strict";
|
|
51931
|
-
|
|
52174
|
+
import_utils91 = require("@frontmcp/utils");
|
|
51932
52175
|
init_common();
|
|
51933
52176
|
SkillHttpAuthValidator = class {
|
|
51934
52177
|
skillsConfig;
|
|
@@ -52010,7 +52253,7 @@ var init_skill_http_auth = __esm({
|
|
|
52010
52253
|
const keyBytes = encoder5.encode(key);
|
|
52011
52254
|
if (keyBytes.length === candidateBytes.length) {
|
|
52012
52255
|
try {
|
|
52013
|
-
if ((0,
|
|
52256
|
+
if ((0, import_utils91.timingSafeEqual)(keyBytes, candidateBytes)) {
|
|
52014
52257
|
found = true;
|
|
52015
52258
|
}
|
|
52016
52259
|
} catch {
|
|
@@ -52291,6 +52534,7 @@ async function createRedisCache(redis, keyPrefix, ttlMs, logger) {
|
|
|
52291
52534
|
enableReadyCheck: false,
|
|
52292
52535
|
maxRetriesPerRequest: 3
|
|
52293
52536
|
});
|
|
52537
|
+
(0, import_utils92.attachRedisErrorListener)(client, { label: "SkillHttpCache" });
|
|
52294
52538
|
await client.connect();
|
|
52295
52539
|
return new RedisSkillHttpCache({
|
|
52296
52540
|
getClient: async () => client,
|
|
@@ -52298,9 +52542,11 @@ async function createRedisCache(redis, keyPrefix, ttlMs, logger) {
|
|
|
52298
52542
|
ttlMs
|
|
52299
52543
|
});
|
|
52300
52544
|
}
|
|
52545
|
+
var import_utils92;
|
|
52301
52546
|
var init_skill_http_cache_factory = __esm({
|
|
52302
52547
|
"libs/sdk/src/skill/cache/skill-http-cache.factory.ts"() {
|
|
52303
52548
|
"use strict";
|
|
52549
|
+
import_utils92 = require("@frontmcp/utils");
|
|
52304
52550
|
init_skill_http_cache();
|
|
52305
52551
|
}
|
|
52306
52552
|
});
|
|
@@ -55471,15 +55717,15 @@ async function readSkillFileByPath(instance, filePath) {
|
|
|
55471
55717
|
let candidatePath;
|
|
55472
55718
|
for (const [subdir, configured] of candidateDirs) {
|
|
55473
55719
|
if (segments[0] !== subdir) continue;
|
|
55474
|
-
const dir = configured ? (0,
|
|
55475
|
-
candidatePath = (0,
|
|
55720
|
+
const dir = configured ? (0, import_utils94.isAbsolute)(configured) ? configured : (0, import_utils94.pathResolve)(baseDir, configured) : (0, import_utils94.joinPath)(baseDir, subdir);
|
|
55721
|
+
candidatePath = (0, import_utils94.pathResolve)(dir, ...segments.slice(1));
|
|
55476
55722
|
if (!isInside(candidatePath, dir)) {
|
|
55477
55723
|
throw new PublicMcpError(`Invalid file path "${filePath}".`, "INVALID_PARAMS", 400);
|
|
55478
55724
|
}
|
|
55479
55725
|
break;
|
|
55480
55726
|
}
|
|
55481
55727
|
if (!candidatePath) {
|
|
55482
|
-
candidatePath = (0,
|
|
55728
|
+
candidatePath = (0, import_utils94.pathResolve)(baseDir, ...segments);
|
|
55483
55729
|
if (!isInside(candidatePath, baseDir)) {
|
|
55484
55730
|
throw new PublicMcpError(`Invalid file path "${filePath}".`, "INVALID_PARAMS", 400);
|
|
55485
55731
|
}
|
|
@@ -55487,7 +55733,7 @@ async function readSkillFileByPath(instance, filePath) {
|
|
|
55487
55733
|
const mimeType = guessMimeType(filePath);
|
|
55488
55734
|
let buffer;
|
|
55489
55735
|
try {
|
|
55490
|
-
buffer = await (0,
|
|
55736
|
+
buffer = await (0, import_utils94.readFileBuffer)(candidatePath);
|
|
55491
55737
|
} catch (err) {
|
|
55492
55738
|
if (isFileMissingError(err)) {
|
|
55493
55739
|
throw new ResourceNotFoundError(`skill://${instance.getSkillPath()}/${filePath}`);
|
|
@@ -55530,7 +55776,7 @@ async function tryResolvedSubFile(instance, kind, tail) {
|
|
|
55530
55776
|
const resolvedPath = resolveEntryPath2(instance, kind, absolutePath, filename);
|
|
55531
55777
|
if (!resolvedPath) continue;
|
|
55532
55778
|
try {
|
|
55533
|
-
const buf = await (0,
|
|
55779
|
+
const buf = await (0, import_utils94.readFileBuffer)(resolvedPath);
|
|
55534
55780
|
const text = (Buffer.isBuffer(buf) ? buf : Buffer.from(buf)).toString("utf-8");
|
|
55535
55781
|
return parseSkillMdFrontmatter(text).body;
|
|
55536
55782
|
} catch (err) {
|
|
@@ -55543,18 +55789,18 @@ async function tryResolvedSubFile(instance, kind, tail) {
|
|
|
55543
55789
|
return void 0;
|
|
55544
55790
|
}
|
|
55545
55791
|
function resolveEntryPath2(instance, kind, absolutePath, filename) {
|
|
55546
|
-
if (absolutePath && (0,
|
|
55792
|
+
if (absolutePath && (0, import_utils94.isAbsolute)(absolutePath)) return absolutePath;
|
|
55547
55793
|
if (!filename) return void 0;
|
|
55548
55794
|
const baseDir = instance.getBaseDir();
|
|
55549
55795
|
const configured = instance.getResources()?.[kind];
|
|
55550
55796
|
let dir;
|
|
55551
|
-
if (configured && (0,
|
|
55797
|
+
if (configured && (0, import_utils94.isAbsolute)(configured)) {
|
|
55552
55798
|
dir = configured;
|
|
55553
55799
|
} else if (baseDir) {
|
|
55554
|
-
dir = configured ? (0,
|
|
55800
|
+
dir = configured ? (0, import_utils94.pathResolve)(baseDir, configured) : (0, import_utils94.joinPath)(baseDir, kind);
|
|
55555
55801
|
}
|
|
55556
55802
|
if (!dir) return void 0;
|
|
55557
|
-
const resolved = (0,
|
|
55803
|
+
const resolved = (0, import_utils94.pathResolve)(dir, filename);
|
|
55558
55804
|
if (!isInside(resolved, dir)) return void 0;
|
|
55559
55805
|
return resolved;
|
|
55560
55806
|
}
|
|
@@ -55586,11 +55832,11 @@ function isInside(child, parent) {
|
|
|
55586
55832
|
const parentPrefix = normParent.endsWith("/") ? normParent : `${normParent}/`;
|
|
55587
55833
|
return normChild === normParent || normChild.startsWith(parentPrefix);
|
|
55588
55834
|
}
|
|
55589
|
-
var
|
|
55835
|
+
var import_utils94;
|
|
55590
55836
|
var init_sep_2640_resource_helpers = __esm({
|
|
55591
55837
|
"libs/sdk/src/skill/sep-2640/sep-2640.resource-helpers.ts"() {
|
|
55592
55838
|
"use strict";
|
|
55593
|
-
|
|
55839
|
+
import_utils94 = require("@frontmcp/utils");
|
|
55594
55840
|
init_errors();
|
|
55595
55841
|
init_skill_filter_helper();
|
|
55596
55842
|
init_skill_md_parser();
|
|
@@ -55817,17 +56063,17 @@ async function resolveLastModifiedForSkill(skill) {
|
|
|
55817
56063
|
return void 0;
|
|
55818
56064
|
}
|
|
55819
56065
|
try {
|
|
55820
|
-
const s = await (0,
|
|
56066
|
+
const s = await (0, import_utils95.stat)(record.filePath);
|
|
55821
56067
|
return s.mtime instanceof Date ? s.mtime.toISOString() : void 0;
|
|
55822
56068
|
} catch {
|
|
55823
56069
|
return void 0;
|
|
55824
56070
|
}
|
|
55825
56071
|
}
|
|
55826
|
-
var
|
|
56072
|
+
var import_utils95;
|
|
55827
56073
|
var init_sep_2640_last_modified = __esm({
|
|
55828
56074
|
"libs/sdk/src/skill/sep-2640/sep-2640.last-modified.ts"() {
|
|
55829
56075
|
"use strict";
|
|
55830
|
-
|
|
56076
|
+
import_utils95 = require("@frontmcp/utils");
|
|
55831
56077
|
}
|
|
55832
56078
|
});
|
|
55833
56079
|
|
|
@@ -56017,7 +56263,7 @@ function createDefaultSigner(mod, logger) {
|
|
|
56017
56263
|
"[skill-audit] refusing to use the default HS256 signer in production. Configure skillsConfig.audit.signer with a host-managed key (e.g. RS256 pointing at the same key registry your bundle signing uses)."
|
|
56018
56264
|
);
|
|
56019
56265
|
}
|
|
56020
|
-
const bytes = (0,
|
|
56266
|
+
const bytes = (0, import_utils96.randomBytes)(32);
|
|
56021
56267
|
logger.warn(
|
|
56022
56268
|
"[skill-audit] no audit signer configured \u2014 using process-local HS256 with a CSPRNG-generated secret. Records signed by this instance cannot be verified after restart. Configure skillsConfig.audit.signer for production."
|
|
56023
56269
|
);
|
|
@@ -56029,12 +56275,12 @@ function createDefaultStore(mod, logger) {
|
|
|
56029
56275
|
);
|
|
56030
56276
|
return new mod.MemoryAuditStore();
|
|
56031
56277
|
}
|
|
56032
|
-
var import_di39,
|
|
56278
|
+
var import_di39, import_utils96, injectedFactory;
|
|
56033
56279
|
var init_skill_audit_helper = __esm({
|
|
56034
56280
|
"libs/sdk/src/skill/skill-audit.helper.ts"() {
|
|
56035
56281
|
"use strict";
|
|
56036
56282
|
import_di39 = require("@frontmcp/di");
|
|
56037
|
-
|
|
56283
|
+
import_utils96 = require("@frontmcp/utils");
|
|
56038
56284
|
}
|
|
56039
56285
|
});
|
|
56040
56286
|
|
|
@@ -56106,6 +56352,51 @@ var init_skill_scope_helper = __esm({
|
|
|
56106
56352
|
}
|
|
56107
56353
|
});
|
|
56108
56354
|
|
|
56355
|
+
// libs/sdk/src/tool/ui/ui-option-warnings.ts
|
|
56356
|
+
function describeIgnoredUiOptions(toolName, ui) {
|
|
56357
|
+
if (!ui) return [];
|
|
56358
|
+
const messages = [];
|
|
56359
|
+
const ignored = IGNORED_UI_OPTIONS.filter((key) => ui[key] !== void 0);
|
|
56360
|
+
if (ignored.length > 0) {
|
|
56361
|
+
messages.push(
|
|
56362
|
+
`Tool "${toolName}": \`ui.${ignored.join("`, `ui.")}\` ${ignored.length === 1 ? "is" : "are"} accepted but not used yet, so ${ignored.length === 1 ? "it has" : "they have"} no effect.`
|
|
56363
|
+
);
|
|
56364
|
+
}
|
|
56365
|
+
const mode = ui["servingMode"];
|
|
56366
|
+
if (UNSUPPORTED_SERVING_MODES.includes(mode)) {
|
|
56367
|
+
messages.push(
|
|
56368
|
+
`Tool "${toolName}": \`ui.servingMode: '${String(mode)}'\` is not implemented; the widget is served inline, as with \`servingMode: 'inline'\`.`
|
|
56369
|
+
);
|
|
56370
|
+
}
|
|
56371
|
+
if (mode === "hybrid") {
|
|
56372
|
+
messages.push(
|
|
56373
|
+
`Tool "${toolName}": \`ui.servingMode: 'hybrid'\` sends only a reference in \`_meta['ui/component']\` ({ type, hash, toolName }), not the component code; the widget is rendered from its \`ui://\` resource.`
|
|
56374
|
+
);
|
|
56375
|
+
}
|
|
56376
|
+
return messages;
|
|
56377
|
+
}
|
|
56378
|
+
var IGNORED_UI_OPTIONS, UNSUPPORTED_SERVING_MODES;
|
|
56379
|
+
var init_ui_option_warnings = __esm({
|
|
56380
|
+
"libs/sdk/src/tool/ui/ui-option-warnings.ts"() {
|
|
56381
|
+
"use strict";
|
|
56382
|
+
IGNORED_UI_OPTIONS = [
|
|
56383
|
+
"widgetDescription",
|
|
56384
|
+
"widgetAccessible",
|
|
56385
|
+
"displayMode",
|
|
56386
|
+
"prefersBorder",
|
|
56387
|
+
"sandboxDomain",
|
|
56388
|
+
"contentSecurity",
|
|
56389
|
+
"hydrate",
|
|
56390
|
+
"runtimeOptions",
|
|
56391
|
+
"mdxComponents",
|
|
56392
|
+
"bundlingMode",
|
|
56393
|
+
"uiType",
|
|
56394
|
+
"htmlResponsePrefix"
|
|
56395
|
+
];
|
|
56396
|
+
UNSUPPORTED_SERVING_MODES = ["direct-url", "custom-url"];
|
|
56397
|
+
}
|
|
56398
|
+
});
|
|
56399
|
+
|
|
56109
56400
|
// libs/sdk/src/transport/bus/redis-transport-bus.ts
|
|
56110
56401
|
var DEFAULT_BUS_PREFIX, DEFAULT_BUS_TTL_SECONDS, REVOKE_LUA, RedisTransportBus;
|
|
56111
56402
|
var init_redis_transport_bus = __esm({
|
|
@@ -56235,10 +56526,11 @@ var redis_event_store_exports = {};
|
|
|
56235
56526
|
__export(redis_event_store_exports, {
|
|
56236
56527
|
RedisEventStore: () => RedisEventStore
|
|
56237
56528
|
});
|
|
56238
|
-
var RedisEventStore;
|
|
56529
|
+
var import_utils97, RedisEventStore;
|
|
56239
56530
|
var init_redis_event_store = __esm({
|
|
56240
56531
|
"libs/sdk/src/transport/event-stores/redis.event-store.ts"() {
|
|
56241
56532
|
"use strict";
|
|
56533
|
+
import_utils97 = require("@frontmcp/utils");
|
|
56242
56534
|
init_sdk_errors();
|
|
56243
56535
|
RedisEventStore = class {
|
|
56244
56536
|
client;
|
|
@@ -56282,6 +56574,9 @@ var init_redis_event_store = __esm({
|
|
|
56282
56574
|
enableReadyCheck: false,
|
|
56283
56575
|
maxRetriesPerRequest: 3
|
|
56284
56576
|
});
|
|
56577
|
+
(0, import_utils97.attachRedisErrorListener)(this.client, {
|
|
56578
|
+
label: "RedisEventStore"
|
|
56579
|
+
});
|
|
56285
56580
|
await this.client.connect();
|
|
56286
56581
|
}
|
|
56287
56582
|
return this.client;
|
|
@@ -56674,11 +56969,11 @@ function createSqliteSessionStore(options, logger) {
|
|
|
56674
56969
|
logger?.info("[SessionStoreFactory] Creating SQLite session store", { path: options.path });
|
|
56675
56970
|
return new SqliteSessionStore(options);
|
|
56676
56971
|
}
|
|
56677
|
-
var
|
|
56972
|
+
var import_utils98;
|
|
56678
56973
|
var init_session_store_factory = __esm({
|
|
56679
56974
|
"libs/sdk/src/auth/session/session-store.factory.ts"() {
|
|
56680
56975
|
"use strict";
|
|
56681
|
-
|
|
56976
|
+
import_utils98 = require("@frontmcp/utils");
|
|
56682
56977
|
init_common();
|
|
56683
56978
|
}
|
|
56684
56979
|
});
|
|
@@ -56753,14 +57048,14 @@ function toClientInfo(value) {
|
|
|
56753
57048
|
const { name: name44, version } = value;
|
|
56754
57049
|
return typeof name44 === "string" && typeof version === "string" ? { name: name44, version } : void 0;
|
|
56755
57050
|
}
|
|
56756
|
-
var import_auth55, import_lazy_zod102, import_protocol52,
|
|
57051
|
+
var import_auth55, import_lazy_zod102, import_protocol52, import_utils99, plan37, subscriptionParamsSchema, requestIdSchema, stateSchema37, name38, Stage38, encoder3, TRACE_META_KEYS, HandleMcp20260728Flow;
|
|
56757
57052
|
var init_handle_mcp_20260728_flow = __esm({
|
|
56758
57053
|
"libs/sdk/src/transport/flows/handle.mcp-20260728.flow.ts"() {
|
|
56759
57054
|
"use strict";
|
|
56760
57055
|
import_auth55 = require("@frontmcp/auth");
|
|
56761
57056
|
import_lazy_zod102 = require("@frontmcp/lazy-zod");
|
|
56762
57057
|
import_protocol52 = require("@frontmcp/protocol");
|
|
56763
|
-
|
|
57058
|
+
import_utils99 = require("@frontmcp/utils");
|
|
56764
57059
|
init_common();
|
|
56765
57060
|
init_context();
|
|
56766
57061
|
init_notification();
|
|
@@ -56858,7 +57153,7 @@ var init_handle_mcp_20260728_flow = __esm({
|
|
|
56858
57153
|
if (!context) return (fn) => fn();
|
|
56859
57154
|
const storage = this.scope.providers.get(FrontMcpContextStorage);
|
|
56860
57155
|
return async (fn) => {
|
|
56861
|
-
await (0,
|
|
57156
|
+
await (0, import_utils99.runRequestExclusive)(() => storage.runWithContext(context, fn));
|
|
56862
57157
|
};
|
|
56863
57158
|
}
|
|
56864
57159
|
findToolSchema(scope, toolName) {
|
|
@@ -56935,7 +57230,7 @@ var init_handle_mcp_20260728_flow = __esm({
|
|
|
56935
57230
|
const sink = new RequestNotificationSink(logLevel, progressToken);
|
|
56936
57231
|
const clientInfo = toClientInfo(meta[import_protocol52.MCP_20260728_META.clientInfo]);
|
|
56937
57232
|
if (clientInfo) {
|
|
56938
|
-
const platformType = detectAIPlatform(clientInfo, this.scope.metadata.transport?.platformDetection);
|
|
57233
|
+
const platformType = detectPlatformFromCapabilities(clientCapabilities) ?? detectAIPlatform(clientInfo, this.scope.metadata.transport?.platformDetection);
|
|
56939
57234
|
this.tryGetContext()?.setClientInfo(clientInfo, platformType);
|
|
56940
57235
|
}
|
|
56941
57236
|
const dispatchOptions = {
|
|
@@ -57033,6 +57328,29 @@ var init_handle_mcp_20260728_flow = __esm({
|
|
|
57033
57328
|
}
|
|
57034
57329
|
});
|
|
57035
57330
|
|
|
57331
|
+
// libs/sdk/src/ha/ha-headers.ts
|
|
57332
|
+
function applyMachineIdHeader(response) {
|
|
57333
|
+
if ((0, import_utils100.getRuntimeContext)().deployment !== "distributed") return false;
|
|
57334
|
+
response.setHeader(DEFAULT_FRONTMCP_MACHINE_ID_HEADER, (0, import_utils100.getMachineId)());
|
|
57335
|
+
return true;
|
|
57336
|
+
}
|
|
57337
|
+
function applyNodeAffinity(response, request) {
|
|
57338
|
+
if (!applyMachineIdHeader(response)) return;
|
|
57339
|
+
const cookie = (0, import_utils100.buildSetCookie)({ name: DEFAULT_FRONTMCP_NODE_COOKIE, value: (0, import_utils100.getMachineId)() }, request);
|
|
57340
|
+
if (!cookie) return;
|
|
57341
|
+
const existing = response.getHeader("Set-Cookie");
|
|
57342
|
+
const existingArr = Array.isArray(existing) ? existing : existing ? [String(existing)] : [];
|
|
57343
|
+
response.setHeader("Set-Cookie", [...existingArr, cookie]);
|
|
57344
|
+
}
|
|
57345
|
+
var import_utils100;
|
|
57346
|
+
var init_ha_headers = __esm({
|
|
57347
|
+
"libs/sdk/src/ha/ha-headers.ts"() {
|
|
57348
|
+
"use strict";
|
|
57349
|
+
import_utils100 = require("@frontmcp/utils");
|
|
57350
|
+
init_ha_constants();
|
|
57351
|
+
}
|
|
57352
|
+
});
|
|
57353
|
+
|
|
57036
57354
|
// libs/sdk/src/skill/skill-mode.utils.ts
|
|
57037
57355
|
function detectSkillsOnlyMode(query) {
|
|
57038
57356
|
if (!query) return false;
|
|
@@ -57085,16 +57403,16 @@ function resolveLegacySseSession(params) {
|
|
|
57085
57403
|
}
|
|
57086
57404
|
return { session: createSession(), createdNew: true, responded404: false };
|
|
57087
57405
|
}
|
|
57088
|
-
var import_lazy_zod103,
|
|
57406
|
+
var import_lazy_zod103, import_utils101, plan38, stateSessionSchema, stateSchema38, name39, Stage39, HandleSseFlow;
|
|
57089
57407
|
var init_handle_sse_flow = __esm({
|
|
57090
57408
|
"libs/sdk/src/transport/flows/handle.sse.flow.ts"() {
|
|
57091
57409
|
"use strict";
|
|
57092
57410
|
import_lazy_zod103 = require("@frontmcp/lazy-zod");
|
|
57093
|
-
|
|
57411
|
+
import_utils101 = require("@frontmcp/utils");
|
|
57094
57412
|
init_session_id_utils();
|
|
57095
57413
|
init_common();
|
|
57096
57414
|
init_errors();
|
|
57097
|
-
|
|
57415
|
+
init_ha_headers();
|
|
57098
57416
|
init_skill_mode_utils();
|
|
57099
57417
|
plan38 = {
|
|
57100
57418
|
pre: ["parseInput", "router"],
|
|
@@ -57168,16 +57486,7 @@ var init_handle_sse_flow = __esm({
|
|
|
57168
57486
|
const { request, response } = this.rawInput;
|
|
57169
57487
|
const { token, session } = this.state.required;
|
|
57170
57488
|
const transport = await transportService.createTransporter("sse", token, session.id, response);
|
|
57171
|
-
|
|
57172
|
-
const nodeId = (0, import_utils96.getMachineId)();
|
|
57173
|
-
response.setHeader(DEFAULT_FRONTMCP_MACHINE_ID_HEADER, nodeId);
|
|
57174
|
-
const cookie = (0, import_utils96.buildSetCookie)({ name: DEFAULT_FRONTMCP_NODE_COOKIE, value: nodeId }, request);
|
|
57175
|
-
if (cookie) {
|
|
57176
|
-
const existing = response.getHeader("Set-Cookie");
|
|
57177
|
-
const existingArr = Array.isArray(existing) ? existing : existing ? [String(existing)] : [];
|
|
57178
|
-
response.setHeader("Set-Cookie", [...existingArr, cookie]);
|
|
57179
|
-
}
|
|
57180
|
-
}
|
|
57489
|
+
applyNodeAffinity(response, request);
|
|
57181
57490
|
await transport.initialize(request, response);
|
|
57182
57491
|
this.handled();
|
|
57183
57492
|
}
|
|
@@ -57193,12 +57502,12 @@ var init_handle_sse_flow = __esm({
|
|
|
57193
57502
|
const { request, response } = this.rawInput;
|
|
57194
57503
|
const { token, session } = this.state.required;
|
|
57195
57504
|
const transport = await transportService.getTransporter("sse", token, session.id);
|
|
57196
|
-
if (!transport && (0,
|
|
57505
|
+
if (!transport && (0, import_utils101.getRuntimeContext)().deployment === "distributed") {
|
|
57197
57506
|
const storedSession = await transportService.getStoredSession("sse", token, session.id);
|
|
57198
57507
|
if (storedSession) {
|
|
57199
57508
|
const haManager = this.scope.haManager;
|
|
57200
57509
|
const relay = haManager?.getRelay();
|
|
57201
|
-
if (relay && storedSession.session.nodeId && storedSession.session.nodeId !== (0,
|
|
57510
|
+
if (relay && storedSession.session.nodeId && storedSession.session.nodeId !== (0, import_utils101.getMachineId)()) {
|
|
57202
57511
|
const isAlive2 = await haManager.isNodeAlive(storedSession.session.nodeId);
|
|
57203
57512
|
if (isAlive2) {
|
|
57204
57513
|
try {
|
|
@@ -57293,9 +57602,10 @@ var init_handle_stateless_http_flow = __esm({
|
|
|
57293
57602
|
import_protocol53 = require("@frontmcp/protocol");
|
|
57294
57603
|
init_common();
|
|
57295
57604
|
init_errors();
|
|
57605
|
+
init_ha_headers();
|
|
57296
57606
|
init_transport_types();
|
|
57297
57607
|
plan39 = {
|
|
57298
|
-
pre: ["parseInput", "router"],
|
|
57608
|
+
pre: ["applyNodeHeaders", "parseInput", "router"],
|
|
57299
57609
|
execute: ["handleRequest"],
|
|
57300
57610
|
post: [],
|
|
57301
57611
|
finalize: ["cleanup"]
|
|
@@ -57309,6 +57619,9 @@ var init_handle_stateless_http_flow = __esm({
|
|
|
57309
57619
|
({ Stage: Stage40 } = FlowHooksOf(name40));
|
|
57310
57620
|
HandleStatelessHttpFlow = class extends FlowBase {
|
|
57311
57621
|
name = name40;
|
|
57622
|
+
async applyNodeHeaders() {
|
|
57623
|
+
applyMachineIdHeader(this.rawInput.response);
|
|
57624
|
+
}
|
|
57312
57625
|
async parseInput() {
|
|
57313
57626
|
const { request } = this.rawInput;
|
|
57314
57627
|
const logger = this.scope.logger.child("HandleStatelessHttpFlow");
|
|
@@ -57370,6 +57683,9 @@ var init_handle_stateless_http_flow = __esm({
|
|
|
57370
57683
|
this.handled();
|
|
57371
57684
|
}
|
|
57372
57685
|
};
|
|
57686
|
+
__decorateClass([
|
|
57687
|
+
Stage40("applyNodeHeaders")
|
|
57688
|
+
], HandleStatelessHttpFlow.prototype, "applyNodeHeaders", 1);
|
|
57373
57689
|
__decorateClass([
|
|
57374
57690
|
Stage40("parseInput")
|
|
57375
57691
|
], HandleStatelessHttpFlow.prototype, "parseInput", 1);
|
|
@@ -57424,11 +57740,11 @@ var init_ext_apps_types = __esm({
|
|
|
57424
57740
|
function createExtAppsMessageHandler(options) {
|
|
57425
57741
|
return new ExtAppsMessageHandler(options);
|
|
57426
57742
|
}
|
|
57427
|
-
var
|
|
57743
|
+
var import_utils102, ExtAppsMessageHandler, ExtAppsError, ExtAppsMethodNotFoundError, ExtAppsInvalidParamsError, ExtAppsNotSupportedError, ExtAppsToolNotFoundError;
|
|
57428
57744
|
var init_ext_apps_handler = __esm({
|
|
57429
57745
|
"libs/sdk/src/ext-apps/ext-apps.handler.ts"() {
|
|
57430
57746
|
"use strict";
|
|
57431
|
-
|
|
57747
|
+
import_utils102 = require("@frontmcp/utils");
|
|
57432
57748
|
init_ext_apps_types();
|
|
57433
57749
|
ExtAppsMessageHandler = class {
|
|
57434
57750
|
context;
|
|
@@ -57472,7 +57788,7 @@ var init_ext_apps_handler = __esm({
|
|
|
57472
57788
|
code: errorCode,
|
|
57473
57789
|
message: errorMessage,
|
|
57474
57790
|
// Only include stack traces in development to avoid leaking internals in production
|
|
57475
|
-
data: (0,
|
|
57791
|
+
data: (0, import_utils102.isDevelopment)() && error instanceof Error ? { stack: error.stack } : void 0
|
|
57476
57792
|
}
|
|
57477
57793
|
};
|
|
57478
57794
|
}
|
|
@@ -57834,22 +58150,21 @@ async function lookupStreamableHttpTransport(params) {
|
|
|
57834
58150
|
}
|
|
57835
58151
|
return { kind: "session-not-initialized", recreationError };
|
|
57836
58152
|
}
|
|
57837
|
-
var import_lazy_zod105, import_protocol54,
|
|
58153
|
+
var import_lazy_zod105, import_protocol54, plan40, stateSessionSchema2, stateSchema40, name41, Stage41, HandleStreamableHttpFlow;
|
|
57838
58154
|
var init_handle_streamable_http_flow = __esm({
|
|
57839
58155
|
"libs/sdk/src/transport/flows/handle.streamable-http.flow.ts"() {
|
|
57840
58156
|
"use strict";
|
|
57841
58157
|
import_lazy_zod105 = require("@frontmcp/lazy-zod");
|
|
57842
58158
|
import_protocol54 = require("@frontmcp/protocol");
|
|
57843
|
-
import_utils98 = require("@frontmcp/utils");
|
|
57844
58159
|
init_session_id_utils();
|
|
57845
58160
|
init_common();
|
|
57846
58161
|
init_errors();
|
|
57847
58162
|
init_ext_apps2();
|
|
57848
|
-
|
|
58163
|
+
init_ha_headers();
|
|
57849
58164
|
init_skill_mode_utils();
|
|
57850
58165
|
init_mcp_surface();
|
|
57851
58166
|
plan40 = {
|
|
57852
|
-
pre: ["parseInput", "router"],
|
|
58167
|
+
pre: ["applyNodeHeaders", "parseInput", "router"],
|
|
57853
58168
|
execute: ["onInitialize", "onMessage", "onElicitResult", "onSseListener", "onExtApps"],
|
|
57854
58169
|
post: [],
|
|
57855
58170
|
finalize: ["cleanup"]
|
|
@@ -57875,6 +58190,9 @@ var init_handle_streamable_http_flow = __esm({
|
|
|
57875
58190
|
({ Stage: Stage41 } = FlowHooksOf(name41));
|
|
57876
58191
|
HandleStreamableHttpFlow = class extends FlowBase {
|
|
57877
58192
|
name = name41;
|
|
58193
|
+
async applyNodeHeaders() {
|
|
58194
|
+
applyMachineIdHeader(this.rawInput.response);
|
|
58195
|
+
}
|
|
57878
58196
|
async parseInput() {
|
|
57879
58197
|
const { request } = this.rawInput;
|
|
57880
58198
|
const authorization = request[ServerRequestTokens.auth];
|
|
@@ -57973,16 +58291,7 @@ var init_handle_streamable_http_flow = __esm({
|
|
|
57973
58291
|
return;
|
|
57974
58292
|
}
|
|
57975
58293
|
}
|
|
57976
|
-
|
|
57977
|
-
const nodeId = (0, import_utils98.getMachineId)();
|
|
57978
|
-
response.setHeader(DEFAULT_FRONTMCP_MACHINE_ID_HEADER, nodeId);
|
|
57979
|
-
const cookie = (0, import_utils98.buildSetCookie)({ name: DEFAULT_FRONTMCP_NODE_COOKIE, value: nodeId }, request);
|
|
57980
|
-
if (cookie) {
|
|
57981
|
-
const existing = response.getHeader("Set-Cookie");
|
|
57982
|
-
const existingArr = Array.isArray(existing) ? existing : existing ? [String(existing)] : [];
|
|
57983
|
-
response.setHeader("Set-Cookie", [...existingArr, cookie]);
|
|
57984
|
-
}
|
|
57985
|
-
}
|
|
58294
|
+
applyNodeAffinity(response, request);
|
|
57986
58295
|
logger.info("onInitialize: transport created, calling initialize");
|
|
57987
58296
|
await transport.initialize(request, response);
|
|
57988
58297
|
logger.info("onInitialize: completed successfully");
|
|
@@ -58165,6 +58474,17 @@ var init_handle_streamable_http_flow = __esm({
|
|
|
58165
58474
|
const logger = this.scopeLogger.child("handle:streamable-http:onExtApps");
|
|
58166
58475
|
const { request, response } = this.rawInput;
|
|
58167
58476
|
const { token, session } = this.state.required;
|
|
58477
|
+
if (this.scope.metadata.extApps?.enabled === false) {
|
|
58478
|
+
const rpc = request.body;
|
|
58479
|
+
logger.verbose("onExtApps: ext-apps disabled by config", { method: rpc?.method });
|
|
58480
|
+
response.status(200).json({
|
|
58481
|
+
jsonrpc: "2.0",
|
|
58482
|
+
id: rpc?.id ?? null,
|
|
58483
|
+
error: { code: -32601, message: `Method not found: ${rpc?.method}` }
|
|
58484
|
+
});
|
|
58485
|
+
this.handled();
|
|
58486
|
+
return;
|
|
58487
|
+
}
|
|
58168
58488
|
logger.info("onExtApps: starting", {
|
|
58169
58489
|
sessionId: session.id?.slice(0, 20),
|
|
58170
58490
|
method: request.body?.method
|
|
@@ -58216,8 +58536,20 @@ var init_handle_streamable_http_flow = __esm({
|
|
|
58216
58536
|
const hostCapabilities = {
|
|
58217
58537
|
serverToolProxy: configuredCapabilities?.serverToolProxy ?? true,
|
|
58218
58538
|
logging: configuredCapabilities?.logging ?? true,
|
|
58219
|
-
...configuredCapabilities
|
|
58539
|
+
...configuredCapabilities,
|
|
58540
|
+
// Only advertise what this host can serve: the context below provides callTool and nothing else
|
|
58541
|
+
openLink: false,
|
|
58542
|
+
modelContextUpdate: false,
|
|
58543
|
+
widgetTools: false
|
|
58220
58544
|
};
|
|
58545
|
+
const unsupported = ["openLink", "modelContextUpdate", "widgetTools"].filter(
|
|
58546
|
+
(key) => configuredCapabilities?.[key] === true
|
|
58547
|
+
);
|
|
58548
|
+
if (unsupported.length > 0) {
|
|
58549
|
+
logger.warn(
|
|
58550
|
+
`onExtApps: extApps.hostCapabilities ${unsupported.join(", ")} are not supported yet and are not advertised`
|
|
58551
|
+
);
|
|
58552
|
+
}
|
|
58221
58553
|
const handler = createExtAppsMessageHandler({
|
|
58222
58554
|
context: {
|
|
58223
58555
|
sessionId: session.id,
|
|
@@ -58251,6 +58583,9 @@ var init_handle_streamable_http_flow = __esm({
|
|
|
58251
58583
|
this.handled();
|
|
58252
58584
|
}
|
|
58253
58585
|
};
|
|
58586
|
+
__decorateClass([
|
|
58587
|
+
Stage41("applyNodeHeaders")
|
|
58588
|
+
], HandleStreamableHttpFlow.prototype, "applyNodeHeaders", 1);
|
|
58254
58589
|
__decorateClass([
|
|
58255
58590
|
Stage41("parseInput")
|
|
58256
58591
|
], HandleStreamableHttpFlow.prototype, "parseInput", 1);
|
|
@@ -58295,21 +58630,21 @@ var init_handle_streamable_http_flow = __esm({
|
|
|
58295
58630
|
});
|
|
58296
58631
|
|
|
58297
58632
|
// libs/sdk/src/transport/transport.error.ts
|
|
58298
|
-
var
|
|
58633
|
+
var import_utils103, JSON_RPC2, rpcError, rpcRequest;
|
|
58299
58634
|
var init_transport_error = __esm({
|
|
58300
58635
|
"libs/sdk/src/transport/transport.error.ts"() {
|
|
58301
58636
|
"use strict";
|
|
58302
|
-
|
|
58637
|
+
import_utils103 = require("@frontmcp/utils");
|
|
58303
58638
|
JSON_RPC2 = "2.0";
|
|
58304
58639
|
rpcError = (message, requestId) => ({
|
|
58305
58640
|
jsonrpc: JSON_RPC2,
|
|
58306
58641
|
error: { code: -32e3, message },
|
|
58307
|
-
id: requestId ?? (0,
|
|
58642
|
+
id: requestId ?? (0, import_utils103.randomUUID)()
|
|
58308
58643
|
// change it to request id + random
|
|
58309
58644
|
});
|
|
58310
58645
|
rpcRequest = (requestId, method, params) => ({
|
|
58311
58646
|
jsonrpc: JSON_RPC2,
|
|
58312
|
-
id: requestId ?? (0,
|
|
58647
|
+
id: requestId ?? (0, import_utils103.randomUUID)(),
|
|
58313
58648
|
method,
|
|
58314
58649
|
...params !== void 0 && { params }
|
|
58315
58650
|
});
|
|
@@ -58372,14 +58707,14 @@ var init_host_validation = __esm({
|
|
|
58372
58707
|
});
|
|
58373
58708
|
|
|
58374
58709
|
// libs/sdk/src/transport/adapters/base-sse-transport.ts
|
|
58375
|
-
var import_content_type, import_raw_body, import_protocol55,
|
|
58710
|
+
var import_content_type, import_raw_body, import_protocol55, import_utils104, MAXIMUM_MESSAGE_SIZE, SSEServerTransport;
|
|
58376
58711
|
var init_base_sse_transport = __esm({
|
|
58377
58712
|
"libs/sdk/src/transport/adapters/base-sse-transport.ts"() {
|
|
58378
58713
|
"use strict";
|
|
58379
58714
|
import_content_type = __toESM(require("content-type"));
|
|
58380
58715
|
import_raw_body = __toESM(require("raw-body"));
|
|
58381
58716
|
import_protocol55 = require("@frontmcp/protocol");
|
|
58382
|
-
|
|
58717
|
+
import_utils104 = require("@frontmcp/utils");
|
|
58383
58718
|
init_transport_errors();
|
|
58384
58719
|
init_host_validation();
|
|
58385
58720
|
MAXIMUM_MESSAGE_SIZE = "4mb";
|
|
@@ -58390,7 +58725,7 @@ var init_base_sse_transport = __esm({
|
|
|
58390
58725
|
constructor(_endpoint, res, options) {
|
|
58391
58726
|
this._endpoint = _endpoint;
|
|
58392
58727
|
this.res = res;
|
|
58393
|
-
this._sessionId = options?.sessionId ?? (0,
|
|
58728
|
+
this._sessionId = options?.sessionId ?? (0, import_utils104.randomUUID)();
|
|
58394
58729
|
this._options = options || { enableDnsRebindingProtection: false };
|
|
58395
58730
|
}
|
|
58396
58731
|
_endpoint;
|
|
@@ -59836,11 +60171,11 @@ var init_transport_remote = __esm({
|
|
|
59836
60171
|
});
|
|
59837
60172
|
|
|
59838
60173
|
// libs/sdk/src/transport/transport.registry.ts
|
|
59839
|
-
var
|
|
60174
|
+
var import_utils105, SESSION_STORE_RETRY_BASE_MS, SESSION_STORE_RETRY_MAX_MS, TransportService;
|
|
59840
60175
|
var init_transport_registry = __esm({
|
|
59841
60176
|
"libs/sdk/src/transport/transport.registry.ts"() {
|
|
59842
60177
|
"use strict";
|
|
59843
|
-
|
|
60178
|
+
import_utils105 = require("@frontmcp/utils");
|
|
59844
60179
|
init_session_store_factory();
|
|
59845
60180
|
init_transport_errors();
|
|
59846
60181
|
init_handle_mcp_20260728_flow();
|
|
@@ -59850,6 +60185,8 @@ var init_transport_registry = __esm({
|
|
|
59850
60185
|
init_transport_local();
|
|
59851
60186
|
init_transport_remote();
|
|
59852
60187
|
init_transport_types();
|
|
60188
|
+
SESSION_STORE_RETRY_BASE_MS = 1e3;
|
|
60189
|
+
SESSION_STORE_RETRY_MAX_MS = 3e4;
|
|
59853
60190
|
TransportService = class {
|
|
59854
60191
|
ready;
|
|
59855
60192
|
byType = /* @__PURE__ */ new Map();
|
|
@@ -59906,8 +60243,28 @@ var init_transport_registry = __esm({
|
|
|
59906
60243
|
* Returns undefined if persistence is disabled or not configured.
|
|
59907
60244
|
*/
|
|
59908
60245
|
getDefaultTtlMs() {
|
|
60246
|
+
if (this.resolvedTtlMs !== void 0) return this.resolvedTtlMs;
|
|
59909
60247
|
return typeof this.persistenceConfig === "object" ? this.persistenceConfig?.defaultTtlMs : void 0;
|
|
59910
60248
|
}
|
|
60249
|
+
/**
|
|
60250
|
+
* TTL resolved from `persistence.defaultTtlMs`, then `persistence.redis.defaultTtlMs`, then 1 hour.
|
|
60251
|
+
* Set only when a persistence backend is configured.
|
|
60252
|
+
*/
|
|
60253
|
+
resolvedTtlMs;
|
|
60254
|
+
/** Store config kept after a successful create so a failed ping can be retried. */
|
|
60255
|
+
storeRetryConfig;
|
|
60256
|
+
storeRetryTimer;
|
|
60257
|
+
storeRetryAttempt = 0;
|
|
60258
|
+
destroyed = false;
|
|
60259
|
+
/** Last time a request refreshed the stored session's TTL, by session id. */
|
|
60260
|
+
lastTtlRefreshAt = /* @__PURE__ */ new Map();
|
|
60261
|
+
/**
|
|
60262
|
+
* Redis key prefix under which session records are stored.
|
|
60263
|
+
* The session store appends `session:` to the transport key prefix.
|
|
60264
|
+
*/
|
|
60265
|
+
getSessionKeyPrefix() {
|
|
60266
|
+
return `${this.transportKeyPrefix}session:`;
|
|
60267
|
+
}
|
|
59911
60268
|
constructor(scope, persistenceConfig, bus) {
|
|
59912
60269
|
this.scope = scope;
|
|
59913
60270
|
this.persistenceConfig = persistenceConfig;
|
|
@@ -59918,10 +60275,11 @@ var init_transport_registry = __esm({
|
|
|
59918
60275
|
this.backendKind = "sqlite";
|
|
59919
60276
|
const sqliteConfig = persistenceConfig.sqlite;
|
|
59920
60277
|
this.transportKeyPrefix = "mcp:transport:";
|
|
60278
|
+
this.resolvedTtlMs = persistenceConfig.defaultTtlMs ?? 36e5;
|
|
59921
60279
|
this.pendingStoreConfig = {
|
|
59922
60280
|
sqlite: sqliteConfig,
|
|
59923
60281
|
keyPrefix: this.transportKeyPrefix,
|
|
59924
|
-
defaultTtlMs:
|
|
60282
|
+
defaultTtlMs: this.resolvedTtlMs
|
|
59925
60283
|
};
|
|
59926
60284
|
this.scope.logger.info("[TransportService] sqlite session store will be initialized for transport persistence");
|
|
59927
60285
|
} else if (persistenceConfig !== false && persistenceConfig?.redis) {
|
|
@@ -59930,43 +60288,23 @@ var init_transport_registry = __esm({
|
|
|
59930
60288
|
const providerType = "provider" in redisConfig ? redisConfig.provider : "redis";
|
|
59931
60289
|
this.backendKind = providerType === "vercel-kv" ? "vercel-kv" : "redis";
|
|
59932
60290
|
this.transportKeyPrefix = redisConfig.keyPrefix ?? "mcp:transport:";
|
|
60291
|
+
this.resolvedTtlMs = persistenceConfig.defaultTtlMs ?? redisConfig.defaultTtlMs ?? 36e5;
|
|
59933
60292
|
this.pendingStoreConfig = {
|
|
59934
60293
|
...redisConfig,
|
|
59935
60294
|
keyPrefix: this.transportKeyPrefix,
|
|
59936
|
-
defaultTtlMs:
|
|
59937
|
-
// 1 hour default
|
|
60295
|
+
defaultTtlMs: this.resolvedTtlMs
|
|
59938
60296
|
};
|
|
59939
60297
|
this.scope.logger.info(
|
|
59940
60298
|
`[TransportService] ${providerType} session store will be initialized for transport persistence`
|
|
59941
60299
|
);
|
|
59942
60300
|
}
|
|
60301
|
+
this.storeRetryConfig = this.pendingStoreConfig;
|
|
59943
60302
|
this.ready = this.initialize();
|
|
59944
60303
|
}
|
|
59945
60304
|
async initialize() {
|
|
59946
60305
|
if (this.pendingStoreConfig) {
|
|
59947
|
-
|
|
59948
|
-
|
|
59949
|
-
this.sessionStore = store;
|
|
59950
|
-
this.pendingStoreConfig = void 0;
|
|
59951
|
-
} catch (error) {
|
|
59952
|
-
const err = error;
|
|
59953
|
-
this.scope.logger.error("[TransportService] Failed to create session store - session persistence disabled", {
|
|
59954
|
-
error: err.message,
|
|
59955
|
-
cause: err.cause?.message
|
|
59956
|
-
});
|
|
59957
|
-
}
|
|
59958
|
-
}
|
|
59959
|
-
if (this.sessionStore) {
|
|
59960
|
-
const isConnected = this.sessionStore.ping ? await this.sessionStore.ping() : true;
|
|
59961
|
-
if (!isConnected) {
|
|
59962
|
-
const providerType = this.backendKind ?? "redis";
|
|
59963
|
-
this.scope.logger.error(
|
|
59964
|
-
`[TransportService] Failed to connect to ${providerType} - session persistence disabled`
|
|
59965
|
-
);
|
|
59966
|
-
await this.teardownSessionStore().catch(() => void 0);
|
|
59967
|
-
} else {
|
|
59968
|
-
this.scope.logger.info("[TransportService] Session store connection validated successfully");
|
|
59969
|
-
}
|
|
60306
|
+
const connected = await this.connectSessionStore();
|
|
60307
|
+
if (!connected && this.backendKind !== "sqlite") this.scheduleSessionStoreRetry();
|
|
59970
60308
|
}
|
|
59971
60309
|
await this.scope.registryFlows(
|
|
59972
60310
|
HandleStreamableHttpFlow,
|
|
@@ -59975,7 +60313,70 @@ var init_transport_registry = __esm({
|
|
|
59975
60313
|
HandleMcp20260728Flow
|
|
59976
60314
|
);
|
|
59977
60315
|
}
|
|
60316
|
+
/**
|
|
60317
|
+
* Create the session store and validate the connection.
|
|
60318
|
+
* Returns true when the store is usable; on failure the store is left undefined.
|
|
60319
|
+
*/
|
|
60320
|
+
async connectSessionStore() {
|
|
60321
|
+
const storeConfig = this.pendingStoreConfig ?? this.storeRetryConfig;
|
|
60322
|
+
if (!storeConfig) return false;
|
|
60323
|
+
try {
|
|
60324
|
+
const store = await createSessionStore(storeConfig, this.scope.logger.child("SessionStore"));
|
|
60325
|
+
this.sessionStore = store;
|
|
60326
|
+
this.pendingStoreConfig = void 0;
|
|
60327
|
+
} catch (error) {
|
|
60328
|
+
const err = error;
|
|
60329
|
+
this.scope.logger.error("[TransportService] Failed to create session store - session persistence disabled", {
|
|
60330
|
+
error: err.message,
|
|
60331
|
+
cause: err.cause?.message
|
|
60332
|
+
});
|
|
60333
|
+
return false;
|
|
60334
|
+
}
|
|
60335
|
+
const isConnected = this.sessionStore?.ping ? await this.sessionStore.ping() : true;
|
|
60336
|
+
if (!isConnected) {
|
|
60337
|
+
const providerType = this.backendKind ?? "redis";
|
|
60338
|
+
this.scope.logger.error(`[TransportService] Failed to connect to ${providerType} - session persistence disabled`);
|
|
60339
|
+
await this.teardownSessionStore().catch(() => void 0);
|
|
60340
|
+
return false;
|
|
60341
|
+
}
|
|
60342
|
+
this.scope.logger.info("[TransportService] Session store connection validated successfully");
|
|
60343
|
+
return true;
|
|
60344
|
+
}
|
|
60345
|
+
/**
|
|
60346
|
+
* Retry the session store with exponential backoff (1s doubling to 30s) so a Redis that
|
|
60347
|
+
* was down at startup is picked up when it comes back, and `/readyz` turns healthy.
|
|
60348
|
+
*/
|
|
60349
|
+
scheduleSessionStoreRetry() {
|
|
60350
|
+
if (this.destroyed || this.storeRetryTimer) return;
|
|
60351
|
+
const delay = Math.min(SESSION_STORE_RETRY_MAX_MS, SESSION_STORE_RETRY_BASE_MS * 2 ** this.storeRetryAttempt);
|
|
60352
|
+
this.storeRetryAttempt++;
|
|
60353
|
+
this.storeRetryTimer = setTimeout(() => {
|
|
60354
|
+
this.storeRetryTimer = void 0;
|
|
60355
|
+
void this.retrySessionStore();
|
|
60356
|
+
}, delay);
|
|
60357
|
+
this.storeRetryTimer.unref?.();
|
|
60358
|
+
}
|
|
60359
|
+
async retrySessionStore() {
|
|
60360
|
+
if (this.destroyed || this.sessionStore) return;
|
|
60361
|
+
const connected = await this.connectSessionStore().catch(() => false);
|
|
60362
|
+
if (this.destroyed) {
|
|
60363
|
+
await this.teardownSessionStore().catch(() => void 0);
|
|
60364
|
+
return;
|
|
60365
|
+
}
|
|
60366
|
+
if (connected) {
|
|
60367
|
+
this.storeRetryAttempt = 0;
|
|
60368
|
+
this.scope.logger.info("[TransportService] Session store recovered after startup failure");
|
|
60369
|
+
return;
|
|
60370
|
+
}
|
|
60371
|
+
this.scheduleSessionStoreRetry();
|
|
60372
|
+
}
|
|
59978
60373
|
async destroy() {
|
|
60374
|
+
this.destroyed = true;
|
|
60375
|
+
if (this.storeRetryTimer) {
|
|
60376
|
+
clearTimeout(this.storeRetryTimer);
|
|
60377
|
+
this.storeRetryTimer = void 0;
|
|
60378
|
+
}
|
|
60379
|
+
this.lastTtlRefreshAt.clear();
|
|
59979
60380
|
await this.teardownSessionStore();
|
|
59980
60381
|
}
|
|
59981
60382
|
/**
|
|
@@ -60036,10 +60437,33 @@ var init_transport_registry = __esm({
|
|
|
60036
60437
|
getBackendKind() {
|
|
60037
60438
|
return this.backendKind;
|
|
60038
60439
|
}
|
|
60440
|
+
/**
|
|
60441
|
+
* Slide the stored session's TTL while this instance is serving it. Redis and Vercel KV
|
|
60442
|
+
* session stores extend the TTL on read, so a plain `get` is enough. Throttled per session
|
|
60443
|
+
* to a quarter of the TTL, fire-and-forget.
|
|
60444
|
+
*/
|
|
60445
|
+
refreshStoredSessionTtl(sessionId) {
|
|
60446
|
+
const store = this.sessionStore;
|
|
60447
|
+
const ttlMs = this.getDefaultTtlMs();
|
|
60448
|
+
if (!store || !ttlMs || this.backendKind !== "redis" && this.backendKind !== "vercel-kv") return;
|
|
60449
|
+
const now = Date.now();
|
|
60450
|
+
const last = this.lastTtlRefreshAt.get(sessionId);
|
|
60451
|
+
if (last !== void 0 && now - last < Math.max(1e3, Math.floor(ttlMs / 4))) return;
|
|
60452
|
+
this.lastTtlRefreshAt.set(sessionId, now);
|
|
60453
|
+
store.get(sessionId).catch((err) => {
|
|
60454
|
+
this.scope.logger.warn("[TransportService] Failed to refresh session TTL", {
|
|
60455
|
+
sessionId: sessionId.slice(0, 20),
|
|
60456
|
+
error: err instanceof Error ? err.message : String(err)
|
|
60457
|
+
});
|
|
60458
|
+
});
|
|
60459
|
+
}
|
|
60039
60460
|
async getTransporter(type, token, sessionId) {
|
|
60040
60461
|
const key = this.keyOf(type, token, sessionId);
|
|
60041
60462
|
const local = this.lookupLocal(key);
|
|
60042
|
-
if (local)
|
|
60463
|
+
if (local) {
|
|
60464
|
+
this.refreshStoredSessionTtl(sessionId);
|
|
60465
|
+
return local;
|
|
60466
|
+
}
|
|
60043
60467
|
if (this.distributed && this.bus) {
|
|
60044
60468
|
const location = await this.bus.lookup(key);
|
|
60045
60469
|
if (location) {
|
|
@@ -60132,9 +60556,9 @@ var init_transport_registry = __esm({
|
|
|
60132
60556
|
protocol: storedSession.session.protocol,
|
|
60133
60557
|
createdAt: storedSession.createdAt
|
|
60134
60558
|
});
|
|
60135
|
-
const currentNodeId = (0,
|
|
60559
|
+
const currentNodeId = (0, import_utils105.getMachineId)();
|
|
60136
60560
|
if (this.scope.haManager && storedSession.session.nodeId && storedSession.session.nodeId !== currentNodeId) {
|
|
60137
|
-
const sessionKey = `${this.
|
|
60561
|
+
const sessionKey = `${this.getSessionKeyPrefix()}${sessionId}`;
|
|
60138
60562
|
const result = await this.scope.haManager.attemptTakeover(sessionKey, storedSession.session.nodeId);
|
|
60139
60563
|
if (!result.claimed) {
|
|
60140
60564
|
this.scope.logger.debug("[HA] Session already claimed by another pod", { sessionId: sessionId.slice(0, 20) });
|
|
@@ -60227,7 +60651,7 @@ var init_transport_registry = __esm({
|
|
|
60227
60651
|
authorizationId: key.tokenHash,
|
|
60228
60652
|
protocol: type === "sse" ? "sse" : "streamable-http",
|
|
60229
60653
|
createdAt: Date.now(),
|
|
60230
|
-
nodeId: (0,
|
|
60654
|
+
nodeId: (0, import_utils105.getMachineId)()
|
|
60231
60655
|
},
|
|
60232
60656
|
authorizationId: key.tokenHash,
|
|
60233
60657
|
createdAt: Date.now(),
|
|
@@ -60373,7 +60797,7 @@ var init_transport_registry = __esm({
|
|
|
60373
60797
|
}
|
|
60374
60798
|
/* --------------------------------- internals -------------------------------- */
|
|
60375
60799
|
sha256(value) {
|
|
60376
|
-
return (0,
|
|
60800
|
+
return (0, import_utils105.sha256Hex)(value);
|
|
60377
60801
|
}
|
|
60378
60802
|
/**
|
|
60379
60803
|
* Create a history key from components.
|
|
@@ -60467,6 +60891,7 @@ var init_transport_registry = __esm({
|
|
|
60467
60891
|
}
|
|
60468
60892
|
}
|
|
60469
60893
|
evictLocal(key) {
|
|
60894
|
+
this.lastTtlRefreshAt.delete(key.sessionId);
|
|
60470
60895
|
const typeBucket = this.byType.get(key.type);
|
|
60471
60896
|
if (!typeBucket) return;
|
|
60472
60897
|
const tokenBucket = typeBucket.get(key.tokenHash);
|
|
@@ -60557,9 +60982,9 @@ function persistentSessionCallerKey(authorization) {
|
|
|
60557
60982
|
const sub = authorization?.user?.sub;
|
|
60558
60983
|
if (!(0, import_auth56.isAnonymousSubject)(sub)) {
|
|
60559
60984
|
const iss = authorization?.user?.iss;
|
|
60560
|
-
return `sub:${(0,
|
|
60985
|
+
return `sub:${(0, import_utils106.sha256Hex)(JSON.stringify([typeof iss === "string" ? iss : "", sub]))}`;
|
|
60561
60986
|
}
|
|
60562
|
-
if (authorization?.token) return `token:${(0,
|
|
60987
|
+
if (authorization?.token) return `token:${(0, import_utils106.sha256Hex)(authorization.token)}`;
|
|
60563
60988
|
return null;
|
|
60564
60989
|
}
|
|
60565
60990
|
function claimPersistentSession(session, callerKey, store) {
|
|
@@ -60572,12 +60997,12 @@ function claimPersistentSession(session, callerKey, store) {
|
|
|
60572
60997
|
}
|
|
60573
60998
|
return owners.get(session) === callerKey ? "owner" : "refused";
|
|
60574
60999
|
}
|
|
60575
|
-
var import_auth56,
|
|
61000
|
+
var import_auth56, import_utils106, owners;
|
|
60576
61001
|
var init_persistent_session_owner = __esm({
|
|
60577
61002
|
"libs/sdk/src/transport/persistent-session-owner.ts"() {
|
|
60578
61003
|
"use strict";
|
|
60579
61004
|
import_auth56 = require("@frontmcp/auth");
|
|
60580
|
-
|
|
61005
|
+
import_utils106 = require("@frontmcp/utils");
|
|
60581
61006
|
owners = /* @__PURE__ */ new WeakMap();
|
|
60582
61007
|
}
|
|
60583
61008
|
});
|
|
@@ -60614,7 +61039,7 @@ async function runWebStandardMcp(scope, request, options) {
|
|
|
60614
61039
|
if (options.persistent) {
|
|
60615
61040
|
return options.persistent.transport.handleRequest(request, { authInfo: options.authInfo });
|
|
60616
61041
|
}
|
|
60617
|
-
const sessionId = `web:${(0,
|
|
61042
|
+
const sessionId = `web:${(0, import_utils107.randomUUID)()}`;
|
|
60618
61043
|
const accept = request.headers.get("accept") ?? "";
|
|
60619
61044
|
const wantsStream = request.method === "GET" || options.sse && accept.includes("text/event-stream");
|
|
60620
61045
|
const { mcpServer, transport } = await wireServer(scope, options.serverOptions, sessionId, void 0, !wantsStream);
|
|
@@ -60637,23 +61062,23 @@ async function runWebStandardMcp(scope, request, options) {
|
|
|
60637
61062
|
}
|
|
60638
61063
|
return response;
|
|
60639
61064
|
}
|
|
60640
|
-
var
|
|
61065
|
+
var import_utils107;
|
|
60641
61066
|
var init_web_standard_mcp = __esm({
|
|
60642
61067
|
"libs/sdk/src/transport/web-standard-mcp.ts"() {
|
|
60643
61068
|
"use strict";
|
|
60644
|
-
|
|
61069
|
+
import_utils107 = require("@frontmcp/utils");
|
|
60645
61070
|
init_build_scoped_server_options();
|
|
60646
61071
|
}
|
|
60647
61072
|
});
|
|
60648
61073
|
|
|
60649
61074
|
// libs/sdk/src/scope/flows/http.request.flow.ts
|
|
60650
|
-
var import_auth57, import_lazy_zod108,
|
|
61075
|
+
var import_auth57, import_lazy_zod108, import_utils108, plan42, httpRequestStateSchema, name43, Stage43, HttpRequestFlow;
|
|
60651
61076
|
var init_http_request_flow = __esm({
|
|
60652
61077
|
"libs/sdk/src/scope/flows/http.request.flow.ts"() {
|
|
60653
61078
|
"use strict";
|
|
60654
61079
|
import_auth57 = require("@frontmcp/auth");
|
|
60655
61080
|
import_lazy_zod108 = require("@frontmcp/lazy-zod");
|
|
60656
|
-
|
|
61081
|
+
import_utils108 = require("@frontmcp/utils");
|
|
60657
61082
|
init_session_verify_flow();
|
|
60658
61083
|
init_common();
|
|
60659
61084
|
init_schema7();
|
|
@@ -60732,7 +61157,7 @@ var init_http_request_flow = __esm({
|
|
|
60732
61157
|
const { request } = this.rawInput;
|
|
60733
61158
|
this.requestStartTime = Date.now();
|
|
60734
61159
|
const ctx = this.tryGetContext();
|
|
60735
|
-
this.requestId = ctx?.requestId ?? `req-${(0,
|
|
61160
|
+
this.requestId = ctx?.requestId ?? `req-${(0, import_utils108.randomUUID)()}`;
|
|
60736
61161
|
const headers = request.headers ?? {};
|
|
60737
61162
|
const body = request.body;
|
|
60738
61163
|
const userAgent = headers["user-agent"];
|
|
@@ -61417,13 +61842,13 @@ var init_http_request_flow = __esm({
|
|
|
61417
61842
|
});
|
|
61418
61843
|
|
|
61419
61844
|
// libs/sdk/src/scope/scope.instance.ts
|
|
61420
|
-
var import_reflect_metadata25, import_guard8,
|
|
61845
|
+
var import_reflect_metadata25, import_guard8, import_utils109, SCOPE_NON_DISPATCHABLE_FLOWS, Scope;
|
|
61421
61846
|
var init_scope_instance = __esm({
|
|
61422
61847
|
"libs/sdk/src/scope/scope.instance.ts"() {
|
|
61423
61848
|
"use strict";
|
|
61424
61849
|
import_reflect_metadata25 = require("reflect-metadata");
|
|
61425
61850
|
import_guard8 = require("@frontmcp/guard");
|
|
61426
|
-
|
|
61851
|
+
import_utils109 = require("@frontmcp/utils");
|
|
61427
61852
|
init_agent_registry();
|
|
61428
61853
|
init_call_agent_flow();
|
|
61429
61854
|
init_app_registry();
|
|
@@ -61464,6 +61889,7 @@ var init_scope_instance = __esm({
|
|
|
61464
61889
|
init_tool_registry();
|
|
61465
61890
|
init_tool_utils();
|
|
61466
61891
|
init_ui();
|
|
61892
|
+
init_ui_option_warnings();
|
|
61467
61893
|
init_bus();
|
|
61468
61894
|
init_event_stores();
|
|
61469
61895
|
init_request_state();
|
|
@@ -61490,6 +61916,8 @@ var init_scope_instance = __esm({
|
|
|
61490
61916
|
transportService;
|
|
61491
61917
|
// TODO: migrate transport service to transport.registry
|
|
61492
61918
|
notificationService;
|
|
61919
|
+
/** ioredis client owned by the HA manager and transport bus; closed in shutdown(). */
|
|
61920
|
+
haRedisClient;
|
|
61493
61921
|
toolUIRegistry;
|
|
61494
61922
|
authUiRegistry;
|
|
61495
61923
|
entryPath;
|
|
@@ -61557,7 +61985,7 @@ var init_scope_instance = __esm({
|
|
|
61557
61985
|
await this.scopeProviders.ready;
|
|
61558
61986
|
const scopeRef = { kind: "scope", id: this.id, ref: _Scope };
|
|
61559
61987
|
const scopeProviders = this.scopeProviders;
|
|
61560
|
-
const perf = (0,
|
|
61988
|
+
const perf = (0, import_utils109.getEnvFlag)("FRONTMCP_PERF");
|
|
61561
61989
|
const t0 = perf ? performance.now() : 0;
|
|
61562
61990
|
const mark = perf ? (label) => this.logger.info(`[PERF] ${label}: ${(performance.now() - t0).toFixed(1)}ms`) : () => {
|
|
61563
61991
|
};
|
|
@@ -61567,24 +61995,26 @@ var init_scope_instance = __esm({
|
|
|
61567
61995
|
this.scopeApps = new AppRegistry(this.scopeProviders, this.metadata.apps, scopeRef);
|
|
61568
61996
|
this.logger.info(`Initializing ${this.metadata.apps.length} app(s)...`);
|
|
61569
61997
|
const transportConfig = this.metadata.transport;
|
|
61570
|
-
const isDistributed = (0,
|
|
61998
|
+
const isDistributed = (0, import_utils109.getRuntimeContext)().deployment === "distributed";
|
|
61571
61999
|
if (isDistributed && this.metadata.redis && !this.cliMode) {
|
|
61572
62000
|
try {
|
|
61573
|
-
const haRedis = this.
|
|
62001
|
+
const haRedis = this.createHaRedisClient();
|
|
61574
62002
|
this.haManager = HaManager.create({
|
|
61575
62003
|
redis: haRedis,
|
|
61576
|
-
nodeId: (0,
|
|
62004
|
+
nodeId: (0, import_utils109.getMachineId)(),
|
|
61577
62005
|
logger: this.logger
|
|
61578
62006
|
});
|
|
61579
62007
|
await this.haManager.start();
|
|
61580
62008
|
this.logger.info("[HA] Manager started for distributed deployment");
|
|
61581
62009
|
} catch (err) {
|
|
61582
62010
|
this.logger.warn("[HA] Failed to start HA manager \u2014 running without HA", { error: err });
|
|
62011
|
+
this.haManager = void 0;
|
|
62012
|
+
await this.closeHaRedisClient();
|
|
61583
62013
|
}
|
|
61584
62014
|
}
|
|
61585
62015
|
let transportBus;
|
|
61586
|
-
if (this.haManager &&
|
|
61587
|
-
transportBus = new RedisTransportBus(this.
|
|
62016
|
+
if (this.haManager && this.haRedisClient && isDistributed) {
|
|
62017
|
+
transportBus = new RedisTransportBus(this.haRedisClient, (0, import_utils109.getMachineId)(), {
|
|
61588
62018
|
logger: this.logger
|
|
61589
62019
|
});
|
|
61590
62020
|
this.logger.info("[HA] Transport bus created for distributed session routing");
|
|
@@ -61596,11 +62026,9 @@ var init_scope_instance = __esm({
|
|
|
61596
62026
|
}
|
|
61597
62027
|
this.transportService = new TransportService(this, effectivePersistence, transportBus);
|
|
61598
62028
|
warnIfRequestStateKeyNotShared({ logger: this.logger, metadata: this.metadata });
|
|
61599
|
-
if (this.haManager && isDistributed) {
|
|
61600
|
-
const persistCfg = transportConfig?.persistence;
|
|
61601
|
-
const resolvedPrefix = persistCfg && typeof persistCfg === "object" && persistCfg.redis?.keyPrefix ? persistCfg.redis.keyPrefix : "mcp:transport:";
|
|
62029
|
+
if (this.haManager && isDistributed && this.transportService.getBackendKind() === "redis") {
|
|
61602
62030
|
this.haManager.startOrphanScanner({
|
|
61603
|
-
sessionKeyPrefix:
|
|
62031
|
+
sessionKeyPrefix: this.transportService.getSessionKeyPrefix(),
|
|
61604
62032
|
onOrphan: (sessionId, previousNodeId) => {
|
|
61605
62033
|
this.logger.info(
|
|
61606
62034
|
`[HA] Orphaned session ${sessionId.slice(0, 20)} from ${previousNodeId} \u2014 available for recreation`
|
|
@@ -61608,7 +62036,7 @@ var init_scope_instance = __esm({
|
|
|
61608
62036
|
}
|
|
61609
62037
|
});
|
|
61610
62038
|
}
|
|
61611
|
-
if ((0,
|
|
62039
|
+
if ((0, import_utils109.getRuntimeContext)().deployment === "serverless") {
|
|
61612
62040
|
const protocolCfg = transportConfig?.protocol;
|
|
61613
62041
|
const hasSSE = typeof protocolCfg === "string" ? protocolCfg !== "stateless-api" : protocolCfg?.sse === true;
|
|
61614
62042
|
if (hasSSE) {
|
|
@@ -61635,7 +62063,7 @@ var init_scope_instance = __esm({
|
|
|
61635
62063
|
sqlite: elicitationSqlite,
|
|
61636
62064
|
keyPrefix: elicitationRedis?.keyPrefix ?? "mcp:elicit:",
|
|
61637
62065
|
logger: this.logger,
|
|
61638
|
-
isEdgeRuntime: (0,
|
|
62066
|
+
isEdgeRuntime: (0, import_utils109.isEdgeRuntime)()
|
|
61639
62067
|
});
|
|
61640
62068
|
this._elicitationStore = elicitStore;
|
|
61641
62069
|
this.elicitationBackendType = elicitType;
|
|
@@ -61649,14 +62077,22 @@ var init_scope_instance = __esm({
|
|
|
61649
62077
|
);
|
|
61650
62078
|
const tasksEnabledForCli = this.cliMode && hasPersistentBackend;
|
|
61651
62079
|
const hasDistributedTaskBackend = Boolean(tasksConfig?.redis ?? this.metadata.redis);
|
|
61652
|
-
const onEdgeRuntime = (0,
|
|
62080
|
+
const onEdgeRuntime = (0, import_utils109.isEdgeRuntime)();
|
|
61653
62081
|
const tasksUnavailableOnEdge = onEdgeRuntime && !hasDistributedTaskBackend && !tasksExplicitlyEnabled;
|
|
61654
62082
|
if (tasksUnavailableOnEdge) {
|
|
61655
62083
|
this.logger.info(
|
|
61656
62084
|
"[tasks] Background tasks are unavailable on this edge/serverless runtime: the in-process runner cannot continue past the HTTP response, and an in-memory store is not shared between isolates. Serving without tasks. Configure `tasks.redis` (Upstash Redis over HTTP works on Workers) to enable them."
|
|
61657
62085
|
);
|
|
61658
62086
|
}
|
|
61659
|
-
const
|
|
62087
|
+
const gateTasksRedis = tasksConfig?.redis ?? this.metadata.redis;
|
|
62088
|
+
const tasksUseSqlite = Boolean(tasksConfig?.sqlite ?? (gateTasksRedis ? void 0 : resolvedTopLevelSqlite));
|
|
62089
|
+
const tasksUnavailableOnVercelKv = !tasksExplicitlyEnabled && !tasksUseSqlite && resolvesToVercelKvTaskBackend(gateTasksRedis);
|
|
62090
|
+
if (tasksUnavailableOnVercelKv && !tasksExplicitlyDisabled) {
|
|
62091
|
+
this.logger.warn(
|
|
62092
|
+
"[tasks] Background tasks are disabled: Vercel KV has no pub/sub, which task results and cancellation require. Serving without tasks. Configure `tasks.redis` with Redis or Upstash to enable them."
|
|
62093
|
+
);
|
|
62094
|
+
}
|
|
62095
|
+
const shouldInitTasks = !tasksExplicitlyDisabled && !tasksUnavailableOnEdge && !tasksUnavailableOnVercelKv && (!this.cliMode || tasksEnabledForCli || isTaskWorker);
|
|
61660
62096
|
const tasksPromise = shouldInitTasks ? (async () => {
|
|
61661
62097
|
const tasksRedis = tasksConfig?.redis ?? this.metadata.redis;
|
|
61662
62098
|
const tasksSqlite = tasksConfig?.sqlite ?? (tasksRedis ? void 0 : resolvedTopLevelSqlite);
|
|
@@ -62155,6 +62591,16 @@ var init_scope_instance = __esm({
|
|
|
62155
62591
|
async compileUIWidgets() {
|
|
62156
62592
|
const toolsWithUI = this.scopeTools.getTools(true).filter((t) => hasUIConfig(t.metadata));
|
|
62157
62593
|
if (toolsWithUI.length === 0) return;
|
|
62594
|
+
for (const tool of toolsWithUI) {
|
|
62595
|
+
const resourceUri = tool.metadata.ui?.resourceUri;
|
|
62596
|
+
this.toolUIRegistry.registerTool(tool.metadata.name, typeof resourceUri === "string" ? resourceUri : void 0);
|
|
62597
|
+
for (const message of describeIgnoredUiOptions(
|
|
62598
|
+
tool.metadata.name,
|
|
62599
|
+
tool.metadata.ui
|
|
62600
|
+
)) {
|
|
62601
|
+
this.logger.warn(message);
|
|
62602
|
+
}
|
|
62603
|
+
}
|
|
62158
62604
|
this.scopeResources.registerDynamicResource(StaticWidgetResourceTemplate);
|
|
62159
62605
|
this.logger.verbose(`Registered UI resource template for ${toolsWithUI.length} tool(s) with UI configs`);
|
|
62160
62606
|
const staticModeTools = toolsWithUI.filter(
|
|
@@ -62733,6 +63179,29 @@ var init_scope_instance = __esm({
|
|
|
62733
63179
|
}
|
|
62734
63180
|
throw new FlowExitedWithoutOutputError();
|
|
62735
63181
|
}
|
|
63182
|
+
/**
|
|
63183
|
+
* Build the ioredis client shared by the HA manager and the transport bus from `metadata.redis`.
|
|
63184
|
+
* The client reconnects on its own and has a permanent, rate-limited 'error' listener.
|
|
63185
|
+
*/
|
|
63186
|
+
createHaRedisClient() {
|
|
63187
|
+
const redis = this.metadata.redis;
|
|
63188
|
+
if (!redis || redis.provider === "vercel-kv") {
|
|
63189
|
+
throw new Error("Distributed HA requires a TCP Redis (ioredis); Vercel KV over REST has no Lua/pub-sub support");
|
|
63190
|
+
}
|
|
63191
|
+
const client = (0, import_utils109.createRedisClient)({ ...redis, label: "ha", logger: this.logger });
|
|
63192
|
+
this.haRedisClient = client;
|
|
63193
|
+
return client;
|
|
63194
|
+
}
|
|
63195
|
+
async closeHaRedisClient() {
|
|
63196
|
+
const client = this.haRedisClient;
|
|
63197
|
+
if (!client) return;
|
|
63198
|
+
this.haRedisClient = void 0;
|
|
63199
|
+
try {
|
|
63200
|
+
await client.quit();
|
|
63201
|
+
} catch {
|
|
63202
|
+
client.disconnect();
|
|
63203
|
+
}
|
|
63204
|
+
}
|
|
62736
63205
|
/**
|
|
62737
63206
|
* Shut down the scope and release resources.
|
|
62738
63207
|
* Disconnects channel service connectors, unsubscribes event handlers,
|
|
@@ -62746,6 +63215,7 @@ var init_scope_instance = __esm({
|
|
|
62746
63215
|
this.logger.error("[HA] Manager shutdown failed", { error: err });
|
|
62747
63216
|
}
|
|
62748
63217
|
}
|
|
63218
|
+
await this.closeHaRedisClient();
|
|
62749
63219
|
if (this._channelTeardown) {
|
|
62750
63220
|
try {
|
|
62751
63221
|
await this._channelTeardown();
|
|
@@ -63867,6 +64337,178 @@ var init_scope_registry = __esm({
|
|
|
63867
64337
|
}
|
|
63868
64338
|
});
|
|
63869
64339
|
|
|
64340
|
+
// libs/sdk/src/transport/ipc-server.transport.ts
|
|
64341
|
+
function isIpcStdioRequested() {
|
|
64342
|
+
return !!process.env["FRONTMCP_DEV_STDIO_FD"] && typeof process.send === "function";
|
|
64343
|
+
}
|
|
64344
|
+
var IPC_READY_MESSAGE, IpcServerTransport;
|
|
64345
|
+
var init_ipc_server_transport = __esm({
|
|
64346
|
+
"libs/sdk/src/transport/ipc-server.transport.ts"() {
|
|
64347
|
+
"use strict";
|
|
64348
|
+
IPC_READY_MESSAGE = { __frontmcp: "ready" };
|
|
64349
|
+
IpcServerTransport = class {
|
|
64350
|
+
onclose;
|
|
64351
|
+
onerror;
|
|
64352
|
+
onmessage;
|
|
64353
|
+
started = false;
|
|
64354
|
+
handleMessage = (raw) => {
|
|
64355
|
+
if (!raw || typeof raw !== "object" || raw.__frontmcp !== void 0) return;
|
|
64356
|
+
this.onmessage?.(raw);
|
|
64357
|
+
};
|
|
64358
|
+
handleDisconnect = () => {
|
|
64359
|
+
this.onclose?.();
|
|
64360
|
+
};
|
|
64361
|
+
async start() {
|
|
64362
|
+
if (this.started) throw new Error("IpcServerTransport already started");
|
|
64363
|
+
if (typeof process.send !== "function") {
|
|
64364
|
+
throw new Error("IpcServerTransport requires a process forked with an IPC channel");
|
|
64365
|
+
}
|
|
64366
|
+
this.started = true;
|
|
64367
|
+
process.on("message", this.handleMessage);
|
|
64368
|
+
process.once("disconnect", this.handleDisconnect);
|
|
64369
|
+
process.send(IPC_READY_MESSAGE);
|
|
64370
|
+
}
|
|
64371
|
+
async send(message) {
|
|
64372
|
+
const send = process.send;
|
|
64373
|
+
if (!process.connected || typeof send !== "function") {
|
|
64374
|
+
throw new Error("IPC channel is closed");
|
|
64375
|
+
}
|
|
64376
|
+
await new Promise((resolve, reject) => {
|
|
64377
|
+
send.call(process, message, (err) => err ? reject(err) : resolve());
|
|
64378
|
+
});
|
|
64379
|
+
}
|
|
64380
|
+
async close() {
|
|
64381
|
+
process.off("message", this.handleMessage);
|
|
64382
|
+
process.off("disconnect", this.handleDisconnect);
|
|
64383
|
+
this.started = false;
|
|
64384
|
+
this.onclose?.();
|
|
64385
|
+
}
|
|
64386
|
+
};
|
|
64387
|
+
}
|
|
64388
|
+
});
|
|
64389
|
+
|
|
64390
|
+
// libs/sdk/src/server/middleware/csp.middleware.ts
|
|
64391
|
+
function readCspFromEnv() {
|
|
64392
|
+
if (!(0, import_utils110.getEnvFlag)("FRONTMCP_CSP_ENABLED")) return void 0;
|
|
64393
|
+
const rawDirectives = (0, import_utils110.getEnv)("FRONTMCP_CSP_DIRECTIVES") ?? "";
|
|
64394
|
+
const directives = {};
|
|
64395
|
+
for (const part of rawDirectives.split(";")) {
|
|
64396
|
+
const trimmed = part.trim();
|
|
64397
|
+
if (!trimmed) continue;
|
|
64398
|
+
const spaceIdx = trimmed.indexOf(" ");
|
|
64399
|
+
if (spaceIdx > 0) {
|
|
64400
|
+
directives[trimmed.slice(0, spaceIdx)] = trimmed.slice(spaceIdx + 1);
|
|
64401
|
+
} else {
|
|
64402
|
+
directives[trimmed] = "";
|
|
64403
|
+
}
|
|
64404
|
+
}
|
|
64405
|
+
return {
|
|
64406
|
+
enabled: true,
|
|
64407
|
+
directives,
|
|
64408
|
+
reportUri: (0, import_utils110.getEnv)("FRONTMCP_CSP_REPORT_URI"),
|
|
64409
|
+
reportOnly: (0, import_utils110.getEnvFlag)("FRONTMCP_CSP_REPORT_ONLY")
|
|
64410
|
+
};
|
|
64411
|
+
}
|
|
64412
|
+
function buildCspHeaderValue(options) {
|
|
64413
|
+
const parts = [];
|
|
64414
|
+
for (const [directive, value] of Object.entries(options.directives)) {
|
|
64415
|
+
parts.push(value ? `${directive} ${value}` : directive);
|
|
64416
|
+
}
|
|
64417
|
+
if (options.reportUri) {
|
|
64418
|
+
parts.push(`report-uri ${options.reportUri}`);
|
|
64419
|
+
}
|
|
64420
|
+
return parts.join("; ");
|
|
64421
|
+
}
|
|
64422
|
+
function getCspHeaderName(reportOnly) {
|
|
64423
|
+
return reportOnly ? "Content-Security-Policy-Report-Only" : "Content-Security-Policy";
|
|
64424
|
+
}
|
|
64425
|
+
function readSecurityHeadersFromEnv() {
|
|
64426
|
+
return {
|
|
64427
|
+
hsts: readHeaderEnv("FRONTMCP_HSTS"),
|
|
64428
|
+
contentTypeOptions: readHeaderEnv("FRONTMCP_CONTENT_TYPE_OPTIONS", "nosniff"),
|
|
64429
|
+
frameOptions: readHeaderEnv("FRONTMCP_FRAME_OPTIONS", "DENY"),
|
|
64430
|
+
custom: readCustomHeadersFromEnv()
|
|
64431
|
+
};
|
|
64432
|
+
}
|
|
64433
|
+
function readHeaderEnv(name44, fallback) {
|
|
64434
|
+
const value = (0, import_utils110.getEnv)(name44);
|
|
64435
|
+
if (value === void 0 || value === "") return fallback;
|
|
64436
|
+
return ["off", "false", "none"].includes(value.trim().toLowerCase()) ? void 0 : value;
|
|
64437
|
+
}
|
|
64438
|
+
function readCustomHeadersFromEnv() {
|
|
64439
|
+
const raw = (0, import_utils110.getEnv)("FRONTMCP_HEADERS_CUSTOM");
|
|
64440
|
+
if (!raw) return void 0;
|
|
64441
|
+
try {
|
|
64442
|
+
const parsed = JSON.parse(raw);
|
|
64443
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) return void 0;
|
|
64444
|
+
return Object.fromEntries(
|
|
64445
|
+
Object.entries(parsed).filter((entry) => typeof entry[1] === "string")
|
|
64446
|
+
);
|
|
64447
|
+
} catch {
|
|
64448
|
+
return void 0;
|
|
64449
|
+
}
|
|
64450
|
+
}
|
|
64451
|
+
function applySecurityHeaders(res, headers, csp) {
|
|
64452
|
+
if (headers.hsts) {
|
|
64453
|
+
res.setHeader("Strict-Transport-Security", headers.hsts);
|
|
64454
|
+
}
|
|
64455
|
+
if (headers.contentTypeOptions) {
|
|
64456
|
+
res.setHeader("X-Content-Type-Options", headers.contentTypeOptions);
|
|
64457
|
+
}
|
|
64458
|
+
if (headers.frameOptions) {
|
|
64459
|
+
res.setHeader("X-Frame-Options", headers.frameOptions);
|
|
64460
|
+
}
|
|
64461
|
+
if (headers.custom) {
|
|
64462
|
+
for (const [name44, value] of Object.entries(headers.custom)) {
|
|
64463
|
+
res.setHeader(name44, value);
|
|
64464
|
+
}
|
|
64465
|
+
}
|
|
64466
|
+
if (csp?.enabled) {
|
|
64467
|
+
const headerName = getCspHeaderName(csp.reportOnly);
|
|
64468
|
+
const headerValue = buildCspHeaderValue(csp);
|
|
64469
|
+
if (headerValue) {
|
|
64470
|
+
res.setHeader(headerName, headerValue);
|
|
64471
|
+
}
|
|
64472
|
+
}
|
|
64473
|
+
}
|
|
64474
|
+
function resolveSecurityHeaders(options = {}) {
|
|
64475
|
+
const env = readSecurityHeadersFromEnv();
|
|
64476
|
+
const pick = (explicit, fromEnv) => explicit === false ? void 0 : explicit ?? fromEnv;
|
|
64477
|
+
const resolved = {
|
|
64478
|
+
hsts: pick(options.hsts, env.hsts),
|
|
64479
|
+
contentTypeOptions: pick(options.contentTypeOptions, env.contentTypeOptions),
|
|
64480
|
+
frameOptions: pick(options.frameOptions, env.frameOptions),
|
|
64481
|
+
custom: { ...env.custom, ...options.custom }
|
|
64482
|
+
};
|
|
64483
|
+
let csp;
|
|
64484
|
+
if (options.csp?.enabled === false) {
|
|
64485
|
+
csp = void 0;
|
|
64486
|
+
} else if (options.csp?.enabled === true) {
|
|
64487
|
+
const directives = {};
|
|
64488
|
+
for (const [name44, value] of Object.entries(options.csp.directives ?? {})) {
|
|
64489
|
+
directives[name44] = Array.isArray(value) ? value.join(" ") : value;
|
|
64490
|
+
}
|
|
64491
|
+
csp = {
|
|
64492
|
+
enabled: true,
|
|
64493
|
+
directives,
|
|
64494
|
+
reportUri: options.csp.reportUri,
|
|
64495
|
+
reportOnly: options.csp.reportOnly ?? false
|
|
64496
|
+
};
|
|
64497
|
+
} else {
|
|
64498
|
+
csp = readCspFromEnv();
|
|
64499
|
+
}
|
|
64500
|
+
const headers = {};
|
|
64501
|
+
applySecurityHeaders({ setHeader: (name44, value) => void (headers[name44] = value) }, resolved, csp);
|
|
64502
|
+
return headers;
|
|
64503
|
+
}
|
|
64504
|
+
var import_utils110;
|
|
64505
|
+
var init_csp_middleware = __esm({
|
|
64506
|
+
"libs/sdk/src/server/middleware/csp.middleware.ts"() {
|
|
64507
|
+
"use strict";
|
|
64508
|
+
import_utils110 = require("@frontmcp/utils");
|
|
64509
|
+
}
|
|
64510
|
+
});
|
|
64511
|
+
|
|
63870
64512
|
// libs/sdk/src/transport/web-response.renderer.ts
|
|
63871
64513
|
function toByteStream(streamLike) {
|
|
63872
64514
|
const iter = streamLike[Symbol.asyncIterator];
|
|
@@ -63971,6 +64613,7 @@ function isCloudflareWorkersRuntime(request) {
|
|
|
63971
64613
|
return typeof cloudflareProperties === "object" && cloudflareProperties !== null;
|
|
63972
64614
|
}
|
|
63973
64615
|
function resolvePeerAddress(request, ctx) {
|
|
64616
|
+
if (carriedPeerAddresses.has(request)) return carriedPeerAddresses.get(request);
|
|
63974
64617
|
if (typeof ctx?.requestIP === "function") return ctx.requestIP(request)?.address;
|
|
63975
64618
|
if (typeof ctx?.remoteAddr?.hostname === "string") return ctx.remoteAddr.hostname;
|
|
63976
64619
|
if (isCloudflareWorkersRuntime(request)) return request.headers.get("cf-connecting-ip") ?? void 0;
|
|
@@ -63989,7 +64632,13 @@ function mapHttpCors(httpCors, scope) {
|
|
|
63989
64632
|
}
|
|
63990
64633
|
function createWebFetchHandler(scope, options = {}) {
|
|
63991
64634
|
const httpConfig = scope.metadata.http;
|
|
63992
|
-
const
|
|
64635
|
+
const healthConfig = scope.metadata.health ?? {};
|
|
64636
|
+
const healthEnabled = healthConfig.enabled !== false;
|
|
64637
|
+
const healthzPath = healthConfig.healthzPath ?? "/healthz";
|
|
64638
|
+
const readyzPath = healthConfig.readyzPath ?? "/readyz";
|
|
64639
|
+
const readyzEnabled = isReadyzEnabled(healthConfig);
|
|
64640
|
+
const bodyLimit = parseByteLimit(httpConfig?.bodyLimit);
|
|
64641
|
+
const securityHeaders = resolveSecurityHeaders(httpConfig?.securityHeaders);
|
|
63993
64642
|
const normalizePath = (p) => {
|
|
63994
64643
|
const withSlash = p.startsWith("/") ? p : `/${p}`;
|
|
63995
64644
|
let end = withSlash.length;
|
|
@@ -64033,7 +64682,13 @@ function createWebFetchHandler(scope, options = {}) {
|
|
|
64033
64682
|
for (const [k, v] of Object.entries(corsHeadersFor(request))) headers.set(k, v);
|
|
64034
64683
|
return new Response(response.body, { status: response.status, statusText: response.statusText, headers });
|
|
64035
64684
|
};
|
|
64036
|
-
|
|
64685
|
+
const withSecurityHeaders = (response) => {
|
|
64686
|
+
const headers = new Headers(response.headers);
|
|
64687
|
+
for (const [k, v] of Object.entries(securityHeaders)) if (!headers.has(k)) headers.set(k, v);
|
|
64688
|
+
headers.delete("x-powered-by");
|
|
64689
|
+
return new Response(response.body, { status: response.status, statusText: response.statusText, headers });
|
|
64690
|
+
};
|
|
64691
|
+
const handleRequest = async (request, ctx, env) => {
|
|
64037
64692
|
const url = new URL(request.url);
|
|
64038
64693
|
if (hostValidation) {
|
|
64039
64694
|
const rejection = validateHostHeaders(
|
|
@@ -64048,17 +64703,34 @@ function createWebFetchHandler(scope, options = {}) {
|
|
|
64048
64703
|
return Response.json({ error: rejection.error, message: rejection.message }, { status: rejection.status });
|
|
64049
64704
|
}
|
|
64050
64705
|
}
|
|
64706
|
+
const limited = await enforceBodyLimit(request, bodyLimit, ctx);
|
|
64707
|
+
if (limited instanceof Response) return withCors(limited, request);
|
|
64708
|
+
request = limited;
|
|
64051
64709
|
if (corsEnabled && request.method === "OPTIONS") {
|
|
64052
64710
|
return new Response(null, { status: 204, headers: corsHeadersFor(request) });
|
|
64053
64711
|
}
|
|
64054
|
-
if (healthPaths.
|
|
64055
|
-
|
|
64056
|
-
|
|
64057
|
-
|
|
64058
|
-
|
|
64059
|
-
|
|
64060
|
-
|
|
64061
|
-
|
|
64712
|
+
if (options.healthPaths ? options.healthPaths.includes(url.pathname) : healthEnabled) {
|
|
64713
|
+
const isReadyz = !options.healthPaths && url.pathname === readyzPath && readyzEnabled;
|
|
64714
|
+
const isHealthz = options.healthPaths ? url.pathname !== "/readyz" : url.pathname === healthzPath || url.pathname === "/health";
|
|
64715
|
+
if (isReadyz && scope.healthService) {
|
|
64716
|
+
const readiness = await scope.healthService.getReadyz();
|
|
64717
|
+
return withCors(
|
|
64718
|
+
Response.json(
|
|
64719
|
+
{ ...readiness, transport: "web-fetch" },
|
|
64720
|
+
{ status: readiness.status === "ready" ? 200 : 503, headers: { "Cache-Control": "no-store" } }
|
|
64721
|
+
),
|
|
64722
|
+
request
|
|
64723
|
+
);
|
|
64724
|
+
}
|
|
64725
|
+
if (isHealthz || isReadyz || options.healthPaths) {
|
|
64726
|
+
return withCors(
|
|
64727
|
+
Response.json(
|
|
64728
|
+
{ status: "ok", server: scope.metadata.info, transport: "web-fetch" },
|
|
64729
|
+
{ headers: { "Cache-Control": "no-store" } }
|
|
64730
|
+
),
|
|
64731
|
+
request
|
|
64732
|
+
);
|
|
64733
|
+
}
|
|
64062
64734
|
}
|
|
64063
64735
|
if (!entryPaths.has(normalizePath(url.pathname))) {
|
|
64064
64736
|
const authResponse = await runMatchingHttpFlowWeb(scope, request, { ctx, env });
|
|
@@ -64072,13 +64744,60 @@ function createWebFetchHandler(scope, options = {}) {
|
|
|
64072
64744
|
const rendered = await runHttpRequestFlowWeb(scope, request, { ctx, env });
|
|
64073
64745
|
return withCors(rendered ?? Response.json({ error: "Not Found" }, { status: 404 }), request);
|
|
64074
64746
|
};
|
|
64747
|
+
return async function handle(request, ctx, env) {
|
|
64748
|
+
return withSecurityHeaders(await handleRequest(request, ctx, env));
|
|
64749
|
+
};
|
|
64750
|
+
}
|
|
64751
|
+
function parseByteLimit(limit) {
|
|
64752
|
+
if (typeof limit === "number" && Number.isFinite(limit) && limit >= 0) return limit;
|
|
64753
|
+
if (typeof limit === "string") {
|
|
64754
|
+
const match = /^(\d+(?:\.\d+)?)\s*(b|kb|mb|gb)?$/i.exec(limit.trim());
|
|
64755
|
+
if (match) return Math.floor(Number(match[1]) * BYTE_UNITS[(match[2] ?? "b").toLowerCase()]);
|
|
64756
|
+
}
|
|
64757
|
+
return DEFAULT_BODY_LIMIT_BYTES;
|
|
64758
|
+
}
|
|
64759
|
+
function payloadTooLarge(limit, length) {
|
|
64760
|
+
const error = new PayloadTooLargeError(limit, length);
|
|
64761
|
+
return Response.json({ jsonrpc: "2.0", error: error.toJsonRpcError(), id: null }, { status: 413 });
|
|
64762
|
+
}
|
|
64763
|
+
async function enforceBodyLimit(request, limit, ctx) {
|
|
64764
|
+
const method = request.method.toUpperCase();
|
|
64765
|
+
if (method === "GET" || method === "HEAD" || method === "OPTIONS" || !request.body) return request;
|
|
64766
|
+
const declared = request.headers.get("content-length");
|
|
64767
|
+
if (declared !== null) {
|
|
64768
|
+
const length = Number(declared);
|
|
64769
|
+
if (Number.isFinite(length) && length > limit) return payloadTooLarge(limit, length);
|
|
64770
|
+
}
|
|
64771
|
+
const peerAddress = resolvePeerAddress(request, ctx);
|
|
64772
|
+
const reader = request.body.getReader();
|
|
64773
|
+
const chunks = [];
|
|
64774
|
+
let total = 0;
|
|
64775
|
+
for (; ; ) {
|
|
64776
|
+
const { done, value } = await reader.read();
|
|
64777
|
+
if (done) break;
|
|
64778
|
+
total += value.byteLength;
|
|
64779
|
+
if (total > limit) {
|
|
64780
|
+
await reader.cancel().catch(() => void 0);
|
|
64781
|
+
return payloadTooLarge(limit, total);
|
|
64782
|
+
}
|
|
64783
|
+
chunks.push(value);
|
|
64784
|
+
}
|
|
64785
|
+
const buffered = new Uint8Array(total);
|
|
64786
|
+
let offset = 0;
|
|
64787
|
+
for (const chunk of chunks) {
|
|
64788
|
+
buffered.set(chunk, offset);
|
|
64789
|
+
offset += chunk.byteLength;
|
|
64790
|
+
}
|
|
64791
|
+
const rebuilt = new Request(request.url, { method: request.method, headers: request.headers, body: buffered });
|
|
64792
|
+
carriedPeerAddresses.set(rebuilt, peerAddress);
|
|
64793
|
+
return rebuilt;
|
|
64075
64794
|
}
|
|
64076
64795
|
async function runHttpRequestFlowWeb(scope, request, opts = {}) {
|
|
64077
64796
|
const url = new URL(request.url);
|
|
64078
64797
|
const serverRequest = await toServerRequest(request, url, opts.ctx, opts.persistent, opts.env);
|
|
64079
64798
|
let output;
|
|
64080
64799
|
try {
|
|
64081
|
-
output = await (0,
|
|
64800
|
+
output = await (0, import_utils111.runRequestExclusive)(
|
|
64082
64801
|
() => scope.runFlow("http:request", {
|
|
64083
64802
|
request: serverRequest,
|
|
64084
64803
|
response: {}
|
|
@@ -64096,7 +64815,7 @@ async function runMatchingHttpFlowWeb(scope, request, opts = {}) {
|
|
|
64096
64815
|
if (!flowName) return void 0;
|
|
64097
64816
|
let output;
|
|
64098
64817
|
try {
|
|
64099
|
-
output = await (0,
|
|
64818
|
+
output = await (0, import_utils111.runRequestExclusive)(
|
|
64100
64819
|
() => scope.runFlow(flowName, {
|
|
64101
64820
|
request: serverRequest,
|
|
64102
64821
|
response: {}
|
|
@@ -64162,22 +64881,7 @@ async function toServerRequest(request, url, ctx, persistent, env) {
|
|
|
64162
64881
|
function misconfigurationResponse(error) {
|
|
64163
64882
|
const misconfiguration = findMisconfiguration(error);
|
|
64164
64883
|
if (!misconfiguration) return void 0;
|
|
64165
|
-
return Response.json(
|
|
64166
|
-
{ error: "server_misconfigured", code: misconfiguration.code, message: misconfiguration.remedy },
|
|
64167
|
-
{ status: 500 }
|
|
64168
|
-
);
|
|
64169
|
-
}
|
|
64170
|
-
function findMisconfiguration(error) {
|
|
64171
|
-
let current = error;
|
|
64172
|
-
for (let depth = 0; current instanceof Error && depth < 5; depth++) {
|
|
64173
|
-
const code = current.code;
|
|
64174
|
-
if (typeof code === "string" && MISCONFIGURATION_REMEDIES[code]) {
|
|
64175
|
-
return { code, remedy: MISCONFIGURATION_REMEDIES[code] };
|
|
64176
|
-
}
|
|
64177
|
-
if (current.name === "ZodError") return { code: "CONFIG_INVALID", remedy: CONFIG_INVALID_REMEDY };
|
|
64178
|
-
current = current.cause;
|
|
64179
|
-
}
|
|
64180
|
-
return void 0;
|
|
64884
|
+
return Response.json(misconfigurationBody(misconfiguration), { status: 500 });
|
|
64181
64885
|
}
|
|
64182
64886
|
function createDeferredServerBuild(build, options = {}) {
|
|
64183
64887
|
const now = options.now ?? Date.now;
|
|
@@ -64232,7 +64936,7 @@ function flowErrorToHttpOutput(error) {
|
|
|
64232
64936
|
kind: "json",
|
|
64233
64937
|
status: 500,
|
|
64234
64938
|
contentType: "application/json; charset=utf-8",
|
|
64235
|
-
body:
|
|
64939
|
+
body: misconfigurationBody(misconfiguration)
|
|
64236
64940
|
};
|
|
64237
64941
|
}
|
|
64238
64942
|
if (error instanceof FlowControl) {
|
|
@@ -64258,27 +64962,24 @@ function flowErrorToHttpOutput(error) {
|
|
|
64258
64962
|
}
|
|
64259
64963
|
return { kind: "text", status: 500, body: "Internal Server Error", contentType: "text/plain; charset=utf-8" };
|
|
64260
64964
|
}
|
|
64261
|
-
var
|
|
64965
|
+
var import_utils111, CLOUDFLARE_WORKERS_USER_AGENT, carriedPeerAddresses, DEFAULT_BODY_LIMIT_BYTES, BYTE_UNITS, STARTUP_RETRY_MIN_MS, STARTUP_RETRY_MAX_MS;
|
|
64262
64966
|
var init_web_fetch_handler = __esm({
|
|
64263
64967
|
"libs/sdk/src/transport/web-fetch-handler.ts"() {
|
|
64264
64968
|
"use strict";
|
|
64265
|
-
|
|
64969
|
+
import_utils111 = require("@frontmcp/utils");
|
|
64266
64970
|
init_common();
|
|
64267
64971
|
init_server_tokens();
|
|
64268
64972
|
init_path_utils();
|
|
64269
64973
|
init_errors();
|
|
64974
|
+
init_misconfiguration();
|
|
64975
|
+
init_health_routes();
|
|
64976
|
+
init_csp_middleware();
|
|
64270
64977
|
init_host_validation();
|
|
64271
64978
|
init_web_response_renderer();
|
|
64272
64979
|
CLOUDFLARE_WORKERS_USER_AGENT = "Cloudflare-Workers";
|
|
64273
|
-
|
|
64274
|
-
|
|
64275
|
-
|
|
64276
|
-
JWT_SECRET_REQUIRED: "Set JWT_SECRET in the deployment environment (e.g. `wrangler secret put JWT_SECRET`). Tokens are signed with it; production refuses the random per-process fallback because tokens would not survive a restart or verify across instances.",
|
|
64277
|
-
// The startup checks: a server whose entries ask for protection nothing gives them does not start.
|
|
64278
|
-
UNENFORCED_METADATA: "An entry declares a field only a plugin enforces (approval, featureFlag, ...) and no installed plugin that enforces it reaches the entry, so the server refuses to start. Install the plugin or remove the field; the server log names the entries.",
|
|
64279
|
-
AUTH_CONFIGURATION_ERROR: "The auth or authorities configuration is invalid (for example, entries declare authorities and the server has no authorities option, or a rule checks nothing), so the server refuses to start. The server log names the entries."
|
|
64280
|
-
};
|
|
64281
|
-
CONFIG_INVALID_REMEDY = "The FrontMCP configuration failed validation, so the server refuses to start. The server log names the invalid fields.";
|
|
64980
|
+
carriedPeerAddresses = /* @__PURE__ */ new WeakMap();
|
|
64981
|
+
DEFAULT_BODY_LIMIT_BYTES = 4 * 1024 * 1024;
|
|
64982
|
+
BYTE_UNITS = { b: 1, kb: 1024, mb: 1024 ** 2, gb: 1024 ** 3 };
|
|
64282
64983
|
STARTUP_RETRY_MIN_MS = 1e3;
|
|
64283
64984
|
STARTUP_RETRY_MAX_MS = 6e4;
|
|
64284
64985
|
}
|
|
@@ -64360,13 +65061,13 @@ function auditSecurityDefaults(config, isProduction7) {
|
|
|
64360
65061
|
message: `Server bound to ${bindAddress}.`
|
|
64361
65062
|
});
|
|
64362
65063
|
}
|
|
64363
|
-
const dnsProtectionEnabled = config.security?.dnsRebindingProtection?.enabled
|
|
65064
|
+
const dnsProtectionEnabled = config.security?.dnsRebindingProtection?.enabled !== false;
|
|
64364
65065
|
if (!dnsProtectionEnabled) {
|
|
64365
65066
|
findings.push({
|
|
64366
65067
|
level: "warn",
|
|
64367
65068
|
code: "DNS_REBINDING_UNPROTECTED",
|
|
64368
|
-
message: "DNS rebinding protection is disabled.",
|
|
64369
|
-
recommendation: "
|
|
65069
|
+
message: "DNS rebinding protection is disabled (security.dnsRebindingProtection.enabled is false).",
|
|
65070
|
+
recommendation: "Remove `enabled: false`, or set security.dnsRebindingProtection.allowedHosts, to prevent DNS rebinding attacks."
|
|
64370
65071
|
});
|
|
64371
65072
|
} else {
|
|
64372
65073
|
findings.push({
|
|
@@ -64546,15 +65247,16 @@ var init_base_host_adapter = __esm({
|
|
|
64546
65247
|
});
|
|
64547
65248
|
|
|
64548
65249
|
// libs/sdk/src/server/adapters/express.host.adapter.ts
|
|
64549
|
-
var http, import_cors, import_express,
|
|
65250
|
+
var http, import_cors, import_express, import_utils112, DEFAULT_EXPRESS_BODY_LIMIT, ExpressHostAdapter;
|
|
64550
65251
|
var init_express_host_adapter = __esm({
|
|
64551
65252
|
"libs/sdk/src/server/adapters/express.host.adapter.ts"() {
|
|
64552
65253
|
"use strict";
|
|
64553
65254
|
http = __toESM(require("node:http"));
|
|
64554
65255
|
import_cors = __toESM(require("cors"));
|
|
64555
65256
|
import_express = __toESM(require("express"));
|
|
64556
|
-
|
|
65257
|
+
import_utils112 = require("@frontmcp/utils");
|
|
64557
65258
|
init_mcp_error();
|
|
65259
|
+
init_csp_middleware();
|
|
64558
65260
|
init_host_validation_middleware();
|
|
64559
65261
|
init_resolve_allowed_hosts();
|
|
64560
65262
|
init_base_host_adapter();
|
|
@@ -64569,6 +65271,12 @@ var init_express_host_adapter = __esm({
|
|
|
64569
65271
|
deriveHostValidation;
|
|
64570
65272
|
constructor(options) {
|
|
64571
65273
|
super();
|
|
65274
|
+
this.app.disable("x-powered-by");
|
|
65275
|
+
const securityHeaders = resolveSecurityHeaders(options?.securityHeaders);
|
|
65276
|
+
this.app.use((_req, res, next) => {
|
|
65277
|
+
for (const [name44, value] of Object.entries(securityHeaders)) res.setHeader(name44, value);
|
|
65278
|
+
next();
|
|
65279
|
+
});
|
|
64572
65280
|
const corsOptions = options?.cors;
|
|
64573
65281
|
const corsEnabled = corsOptions?.origin !== void 0 && corsOptions.origin !== false;
|
|
64574
65282
|
if (corsEnabled) {
|
|
@@ -64736,8 +65444,8 @@ var init_express_host_adapter = __esm({
|
|
|
64736
65444
|
}
|
|
64737
65445
|
async cleanupStaleSocket(socketPath) {
|
|
64738
65446
|
try {
|
|
64739
|
-
if (await (0,
|
|
64740
|
-
await (0,
|
|
65447
|
+
if (await (0, import_utils112.fileExists)(socketPath)) {
|
|
65448
|
+
await (0, import_utils112.unlink)(socketPath);
|
|
64741
65449
|
}
|
|
64742
65450
|
} catch {
|
|
64743
65451
|
}
|
|
@@ -64760,11 +65468,11 @@ var init_node_express_host = __esm({
|
|
|
64760
65468
|
});
|
|
64761
65469
|
|
|
64762
65470
|
// libs/sdk/src/server/server.instance.ts
|
|
64763
|
-
var
|
|
65471
|
+
var import_utils113, DEFAULT_CORS, FrontMcpServerInstance;
|
|
64764
65472
|
var init_server_instance = __esm({
|
|
64765
65473
|
"libs/sdk/src/server/server.instance.ts"() {
|
|
64766
65474
|
"use strict";
|
|
64767
|
-
|
|
65475
|
+
import_utils113 = require("@frontmcp/utils");
|
|
64768
65476
|
init_common();
|
|
64769
65477
|
init_health2();
|
|
64770
65478
|
init_metrics2();
|
|
@@ -64796,6 +65504,7 @@ var init_server_instance = __esm({
|
|
|
64796
65504
|
this.host = new ExpressHostAdapter2({
|
|
64797
65505
|
...corsConfig ? { cors: corsConfig } : {},
|
|
64798
65506
|
...this.config.security ? { security: this.config.security } : {},
|
|
65507
|
+
...this.config.securityHeaders ? { securityHeaders: this.config.securityHeaders } : {},
|
|
64799
65508
|
...this.config.bodyLimit !== void 0 ? { bodyLimit: this.config.bodyLimit } : {},
|
|
64800
65509
|
...this.config.urlencodedLimit !== void 0 ? { urlencodedLimit: this.config.urlencodedLimit } : {}
|
|
64801
65510
|
});
|
|
@@ -64866,7 +65575,7 @@ var init_server_instance = __esm({
|
|
|
64866
65575
|
}
|
|
64867
65576
|
async start() {
|
|
64868
65577
|
this.prepare();
|
|
64869
|
-
const deploymentMode = (0,
|
|
65578
|
+
const deploymentMode = (0, import_utils113.getRuntimeContext)().deployment;
|
|
64870
65579
|
const bindAddress = resolveBindAddress(this.config.security, deploymentMode);
|
|
64871
65580
|
const isProduction7 = process.env["NODE_ENV"] === "production";
|
|
64872
65581
|
const findings = auditSecurityDefaults(
|
|
@@ -65104,11 +65813,11 @@ function resolveConfigInput(optionsOrClass) {
|
|
|
65104
65813
|
}
|
|
65105
65814
|
return optionsOrClass;
|
|
65106
65815
|
}
|
|
65107
|
-
var
|
|
65816
|
+
var import_utils114, stdioServing, FrontMcpInstance;
|
|
65108
65817
|
var init_front_mcp = __esm({
|
|
65109
65818
|
"libs/sdk/src/front-mcp/front-mcp.ts"() {
|
|
65110
65819
|
"use strict";
|
|
65111
|
-
|
|
65820
|
+
import_utils114 = require("@frontmcp/utils");
|
|
65112
65821
|
init_common();
|
|
65113
65822
|
init_direct();
|
|
65114
65823
|
init_errors();
|
|
@@ -65120,6 +65829,7 @@ var init_front_mcp = __esm({
|
|
|
65120
65829
|
init_scope_registry();
|
|
65121
65830
|
init_skill_instructions_helper();
|
|
65122
65831
|
init_task();
|
|
65832
|
+
init_ipc_server_transport();
|
|
65123
65833
|
init_web_fetch_handler();
|
|
65124
65834
|
init_front_mcp_providers();
|
|
65125
65835
|
init_static_startup_check();
|
|
@@ -65239,7 +65949,13 @@ var init_front_mcp = __esm({
|
|
|
65239
65949
|
}
|
|
65240
65950
|
}
|
|
65241
65951
|
static async bootstrap(options) {
|
|
65242
|
-
const
|
|
65952
|
+
const sqlitePath = process.env["FRONTMCP_SQLITE_PATH"];
|
|
65953
|
+
const parsedConfig = frontMcpMetadataSchema.parse(
|
|
65954
|
+
sqlitePath ? {
|
|
65955
|
+
...options,
|
|
65956
|
+
sqlite: { ...options.sqlite ?? {}, path: sqlitePath }
|
|
65957
|
+
} : options
|
|
65958
|
+
);
|
|
65243
65959
|
const daemonSocket = process.env["FRONTMCP_DAEMON_SOCKET"];
|
|
65244
65960
|
if (daemonSocket) {
|
|
65245
65961
|
await _FrontMcpInstance2.runUnixSocket({ ...parsedConfig, socketPath: daemonSocket });
|
|
@@ -65312,7 +66028,7 @@ var init_front_mcp = __esm({
|
|
|
65312
66028
|
frontMcp.log?.info("FrontMCP fetch handler created (web-standard transport)");
|
|
65313
66029
|
return createWebFetchHandler(scope);
|
|
65314
66030
|
};
|
|
65315
|
-
if (!(0,
|
|
66031
|
+
if (!(0, import_utils114.isEdgeRuntime)()) return build(false);
|
|
65316
66032
|
assertStaticStartupConfig(options);
|
|
65317
66033
|
const server = createDeferredServerBuild(() => build(true));
|
|
65318
66034
|
return async (request, ctx, env) => {
|
|
@@ -65457,8 +66173,8 @@ var init_front_mcp = __esm({
|
|
|
65457
66173
|
frontMcp.log?.info(`MCP server listening on unix://${socketPath}`);
|
|
65458
66174
|
const cleanup = async () => {
|
|
65459
66175
|
try {
|
|
65460
|
-
if (await (0,
|
|
65461
|
-
await (0,
|
|
66176
|
+
if (await (0, import_utils114.fileExists)(socketPath)) {
|
|
66177
|
+
await (0, import_utils114.unlink)(socketPath);
|
|
65462
66178
|
}
|
|
65463
66179
|
} catch {
|
|
65464
66180
|
}
|
|
@@ -65602,7 +66318,7 @@ var init_front_mcp = __esm({
|
|
|
65602
66318
|
};
|
|
65603
66319
|
const mcpServer = new McpServer2(scope.metadata.info, serverOptions);
|
|
65604
66320
|
startupCleanups.push(() => mcpServer.close());
|
|
65605
|
-
const sessionId = `stdio:${(0,
|
|
66321
|
+
const sessionId = `stdio:${(0, import_utils114.randomUUID)()}`;
|
|
65606
66322
|
const handlers = createMcpHandlers2({ scope, serverOptions, composeInstructions });
|
|
65607
66323
|
for (const handler of handlers) {
|
|
65608
66324
|
const originalHandler = handler.handler;
|
|
@@ -65616,7 +66332,7 @@ var init_front_mcp = __esm({
|
|
|
65616
66332
|
};
|
|
65617
66333
|
mcpServer.setRequestHandler(handler.requestSchema, wrappedHandler);
|
|
65618
66334
|
}
|
|
65619
|
-
const transport = new StdioServerTransport();
|
|
66335
|
+
const transport = isIpcStdioRequested() ? new IpcServerTransport() : new StdioServerTransport();
|
|
65620
66336
|
startupCleanups.push(() => transport.close());
|
|
65621
66337
|
await mcpServer.connect(transport);
|
|
65622
66338
|
startupSucceeded = true;
|
|
@@ -65922,9 +66638,9 @@ function FrontMcp(providedMetadata) {
|
|
|
65922
66638
|
Reflect.defineMetadata(FrontMcpTokens[property] ?? property, metadata[property], target);
|
|
65923
66639
|
}
|
|
65924
66640
|
Reflect.defineMetadata(FRONTMCP_CONFIG_METADATA_KEY, metadata, target);
|
|
65925
|
-
const isServerless2 = (0,
|
|
65926
|
-
const isSchemaExtract = (0,
|
|
65927
|
-
const isStdio = (0,
|
|
66641
|
+
const isServerless2 = (0, import_utils115.getEnvFlag)("FRONTMCP_SERVERLESS");
|
|
66642
|
+
const isSchemaExtract = (0, import_utils115.getEnvFlag)("FRONTMCP_SCHEMA_EXTRACT");
|
|
66643
|
+
const isStdio = (0, import_utils115.getEnvFlag)("FRONTMCP_STDIO");
|
|
65928
66644
|
const taskWorkerId = typeof process !== "undefined" ? process.env?.["FRONTMCP_RUN_TASK_ID"] : void 0;
|
|
65929
66645
|
if (taskWorkerId && metadata.serve !== false) {
|
|
65930
66646
|
void (async () => {
|
|
@@ -65950,7 +66666,7 @@ function FrontMcp(providedMetadata) {
|
|
|
65950
66666
|
} else if (isServerless2) {
|
|
65951
66667
|
const ServerlessInstance = getFrontMcpInstance();
|
|
65952
66668
|
const { setServerlessHandler: setServerlessHandler2, setServerlessHandlerPromise: setServerlessHandlerPromise2, setServerlessHandlerError: setServerlessHandlerError2 } = getServerlessHandlerFns();
|
|
65953
|
-
const isWorker = (0,
|
|
66669
|
+
const isWorker = (0, import_utils115.getEnvFlag)("FRONTMCP_WORKER");
|
|
65954
66670
|
const handlerPromise = isWorker ? ServerlessInstance.createFetchHandler(metadata) : ServerlessInstance.createHandler(metadata);
|
|
65955
66671
|
setServerlessHandlerPromise2(handlerPromise);
|
|
65956
66672
|
handlerPromise.then(setServerlessHandler2).catch((err) => {
|
|
@@ -65963,12 +66679,12 @@ function FrontMcp(providedMetadata) {
|
|
|
65963
66679
|
}
|
|
65964
66680
|
};
|
|
65965
66681
|
}
|
|
65966
|
-
var import_reflect_metadata28,
|
|
66682
|
+
var import_reflect_metadata28, import_utils115, FRONT_MCP_DECORATOR_BASENAMES, FRONTMCP_CONFIG_METADATA_KEY, _FrontMcpInstance, _serverlessHandlerFns;
|
|
65967
66683
|
var init_front_mcp_decorator = __esm({
|
|
65968
66684
|
"libs/sdk/src/common/decorators/front-mcp.decorator.ts"() {
|
|
65969
66685
|
"use strict";
|
|
65970
66686
|
import_reflect_metadata28 = require("reflect-metadata");
|
|
65971
|
-
|
|
66687
|
+
import_utils115 = require("@frontmcp/utils");
|
|
65972
66688
|
init_decorator_errors();
|
|
65973
66689
|
init_mcp_error();
|
|
65974
66690
|
init_metadata();
|
|
@@ -66682,7 +67398,7 @@ var init_dynamic_adapter = __esm({
|
|
|
66682
67398
|
* @param options - Adapter options including required `name` field
|
|
66683
67399
|
* @throws Error if `name` is missing/empty or if a duplicate name is detected
|
|
66684
67400
|
*/
|
|
66685
|
-
static init(options) {
|
|
67401
|
+
static init(options = {}) {
|
|
66686
67402
|
const typedOptions = options;
|
|
66687
67403
|
const adapterName = typedOptions.name;
|
|
66688
67404
|
if (!adapterName || typeof adapterName !== "string" || adapterName.trim() === "") {
|
|
@@ -66884,12 +67600,12 @@ var init_adapter_entry = __esm({
|
|
|
66884
67600
|
});
|
|
66885
67601
|
|
|
66886
67602
|
// libs/sdk/src/common/entries/tool.entry.ts
|
|
66887
|
-
var import_lazy_zod109,
|
|
67603
|
+
var import_lazy_zod109, import_utils117, ToolEntry;
|
|
66888
67604
|
var init_tool_entry = __esm({
|
|
66889
67605
|
"libs/sdk/src/common/entries/tool.entry.ts"() {
|
|
66890
67606
|
"use strict";
|
|
66891
67607
|
import_lazy_zod109 = require("@frontmcp/lazy-zod");
|
|
66892
|
-
|
|
67608
|
+
import_utils117 = require("@frontmcp/utils");
|
|
66893
67609
|
init_base_entry();
|
|
66894
67610
|
ToolEntry = class extends BaseEntry {
|
|
66895
67611
|
owner;
|
|
@@ -66956,7 +67672,7 @@ var init_tool_entry = __esm({
|
|
|
66956
67672
|
try {
|
|
66957
67673
|
return (0, import_lazy_zod109.toJSONSchema)(import_lazy_zod109.z.object(this.inputSchema), { io: "input", unrepresentable: "any" });
|
|
66958
67674
|
} catch (error) {
|
|
66959
|
-
if ((0,
|
|
67675
|
+
if ((0, import_utils117.isDebug)() || (0, import_utils117.isDevelopment)()) {
|
|
66960
67676
|
console.warn("[ToolEntry] Failed to convert Zod schema to JSON Schema:", error);
|
|
66961
67677
|
}
|
|
66962
67678
|
return { type: "object", properties: {} };
|
|
@@ -67002,7 +67718,7 @@ var init_tool_entry = __esm({
|
|
|
67002
67718
|
const json = (0, import_lazy_zod109.toJSONSchema)(zodSchema);
|
|
67003
67719
|
return json && json["type"] === "object" ? json : null;
|
|
67004
67720
|
} catch (error) {
|
|
67005
|
-
if ((0,
|
|
67721
|
+
if ((0, import_utils117.isDebug)() || (0, import_utils117.isDevelopment)()) {
|
|
67006
67722
|
console.warn("[ToolEntry] Failed to convert output Zod schema to JSON Schema:", error);
|
|
67007
67723
|
}
|
|
67008
67724
|
return null;
|
|
@@ -68443,6 +69159,7 @@ __export(index_exports, {
|
|
|
68443
69159
|
resolveSchemaDescriptionFormat: () => resolveSchemaDescriptionFormat,
|
|
68444
69160
|
resolveToolVisibility: () => resolveToolVisibility,
|
|
68445
69161
|
resolveWithFallbacks: () => resolveWithFallbacks,
|
|
69162
|
+
resolvesToVercelKvTaskBackend: () => resolvesToVercelKvTaskBackend,
|
|
68446
69163
|
resource: () => frontMcpResource,
|
|
68447
69164
|
resourceTemplate: () => frontMcpResourceTemplate,
|
|
68448
69165
|
resourceUriMatches: () => resourceUriMatches,
|
|
@@ -68667,22 +69384,22 @@ function createMemorySkillProvider(options = {}) {
|
|
|
68667
69384
|
init_skill_utils();
|
|
68668
69385
|
|
|
68669
69386
|
// libs/sdk/src/skill/skill-directory-loader.ts
|
|
68670
|
-
var
|
|
69387
|
+
var import_utils118 = require("@frontmcp/utils");
|
|
68671
69388
|
init_skill_metadata();
|
|
68672
69389
|
init_sdk_errors();
|
|
68673
69390
|
init_skill_md_parser();
|
|
68674
69391
|
async function scanSkillResources(dirPath) {
|
|
68675
69392
|
const resources = {};
|
|
68676
|
-
const scriptsPath = (0,
|
|
68677
|
-
const referencesPath = (0,
|
|
68678
|
-
const assetsPath = (0,
|
|
68679
|
-
const examplesPath = (0,
|
|
69393
|
+
const scriptsPath = (0, import_utils118.joinPath)(dirPath, "scripts");
|
|
69394
|
+
const referencesPath = (0, import_utils118.joinPath)(dirPath, "references");
|
|
69395
|
+
const assetsPath = (0, import_utils118.joinPath)(dirPath, "assets");
|
|
69396
|
+
const examplesPath = (0, import_utils118.joinPath)(dirPath, "examples");
|
|
68680
69397
|
const checks = await Promise.all([
|
|
68681
69398
|
checkDirectory(scriptsPath),
|
|
68682
69399
|
checkDirectory(referencesPath),
|
|
68683
69400
|
checkDirectory(assetsPath),
|
|
68684
69401
|
checkDirectory(examplesPath),
|
|
68685
|
-
(0,
|
|
69402
|
+
(0, import_utils118.fileExists)((0, import_utils118.joinPath)(dirPath, "SKILL.md"))
|
|
68686
69403
|
]);
|
|
68687
69404
|
if (checks[0]) resources.scripts = scriptsPath;
|
|
68688
69405
|
if (checks[1]) resources.references = referencesPath;
|
|
@@ -68695,7 +69412,7 @@ async function scanSkillResources(dirPath) {
|
|
|
68695
69412
|
}
|
|
68696
69413
|
async function checkDirectory(path3) {
|
|
68697
69414
|
try {
|
|
68698
|
-
const s = await (0,
|
|
69415
|
+
const s = await (0, import_utils118.stat)(path3);
|
|
68699
69416
|
return s.isDirectory();
|
|
68700
69417
|
} catch (err) {
|
|
68701
69418
|
if (err.code === "ENOENT") return false;
|
|
@@ -68708,16 +69425,16 @@ async function findNestedSkillMd(dirPath, maxDepth = Number.POSITIVE_INFINITY) {
|
|
|
68708
69425
|
if (depth > maxDepth) return;
|
|
68709
69426
|
let entries;
|
|
68710
69427
|
try {
|
|
68711
|
-
entries = await (0,
|
|
69428
|
+
entries = await (0, import_utils118.readdir)(current);
|
|
68712
69429
|
} catch (err) {
|
|
68713
69430
|
if (err.code === "ENOENT") return;
|
|
68714
69431
|
throw err;
|
|
68715
69432
|
}
|
|
68716
69433
|
for (const entry of entries) {
|
|
68717
|
-
const full = (0,
|
|
69434
|
+
const full = (0, import_utils118.joinPath)(current, entry);
|
|
68718
69435
|
let entryStat;
|
|
68719
69436
|
try {
|
|
68720
|
-
entryStat = await (0,
|
|
69437
|
+
entryStat = await (0, import_utils118.stat)(full);
|
|
68721
69438
|
} catch (err) {
|
|
68722
69439
|
if (err.code === "ENOENT") continue;
|
|
68723
69440
|
throw err;
|
|
@@ -68733,19 +69450,19 @@ async function findNestedSkillMd(dirPath, maxDepth = Number.POSITIVE_INFINITY) {
|
|
|
68733
69450
|
return found.sort();
|
|
68734
69451
|
}
|
|
68735
69452
|
async function loadSkillDirectory(dirPath, logger) {
|
|
68736
|
-
const skillMdPath = (0,
|
|
68737
|
-
const exists = await (0,
|
|
69453
|
+
const skillMdPath = (0, import_utils118.joinPath)(dirPath, "SKILL.md");
|
|
69454
|
+
const exists = await (0, import_utils118.fileExists)(skillMdPath);
|
|
68738
69455
|
if (!exists) {
|
|
68739
|
-
throw new InvalidSkillError((0,
|
|
69456
|
+
throw new InvalidSkillError((0, import_utils118.basename)(dirPath), "SKILL.md not found in directory");
|
|
68740
69457
|
}
|
|
68741
69458
|
let content;
|
|
68742
69459
|
let partialMetadata;
|
|
68743
69460
|
try {
|
|
68744
|
-
content = await (0,
|
|
69461
|
+
content = await (0, import_utils118.readFile)(skillMdPath, "utf-8");
|
|
68745
69462
|
const { frontmatter, body } = parseSkillMdFrontmatter(content);
|
|
68746
69463
|
partialMetadata = skillMdFrontmatterToMetadata(frontmatter, body);
|
|
68747
69464
|
} catch (err) {
|
|
68748
|
-
const name44 = (0,
|
|
69465
|
+
const name44 = (0, import_utils118.basename)(dirPath);
|
|
68749
69466
|
const message = err instanceof Error ? err.message : String(err);
|
|
68750
69467
|
throw new InvalidSkillError(name44, `Failed to read or parse SKILL.md: ${message}`);
|
|
68751
69468
|
}
|
|
@@ -68756,13 +69473,13 @@ async function loadSkillDirectory(dirPath, logger) {
|
|
|
68756
69473
|
}
|
|
68757
69474
|
const nested = await findNestedSkillMd(dirPath);
|
|
68758
69475
|
if (nested.length > 0) {
|
|
68759
|
-
const skillName2 = partialMetadata.name ?? (0,
|
|
69476
|
+
const skillName2 = partialMetadata.name ?? (0, import_utils118.basename)(dirPath);
|
|
68760
69477
|
throw new InvalidSkillError(
|
|
68761
69478
|
skillName2,
|
|
68762
69479
|
`Skill directory contains nested SKILL.md files (forbidden by SEP-2640 \xA7Resource Mapping): ${nested.join(", ")}`
|
|
68763
69480
|
);
|
|
68764
69481
|
}
|
|
68765
|
-
const dirName = (0,
|
|
69482
|
+
const dirName = (0, import_utils118.basename)(dirPath);
|
|
68766
69483
|
if (partialMetadata.name && dirName && partialMetadata.name !== dirName) {
|
|
68767
69484
|
const msg = `Skill name "${partialMetadata.name}" does not match directory name "${dirName}"`;
|
|
68768
69485
|
if (logger) {
|
|
@@ -69686,6 +70403,7 @@ var ChannelListHook = FlowHooksOf("channels:list");
|
|
|
69686
70403
|
resolveSchemaDescriptionFormat,
|
|
69687
70404
|
resolveToolVisibility,
|
|
69688
70405
|
resolveWithFallbacks,
|
|
70406
|
+
resolvesToVercelKvTaskBackend,
|
|
69689
70407
|
resource,
|
|
69690
70408
|
resourceTemplate,
|
|
69691
70409
|
resourceUriMatches,
|