@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.
Files changed (86) hide show
  1. package/agent/agent.instance.d.ts +9 -3
  2. package/agent/agent.instance.d.ts.map +1 -1
  3. package/agent/flows/call-agent.flow.d.ts +19 -1
  4. package/agent/flows/call-agent.flow.d.ts.map +1 -1
  5. package/agent/hooks/agent.hooks.d.ts +4 -4
  6. package/agent/hooks/agent.hooks.d.ts.map +1 -1
  7. package/auth/flows/oauth.auth-ui.flow.d.ts +1 -1
  8. package/auth/flows/oauth.authorize.flow.d.ts +3 -3
  9. package/auth/flows/oauth.authorize.flow.d.ts.map +1 -1
  10. package/auth/flows/oauth.callback.flow.d.ts +2 -2
  11. package/auth/flows/oauth.connect.flow.d.ts +1 -1
  12. package/auth/flows/oauth.provider-callback.flow.d.ts +2 -2
  13. package/auth/flows/oauth.provider-callback.flow.d.ts.map +1 -1
  14. package/auth/flows/oauth.register.flow.d.ts +1 -1
  15. package/auth/flows/oauth.token.flow.d.ts +1 -1
  16. package/auth/flows/oauth.userinfo.flow.d.ts +1 -1
  17. package/auth/flows/session.verify.flow.d.ts +1 -0
  18. package/auth/flows/session.verify.flow.d.ts.map +1 -1
  19. package/auth/flows/well-known.jwks.flow.d.ts +3 -3
  20. package/auth/flows/well-known.oauth-authorization-server.flow.d.ts +3 -2
  21. package/auth/flows/well-known.oauth-authorization-server.flow.d.ts.map +1 -1
  22. package/auth/flows/well-known.prm.flow.d.ts +4 -3
  23. package/auth/flows/well-known.prm.flow.d.ts.map +1 -1
  24. package/auth/instances/instance.local-primary-auth.d.ts +30 -19
  25. package/auth/instances/instance.local-primary-auth.d.ts.map +1 -1
  26. package/channel/channel-scope.helper.d.ts +13 -1
  27. package/channel/channel-scope.helper.d.ts.map +1 -1
  28. package/channel/sources/completion-events.d.ts +24 -0
  29. package/channel/sources/completion-events.d.ts.map +1 -0
  30. package/channel/sources/job-completion.source.d.ts +1 -1
  31. package/channel/sources/job-completion.source.d.ts.map +1 -1
  32. package/common/entries/agent.entry.d.ts +6 -0
  33. package/common/entries/agent.entry.d.ts.map +1 -1
  34. package/common/entries/scope.entry.d.ts +2 -3
  35. package/common/entries/scope.entry.d.ts.map +1 -1
  36. package/common/interfaces/internal/primary-auth-provider.interface.d.ts +5 -5
  37. package/common/interfaces/internal/primary-auth-provider.interface.d.ts.map +1 -1
  38. package/common/interfaces/tool.interface.d.ts.map +1 -1
  39. package/common/metadata/tool.metadata.d.ts +2 -2
  40. package/common/metadata/workflow.metadata.d.ts +1 -1
  41. package/common/schemas/http-output.schema.d.ts +24 -24
  42. package/common/utils/path.utils.d.ts +2 -0
  43. package/common/utils/path.utils.d.ts.map +1 -1
  44. package/context/frontmcp-context.d.ts +4 -0
  45. package/context/frontmcp-context.d.ts.map +1 -1
  46. package/elicitation/helpers/elicit.helper.d.ts.map +1 -1
  47. package/esm/index.mjs +905 -315
  48. package/front-mcp/front-mcp.d.ts.map +1 -1
  49. package/index.d.ts +6 -6
  50. package/index.d.ts.map +1 -1
  51. package/index.js +1031 -439
  52. package/job/job-scope.helper.d.ts.map +1 -1
  53. package/package.json +10 -10
  54. package/scope/flows/http.request.flow.d.ts.map +1 -1
  55. package/scope/scope.instance.d.ts +3 -3
  56. package/scope/scope.instance.d.ts.map +1 -1
  57. package/skill/auth/skill-http-auth.d.ts.map +1 -1
  58. package/skill/flows/http/llm-full-txt.flow.d.ts +1 -1
  59. package/skill/flows/http/llm-txt.flow.d.ts +1 -1
  60. package/skill/flows/http/skills-api.flow.d.ts +1 -1
  61. package/skill/flows/http/skills-api.flow.d.ts.map +1 -1
  62. package/skill/flows/load-skill.flow.d.ts.map +1 -1
  63. package/skill/index.d.ts +1 -1
  64. package/skill/index.d.ts.map +1 -1
  65. package/skill/sep-2640/sep-2640.resource-helpers.d.ts +6 -0
  66. package/skill/sep-2640/sep-2640.resource-helpers.d.ts.map +1 -1
  67. package/skill/skill-filter.helper.d.ts +25 -0
  68. package/skill/skill-filter.helper.d.ts.map +1 -1
  69. package/skill/skill-http.utils.d.ts.map +1 -1
  70. package/skill/skill.events.d.ts.map +1 -1
  71. package/skill/skill.registry.d.ts +94 -17
  72. package/skill/skill.registry.d.ts.map +1 -1
  73. package/tool/flows/call-tool.flow.d.ts.map +1 -1
  74. package/transport/flows/handle.mcp-20260728.flow.d.ts +1 -0
  75. package/transport/flows/handle.mcp-20260728.flow.d.ts.map +1 -1
  76. package/transport/flows/handle.streamable-http.flow.d.ts +1 -1
  77. package/transport/index.d.ts +1 -1
  78. package/transport/index.d.ts.map +1 -1
  79. package/transport/mcp-20260728/dispatcher.d.ts +5 -0
  80. package/transport/mcp-20260728/dispatcher.d.ts.map +1 -1
  81. package/transport/mcp-20260728/mrtr.d.ts +8 -0
  82. package/transport/mcp-20260728/mrtr.d.ts.map +1 -1
  83. package/transport/mcp-handlers/skills-load-request.handler.d.ts.map +1 -1
  84. package/transport/web-fetch-handler.d.ts +33 -0
  85. package/transport/web-fetch-handler.d.ts.map +1 -1
  86. 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 import_utils113 = require("@frontmcp/utils");
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, import_utils113.hkdfSha256)(ikm, HKDF_SALT, HKDF_INFO, KEY_LENGTH);
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, import_utils113.randomBytes)(12);
6442
+ const iv = (0, import_utils114.randomBytes)(12);
6443
6443
  const plaintextBytes = new TextEncoder().encode(plaintext);
