@frontmcp/sdk 1.8.4 → 1.8.5
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/agent.instance.d.ts +9 -3
- package/agent/agent.instance.d.ts.map +1 -1
- package/agent/flows/call-agent.flow.d.ts +19 -1
- package/agent/flows/call-agent.flow.d.ts.map +1 -1
- package/agent/hooks/agent.hooks.d.ts +4 -4
- package/agent/hooks/agent.hooks.d.ts.map +1 -1
- package/auth/flows/oauth.auth-ui.flow.d.ts +1 -1
- package/auth/flows/oauth.authorize.flow.d.ts +3 -3
- package/auth/flows/oauth.authorize.flow.d.ts.map +1 -1
- package/auth/flows/oauth.callback.flow.d.ts +2 -2
- package/auth/flows/oauth.connect.flow.d.ts +1 -1
- package/auth/flows/oauth.provider-callback.flow.d.ts +2 -2
- package/auth/flows/oauth.provider-callback.flow.d.ts.map +1 -1
- package/auth/flows/oauth.register.flow.d.ts +1 -1
- package/auth/flows/oauth.token.flow.d.ts +1 -1
- package/auth/flows/oauth.userinfo.flow.d.ts +1 -1
- package/auth/flows/session.verify.flow.d.ts +1 -0
- package/auth/flows/session.verify.flow.d.ts.map +1 -1
- package/auth/flows/well-known.jwks.flow.d.ts +3 -3
- package/auth/flows/well-known.oauth-authorization-server.flow.d.ts +3 -2
- package/auth/flows/well-known.oauth-authorization-server.flow.d.ts.map +1 -1
- package/auth/flows/well-known.prm.flow.d.ts +4 -3
- package/auth/flows/well-known.prm.flow.d.ts.map +1 -1
- package/auth/instances/instance.local-primary-auth.d.ts +30 -19
- package/auth/instances/instance.local-primary-auth.d.ts.map +1 -1
- package/channel/channel-scope.helper.d.ts +13 -1
- package/channel/channel-scope.helper.d.ts.map +1 -1
- package/channel/sources/completion-events.d.ts +24 -0
- package/channel/sources/completion-events.d.ts.map +1 -0
- package/channel/sources/job-completion.source.d.ts +1 -1
- package/channel/sources/job-completion.source.d.ts.map +1 -1
- package/common/entries/agent.entry.d.ts +6 -0
- package/common/entries/agent.entry.d.ts.map +1 -1
- package/common/entries/scope.entry.d.ts +2 -3
- package/common/entries/scope.entry.d.ts.map +1 -1
- package/common/interfaces/internal/primary-auth-provider.interface.d.ts +5 -5
- package/common/interfaces/internal/primary-auth-provider.interface.d.ts.map +1 -1
- package/common/interfaces/tool.interface.d.ts.map +1 -1
- package/common/metadata/tool.metadata.d.ts +2 -2
- package/common/metadata/workflow.metadata.d.ts +1 -1
- package/common/schemas/http-output.schema.d.ts +24 -24
- package/common/utils/path.utils.d.ts +2 -0
- package/common/utils/path.utils.d.ts.map +1 -1
- package/context/frontmcp-context.d.ts +4 -0
- package/context/frontmcp-context.d.ts.map +1 -1
- package/elicitation/helpers/elicit.helper.d.ts.map +1 -1
- package/esm/index.mjs +905 -315
- package/front-mcp/front-mcp.d.ts.map +1 -1
- package/index.d.ts +6 -6
- package/index.d.ts.map +1 -1
- package/index.js +1031 -439
- package/job/job-scope.helper.d.ts.map +1 -1
- package/package.json +10 -10
- package/scope/flows/http.request.flow.d.ts.map +1 -1
- package/scope/scope.instance.d.ts +3 -3
- package/scope/scope.instance.d.ts.map +1 -1
- package/skill/auth/skill-http-auth.d.ts.map +1 -1
- package/skill/flows/http/llm-full-txt.flow.d.ts +1 -1
- package/skill/flows/http/llm-txt.flow.d.ts +1 -1
- package/skill/flows/http/skills-api.flow.d.ts +1 -1
- package/skill/flows/http/skills-api.flow.d.ts.map +1 -1
- package/skill/flows/load-skill.flow.d.ts.map +1 -1
- package/skill/index.d.ts +1 -1
- package/skill/index.d.ts.map +1 -1
- package/skill/sep-2640/sep-2640.resource-helpers.d.ts +6 -0
- package/skill/sep-2640/sep-2640.resource-helpers.d.ts.map +1 -1
- package/skill/skill-filter.helper.d.ts +25 -0
- package/skill/skill-filter.helper.d.ts.map +1 -1
- package/skill/skill-http.utils.d.ts.map +1 -1
- package/skill/skill.events.d.ts.map +1 -1
- package/skill/skill.registry.d.ts +94 -17
- package/skill/skill.registry.d.ts.map +1 -1
- package/tool/flows/call-tool.flow.d.ts.map +1 -1
- package/transport/flows/handle.mcp-20260728.flow.d.ts +1 -0
- package/transport/flows/handle.mcp-20260728.flow.d.ts.map +1 -1
- package/transport/flows/handle.streamable-http.flow.d.ts +1 -1
- package/transport/index.d.ts +1 -1
- package/transport/index.d.ts.map +1 -1
- package/transport/mcp-20260728/dispatcher.d.ts +5 -0
- package/transport/mcp-20260728/dispatcher.d.ts.map +1 -1
- package/transport/mcp-20260728/mrtr.d.ts +8 -0
- package/transport/mcp-20260728/mrtr.d.ts.map +1 -1
- package/transport/mcp-handlers/skills-load-request.handler.d.ts.map +1 -1
- package/transport/web-fetch-handler.d.ts +33 -0
- package/transport/web-fetch-handler.d.ts.map +1 -1
- package/workflow/engine/workflow.engine.d.ts.map +1 -1
package/index.js
CHANGED
|
@@ -6429,30 +6429,30 @@ var require_dist = __commonJS({
|
|
|
6429
6429
|
sqliteStorageOptionsSchema: () => sqliteStorageOptionsSchema
|
|
6430
6430
|
});
|
|
6431
6431
|
module2.exports = __toCommonJS2(index_exports2);
|
|
6432
|
-
var
|
|
6432
|
+
var import_utils114 = require("@frontmcp/utils");
|
|
6433
6433
|
var HKDF_SALT = new TextEncoder().encode("frontmcp-sqlite-storage-v1");
|
|
6434
6434
|
var HKDF_INFO = new TextEncoder().encode("aes-256-gcm-value-encryption");
|
|
6435
6435
|
var KEY_LENGTH = 32;
|
|
6436
6436
|
function deriveEncryptionKey(secret) {
|
|
6437
6437
|
const ikm = new TextEncoder().encode(secret);
|
|
6438
|
-
return (0,
|
|
6438
|
+
return (0, import_utils114.hkdfSha256)(ikm, HKDF_SALT, HKDF_INFO, KEY_LENGTH);
|
|
6439
6439
|
}
|
|
6440
6440
|
var SEPARATOR = ":";
|
|
6441
6441
|
function encryptValue(key, plaintext) {
|
|
6442
|
-
const iv = (0,
|
|
6442
|
+
const iv = (0, import_utils114.randomBytes)(12);
|
|
6443
6443
|
const plaintextBytes = new TextEncoder().encode(plaintext);
|
|
6444
|
-
const { ciphertext, tag } = (0,
|
|
6445
|
-
return [(0,
|
|
6444
|
+
const { ciphertext, tag } = (0, import_utils114.encryptAesGcm)(key, plaintextBytes, iv);
|
|
6445
|
+
return [(0, import_utils114.base64urlEncode)(iv), (0, import_utils114.base64urlEncode)(tag), (0, import_utils114.base64urlEncode)(ciphertext)].join(SEPARATOR);
|
|
6446
6446
|
}
|
|
6447
6447
|
function decryptValue(key, encrypted) {
|
|
6448
6448
|
const parts = encrypted.split(SEPARATOR);
|
|
6449
6449
|
if (parts.length !== 3) {
|
|
6450
6450
|
throw new Error("Invalid encrypted value format");
|
|
6451
6451
|
}
|
|
6452
|
-
const iv = (0,
|
|
6453
|
-
const tag = (0,
|
|
6454
|
-
const ciphertext = (0,
|
|
6455
|
-
const plaintext = (0,
|
|
6452
|
+
const iv = (0, import_utils114.base64urlDecode)(parts[0]);
|
|
6453
|
+
const tag = (0, import_utils114.base64urlDecode)(parts[1]);
|
|
6454
|
+
const ciphertext = (0, import_utils114.base64urlDecode)(parts[2]);
|
|
6455
|
+
const plaintext = (0, import_utils114.decryptAesGcm)(key, ciphertext, iv, tag);
|
|
6456
6456
|
return new TextDecoder().decode(plaintext);
|
|
6457
6457
|
}
|
|
6458
6458
|
var import_utils210 = require("@frontmcp/utils");
|
|
@@ -10203,7 +10203,33 @@ async function filterDiscoverableSkillResults(scope, registry, results, caller =
|
|
|
10203
10203
|
return entry === void 0 || servable.has(entry);
|
|
10204
10204
|
});
|
|
10205
10205
|
}
|
|
10206
|
-
|
|
10206
|
+
function skillToolsForCaller(loaded, tools, ctx, surface) {
|
|
10207
|
+
const caller = callerSurface(ctx, surface);
|
|
10208
|
+
if (!tools || caller === void 0 || loaded.availableTools.length === 0) return loaded;
|
|
10209
|
+
const availability = new Map(tools.getTools(true).map((tool) => [tool.name, tool.metadata?.availableWhen]));
|
|
10210
|
+
const notOffered = loaded.availableTools.filter((name44) => !isOfferedOnSurface(availability.get(name44), caller));
|
|
10211
|
+
if (notOffered.length === 0) return loaded;
|
|
10212
|
+
return {
|
|
10213
|
+
...loaded,
|
|
10214
|
+
availableTools: loaded.availableTools.filter((name44) => !notOffered.includes(name44)),
|
|
10215
|
+
missingTools: [...loaded.missingTools, ...notOffered],
|
|
10216
|
+
isComplete: false,
|
|
10217
|
+
warning: warningWithMissingTools(loaded.warning, loaded.skill?.name, notOffered)
|
|
10218
|
+
};
|
|
10219
|
+
}
|
|
10220
|
+
function warningWithMissingTools(warning, skillName, names) {
|
|
10221
|
+
const prefix = warning?.match(WARNING_PREFIX);
|
|
10222
|
+
if (warning && prefix && warning.endsWith(WARNING_SUFFIX)) {
|
|
10223
|
+
const parts = warning.slice(prefix[0].length, -WARNING_SUFFIX.length).split("; ");
|
|
10224
|
+
const missing = parts.findIndex((part) => part.startsWith(MISSING_PART));
|
|
10225
|
+
if (missing === -1) parts.unshift(`${MISSING_PART}${names.join(", ")}`);
|
|
10226
|
+
else parts[missing] = `${parts[missing]}, ${names.join(", ")}`;
|
|
10227
|
+
return `${prefix[0]}${parts.join("; ")}${WARNING_SUFFIX}`;
|
|
10228
|
+
}
|
|
10229
|
+
const sentence = `Skill "${skillName ?? "skill"}" references ${MISSING_PART}${names.join(", ")}${WARNING_SUFFIX}`;
|
|
10230
|
+
return warning ? `${warning} ${sentence}` : sentence;
|
|
10231
|
+
}
|
|
10232
|
+
var SKILLS_HTTP_SURFACE, WARNING_PREFIX, WARNING_SUFFIX, MISSING_PART;
|
|
10207
10233
|
var init_skill_filter_helper = __esm({
|
|
10208
10234
|
"libs/sdk/src/skill/skill-filter.helper.ts"() {
|
|
10209
10235
|
"use strict";
|
|
@@ -10212,6 +10238,9 @@ var init_skill_filter_helper = __esm({
|
|
|
10212
10238
|
init_skill_authorities_helper();
|
|
10213
10239
|
init_skill_entry_resolver();
|
|
10214
10240
|
SKILLS_HTTP_SURFACE = "mcp";
|
|
10241
|
+
WARNING_PREFIX = /^Skill "(.*)" references /;
|
|
10242
|
+
WARNING_SUFFIX = ". Some functionality may be limited.";
|
|
10243
|
+
MISSING_PART = "missing tools: ";
|
|
10215
10244
|
}
|
|
10216
10245
|
});
|
|
10217
10246
|
|
|
@@ -10312,7 +10341,13 @@ async function formatSkillsForLlmFull(registry, toolRegistry, visibility = "both
|
|
|
10312
10341
|
}
|
|
10313
10342
|
const loaded = await registry.loadSkill(skill.name);
|
|
10314
10343
|
if (loaded) {
|
|
10315
|
-
|
|
10344
|
+
const { availableTools, missingTools } = skillToolsForCaller(
|
|
10345
|
+
loaded,
|
|
10346
|
+
toolRegistry,
|
|
10347
|
+
void 0,
|
|
10348
|
+
SKILLS_HTTP_SURFACE
|
|
10349
|
+
);
|
|
10350
|
+
parts.push(formatSkillForLLMWithSchemas(loaded.skill, availableTools, missingTools, toolRegistry));
|
|
10316
10351
|
}
|
|
10317
10352
|
}
|
|
10318
10353
|
return parts.join("\n\n---\n\n");
|
|
@@ -10432,6 +10467,7 @@ function skillToApiResponse(skill, loadResult) {
|
|
|
10432
10467
|
var init_skill_http_utils = __esm({
|
|
10433
10468
|
"libs/sdk/src/skill/skill-http.utils.ts"() {
|
|
10434
10469
|
"use strict";
|
|
10470
|
+
init_skill_filter_helper();
|
|
10435
10471
|
}
|
|
10436
10472
|
});
|
|
10437
10473
|
|
|
@@ -10995,7 +11031,11 @@ function skillsLoadRequestHandler({
|
|
|
10995
11031
|
throw toSdkMcpError(err);
|
|
10996
11032
|
}
|
|
10997
11033
|
}
|
|
10998
|
-
const { skill, availableTools, missingTools, isComplete, warning } =
|
|
11034
|
+
const { skill, availableTools, missingTools, isComplete, warning } = skillToolsForCaller(
|
|
11035
|
+
loadResult,
|
|
11036
|
+
toolRegistry,
|
|
11037
|
+
withMcpSurface(scope, ctx)
|
|
11038
|
+
);
|
|
10999
11039
|
if (warning) {
|
|
11000
11040
|
warnings.push(warning);
|
|
11001
11041
|
}
|
|
@@ -14550,6 +14590,9 @@ function readPublicUrlPin() {
|
|
|
14550
14590
|
function isPublicUrlPinned() {
|
|
14551
14591
|
return readPublicUrlPin() !== void 0;
|
|
14552
14592
|
}
|
|
14593
|
+
function getPinnedPublicUrl() {
|
|
14594
|
+
return readPublicUrlPin();
|
|
14595
|
+
}
|
|
14553
14596
|
function isProxyTrusted() {
|
|
14554
14597
|
try {
|
|
14555
14598
|
if (typeof process !== "undefined" && process.env) {
|
|
@@ -15999,6 +16042,11 @@ async function performElicit(deps, message, requestedSchema, options) {
|
|
|
15999
16042
|
}
|
|
16000
16043
|
const ctx = tryGetContext();
|
|
16001
16044
|
const mrtr = ctx?.getMrtrExchange?.();
|
|
16045
|
+
if (mrtr && mrtr.clientDeclaredRevision === false && mrtr.supportsElicitation?.(options?.mode ?? "form") !== true) {
|
|
16046
|
+
throw new ElicitationNotSupportedError(
|
|
16047
|
+
"This client does not support elicitation: it declared no elicitation capability, and without a session the elicitation fallback has no one to answer it"
|
|
16048
|
+
);
|
|
16049
|
+
}
|
|
16002
16050
|
if (mrtr) {
|
|
16003
16051
|
const zodSchema = requestedSchema instanceof import_lazy_zod45.z.ZodType ? requestedSchema : import_lazy_zod45.z.object(requestedSchema);
|
|
16004
16052
|
const answer = mrtr.resolveElicitation({
|
|
@@ -22364,6 +22412,7 @@ var init_call_tool_flow = __esm({
|
|
|
22364
22412
|
context.mark("createToolCallContext");
|
|
22365
22413
|
context._toolNameInternal = tool.metadata.id ?? tool.metadata.name;
|
|
22366
22414
|
context._toolInputInternal = input.arguments;
|
|
22415
|
+
context._jsonRpcRequestIdInternal = this.state.jsonRpcRequestId;
|
|
22367
22416
|
const frontmcpContext = context.tryGetContext();
|
|
22368
22417
|
const authInfoWithTransport = authInfo;
|
|
22369
22418
|
if (frontmcpContext && authInfoWithTransport?.transport?.sendElicitRequest) {
|
|
@@ -27238,7 +27287,11 @@ var init_skill_events = __esm({
|
|
|
27238
27287
|
*/
|
|
27239
27288
|
emit(event) {
|
|
27240
27289
|
for (const listener of [...this.listeners]) {
|
|
27241
|
-
|
|
27290
|
+
try {
|
|
27291
|
+
listener(event);
|
|
27292
|
+
} catch (err) {
|
|
27293
|
+
console.error("SkillEmitter listener error:", err instanceof Error ? err.message : "Unknown error");
|
|
27294
|
+
}
|
|
27242
27295
|
}
|
|
27243
27296
|
}
|
|
27244
27297
|
/**
|
|
@@ -27517,6 +27570,24 @@ var init_skill_instance = __esm({
|
|
|
27517
27570
|
});
|
|
27518
27571
|
|
|
27519
27572
|
// libs/sdk/src/skill/skill.registry.ts
|
|
27573
|
+
function findIdPathCollision(skills) {
|
|
27574
|
+
const byPath = /* @__PURE__ */ new Map();
|
|
27575
|
+
for (const skill of skills) {
|
|
27576
|
+
const path3 = skill.getSkillPath();
|
|
27577
|
+
if (!byPath.has(path3)) byPath.set(path3, skill);
|
|
27578
|
+
}
|
|
27579
|
+
for (const idOwner of skills) {
|
|
27580
|
+
const id = idOwner.metadata.id;
|
|
27581
|
+
if (!id || id === idOwner.getSkillPath()) continue;
|
|
27582
|
+
const pathOwner = byPath.get(id);
|
|
27583
|
+
if (pathOwner) return { idOwner, pathOwner };
|
|
27584
|
+
}
|
|
27585
|
+
return void 0;
|
|
27586
|
+
}
|
|
27587
|
+
function describeIdPathCollision({ idOwner, pathOwner }) {
|
|
27588
|
+
const id = idOwner.metadata.id;
|
|
27589
|
+
return `id "${id}" of skill "${idOwner.metadata.name}" is the skill:// path of skill "${pathOwner.metadata.name}", so skill://${id}/SKILL.md would serve one for the other. Give one of them another id or name.`;
|
|
27590
|
+
}
|
|
27520
27591
|
var import_di22, import_utils61, DEFAULT_LIST_LIMIT, SkillRegistry;
|
|
27521
27592
|
var init_skill_registry = __esm({
|
|
27522
27593
|
"libs/sdk/src/skill/skill.registry.ts"() {
|
|
@@ -27550,16 +27621,30 @@ var init_skill_registry = __esm({
|
|
|
27550
27621
|
dynamicContents = /* @__PURE__ */ new Map();
|
|
27551
27622
|
/**
|
|
27552
27623
|
* Per-id generation counter for dynamic registrations. Bumped on every
|
|
27553
|
-
* registerSkillContent call so a stale unregister
|
|
27554
|
-
* caller cannot remove a
|
|
27624
|
+
* registerSkillContent call, and on every removal, so a stale unregister
|
|
27625
|
+
* handle held by an earlier caller cannot remove a later registration. Never
|
|
27626
|
+
* reset: a counter restarted at 1 would match an old handle's generation.
|
|
27555
27627
|
*/
|
|
27556
27628
|
dynamicGenerations = /* @__PURE__ */ new Map();
|
|
27629
|
+
/**
|
|
27630
|
+
* Ids of skills that left the registry (its own dynamic skills, or an adopted child's) whose copy
|
|
27631
|
+
* in the storage provider is not removed yet: the removal is pending, or the provider failed it.
|
|
27632
|
+
* Provider-backed reads skip them until the removal succeeds or the id is served again. Each maps
|
|
27633
|
+
* to the number of its withdrawal.
|
|
27634
|
+
*/
|
|
27635
|
+
withdrawn = /* @__PURE__ */ new Map();
|
|
27636
|
+
/** Numbers each withdrawal, so a removal that completes late clears only its own. */
|
|
27637
|
+
withdrawals = 0;
|
|
27638
|
+
/** The last storage-provider write queued per skill id ({@link syncProvider}). */
|
|
27639
|
+
providerWrites = /* @__PURE__ */ new Map();
|
|
27557
27640
|
/** Whether a plugin registers skills at runtime ({@link expectDynamicSkills}). */
|
|
27558
27641
|
dynamicSkillsExpected = false;
|
|
27559
27642
|
/** Adopted skills from child registries */
|
|
27560
27643
|
adopted = /* @__PURE__ */ new Map();
|
|
27561
27644
|
/** Children registries */
|
|
27562
27645
|
children = /* @__PURE__ */ new Set();
|
|
27646
|
+
/** The registries that adopted this one: a skill registered here is served through them too. */
|
|
27647
|
+
adopters = /* @__PURE__ */ new Set();
|
|
27563
27648
|
/** O(1) indexes */
|
|
27564
27649
|
byQualifiedId = /* @__PURE__ */ new Map();
|
|
27565
27650
|
/** Owner lineage per skill instance, rebuilt with the index so `lineageOf` is O(1). */
|
|
@@ -27644,8 +27729,10 @@ var init_skill_registry = __esm({
|
|
|
27644
27729
|
const row = this.makeRow(token, instance, lineage, this);
|
|
27645
27730
|
this.localRows.push(row);
|
|
27646
27731
|
try {
|
|
27647
|
-
|
|
27648
|
-
|
|
27732
|
+
await this.syncProvider(instance.metadata.id ?? instance.metadata.name, async () => {
|
|
27733
|
+
const content = await instance.load();
|
|
27734
|
+
await this.storageProvider.add(content);
|
|
27735
|
+
});
|
|
27649
27736
|
} catch (error) {
|
|
27650
27737
|
this.scope.logger.warn(`Failed to load skill ${instance.name}: ${error.message}`);
|
|
27651
27738
|
}
|
|
@@ -27668,6 +27755,10 @@ var init_skill_registry = __esm({
|
|
|
27668
27755
|
await this.adoptFromChild(child, child.owner);
|
|
27669
27756
|
}
|
|
27670
27757
|
}
|
|
27758
|
+
const collision = findIdPathCollision(this.listAllInstances());
|
|
27759
|
+
if (collision) {
|
|
27760
|
+
throw new InvalidSkillError(collision.idOwner.metadata.name, describeIdPathCollision(collision));
|
|
27761
|
+
}
|
|
27671
27762
|
this.reindex();
|
|
27672
27763
|
logAvailabilityFiltering(
|
|
27673
27764
|
"SkillRegistry",
|
|
@@ -27687,24 +27778,31 @@ var init_skill_registry = __esm({
|
|
|
27687
27778
|
const adoptedRows = childRows.map((r) => this.relineage(r, prepend));
|
|
27688
27779
|
this.adopted.set(child, adoptedRows);
|
|
27689
27780
|
this.children.add(child);
|
|
27781
|
+
child.adopters.add(this);
|
|
27690
27782
|
child.subscribe({ immediate: false }, () => {
|
|
27783
|
+
const previous = this.adopted.get(child) ?? [];
|
|
27691
27784
|
const latest = child.listAllIndexed().map((r) => this.relineage(r, prepend));
|
|
27692
27785
|
this.adopted.set(child, latest);
|
|
27693
27786
|
this.reindex();
|
|
27694
|
-
this.
|
|
27787
|
+
this.withdrawGoneAdoptedSkills(previous);
|
|
27695
27788
|
for (const row of latest) {
|
|
27696
|
-
row.instance.
|
|
27697
|
-
|
|
27789
|
+
const skillId = row.instance.metadata.id ?? row.instance.metadata.name;
|
|
27790
|
+
this.syncProvider(skillId, async () => {
|
|
27791
|
+
const content = await row.instance.load();
|
|
27792
|
+
await this.storageProvider.add(content);
|
|
27698
27793
|
}).catch((error) => {
|
|
27699
27794
|
this.scope.logger.warn(`Failed to reload adopted skill ${row.baseName}: ${error.message}`);
|
|
27700
27795
|
});
|
|
27701
27796
|
}
|
|
27797
|
+
this.bump("reset");
|
|
27702
27798
|
});
|
|
27703
27799
|
await Promise.all(
|
|
27704
27800
|
adoptedRows.map(async (row) => {
|
|
27705
27801
|
try {
|
|
27706
|
-
|
|
27707
|
-
|
|
27802
|
+
await this.syncProvider(row.instance.metadata.id ?? row.instance.metadata.name, async () => {
|
|
27803
|
+
const content = await row.instance.load();
|
|
27804
|
+
await this.storageProvider.add(content);
|
|
27805
|
+
});
|
|
27708
27806
|
} catch (error) {
|
|
27709
27807
|
this.scope.logger.warn(`Failed to load adopted skill ${row.baseName}: ${error.message}`);
|
|
27710
27808
|
}
|
|
@@ -27713,6 +27811,37 @@ var init_skill_registry = __esm({
|
|
|
27713
27811
|
this.reindex();
|
|
27714
27812
|
this.bump("reset");
|
|
27715
27813
|
}
|
|
27814
|
+
/**
|
|
27815
|
+
* The `skill://` namespaces this registry's skills are served in: its own, and that of every
|
|
27816
|
+
* registry that adopts it, directly or through another, sibling registries included.
|
|
27817
|
+
*/
|
|
27818
|
+
namespacesServingThis() {
|
|
27819
|
+
const registries = /* @__PURE__ */ new Set();
|
|
27820
|
+
const climb = (registry) => {
|
|
27821
|
+
if (registries.has(registry)) return;
|
|
27822
|
+
registries.add(registry);
|
|
27823
|
+
for (const adopter of registry.adopters) climb(adopter);
|
|
27824
|
+
};
|
|
27825
|
+
climb(this);
|
|
27826
|
+
return [...registries].map((registry) => registry.liveSkills());
|
|
27827
|
+
}
|
|
27828
|
+
/**
|
|
27829
|
+
* This registry's skills and those of every registry it adopted, read from each registry's own rows.
|
|
27830
|
+
* An adopter's copy of a child's rows is refreshed only on the child's change event, which a
|
|
27831
|
+
* registration emits after it has committed its row and awaited the storage provider.
|
|
27832
|
+
*/
|
|
27833
|
+
liveSkills() {
|
|
27834
|
+
const registries = /* @__PURE__ */ new Set();
|
|
27835
|
+
const skills = /* @__PURE__ */ new Set();
|
|
27836
|
+
const descend = (registry) => {
|
|
27837
|
+
if (registries.has(registry)) return;
|
|
27838
|
+
registries.add(registry);
|
|
27839
|
+
for (const row of [...registry.localRows, ...registry.dynamicRows]) skills.add(row.instance);
|
|
27840
|
+
for (const child of registry.children) descend(child);
|
|
27841
|
+
};
|
|
27842
|
+
descend(this);
|
|
27843
|
+
return [...skills];
|
|
27844
|
+
}
|
|
27716
27845
|
/* -------------------- Public API -------------------- */
|
|
27717
27846
|
/**
|
|
27718
27847
|
* Get all skills in the registry.
|
|
@@ -27830,7 +27959,14 @@ var init_skill_registry = __esm({
|
|
|
27830
27959
|
}
|
|
27831
27960
|
}
|
|
27832
27961
|
async search(query, options) {
|
|
27833
|
-
const
|
|
27962
|
+
const topK = options?.topK;
|
|
27963
|
+
const hidden = this.hiddenFromProviderRead();
|
|
27964
|
+
const providerOptions = hidden.size > 0 && typeof topK === "number" && topK >= 0 ? { ...options, topK: topK + hidden.size } : options;
|
|
27965
|
+
let baseResults = await this.storageProvider.search(query, providerOptions);
|
|
27966
|
+
if (hidden.size > 0 || this.withdrawn.size > 0) {
|
|
27967
|
+
baseResults = baseResults.filter((r) => !hidden.has(r.metadata));
|
|
27968
|
+
if (typeof topK === "number" && topK >= 0) baseResults = baseResults.slice(0, topK);
|
|
27969
|
+
}
|
|
27834
27970
|
if (this.dynamicContents.size === 0) return baseResults;
|
|
27835
27971
|
const baseSeen = /* @__PURE__ */ new Set();
|
|
27836
27972
|
let rewrites = 0;
|
|
@@ -27854,7 +27990,6 @@ var init_skill_registry = __esm({
|
|
|
27854
27990
|
const overlayOnly = this.searchDynamicOverlay(query, options, baseSeen);
|
|
27855
27991
|
if (overlayOnly.length === 0 && rewrites === 0) return baseResults;
|
|
27856
27992
|
const merged = [...rewrittenBase, ...overlayOnly].sort((a, b) => b.score - a.score);
|
|
27857
|
-
const topK = options?.topK;
|
|
27858
27993
|
return typeof topK === "number" && topK >= 0 ? merged.slice(0, topK) : merged;
|
|
27859
27994
|
}
|
|
27860
27995
|
/**
|
|
@@ -27909,7 +28044,10 @@ var init_skill_registry = __esm({
|
|
|
27909
28044
|
warning
|
|
27910
28045
|
};
|
|
27911
28046
|
}
|
|
28047
|
+
const hidden = this.hiddenFromProviderRead();
|
|
28048
|
+
if (hidden.has({ name: skillId })) return void 0;
|
|
27912
28049
|
const result = await this.storageProvider.load(skillId);
|
|
28050
|
+
if (result && hidden.has(result.skill)) return void 0;
|
|
27913
28051
|
return result ?? void 0;
|
|
27914
28052
|
}
|
|
27915
28053
|
/**
|
|
@@ -27945,8 +28083,12 @@ var init_skill_registry = __esm({
|
|
|
27945
28083
|
* and `hasMore` stay correct.
|
|
27946
28084
|
*/
|
|
27947
28085
|
async listSkills(options) {
|
|
27948
|
-
|
|
27949
|
-
|
|
28086
|
+
const hidden = this.hiddenFromProviderRead();
|
|
28087
|
+
if (this.dynamicContents.size === 0 && hidden.size === 0) {
|
|
28088
|
+
const page = await this.storageProvider.list(options);
|
|
28089
|
+
if (this.withdrawn.size === 0) return page;
|
|
28090
|
+
}
|
|
28091
|
+
const base = (await this.listAllFromProvider(options)).filter((meta) => !hidden.has(meta));
|
|
27950
28092
|
const baseIds = /* @__PURE__ */ new Set();
|
|
27951
28093
|
const merged = base.map((meta) => {
|
|
27952
28094
|
const id = meta.id ?? meta.name;
|
|
@@ -28006,21 +28148,20 @@ var init_skill_registry = __esm({
|
|
|
28006
28148
|
/**
|
|
28007
28149
|
* Get total skill count.
|
|
28008
28150
|
*
|
|
28009
|
-
*
|
|
28151
|
+
* Counts the provider's rows with overlay rows the provider does not know
|
|
28010
28152
|
* about so callers see the same surface as `search` / `listSkills`.
|
|
28011
28153
|
*/
|
|
28012
28154
|
async count(options) {
|
|
28013
|
-
const
|
|
28014
|
-
if (this.dynamicContents.size === 0)
|
|
28015
|
-
|
|
28016
|
-
|
|
28017
|
-
|
|
28018
|
-
|
|
28019
|
-
});
|
|
28155
|
+
const hidden = this.hiddenFromProviderRead();
|
|
28156
|
+
if (this.dynamicContents.size === 0 && hidden.size === 0) {
|
|
28157
|
+
const counted = await this.storageProvider.count(options);
|
|
28158
|
+
if (this.withdrawn.size === 0) return counted;
|
|
28159
|
+
}
|
|
28160
|
+
const baseList = (await this.listAllFromProvider({ tags: options?.tags, includeHidden: options?.includeHidden })).filter((m) => !hidden.has(m));
|
|
28020
28161
|
const baseIds = /* @__PURE__ */ new Set();
|
|
28021
|
-
for (const m of baseList
|
|
28162
|
+
for (const m of baseList) baseIds.add(m.id ?? m.name);
|
|
28022
28163
|
const overlay = this.collectDynamicMetadata(options).filter((m) => !baseIds.has(m.id ?? m.name));
|
|
28023
|
-
return
|
|
28164
|
+
return baseList.length + overlay.length;
|
|
28024
28165
|
}
|
|
28025
28166
|
/**
|
|
28026
28167
|
* Project a SkillContent into the SkillMetadata shape callers expect from
|
|
@@ -28310,10 +28451,6 @@ var init_skill_registry = __esm({
|
|
|
28310
28451
|
throw new PublicMcpError("registerSkillContent: SkillContent.name is required", "INVALID_PARAMS");
|
|
28311
28452
|
}
|
|
28312
28453
|
const id = content.id;
|
|
28313
|
-
const existingIdx = this.dynamicRows.findIndex((r) => r.instance.name === id);
|
|
28314
|
-
if (existingIdx !== -1) {
|
|
28315
|
-
this.dynamicRows.splice(existingIdx, 1);
|
|
28316
|
-
}
|
|
28317
28454
|
const metadata = {
|
|
28318
28455
|
id,
|
|
28319
28456
|
name: content.name,
|
|
@@ -28349,28 +28486,49 @@ var init_skill_registry = __esm({
|
|
|
28349
28486
|
`[SkillRegistry] Failed to pre-load dynamic skill ${id} from source ${opts?.source ?? "unknown"}: ${error.message}`
|
|
28350
28487
|
);
|
|
28351
28488
|
}
|
|
28489
|
+
const existingIdx = this.dynamicRows.findIndex((r) => r.instance.name === id);
|
|
28490
|
+
const superseded = new Set(opts?.supersedes);
|
|
28491
|
+
const leaving = new Set(
|
|
28492
|
+
this.dynamicRows.filter((r) => r.instance.name === id || superseded.has(r.instance.name)).map((r) => r.instance)
|
|
28493
|
+
);
|
|
28494
|
+
for (const skills of this.namespacesServingThis()) {
|
|
28495
|
+
const collision = findIdPathCollision([...skills.filter((other) => !leaving.has(other)), instance]);
|
|
28496
|
+
if (collision) {
|
|
28497
|
+
throw new PublicMcpError(`registerSkillContent: ${describeIdPathCollision(collision)}`, "INVALID_PARAMS");
|
|
28498
|
+
}
|
|
28499
|
+
}
|
|
28500
|
+
if (existingIdx !== -1) {
|
|
28501
|
+
this.dynamicRows.splice(existingIdx, 1);
|
|
28502
|
+
}
|
|
28503
|
+
const mutable = this.asMutableProvider(this.storageProvider);
|
|
28504
|
+
const removed = this.dynamicRows.filter((r) => superseded.has(r.instance.name) && findIdPathCollision([r.instance, instance])).map((r) => r.instance.name);
|
|
28505
|
+
for (const removedId of removed) {
|
|
28506
|
+
this.withdrawDynamicSkill(removedId, mutable);
|
|
28507
|
+
}
|
|
28352
28508
|
const lineage = this.owner ? [this.owner] : [];
|
|
28353
28509
|
const row = this.makeRow(provideToken, instance, lineage, this);
|
|
28354
28510
|
this.dynamicRows.push(row);
|
|
28355
28511
|
this.dynamicContents.set(id, structuredClone(content));
|
|
28512
|
+
this.withdrawn.delete(id);
|
|
28356
28513
|
const generation = (this.dynamicGenerations.get(id) ?? 0) + 1;
|
|
28357
28514
|
this.dynamicGenerations.set(id, generation);
|
|
28358
|
-
|
|
28359
|
-
|
|
28360
|
-
|
|
28515
|
+
this.reindex();
|
|
28516
|
+
const providerWrites = mutable ? [
|
|
28517
|
+
...removed.map((removedId) => this.removeFromProvider(mutable, removedId)),
|
|
28518
|
+
this.syncProvider(id, async () => {
|
|
28361
28519
|
if (await mutable.exists(id)) {
|
|
28362
28520
|
await mutable.update(id, content);
|
|
28363
28521
|
} else {
|
|
28364
28522
|
await mutable.add(content);
|
|
28365
28523
|
}
|
|
28366
|
-
}
|
|
28524
|
+
}).catch((error) => {
|
|
28367
28525
|
this.scope.logger.warn(
|
|
28368
28526
|
`[SkillRegistry] Failed to index dynamic skill ${id} in storage provider: ${error.message}`
|
|
28369
28527
|
);
|
|
28370
|
-
}
|
|
28371
|
-
|
|
28372
|
-
this.reindex();
|
|
28528
|
+
})
|
|
28529
|
+
] : [];
|
|
28373
28530
|
this.bump("reset");
|
|
28531
|
+
await Promise.all(providerWrites);
|
|
28374
28532
|
let unregistered = false;
|
|
28375
28533
|
const unregister = async () => {
|
|
28376
28534
|
if (unregistered) return;
|
|
@@ -28383,32 +28541,99 @@ var init_skill_registry = __esm({
|
|
|
28383
28541
|
this.scope.logger.debug(
|
|
28384
28542
|
`[SkillRegistry] Registered dynamic skill ${id}${opts?.source ? ` (source: ${opts.source})` : ""}`
|
|
28385
28543
|
);
|
|
28386
|
-
return { id, unregister };
|
|
28544
|
+
return { id, unregister, removed };
|
|
28387
28545
|
}
|
|
28388
28546
|
/**
|
|
28389
28547
|
* Remove a previously-registered dynamic skill by id.
|
|
28390
28548
|
*/
|
|
28391
28549
|
async unregisterSkill(id) {
|
|
28392
|
-
|
|
28393
|
-
if (idx === -1) return false;
|
|
28394
|
-
this.dynamicRows.splice(idx, 1);
|
|
28395
|
-
this.dynamicContents.delete(id);
|
|
28396
|
-
this.dynamicGenerations.delete(id);
|
|
28550
|
+
if (!this.dynamicRows.some((r) => r.instance.name === id)) return false;
|
|
28397
28551
|
const mutable = this.asMutableProvider(this.storageProvider);
|
|
28398
|
-
|
|
28399
|
-
try {
|
|
28400
|
-
await mutable.remove(id);
|
|
28401
|
-
} catch (error) {
|
|
28402
|
-
this.scope.logger.warn(
|
|
28403
|
-
`[SkillRegistry] Failed to remove dynamic skill ${id} from storage provider: ${error.message}`
|
|
28404
|
-
);
|
|
28405
|
-
}
|
|
28406
|
-
}
|
|
28552
|
+
this.withdrawDynamicSkill(id, mutable);
|
|
28407
28553
|
this.reindex();
|
|
28554
|
+
const removal = mutable ? this.removeFromProvider(mutable, id) : void 0;
|
|
28408
28555
|
this.bump("reset");
|
|
28556
|
+
await removal;
|
|
28409
28557
|
this.scope.logger.debug(`[SkillRegistry] Unregistered dynamic skill ${id}`);
|
|
28410
28558
|
return true;
|
|
28411
28559
|
}
|
|
28560
|
+
/**
|
|
28561
|
+
* Take a dynamic skill out of the registry, synchronously. Its generation advances so a handle
|
|
28562
|
+
* from an earlier registration stays a no-op, and while a mutable provider still holds its copy it
|
|
28563
|
+
* is withdrawn from provider-backed reads.
|
|
28564
|
+
*/
|
|
28565
|
+
withdrawDynamicSkill(id, mutable) {
|
|
28566
|
+
this.dynamicRows.splice(
|
|
28567
|
+
this.dynamicRows.findIndex((r) => r.instance.name === id),
|
|
28568
|
+
1
|
|
28569
|
+
);
|
|
28570
|
+
this.dynamicContents.delete(id);
|
|
28571
|
+
this.dynamicGenerations.set(id, (this.dynamicGenerations.get(id) ?? 0) + 1);
|
|
28572
|
+
if (mutable) this.withdrawn.set(id, ++this.withdrawals);
|
|
28573
|
+
}
|
|
28574
|
+
/** Remove a withdrawn skill's copy from the provider. If that fails it stays withdrawn from reads. */
|
|
28575
|
+
async removeFromProvider(mutable, id) {
|
|
28576
|
+
const withdrawal = this.withdrawn.get(id);
|
|
28577
|
+
try {
|
|
28578
|
+
await this.syncProvider(id, () => mutable.remove(id));
|
|
28579
|
+
if (this.withdrawn.get(id) === withdrawal) this.withdrawn.delete(id);
|
|
28580
|
+
} catch (error) {
|
|
28581
|
+
this.scope.logger.warn(
|
|
28582
|
+
`[SkillRegistry] Failed to remove skill ${id} from storage provider; it stays hidden from provider-backed reads: ${error.message}`
|
|
28583
|
+
);
|
|
28584
|
+
}
|
|
28585
|
+
}
|
|
28586
|
+
/**
|
|
28587
|
+
* Run a storage-provider write for one skill id after the ones queued before it for that id, so a
|
|
28588
|
+
* removal that completes late cannot delete the copy a later registration added. The returned
|
|
28589
|
+
* promise settles with the write; a failure does not hold back the next one.
|
|
28590
|
+
*/
|
|
28591
|
+
syncProvider(id, write) {
|
|
28592
|
+
const run = (this.providerWrites.get(id) ?? Promise.resolve()).then(write);
|
|
28593
|
+
const settled = run.then(
|
|
28594
|
+
() => void 0,
|
|
28595
|
+
() => void 0
|
|
28596
|
+
);
|
|
28597
|
+
this.providerWrites.set(id, settled);
|
|
28598
|
+
void settled.then(() => {
|
|
28599
|
+
if (this.providerWrites.get(id) === settled) this.providerWrites.delete(id);
|
|
28600
|
+
});
|
|
28601
|
+
return run;
|
|
28602
|
+
}
|
|
28603
|
+
/**
|
|
28604
|
+
* After a child's change, take the skills that left it (and are not served here some other way)
|
|
28605
|
+
* out of this registry's storage provider too. The re-add on change only adds, so without this an
|
|
28606
|
+
* adopter's provider kept serving a skill the child had removed. Until the removal completes, and
|
|
28607
|
+
* if it fails, provider-backed reads skip it.
|
|
28608
|
+
*/
|
|
28609
|
+
withdrawGoneAdoptedSkills(previous) {
|
|
28610
|
+
const mutable = this.asMutableProvider(this.storageProvider);
|
|
28611
|
+
if (!mutable) return;
|
|
28612
|
+
const skillIdOf = (entry) => entry.metadata.id ?? entry.metadata.name;
|
|
28613
|
+
const live = new Set(this.listAllIndexed().map((r) => skillIdOf(r.instance)));
|
|
28614
|
+
for (const id of live) this.withdrawn.delete(id);
|
|
28615
|
+
for (const row of previous) {
|
|
28616
|
+
const id = skillIdOf(row.instance);
|
|
28617
|
+
if (live.has(id) || this.withdrawn.has(id)) continue;
|
|
28618
|
+
this.withdrawn.set(id, ++this.withdrawals);
|
|
28619
|
+
void this.removeFromProvider(mutable, id);
|
|
28620
|
+
}
|
|
28621
|
+
}
|
|
28622
|
+
/**
|
|
28623
|
+
* Which provider-backed rows a read that starts now must skip: a skill withdrawn when the read
|
|
28624
|
+
* starts (its removal may complete while the provider answers, clearing the withdrawal, yet the
|
|
28625
|
+
* answer can still hold it), or withdrawn by the time the answer arrives.
|
|
28626
|
+
*/
|
|
28627
|
+
hiddenFromProviderRead() {
|
|
28628
|
+
const atStart = new Set(this.withdrawn.keys());
|
|
28629
|
+
return {
|
|
28630
|
+
size: atStart.size,
|
|
28631
|
+
has: (meta) => {
|
|
28632
|
+
const id = meta.id ?? meta.name;
|
|
28633
|
+
return atStart.has(id) || this.withdrawn.has(id);
|
|
28634
|
+
}
|
|
28635
|
+
};
|
|
28636
|
+
}
|
|
28412
28637
|
/* -------------------- Internal helpers -------------------- */
|
|
28413
28638
|
asMutableProvider(provider) {
|
|
28414
28639
|
const candidate = provider;
|
|
@@ -29393,7 +29618,7 @@ var init_agent_instance = __esm({
|
|
|
29393
29618
|
"hideFromDiscovery"
|
|
29394
29619
|
]);
|
|
29395
29620
|
AgentInstance = class extends AgentEntry {
|
|
29396
|
-
|
|
29621
|
+
providerRegistry;
|
|
29397
29622
|
scope;
|
|
29398
29623
|
hooks;
|
|
29399
29624
|
/** The LLM adapter for this agent */
|
|
@@ -29411,11 +29636,11 @@ var init_agent_instance = __esm({
|
|
|
29411
29636
|
constructor(record, providers, owner) {
|
|
29412
29637
|
super(record);
|
|
29413
29638
|
this.owner = owner;
|
|
29414
|
-
this.
|
|
29639
|
+
this.providerRegistry = providers;
|
|
29415
29640
|
this.name = record.metadata.id ?? record.metadata.name;
|
|
29416
29641
|
this.id = record.metadata.id ?? record.metadata.name;
|
|
29417
29642
|
this.fullName = this.owner.id + ":" + this.name;
|
|
29418
|
-
this.scope = this.
|
|
29643
|
+
this.scope = this.providerRegistry.getActiveScope();
|
|
29419
29644
|
this.hooks = this.scope.hooks;
|
|
29420
29645
|
this.inputSchema = record.metadata.inputSchema ?? {};
|
|
29421
29646
|
this.outputSchema = record.metadata.outputSchema;
|
|
@@ -29474,11 +29699,11 @@ var init_agent_instance = __esm({
|
|
|
29474
29699
|
const adapterOptions = {
|
|
29475
29700
|
providerResolver: {
|
|
29476
29701
|
get: (token) => {
|
|
29477
|
-
return this.
|
|
29702
|
+
return this.providerRegistry.get(token);
|
|
29478
29703
|
},
|
|
29479
29704
|
tryGet: (token) => {
|
|
29480
29705
|
try {
|
|
29481
|
-
return this.
|
|
29706
|
+
return this.providerRegistry.get(token);
|
|
29482
29707
|
} catch {
|
|
29483
29708
|
return void 0;
|
|
29484
29709
|
}
|
|
@@ -29491,7 +29716,7 @@ var init_agent_instance = __esm({
|
|
|
29491
29716
|
}
|
|
29492
29717
|
};
|
|
29493
29718
|
try {
|
|
29494
|
-
const configService = this.
|
|
29719
|
+
const configService = this.providerRegistry.get(ConfigService);
|
|
29495
29720
|
if (configService) {
|
|
29496
29721
|
adapterOptions.configResolver = this.createConfigResolver(configService);
|
|
29497
29722
|
}
|
|
@@ -29599,7 +29824,7 @@ var init_agent_instance = __esm({
|
|
|
29599
29824
|
const toolMetadata = this.buildAgentToolMetadata();
|
|
29600
29825
|
const agentToolFunction = frontMcpTool(toolMetadata)(this.createAgentToolExecuteHandler());
|
|
29601
29826
|
const toolRecord = normalizeTool(agentToolFunction);
|
|
29602
|
-
this.agentToolInstance = new ToolInstance(toolRecord, this.
|
|
29827
|
+
this.agentToolInstance = new ToolInstance(toolRecord, this.providerRegistry, this.owner);
|
|
29603
29828
|
await this.agentToolInstance.ready;
|
|
29604
29829
|
this.scope.logger.debug(`Created agent tool instance: ${this.agentToolInstance.name} for agent ${this.name}`);
|
|
29605
29830
|
} catch (error) {
|
|
@@ -29641,16 +29866,34 @@ var init_agent_instance = __esm({
|
|
|
29641
29866
|
/**
|
|
29642
29867
|
* Create the execute handler for the agent tool.
|
|
29643
29868
|
*
|
|
29644
|
-
*
|
|
29645
|
-
*
|
|
29869
|
+
* The tool's `tools:call-tool` flow has already applied everything the agent declares
|
|
29870
|
+
* (authorities, rate limit, concurrency, timeout and plugin gates, copied onto the tool). The
|
|
29871
|
+
* handler then runs the agent through the `agents:call-agent` flow, so the hooks registered for
|
|
29872
|
+
* agent invocation (`AgentCallHook`, and the agent class's own hooks on that flow) run and the
|
|
29873
|
+
* agent's context gets the request's context providers. The flow's own gates are left to the
|
|
29874
|
+
* tool flow (`gatedBy`), so nothing is counted twice.
|
|
29646
29875
|
*/
|
|
29647
29876
|
createAgentToolExecuteHandler() {
|
|
29648
29877
|
return async (input, toolCtx) => {
|
|
29649
29878
|
const authInfo = toolCtx.authInfo;
|
|
29650
|
-
const
|
|
29651
|
-
const
|
|
29652
|
-
|
|
29653
|
-
|
|
29879
|
+
const progressToken = toolCtx._progressTokenInternal;
|
|
29880
|
+
const requestId = toolCtx._jsonRpcRequestIdInternal;
|
|
29881
|
+
return this.scope.runFlowForOutput("agents:call-agent", {
|
|
29882
|
+
request: {
|
|
29883
|
+
method: "tools/call",
|
|
29884
|
+
params: {
|
|
29885
|
+
name: this.id,
|
|
29886
|
+
arguments: input,
|
|
29887
|
+
...progressToken !== void 0 ? { _meta: { progressToken } } : {}
|
|
29888
|
+
}
|
|
29889
|
+
},
|
|
29890
|
+
ctx: {
|
|
29891
|
+
authInfo,
|
|
29892
|
+
...requestId !== void 0 ? { requestId } : {},
|
|
29893
|
+
...toolCtx.signal ? { signal: toolCtx.signal } : {}
|
|
29894
|
+
},
|
|
29895
|
+
gatedBy: "tools:call-tool"
|
|
29896
|
+
});
|
|
29654
29897
|
};
|
|
29655
29898
|
}
|
|
29656
29899
|
/**
|
|
@@ -29664,6 +29907,10 @@ var init_agent_instance = __esm({
|
|
|
29664
29907
|
getToolInstance() {
|
|
29665
29908
|
return this.agentToolInstance;
|
|
29666
29909
|
}
|
|
29910
|
+
/** The provider registry this agent was registered with (its app's). */
|
|
29911
|
+
get providers() {
|
|
29912
|
+
return this.providerRegistry;
|
|
29913
|
+
}
|
|
29667
29914
|
/**
|
|
29668
29915
|
* The tools this agent can call: those it declares and those its plugins contribute, as its private
|
|
29669
29916
|
* scope holds them. Empty until the agent is initialized.
|
|
@@ -29713,7 +29960,7 @@ var init_agent_instance = __esm({
|
|
|
29713
29960
|
case "VALUE" /* VALUE */:
|
|
29714
29961
|
return this.record.useValue;
|
|
29715
29962
|
case "FACTORY" /* FACTORY */:
|
|
29716
|
-
return this.record.useFactory(this.
|
|
29963
|
+
return this.record.useFactory(this.providerRegistry);
|
|
29717
29964
|
case "ESM" /* ESM */:
|
|
29718
29965
|
throw new AgentNotConfiguredError(`ESM agent "${this.name}" cannot be created via AgentInstance.create()`);
|
|
29719
29966
|
case "REMOTE" /* REMOTE */:
|
|
@@ -29732,11 +29979,13 @@ var init_agent_instance = __esm({
|
|
|
29732
29979
|
* @param llmAdapter - The LLM adapter (passed explicitly to avoid non-null assertion)
|
|
29733
29980
|
*/
|
|
29734
29981
|
buildAgentCtorArgs(input, ctx, llmAdapter) {
|
|
29735
|
-
const scope = this.
|
|
29982
|
+
const scope = this.providerRegistry.getActiveScope();
|
|
29736
29983
|
return {
|
|
29737
29984
|
metadata: this.metadata,
|
|
29738
29985
|
input,
|
|
29739
|
-
providers:
|
|
29986
|
+
// The request's context-aware providers when the agents:call-agent flow built them, so
|
|
29987
|
+
// `this.context` and CONTEXT-scoped providers resolve inside the agent.
|
|
29988
|
+
providers: ctx.contextProviders ?? this.providerRegistry,
|
|
29740
29989
|
logger: scope.logger,
|
|
29741
29990
|
authInfo: ctx.authInfo,
|
|
29742
29991
|
llmAdapter,
|
|
@@ -29807,7 +30056,8 @@ var init_agent_instance = __esm({
|
|
|
29807
30056
|
await this.assertToolAuthorized(tool, ctx.authInfo, args);
|
|
29808
30057
|
const runningTool = { name: tool.name, fullName: tool.fullName };
|
|
29809
30058
|
return runOnSurface(AGENT_SURFACE, () => {
|
|
29810
|
-
const
|
|
30059
|
+
const { contextProviders: _agentProviders, ...toolCtx } = ctx;
|
|
30060
|
+
const toolContext = runAsTool(runningTool, () => tool.create(args, toolCtx));
|
|
29811
30061
|
return runAsTool(runningTool, () => Promise.resolve(toolContext.execute(args)));
|
|
29812
30062
|
});
|
|
29813
30063
|
};
|
|
@@ -30346,6 +30596,53 @@ var init_agent_registry = __esm({
|
|
|
30346
30596
|
}
|
|
30347
30597
|
});
|
|
30348
30598
|
|
|
30599
|
+
// libs/sdk/src/channel/sources/completion-events.ts
|
|
30600
|
+
function completionEventsOf(scope) {
|
|
30601
|
+
let events = byScope.get(scope);
|
|
30602
|
+
if (!events) {
|
|
30603
|
+
events = new ScopeCompletionEvents();
|
|
30604
|
+
byScope.set(scope, events);
|
|
30605
|
+
}
|
|
30606
|
+
return events;
|
|
30607
|
+
}
|
|
30608
|
+
function completionOutputText(value) {
|
|
30609
|
+
if (value === void 0) return void 0;
|
|
30610
|
+
if (typeof value === "string") return value;
|
|
30611
|
+
try {
|
|
30612
|
+
return JSON.stringify(value);
|
|
30613
|
+
} catch {
|
|
30614
|
+
return String(value);
|
|
30615
|
+
}
|
|
30616
|
+
}
|
|
30617
|
+
var CompletionStream, ScopeCompletionEvents, byScope;
|
|
30618
|
+
var init_completion_events = __esm({
|
|
30619
|
+
"libs/sdk/src/channel/sources/completion-events.ts"() {
|
|
30620
|
+
"use strict";
|
|
30621
|
+
CompletionStream = class {
|
|
30622
|
+
listeners = /* @__PURE__ */ new Set();
|
|
30623
|
+
subscribe(listener) {
|
|
30624
|
+
this.listeners.add(listener);
|
|
30625
|
+
return () => {
|
|
30626
|
+
this.listeners.delete(listener);
|
|
30627
|
+
};
|
|
30628
|
+
}
|
|
30629
|
+
emit(event) {
|
|
30630
|
+
for (const listener of [...this.listeners]) {
|
|
30631
|
+
try {
|
|
30632
|
+
listener(event);
|
|
30633
|
+
} catch {
|
|
30634
|
+
}
|
|
30635
|
+
}
|
|
30636
|
+
}
|
|
30637
|
+
};
|
|
30638
|
+
ScopeCompletionEvents = class {
|
|
30639
|
+
agents = new CompletionStream();
|
|
30640
|
+
jobs = new CompletionStream();
|
|
30641
|
+
};
|
|
30642
|
+
byScope = /* @__PURE__ */ new WeakMap();
|
|
30643
|
+
}
|
|
30644
|
+
});
|
|
30645
|
+
|
|
30349
30646
|
// libs/sdk/src/agent/flows/call-agent.flow.ts
|
|
30350
30647
|
var import_auth26, import_guard6, import_lazy_zod60, import_protocol41, inputSchema15, outputSchema14, stateSchema14, plan14, name14, Stage14, CallAgentFlow;
|
|
30351
30648
|
var init_call_agent_flow = __esm({
|
|
@@ -30355,13 +30652,22 @@ var init_call_agent_flow = __esm({
|
|
|
30355
30652
|
import_guard6 = require("@frontmcp/guard");
|
|
30356
30653
|
import_lazy_zod60 = require("@frontmcp/lazy-zod");
|
|
30357
30654
|
import_protocol41 = require("@frontmcp/protocol");
|
|
30655
|
+
init_completion_events();
|
|
30358
30656
|
init_common();
|
|
30359
30657
|
init_errors();
|
|
30360
30658
|
init_hooks_utils();
|
|
30659
|
+
init_flow_context_providers();
|
|
30361
30660
|
inputSchema15 = import_lazy_zod60.z.object({
|
|
30362
30661
|
request: import_protocol41.CallToolRequestSchema,
|
|
30363
30662
|
// z.any() used because ctx is the MCP SDK's ToolCallExtra type which varies by SDK version
|
|
30364
|
-
ctx: import_lazy_zod60.z.any()
|
|
30663
|
+
ctx: import_lazy_zod60.z.any(),
|
|
30664
|
+
/**
|
|
30665
|
+
* Set when the agent's `invoke_<agent>` tool runs this flow. That tool's `tools:call-tool` flow
|
|
30666
|
+
* has already applied the agent's authorities, rate limit, concurrency and timeout (they are
|
|
30667
|
+
* copied onto the tool), so this flow's stages for them leave the checks to it instead of
|
|
30668
|
+
* counting the call twice. Their hooks still run.
|
|
30669
|
+
*/
|
|
30670
|
+
gatedBy: import_lazy_zod60.z.literal("tools:call-tool").optional()
|
|
30365
30671
|
});
|
|
30366
30672
|
outputSchema14 = import_protocol41.CallToolResultSchema;
|
|
30367
30673
|
stateSchema14 = import_lazy_zod60.z.object({
|
|
@@ -30394,7 +30700,13 @@ var init_call_agent_flow = __esm({
|
|
|
30394
30700
|
// Semaphore ticket for concurrency control (set by acquireSemaphore, used by releaseSemaphore)
|
|
30395
30701
|
semaphoreTicket: import_lazy_zod60.z.any().optional(),
|
|
30396
30702
|
// A timed-out execute() that is still running; its concurrency slot is released when it settles
|
|
30397
|
-
abandonedExecution: import_lazy_zod60.z.instanceof(Promise).optional()
|
|
30703
|
+
abandonedExecution: import_lazy_zod60.z.instanceof(Promise).optional(),
|
|
30704
|
+
// When execute() started, and the error it ended with (for the completion event)
|
|
30705
|
+
executionStartedAt: import_lazy_zod60.z.number().optional(),
|
|
30706
|
+
executionError: import_lazy_zod60.z.any().optional(),
|
|
30707
|
+
// The MCP result built from rawOutput (parseOutput), or why it could not be built
|
|
30708
|
+
parsedOutput: import_lazy_zod60.z.any().optional(),
|
|
30709
|
+
outputError: import_lazy_zod60.z.any().optional()
|
|
30398
30710
|
});
|
|
30399
30711
|
plan14 = {
|
|
30400
30712
|
pre: [
|
|
@@ -30407,12 +30719,16 @@ var init_call_agent_flow = __esm({
|
|
|
30407
30719
|
"acquireSemaphore"
|
|
30408
30720
|
],
|
|
30409
30721
|
execute: ["validateInput", "execute", "validateOutput"],
|
|
30410
|
-
finalize: ["releaseSemaphore", "releaseQuota", "finalize"]
|
|
30722
|
+
finalize: ["releaseSemaphore", "releaseQuota", "parseOutput", "emitCompletion", "finalize"]
|
|
30411
30723
|
};
|
|
30412
30724
|
name14 = "agents:call-agent";
|
|
30413
30725
|
({ Stage: Stage14 } = FlowHooksOf(name14));
|
|
30414
30726
|
CallAgentFlow = class extends FlowBase {
|
|
30415
30727
|
logger = this.scopeLogger.child("CallAgentFlow");
|
|
30728
|
+
/** Whether the `invoke_<agent>` tool's flow already applied the agent's gates. */
|
|
30729
|
+
get gatedByToolFlow() {
|
|
30730
|
+
return this.input.gatedBy === "tools:call-tool";
|
|
30731
|
+
}
|
|
30416
30732
|
async parseInput() {
|
|
30417
30733
|
this.logger.verbose("parseInput:start");
|
|
30418
30734
|
let method;
|
|
@@ -30479,6 +30795,10 @@ var init_call_agent_flow = __esm({
|
|
|
30479
30795
|
}
|
|
30480
30796
|
async checkEntryAuthorities() {
|
|
30481
30797
|
this.logger.verbose("checkEntryAuthorities:start");
|
|
30798
|
+
if (this.gatedByToolFlow) {
|
|
30799
|
+
this.logger.verbose("checkEntryAuthorities:skip (checked by the invoke tool flow)");
|
|
30800
|
+
return;
|
|
30801
|
+
}
|
|
30482
30802
|
const engine = this.scope.authoritiesEngine;
|
|
30483
30803
|
const ctxBuilder = this.scope.authoritiesContextBuilder;
|
|
30484
30804
|
if (!engine || !ctxBuilder) {
|
|
@@ -30544,14 +30864,17 @@ var init_call_agent_flow = __esm({
|
|
|
30544
30864
|
const progressToken = this.state.progressToken;
|
|
30545
30865
|
const authInfo = this.state.authInfo;
|
|
30546
30866
|
try {
|
|
30547
|
-
const
|
|
30867
|
+
const sessionKey = authInfo?.sessionId ?? "anonymous";
|
|
30868
|
+
const agentViews = await agent.providers.buildViews(sessionKey, new Map(this.deps), this.scope.providers);
|
|
30869
|
+
const contextProviders = new FlowContextProviders(agent.providers, agentViews.context);
|
|
30870
|
+
const context = agent.create(input.arguments, { ...ctx, progressToken, contextProviders });
|
|
30548
30871
|
this.appendContextHooks(hooksBoundTo(this.scope.hooks.getClsHooks(agent.record.provide), context));
|
|
30549
30872
|
context.mark("createAgentContext");
|
|
30550
30873
|
const frontmcpContext = context.tryGetContext();
|
|
30551
30874
|
const sdkAuthInfo = authInfo;
|
|
30552
|
-
|
|
30875
|
+
const jsonRpcRequestId = this.state.jsonRpcRequestId;
|
|
30876
|
+
if (frontmcpContext && sdkAuthInfo?.transport?.sendElicitRequest && jsonRpcRequestId !== void 0) {
|
|
30553
30877
|
const transport = sdkAuthInfo.transport;
|
|
30554
|
-
const jsonRpcRequestId = this.state.jsonRpcRequestId;
|
|
30555
30878
|
frontmcpContext.setTransport({
|
|
30556
30879
|
sendElicitRequest: transport.sendElicitRequest.bind(transport),
|
|
30557
30880
|
type: transport.type ?? "unknown",
|
|
@@ -30568,7 +30891,7 @@ var init_call_agent_flow = __esm({
|
|
|
30568
30891
|
async acquireQuota() {
|
|
30569
30892
|
this.logger.verbose("acquireQuota:start");
|
|
30570
30893
|
const manager = this.scope.rateLimitManager;
|
|
30571
|
-
if (!manager) {
|
|
30894
|
+
if (!manager || this.gatedByToolFlow) {
|
|
30572
30895
|
this.state.agentContext?.mark("acquireQuota");
|
|
30573
30896
|
this.logger.verbose("acquireQuota:done (no rate limit manager)");
|
|
30574
30897
|
return;
|
|
@@ -30592,7 +30915,7 @@ var init_call_agent_flow = __esm({
|
|
|
30592
30915
|
async acquireSemaphore() {
|
|
30593
30916
|
this.logger.verbose("acquireSemaphore:start");
|
|
30594
30917
|
const manager = this.scope.rateLimitManager;
|
|
30595
|
-
if (!manager) {
|
|
30918
|
+
if (!manager || this.gatedByToolFlow) {
|
|
30596
30919
|
this.state.agentContext?.mark("acquireSemaphore");
|
|
30597
30920
|
this.logger.verbose("acquireSemaphore:done (no rate limit manager)");
|
|
30598
30921
|
return;
|
|
@@ -30637,7 +30960,8 @@ var init_call_agent_flow = __esm({
|
|
|
30637
30960
|
}
|
|
30638
30961
|
agentContext.mark("execute");
|
|
30639
30962
|
const startTime = Date.now();
|
|
30640
|
-
|
|
30963
|
+
this.state.set("executionStartedAt", startTime);
|
|
30964
|
+
const timeoutMs = this.gatedByToolFlow ? agent.metadata.timeout?.executeMs === void 0 ? agent.metadata.execution?.timeout : void 0 : agent.metadata.timeout?.executeMs ?? agent.metadata.execution?.timeout ?? this.scope.rateLimitManager?.config?.defaultTimeout?.executeMs;
|
|
30641
30965
|
const running = (async () => {
|
|
30642
30966
|
agentContext.output = await agentContext.execute(agentContext.input);
|
|
30643
30967
|
})();
|
|
@@ -30648,6 +30972,7 @@ var init_call_agent_flow = __esm({
|
|
|
30648
30972
|
});
|
|
30649
30973
|
this.logger.verbose("execute:done");
|
|
30650
30974
|
} catch (error) {
|
|
30975
|
+
this.state.set("executionError", error);
|
|
30651
30976
|
if (error instanceof import_guard6.ExecutionTimeoutError) {
|
|
30652
30977
|
this.logger.warn("execute: agent execution timed out", {
|
|
30653
30978
|
agent: agent.metadata.name,
|
|
@@ -30656,6 +30981,7 @@ var init_call_agent_flow = __esm({
|
|
|
30656
30981
|
this.state.set("abandonedExecution", running);
|
|
30657
30982
|
throw error;
|
|
30658
30983
|
}
|
|
30984
|
+
if (this.gatedByToolFlow) throw error;
|
|
30659
30985
|
throw new AgentExecutionError(agent.metadata.name, error instanceof Error ? error : void 0);
|
|
30660
30986
|
}
|
|
30661
30987
|
}
|
|
@@ -30693,9 +31019,34 @@ var init_call_agent_flow = __esm({
|
|
|
30693
31019
|
this.state.agentContext?.mark("releaseQuota");
|
|
30694
31020
|
this.logger.verbose("releaseQuota:done");
|
|
30695
31021
|
}
|
|
31022
|
+
async parseOutput() {
|
|
31023
|
+
const { agent, rawOutput } = this.state;
|
|
31024
|
+
if (!agent || rawOutput === void 0) return;
|
|
31025
|
+
const parseResult = agent.safeParseOutput(rawOutput);
|
|
31026
|
+
if (parseResult.success) this.state.set("parsedOutput", parseResult.data);
|
|
31027
|
+
else this.state.set("outputError", parseResult.error);
|
|
31028
|
+
}
|
|
31029
|
+
async emitCompletion() {
|
|
31030
|
+
const { agent, agentContext, executionStartedAt, executionError, outputError, authInfo } = this.state;
|
|
31031
|
+
if (!agent || executionStartedAt === void 0) return;
|
|
31032
|
+
if (executionError instanceof ElicitationFallbackRequired || executionError instanceof FlowControl) return;
|
|
31033
|
+
const failure = executionError ?? (outputError !== void 0 ? new InvalidOutputError() : void 0);
|
|
31034
|
+
const failed = failure !== void 0;
|
|
31035
|
+
const requestId = this.tryGetContext()?.requestId;
|
|
31036
|
+
const sessionId = typeof authInfo?.sessionId === "string" && authInfo.sessionId ? authInfo.sessionId : void 0;
|
|
31037
|
+
completionEventsOf(this.scope).agents.emit({
|
|
31038
|
+
agentId: agent.id,
|
|
31039
|
+
agentName: agent.name,
|
|
31040
|
+
status: failed ? "error" : "success",
|
|
31041
|
+
durationMs: Date.now() - executionStartedAt,
|
|
31042
|
+
...failed ? { error: failure instanceof Error ? failure.message : String(failure) } : { output: completionOutputText(agentContext?.output) },
|
|
31043
|
+
...requestId ? { runId: requestId } : {},
|
|
31044
|
+
...sessionId ? { sessionId } : {}
|
|
31045
|
+
});
|
|
31046
|
+
}
|
|
30696
31047
|
async finalize() {
|
|
30697
31048
|
this.logger.verbose("finalize:start");
|
|
30698
|
-
const { agent, rawOutput, executionMeta } = this.state;
|
|
31049
|
+
const { agent, rawOutput, parsedOutput, outputError, executionMeta } = this.state;
|
|
30699
31050
|
if (!agent) {
|
|
30700
31051
|
this.logger.verbose("finalize: skipping (no agent in state)");
|
|
30701
31052
|
return;
|
|
@@ -30704,15 +31055,14 @@ var init_call_agent_flow = __esm({
|
|
|
30704
31055
|
this.logger.verbose("finalize: skipping (no output - execute stage likely failed)");
|
|
30705
31056
|
return;
|
|
30706
31057
|
}
|
|
30707
|
-
|
|
30708
|
-
if (!parseResult.success) {
|
|
31058
|
+
if (outputError !== void 0 || parsedOutput === void 0) {
|
|
30709
31059
|
this.logger.error("finalize: output validation failed", {
|
|
30710
31060
|
agent: agent.metadata.name,
|
|
30711
|
-
errors:
|
|
31061
|
+
errors: outputError
|
|
30712
31062
|
});
|
|
30713
31063
|
throw new InvalidOutputError();
|
|
30714
31064
|
}
|
|
30715
|
-
const result =
|
|
31065
|
+
const result = parsedOutput;
|
|
30716
31066
|
if (executionMeta) {
|
|
30717
31067
|
result._meta = {
|
|
30718
31068
|
...result._meta,
|
|
@@ -30778,6 +31128,12 @@ var init_call_agent_flow = __esm({
|
|
|
30778
31128
|
__decorateClass([
|
|
30779
31129
|
Stage14("releaseQuota")
|
|
30780
31130
|
], CallAgentFlow.prototype, "releaseQuota", 1);
|
|
31131
|
+
__decorateClass([
|
|
31132
|
+
Stage14("parseOutput")
|
|
31133
|
+
], CallAgentFlow.prototype, "parseOutput", 1);
|
|
31134
|
+
__decorateClass([
|
|
31135
|
+
Stage14("emitCompletion")
|
|
31136
|
+
], CallAgentFlow.prototype, "emitCompletion", 1);
|
|
30781
31137
|
__decorateClass([
|
|
30782
31138
|
Stage14("finalize")
|
|
30783
31139
|
], CallAgentFlow.prototype, "finalize", 1);
|
|
@@ -31580,10 +31936,17 @@ var init_mrtr = __esm({
|
|
|
31580
31936
|
/** Per-kind call counters, used to derive stable keys across a replay. */
|
|
31581
31937
|
counters = { elicitation: 0, sampling: 0, roots: 0 };
|
|
31582
31938
|
clientCapabilities;
|
|
31939
|
+
/**
|
|
31940
|
+
* Whether the client itself declared this revision. `false` when the SERVER served an
|
|
31941
|
+
* unversioned call as 2026-07-28 (`transport.defaultProtocolVersion`, the Worker default): such a
|
|
31942
|
+
* client, typically a 2025-03-26 one, never agreed to the revision's input-request contract.
|
|
31943
|
+
*/
|
|
31944
|
+
clientDeclaredRevision;
|
|
31583
31945
|
binding;
|
|
31584
31946
|
constructor(params) {
|
|
31585
31947
|
this.responses = { ...params.carriedResponses ?? {}, ...params.inputResponses ?? {} };
|
|
31586
31948
|
this.clientCapabilities = params.clientCapabilities;
|
|
31949
|
+
this.clientDeclaredRevision = params.clientDeclaredRevision ?? true;
|
|
31587
31950
|
this.binding = params.binding;
|
|
31588
31951
|
}
|
|
31589
31952
|
/** True when the client declared the capability a given request kind needs. */
|
|
@@ -31929,7 +32292,7 @@ function shouldCreateTask(params) {
|
|
|
31929
32292
|
return { kind: "create", owner: ownership.owner };
|
|
31930
32293
|
}
|
|
31931
32294
|
async function resumeTask(params) {
|
|
31932
|
-
const { scope, record, authInfo, clientCapabilities, frontmcpContext } = params;
|
|
32295
|
+
const { scope, record, authInfo, clientCapabilities, frontmcpContext, clientDeclaredRevision } = params;
|
|
31933
32296
|
const registry = scope.tasks;
|
|
31934
32297
|
const runner = registry?.runner;
|
|
31935
32298
|
if (!runner) {
|
|
@@ -31940,6 +32303,8 @@ async function resumeTask(params) {
|
|
|
31940
32303
|
new MrtrExchange({
|
|
31941
32304
|
carriedResponses: record.inputResponses ?? {},
|
|
31942
32305
|
clientCapabilities,
|
|
32306
|
+
// A caller that never declared the revision is refused an elicitation as a legacy caller is.
|
|
32307
|
+
clientDeclaredRevision,
|
|
31943
32308
|
binding: {
|
|
31944
32309
|
principal: resolveTaskPrincipal(authInfo),
|
|
31945
32310
|
binding: computeRequestBinding("tasks/resume", { name: record.taskId })
|
|
@@ -31986,6 +32351,7 @@ async function dispatch20260728(options) {
|
|
|
31986
32351
|
scope,
|
|
31987
32352
|
body,
|
|
31988
32353
|
clientCapabilities,
|
|
32354
|
+
clientDeclaredRevision,
|
|
31989
32355
|
frontmcpContext,
|
|
31990
32356
|
authInfo,
|
|
31991
32357
|
isAnonymous,
|
|
@@ -32032,7 +32398,8 @@ async function dispatch20260728(options) {
|
|
|
32032
32398
|
record,
|
|
32033
32399
|
authInfo: { ...authInfo ?? {}, sessionId: ownership.owner },
|
|
32034
32400
|
clientCapabilities,
|
|
32035
|
-
frontmcpContext
|
|
32401
|
+
frontmcpContext,
|
|
32402
|
+
clientDeclaredRevision
|
|
32036
32403
|
})
|
|
32037
32404
|
});
|
|
32038
32405
|
return outcome.kind === "result" ? { kind: "result", result: decorate(outcome.result) } : outcome;
|
|
@@ -32062,7 +32429,8 @@ async function dispatch20260728(options) {
|
|
|
32062
32429
|
inputResponses: params["inputResponses"],
|
|
32063
32430
|
carriedResponses: carried.ok ? carried.responses : {},
|
|
32064
32431
|
clientCapabilities,
|
|
32065
|
-
binding
|
|
32432
|
+
binding,
|
|
32433
|
+
clientDeclaredRevision
|
|
32066
32434
|
});
|
|
32067
32435
|
frontmcpContext?.setMrtrExchange(exchange);
|
|
32068
32436
|
if (notificationSink) frontmcpContext?.setRequestNotificationSink(notificationSink);
|
|
@@ -38029,6 +38397,8 @@ var init_oauth_authorize_flow = __esm({
|
|
|
38029
38397
|
if (state) {
|
|
38030
38398
|
url.searchParams.set("state", state);
|
|
38031
38399
|
}
|
|
38400
|
+
const issuer = this.resolveIssuer(this.rawInput.request);
|
|
38401
|
+
if (issuer) url.searchParams.set("iss", issuer);
|
|
38032
38402
|
this.respond(httpRespond.redirect(url.toString()));
|
|
38033
38403
|
return;
|
|
38034
38404
|
}
|
|
@@ -40148,7 +40518,7 @@ var init_oauth_provider_callback_flow = __esm({
|
|
|
40148
40518
|
if (session.state) {
|
|
40149
40519
|
url.searchParams.set("state", session.state);
|
|
40150
40520
|
}
|
|
40151
|
-
url.searchParams.set("iss", this.getLocalAuth().
|
|
40521
|
+
url.searchParams.set("iss", this.getLocalAuth().issuerFor(this.rawInput.request));
|
|
40152
40522
|
this.logger.info(
|
|
40153
40523
|
`Federated auth complete: ${selectedProviderIds.length} providers authenticated, redirecting to client`
|
|
40154
40524
|
);
|
|
@@ -41061,7 +41431,7 @@ var init_oauth_userinfo_flow = __esm({
|
|
|
41061
41431
|
return;
|
|
41062
41432
|
}
|
|
41063
41433
|
const localAuth = this.scope.auth;
|
|
41064
|
-
const issuer = localAuth.
|
|
41434
|
+
const issuer = localAuth.acceptedIssuersFor?.(this.rawInput.request);
|
|
41065
41435
|
const result = await localAuth.verifyGatewayToken(token, baseUrl, void 0, issuer);
|
|
41066
41436
|
if (!result.ok) {
|
|
41067
41437
|
this.logger.warn("userinfo: token verification failed", { error: result.error });
|
|
@@ -41145,7 +41515,14 @@ var init_session_verify_flow = __esm({
|
|
|
41145
41515
|
import_utils78 = require("@frontmcp/utils");
|
|
41146
41516
|
init_notification_service();
|
|
41147
41517
|
init_session_id_utils();
|
|
41148
|
-
inputSchema23 = httpRequestInputSchema
|
|
41518
|
+
inputSchema23 = httpRequestInputSchema.extend({
|
|
41519
|
+
/**
|
|
41520
|
+
* The request is served without a session (MCP 2026-07-28): an anonymous or static-key caller is
|
|
41521
|
+
* identified for this request only, and no session is minted for it (#629). Minting one needs
|
|
41522
|
+
* `MCP_SESSION_SECRET`, for a session id the client would never present.
|
|
41523
|
+
*/
|
|
41524
|
+
sessionless: import_lazy_zod72.z.boolean().optional()
|
|
41525
|
+
});
|
|
41149
41526
|
stateSchema22 = import_lazy_zod72.z.object({
|
|
41150
41527
|
baseUrl: import_lazy_zod72.z.string().min(1),
|
|
41151
41528
|
authorizationHeader: import_lazy_zod72.z.string().optional(),
|
|
@@ -41203,20 +41580,22 @@ var init_session_verify_flow = __esm({
|
|
|
41203
41580
|
* Encapsulates the shared logic for session creation, payload encryption, and user derivation.
|
|
41204
41581
|
*/
|
|
41205
41582
|
createAnonymousSession(options) {
|
|
41206
|
-
const { authMode,
|
|
41583
|
+
const { authMode, subject, sessionIdHeader } = options;
|
|
41207
41584
|
this.logger.verbose("createAnonymousSession", { authMode, hasExistingSession: !!sessionIdHeader });
|
|
41585
|
+
if (this.rawInput.sessionless) {
|
|
41586
|
+
this.respond({
|
|
41587
|
+
kind: "authorized",
|
|
41588
|
+
authorization: { token: "", user: (0, import_auth39.anonymousCallerClaims)(options, (0, import_utils78.randomUUID)()) }
|
|
41589
|
+
});
|
|
41590
|
+
return;
|
|
41591
|
+
}
|
|
41208
41592
|
const machineId = (0, import_utils78.getMachineId)();
|
|
41209
41593
|
const authSignature = subject ? `${authMode}:${subject}` : authMode;
|
|
41210
41594
|
if (sessionIdHeader) {
|
|
41211
41595
|
const existingPayload = decryptPublicSession(sessionIdHeader, authSignature);
|
|
41212
41596
|
if (existingPayload && existingPayload.nodeId === machineId) {
|
|
41213
41597
|
const anonId = existingPayload.uuid ?? `${existingPayload.iat * 1e3}`;
|
|
41214
|
-
const user2 =
|
|
41215
|
-
sub: subject ?? `anon:${anonId}`,
|
|
41216
|
-
iss: issuer,
|
|
41217
|
-
name: subject ? "Static token" : "Anonymous",
|
|
41218
|
-
scope: scopes.join(" ")
|
|
41219
|
-
};
|
|
41598
|
+
const user2 = (0, import_auth39.anonymousCallerClaims)(options, anonId);
|
|
41220
41599
|
this.respond({
|
|
41221
41600
|
kind: "authorized",
|
|
41222
41601
|
authorization: {
|
|
@@ -41230,13 +41609,8 @@ var init_session_verify_flow = __esm({
|
|
|
41230
41609
|
this.logger.verbose("createAnonymousSession: ignoring unrecognized client session id; minting a fresh one");
|
|
41231
41610
|
}
|
|
41232
41611
|
const now = Date.now();
|
|
41233
|
-
const user = {
|
|
41234
|
-
sub: subject ?? `anon:${(0, import_utils78.randomUUID)()}`,
|
|
41235
|
-
iss: issuer,
|
|
41236
|
-
name: subject ? "Static token" : "Anonymous",
|
|
41237
|
-
scope: scopes.join(" ")
|
|
41238
|
-
};
|
|
41239
41612
|
const uuid = (0, import_utils78.randomUUID)();
|
|
41613
|
+
const user = (0, import_auth39.anonymousCallerClaims)(options, uuid);
|
|
41240
41614
|
const platformDetectionConfig = this.scope.metadata.transport?.platformDetection;
|
|
41241
41615
|
const platformType = detectPlatformFromUserAgent(this.state.userAgent, platformDetectionConfig);
|
|
41242
41616
|
const payload = {
|
|
@@ -41429,7 +41803,7 @@ var init_session_verify_flow = __esm({
|
|
|
41429
41803
|
} else {
|
|
41430
41804
|
const configuredAudience = (0, import_auth12.isOrchestratedMode)(authOptions) ? authOptions.expectedAudience : void 0;
|
|
41431
41805
|
const resource = computeResource(this.rawInput.request, this.scope.entryPath, this.scope.routeBase);
|
|
41432
|
-
const issuer = auth.
|
|
41806
|
+
const issuer = auth.acceptedIssuersFor?.(this.rawInput.request);
|
|
41433
41807
|
verify = auth.verifyGatewayToken(token, this.state.required.baseUrl, configuredAudience ?? resource, issuer);
|
|
41434
41808
|
}
|
|
41435
41809
|
const result = await verify;
|
|
@@ -41722,6 +42096,9 @@ var init_well_known_oauth_authorization_server_flow = __esm({
|
|
|
41722
42096
|
wellKnownAsStateSchema = import_lazy_zod74.z.object({
|
|
41723
42097
|
baseUrl: import_lazy_zod74.z.string().min(1),
|
|
41724
42098
|
// baseUrl + entryPrefix (unsuffixed)
|
|
42099
|
+
// The issuer this server names in answer to the request (`LocalPrimaryAuth.issuerFor`, #629): the
|
|
42100
|
+
// one on its authorization responses and tokens. Without it, `baseUrl`.
|
|
42101
|
+
issuer: import_lazy_zod74.z.string().min(1).optional(),
|
|
41725
42102
|
// Root origin (proto://host WITHOUT the entryPath prefix). The OAuth
|
|
41726
42103
|
// endpoints (/oauth/authorize, /oauth/token, /oauth/register, …) are
|
|
41727
42104
|
// registered at literal root paths by their flows, NOT under entryPath, so
|
|
@@ -41753,6 +42130,9 @@ var init_well_known_oauth_authorization_server_flow = __esm({
|
|
|
41753
42130
|
if (!request) throw new FlowInputMissingError("request", "well-known:oauth-authorization-server");
|
|
41754
42131
|
const { metadata } = this.scope;
|
|
41755
42132
|
const baseUrl = getRequestBaseUrl(request, this.scope.entryPath);
|
|
42133
|
+
const issuer = this.scope.auth?.issuerFor?.(
|
|
42134
|
+
request
|
|
42135
|
+
);
|
|
41756
42136
|
const oauthBaseUrl = getRequestBaseUrl(request);
|
|
41757
42137
|
let cimdEnabled = true;
|
|
41758
42138
|
if (metadata.auth && (0, import_auth12.isOrchestratedMode)(metadata.auth)) {
|
|
@@ -41761,6 +42141,7 @@ var init_well_known_oauth_authorization_server_flow = __esm({
|
|
|
41761
42141
|
this.state.set(
|
|
41762
42142
|
wellKnownAsStateSchema.parse({
|
|
41763
42143
|
baseUrl,
|
|
42144
|
+
issuer,
|
|
41764
42145
|
oauthBaseUrl,
|
|
41765
42146
|
// The scopes this server grants (`allowedScopes`, #262): the literal
|
|
41766
42147
|
// entries; a `*` glob names no scope a client could ask for.
|
|
@@ -41775,6 +42156,7 @@ var init_well_known_oauth_authorization_server_flow = __esm({
|
|
|
41775
42156
|
);
|
|
41776
42157
|
}
|
|
41777
42158
|
async collectData() {
|
|
42159
|
+
const { issuer } = this.state;
|
|
41778
42160
|
const {
|
|
41779
42161
|
baseUrl,
|
|
41780
42162
|
oauthBaseUrl,
|
|
@@ -41785,7 +42167,7 @@ var init_well_known_oauth_authorization_server_flow = __esm({
|
|
|
41785
42167
|
cimdEnabled
|
|
41786
42168
|
} = this.state.required;
|
|
41787
42169
|
if (isOrchestrated) {
|
|
41788
|
-
const baseIssuer =
|
|
42170
|
+
const baseIssuer = issuer ?? baseUrl;
|
|
41789
42171
|
this.respond({
|
|
41790
42172
|
kind: "json",
|
|
41791
42173
|
contentType: "application/json; charset=utf-8",
|
|
@@ -41812,6 +42194,8 @@ var init_well_known_oauth_authorization_server_flow = __esm({
|
|
|
41812
42194
|
grant_types_supported: ["authorization_code", "refresh_token"],
|
|
41813
42195
|
scopes_supported: scopesSupported,
|
|
41814
42196
|
code_challenge_methods_supported: ["S256"],
|
|
42197
|
+
// Every authorization response, error responses included, carries `iss` (RFC 9207 §2, §3).
|
|
42198
|
+
authorization_response_iss_parameter_supported: true,
|
|
41815
42199
|
// CIMD support advertisement per draft-ietf-oauth-client-id-metadata-document-00
|
|
41816
42200
|
client_id_metadata_document_supported: cimdEnabled
|
|
41817
42201
|
}
|
|
@@ -41860,14 +42244,16 @@ var init_well_known_prm_flow = __esm({
|
|
|
41860
42244
|
stateSchema24 = import_lazy_zod75.z.object({
|
|
41861
42245
|
resource: import_lazy_zod75.z.string().min(1),
|
|
41862
42246
|
baseUrl: import_lazy_zod75.z.string().min(1),
|
|
41863
|
-
|
|
42247
|
+
// The authorization server to name: the issuer this server names for the request (#629).
|
|
42248
|
+
authorizationServer: import_lazy_zod75.z.string().min(1).optional(),
|
|
42249
|
+
scopesSupported: import_lazy_zod75.z.array(import_lazy_zod75.z.string()),
|
|
41864
42250
|
isOrchestrated: import_lazy_zod75.z.boolean()
|
|
41865
42251
|
});
|
|
41866
42252
|
outputSchema24 = HttpJsonSchema.extend({
|
|
41867
42253
|
body: import_lazy_zod75.z.object({
|
|
41868
42254
|
resource: import_lazy_zod75.z.string().min(1),
|
|
41869
42255
|
authorization_servers: import_lazy_zod75.z.array(import_lazy_zod75.z.string().min(1)).min(1),
|
|
41870
|
-
scopes_supported: import_lazy_zod75.z.array(import_lazy_zod75.z.string()).
|
|
42256
|
+
scopes_supported: import_lazy_zod75.z.array(import_lazy_zod75.z.string()).optional(),
|
|
41871
42257
|
bearer_methods_supported: import_lazy_zod75.z.array(import_lazy_zod75.z.string()).default(["header"])
|
|
41872
42258
|
}).passthrough()
|
|
41873
42259
|
});
|
|
@@ -41891,12 +42277,17 @@ var init_well_known_prm_flow = __esm({
|
|
|
41891
42277
|
if (!request) throw new FlowInputMissingError("request", "well-known:prm");
|
|
41892
42278
|
const resource = computeResource(request, scope.entryPath, scope.routeBase);
|
|
41893
42279
|
const baseUrl = getRequestBaseUrl(request, scope.entryPath);
|
|
41894
|
-
const
|
|
42280
|
+
const authorizationServer = scope.auth?.issuerFor?.(request);
|
|
42281
|
+
const authOptions = scope.auth?.options ?? scope.metadata.auth;
|
|
42282
|
+
const granted = (0, import_auth42.resourceScopesFor)(authOptions);
|
|
42283
|
+
const orchestrated = authOptions?.mode === "local" || authOptions?.mode === "remote";
|
|
42284
|
+
const scopesSupported = orchestrated ? granted : [.../* @__PURE__ */ new Set([...granted, ...scope.getAllSupportedScopes()])];
|
|
41895
42285
|
this.state.set(
|
|
41896
42286
|
stateSchema24.parse({
|
|
41897
42287
|
resource,
|
|
41898
42288
|
baseUrl,
|
|
41899
|
-
|
|
42289
|
+
authorizationServer,
|
|
42290
|
+
scopesSupported,
|
|
41900
42291
|
isOrchestrated: false
|
|
41901
42292
|
//scope.orchestrated,// TODO: fix
|
|
41902
42293
|
})
|
|
@@ -41904,6 +42295,8 @@ var init_well_known_prm_flow = __esm({
|
|
|
41904
42295
|
}
|
|
41905
42296
|
async collectData() {
|
|
41906
42297
|
const { resource, baseUrl, scopesSupported, isOrchestrated } = this.state.required;
|
|
42298
|
+
const { authorizationServer } = this.state;
|
|
42299
|
+
const scopes = scopesSupported.length > 0 ? { scopes_supported: scopesSupported } : {};
|
|
41907
42300
|
if (isOrchestrated) {
|
|
41908
42301
|
this.respond({
|
|
41909
42302
|
kind: "json",
|
|
@@ -41915,8 +42308,8 @@ var init_well_known_prm_flow = __esm({
|
|
|
41915
42308
|
headers: { "cache-control": "no-store" },
|
|
41916
42309
|
body: {
|
|
41917
42310
|
resource,
|
|
41918
|
-
authorization_servers: [baseUrl],
|
|
41919
|
-
|
|
42311
|
+
authorization_servers: [authorizationServer ?? baseUrl],
|
|
42312
|
+
...scopes,
|
|
41920
42313
|
bearer_methods_supported: ["header"]
|
|
41921
42314
|
}
|
|
41922
42315
|
});
|
|
@@ -41929,8 +42322,8 @@ var init_well_known_prm_flow = __esm({
|
|
|
41929
42322
|
headers: { "cache-control": "no-store" },
|
|
41930
42323
|
body: {
|
|
41931
42324
|
resource,
|
|
41932
|
-
authorization_servers: [baseUrl],
|
|
41933
|
-
|
|
42325
|
+
authorization_servers: [authorizationServer ?? baseUrl],
|
|
42326
|
+
...scopes,
|
|
41934
42327
|
bearer_methods_supported: ["header"]
|
|
41935
42328
|
}
|
|
41936
42329
|
});
|
|
@@ -42078,6 +42471,14 @@ function audienceMatchesResource(aud, expected) {
|
|
|
42078
42471
|
function isUnsupportedOperation(error) {
|
|
42079
42472
|
return error instanceof import_utils80.StorageNotSupportedError;
|
|
42080
42473
|
}
|
|
42474
|
+
function originOf2(value) {
|
|
42475
|
+
try {
|
|
42476
|
+
const origin = new URL(value).origin;
|
|
42477
|
+
return origin === "null" ? void 0 : origin;
|
|
42478
|
+
} catch {
|
|
42479
|
+
return void 0;
|
|
42480
|
+
}
|
|
42481
|
+
}
|
|
42081
42482
|
var import_jose, import_auth46, import_utils80, MIN_HS256_SECRET_BYTES, defaultNoAuthSecret, RESERVED_JWT_CLAIMS, LocalPrimaryAuth;
|
|
42082
42483
|
var init_instance_local_primary_auth = __esm({
|
|
42083
42484
|
"libs/sdk/src/auth/instances/instance.local-primary-auth.ts"() {
|
|
@@ -42133,7 +42534,7 @@ var init_instance_local_primary_auth = __esm({
|
|
|
42133
42534
|
this.port = this.providers.getActiveScope().metadata.http?.port ?? 3001;
|
|
42134
42535
|
this.host = (0, import_utils80.getEnv)("FRONTMCP_PUBLIC_HOST")?.trim() || "localhost";
|
|
42135
42536
|
this.issuer = this.deriveIssuer(options);
|
|
42136
|
-
this.
|
|
42537
|
+
this.publicHostPinned = !!(0, import_utils80.getEnv)("FRONTMCP_PUBLIC_HOST")?.trim();
|
|
42137
42538
|
const jwtSecret = (0, import_utils80.getEnv)("JWT_SECRET");
|
|
42138
42539
|
const hasJwtSecret = !!jwtSecret?.trim();
|
|
42139
42540
|
if (hasJwtSecret && jwtSecret) {
|
|
@@ -42174,8 +42575,8 @@ var init_instance_local_primary_auth = __esm({
|
|
|
42174
42575
|
port;
|
|
42175
42576
|
/** The boot-time issuer: the configured one, else this server's listener address (see {@link issuerFor}). */
|
|
42176
42577
|
issuer;
|
|
42177
|
-
/** Whether
|
|
42178
|
-
|
|
42578
|
+
/** Whether `FRONTMCP_PUBLIC_HOST` names the boot-time issuer's host, pinning it for every request. */
|
|
42579
|
+
publicHostPinned;
|
|
42179
42580
|
keys = [];
|
|
42180
42581
|
secret;
|
|
42181
42582
|
logger;
|
|
@@ -42333,27 +42734,52 @@ var init_instance_local_primary_auth = __esm({
|
|
|
42333
42734
|
return void 0;
|
|
42334
42735
|
}
|
|
42335
42736
|
/**
|
|
42336
|
-
* The issuer
|
|
42337
|
-
* `
|
|
42338
|
-
*
|
|
42737
|
+
* The issuer this server names in answer to `request`: in its discovery
|
|
42738
|
+
* documents (`/.well-known/oauth-authorization-server`'s `issuer`, the
|
|
42739
|
+
* protected resource metadata's `authorization_servers`), on its
|
|
42740
|
+
* authorization responses (the RFC 9207 `iss`, errors included), and as the
|
|
42741
|
+
* `iss` of the tokens it issues; the tokens it accepts there must name it
|
|
42742
|
+
* (#269, and see {@link acceptedIssuersFor}). Every entry point asks this, so
|
|
42743
|
+
* none of them can disagree, on the Node server and under a Web fetch
|
|
42744
|
+
* handler alike (#629). In order:
|
|
42339
42745
|
*
|
|
42340
|
-
*
|
|
42341
|
-
*
|
|
42342
|
-
*
|
|
42343
|
-
*
|
|
42344
|
-
*
|
|
42345
|
-
*
|
|
42346
|
-
* Durable Object) has no listener the boot-time issuer could describe, so
|
|
42347
|
-
* the issuer is the request's own origin plus this scope's path;
|
|
42348
|
-
* - otherwise (the Node server) the boot-time issuer.
|
|
42746
|
+
* 1. a configured issuer (`issuer` / `local.issuer`);
|
|
42747
|
+
* 2. with `FRONTMCP_PUBLIC_URL` pinned, that URL plus this scope's path;
|
|
42748
|
+
* 3. with `FRONTMCP_PUBLIC_HOST` set, the boot-time issuer it names the host
|
|
42749
|
+
* of (`http://<host>:<http.port><path>`);
|
|
42750
|
+
* 4. the request's own origin plus this scope's path;
|
|
42751
|
+
* 5. without a request, the boot-time issuer.
|
|
42349
42752
|
*/
|
|
42350
42753
|
issuerFor(request) {
|
|
42351
|
-
|
|
42352
|
-
if (
|
|
42754
|
+
const configured = this.configuredIssuer(this.options);
|
|
42755
|
+
if (configured !== void 0) return configured;
|
|
42756
|
+
const pinned = getPinnedPublicUrl();
|
|
42757
|
+
if (pinned !== void 0) {
|
|
42758
|
+
return request ? computeIssuer(request, this.scope.entryPath, this.scope.routeBase) : `${pinned}${this.scope.fullPath}`;
|
|
42759
|
+
}
|
|
42760
|
+
if (this.publicHostPinned || !request) return this.issuer;
|
|
42761
|
+
return computeIssuer(request, this.scope.entryPath, this.scope.routeBase);
|
|
42762
|
+
}
|
|
42763
|
+
/**
|
|
42764
|
+
* The issuers a token presented with `request` may name: {@link issuerFor}
|
|
42765
|
+
* the request. When the issuer follows the request (nothing pins it) and
|
|
42766
|
+
* local or remote mode lists `expectedAudience`, a token for any listed
|
|
42767
|
+
* address was issued under that address's issuer, so each listed address's
|
|
42768
|
+
* issuer is accepted too, and so is the boot-time issuer that 1.8.4 named on
|
|
42769
|
+
* those tokens. The token's `aud` must still name a listed resource.
|
|
42770
|
+
*/
|
|
42771
|
+
acceptedIssuersFor(request) {
|
|
42772
|
+
const issuer = this.issuerFor(request);
|
|
42353
42773
|
const options = this.options;
|
|
42354
|
-
|
|
42355
|
-
|
|
42356
|
-
|
|
42774
|
+
const followsRequest = !!request && this.configuredIssuer(options) === void 0 && getPinnedPublicUrl() === void 0 && !this.publicHostPinned;
|
|
42775
|
+
if (!followsRequest || !(0, import_auth12.isOrchestratedMode)(options) || options.expectedAudience === void 0) return issuer;
|
|
42776
|
+
const listed = Array.isArray(options.expectedAudience) ? options.expectedAudience : [options.expectedAudience];
|
|
42777
|
+
const accepted = /* @__PURE__ */ new Set([issuer, this.issuer]);
|
|
42778
|
+
for (const audience of listed) {
|
|
42779
|
+
const origin = originOf2(audience);
|
|
42780
|
+
if (origin) accepted.add(`${origin}${this.scope.fullPath}`);
|
|
42781
|
+
}
|
|
42782
|
+
return [...accepted];
|
|
42357
42783
|
}
|
|
42358
42784
|
/**
|
|
42359
42785
|
* Read the `tokenStorage` selection off the auth options. Only
|
|
@@ -42604,10 +43030,10 @@ var init_instance_local_primary_auth = __esm({
|
|
|
42604
43030
|
*
|
|
42605
43031
|
* The secret alone doesn't make a token this server's: every server started
|
|
42606
43032
|
* with the same JWT_SECRET holds it. So a token must also (#269):
|
|
42607
|
-
* - name THIS instance as its issuer (`iss`): `expectedIssuer
|
|
42608
|
-
* take from {@link
|
|
42609
|
-
*
|
|
42610
|
-
* claim; and
|
|
43033
|
+
* - name THIS instance as its issuer (`iss`): `expectedIssuer` (one issuer or
|
|
43034
|
+
* several), which callers take from {@link acceptedIssuersFor} the request,
|
|
43035
|
+
* matching what the token endpoint names when it signs ({@link issuerFor});
|
|
43036
|
+
* the boot-time issuer when not given. Never the token's own claim; and
|
|
42611
43037
|
* - when `expectedAudience` is given, be issued for that protected resource,
|
|
42612
43038
|
* or one of those resources (`aud`, compared as RFC 8707 resource URIs).
|
|
42613
43039
|
* `session:verify` passes the request's resource URL, or the configured
|
|
@@ -42620,7 +43046,7 @@ var init_instance_local_primary_auth = __esm({
|
|
|
42620
43046
|
try {
|
|
42621
43047
|
const { payload, protectedHeader } = await (0, import_jose.jwtVerify)(token, this.secret, {
|
|
42622
43048
|
algorithms: ["HS256"],
|
|
42623
|
-
issuer: expectedIssuer
|
|
43049
|
+
issuer: typeof expectedIssuer === "string" ? expectedIssuer : expectedIssuer ? [...expectedIssuer] : this.issuer,
|
|
42624
43050
|
requiredClaims: ["exp"]
|
|
42625
43051
|
});
|
|
42626
43052
|
if (expectedAudience !== void 0 && !audienceMatchesResource(payload.aud, expectedAudience)) {
|
|
@@ -43576,6 +44002,111 @@ var init_auth_registry = __esm({
|
|
|
43576
44002
|
}
|
|
43577
44003
|
});
|
|
43578
44004
|
|
|
44005
|
+
// libs/sdk/src/server/custom-routes.helper.ts
|
|
44006
|
+
function computeReservedPaths(entryPath, routeBase) {
|
|
44007
|
+
const prefix = normalizeEntryPrefix(entryPath);
|
|
44008
|
+
const base = normalizeScopeBase(routeBase);
|
|
44009
|
+
const mcpBase = `${prefix}${base}`;
|
|
44010
|
+
const exact = /* @__PURE__ */ new Set([
|
|
44011
|
+
mcpBase === "" ? "/" : mcpBase,
|
|
44012
|
+
`${mcpBase}/sse`,
|
|
44013
|
+
`${mcpBase}/message`,
|
|
44014
|
+
"/health",
|
|
44015
|
+
"/metrics"
|
|
44016
|
+
]);
|
|
44017
|
+
return { exact, prefixes: ["/oauth", "/.well-known"] };
|
|
44018
|
+
}
|
|
44019
|
+
function normalizeForCompare(path3) {
|
|
44020
|
+
if (path3.length > 1 && path3.endsWith("/")) return path3.slice(0, -1);
|
|
44021
|
+
return path3;
|
|
44022
|
+
}
|
|
44023
|
+
function assertNotReserved(method, path3, reserved) {
|
|
44024
|
+
const normalized = normalizeForCompare(path3);
|
|
44025
|
+
if (reserved.exact.has(normalized)) {
|
|
44026
|
+
throw new ReservedRouteCollisionError(method, path3, normalized);
|
|
44027
|
+
}
|
|
44028
|
+
for (const prefix of reserved.prefixes) {
|
|
44029
|
+
if (normalized === prefix || normalized.startsWith(`${prefix}/`)) {
|
|
44030
|
+
throw new ReservedRouteCollisionError(method, path3, prefix);
|
|
44031
|
+
}
|
|
44032
|
+
}
|
|
44033
|
+
}
|
|
44034
|
+
function wrapWithAuth(handler, verifySession, logger) {
|
|
44035
|
+
return async (req, res, next) => {
|
|
44036
|
+
let result;
|
|
44037
|
+
try {
|
|
44038
|
+
result = await verifySession(req);
|
|
44039
|
+
} catch (error) {
|
|
44040
|
+
if (error instanceof FlowControl && error.type === "respond") {
|
|
44041
|
+
result = error.output;
|
|
44042
|
+
} else {
|
|
44043
|
+
throw error;
|
|
44044
|
+
}
|
|
44045
|
+
}
|
|
44046
|
+
if (!result || result.kind === "unauthorized") {
|
|
44047
|
+
logger.verbose("custom-route auth: unauthorized");
|
|
44048
|
+
return writeHttpResponse(
|
|
44049
|
+
res,
|
|
44050
|
+
httpRespond.unauthorized({
|
|
44051
|
+
headers: result?.kind === "unauthorized" ? { "WWW-Authenticate": result.prmMetadataHeader } : void 0
|
|
44052
|
+
})
|
|
44053
|
+
);
|
|
44054
|
+
}
|
|
44055
|
+
if (result.kind === "forbidden") {
|
|
44056
|
+
logger.verbose("custom-route auth: forbidden (insufficient scope)");
|
|
44057
|
+
return writeHttpResponse(
|
|
44058
|
+
res,
|
|
44059
|
+
httpRespond.forbidden({
|
|
44060
|
+
headers: { "WWW-Authenticate": result.prmMetadataHeader }
|
|
44061
|
+
})
|
|
44062
|
+
);
|
|
44063
|
+
}
|
|
44064
|
+
req.authSession = result.authorization;
|
|
44065
|
+
req[ServerRequestTokens.auth] = result.authorization;
|
|
44066
|
+
return handler(req, res, next);
|
|
44067
|
+
};
|
|
44068
|
+
}
|
|
44069
|
+
function wrapWithIpFilter(handler, checkClientIp) {
|
|
44070
|
+
return async (req, res, next) => {
|
|
44071
|
+
const rejection = await checkClientIp(req, res);
|
|
44072
|
+
if (rejection) return writeHttpResponse(res, rejection);
|
|
44073
|
+
return handler(req, res, next);
|
|
44074
|
+
};
|
|
44075
|
+
}
|
|
44076
|
+
function registerCustomHttpRoutes(args) {
|
|
44077
|
+
const { routes, server, verifySession, checkClientIp, entryPath, routeBase, logger } = args;
|
|
44078
|
+
if (!routes || routes.length === 0) return;
|
|
44079
|
+
const reserved = computeReservedPaths(entryPath, routeBase);
|
|
44080
|
+
for (const route of routes) {
|
|
44081
|
+
assertNotReserved(route.method, route.path, reserved);
|
|
44082
|
+
const handler = route.auth === true ? wrapWithAuth(route.handler, verifySession, logger) : route.handler;
|
|
44083
|
+
server.registerRoute(route.method, route.path, wrapWithIpFilter(handler, checkClientIp));
|
|
44084
|
+
logger.info(`Registered custom http route: ${route.method} ${route.path}${route.auth ? " (auth)" : ""}`);
|
|
44085
|
+
}
|
|
44086
|
+
}
|
|
44087
|
+
var ReservedRouteCollisionError;
|
|
44088
|
+
var init_custom_routes_helper = __esm({
|
|
44089
|
+
"libs/sdk/src/server/custom-routes.helper.ts"() {
|
|
44090
|
+
"use strict";
|
|
44091
|
+
init_common();
|
|
44092
|
+
init_server_validation();
|
|
44093
|
+
ReservedRouteCollisionError = class extends Error {
|
|
44094
|
+
constructor(method, path3, reserved) {
|
|
44095
|
+
super(
|
|
44096
|
+
`Custom http.route "${method} ${path3}" collides with the reserved FrontMCP path "${reserved}". Reserved prefixes are the MCP entry path (and its /sse + /message siblings), /oauth/*, /.well-known/*, /health, and /metrics. Choose a different path.`
|
|
44097
|
+
);
|
|
44098
|
+
this.method = method;
|
|
44099
|
+
this.path = path3;
|
|
44100
|
+
this.reserved = reserved;
|
|
44101
|
+
this.name = "ReservedRouteCollisionError";
|
|
44102
|
+
}
|
|
44103
|
+
method;
|
|
44104
|
+
path;
|
|
44105
|
+
reserved;
|
|
44106
|
+
};
|
|
44107
|
+
}
|
|
44108
|
+
});
|
|
44109
|
+
|
|
43579
44110
|
// libs/sdk/src/channel/channel-notification.service.ts
|
|
43580
44111
|
var CHANNEL_NOTIFICATION_METHOD, ChannelNotificationService;
|
|
43581
44112
|
var init_channel_notification_service = __esm({
|
|
@@ -44546,6 +45077,12 @@ function wireJobCompletionSource(channel2, sourceConfig, jobEmitterSubscribe, lo
|
|
|
44546
45077
|
return;
|
|
44547
45078
|
}
|
|
44548
45079
|
}
|
|
45080
|
+
if (!event.sessionId) {
|
|
45081
|
+
logger.verbose(
|
|
45082
|
+
`Skipping job completion for channel "${channel2.name}": no sessionId (prevents cross-session leak)`
|
|
45083
|
+
);
|
|
45084
|
+
return;
|
|
45085
|
+
}
|
|
44549
45086
|
logger.verbose(`Job completion event for channel "${channel2.name}": job=${event.jobName}, status=${event.status}`);
|
|
44550
45087
|
channel2.handleEvent(event, event.sessionId).catch((err) => {
|
|
44551
45088
|
logger.error(`Failed to handle job completion event in channel "${channel2.name}"`, { error: err });
|
|
@@ -44558,6 +45095,41 @@ var init_job_completion_source = __esm({
|
|
|
44558
45095
|
}
|
|
44559
45096
|
});
|
|
44560
45097
|
|
|
45098
|
+
// libs/sdk/src/channel/sources/webhook.source.ts
|
|
45099
|
+
function createWebhookMiddleware(channel2, sourceConfig, logger) {
|
|
45100
|
+
return async (req, res) => {
|
|
45101
|
+
const rawHeaders = req.headers ?? {};
|
|
45102
|
+
const normalizedHeaders = {};
|
|
45103
|
+
for (const [key, value] of Object.entries(rawHeaders)) {
|
|
45104
|
+
normalizedHeaders[key.toLowerCase()] = value;
|
|
45105
|
+
}
|
|
45106
|
+
const payload = {
|
|
45107
|
+
body: req.body,
|
|
45108
|
+
headers: normalizedHeaders,
|
|
45109
|
+
method: req.method ?? "POST",
|
|
45110
|
+
query: req.query
|
|
45111
|
+
};
|
|
45112
|
+
logger.verbose(`Webhook received for channel "${channel2.name}" at ${sourceConfig.path}`);
|
|
45113
|
+
try {
|
|
45114
|
+
const notification = await runOnSurface("http-trigger", () => channel2.handleEvent(payload));
|
|
45115
|
+
if (notification) {
|
|
45116
|
+
res.status(200).json({ ok: true, channel: channel2.name });
|
|
45117
|
+
} else {
|
|
45118
|
+
res.status(200).json({ ok: true, channel: channel2.name, skipped: true });
|
|
45119
|
+
}
|
|
45120
|
+
} catch (err) {
|
|
45121
|
+
logger.error(`Webhook handler failed for channel "${channel2.name}"`, { error: err });
|
|
45122
|
+
res.status(500).json({ ok: false, error: "Internal channel error" });
|
|
45123
|
+
}
|
|
45124
|
+
};
|
|
45125
|
+
}
|
|
45126
|
+
var init_webhook_source = __esm({
|
|
45127
|
+
"libs/sdk/src/channel/sources/webhook.source.ts"() {
|
|
45128
|
+
"use strict";
|
|
45129
|
+
init_call_surface();
|
|
45130
|
+
}
|
|
45131
|
+
});
|
|
45132
|
+
|
|
44561
45133
|
// libs/sdk/src/channel/channel-scope.helper.ts
|
|
44562
45134
|
async function registerChannelCapabilities(args) {
|
|
44563
45135
|
const {
|
|
@@ -44568,11 +45140,15 @@ async function registerChannelCapabilities(args) {
|
|
|
44568
45140
|
notificationService,
|
|
44569
45141
|
flowRegistry,
|
|
44570
45142
|
toolRegistry,
|
|
44571
|
-
|
|
44572
|
-
jobEmitterSubscribe,
|
|
45143
|
+
http: http2,
|
|
44573
45144
|
logger
|
|
44574
45145
|
} = args;
|
|
45146
|
+
const completions = completionEventsOf(providers.getActiveScope());
|
|
45147
|
+
const agentEmitterSubscribe = args.agentEmitterSubscribe ?? ((cb) => completions.agents.subscribe(cb));
|
|
45148
|
+
const jobEmitterSubscribe = args.jobEmitterSubscribe ?? ((cb) => completions.jobs.subscribe(cb));
|
|
44575
45149
|
const unsubscribers = [];
|
|
45150
|
+
const reservedPaths = http2 ? computeReservedPaths(http2.entryPath, http2.routeBase) : void 0;
|
|
45151
|
+
const webhookPaths = /* @__PURE__ */ new Map();
|
|
44576
45152
|
const channelRegistry = new ChannelRegistry(providers, channelsList, owner);
|
|
44577
45153
|
await channelRegistry.ready;
|
|
44578
45154
|
const channelNotificationService = new ChannelNotificationService(
|
|
@@ -44588,31 +45164,23 @@ async function registerChannelCapabilities(args) {
|
|
|
44588
45164
|
const sourceType = instance.source.type;
|
|
44589
45165
|
switch (sourceType) {
|
|
44590
45166
|
case "agent-completion": {
|
|
44591
|
-
|
|
44592
|
-
|
|
44593
|
-
|
|
44594
|
-
|
|
44595
|
-
|
|
44596
|
-
|
|
44597
|
-
|
|
44598
|
-
unsubscribers.push(unsub);
|
|
44599
|
-
} else {
|
|
44600
|
-
logger.warn(`Channel "${instance.name}" has agent-completion source but no agent emitter available`);
|
|
44601
|
-
}
|
|
45167
|
+
const unsub = wireAgentCompletionSource(
|
|
45168
|
+
instance,
|
|
45169
|
+
instance.metadata.source,
|
|
45170
|
+
agentEmitterSubscribe,
|
|
45171
|
+
logger
|
|
45172
|
+
);
|
|
45173
|
+
unsubscribers.push(unsub);
|
|
44602
45174
|
break;
|
|
44603
45175
|
}
|
|
44604
45176
|
case "job-completion": {
|
|
44605
|
-
|
|
44606
|
-
|
|
44607
|
-
|
|
44608
|
-
|
|
44609
|
-
|
|
44610
|
-
|
|
44611
|
-
|
|
44612
|
-
unsubscribers.push(unsub);
|
|
44613
|
-
} else {
|
|
44614
|
-
logger.warn(`Channel "${instance.name}" has job-completion source but no job emitter available`);
|
|
44615
|
-
}
|
|
45177
|
+
const unsub = wireJobCompletionSource(
|
|
45178
|
+
instance,
|
|
45179
|
+
instance.metadata.source,
|
|
45180
|
+
jobEmitterSubscribe,
|
|
45181
|
+
logger
|
|
45182
|
+
);
|
|
45183
|
+
unsubscribers.push(unsub);
|
|
44616
45184
|
break;
|
|
44617
45185
|
}
|
|
44618
45186
|
case "app-event": {
|
|
@@ -44621,7 +45189,25 @@ async function registerChannelCapabilities(args) {
|
|
|
44621
45189
|
unsubscribers.push(unsub);
|
|
44622
45190
|
break;
|
|
44623
45191
|
}
|
|
44624
|
-
case "webhook":
|
|
45192
|
+
case "webhook": {
|
|
45193
|
+
const source = instance.metadata.source;
|
|
45194
|
+
if (!http2 || !reservedPaths) {
|
|
45195
|
+
logger.warn(`Channel "${instance.name}" has a webhook source but no HTTP server to serve ${source.path}`);
|
|
45196
|
+
break;
|
|
45197
|
+
}
|
|
45198
|
+
assertNotReserved("POST", source.path, reservedPaths);
|
|
45199
|
+
const claimedBy = webhookPaths.get(source.path);
|
|
45200
|
+
if (claimedBy) {
|
|
45201
|
+
throw new Error(
|
|
45202
|
+
`Channels "${claimedBy}" and "${instance.name}" both declare the webhook path ${source.path}. Give each webhook channel its own path.`
|
|
45203
|
+
);
|
|
45204
|
+
}
|
|
45205
|
+
webhookPaths.set(source.path, instance.name);
|
|
45206
|
+
const handler = createWebhookMiddleware(instance, source, logger);
|
|
45207
|
+
await http2.server.registerRoute("POST", source.path, wrapWithIpFilter(handler, http2.checkClientIp));
|
|
45208
|
+
logger.info(`Registered webhook route for channel "${instance.name}": POST ${source.path}`);
|
|
45209
|
+
break;
|
|
45210
|
+
}
|
|
44625
45211
|
case "manual":
|
|
44626
45212
|
break;
|
|
44627
45213
|
case "service":
|
|
@@ -44704,6 +45290,7 @@ async function registerChannelCapabilities(args) {
|
|
|
44704
45290
|
var init_channel_scope_helper = __esm({
|
|
44705
45291
|
"libs/sdk/src/channel/channel-scope.helper.ts"() {
|
|
44706
45292
|
"use strict";
|
|
45293
|
+
init_custom_routes_helper();
|
|
44707
45294
|
init_tool_instance();
|
|
44708
45295
|
init_tool_utils();
|
|
44709
45296
|
init_channel_notification_service();
|
|
@@ -44713,7 +45300,9 @@ var init_channel_scope_helper = __esm({
|
|
|
44713
45300
|
init_channel_reply_tool();
|
|
44714
45301
|
init_agent_completion_source();
|
|
44715
45302
|
init_app_event_source();
|
|
45303
|
+
init_completion_events();
|
|
44716
45304
|
init_job_completion_source();
|
|
45305
|
+
init_webhook_source();
|
|
44717
45306
|
}
|
|
44718
45307
|
});
|
|
44719
45308
|
|
|
@@ -47780,10 +48369,11 @@ var init_workflow_step_executor = __esm({
|
|
|
47780
48369
|
});
|
|
47781
48370
|
|
|
47782
48371
|
// libs/sdk/src/workflow/engine/workflow.engine.ts
|
|
47783
|
-
var WorkflowEngine;
|
|
48372
|
+
var import_utils86, WorkflowEngine;
|
|
47784
48373
|
var init_workflow_engine = __esm({
|
|
47785
48374
|
"libs/sdk/src/workflow/engine/workflow.engine.ts"() {
|
|
47786
48375
|
"use strict";
|
|
48376
|
+
import_utils86 = require("@frontmcp/utils");
|
|
47787
48377
|
init_workflow_errors();
|
|
47788
48378
|
init_workflow_step_executor();
|
|
47789
48379
|
WorkflowEngine = class {
|
|
@@ -47849,7 +48439,7 @@ var init_workflow_engine = __esm({
|
|
|
47849
48439
|
}
|
|
47850
48440
|
continue;
|
|
47851
48441
|
}
|
|
47852
|
-
const batch = ready.slice(0, this.maxConcurrency);
|
|
48442
|
+
const batch = ready.slice(0, (0, import_utils86.getAsyncContextMode)() === "serialized" ? 1 : this.maxConcurrency);
|
|
47853
48443
|
await Promise.allSettled(
|
|
47854
48444
|
batch.map(async (step) => {
|
|
47855
48445
|
if (step.condition) {
|
|
@@ -47956,11 +48546,11 @@ var init_workflow_engine = __esm({
|
|
|
47956
48546
|
});
|
|
47957
48547
|
|
|
47958
48548
|
// libs/sdk/src/job/execution/job-execution.manager.ts
|
|
47959
|
-
var
|
|
48549
|
+
var import_utils87, JobExecutionManager;
|
|
47960
48550
|
var init_job_execution_manager = __esm({
|
|
47961
48551
|
"libs/sdk/src/job/execution/job-execution.manager.ts"() {
|
|
47962
48552
|
"use strict";
|
|
47963
|
-
|
|
48553
|
+
import_utils87 = require("@frontmcp/utils");
|
|
47964
48554
|
init_principal_utils();
|
|
47965
48555
|
init_call_surface();
|
|
47966
48556
|
init_errors();
|
|
@@ -47980,7 +48570,7 @@ var init_job_execution_manager = __esm({
|
|
|
47980
48570
|
*/
|
|
47981
48571
|
async executeJob(job, input, opts = {}) {
|
|
47982
48572
|
await this.assertMayExecute(job.metadata.permissions, job.name, opts);
|
|
47983
|
-
const runId = (0,
|
|
48573
|
+
const runId = (0, import_utils87.randomUUID)();
|
|
47984
48574
|
const retryConfig = job.metadata.retry ?? {};
|
|
47985
48575
|
const maxAttempts = retryConfig.maxAttempts ?? 1;
|
|
47986
48576
|
const runRecord = {
|
|
@@ -47999,7 +48589,7 @@ var init_job_execution_manager = __esm({
|
|
|
47999
48589
|
};
|
|
48000
48590
|
await this.stateStore.createRun(runRecord);
|
|
48001
48591
|
if (opts.background) {
|
|
48002
|
-
(0,
|
|
48592
|
+
(0, import_utils87.runRequestExclusive)(() => this.executeJobBackground(job, input, runId, opts)).catch(async (err) => {
|
|
48003
48593
|
this.logger.error(`Background job execution failed: ${err}`);
|
|
48004
48594
|
try {
|
|
48005
48595
|
await this.updateState(runId, {
|
|
@@ -48020,7 +48610,7 @@ var init_job_execution_manager = __esm({
|
|
|
48020
48610
|
*/
|
|
48021
48611
|
async executeWorkflow(workflow, jobRegistry, opts = {}) {
|
|
48022
48612
|
await this.assertMayExecute(workflow.metadata.permissions, workflow.name, opts);
|
|
48023
|
-
const runId = (0,
|
|
48613
|
+
const runId = (0, import_utils87.randomUUID)();
|
|
48024
48614
|
const runRecord = {
|
|
48025
48615
|
runId,
|
|
48026
48616
|
jobId: workflow.metadata.id ?? workflow.name,
|
|
@@ -48039,7 +48629,7 @@ var init_job_execution_manager = __esm({
|
|
|
48039
48629
|
};
|
|
48040
48630
|
await this.stateStore.createRun(runRecord);
|
|
48041
48631
|
if (opts.background) {
|
|
48042
|
-
(0,
|
|
48632
|
+
(0, import_utils87.runRequestExclusive)(() => this.executeWorkflowBackground(workflow, jobRegistry, runId, opts)).catch(
|
|
48043
48633
|
async (err) => {
|
|
48044
48634
|
this.logger.error(`Background workflow execution failed: ${err}`);
|
|
48045
48635
|
try {
|
|
@@ -49447,7 +50037,11 @@ async function registerJobCapabilities(args) {
|
|
|
49447
50037
|
for (const dynWf of dynamicWorkflows) {
|
|
49448
50038
|
workflowRegistry.registerDynamic(dynWf);
|
|
49449
50039
|
}
|
|
49450
|
-
const
|
|
50040
|
+
const completions = completionEventsOf(providers.getActiveScope());
|
|
50041
|
+
const executionManager = new JobExecutionManager(stateStore, logger, async (data) => {
|
|
50042
|
+
await notifyFn?.(data);
|
|
50043
|
+
await publishJobCompletion(data, stateStore, completions);
|
|
50044
|
+
});
|
|
49451
50045
|
const managementTools = [
|
|
49452
50046
|
ListJobsTool,
|
|
49453
50047
|
ExecuteJobTool,
|
|
@@ -49476,9 +50070,27 @@ async function registerJobCapabilities(args) {
|
|
|
49476
50070
|
managementTools
|
|
49477
50071
|
};
|
|
49478
50072
|
}
|
|
50073
|
+
async function publishJobCompletion(data, stateStore, completions) {
|
|
50074
|
+
const { type, state, runId } = data;
|
|
50075
|
+
if (type !== "job:status" && type !== "workflow:status") return;
|
|
50076
|
+
if (state !== "completed" && state !== "failed" || typeof runId !== "string") return;
|
|
50077
|
+
const run = await stateStore.getRun(runId);
|
|
50078
|
+
if (!run) return;
|
|
50079
|
+
const workflowName = type === "workflow:status" ? run.workflowName : void 0;
|
|
50080
|
+
completions.jobs.emit({
|
|
50081
|
+
jobName: workflowName ?? run.jobName,
|
|
50082
|
+
jobId: run.jobId,
|
|
50083
|
+
status: state === "completed" ? "success" : "error",
|
|
50084
|
+
...run.completedAt !== void 0 ? { durationMs: run.completedAt - run.startedAt } : {},
|
|
50085
|
+
...state === "completed" ? { output: completionOutputText(run.result) } : { error: run.error?.message },
|
|
50086
|
+
attempt: run.attempt,
|
|
50087
|
+
...run.sessionId ? { sessionId: run.sessionId } : {}
|
|
50088
|
+
});
|
|
50089
|
+
}
|
|
49479
50090
|
var init_job_scope_helper = __esm({
|
|
49480
50091
|
"libs/sdk/src/job/job-scope.helper.ts"() {
|
|
49481
50092
|
"use strict";
|
|
50093
|
+
init_completion_events();
|
|
49482
50094
|
init_execute_workflow_tool();
|
|
49483
50095
|
init_get_workflow_status_tool();
|
|
49484
50096
|
init_list_workflows_tool();
|
|
@@ -49704,111 +50316,6 @@ var init_enforced_metadata_check = __esm({
|
|
|
49704
50316
|
}
|
|
49705
50317
|
});
|
|
49706
50318
|
|
|
49707
|
-
// libs/sdk/src/server/custom-routes.helper.ts
|
|
49708
|
-
function computeReservedPaths(entryPath, routeBase) {
|
|
49709
|
-
const prefix = normalizeEntryPrefix(entryPath);
|
|
49710
|
-
const base = normalizeScopeBase(routeBase);
|
|
49711
|
-
const mcpBase = `${prefix}${base}`;
|
|
49712
|
-
const exact = /* @__PURE__ */ new Set([
|
|
49713
|
-
mcpBase === "" ? "/" : mcpBase,
|
|
49714
|
-
`${mcpBase}/sse`,
|
|
49715
|
-
`${mcpBase}/message`,
|
|
49716
|
-
"/health",
|
|
49717
|
-
"/metrics"
|
|
49718
|
-
]);
|
|
49719
|
-
return { exact, prefixes: ["/oauth", "/.well-known"] };
|
|
49720
|
-
}
|
|
49721
|
-
function normalizeForCompare(path3) {
|
|
49722
|
-
if (path3.length > 1 && path3.endsWith("/")) return path3.slice(0, -1);
|
|
49723
|
-
return path3;
|
|
49724
|
-
}
|
|
49725
|
-
function assertNotReserved(method, path3, reserved) {
|
|
49726
|
-
const normalized = normalizeForCompare(path3);
|
|
49727
|
-
if (reserved.exact.has(normalized)) {
|
|
49728
|
-
throw new ReservedRouteCollisionError(method, path3, normalized);
|
|
49729
|
-
}
|
|
49730
|
-
for (const prefix of reserved.prefixes) {
|
|
49731
|
-
if (normalized === prefix || normalized.startsWith(`${prefix}/`)) {
|
|
49732
|
-
throw new ReservedRouteCollisionError(method, path3, prefix);
|
|
49733
|
-
}
|
|
49734
|
-
}
|
|
49735
|
-
}
|
|
49736
|
-
function wrapWithAuth(handler, verifySession, logger) {
|
|
49737
|
-
return async (req, res, next) => {
|
|
49738
|
-
let result;
|
|
49739
|
-
try {
|
|
49740
|
-
result = await verifySession(req);
|
|
49741
|
-
} catch (error) {
|
|
49742
|
-
if (error instanceof FlowControl && error.type === "respond") {
|
|
49743
|
-
result = error.output;
|
|
49744
|
-
} else {
|
|
49745
|
-
throw error;
|
|
49746
|
-
}
|
|
49747
|
-
}
|
|
49748
|
-
if (!result || result.kind === "unauthorized") {
|
|
49749
|
-
logger.verbose("custom-route auth: unauthorized");
|
|
49750
|
-
return writeHttpResponse(
|
|
49751
|
-
res,
|
|
49752
|
-
httpRespond.unauthorized({
|
|
49753
|
-
headers: result?.kind === "unauthorized" ? { "WWW-Authenticate": result.prmMetadataHeader } : void 0
|
|
49754
|
-
})
|
|
49755
|
-
);
|
|
49756
|
-
}
|
|
49757
|
-
if (result.kind === "forbidden") {
|
|
49758
|
-
logger.verbose("custom-route auth: forbidden (insufficient scope)");
|
|
49759
|
-
return writeHttpResponse(
|
|
49760
|
-
res,
|
|
49761
|
-
httpRespond.forbidden({
|
|
49762
|
-
headers: { "WWW-Authenticate": result.prmMetadataHeader }
|
|
49763
|
-
})
|
|
49764
|
-
);
|
|
49765
|
-
}
|
|
49766
|
-
req.authSession = result.authorization;
|
|
49767
|
-
req[ServerRequestTokens.auth] = result.authorization;
|
|
49768
|
-
return handler(req, res, next);
|
|
49769
|
-
};
|
|
49770
|
-
}
|
|
49771
|
-
function wrapWithIpFilter(handler, checkClientIp) {
|
|
49772
|
-
return async (req, res, next) => {
|
|
49773
|
-
const rejection = await checkClientIp(req, res);
|
|
49774
|
-
if (rejection) return writeHttpResponse(res, rejection);
|
|
49775
|
-
return handler(req, res, next);
|
|
49776
|
-
};
|
|
49777
|
-
}
|
|
49778
|
-
function registerCustomHttpRoutes(args) {
|
|
49779
|
-
const { routes, server, verifySession, checkClientIp, entryPath, routeBase, logger } = args;
|
|
49780
|
-
if (!routes || routes.length === 0) return;
|
|
49781
|
-
const reserved = computeReservedPaths(entryPath, routeBase);
|
|
49782
|
-
for (const route of routes) {
|
|
49783
|
-
assertNotReserved(route.method, route.path, reserved);
|
|
49784
|
-
const handler = route.auth === true ? wrapWithAuth(route.handler, verifySession, logger) : route.handler;
|
|
49785
|
-
server.registerRoute(route.method, route.path, wrapWithIpFilter(handler, checkClientIp));
|
|
49786
|
-
logger.info(`Registered custom http route: ${route.method} ${route.path}${route.auth ? " (auth)" : ""}`);
|
|
49787
|
-
}
|
|
49788
|
-
}
|
|
49789
|
-
var ReservedRouteCollisionError;
|
|
49790
|
-
var init_custom_routes_helper = __esm({
|
|
49791
|
-
"libs/sdk/src/server/custom-routes.helper.ts"() {
|
|
49792
|
-
"use strict";
|
|
49793
|
-
init_common();
|
|
49794
|
-
init_server_validation();
|
|
49795
|
-
ReservedRouteCollisionError = class extends Error {
|
|
49796
|
-
constructor(method, path3, reserved) {
|
|
49797
|
-
super(
|
|
49798
|
-
`Custom http.route "${method} ${path3}" collides with the reserved FrontMCP path "${reserved}". Reserved prefixes are the MCP entry path (and its /sse + /message siblings), /oauth/*, /.well-known/*, /health, and /metrics. Choose a different path.`
|
|
49799
|
-
);
|
|
49800
|
-
this.method = method;
|
|
49801
|
-
this.path = path3;
|
|
49802
|
-
this.reserved = reserved;
|
|
49803
|
-
this.name = "ReservedRouteCollisionError";
|
|
49804
|
-
}
|
|
49805
|
-
method;
|
|
49806
|
-
path;
|
|
49807
|
-
reserved;
|
|
49808
|
-
};
|
|
49809
|
-
}
|
|
49810
|
-
});
|
|
49811
|
-
|
|
49812
50319
|
// libs/sdk/src/skill/errors/tool-not-allowed.error.ts
|
|
49813
50320
|
var ToolNotAllowedError, ToolApprovalRequiredError;
|
|
49814
50321
|
var init_tool_not_allowed_error = __esm({
|
|
@@ -50048,15 +50555,15 @@ var init_skill_session_types = __esm({
|
|
|
50048
50555
|
});
|
|
50049
50556
|
|
|
50050
50557
|
// libs/sdk/src/skill/session/skill-session.manager.ts
|
|
50051
|
-
var
|
|
50558
|
+
var import_utils88, SkillSessionManager;
|
|
50052
50559
|
var init_skill_session_manager = __esm({
|
|
50053
50560
|
"libs/sdk/src/skill/session/skill-session.manager.ts"() {
|
|
50054
50561
|
"use strict";
|
|
50055
|
-
|
|
50562
|
+
import_utils88 = require("@frontmcp/utils");
|
|
50056
50563
|
init_errors();
|
|
50057
50564
|
init_skill_session_types();
|
|
50058
|
-
SkillSessionManager = class extends
|
|
50059
|
-
storage = new
|
|
50565
|
+
SkillSessionManager = class extends import_utils88.EventEmitter {
|
|
50566
|
+
storage = new import_utils88.AsyncLocalStorage();
|
|
50060
50567
|
options;
|
|
50061
50568
|
logger;
|
|
50062
50569
|
store;
|
|
@@ -50983,7 +51490,7 @@ var init_load_skill_flow = __esm({
|
|
|
50983
51490
|
}
|
|
50984
51491
|
await assertSkillAuthorized(this.scope, entry, authInfo);
|
|
50985
51492
|
}
|
|
50986
|
-
loadResults.push({ loadResult: result });
|
|
51493
|
+
loadResults.push({ loadResult: skillToolsForCaller(result, this.scope.tools, ctx) });
|
|
50987
51494
|
}
|
|
50988
51495
|
this.state.set({ loadResults, warnings });
|
|
50989
51496
|
this.logger.verbose("loadSkills:done", { loaded: loadResults.length, notFound: warnings.length });
|
|
@@ -51228,7 +51735,10 @@ async function authorizeSkillHttpRequest(scope, skillsConfig, request, logger) {
|
|
|
51228
51735
|
if (mode === "inherit") {
|
|
51229
51736
|
const authOptions = scope.auth?.options;
|
|
51230
51737
|
if (!authOptions || (0, import_auth12.isPublicMode)(authOptions)) return { allowed: true, authInfo: {} };
|
|
51231
|
-
const verified = await scope.runFlow("session:verify", {
|
|
51738
|
+
const verified = await scope.runFlow("session:verify", {
|
|
51739
|
+
request,
|
|
51740
|
+
sessionless: true
|
|
51741
|
+
});
|
|
51232
51742
|
if (verified?.kind === "authorized") {
|
|
51233
51743
|
return { allowed: true, authInfo: { ...authInfoFromAuthorization(verified.authorization) } };
|
|
51234
51744
|
}
|
|
@@ -51252,11 +51762,11 @@ function createSkillHttpAuthValidator(skillsConfig, logger) {
|
|
|
51252
51762
|
logger
|
|
51253
51763
|
});
|
|
51254
51764
|
}
|
|
51255
|
-
var
|
|
51765
|
+
var import_utils89, SkillHttpAuthValidator;
|
|
51256
51766
|
var init_skill_http_auth = __esm({
|
|
51257
51767
|
"libs/sdk/src/skill/auth/skill-http-auth.ts"() {
|
|
51258
51768
|
"use strict";
|
|
51259
|
-
|
|
51769
|
+
import_utils89 = require("@frontmcp/utils");
|
|
51260
51770
|
init_common();
|
|
51261
51771
|
SkillHttpAuthValidator = class {
|
|
51262
51772
|
skillsConfig;
|
|
@@ -51338,7 +51848,7 @@ var init_skill_http_auth = __esm({
|
|
|
51338
51848
|
const keyBytes = encoder5.encode(key);
|
|
51339
51849
|
if (keyBytes.length === candidateBytes.length) {
|
|
51340
51850
|
try {
|
|
51341
|
-
if ((0,
|
|
51851
|
+
if ((0, import_utils89.timingSafeEqual)(keyBytes, candidateBytes)) {
|
|
51342
51852
|
found = true;
|
|
51343
51853
|
}
|
|
51344
51854
|
} catch {
|
|
@@ -52198,7 +52708,12 @@ var init_skills_api_flow = __esm({
|
|
|
52198
52708
|
respondNotFound();
|
|
52199
52709
|
return;
|
|
52200
52710
|
}
|
|
52201
|
-
const { skill, availableTools, missingTools, isComplete, warning } =
|
|
52711
|
+
const { skill, availableTools, missingTools, isComplete, warning } = skillToolsForCaller(
|
|
52712
|
+
loadResult,
|
|
52713
|
+
toolRegistry,
|
|
52714
|
+
void 0,
|
|
52715
|
+
SKILLS_HTTP_SURFACE
|
|
52716
|
+
);
|
|
52202
52717
|
const skillEntry = createSkillEntryResolver(skillRegistry)(skillId, skill.id);
|
|
52203
52718
|
if (skillEntry) {
|
|
52204
52719
|
if (!await isSkillServable(this.scope, skillEntry, void 0, SKILLS_HTTP_SURFACE)) {
|
|
@@ -52559,6 +53074,8 @@ var init_tool_interface = __esm({
|
|
|
52559
53074
|
_toolNameInternal;
|
|
52560
53075
|
/** @internal Tool input for fallback elicitation - set by CallToolFlow */
|
|
52561
53076
|
_toolInputInternal;
|
|
53077
|
+
/** @internal JSON-RPC id of the tools/call request - set by CallToolFlow, for flows the call is handed on to */
|
|
53078
|
+
_jsonRpcRequestIdInternal;
|
|
52562
53079
|
// ---- INPUT storages (backing fields)
|
|
52563
53080
|
_rawInput;
|
|
52564
53081
|
_input;
|
|
@@ -52591,6 +53108,10 @@ var init_tool_interface = __esm({
|
|
|
52591
53108
|
this._progressToken = progressToken;
|
|
52592
53109
|
this.signal = signal;
|
|
52593
53110
|
}
|
|
53111
|
+
/** @internal The request's progress token, for flows the call is handed on to (the agent tool). */
|
|
53112
|
+
get _progressTokenInternal() {
|
|
53113
|
+
return this._progressToken;
|
|
53114
|
+
}
|
|
52594
53115
|
/**
|
|
52595
53116
|
* Like {@link ExecutionContextBase.fetch}, and also aborted with {@link signal}, so a
|
|
52596
53117
|
* cancelled or timed-out call stops its outbound requests.
|
|
@@ -54636,10 +55157,10 @@ var init_primary_auth_provider_interface = __esm({
|
|
|
54636
55157
|
* @param _expectedAudience The protected resource(s) the token must be for
|
|
54637
55158
|
* (`aud` names at least one), when the caller is a
|
|
54638
55159
|
* resource endpoint.
|
|
54639
|
-
* @param _expectedIssuer The issuer the token must name
|
|
54640
|
-
*
|
|
54641
|
-
* (`LocalPrimaryAuth.
|
|
54642
|
-
* boot-time issuer.
|
|
55160
|
+
* @param _expectedIssuer The issuer the token must name (or one of them):
|
|
55161
|
+
* the ones this instance accepts for the request
|
|
55162
|
+
* (`LocalPrimaryAuth.acceptedIssuersFor`). Default:
|
|
55163
|
+
* its boot-time issuer.
|
|
54643
55164
|
*/
|
|
54644
55165
|
verifyGatewayToken(_token, _requestBaseUrl, _expectedAudience, _expectedIssuer) {
|
|
54645
55166
|
return Promise.resolve({
|
|
@@ -54713,7 +55234,10 @@ function findSkillByPath(scope, skillPath) {
|
|
|
54713
55234
|
const segments = skillPath.split("/").filter((s) => s.length > 0);
|
|
54714
55235
|
if (segments.length === 0) return void 0;
|
|
54715
55236
|
const lastSegment = segments[segments.length - 1];
|
|
54716
|
-
|
|
55237
|
+
const byName = visible.find((s) => s.name === lastSegment && s.getSkillPath() === lastSegment);
|
|
55238
|
+
if (byName) return byName;
|
|
55239
|
+
if (segments.length !== 1) return void 0;
|
|
55240
|
+
return visible.find((s) => s.metadata.id === lastSegment && s.getSkillPath() === s.metadata.name);
|
|
54717
55241
|
}
|
|
54718
55242
|
async function findAndLoadSkillByPath(scope, skillPath) {
|
|
54719
55243
|
const registry = scope.skills;
|
|
@@ -54785,15 +55309,15 @@ async function readSkillFileByPath(instance, filePath) {
|
|
|
54785
55309
|
let candidatePath;
|
|
54786
55310
|
for (const [subdir, configured] of candidateDirs) {
|
|
54787
55311
|
if (segments[0] !== subdir) continue;
|
|
54788
|
-
const dir = configured ? (0,
|
|
54789
|
-
candidatePath = (0,
|
|
55312
|
+
const dir = configured ? (0, import_utils91.isAbsolute)(configured) ? configured : (0, import_utils91.pathResolve)(baseDir, configured) : (0, import_utils91.joinPath)(baseDir, subdir);
|
|
55313
|
+
candidatePath = (0, import_utils91.pathResolve)(dir, ...segments.slice(1));
|
|
54790
55314
|
if (!isInside(candidatePath, dir)) {
|
|
54791
55315
|
throw new PublicMcpError(`Invalid file path "${filePath}".`, "INVALID_PARAMS", 400);
|
|
54792
55316
|
}
|
|
54793
55317
|
break;
|
|
54794
55318
|
}
|
|
54795
55319
|
if (!candidatePath) {
|
|
54796
|
-
candidatePath = (0,
|
|
55320
|
+
candidatePath = (0, import_utils91.pathResolve)(baseDir, ...segments);
|
|
54797
55321
|
if (!isInside(candidatePath, baseDir)) {
|
|
54798
55322
|
throw new PublicMcpError(`Invalid file path "${filePath}".`, "INVALID_PARAMS", 400);
|
|
54799
55323
|
}
|
|
@@ -54801,7 +55325,7 @@ async function readSkillFileByPath(instance, filePath) {
|
|
|
54801
55325
|
const mimeType = guessMimeType(filePath);
|
|
54802
55326
|
let buffer;
|
|
54803
55327
|
try {
|
|
54804
|
-
buffer = await (0,
|
|
55328
|
+
buffer = await (0, import_utils91.readFileBuffer)(candidatePath);
|
|
54805
55329
|
} catch (err) {
|
|
54806
55330
|
if (isFileMissingError(err)) {
|
|
54807
55331
|
throw new ResourceNotFoundError(`skill://${instance.getSkillPath()}/${filePath}`);
|
|
@@ -54844,7 +55368,7 @@ async function tryResolvedSubFile(instance, kind, tail) {
|
|
|
54844
55368
|
const resolvedPath = resolveEntryPath2(instance, kind, absolutePath, filename);
|
|
54845
55369
|
if (!resolvedPath) continue;
|
|
54846
55370
|
try {
|
|
54847
|
-
const buf = await (0,
|
|
55371
|
+
const buf = await (0, import_utils91.readFileBuffer)(resolvedPath);
|
|
54848
55372
|
const text = (Buffer.isBuffer(buf) ? buf : Buffer.from(buf)).toString("utf-8");
|
|
54849
55373
|
return parseSkillMdFrontmatter(text).body;
|
|
54850
55374
|
} catch (err) {
|
|
@@ -54857,18 +55381,18 @@ async function tryResolvedSubFile(instance, kind, tail) {
|
|
|
54857
55381
|
return void 0;
|
|
54858
55382
|
}
|
|
54859
55383
|
function resolveEntryPath2(instance, kind, absolutePath, filename) {
|
|
54860
|
-
if (absolutePath && (0,
|
|
55384
|
+
if (absolutePath && (0, import_utils91.isAbsolute)(absolutePath)) return absolutePath;
|
|
54861
55385
|
if (!filename) return void 0;
|
|
54862
55386
|
const baseDir = instance.getBaseDir();
|
|
54863
55387
|
const configured = instance.getResources()?.[kind];
|
|
54864
55388
|
let dir;
|
|
54865
|
-
if (configured && (0,
|
|
55389
|
+
if (configured && (0, import_utils91.isAbsolute)(configured)) {
|
|
54866
55390
|
dir = configured;
|
|
54867
55391
|
} else if (baseDir) {
|
|
54868
|
-
dir = configured ? (0,
|
|
55392
|
+
dir = configured ? (0, import_utils91.pathResolve)(baseDir, configured) : (0, import_utils91.joinPath)(baseDir, kind);
|
|
54869
55393
|
}
|
|
54870
55394
|
if (!dir) return void 0;
|
|
54871
|
-
const resolved = (0,
|
|
55395
|
+
const resolved = (0, import_utils91.pathResolve)(dir, filename);
|
|
54872
55396
|
if (!isInside(resolved, dir)) return void 0;
|
|
54873
55397
|
return resolved;
|
|
54874
55398
|
}
|
|
@@ -54900,11 +55424,11 @@ function isInside(child, parent) {
|
|
|
54900
55424
|
const parentPrefix = normParent.endsWith("/") ? normParent : `${normParent}/`;
|
|
54901
55425
|
return normChild === normParent || normChild.startsWith(parentPrefix);
|
|
54902
55426
|
}
|
|
54903
|
-
var
|
|
55427
|
+
var import_utils91;
|
|
54904
55428
|
var init_sep_2640_resource_helpers = __esm({
|
|
54905
55429
|
"libs/sdk/src/skill/sep-2640/sep-2640.resource-helpers.ts"() {
|
|
54906
55430
|
"use strict";
|
|
54907
|
-
|
|
55431
|
+
import_utils91 = require("@frontmcp/utils");
|
|
54908
55432
|
init_errors();
|
|
54909
55433
|
init_skill_filter_helper();
|
|
54910
55434
|
init_skill_md_parser();
|
|
@@ -55131,17 +55655,17 @@ async function resolveLastModifiedForSkill(skill) {
|
|
|
55131
55655
|
return void 0;
|
|
55132
55656
|
}
|
|
55133
55657
|
try {
|
|
55134
|
-
const s = await (0,
|
|
55658
|
+
const s = await (0, import_utils92.stat)(record.filePath);
|
|
55135
55659
|
return s.mtime instanceof Date ? s.mtime.toISOString() : void 0;
|
|
55136
55660
|
} catch {
|
|
55137
55661
|
return void 0;
|
|
55138
55662
|
}
|
|
55139
55663
|
}
|
|
55140
|
-
var
|
|
55664
|
+
var import_utils92;
|
|
55141
55665
|
var init_sep_2640_last_modified = __esm({
|
|
55142
55666
|
"libs/sdk/src/skill/sep-2640/sep-2640.last-modified.ts"() {
|
|
55143
55667
|
"use strict";
|
|
55144
|
-
|
|
55668
|
+
import_utils92 = require("@frontmcp/utils");
|
|
55145
55669
|
}
|
|
55146
55670
|
});
|
|
55147
55671
|
|
|
@@ -55331,7 +55855,7 @@ function createDefaultSigner(mod, logger) {
|
|
|
55331
55855
|
"[skill-audit] refusing to use the default HS256 signer in production. Configure skillsConfig.audit.signer with a host-managed key (e.g. RS256 pointing at the same key registry your bundle signing uses)."
|
|
55332
55856
|
);
|
|
55333
55857
|
}
|
|
55334
|
-
const bytes = (0,
|
|
55858
|
+
const bytes = (0, import_utils93.randomBytes)(32);
|
|
55335
55859
|
logger.warn(
|
|
55336
55860
|
"[skill-audit] no audit signer configured \u2014 using process-local HS256 with a CSPRNG-generated secret. Records signed by this instance cannot be verified after restart. Configure skillsConfig.audit.signer for production."
|
|
55337
55861
|
);
|
|
@@ -55343,12 +55867,12 @@ function createDefaultStore(mod, logger) {
|
|
|
55343
55867
|
);
|
|
55344
55868
|
return new mod.MemoryAuditStore();
|
|
55345
55869
|
}
|
|
55346
|
-
var import_di39,
|
|
55870
|
+
var import_di39, import_utils93, injectedFactory;
|
|
55347
55871
|
var init_skill_audit_helper = __esm({
|
|
55348
55872
|
"libs/sdk/src/skill/skill-audit.helper.ts"() {
|
|
55349
55873
|
"use strict";
|
|
55350
55874
|
import_di39 = require("@frontmcp/di");
|
|
55351
|
-
|
|
55875
|
+
import_utils93 = require("@frontmcp/utils");
|
|
55352
55876
|
}
|
|
55353
55877
|
});
|
|
55354
55878
|
|
|
@@ -55988,11 +56512,11 @@ function createSqliteSessionStore(options, logger) {
|
|
|
55988
56512
|
logger?.info("[SessionStoreFactory] Creating SQLite session store", { path: options.path });
|
|
55989
56513
|
return new SqliteSessionStore(options);
|
|
55990
56514
|
}
|
|
55991
|
-
var
|
|
56515
|
+
var import_utils94;
|
|
55992
56516
|
var init_session_store_factory = __esm({
|
|
55993
56517
|
"libs/sdk/src/auth/session/session-store.factory.ts"() {
|
|
55994
56518
|
"use strict";
|
|
55995
|
-
|
|
56519
|
+
import_utils94 = require("@frontmcp/utils");
|
|
55996
56520
|
init_common();
|
|
55997
56521
|
}
|
|
55998
56522
|
});
|
|
@@ -56067,14 +56591,14 @@ function toClientInfo(value) {
|
|
|
56067
56591
|
const { name: name44, version } = value;
|
|
56068
56592
|
return typeof name44 === "string" && typeof version === "string" ? { name: name44, version } : void 0;
|
|
56069
56593
|
}
|
|
56070
|
-
var import_auth55, import_lazy_zod102, import_protocol52,
|
|
56594
|
+
var import_auth55, import_lazy_zod102, import_protocol52, import_utils95, plan37, subscriptionParamsSchema, requestIdSchema, stateSchema37, name38, Stage38, encoder3, TRACE_META_KEYS, HandleMcp20260728Flow;
|
|
56071
56595
|
var init_handle_mcp_20260728_flow = __esm({
|
|
56072
56596
|
"libs/sdk/src/transport/flows/handle.mcp-20260728.flow.ts"() {
|
|
56073
56597
|
"use strict";
|
|
56074
56598
|
import_auth55 = require("@frontmcp/auth");
|
|
56075
56599
|
import_lazy_zod102 = require("@frontmcp/lazy-zod");
|
|
56076
56600
|
import_protocol52 = require("@frontmcp/protocol");
|
|
56077
|
-
|
|
56601
|
+
import_utils95 = require("@frontmcp/utils");
|
|
56078
56602
|
init_common();
|
|
56079
56603
|
init_context();
|
|
56080
56604
|
init_notification();
|
|
@@ -56098,7 +56622,9 @@ var init_handle_mcp_20260728_flow = __esm({
|
|
|
56098
56622
|
/** Negotiated protocol version for this request. */
|
|
56099
56623
|
version: import_lazy_zod102.z.string().optional(),
|
|
56100
56624
|
requestType: import_lazy_zod102.z.enum(["notification", "subscriptions", "message"]).optional(),
|
|
56101
|
-
isAnonymous: import_lazy_zod102.z.boolean().default(true)
|
|
56625
|
+
isAnonymous: import_lazy_zod102.z.boolean().default(true),
|
|
56626
|
+
/** Whether the client declared this revision itself (not served under it by default). */
|
|
56627
|
+
clientDeclaredRevision: import_lazy_zod102.z.boolean().optional()
|
|
56102
56628
|
});
|
|
56103
56629
|
name38 = "handle:mcp-202607280728";
|
|
56104
56630
|
({ Stage: Stage38 } = FlowHooksOf(name38));
|
|
@@ -56145,6 +56671,7 @@ var init_handle_mcp_20260728_flow = __esm({
|
|
|
56145
56671
|
return;
|
|
56146
56672
|
}
|
|
56147
56673
|
this.state.set("version", result.version);
|
|
56674
|
+
this.state.set("clientDeclaredRevision", strictHeaders);
|
|
56148
56675
|
}
|
|
56149
56676
|
/**
|
|
56150
56677
|
* Resolve a tool's input JSON Schema so `x-mcp-header` annotations can be
|
|
@@ -56169,7 +56696,7 @@ var init_handle_mcp_20260728_flow = __esm({
|
|
|
56169
56696
|
if (!context) return (fn) => fn();
|
|
56170
56697
|
const storage = this.scope.providers.get(FrontMcpContextStorage);
|
|
56171
56698
|
return async (fn) => {
|
|
56172
|
-
await (0,
|
|
56699
|
+
await (0, import_utils95.runRequestExclusive)(() => storage.runWithContext(context, fn));
|
|
56173
56700
|
};
|
|
56174
56701
|
}
|
|
56175
56702
|
findToolSchema(scope, toolName) {
|
|
@@ -56260,10 +56787,12 @@ var init_handle_mcp_20260728_flow = __esm({
|
|
|
56260
56787
|
// authenticated caller.
|
|
56261
56788
|
...authInfoFromAuthorization(auth),
|
|
56262
56789
|
// The shared handlers still key per-request state (provider views, notifications)
|
|
56263
|
-
// off an id.
|
|
56264
|
-
sessionId
|
|
56790
|
+
// off an id. It names the request only, the same per-request id as the request
|
|
56791
|
+
// context's `sessionId`; the request context does not vouch for it (#629).
|
|
56792
|
+
sessionId: auth.session?.id ?? this.tryGetContext()?.sessionId
|
|
56265
56793
|
} : void 0,
|
|
56266
56794
|
isAnonymous: this.state.required.isAnonymous,
|
|
56795
|
+
clientDeclaredRevision: this.state.clientDeclaredRevision !== false,
|
|
56267
56796
|
composeInstructions: () => this.scope.metadata.instructions,
|
|
56268
56797
|
notificationSink: sink,
|
|
56269
56798
|
traceContext: extractTraceContext(meta)
|
|
@@ -56370,12 +56899,12 @@ function getQuerySessionId2(urlPath) {
|
|
|
56370
56899
|
return void 0;
|
|
56371
56900
|
}
|
|
56372
56901
|
}
|
|
56373
|
-
var import_lazy_zod103,
|
|
56902
|
+
var import_lazy_zod103, import_utils96, plan38, stateSessionSchema, stateSchema38, name39, Stage39, HandleSseFlow;
|
|
56374
56903
|
var init_handle_sse_flow = __esm({
|
|
56375
56904
|
"libs/sdk/src/transport/flows/handle.sse.flow.ts"() {
|
|
56376
56905
|
"use strict";
|
|
56377
56906
|
import_lazy_zod103 = require("@frontmcp/lazy-zod");
|
|
56378
|
-
|
|
56907
|
+
import_utils96 = require("@frontmcp/utils");
|
|
56379
56908
|
init_session_id_utils();
|
|
56380
56909
|
init_common();
|
|
56381
56910
|
init_errors();
|
|
@@ -56466,10 +56995,10 @@ var init_handle_sse_flow = __esm({
|
|
|
56466
56995
|
const { request, response } = this.rawInput;
|
|
56467
56996
|
const { token, session } = this.state.required;
|
|
56468
56997
|
const transport = await transportService.createTransporter("sse", token, session.id, response);
|
|
56469
|
-
if ((0,
|
|
56470
|
-
const nodeId = (0,
|
|
56998
|
+
if ((0, import_utils96.getRuntimeContext)().deployment === "distributed") {
|
|
56999
|
+
const nodeId = (0, import_utils96.getMachineId)();
|
|
56471
57000
|
response.setHeader(DEFAULT_FRONTMCP_MACHINE_ID_HEADER, nodeId);
|
|
56472
|
-
const cookie = (0,
|
|
57001
|
+
const cookie = (0, import_utils96.buildSetCookie)({ name: DEFAULT_FRONTMCP_NODE_COOKIE, value: nodeId }, request);
|
|
56473
57002
|
if (cookie) {
|
|
56474
57003
|
const existing = response.getHeader("Set-Cookie");
|
|
56475
57004
|
const existingArr = Array.isArray(existing) ? existing : existing ? [String(existing)] : [];
|
|
@@ -56491,12 +57020,12 @@ var init_handle_sse_flow = __esm({
|
|
|
56491
57020
|
const { request, response } = this.rawInput;
|
|
56492
57021
|
const { token, session } = this.state.required;
|
|
56493
57022
|
const transport = await transportService.getTransporter("sse", token, session.id);
|
|
56494
|
-
if (!transport && (0,
|
|
57023
|
+
if (!transport && (0, import_utils96.getRuntimeContext)().deployment === "distributed") {
|
|
56495
57024
|
const storedSession = await transportService.getStoredSession("sse", token, session.id);
|
|
56496
57025
|
if (storedSession) {
|
|
56497
57026
|
const haManager = this.scope.haManager;
|
|
56498
57027
|
const relay = haManager?.getRelay();
|
|
56499
|
-
if (relay && storedSession.session.nodeId && storedSession.session.nodeId !== (0,
|
|
57028
|
+
if (relay && storedSession.session.nodeId && storedSession.session.nodeId !== (0, import_utils96.getMachineId)()) {
|
|
56500
57029
|
const isAlive2 = await haManager.isNodeAlive(storedSession.session.nodeId);
|
|
56501
57030
|
if (isAlive2) {
|
|
56502
57031
|
try {
|
|
@@ -56722,11 +57251,11 @@ var init_ext_apps_types = __esm({
|
|
|
56722
57251
|
function createExtAppsMessageHandler(options) {
|
|
56723
57252
|
return new ExtAppsMessageHandler(options);
|
|
56724
57253
|
}
|
|
56725
|
-
var
|
|
57254
|
+
var import_utils97, ExtAppsMessageHandler, ExtAppsError, ExtAppsMethodNotFoundError, ExtAppsInvalidParamsError, ExtAppsNotSupportedError, ExtAppsToolNotFoundError;
|
|
56726
57255
|
var init_ext_apps_handler = __esm({
|
|
56727
57256
|
"libs/sdk/src/ext-apps/ext-apps.handler.ts"() {
|
|
56728
57257
|
"use strict";
|
|
56729
|
-
|
|
57258
|
+
import_utils97 = require("@frontmcp/utils");
|
|
56730
57259
|
init_ext_apps_types();
|
|
56731
57260
|
ExtAppsMessageHandler = class {
|
|
56732
57261
|
context;
|
|
@@ -56770,7 +57299,7 @@ var init_ext_apps_handler = __esm({
|
|
|
56770
57299
|
code: errorCode,
|
|
56771
57300
|
message: errorMessage,
|
|
56772
57301
|
// Only include stack traces in development to avoid leaking internals in production
|
|
56773
|
-
data: (0,
|
|
57302
|
+
data: (0, import_utils97.isDevelopment)() && error instanceof Error ? { stack: error.stack } : void 0
|
|
56774
57303
|
}
|
|
56775
57304
|
};
|
|
56776
57305
|
}
|
|
@@ -57132,13 +57661,13 @@ async function lookupStreamableHttpTransport(params) {
|
|
|
57132
57661
|
}
|
|
57133
57662
|
return { kind: "session-not-initialized", recreationError };
|
|
57134
57663
|
}
|
|
57135
|
-
var import_lazy_zod105, import_protocol54,
|
|
57664
|
+
var import_lazy_zod105, import_protocol54, import_utils98, plan40, stateSessionSchema2, stateSchema40, name41, Stage41, HandleStreamableHttpFlow;
|
|
57136
57665
|
var init_handle_streamable_http_flow = __esm({
|
|
57137
57666
|
"libs/sdk/src/transport/flows/handle.streamable-http.flow.ts"() {
|
|
57138
57667
|
"use strict";
|
|
57139
57668
|
import_lazy_zod105 = require("@frontmcp/lazy-zod");
|
|
57140
57669
|
import_protocol54 = require("@frontmcp/protocol");
|
|
57141
|
-
|
|
57670
|
+
import_utils98 = require("@frontmcp/utils");
|
|
57142
57671
|
init_session_id_utils();
|
|
57143
57672
|
init_common();
|
|
57144
57673
|
init_errors();
|
|
@@ -57271,10 +57800,10 @@ var init_handle_streamable_http_flow = __esm({
|
|
|
57271
57800
|
return;
|
|
57272
57801
|
}
|
|
57273
57802
|
}
|
|
57274
|
-
if ((0,
|
|
57275
|
-
const nodeId = (0,
|
|
57803
|
+
if ((0, import_utils98.getRuntimeContext)().deployment === "distributed") {
|
|
57804
|
+
const nodeId = (0, import_utils98.getMachineId)();
|
|
57276
57805
|
response.setHeader(DEFAULT_FRONTMCP_MACHINE_ID_HEADER, nodeId);
|
|
57277
|
-
const cookie = (0,
|
|
57806
|
+
const cookie = (0, import_utils98.buildSetCookie)({ name: DEFAULT_FRONTMCP_NODE_COOKIE, value: nodeId }, request);
|
|
57278
57807
|
if (cookie) {
|
|
57279
57808
|
const existing = response.getHeader("Set-Cookie");
|
|
57280
57809
|
const existingArr = Array.isArray(existing) ? existing : existing ? [String(existing)] : [];
|
|
@@ -57593,21 +58122,21 @@ var init_handle_streamable_http_flow = __esm({
|
|
|
57593
58122
|
});
|
|
57594
58123
|
|
|
57595
58124
|
// libs/sdk/src/transport/transport.error.ts
|
|
57596
|
-
var
|
|
58125
|
+
var import_utils99, JSON_RPC2, rpcError, rpcRequest;
|
|
57597
58126
|
var init_transport_error = __esm({
|
|
57598
58127
|
"libs/sdk/src/transport/transport.error.ts"() {
|
|
57599
58128
|
"use strict";
|
|
57600
|
-
|
|
58129
|
+
import_utils99 = require("@frontmcp/utils");
|
|
57601
58130
|
JSON_RPC2 = "2.0";
|
|
57602
58131
|
rpcError = (message, requestId) => ({
|
|
57603
58132
|
jsonrpc: JSON_RPC2,
|
|
57604
58133
|
error: { code: -32e3, message },
|
|
57605
|
-
id: requestId ?? (0,
|
|
58134
|
+
id: requestId ?? (0, import_utils99.randomUUID)()
|
|
57606
58135
|
// change it to request id + random
|
|
57607
58136
|
});
|
|
57608
58137
|
rpcRequest = (requestId, method, params) => ({
|
|
57609
58138
|
jsonrpc: JSON_RPC2,
|
|
57610
|
-
id: requestId ?? (0,
|
|
58139
|
+
id: requestId ?? (0, import_utils99.randomUUID)(),
|
|
57611
58140
|
method,
|
|
57612
58141
|
...params !== void 0 && { params }
|
|
57613
58142
|
});
|
|
@@ -57670,14 +58199,14 @@ var init_host_validation = __esm({
|
|
|
57670
58199
|
});
|
|
57671
58200
|
|
|
57672
58201
|
// libs/sdk/src/transport/adapters/base-sse-transport.ts
|
|
57673
|
-
var import_content_type, import_raw_body, import_protocol55,
|
|
58202
|
+
var import_content_type, import_raw_body, import_protocol55, import_utils100, MAXIMUM_MESSAGE_SIZE, SSEServerTransport;
|
|
57674
58203
|
var init_base_sse_transport = __esm({
|
|
57675
58204
|
"libs/sdk/src/transport/adapters/base-sse-transport.ts"() {
|
|
57676
58205
|
"use strict";
|
|
57677
58206
|
import_content_type = __toESM(require("content-type"));
|
|
57678
58207
|
import_raw_body = __toESM(require("raw-body"));
|
|
57679
58208
|
import_protocol55 = require("@frontmcp/protocol");
|
|
57680
|
-
|
|
58209
|
+
import_utils100 = require("@frontmcp/utils");
|
|
57681
58210
|
init_transport_errors();
|
|
57682
58211
|
init_host_validation();
|
|
57683
58212
|
MAXIMUM_MESSAGE_SIZE = "4mb";
|
|
@@ -57688,7 +58217,7 @@ var init_base_sse_transport = __esm({
|
|
|
57688
58217
|
constructor(_endpoint, res, options) {
|
|
57689
58218
|
this._endpoint = _endpoint;
|
|
57690
58219
|
this.res = res;
|
|
57691
|
-
this._sessionId = options?.sessionId ?? (0,
|
|
58220
|
+
this._sessionId = options?.sessionId ?? (0, import_utils100.randomUUID)();
|
|
57692
58221
|
this._options = options || { enableDnsRebindingProtection: false };
|
|
57693
58222
|
}
|
|
57694
58223
|
_endpoint;
|
|
@@ -59134,11 +59663,11 @@ var init_transport_remote = __esm({
|
|
|
59134
59663
|
});
|
|
59135
59664
|
|
|
59136
59665
|
// libs/sdk/src/transport/transport.registry.ts
|
|
59137
|
-
var
|
|
59666
|
+
var import_utils101, TransportService;
|
|
59138
59667
|
var init_transport_registry = __esm({
|
|
59139
59668
|
"libs/sdk/src/transport/transport.registry.ts"() {
|
|
59140
59669
|
"use strict";
|
|
59141
|
-
|
|
59670
|
+
import_utils101 = require("@frontmcp/utils");
|
|
59142
59671
|
init_session_store_factory();
|
|
59143
59672
|
init_transport_errors();
|
|
59144
59673
|
init_handle_mcp_20260728_flow();
|
|
@@ -59430,7 +59959,7 @@ var init_transport_registry = __esm({
|
|
|
59430
59959
|
protocol: storedSession.session.protocol,
|
|
59431
59960
|
createdAt: storedSession.createdAt
|
|
59432
59961
|
});
|
|
59433
|
-
const currentNodeId = (0,
|
|
59962
|
+
const currentNodeId = (0, import_utils101.getMachineId)();
|
|
59434
59963
|
if (this.scope.haManager && storedSession.session.nodeId && storedSession.session.nodeId !== currentNodeId) {
|
|
59435
59964
|
const sessionKey = `${this.transportKeyPrefix}${sessionId}`;
|
|
59436
59965
|
const result = await this.scope.haManager.attemptTakeover(sessionKey, storedSession.session.nodeId);
|
|
@@ -59525,7 +60054,7 @@ var init_transport_registry = __esm({
|
|
|
59525
60054
|
authorizationId: key.tokenHash,
|
|
59526
60055
|
protocol: type === "sse" ? "sse" : "streamable-http",
|
|
59527
60056
|
createdAt: Date.now(),
|
|
59528
|
-
nodeId: (0,
|
|
60057
|
+
nodeId: (0, import_utils101.getMachineId)()
|
|
59529
60058
|
},
|
|
59530
60059
|
authorizationId: key.tokenHash,
|
|
59531
60060
|
createdAt: Date.now(),
|
|
@@ -59671,7 +60200,7 @@ var init_transport_registry = __esm({
|
|
|
59671
60200
|
}
|
|
59672
60201
|
/* --------------------------------- internals -------------------------------- */
|
|
59673
60202
|
sha256(value) {
|
|
59674
|
-
return (0,
|
|
60203
|
+
return (0, import_utils101.sha256Hex)(value);
|
|
59675
60204
|
}
|
|
59676
60205
|
/**
|
|
59677
60206
|
* Create a history key from components.
|
|
@@ -59855,9 +60384,9 @@ function persistentSessionCallerKey(authorization) {
|
|
|
59855
60384
|
const sub = authorization?.user?.sub;
|
|
59856
60385
|
if (!(0, import_auth56.isAnonymousSubject)(sub)) {
|
|
59857
60386
|
const iss = authorization?.user?.iss;
|
|
59858
|
-
return `sub:${(0,
|
|
60387
|
+
return `sub:${(0, import_utils102.sha256Hex)(JSON.stringify([typeof iss === "string" ? iss : "", sub]))}`;
|
|
59859
60388
|
}
|
|
59860
|
-
if (authorization?.token) return `token:${(0,
|
|
60389
|
+
if (authorization?.token) return `token:${(0, import_utils102.sha256Hex)(authorization.token)}`;
|
|
59861
60390
|
return null;
|
|
59862
60391
|
}
|
|
59863
60392
|
function claimPersistentSession(session, callerKey, store) {
|
|
@@ -59870,12 +60399,12 @@ function claimPersistentSession(session, callerKey, store) {
|
|
|
59870
60399
|
}
|
|
59871
60400
|
return owners.get(session) === callerKey ? "owner" : "refused";
|
|
59872
60401
|
}
|
|
59873
|
-
var import_auth56,
|
|
60402
|
+
var import_auth56, import_utils102, owners;
|
|
59874
60403
|
var init_persistent_session_owner = __esm({
|
|
59875
60404
|
"libs/sdk/src/transport/persistent-session-owner.ts"() {
|
|
59876
60405
|
"use strict";
|
|
59877
60406
|
import_auth56 = require("@frontmcp/auth");
|
|
59878
|
-
|
|
60407
|
+
import_utils102 = require("@frontmcp/utils");
|
|
59879
60408
|
owners = /* @__PURE__ */ new WeakMap();
|
|
59880
60409
|
}
|
|
59881
60410
|
});
|
|
@@ -59912,7 +60441,7 @@ async function runWebStandardMcp(scope, request, options) {
|
|
|
59912
60441
|
if (options.persistent) {
|
|
59913
60442
|
return options.persistent.transport.handleRequest(request, { authInfo: options.authInfo });
|
|
59914
60443
|
}
|
|
59915
|
-
const sessionId = `web:${(0,
|
|
60444
|
+
const sessionId = `web:${(0, import_utils103.randomUUID)()}`;
|
|
59916
60445
|
const accept = request.headers.get("accept") ?? "";
|
|
59917
60446
|
const wantsStream = request.method === "GET" || options.sse && accept.includes("text/event-stream");
|
|
59918
60447
|
const { mcpServer, transport } = await wireServer(scope, options.serverOptions, sessionId, void 0, !wantsStream);
|
|
@@ -59935,23 +60464,23 @@ async function runWebStandardMcp(scope, request, options) {
|
|
|
59935
60464
|
}
|
|
59936
60465
|
return response;
|
|
59937
60466
|
}
|
|
59938
|
-
var
|
|
60467
|
+
var import_utils103;
|
|
59939
60468
|
var init_web_standard_mcp = __esm({
|
|
59940
60469
|
"libs/sdk/src/transport/web-standard-mcp.ts"() {
|
|
59941
60470
|
"use strict";
|
|
59942
|
-
|
|
60471
|
+
import_utils103 = require("@frontmcp/utils");
|
|
59943
60472
|
init_build_scoped_server_options();
|
|
59944
60473
|
}
|
|
59945
60474
|
});
|
|
59946
60475
|
|
|
59947
60476
|
// libs/sdk/src/scope/flows/http.request.flow.ts
|
|
59948
|
-
var import_auth57, import_lazy_zod108,
|
|
60477
|
+
var import_auth57, import_lazy_zod108, import_utils104, plan42, httpRequestStateSchema, name43, Stage43, HttpRequestFlow;
|
|
59949
60478
|
var init_http_request_flow = __esm({
|
|
59950
60479
|
"libs/sdk/src/scope/flows/http.request.flow.ts"() {
|
|
59951
60480
|
"use strict";
|
|
59952
60481
|
import_auth57 = require("@frontmcp/auth");
|
|
59953
60482
|
import_lazy_zod108 = require("@frontmcp/lazy-zod");
|
|
59954
|
-
|
|
60483
|
+
import_utils104 = require("@frontmcp/utils");
|
|
59955
60484
|
init_session_verify_flow();
|
|
59956
60485
|
init_common();
|
|
59957
60486
|
init_schema7();
|
|
@@ -60030,7 +60559,7 @@ var init_http_request_flow = __esm({
|
|
|
60030
60559
|
const { request } = this.rawInput;
|
|
60031
60560
|
this.requestStartTime = Date.now();
|
|
60032
60561
|
const ctx = this.tryGetContext();
|
|
60033
|
-
this.requestId = ctx?.requestId ?? `req-${(0,
|
|
60562
|
+
this.requestId = ctx?.requestId ?? `req-${(0, import_utils104.randomUUID)()}`;
|
|
60034
60563
|
const headers = request.headers ?? {};
|
|
60035
60564
|
const body = request.body;
|
|
60036
60565
|
const userAgent = headers["user-agent"];
|
|
@@ -60102,7 +60631,10 @@ var init_http_request_flow = __esm({
|
|
|
60102
60631
|
const { request } = this.rawInput;
|
|
60103
60632
|
this.logger.verbose(`[${this.requestId}] checkAuthorization: verifying session`);
|
|
60104
60633
|
try {
|
|
60105
|
-
const result = await this.scope.runFlow("session:verify", {
|
|
60634
|
+
const result = await this.scope.runFlow("session:verify", {
|
|
60635
|
+
request,
|
|
60636
|
+
sessionless: this.servesProtocol20260728()
|
|
60637
|
+
});
|
|
60106
60638
|
if (!result) {
|
|
60107
60639
|
this.logger.error(`[${this.requestId}] failed to verify session`);
|
|
60108
60640
|
throw new SessionVerificationFailedError();
|
|
@@ -60712,13 +61244,13 @@ var init_http_request_flow = __esm({
|
|
|
60712
61244
|
});
|
|
60713
61245
|
|
|
60714
61246
|
// libs/sdk/src/scope/scope.instance.ts
|
|
60715
|
-
var import_reflect_metadata25, import_guard8,
|
|
61247
|
+
var import_reflect_metadata25, import_guard8, import_utils105, SCOPE_NON_DISPATCHABLE_FLOWS, Scope;
|
|
60716
61248
|
var init_scope_instance = __esm({
|
|
60717
61249
|
"libs/sdk/src/scope/scope.instance.ts"() {
|
|
60718
61250
|
"use strict";
|
|
60719
61251
|
import_reflect_metadata25 = require("reflect-metadata");
|
|
60720
61252
|
import_guard8 = require("@frontmcp/guard");
|
|
60721
|
-
|
|
61253
|
+
import_utils105 = require("@frontmcp/utils");
|
|
60722
61254
|
init_agent_registry();
|
|
60723
61255
|
init_call_agent_flow();
|
|
60724
61256
|
init_app_registry();
|
|
@@ -60851,7 +61383,7 @@ var init_scope_instance = __esm({
|
|
|
60851
61383
|
await this.scopeProviders.ready;
|
|
60852
61384
|
const scopeRef = { kind: "scope", id: this.id, ref: _Scope };
|
|
60853
61385
|
const scopeProviders = this.scopeProviders;
|
|
60854
|
-
const perf = (0,
|
|
61386
|
+
const perf = (0, import_utils105.getEnvFlag)("FRONTMCP_PERF");
|
|
60855
61387
|
const t0 = perf ? performance.now() : 0;
|
|
60856
61388
|
const mark = perf ? (label) => this.logger.info(`[PERF] ${label}: ${(performance.now() - t0).toFixed(1)}ms`) : () => {
|
|
60857
61389
|
};
|
|
@@ -60861,13 +61393,13 @@ var init_scope_instance = __esm({
|
|
|
60861
61393
|
this.scopeApps = new AppRegistry(this.scopeProviders, this.metadata.apps, scopeRef);
|
|
60862
61394
|
this.logger.info(`Initializing ${this.metadata.apps.length} app(s)...`);
|
|
60863
61395
|
const transportConfig = this.metadata.transport;
|
|
60864
|
-
const isDistributed = (0,
|
|
61396
|
+
const isDistributed = (0, import_utils105.getRuntimeContext)().deployment === "distributed";
|
|
60865
61397
|
if (isDistributed && this.metadata.redis && !this.cliMode) {
|
|
60866
61398
|
try {
|
|
60867
61399
|
const haRedis = this.metadata.redis;
|
|
60868
61400
|
this.haManager = HaManager.create({
|
|
60869
61401
|
redis: haRedis,
|
|
60870
|
-
nodeId: (0,
|
|
61402
|
+
nodeId: (0, import_utils105.getMachineId)(),
|
|
60871
61403
|
logger: this.logger
|
|
60872
61404
|
});
|
|
60873
61405
|
await this.haManager.start();
|
|
@@ -60878,7 +61410,7 @@ var init_scope_instance = __esm({
|
|
|
60878
61410
|
}
|
|
60879
61411
|
let transportBus;
|
|
60880
61412
|
if (this.haManager && isDistributed && this.metadata.redis) {
|
|
60881
|
-
transportBus = new RedisTransportBus(this.metadata.redis, (0,
|
|
61413
|
+
transportBus = new RedisTransportBus(this.metadata.redis, (0, import_utils105.getMachineId)(), {
|
|
60882
61414
|
logger: this.logger
|
|
60883
61415
|
});
|
|
60884
61416
|
this.logger.info("[HA] Transport bus created for distributed session routing");
|
|
@@ -60901,7 +61433,7 @@ var init_scope_instance = __esm({
|
|
|
60901
61433
|
}
|
|
60902
61434
|
});
|
|
60903
61435
|
}
|
|
60904
|
-
if ((0,
|
|
61436
|
+
if ((0, import_utils105.getRuntimeContext)().deployment === "serverless") {
|
|
60905
61437
|
const protocolCfg = transportConfig?.protocol;
|
|
60906
61438
|
const hasSSE = typeof protocolCfg === "string" ? protocolCfg !== "stateless-api" : protocolCfg?.sse === true;
|
|
60907
61439
|
if (hasSSE) {
|
|
@@ -60928,7 +61460,7 @@ var init_scope_instance = __esm({
|
|
|
60928
61460
|
sqlite: elicitationSqlite,
|
|
60929
61461
|
keyPrefix: elicitationRedis?.keyPrefix ?? "mcp:elicit:",
|
|
60930
61462
|
logger: this.logger,
|
|
60931
|
-
isEdgeRuntime: (0,
|
|
61463
|
+
isEdgeRuntime: (0, import_utils105.isEdgeRuntime)()
|
|
60932
61464
|
});
|
|
60933
61465
|
this._elicitationStore = elicitStore;
|
|
60934
61466
|
this.elicitationBackendType = elicitType;
|
|
@@ -60942,7 +61474,7 @@ var init_scope_instance = __esm({
|
|
|
60942
61474
|
);
|
|
60943
61475
|
const tasksEnabledForCli = this.cliMode && hasPersistentBackend;
|
|
60944
61476
|
const hasDistributedTaskBackend = Boolean(tasksConfig?.redis ?? this.metadata.redis);
|
|
60945
|
-
const onEdgeRuntime = (0,
|
|
61477
|
+
const onEdgeRuntime = (0, import_utils105.isEdgeRuntime)();
|
|
60946
61478
|
const tasksUnavailableOnEdge = onEdgeRuntime && !hasDistributedTaskBackend && !tasksExplicitlyEnabled;
|
|
60947
61479
|
if (tasksUnavailableOnEdge) {
|
|
60948
61480
|
this.logger.info(
|
|
@@ -61386,6 +61918,15 @@ var init_scope_instance = __esm({
|
|
|
61386
61918
|
notificationService: this.notificationService,
|
|
61387
61919
|
flowRegistry: this.scopeFlows,
|
|
61388
61920
|
toolRegistry: this.scopeTools,
|
|
61921
|
+
http: {
|
|
61922
|
+
server: this.server,
|
|
61923
|
+
checkClientIp: (request, response) => this.runFlow("http:ip-filter", {
|
|
61924
|
+
request,
|
|
61925
|
+
response
|
|
61926
|
+
}),
|
|
61927
|
+
entryPath: this.entryPath,
|
|
61928
|
+
routeBase: this.routeBase
|
|
61929
|
+
},
|
|
61389
61930
|
logger: this.logger
|
|
61390
61931
|
});
|
|
61391
61932
|
this._scopeChannels = channelResult.channelRegistry;
|
|
@@ -61790,12 +62331,12 @@ var init_scope_instance = __esm({
|
|
|
61790
62331
|
return this._authoritiesScopeMapping;
|
|
61791
62332
|
}
|
|
61792
62333
|
/**
|
|
61793
|
-
* Collect
|
|
61794
|
-
*
|
|
61795
|
-
*
|
|
62334
|
+
* Collect the OAuth scopes entries declare on their `authProviders`. The PRM
|
|
62335
|
+
* endpoint advertises them in `scopes_supported` (RFC 9728) next to the scopes
|
|
62336
|
+
* the auth mode grants (`resourceScopesFor`), not a fixed OIDC list (#629).
|
|
61796
62337
|
*/
|
|
61797
62338
|
getAllSupportedScopes() {
|
|
61798
|
-
const scopes = /* @__PURE__ */ new Set(
|
|
62339
|
+
const scopes = /* @__PURE__ */ new Set();
|
|
61799
62340
|
const collectScopes = (metadata) => {
|
|
61800
62341
|
const authProviders = metadata?.["authProviders"];
|
|
61801
62342
|
if (!Array.isArray(authProviders)) return;
|
|
@@ -63362,7 +63903,7 @@ async function runHttpRequestFlowWeb(scope, request, opts = {}) {
|
|
|
63362
63903
|
const serverRequest = await toServerRequest(request, url, opts.ctx, opts.persistent, opts.env);
|
|
63363
63904
|
let output;
|
|
63364
63905
|
try {
|
|
63365
|
-
output = await (0,
|
|
63906
|
+
output = await (0, import_utils106.runRequestExclusive)(
|
|
63366
63907
|
() => scope.runFlow("http:request", {
|
|
63367
63908
|
request: serverRequest,
|
|
63368
63909
|
response: {}
|
|
@@ -63380,7 +63921,7 @@ async function runMatchingHttpFlowWeb(scope, request, opts = {}) {
|
|
|
63380
63921
|
if (!flowName) return void 0;
|
|
63381
63922
|
let output;
|
|
63382
63923
|
try {
|
|
63383
|
-
output = await (0,
|
|
63924
|
+
output = await (0, import_utils106.runRequestExclusive)(
|
|
63384
63925
|
() => scope.runFlow(flowName, {
|
|
63385
63926
|
request: serverRequest,
|
|
63386
63927
|
response: {}
|
|
@@ -63458,10 +63999,57 @@ function findMisconfiguration(error) {
|
|
|
63458
63999
|
if (typeof code === "string" && MISCONFIGURATION_REMEDIES[code]) {
|
|
63459
64000
|
return { code, remedy: MISCONFIGURATION_REMEDIES[code] };
|
|
63460
64001
|
}
|
|
64002
|
+
if (current.name === "ZodError") return { code: "CONFIG_INVALID", remedy: CONFIG_INVALID_REMEDY };
|
|
63461
64003
|
current = current.cause;
|
|
63462
64004
|
}
|
|
63463
64005
|
return void 0;
|
|
63464
64006
|
}
|
|
64007
|
+
function createDeferredServerBuild(build, options = {}) {
|
|
64008
|
+
const now = options.now ?? Date.now;
|
|
64009
|
+
let built;
|
|
64010
|
+
let inFlight;
|
|
64011
|
+
let failure;
|
|
64012
|
+
return {
|
|
64013
|
+
get(arg) {
|
|
64014
|
+
if (built) return Promise.resolve(built.value);
|
|
64015
|
+
if (inFlight) return inFlight;
|
|
64016
|
+
if (failure && now() < failure.retryAt) return Promise.reject(failure.error);
|
|
64017
|
+
const failures = failure?.count ?? 0;
|
|
64018
|
+
inFlight = build(arg).then(
|
|
64019
|
+
(value) => {
|
|
64020
|
+
built = { value };
|
|
64021
|
+
failure = void 0;
|
|
64022
|
+
inFlight = void 0;
|
|
64023
|
+
return value;
|
|
64024
|
+
},
|
|
64025
|
+
(error) => {
|
|
64026
|
+
const delay = Math.min(STARTUP_RETRY_MAX_MS, STARTUP_RETRY_MIN_MS * 2 ** failures);
|
|
64027
|
+
failure = { error, retryAt: now() + delay, count: failures + 1 };
|
|
64028
|
+
inFlight = void 0;
|
|
64029
|
+
options.onFailure?.(error);
|
|
64030
|
+
throw error;
|
|
64031
|
+
}
|
|
64032
|
+
);
|
|
64033
|
+
return inFlight;
|
|
64034
|
+
},
|
|
64035
|
+
retryAfterSeconds() {
|
|
64036
|
+
if (!failure) return 0;
|
|
64037
|
+
return Math.max(1, Math.ceil((failure.retryAt - now()) / 1e3));
|
|
64038
|
+
}
|
|
64039
|
+
};
|
|
64040
|
+
}
|
|
64041
|
+
function startupFailureResponse(error, retryAfterSeconds = 1) {
|
|
64042
|
+
const misconfigured = misconfigurationResponse(error);
|
|
64043
|
+
if (misconfigured) return misconfigured;
|
|
64044
|
+
return Response.json(
|
|
64045
|
+
{
|
|
64046
|
+
error: "server_unavailable",
|
|
64047
|
+
code: "SERVER_START_FAILED",
|
|
64048
|
+
message: "The server failed to start, so it refuses requests. Its log has the cause; a request after the Retry-After delay tries to start it again."
|
|
64049
|
+
},
|
|
64050
|
+
{ status: 503, headers: { "Retry-After": String(Math.max(1, retryAfterSeconds)) } }
|
|
64051
|
+
);
|
|
64052
|
+
}
|
|
63465
64053
|
function flowErrorToHttpOutput(error) {
|
|
63466
64054
|
const misconfiguration = findMisconfiguration(error);
|
|
63467
64055
|
if (misconfiguration) {
|
|
@@ -63495,11 +64083,11 @@ function flowErrorToHttpOutput(error) {
|
|
|
63495
64083
|
}
|
|
63496
64084
|
return { kind: "text", status: 500, body: "Internal Server Error", contentType: "text/plain; charset=utf-8" };
|
|
63497
64085
|
}
|
|
63498
|
-
var
|
|
64086
|
+
var import_utils106, CLOUDFLARE_WORKERS_USER_AGENT, MISCONFIGURATION_REMEDIES, CONFIG_INVALID_REMEDY, STARTUP_RETRY_MIN_MS, STARTUP_RETRY_MAX_MS;
|
|
63499
64087
|
var init_web_fetch_handler = __esm({
|
|
63500
64088
|
"libs/sdk/src/transport/web-fetch-handler.ts"() {
|
|
63501
64089
|
"use strict";
|
|
63502
|
-
|
|
64090
|
+
import_utils106 = require("@frontmcp/utils");
|
|
63503
64091
|
init_common();
|
|
63504
64092
|
init_server_tokens();
|
|
63505
64093
|
init_path_utils();
|
|
@@ -63515,6 +64103,9 @@ var init_web_fetch_handler = __esm({
|
|
|
63515
64103
|
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.",
|
|
63516
64104
|
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."
|
|
63517
64105
|
};
|
|
64106
|
+
CONFIG_INVALID_REMEDY = "The FrontMCP configuration failed validation, so the server refuses to start. The server log names the invalid fields.";
|
|
64107
|
+
STARTUP_RETRY_MIN_MS = 1e3;
|
|
64108
|
+
STARTUP_RETRY_MAX_MS = 6e4;
|
|
63518
64109
|
}
|
|
63519
64110
|
});
|
|
63520
64111
|
|
|
@@ -63780,14 +64371,14 @@ var init_base_host_adapter = __esm({
|
|
|
63780
64371
|
});
|
|
63781
64372
|
|
|
63782
64373
|
// libs/sdk/src/server/adapters/express.host.adapter.ts
|
|
63783
|
-
var http, import_cors, import_express,
|
|
64374
|
+
var http, import_cors, import_express, import_utils107, DEFAULT_EXPRESS_BODY_LIMIT, ExpressHostAdapter;
|
|
63784
64375
|
var init_express_host_adapter = __esm({
|
|
63785
64376
|
"libs/sdk/src/server/adapters/express.host.adapter.ts"() {
|
|
63786
64377
|
"use strict";
|
|
63787
64378
|
http = __toESM(require("node:http"));
|
|
63788
64379
|
import_cors = __toESM(require("cors"));
|
|
63789
64380
|
import_express = __toESM(require("express"));
|
|
63790
|
-
|
|
64381
|
+
import_utils107 = require("@frontmcp/utils");
|
|
63791
64382
|
init_mcp_error();
|
|
63792
64383
|
init_host_validation_middleware();
|
|
63793
64384
|
init_resolve_allowed_hosts();
|
|
@@ -63970,8 +64561,8 @@ var init_express_host_adapter = __esm({
|
|
|
63970
64561
|
}
|
|
63971
64562
|
async cleanupStaleSocket(socketPath) {
|
|
63972
64563
|
try {
|
|
63973
|
-
if (await (0,
|
|
63974
|
-
await (0,
|
|
64564
|
+
if (await (0, import_utils107.fileExists)(socketPath)) {
|
|
64565
|
+
await (0, import_utils107.unlink)(socketPath);
|
|
63975
64566
|
}
|
|
63976
64567
|
} catch {
|
|
63977
64568
|
}
|
|
@@ -63994,11 +64585,11 @@ var init_node_express_host = __esm({
|
|
|
63994
64585
|
});
|
|
63995
64586
|
|
|
63996
64587
|
// libs/sdk/src/server/server.instance.ts
|
|
63997
|
-
var
|
|
64588
|
+
var import_utils108, DEFAULT_CORS, FrontMcpServerInstance;
|
|
63998
64589
|
var init_server_instance = __esm({
|
|
63999
64590
|
"libs/sdk/src/server/server.instance.ts"() {
|
|
64000
64591
|
"use strict";
|
|
64001
|
-
|
|
64592
|
+
import_utils108 = require("@frontmcp/utils");
|
|
64002
64593
|
init_common();
|
|
64003
64594
|
init_health2();
|
|
64004
64595
|
init_metrics2();
|
|
@@ -64100,7 +64691,7 @@ var init_server_instance = __esm({
|
|
|
64100
64691
|
}
|
|
64101
64692
|
async start() {
|
|
64102
64693
|
this.prepare();
|
|
64103
|
-
const deploymentMode = (0,
|
|
64694
|
+
const deploymentMode = (0, import_utils108.getRuntimeContext)().deployment;
|
|
64104
64695
|
const bindAddress = resolveBindAddress(this.config.security, deploymentMode);
|
|
64105
64696
|
const isProduction6 = process.env["NODE_ENV"] === "production";
|
|
64106
64697
|
const findings = auditSecurityDefaults(
|
|
@@ -64338,11 +64929,11 @@ function resolveConfigInput(optionsOrClass) {
|
|
|
64338
64929
|
}
|
|
64339
64930
|
return optionsOrClass;
|
|
64340
64931
|
}
|
|
64341
|
-
var
|
|
64932
|
+
var import_utils109, stdioServing, FrontMcpInstance;
|
|
64342
64933
|
var init_front_mcp = __esm({
|
|
64343
64934
|
"libs/sdk/src/front-mcp/front-mcp.ts"() {
|
|
64344
64935
|
"use strict";
|
|
64345
|
-
|
|
64936
|
+
import_utils109 = require("@frontmcp/utils");
|
|
64346
64937
|
init_common();
|
|
64347
64938
|
init_direct();
|
|
64348
64939
|
init_errors();
|
|
@@ -64527,11 +65118,16 @@ var init_front_mcp = __esm({
|
|
|
64527
65118
|
*/
|
|
64528
65119
|
static async createFetchHandler(options) {
|
|
64529
65120
|
const build = async (deferred) => {
|
|
64530
|
-
|
|
65121
|
+
let frontMcp;
|
|
64531
65122
|
try {
|
|
65123
|
+
frontMcp = new _FrontMcpInstance2(frontMcpMetadataSchema.parse(options));
|
|
64532
65124
|
await frontMcp.ready;
|
|
64533
65125
|
} catch (err) {
|
|
64534
|
-
if (deferred)
|
|
65126
|
+
if (deferred) {
|
|
65127
|
+
const message = "FrontMCP failed to start; requests are refused until a retry succeeds";
|
|
65128
|
+
if (frontMcp?.log) frontMcp.log.error(message, err);
|
|
65129
|
+
else console.error(`[frontmcp] ${message}`, err);
|
|
65130
|
+
}
|
|
64535
65131
|
throw err;
|
|
64536
65132
|
}
|
|
64537
65133
|
const scope = frontMcp.getPrimaryScope();
|
|
@@ -64541,25 +65137,15 @@ var init_front_mcp = __esm({
|
|
|
64541
65137
|
frontMcp.log?.info("FrontMCP fetch handler created (web-standard transport)");
|
|
64542
65138
|
return createWebFetchHandler(scope);
|
|
64543
65139
|
};
|
|
64544
|
-
if (!(0,
|
|
65140
|
+
if (!(0, import_utils109.isEdgeRuntime)()) return build(false);
|
|
64545
65141
|
assertStaticStartupConfig(options);
|
|
64546
|
-
|
|
64547
|
-
let building;
|
|
65142
|
+
const server = createDeferredServerBuild(() => build(true));
|
|
64548
65143
|
return async (request, ctx, env) => {
|
|
64549
|
-
|
|
64550
|
-
|
|
64551
|
-
|
|
64552
|
-
|
|
64553
|
-
|
|
64554
|
-
});
|
|
64555
|
-
}
|
|
64556
|
-
try {
|
|
64557
|
-
inner = await building;
|
|
64558
|
-
} catch (err) {
|
|
64559
|
-
const misconfigured = misconfigurationResponse(err);
|
|
64560
|
-
if (misconfigured) return misconfigured;
|
|
64561
|
-
throw err;
|
|
64562
|
-
}
|
|
65144
|
+
let inner;
|
|
65145
|
+
try {
|
|
65146
|
+
inner = await server.get();
|
|
65147
|
+
} catch (err) {
|
|
65148
|
+
return startupFailureResponse(err, server.retryAfterSeconds());
|
|
64563
65149
|
}
|
|
64564
65150
|
return inner(request, ctx, env);
|
|
64565
65151
|
};
|
|
@@ -64696,8 +65282,8 @@ var init_front_mcp = __esm({
|
|
|
64696
65282
|
frontMcp.log?.info(`MCP server listening on unix://${socketPath}`);
|
|
64697
65283
|
const cleanup = async () => {
|
|
64698
65284
|
try {
|
|
64699
|
-
if (await (0,
|
|
64700
|
-
await (0,
|
|
65285
|
+
if (await (0, import_utils109.fileExists)(socketPath)) {
|
|
65286
|
+
await (0, import_utils109.unlink)(socketPath);
|
|
64701
65287
|
}
|
|
64702
65288
|
} catch {
|
|
64703
65289
|
}
|
|
@@ -64841,7 +65427,7 @@ var init_front_mcp = __esm({
|
|
|
64841
65427
|
};
|
|
64842
65428
|
const mcpServer = new McpServer2(scope.metadata.info, serverOptions);
|
|
64843
65429
|
startupCleanups.push(() => mcpServer.close());
|
|
64844
|
-
const sessionId = `stdio:${(0,
|
|
65430
|
+
const sessionId = `stdio:${(0, import_utils109.randomUUID)()}`;
|
|
64845
65431
|
const handlers = createMcpHandlers2({ scope, serverOptions, composeInstructions });
|
|
64846
65432
|
for (const handler of handlers) {
|
|
64847
65433
|
const originalHandler = handler.handler;
|
|
@@ -65161,9 +65747,9 @@ function FrontMcp(providedMetadata) {
|
|
|
65161
65747
|
Reflect.defineMetadata(FrontMcpTokens[property] ?? property, metadata[property], target);
|
|
65162
65748
|
}
|
|
65163
65749
|
Reflect.defineMetadata(FRONTMCP_CONFIG_METADATA_KEY, metadata, target);
|
|
65164
|
-
const isServerless2 = (0,
|
|
65165
|
-
const isSchemaExtract = (0,
|
|
65166
|
-
const isStdio = (0,
|
|
65750
|
+
const isServerless2 = (0, import_utils110.getEnvFlag)("FRONTMCP_SERVERLESS");
|
|
65751
|
+
const isSchemaExtract = (0, import_utils110.getEnvFlag)("FRONTMCP_SCHEMA_EXTRACT");
|
|
65752
|
+
const isStdio = (0, import_utils110.getEnvFlag)("FRONTMCP_STDIO");
|
|
65167
65753
|
const taskWorkerId = typeof process !== "undefined" ? process.env?.["FRONTMCP_RUN_TASK_ID"] : void 0;
|
|
65168
65754
|
if (taskWorkerId && metadata.serve !== false) {
|
|
65169
65755
|
void (async () => {
|
|
@@ -65189,7 +65775,7 @@ function FrontMcp(providedMetadata) {
|
|
|
65189
65775
|
} else if (isServerless2) {
|
|
65190
65776
|
const ServerlessInstance = getFrontMcpInstance();
|
|
65191
65777
|
const { setServerlessHandler: setServerlessHandler2, setServerlessHandlerPromise: setServerlessHandlerPromise2, setServerlessHandlerError: setServerlessHandlerError2 } = getServerlessHandlerFns();
|
|
65192
|
-
const isWorker = (0,
|
|
65778
|
+
const isWorker = (0, import_utils110.getEnvFlag)("FRONTMCP_WORKER");
|
|
65193
65779
|
const handlerPromise = isWorker ? ServerlessInstance.createFetchHandler(metadata) : ServerlessInstance.createHandler(metadata);
|
|
65194
65780
|
setServerlessHandlerPromise2(handlerPromise);
|
|
65195
65781
|
handlerPromise.then(setServerlessHandler2).catch((err) => {
|
|
@@ -65202,12 +65788,12 @@ function FrontMcp(providedMetadata) {
|
|
|
65202
65788
|
}
|
|
65203
65789
|
};
|
|
65204
65790
|
}
|
|
65205
|
-
var import_reflect_metadata28,
|
|
65791
|
+
var import_reflect_metadata28, import_utils110, FRONT_MCP_DECORATOR_BASENAMES, FRONTMCP_CONFIG_METADATA_KEY, _FrontMcpInstance, _serverlessHandlerFns;
|
|
65206
65792
|
var init_front_mcp_decorator = __esm({
|
|
65207
65793
|
"libs/sdk/src/common/decorators/front-mcp.decorator.ts"() {
|
|
65208
65794
|
"use strict";
|
|
65209
65795
|
import_reflect_metadata28 = require("reflect-metadata");
|
|
65210
|
-
|
|
65796
|
+
import_utils110 = require("@frontmcp/utils");
|
|
65211
65797
|
init_decorator_errors();
|
|
65212
65798
|
init_mcp_error();
|
|
65213
65799
|
init_metadata();
|
|
@@ -66166,12 +66752,12 @@ var init_adapter_entry = __esm({
|
|
|
66166
66752
|
});
|
|
66167
66753
|
|
|
66168
66754
|
// libs/sdk/src/common/entries/tool.entry.ts
|
|
66169
|
-
var import_lazy_zod109,
|
|
66755
|
+
var import_lazy_zod109, import_utils112, ToolEntry;
|
|
66170
66756
|
var init_tool_entry = __esm({
|
|
66171
66757
|
"libs/sdk/src/common/entries/tool.entry.ts"() {
|
|
66172
66758
|
"use strict";
|
|
66173
66759
|
import_lazy_zod109 = require("@frontmcp/lazy-zod");
|
|
66174
|
-
|
|
66760
|
+
import_utils112 = require("@frontmcp/utils");
|
|
66175
66761
|
init_base_entry();
|
|
66176
66762
|
ToolEntry = class extends BaseEntry {
|
|
66177
66763
|
owner;
|
|
@@ -66238,7 +66824,7 @@ var init_tool_entry = __esm({
|
|
|
66238
66824
|
try {
|
|
66239
66825
|
return (0, import_lazy_zod109.toJSONSchema)(import_lazy_zod109.z.object(this.inputSchema), { io: "input", unrepresentable: "any" });
|
|
66240
66826
|
} catch (error) {
|
|
66241
|
-
if ((0,
|
|
66827
|
+
if ((0, import_utils112.isDebug)() || (0, import_utils112.isDevelopment)()) {
|
|
66242
66828
|
console.warn("[ToolEntry] Failed to convert Zod schema to JSON Schema:", error);
|
|
66243
66829
|
}
|
|
66244
66830
|
return { type: "object", properties: {} };
|
|
@@ -66284,7 +66870,7 @@ var init_tool_entry = __esm({
|
|
|
66284
66870
|
const json = (0, import_lazy_zod109.toJSONSchema)(zodSchema);
|
|
66285
66871
|
return json && json["type"] === "object" ? json : null;
|
|
66286
66872
|
} catch (error) {
|
|
66287
|
-
if ((0,
|
|
66873
|
+
if ((0, import_utils112.isDebug)() || (0, import_utils112.isDevelopment)()) {
|
|
66288
66874
|
console.warn("[ToolEntry] Failed to convert output Zod schema to JSON Schema:", error);
|
|
66289
66875
|
}
|
|
66290
66876
|
return null;
|
|
@@ -67476,6 +68062,7 @@ __export(index_exports, {
|
|
|
67476
68062
|
create: () => create,
|
|
67477
68063
|
createChildSpanContext: () => createChildSpanContext,
|
|
67478
68064
|
createContextResolver: () => createContextResolver,
|
|
68065
|
+
createDeferredServerBuild: () => createDeferredServerBuild,
|
|
67479
68066
|
createDirectResolver: () => createDirectResolver,
|
|
67480
68067
|
createElicitationStore: () => createElicitationStore,
|
|
67481
68068
|
createElicitationStoreFromStorage: () => createElicitationStoreFromStorage,
|
|
@@ -67583,6 +68170,7 @@ __export(index_exports, {
|
|
|
67583
68170
|
getElicitationSecret: () => getElicitationSecret,
|
|
67584
68171
|
getGlobalStoreConfig: () => getGlobalStoreConfig,
|
|
67585
68172
|
getNestedValue: () => getNestedValue,
|
|
68173
|
+
getPinnedPublicUrl: () => getPinnedPublicUrl,
|
|
67586
68174
|
getRegistryUrl: () => getRegistryUrl,
|
|
67587
68175
|
getRequestBaseUrl: () => getRequestBaseUrl,
|
|
67588
68176
|
getRunningTool: () => getRunningTool,
|
|
@@ -67758,6 +68346,7 @@ __export(index_exports, {
|
|
|
67758
68346
|
skillsConfigOptionsSchema: () => skillsConfigOptionsSchema,
|
|
67759
68347
|
skippedAppBehaviorSchema: () => import_auth5.skippedAppBehaviorSchema,
|
|
67760
68348
|
sqliteOptionsSchema: () => sqliteOptionsSchema,
|
|
68349
|
+
startupFailureResponse: () => startupFailureResponse,
|
|
67761
68350
|
staticAuthOptionsSchema: () => import_auth7.staticAuthOptionsSchema,
|
|
67762
68351
|
storageProviderSchema: () => storageProviderSchema,
|
|
67763
68352
|
supportsStreaming: () => supportsStreaming,
|
|
@@ -67945,22 +68534,22 @@ function createMemorySkillProvider(options = {}) {
|
|
|
67945
68534
|
init_skill_utils();
|
|
67946
68535
|
|
|
67947
68536
|
// libs/sdk/src/skill/skill-directory-loader.ts
|
|
67948
|
-
var
|
|
68537
|
+
var import_utils113 = require("@frontmcp/utils");
|
|
67949
68538
|
init_skill_metadata();
|
|
67950
68539
|
init_sdk_errors();
|
|
67951
68540
|
init_skill_md_parser();
|
|
67952
68541
|
async function scanSkillResources(dirPath) {
|
|
67953
68542
|
const resources = {};
|
|
67954
|
-
const scriptsPath = (0,
|
|
67955
|
-
const referencesPath = (0,
|
|
67956
|
-
const assetsPath = (0,
|
|
67957
|
-
const examplesPath = (0,
|
|
68543
|
+
const scriptsPath = (0, import_utils113.joinPath)(dirPath, "scripts");
|
|
68544
|
+
const referencesPath = (0, import_utils113.joinPath)(dirPath, "references");
|
|
68545
|
+
const assetsPath = (0, import_utils113.joinPath)(dirPath, "assets");
|
|
68546
|
+
const examplesPath = (0, import_utils113.joinPath)(dirPath, "examples");
|
|
67958
68547
|
const checks = await Promise.all([
|
|
67959
68548
|
checkDirectory(scriptsPath),
|
|
67960
68549
|
checkDirectory(referencesPath),
|
|
67961
68550
|
checkDirectory(assetsPath),
|
|
67962
68551
|
checkDirectory(examplesPath),
|
|
67963
|
-
(0,
|
|
68552
|
+
(0, import_utils113.fileExists)((0, import_utils113.joinPath)(dirPath, "SKILL.md"))
|
|
67964
68553
|
]);
|
|
67965
68554
|
if (checks[0]) resources.scripts = scriptsPath;
|
|
67966
68555
|
if (checks[1]) resources.references = referencesPath;
|
|
@@ -67973,7 +68562,7 @@ async function scanSkillResources(dirPath) {
|
|
|
67973
68562
|
}
|
|
67974
68563
|
async function checkDirectory(path3) {
|
|
67975
68564
|
try {
|
|
67976
|
-
const s = await (0,
|
|
68565
|
+
const s = await (0, import_utils113.stat)(path3);
|
|
67977
68566
|
return s.isDirectory();
|
|
67978
68567
|
} catch (err) {
|
|
67979
68568
|
if (err.code === "ENOENT") return false;
|
|
@@ -67986,16 +68575,16 @@ async function findNestedSkillMd(dirPath, maxDepth = Number.POSITIVE_INFINITY) {
|
|
|
67986
68575
|
if (depth > maxDepth) return;
|
|
67987
68576
|
let entries;
|
|
67988
68577
|
try {
|
|
67989
|
-
entries = await (0,
|
|
68578
|
+
entries = await (0, import_utils113.readdir)(current);
|
|
67990
68579
|
} catch (err) {
|
|
67991
68580
|
if (err.code === "ENOENT") return;
|
|
67992
68581
|
throw err;
|
|
67993
68582
|
}
|
|
67994
68583
|
for (const entry of entries) {
|
|
67995
|
-
const full = (0,
|
|
68584
|
+
const full = (0, import_utils113.joinPath)(current, entry);
|
|
67996
68585
|
let entryStat;
|
|
67997
68586
|
try {
|
|
67998
|
-
entryStat = await (0,
|
|
68587
|
+
entryStat = await (0, import_utils113.stat)(full);
|
|
67999
68588
|
} catch (err) {
|
|
68000
68589
|
if (err.code === "ENOENT") continue;
|
|
68001
68590
|
throw err;
|
|
@@ -68011,19 +68600,19 @@ async function findNestedSkillMd(dirPath, maxDepth = Number.POSITIVE_INFINITY) {
|
|
|
68011
68600
|
return found.sort();
|
|
68012
68601
|
}
|
|
68013
68602
|
async function loadSkillDirectory(dirPath, logger) {
|
|
68014
|
-
const skillMdPath = (0,
|
|
68015
|
-
const exists = await (0,
|
|
68603
|
+
const skillMdPath = (0, import_utils113.joinPath)(dirPath, "SKILL.md");
|
|
68604
|
+
const exists = await (0, import_utils113.fileExists)(skillMdPath);
|
|
68016
68605
|
if (!exists) {
|
|
68017
|
-
throw new InvalidSkillError((0,
|
|
68606
|
+
throw new InvalidSkillError((0, import_utils113.basename)(dirPath), "SKILL.md not found in directory");
|
|
68018
68607
|
}
|
|
68019
68608
|
let content;
|
|
68020
68609
|
let partialMetadata;
|
|
68021
68610
|
try {
|
|
68022
|
-
content = await (0,
|
|
68611
|
+
content = await (0, import_utils113.readFile)(skillMdPath, "utf-8");
|
|
68023
68612
|
const { frontmatter, body } = parseSkillMdFrontmatter(content);
|
|
68024
68613
|
partialMetadata = skillMdFrontmatterToMetadata(frontmatter, body);
|
|
68025
68614
|
} catch (err) {
|
|
68026
|
-
const name44 = (0,
|
|
68615
|
+
const name44 = (0, import_utils113.basename)(dirPath);
|
|
68027
68616
|
const message = err instanceof Error ? err.message : String(err);
|
|
68028
68617
|
throw new InvalidSkillError(name44, `Failed to read or parse SKILL.md: ${message}`);
|
|
68029
68618
|
}
|
|
@@ -68034,13 +68623,13 @@ async function loadSkillDirectory(dirPath, logger) {
|
|
|
68034
68623
|
}
|
|
68035
68624
|
const nested = await findNestedSkillMd(dirPath);
|
|
68036
68625
|
if (nested.length > 0) {
|
|
68037
|
-
const skillName2 = partialMetadata.name ?? (0,
|
|
68626
|
+
const skillName2 = partialMetadata.name ?? (0, import_utils113.basename)(dirPath);
|
|
68038
68627
|
throw new InvalidSkillError(
|
|
68039
68628
|
skillName2,
|
|
68040
68629
|
`Skill directory contains nested SKILL.md files (forbidden by SEP-2640 \xA7Resource Mapping): ${nested.join(", ")}`
|
|
68041
68630
|
);
|
|
68042
68631
|
}
|
|
68043
|
-
const dirName = (0,
|
|
68632
|
+
const dirName = (0, import_utils113.basename)(dirPath);
|
|
68044
68633
|
if (partialMetadata.name && dirName && partialMetadata.name !== dirName) {
|
|
68045
68634
|
const msg = `Skill name "${partialMetadata.name}" does not match directory name "${dirName}"`;
|
|
68046
68635
|
if (logger) {
|
|
@@ -68716,6 +69305,7 @@ var ChannelListHook = FlowHooksOf("channels:list");
|
|
|
68716
69305
|
create,
|
|
68717
69306
|
createChildSpanContext,
|
|
68718
69307
|
createContextResolver,
|
|
69308
|
+
createDeferredServerBuild,
|
|
68719
69309
|
createDirectResolver,
|
|
68720
69310
|
createElicitationStore,
|
|
68721
69311
|
createElicitationStoreFromStorage,
|
|
@@ -68822,6 +69412,7 @@ var ChannelListHook = FlowHooksOf("channels:list");
|
|
|
68822
69412
|
getElicitationSecret,
|
|
68823
69413
|
getGlobalStoreConfig,
|
|
68824
69414
|
getNestedValue,
|
|
69415
|
+
getPinnedPublicUrl,
|
|
68825
69416
|
getRegistryUrl,
|
|
68826
69417
|
getRequestBaseUrl,
|
|
68827
69418
|
getRunningTool,
|
|
@@ -68997,6 +69588,7 @@ var ChannelListHook = FlowHooksOf("channels:list");
|
|
|
68997
69588
|
skillsConfigOptionsSchema,
|
|
68998
69589
|
skippedAppBehaviorSchema,
|
|
68999
69590
|
sqliteOptionsSchema,
|
|
69591
|
+
startupFailureResponse,
|
|
69000
69592
|
staticAuthOptionsSchema,
|
|
69001
69593
|
storageProviderSchema,
|
|
69002
69594
|
supportsStreaming,
|