@frontmcp/sdk 1.8.5 → 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/flows/session.verify.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 +18 -3
- 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/common/utils/list-all-pages.utils.d.ts +17 -0
- package/common/utils/list-all-pages.utils.d.ts.map +1 -0
- 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 +1181 -325
- 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 +1259 -407
- 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/plugin/plugin.registry.d.ts.map +1 -1
- package/remote-mcp/mcp-client.service.d.ts.map +1 -1
- package/remote-mcp/mcp-stateless-client.adapter.d.ts +25 -6
- 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 +1 -0
- 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 +25 -0
- 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 +12 -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 +9 -2
- package/transport/mcp-20260728/client/mcp-stateless.client.d.ts.map +1 -1
- package/transport/mcp-20260728/dispatcher.d.ts.map +1 -1
- package/transport/mcp-20260728/request-state.d.ts +28 -0
- package/transport/mcp-20260728/request-state.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/esm/index.mjs
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
const require =
|
|
1
|
+
const __frontmcpModule = typeof process !== "undefined" && typeof process.getBuiltinModule === "function" ? process.getBuiltinModule("module") : undefined;
|
|
2
|
+
const require = __frontmcpModule ? __frontmcpModule.createRequire(import.meta.url || 'file:///') : undefined;
|
|
3
|
+
// __frontmcpCreateRequire
|
|
3
4
|
var __defProp = Object.defineProperty;
|
|
4
5
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
6
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
@@ -1861,7 +1862,7 @@ var init_session = __esm({
|
|
|
1861
1862
|
|
|
1862
1863
|
// libs/sdk/src/common/types/options/http/schema.ts
|
|
1863
1864
|
import { z as z8 } from "@frontmcp/lazy-zod";
|
|
1864
|
-
var HTTP_METHODS, httpRouteSchema, bodyParserLimitSchema, corsOptionsSchema, httpOptionsSchema;
|
|
1865
|
+
var HTTP_METHODS, httpRouteSchema, bodyParserLimitSchema, corsOptionsSchema, securityHeaderValueSchema, securityHeadersSchema, httpOptionsSchema;
|
|
1865
1866
|
var init_schema3 = __esm({
|
|
1866
1867
|
"libs/sdk/src/common/types/options/http/schema.ts"() {
|
|
1867
1868
|
"use strict";
|
|
@@ -1898,6 +1899,19 @@ var init_schema3 = __esm({
|
|
|
1898
1899
|
credentials: z8.boolean().optional(),
|
|
1899
1900
|
maxAge: z8.number().optional()
|
|
1900
1901
|
});
|
|
1902
|
+
securityHeaderValueSchema = z8.union([z8.string(), z8.literal(false)]);
|
|
1903
|
+
securityHeadersSchema = z8.object({
|
|
1904
|
+
hsts: securityHeaderValueSchema.optional(),
|
|
1905
|
+
contentTypeOptions: securityHeaderValueSchema.optional(),
|
|
1906
|
+
frameOptions: securityHeaderValueSchema.optional(),
|
|
1907
|
+
custom: z8.record(z8.string(), z8.string()).optional(),
|
|
1908
|
+
csp: z8.object({
|
|
1909
|
+
enabled: z8.boolean().optional(),
|
|
1910
|
+
directives: z8.record(z8.string(), z8.union([z8.string(), z8.array(z8.string())])).optional(),
|
|
1911
|
+
reportUri: z8.string().optional(),
|
|
1912
|
+
reportOnly: z8.boolean().optional()
|
|
1913
|
+
}).optional()
|
|
1914
|
+
});
|
|
1901
1915
|
httpOptionsSchema = z8.object({
|
|
1902
1916
|
port: z8.number().optional().default(Number(process.env["PORT"]) || 3e3),
|
|
1903
1917
|
// `FRONTMCP_HTTP_ENTRY_PATH` lets `frontmcp dev` propagate the configured
|
|
@@ -1956,7 +1970,9 @@ var init_schema3 = __esm({
|
|
|
1956
1970
|
* app as the MCP endpoint; public by default, `auth: true` opts into the MCP
|
|
1957
1971
|
* `session:verify` flow. Reserved-path collisions are rejected at startup.
|
|
1958
1972
|
*/
|
|
1959
|
-
routes: z8.array(httpRouteSchema).optional()
|
|
1973
|
+
routes: z8.array(httpRouteSchema).optional(),
|
|
1974
|
+
/** Response security headers; see `SecurityHeadersOptions`. */
|
|
1975
|
+
securityHeaders: securityHeadersSchema.optional()
|
|
1960
1976
|
});
|
|
1961
1977
|
}
|
|
1962
1978
|
});
|
|
@@ -2394,7 +2410,7 @@ var init_schema7 = __esm({
|
|
|
2394
2410
|
persistenceConfigSchema = z12.object({
|
|
2395
2411
|
redis: redisOptionsSchema.optional(),
|
|
2396
2412
|
sqlite: sqliteOptionsSchema.optional(),
|
|
2397
|
-
defaultTtlMs: z12.number().int().positive().
|
|
2413
|
+
defaultTtlMs: z12.number().int().positive().optional()
|
|
2398
2414
|
}).refine((cfg) => !(cfg.redis && cfg.sqlite), {
|
|
2399
2415
|
message: "Invalid persistence config: choose either redis or sqlite, not both",
|
|
2400
2416
|
path: ["sqlite"]
|
|
@@ -6035,6 +6051,33 @@ var init_errors = __esm({
|
|
|
6035
6051
|
}
|
|
6036
6052
|
});
|
|
6037
6053
|
|
|
6054
|
+
// libs/sdk/src/common/utils/list-all-pages.utils.ts
|
|
6055
|
+
async function listAllPages(method, fetchPage, fail = (message) => new InternalMcpError(message, "LIST_PAGINATION_STALLED")) {
|
|
6056
|
+
const items = [];
|
|
6057
|
+
const seenCursors = /* @__PURE__ */ new Set();
|
|
6058
|
+
let cursor;
|
|
6059
|
+
for (let page = 0; page < MAX_LIST_PAGES; page++) {
|
|
6060
|
+
const result = await fetchPage(cursor);
|
|
6061
|
+
items.push(...result.items ?? []);
|
|
6062
|
+
const next = result.nextCursor;
|
|
6063
|
+
if (!next) return items;
|
|
6064
|
+
if (seenCursors.has(next)) {
|
|
6065
|
+
throw fail(`${method} returned the cursor "${next}" twice`);
|
|
6066
|
+
}
|
|
6067
|
+
seenCursors.add(next);
|
|
6068
|
+
cursor = next;
|
|
6069
|
+
}
|
|
6070
|
+
throw fail(`${method} did not finish within ${MAX_LIST_PAGES} pages`);
|
|
6071
|
+
}
|
|
6072
|
+
var MAX_LIST_PAGES;
|
|
6073
|
+
var init_list_all_pages_utils = __esm({
|
|
6074
|
+
"libs/sdk/src/common/utils/list-all-pages.utils.ts"() {
|
|
6075
|
+
"use strict";
|
|
6076
|
+
init_mcp_error();
|
|
6077
|
+
MAX_LIST_PAGES = 1e3;
|
|
6078
|
+
}
|
|
6079
|
+
});
|
|
6080
|
+
|
|
6038
6081
|
// libs/sdk/src/transport/mcp-handlers/skills-mcp.types.ts
|
|
6039
6082
|
import { z as z40 } from "@frontmcp/lazy-zod";
|
|
6040
6083
|
var SkillsSearchRequestSchema, SkillsSearchResultSchema, SkillsLoadRequestSchema, SkillsLoadResultSchema, SkillsListRequestSchema, SkillsListResultSchema;
|
|
@@ -6483,6 +6526,7 @@ var require_dist = __commonJS({
|
|
|
6483
6526
|
var __toCommonJS2 = (mod) => __copyProps2(__defProp2({}, "__esModule", { value: true }), mod);
|
|
6484
6527
|
var index_exports = {};
|
|
6485
6528
|
__export2(index_exports, {
|
|
6529
|
+
SqliteDecryptionError: () => SqliteDecryptionError,
|
|
6486
6530
|
SqliteElicitationStore: () => SqliteElicitationStore,
|
|
6487
6531
|
SqliteEventStore: () => SqliteEventStore,
|
|
6488
6532
|
SqliteKvStore: () => SqliteKvStore,
|
|
@@ -6494,34 +6538,47 @@ var require_dist = __commonJS({
|
|
|
6494
6538
|
encryptValue: () => encryptValue,
|
|
6495
6539
|
loadBetterSqlite3: () => loadBetterSqlite3,
|
|
6496
6540
|
openDatabase: () => openDatabase,
|
|
6497
|
-
sqliteStorageOptionsSchema: () => sqliteStorageOptionsSchema
|
|
6541
|
+
sqliteStorageOptionsSchema: () => sqliteStorageOptionsSchema,
|
|
6542
|
+
withBusyRetry: () => withBusyRetry
|
|
6498
6543
|
});
|
|
6499
6544
|
module.exports = __toCommonJS2(index_exports);
|
|
6500
|
-
var
|
|
6545
|
+
var import_utils119 = __require("@frontmcp/utils");
|
|
6546
|
+
var SqliteDecryptionError = class extends Error {
|
|
6547
|
+
code = "SQLITE_DECRYPTION_FAILED";
|
|
6548
|
+
constructor() {
|
|
6549
|
+
super(
|
|
6550
|
+
"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."
|
|
6551
|
+
);
|
|
6552
|
+
this.name = "SqliteDecryptionError";
|
|
6553
|
+
}
|
|
6554
|
+
};
|
|
6501
6555
|
var HKDF_SALT = new TextEncoder().encode("frontmcp-sqlite-storage-v1");
|
|
6502
6556
|
var HKDF_INFO = new TextEncoder().encode("aes-256-gcm-value-encryption");
|
|
6503
6557
|
var KEY_LENGTH = 32;
|
|
6504
6558
|
function deriveEncryptionKey(secret) {
|
|
6505
6559
|
const ikm = new TextEncoder().encode(secret);
|
|
6506
|
-
return (0,
|
|
6560
|
+
return (0, import_utils119.hkdfSha256)(ikm, HKDF_SALT, HKDF_INFO, KEY_LENGTH);
|
|
6507
6561
|
}
|
|
6508
6562
|
var SEPARATOR = ":";
|
|
6509
6563
|
function encryptValue(key, plaintext) {
|
|
6510
|
-
const iv = (0,
|
|
6564
|
+
const iv = (0, import_utils119.randomBytes)(12);
|
|
6511
6565
|
const plaintextBytes = new TextEncoder().encode(plaintext);
|
|
6512
|
-
const { ciphertext, tag } = (0,
|
|
6513
|
-
return [(0,
|
|
6566
|
+
const { ciphertext, tag } = (0, import_utils119.encryptAesGcm)(key, plaintextBytes, iv);
|
|
6567
|
+
return [(0, import_utils119.base64urlEncode)(iv), (0, import_utils119.base64urlEncode)(tag), (0, import_utils119.base64urlEncode)(ciphertext)].join(SEPARATOR);
|
|
6514
6568
|
}
|
|
6515
6569
|
function decryptValue(key, encrypted) {
|
|
6516
6570
|
const parts = encrypted.split(SEPARATOR);
|
|
6517
6571
|
if (parts.length !== 3) {
|
|
6518
6572
|
throw new Error("Invalid encrypted value format");
|
|
6519
6573
|
}
|
|
6520
|
-
const iv = (0,
|
|
6521
|
-
const tag = (0,
|
|
6522
|
-
const ciphertext = (0,
|
|
6523
|
-
|
|
6524
|
-
|
|
6574
|
+
const iv = (0, import_utils119.base64urlDecode)(parts[0]);
|
|
6575
|
+
const tag = (0, import_utils119.base64urlDecode)(parts[1]);
|
|
6576
|
+
const ciphertext = (0, import_utils119.base64urlDecode)(parts[2]);
|
|
6577
|
+
try {
|
|
6578
|
+
return new TextDecoder().decode((0, import_utils119.decryptAesGcm)(key, ciphertext, iv, tag));
|
|
6579
|
+
} catch {
|
|
6580
|
+
throw new SqliteDecryptionError();
|
|
6581
|
+
}
|
|
6525
6582
|
}
|
|
6526
6583
|
var import_utils210 = __require("@frontmcp/utils");
|
|
6527
6584
|
var cached = null;
|
|
@@ -6534,7 +6591,27 @@ var require_dist = __commonJS({
|
|
|
6534
6591
|
return mod;
|
|
6535
6592
|
}
|
|
6536
6593
|
var MEMORY_PATH = ":memory:";
|
|
6537
|
-
|
|
6594
|
+
var DEFAULT_BUSY_TIMEOUT_MS = 5e3;
|
|
6595
|
+
function isBusyError(err) {
|
|
6596
|
+
const code = err?.code;
|
|
6597
|
+
return code === "SQLITE_BUSY" || code === "SQLITE_BUSY_RECOVERY" || code === "SQLITE_BUSY_SNAPSHOT";
|
|
6598
|
+
}
|
|
6599
|
+
function sleepSync(ms) {
|
|
6600
|
+
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
|
|
6601
|
+
}
|
|
6602
|
+
function withBusyRetry(operation, options = {}) {
|
|
6603
|
+
const attempts = options.attempts ?? 5;
|
|
6604
|
+
const delayMs = options.delayMs ?? 50;
|
|
6605
|
+
for (let attempt = 1; ; attempt++) {
|
|
6606
|
+
try {
|
|
6607
|
+
return operation();
|
|
6608
|
+
} catch (err) {
|
|
6609
|
+
if (!isBusyError(err) || attempt >= attempts) throw err;
|
|
6610
|
+
sleepSync(delayMs * attempt);
|
|
6611
|
+
}
|
|
6612
|
+
}
|
|
6613
|
+
}
|
|
6614
|
+
function openDatabase(path3, storeName, options = {}) {
|
|
6538
6615
|
const BetterSqlite3 = loadBetterSqlite3();
|
|
6539
6616
|
if (path3 !== MEMORY_PATH && path3 !== "") {
|
|
6540
6617
|
const dir = (0, import_utils210.dirname)(path3);
|
|
@@ -6543,16 +6620,28 @@ var require_dist = __commonJS({
|
|
|
6543
6620
|
(0, import_utils210.ensureDirSync)(dir);
|
|
6544
6621
|
} catch (err) {
|
|
6545
6622
|
const message = err instanceof Error ? err.message : String(err);
|
|
6546
|
-
throw new Error(`${storeName}: failed to create directory "${dir}" for database "${path3}": ${message}
|
|
6623
|
+
throw new Error(`${storeName}: failed to create directory "${dir}" for database "${path3}": ${message}`, {
|
|
6624
|
+
cause: err
|
|
6625
|
+
});
|
|
6547
6626
|
}
|
|
6548
6627
|
}
|
|
6549
6628
|
}
|
|
6629
|
+
let db;
|
|
6630
|
+
try {
|
|
6631
|
+
db = new BetterSqlite3(path3);
|
|
6632
|
+
} catch (err) {
|
|
6633
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
6634
|
+
throw new Error(`${storeName}: failed to open database at "${path3}": ${message}`, { cause: err });
|
|
6635
|
+
}
|
|
6550
6636
|
try {
|
|
6551
|
-
|
|
6637
|
+
db.pragma(`busy_timeout = ${Math.max(0, Math.floor(options.busyTimeoutMs ?? DEFAULT_BUSY_TIMEOUT_MS))}`);
|
|
6638
|
+
if (options.walMode) withBusyRetry(() => db.pragma("journal_mode = WAL"));
|
|
6552
6639
|
} catch (err) {
|
|
6640
|
+
db.close();
|
|
6553
6641
|
const message = err instanceof Error ? err.message : String(err);
|
|
6554
|
-
throw new Error(`${storeName}: failed to
|
|
6642
|
+
throw new Error(`${storeName}: failed to configure database at "${path3}": ${message}`, { cause: err });
|
|
6555
6643
|
}
|
|
6644
|
+
return db;
|
|
6556
6645
|
}
|
|
6557
6646
|
var SqliteKvStore = class {
|
|
6558
6647
|
db;
|
|
@@ -6560,14 +6649,14 @@ var require_dist = __commonJS({
|
|
|
6560
6649
|
cleanupTimer = null;
|
|
6561
6650
|
stmts = null;
|
|
6562
6651
|
constructor(options) {
|
|
6563
|
-
this.db = openDatabase(options.path, "SqliteKvStore"
|
|
6564
|
-
|
|
6565
|
-
|
|
6566
|
-
}
|
|
6652
|
+
this.db = openDatabase(options.path, "SqliteKvStore", {
|
|
6653
|
+
busyTimeoutMs: options.busyTimeoutMs,
|
|
6654
|
+
walMode: options.walMode !== false
|
|
6655
|
+
});
|
|
6567
6656
|
if (options.encryption?.secret) {
|
|
6568
6657
|
this.encryptionKey = deriveEncryptionKey(options.encryption.secret);
|
|
6569
6658
|
}
|
|
6570
|
-
this.initSchema();
|
|
6659
|
+
withBusyRetry(() => this.initSchema());
|
|
6571
6660
|
this.prepareStatements();
|
|
6572
6661
|
const cleanupInterval = options.ttlCleanupIntervalMs ?? 6e4;
|
|
6573
6662
|
if (cleanupInterval > 0) {
|
|
@@ -7026,17 +7115,17 @@ var require_dist = __commonJS({
|
|
|
7026
7115
|
encryptionKey = null;
|
|
7027
7116
|
stmts = null;
|
|
7028
7117
|
constructor(options) {
|
|
7029
|
-
this.db = openDatabase(options.path, "SqliteTaskStore"
|
|
7030
|
-
|
|
7031
|
-
|
|
7032
|
-
}
|
|
7118
|
+
this.db = openDatabase(options.path, "SqliteTaskStore", {
|
|
7119
|
+
busyTimeoutMs: options.busyTimeoutMs,
|
|
7120
|
+
walMode: options.walMode !== false
|
|
7121
|
+
});
|
|
7033
7122
|
this.logger = options.logger;
|
|
7034
7123
|
this.liveness = options.livenessProbe ?? defaultLivenessProbe;
|
|
7035
7124
|
if (options.encryption?.secret) {
|
|
7036
7125
|
this.encryptionKey = deriveEncryptionKey(options.encryption.secret);
|
|
7037
7126
|
}
|
|
7038
7127
|
this.emitter.setMaxListeners(200);
|
|
7039
|
-
this.initSchema();
|
|
7128
|
+
withBusyRetry(() => this.initSchema());
|
|
7040
7129
|
this.prepareStatements();
|
|
7041
7130
|
const cleanupInterval = options.ttlCleanupIntervalMs ?? 6e4;
|
|
7042
7131
|
if (cleanupInterval > 0) {
|
|
@@ -7273,13 +7362,13 @@ var require_dist = __commonJS({
|
|
|
7273
7362
|
cleanupTimer = null;
|
|
7274
7363
|
stmts = null;
|
|
7275
7364
|
constructor(options) {
|
|
7276
|
-
this.db = openDatabase(options.path, "SqliteEventStore"
|
|
7277
|
-
|
|
7278
|
-
|
|
7279
|
-
}
|
|
7365
|
+
this.db = openDatabase(options.path, "SqliteEventStore", {
|
|
7366
|
+
busyTimeoutMs: options.busyTimeoutMs,
|
|
7367
|
+
walMode: options.walMode !== false
|
|
7368
|
+
});
|
|
7280
7369
|
this.maxEvents = options.maxEvents ?? 1e4;
|
|
7281
7370
|
this.ttlMs = options.ttlMs ?? 3e5;
|
|
7282
|
-
this.initSchema();
|
|
7371
|
+
withBusyRetry(() => this.initSchema());
|
|
7283
7372
|
this.prepareStatements();
|
|
7284
7373
|
const cleanupInterval = options.ttlCleanupIntervalMs ?? 6e4;
|
|
7285
7374
|
if (cleanupInterval > 0) {
|
|
@@ -7414,7 +7503,8 @@ var require_dist = __commonJS({
|
|
|
7414
7503
|
secret: import_lazy_zod113.z.string().min(1)
|
|
7415
7504
|
}).optional(),
|
|
7416
7505
|
ttlCleanupIntervalMs: import_lazy_zod113.z.number().int().nonnegative().optional().default(6e4),
|
|
7417
|
-
walMode: import_lazy_zod113.z.boolean().optional().default(true)
|
|
7506
|
+
walMode: import_lazy_zod113.z.boolean().optional().default(true),
|
|
7507
|
+
busyTimeoutMs: import_lazy_zod113.z.number().int().nonnegative().optional().default(5e3)
|
|
7418
7508
|
});
|
|
7419
7509
|
}
|
|
7420
7510
|
});
|
|
@@ -7433,6 +7523,14 @@ function detectStorageType(storage, configuredType) {
|
|
|
7433
7523
|
if (getEnv("REDIS_URL") || getEnv("REDIS_HOST")) return "redis";
|
|
7434
7524
|
return "memory";
|
|
7435
7525
|
}
|
|
7526
|
+
function isExplicitStorage(storage) {
|
|
7527
|
+
return Boolean(storage?.type && storage.type !== "auto");
|
|
7528
|
+
}
|
|
7529
|
+
function resolvesToVercelKvTaskBackend(redis, storage) {
|
|
7530
|
+
if (isExplicitStorage(storage)) return storage.type === "vercel-kv";
|
|
7531
|
+
if (redis) return "provider" in redis && redis.provider === "vercel-kv";
|
|
7532
|
+
return Boolean(getEnv("KV_REST_API_URL"));
|
|
7533
|
+
}
|
|
7436
7534
|
async function createTaskStore(options = {}) {
|
|
7437
7535
|
const { storage: storageConfig, redis, keyPrefix = "mcp:task:", logger, isEdgeRuntime: isEdgeRuntime3 = false, sqlite } = options;
|
|
7438
7536
|
if (isEdgeRuntime3 && sqlite) {
|
|
@@ -7457,7 +7555,7 @@ async function createTaskStore(options = {}) {
|
|
|
7457
7555
|
return { store: store2, type: "sqlite" };
|
|
7458
7556
|
}
|
|
7459
7557
|
let finalStorageConfig = storageConfig;
|
|
7460
|
-
if (redis && !storageConfig) {
|
|
7558
|
+
if (redis && !isExplicitStorage(storageConfig)) {
|
|
7461
7559
|
if ("provider" in redis && redis.provider === "vercel-kv") {
|
|
7462
7560
|
throw new TaskStoreNotSupportedError(
|
|
7463
7561
|
"Vercel KV is not supported for task stores. Task result blocking and cancel signalling require pub/sub. Use Redis or Upstash instead."
|
|
@@ -7485,7 +7583,8 @@ async function createTaskStore(options = {}) {
|
|
|
7485
7583
|
};
|
|
7486
7584
|
}
|
|
7487
7585
|
}
|
|
7488
|
-
|
|
7586
|
+
const explicitBackend = isExplicitStorage(storageConfig) || Boolean(redis);
|
|
7587
|
+
if (finalStorageConfig?.type === "vercel-kv" || !explicitBackend && getEnv("KV_REST_API_URL")) {
|
|
7489
7588
|
throw new TaskStoreNotSupportedError(
|
|
7490
7589
|
"Vercel KV is not supported for task stores (pub/sub required). Use Redis or Upstash."
|
|
7491
7590
|
);
|
|
@@ -8726,11 +8825,8 @@ import {
|
|
|
8726
8825
|
} from "@frontmcp/protocol";
|
|
8727
8826
|
import { awaitOutsideRequest } from "@frontmcp/utils";
|
|
8728
8827
|
function hasMcpAppsExtension(capabilities) {
|
|
8729
|
-
|
|
8730
|
-
|
|
8731
|
-
}
|
|
8732
|
-
const uiExtension = capabilities.experimental[MCP_APPS_EXTENSION_KEY];
|
|
8733
|
-
return uiExtension !== void 0 && uiExtension !== null;
|
|
8828
|
+
const declared = capabilities?.experimental?.[MCP_APPS_EXTENSION_KEY] ?? capabilities?.extensions?.[MCP_APPS_EXTENSION_KEY];
|
|
8829
|
+
return declared !== void 0 && declared !== null;
|
|
8734
8830
|
}
|
|
8735
8831
|
function detectPlatformFromCapabilities(capabilities) {
|
|
8736
8832
|
if (hasMcpAppsExtension(capabilities)) {
|
|
@@ -11337,6 +11433,8 @@ var init_ui_shared = __esm({
|
|
|
11337
11433
|
widgets = /* @__PURE__ */ new Map();
|
|
11338
11434
|
manifests = /* @__PURE__ */ new Map();
|
|
11339
11435
|
resourceMeta = /* @__PURE__ */ new Map();
|
|
11436
|
+
uiTools = /* @__PURE__ */ new Set();
|
|
11437
|
+
customUris = /* @__PURE__ */ new Map();
|
|
11340
11438
|
resolver;
|
|
11341
11439
|
options;
|
|
11342
11440
|
constructor(resolver, options = {}) {
|
|
@@ -11351,6 +11449,28 @@ var init_ui_shared = __esm({
|
|
|
11351
11449
|
logger: this.options.logger
|
|
11352
11450
|
};
|
|
11353
11451
|
}
|
|
11452
|
+
/**
|
|
11453
|
+
* Record that `toolName` has a UI, and the custom `ui.resourceUri` it advertises (if any).
|
|
11454
|
+
* `tools/list` advertises that URI verbatim, so `resources/read` must resolve it back to the
|
|
11455
|
+
* tool; and only registered tools may be served a placeholder widget.
|
|
11456
|
+
*/
|
|
11457
|
+
registerTool(toolName, resourceUri) {
|
|
11458
|
+
this.uiTools.add(toolName);
|
|
11459
|
+
if (resourceUri) this.customUris.set(resourceUri, toolName);
|
|
11460
|
+
}
|
|
11461
|
+
/** The tool that advertised `uri` as its custom `ui.resourceUri`. */
|
|
11462
|
+
resolveCustomUri(uri) {
|
|
11463
|
+
return this.customUris.get(uri);
|
|
11464
|
+
}
|
|
11465
|
+
/**
|
|
11466
|
+
* Whether `name` (or its app-qualified form `app:name`) is a tool with a UI.
|
|
11467
|
+
*/
|
|
11468
|
+
hasTool(name44) {
|
|
11469
|
+
const known = (n) => this.uiTools.has(n) || this.widgets.has(n) || this.resourceMeta.has(n);
|
|
11470
|
+
if (known(name44)) return true;
|
|
11471
|
+
const sep = name44.indexOf(":");
|
|
11472
|
+
return sep > 0 && known(name44.slice(sep + 1));
|
|
11473
|
+
}
|
|
11354
11474
|
getStaticWidget(name44) {
|
|
11355
11475
|
return this.widgets.get(name44);
|
|
11356
11476
|
}
|
|
@@ -11379,8 +11499,9 @@ var init_ui_shared = __esm({
|
|
|
11379
11499
|
const output = options["output"] ?? {};
|
|
11380
11500
|
const uiConfig = options["uiConfig"];
|
|
11381
11501
|
const resourceMode = uiConfig?.["resourceMode"] ?? options["resourceMode"];
|
|
11382
|
-
if (!toolName
|
|
11502
|
+
if (!toolName) return;
|
|
11383
11503
|
this.updateResourceMetaFromConfig(toolName, uiConfig);
|
|
11504
|
+
if (!template) return;
|
|
11384
11505
|
const result = renderToolTemplate({
|
|
11385
11506
|
toolName,
|
|
11386
11507
|
input,
|
|
@@ -11389,6 +11510,7 @@ var init_ui_shared = __esm({
|
|
|
11389
11510
|
resolver: this.resolver,
|
|
11390
11511
|
resourceMode,
|
|
11391
11512
|
sizing: extractSizing(uiConfig),
|
|
11513
|
+
csp: uiConfig?.["csp"],
|
|
11392
11514
|
...this.stringResultOptions(uiConfig)
|
|
11393
11515
|
});
|
|
11394
11516
|
this.widgets.set(toolName, result.html);
|
|
@@ -11467,6 +11589,7 @@ var init_ui_shared = __esm({
|
|
|
11467
11589
|
resolver: this.resolver,
|
|
11468
11590
|
resourceMode,
|
|
11469
11591
|
sizing: extractSizing(uiConfig),
|
|
11592
|
+
csp: uiConfig?.["csp"],
|
|
11470
11593
|
...this.stringResultOptions(uiConfig)
|
|
11471
11594
|
});
|
|
11472
11595
|
return { meta: result.meta };
|
|
@@ -11519,26 +11642,14 @@ function handleUIResourceRead(uri, registry, platformType) {
|
|
|
11519
11642
|
return { handled: false };
|
|
11520
11643
|
}
|
|
11521
11644
|
const mimeType = getUIResourceMimeType(platformType);
|
|
11522
|
-
const
|
|
11645
|
+
const customTool = registry.resolveCustomUri(uri);
|
|
11646
|
+
const widgetParsed = customTool ? { toolName: customTool } : parseWidgetUri(uri);
|
|
11523
11647
|
if (widgetParsed) {
|
|
11524
|
-
|
|
11525
|
-
|
|
11526
|
-
if (cachedWidget) {
|
|
11527
|
-
return {
|
|
11528
|
-
handled: true,
|
|
11529
|
-
result: {
|
|
11530
|
-
contents: [
|
|
11531
|
-
{
|
|
11532
|
-
uri,
|
|
11533
|
-
mimeType,
|
|
11534
|
-
text: cachedWidget,
|
|
11535
|
-
...resourceMeta ? { _meta: resourceMeta } : {}
|
|
11536
|
-
}
|
|
11537
|
-
]
|
|
11538
|
-
}
|
|
11539
|
-
};
|
|
11648
|
+
if (!registry.hasTool(widgetParsed.toolName)) {
|
|
11649
|
+
return { handled: true, error: `No UI widget is registered for ${uri}` };
|
|
11540
11650
|
}
|
|
11541
|
-
const
|
|
11651
|
+
const resourceMeta = buildResourceMetaForWidget(registry, widgetParsed.toolName);
|
|
11652
|
+
const html = registry.getStaticWidget(widgetParsed.toolName) ?? generatePlaceholderWidget(widgetParsed.toolName);
|
|
11542
11653
|
return {
|
|
11543
11654
|
handled: true,
|
|
11544
11655
|
result: {
|
|
@@ -12108,6 +12219,7 @@ var DirectClientImpl;
|
|
|
12108
12219
|
var init_direct_client = __esm({
|
|
12109
12220
|
"libs/sdk/src/direct/direct-client.ts"() {
|
|
12110
12221
|
"use strict";
|
|
12222
|
+
init_list_all_pages_utils();
|
|
12111
12223
|
init_errors();
|
|
12112
12224
|
init_skills_mcp_types();
|
|
12113
12225
|
init_llm_platform();
|
|
@@ -12254,8 +12366,11 @@ var init_direct_client = __esm({
|
|
|
12254
12366
|
// Tool Operations (platform-formatted)
|
|
12255
12367
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
12256
12368
|
async listTools() {
|
|
12257
|
-
const
|
|
12258
|
-
|
|
12369
|
+
const tools = await listAllPages("tools/list", async (cursor) => {
|
|
12370
|
+
const result = await this.mcpClient.listTools(cursor ? { cursor } : void 0);
|
|
12371
|
+
return { items: result.tools, nextCursor: result.nextCursor };
|
|
12372
|
+
});
|
|
12373
|
+
return formatToolsForPlatform(tools, this.platform);
|
|
12259
12374
|
}
|
|
12260
12375
|
async callTool(name44, args) {
|
|
12261
12376
|
const result = await this.mcpClient.callTool({
|
|
@@ -12268,19 +12383,34 @@ var init_direct_client = __esm({
|
|
|
12268
12383
|
// Resource Operations (raw format)
|
|
12269
12384
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
12270
12385
|
async listResources() {
|
|
12271
|
-
|
|
12386
|
+
const resources = await listAllPages("resources/list", async (cursor) => {
|
|
12387
|
+
const result = await this.mcpClient.listResources(cursor ? { cursor } : void 0);
|
|
12388
|
+
return { items: result.resources, nextCursor: result.nextCursor };
|
|
12389
|
+
});
|
|
12390
|
+
return { resources };
|
|
12272
12391
|
}
|
|
12273
12392
|
async readResource(uri) {
|
|
12274
12393
|
return this.mcpClient.readResource({ uri });
|
|
12275
12394
|
}
|
|
12276
12395
|
async listResourceTemplates() {
|
|
12277
|
-
|
|
12396
|
+
const resourceTemplates = await listAllPages(
|
|
12397
|
+
"resources/templates/list",
|
|
12398
|
+
async (cursor) => {
|
|
12399
|
+
const result = await this.mcpClient.listResourceTemplates(cursor ? { cursor } : void 0);
|
|
12400
|
+
return { items: result.resourceTemplates, nextCursor: result.nextCursor };
|
|
12401
|
+
}
|
|
12402
|
+
);
|
|
12403
|
+
return { resourceTemplates };
|
|
12278
12404
|
}
|
|
12279
12405
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
12280
12406
|
// Prompt Operations (raw format)
|
|
12281
12407
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
12282
12408
|
async listPrompts() {
|
|
12283
|
-
|
|
12409
|
+
const prompts = await listAllPages("prompts/list", async (cursor) => {
|
|
12410
|
+
const result = await this.mcpClient.listPrompts(cursor ? { cursor } : void 0);
|
|
12411
|
+
return { items: result.prompts, nextCursor: result.nextCursor };
|
|
12412
|
+
});
|
|
12413
|
+
return { prompts };
|
|
12284
12414
|
}
|
|
12285
12415
|
async getPrompt(name44, args) {
|
|
12286
12416
|
return this.mcpClient.getPrompt({
|
|
@@ -13008,8 +13138,8 @@ function createRemoteAppProbe(appId, provider) {
|
|
|
13008
13138
|
name: `remote:${appId}`,
|
|
13009
13139
|
async check() {
|
|
13010
13140
|
const result = provider.getHealthStatus(appId);
|
|
13011
|
-
if (!result) {
|
|
13012
|
-
return { status: "
|
|
13141
|
+
if (!result || result.status === "unknown" && result.consecutiveFailures === 0) {
|
|
13142
|
+
return { status: "degraded", details: { state: "unknown" } };
|
|
13013
13143
|
}
|
|
13014
13144
|
return {
|
|
13015
13145
|
status: result.status === "healthy" ? "healthy" : result.status === "degraded" ? "degraded" : "unhealthy",
|
|
@@ -14122,6 +14252,12 @@ var init_frontmcp_context = __esm({
|
|
|
14122
14252
|
// =====================
|
|
14123
14253
|
/** Request metadata (headers, user-agent, etc.) */
|
|
14124
14254
|
metadata;
|
|
14255
|
+
/**
|
|
14256
|
+
* The hosting platform's bindings object for this request: a Cloudflare Worker's `env`
|
|
14257
|
+
* (KV namespaces, D1 databases, R2 buckets, Durable Objects, `[vars]`, secrets).
|
|
14258
|
+
* `undefined` on Node, where bindings do not exist.
|
|
14259
|
+
*/
|
|
14260
|
+
platformEnv;
|
|
14125
14261
|
// =====================
|
|
14126
14262
|
// Auth (mutable, progressively populated)
|
|
14127
14263
|
// =====================
|
|
@@ -14157,6 +14293,7 @@ var init_frontmcp_context = __esm({
|
|
|
14157
14293
|
this.traceContext = args.traceContext ?? generateTraceContext();
|
|
14158
14294
|
this.timestamp = args.timestamp ?? Date.now();
|
|
14159
14295
|
this._authInfo = args.authInfo ?? {};
|
|
14296
|
+
this.platformEnv = args.platformEnv;
|
|
14160
14297
|
this.config = {
|
|
14161
14298
|
forwardCallerTokenTo: toOrigins(args.config?.forwardCallerTokenTo),
|
|
14162
14299
|
forwardCustomHeadersTo: toOrigins(args.config?.forwardCustomHeadersTo),
|
|
@@ -14858,6 +14995,7 @@ var init_frontmcp_context_storage = __esm({
|
|
|
14858
14995
|
"use strict";
|
|
14859
14996
|
init_decorators();
|
|
14860
14997
|
init_metadata();
|
|
14998
|
+
init_tokens();
|
|
14861
14999
|
init_mcp_error();
|
|
14862
15000
|
init_frontmcp_context();
|
|
14863
15001
|
init_metadata_utils();
|
|
@@ -14925,7 +15063,12 @@ var init_frontmcp_context_storage = __esm({
|
|
|
14925
15063
|
const headers = request.headers ?? {};
|
|
14926
15064
|
const headerSessionId = typeof headers["mcp-session-id"] === "string" ? headers["mcp-session-id"].trim() : "";
|
|
14927
15065
|
const sessionId = headerSessionId.length > 0 ? headerSessionId : `anon:${randomUUID9()}`;
|
|
14928
|
-
|
|
15066
|
+
const platformEnv = request[ServerRequestTokens.webEnv];
|
|
15067
|
+
return this.runFromHeaders(
|
|
15068
|
+
headers,
|
|
15069
|
+
{ sessionId, scopeId, peerAddress: request.socket?.remoteAddress, platformEnv },
|
|
15070
|
+
fn
|
|
15071
|
+
);
|
|
14929
15072
|
}
|
|
14930
15073
|
withServerConfig(args) {
|
|
14931
15074
|
return { ...args, config: { ...this.contextConfig, ...args.config } };
|
|
@@ -21987,6 +22130,14 @@ import {
|
|
|
21987
22130
|
isDevelopment as isDevelopment2,
|
|
21988
22131
|
randomUUID as randomUUID11
|
|
21989
22132
|
} from "@frontmcp/utils";
|
|
22133
|
+
function declaredOutput(tool, output) {
|
|
22134
|
+
if (!tool?.outputSchema || typeof output !== "object" || output === null || Array.isArray(output)) return output;
|
|
22135
|
+
if (Array.isArray(output["content"])) return output;
|
|
22136
|
+
const parsed = tool.safeParseOutput(output);
|
|
22137
|
+
if (!parsed.success) return output;
|
|
22138
|
+
const structured = parsed.data.structuredContent;
|
|
22139
|
+
return typeof structured === "object" && structured !== null && !Array.isArray(structured) ? structured : output;
|
|
22140
|
+
}
|
|
21990
22141
|
function toolNameCandidates(name44) {
|
|
21991
22142
|
if (!/[-_]/.test(name44)) return [name44];
|
|
21992
22143
|
return [name44, name44.includes("_") ? name44.replace(/_/g, "-") : name44.replace(/-/g, "_")];
|
|
@@ -22056,6 +22207,9 @@ var init_call_tool_flow = __esm({
|
|
|
22056
22207
|
uiResult: z57.any().optional(),
|
|
22057
22208
|
// UI metadata from rendering (merged into _meta)
|
|
22058
22209
|
uiMeta: z57.record(z57.string(), z57.unknown()).optional(),
|
|
22210
|
+
// Metadata hooks add to the result (e.g. the cache plugin's `cache: 'hit'`), merged into the
|
|
22211
|
+
// result's _meta; never merged into rawOutput, which is the tool's data
|
|
22212
|
+
resultMeta: z57.record(z57.string(), z57.unknown()).optional(),
|
|
22059
22213
|
// Progress token from request's _meta (for progress notifications)
|
|
22060
22214
|
progressToken: z57.union([z57.string(), z57.number()]).optional(),
|
|
22061
22215
|
// JSON-RPC request ID (for elicitation routing)
|
|
@@ -22711,7 +22865,7 @@ ${JSON.stringify(error.schema, null, 2)}
|
|
|
22711
22865
|
this.logger.verbose("execute:done (elicitation fire-and-forget fallback)");
|
|
22712
22866
|
return;
|
|
22713
22867
|
}
|
|
22714
|
-
if (isClientFacingError(error)) throw error;
|
|
22868
|
+
if (isClientFacingError(error) || error instanceof InvalidOutputError) throw error;
|
|
22715
22869
|
throw new ToolExecutionError(
|
|
22716
22870
|
this.state.tool?.metadata.name || "unknown",
|
|
22717
22871
|
error instanceof Error ? error : void 0
|
|
@@ -22754,7 +22908,8 @@ ${JSON.stringify(error.schema, null, 2)}
|
|
|
22754
22908
|
}
|
|
22755
22909
|
async applyUI() {
|
|
22756
22910
|
this.logger.verbose("applyUI:start");
|
|
22757
|
-
const { tool, rawOutput, input } = this.state;
|
|
22911
|
+
const { tool, rawOutput: executeOutput, input } = this.state;
|
|
22912
|
+
const rawOutput = declaredOutput(tool, executeOutput);
|
|
22758
22913
|
const ctx = this.input.ctx;
|
|
22759
22914
|
if (ctx?._skipUI) {
|
|
22760
22915
|
this.logger.verbose("applyUI:skip (agent context - structured data only)");
|
|
@@ -22883,7 +23038,7 @@ ${JSON.stringify(error.schema, null, 2)}
|
|
|
22883
23038
|
}
|
|
22884
23039
|
async finalize() {
|
|
22885
23040
|
this.logger.verbose("finalize:start");
|
|
22886
|
-
const { tool, rawOutput, uiResult, uiMeta } = this.state;
|
|
23041
|
+
const { tool, rawOutput, uiResult, uiMeta, resultMeta } = this.state;
|
|
22887
23042
|
if (!tool) {
|
|
22888
23043
|
this.logger.verbose("finalize: skipping (no tool in state)");
|
|
22889
23044
|
return;
|
|
@@ -22926,6 +23081,9 @@ ${JSON.stringify(error.schema, null, 2)}
|
|
|
22926
23081
|
if (rawMeta) {
|
|
22927
23082
|
result._meta = { ...result._meta, ...rawMeta };
|
|
22928
23083
|
}
|
|
23084
|
+
if (resultMeta) {
|
|
23085
|
+
result._meta = { ...result._meta, ...resultMeta };
|
|
23086
|
+
}
|
|
22929
23087
|
if (uiResult) {
|
|
22930
23088
|
result.content = uiResult.content;
|
|
22931
23089
|
if (uiResult.structuredContent !== void 0 && uiResult.structuredContent !== null) {
|
|
@@ -24376,6 +24534,38 @@ var init_context = __esm({
|
|
|
24376
24534
|
}
|
|
24377
24535
|
});
|
|
24378
24536
|
|
|
24537
|
+
// libs/sdk/src/errors/misconfiguration.ts
|
|
24538
|
+
function findMisconfiguration(error) {
|
|
24539
|
+
let current = error;
|
|
24540
|
+
for (let depth = 0; current instanceof Error && depth < 5; depth++) {
|
|
24541
|
+
const code = current.code;
|
|
24542
|
+
if (typeof code === "string" && Object.prototype.hasOwnProperty.call(MISCONFIGURATION_REMEDIES, code)) {
|
|
24543
|
+
return { code, remedy: MISCONFIGURATION_REMEDIES[code] };
|
|
24544
|
+
}
|
|
24545
|
+
if (current.name === "ZodError") return { code: "CONFIG_INVALID", remedy: CONFIG_INVALID_REMEDY };
|
|
24546
|
+
current = current.cause;
|
|
24547
|
+
}
|
|
24548
|
+
return void 0;
|
|
24549
|
+
}
|
|
24550
|
+
function misconfigurationBody(m) {
|
|
24551
|
+
return { error: "server_misconfigured", code: m.code, message: m.remedy };
|
|
24552
|
+
}
|
|
24553
|
+
var MISCONFIGURATION_REMEDIES, CONFIG_INVALID_REMEDY;
|
|
24554
|
+
var init_misconfiguration = __esm({
|
|
24555
|
+
"libs/sdk/src/errors/misconfiguration.ts"() {
|
|
24556
|
+
"use strict";
|
|
24557
|
+
MISCONFIGURATION_REMEDIES = {
|
|
24558
|
+
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.",
|
|
24559
|
+
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`.",
|
|
24560
|
+
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.",
|
|
24561
|
+
// The startup checks: a server whose entries ask for protection nothing gives them does not start.
|
|
24562
|
+
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.",
|
|
24563
|
+
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."
|
|
24564
|
+
};
|
|
24565
|
+
CONFIG_INVALID_REMEDY = "The FrontMCP configuration failed validation, so the server refuses to start. The server log names the invalid fields.";
|
|
24566
|
+
}
|
|
24567
|
+
});
|
|
24568
|
+
|
|
24379
24569
|
// libs/sdk/src/server/server.validation.ts
|
|
24380
24570
|
import { base64Decode } from "@frontmcp/utils";
|
|
24381
24571
|
function applyHeaders(res, headers) {
|
|
@@ -24687,6 +24877,7 @@ var init_flow_instance = __esm({
|
|
|
24687
24877
|
init_common();
|
|
24688
24878
|
init_context();
|
|
24689
24879
|
init_errors();
|
|
24880
|
+
init_misconfiguration();
|
|
24690
24881
|
init_server_validation();
|
|
24691
24882
|
init_flow_http_path();
|
|
24692
24883
|
init_flow_stages();
|
|
@@ -24746,6 +24937,15 @@ var init_flow_instance = __esm({
|
|
|
24746
24937
|
return writeHttpResponse(response, e.output);
|
|
24747
24938
|
}
|
|
24748
24939
|
}
|
|
24940
|
+
const misconfiguration = findMisconfiguration(e);
|
|
24941
|
+
if (misconfiguration) {
|
|
24942
|
+
this.logger.error("Server misconfigured", { flow: this.name, code: misconfiguration.code });
|
|
24943
|
+
return writeHttpResponse(response, {
|
|
24944
|
+
kind: "json",
|
|
24945
|
+
status: 500,
|
|
24946
|
+
body: misconfigurationBody(misconfiguration)
|
|
24947
|
+
});
|
|
24948
|
+
}
|
|
24749
24949
|
if (e instanceof PublicMcpError) {
|
|
24750
24950
|
const challenge = e.wwwAuthenticate;
|
|
24751
24951
|
this.logger.warn("Public error in flow", {
|
|
@@ -25457,14 +25657,90 @@ function collectDynamicProviders(klass, options) {
|
|
|
25457
25657
|
}
|
|
25458
25658
|
return out;
|
|
25459
25659
|
}
|
|
25660
|
+
function collectDynamicTools(klass, options) {
|
|
25661
|
+
const chain = [];
|
|
25662
|
+
for (let k = klass; k && k !== Function.prototype; k = Object.getPrototypeOf(k)) {
|
|
25663
|
+
chain.push(k);
|
|
25664
|
+
}
|
|
25665
|
+
const out = [];
|
|
25666
|
+
for (let i = chain.length - 1; i >= 0; i--) {
|
|
25667
|
+
const k = chain[i];
|
|
25668
|
+
if (Object.prototype.hasOwnProperty.call(k, "dynamicTools") && typeof k.dynamicTools === "function") {
|
|
25669
|
+
out.push(...k.dynamicTools(options) ?? []);
|
|
25670
|
+
}
|
|
25671
|
+
}
|
|
25672
|
+
return out;
|
|
25673
|
+
}
|
|
25674
|
+
function pluginMetadataFromOptions(options, dynamicTools) {
|
|
25675
|
+
const out = { ...options };
|
|
25676
|
+
for (const key of LIST_METADATA_KEYS) {
|
|
25677
|
+
if (key in out && !Array.isArray(out[key])) delete out[key];
|
|
25678
|
+
}
|
|
25679
|
+
const tools = [...out["tools"] ?? [], ...dynamicTools];
|
|
25680
|
+
if (tools.length > 0) out["tools"] = tools;
|
|
25681
|
+
return out;
|
|
25682
|
+
}
|
|
25460
25683
|
function dedupePluginProviders(providers) {
|
|
25461
25684
|
const map = /* @__PURE__ */ new Map();
|
|
25462
25685
|
for (const p of providers) map.set(p["provide"] ?? p, p);
|
|
25463
25686
|
return [...map.values()];
|
|
25464
25687
|
}
|
|
25688
|
+
var LIST_METADATA_KEYS;
|
|
25465
25689
|
var init_dynamic_utils = __esm({
|
|
25466
25690
|
"libs/sdk/src/common/dynamic/dynamic.utils.ts"() {
|
|
25467
25691
|
"use strict";
|
|
25692
|
+
LIST_METADATA_KEYS = ["exports", "plugins", "adapters", "tools", "resources", "prompts", "skills"];
|
|
25693
|
+
}
|
|
25694
|
+
});
|
|
25695
|
+
|
|
25696
|
+
// libs/sdk/src/common/dynamic/dynamic.plugin.ts
|
|
25697
|
+
function isDynamicPluginClass(value) {
|
|
25698
|
+
return typeof value === "function" && value.prototype instanceof DynamicPlugin;
|
|
25699
|
+
}
|
|
25700
|
+
function initOptionsOf(instance) {
|
|
25701
|
+
return initOptionsByInstance.has(instance) ? { options: initOptionsByInstance.get(instance) } : void 0;
|
|
25702
|
+
}
|
|
25703
|
+
var initOptionsByInstance, DynamicPlugin;
|
|
25704
|
+
var init_dynamic_plugin = __esm({
|
|
25705
|
+
"libs/sdk/src/common/dynamic/dynamic.plugin.ts"() {
|
|
25706
|
+
"use strict";
|
|
25707
|
+
init_transport_errors();
|
|
25708
|
+
init_dynamic_utils();
|
|
25709
|
+
initOptionsByInstance = /* @__PURE__ */ new WeakMap();
|
|
25710
|
+
DynamicPlugin = class {
|
|
25711
|
+
get(token) {
|
|
25712
|
+
throw new MethodNotImplementedError("DynamicPlugin", "get");
|
|
25713
|
+
}
|
|
25714
|
+
/**
|
|
25715
|
+
* Static init() method to create a plugin provider.
|
|
25716
|
+
* @param options - Input options (with optional fields for defaults). Omitted, the plugin is
|
|
25717
|
+
* built with `{}`, as `ApprovalPlugin.init()` is documented.
|
|
25718
|
+
*/
|
|
25719
|
+
static init(options = {}) {
|
|
25720
|
+
const extraProviders = options.providers;
|
|
25721
|
+
const typedOptions = options;
|
|
25722
|
+
if ("useFactory" in options) {
|
|
25723
|
+
return {
|
|
25724
|
+
...pluginMetadataFromOptions(typedOptions, []),
|
|
25725
|
+
provide: this,
|
|
25726
|
+
inject: options.inject,
|
|
25727
|
+
useFactory: options.useFactory,
|
|
25728
|
+
providers: dedupePluginProviders(extraProviders ?? [])
|
|
25729
|
+
};
|
|
25730
|
+
}
|
|
25731
|
+
const dyn = collectDynamicProviders(this, typedOptions);
|
|
25732
|
+
const mergedProviders = dedupePluginProviders([...dyn ?? [], ...extraProviders ?? []]);
|
|
25733
|
+
const dynamicTools = collectDynamicTools(this, typedOptions);
|
|
25734
|
+
const instance = new this(options);
|
|
25735
|
+
initOptionsByInstance.set(instance, options);
|
|
25736
|
+
return {
|
|
25737
|
+
...pluginMetadataFromOptions(typedOptions, dynamicTools),
|
|
25738
|
+
provide: this,
|
|
25739
|
+
useValue: instance,
|
|
25740
|
+
providers: mergedProviders
|
|
25741
|
+
};
|
|
25742
|
+
}
|
|
25743
|
+
};
|
|
25468
25744
|
}
|
|
25469
25745
|
});
|
|
25470
25746
|
|
|
@@ -25720,9 +25996,18 @@ var init_execution_context_interface = __esm({
|
|
|
25720
25996
|
if (this._authContext) return this._authContext;
|
|
25721
25997
|
const rawAuth = __require("@frontmcp/auth");
|
|
25722
25998
|
const auth = rawAuth.default ?? rawAuth;
|
|
25723
|
-
this._authContext = auth.buildAuthContext(this.
|
|
25999
|
+
this._authContext = auth.buildAuthContext(this.resolveAuthSource(), this.scope.metadata.authorities?.claimsMapping);
|
|
25724
26000
|
return this._authContext;
|
|
25725
26001
|
}
|
|
26002
|
+
resolveAuthSource() {
|
|
26003
|
+
const fromContext = this.tryGetContext()?.authInfo;
|
|
26004
|
+
if (!fromContext) return this._authInfo;
|
|
26005
|
+
const merged = { ...this._authInfo };
|
|
26006
|
+
for (const [key, value] of Object.entries(fromContext)) {
|
|
26007
|
+
if (value !== void 0) merged[key] = value;
|
|
26008
|
+
}
|
|
26009
|
+
return merged;
|
|
26010
|
+
}
|
|
25726
26011
|
/**
|
|
25727
26012
|
* Try to get the context, returning undefined if not available.
|
|
25728
26013
|
*
|
|
@@ -25908,6 +26193,23 @@ var init_execution_context_interface = __esm({
|
|
|
25908
26193
|
get config() {
|
|
25909
26194
|
return this.providers.get(ConfigService);
|
|
25910
26195
|
}
|
|
26196
|
+
// ---- Platform bindings ----
|
|
26197
|
+
/**
|
|
26198
|
+
* The hosting platform's bindings for the current request — on a Cloudflare Worker, the `env`
|
|
26199
|
+
* object holding KV namespaces, D1 databases, R2 buckets, Durable Object namespaces, `[vars]`
|
|
26200
|
+
* and secrets. `undefined` on Node/Express, where no such object exists.
|
|
26201
|
+
*
|
|
26202
|
+
* Only string bindings are mirrored into `process.env`; use this for the rest.
|
|
26203
|
+
*
|
|
26204
|
+
* @example
|
|
26205
|
+
* ```typescript
|
|
26206
|
+
* const kv = this.workerEnv?.['MY_KV'] as KVNamespace | undefined;
|
|
26207
|
+
* ```
|
|
26208
|
+
*/
|
|
26209
|
+
get workerEnv() {
|
|
26210
|
+
const env = this.tryGetContext()?.platformEnv;
|
|
26211
|
+
return env !== null && typeof env === "object" ? env : void 0;
|
|
26212
|
+
}
|
|
25911
26213
|
// ---- Runtime context helpers ----
|
|
25912
26214
|
/**
|
|
25913
26215
|
* Get the current runtime context (platform, runtime, deployment, env).
|
|
@@ -28955,12 +29257,13 @@ var init_plugin_utils = __esm({
|
|
|
28955
29257
|
// libs/sdk/src/plugin/plugin.registry.ts
|
|
28956
29258
|
import "reflect-metadata";
|
|
28957
29259
|
import { tokenName as tokenName11 } from "@frontmcp/di";
|
|
28958
|
-
var PluginRegistry;
|
|
29260
|
+
var installedPluginValues, PluginRegistry;
|
|
28959
29261
|
var init_plugin_registry = __esm({
|
|
28960
29262
|
"libs/sdk/src/plugin/plugin.registry.ts"() {
|
|
28961
29263
|
"use strict";
|
|
28962
29264
|
init_adapter_registry();
|
|
28963
29265
|
init_common();
|
|
29266
|
+
init_dynamic_plugin();
|
|
28964
29267
|
init_dynamic_utils();
|
|
28965
29268
|
init_context_extension();
|
|
28966
29269
|
init_errors();
|
|
@@ -28973,6 +29276,7 @@ var init_plugin_registry = __esm({
|
|
|
28973
29276
|
init_skill_registry();
|
|
28974
29277
|
init_tool_registry();
|
|
28975
29278
|
init_plugin_utils();
|
|
29279
|
+
installedPluginValues = /* @__PURE__ */ new WeakSet();
|
|
28976
29280
|
PluginRegistry = class _PluginRegistry extends RegistryAbstract {
|
|
28977
29281
|
/** providers by token */
|
|
28978
29282
|
pProviders = /* @__PURE__ */ new Map();
|
|
@@ -29161,8 +29465,15 @@ var init_plugin_registry = __esm({
|
|
|
29161
29465
|
pluginInstance: new rec.provide(...depsInstances),
|
|
29162
29466
|
dynamicProviders: rec.providers
|
|
29163
29467
|
};
|
|
29164
|
-
case "VALUE" /* VALUE */:
|
|
29165
|
-
|
|
29468
|
+
case "VALUE" /* VALUE */: {
|
|
29469
|
+
const value = rec.useValue;
|
|
29470
|
+
const init = installedPluginValues.has(value) ? initOptionsOf(value) : void 0;
|
|
29471
|
+
installedPluginValues.add(value);
|
|
29472
|
+
if (init && isDynamicPluginClass(rec.provide)) {
|
|
29473
|
+
return { pluginInstance: new rec.provide(init.options), dynamicProviders: rec.providers };
|
|
29474
|
+
}
|
|
29475
|
+
return { pluginInstance: value, dynamicProviders: rec.providers };
|
|
29476
|
+
}
|
|
29166
29477
|
case "FACTORY" /* FACTORY */: {
|
|
29167
29478
|
const args = [];
|
|
29168
29479
|
for (const d of rec.inject()) args.push(await this.providers.resolveBootstrapDep(d));
|
|
@@ -30705,6 +31016,9 @@ import { AuthorityDeniedError as AuthorityDeniedError7, resolveRequiredScopes as
|
|
|
30705
31016
|
import { ExecutionTimeoutError as ExecutionTimeoutError2, withTimeout as withTimeout2 } from "@frontmcp/guard";
|
|
30706
31017
|
import { z as z60 } from "@frontmcp/lazy-zod";
|
|
30707
31018
|
import { CallToolRequestSchema as CallToolRequestSchema3, CallToolResultSchema as CallToolResultSchema3 } from "@frontmcp/protocol";
|
|
31019
|
+
function invalidOutputFrom(outputError) {
|
|
31020
|
+
return outputError instanceof InvalidOutputError ? outputError : new InvalidOutputError();
|
|
31021
|
+
}
|
|
30708
31022
|
var inputSchema15, outputSchema14, stateSchema14, plan14, name14, Stage14, CallAgentFlow;
|
|
30709
31023
|
var init_call_agent_flow = __esm({
|
|
30710
31024
|
"libs/sdk/src/agent/flows/call-agent.flow.ts"() {
|
|
@@ -31087,7 +31401,7 @@ var init_call_agent_flow = __esm({
|
|
|
31087
31401
|
const { agent, agentContext, executionStartedAt, executionError, outputError, authInfo } = this.state;
|
|
31088
31402
|
if (!agent || executionStartedAt === void 0) return;
|
|
31089
31403
|
if (executionError instanceof ElicitationFallbackRequired || executionError instanceof FlowControl) return;
|
|
31090
|
-
const failure = executionError ?? (outputError !== void 0 ?
|
|
31404
|
+
const failure = executionError ?? (outputError !== void 0 ? invalidOutputFrom(outputError) : void 0);
|
|
31091
31405
|
const failed = failure !== void 0;
|
|
31092
31406
|
const requestId = this.tryGetContext()?.requestId;
|
|
31093
31407
|
const sessionId = typeof authInfo?.sessionId === "string" && authInfo.sessionId ? authInfo.sessionId : void 0;
|
|
@@ -31117,7 +31431,7 @@ var init_call_agent_flow = __esm({
|
|
|
31117
31431
|
agent: agent.metadata.name,
|
|
31118
31432
|
errors: outputError
|
|
31119
31433
|
});
|
|
31120
|
-
throw
|
|
31434
|
+
throw invalidOutputFrom(outputError);
|
|
31121
31435
|
}
|
|
31122
31436
|
const result = parsedOutput;
|
|
31123
31437
|
if (executionMeta) {
|
|
@@ -31697,13 +32011,46 @@ var init_request_validation = __esm({
|
|
|
31697
32011
|
});
|
|
31698
32012
|
|
|
31699
32013
|
// libs/sdk/src/transport/mcp-20260728/request-state.ts
|
|
31700
|
-
import {
|
|
31701
|
-
|
|
32014
|
+
import {
|
|
32015
|
+
base64urlDecode as base64urlDecode2,
|
|
32016
|
+
base64urlEncode as base64urlEncode2,
|
|
32017
|
+
getEnv as getEnv4,
|
|
32018
|
+
hmacSha256,
|
|
32019
|
+
isProduction as isProduction4,
|
|
32020
|
+
randomBytes as randomBytes3,
|
|
32021
|
+
sha256Hex as sha256Hex5,
|
|
32022
|
+
timingSafeEqual
|
|
32023
|
+
} from "@frontmcp/utils";
|
|
32024
|
+
function configuredSecret(name44) {
|
|
32025
|
+
const value = getEnv4(name44);
|
|
32026
|
+
return value && value.trim().length > 0 ? value : void 0;
|
|
32027
|
+
}
|
|
32028
|
+
function resolveKey() {
|
|
31702
32029
|
if (cachedKey) return cachedKey;
|
|
31703
|
-
const
|
|
31704
|
-
|
|
32030
|
+
const vault = configuredSecret("VAULT_SECRET");
|
|
32031
|
+
const jwt = vault ? void 0 : configuredSecret("JWT_SECRET");
|
|
32032
|
+
if (vault) cachedKey = { key: encoder.encode(vault), source: "VAULT_SECRET" };
|
|
32033
|
+
else if (jwt) cachedKey = { key: encoder.encode(jwt), source: "JWT_SECRET" };
|
|
32034
|
+
else cachedKey = { key: randomBytes3(32), source: "per-process" };
|
|
31705
32035
|
return cachedKey;
|
|
31706
32036
|
}
|
|
32037
|
+
function getRequestStateKey() {
|
|
32038
|
+
return resolveKey().key;
|
|
32039
|
+
}
|
|
32040
|
+
function getRequestStateKeySource() {
|
|
32041
|
+
return resolveKey().source;
|
|
32042
|
+
}
|
|
32043
|
+
function warnIfRequestStateKeyNotShared(options) {
|
|
32044
|
+
if (warnedKeyNotShared || !isProduction4()) return;
|
|
32045
|
+
const { metadata } = options;
|
|
32046
|
+
const persistence = metadata.transport?.persistence;
|
|
32047
|
+
const sharesState = Boolean(metadata.redis) || typeof persistence === "object" && persistence !== null;
|
|
32048
|
+
if (!sharesState || getRequestStateKeySource() !== "per-process") return;
|
|
32049
|
+
warnedKeyNotShared = true;
|
|
32050
|
+
options.logger.warn(
|
|
32051
|
+
"requestState is signed with a per-process key (neither VAULT_SECRET nor JWT_SECRET is set). Multi-round tools (elicit/sample) restart when a round lands on another instance; set VAULT_SECRET (or JWT_SECRET) to the same value on every instance."
|
|
32052
|
+
);
|
|
32053
|
+
}
|
|
31707
32054
|
function computeRequestBinding(method, params) {
|
|
31708
32055
|
const salient = {};
|
|
31709
32056
|
for (const key of ["name", "uri", "arguments"]) {
|
|
@@ -31753,12 +32100,13 @@ function decodeRequestState(state, expected) {
|
|
|
31753
32100
|
if (payload.b !== expected.binding) return { ok: false, reason: "request-mismatch" };
|
|
31754
32101
|
return { ok: true, responses: payload.r };
|
|
31755
32102
|
}
|
|
31756
|
-
var DEFAULT_REQUEST_STATE_TTL_MS, MAX_REQUEST_STATE_BYTES, cachedKey, encoder, decoder;
|
|
32103
|
+
var DEFAULT_REQUEST_STATE_TTL_MS, MAX_REQUEST_STATE_BYTES, cachedKey, warnedKeyNotShared, encoder, decoder;
|
|
31757
32104
|
var init_request_state = __esm({
|
|
31758
32105
|
"libs/sdk/src/transport/mcp-20260728/request-state.ts"() {
|
|
31759
32106
|
"use strict";
|
|
31760
32107
|
DEFAULT_REQUEST_STATE_TTL_MS = 10 * 60 * 1e3;
|
|
31761
32108
|
MAX_REQUEST_STATE_BYTES = 256 * 1024;
|
|
32109
|
+
warnedKeyNotShared = false;
|
|
31762
32110
|
encoder = new TextEncoder();
|
|
31763
32111
|
decoder = new TextDecoder();
|
|
31764
32112
|
}
|
|
@@ -32486,7 +32834,8 @@ async function dispatch20260728(options) {
|
|
|
32486
32834
|
};
|
|
32487
32835
|
const carried = decodeRequestState(params["requestState"], binding);
|
|
32488
32836
|
if (!carried.ok && carried.reason !== "absent") {
|
|
32489
|
-
|
|
32837
|
+
const hint = carried.reason === "bad-signature" && getRequestStateKeySource() === "per-process" ? "requestState is signed with a per-process key; set VAULT_SECRET (or JWT_SECRET) to the same value on every instance so a round can land on any of them" : void 0;
|
|
32838
|
+
scope.logger.warn("mcp-20260728: rejected requestState", { method, reason: carried.reason, ...hint && { hint } });
|
|
32490
32839
|
}
|
|
32491
32840
|
const exchange = new MrtrExchange({
|
|
32492
32841
|
inputResponses: params["inputResponses"],
|
|
@@ -32640,6 +32989,7 @@ var McpStatelessError, DEFAULT_CLIENT_INFO, McpStatelessClient, TASKS_CLIENT_CAP
|
|
|
32640
32989
|
var init_mcp_stateless_client = __esm({
|
|
32641
32990
|
"libs/sdk/src/transport/mcp-20260728/client/mcp-stateless.client.ts"() {
|
|
32642
32991
|
"use strict";
|
|
32992
|
+
init_list_all_pages_utils();
|
|
32643
32993
|
init_header_codec();
|
|
32644
32994
|
init_protocol_20260728_constants();
|
|
32645
32995
|
init_tasks_extension();
|
|
@@ -32680,10 +33030,22 @@ var init_mcp_stateless_client = __esm({
|
|
|
32680
33030
|
* The spec requires this: one malformed tool definition must not prevent the
|
|
32681
33031
|
* other tools from being used, so the offender is excluded and logged rather
|
|
32682
33032
|
* than failing the call.
|
|
33033
|
+
*
|
|
33034
|
+
* Every page is fetched (`nextCursor` is followed to the end), since the
|
|
33035
|
+
* schemas cached here are what later `tools/call` headers are derived from.
|
|
32683
33036
|
*/
|
|
32684
33037
|
async listTools() {
|
|
32685
|
-
const
|
|
32686
|
-
|
|
33038
|
+
const tools = await listAllPages(
|
|
33039
|
+
"tools/list",
|
|
33040
|
+
async (cursor) => {
|
|
33041
|
+
const result = await this.request("tools/list", cursor ? { cursor } : {});
|
|
33042
|
+
return {
|
|
33043
|
+
items: Array.isArray(result["tools"]) ? result["tools"] : [],
|
|
33044
|
+
nextCursor: typeof result["nextCursor"] === "string" ? result["nextCursor"] : void 0
|
|
33045
|
+
};
|
|
33046
|
+
},
|
|
33047
|
+
(message) => new McpStatelessError(-32603, message)
|
|
33048
|
+
);
|
|
32687
33049
|
const usable = [];
|
|
32688
33050
|
for (const tool of tools) {
|
|
32689
33051
|
const validation = validateHeaderParams(tool["inputSchema"]);
|
|
@@ -32702,11 +33064,17 @@ var init_mcp_stateless_client = __esm({
|
|
|
32702
33064
|
async readResource(uri) {
|
|
32703
33065
|
return this.request("resources/read", { uri });
|
|
32704
33066
|
}
|
|
32705
|
-
|
|
32706
|
-
|
|
33067
|
+
/** One page of `resources/list`; pass the previous page's `nextCursor` for the next. */
|
|
33068
|
+
async listResources(cursor) {
|
|
33069
|
+
return this.request("resources/list", cursor ? { cursor } : {});
|
|
32707
33070
|
}
|
|
32708
|
-
|
|
32709
|
-
|
|
33071
|
+
/** One page of `resources/templates/list`; pass the previous page's `nextCursor` for the next. */
|
|
33072
|
+
async listResourceTemplates(cursor) {
|
|
33073
|
+
return this.request("resources/templates/list", cursor ? { cursor } : {});
|
|
33074
|
+
}
|
|
33075
|
+
/** One page of `prompts/list`; pass the previous page's `nextCursor` for the next. */
|
|
33076
|
+
async listPrompts(cursor) {
|
|
33077
|
+
return this.request("prompts/list", cursor ? { cursor } : {});
|
|
32710
33078
|
}
|
|
32711
33079
|
async getPrompt(name44, args = {}) {
|
|
32712
33080
|
return this.request("prompts/get", { name: name44, arguments: args });
|
|
@@ -32992,6 +33360,10 @@ var init_mcp_20260728 = __esm({
|
|
|
32992
33360
|
});
|
|
32993
33361
|
|
|
32994
33362
|
// libs/sdk/src/remote-mcp/mcp-stateless-client.adapter.ts
|
|
33363
|
+
function nextCursorOf(result) {
|
|
33364
|
+
const next = result["nextCursor"];
|
|
33365
|
+
return typeof next === "string" && next.length > 0 ? { nextCursor: next } : {};
|
|
33366
|
+
}
|
|
32995
33367
|
async function negotiateRemoteProtocol(url, configured, headers, fetchImpl = fetch) {
|
|
32996
33368
|
if (configured === "2026-07-28") return "2026-07-28";
|
|
32997
33369
|
if (configured !== "auto") return "legacy";
|
|
@@ -33034,16 +33406,29 @@ var init_mcp_stateless_client_adapter = __esm({
|
|
|
33034
33406
|
async callTool(params) {
|
|
33035
33407
|
return this.client.callTool(params.name, params.arguments ?? {});
|
|
33036
33408
|
}
|
|
33037
|
-
async listResources() {
|
|
33038
|
-
const result = await this.client.listResources();
|
|
33039
|
-
return {
|
|
33409
|
+
async listResources(params) {
|
|
33410
|
+
const result = await this.client.listResources(params?.cursor);
|
|
33411
|
+
return {
|
|
33412
|
+
resources: Array.isArray(result["resources"]) ? result["resources"] : [],
|
|
33413
|
+
...nextCursorOf(result)
|
|
33414
|
+
};
|
|
33415
|
+
}
|
|
33416
|
+
async listResourceTemplates(params) {
|
|
33417
|
+
const result = await this.client.listResourceTemplates(params?.cursor);
|
|
33418
|
+
return {
|
|
33419
|
+
resourceTemplates: Array.isArray(result["resourceTemplates"]) ? result["resourceTemplates"] : [],
|
|
33420
|
+
...nextCursorOf(result)
|
|
33421
|
+
};
|
|
33040
33422
|
}
|
|
33041
33423
|
async readResource(params) {
|
|
33042
33424
|
return this.client.readResource(params.uri);
|
|
33043
33425
|
}
|
|
33044
|
-
async listPrompts() {
|
|
33045
|
-
const result = await this.client.listPrompts();
|
|
33046
|
-
return {
|
|
33426
|
+
async listPrompts(params) {
|
|
33427
|
+
const result = await this.client.listPrompts(params?.cursor);
|
|
33428
|
+
return {
|
|
33429
|
+
prompts: Array.isArray(result["prompts"]) ? result["prompts"] : [],
|
|
33430
|
+
...nextCursorOf(result)
|
|
33431
|
+
};
|
|
33047
33432
|
}
|
|
33048
33433
|
async getPrompt(params) {
|
|
33049
33434
|
return this.client.getPrompt(params.name, params.arguments ?? {});
|
|
@@ -33402,7 +33787,7 @@ var init_health_check = __esm({
|
|
|
33402
33787
|
this.consecutiveSuccesses++;
|
|
33403
33788
|
this.consecutiveFailures = 0;
|
|
33404
33789
|
const previousStatus = this.status;
|
|
33405
|
-
if (this.consecutiveSuccesses >= this.options.healthyThreshold) {
|
|
33790
|
+
if (previousStatus === "unknown" || this.consecutiveSuccesses >= this.options.healthyThreshold) {
|
|
33406
33791
|
this.status = latencyMs > this.options.degradedLatencyMs ? "degraded" : "healthy";
|
|
33407
33792
|
}
|
|
33408
33793
|
this.lastResult = {
|
|
@@ -33570,6 +33955,7 @@ var DEFAULT_RETRY_OPTIONS2, DEFAULT_CIRCUIT_BREAKER_OPTIONS, DEFAULT_HEALTH_CHEC
|
|
|
33570
33955
|
var init_mcp_client_service = __esm({
|
|
33571
33956
|
"libs/sdk/src/remote-mcp/mcp-client.service.ts"() {
|
|
33572
33957
|
"use strict";
|
|
33958
|
+
init_list_all_pages_utils();
|
|
33573
33959
|
init_mcp_error();
|
|
33574
33960
|
init_remote_errors();
|
|
33575
33961
|
init_transport_errors();
|
|
@@ -34173,10 +34559,15 @@ var init_mcp_client_service = __esm({
|
|
|
34173
34559
|
const httpOptions = request.transportOptions;
|
|
34174
34560
|
return httpOptions?.fallbackToSSE ?? true;
|
|
34175
34561
|
}
|
|
34562
|
+
// Each list follows `nextCursor` to the end: a FrontMCP remote pages its lists
|
|
34563
|
+
// (40 per page by default), and proxying only the first page silently dropped
|
|
34564
|
+
// everything after it.
|
|
34176
34565
|
async listToolsInternal(connection) {
|
|
34177
34566
|
try {
|
|
34178
|
-
|
|
34179
|
-
|
|
34567
|
+
return await listAllPages("tools/list", async (cursor) => {
|
|
34568
|
+
const result = await connection.client.listTools(cursor ? { cursor } : void 0);
|
|
34569
|
+
return { items: result.tools, nextCursor: result.nextCursor };
|
|
34570
|
+
});
|
|
34180
34571
|
} catch (error) {
|
|
34181
34572
|
this.logger.warn(`Failed to list tools: ${error.message}`);
|
|
34182
34573
|
return [];
|
|
@@ -34184,8 +34575,10 @@ var init_mcp_client_service = __esm({
|
|
|
34184
34575
|
}
|
|
34185
34576
|
async listResourcesInternal(connection) {
|
|
34186
34577
|
try {
|
|
34187
|
-
|
|
34188
|
-
|
|
34578
|
+
return await listAllPages("resources/list", async (cursor) => {
|
|
34579
|
+
const result = await connection.client.listResources(cursor ? { cursor } : void 0);
|
|
34580
|
+
return { items: result.resources, nextCursor: result.nextCursor };
|
|
34581
|
+
});
|
|
34189
34582
|
} catch (error) {
|
|
34190
34583
|
this.logger.warn(`Failed to list resources: ${error.message}`);
|
|
34191
34584
|
return [];
|
|
@@ -34193,8 +34586,10 @@ var init_mcp_client_service = __esm({
|
|
|
34193
34586
|
}
|
|
34194
34587
|
async listResourceTemplatesInternal(connection) {
|
|
34195
34588
|
try {
|
|
34196
|
-
|
|
34197
|
-
|
|
34589
|
+
return await listAllPages("resources/templates/list", async (cursor) => {
|
|
34590
|
+
const result = await connection.client.listResourceTemplates(cursor ? { cursor } : void 0);
|
|
34591
|
+
return { items: result.resourceTemplates, nextCursor: result.nextCursor };
|
|
34592
|
+
});
|
|
34198
34593
|
} catch (error) {
|
|
34199
34594
|
this.logger.warn(`Failed to list resource templates: ${error.message}`);
|
|
34200
34595
|
return [];
|
|
@@ -34202,8 +34597,10 @@ var init_mcp_client_service = __esm({
|
|
|
34202
34597
|
}
|
|
34203
34598
|
async listPromptsInternal(connection) {
|
|
34204
34599
|
try {
|
|
34205
|
-
|
|
34206
|
-
|
|
34600
|
+
return await listAllPages("prompts/list", async (cursor) => {
|
|
34601
|
+
const result = await connection.client.listPrompts(cursor ? { cursor } : void 0);
|
|
34602
|
+
return { items: result.prompts, nextCursor: result.nextCursor };
|
|
34603
|
+
});
|
|
34207
34604
|
} catch (error) {
|
|
34208
34605
|
this.logger.warn(`Failed to list prompts: ${error.message}`);
|
|
34209
34606
|
return [];
|
|
@@ -41748,8 +42145,11 @@ var init_session_verify_flow = __esm({
|
|
|
41748
42145
|
const machineId = getMachineId2();
|
|
41749
42146
|
const authSignature = subject ? `${authMode}:${subject}` : authMode;
|
|
41750
42147
|
if (sessionIdHeader) {
|
|
41751
|
-
const
|
|
41752
|
-
|
|
42148
|
+
const body = this.rawInput.request.body;
|
|
42149
|
+
const isInitialize2 = typeof body === "object" && body !== null && "method" in body && body.method === "initialize";
|
|
42150
|
+
const decrypted = decryptPublicSession(sessionIdHeader, authSignature);
|
|
42151
|
+
const existingPayload = decrypted && isInitialize2 && decrypted.nodeId !== machineId ? void 0 : decrypted;
|
|
42152
|
+
if (existingPayload) {
|
|
41753
42153
|
const anonId = existingPayload.uuid ?? `${existingPayload.iat * 1e3}`;
|
|
41754
42154
|
const user2 = anonymousCallerClaims(options, anonId);
|
|
41755
42155
|
this.respond({
|
|
@@ -42209,7 +42609,7 @@ var init_well_known_jwks_flow = __esm({
|
|
|
42209
42609
|
import "reflect-metadata";
|
|
42210
42610
|
import { advertisedScopes } from "@frontmcp/auth";
|
|
42211
42611
|
import { z as z74 } from "@frontmcp/lazy-zod";
|
|
42212
|
-
import { isProduction as
|
|
42612
|
+
import { isProduction as isProduction5 } from "@frontmcp/utils";
|
|
42213
42613
|
function resolveDcrEnabled(auth) {
|
|
42214
42614
|
if (auth && isLocalMode(auth)) {
|
|
42215
42615
|
const dcr = auth.dcr;
|
|
@@ -42217,7 +42617,7 @@ function resolveDcrEnabled(auth) {
|
|
|
42217
42617
|
return dcr.enabled;
|
|
42218
42618
|
}
|
|
42219
42619
|
}
|
|
42220
|
-
return !
|
|
42620
|
+
return !isProduction5();
|
|
42221
42621
|
}
|
|
42222
42622
|
var inputSchema25, AuthServerMetadataSchema, outputSchema23, wellKnownAsStateSchema, wellKnownAsPlan, name24, Stage24, WellKnownAsFlow;
|
|
42223
42623
|
var init_well_known_oauth_authorization_server_flow = __esm({
|
|
@@ -42619,8 +43019,8 @@ import {
|
|
|
42619
43019
|
verifyPkce
|
|
42620
43020
|
} from "@frontmcp/auth";
|
|
42621
43021
|
import {
|
|
42622
|
-
getEnv as
|
|
42623
|
-
isProduction as
|
|
43022
|
+
getEnv as getEnv5,
|
|
43023
|
+
isProduction as isProduction6,
|
|
42624
43024
|
MemoryStorageAdapter,
|
|
42625
43025
|
randomBytes as randomBytes8,
|
|
42626
43026
|
randomUUID as randomUUID21,
|
|
@@ -42716,26 +43116,28 @@ var init_instance_local_primary_auth = __esm({
|
|
|
42716
43116
|
this.providers = providers;
|
|
42717
43117
|
this.logger = this.providers.getActiveScope().logger.child("LocalPrimaryAuth");
|
|
42718
43118
|
this.port = this.providers.getActiveScope().metadata.http?.port ?? 3001;
|
|
42719
|
-
this.host =
|
|
43119
|
+
this.host = getEnv5("FRONTMCP_PUBLIC_HOST")?.trim() || "localhost";
|
|
42720
43120
|
this.issuer = this.deriveIssuer(options);
|
|
42721
|
-
this.publicHostPinned = !!
|
|
42722
|
-
const jwtSecret =
|
|
43121
|
+
this.publicHostPinned = !!getEnv5("FRONTMCP_PUBLIC_HOST")?.trim();
|
|
43122
|
+
const jwtSecret = getEnv5("JWT_SECRET");
|
|
42723
43123
|
const hasJwtSecret = !!jwtSecret?.trim();
|
|
42724
43124
|
if (hasJwtSecret && jwtSecret) {
|
|
42725
43125
|
const encoded = new TextEncoder().encode(jwtSecret);
|
|
42726
43126
|
if (encoded.length < MIN_HS256_SECRET_BYTES) {
|
|
42727
|
-
if (
|
|
43127
|
+
if (isProduction6()) throw new JwtSecretWeakError(encoded.length);
|
|
42728
43128
|
this.logger.warn(
|
|
42729
43129
|
`JWT_SECRET is ${encoded.length} bytes; HS256 wants at least ${MIN_HS256_SECRET_BYTES} (RFC 7518). This is refused in production \u2014 generate one with \`openssl rand -hex 32\`.`
|
|
42730
43130
|
);
|
|
42731
43131
|
}
|
|
42732
43132
|
this.secret = encoded;
|
|
42733
|
-
} else if (
|
|
43133
|
+
} else if (isProduction6() && isOrchestratedMode(options)) {
|
|
42734
43134
|
throw new JwtSecretRequiredError(options.mode);
|
|
42735
43135
|
} else {
|
|
42736
|
-
|
|
42737
|
-
|
|
42738
|
-
|
|
43136
|
+
if (!isPublicMode(options)) {
|
|
43137
|
+
this.logger.warn(
|
|
43138
|
+
"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."
|
|
43139
|
+
);
|
|
43140
|
+
}
|
|
42739
43141
|
this.secret = getDefaultNoAuthSecret();
|
|
42740
43142
|
}
|
|
42741
43143
|
this.tokenStorage = this.readTokenStorage(options);
|
|
@@ -42874,7 +43276,7 @@ var init_instance_local_primary_auth = __esm({
|
|
|
42874
43276
|
if (dcr && typeof dcr.enabled === "boolean") {
|
|
42875
43277
|
return dcr.enabled;
|
|
42876
43278
|
}
|
|
42877
|
-
return !
|
|
43279
|
+
return !isProduction6();
|
|
42878
43280
|
}
|
|
42879
43281
|
/** Provider configurations (indexed by provider ID) */
|
|
42880
43282
|
providerConfigs = /* @__PURE__ */ new Map();
|
|
@@ -43103,7 +43505,7 @@ var init_instance_local_primary_auth = __esm({
|
|
|
43103
43505
|
await memory.connect();
|
|
43104
43506
|
storage = memory;
|
|
43105
43507
|
}
|
|
43106
|
-
const pepper =
|
|
43508
|
+
const pepper = getEnv5("VAULT_SECRET") ?? getEnv5("JWT_SECRET") ?? void 0;
|
|
43107
43509
|
this.credentialVaultImpl = new SessionCredentialVault({
|
|
43108
43510
|
storage,
|
|
43109
43511
|
pepper,
|
|
@@ -43154,7 +43556,7 @@ var init_instance_local_primary_auth = __esm({
|
|
|
43154
43556
|
}
|
|
43155
43557
|
const config = this.readSecureStoreConfig();
|
|
43156
43558
|
const reuseAdapter = this.storageAdapter && this.secureStoreSharesTokenStorage(config) ? this.storageAdapter : void 0;
|
|
43157
|
-
const pepper =
|
|
43559
|
+
const pepper = getEnv5("VAULT_SECRET") ?? getEnv5("JWT_SECRET") ?? new TextDecoder().decode(this.secret);
|
|
43158
43560
|
const resolved = await createSecureStore({
|
|
43159
43561
|
config,
|
|
43160
43562
|
pepper,
|
|
@@ -46231,10 +46633,10 @@ import {
|
|
|
46231
46633
|
randomBytes as randomBytes9,
|
|
46232
46634
|
base64urlEncode as base64urlEncode6,
|
|
46233
46635
|
base64urlDecode as base64urlDecode3,
|
|
46234
|
-
getEnv as
|
|
46636
|
+
getEnv as getEnv6
|
|
46235
46637
|
} from "@frontmcp/utils";
|
|
46236
46638
|
function getElicitationSecret() {
|
|
46237
|
-
return
|
|
46639
|
+
return getEnv6("MCP_ELICITATION_SECRET") || getEnv6("MCP_SESSION_SECRET") || getEnv6("MCP_SERVER_SECRET") || null;
|
|
46238
46640
|
}
|
|
46239
46641
|
function isElicitationEncryptionAvailable() {
|
|
46240
46642
|
return getElicitationSecret() !== null;
|
|
@@ -46711,12 +47113,19 @@ var init_encrypted_elicitation_store = __esm({
|
|
|
46711
47113
|
});
|
|
46712
47114
|
|
|
46713
47115
|
// libs/sdk/src/elicitation/store/elicitation-store.factory.ts
|
|
46714
|
-
import { createMemoryStorage as createMemoryStorage2, createStorage as createStorage2, getEnv as
|
|
46715
|
-
function detectStorageType2(storage) {
|
|
46716
|
-
if (
|
|
47116
|
+
import { createMemoryStorage as createMemoryStorage2, createStorage as createStorage2, getEnv as getEnv7 } from "@frontmcp/utils";
|
|
47117
|
+
function detectStorageType2(storage, configuredType) {
|
|
47118
|
+
if (configuredType === "redis" || configuredType === "upstash" || configuredType === "memory") {
|
|
47119
|
+
return configuredType;
|
|
47120
|
+
}
|
|
47121
|
+
const backend = storage.type ?? storage.backendName;
|
|
47122
|
+
if (backend === "redis" || backend === "upstash" || backend === "memory") {
|
|
47123
|
+
return backend;
|
|
47124
|
+
}
|
|
47125
|
+
if (getEnv7("UPSTASH_REDIS_REST_URL")) {
|
|
46717
47126
|
return "upstash";
|
|
46718
47127
|
}
|
|
46719
|
-
if (
|
|
47128
|
+
if (getEnv7("REDIS_URL") || getEnv7("REDIS_HOST")) {
|
|
46720
47129
|
return "redis";
|
|
46721
47130
|
}
|
|
46722
47131
|
return "memory";
|
|
@@ -46802,7 +47211,8 @@ async function createElicitationStore(options = {}) {
|
|
|
46802
47211
|
});
|
|
46803
47212
|
}
|
|
46804
47213
|
}
|
|
46805
|
-
|
|
47214
|
+
const explicitBackend = Boolean(storageConfig || redis);
|
|
47215
|
+
if (finalStorageConfig?.type === "vercel-kv" || !explicitBackend && getEnv7("KV_REST_API_URL")) {
|
|
46806
47216
|
throw new ElicitationNotSupportedError(
|
|
46807
47217
|
"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."
|
|
46808
47218
|
);
|
|
@@ -46823,7 +47233,7 @@ async function createElicitationStore(options = {}) {
|
|
|
46823
47233
|
);
|
|
46824
47234
|
}
|
|
46825
47235
|
let store = new StorageElicitationStore(storage, logger);
|
|
46826
|
-
const type = detectStorageType2(storage);
|
|
47236
|
+
const type = detectStorageType2(storage, finalStorageConfig?.type);
|
|
46827
47237
|
const encryptionEnabled = encryption?.enabled ?? "auto";
|
|
46828
47238
|
const encryptionSecret = encryption?.secret;
|
|
46829
47239
|
const hasSecret = encryptionSecret || isElicitationEncryptionAvailable();
|
|
@@ -49635,6 +50045,7 @@ var init_redis_job_definition_store = __esm({
|
|
|
49635
50045
|
});
|
|
49636
50046
|
|
|
49637
50047
|
// libs/sdk/src/job/store/job-definition-store.factory.ts
|
|
50048
|
+
import { attachRedisErrorListener } from "@frontmcp/utils";
|
|
49638
50049
|
function createJobDefinitionStore(options, logger) {
|
|
49639
50050
|
const keyPrefix = options?.keyPrefix ?? "mcp:jobs:def:";
|
|
49640
50051
|
const effectiveLogger = logger ?? noopLogger2;
|
|
@@ -49646,6 +50057,7 @@ function createJobDefinitionStore(options, logger) {
|
|
|
49646
50057
|
host: options.redis.host ?? "localhost",
|
|
49647
50058
|
port: options.redis.port ?? 6379
|
|
49648
50059
|
});
|
|
50060
|
+
attachRedisErrorListener(client, { label: "JobDefinitionStore", logger: effectiveLogger });
|
|
49649
50061
|
return {
|
|
49650
50062
|
// Factory-created client lifetime is bound to the store, so the store
|
|
49651
50063
|
// must close it on dispose() — pass ownsClient: true.
|
|
@@ -49846,6 +50258,7 @@ var init_redis_job_state_store = __esm({
|
|
|
49846
50258
|
});
|
|
49847
50259
|
|
|
49848
50260
|
// libs/sdk/src/job/store/job-state-store.factory.ts
|
|
50261
|
+
import { attachRedisErrorListener as attachRedisErrorListener2 } from "@frontmcp/utils";
|
|
49849
50262
|
function createJobStateStore(options, logger) {
|
|
49850
50263
|
const keyPrefix = options?.keyPrefix ?? "mcp:jobs:";
|
|
49851
50264
|
const ttlSeconds = options?.ttlSeconds ?? 86400;
|
|
@@ -49857,6 +50270,7 @@ function createJobStateStore(options, logger) {
|
|
|
49857
50270
|
host: options.redis.host ?? "localhost",
|
|
49858
50271
|
port: options.redis.port ?? 6379
|
|
49859
50272
|
});
|
|
50273
|
+
attachRedisErrorListener2(client, { label: "JobStateStore", logger });
|
|
49860
50274
|
return {
|
|
49861
50275
|
store: new RedisJobStateStore2(client, logger, keyPrefix, ttlSeconds),
|
|
49862
50276
|
type: "redis"
|
|
@@ -52272,6 +52686,7 @@ var init_skill_http_cache = __esm({
|
|
|
52272
52686
|
});
|
|
52273
52687
|
|
|
52274
52688
|
// libs/sdk/src/skill/cache/skill-http-cache.factory.ts
|
|
52689
|
+
import { attachRedisErrorListener as attachRedisErrorListener3 } from "@frontmcp/utils";
|
|
52275
52690
|
function hasRedisProvider(redis) {
|
|
52276
52691
|
if (!redis?.provider) return false;
|
|
52277
52692
|
const provider = redis.provider;
|
|
@@ -52316,6 +52731,7 @@ async function createRedisCache(redis, keyPrefix, ttlMs, logger) {
|
|
|
52316
52731
|
enableReadyCheck: false,
|
|
52317
52732
|
maxRetriesPerRequest: 3
|
|
52318
52733
|
});
|
|
52734
|
+
attachRedisErrorListener3(client, { label: "SkillHttpCache" });
|
|
52319
52735
|
await client.connect();
|
|
52320
52736
|
return new RedisSkillHttpCache({
|
|
52321
52737
|
getClient: async () => client,
|
|
@@ -56131,6 +56547,51 @@ var init_skill_scope_helper = __esm({
|
|
|
56131
56547
|
}
|
|
56132
56548
|
});
|
|
56133
56549
|
|
|
56550
|
+
// libs/sdk/src/tool/ui/ui-option-warnings.ts
|
|
56551
|
+
function describeIgnoredUiOptions(toolName, ui) {
|
|
56552
|
+
if (!ui) return [];
|
|
56553
|
+
const messages = [];
|
|
56554
|
+
const ignored = IGNORED_UI_OPTIONS.filter((key) => ui[key] !== void 0);
|
|
56555
|
+
if (ignored.length > 0) {
|
|
56556
|
+
messages.push(
|
|
56557
|
+
`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.`
|
|
56558
|
+
);
|
|
56559
|
+
}
|
|
56560
|
+
const mode = ui["servingMode"];
|
|
56561
|
+
if (UNSUPPORTED_SERVING_MODES.includes(mode)) {
|
|
56562
|
+
messages.push(
|
|
56563
|
+
`Tool "${toolName}": \`ui.servingMode: '${String(mode)}'\` is not implemented; the widget is served inline, as with \`servingMode: 'inline'\`.`
|
|
56564
|
+
);
|
|
56565
|
+
}
|
|
56566
|
+
if (mode === "hybrid") {
|
|
56567
|
+
messages.push(
|
|
56568
|
+
`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.`
|
|
56569
|
+
);
|
|
56570
|
+
}
|
|
56571
|
+
return messages;
|
|
56572
|
+
}
|
|
56573
|
+
var IGNORED_UI_OPTIONS, UNSUPPORTED_SERVING_MODES;
|
|
56574
|
+
var init_ui_option_warnings = __esm({
|
|
56575
|
+
"libs/sdk/src/tool/ui/ui-option-warnings.ts"() {
|
|
56576
|
+
"use strict";
|
|
56577
|
+
IGNORED_UI_OPTIONS = [
|
|
56578
|
+
"widgetDescription",
|
|
56579
|
+
"widgetAccessible",
|
|
56580
|
+
"displayMode",
|
|
56581
|
+
"prefersBorder",
|
|
56582
|
+
"sandboxDomain",
|
|
56583
|
+
"contentSecurity",
|
|
56584
|
+
"hydrate",
|
|
56585
|
+
"runtimeOptions",
|
|
56586
|
+
"mdxComponents",
|
|
56587
|
+
"bundlingMode",
|
|
56588
|
+
"uiType",
|
|
56589
|
+
"htmlResponsePrefix"
|
|
56590
|
+
];
|
|
56591
|
+
UNSUPPORTED_SERVING_MODES = ["direct-url", "custom-url"];
|
|
56592
|
+
}
|
|
56593
|
+
});
|
|
56594
|
+
|
|
56134
56595
|
// libs/sdk/src/transport/bus/redis-transport-bus.ts
|
|
56135
56596
|
var DEFAULT_BUS_PREFIX, DEFAULT_BUS_TTL_SECONDS, REVOKE_LUA, RedisTransportBus;
|
|
56136
56597
|
var init_redis_transport_bus = __esm({
|
|
@@ -56260,6 +56721,7 @@ var redis_event_store_exports = {};
|
|
|
56260
56721
|
__export(redis_event_store_exports, {
|
|
56261
56722
|
RedisEventStore: () => RedisEventStore
|
|
56262
56723
|
});
|
|
56724
|
+
import { attachRedisErrorListener as attachRedisErrorListener4 } from "@frontmcp/utils";
|
|
56263
56725
|
var RedisEventStore;
|
|
56264
56726
|
var init_redis_event_store = __esm({
|
|
56265
56727
|
"libs/sdk/src/transport/event-stores/redis.event-store.ts"() {
|
|
@@ -56307,6 +56769,9 @@ var init_redis_event_store = __esm({
|
|
|
56307
56769
|
enableReadyCheck: false,
|
|
56308
56770
|
maxRetriesPerRequest: 3
|
|
56309
56771
|
});
|
|
56772
|
+
attachRedisErrorListener4(this.client, {
|
|
56773
|
+
label: "RedisEventStore"
|
|
56774
|
+
});
|
|
56310
56775
|
await this.client.connect();
|
|
56311
56776
|
}
|
|
56312
56777
|
return this.client;
|
|
@@ -56959,7 +57424,7 @@ var init_handle_mcp_20260728_flow = __esm({
|
|
|
56959
57424
|
const sink = new RequestNotificationSink(logLevel, progressToken);
|
|
56960
57425
|
const clientInfo = toClientInfo(meta[MCP_20260728_META5.clientInfo]);
|
|
56961
57426
|
if (clientInfo) {
|
|
56962
|
-
const platformType = detectAIPlatform(clientInfo, this.scope.metadata.transport?.platformDetection);
|
|
57427
|
+
const platformType = detectPlatformFromCapabilities(clientCapabilities) ?? detectAIPlatform(clientInfo, this.scope.metadata.transport?.platformDetection);
|
|
56963
57428
|
this.tryGetContext()?.setClientInfo(clientInfo, platformType);
|
|
56964
57429
|
}
|
|
56965
57430
|
const dispatchOptions = {
|
|
@@ -57057,6 +57522,28 @@ var init_handle_mcp_20260728_flow = __esm({
|
|
|
57057
57522
|
}
|
|
57058
57523
|
});
|
|
57059
57524
|
|
|
57525
|
+
// libs/sdk/src/ha/ha-headers.ts
|
|
57526
|
+
import { buildSetCookie, getMachineId as getMachineId3, getRuntimeContext as getRuntimeContext13 } from "@frontmcp/utils";
|
|
57527
|
+
function applyMachineIdHeader(response) {
|
|
57528
|
+
if (getRuntimeContext13().deployment !== "distributed") return false;
|
|
57529
|
+
response.setHeader(DEFAULT_FRONTMCP_MACHINE_ID_HEADER, getMachineId3());
|
|
57530
|
+
return true;
|
|
57531
|
+
}
|
|
57532
|
+
function applyNodeAffinity(response, request) {
|
|
57533
|
+
if (!applyMachineIdHeader(response)) return;
|
|
57534
|
+
const cookie = buildSetCookie({ name: DEFAULT_FRONTMCP_NODE_COOKIE, value: getMachineId3() }, request);
|
|
57535
|
+
if (!cookie) return;
|
|
57536
|
+
const existing = response.getHeader("Set-Cookie");
|
|
57537
|
+
const existingArr = Array.isArray(existing) ? existing : existing ? [String(existing)] : [];
|
|
57538
|
+
response.setHeader("Set-Cookie", [...existingArr, cookie]);
|
|
57539
|
+
}
|
|
57540
|
+
var init_ha_headers = __esm({
|
|
57541
|
+
"libs/sdk/src/ha/ha-headers.ts"() {
|
|
57542
|
+
"use strict";
|
|
57543
|
+
init_ha_constants();
|
|
57544
|
+
}
|
|
57545
|
+
});
|
|
57546
|
+
|
|
57060
57547
|
// libs/sdk/src/skill/skill-mode.utils.ts
|
|
57061
57548
|
function detectSkillsOnlyMode(query) {
|
|
57062
57549
|
if (!query) return false;
|
|
@@ -57077,7 +57564,7 @@ var init_skill_mode_utils = __esm({
|
|
|
57077
57564
|
|
|
57078
57565
|
// libs/sdk/src/transport/flows/handle.sse.flow.ts
|
|
57079
57566
|
import { z as z103 } from "@frontmcp/lazy-zod";
|
|
57080
|
-
import {
|
|
57567
|
+
import { getMachineId as getMachineId4, getRuntimeContext as getRuntimeContext14 } from "@frontmcp/utils";
|
|
57081
57568
|
function getQuerySessionId2(urlPath) {
|
|
57082
57569
|
if (!urlPath) return void 0;
|
|
57083
57570
|
try {
|
|
@@ -57087,6 +57574,30 @@ function getQuerySessionId2(urlPath) {
|
|
|
57087
57574
|
return void 0;
|
|
57088
57575
|
}
|
|
57089
57576
|
}
|
|
57577
|
+
function resolveLegacySseSession(params) {
|
|
57578
|
+
const { rawHeader, requestUrl, authorizationSession, createSession } = params;
|
|
57579
|
+
const rawMcpSessionHeader = typeof rawHeader === "string" ? rawHeader : void 0;
|
|
57580
|
+
const mcpSessionHeader = validateMcpSessionHeader(rawMcpSessionHeader);
|
|
57581
|
+
const querySessionId = getQuerySessionId2(requestUrl);
|
|
57582
|
+
const validatedQuerySessionId = querySessionId ? validateMcpSessionHeader(querySessionId) : void 0;
|
|
57583
|
+
if (rawHeader !== void 0 && !mcpSessionHeader) {
|
|
57584
|
+
return { responded404: true, createdNew: false };
|
|
57585
|
+
}
|
|
57586
|
+
if (querySessionId !== void 0 && !validatedQuerySessionId) {
|
|
57587
|
+
return { responded404: true, createdNew: false };
|
|
57588
|
+
}
|
|
57589
|
+
const effectiveSessionId = mcpSessionHeader ?? validatedQuerySessionId;
|
|
57590
|
+
if (effectiveSessionId) {
|
|
57591
|
+
if (authorizationSession?.id === effectiveSessionId) {
|
|
57592
|
+
return { session: authorizationSession, createdNew: false, responded404: false };
|
|
57593
|
+
}
|
|
57594
|
+
return { responded404: true, createdNew: false };
|
|
57595
|
+
}
|
|
57596
|
+
if (authorizationSession) {
|
|
57597
|
+
return { session: authorizationSession, createdNew: false, responded404: false };
|
|
57598
|
+
}
|
|
57599
|
+
return { session: createSession(), createdNew: true, responded404: false };
|
|
57600
|
+
}
|
|
57090
57601
|
var plan38, stateSessionSchema, stateSchema38, name39, Stage39, HandleSseFlow;
|
|
57091
57602
|
var init_handle_sse_flow = __esm({
|
|
57092
57603
|
"libs/sdk/src/transport/flows/handle.sse.flow.ts"() {
|
|
@@ -57094,7 +57605,7 @@ var init_handle_sse_flow = __esm({
|
|
|
57094
57605
|
init_session_id_utils();
|
|
57095
57606
|
init_common();
|
|
57096
57607
|
init_errors();
|
|
57097
|
-
|
|
57608
|
+
init_ha_headers();
|
|
57098
57609
|
init_skill_mode_utils();
|
|
57099
57610
|
plan38 = {
|
|
57100
57611
|
pre: ["parseInput", "router"],
|
|
@@ -57126,39 +57637,26 @@ var init_handle_sse_flow = __esm({
|
|
|
57126
57637
|
const { request } = this.rawInput;
|
|
57127
57638
|
const authorization = request[ServerRequestTokens.auth];
|
|
57128
57639
|
const { token } = authorization;
|
|
57129
|
-
const raw = request.headers?.["mcp-session-id"];
|
|
57130
|
-
const rawMcpSessionHeader = typeof raw === "string" ? raw : void 0;
|
|
57131
|
-
const mcpSessionHeader = validateMcpSessionHeader(rawMcpSessionHeader);
|
|
57132
57640
|
const anyReq = request;
|
|
57133
|
-
const
|
|
57134
|
-
|
|
57135
|
-
|
|
57136
|
-
|
|
57137
|
-
|
|
57138
|
-
|
|
57139
|
-
|
|
57641
|
+
const resolution = resolveLegacySseSession({
|
|
57642
|
+
rawHeader: request.headers?.["mcp-session-id"],
|
|
57643
|
+
requestUrl: anyReq.url ?? anyReq.path,
|
|
57644
|
+
authorizationSession: authorization.session,
|
|
57645
|
+
createSession: () => {
|
|
57646
|
+
const query = request.query;
|
|
57647
|
+
const skillsOnlyMode = detectSkillsOnlyMode(query);
|
|
57648
|
+
return createSessionId("legacy-sse", token, {
|
|
57649
|
+
userAgent: request.headers?.["user-agent"],
|
|
57650
|
+
platformDetectionConfig: this.scope.metadata.transport?.platformDetection,
|
|
57651
|
+
skillsOnlyMode
|
|
57652
|
+
});
|
|
57653
|
+
}
|
|
57654
|
+
});
|
|
57655
|
+
if (resolution.responded404 || !resolution.session) {
|
|
57140
57656
|
this.respond(httpRespond.sessionNotFound("invalid session id"));
|
|
57141
57657
|
return;
|
|
57142
57658
|
}
|
|
57143
|
-
const
|
|
57144
|
-
let session;
|
|
57145
|
-
if (effectiveSessionId) {
|
|
57146
|
-
if (authorization.session?.id === effectiveSessionId) {
|
|
57147
|
-
session = authorization.session;
|
|
57148
|
-
} else {
|
|
57149
|
-
session = { id: effectiveSessionId };
|
|
57150
|
-
}
|
|
57151
|
-
} else if (authorization.session) {
|
|
57152
|
-
session = authorization.session;
|
|
57153
|
-
} else {
|
|
57154
|
-
const query = request.query;
|
|
57155
|
-
const skillsOnlyMode = detectSkillsOnlyMode(query);
|
|
57156
|
-
session = createSessionId("legacy-sse", token, {
|
|
57157
|
-
userAgent: request.headers?.["user-agent"],
|
|
57158
|
-
platformDetectionConfig: this.scope.metadata.transport?.platformDetection,
|
|
57159
|
-
skillsOnlyMode
|
|
57160
|
-
});
|
|
57161
|
-
}
|
|
57659
|
+
const session = resolution.session;
|
|
57162
57660
|
this.state.set(stateSchema38.parse({ token, session }));
|
|
57163
57661
|
}
|
|
57164
57662
|
async router() {
|
|
@@ -57181,16 +57679,7 @@ var init_handle_sse_flow = __esm({
|
|
|
57181
57679
|
const { request, response } = this.rawInput;
|
|
57182
57680
|
const { token, session } = this.state.required;
|
|
57183
57681
|
const transport = await transportService.createTransporter("sse", token, session.id, response);
|
|
57184
|
-
|
|
57185
|
-
const nodeId = getMachineId3();
|
|
57186
|
-
response.setHeader(DEFAULT_FRONTMCP_MACHINE_ID_HEADER, nodeId);
|
|
57187
|
-
const cookie = buildSetCookie({ name: DEFAULT_FRONTMCP_NODE_COOKIE, value: nodeId }, request);
|
|
57188
|
-
if (cookie) {
|
|
57189
|
-
const existing = response.getHeader("Set-Cookie");
|
|
57190
|
-
const existingArr = Array.isArray(existing) ? existing : existing ? [String(existing)] : [];
|
|
57191
|
-
response.setHeader("Set-Cookie", [...existingArr, cookie]);
|
|
57192
|
-
}
|
|
57193
|
-
}
|
|
57682
|
+
applyNodeAffinity(response, request);
|
|
57194
57683
|
await transport.initialize(request, response);
|
|
57195
57684
|
this.handled();
|
|
57196
57685
|
}
|
|
@@ -57206,12 +57695,12 @@ var init_handle_sse_flow = __esm({
|
|
|
57206
57695
|
const { request, response } = this.rawInput;
|
|
57207
57696
|
const { token, session } = this.state.required;
|
|
57208
57697
|
const transport = await transportService.getTransporter("sse", token, session.id);
|
|
57209
|
-
if (!transport &&
|
|
57698
|
+
if (!transport && getRuntimeContext14().deployment === "distributed") {
|
|
57210
57699
|
const storedSession = await transportService.getStoredSession("sse", token, session.id);
|
|
57211
57700
|
if (storedSession) {
|
|
57212
57701
|
const haManager = this.scope.haManager;
|
|
57213
57702
|
const relay = haManager?.getRelay();
|
|
57214
|
-
if (relay && storedSession.session.nodeId && storedSession.session.nodeId !==
|
|
57703
|
+
if (relay && storedSession.session.nodeId && storedSession.session.nodeId !== getMachineId4()) {
|
|
57215
57704
|
const isAlive2 = await haManager.isNodeAlive(storedSession.session.nodeId);
|
|
57216
57705
|
if (isAlive2) {
|
|
57217
57706
|
try {
|
|
@@ -57306,9 +57795,10 @@ var init_handle_stateless_http_flow = __esm({
|
|
|
57306
57795
|
"use strict";
|
|
57307
57796
|
init_common();
|
|
57308
57797
|
init_errors();
|
|
57798
|
+
init_ha_headers();
|
|
57309
57799
|
init_transport_types();
|
|
57310
57800
|
plan39 = {
|
|
57311
|
-
pre: ["parseInput", "router"],
|
|
57801
|
+
pre: ["applyNodeHeaders", "parseInput", "router"],
|
|
57312
57802
|
execute: ["handleRequest"],
|
|
57313
57803
|
post: [],
|
|
57314
57804
|
finalize: ["cleanup"]
|
|
@@ -57322,6 +57812,9 @@ var init_handle_stateless_http_flow = __esm({
|
|
|
57322
57812
|
({ Stage: Stage40 } = FlowHooksOf(name40));
|
|
57323
57813
|
HandleStatelessHttpFlow = class extends FlowBase {
|
|
57324
57814
|
name = name40;
|
|
57815
|
+
async applyNodeHeaders() {
|
|
57816
|
+
applyMachineIdHeader(this.rawInput.response);
|
|
57817
|
+
}
|
|
57325
57818
|
async parseInput() {
|
|
57326
57819
|
const { request } = this.rawInput;
|
|
57327
57820
|
const logger = this.scope.logger.child("HandleStatelessHttpFlow");
|
|
@@ -57383,6 +57876,9 @@ var init_handle_stateless_http_flow = __esm({
|
|
|
57383
57876
|
this.handled();
|
|
57384
57877
|
}
|
|
57385
57878
|
};
|
|
57879
|
+
__decorateClass([
|
|
57880
|
+
Stage40("applyNodeHeaders")
|
|
57881
|
+
], HandleStatelessHttpFlow.prototype, "applyNodeHeaders", 1);
|
|
57386
57882
|
__decorateClass([
|
|
57387
57883
|
Stage40("parseInput")
|
|
57388
57884
|
], HandleStatelessHttpFlow.prototype, "parseInput", 1);
|
|
@@ -57762,7 +58258,6 @@ var init_ext_apps2 = __esm({
|
|
|
57762
58258
|
// libs/sdk/src/transport/flows/handle.streamable-http.flow.ts
|
|
57763
58259
|
import { z as z105 } from "@frontmcp/lazy-zod";
|
|
57764
58260
|
import { CallToolResultSchema as CallToolResultSchema4, ElicitResultSchema, RequestSchema as RequestSchema2 } from "@frontmcp/protocol";
|
|
57765
|
-
import { buildSetCookie as buildSetCookie2, getMachineId as getMachineId4, getRuntimeContext as getRuntimeContext14 } from "@frontmcp/utils";
|
|
57766
58261
|
function resolveStreamableHttpSession(params) {
|
|
57767
58262
|
const { rawHeader, authorizationSession, createSession } = params;
|
|
57768
58263
|
const rawMcpSessionHeader = typeof rawHeader === "string" ? rawHeader : void 0;
|
|
@@ -57774,7 +58269,7 @@ function resolveStreamableHttpSession(params) {
|
|
|
57774
58269
|
if (authorizationSession?.id === mcpSessionHeader) {
|
|
57775
58270
|
return { session: authorizationSession, createdNew: false, responded404: false };
|
|
57776
58271
|
}
|
|
57777
|
-
return {
|
|
58272
|
+
return { responded404: true, createdNew: false };
|
|
57778
58273
|
}
|
|
57779
58274
|
if (authorizationSession) {
|
|
57780
58275
|
return { session: authorizationSession, createdNew: false, responded404: false };
|
|
@@ -57858,11 +58353,11 @@ var init_handle_streamable_http_flow = __esm({
|
|
|
57858
58353
|
init_common();
|
|
57859
58354
|
init_errors();
|
|
57860
58355
|
init_ext_apps2();
|
|
57861
|
-
|
|
58356
|
+
init_ha_headers();
|
|
57862
58357
|
init_skill_mode_utils();
|
|
57863
58358
|
init_mcp_surface();
|
|
57864
58359
|
plan40 = {
|
|
57865
|
-
pre: ["parseInput", "router"],
|
|
58360
|
+
pre: ["applyNodeHeaders", "parseInput", "router"],
|
|
57866
58361
|
execute: ["onInitialize", "onMessage", "onElicitResult", "onSseListener", "onExtApps"],
|
|
57867
58362
|
post: [],
|
|
57868
58363
|
finalize: ["cleanup"]
|
|
@@ -57888,6 +58383,9 @@ var init_handle_streamable_http_flow = __esm({
|
|
|
57888
58383
|
({ Stage: Stage41 } = FlowHooksOf(name41));
|
|
57889
58384
|
HandleStreamableHttpFlow = class extends FlowBase {
|
|
57890
58385
|
name = name41;
|
|
58386
|
+
async applyNodeHeaders() {
|
|
58387
|
+
applyMachineIdHeader(this.rawInput.response);
|
|
58388
|
+
}
|
|
57891
58389
|
async parseInput() {
|
|
57892
58390
|
const { request } = this.rawInput;
|
|
57893
58391
|
const authorization = request[ServerRequestTokens.auth];
|
|
@@ -57919,7 +58417,7 @@ var init_handle_streamable_http_flow = __esm({
|
|
|
57919
58417
|
});
|
|
57920
58418
|
}
|
|
57921
58419
|
if (sessionResolution.responded404 || !sessionResolution.session) {
|
|
57922
|
-
logger.warn("parseInput:
|
|
58420
|
+
logger.warn("parseInput: mcp-session-id is not a session this server verified");
|
|
57923
58421
|
this.respond(httpRespond.sessionNotFound("invalid session id"));
|
|
57924
58422
|
return;
|
|
57925
58423
|
}
|
|
@@ -57986,16 +58484,7 @@ var init_handle_streamable_http_flow = __esm({
|
|
|
57986
58484
|
return;
|
|
57987
58485
|
}
|
|
57988
58486
|
}
|
|
57989
|
-
|
|
57990
|
-
const nodeId = getMachineId4();
|
|
57991
|
-
response.setHeader(DEFAULT_FRONTMCP_MACHINE_ID_HEADER, nodeId);
|
|
57992
|
-
const cookie = buildSetCookie2({ name: DEFAULT_FRONTMCP_NODE_COOKIE, value: nodeId }, request);
|
|
57993
|
-
if (cookie) {
|
|
57994
|
-
const existing = response.getHeader("Set-Cookie");
|
|
57995
|
-
const existingArr = Array.isArray(existing) ? existing : existing ? [String(existing)] : [];
|
|
57996
|
-
response.setHeader("Set-Cookie", [...existingArr, cookie]);
|
|
57997
|
-
}
|
|
57998
|
-
}
|
|
58487
|
+
applyNodeAffinity(response, request);
|
|
57999
58488
|
logger.info("onInitialize: transport created, calling initialize");
|
|
58000
58489
|
await transport.initialize(request, response);
|
|
58001
58490
|
logger.info("onInitialize: completed successfully");
|
|
@@ -58178,6 +58667,17 @@ var init_handle_streamable_http_flow = __esm({
|
|
|
58178
58667
|
const logger = this.scopeLogger.child("handle:streamable-http:onExtApps");
|
|
58179
58668
|
const { request, response } = this.rawInput;
|
|
58180
58669
|
const { token, session } = this.state.required;
|
|
58670
|
+
if (this.scope.metadata.extApps?.enabled === false) {
|
|
58671
|
+
const rpc = request.body;
|
|
58672
|
+
logger.verbose("onExtApps: ext-apps disabled by config", { method: rpc?.method });
|
|
58673
|
+
response.status(200).json({
|
|
58674
|
+
jsonrpc: "2.0",
|
|
58675
|
+
id: rpc?.id ?? null,
|
|
58676
|
+
error: { code: -32601, message: `Method not found: ${rpc?.method}` }
|
|
58677
|
+
});
|
|
58678
|
+
this.handled();
|
|
58679
|
+
return;
|
|
58680
|
+
}
|
|
58181
58681
|
logger.info("onExtApps: starting", {
|
|
58182
58682
|
sessionId: session.id?.slice(0, 20),
|
|
58183
58683
|
method: request.body?.method
|
|
@@ -58229,8 +58729,20 @@ var init_handle_streamable_http_flow = __esm({
|
|
|
58229
58729
|
const hostCapabilities = {
|
|
58230
58730
|
serverToolProxy: configuredCapabilities?.serverToolProxy ?? true,
|
|
58231
58731
|
logging: configuredCapabilities?.logging ?? true,
|
|
58232
|
-
...configuredCapabilities
|
|
58732
|
+
...configuredCapabilities,
|
|
58733
|
+
// Only advertise what this host can serve: the context below provides callTool and nothing else
|
|
58734
|
+
openLink: false,
|
|
58735
|
+
modelContextUpdate: false,
|
|
58736
|
+
widgetTools: false
|
|
58233
58737
|
};
|
|
58738
|
+
const unsupported = ["openLink", "modelContextUpdate", "widgetTools"].filter(
|
|
58739
|
+
(key) => configuredCapabilities?.[key] === true
|
|
58740
|
+
);
|
|
58741
|
+
if (unsupported.length > 0) {
|
|
58742
|
+
logger.warn(
|
|
58743
|
+
`onExtApps: extApps.hostCapabilities ${unsupported.join(", ")} are not supported yet and are not advertised`
|
|
58744
|
+
);
|
|
58745
|
+
}
|
|
58234
58746
|
const handler = createExtAppsMessageHandler({
|
|
58235
58747
|
context: {
|
|
58236
58748
|
sessionId: session.id,
|
|
@@ -58264,6 +58776,9 @@ var init_handle_streamable_http_flow = __esm({
|
|
|
58264
58776
|
this.handled();
|
|
58265
58777
|
}
|
|
58266
58778
|
};
|
|
58779
|
+
__decorateClass([
|
|
58780
|
+
Stage41("applyNodeHeaders")
|
|
58781
|
+
], HandleStreamableHttpFlow.prototype, "applyNodeHeaders", 1);
|
|
58267
58782
|
__decorateClass([
|
|
58268
58783
|
Stage41("parseInput")
|
|
58269
58784
|
], HandleStreamableHttpFlow.prototype, "parseInput", 1);
|
|
@@ -59856,7 +60371,7 @@ var init_transport_remote = __esm({
|
|
|
59856
60371
|
|
|
59857
60372
|
// libs/sdk/src/transport/transport.registry.ts
|
|
59858
60373
|
import { getMachineId as getMachineId5, sha256Hex as sha256Hex12 } from "@frontmcp/utils";
|
|
59859
|
-
var TransportService;
|
|
60374
|
+
var SESSION_STORE_RETRY_BASE_MS, SESSION_STORE_RETRY_MAX_MS, TransportService;
|
|
59860
60375
|
var init_transport_registry = __esm({
|
|
59861
60376
|
"libs/sdk/src/transport/transport.registry.ts"() {
|
|
59862
60377
|
"use strict";
|
|
@@ -59869,6 +60384,8 @@ var init_transport_registry = __esm({
|
|
|
59869
60384
|
init_transport_local();
|
|
59870
60385
|
init_transport_remote();
|
|
59871
60386
|
init_transport_types();
|
|
60387
|
+
SESSION_STORE_RETRY_BASE_MS = 1e3;
|
|
60388
|
+
SESSION_STORE_RETRY_MAX_MS = 3e4;
|
|
59872
60389
|
TransportService = class {
|
|
59873
60390
|
ready;
|
|
59874
60391
|
byType = /* @__PURE__ */ new Map();
|
|
@@ -59925,8 +60442,28 @@ var init_transport_registry = __esm({
|
|
|
59925
60442
|
* Returns undefined if persistence is disabled or not configured.
|
|
59926
60443
|
*/
|
|
59927
60444
|
getDefaultTtlMs() {
|
|
60445
|
+
if (this.resolvedTtlMs !== void 0) return this.resolvedTtlMs;
|
|
59928
60446
|
return typeof this.persistenceConfig === "object" ? this.persistenceConfig?.defaultTtlMs : void 0;
|
|
59929
60447
|
}
|
|
60448
|
+
/**
|
|
60449
|
+
* TTL resolved from `persistence.defaultTtlMs`, then `persistence.redis.defaultTtlMs`, then 1 hour.
|
|
60450
|
+
* Set only when a persistence backend is configured.
|
|
60451
|
+
*/
|
|
60452
|
+
resolvedTtlMs;
|
|
60453
|
+
/** Store config kept after a successful create so a failed ping can be retried. */
|
|
60454
|
+
storeRetryConfig;
|
|
60455
|
+
storeRetryTimer;
|
|
60456
|
+
storeRetryAttempt = 0;
|
|
60457
|
+
destroyed = false;
|
|
60458
|
+
/** Last time a request refreshed the stored session's TTL, by session id. */
|
|
60459
|
+
lastTtlRefreshAt = /* @__PURE__ */ new Map();
|
|
60460
|
+
/**
|
|
60461
|
+
* Redis key prefix under which session records are stored.
|
|
60462
|
+
* The session store appends `session:` to the transport key prefix.
|
|
60463
|
+
*/
|
|
60464
|
+
getSessionKeyPrefix() {
|
|
60465
|
+
return `${this.transportKeyPrefix}session:`;
|
|
60466
|
+
}
|
|
59930
60467
|
constructor(scope, persistenceConfig, bus) {
|
|
59931
60468
|
this.scope = scope;
|
|
59932
60469
|
this.persistenceConfig = persistenceConfig;
|
|
@@ -59937,10 +60474,11 @@ var init_transport_registry = __esm({
|
|
|
59937
60474
|
this.backendKind = "sqlite";
|
|
59938
60475
|
const sqliteConfig = persistenceConfig.sqlite;
|
|
59939
60476
|
this.transportKeyPrefix = "mcp:transport:";
|
|
60477
|
+
this.resolvedTtlMs = persistenceConfig.defaultTtlMs ?? 36e5;
|
|
59940
60478
|
this.pendingStoreConfig = {
|
|
59941
60479
|
sqlite: sqliteConfig,
|
|
59942
60480
|
keyPrefix: this.transportKeyPrefix,
|
|
59943
|
-
defaultTtlMs:
|
|
60481
|
+
defaultTtlMs: this.resolvedTtlMs
|
|
59944
60482
|
};
|
|
59945
60483
|
this.scope.logger.info("[TransportService] sqlite session store will be initialized for transport persistence");
|
|
59946
60484
|
} else if (persistenceConfig !== false && persistenceConfig?.redis) {
|
|
@@ -59949,43 +60487,23 @@ var init_transport_registry = __esm({
|
|
|
59949
60487
|
const providerType = "provider" in redisConfig ? redisConfig.provider : "redis";
|
|
59950
60488
|
this.backendKind = providerType === "vercel-kv" ? "vercel-kv" : "redis";
|
|
59951
60489
|
this.transportKeyPrefix = redisConfig.keyPrefix ?? "mcp:transport:";
|
|
60490
|
+
this.resolvedTtlMs = persistenceConfig.defaultTtlMs ?? redisConfig.defaultTtlMs ?? 36e5;
|
|
59952
60491
|
this.pendingStoreConfig = {
|
|
59953
60492
|
...redisConfig,
|
|
59954
60493
|
keyPrefix: this.transportKeyPrefix,
|
|
59955
|
-
defaultTtlMs:
|
|
59956
|
-
// 1 hour default
|
|
60494
|
+
defaultTtlMs: this.resolvedTtlMs
|
|
59957
60495
|
};
|
|
59958
60496
|
this.scope.logger.info(
|
|
59959
60497
|
`[TransportService] ${providerType} session store will be initialized for transport persistence`
|
|
59960
60498
|
);
|
|
59961
60499
|
}
|
|
60500
|
+
this.storeRetryConfig = this.pendingStoreConfig;
|
|
59962
60501
|
this.ready = this.initialize();
|
|
59963
60502
|
}
|
|
59964
60503
|
async initialize() {
|
|
59965
60504
|
if (this.pendingStoreConfig) {
|
|
59966
|
-
|
|
59967
|
-
|
|
59968
|
-
this.sessionStore = store;
|
|
59969
|
-
this.pendingStoreConfig = void 0;
|
|
59970
|
-
} catch (error) {
|
|
59971
|
-
const err = error;
|
|
59972
|
-
this.scope.logger.error("[TransportService] Failed to create session store - session persistence disabled", {
|
|
59973
|
-
error: err.message,
|
|
59974
|
-
cause: err.cause?.message
|
|
59975
|
-
});
|
|
59976
|
-
}
|
|
59977
|
-
}
|
|
59978
|
-
if (this.sessionStore) {
|
|
59979
|
-
const isConnected = this.sessionStore.ping ? await this.sessionStore.ping() : true;
|
|
59980
|
-
if (!isConnected) {
|
|
59981
|
-
const providerType = this.backendKind ?? "redis";
|
|
59982
|
-
this.scope.logger.error(
|
|
59983
|
-
`[TransportService] Failed to connect to ${providerType} - session persistence disabled`
|
|
59984
|
-
);
|
|
59985
|
-
await this.teardownSessionStore().catch(() => void 0);
|
|
59986
|
-
} else {
|
|
59987
|
-
this.scope.logger.info("[TransportService] Session store connection validated successfully");
|
|
59988
|
-
}
|
|
60505
|
+
const connected = await this.connectSessionStore();
|
|
60506
|
+
if (!connected && this.backendKind !== "sqlite") this.scheduleSessionStoreRetry();
|
|
59989
60507
|
}
|
|
59990
60508
|
await this.scope.registryFlows(
|
|
59991
60509
|
HandleStreamableHttpFlow,
|
|
@@ -59994,7 +60512,70 @@ var init_transport_registry = __esm({
|
|
|
59994
60512
|
HandleMcp20260728Flow
|
|
59995
60513
|
);
|
|
59996
60514
|
}
|
|
60515
|
+
/**
|
|
60516
|
+
* Create the session store and validate the connection.
|
|
60517
|
+
* Returns true when the store is usable; on failure the store is left undefined.
|
|
60518
|
+
*/
|
|
60519
|
+
async connectSessionStore() {
|
|
60520
|
+
const storeConfig = this.pendingStoreConfig ?? this.storeRetryConfig;
|
|
60521
|
+
if (!storeConfig) return false;
|
|
60522
|
+
try {
|
|
60523
|
+
const store = await createSessionStore(storeConfig, this.scope.logger.child("SessionStore"));
|
|
60524
|
+
this.sessionStore = store;
|
|
60525
|
+
this.pendingStoreConfig = void 0;
|
|
60526
|
+
} catch (error) {
|
|
60527
|
+
const err = error;
|
|
60528
|
+
this.scope.logger.error("[TransportService] Failed to create session store - session persistence disabled", {
|
|
60529
|
+
error: err.message,
|
|
60530
|
+
cause: err.cause?.message
|
|
60531
|
+
});
|
|
60532
|
+
return false;
|
|
60533
|
+
}
|
|
60534
|
+
const isConnected = this.sessionStore?.ping ? await this.sessionStore.ping() : true;
|
|
60535
|
+
if (!isConnected) {
|
|
60536
|
+
const providerType = this.backendKind ?? "redis";
|
|
60537
|
+
this.scope.logger.error(`[TransportService] Failed to connect to ${providerType} - session persistence disabled`);
|
|
60538
|
+
await this.teardownSessionStore().catch(() => void 0);
|
|
60539
|
+
return false;
|
|
60540
|
+
}
|
|
60541
|
+
this.scope.logger.info("[TransportService] Session store connection validated successfully");
|
|
60542
|
+
return true;
|
|
60543
|
+
}
|
|
60544
|
+
/**
|
|
60545
|
+
* Retry the session store with exponential backoff (1s doubling to 30s) so a Redis that
|
|
60546
|
+
* was down at startup is picked up when it comes back, and `/readyz` turns healthy.
|
|
60547
|
+
*/
|
|
60548
|
+
scheduleSessionStoreRetry() {
|
|
60549
|
+
if (this.destroyed || this.storeRetryTimer) return;
|
|
60550
|
+
const delay = Math.min(SESSION_STORE_RETRY_MAX_MS, SESSION_STORE_RETRY_BASE_MS * 2 ** this.storeRetryAttempt);
|
|
60551
|
+
this.storeRetryAttempt++;
|
|
60552
|
+
this.storeRetryTimer = setTimeout(() => {
|
|
60553
|
+
this.storeRetryTimer = void 0;
|
|
60554
|
+
void this.retrySessionStore();
|
|
60555
|
+
}, delay);
|
|
60556
|
+
this.storeRetryTimer.unref?.();
|
|
60557
|
+
}
|
|
60558
|
+
async retrySessionStore() {
|
|
60559
|
+
if (this.destroyed || this.sessionStore) return;
|
|
60560
|
+
const connected = await this.connectSessionStore().catch(() => false);
|
|
60561
|
+
if (this.destroyed) {
|
|
60562
|
+
await this.teardownSessionStore().catch(() => void 0);
|
|
60563
|
+
return;
|
|
60564
|
+
}
|
|
60565
|
+
if (connected) {
|
|
60566
|
+
this.storeRetryAttempt = 0;
|
|
60567
|
+
this.scope.logger.info("[TransportService] Session store recovered after startup failure");
|
|
60568
|
+
return;
|
|
60569
|
+
}
|
|
60570
|
+
this.scheduleSessionStoreRetry();
|
|
60571
|
+
}
|
|
59997
60572
|
async destroy() {
|
|
60573
|
+
this.destroyed = true;
|
|
60574
|
+
if (this.storeRetryTimer) {
|
|
60575
|
+
clearTimeout(this.storeRetryTimer);
|
|
60576
|
+
this.storeRetryTimer = void 0;
|
|
60577
|
+
}
|
|
60578
|
+
this.lastTtlRefreshAt.clear();
|
|
59998
60579
|
await this.teardownSessionStore();
|
|
59999
60580
|
}
|
|
60000
60581
|
/**
|
|
@@ -60055,10 +60636,33 @@ var init_transport_registry = __esm({
|
|
|
60055
60636
|
getBackendKind() {
|
|
60056
60637
|
return this.backendKind;
|
|
60057
60638
|
}
|
|
60639
|
+
/**
|
|
60640
|
+
* Slide the stored session's TTL while this instance is serving it. Redis and Vercel KV
|
|
60641
|
+
* session stores extend the TTL on read, so a plain `get` is enough. Throttled per session
|
|
60642
|
+
* to a quarter of the TTL, fire-and-forget.
|
|
60643
|
+
*/
|
|
60644
|
+
refreshStoredSessionTtl(sessionId) {
|
|
60645
|
+
const store = this.sessionStore;
|
|
60646
|
+
const ttlMs = this.getDefaultTtlMs();
|
|
60647
|
+
if (!store || !ttlMs || this.backendKind !== "redis" && this.backendKind !== "vercel-kv") return;
|
|
60648
|
+
const now = Date.now();
|
|
60649
|
+
const last = this.lastTtlRefreshAt.get(sessionId);
|
|
60650
|
+
if (last !== void 0 && now - last < Math.max(1e3, Math.floor(ttlMs / 4))) return;
|
|
60651
|
+
this.lastTtlRefreshAt.set(sessionId, now);
|
|
60652
|
+
store.get(sessionId).catch((err) => {
|
|
60653
|
+
this.scope.logger.warn("[TransportService] Failed to refresh session TTL", {
|
|
60654
|
+
sessionId: sessionId.slice(0, 20),
|
|
60655
|
+
error: err instanceof Error ? err.message : String(err)
|
|
60656
|
+
});
|
|
60657
|
+
});
|
|
60658
|
+
}
|
|
60058
60659
|
async getTransporter(type, token, sessionId) {
|
|
60059
60660
|
const key = this.keyOf(type, token, sessionId);
|
|
60060
60661
|
const local = this.lookupLocal(key);
|
|
60061
|
-
if (local)
|
|
60662
|
+
if (local) {
|
|
60663
|
+
this.refreshStoredSessionTtl(sessionId);
|
|
60664
|
+
return local;
|
|
60665
|
+
}
|
|
60062
60666
|
if (this.distributed && this.bus) {
|
|
60063
60667
|
const location = await this.bus.lookup(key);
|
|
60064
60668
|
if (location) {
|
|
@@ -60153,7 +60757,7 @@ var init_transport_registry = __esm({
|
|
|
60153
60757
|
});
|
|
60154
60758
|
const currentNodeId = getMachineId5();
|
|
60155
60759
|
if (this.scope.haManager && storedSession.session.nodeId && storedSession.session.nodeId !== currentNodeId) {
|
|
60156
|
-
const sessionKey = `${this.
|
|
60760
|
+
const sessionKey = `${this.getSessionKeyPrefix()}${sessionId}`;
|
|
60157
60761
|
const result = await this.scope.haManager.attemptTakeover(sessionKey, storedSession.session.nodeId);
|
|
60158
60762
|
if (!result.claimed) {
|
|
60159
60763
|
this.scope.logger.debug("[HA] Session already claimed by another pod", { sessionId: sessionId.slice(0, 20) });
|
|
@@ -60486,6 +61090,7 @@ var init_transport_registry = __esm({
|
|
|
60486
61090
|
}
|
|
60487
61091
|
}
|
|
60488
61092
|
evictLocal(key) {
|
|
61093
|
+
this.lastTtlRefreshAt.delete(key.sessionId);
|
|
60489
61094
|
const typeBucket = this.byType.get(key.type);
|
|
60490
61095
|
if (!typeBucket) return;
|
|
60491
61096
|
const tokenBucket = typeBucket.get(key.tokenHash);
|
|
@@ -61442,7 +62047,7 @@ var init_http_request_flow = __esm({
|
|
|
61442
62047
|
// libs/sdk/src/scope/scope.instance.ts
|
|
61443
62048
|
import "reflect-metadata";
|
|
61444
62049
|
import { createGuardManager } from "@frontmcp/guard";
|
|
61445
|
-
import { getEnvFlag, getMachineId as getMachineId6, getRuntimeContext as getRuntimeContext15, isEdgeRuntime } from "@frontmcp/utils";
|
|
62050
|
+
import { createRedisClient, getEnvFlag, getMachineId as getMachineId6, getRuntimeContext as getRuntimeContext15, isEdgeRuntime } from "@frontmcp/utils";
|
|
61446
62051
|
var SCOPE_NON_DISPATCHABLE_FLOWS, Scope;
|
|
61447
62052
|
var init_scope_instance = __esm({
|
|
61448
62053
|
"libs/sdk/src/scope/scope.instance.ts"() {
|
|
@@ -61487,8 +62092,10 @@ var init_scope_instance = __esm({
|
|
|
61487
62092
|
init_tool_registry();
|
|
61488
62093
|
init_tool_utils();
|
|
61489
62094
|
init_ui();
|
|
62095
|
+
init_ui_option_warnings();
|
|
61490
62096
|
init_bus();
|
|
61491
62097
|
init_event_stores();
|
|
62098
|
+
init_request_state();
|
|
61492
62099
|
init_transport_registry();
|
|
61493
62100
|
init_http_ip_filter_flow();
|
|
61494
62101
|
init_http_request_flow();
|
|
@@ -61512,6 +62119,8 @@ var init_scope_instance = __esm({
|
|
|
61512
62119
|
transportService;
|
|
61513
62120
|
// TODO: migrate transport service to transport.registry
|
|
61514
62121
|
notificationService;
|
|
62122
|
+
/** ioredis client owned by the HA manager and transport bus; closed in shutdown(). */
|
|
62123
|
+
haRedisClient;
|
|
61515
62124
|
toolUIRegistry;
|
|
61516
62125
|
authUiRegistry;
|
|
61517
62126
|
entryPath;
|
|
@@ -61592,7 +62201,7 @@ var init_scope_instance = __esm({
|
|
|
61592
62201
|
const isDistributed = getRuntimeContext15().deployment === "distributed";
|
|
61593
62202
|
if (isDistributed && this.metadata.redis && !this.cliMode) {
|
|
61594
62203
|
try {
|
|
61595
|
-
const haRedis = this.
|
|
62204
|
+
const haRedis = this.createHaRedisClient();
|
|
61596
62205
|
this.haManager = HaManager.create({
|
|
61597
62206
|
redis: haRedis,
|
|
61598
62207
|
nodeId: getMachineId6(),
|
|
@@ -61602,11 +62211,13 @@ var init_scope_instance = __esm({
|
|
|
61602
62211
|
this.logger.info("[HA] Manager started for distributed deployment");
|
|
61603
62212
|
} catch (err) {
|
|
61604
62213
|
this.logger.warn("[HA] Failed to start HA manager \u2014 running without HA", { error: err });
|
|
62214
|
+
this.haManager = void 0;
|
|
62215
|
+
await this.closeHaRedisClient();
|
|
61605
62216
|
}
|
|
61606
62217
|
}
|
|
61607
62218
|
let transportBus;
|
|
61608
|
-
if (this.haManager &&
|
|
61609
|
-
transportBus = new RedisTransportBus(this.
|
|
62219
|
+
if (this.haManager && this.haRedisClient && isDistributed) {
|
|
62220
|
+
transportBus = new RedisTransportBus(this.haRedisClient, getMachineId6(), {
|
|
61610
62221
|
logger: this.logger
|
|
61611
62222
|
});
|
|
61612
62223
|
this.logger.info("[HA] Transport bus created for distributed session routing");
|
|
@@ -61617,11 +62228,10 @@ var init_scope_instance = __esm({
|
|
|
61617
62228
|
effectivePersistence = { sqlite: resolvedTopLevelSqlite };
|
|
61618
62229
|
}
|
|
61619
62230
|
this.transportService = new TransportService(this, effectivePersistence, transportBus);
|
|
61620
|
-
|
|
61621
|
-
|
|
61622
|
-
const resolvedPrefix = persistCfg && typeof persistCfg === "object" && persistCfg.redis?.keyPrefix ? persistCfg.redis.keyPrefix : "mcp:transport:";
|
|
62231
|
+
warnIfRequestStateKeyNotShared({ logger: this.logger, metadata: this.metadata });
|
|
62232
|
+
if (this.haManager && isDistributed && this.transportService.getBackendKind() === "redis") {
|
|
61623
62233
|
this.haManager.startOrphanScanner({
|
|
61624
|
-
sessionKeyPrefix:
|
|
62234
|
+
sessionKeyPrefix: this.transportService.getSessionKeyPrefix(),
|
|
61625
62235
|
onOrphan: (sessionId, previousNodeId) => {
|
|
61626
62236
|
this.logger.info(
|
|
61627
62237
|
`[HA] Orphaned session ${sessionId.slice(0, 20)} from ${previousNodeId} \u2014 available for recreation`
|
|
@@ -61677,7 +62287,15 @@ var init_scope_instance = __esm({
|
|
|
61677
62287
|
"[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."
|
|
61678
62288
|
);
|
|
61679
62289
|
}
|
|
61680
|
-
const
|
|
62290
|
+
const gateTasksRedis = tasksConfig?.redis ?? this.metadata.redis;
|
|
62291
|
+
const tasksUseSqlite = Boolean(tasksConfig?.sqlite ?? (gateTasksRedis ? void 0 : resolvedTopLevelSqlite));
|
|
62292
|
+
const tasksUnavailableOnVercelKv = !tasksExplicitlyEnabled && !tasksUseSqlite && resolvesToVercelKvTaskBackend(gateTasksRedis);
|
|
62293
|
+
if (tasksUnavailableOnVercelKv && !tasksExplicitlyDisabled) {
|
|
62294
|
+
this.logger.warn(
|
|
62295
|
+
"[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."
|
|
62296
|
+
);
|
|
62297
|
+
}
|
|
62298
|
+
const shouldInitTasks = !tasksExplicitlyDisabled && !tasksUnavailableOnEdge && !tasksUnavailableOnVercelKv && (!this.cliMode || tasksEnabledForCli || isTaskWorker);
|
|
61681
62299
|
const tasksPromise = shouldInitTasks ? (async () => {
|
|
61682
62300
|
const tasksRedis = tasksConfig?.redis ?? this.metadata.redis;
|
|
61683
62301
|
const tasksSqlite = tasksConfig?.sqlite ?? (tasksRedis ? void 0 : resolvedTopLevelSqlite);
|
|
@@ -62176,6 +62794,16 @@ var init_scope_instance = __esm({
|
|
|
62176
62794
|
async compileUIWidgets() {
|
|
62177
62795
|
const toolsWithUI = this.scopeTools.getTools(true).filter((t) => hasUIConfig(t.metadata));
|
|
62178
62796
|
if (toolsWithUI.length === 0) return;
|
|
62797
|
+
for (const tool of toolsWithUI) {
|
|
62798
|
+
const resourceUri = tool.metadata.ui?.resourceUri;
|
|
62799
|
+
this.toolUIRegistry.registerTool(tool.metadata.name, typeof resourceUri === "string" ? resourceUri : void 0);
|
|
62800
|
+
for (const message of describeIgnoredUiOptions(
|
|
62801
|
+
tool.metadata.name,
|
|
62802
|
+
tool.metadata.ui
|
|
62803
|
+
)) {
|
|
62804
|
+
this.logger.warn(message);
|
|
62805
|
+
}
|
|
62806
|
+
}
|
|
62179
62807
|
this.scopeResources.registerDynamicResource(StaticWidgetResourceTemplate);
|
|
62180
62808
|
this.logger.verbose(`Registered UI resource template for ${toolsWithUI.length} tool(s) with UI configs`);
|
|
62181
62809
|
const staticModeTools = toolsWithUI.filter(
|
|
@@ -62754,6 +63382,29 @@ var init_scope_instance = __esm({
|
|
|
62754
63382
|
}
|
|
62755
63383
|
throw new FlowExitedWithoutOutputError();
|
|
62756
63384
|
}
|
|
63385
|
+
/**
|
|
63386
|
+
* Build the ioredis client shared by the HA manager and the transport bus from `metadata.redis`.
|
|
63387
|
+
* The client reconnects on its own and has a permanent, rate-limited 'error' listener.
|
|
63388
|
+
*/
|
|
63389
|
+
createHaRedisClient() {
|
|
63390
|
+
const redis = this.metadata.redis;
|
|
63391
|
+
if (!redis || redis.provider === "vercel-kv") {
|
|
63392
|
+
throw new Error("Distributed HA requires a TCP Redis (ioredis); Vercel KV over REST has no Lua/pub-sub support");
|
|
63393
|
+
}
|
|
63394
|
+
const client = createRedisClient({ ...redis, label: "ha", logger: this.logger });
|
|
63395
|
+
this.haRedisClient = client;
|
|
63396
|
+
return client;
|
|
63397
|
+
}
|
|
63398
|
+
async closeHaRedisClient() {
|
|
63399
|
+
const client = this.haRedisClient;
|
|
63400
|
+
if (!client) return;
|
|
63401
|
+
this.haRedisClient = void 0;
|
|
63402
|
+
try {
|
|
63403
|
+
await client.quit();
|
|
63404
|
+
} catch {
|
|
63405
|
+
client.disconnect();
|
|
63406
|
+
}
|
|
63407
|
+
}
|
|
62757
63408
|
/**
|
|
62758
63409
|
* Shut down the scope and release resources.
|
|
62759
63410
|
* Disconnects channel service connectors, unsubscribes event handlers,
|
|
@@ -62767,6 +63418,7 @@ var init_scope_instance = __esm({
|
|
|
62767
63418
|
this.logger.error("[HA] Manager shutdown failed", { error: err });
|
|
62768
63419
|
}
|
|
62769
63420
|
}
|
|
63421
|
+
await this.closeHaRedisClient();
|
|
62770
63422
|
if (this._channelTeardown) {
|
|
62771
63423
|
try {
|
|
62772
63424
|
await this._channelTeardown();
|
|
@@ -63895,6 +64547,177 @@ var init_scope_registry = __esm({
|
|
|
63895
64547
|
}
|
|
63896
64548
|
});
|
|
63897
64549
|
|
|
64550
|
+
// libs/sdk/src/transport/ipc-server.transport.ts
|
|
64551
|
+
function isIpcStdioRequested() {
|
|
64552
|
+
return !!process.env["FRONTMCP_DEV_STDIO_FD"] && typeof process.send === "function";
|
|
64553
|
+
}
|
|
64554
|
+
var IPC_READY_MESSAGE, IpcServerTransport;
|
|
64555
|
+
var init_ipc_server_transport = __esm({
|
|
64556
|
+
"libs/sdk/src/transport/ipc-server.transport.ts"() {
|
|
64557
|
+
"use strict";
|
|
64558
|
+
IPC_READY_MESSAGE = { __frontmcp: "ready" };
|
|
64559
|
+
IpcServerTransport = class {
|
|
64560
|
+
onclose;
|
|
64561
|
+
onerror;
|
|
64562
|
+
onmessage;
|
|
64563
|
+
started = false;
|
|
64564
|
+
handleMessage = (raw) => {
|
|
64565
|
+
if (!raw || typeof raw !== "object" || raw.__frontmcp !== void 0) return;
|
|
64566
|
+
this.onmessage?.(raw);
|
|
64567
|
+
};
|
|
64568
|
+
handleDisconnect = () => {
|
|
64569
|
+
this.onclose?.();
|
|
64570
|
+
};
|
|
64571
|
+
async start() {
|
|
64572
|
+
if (this.started) throw new Error("IpcServerTransport already started");
|
|
64573
|
+
if (typeof process.send !== "function") {
|
|
64574
|
+
throw new Error("IpcServerTransport requires a process forked with an IPC channel");
|
|
64575
|
+
}
|
|
64576
|
+
this.started = true;
|
|
64577
|
+
process.on("message", this.handleMessage);
|
|
64578
|
+
process.once("disconnect", this.handleDisconnect);
|
|
64579
|
+
process.send(IPC_READY_MESSAGE);
|
|
64580
|
+
}
|
|
64581
|
+
async send(message) {
|
|
64582
|
+
const send = process.send;
|
|
64583
|
+
if (!process.connected || typeof send !== "function") {
|
|
64584
|
+
throw new Error("IPC channel is closed");
|
|
64585
|
+
}
|
|
64586
|
+
await new Promise((resolve, reject) => {
|
|
64587
|
+
send.call(process, message, (err) => err ? reject(err) : resolve());
|
|
64588
|
+
});
|
|
64589
|
+
}
|
|
64590
|
+
async close() {
|
|
64591
|
+
process.off("message", this.handleMessage);
|
|
64592
|
+
process.off("disconnect", this.handleDisconnect);
|
|
64593
|
+
this.started = false;
|
|
64594
|
+
this.onclose?.();
|
|
64595
|
+
}
|
|
64596
|
+
};
|
|
64597
|
+
}
|
|
64598
|
+
});
|
|
64599
|
+
|
|
64600
|
+
// libs/sdk/src/server/middleware/csp.middleware.ts
|
|
64601
|
+
import { getEnv as getEnv8, getEnvFlag as getEnvFlag2 } from "@frontmcp/utils";
|
|
64602
|
+
function readCspFromEnv() {
|
|
64603
|
+
if (!getEnvFlag2("FRONTMCP_CSP_ENABLED")) return void 0;
|
|
64604
|
+
const rawDirectives = getEnv8("FRONTMCP_CSP_DIRECTIVES") ?? "";
|
|
64605
|
+
const directives = {};
|
|
64606
|
+
for (const part of rawDirectives.split(";")) {
|
|
64607
|
+
const trimmed = part.trim();
|
|
64608
|
+
if (!trimmed) continue;
|
|
64609
|
+
const spaceIdx = trimmed.indexOf(" ");
|
|
64610
|
+
if (spaceIdx > 0) {
|
|
64611
|
+
directives[trimmed.slice(0, spaceIdx)] = trimmed.slice(spaceIdx + 1);
|
|
64612
|
+
} else {
|
|
64613
|
+
directives[trimmed] = "";
|
|
64614
|
+
}
|
|
64615
|
+
}
|
|
64616
|
+
return {
|
|
64617
|
+
enabled: true,
|
|
64618
|
+
directives,
|
|
64619
|
+
reportUri: getEnv8("FRONTMCP_CSP_REPORT_URI"),
|
|
64620
|
+
reportOnly: getEnvFlag2("FRONTMCP_CSP_REPORT_ONLY")
|
|
64621
|
+
};
|
|
64622
|
+
}
|
|
64623
|
+
function buildCspHeaderValue(options) {
|
|
64624
|
+
const parts = [];
|
|
64625
|
+
for (const [directive, value] of Object.entries(options.directives)) {
|
|
64626
|
+
parts.push(value ? `${directive} ${value}` : directive);
|
|
64627
|
+
}
|
|
64628
|
+
if (options.reportUri) {
|
|
64629
|
+
parts.push(`report-uri ${options.reportUri}`);
|
|
64630
|
+
}
|
|
64631
|
+
return parts.join("; ");
|
|
64632
|
+
}
|
|
64633
|
+
function getCspHeaderName(reportOnly) {
|
|
64634
|
+
return reportOnly ? "Content-Security-Policy-Report-Only" : "Content-Security-Policy";
|
|
64635
|
+
}
|
|
64636
|
+
function readSecurityHeadersFromEnv() {
|
|
64637
|
+
return {
|
|
64638
|
+
hsts: readHeaderEnv("FRONTMCP_HSTS"),
|
|
64639
|
+
contentTypeOptions: readHeaderEnv("FRONTMCP_CONTENT_TYPE_OPTIONS", "nosniff"),
|
|
64640
|
+
frameOptions: readHeaderEnv("FRONTMCP_FRAME_OPTIONS", "DENY"),
|
|
64641
|
+
custom: readCustomHeadersFromEnv()
|
|
64642
|
+
};
|
|
64643
|
+
}
|
|
64644
|
+
function readHeaderEnv(name44, fallback) {
|
|
64645
|
+
const value = getEnv8(name44);
|
|
64646
|
+
if (value === void 0 || value === "") return fallback;
|
|
64647
|
+
return ["off", "false", "none"].includes(value.trim().toLowerCase()) ? void 0 : value;
|
|
64648
|
+
}
|
|
64649
|
+
function readCustomHeadersFromEnv() {
|
|
64650
|
+
const raw = getEnv8("FRONTMCP_HEADERS_CUSTOM");
|
|
64651
|
+
if (!raw) return void 0;
|
|
64652
|
+
try {
|
|
64653
|
+
const parsed = JSON.parse(raw);
|
|
64654
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) return void 0;
|
|
64655
|
+
return Object.fromEntries(
|
|
64656
|
+
Object.entries(parsed).filter((entry) => typeof entry[1] === "string")
|
|
64657
|
+
);
|
|
64658
|
+
} catch {
|
|
64659
|
+
return void 0;
|
|
64660
|
+
}
|
|
64661
|
+
}
|
|
64662
|
+
function applySecurityHeaders(res, headers, csp) {
|
|
64663
|
+
if (headers.hsts) {
|
|
64664
|
+
res.setHeader("Strict-Transport-Security", headers.hsts);
|
|
64665
|
+
}
|
|
64666
|
+
if (headers.contentTypeOptions) {
|
|
64667
|
+
res.setHeader("X-Content-Type-Options", headers.contentTypeOptions);
|
|
64668
|
+
}
|
|
64669
|
+
if (headers.frameOptions) {
|
|
64670
|
+
res.setHeader("X-Frame-Options", headers.frameOptions);
|
|
64671
|
+
}
|
|
64672
|
+
if (headers.custom) {
|
|
64673
|
+
for (const [name44, value] of Object.entries(headers.custom)) {
|
|
64674
|
+
res.setHeader(name44, value);
|
|
64675
|
+
}
|
|
64676
|
+
}
|
|
64677
|
+
if (csp?.enabled) {
|
|
64678
|
+
const headerName = getCspHeaderName(csp.reportOnly);
|
|
64679
|
+
const headerValue = buildCspHeaderValue(csp);
|
|
64680
|
+
if (headerValue) {
|
|
64681
|
+
res.setHeader(headerName, headerValue);
|
|
64682
|
+
}
|
|
64683
|
+
}
|
|
64684
|
+
}
|
|
64685
|
+
function resolveSecurityHeaders(options = {}) {
|
|
64686
|
+
const env = readSecurityHeadersFromEnv();
|
|
64687
|
+
const pick = (explicit, fromEnv) => explicit === false ? void 0 : explicit ?? fromEnv;
|
|
64688
|
+
const resolved = {
|
|
64689
|
+
hsts: pick(options.hsts, env.hsts),
|
|
64690
|
+
contentTypeOptions: pick(options.contentTypeOptions, env.contentTypeOptions),
|
|
64691
|
+
frameOptions: pick(options.frameOptions, env.frameOptions),
|
|
64692
|
+
custom: { ...env.custom, ...options.custom }
|
|
64693
|
+
};
|
|
64694
|
+
let csp;
|
|
64695
|
+
if (options.csp?.enabled === false) {
|
|
64696
|
+
csp = void 0;
|
|
64697
|
+
} else if (options.csp?.enabled === true) {
|
|
64698
|
+
const directives = {};
|
|
64699
|
+
for (const [name44, value] of Object.entries(options.csp.directives ?? {})) {
|
|
64700
|
+
directives[name44] = Array.isArray(value) ? value.join(" ") : value;
|
|
64701
|
+
}
|
|
64702
|
+
csp = {
|
|
64703
|
+
enabled: true,
|
|
64704
|
+
directives,
|
|
64705
|
+
reportUri: options.csp.reportUri,
|
|
64706
|
+
reportOnly: options.csp.reportOnly ?? false
|
|
64707
|
+
};
|
|
64708
|
+
} else {
|
|
64709
|
+
csp = readCspFromEnv();
|
|
64710
|
+
}
|
|
64711
|
+
const headers = {};
|
|
64712
|
+
applySecurityHeaders({ setHeader: (name44, value) => void (headers[name44] = value) }, resolved, csp);
|
|
64713
|
+
return headers;
|
|
64714
|
+
}
|
|
64715
|
+
var init_csp_middleware = __esm({
|
|
64716
|
+
"libs/sdk/src/server/middleware/csp.middleware.ts"() {
|
|
64717
|
+
"use strict";
|
|
64718
|
+
}
|
|
64719
|
+
});
|
|
64720
|
+
|
|
63898
64721
|
// libs/sdk/src/transport/web-response.renderer.ts
|
|
63899
64722
|
function toByteStream(streamLike) {
|
|
63900
64723
|
const iter = streamLike[Symbol.asyncIterator];
|
|
@@ -64000,6 +64823,7 @@ function isCloudflareWorkersRuntime(request) {
|
|
|
64000
64823
|
return typeof cloudflareProperties === "object" && cloudflareProperties !== null;
|
|
64001
64824
|
}
|
|
64002
64825
|
function resolvePeerAddress(request, ctx) {
|
|
64826
|
+
if (carriedPeerAddresses.has(request)) return carriedPeerAddresses.get(request);
|
|
64003
64827
|
if (typeof ctx?.requestIP === "function") return ctx.requestIP(request)?.address;
|
|
64004
64828
|
if (typeof ctx?.remoteAddr?.hostname === "string") return ctx.remoteAddr.hostname;
|
|
64005
64829
|
if (isCloudflareWorkersRuntime(request)) return request.headers.get("cf-connecting-ip") ?? void 0;
|
|
@@ -64018,7 +64842,13 @@ function mapHttpCors(httpCors, scope) {
|
|
|
64018
64842
|
}
|
|
64019
64843
|
function createWebFetchHandler(scope, options = {}) {
|
|
64020
64844
|
const httpConfig = scope.metadata.http;
|
|
64021
|
-
const
|
|
64845
|
+
const healthConfig = scope.metadata.health ?? {};
|
|
64846
|
+
const healthEnabled = healthConfig.enabled !== false;
|
|
64847
|
+
const healthzPath = healthConfig.healthzPath ?? "/healthz";
|
|
64848
|
+
const readyzPath = healthConfig.readyzPath ?? "/readyz";
|
|
64849
|
+
const readyzEnabled = isReadyzEnabled(healthConfig);
|
|
64850
|
+
const bodyLimit = parseByteLimit(httpConfig?.bodyLimit);
|
|
64851
|
+
const securityHeaders = resolveSecurityHeaders(httpConfig?.securityHeaders);
|
|
64022
64852
|
const normalizePath = (p) => {
|
|
64023
64853
|
const withSlash = p.startsWith("/") ? p : `/${p}`;
|
|
64024
64854
|
let end = withSlash.length;
|
|
@@ -64062,7 +64892,13 @@ function createWebFetchHandler(scope, options = {}) {
|
|
|
64062
64892
|
for (const [k, v] of Object.entries(corsHeadersFor(request))) headers.set(k, v);
|
|
64063
64893
|
return new Response(response.body, { status: response.status, statusText: response.statusText, headers });
|
|
64064
64894
|
};
|
|
64065
|
-
|
|
64895
|
+
const withSecurityHeaders = (response) => {
|
|
64896
|
+
const headers = new Headers(response.headers);
|
|
64897
|
+
for (const [k, v] of Object.entries(securityHeaders)) if (!headers.has(k)) headers.set(k, v);
|
|
64898
|
+
headers.delete("x-powered-by");
|
|
64899
|
+
return new Response(response.body, { status: response.status, statusText: response.statusText, headers });
|
|
64900
|
+
};
|
|
64901
|
+
const handleRequest = async (request, ctx, env) => {
|
|
64066
64902
|
const url = new URL(request.url);
|
|
64067
64903
|
if (hostValidation) {
|
|
64068
64904
|
const rejection = validateHostHeaders(
|
|
@@ -64077,17 +64913,34 @@ function createWebFetchHandler(scope, options = {}) {
|
|
|
64077
64913
|
return Response.json({ error: rejection.error, message: rejection.message }, { status: rejection.status });
|
|
64078
64914
|
}
|
|
64079
64915
|
}
|
|
64916
|
+
const limited = await enforceBodyLimit(request, bodyLimit, ctx);
|
|
64917
|
+
if (limited instanceof Response) return withCors(limited, request);
|
|
64918
|
+
request = limited;
|
|
64080
64919
|
if (corsEnabled && request.method === "OPTIONS") {
|
|
64081
64920
|
return new Response(null, { status: 204, headers: corsHeadersFor(request) });
|
|
64082
64921
|
}
|
|
64083
|
-
if (healthPaths.
|
|
64084
|
-
|
|
64085
|
-
|
|
64086
|
-
|
|
64087
|
-
|
|
64088
|
-
|
|
64089
|
-
|
|
64090
|
-
|
|
64922
|
+
if (options.healthPaths ? options.healthPaths.includes(url.pathname) : healthEnabled) {
|
|
64923
|
+
const isReadyz = !options.healthPaths && url.pathname === readyzPath && readyzEnabled;
|
|
64924
|
+
const isHealthz = options.healthPaths ? url.pathname !== "/readyz" : url.pathname === healthzPath || url.pathname === "/health";
|
|
64925
|
+
if (isReadyz && scope.healthService) {
|
|
64926
|
+
const readiness = await scope.healthService.getReadyz();
|
|
64927
|
+
return withCors(
|
|
64928
|
+
Response.json(
|
|
64929
|
+
{ ...readiness, transport: "web-fetch" },
|
|
64930
|
+
{ status: readiness.status === "ready" ? 200 : 503, headers: { "Cache-Control": "no-store" } }
|
|
64931
|
+
),
|
|
64932
|
+
request
|
|
64933
|
+
);
|
|
64934
|
+
}
|
|
64935
|
+
if (isHealthz || isReadyz || options.healthPaths) {
|
|
64936
|
+
return withCors(
|
|
64937
|
+
Response.json(
|
|
64938
|
+
{ status: "ok", server: scope.metadata.info, transport: "web-fetch" },
|
|
64939
|
+
{ headers: { "Cache-Control": "no-store" } }
|
|
64940
|
+
),
|
|
64941
|
+
request
|
|
64942
|
+
);
|
|
64943
|
+
}
|
|
64091
64944
|
}
|
|
64092
64945
|
if (!entryPaths.has(normalizePath(url.pathname))) {
|
|
64093
64946
|
const authResponse = await runMatchingHttpFlowWeb(scope, request, { ctx, env });
|
|
@@ -64101,6 +64954,53 @@ function createWebFetchHandler(scope, options = {}) {
|
|
|
64101
64954
|
const rendered = await runHttpRequestFlowWeb(scope, request, { ctx, env });
|
|
64102
64955
|
return withCors(rendered ?? Response.json({ error: "Not Found" }, { status: 404 }), request);
|
|
64103
64956
|
};
|
|
64957
|
+
return async function handle(request, ctx, env) {
|
|
64958
|
+
return withSecurityHeaders(await handleRequest(request, ctx, env));
|
|
64959
|
+
};
|
|
64960
|
+
}
|
|
64961
|
+
function parseByteLimit(limit) {
|
|
64962
|
+
if (typeof limit === "number" && Number.isFinite(limit) && limit >= 0) return limit;
|
|
64963
|
+
if (typeof limit === "string") {
|
|
64964
|
+
const match = /^(\d+(?:\.\d+)?)\s*(b|kb|mb|gb)?$/i.exec(limit.trim());
|
|
64965
|
+
if (match) return Math.floor(Number(match[1]) * BYTE_UNITS[(match[2] ?? "b").toLowerCase()]);
|
|
64966
|
+
}
|
|
64967
|
+
return DEFAULT_BODY_LIMIT_BYTES;
|
|
64968
|
+
}
|
|
64969
|
+
function payloadTooLarge(limit, length) {
|
|
64970
|
+
const error = new PayloadTooLargeError(limit, length);
|
|
64971
|
+
return Response.json({ jsonrpc: "2.0", error: error.toJsonRpcError(), id: null }, { status: 413 });
|
|
64972
|
+
}
|
|
64973
|
+
async function enforceBodyLimit(request, limit, ctx) {
|
|
64974
|
+
const method = request.method.toUpperCase();
|
|
64975
|
+
if (method === "GET" || method === "HEAD" || method === "OPTIONS" || !request.body) return request;
|
|
64976
|
+
const declared = request.headers.get("content-length");
|
|
64977
|
+
if (declared !== null) {
|
|
64978
|
+
const length = Number(declared);
|
|
64979
|
+
if (Number.isFinite(length) && length > limit) return payloadTooLarge(limit, length);
|
|
64980
|
+
}
|
|
64981
|
+
const peerAddress = resolvePeerAddress(request, ctx);
|
|
64982
|
+
const reader = request.body.getReader();
|
|
64983
|
+
const chunks = [];
|
|
64984
|
+
let total = 0;
|
|
64985
|
+
for (; ; ) {
|
|
64986
|
+
const { done, value } = await reader.read();
|
|
64987
|
+
if (done) break;
|
|
64988
|
+
total += value.byteLength;
|
|
64989
|
+
if (total > limit) {
|
|
64990
|
+
await reader.cancel().catch(() => void 0);
|
|
64991
|
+
return payloadTooLarge(limit, total);
|
|
64992
|
+
}
|
|
64993
|
+
chunks.push(value);
|
|
64994
|
+
}
|
|
64995
|
+
const buffered = new Uint8Array(total);
|
|
64996
|
+
let offset = 0;
|
|
64997
|
+
for (const chunk of chunks) {
|
|
64998
|
+
buffered.set(chunk, offset);
|
|
64999
|
+
offset += chunk.byteLength;
|
|
65000
|
+
}
|
|
65001
|
+
const rebuilt = new Request(request.url, { method: request.method, headers: request.headers, body: buffered });
|
|
65002
|
+
carriedPeerAddresses.set(rebuilt, peerAddress);
|
|
65003
|
+
return rebuilt;
|
|
64104
65004
|
}
|
|
64105
65005
|
async function runHttpRequestFlowWeb(scope, request, opts = {}) {
|
|
64106
65006
|
const url = new URL(request.url);
|
|
@@ -64191,22 +65091,7 @@ async function toServerRequest(request, url, ctx, persistent, env) {
|
|
|
64191
65091
|
function misconfigurationResponse(error) {
|
|
64192
65092
|
const misconfiguration = findMisconfiguration(error);
|
|
64193
65093
|
if (!misconfiguration) return void 0;
|
|
64194
|
-
return Response.json(
|
|
64195
|
-
{ error: "server_misconfigured", code: misconfiguration.code, message: misconfiguration.remedy },
|
|
64196
|
-
{ status: 500 }
|
|
64197
|
-
);
|
|
64198
|
-
}
|
|
64199
|
-
function findMisconfiguration(error) {
|
|
64200
|
-
let current = error;
|
|
64201
|
-
for (let depth = 0; current instanceof Error && depth < 5; depth++) {
|
|
64202
|
-
const code = current.code;
|
|
64203
|
-
if (typeof code === "string" && MISCONFIGURATION_REMEDIES[code]) {
|
|
64204
|
-
return { code, remedy: MISCONFIGURATION_REMEDIES[code] };
|
|
64205
|
-
}
|
|
64206
|
-
if (current.name === "ZodError") return { code: "CONFIG_INVALID", remedy: CONFIG_INVALID_REMEDY };
|
|
64207
|
-
current = current.cause;
|
|
64208
|
-
}
|
|
64209
|
-
return void 0;
|
|
65094
|
+
return Response.json(misconfigurationBody(misconfiguration), { status: 500 });
|
|
64210
65095
|
}
|
|
64211
65096
|
function createDeferredServerBuild(build, options = {}) {
|
|
64212
65097
|
const now = options.now ?? Date.now;
|
|
@@ -64261,7 +65146,7 @@ function flowErrorToHttpOutput(error) {
|
|
|
64261
65146
|
kind: "json",
|
|
64262
65147
|
status: 500,
|
|
64263
65148
|
contentType: "application/json; charset=utf-8",
|
|
64264
|
-
body:
|
|
65149
|
+
body: misconfigurationBody(misconfiguration)
|
|
64265
65150
|
};
|
|
64266
65151
|
}
|
|
64267
65152
|
if (error instanceof FlowControl) {
|
|
@@ -64287,7 +65172,7 @@ function flowErrorToHttpOutput(error) {
|
|
|
64287
65172
|
}
|
|
64288
65173
|
return { kind: "text", status: 500, body: "Internal Server Error", contentType: "text/plain; charset=utf-8" };
|
|
64289
65174
|
}
|
|
64290
|
-
var CLOUDFLARE_WORKERS_USER_AGENT,
|
|
65175
|
+
var CLOUDFLARE_WORKERS_USER_AGENT, carriedPeerAddresses, DEFAULT_BODY_LIMIT_BYTES, BYTE_UNITS, STARTUP_RETRY_MIN_MS, STARTUP_RETRY_MAX_MS;
|
|
64291
65176
|
var init_web_fetch_handler = __esm({
|
|
64292
65177
|
"libs/sdk/src/transport/web-fetch-handler.ts"() {
|
|
64293
65178
|
"use strict";
|
|
@@ -64295,18 +65180,15 @@ var init_web_fetch_handler = __esm({
|
|
|
64295
65180
|
init_server_tokens();
|
|
64296
65181
|
init_path_utils();
|
|
64297
65182
|
init_errors();
|
|
65183
|
+
init_misconfiguration();
|
|
65184
|
+
init_health_routes();
|
|
65185
|
+
init_csp_middleware();
|
|
64298
65186
|
init_host_validation();
|
|
64299
65187
|
init_web_response_renderer();
|
|
64300
65188
|
CLOUDFLARE_WORKERS_USER_AGENT = "Cloudflare-Workers";
|
|
64301
|
-
|
|
64302
|
-
|
|
64303
|
-
|
|
64304
|
-
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.",
|
|
64305
|
-
// The startup checks: a server whose entries ask for protection nothing gives them does not start.
|
|
64306
|
-
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.",
|
|
64307
|
-
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."
|
|
64308
|
-
};
|
|
64309
|
-
CONFIG_INVALID_REMEDY = "The FrontMCP configuration failed validation, so the server refuses to start. The server log names the invalid fields.";
|
|
65189
|
+
carriedPeerAddresses = /* @__PURE__ */ new WeakMap();
|
|
65190
|
+
DEFAULT_BODY_LIMIT_BYTES = 4 * 1024 * 1024;
|
|
65191
|
+
BYTE_UNITS = { b: 1, kb: 1024, mb: 1024 ** 2, gb: 1024 ** 3 };
|
|
64310
65192
|
STARTUP_RETRY_MIN_MS = 1e3;
|
|
64311
65193
|
STARTUP_RETRY_MAX_MS = 6e4;
|
|
64312
65194
|
}
|
|
@@ -64338,9 +65220,9 @@ var init_noop_server = __esm({
|
|
|
64338
65220
|
});
|
|
64339
65221
|
|
|
64340
65222
|
// libs/sdk/src/server/security/security-audit.ts
|
|
64341
|
-
function auditSecurityDefaults(config,
|
|
65223
|
+
function auditSecurityDefaults(config, isProduction7) {
|
|
64342
65224
|
const findings = [];
|
|
64343
|
-
if (!
|
|
65225
|
+
if (!isProduction7 && config.deploymentMode !== "distributed") {
|
|
64344
65226
|
return findings;
|
|
64345
65227
|
}
|
|
64346
65228
|
const strict = config.security?.strict === true;
|
|
@@ -64388,13 +65270,13 @@ function auditSecurityDefaults(config, isProduction6) {
|
|
|
64388
65270
|
message: `Server bound to ${bindAddress}.`
|
|
64389
65271
|
});
|
|
64390
65272
|
}
|
|
64391
|
-
const dnsProtectionEnabled = config.security?.dnsRebindingProtection?.enabled
|
|
65273
|
+
const dnsProtectionEnabled = config.security?.dnsRebindingProtection?.enabled !== false;
|
|
64392
65274
|
if (!dnsProtectionEnabled) {
|
|
64393
65275
|
findings.push({
|
|
64394
65276
|
level: "warn",
|
|
64395
65277
|
code: "DNS_REBINDING_UNPROTECTED",
|
|
64396
|
-
message: "DNS rebinding protection is disabled.",
|
|
64397
|
-
recommendation: "
|
|
65278
|
+
message: "DNS rebinding protection is disabled (security.dnsRebindingProtection.enabled is false).",
|
|
65279
|
+
recommendation: "Remove `enabled: false`, or set security.dnsRebindingProtection.allowedHosts, to prevent DNS rebinding attacks."
|
|
64398
65280
|
});
|
|
64399
65281
|
} else {
|
|
64400
65282
|
findings.push({
|
|
@@ -64583,6 +65465,7 @@ var init_express_host_adapter = __esm({
|
|
|
64583
65465
|
"libs/sdk/src/server/adapters/express.host.adapter.ts"() {
|
|
64584
65466
|
"use strict";
|
|
64585
65467
|
init_mcp_error();
|
|
65468
|
+
init_csp_middleware();
|
|
64586
65469
|
init_host_validation_middleware();
|
|
64587
65470
|
init_resolve_allowed_hosts();
|
|
64588
65471
|
init_base_host_adapter();
|
|
@@ -64597,6 +65480,12 @@ var init_express_host_adapter = __esm({
|
|
|
64597
65480
|
deriveHostValidation;
|
|
64598
65481
|
constructor(options) {
|
|
64599
65482
|
super();
|
|
65483
|
+
this.app.disable("x-powered-by");
|
|
65484
|
+
const securityHeaders = resolveSecurityHeaders(options?.securityHeaders);
|
|
65485
|
+
this.app.use((_req, res, next) => {
|
|
65486
|
+
for (const [name44, value] of Object.entries(securityHeaders)) res.setHeader(name44, value);
|
|
65487
|
+
next();
|
|
65488
|
+
});
|
|
64600
65489
|
const corsOptions = options?.cors;
|
|
64601
65490
|
const corsEnabled = corsOptions?.origin !== void 0 && corsOptions.origin !== false;
|
|
64602
65491
|
if (corsEnabled) {
|
|
@@ -64824,6 +65713,7 @@ var init_server_instance = __esm({
|
|
|
64824
65713
|
this.host = new ExpressHostAdapter2({
|
|
64825
65714
|
...corsConfig ? { cors: corsConfig } : {},
|
|
64826
65715
|
...this.config.security ? { security: this.config.security } : {},
|
|
65716
|
+
...this.config.securityHeaders ? { securityHeaders: this.config.securityHeaders } : {},
|
|
64827
65717
|
...this.config.bodyLimit !== void 0 ? { bodyLimit: this.config.bodyLimit } : {},
|
|
64828
65718
|
...this.config.urlencodedLimit !== void 0 ? { urlencodedLimit: this.config.urlencodedLimit } : {}
|
|
64829
65719
|
});
|
|
@@ -64896,7 +65786,7 @@ var init_server_instance = __esm({
|
|
|
64896
65786
|
this.prepare();
|
|
64897
65787
|
const deploymentMode = getRuntimeContext16().deployment;
|
|
64898
65788
|
const bindAddress = resolveBindAddress(this.config.security, deploymentMode);
|
|
64899
|
-
const
|
|
65789
|
+
const isProduction7 = process.env["NODE_ENV"] === "production";
|
|
64900
65790
|
const findings = auditSecurityDefaults(
|
|
64901
65791
|
{
|
|
64902
65792
|
cors: this.config.cors,
|
|
@@ -64904,7 +65794,7 @@ var init_server_instance = __esm({
|
|
|
64904
65794
|
resolvedBindAddress: bindAddress,
|
|
64905
65795
|
deploymentMode
|
|
64906
65796
|
},
|
|
64907
|
-
|
|
65797
|
+
isProduction7
|
|
64908
65798
|
);
|
|
64909
65799
|
logSecurityFindings(findings, console);
|
|
64910
65800
|
await this.host.start(this.config.socketPath ?? this.config.port, bindAddress);
|
|
@@ -65148,6 +66038,7 @@ var init_front_mcp = __esm({
|
|
|
65148
66038
|
init_scope_registry();
|
|
65149
66039
|
init_skill_instructions_helper();
|
|
65150
66040
|
init_task();
|
|
66041
|
+
init_ipc_server_transport();
|
|
65151
66042
|
init_web_fetch_handler();
|
|
65152
66043
|
init_front_mcp_providers();
|
|
65153
66044
|
init_static_startup_check();
|
|
@@ -65267,7 +66158,13 @@ var init_front_mcp = __esm({
|
|
|
65267
66158
|
}
|
|
65268
66159
|
}
|
|
65269
66160
|
static async bootstrap(options) {
|
|
65270
|
-
const
|
|
66161
|
+
const sqlitePath = process.env["FRONTMCP_SQLITE_PATH"];
|
|
66162
|
+
const parsedConfig = frontMcpMetadataSchema.parse(
|
|
66163
|
+
sqlitePath ? {
|
|
66164
|
+
...options,
|
|
66165
|
+
sqlite: { ...options.sqlite ?? {}, path: sqlitePath }
|
|
66166
|
+
} : options
|
|
66167
|
+
);
|
|
65271
66168
|
const daemonSocket = process.env["FRONTMCP_DAEMON_SOCKET"];
|
|
65272
66169
|
if (daemonSocket) {
|
|
65273
66170
|
await _FrontMcpInstance2.runUnixSocket({ ...parsedConfig, socketPath: daemonSocket });
|
|
@@ -65644,7 +66541,7 @@ var init_front_mcp = __esm({
|
|
|
65644
66541
|
};
|
|
65645
66542
|
mcpServer.setRequestHandler(handler.requestSchema, wrappedHandler);
|
|
65646
66543
|
}
|
|
65647
|
-
const transport = new StdioServerTransport();
|
|
66544
|
+
const transport = isIpcStdioRequested() ? new IpcServerTransport() : new StdioServerTransport();
|
|
65648
66545
|
startupCleanups.push(() => transport.close());
|
|
65649
66546
|
await mcpServer.connect(transport);
|
|
65650
66547
|
startupSucceeded = true;
|
|
@@ -65897,7 +66794,7 @@ var init_execute_task = __esm({
|
|
|
65897
66794
|
|
|
65898
66795
|
// libs/sdk/src/common/decorators/front-mcp.decorator.ts
|
|
65899
66796
|
import "reflect-metadata";
|
|
65900
|
-
import { getEnvFlag as
|
|
66797
|
+
import { getEnvFlag as getEnvFlag3 } from "@frontmcp/utils";
|
|
65901
66798
|
function getDecoratorConfig(target) {
|
|
65902
66799
|
if (typeof target !== "function") return void 0;
|
|
65903
66800
|
if (typeof Reflect === "undefined") return void 0;
|
|
@@ -65952,9 +66849,9 @@ function FrontMcp(providedMetadata) {
|
|
|
65952
66849
|
Reflect.defineMetadata(FrontMcpTokens[property] ?? property, metadata[property], target);
|
|
65953
66850
|
}
|
|
65954
66851
|
Reflect.defineMetadata(FRONTMCP_CONFIG_METADATA_KEY, metadata, target);
|
|
65955
|
-
const isServerless2 =
|
|
65956
|
-
const isSchemaExtract =
|
|
65957
|
-
const isStdio =
|
|
66852
|
+
const isServerless2 = getEnvFlag3("FRONTMCP_SERVERLESS");
|
|
66853
|
+
const isSchemaExtract = getEnvFlag3("FRONTMCP_SCHEMA_EXTRACT");
|
|
66854
|
+
const isStdio = getEnvFlag3("FRONTMCP_STDIO");
|
|
65958
66855
|
const taskWorkerId = typeof process !== "undefined" ? process.env?.["FRONTMCP_RUN_TASK_ID"] : void 0;
|
|
65959
66856
|
if (taskWorkerId && metadata.serve !== false) {
|
|
65960
66857
|
void (async () => {
|
|
@@ -65980,7 +66877,7 @@ function FrontMcp(providedMetadata) {
|
|
|
65980
66877
|
} else if (isServerless2) {
|
|
65981
66878
|
const ServerlessInstance = getFrontMcpInstance();
|
|
65982
66879
|
const { setServerlessHandler: setServerlessHandler2, setServerlessHandlerPromise: setServerlessHandlerPromise2, setServerlessHandlerError: setServerlessHandlerError2 } = getServerlessHandlerFns();
|
|
65983
|
-
const isWorker =
|
|
66880
|
+
const isWorker = getEnvFlag3("FRONTMCP_WORKER");
|
|
65984
66881
|
const handlerPromise = isWorker ? ServerlessInstance.createFetchHandler(metadata) : ServerlessInstance.createHandler(metadata);
|
|
65985
66882
|
setServerlessHandlerPromise2(handlerPromise);
|
|
65986
66883
|
handlerPromise.then(setServerlessHandler2).catch((err) => {
|
|
@@ -66704,7 +67601,7 @@ var init_dynamic_adapter = __esm({
|
|
|
66704
67601
|
* @param options - Adapter options including required `name` field
|
|
66705
67602
|
* @throws Error if `name` is missing/empty or if a duplicate name is detected
|
|
66706
67603
|
*/
|
|
66707
|
-
static init(options) {
|
|
67604
|
+
static init(options = {}) {
|
|
66708
67605
|
const typedOptions = options;
|
|
66709
67606
|
const adapterName = typedOptions.name;
|
|
66710
67607
|
if (!adapterName || typeof adapterName !== "string" || adapterName.trim() === "") {
|
|
@@ -66743,49 +67640,6 @@ var init_dynamic_adapter = __esm({
|
|
|
66743
67640
|
}
|
|
66744
67641
|
});
|
|
66745
67642
|
|
|
66746
|
-
// libs/sdk/src/common/dynamic/dynamic.plugin.ts
|
|
66747
|
-
function isDynamicPluginClass(value) {
|
|
66748
|
-
return typeof value === "function" && value.prototype instanceof DynamicPlugin;
|
|
66749
|
-
}
|
|
66750
|
-
var DynamicPlugin;
|
|
66751
|
-
var init_dynamic_plugin = __esm({
|
|
66752
|
-
"libs/sdk/src/common/dynamic/dynamic.plugin.ts"() {
|
|
66753
|
-
"use strict";
|
|
66754
|
-
init_transport_errors();
|
|
66755
|
-
init_dynamic_utils();
|
|
66756
|
-
DynamicPlugin = class {
|
|
66757
|
-
get(token) {
|
|
66758
|
-
throw new MethodNotImplementedError("DynamicPlugin", "get");
|
|
66759
|
-
}
|
|
66760
|
-
/**
|
|
66761
|
-
* Static init() method to create a plugin provider.
|
|
66762
|
-
* @param options - Input options (with optional fields for defaults)
|
|
66763
|
-
*/
|
|
66764
|
-
static init(options) {
|
|
66765
|
-
const extraProviders = options.providers;
|
|
66766
|
-
const typedOptions = options;
|
|
66767
|
-
if ("useFactory" in options) {
|
|
66768
|
-
return {
|
|
66769
|
-
...typedOptions,
|
|
66770
|
-
provide: this,
|
|
66771
|
-
inject: options.inject,
|
|
66772
|
-
useFactory: options.useFactory,
|
|
66773
|
-
providers: dedupePluginProviders(extraProviders ?? [])
|
|
66774
|
-
};
|
|
66775
|
-
}
|
|
66776
|
-
const dyn = collectDynamicProviders(this, typedOptions);
|
|
66777
|
-
const mergedProviders = dedupePluginProviders([...dyn ?? [], ...extraProviders ?? []]);
|
|
66778
|
-
return {
|
|
66779
|
-
...typedOptions,
|
|
66780
|
-
provide: this,
|
|
66781
|
-
useValue: new this(options),
|
|
66782
|
-
providers: mergedProviders
|
|
66783
|
-
};
|
|
66784
|
-
}
|
|
66785
|
-
};
|
|
66786
|
-
}
|
|
66787
|
-
});
|
|
66788
|
-
|
|
66789
67643
|
// libs/sdk/src/common/dynamic/index.ts
|
|
66790
67644
|
var init_dynamic = __esm({
|
|
66791
67645
|
"libs/sdk/src/common/dynamic/index.ts"() {
|
|
@@ -68801,6 +69655,7 @@ export {
|
|
|
68801
69655
|
httpRequestInputSchema,
|
|
68802
69656
|
httpRespond,
|
|
68803
69657
|
incrementalAuthConfigSchema,
|
|
69658
|
+
initOptionsOf,
|
|
68804
69659
|
intentSchema,
|
|
68805
69660
|
isAlive,
|
|
68806
69661
|
isAnonymousSubject7 as isAnonymousSubject,
|
|
@@ -68923,6 +69778,7 @@ export {
|
|
|
68923
69778
|
resolveSchemaDescriptionFormat,
|
|
68924
69779
|
resolveToolVisibility,
|
|
68925
69780
|
resolveWithFallbacks,
|
|
69781
|
+
resolvesToVercelKvTaskBackend,
|
|
68926
69782
|
frontMcpResource as resource,
|
|
68927
69783
|
frontMcpResourceTemplate as resourceTemplate,
|
|
68928
69784
|
resourceUriMatches,
|