6444
- const { ciphertext, tag } = (0, import_utils113.encryptAesGcm)(key, plaintextBytes, iv);
6445
- return [(0, import_utils113.base64urlEncode)(iv), (0, import_utils113.base64urlEncode)(tag), (0, import_utils113.base64urlEncode)(ciphertext)].join(SEPARATOR);
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, import_utils113.base64urlDecode)(parts[0]);
6453
- const tag = (0, import_utils113.base64urlDecode)(parts[1]);
6454
- const ciphertext = (0, import_utils113.base64urlDecode)(parts[2]);
6455
- const plaintext = (0, import_utils113.decryptAesGcm)(key, ciphertext, iv, tag);
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
- var SKILLS_HTTP_SURFACE;
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
- parts.push(formatSkillForLLMWithSchemas(loaded.skill, loaded.availableTools, loaded.missingTools, toolRegistry));
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 } = loadResult;
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
- listener(event);
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 handle held by an earlier
27554
- * caller cannot remove a replacement registration.
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
- const content = await instance.load();
27648
- await this.storageProvider.add(content);
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.bump("reset");
27787
+ this.withdrawGoneAdoptedSkills(previous);
27695
27788
  for (const row of latest) {
27696
- row.instance.load().then((content) => {
27697
- this.storageProvider.add(content);
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
- const content = await row.instance.load();
27707
- await this.storageProvider.add(content);
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 baseResults = await this.storageProvider.search(query, options);
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
- if (this.dynamicContents.size === 0) return this.storageProvider.list(options);
27949
- const base = await this.listAllFromProvider(options);
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
- * Sums the provider count with overlay rows the provider does not know
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 baseCount = await this.storageProvider.count(options);
28014
- if (this.dynamicContents.size === 0) return baseCount;
28015
- const baseList = await this.storageProvider.list({
28016
- tags: options?.tags,
28017
- includeHidden: options?.includeHidden,
28018
- limit: Number.MAX_SAFE_INTEGER
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.skills) baseIds.add(m.id ?? m.name);
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 baseCount + overlay.length;
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
- const mutable = this.asMutableProvider(this.storageProvider);
28359
- if (mutable) {
28360
- try {
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
- } catch (error) {
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
- const idx = this.dynamicRows.findIndex((r) => r.instance.name === id);
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
- if (mutable) {
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
- providers;
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.providers = providers;
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.providers.getActiveScope();
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.providers.get(token);
29702
+ return this.providerRegistry.get(token);
29478
29703
  },
29479
29704
  tryGet: (token) => {
29480
29705
  try {
29481
- return this.providers.get(token);
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.providers.get(ConfigService);
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.providers, this.owner);
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
- * This handler is called when the tool is invoked through the standard
29645
- * tools:call-tool flow. It creates an AgentContext and runs the LLM loop.
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 agentCallExtra = { authInfo };
29651
- const agentContext = this.create(input, agentCallExtra);
29652
- const result = await agentContext.execute(input);
29653
- return result;
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.providers);
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.providers.getActiveScope();
29982
+ const scope = this.providerRegistry.getActiveScope();
29736
29983
  return {
29737
29984
  metadata: this.metadata,
29738
29985
  input,
29739
- providers: this.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 toolContext = runAsTool(runningTool, () => tool.create(args, ctx));
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 context = agent.create(input.arguments, { ...ctx, progressToken });
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
- if (frontmcpContext && sdkAuthInfo?.transport?.sendElicitRequest) {
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
- const timeoutMs = agent.metadata.timeout?.executeMs ?? agent.metadata.execution?.timeout ?? this.scope.rateLimitManager?.config?.defaultTimeout?.executeMs;
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
- const parseResult = agent.safeParseOutput(rawOutput);
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: parseResult.error
31061
+ errors: outputError
30712
31062
  });
30713
31063
  throw new InvalidOutputError();
30714
31064
  }
30715
- const result = parseResult.data;
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().issuer);
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.issuerFor?.(this.rawInput.request);
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, issuer, scopes = ["anonymous"], subject, sessionIdHeader } = options;
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.issuerFor?.(this.rawInput.request);
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 = `${baseUrl}`;
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
- scopesSupported: import_lazy_zod75.z.array(import_lazy_zod75.z.string()).default(["openid", "profile", "email"]),
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()).default(["openid", "profile", "email"]),
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 auth = scope.metadata.auth;
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
- scopesSupported: auth && (0, import_auth12.isOrchestratedMode)(auth) ? (0, import_auth42.advertisedScopes)(auth.allowedScopes) : scope.getAllSupportedScopes(),
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
- scopes_supported: scopesSupported,
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
- scopes_supported: scopesSupported,
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.issuerIsConfigured = this.configuredIssuer(options) !== void 0 || !!(0, import_utils80.getEnv)("FRONTMCP_PUBLIC_HOST")?.trim();
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 the operator named the issuer (`issuer` / `local.issuer`, or its host with `FRONTMCP_PUBLIC_HOST`). */
42178
- issuerIsConfigured;
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 (`iss`) this server names on the tokens it issues in answer to
42337
- * `request`, and requires on the tokens it accepts there (#269). Issuing and
42338
- * verifying both ask this, so they can't disagree:
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
- * - a configured issuer (`issuer` / `local.issuer`) is the issuer;
42341
- * - with `FRONTMCP_PUBLIC_URL` pinned, the issuer is that URL plus this
42342
- * scope's path, the address discovery advertises;
42343
- * - with `FRONTMCP_PUBLIC_HOST` or `expectedAudience` set, the boot-time
42344
- * issuer, so a token serves at every address the server answers to;
42345
- * - a request that came through a Web fetch handler (a Worker, Deno, Bun, a
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
- if (!request || this.issuerIsConfigured) return this.issuer;
42352
- if (isPublicUrlPinned()) return computeIssuer(request, this.scope.entryPath, this.scope.routeBase);
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
- if ((0, import_auth12.isOrchestratedMode)(options) && options.expectedAudience !== void 0) return this.issuer;
42355
- const webRequest = request[ServerRequestTokens.webRequest];
42356
- return webRequest === void 0 ? this.issuer : computeIssuer(request, this.scope.entryPath, this.scope.routeBase);
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`, which callers
42608
- * take from {@link issuerFor} the request, as the token endpoint does when
42609
- * it signs; the boot-time issuer when not given. Never the token's own
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 ?? this.issuer,
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
- agentEmitterSubscribe,
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
- if (agentEmitterSubscribe) {
44592
- const unsub = wireAgentCompletionSource(
44593
- instance,
44594
- instance.metadata.source,
44595
- agentEmitterSubscribe,
44596
- logger
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
- if (jobEmitterSubscribe) {
44606
- const unsub = wireJobCompletionSource(
44607
- instance,
44608
- instance.metadata.source,
44609
- jobEmitterSubscribe,
44610
- logger
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 import_utils86, JobExecutionManager;
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
- import_utils86 = require("@frontmcp/utils");
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, import_utils86.randomUUID)();
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, import_utils86.runRequestExclusive)(() => this.executeJobBackground(job, input, runId, opts)).catch(async (err) => {
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, import_utils86.randomUUID)();
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, import_utils86.runRequestExclusive)(() => this.executeWorkflowBackground(workflow, jobRegistry, runId, opts)).catch(
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 executionManager = new JobExecutionManager(stateStore, logger, notifyFn);
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 import_utils87, SkillSessionManager;
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
- import_utils87 = require("@frontmcp/utils");
50562
+ import_utils88 = require("@frontmcp/utils");
50056
50563
  init_errors();
50057
50564
  init_skill_session_types();
50058
- SkillSessionManager = class extends import_utils87.EventEmitter {
50059
- storage = new import_utils87.AsyncLocalStorage();
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", { request });
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 import_utils88, SkillHttpAuthValidator;
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
- import_utils88 = require("@frontmcp/utils");
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, import_utils88.timingSafeEqual)(keyBytes, candidateBytes)) {
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 } = loadResult;
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: the one this
54640
- * instance issues tokens under for the request
54641
- * (`LocalPrimaryAuth.issuerFor`). Default: its
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
- return visible.find((s) => s.name === lastSegment && s.getSkillPath() === lastSegment);
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, import_utils90.isAbsolute)(configured) ? configured : (0, import_utils90.pathResolve)(baseDir, configured) : (0, import_utils90.joinPath)(baseDir, subdir);
54789
- candidatePath = (0, import_utils90.pathResolve)(dir, ...segments.slice(1));
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, import_utils90.pathResolve)(baseDir, ...segments);
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, import_utils90.readFileBuffer)(candidatePath);
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, import_utils90.readFileBuffer)(resolvedPath);
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, import_utils90.isAbsolute)(absolutePath)) return absolutePath;
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, import_utils90.isAbsolute)(configured)) {
55389
+ if (configured && (0, import_utils91.isAbsolute)(configured)) {
54866
55390
  dir = configured;
54867
55391
  } else if (baseDir) {
54868
- dir = configured ? (0, import_utils90.pathResolve)(baseDir, configured) : (0, import_utils90.joinPath)(baseDir, kind);
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, import_utils90.pathResolve)(dir, filename);
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 import_utils90;
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
- import_utils90 = require("@frontmcp/utils");
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, import_utils91.stat)(record.filePath);
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 import_utils91;
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
- import_utils91 = require("@frontmcp/utils");
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, import_utils92.randomBytes)(32);
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, import_utils92, injectedFactory;
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
- import_utils92 = require("@frontmcp/utils");
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 import_utils93;
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
- import_utils93 = require("@frontmcp/utils");
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, import_utils94, plan37, subscriptionParamsSchema, requestIdSchema, stateSchema37, name38, Stage38, encoder3, TRACE_META_KEYS, HandleMcp20260728Flow;
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
- import_utils94 = require("@frontmcp/utils");
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, import_utils94.runRequestExclusive)(() => storage.runWithContext(context, fn));
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. This one names the request only; the request context does not vouch for it.
56264
- sessionId: auth.session?.id
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, import_utils95, plan38, stateSessionSchema, stateSchema38, name39, Stage39, HandleSseFlow;
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
- import_utils95 = require("@frontmcp/utils");
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, import_utils95.getRuntimeContext)().deployment === "distributed") {
56470
- const nodeId = (0, import_utils95.getMachineId)();
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, import_utils95.buildSetCookie)({ name: DEFAULT_FRONTMCP_NODE_COOKIE, value: nodeId }, request);
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, import_utils95.getRuntimeContext)().deployment === "distributed") {
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, import_utils95.getMachineId)()) {
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 import_utils96, ExtAppsMessageHandler, ExtAppsError, ExtAppsMethodNotFoundError, ExtAppsInvalidParamsError, ExtAppsNotSupportedError, ExtAppsToolNotFoundError;
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
- import_utils96 = require("@frontmcp/utils");
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, import_utils96.isDevelopment)() && error instanceof Error ? { stack: error.stack } : void 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, import_utils97, plan40, stateSessionSchema2, stateSchema40, name41, Stage41, HandleStreamableHttpFlow;
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
- import_utils97 = require("@frontmcp/utils");
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, import_utils97.getRuntimeContext)().deployment === "distributed") {
57275
- const nodeId = (0, import_utils97.getMachineId)();
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, import_utils97.buildSetCookie)({ name: DEFAULT_FRONTMCP_NODE_COOKIE, value: nodeId }, request);
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 import_utils98, JSON_RPC2, rpcError, rpcRequest;
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
- import_utils98 = require("@frontmcp/utils");
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, import_utils98.randomUUID)()
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, import_utils98.randomUUID)(),
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, import_utils99, MAXIMUM_MESSAGE_SIZE, SSEServerTransport;
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
- import_utils99 = require("@frontmcp/utils");
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, import_utils99.randomUUID)();
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 import_utils100, TransportService;
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
- import_utils100 = require("@frontmcp/utils");
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, import_utils100.getMachineId)();
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, import_utils100.getMachineId)()
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, import_utils100.sha256Hex)(value);
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, import_utils101.sha256Hex)(JSON.stringify([typeof iss === "string" ? iss : "", sub]))}`;
60387
+ return `sub:${(0, import_utils102.sha256Hex)(JSON.stringify([typeof iss === "string" ? iss : "", sub]))}`;
59859
60388
  }
59860
- if (authorization?.token) return `token:${(0, import_utils101.sha256Hex)(authorization.token)}`;
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, import_utils101, owners;
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
- import_utils101 = require("@frontmcp/utils");
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, import_utils102.randomUUID)()}`;
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 import_utils102;
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
- import_utils102 = require("@frontmcp/utils");
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, import_utils103, plan42, httpRequestStateSchema, name43, Stage43, HttpRequestFlow;
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
- import_utils103 = require("@frontmcp/utils");
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, import_utils103.randomUUID)()}`;
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", { request });
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, import_utils104, SCOPE_NON_DISPATCHABLE_FLOWS, Scope;
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
- import_utils104 = require("@frontmcp/utils");
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, import_utils104.getEnvFlag)("FRONTMCP_PERF");
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, import_utils104.getRuntimeContext)().deployment === "distributed";
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, import_utils104.getMachineId)(),
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, import_utils104.getMachineId)(), {
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, import_utils104.getRuntimeContext)().deployment === "serverless") {
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, import_utils104.isEdgeRuntime)()
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, import_utils104.isEdgeRuntime)();
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 all supported OAuth scopes from base OIDC scopes and
61794
- * tool-level authProvider scope declarations.
61795
- * Used by PRM endpoint to populate `scopes_supported` (RFC 9728).
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(["openid", "profile", "email"]);
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, import_utils105.runRequestExclusive)(
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, import_utils105.runRequestExclusive)(
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 import_utils105, CLOUDFLARE_WORKERS_USER_AGENT, MISCONFIGURATION_REMEDIES;
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
- import_utils105 = require("@frontmcp/utils");
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, import_utils106, DEFAULT_EXPRESS_BODY_LIMIT, ExpressHostAdapter;
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
- import_utils106 = require("@frontmcp/utils");
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, import_utils106.fileExists)(socketPath)) {
63974
- await (0, import_utils106.unlink)(socketPath);
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 import_utils107, DEFAULT_CORS, FrontMcpServerInstance;
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
- import_utils107 = require("@frontmcp/utils");
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, import_utils107.getRuntimeContext)().deployment;
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 import_utils108, stdioServing, FrontMcpInstance;
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
- import_utils108 = require("@frontmcp/utils");
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
- const frontMcp = new _FrontMcpInstance2(frontMcpMetadataSchema.parse(options));
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) frontMcp.log?.error("FrontMCP failed to start; requests are refused until it does", err);
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, import_utils108.isEdgeRuntime)()) return build(false);
65140
+ if (!(0, import_utils109.isEdgeRuntime)()) return build(false);
64545
65141
  assertStaticStartupConfig(options);
64546
- let inner;
64547
- let building;
65142
+ const server = createDeferredServerBuild(() => build(true));
64548
65143
  return async (request, ctx, env) => {
64549
- if (!inner) {
64550
- if (!building) {
64551
- building = build(true).catch((err) => {
64552
- building = void 0;
64553
- throw err;
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, import_utils108.fileExists)(socketPath)) {
64700
- await (0, import_utils108.unlink)(socketPath);
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, import_utils108.randomUUID)()}`;
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, import_utils109.getEnvFlag)("FRONTMCP_SERVERLESS");
65165
- const isSchemaExtract = (0, import_utils109.getEnvFlag)("FRONTMCP_SCHEMA_EXTRACT");
65166
- const isStdio = (0, import_utils109.getEnvFlag)("FRONTMCP_STDIO");
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, import_utils109.getEnvFlag)("FRONTMCP_WORKER");
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, import_utils109, FRONT_MCP_DECORATOR_BASENAMES, FRONTMCP_CONFIG_METADATA_KEY, _FrontMcpInstance, _serverlessHandlerFns;
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
- import_utils109 = require("@frontmcp/utils");
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, import_utils111, ToolEntry;
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
- import_utils111 = require("@frontmcp/utils");
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, import_utils111.isDebug)() || (0, import_utils111.isDevelopment)()) {
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, import_utils111.isDebug)() || (0, import_utils111.isDevelopment)()) {
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 import_utils112 = require("@frontmcp/utils");
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, import_utils112.joinPath)(dirPath, "scripts");
67955
- const referencesPath = (0, import_utils112.joinPath)(dirPath, "references");
67956
- const assetsPath = (0, import_utils112.joinPath)(dirPath, "assets");
67957
- const examplesPath = (0, import_utils112.joinPath)(dirPath, "examples");
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, import_utils112.fileExists)((0, import_utils112.joinPath)(dirPath, "SKILL.md"))
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, import_utils112.stat)(path3);
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, import_utils112.readdir)(current);
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, import_utils112.joinPath)(current, entry);
68584
+ const full = (0, import_utils113.joinPath)(current, entry);
67996
68585
  let entryStat;
67997
68586
  try {
67998
- entryStat = await (0, import_utils112.stat)(full);
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, import_utils112.joinPath)(dirPath, "SKILL.md");
68015
- const exists = await (0, import_utils112.fileExists)(skillMdPath);
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, import_utils112.basename)(dirPath), "SKILL.md not found in directory");
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, import_utils112.readFile)(skillMdPath, "utf-8");
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, import_utils112.basename)(dirPath);
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, import_utils112.basename)(dirPath);
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, import_utils112.basename)(dirPath);
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,