@frontmcp/sdk 1.8.4 → 1.8.6

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 (102) 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/dynamic/dynamic.plugin.d.ts +11 -2
  33. package/common/dynamic/dynamic.plugin.d.ts.map +1 -1
  34. package/common/entries/agent.entry.d.ts +6 -0
  35. package/common/entries/agent.entry.d.ts.map +1 -1
  36. package/common/entries/scope.entry.d.ts +2 -3
  37. package/common/entries/scope.entry.d.ts.map +1 -1
  38. package/common/interfaces/internal/primary-auth-provider.interface.d.ts +5 -5
  39. package/common/interfaces/internal/primary-auth-provider.interface.d.ts.map +1 -1
  40. package/common/interfaces/tool.interface.d.ts.map +1 -1
  41. package/common/metadata/tool.metadata.d.ts +2 -2
  42. package/common/metadata/workflow.metadata.d.ts +1 -1
  43. package/common/schemas/http-output.schema.d.ts +24 -24
  44. package/common/utils/list-all-pages.utils.d.ts +17 -0
  45. package/common/utils/list-all-pages.utils.d.ts.map +1 -0
  46. package/common/utils/path.utils.d.ts +2 -0
  47. package/common/utils/path.utils.d.ts.map +1 -1
  48. package/context/frontmcp-context.d.ts +4 -0
  49. package/context/frontmcp-context.d.ts.map +1 -1
  50. package/elicitation/helpers/elicit.helper.d.ts.map +1 -1
  51. package/esm/index.mjs +1176 -444
  52. package/front-mcp/front-mcp.d.ts.map +1 -1
  53. package/index.d.ts +6 -6
  54. package/index.d.ts.map +1 -1
  55. package/index.js +1276 -550
  56. package/job/job-scope.helper.d.ts.map +1 -1
  57. package/package.json +10 -10
  58. package/plugin/plugin.registry.d.ts.map +1 -1
  59. package/remote-mcp/mcp-client.service.d.ts.map +1 -1
  60. package/remote-mcp/mcp-stateless-client.adapter.d.ts +16 -5
  61. package/remote-mcp/mcp-stateless-client.adapter.d.ts.map +1 -1
  62. package/scope/flows/http.request.flow.d.ts.map +1 -1
  63. package/scope/scope.instance.d.ts +3 -3
  64. package/scope/scope.instance.d.ts.map +1 -1
  65. package/skill/auth/skill-http-auth.d.ts.map +1 -1
  66. package/skill/flows/http/llm-full-txt.flow.d.ts +1 -1
  67. package/skill/flows/http/llm-txt.flow.d.ts +1 -1
  68. package/skill/flows/http/skills-api.flow.d.ts +1 -1
  69. package/skill/flows/http/skills-api.flow.d.ts.map +1 -1
  70. package/skill/flows/load-skill.flow.d.ts.map +1 -1
  71. package/skill/index.d.ts +1 -1
  72. package/skill/index.d.ts.map +1 -1
  73. package/skill/sep-2640/sep-2640.resource-helpers.d.ts +6 -0
  74. package/skill/sep-2640/sep-2640.resource-helpers.d.ts.map +1 -1
  75. package/skill/skill-filter.helper.d.ts +25 -0
  76. package/skill/skill-filter.helper.d.ts.map +1 -1
  77. package/skill/skill-http.utils.d.ts.map +1 -1
  78. package/skill/skill.events.d.ts.map +1 -1
  79. package/skill/skill.registry.d.ts +94 -17
  80. package/skill/skill.registry.d.ts.map +1 -1
  81. package/tool/flows/call-tool.flow.d.ts +1 -0
  82. package/tool/flows/call-tool.flow.d.ts.map +1 -1
  83. package/transport/flows/handle.mcp-20260728.flow.d.ts +1 -0
  84. package/transport/flows/handle.mcp-20260728.flow.d.ts.map +1 -1
  85. package/transport/flows/handle.sse.flow.d.ts +25 -0
  86. package/transport/flows/handle.sse.flow.d.ts.map +1 -1
  87. package/transport/flows/handle.streamable-http.flow.d.ts +11 -1
  88. package/transport/flows/handle.streamable-http.flow.d.ts.map +1 -1
  89. package/transport/index.d.ts +1 -1
  90. package/transport/index.d.ts.map +1 -1
  91. package/transport/mcp-20260728/client/mcp-stateless.client.d.ts +7 -2
  92. package/transport/mcp-20260728/client/mcp-stateless.client.d.ts.map +1 -1
  93. package/transport/mcp-20260728/dispatcher.d.ts +5 -0
  94. package/transport/mcp-20260728/dispatcher.d.ts.map +1 -1
  95. package/transport/mcp-20260728/mrtr.d.ts +8 -0
  96. package/transport/mcp-20260728/mrtr.d.ts.map +1 -1
  97. package/transport/mcp-20260728/request-state.d.ts +28 -0
  98. package/transport/mcp-20260728/request-state.d.ts.map +1 -1
  99. package/transport/mcp-handlers/skills-load-request.handler.d.ts.map +1 -1
  100. package/transport/web-fetch-handler.d.ts +33 -0
  101. package/transport/web-fetch-handler.d.ts.map +1 -1
  102. package/workflow/engine/workflow.engine.d.ts.map +1 -1
package/index.js CHANGED
@@ -5972,6 +5972,33 @@ var init_errors = __esm({
5972
5972
  }
5973
5973
  });
5974
5974
 
5975
+ // libs/sdk/src/common/utils/list-all-pages.utils.ts
5976
+ async function listAllPages(method, fetchPage, fail = (message) => new InternalMcpError(message, "LIST_PAGINATION_STALLED")) {
5977
+ const items = [];
5978
+ const seenCursors = /* @__PURE__ */ new Set();
5979
+ let cursor;
5980
+ for (let page = 0; page < MAX_LIST_PAGES; page++) {
5981
+ const result = await fetchPage(cursor);
5982
+ items.push(...result.items ?? []);
5983
+ const next = result.nextCursor;
5984
+ if (!next) return items;
5985
+ if (seenCursors.has(next)) {
5986
+ throw fail(`${method} returned the cursor "${next}" twice`);
5987
+ }
5988
+ seenCursors.add(next);
5989
+ cursor = next;
5990
+ }
5991
+ throw fail(`${method} did not finish within ${MAX_LIST_PAGES} pages`);
5992
+ }
5993
+ var MAX_LIST_PAGES;
5994
+ var init_list_all_pages_utils = __esm({
5995
+ "libs/sdk/src/common/utils/list-all-pages.utils.ts"() {
5996
+ "use strict";
5997
+ init_mcp_error();
5998
+ MAX_LIST_PAGES = 1e3;
5999
+ }
6000
+ });
6001
+
5975
6002
  // libs/sdk/src/transport/mcp-handlers/skills-mcp.types.ts
5976
6003
  var import_lazy_zod40, SkillsSearchRequestSchema, SkillsSearchResultSchema, SkillsLoadRequestSchema, SkillsLoadResultSchema, SkillsListRequestSchema, SkillsListResultSchema;
5977
6004
  var init_skills_mcp_types = __esm({
@@ -6429,30 +6456,30 @@ var require_dist = __commonJS({
6429
6456
  sqliteStorageOptionsSchema: () => sqliteStorageOptionsSchema
6430
6457
  });
6431
6458
  module2.exports = __toCommonJS2(index_exports2);
6432
- var import_utils113 = require("@frontmcp/utils");
6459
+ var import_utils114 = require("@frontmcp/utils");
6433
6460
  var HKDF_SALT = new TextEncoder().encode("frontmcp-sqlite-storage-v1");
6434
6461
  var HKDF_INFO = new TextEncoder().encode("aes-256-gcm-value-encryption");
6435
6462
  var KEY_LENGTH = 32;
6436
6463
  function deriveEncryptionKey(secret) {
6437
6464
  const ikm = new TextEncoder().encode(secret);
6438
- return (0, import_utils113.hkdfSha256)(ikm, HKDF_SALT, HKDF_INFO, KEY_LENGTH);
6465
+ return (0, import_utils114.hkdfSha256)(ikm, HKDF_SALT, HKDF_INFO, KEY_LENGTH);
6439
6466
  }
6440
6467
  var SEPARATOR = ":";
6441
6468
  function encryptValue(key, plaintext) {
6442
- const iv = (0, import_utils113.randomBytes)(12);
6469
+ const iv = (0, import_utils114.randomBytes)(12);
6443
6470
  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);
6471
+ const { ciphertext, tag } = (0, import_utils114.encryptAesGcm)(key, plaintextBytes, iv);
6472
+ return [(0, import_utils114.base64urlEncode)(iv), (0, import_utils114.base64urlEncode)(tag), (0, import_utils114.base64urlEncode)(ciphertext)].join(SEPARATOR);
6446
6473
  }
6447
6474
  function decryptValue(key, encrypted) {
6448
6475
  const parts = encrypted.split(SEPARATOR);
6449
6476
  if (parts.length !== 3) {
6450
6477
  throw new Error("Invalid encrypted value format");
6451
6478
  }
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);
6479
+ const iv = (0, import_utils114.base64urlDecode)(parts[0]);
6480
+ const tag = (0, import_utils114.base64urlDecode)(parts[1]);
6481
+ const ciphertext = (0, import_utils114.base64urlDecode)(parts[2]);
6482
+ const plaintext = (0, import_utils114.decryptAesGcm)(key, ciphertext, iv, tag);
6456
6483
  return new TextDecoder().decode(plaintext);
6457
6484
  }
6458
6485
  var import_utils210 = require("@frontmcp/utils");
@@ -10203,7 +10230,33 @@ async function filterDiscoverableSkillResults(scope, registry, results, caller =
10203
10230
  return entry === void 0 || servable.has(entry);
10204
10231
  });
10205
10232
  }
10206
- var SKILLS_HTTP_SURFACE;
10233
+ function skillToolsForCaller(loaded, tools, ctx, surface) {
10234
+ const caller = callerSurface(ctx, surface);
10235
+ if (!tools || caller === void 0 || loaded.availableTools.length === 0) return loaded;
10236
+ const availability = new Map(tools.getTools(true).map((tool) => [tool.name, tool.metadata?.availableWhen]));
10237
+ const notOffered = loaded.availableTools.filter((name44) => !isOfferedOnSurface(availability.get(name44), caller));
10238
+ if (notOffered.length === 0) return loaded;
10239
+ return {
10240
+ ...loaded,
10241
+ availableTools: loaded.availableTools.filter((name44) => !notOffered.includes(name44)),
10242
+ missingTools: [...loaded.missingTools, ...notOffered],
10243
+ isComplete: false,
10244
+ warning: warningWithMissingTools(loaded.warning, loaded.skill?.name, notOffered)
10245
+ };
10246
+ }
10247
+ function warningWithMissingTools(warning, skillName, names) {
10248
+ const prefix = warning?.match(WARNING_PREFIX);
10249
+ if (warning && prefix && warning.endsWith(WARNING_SUFFIX)) {
10250
+ const parts = warning.slice(prefix[0].length, -WARNING_SUFFIX.length).split("; ");
10251
+ const missing = parts.findIndex((part) => part.startsWith(MISSING_PART));
10252
+ if (missing === -1) parts.unshift(`${MISSING_PART}${names.join(", ")}`);
10253
+ else parts[missing] = `${parts[missing]}, ${names.join(", ")}`;
10254
+ return `${prefix[0]}${parts.join("; ")}${WARNING_SUFFIX}`;
10255
+ }
10256
+ const sentence = `Skill "${skillName ?? "skill"}" references ${MISSING_PART}${names.join(", ")}${WARNING_SUFFIX}`;
10257
+ return warning ? `${warning} ${sentence}` : sentence;
10258
+ }
10259
+ var SKILLS_HTTP_SURFACE, WARNING_PREFIX, WARNING_SUFFIX, MISSING_PART;
10207
10260
  var init_skill_filter_helper = __esm({
10208
10261
  "libs/sdk/src/skill/skill-filter.helper.ts"() {
10209
10262
  "use strict";
@@ -10212,6 +10265,9 @@ var init_skill_filter_helper = __esm({
10212
10265
  init_skill_authorities_helper();
10213
10266
  init_skill_entry_resolver();
10214
10267
  SKILLS_HTTP_SURFACE = "mcp";
10268
+ WARNING_PREFIX = /^Skill "(.*)" references /;
10269
+ WARNING_SUFFIX = ". Some functionality may be limited.";
10270
+ MISSING_PART = "missing tools: ";
10215
10271
  }
10216
10272
  });
10217
10273
 
@@ -10312,7 +10368,13 @@ async function formatSkillsForLlmFull(registry, toolRegistry, visibility = "both
10312
10368
  }
10313
10369
  const loaded = await registry.loadSkill(skill.name);
10314
10370
  if (loaded) {
10315
- parts.push(formatSkillForLLMWithSchemas(loaded.skill, loaded.availableTools, loaded.missingTools, toolRegistry));
10371
+ const { availableTools, missingTools } = skillToolsForCaller(
10372
+ loaded,
10373
+ toolRegistry,
10374
+ void 0,
10375
+ SKILLS_HTTP_SURFACE
10376
+ );
10377
+ parts.push(formatSkillForLLMWithSchemas(loaded.skill, availableTools, missingTools, toolRegistry));
10316
10378
  }
10317
10379
  }
10318
10380
  return parts.join("\n\n---\n\n");
@@ -10432,6 +10494,7 @@ function skillToApiResponse(skill, loadResult) {
10432
10494
  var init_skill_http_utils = __esm({
10433
10495
  "libs/sdk/src/skill/skill-http.utils.ts"() {
10434
10496
  "use strict";
10497
+ init_skill_filter_helper();
10435
10498
  }
10436
10499
  });
10437
10500
 
@@ -10995,7 +11058,11 @@ function skillsLoadRequestHandler({
10995
11058
  throw toSdkMcpError(err);
10996
11059
  }
10997
11060
  }
10998
- const { skill, availableTools, missingTools, isComplete, warning } = loadResult;
11061
+ const { skill, availableTools, missingTools, isComplete, warning } = skillToolsForCaller(
11062
+ loadResult,
11063
+ toolRegistry,
11064
+ withMcpSurface(scope, ctx)
11065
+ );
10999
11066
  if (warning) {
11000
11067
  warnings.push(warning);
11001
11068
  }
@@ -12003,6 +12070,7 @@ var init_direct_client = __esm({
12003
12070
  "use strict";
12004
12071
  import_protocol30 = require("@frontmcp/protocol");
12005
12072
  import_utils29 = require("@frontmcp/utils");
12073
+ init_list_all_pages_utils();
12006
12074
  init_errors();
12007
12075
  init_skills_mcp_types();
12008
12076
  init_llm_platform();
@@ -12149,8 +12217,11 @@ var init_direct_client = __esm({
12149
12217
  // Tool Operations (platform-formatted)
12150
12218
  // ─────────────────────────────────────────────────────────────────────────────
12151
12219
  async listTools() {
12152
- const result = await this.mcpClient.listTools();
12153
- return formatToolsForPlatform(result.tools, this.platform);
12220
+ const tools = await listAllPages("tools/list", async (cursor) => {
12221
+ const result = await this.mcpClient.listTools(cursor ? { cursor } : void 0);
12222
+ return { items: result.tools, nextCursor: result.nextCursor };
12223
+ });
12224
+ return formatToolsForPlatform(tools, this.platform);
12154
12225
  }
12155
12226
  async callTool(name44, args) {
12156
12227
  const result = await this.mcpClient.callTool({
@@ -14550,6 +14621,9 @@ function readPublicUrlPin() {
14550
14621
  function isPublicUrlPinned() {
14551
14622
  return readPublicUrlPin() !== void 0;
14552
14623
  }
14624
+ function getPinnedPublicUrl() {
14625
+ return readPublicUrlPin();
14626
+ }
14553
14627
  function isProxyTrusted() {
14554
14628
  try {
14555
14629
  if (typeof process !== "undefined" && process.env) {
@@ -15999,6 +16073,11 @@ async function performElicit(deps, message, requestedSchema, options) {
15999
16073
  }
16000
16074
  const ctx = tryGetContext();
16001
16075
  const mrtr = ctx?.getMrtrExchange?.();
16076
+ if (mrtr && mrtr.clientDeclaredRevision === false && mrtr.supportsElicitation?.(options?.mode ?? "form") !== true) {
16077
+ throw new ElicitationNotSupportedError(
16078
+ "This client does not support elicitation: it declared no elicitation capability, and without a session the elicitation fallback has no one to answer it"
16079
+ );
16080
+ }
16002
16081
  if (mrtr) {
16003
16082
  const zodSchema = requestedSchema instanceof import_lazy_zod45.z.ZodType ? requestedSchema : import_lazy_zod45.z.object(requestedSchema);
16004
16083
  const answer = mrtr.resolveElicitation({
@@ -21944,6 +22023,9 @@ var init_call_tool_flow = __esm({
21944
22023
  uiResult: import_lazy_zod57.z.any().optional(),
21945
22024
  // UI metadata from rendering (merged into _meta)
21946
22025
  uiMeta: import_lazy_zod57.z.record(import_lazy_zod57.z.string(), import_lazy_zod57.z.unknown()).optional(),
22026
+ // Metadata hooks add to the result (e.g. the cache plugin's `cache: 'hit'`), merged into the
22027
+ // result's _meta; never merged into rawOutput, which is the tool's data
22028
+ resultMeta: import_lazy_zod57.z.record(import_lazy_zod57.z.string(), import_lazy_zod57.z.unknown()).optional(),
21947
22029
  // Progress token from request's _meta (for progress notifications)
21948
22030
  progressToken: import_lazy_zod57.z.union([import_lazy_zod57.z.string(), import_lazy_zod57.z.number()]).optional(),
21949
22031
  // JSON-RPC request ID (for elicitation routing)
@@ -22364,6 +22446,7 @@ var init_call_tool_flow = __esm({
22364
22446
  context.mark("createToolCallContext");
22365
22447
  context._toolNameInternal = tool.metadata.id ?? tool.metadata.name;
22366
22448
  context._toolInputInternal = input.arguments;
22449
+ context._jsonRpcRequestIdInternal = this.state.jsonRpcRequestId;
22367
22450
  const frontmcpContext = context.tryGetContext();
22368
22451
  const authInfoWithTransport = authInfo;
22369
22452
  if (frontmcpContext && authInfoWithTransport?.transport?.sendElicitRequest) {
@@ -22770,7 +22853,7 @@ ${JSON.stringify(error.schema, null, 2)}
22770
22853
  }
22771
22854
  async finalize() {
22772
22855
  this.logger.verbose("finalize:start");
22773
- const { tool, rawOutput, uiResult, uiMeta } = this.state;
22856
+ const { tool, rawOutput, uiResult, uiMeta, resultMeta } = this.state;
22774
22857
  if (!tool) {
22775
22858
  this.logger.verbose("finalize: skipping (no tool in state)");
22776
22859
  return;
@@ -22813,6 +22896,9 @@ ${JSON.stringify(error.schema, null, 2)}
22813
22896
  if (rawMeta) {
22814
22897
  result._meta = { ...result._meta, ...rawMeta };
22815
22898
  }
22899
+ if (resultMeta) {
22900
+ result._meta = { ...result._meta, ...resultMeta };
22901
+ }
22816
22902
  if (uiResult) {
22817
22903
  result.content = uiResult.content;
22818
22904
  if (uiResult.structuredContent !== void 0 && uiResult.structuredContent !== null) {
@@ -25358,6 +25444,56 @@ var init_dynamic_utils = __esm({
25358
25444
  }
25359
25445
  });
25360
25446
 
25447
+ // libs/sdk/src/common/dynamic/dynamic.plugin.ts
25448
+ function isDynamicPluginClass(value) {
25449
+ return typeof value === "function" && value.prototype instanceof DynamicPlugin;
25450
+ }
25451
+ function initOptionsOf(instance) {
25452
+ return initOptionsByInstance.has(instance) ? { options: initOptionsByInstance.get(instance) } : void 0;
25453
+ }
25454
+ var initOptionsByInstance, DynamicPlugin;
25455
+ var init_dynamic_plugin = __esm({
25456
+ "libs/sdk/src/common/dynamic/dynamic.plugin.ts"() {
25457
+ "use strict";
25458
+ init_transport_errors();
25459
+ init_dynamic_utils();
25460
+ initOptionsByInstance = /* @__PURE__ */ new WeakMap();
25461
+ DynamicPlugin = class {
25462
+ get(token) {
25463
+ throw new MethodNotImplementedError("DynamicPlugin", "get");
25464
+ }
25465
+ /**
25466
+ * Static init() method to create a plugin provider.
25467
+ * @param options - Input options (with optional fields for defaults). Omitted, the plugin is
25468
+ * built with `{}`, as `ApprovalPlugin.init()` is documented.
25469
+ */
25470
+ static init(options = {}) {
25471
+ const extraProviders = options.providers;
25472
+ const typedOptions = options;
25473
+ if ("useFactory" in options) {
25474
+ return {
25475
+ ...typedOptions,
25476
+ provide: this,
25477
+ inject: options.inject,
25478
+ useFactory: options.useFactory,
25479
+ providers: dedupePluginProviders(extraProviders ?? [])
25480
+ };
25481
+ }
25482
+ const dyn = collectDynamicProviders(this, typedOptions);
25483
+ const mergedProviders = dedupePluginProviders([...dyn ?? [], ...extraProviders ?? []]);
25484
+ const instance = new this(options);
25485
+ initOptionsByInstance.set(instance, options);
25486
+ return {
25487
+ ...typedOptions,
25488
+ provide: this,
25489
+ useValue: instance,
25490
+ providers: mergedProviders
25491
+ };
25492
+ }
25493
+ };
25494
+ }
25495
+ });
25496
+
25361
25497
  // libs/sdk/src/common/interfaces/internal/flow.utils.ts
25362
25498
  var FlowState;
25363
25499
  var init_flow_utils2 = __esm({
@@ -27238,7 +27374,11 @@ var init_skill_events = __esm({
27238
27374
  */
27239
27375
  emit(event) {
27240
27376
  for (const listener of [...this.listeners]) {
27241
- listener(event);
27377
+ try {
27378
+ listener(event);
27379
+ } catch (err) {
27380
+ console.error("SkillEmitter listener error:", err instanceof Error ? err.message : "Unknown error");
27381
+ }
27242
27382
  }
27243
27383
  }
27244
27384
  /**
@@ -27517,6 +27657,24 @@ var init_skill_instance = __esm({
27517
27657
  });
27518
27658
 
27519
27659
  // libs/sdk/src/skill/skill.registry.ts
27660
+ function findIdPathCollision(skills) {
27661
+ const byPath = /* @__PURE__ */ new Map();
27662
+ for (const skill of skills) {
27663
+ const path3 = skill.getSkillPath();
27664
+ if (!byPath.has(path3)) byPath.set(path3, skill);
27665
+ }
27666
+ for (const idOwner of skills) {
27667
+ const id = idOwner.metadata.id;
27668
+ if (!id || id === idOwner.getSkillPath()) continue;
27669
+ const pathOwner = byPath.get(id);
27670
+ if (pathOwner) return { idOwner, pathOwner };
27671
+ }
27672
+ return void 0;
27673
+ }
27674
+ function describeIdPathCollision({ idOwner, pathOwner }) {
27675
+ const id = idOwner.metadata.id;
27676
+ 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.`;
27677
+ }
27520
27678
  var import_di22, import_utils61, DEFAULT_LIST_LIMIT, SkillRegistry;
27521
27679
  var init_skill_registry = __esm({
27522
27680
  "libs/sdk/src/skill/skill.registry.ts"() {
@@ -27550,16 +27708,30 @@ var init_skill_registry = __esm({
27550
27708
  dynamicContents = /* @__PURE__ */ new Map();
27551
27709
  /**
27552
27710
  * 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.
27711
+ * registerSkillContent call, and on every removal, so a stale unregister
27712
+ * handle held by an earlier caller cannot remove a later registration. Never
27713
+ * reset: a counter restarted at 1 would match an old handle's generation.
27555
27714
  */
27556
27715
  dynamicGenerations = /* @__PURE__ */ new Map();
27716
+ /**
27717
+ * Ids of skills that left the registry (its own dynamic skills, or an adopted child's) whose copy
27718
+ * in the storage provider is not removed yet: the removal is pending, or the provider failed it.
27719
+ * Provider-backed reads skip them until the removal succeeds or the id is served again. Each maps
27720
+ * to the number of its withdrawal.
27721
+ */
27722
+ withdrawn = /* @__PURE__ */ new Map();
27723
+ /** Numbers each withdrawal, so a removal that completes late clears only its own. */
27724
+ withdrawals = 0;
27725
+ /** The last storage-provider write queued per skill id ({@link syncProvider}). */
27726
+ providerWrites = /* @__PURE__ */ new Map();
27557
27727
  /** Whether a plugin registers skills at runtime ({@link expectDynamicSkills}). */
27558
27728
  dynamicSkillsExpected = false;
27559
27729
  /** Adopted skills from child registries */
27560
27730
  adopted = /* @__PURE__ */ new Map();
27561
27731
  /** Children registries */
27562
27732
  children = /* @__PURE__ */ new Set();
27733
+ /** The registries that adopted this one: a skill registered here is served through them too. */
27734
+ adopters = /* @__PURE__ */ new Set();
27563
27735
  /** O(1) indexes */
27564
27736
  byQualifiedId = /* @__PURE__ */ new Map();
27565
27737
  /** Owner lineage per skill instance, rebuilt with the index so `lineageOf` is O(1). */
@@ -27644,8 +27816,10 @@ var init_skill_registry = __esm({
27644
27816
  const row = this.makeRow(token, instance, lineage, this);
27645
27817
  this.localRows.push(row);
27646
27818
  try {
27647
- const content = await instance.load();
27648
- await this.storageProvider.add(content);
27819
+ await this.syncProvider(instance.metadata.id ?? instance.metadata.name, async () => {
27820
+ const content = await instance.load();
27821
+ await this.storageProvider.add(content);
27822
+ });
27649
27823
  } catch (error) {
27650
27824
  this.scope.logger.warn(`Failed to load skill ${instance.name}: ${error.message}`);
27651
27825
  }
@@ -27668,6 +27842,10 @@ var init_skill_registry = __esm({
27668
27842
  await this.adoptFromChild(child, child.owner);
27669
27843
  }
27670
27844
  }
27845
+ const collision = findIdPathCollision(this.listAllInstances());
27846
+ if (collision) {
27847
+ throw new InvalidSkillError(collision.idOwner.metadata.name, describeIdPathCollision(collision));
27848
+ }
27671
27849
  this.reindex();
27672
27850
  logAvailabilityFiltering(
27673
27851
  "SkillRegistry",
@@ -27687,24 +27865,31 @@ var init_skill_registry = __esm({
27687
27865
  const adoptedRows = childRows.map((r) => this.relineage(r, prepend));
27688
27866
  this.adopted.set(child, adoptedRows);
27689
27867
  this.children.add(child);
27868
+ child.adopters.add(this);
27690
27869
  child.subscribe({ immediate: false }, () => {
27870
+ const previous = this.adopted.get(child) ?? [];
27691
27871
  const latest = child.listAllIndexed().map((r) => this.relineage(r, prepend));
27692
27872
  this.adopted.set(child, latest);
27693
27873
  this.reindex();
27694
- this.bump("reset");
27874
+ this.withdrawGoneAdoptedSkills(previous);
27695
27875
  for (const row of latest) {
27696
- row.instance.load().then((content) => {
27697
- this.storageProvider.add(content);
27876
+ const skillId = row.instance.metadata.id ?? row.instance.metadata.name;
27877
+ this.syncProvider(skillId, async () => {
27878
+ const content = await row.instance.load();
27879
+ await this.storageProvider.add(content);
27698
27880
  }).catch((error) => {
27699
27881
  this.scope.logger.warn(`Failed to reload adopted skill ${row.baseName}: ${error.message}`);
27700
27882
  });
27701
27883
  }
27884
+ this.bump("reset");
27702
27885
  });
27703
27886
  await Promise.all(
27704
27887
  adoptedRows.map(async (row) => {
27705
27888
  try {
27706
- const content = await row.instance.load();
27707
- await this.storageProvider.add(content);
27889
+ await this.syncProvider(row.instance.metadata.id ?? row.instance.metadata.name, async () => {
27890
+ const content = await row.instance.load();
27891
+ await this.storageProvider.add(content);
27892
+ });
27708
27893
  } catch (error) {
27709
27894
  this.scope.logger.warn(`Failed to load adopted skill ${row.baseName}: ${error.message}`);
27710
27895
  }
@@ -27713,6 +27898,37 @@ var init_skill_registry = __esm({
27713
27898
  this.reindex();
27714
27899
  this.bump("reset");
27715
27900
  }
27901
+ /**
27902
+ * The `skill://` namespaces this registry's skills are served in: its own, and that of every
27903
+ * registry that adopts it, directly or through another, sibling registries included.
27904
+ */
27905
+ namespacesServingThis() {
27906
+ const registries = /* @__PURE__ */ new Set();
27907
+ const climb = (registry) => {
27908
+ if (registries.has(registry)) return;
27909
+ registries.add(registry);
27910
+ for (const adopter of registry.adopters) climb(adopter);
27911
+ };
27912
+ climb(this);
27913
+ return [...registries].map((registry) => registry.liveSkills());
27914
+ }
27915
+ /**
27916
+ * This registry's skills and those of every registry it adopted, read from each registry's own rows.
27917
+ * An adopter's copy of a child's rows is refreshed only on the child's change event, which a
27918
+ * registration emits after it has committed its row and awaited the storage provider.
27919
+ */
27920
+ liveSkills() {
27921
+ const registries = /* @__PURE__ */ new Set();
27922
+ const skills = /* @__PURE__ */ new Set();
27923
+ const descend = (registry) => {
27924
+ if (registries.has(registry)) return;
27925
+ registries.add(registry);
27926
+ for (const row of [...registry.localRows, ...registry.dynamicRows]) skills.add(row.instance);
27927
+ for (const child of registry.children) descend(child);
27928
+ };
27929
+ descend(this);
27930
+ return [...skills];
27931
+ }
27716
27932
  /* -------------------- Public API -------------------- */
27717
27933
  /**
27718
27934
  * Get all skills in the registry.
@@ -27830,7 +28046,14 @@ var init_skill_registry = __esm({
27830
28046
  }
27831
28047
  }
27832
28048
  async search(query, options) {
27833
- const baseResults = await this.storageProvider.search(query, options);
28049
+ const topK = options?.topK;
28050
+ const hidden = this.hiddenFromProviderRead();
28051
+ const providerOptions = hidden.size > 0 && typeof topK === "number" && topK >= 0 ? { ...options, topK: topK + hidden.size } : options;
28052
+ let baseResults = await this.storageProvider.search(query, providerOptions);
28053
+ if (hidden.size > 0 || this.withdrawn.size > 0) {
28054
+ baseResults = baseResults.filter((r) => !hidden.has(r.metadata));
28055
+ if (typeof topK === "number" && topK >= 0) baseResults = baseResults.slice(0, topK);
28056
+ }
27834
28057
  if (this.dynamicContents.size === 0) return baseResults;
27835
28058
  const baseSeen = /* @__PURE__ */ new Set();
27836
28059
  let rewrites = 0;
@@ -27854,7 +28077,6 @@ var init_skill_registry = __esm({
27854
28077
  const overlayOnly = this.searchDynamicOverlay(query, options, baseSeen);
27855
28078
  if (overlayOnly.length === 0 && rewrites === 0) return baseResults;
27856
28079
  const merged = [...rewrittenBase, ...overlayOnly].sort((a, b) => b.score - a.score);
27857
- const topK = options?.topK;
27858
28080
  return typeof topK === "number" && topK >= 0 ? merged.slice(0, topK) : merged;
27859
28081
  }
27860
28082
  /**
@@ -27909,7 +28131,10 @@ var init_skill_registry = __esm({
27909
28131
  warning
27910
28132
  };
27911
28133
  }
28134
+ const hidden = this.hiddenFromProviderRead();
28135
+ if (hidden.has({ name: skillId })) return void 0;
27912
28136
  const result = await this.storageProvider.load(skillId);
28137
+ if (result && hidden.has(result.skill)) return void 0;
27913
28138
  return result ?? void 0;
27914
28139
  }
27915
28140
  /**
@@ -27945,8 +28170,12 @@ var init_skill_registry = __esm({
27945
28170
  * and `hasMore` stay correct.
27946
28171
  */
27947
28172
  async listSkills(options) {
27948
- if (this.dynamicContents.size === 0) return this.storageProvider.list(options);
27949
- const base = await this.listAllFromProvider(options);
28173
+ const hidden = this.hiddenFromProviderRead();
28174
+ if (this.dynamicContents.size === 0 && hidden.size === 0) {
28175
+ const page = await this.storageProvider.list(options);
28176
+ if (this.withdrawn.size === 0) return page;
28177
+ }
28178
+ const base = (await this.listAllFromProvider(options)).filter((meta) => !hidden.has(meta));
27950
28179
  const baseIds = /* @__PURE__ */ new Set();
27951
28180
  const merged = base.map((meta) => {
27952
28181
  const id = meta.id ?? meta.name;
@@ -28006,21 +28235,20 @@ var init_skill_registry = __esm({
28006
28235
  /**
28007
28236
  * Get total skill count.
28008
28237
  *
28009
- * Sums the provider count with overlay rows the provider does not know
28238
+ * Counts the provider's rows with overlay rows the provider does not know
28010
28239
  * about so callers see the same surface as `search` / `listSkills`.
28011
28240
  */
28012
28241
  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
- });
28242
+ const hidden = this.hiddenFromProviderRead();
28243
+ if (this.dynamicContents.size === 0 && hidden.size === 0) {
28244
+ const counted = await this.storageProvider.count(options);
28245
+ if (this.withdrawn.size === 0) return counted;
28246
+ }
28247
+ const baseList = (await this.listAllFromProvider({ tags: options?.tags, includeHidden: options?.includeHidden })).filter((m) => !hidden.has(m));
28020
28248
  const baseIds = /* @__PURE__ */ new Set();
28021
- for (const m of baseList.skills) baseIds.add(m.id ?? m.name);
28249
+ for (const m of baseList) baseIds.add(m.id ?? m.name);
28022
28250
  const overlay = this.collectDynamicMetadata(options).filter((m) => !baseIds.has(m.id ?? m.name));
28023
- return baseCount + overlay.length;
28251
+ return baseList.length + overlay.length;
28024
28252
  }
28025
28253
  /**
28026
28254
  * Project a SkillContent into the SkillMetadata shape callers expect from
@@ -28310,10 +28538,6 @@ var init_skill_registry = __esm({
28310
28538
  throw new PublicMcpError("registerSkillContent: SkillContent.name is required", "INVALID_PARAMS");
28311
28539
  }
28312
28540
  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
28541
  const metadata = {
28318
28542
  id,
28319
28543
  name: content.name,
@@ -28349,28 +28573,49 @@ var init_skill_registry = __esm({
28349
28573
  `[SkillRegistry] Failed to pre-load dynamic skill ${id} from source ${opts?.source ?? "unknown"}: ${error.message}`
28350
28574
  );
28351
28575
  }
28576
+ const existingIdx = this.dynamicRows.findIndex((r) => r.instance.name === id);
28577
+ const superseded = new Set(opts?.supersedes);
28578
+ const leaving = new Set(
28579
+ this.dynamicRows.filter((r) => r.instance.name === id || superseded.has(r.instance.name)).map((r) => r.instance)
28580
+ );
28581
+ for (const skills of this.namespacesServingThis()) {
28582
+ const collision = findIdPathCollision([...skills.filter((other) => !leaving.has(other)), instance]);
28583
+ if (collision) {
28584
+ throw new PublicMcpError(`registerSkillContent: ${describeIdPathCollision(collision)}`, "INVALID_PARAMS");
28585
+ }
28586
+ }
28587
+ if (existingIdx !== -1) {
28588
+ this.dynamicRows.splice(existingIdx, 1);
28589
+ }
28590
+ const mutable = this.asMutableProvider(this.storageProvider);
28591
+ const removed = this.dynamicRows.filter((r) => superseded.has(r.instance.name) && findIdPathCollision([r.instance, instance])).map((r) => r.instance.name);
28592
+ for (const removedId of removed) {
28593
+ this.withdrawDynamicSkill(removedId, mutable);
28594
+ }
28352
28595
  const lineage = this.owner ? [this.owner] : [];
28353
28596
  const row = this.makeRow(provideToken, instance, lineage, this);
28354
28597
  this.dynamicRows.push(row);
28355
28598
  this.dynamicContents.set(id, structuredClone(content));
28599
+ this.withdrawn.delete(id);
28356
28600
  const generation = (this.dynamicGenerations.get(id) ?? 0) + 1;
28357
28601
  this.dynamicGenerations.set(id, generation);
28358
- const mutable = this.asMutableProvider(this.storageProvider);
28359
- if (mutable) {
28360
- try {
28602
+ this.reindex();
28603
+ const providerWrites = mutable ? [
28604
+ ...removed.map((removedId) => this.removeFromProvider(mutable, removedId)),
28605
+ this.syncProvider(id, async () => {
28361
28606
  if (await mutable.exists(id)) {
28362
28607
  await mutable.update(id, content);
28363
28608
  } else {
28364
28609
  await mutable.add(content);
28365
28610
  }
28366
- } catch (error) {
28611
+ }).catch((error) => {
28367
28612
  this.scope.logger.warn(
28368
28613
  `[SkillRegistry] Failed to index dynamic skill ${id} in storage provider: ${error.message}`
28369
28614
  );
28370
- }
28371
- }
28372
- this.reindex();
28615
+ })
28616
+ ] : [];
28373
28617
  this.bump("reset");
28618
+ await Promise.all(providerWrites);
28374
28619
  let unregistered = false;
28375
28620
  const unregister = async () => {
28376
28621
  if (unregistered) return;
@@ -28383,32 +28628,99 @@ var init_skill_registry = __esm({
28383
28628
  this.scope.logger.debug(
28384
28629
  `[SkillRegistry] Registered dynamic skill ${id}${opts?.source ? ` (source: ${opts.source})` : ""}`
28385
28630
  );
28386
- return { id, unregister };
28631
+ return { id, unregister, removed };
28387
28632
  }
28388
28633
  /**
28389
28634
  * Remove a previously-registered dynamic skill by id.
28390
28635
  */
28391
28636
  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);
28637
+ if (!this.dynamicRows.some((r) => r.instance.name === id)) return false;
28397
28638
  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
- }
28639
+ this.withdrawDynamicSkill(id, mutable);
28407
28640
  this.reindex();
28641
+ const removal = mutable ? this.removeFromProvider(mutable, id) : void 0;
28408
28642
  this.bump("reset");
28643
+ await removal;
28409
28644
  this.scope.logger.debug(`[SkillRegistry] Unregistered dynamic skill ${id}`);
28410
28645
  return true;
28411
28646
  }
28647
+ /**
28648
+ * Take a dynamic skill out of the registry, synchronously. Its generation advances so a handle
28649
+ * from an earlier registration stays a no-op, and while a mutable provider still holds its copy it
28650
+ * is withdrawn from provider-backed reads.
28651
+ */
28652
+ withdrawDynamicSkill(id, mutable) {
28653
+ this.dynamicRows.splice(
28654
+ this.dynamicRows.findIndex((r) => r.instance.name === id),
28655
+ 1
28656
+ );
28657
+ this.dynamicContents.delete(id);
28658
+ this.dynamicGenerations.set(id, (this.dynamicGenerations.get(id) ?? 0) + 1);
28659
+ if (mutable) this.withdrawn.set(id, ++this.withdrawals);
28660
+ }
28661
+ /** Remove a withdrawn skill's copy from the provider. If that fails it stays withdrawn from reads. */
28662
+ async removeFromProvider(mutable, id) {
28663
+ const withdrawal = this.withdrawn.get(id);
28664
+ try {
28665
+ await this.syncProvider(id, () => mutable.remove(id));
28666
+ if (this.withdrawn.get(id) === withdrawal) this.withdrawn.delete(id);
28667
+ } catch (error) {
28668
+ this.scope.logger.warn(
28669
+ `[SkillRegistry] Failed to remove skill ${id} from storage provider; it stays hidden from provider-backed reads: ${error.message}`
28670
+ );
28671
+ }
28672
+ }
28673
+ /**
28674
+ * Run a storage-provider write for one skill id after the ones queued before it for that id, so a
28675
+ * removal that completes late cannot delete the copy a later registration added. The returned
28676
+ * promise settles with the write; a failure does not hold back the next one.
28677
+ */
28678
+ syncProvider(id, write) {
28679
+ const run = (this.providerWrites.get(id) ?? Promise.resolve()).then(write);
28680
+ const settled = run.then(
28681
+ () => void 0,
28682
+ () => void 0
28683
+ );
28684
+ this.providerWrites.set(id, settled);
28685
+ void settled.then(() => {
28686
+ if (this.providerWrites.get(id) === settled) this.providerWrites.delete(id);
28687
+ });
28688
+ return run;
28689
+ }
28690
+ /**
28691
+ * After a child's change, take the skills that left it (and are not served here some other way)
28692
+ * out of this registry's storage provider too. The re-add on change only adds, so without this an
28693
+ * adopter's provider kept serving a skill the child had removed. Until the removal completes, and
28694
+ * if it fails, provider-backed reads skip it.
28695
+ */
28696
+ withdrawGoneAdoptedSkills(previous) {
28697
+ const mutable = this.asMutableProvider(this.storageProvider);
28698
+ if (!mutable) return;
28699
+ const skillIdOf = (entry) => entry.metadata.id ?? entry.metadata.name;
28700
+ const live = new Set(this.listAllIndexed().map((r) => skillIdOf(r.instance)));
28701
+ for (const id of live) this.withdrawn.delete(id);
28702
+ for (const row of previous) {
28703
+ const id = skillIdOf(row.instance);
28704
+ if (live.has(id) || this.withdrawn.has(id)) continue;
28705
+ this.withdrawn.set(id, ++this.withdrawals);
28706
+ void this.removeFromProvider(mutable, id);
28707
+ }
28708
+ }
28709
+ /**
28710
+ * Which provider-backed rows a read that starts now must skip: a skill withdrawn when the read
28711
+ * starts (its removal may complete while the provider answers, clearing the withdrawal, yet the
28712
+ * answer can still hold it), or withdrawn by the time the answer arrives.
28713
+ */
28714
+ hiddenFromProviderRead() {
28715
+ const atStart = new Set(this.withdrawn.keys());
28716
+ return {
28717
+ size: atStart.size,
28718
+ has: (meta) => {
28719
+ const id = meta.id ?? meta.name;
28720
+ return atStart.has(id) || this.withdrawn.has(id);
28721
+ }
28722
+ };
28723
+ }
28412
28724
  /* -------------------- Internal helpers -------------------- */
28413
28725
  asMutableProvider(provider) {
28414
28726
  const candidate = provider;
@@ -28670,7 +28982,7 @@ var init_plugin_utils = __esm({
28670
28982
  });
28671
28983
 
28672
28984
  // libs/sdk/src/plugin/plugin.registry.ts
28673
- var import_reflect_metadata12, import_di24, PluginRegistry;
28985
+ var import_reflect_metadata12, import_di24, installedPluginValues, PluginRegistry;
28674
28986
  var init_plugin_registry = __esm({
28675
28987
  "libs/sdk/src/plugin/plugin.registry.ts"() {
28676
28988
  "use strict";
@@ -28678,6 +28990,7 @@ var init_plugin_registry = __esm({
28678
28990
  import_di24 = require("@frontmcp/di");
28679
28991
  init_adapter_registry();
28680
28992
  init_common();
28993
+ init_dynamic_plugin();
28681
28994
  init_dynamic_utils();
28682
28995
  init_context_extension();
28683
28996
  init_errors();
@@ -28690,6 +29003,7 @@ var init_plugin_registry = __esm({
28690
29003
  init_skill_registry();
28691
29004
  init_tool_registry();
28692
29005
  init_plugin_utils();
29006
+ installedPluginValues = /* @__PURE__ */ new WeakSet();
28693
29007
  PluginRegistry = class _PluginRegistry extends RegistryAbstract {
28694
29008
  /** providers by token */
28695
29009
  pProviders = /* @__PURE__ */ new Map();
@@ -28878,8 +29192,15 @@ var init_plugin_registry = __esm({
28878
29192
  pluginInstance: new rec.provide(...depsInstances),
28879
29193
  dynamicProviders: rec.providers
28880
29194
  };
28881
- case "VALUE" /* VALUE */:
28882
- return { pluginInstance: rec.useValue, dynamicProviders: rec.providers };
29195
+ case "VALUE" /* VALUE */: {
29196
+ const value = rec.useValue;
29197
+ const init = installedPluginValues.has(value) ? initOptionsOf(value) : void 0;
29198
+ installedPluginValues.add(value);
29199
+ if (init && isDynamicPluginClass(rec.provide)) {
29200
+ return { pluginInstance: new rec.provide(init.options), dynamicProviders: rec.providers };
29201
+ }
29202
+ return { pluginInstance: value, dynamicProviders: rec.providers };
29203
+ }
28883
29204
  case "FACTORY" /* FACTORY */: {
28884
29205
  const args = [];
28885
29206
  for (const d of rec.inject()) args.push(await this.providers.resolveBootstrapDep(d));
@@ -29393,7 +29714,7 @@ var init_agent_instance = __esm({
29393
29714
  "hideFromDiscovery"
29394
29715
  ]);
29395
29716
  AgentInstance = class extends AgentEntry {
29396
- providers;
29717
+ providerRegistry;
29397
29718
  scope;
29398
29719
  hooks;
29399
29720
  /** The LLM adapter for this agent */
@@ -29411,11 +29732,11 @@ var init_agent_instance = __esm({
29411
29732
  constructor(record, providers, owner) {
29412
29733
  super(record);
29413
29734
  this.owner = owner;
29414
- this.providers = providers;
29735
+ this.providerRegistry = providers;
29415
29736
  this.name = record.metadata.id ?? record.metadata.name;
29416
29737
  this.id = record.metadata.id ?? record.metadata.name;
29417
29738
  this.fullName = this.owner.id + ":" + this.name;
29418
- this.scope = this.providers.getActiveScope();
29739
+ this.scope = this.providerRegistry.getActiveScope();
29419
29740
  this.hooks = this.scope.hooks;
29420
29741
  this.inputSchema = record.metadata.inputSchema ?? {};
29421
29742
  this.outputSchema = record.metadata.outputSchema;
@@ -29474,11 +29795,11 @@ var init_agent_instance = __esm({
29474
29795
  const adapterOptions = {
29475
29796
  providerResolver: {
29476
29797
  get: (token) => {
29477
- return this.providers.get(token);
29798
+ return this.providerRegistry.get(token);
29478
29799
  },
29479
29800
  tryGet: (token) => {
29480
29801
  try {
29481
- return this.providers.get(token);
29802
+ return this.providerRegistry.get(token);
29482
29803
  } catch {
29483
29804
  return void 0;
29484
29805
  }
@@ -29491,7 +29812,7 @@ var init_agent_instance = __esm({
29491
29812
  }
29492
29813
  };
29493
29814
  try {
29494
- const configService = this.providers.get(ConfigService);
29815
+ const configService = this.providerRegistry.get(ConfigService);
29495
29816
  if (configService) {
29496
29817
  adapterOptions.configResolver = this.createConfigResolver(configService);
29497
29818
  }
@@ -29599,7 +29920,7 @@ var init_agent_instance = __esm({
29599
29920
  const toolMetadata = this.buildAgentToolMetadata();
29600
29921
  const agentToolFunction = frontMcpTool(toolMetadata)(this.createAgentToolExecuteHandler());
29601
29922
  const toolRecord = normalizeTool(agentToolFunction);
29602
- this.agentToolInstance = new ToolInstance(toolRecord, this.providers, this.owner);
29923
+ this.agentToolInstance = new ToolInstance(toolRecord, this.providerRegistry, this.owner);
29603
29924
  await this.agentToolInstance.ready;
29604
29925
  this.scope.logger.debug(`Created agent tool instance: ${this.agentToolInstance.name} for agent ${this.name}`);
29605
29926
  } catch (error) {
@@ -29641,16 +29962,34 @@ var init_agent_instance = __esm({
29641
29962
  /**
29642
29963
  * Create the execute handler for the agent tool.
29643
29964
  *
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.
29965
+ * The tool's `tools:call-tool` flow has already applied everything the agent declares
29966
+ * (authorities, rate limit, concurrency, timeout and plugin gates, copied onto the tool). The
29967
+ * handler then runs the agent through the `agents:call-agent` flow, so the hooks registered for
29968
+ * agent invocation (`AgentCallHook`, and the agent class's own hooks on that flow) run and the
29969
+ * agent's context gets the request's context providers. The flow's own gates are left to the
29970
+ * tool flow (`gatedBy`), so nothing is counted twice.
29646
29971
  */
29647
29972
  createAgentToolExecuteHandler() {
29648
29973
  return async (input, toolCtx) => {
29649
29974
  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;
29975
+ const progressToken = toolCtx._progressTokenInternal;
29976
+ const requestId = toolCtx._jsonRpcRequestIdInternal;
29977
+ return this.scope.runFlowForOutput("agents:call-agent", {
29978
+ request: {
29979
+ method: "tools/call",
29980
+ params: {
29981
+ name: this.id,
29982
+ arguments: input,
29983
+ ...progressToken !== void 0 ? { _meta: { progressToken } } : {}
29984
+ }
29985
+ },
29986
+ ctx: {
29987
+ authInfo,
29988
+ ...requestId !== void 0 ? { requestId } : {},
29989
+ ...toolCtx.signal ? { signal: toolCtx.signal } : {}
29990
+ },
29991
+ gatedBy: "tools:call-tool"
29992
+ });
29654
29993
  };
29655
29994
  }
29656
29995
  /**
@@ -29664,6 +30003,10 @@ var init_agent_instance = __esm({
29664
30003
  getToolInstance() {
29665
30004
  return this.agentToolInstance;
29666
30005
  }
30006
+ /** The provider registry this agent was registered with (its app's). */
30007
+ get providers() {
30008
+ return this.providerRegistry;
30009
+ }
29667
30010
  /**
29668
30011
  * The tools this agent can call: those it declares and those its plugins contribute, as its private
29669
30012
  * scope holds them. Empty until the agent is initialized.
@@ -29713,7 +30056,7 @@ var init_agent_instance = __esm({
29713
30056
  case "VALUE" /* VALUE */:
29714
30057
  return this.record.useValue;
29715
30058
  case "FACTORY" /* FACTORY */:
29716
- return this.record.useFactory(this.providers);
30059
+ return this.record.useFactory(this.providerRegistry);
29717
30060
  case "ESM" /* ESM */:
29718
30061
  throw new AgentNotConfiguredError(`ESM agent "${this.name}" cannot be created via AgentInstance.create()`);
29719
30062
  case "REMOTE" /* REMOTE */:
@@ -29732,11 +30075,13 @@ var init_agent_instance = __esm({
29732
30075
  * @param llmAdapter - The LLM adapter (passed explicitly to avoid non-null assertion)
29733
30076
  */
29734
30077
  buildAgentCtorArgs(input, ctx, llmAdapter) {
29735
- const scope = this.providers.getActiveScope();
30078
+ const scope = this.providerRegistry.getActiveScope();
29736
30079
  return {
29737
30080
  metadata: this.metadata,
29738
30081
  input,
29739
- providers: this.providers,
30082
+ // The request's context-aware providers when the agents:call-agent flow built them, so
30083
+ // `this.context` and CONTEXT-scoped providers resolve inside the agent.
30084
+ providers: ctx.contextProviders ?? this.providerRegistry,
29740
30085
  logger: scope.logger,
29741
30086
  authInfo: ctx.authInfo,
29742
30087
  llmAdapter,
@@ -29807,7 +30152,8 @@ var init_agent_instance = __esm({
29807
30152
  await this.assertToolAuthorized(tool, ctx.authInfo, args);
29808
30153
  const runningTool = { name: tool.name, fullName: tool.fullName };
29809
30154
  return runOnSurface(AGENT_SURFACE, () => {
29810
- const toolContext = runAsTool(runningTool, () => tool.create(args, ctx));
30155
+ const { contextProviders: _agentProviders, ...toolCtx } = ctx;
30156
+ const toolContext = runAsTool(runningTool, () => tool.create(args, toolCtx));
29811
30157
  return runAsTool(runningTool, () => Promise.resolve(toolContext.execute(args)));
29812
30158
  });
29813
30159
  };
@@ -30346,6 +30692,53 @@ var init_agent_registry = __esm({
30346
30692
  }
30347
30693
  });
30348
30694
 
30695
+ // libs/sdk/src/channel/sources/completion-events.ts
30696
+ function completionEventsOf(scope) {
30697
+ let events = byScope.get(scope);
30698
+ if (!events) {
30699
+ events = new ScopeCompletionEvents();
30700
+ byScope.set(scope, events);
30701
+ }
30702
+ return events;
30703
+ }
30704
+ function completionOutputText(value) {
30705
+ if (value === void 0) return void 0;
30706
+ if (typeof value === "string") return value;
30707
+ try {
30708
+ return JSON.stringify(value);
30709
+ } catch {
30710
+ return String(value);
30711
+ }
30712
+ }
30713
+ var CompletionStream, ScopeCompletionEvents, byScope;
30714
+ var init_completion_events = __esm({
30715
+ "libs/sdk/src/channel/sources/completion-events.ts"() {
30716
+ "use strict";
30717
+ CompletionStream = class {
30718
+ listeners = /* @__PURE__ */ new Set();
30719
+ subscribe(listener) {
30720
+ this.listeners.add(listener);
30721
+ return () => {
30722
+ this.listeners.delete(listener);
30723
+ };
30724
+ }
30725
+ emit(event) {
30726
+ for (const listener of [...this.listeners]) {
30727
+ try {
30728
+ listener(event);
30729
+ } catch {
30730
+ }
30731
+ }
30732
+ }
30733
+ };
30734
+ ScopeCompletionEvents = class {
30735
+ agents = new CompletionStream();
30736
+ jobs = new CompletionStream();
30737
+ };
30738
+ byScope = /* @__PURE__ */ new WeakMap();
30739
+ }
30740
+ });
30741
+
30349
30742
  // libs/sdk/src/agent/flows/call-agent.flow.ts
30350
30743
  var import_auth26, import_guard6, import_lazy_zod60, import_protocol41, inputSchema15, outputSchema14, stateSchema14, plan14, name14, Stage14, CallAgentFlow;
30351
30744
  var init_call_agent_flow = __esm({
@@ -30355,13 +30748,22 @@ var init_call_agent_flow = __esm({
30355
30748
  import_guard6 = require("@frontmcp/guard");
30356
30749
  import_lazy_zod60 = require("@frontmcp/lazy-zod");
30357
30750
  import_protocol41 = require("@frontmcp/protocol");
30751
+ init_completion_events();
30358
30752
  init_common();
30359
30753
  init_errors();
30360
30754
  init_hooks_utils();
30755
+ init_flow_context_providers();
30361
30756
  inputSchema15 = import_lazy_zod60.z.object({
30362
30757
  request: import_protocol41.CallToolRequestSchema,
30363
30758
  // z.any() used because ctx is the MCP SDK's ToolCallExtra type which varies by SDK version
30364
- ctx: import_lazy_zod60.z.any()
30759
+ ctx: import_lazy_zod60.z.any(),
30760
+ /**
30761
+ * Set when the agent's `invoke_<agent>` tool runs this flow. That tool's `tools:call-tool` flow
30762
+ * has already applied the agent's authorities, rate limit, concurrency and timeout (they are
30763
+ * copied onto the tool), so this flow's stages for them leave the checks to it instead of
30764
+ * counting the call twice. Their hooks still run.
30765
+ */
30766
+ gatedBy: import_lazy_zod60.z.literal("tools:call-tool").optional()
30365
30767
  });
30366
30768
  outputSchema14 = import_protocol41.CallToolResultSchema;
30367
30769
  stateSchema14 = import_lazy_zod60.z.object({
@@ -30394,7 +30796,13 @@ var init_call_agent_flow = __esm({
30394
30796
  // Semaphore ticket for concurrency control (set by acquireSemaphore, used by releaseSemaphore)
30395
30797
  semaphoreTicket: import_lazy_zod60.z.any().optional(),
30396
30798
  // 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()
30799
+ abandonedExecution: import_lazy_zod60.z.instanceof(Promise).optional(),
30800
+ // When execute() started, and the error it ended with (for the completion event)
30801
+ executionStartedAt: import_lazy_zod60.z.number().optional(),
30802
+ executionError: import_lazy_zod60.z.any().optional(),
30803
+ // The MCP result built from rawOutput (parseOutput), or why it could not be built
30804
+ parsedOutput: import_lazy_zod60.z.any().optional(),
30805
+ outputError: import_lazy_zod60.z.any().optional()
30398
30806
  });
30399
30807
  plan14 = {
30400
30808
  pre: [
@@ -30407,12 +30815,16 @@ var init_call_agent_flow = __esm({
30407
30815
  "acquireSemaphore"
30408
30816
  ],
30409
30817
  execute: ["validateInput", "execute", "validateOutput"],
30410
- finalize: ["releaseSemaphore", "releaseQuota", "finalize"]
30818
+ finalize: ["releaseSemaphore", "releaseQuota", "parseOutput", "emitCompletion", "finalize"]
30411
30819
  };
30412
30820
  name14 = "agents:call-agent";
30413
30821
  ({ Stage: Stage14 } = FlowHooksOf(name14));
30414
30822
  CallAgentFlow = class extends FlowBase {
30415
30823
  logger = this.scopeLogger.child("CallAgentFlow");
30824
+ /** Whether the `invoke_<agent>` tool's flow already applied the agent's gates. */
30825
+ get gatedByToolFlow() {
30826
+ return this.input.gatedBy === "tools:call-tool";
30827
+ }
30416
30828
  async parseInput() {
30417
30829
  this.logger.verbose("parseInput:start");
30418
30830
  let method;
@@ -30479,6 +30891,10 @@ var init_call_agent_flow = __esm({
30479
30891
  }
30480
30892
  async checkEntryAuthorities() {
30481
30893
  this.logger.verbose("checkEntryAuthorities:start");
30894
+ if (this.gatedByToolFlow) {
30895
+ this.logger.verbose("checkEntryAuthorities:skip (checked by the invoke tool flow)");
30896
+ return;
30897
+ }
30482
30898
  const engine = this.scope.authoritiesEngine;
30483
30899
  const ctxBuilder = this.scope.authoritiesContextBuilder;
30484
30900
  if (!engine || !ctxBuilder) {
@@ -30544,14 +30960,17 @@ var init_call_agent_flow = __esm({
30544
30960
  const progressToken = this.state.progressToken;
30545
30961
  const authInfo = this.state.authInfo;
30546
30962
  try {
30547
- const context = agent.create(input.arguments, { ...ctx, progressToken });
30963
+ const sessionKey = authInfo?.sessionId ?? "anonymous";
30964
+ const agentViews = await agent.providers.buildViews(sessionKey, new Map(this.deps), this.scope.providers);
30965
+ const contextProviders = new FlowContextProviders(agent.providers, agentViews.context);
30966
+ const context = agent.create(input.arguments, { ...ctx, progressToken, contextProviders });
30548
30967
  this.appendContextHooks(hooksBoundTo(this.scope.hooks.getClsHooks(agent.record.provide), context));
30549
30968
  context.mark("createAgentContext");
30550
30969
  const frontmcpContext = context.tryGetContext();
30551
30970
  const sdkAuthInfo = authInfo;
30552
- if (frontmcpContext && sdkAuthInfo?.transport?.sendElicitRequest) {
30971
+ const jsonRpcRequestId = this.state.jsonRpcRequestId;
30972
+ if (frontmcpContext && sdkAuthInfo?.transport?.sendElicitRequest && jsonRpcRequestId !== void 0) {
30553
30973
  const transport = sdkAuthInfo.transport;
30554
- const jsonRpcRequestId = this.state.jsonRpcRequestId;
30555
30974
  frontmcpContext.setTransport({
30556
30975
  sendElicitRequest: transport.sendElicitRequest.bind(transport),
30557
30976
  type: transport.type ?? "unknown",
@@ -30568,7 +30987,7 @@ var init_call_agent_flow = __esm({
30568
30987
  async acquireQuota() {
30569
30988
  this.logger.verbose("acquireQuota:start");
30570
30989
  const manager = this.scope.rateLimitManager;
30571
- if (!manager) {
30990
+ if (!manager || this.gatedByToolFlow) {
30572
30991
  this.state.agentContext?.mark("acquireQuota");
30573
30992
  this.logger.verbose("acquireQuota:done (no rate limit manager)");
30574
30993
  return;
@@ -30592,7 +31011,7 @@ var init_call_agent_flow = __esm({
30592
31011
  async acquireSemaphore() {
30593
31012
  this.logger.verbose("acquireSemaphore:start");
30594
31013
  const manager = this.scope.rateLimitManager;
30595
- if (!manager) {
31014
+ if (!manager || this.gatedByToolFlow) {
30596
31015
  this.state.agentContext?.mark("acquireSemaphore");
30597
31016
  this.logger.verbose("acquireSemaphore:done (no rate limit manager)");
30598
31017
  return;
@@ -30637,7 +31056,8 @@ var init_call_agent_flow = __esm({
30637
31056
  }
30638
31057
  agentContext.mark("execute");
30639
31058
  const startTime = Date.now();
30640
- const timeoutMs = agent.metadata.timeout?.executeMs ?? agent.metadata.execution?.timeout ?? this.scope.rateLimitManager?.config?.defaultTimeout?.executeMs;
31059
+ this.state.set("executionStartedAt", startTime);
31060
+ 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
31061
  const running = (async () => {
30642
31062
  agentContext.output = await agentContext.execute(agentContext.input);
30643
31063
  })();
@@ -30648,6 +31068,7 @@ var init_call_agent_flow = __esm({
30648
31068
  });
30649
31069
  this.logger.verbose("execute:done");
30650
31070
  } catch (error) {
31071
+ this.state.set("executionError", error);
30651
31072
  if (error instanceof import_guard6.ExecutionTimeoutError) {
30652
31073
  this.logger.warn("execute: agent execution timed out", {
30653
31074
  agent: agent.metadata.name,
@@ -30656,6 +31077,7 @@ var init_call_agent_flow = __esm({
30656
31077
  this.state.set("abandonedExecution", running);
30657
31078
  throw error;
30658
31079
  }
31080
+ if (this.gatedByToolFlow) throw error;
30659
31081
  throw new AgentExecutionError(agent.metadata.name, error instanceof Error ? error : void 0);
30660
31082
  }
30661
31083
  }
@@ -30693,9 +31115,34 @@ var init_call_agent_flow = __esm({
30693
31115
  this.state.agentContext?.mark("releaseQuota");
30694
31116
  this.logger.verbose("releaseQuota:done");
30695
31117
  }
31118
+ async parseOutput() {
31119
+ const { agent, rawOutput } = this.state;
31120
+ if (!agent || rawOutput === void 0) return;
31121
+ const parseResult = agent.safeParseOutput(rawOutput);
31122
+ if (parseResult.success) this.state.set("parsedOutput", parseResult.data);
31123
+ else this.state.set("outputError", parseResult.error);
31124
+ }
31125
+ async emitCompletion() {
31126
+ const { agent, agentContext, executionStartedAt, executionError, outputError, authInfo } = this.state;
31127
+ if (!agent || executionStartedAt === void 0) return;
31128
+ if (executionError instanceof ElicitationFallbackRequired || executionError instanceof FlowControl) return;
31129
+ const failure = executionError ?? (outputError !== void 0 ? new InvalidOutputError() : void 0);
31130
+ const failed = failure !== void 0;
31131
+ const requestId = this.tryGetContext()?.requestId;
31132
+ const sessionId = typeof authInfo?.sessionId === "string" && authInfo.sessionId ? authInfo.sessionId : void 0;
31133
+ completionEventsOf(this.scope).agents.emit({
31134
+ agentId: agent.id,
31135
+ agentName: agent.name,
31136
+ status: failed ? "error" : "success",
31137
+ durationMs: Date.now() - executionStartedAt,
31138
+ ...failed ? { error: failure instanceof Error ? failure.message : String(failure) } : { output: completionOutputText(agentContext?.output) },
31139
+ ...requestId ? { runId: requestId } : {},
31140
+ ...sessionId ? { sessionId } : {}
31141
+ });
31142
+ }
30696
31143
  async finalize() {
30697
31144
  this.logger.verbose("finalize:start");
30698
- const { agent, rawOutput, executionMeta } = this.state;
31145
+ const { agent, rawOutput, parsedOutput, outputError, executionMeta } = this.state;
30699
31146
  if (!agent) {
30700
31147
  this.logger.verbose("finalize: skipping (no agent in state)");
30701
31148
  return;
@@ -30704,15 +31151,14 @@ var init_call_agent_flow = __esm({
30704
31151
  this.logger.verbose("finalize: skipping (no output - execute stage likely failed)");
30705
31152
  return;
30706
31153
  }
30707
- const parseResult = agent.safeParseOutput(rawOutput);
30708
- if (!parseResult.success) {
31154
+ if (outputError !== void 0 || parsedOutput === void 0) {
30709
31155
  this.logger.error("finalize: output validation failed", {
30710
31156
  agent: agent.metadata.name,
30711
- errors: parseResult.error
31157
+ errors: outputError
30712
31158
  });
30713
31159
  throw new InvalidOutputError();
30714
31160
  }
30715
- const result = parseResult.data;
31161
+ const result = parsedOutput;
30716
31162
  if (executionMeta) {
30717
31163
  result._meta = {
30718
31164
  ...result._meta,
@@ -30778,6 +31224,12 @@ var init_call_agent_flow = __esm({
30778
31224
  __decorateClass([
30779
31225
  Stage14("releaseQuota")
30780
31226
  ], CallAgentFlow.prototype, "releaseQuota", 1);
31227
+ __decorateClass([
31228
+ Stage14("parseOutput")
31229
+ ], CallAgentFlow.prototype, "parseOutput", 1);
31230
+ __decorateClass([
31231
+ Stage14("emitCompletion")
31232
+ ], CallAgentFlow.prototype, "emitCompletion", 1);
30781
31233
  __decorateClass([
30782
31234
  Stage14("finalize")
30783
31235
  ], CallAgentFlow.prototype, "finalize", 1);
@@ -31285,12 +31737,36 @@ var init_request_validation = __esm({
31285
31737
  });
31286
31738
 
31287
31739
  // libs/sdk/src/transport/mcp-20260728/request-state.ts
31288
- function getRequestStateKey() {
31740
+ function configuredSecret(name44) {
31741
+ const value = (0, import_utils65.getEnv)(name44);
31742
+ return value && value.trim().length > 0 ? value : void 0;
31743
+ }
31744
+ function resolveKey() {
31289
31745
  if (cachedKey) return cachedKey;
31290
- const secret = process.env["VAULT_SECRET"] ?? process.env["JWT_SECRET"];
31291
- cachedKey = secret ? encoder.encode(secret) : (0, import_utils65.randomBytes)(32);
31746
+ const vault = configuredSecret("VAULT_SECRET");
31747
+ const jwt = vault ? void 0 : configuredSecret("JWT_SECRET");
31748
+ if (vault) cachedKey = { key: encoder.encode(vault), source: "VAULT_SECRET" };
31749
+ else if (jwt) cachedKey = { key: encoder.encode(jwt), source: "JWT_SECRET" };
31750
+ else cachedKey = { key: (0, import_utils65.randomBytes)(32), source: "per-process" };
31292
31751
  return cachedKey;
31293
31752
  }
31753
+ function getRequestStateKey() {
31754
+ return resolveKey().key;
31755
+ }
31756
+ function getRequestStateKeySource() {
31757
+ return resolveKey().source;
31758
+ }
31759
+ function warnIfRequestStateKeyNotShared(options) {
31760
+ if (warnedKeyNotShared || !(0, import_utils65.isProduction)()) return;
31761
+ const { metadata } = options;
31762
+ const persistence = metadata.transport?.persistence;
31763
+ const sharesState = Boolean(metadata.redis) || typeof persistence === "object" && persistence !== null;
31764
+ if (!sharesState || getRequestStateKeySource() !== "per-process") return;
31765
+ warnedKeyNotShared = true;
31766
+ options.logger.warn(
31767
+ "requestState is signed with a per-process key (neither VAULT_SECRET nor JWT_SECRET is set). Multi-round tools (elicit/sample) restart when a round lands on another instance; set VAULT_SECRET (or JWT_SECRET) to the same value on every instance."
31768
+ );
31769
+ }
31294
31770
  function computeRequestBinding(method, params) {
31295
31771
  const salient = {};
31296
31772
  for (const key of ["name", "uri", "arguments"]) {
@@ -31340,13 +31816,14 @@ function decodeRequestState(state, expected) {
31340
31816
  if (payload.b !== expected.binding) return { ok: false, reason: "request-mismatch" };
31341
31817
  return { ok: true, responses: payload.r };
31342
31818
  }
31343
- var import_utils65, DEFAULT_REQUEST_STATE_TTL_MS, MAX_REQUEST_STATE_BYTES, cachedKey, encoder, decoder;
31819
+ var import_utils65, DEFAULT_REQUEST_STATE_TTL_MS, MAX_REQUEST_STATE_BYTES, cachedKey, warnedKeyNotShared, encoder, decoder;
31344
31820
  var init_request_state = __esm({
31345
31821
  "libs/sdk/src/transport/mcp-20260728/request-state.ts"() {
31346
31822
  "use strict";
31347
31823
  import_utils65 = require("@frontmcp/utils");
31348
31824
  DEFAULT_REQUEST_STATE_TTL_MS = 10 * 60 * 1e3;
31349
31825
  MAX_REQUEST_STATE_BYTES = 256 * 1024;
31826
+ warnedKeyNotShared = false;
31350
31827
  encoder = new TextEncoder();
31351
31828
  decoder = new TextDecoder();
31352
31829
  }
@@ -31580,10 +32057,17 @@ var init_mrtr = __esm({
31580
32057
  /** Per-kind call counters, used to derive stable keys across a replay. */
31581
32058
  counters = { elicitation: 0, sampling: 0, roots: 0 };
31582
32059
  clientCapabilities;
32060
+ /**
32061
+ * Whether the client itself declared this revision. `false` when the SERVER served an
32062
+ * unversioned call as 2026-07-28 (`transport.defaultProtocolVersion`, the Worker default): such a
32063
+ * client, typically a 2025-03-26 one, never agreed to the revision's input-request contract.
32064
+ */
32065
+ clientDeclaredRevision;
31583
32066
  binding;
31584
32067
  constructor(params) {
31585
32068
  this.responses = { ...params.carriedResponses ?? {}, ...params.inputResponses ?? {} };
31586
32069
  this.clientCapabilities = params.clientCapabilities;
32070
+ this.clientDeclaredRevision = params.clientDeclaredRevision ?? true;
31587
32071
  this.binding = params.binding;
31588
32072
  }
31589
32073
  /** True when the client declared the capability a given request kind needs. */
@@ -31929,7 +32413,7 @@ function shouldCreateTask(params) {
31929
32413
  return { kind: "create", owner: ownership.owner };
31930
32414
  }
31931
32415
  async function resumeTask(params) {
31932
- const { scope, record, authInfo, clientCapabilities, frontmcpContext } = params;
32416
+ const { scope, record, authInfo, clientCapabilities, frontmcpContext, clientDeclaredRevision } = params;
31933
32417
  const registry = scope.tasks;
31934
32418
  const runner = registry?.runner;
31935
32419
  if (!runner) {
@@ -31940,6 +32424,8 @@ async function resumeTask(params) {
31940
32424
  new MrtrExchange({
31941
32425
  carriedResponses: record.inputResponses ?? {},
31942
32426
  clientCapabilities,
32427
+ // A caller that never declared the revision is refused an elicitation as a legacy caller is.
32428
+ clientDeclaredRevision,
31943
32429
  binding: {
31944
32430
  principal: resolveTaskPrincipal(authInfo),
31945
32431
  binding: computeRequestBinding("tasks/resume", { name: record.taskId })
@@ -31986,6 +32472,7 @@ async function dispatch20260728(options) {
31986
32472
  scope,
31987
32473
  body,
31988
32474
  clientCapabilities,
32475
+ clientDeclaredRevision,
31989
32476
  frontmcpContext,
31990
32477
  authInfo,
31991
32478
  isAnonymous,
@@ -32032,7 +32519,8 @@ async function dispatch20260728(options) {
32032
32519
  record,
32033
32520
  authInfo: { ...authInfo ?? {}, sessionId: ownership.owner },
32034
32521
  clientCapabilities,
32035
- frontmcpContext
32522
+ frontmcpContext,
32523
+ clientDeclaredRevision
32036
32524
  })
32037
32525
  });
32038
32526
  return outcome.kind === "result" ? { kind: "result", result: decorate(outcome.result) } : outcome;
@@ -32056,13 +32544,15 @@ async function dispatch20260728(options) {
32056
32544
  };
32057
32545
  const carried = decodeRequestState(params["requestState"], binding);
32058
32546
  if (!carried.ok && carried.reason !== "absent") {
32059
- scope.logger.warn("mcp-20260728: rejected requestState", { method, reason: carried.reason });
32547
+ const hint = carried.reason === "bad-signature" && getRequestStateKeySource() === "per-process" ? "requestState is signed with a per-process key; set VAULT_SECRET (or JWT_SECRET) to the same value on every instance so a round can land on any of them" : void 0;
32548
+ scope.logger.warn("mcp-20260728: rejected requestState", { method, reason: carried.reason, ...hint && { hint } });
32060
32549
  }
32061
32550
  const exchange = new MrtrExchange({
32062
32551
  inputResponses: params["inputResponses"],
32063
32552
  carriedResponses: carried.ok ? carried.responses : {},
32064
32553
  clientCapabilities,
32065
- binding
32554
+ binding,
32555
+ clientDeclaredRevision
32066
32556
  });
32067
32557
  frontmcpContext?.setMrtrExchange(exchange);
32068
32558
  if (notificationSink) frontmcpContext?.setRequestNotificationSink(notificationSink);
@@ -32212,6 +32702,7 @@ var init_mcp_stateless_client = __esm({
32212
32702
  "libs/sdk/src/transport/mcp-20260728/client/mcp-stateless.client.ts"() {
32213
32703
  "use strict";
32214
32704
  import_protocol47 = require("@frontmcp/protocol");
32705
+ init_list_all_pages_utils();
32215
32706
  init_header_codec();
32216
32707
  init_protocol_20260728_constants();
32217
32708
  init_tasks_extension();
@@ -32252,10 +32743,22 @@ var init_mcp_stateless_client = __esm({
32252
32743
  * The spec requires this: one malformed tool definition must not prevent the
32253
32744
  * other tools from being used, so the offender is excluded and logged rather
32254
32745
  * than failing the call.
32746
+ *
32747
+ * Every page is fetched (`nextCursor` is followed to the end), since the
32748
+ * schemas cached here are what later `tools/call` headers are derived from.
32255
32749
  */
32256
32750
  async listTools() {
32257
- const result = await this.request("tools/list");
32258
- const tools = Array.isArray(result["tools"]) ? result["tools"] : [];
32751
+ const tools = await listAllPages(
32752
+ "tools/list",
32753
+ async (cursor) => {
32754
+ const result = await this.request("tools/list", cursor ? { cursor } : {});
32755
+ return {
32756
+ items: Array.isArray(result["tools"]) ? result["tools"] : [],
32757
+ nextCursor: typeof result["nextCursor"] === "string" ? result["nextCursor"] : void 0
32758
+ };
32759
+ },
32760
+ (message) => new McpStatelessError(-32603, message)
32761
+ );
32259
32762
  const usable = [];
32260
32763
  for (const tool of tools) {
32261
32764
  const validation = validateHeaderParams(tool["inputSchema"]);
@@ -32274,11 +32777,13 @@ var init_mcp_stateless_client = __esm({
32274
32777
  async readResource(uri) {
32275
32778
  return this.request("resources/read", { uri });
32276
32779
  }
32277
- async listResources() {
32278
- return this.request("resources/list");
32780
+ /** One page of `resources/list`; pass the previous page's `nextCursor` for the next. */
32781
+ async listResources(cursor) {
32782
+ return this.request("resources/list", cursor ? { cursor } : {});
32279
32783
  }
32280
- async listPrompts() {
32281
- return this.request("prompts/list");
32784
+ /** One page of `prompts/list`; pass the previous page's `nextCursor` for the next. */
32785
+ async listPrompts(cursor) {
32786
+ return this.request("prompts/list", cursor ? { cursor } : {});
32282
32787
  }
32283
32788
  async getPrompt(name44, args = {}) {
32284
32789
  return this.request("prompts/get", { name: name44, arguments: args });
@@ -32564,6 +33069,10 @@ var init_mcp_20260728 = __esm({
32564
33069
  });
32565
33070
 
32566
33071
  // libs/sdk/src/remote-mcp/mcp-stateless-client.adapter.ts
33072
+ function nextCursorOf(result) {
33073
+ const next = result["nextCursor"];
33074
+ return typeof next === "string" && next.length > 0 ? { nextCursor: next } : {};
33075
+ }
32567
33076
  async function negotiateRemoteProtocol(url, configured, headers, fetchImpl = fetch) {
32568
33077
  if (configured === "2026-07-28") return "2026-07-28";
32569
33078
  if (configured !== "auto") return "legacy";
@@ -32606,16 +33115,22 @@ var init_mcp_stateless_client_adapter = __esm({
32606
33115
  async callTool(params) {
32607
33116
  return this.client.callTool(params.name, params.arguments ?? {});
32608
33117
  }
32609
- async listResources() {
32610
- const result = await this.client.listResources();
32611
- return { resources: Array.isArray(result["resources"]) ? result["resources"] : [] };
33118
+ async listResources(params) {
33119
+ const result = await this.client.listResources(params?.cursor);
33120
+ return {
33121
+ resources: Array.isArray(result["resources"]) ? result["resources"] : [],
33122
+ ...nextCursorOf(result)
33123
+ };
32612
33124
  }
32613
33125
  async readResource(params) {
32614
33126
  return this.client.readResource(params.uri);
32615
33127
  }
32616
- async listPrompts() {
32617
- const result = await this.client.listPrompts();
32618
- return { prompts: Array.isArray(result["prompts"]) ? result["prompts"] : [] };
33128
+ async listPrompts(params) {
33129
+ const result = await this.client.listPrompts(params?.cursor);
33130
+ return {
33131
+ prompts: Array.isArray(result["prompts"]) ? result["prompts"] : [],
33132
+ ...nextCursorOf(result)
33133
+ };
32619
33134
  }
32620
33135
  async getPrompt(params) {
32621
33136
  return this.client.getPrompt(params.name, params.arguments ?? {});
@@ -33138,6 +33653,7 @@ var init_mcp_client_service = __esm({
33138
33653
  "libs/sdk/src/remote-mcp/mcp-client.service.ts"() {
33139
33654
  "use strict";
33140
33655
  import_protocol48 = require("@frontmcp/protocol");
33656
+ init_list_all_pages_utils();
33141
33657
  init_mcp_error();
33142
33658
  init_remote_errors();
33143
33659
  init_transport_errors();
@@ -33741,10 +34257,15 @@ var init_mcp_client_service = __esm({
33741
34257
  const httpOptions = request.transportOptions;
33742
34258
  return httpOptions?.fallbackToSSE ?? true;
33743
34259
  }
34260
+ // Each list follows `nextCursor` to the end: a FrontMCP remote pages its lists
34261
+ // (40 per page by default), and proxying only the first page silently dropped
34262
+ // everything after it.
33744
34263
  async listToolsInternal(connection) {
33745
34264
  try {
33746
- const result = await connection.client.listTools();
33747
- return result.tools || [];
34265
+ return await listAllPages("tools/list", async (cursor) => {
34266
+ const result = await connection.client.listTools(cursor ? { cursor } : void 0);
34267
+ return { items: result.tools, nextCursor: result.nextCursor };
34268
+ });
33748
34269
  } catch (error) {
33749
34270
  this.logger.warn(`Failed to list tools: ${error.message}`);
33750
34271
  return [];
@@ -33752,8 +34273,10 @@ var init_mcp_client_service = __esm({
33752
34273
  }
33753
34274
  async listResourcesInternal(connection) {
33754
34275
  try {
33755
- const result = await connection.client.listResources();
33756
- return result.resources || [];
34276
+ return await listAllPages("resources/list", async (cursor) => {
34277
+ const result = await connection.client.listResources(cursor ? { cursor } : void 0);
34278
+ return { items: result.resources, nextCursor: result.nextCursor };
34279
+ });
33757
34280
  } catch (error) {
33758
34281
  this.logger.warn(`Failed to list resources: ${error.message}`);
33759
34282
  return [];
@@ -33761,8 +34284,10 @@ var init_mcp_client_service = __esm({
33761
34284
  }
33762
34285
  async listResourceTemplatesInternal(connection) {
33763
34286
  try {
33764
- const result = await connection.client.listResourceTemplates();
33765
- return result.resourceTemplates || [];
34287
+ return await listAllPages("resources/templates/list", async (cursor) => {
34288
+ const result = await connection.client.listResourceTemplates(cursor ? { cursor } : void 0);
34289
+ return { items: result.resourceTemplates, nextCursor: result.nextCursor };
34290
+ });
33766
34291
  } catch (error) {
33767
34292
  this.logger.warn(`Failed to list resource templates: ${error.message}`);
33768
34293
  return [];
@@ -33770,8 +34295,10 @@ var init_mcp_client_service = __esm({
33770
34295
  }
33771
34296
  async listPromptsInternal(connection) {
33772
34297
  try {
33773
- const result = await connection.client.listPrompts();
33774
- return result.prompts || [];
34298
+ return await listAllPages("prompts/list", async (cursor) => {
34299
+ const result = await connection.client.listPrompts(cursor ? { cursor } : void 0);
34300
+ return { items: result.prompts, nextCursor: result.nextCursor };
34301
+ });
33775
34302
  } catch (error) {
33776
34303
  this.logger.warn(`Failed to list prompts: ${error.message}`);
33777
34304
  return [];
@@ -38029,6 +38556,8 @@ var init_oauth_authorize_flow = __esm({
38029
38556
  if (state) {
38030
38557
  url.searchParams.set("state", state);
38031
38558
  }
38559
+ const issuer = this.resolveIssuer(this.rawInput.request);
38560
+ if (issuer) url.searchParams.set("iss", issuer);
38032
38561
  this.respond(httpRespond.redirect(url.toString()));
38033
38562
  return;
38034
38563
  }
@@ -40148,7 +40677,7 @@ var init_oauth_provider_callback_flow = __esm({
40148
40677
  if (session.state) {
40149
40678
  url.searchParams.set("state", session.state);
40150
40679
  }
40151
- url.searchParams.set("iss", this.getLocalAuth().issuer);
40680
+ url.searchParams.set("iss", this.getLocalAuth().issuerFor(this.rawInput.request));
40152
40681
  this.logger.info(
40153
40682
  `Federated auth complete: ${selectedProviderIds.length} providers authenticated, redirecting to client`
40154
40683
  );
@@ -41061,7 +41590,7 @@ var init_oauth_userinfo_flow = __esm({
41061
41590
  return;
41062
41591
  }
41063
41592
  const localAuth = this.scope.auth;
41064
- const issuer = localAuth.issuerFor?.(this.rawInput.request);
41593
+ const issuer = localAuth.acceptedIssuersFor?.(this.rawInput.request);
41065
41594
  const result = await localAuth.verifyGatewayToken(token, baseUrl, void 0, issuer);
41066
41595
  if (!result.ok) {
41067
41596
  this.logger.warn("userinfo: token verification failed", { error: result.error });
@@ -41145,7 +41674,14 @@ var init_session_verify_flow = __esm({
41145
41674
  import_utils78 = require("@frontmcp/utils");
41146
41675
  init_notification_service();
41147
41676
  init_session_id_utils();
41148
- inputSchema23 = httpRequestInputSchema;
41677
+ inputSchema23 = httpRequestInputSchema.extend({
41678
+ /**
41679
+ * The request is served without a session (MCP 2026-07-28): an anonymous or static-key caller is
41680
+ * identified for this request only, and no session is minted for it (#629). Minting one needs
41681
+ * `MCP_SESSION_SECRET`, for a session id the client would never present.
41682
+ */
41683
+ sessionless: import_lazy_zod72.z.boolean().optional()
41684
+ });
41149
41685
  stateSchema22 = import_lazy_zod72.z.object({
41150
41686
  baseUrl: import_lazy_zod72.z.string().min(1),
41151
41687
  authorizationHeader: import_lazy_zod72.z.string().optional(),
@@ -41203,20 +41739,25 @@ var init_session_verify_flow = __esm({
41203
41739
  * Encapsulates the shared logic for session creation, payload encryption, and user derivation.
41204
41740
  */
41205
41741
  createAnonymousSession(options) {
41206
- const { authMode, issuer, scopes = ["anonymous"], subject, sessionIdHeader } = options;
41742
+ const { authMode, subject, sessionIdHeader } = options;
41207
41743
  this.logger.verbose("createAnonymousSession", { authMode, hasExistingSession: !!sessionIdHeader });
41744
+ if (this.rawInput.sessionless) {
41745
+ this.respond({
41746
+ kind: "authorized",
41747
+ authorization: { token: "", user: (0, import_auth39.anonymousCallerClaims)(options, (0, import_utils78.randomUUID)()) }
41748
+ });
41749
+ return;
41750
+ }
41208
41751
  const machineId = (0, import_utils78.getMachineId)();
41209
41752
  const authSignature = subject ? `${authMode}:${subject}` : authMode;
41210
41753
  if (sessionIdHeader) {
41211
- const existingPayload = decryptPublicSession(sessionIdHeader, authSignature);
41212
- if (existingPayload && existingPayload.nodeId === machineId) {
41754
+ const body = this.rawInput.request.body;
41755
+ const isInitialize2 = typeof body === "object" && body !== null && "method" in body && body.method === "initialize";
41756
+ const decrypted = decryptPublicSession(sessionIdHeader, authSignature);
41757
+ const existingPayload = decrypted && isInitialize2 && decrypted.nodeId !== machineId ? void 0 : decrypted;
41758
+ if (existingPayload) {
41213
41759
  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
- };
41760
+ const user2 = (0, import_auth39.anonymousCallerClaims)(options, anonId);
41220
41761
  this.respond({
41221
41762
  kind: "authorized",
41222
41763
  authorization: {
@@ -41230,13 +41771,8 @@ var init_session_verify_flow = __esm({
41230
41771
  this.logger.verbose("createAnonymousSession: ignoring unrecognized client session id; minting a fresh one");
41231
41772
  }
41232
41773
  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
41774
  const uuid = (0, import_utils78.randomUUID)();
41775
+ const user = (0, import_auth39.anonymousCallerClaims)(options, uuid);
41240
41776
  const platformDetectionConfig = this.scope.metadata.transport?.platformDetection;
41241
41777
  const platformType = detectPlatformFromUserAgent(this.state.userAgent, platformDetectionConfig);
41242
41778
  const payload = {
@@ -41429,7 +41965,7 @@ var init_session_verify_flow = __esm({
41429
41965
  } else {
41430
41966
  const configuredAudience = (0, import_auth12.isOrchestratedMode)(authOptions) ? authOptions.expectedAudience : void 0;
41431
41967
  const resource = computeResource(this.rawInput.request, this.scope.entryPath, this.scope.routeBase);
41432
- const issuer = auth.issuerFor?.(this.rawInput.request);
41968
+ const issuer = auth.acceptedIssuersFor?.(this.rawInput.request);
41433
41969
  verify = auth.verifyGatewayToken(token, this.state.required.baseUrl, configuredAudience ?? resource, issuer);
41434
41970
  }
41435
41971
  const result = await verify;
@@ -41722,6 +42258,9 @@ var init_well_known_oauth_authorization_server_flow = __esm({
41722
42258
  wellKnownAsStateSchema = import_lazy_zod74.z.object({
41723
42259
  baseUrl: import_lazy_zod74.z.string().min(1),
41724
42260
  // baseUrl + entryPrefix (unsuffixed)
42261
+ // The issuer this server names in answer to the request (`LocalPrimaryAuth.issuerFor`, #629): the
42262
+ // one on its authorization responses and tokens. Without it, `baseUrl`.
42263
+ issuer: import_lazy_zod74.z.string().min(1).optional(),
41725
42264
  // Root origin (proto://host WITHOUT the entryPath prefix). The OAuth
41726
42265
  // endpoints (/oauth/authorize, /oauth/token, /oauth/register, …) are
41727
42266
  // registered at literal root paths by their flows, NOT under entryPath, so
@@ -41753,6 +42292,9 @@ var init_well_known_oauth_authorization_server_flow = __esm({
41753
42292
  if (!request) throw new FlowInputMissingError("request", "well-known:oauth-authorization-server");
41754
42293
  const { metadata } = this.scope;
41755
42294
  const baseUrl = getRequestBaseUrl(request, this.scope.entryPath);
42295
+ const issuer = this.scope.auth?.issuerFor?.(
42296
+ request
42297
+ );
41756
42298
  const oauthBaseUrl = getRequestBaseUrl(request);
41757
42299
  let cimdEnabled = true;
41758
42300
  if (metadata.auth && (0, import_auth12.isOrchestratedMode)(metadata.auth)) {
@@ -41761,6 +42303,7 @@ var init_well_known_oauth_authorization_server_flow = __esm({
41761
42303
  this.state.set(
41762
42304
  wellKnownAsStateSchema.parse({
41763
42305
  baseUrl,
42306
+ issuer,
41764
42307
  oauthBaseUrl,
41765
42308
  // The scopes this server grants (`allowedScopes`, #262): the literal
41766
42309
  // entries; a `*` glob names no scope a client could ask for.
@@ -41775,6 +42318,7 @@ var init_well_known_oauth_authorization_server_flow = __esm({
41775
42318
  );
41776
42319
  }
41777
42320
  async collectData() {
42321
+ const { issuer } = this.state;
41778
42322
  const {
41779
42323
  baseUrl,
41780
42324
  oauthBaseUrl,
@@ -41785,7 +42329,7 @@ var init_well_known_oauth_authorization_server_flow = __esm({
41785
42329
  cimdEnabled
41786
42330
  } = this.state.required;
41787
42331
  if (isOrchestrated) {
41788
- const baseIssuer = `${baseUrl}`;
42332
+ const baseIssuer = issuer ?? baseUrl;
41789
42333
  this.respond({
41790
42334
  kind: "json",
41791
42335
  contentType: "application/json; charset=utf-8",
@@ -41812,6 +42356,8 @@ var init_well_known_oauth_authorization_server_flow = __esm({
41812
42356
  grant_types_supported: ["authorization_code", "refresh_token"],
41813
42357
  scopes_supported: scopesSupported,
41814
42358
  code_challenge_methods_supported: ["S256"],
42359
+ // Every authorization response, error responses included, carries `iss` (RFC 9207 §2, §3).
42360
+ authorization_response_iss_parameter_supported: true,
41815
42361
  // CIMD support advertisement per draft-ietf-oauth-client-id-metadata-document-00
41816
42362
  client_id_metadata_document_supported: cimdEnabled
41817
42363
  }
@@ -41860,14 +42406,16 @@ var init_well_known_prm_flow = __esm({
41860
42406
  stateSchema24 = import_lazy_zod75.z.object({
41861
42407
  resource: import_lazy_zod75.z.string().min(1),
41862
42408
  baseUrl: import_lazy_zod75.z.string().min(1),
41863
- scopesSupported: import_lazy_zod75.z.array(import_lazy_zod75.z.string()).default(["openid", "profile", "email"]),
42409
+ // The authorization server to name: the issuer this server names for the request (#629).
42410
+ authorizationServer: import_lazy_zod75.z.string().min(1).optional(),
42411
+ scopesSupported: import_lazy_zod75.z.array(import_lazy_zod75.z.string()),
41864
42412
  isOrchestrated: import_lazy_zod75.z.boolean()
41865
42413
  });
41866
42414
  outputSchema24 = HttpJsonSchema.extend({
41867
42415
  body: import_lazy_zod75.z.object({
41868
42416
  resource: import_lazy_zod75.z.string().min(1),
41869
42417
  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"]),
42418
+ scopes_supported: import_lazy_zod75.z.array(import_lazy_zod75.z.string()).optional(),
41871
42419
  bearer_methods_supported: import_lazy_zod75.z.array(import_lazy_zod75.z.string()).default(["header"])
41872
42420
  }).passthrough()
41873
42421
  });
@@ -41891,12 +42439,17 @@ var init_well_known_prm_flow = __esm({
41891
42439
  if (!request) throw new FlowInputMissingError("request", "well-known:prm");
41892
42440
  const resource = computeResource(request, scope.entryPath, scope.routeBase);
41893
42441
  const baseUrl = getRequestBaseUrl(request, scope.entryPath);
41894
- const auth = scope.metadata.auth;
42442
+ const authorizationServer = scope.auth?.issuerFor?.(request);
42443
+ const authOptions = scope.auth?.options ?? scope.metadata.auth;
42444
+ const granted = (0, import_auth42.resourceScopesFor)(authOptions);
42445
+ const orchestrated = authOptions?.mode === "local" || authOptions?.mode === "remote";
42446
+ const scopesSupported = orchestrated ? granted : [.../* @__PURE__ */ new Set([...granted, ...scope.getAllSupportedScopes()])];
41895
42447
  this.state.set(
41896
42448
  stateSchema24.parse({
41897
42449
  resource,
41898
42450
  baseUrl,
41899
- scopesSupported: auth && (0, import_auth12.isOrchestratedMode)(auth) ? (0, import_auth42.advertisedScopes)(auth.allowedScopes) : scope.getAllSupportedScopes(),
42451
+ authorizationServer,
42452
+ scopesSupported,
41900
42453
  isOrchestrated: false
41901
42454
  //scope.orchestrated,// TODO: fix
41902
42455
  })
@@ -41904,6 +42457,8 @@ var init_well_known_prm_flow = __esm({
41904
42457
  }
41905
42458
  async collectData() {
41906
42459
  const { resource, baseUrl, scopesSupported, isOrchestrated } = this.state.required;
42460
+ const { authorizationServer } = this.state;
42461
+ const scopes = scopesSupported.length > 0 ? { scopes_supported: scopesSupported } : {};
41907
42462
  if (isOrchestrated) {
41908
42463
  this.respond({
41909
42464
  kind: "json",
@@ -41915,8 +42470,8 @@ var init_well_known_prm_flow = __esm({
41915
42470
  headers: { "cache-control": "no-store" },
41916
42471
  body: {
41917
42472
  resource,
41918
- authorization_servers: [baseUrl],
41919
- scopes_supported: scopesSupported,
42473
+ authorization_servers: [authorizationServer ?? baseUrl],
42474
+ ...scopes,
41920
42475
  bearer_methods_supported: ["header"]
41921
42476
  }
41922
42477
  });
@@ -41929,8 +42484,8 @@ var init_well_known_prm_flow = __esm({
41929
42484
  headers: { "cache-control": "no-store" },
41930
42485
  body: {
41931
42486
  resource,
41932
- authorization_servers: [baseUrl],
41933
- scopes_supported: scopesSupported,
42487
+ authorization_servers: [authorizationServer ?? baseUrl],
42488
+ ...scopes,
41934
42489
  bearer_methods_supported: ["header"]
41935
42490
  }
41936
42491
  });
@@ -42078,6 +42633,14 @@ function audienceMatchesResource(aud, expected) {
42078
42633
  function isUnsupportedOperation(error) {
42079
42634
  return error instanceof import_utils80.StorageNotSupportedError;
42080
42635
  }
42636
+ function originOf2(value) {
42637
+ try {
42638
+ const origin = new URL(value).origin;
42639
+ return origin === "null" ? void 0 : origin;
42640
+ } catch {
42641
+ return void 0;
42642
+ }
42643
+ }
42081
42644
  var import_jose, import_auth46, import_utils80, MIN_HS256_SECRET_BYTES, defaultNoAuthSecret, RESERVED_JWT_CLAIMS, LocalPrimaryAuth;
42082
42645
  var init_instance_local_primary_auth = __esm({
42083
42646
  "libs/sdk/src/auth/instances/instance.local-primary-auth.ts"() {
@@ -42133,7 +42696,7 @@ var init_instance_local_primary_auth = __esm({
42133
42696
  this.port = this.providers.getActiveScope().metadata.http?.port ?? 3001;
42134
42697
  this.host = (0, import_utils80.getEnv)("FRONTMCP_PUBLIC_HOST")?.trim() || "localhost";
42135
42698
  this.issuer = this.deriveIssuer(options);
42136
- this.issuerIsConfigured = this.configuredIssuer(options) !== void 0 || !!(0, import_utils80.getEnv)("FRONTMCP_PUBLIC_HOST")?.trim();
42699
+ this.publicHostPinned = !!(0, import_utils80.getEnv)("FRONTMCP_PUBLIC_HOST")?.trim();
42137
42700
  const jwtSecret = (0, import_utils80.getEnv)("JWT_SECRET");
42138
42701
  const hasJwtSecret = !!jwtSecret?.trim();
42139
42702
  if (hasJwtSecret && jwtSecret) {
@@ -42174,8 +42737,8 @@ var init_instance_local_primary_auth = __esm({
42174
42737
  port;
42175
42738
  /** The boot-time issuer: the configured one, else this server's listener address (see {@link issuerFor}). */
42176
42739
  issuer;
42177
- /** Whether the operator named the issuer (`issuer` / `local.issuer`, or its host with `FRONTMCP_PUBLIC_HOST`). */
42178
- issuerIsConfigured;
42740
+ /** Whether `FRONTMCP_PUBLIC_HOST` names the boot-time issuer's host, pinning it for every request. */
42741
+ publicHostPinned;
42179
42742
  keys = [];
42180
42743
  secret;
42181
42744
  logger;
@@ -42333,27 +42896,52 @@ var init_instance_local_primary_auth = __esm({
42333
42896
  return void 0;
42334
42897
  }
42335
42898
  /**
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:
42899
+ * The issuer this server names in answer to `request`: in its discovery
42900
+ * documents (`/.well-known/oauth-authorization-server`'s `issuer`, the
42901
+ * protected resource metadata's `authorization_servers`), on its
42902
+ * authorization responses (the RFC 9207 `iss`, errors included), and as the
42903
+ * `iss` of the tokens it issues; the tokens it accepts there must name it
42904
+ * (#269, and see {@link acceptedIssuersFor}). Every entry point asks this, so
42905
+ * none of them can disagree, on the Node server and under a Web fetch
42906
+ * handler alike (#629). In order:
42339
42907
  *
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.
42908
+ * 1. a configured issuer (`issuer` / `local.issuer`);
42909
+ * 2. with `FRONTMCP_PUBLIC_URL` pinned, that URL plus this scope's path;
42910
+ * 3. with `FRONTMCP_PUBLIC_HOST` set, the boot-time issuer it names the host
42911
+ * of (`http://<host>:<http.port><path>`);
42912
+ * 4. the request's own origin plus this scope's path;
42913
+ * 5. without a request, the boot-time issuer.
42349
42914
  */
42350
42915
  issuerFor(request) {
42351
- if (!request || this.issuerIsConfigured) return this.issuer;
42352
- if (isPublicUrlPinned()) return computeIssuer(request, this.scope.entryPath, this.scope.routeBase);
42916
+ const configured = this.configuredIssuer(this.options);
42917
+ if (configured !== void 0) return configured;
42918
+ const pinned = getPinnedPublicUrl();
42919
+ if (pinned !== void 0) {
42920
+ return request ? computeIssuer(request, this.scope.entryPath, this.scope.routeBase) : `${pinned}${this.scope.fullPath}`;
42921
+ }
42922
+ if (this.publicHostPinned || !request) return this.issuer;
42923
+ return computeIssuer(request, this.scope.entryPath, this.scope.routeBase);
42924
+ }
42925
+ /**
42926
+ * The issuers a token presented with `request` may name: {@link issuerFor}
42927
+ * the request. When the issuer follows the request (nothing pins it) and
42928
+ * local or remote mode lists `expectedAudience`, a token for any listed
42929
+ * address was issued under that address's issuer, so each listed address's
42930
+ * issuer is accepted too, and so is the boot-time issuer that 1.8.4 named on
42931
+ * those tokens. The token's `aud` must still name a listed resource.
42932
+ */
42933
+ acceptedIssuersFor(request) {
42934
+ const issuer = this.issuerFor(request);
42353
42935
  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);
42936
+ const followsRequest = !!request && this.configuredIssuer(options) === void 0 && getPinnedPublicUrl() === void 0 && !this.publicHostPinned;
42937
+ if (!followsRequest || !(0, import_auth12.isOrchestratedMode)(options) || options.expectedAudience === void 0) return issuer;
42938
+ const listed = Array.isArray(options.expectedAudience) ? options.expectedAudience : [options.expectedAudience];
42939
+ const accepted = /* @__PURE__ */ new Set([issuer, this.issuer]);
42940
+ for (const audience of listed) {
42941
+ const origin = originOf2(audience);
42942
+ if (origin) accepted.add(`${origin}${this.scope.fullPath}`);
42943
+ }
42944
+ return [...accepted];
42357
42945
  }
42358
42946
  /**
42359
42947
  * Read the `tokenStorage` selection off the auth options. Only
@@ -42604,10 +43192,10 @@ var init_instance_local_primary_auth = __esm({
42604
43192
  *
42605
43193
  * The secret alone doesn't make a token this server's: every server started
42606
43194
  * 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
43195
+ * - name THIS instance as its issuer (`iss`): `expectedIssuer` (one issuer or
43196
+ * several), which callers take from {@link acceptedIssuersFor} the request,
43197
+ * matching what the token endpoint names when it signs ({@link issuerFor});
43198
+ * the boot-time issuer when not given. Never the token's own claim; and
42611
43199
  * - when `expectedAudience` is given, be issued for that protected resource,
42612
43200
  * or one of those resources (`aud`, compared as RFC 8707 resource URIs).
42613
43201
  * `session:verify` passes the request's resource URL, or the configured
@@ -42620,7 +43208,7 @@ var init_instance_local_primary_auth = __esm({
42620
43208
  try {
42621
43209
  const { payload, protectedHeader } = await (0, import_jose.jwtVerify)(token, this.secret, {
42622
43210
  algorithms: ["HS256"],
42623
- issuer: expectedIssuer ?? this.issuer,
43211
+ issuer: typeof expectedIssuer === "string" ? expectedIssuer : expectedIssuer ? [...expectedIssuer] : this.issuer,
42624
43212
  requiredClaims: ["exp"]
42625
43213
  });
42626
43214
  if (expectedAudience !== void 0 && !audienceMatchesResource(payload.aud, expectedAudience)) {
@@ -43576,6 +44164,111 @@ var init_auth_registry = __esm({
43576
44164
  }
43577
44165
  });
43578
44166
 
44167
+ // libs/sdk/src/server/custom-routes.helper.ts
44168
+ function computeReservedPaths(entryPath, routeBase) {
44169
+ const prefix = normalizeEntryPrefix(entryPath);
44170
+ const base = normalizeScopeBase(routeBase);
44171
+ const mcpBase = `${prefix}${base}`;
44172
+ const exact = /* @__PURE__ */ new Set([
44173
+ mcpBase === "" ? "/" : mcpBase,
44174
+ `${mcpBase}/sse`,
44175
+ `${mcpBase}/message`,
44176
+ "/health",
44177
+ "/metrics"
44178
+ ]);
44179
+ return { exact, prefixes: ["/oauth", "/.well-known"] };
44180
+ }
44181
+ function normalizeForCompare(path3) {
44182
+ if (path3.length > 1 && path3.endsWith("/")) return path3.slice(0, -1);
44183
+ return path3;
44184
+ }
44185
+ function assertNotReserved(method, path3, reserved) {
44186
+ const normalized = normalizeForCompare(path3);
44187
+ if (reserved.exact.has(normalized)) {
44188
+ throw new ReservedRouteCollisionError(method, path3, normalized);
44189
+ }
44190
+ for (const prefix of reserved.prefixes) {
44191
+ if (normalized === prefix || normalized.startsWith(`${prefix}/`)) {
44192
+ throw new ReservedRouteCollisionError(method, path3, prefix);
44193
+ }
44194
+ }
44195
+ }
44196
+ function wrapWithAuth(handler, verifySession, logger) {
44197
+ return async (req, res, next) => {
44198
+ let result;
44199
+ try {
44200
+ result = await verifySession(req);
44201
+ } catch (error) {
44202
+ if (error instanceof FlowControl && error.type === "respond") {
44203
+ result = error.output;
44204
+ } else {
44205
+ throw error;
44206
+ }
44207
+ }
44208
+ if (!result || result.kind === "unauthorized") {
44209
+ logger.verbose("custom-route auth: unauthorized");
44210
+ return writeHttpResponse(
44211
+ res,
44212
+ httpRespond.unauthorized({
44213
+ headers: result?.kind === "unauthorized" ? { "WWW-Authenticate": result.prmMetadataHeader } : void 0
44214
+ })
44215
+ );
44216
+ }
44217
+ if (result.kind === "forbidden") {
44218
+ logger.verbose("custom-route auth: forbidden (insufficient scope)");
44219
+ return writeHttpResponse(
44220
+ res,
44221
+ httpRespond.forbidden({
44222
+ headers: { "WWW-Authenticate": result.prmMetadataHeader }
44223
+ })
44224
+ );
44225
+ }
44226
+ req.authSession = result.authorization;
44227
+ req[ServerRequestTokens.auth] = result.authorization;
44228
+ return handler(req, res, next);
44229
+ };
44230
+ }
44231
+ function wrapWithIpFilter(handler, checkClientIp) {
44232
+ return async (req, res, next) => {
44233
+ const rejection = await checkClientIp(req, res);
44234
+ if (rejection) return writeHttpResponse(res, rejection);
44235
+ return handler(req, res, next);
44236
+ };
44237
+ }
44238
+ function registerCustomHttpRoutes(args) {
44239
+ const { routes, server, verifySession, checkClientIp, entryPath, routeBase, logger } = args;
44240
+ if (!routes || routes.length === 0) return;
44241
+ const reserved = computeReservedPaths(entryPath, routeBase);
44242
+ for (const route of routes) {
44243
+ assertNotReserved(route.method, route.path, reserved);
44244
+ const handler = route.auth === true ? wrapWithAuth(route.handler, verifySession, logger) : route.handler;
44245
+ server.registerRoute(route.method, route.path, wrapWithIpFilter(handler, checkClientIp));
44246
+ logger.info(`Registered custom http route: ${route.method} ${route.path}${route.auth ? " (auth)" : ""}`);
44247
+ }
44248
+ }
44249
+ var ReservedRouteCollisionError;
44250
+ var init_custom_routes_helper = __esm({
44251
+ "libs/sdk/src/server/custom-routes.helper.ts"() {
44252
+ "use strict";
44253
+ init_common();
44254
+ init_server_validation();
44255
+ ReservedRouteCollisionError = class extends Error {
44256
+ constructor(method, path3, reserved) {
44257
+ super(
44258
+ `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.`
44259
+ );
44260
+ this.method = method;
44261
+ this.path = path3;
44262
+ this.reserved = reserved;
44263
+ this.name = "ReservedRouteCollisionError";
44264
+ }
44265
+ method;
44266
+ path;
44267
+ reserved;
44268
+ };
44269
+ }
44270
+ });
44271
+
43579
44272
  // libs/sdk/src/channel/channel-notification.service.ts
43580
44273
  var CHANNEL_NOTIFICATION_METHOD, ChannelNotificationService;
43581
44274
  var init_channel_notification_service = __esm({
@@ -44546,6 +45239,12 @@ function wireJobCompletionSource(channel2, sourceConfig, jobEmitterSubscribe, lo
44546
45239
  return;
44547
45240
  }
44548
45241
  }
45242
+ if (!event.sessionId) {
45243
+ logger.verbose(
45244
+ `Skipping job completion for channel "${channel2.name}": no sessionId (prevents cross-session leak)`
45245
+ );
45246
+ return;
45247
+ }
44549
45248
  logger.verbose(`Job completion event for channel "${channel2.name}": job=${event.jobName}, status=${event.status}`);
44550
45249
  channel2.handleEvent(event, event.sessionId).catch((err) => {
44551
45250
  logger.error(`Failed to handle job completion event in channel "${channel2.name}"`, { error: err });
@@ -44558,6 +45257,41 @@ var init_job_completion_source = __esm({
44558
45257
  }
44559
45258
  });
44560
45259
 
45260
+ // libs/sdk/src/channel/sources/webhook.source.ts
45261
+ function createWebhookMiddleware(channel2, sourceConfig, logger) {
45262
+ return async (req, res) => {
45263
+ const rawHeaders = req.headers ?? {};
45264
+ const normalizedHeaders = {};
45265
+ for (const [key, value] of Object.entries(rawHeaders)) {
45266
+ normalizedHeaders[key.toLowerCase()] = value;
45267
+ }
45268
+ const payload = {
45269
+ body: req.body,
45270
+ headers: normalizedHeaders,
45271
+ method: req.method ?? "POST",
45272
+ query: req.query
45273
+ };
45274
+ logger.verbose(`Webhook received for channel "${channel2.name}" at ${sourceConfig.path}`);
45275
+ try {
45276
+ const notification = await runOnSurface("http-trigger", () => channel2.handleEvent(payload));
45277
+ if (notification) {
45278
+ res.status(200).json({ ok: true, channel: channel2.name });
45279
+ } else {
45280
+ res.status(200).json({ ok: true, channel: channel2.name, skipped: true });
45281
+ }
45282
+ } catch (err) {
45283
+ logger.error(`Webhook handler failed for channel "${channel2.name}"`, { error: err });
45284
+ res.status(500).json({ ok: false, error: "Internal channel error" });
45285
+ }
45286
+ };
45287
+ }
45288
+ var init_webhook_source = __esm({
45289
+ "libs/sdk/src/channel/sources/webhook.source.ts"() {
45290
+ "use strict";
45291
+ init_call_surface();
45292
+ }
45293
+ });
45294
+
44561
45295
  // libs/sdk/src/channel/channel-scope.helper.ts
44562
45296
  async function registerChannelCapabilities(args) {
44563
45297
  const {
@@ -44568,11 +45302,15 @@ async function registerChannelCapabilities(args) {
44568
45302
  notificationService,
44569
45303
  flowRegistry,
44570
45304
  toolRegistry,
44571
- agentEmitterSubscribe,
44572
- jobEmitterSubscribe,
45305
+ http: http2,
44573
45306
  logger
44574
45307
  } = args;
45308
+ const completions = completionEventsOf(providers.getActiveScope());
45309
+ const agentEmitterSubscribe = args.agentEmitterSubscribe ?? ((cb) => completions.agents.subscribe(cb));
45310
+ const jobEmitterSubscribe = args.jobEmitterSubscribe ?? ((cb) => completions.jobs.subscribe(cb));
44575
45311
  const unsubscribers = [];
45312
+ const reservedPaths = http2 ? computeReservedPaths(http2.entryPath, http2.routeBase) : void 0;
45313
+ const webhookPaths = /* @__PURE__ */ new Map();
44576
45314
  const channelRegistry = new ChannelRegistry(providers, channelsList, owner);
44577
45315
  await channelRegistry.ready;
44578
45316
  const channelNotificationService = new ChannelNotificationService(
@@ -44588,31 +45326,23 @@ async function registerChannelCapabilities(args) {
44588
45326
  const sourceType = instance.source.type;
44589
45327
  switch (sourceType) {
44590
45328
  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
- }
45329
+ const unsub = wireAgentCompletionSource(
45330
+ instance,
45331
+ instance.metadata.source,
45332
+ agentEmitterSubscribe,
45333
+ logger
45334
+ );
45335
+ unsubscribers.push(unsub);
44602
45336
  break;
44603
45337
  }
44604
45338
  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
- }
45339
+ const unsub = wireJobCompletionSource(
45340
+ instance,
45341
+ instance.metadata.source,
45342
+ jobEmitterSubscribe,
45343
+ logger
45344
+ );
45345
+ unsubscribers.push(unsub);
44616
45346
  break;
44617
45347
  }
44618
45348
  case "app-event": {
@@ -44621,7 +45351,25 @@ async function registerChannelCapabilities(args) {
44621
45351
  unsubscribers.push(unsub);
44622
45352
  break;
44623
45353
  }
44624
- case "webhook":
45354
+ case "webhook": {
45355
+ const source = instance.metadata.source;
45356
+ if (!http2 || !reservedPaths) {
45357
+ logger.warn(`Channel "${instance.name}" has a webhook source but no HTTP server to serve ${source.path}`);
45358
+ break;
45359
+ }
45360
+ assertNotReserved("POST", source.path, reservedPaths);
45361
+ const claimedBy = webhookPaths.get(source.path);
45362
+ if (claimedBy) {
45363
+ throw new Error(
45364
+ `Channels "${claimedBy}" and "${instance.name}" both declare the webhook path ${source.path}. Give each webhook channel its own path.`
45365
+ );
45366
+ }
45367
+ webhookPaths.set(source.path, instance.name);
45368
+ const handler = createWebhookMiddleware(instance, source, logger);
45369
+ await http2.server.registerRoute("POST", source.path, wrapWithIpFilter(handler, http2.checkClientIp));
45370
+ logger.info(`Registered webhook route for channel "${instance.name}": POST ${source.path}`);
45371
+ break;
45372
+ }
44625
45373
  case "manual":
44626
45374
  break;
44627
45375
  case "service":
@@ -44704,6 +45452,7 @@ async function registerChannelCapabilities(args) {
44704
45452
  var init_channel_scope_helper = __esm({
44705
45453
  "libs/sdk/src/channel/channel-scope.helper.ts"() {
44706
45454
  "use strict";
45455
+ init_custom_routes_helper();
44707
45456
  init_tool_instance();
44708
45457
  init_tool_utils();
44709
45458
  init_channel_notification_service();
@@ -44713,7 +45462,9 @@ var init_channel_scope_helper = __esm({
44713
45462
  init_channel_reply_tool();
44714
45463
  init_agent_completion_source();
44715
45464
  init_app_event_source();
45465
+ init_completion_events();
44716
45466
  init_job_completion_source();
45467
+ init_webhook_source();
44717
45468
  }
44718
45469
  });
44719
45470
 
@@ -47780,10 +48531,11 @@ var init_workflow_step_executor = __esm({
47780
48531
  });
47781
48532
 
47782
48533
  // libs/sdk/src/workflow/engine/workflow.engine.ts
47783
- var WorkflowEngine;
48534
+ var import_utils86, WorkflowEngine;
47784
48535
  var init_workflow_engine = __esm({
47785
48536
  "libs/sdk/src/workflow/engine/workflow.engine.ts"() {
47786
48537
  "use strict";
48538
+ import_utils86 = require("@frontmcp/utils");
47787
48539
  init_workflow_errors();
47788
48540
  init_workflow_step_executor();
47789
48541
  WorkflowEngine = class {
@@ -47849,7 +48601,7 @@ var init_workflow_engine = __esm({
47849
48601
  }
47850
48602
  continue;
47851
48603
  }
47852
- const batch = ready.slice(0, this.maxConcurrency);
48604
+ const batch = ready.slice(0, (0, import_utils86.getAsyncContextMode)() === "serialized" ? 1 : this.maxConcurrency);
47853
48605
  await Promise.allSettled(
47854
48606
  batch.map(async (step) => {
47855
48607
  if (step.condition) {
@@ -47956,11 +48708,11 @@ var init_workflow_engine = __esm({
47956
48708
  });
47957
48709
 
47958
48710
  // libs/sdk/src/job/execution/job-execution.manager.ts
47959
- var import_utils86, JobExecutionManager;
48711
+ var import_utils87, JobExecutionManager;
47960
48712
  var init_job_execution_manager = __esm({
47961
48713
  "libs/sdk/src/job/execution/job-execution.manager.ts"() {
47962
48714
  "use strict";
47963
- import_utils86 = require("@frontmcp/utils");
48715
+ import_utils87 = require("@frontmcp/utils");
47964
48716
  init_principal_utils();
47965
48717
  init_call_surface();
47966
48718
  init_errors();
@@ -47980,7 +48732,7 @@ var init_job_execution_manager = __esm({
47980
48732
  */
47981
48733
  async executeJob(job, input, opts = {}) {
47982
48734
  await this.assertMayExecute(job.metadata.permissions, job.name, opts);
47983
- const runId = (0, import_utils86.randomUUID)();
48735
+ const runId = (0, import_utils87.randomUUID)();
47984
48736
  const retryConfig = job.metadata.retry ?? {};
47985
48737
  const maxAttempts = retryConfig.maxAttempts ?? 1;
47986
48738
  const runRecord = {
@@ -47999,7 +48751,7 @@ var init_job_execution_manager = __esm({
47999
48751
  };
48000
48752
  await this.stateStore.createRun(runRecord);
48001
48753
  if (opts.background) {
48002
- (0, import_utils86.runRequestExclusive)(() => this.executeJobBackground(job, input, runId, opts)).catch(async (err) => {
48754
+ (0, import_utils87.runRequestExclusive)(() => this.executeJobBackground(job, input, runId, opts)).catch(async (err) => {
48003
48755
  this.logger.error(`Background job execution failed: ${err}`);
48004
48756
  try {
48005
48757
  await this.updateState(runId, {
@@ -48020,7 +48772,7 @@ var init_job_execution_manager = __esm({
48020
48772
  */
48021
48773
  async executeWorkflow(workflow, jobRegistry, opts = {}) {
48022
48774
  await this.assertMayExecute(workflow.metadata.permissions, workflow.name, opts);
48023
- const runId = (0, import_utils86.randomUUID)();
48775
+ const runId = (0, import_utils87.randomUUID)();
48024
48776
  const runRecord = {
48025
48777
  runId,
48026
48778
  jobId: workflow.metadata.id ?? workflow.name,
@@ -48039,7 +48791,7 @@ var init_job_execution_manager = __esm({
48039
48791
  };
48040
48792
  await this.stateStore.createRun(runRecord);
48041
48793
  if (opts.background) {
48042
- (0, import_utils86.runRequestExclusive)(() => this.executeWorkflowBackground(workflow, jobRegistry, runId, opts)).catch(
48794
+ (0, import_utils87.runRequestExclusive)(() => this.executeWorkflowBackground(workflow, jobRegistry, runId, opts)).catch(
48043
48795
  async (err) => {
48044
48796
  this.logger.error(`Background workflow execution failed: ${err}`);
48045
48797
  try {
@@ -49447,7 +50199,11 @@ async function registerJobCapabilities(args) {
49447
50199
  for (const dynWf of dynamicWorkflows) {
49448
50200
  workflowRegistry.registerDynamic(dynWf);
49449
50201
  }
49450
- const executionManager = new JobExecutionManager(stateStore, logger, notifyFn);
50202
+ const completions = completionEventsOf(providers.getActiveScope());
50203
+ const executionManager = new JobExecutionManager(stateStore, logger, async (data) => {
50204
+ await notifyFn?.(data);
50205
+ await publishJobCompletion(data, stateStore, completions);
50206
+ });
49451
50207
  const managementTools = [
49452
50208
  ListJobsTool,
49453
50209
  ExecuteJobTool,
@@ -49476,9 +50232,27 @@ async function registerJobCapabilities(args) {
49476
50232
  managementTools
49477
50233
  };
49478
50234
  }
50235
+ async function publishJobCompletion(data, stateStore, completions) {
50236
+ const { type, state, runId } = data;
50237
+ if (type !== "job:status" && type !== "workflow:status") return;
50238
+ if (state !== "completed" && state !== "failed" || typeof runId !== "string") return;
50239
+ const run = await stateStore.getRun(runId);
50240
+ if (!run) return;
50241
+ const workflowName = type === "workflow:status" ? run.workflowName : void 0;
50242
+ completions.jobs.emit({
50243
+ jobName: workflowName ?? run.jobName,
50244
+ jobId: run.jobId,
50245
+ status: state === "completed" ? "success" : "error",
50246
+ ...run.completedAt !== void 0 ? { durationMs: run.completedAt - run.startedAt } : {},
50247
+ ...state === "completed" ? { output: completionOutputText(run.result) } : { error: run.error?.message },
50248
+ attempt: run.attempt,
50249
+ ...run.sessionId ? { sessionId: run.sessionId } : {}
50250
+ });
50251
+ }
49479
50252
  var init_job_scope_helper = __esm({
49480
50253
  "libs/sdk/src/job/job-scope.helper.ts"() {
49481
50254
  "use strict";
50255
+ init_completion_events();
49482
50256
  init_execute_workflow_tool();
49483
50257
  init_get_workflow_status_tool();
49484
50258
  init_list_workflows_tool();
@@ -49704,111 +50478,6 @@ var init_enforced_metadata_check = __esm({
49704
50478
  }
49705
50479
  });
49706
50480
 
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
50481
  // libs/sdk/src/skill/errors/tool-not-allowed.error.ts
49813
50482
  var ToolNotAllowedError, ToolApprovalRequiredError;
49814
50483
  var init_tool_not_allowed_error = __esm({
@@ -50048,15 +50717,15 @@ var init_skill_session_types = __esm({
50048
50717
  });
50049
50718
 
50050
50719
  // libs/sdk/src/skill/session/skill-session.manager.ts
50051
- var import_utils87, SkillSessionManager;
50720
+ var import_utils88, SkillSessionManager;
50052
50721
  var init_skill_session_manager = __esm({
50053
50722
  "libs/sdk/src/skill/session/skill-session.manager.ts"() {
50054
50723
  "use strict";
50055
- import_utils87 = require("@frontmcp/utils");
50724
+ import_utils88 = require("@frontmcp/utils");
50056
50725
  init_errors();
50057
50726
  init_skill_session_types();
50058
- SkillSessionManager = class extends import_utils87.EventEmitter {
50059
- storage = new import_utils87.AsyncLocalStorage();
50727
+ SkillSessionManager = class extends import_utils88.EventEmitter {
50728
+ storage = new import_utils88.AsyncLocalStorage();
50060
50729
  options;
50061
50730
  logger;
50062
50731
  store;
@@ -50983,7 +51652,7 @@ var init_load_skill_flow = __esm({
50983
51652
  }
50984
51653
  await assertSkillAuthorized(this.scope, entry, authInfo);
50985
51654
  }
50986
- loadResults.push({ loadResult: result });
51655
+ loadResults.push({ loadResult: skillToolsForCaller(result, this.scope.tools, ctx) });
50987
51656
  }
50988
51657
  this.state.set({ loadResults, warnings });
50989
51658
  this.logger.verbose("loadSkills:done", { loaded: loadResults.length, notFound: warnings.length });
@@ -51228,7 +51897,10 @@ async function authorizeSkillHttpRequest(scope, skillsConfig, request, logger) {
51228
51897
  if (mode === "inherit") {
51229
51898
  const authOptions = scope.auth?.options;
51230
51899
  if (!authOptions || (0, import_auth12.isPublicMode)(authOptions)) return { allowed: true, authInfo: {} };
51231
- const verified = await scope.runFlow("session:verify", { request });
51900
+ const verified = await scope.runFlow("session:verify", {
51901
+ request,
51902
+ sessionless: true
51903
+ });
51232
51904
  if (verified?.kind === "authorized") {
51233
51905
  return { allowed: true, authInfo: { ...authInfoFromAuthorization(verified.authorization) } };
51234
51906
  }
@@ -51252,11 +51924,11 @@ function createSkillHttpAuthValidator(skillsConfig, logger) {
51252
51924
  logger
51253
51925
  });
51254
51926
  }
51255
- var import_utils88, SkillHttpAuthValidator;
51927
+ var import_utils89, SkillHttpAuthValidator;
51256
51928
  var init_skill_http_auth = __esm({
51257
51929
  "libs/sdk/src/skill/auth/skill-http-auth.ts"() {
51258
51930
  "use strict";
51259
- import_utils88 = require("@frontmcp/utils");
51931
+ import_utils89 = require("@frontmcp/utils");
51260
51932
  init_common();
51261
51933
  SkillHttpAuthValidator = class {
51262
51934
  skillsConfig;
@@ -51338,7 +52010,7 @@ var init_skill_http_auth = __esm({
51338
52010
  const keyBytes = encoder5.encode(key);
51339
52011
  if (keyBytes.length === candidateBytes.length) {
51340
52012
  try {
51341
- if ((0, import_utils88.timingSafeEqual)(keyBytes, candidateBytes)) {
52013
+ if ((0, import_utils89.timingSafeEqual)(keyBytes, candidateBytes)) {
51342
52014
  found = true;
51343
52015
  }
51344
52016
  } catch {
@@ -52198,7 +52870,12 @@ var init_skills_api_flow = __esm({
52198
52870
  respondNotFound();
52199
52871
  return;
52200
52872
  }
52201
- const { skill, availableTools, missingTools, isComplete, warning } = loadResult;
52873
+ const { skill, availableTools, missingTools, isComplete, warning } = skillToolsForCaller(
52874
+ loadResult,
52875
+ toolRegistry,
52876
+ void 0,
52877
+ SKILLS_HTTP_SURFACE
52878
+ );
52202
52879
  const skillEntry = createSkillEntryResolver(skillRegistry)(skillId, skill.id);
52203
52880
  if (skillEntry) {
52204
52881
  if (!await isSkillServable(this.scope, skillEntry, void 0, SKILLS_HTTP_SURFACE)) {
@@ -52559,6 +53236,8 @@ var init_tool_interface = __esm({
52559
53236
  _toolNameInternal;
52560
53237
  /** @internal Tool input for fallback elicitation - set by CallToolFlow */
52561
53238
  _toolInputInternal;
53239
+ /** @internal JSON-RPC id of the tools/call request - set by CallToolFlow, for flows the call is handed on to */
53240
+ _jsonRpcRequestIdInternal;
52562
53241
  // ---- INPUT storages (backing fields)
52563
53242
  _rawInput;
52564
53243
  _input;
@@ -52591,6 +53270,10 @@ var init_tool_interface = __esm({
52591
53270
  this._progressToken = progressToken;
52592
53271
  this.signal = signal;
52593
53272
  }
53273
+ /** @internal The request's progress token, for flows the call is handed on to (the agent tool). */
53274
+ get _progressTokenInternal() {
53275
+ return this._progressToken;
53276
+ }
52594
53277
  /**
52595
53278
  * Like {@link ExecutionContextBase.fetch}, and also aborted with {@link signal}, so a
52596
53279
  * cancelled or timed-out call stops its outbound requests.
@@ -54636,10 +55319,10 @@ var init_primary_auth_provider_interface = __esm({
54636
55319
  * @param _expectedAudience The protected resource(s) the token must be for
54637
55320
  * (`aud` names at least one), when the caller is a
54638
55321
  * 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.
55322
+ * @param _expectedIssuer The issuer the token must name (or one of them):
55323
+ * the ones this instance accepts for the request
55324
+ * (`LocalPrimaryAuth.acceptedIssuersFor`). Default:
55325
+ * its boot-time issuer.
54643
55326
  */
54644
55327
  verifyGatewayToken(_token, _requestBaseUrl, _expectedAudience, _expectedIssuer) {
54645
55328
  return Promise.resolve({
@@ -54713,7 +55396,10 @@ function findSkillByPath(scope, skillPath) {
54713
55396
  const segments = skillPath.split("/").filter((s) => s.length > 0);
54714
55397
  if (segments.length === 0) return void 0;
54715
55398
  const lastSegment = segments[segments.length - 1];
54716
- return visible.find((s) => s.name === lastSegment && s.getSkillPath() === lastSegment);
55399
+ const byName = visible.find((s) => s.name === lastSegment && s.getSkillPath() === lastSegment);
55400
+ if (byName) return byName;
55401
+ if (segments.length !== 1) return void 0;
55402
+ return visible.find((s) => s.metadata.id === lastSegment && s.getSkillPath() === s.metadata.name);
54717
55403
  }
54718
55404
  async function findAndLoadSkillByPath(scope, skillPath) {
54719
55405
  const registry = scope.skills;
@@ -54785,15 +55471,15 @@ async function readSkillFileByPath(instance, filePath) {
54785
55471
  let candidatePath;
54786
55472
  for (const [subdir, configured] of candidateDirs) {
54787
55473
  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));
55474
+ const dir = configured ? (0, import_utils91.isAbsolute)(configured) ? configured : (0, import_utils91.pathResolve)(baseDir, configured) : (0, import_utils91.joinPath)(baseDir, subdir);
55475
+ candidatePath = (0, import_utils91.pathResolve)(dir, ...segments.slice(1));
54790
55476
  if (!isInside(candidatePath, dir)) {
54791
55477
  throw new PublicMcpError(`Invalid file path "${filePath}".`, "INVALID_PARAMS", 400);
54792
55478
  }
54793
55479
  break;
54794
55480
  }
54795
55481
  if (!candidatePath) {
54796
- candidatePath = (0, import_utils90.pathResolve)(baseDir, ...segments);
55482
+ candidatePath = (0, import_utils91.pathResolve)(baseDir, ...segments);
54797
55483
  if (!isInside(candidatePath, baseDir)) {
54798
55484
  throw new PublicMcpError(`Invalid file path "${filePath}".`, "INVALID_PARAMS", 400);
54799
55485
  }
@@ -54801,7 +55487,7 @@ async function readSkillFileByPath(instance, filePath) {
54801
55487
  const mimeType = guessMimeType(filePath);
54802
55488
  let buffer;
54803
55489
  try {
54804
- buffer = await (0, import_utils90.readFileBuffer)(candidatePath);
55490
+ buffer = await (0, import_utils91.readFileBuffer)(candidatePath);
54805
55491
  } catch (err) {
54806
55492
  if (isFileMissingError(err)) {
54807
55493
  throw new ResourceNotFoundError(`skill://${instance.getSkillPath()}/${filePath}`);
@@ -54844,7 +55530,7 @@ async function tryResolvedSubFile(instance, kind, tail) {
54844
55530
  const resolvedPath = resolveEntryPath2(instance, kind, absolutePath, filename);
54845
55531
  if (!resolvedPath) continue;
54846
55532
  try {
54847
- const buf = await (0, import_utils90.readFileBuffer)(resolvedPath);
55533
+ const buf = await (0, import_utils91.readFileBuffer)(resolvedPath);
54848
55534
  const text = (Buffer.isBuffer(buf) ? buf : Buffer.from(buf)).toString("utf-8");
54849
55535
  return parseSkillMdFrontmatter(text).body;
54850
55536
  } catch (err) {
@@ -54857,18 +55543,18 @@ async function tryResolvedSubFile(instance, kind, tail) {
54857
55543
  return void 0;
54858
55544
  }
54859
55545
  function resolveEntryPath2(instance, kind, absolutePath, filename) {
54860
- if (absolutePath && (0, import_utils90.isAbsolute)(absolutePath)) return absolutePath;
55546
+ if (absolutePath && (0, import_utils91.isAbsolute)(absolutePath)) return absolutePath;
54861
55547
  if (!filename) return void 0;
54862
55548
  const baseDir = instance.getBaseDir();
54863
55549
  const configured = instance.getResources()?.[kind];
54864
55550
  let dir;
54865
- if (configured && (0, import_utils90.isAbsolute)(configured)) {
55551
+ if (configured && (0, import_utils91.isAbsolute)(configured)) {
54866
55552
  dir = configured;
54867
55553
  } else if (baseDir) {
54868
- dir = configured ? (0, import_utils90.pathResolve)(baseDir, configured) : (0, import_utils90.joinPath)(baseDir, kind);
55554
+ dir = configured ? (0, import_utils91.pathResolve)(baseDir, configured) : (0, import_utils91.joinPath)(baseDir, kind);
54869
55555
  }
54870
55556
  if (!dir) return void 0;
54871
- const resolved = (0, import_utils90.pathResolve)(dir, filename);
55557
+ const resolved = (0, import_utils91.pathResolve)(dir, filename);
54872
55558
  if (!isInside(resolved, dir)) return void 0;
54873
55559
  return resolved;
54874
55560
  }
@@ -54900,11 +55586,11 @@ function isInside(child, parent) {
54900
55586
  const parentPrefix = normParent.endsWith("/") ? normParent : `${normParent}/`;
54901
55587
  return normChild === normParent || normChild.startsWith(parentPrefix);
54902
55588
  }
54903
- var import_utils90;
55589
+ var import_utils91;
54904
55590
  var init_sep_2640_resource_helpers = __esm({
54905
55591
  "libs/sdk/src/skill/sep-2640/sep-2640.resource-helpers.ts"() {
54906
55592
  "use strict";
54907
- import_utils90 = require("@frontmcp/utils");
55593
+ import_utils91 = require("@frontmcp/utils");
54908
55594
  init_errors();
54909
55595
  init_skill_filter_helper();
54910
55596
  init_skill_md_parser();
@@ -55131,17 +55817,17 @@ async function resolveLastModifiedForSkill(skill) {
55131
55817
  return void 0;
55132
55818
  }
55133
55819
  try {
55134
- const s = await (0, import_utils91.stat)(record.filePath);
55820
+ const s = await (0, import_utils92.stat)(record.filePath);
55135
55821
  return s.mtime instanceof Date ? s.mtime.toISOString() : void 0;
55136
55822
  } catch {
55137
55823
  return void 0;
55138
55824
  }
55139
55825
  }
55140
- var import_utils91;
55826
+ var import_utils92;
55141
55827
  var init_sep_2640_last_modified = __esm({
55142
55828
  "libs/sdk/src/skill/sep-2640/sep-2640.last-modified.ts"() {
55143
55829
  "use strict";
55144
- import_utils91 = require("@frontmcp/utils");
55830
+ import_utils92 = require("@frontmcp/utils");
55145
55831
  }
55146
55832
  });
55147
55833
 
@@ -55331,7 +56017,7 @@ function createDefaultSigner(mod, logger) {
55331
56017
  "[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
56018
  );
55333
56019
  }
55334
- const bytes = (0, import_utils92.randomBytes)(32);
56020
+ const bytes = (0, import_utils93.randomBytes)(32);
55335
56021
  logger.warn(
55336
56022
  "[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
56023
  );
@@ -55343,12 +56029,12 @@ function createDefaultStore(mod, logger) {
55343
56029
  );
55344
56030
  return new mod.MemoryAuditStore();
55345
56031
  }
55346
- var import_di39, import_utils92, injectedFactory;
56032
+ var import_di39, import_utils93, injectedFactory;
55347
56033
  var init_skill_audit_helper = __esm({
55348
56034
  "libs/sdk/src/skill/skill-audit.helper.ts"() {
55349
56035
  "use strict";
55350
56036
  import_di39 = require("@frontmcp/di");
55351
- import_utils92 = require("@frontmcp/utils");
56037
+ import_utils93 = require("@frontmcp/utils");
55352
56038
  }
55353
56039
  });
55354
56040
 
@@ -55988,11 +56674,11 @@ function createSqliteSessionStore(options, logger) {
55988
56674
  logger?.info("[SessionStoreFactory] Creating SQLite session store", { path: options.path });
55989
56675
  return new SqliteSessionStore(options);
55990
56676
  }
55991
- var import_utils93;
56677
+ var import_utils94;
55992
56678
  var init_session_store_factory = __esm({
55993
56679
  "libs/sdk/src/auth/session/session-store.factory.ts"() {
55994
56680
  "use strict";
55995
- import_utils93 = require("@frontmcp/utils");
56681
+ import_utils94 = require("@frontmcp/utils");
55996
56682
  init_common();
55997
56683
  }
55998
56684
  });
@@ -56067,14 +56753,14 @@ function toClientInfo(value) {
56067
56753
  const { name: name44, version } = value;
56068
56754
  return typeof name44 === "string" && typeof version === "string" ? { name: name44, version } : void 0;
56069
56755
  }
56070
- var import_auth55, import_lazy_zod102, import_protocol52, import_utils94, plan37, subscriptionParamsSchema, requestIdSchema, stateSchema37, name38, Stage38, encoder3, TRACE_META_KEYS, HandleMcp20260728Flow;
56756
+ var import_auth55, import_lazy_zod102, import_protocol52, import_utils95, plan37, subscriptionParamsSchema, requestIdSchema, stateSchema37, name38, Stage38, encoder3, TRACE_META_KEYS, HandleMcp20260728Flow;
56071
56757
  var init_handle_mcp_20260728_flow = __esm({
56072
56758
  "libs/sdk/src/transport/flows/handle.mcp-20260728.flow.ts"() {
56073
56759
  "use strict";
56074
56760
  import_auth55 = require("@frontmcp/auth");
56075
56761
  import_lazy_zod102 = require("@frontmcp/lazy-zod");
56076
56762
  import_protocol52 = require("@frontmcp/protocol");
56077
- import_utils94 = require("@frontmcp/utils");
56763
+ import_utils95 = require("@frontmcp/utils");
56078
56764
  init_common();
56079
56765
  init_context();
56080
56766
  init_notification();
@@ -56098,7 +56784,9 @@ var init_handle_mcp_20260728_flow = __esm({
56098
56784
  /** Negotiated protocol version for this request. */
56099
56785
  version: import_lazy_zod102.z.string().optional(),
56100
56786
  requestType: import_lazy_zod102.z.enum(["notification", "subscriptions", "message"]).optional(),
56101
- isAnonymous: import_lazy_zod102.z.boolean().default(true)
56787
+ isAnonymous: import_lazy_zod102.z.boolean().default(true),
56788
+ /** Whether the client declared this revision itself (not served under it by default). */
56789
+ clientDeclaredRevision: import_lazy_zod102.z.boolean().optional()
56102
56790
  });
56103
56791
  name38 = "handle:mcp-202607280728";
56104
56792
  ({ Stage: Stage38 } = FlowHooksOf(name38));
@@ -56145,6 +56833,7 @@ var init_handle_mcp_20260728_flow = __esm({
56145
56833
  return;
56146
56834
  }
56147
56835
  this.state.set("version", result.version);
56836
+ this.state.set("clientDeclaredRevision", strictHeaders);
56148
56837
  }
56149
56838
  /**
56150
56839
  * Resolve a tool's input JSON Schema so `x-mcp-header` annotations can be
@@ -56169,7 +56858,7 @@ var init_handle_mcp_20260728_flow = __esm({
56169
56858
  if (!context) return (fn) => fn();
56170
56859
  const storage = this.scope.providers.get(FrontMcpContextStorage);
56171
56860
  return async (fn) => {
56172
- await (0, import_utils94.runRequestExclusive)(() => storage.runWithContext(context, fn));
56861
+ await (0, import_utils95.runRequestExclusive)(() => storage.runWithContext(context, fn));
56173
56862
  };
56174
56863
  }
56175
56864
  findToolSchema(scope, toolName) {
@@ -56260,10 +56949,12 @@ var init_handle_mcp_20260728_flow = __esm({
56260
56949
  // authenticated caller.
56261
56950
  ...authInfoFromAuthorization(auth),
56262
56951
  // 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
56952
+ // off an id. It names the request only, the same per-request id as the request
56953
+ // context's `sessionId`; the request context does not vouch for it (#629).
56954
+ sessionId: auth.session?.id ?? this.tryGetContext()?.sessionId
56265
56955
  } : void 0,
56266
56956
  isAnonymous: this.state.required.isAnonymous,
56957
+ clientDeclaredRevision: this.state.clientDeclaredRevision !== false,
56267
56958
  composeInstructions: () => this.scope.metadata.instructions,
56268
56959
  notificationSink: sink,
56269
56960
  traceContext: extractTraceContext(meta)
@@ -56370,12 +57061,36 @@ function getQuerySessionId2(urlPath) {
56370
57061
  return void 0;
56371
57062
  }
56372
57063
  }
56373
- var import_lazy_zod103, import_utils95, plan38, stateSessionSchema, stateSchema38, name39, Stage39, HandleSseFlow;
57064
+ function resolveLegacySseSession(params) {
57065
+ const { rawHeader, requestUrl, authorizationSession, createSession } = params;
57066
+ const rawMcpSessionHeader = typeof rawHeader === "string" ? rawHeader : void 0;
57067
+ const mcpSessionHeader = validateMcpSessionHeader(rawMcpSessionHeader);
57068
+ const querySessionId = getQuerySessionId2(requestUrl);
57069
+ const validatedQuerySessionId = querySessionId ? validateMcpSessionHeader(querySessionId) : void 0;
57070
+ if (rawHeader !== void 0 && !mcpSessionHeader) {
57071
+ return { responded404: true, createdNew: false };
57072
+ }
57073
+ if (querySessionId !== void 0 && !validatedQuerySessionId) {
57074
+ return { responded404: true, createdNew: false };
57075
+ }
57076
+ const effectiveSessionId = mcpSessionHeader ?? validatedQuerySessionId;
57077
+ if (effectiveSessionId) {
57078
+ if (authorizationSession?.id === effectiveSessionId) {
57079
+ return { session: authorizationSession, createdNew: false, responded404: false };
57080
+ }
57081
+ return { responded404: true, createdNew: false };
57082
+ }
57083
+ if (authorizationSession) {
57084
+ return { session: authorizationSession, createdNew: false, responded404: false };
57085
+ }
57086
+ return { session: createSession(), createdNew: true, responded404: false };
57087
+ }
57088
+ var import_lazy_zod103, import_utils96, plan38, stateSessionSchema, stateSchema38, name39, Stage39, HandleSseFlow;
56374
57089
  var init_handle_sse_flow = __esm({
56375
57090
  "libs/sdk/src/transport/flows/handle.sse.flow.ts"() {
56376
57091
  "use strict";
56377
57092
  import_lazy_zod103 = require("@frontmcp/lazy-zod");
56378
- import_utils95 = require("@frontmcp/utils");
57093
+ import_utils96 = require("@frontmcp/utils");
56379
57094
  init_session_id_utils();
56380
57095
  init_common();
56381
57096
  init_errors();
@@ -56411,39 +57126,26 @@ var init_handle_sse_flow = __esm({
56411
57126
  const { request } = this.rawInput;
56412
57127
  const authorization = request[ServerRequestTokens.auth];
56413
57128
  const { token } = authorization;
56414
- const raw = request.headers?.["mcp-session-id"];
56415
- const rawMcpSessionHeader = typeof raw === "string" ? raw : void 0;
56416
- const mcpSessionHeader = validateMcpSessionHeader(rawMcpSessionHeader);
56417
57129
  const anyReq = request;
56418
- const querySessionId = getQuerySessionId2(anyReq.url ?? anyReq.path);
56419
- const validatedQuerySessionId = querySessionId ? validateMcpSessionHeader(querySessionId) : void 0;
56420
- if (raw !== void 0 && !mcpSessionHeader) {
56421
- this.respond(httpRespond.sessionNotFound("invalid session id"));
56422
- return;
56423
- }
56424
- if (querySessionId !== void 0 && !validatedQuerySessionId) {
57130
+ const resolution = resolveLegacySseSession({
57131
+ rawHeader: request.headers?.["mcp-session-id"],
57132
+ requestUrl: anyReq.url ?? anyReq.path,
57133
+ authorizationSession: authorization.session,
57134
+ createSession: () => {
57135
+ const query = request.query;
57136
+ const skillsOnlyMode = detectSkillsOnlyMode(query);
57137
+ return createSessionId("legacy-sse", token, {
57138
+ userAgent: request.headers?.["user-agent"],
57139
+ platformDetectionConfig: this.scope.metadata.transport?.platformDetection,
57140
+ skillsOnlyMode
57141
+ });
57142
+ }
57143
+ });
57144
+ if (resolution.responded404 || !resolution.session) {
56425
57145
  this.respond(httpRespond.sessionNotFound("invalid session id"));
56426
57146
  return;
56427
57147
  }
56428
- const effectiveSessionId = mcpSessionHeader ?? validatedQuerySessionId;
56429
- let session;
56430
- if (effectiveSessionId) {
56431
- if (authorization.session?.id === effectiveSessionId) {
56432
- session = authorization.session;
56433
- } else {
56434
- session = { id: effectiveSessionId };
56435
- }
56436
- } else if (authorization.session) {
56437
- session = authorization.session;
56438
- } else {
56439
- const query = request.query;
56440
- const skillsOnlyMode = detectSkillsOnlyMode(query);
56441
- session = createSessionId("legacy-sse", token, {
56442
- userAgent: request.headers?.["user-agent"],
56443
- platformDetectionConfig: this.scope.metadata.transport?.platformDetection,
56444
- skillsOnlyMode
56445
- });
56446
- }
57148
+ const session = resolution.session;
56447
57149
  this.state.set(stateSchema38.parse({ token, session }));
56448
57150
  }
56449
57151
  async router() {
@@ -56466,10 +57168,10 @@ var init_handle_sse_flow = __esm({
56466
57168
  const { request, response } = this.rawInput;
56467
57169
  const { token, session } = this.state.required;
56468
57170
  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)();
57171
+ if ((0, import_utils96.getRuntimeContext)().deployment === "distributed") {
57172
+ const nodeId = (0, import_utils96.getMachineId)();
56471
57173
  response.setHeader(DEFAULT_FRONTMCP_MACHINE_ID_HEADER, nodeId);
56472
- const cookie = (0, import_utils95.buildSetCookie)({ name: DEFAULT_FRONTMCP_NODE_COOKIE, value: nodeId }, request);
57174
+ const cookie = (0, import_utils96.buildSetCookie)({ name: DEFAULT_FRONTMCP_NODE_COOKIE, value: nodeId }, request);
56473
57175
  if (cookie) {
56474
57176
  const existing = response.getHeader("Set-Cookie");
56475
57177
  const existingArr = Array.isArray(existing) ? existing : existing ? [String(existing)] : [];
@@ -56491,12 +57193,12 @@ var init_handle_sse_flow = __esm({
56491
57193
  const { request, response } = this.rawInput;
56492
57194
  const { token, session } = this.state.required;
56493
57195
  const transport = await transportService.getTransporter("sse", token, session.id);
56494
- if (!transport && (0, import_utils95.getRuntimeContext)().deployment === "distributed") {
57196
+ if (!transport && (0, import_utils96.getRuntimeContext)().deployment === "distributed") {
56495
57197
  const storedSession = await transportService.getStoredSession("sse", token, session.id);
56496
57198
  if (storedSession) {
56497
57199
  const haManager = this.scope.haManager;
56498
57200
  const relay = haManager?.getRelay();
56499
- if (relay && storedSession.session.nodeId && storedSession.session.nodeId !== (0, import_utils95.getMachineId)()) {
57201
+ if (relay && storedSession.session.nodeId && storedSession.session.nodeId !== (0, import_utils96.getMachineId)()) {
56500
57202
  const isAlive2 = await haManager.isNodeAlive(storedSession.session.nodeId);
56501
57203
  if (isAlive2) {
56502
57204
  try {
@@ -56722,11 +57424,11 @@ var init_ext_apps_types = __esm({
56722
57424
  function createExtAppsMessageHandler(options) {
56723
57425
  return new ExtAppsMessageHandler(options);
56724
57426
  }
56725
- var import_utils96, ExtAppsMessageHandler, ExtAppsError, ExtAppsMethodNotFoundError, ExtAppsInvalidParamsError, ExtAppsNotSupportedError, ExtAppsToolNotFoundError;
57427
+ var import_utils97, ExtAppsMessageHandler, ExtAppsError, ExtAppsMethodNotFoundError, ExtAppsInvalidParamsError, ExtAppsNotSupportedError, ExtAppsToolNotFoundError;
56726
57428
  var init_ext_apps_handler = __esm({
56727
57429
  "libs/sdk/src/ext-apps/ext-apps.handler.ts"() {
56728
57430
  "use strict";
56729
- import_utils96 = require("@frontmcp/utils");
57431
+ import_utils97 = require("@frontmcp/utils");
56730
57432
  init_ext_apps_types();
56731
57433
  ExtAppsMessageHandler = class {
56732
57434
  context;
@@ -56770,7 +57472,7 @@ var init_ext_apps_handler = __esm({
56770
57472
  code: errorCode,
56771
57473
  message: errorMessage,
56772
57474
  // 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
57475
+ data: (0, import_utils97.isDevelopment)() && error instanceof Error ? { stack: error.stack } : void 0
56774
57476
  }
56775
57477
  };
56776
57478
  }
@@ -57056,7 +57758,7 @@ function resolveStreamableHttpSession(params) {
57056
57758
  if (authorizationSession?.id === mcpSessionHeader) {
57057
57759
  return { session: authorizationSession, createdNew: false, responded404: false };
57058
57760
  }
57059
- return { session: { id: mcpSessionHeader }, createdNew: false, responded404: false };
57761
+ return { responded404: true, createdNew: false };
57060
57762
  }
57061
57763
  if (authorizationSession) {
57062
57764
  return { session: authorizationSession, createdNew: false, responded404: false };
@@ -57132,13 +57834,13 @@ async function lookupStreamableHttpTransport(params) {
57132
57834
  }
57133
57835
  return { kind: "session-not-initialized", recreationError };
57134
57836
  }
57135
- var import_lazy_zod105, import_protocol54, import_utils97, plan40, stateSessionSchema2, stateSchema40, name41, Stage41, HandleStreamableHttpFlow;
57837
+ var import_lazy_zod105, import_protocol54, import_utils98, plan40, stateSessionSchema2, stateSchema40, name41, Stage41, HandleStreamableHttpFlow;
57136
57838
  var init_handle_streamable_http_flow = __esm({
57137
57839
  "libs/sdk/src/transport/flows/handle.streamable-http.flow.ts"() {
57138
57840
  "use strict";
57139
57841
  import_lazy_zod105 = require("@frontmcp/lazy-zod");
57140
57842
  import_protocol54 = require("@frontmcp/protocol");
57141
- import_utils97 = require("@frontmcp/utils");
57843
+ import_utils98 = require("@frontmcp/utils");
57142
57844
  init_session_id_utils();
57143
57845
  init_common();
57144
57846
  init_errors();
@@ -57204,7 +57906,7 @@ var init_handle_streamable_http_flow = __esm({
57204
57906
  });
57205
57907
  }
57206
57908
  if (sessionResolution.responded404 || !sessionResolution.session) {
57207
- logger.warn("parseInput: invalid mcp-session-id header");
57909
+ logger.warn("parseInput: mcp-session-id is not a session this server verified");
57208
57910
  this.respond(httpRespond.sessionNotFound("invalid session id"));
57209
57911
  return;
57210
57912
  }
@@ -57271,10 +57973,10 @@ var init_handle_streamable_http_flow = __esm({
57271
57973
  return;
57272
57974
  }
57273
57975
  }
57274
- if ((0, import_utils97.getRuntimeContext)().deployment === "distributed") {
57275
- const nodeId = (0, import_utils97.getMachineId)();
57976
+ if ((0, import_utils98.getRuntimeContext)().deployment === "distributed") {
57977
+ const nodeId = (0, import_utils98.getMachineId)();
57276
57978
  response.setHeader(DEFAULT_FRONTMCP_MACHINE_ID_HEADER, nodeId);
57277
- const cookie = (0, import_utils97.buildSetCookie)({ name: DEFAULT_FRONTMCP_NODE_COOKIE, value: nodeId }, request);
57979
+ const cookie = (0, import_utils98.buildSetCookie)({ name: DEFAULT_FRONTMCP_NODE_COOKIE, value: nodeId }, request);
57278
57980
  if (cookie) {
57279
57981
  const existing = response.getHeader("Set-Cookie");
57280
57982
  const existingArr = Array.isArray(existing) ? existing : existing ? [String(existing)] : [];
@@ -57593,21 +58295,21 @@ var init_handle_streamable_http_flow = __esm({
57593
58295
  });
57594
58296
 
57595
58297
  // libs/sdk/src/transport/transport.error.ts
57596
- var import_utils98, JSON_RPC2, rpcError, rpcRequest;
58298
+ var import_utils99, JSON_RPC2, rpcError, rpcRequest;
57597
58299
  var init_transport_error = __esm({
57598
58300
  "libs/sdk/src/transport/transport.error.ts"() {
57599
58301
  "use strict";
57600
- import_utils98 = require("@frontmcp/utils");
58302
+ import_utils99 = require("@frontmcp/utils");
57601
58303
  JSON_RPC2 = "2.0";
57602
58304
  rpcError = (message, requestId) => ({
57603
58305
  jsonrpc: JSON_RPC2,
57604
58306
  error: { code: -32e3, message },
57605
- id: requestId ?? (0, import_utils98.randomUUID)()
58307
+ id: requestId ?? (0, import_utils99.randomUUID)()
57606
58308
  // change it to request id + random
57607
58309
  });
57608
58310
  rpcRequest = (requestId, method, params) => ({
57609
58311
  jsonrpc: JSON_RPC2,
57610
- id: requestId ?? (0, import_utils98.randomUUID)(),
58312
+ id: requestId ?? (0, import_utils99.randomUUID)(),
57611
58313
  method,
57612
58314
  ...params !== void 0 && { params }
57613
58315
  });
@@ -57670,14 +58372,14 @@ var init_host_validation = __esm({
57670
58372
  });
57671
58373
 
57672
58374
  // 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;
58375
+ var import_content_type, import_raw_body, import_protocol55, import_utils100, MAXIMUM_MESSAGE_SIZE, SSEServerTransport;
57674
58376
  var init_base_sse_transport = __esm({
57675
58377
  "libs/sdk/src/transport/adapters/base-sse-transport.ts"() {
57676
58378
  "use strict";
57677
58379
  import_content_type = __toESM(require("content-type"));
57678
58380
  import_raw_body = __toESM(require("raw-body"));
57679
58381
  import_protocol55 = require("@frontmcp/protocol");
57680
- import_utils99 = require("@frontmcp/utils");
58382
+ import_utils100 = require("@frontmcp/utils");
57681
58383
  init_transport_errors();
57682
58384
  init_host_validation();
57683
58385
  MAXIMUM_MESSAGE_SIZE = "4mb";
@@ -57688,7 +58390,7 @@ var init_base_sse_transport = __esm({
57688
58390
  constructor(_endpoint, res, options) {
57689
58391
  this._endpoint = _endpoint;
57690
58392
  this.res = res;
57691
- this._sessionId = options?.sessionId ?? (0, import_utils99.randomUUID)();
58393
+ this._sessionId = options?.sessionId ?? (0, import_utils100.randomUUID)();
57692
58394
  this._options = options || { enableDnsRebindingProtection: false };
57693
58395
  }
57694
58396
  _endpoint;
@@ -59134,11 +59836,11 @@ var init_transport_remote = __esm({
59134
59836
  });
59135
59837
 
59136
59838
  // libs/sdk/src/transport/transport.registry.ts
59137
- var import_utils100, TransportService;
59839
+ var import_utils101, TransportService;
59138
59840
  var init_transport_registry = __esm({
59139
59841
  "libs/sdk/src/transport/transport.registry.ts"() {
59140
59842
  "use strict";
59141
- import_utils100 = require("@frontmcp/utils");
59843
+ import_utils101 = require("@frontmcp/utils");
59142
59844
  init_session_store_factory();
59143
59845
  init_transport_errors();
59144
59846
  init_handle_mcp_20260728_flow();
@@ -59430,7 +60132,7 @@ var init_transport_registry = __esm({
59430
60132
  protocol: storedSession.session.protocol,
59431
60133
  createdAt: storedSession.createdAt
59432
60134
  });
59433
- const currentNodeId = (0, import_utils100.getMachineId)();
60135
+ const currentNodeId = (0, import_utils101.getMachineId)();
59434
60136
  if (this.scope.haManager && storedSession.session.nodeId && storedSession.session.nodeId !== currentNodeId) {
59435
60137
  const sessionKey = `${this.transportKeyPrefix}${sessionId}`;
59436
60138
  const result = await this.scope.haManager.attemptTakeover(sessionKey, storedSession.session.nodeId);
@@ -59525,7 +60227,7 @@ var init_transport_registry = __esm({
59525
60227
  authorizationId: key.tokenHash,
59526
60228
  protocol: type === "sse" ? "sse" : "streamable-http",
59527
60229
  createdAt: Date.now(),
59528
- nodeId: (0, import_utils100.getMachineId)()
60230
+ nodeId: (0, import_utils101.getMachineId)()
59529
60231
  },
59530
60232
  authorizationId: key.tokenHash,
59531
60233
  createdAt: Date.now(),
@@ -59671,7 +60373,7 @@ var init_transport_registry = __esm({
59671
60373
  }
59672
60374
  /* --------------------------------- internals -------------------------------- */
59673
60375
  sha256(value) {
59674
- return (0, import_utils100.sha256Hex)(value);
60376
+ return (0, import_utils101.sha256Hex)(value);
59675
60377
  }
59676
60378
  /**
59677
60379
  * Create a history key from components.
@@ -59855,9 +60557,9 @@ function persistentSessionCallerKey(authorization) {
59855
60557
  const sub = authorization?.user?.sub;
59856
60558
  if (!(0, import_auth56.isAnonymousSubject)(sub)) {
59857
60559
  const iss = authorization?.user?.iss;
59858
- return `sub:${(0, import_utils101.sha256Hex)(JSON.stringify([typeof iss === "string" ? iss : "", sub]))}`;
60560
+ return `sub:${(0, import_utils102.sha256Hex)(JSON.stringify([typeof iss === "string" ? iss : "", sub]))}`;
59859
60561
  }
59860
- if (authorization?.token) return `token:${(0, import_utils101.sha256Hex)(authorization.token)}`;
60562
+ if (authorization?.token) return `token:${(0, import_utils102.sha256Hex)(authorization.token)}`;
59861
60563
  return null;
59862
60564
  }
59863
60565
  function claimPersistentSession(session, callerKey, store) {
@@ -59870,12 +60572,12 @@ function claimPersistentSession(session, callerKey, store) {
59870
60572
  }
59871
60573
  return owners.get(session) === callerKey ? "owner" : "refused";
59872
60574
  }
59873
- var import_auth56, import_utils101, owners;
60575
+ var import_auth56, import_utils102, owners;
59874
60576
  var init_persistent_session_owner = __esm({
59875
60577
  "libs/sdk/src/transport/persistent-session-owner.ts"() {
59876
60578
  "use strict";
59877
60579
  import_auth56 = require("@frontmcp/auth");
59878
- import_utils101 = require("@frontmcp/utils");
60580
+ import_utils102 = require("@frontmcp/utils");
59879
60581
  owners = /* @__PURE__ */ new WeakMap();
59880
60582
  }
59881
60583
  });
@@ -59912,7 +60614,7 @@ async function runWebStandardMcp(scope, request, options) {
59912
60614
  if (options.persistent) {
59913
60615
  return options.persistent.transport.handleRequest(request, { authInfo: options.authInfo });
59914
60616
  }
59915
- const sessionId = `web:${(0, import_utils102.randomUUID)()}`;
60617
+ const sessionId = `web:${(0, import_utils103.randomUUID)()}`;
59916
60618
  const accept = request.headers.get("accept") ?? "";
59917
60619
  const wantsStream = request.method === "GET" || options.sse && accept.includes("text/event-stream");
59918
60620
  const { mcpServer, transport } = await wireServer(scope, options.serverOptions, sessionId, void 0, !wantsStream);
@@ -59935,23 +60637,23 @@ async function runWebStandardMcp(scope, request, options) {
59935
60637
  }
59936
60638
  return response;
59937
60639
  }
59938
- var import_utils102;
60640
+ var import_utils103;
59939
60641
  var init_web_standard_mcp = __esm({
59940
60642
  "libs/sdk/src/transport/web-standard-mcp.ts"() {
59941
60643
  "use strict";
59942
- import_utils102 = require("@frontmcp/utils");
60644
+ import_utils103 = require("@frontmcp/utils");
59943
60645
  init_build_scoped_server_options();
59944
60646
  }
59945
60647
  });
59946
60648
 
59947
60649
  // libs/sdk/src/scope/flows/http.request.flow.ts
59948
- var import_auth57, import_lazy_zod108, import_utils103, plan42, httpRequestStateSchema, name43, Stage43, HttpRequestFlow;
60650
+ var import_auth57, import_lazy_zod108, import_utils104, plan42, httpRequestStateSchema, name43, Stage43, HttpRequestFlow;
59949
60651
  var init_http_request_flow = __esm({
59950
60652
  "libs/sdk/src/scope/flows/http.request.flow.ts"() {
59951
60653
  "use strict";
59952
60654
  import_auth57 = require("@frontmcp/auth");
59953
60655
  import_lazy_zod108 = require("@frontmcp/lazy-zod");
59954
- import_utils103 = require("@frontmcp/utils");
60656
+ import_utils104 = require("@frontmcp/utils");
59955
60657
  init_session_verify_flow();
59956
60658
  init_common();
59957
60659
  init_schema7();
@@ -60030,7 +60732,7 @@ var init_http_request_flow = __esm({
60030
60732
  const { request } = this.rawInput;
60031
60733
  this.requestStartTime = Date.now();
60032
60734
  const ctx = this.tryGetContext();
60033
- this.requestId = ctx?.requestId ?? `req-${(0, import_utils103.randomUUID)()}`;
60735
+ this.requestId = ctx?.requestId ?? `req-${(0, import_utils104.randomUUID)()}`;
60034
60736
  const headers = request.headers ?? {};
60035
60737
  const body = request.body;
60036
60738
  const userAgent = headers["user-agent"];
@@ -60102,7 +60804,10 @@ var init_http_request_flow = __esm({
60102
60804
  const { request } = this.rawInput;
60103
60805
  this.logger.verbose(`[${this.requestId}] checkAuthorization: verifying session`);
60104
60806
  try {
60105
- const result = await this.scope.runFlow("session:verify", { request });
60807
+ const result = await this.scope.runFlow("session:verify", {
60808
+ request,
60809
+ sessionless: this.servesProtocol20260728()
60810
+ });
60106
60811
  if (!result) {
60107
60812
  this.logger.error(`[${this.requestId}] failed to verify session`);
60108
60813
  throw new SessionVerificationFailedError();
@@ -60712,13 +61417,13 @@ var init_http_request_flow = __esm({
60712
61417
  });
60713
61418
 
60714
61419
  // libs/sdk/src/scope/scope.instance.ts
60715
- var import_reflect_metadata25, import_guard8, import_utils104, SCOPE_NON_DISPATCHABLE_FLOWS, Scope;
61420
+ var import_reflect_metadata25, import_guard8, import_utils105, SCOPE_NON_DISPATCHABLE_FLOWS, Scope;
60716
61421
  var init_scope_instance = __esm({
60717
61422
  "libs/sdk/src/scope/scope.instance.ts"() {
60718
61423
  "use strict";
60719
61424
  import_reflect_metadata25 = require("reflect-metadata");
60720
61425
  import_guard8 = require("@frontmcp/guard");
60721
- import_utils104 = require("@frontmcp/utils");
61426
+ import_utils105 = require("@frontmcp/utils");
60722
61427
  init_agent_registry();
60723
61428
  init_call_agent_flow();
60724
61429
  init_app_registry();
@@ -60761,6 +61466,7 @@ var init_scope_instance = __esm({
60761
61466
  init_ui();
60762
61467
  init_bus();
60763
61468
  init_event_stores();
61469
+ init_request_state();
60764
61470
  init_transport_registry();
60765
61471
  init_http_ip_filter_flow();
60766
61472
  init_http_request_flow();
@@ -60851,7 +61557,7 @@ var init_scope_instance = __esm({
60851
61557
  await this.scopeProviders.ready;
60852
61558
  const scopeRef = { kind: "scope", id: this.id, ref: _Scope };
60853
61559
  const scopeProviders = this.scopeProviders;
60854
- const perf = (0, import_utils104.getEnvFlag)("FRONTMCP_PERF");
61560
+ const perf = (0, import_utils105.getEnvFlag)("FRONTMCP_PERF");
60855
61561
  const t0 = perf ? performance.now() : 0;
60856
61562
  const mark = perf ? (label) => this.logger.info(`[PERF] ${label}: ${(performance.now() - t0).toFixed(1)}ms`) : () => {
60857
61563
  };
@@ -60861,13 +61567,13 @@ var init_scope_instance = __esm({
60861
61567
  this.scopeApps = new AppRegistry(this.scopeProviders, this.metadata.apps, scopeRef);
60862
61568
  this.logger.info(`Initializing ${this.metadata.apps.length} app(s)...`);
60863
61569
  const transportConfig = this.metadata.transport;
60864
- const isDistributed = (0, import_utils104.getRuntimeContext)().deployment === "distributed";
61570
+ const isDistributed = (0, import_utils105.getRuntimeContext)().deployment === "distributed";
60865
61571
  if (isDistributed && this.metadata.redis && !this.cliMode) {
60866
61572
  try {
60867
61573
  const haRedis = this.metadata.redis;
60868
61574
  this.haManager = HaManager.create({
60869
61575
  redis: haRedis,
60870
- nodeId: (0, import_utils104.getMachineId)(),
61576
+ nodeId: (0, import_utils105.getMachineId)(),
60871
61577
  logger: this.logger
60872
61578
  });
60873
61579
  await this.haManager.start();
@@ -60878,7 +61584,7 @@ var init_scope_instance = __esm({
60878
61584
  }
60879
61585
  let transportBus;
60880
61586
  if (this.haManager && isDistributed && this.metadata.redis) {
60881
- transportBus = new RedisTransportBus(this.metadata.redis, (0, import_utils104.getMachineId)(), {
61587
+ transportBus = new RedisTransportBus(this.metadata.redis, (0, import_utils105.getMachineId)(), {
60882
61588
  logger: this.logger
60883
61589
  });
60884
61590
  this.logger.info("[HA] Transport bus created for distributed session routing");
@@ -60889,6 +61595,7 @@ var init_scope_instance = __esm({
60889
61595
  effectivePersistence = { sqlite: resolvedTopLevelSqlite };
60890
61596
  }
60891
61597
  this.transportService = new TransportService(this, effectivePersistence, transportBus);
61598
+ warnIfRequestStateKeyNotShared({ logger: this.logger, metadata: this.metadata });
60892
61599
  if (this.haManager && isDistributed) {
60893
61600
  const persistCfg = transportConfig?.persistence;
60894
61601
  const resolvedPrefix = persistCfg && typeof persistCfg === "object" && persistCfg.redis?.keyPrefix ? persistCfg.redis.keyPrefix : "mcp:transport:";
@@ -60901,7 +61608,7 @@ var init_scope_instance = __esm({
60901
61608
  }
60902
61609
  });
60903
61610
  }
60904
- if ((0, import_utils104.getRuntimeContext)().deployment === "serverless") {
61611
+ if ((0, import_utils105.getRuntimeContext)().deployment === "serverless") {
60905
61612
  const protocolCfg = transportConfig?.protocol;
60906
61613
  const hasSSE = typeof protocolCfg === "string" ? protocolCfg !== "stateless-api" : protocolCfg?.sse === true;
60907
61614
  if (hasSSE) {
@@ -60928,7 +61635,7 @@ var init_scope_instance = __esm({
60928
61635
  sqlite: elicitationSqlite,
60929
61636
  keyPrefix: elicitationRedis?.keyPrefix ?? "mcp:elicit:",
60930
61637
  logger: this.logger,
60931
- isEdgeRuntime: (0, import_utils104.isEdgeRuntime)()
61638
+ isEdgeRuntime: (0, import_utils105.isEdgeRuntime)()
60932
61639
  });
60933
61640
  this._elicitationStore = elicitStore;
60934
61641
  this.elicitationBackendType = elicitType;
@@ -60942,7 +61649,7 @@ var init_scope_instance = __esm({
60942
61649
  );
60943
61650
  const tasksEnabledForCli = this.cliMode && hasPersistentBackend;
60944
61651
  const hasDistributedTaskBackend = Boolean(tasksConfig?.redis ?? this.metadata.redis);
60945
- const onEdgeRuntime = (0, import_utils104.isEdgeRuntime)();
61652
+ const onEdgeRuntime = (0, import_utils105.isEdgeRuntime)();
60946
61653
  const tasksUnavailableOnEdge = onEdgeRuntime && !hasDistributedTaskBackend && !tasksExplicitlyEnabled;
60947
61654
  if (tasksUnavailableOnEdge) {
60948
61655
  this.logger.info(
@@ -61386,6 +62093,15 @@ var init_scope_instance = __esm({
61386
62093
  notificationService: this.notificationService,
61387
62094
  flowRegistry: this.scopeFlows,
61388
62095
  toolRegistry: this.scopeTools,
62096
+ http: {
62097
+ server: this.server,
62098
+ checkClientIp: (request, response) => this.runFlow("http:ip-filter", {
62099
+ request,
62100
+ response
62101
+ }),
62102
+ entryPath: this.entryPath,
62103
+ routeBase: this.routeBase
62104
+ },
61389
62105
  logger: this.logger
61390
62106
  });
61391
62107
  this._scopeChannels = channelResult.channelRegistry;
@@ -61790,12 +62506,12 @@ var init_scope_instance = __esm({
61790
62506
  return this._authoritiesScopeMapping;
61791
62507
  }
61792
62508
  /**
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).
62509
+ * Collect the OAuth scopes entries declare on their `authProviders`. The PRM
62510
+ * endpoint advertises them in `scopes_supported` (RFC 9728) next to the scopes
62511
+ * the auth mode grants (`resourceScopesFor`), not a fixed OIDC list (#629).
61796
62512
  */
61797
62513
  getAllSupportedScopes() {
61798
- const scopes = /* @__PURE__ */ new Set(["openid", "profile", "email"]);
62514
+ const scopes = /* @__PURE__ */ new Set();
61799
62515
  const collectScopes = (metadata) => {
61800
62516
  const authProviders = metadata?.["authProviders"];
61801
62517
  if (!Array.isArray(authProviders)) return;
@@ -63362,7 +64078,7 @@ async function runHttpRequestFlowWeb(scope, request, opts = {}) {
63362
64078
  const serverRequest = await toServerRequest(request, url, opts.ctx, opts.persistent, opts.env);
63363
64079
  let output;
63364
64080
  try {
63365
- output = await (0, import_utils105.runRequestExclusive)(
64081
+ output = await (0, import_utils106.runRequestExclusive)(
63366
64082
  () => scope.runFlow("http:request", {
63367
64083
  request: serverRequest,
63368
64084
  response: {}
@@ -63380,7 +64096,7 @@ async function runMatchingHttpFlowWeb(scope, request, opts = {}) {
63380
64096
  if (!flowName) return void 0;
63381
64097
  let output;
63382
64098
  try {
63383
- output = await (0, import_utils105.runRequestExclusive)(
64099
+ output = await (0, import_utils106.runRequestExclusive)(
63384
64100
  () => scope.runFlow(flowName, {
63385
64101
  request: serverRequest,
63386
64102
  response: {}
@@ -63458,10 +64174,57 @@ function findMisconfiguration(error) {
63458
64174
  if (typeof code === "string" && MISCONFIGURATION_REMEDIES[code]) {
63459
64175
  return { code, remedy: MISCONFIGURATION_REMEDIES[code] };
63460
64176
  }
64177
+ if (current.name === "ZodError") return { code: "CONFIG_INVALID", remedy: CONFIG_INVALID_REMEDY };
63461
64178
  current = current.cause;
63462
64179
  }
63463
64180
  return void 0;
63464
64181
  }
64182
+ function createDeferredServerBuild(build, options = {}) {
64183
+ const now = options.now ?? Date.now;
64184
+ let built;
64185
+ let inFlight;
64186
+ let failure;
64187
+ return {
64188
+ get(arg) {
64189
+ if (built) return Promise.resolve(built.value);
64190
+ if (inFlight) return inFlight;
64191
+ if (failure && now() < failure.retryAt) return Promise.reject(failure.error);
64192
+ const failures = failure?.count ?? 0;
64193
+ inFlight = build(arg).then(
64194
+ (value) => {
64195
+ built = { value };
64196
+ failure = void 0;
64197
+ inFlight = void 0;
64198
+ return value;
64199
+ },
64200
+ (error) => {
64201
+ const delay = Math.min(STARTUP_RETRY_MAX_MS, STARTUP_RETRY_MIN_MS * 2 ** failures);
64202
+ failure = { error, retryAt: now() + delay, count: failures + 1 };
64203
+ inFlight = void 0;
64204
+ options.onFailure?.(error);
64205
+ throw error;
64206
+ }
64207
+ );
64208
+ return inFlight;
64209
+ },
64210
+ retryAfterSeconds() {
64211
+ if (!failure) return 0;
64212
+ return Math.max(1, Math.ceil((failure.retryAt - now()) / 1e3));
64213
+ }
64214
+ };
64215
+ }
64216
+ function startupFailureResponse(error, retryAfterSeconds = 1) {
64217
+ const misconfigured = misconfigurationResponse(error);
64218
+ if (misconfigured) return misconfigured;
64219
+ return Response.json(
64220
+ {
64221
+ error: "server_unavailable",
64222
+ code: "SERVER_START_FAILED",
64223
+ 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."
64224
+ },
64225
+ { status: 503, headers: { "Retry-After": String(Math.max(1, retryAfterSeconds)) } }
64226
+ );
64227
+ }
63465
64228
  function flowErrorToHttpOutput(error) {
63466
64229
  const misconfiguration = findMisconfiguration(error);
63467
64230
  if (misconfiguration) {
@@ -63495,11 +64258,11 @@ function flowErrorToHttpOutput(error) {
63495
64258
  }
63496
64259
  return { kind: "text", status: 500, body: "Internal Server Error", contentType: "text/plain; charset=utf-8" };
63497
64260
  }
63498
- var import_utils105, CLOUDFLARE_WORKERS_USER_AGENT, MISCONFIGURATION_REMEDIES;
64261
+ var import_utils106, CLOUDFLARE_WORKERS_USER_AGENT, MISCONFIGURATION_REMEDIES, CONFIG_INVALID_REMEDY, STARTUP_RETRY_MIN_MS, STARTUP_RETRY_MAX_MS;
63499
64262
  var init_web_fetch_handler = __esm({
63500
64263
  "libs/sdk/src/transport/web-fetch-handler.ts"() {
63501
64264
  "use strict";
63502
- import_utils105 = require("@frontmcp/utils");
64265
+ import_utils106 = require("@frontmcp/utils");
63503
64266
  init_common();
63504
64267
  init_server_tokens();
63505
64268
  init_path_utils();
@@ -63515,6 +64278,9 @@ var init_web_fetch_handler = __esm({
63515
64278
  UNENFORCED_METADATA: "An entry declares a field only a plugin enforces (approval, featureFlag, ...) and no installed plugin that enforces it reaches the entry, so the server refuses to start. Install the plugin or remove the field; the server log names the entries.",
63516
64279
  AUTH_CONFIGURATION_ERROR: "The auth or authorities configuration is invalid (for example, entries declare authorities and the server has no authorities option, or a rule checks nothing), so the server refuses to start. The server log names the entries."
63517
64280
  };
64281
+ CONFIG_INVALID_REMEDY = "The FrontMCP configuration failed validation, so the server refuses to start. The server log names the invalid fields.";
64282
+ STARTUP_RETRY_MIN_MS = 1e3;
64283
+ STARTUP_RETRY_MAX_MS = 6e4;
63518
64284
  }
63519
64285
  });
63520
64286
 
@@ -63544,9 +64310,9 @@ var init_noop_server = __esm({
63544
64310
  });
63545
64311
 
63546
64312
  // libs/sdk/src/server/security/security-audit.ts
63547
- function auditSecurityDefaults(config, isProduction6) {
64313
+ function auditSecurityDefaults(config, isProduction7) {
63548
64314
  const findings = [];
63549
- if (!isProduction6 && config.deploymentMode !== "distributed") {
64315
+ if (!isProduction7 && config.deploymentMode !== "distributed") {
63550
64316
  return findings;
63551
64317
  }
63552
64318
  const strict = config.security?.strict === true;
@@ -63780,14 +64546,14 @@ var init_base_host_adapter = __esm({
63780
64546
  });
63781
64547
 
63782
64548
  // libs/sdk/src/server/adapters/express.host.adapter.ts
63783
- var http, import_cors, import_express, import_utils106, DEFAULT_EXPRESS_BODY_LIMIT, ExpressHostAdapter;
64549
+ var http, import_cors, import_express, import_utils107, DEFAULT_EXPRESS_BODY_LIMIT, ExpressHostAdapter;
63784
64550
  var init_express_host_adapter = __esm({
63785
64551
  "libs/sdk/src/server/adapters/express.host.adapter.ts"() {
63786
64552
  "use strict";
63787
64553
  http = __toESM(require("node:http"));
63788
64554
  import_cors = __toESM(require("cors"));
63789
64555
  import_express = __toESM(require("express"));
63790
- import_utils106 = require("@frontmcp/utils");
64556
+ import_utils107 = require("@frontmcp/utils");
63791
64557
  init_mcp_error();
63792
64558
  init_host_validation_middleware();
63793
64559
  init_resolve_allowed_hosts();
@@ -63970,8 +64736,8 @@ var init_express_host_adapter = __esm({
63970
64736
  }
63971
64737
  async cleanupStaleSocket(socketPath) {
63972
64738
  try {
63973
- if (await (0, import_utils106.fileExists)(socketPath)) {
63974
- await (0, import_utils106.unlink)(socketPath);
64739
+ if (await (0, import_utils107.fileExists)(socketPath)) {
64740
+ await (0, import_utils107.unlink)(socketPath);
63975
64741
  }
63976
64742
  } catch {
63977
64743
  }
@@ -63994,11 +64760,11 @@ var init_node_express_host = __esm({
63994
64760
  });
63995
64761
 
63996
64762
  // libs/sdk/src/server/server.instance.ts
63997
- var import_utils107, DEFAULT_CORS, FrontMcpServerInstance;
64763
+ var import_utils108, DEFAULT_CORS, FrontMcpServerInstance;
63998
64764
  var init_server_instance = __esm({
63999
64765
  "libs/sdk/src/server/server.instance.ts"() {
64000
64766
  "use strict";
64001
- import_utils107 = require("@frontmcp/utils");
64767
+ import_utils108 = require("@frontmcp/utils");
64002
64768
  init_common();
64003
64769
  init_health2();
64004
64770
  init_metrics2();
@@ -64100,9 +64866,9 @@ var init_server_instance = __esm({
64100
64866
  }
64101
64867
  async start() {
64102
64868
  this.prepare();
64103
- const deploymentMode = (0, import_utils107.getRuntimeContext)().deployment;
64869
+ const deploymentMode = (0, import_utils108.getRuntimeContext)().deployment;
64104
64870
  const bindAddress = resolveBindAddress(this.config.security, deploymentMode);
64105
- const isProduction6 = process.env["NODE_ENV"] === "production";
64871
+ const isProduction7 = process.env["NODE_ENV"] === "production";
64106
64872
  const findings = auditSecurityDefaults(
64107
64873
  {
64108
64874
  cors: this.config.cors,
@@ -64110,7 +64876,7 @@ var init_server_instance = __esm({
64110
64876
  resolvedBindAddress: bindAddress,
64111
64877
  deploymentMode
64112
64878
  },
64113
- isProduction6
64879
+ isProduction7
64114
64880
  );
64115
64881
  logSecurityFindings(findings, console);
64116
64882
  await this.host.start(this.config.socketPath ?? this.config.port, bindAddress);
@@ -64338,11 +65104,11 @@ function resolveConfigInput(optionsOrClass) {
64338
65104
  }
64339
65105
  return optionsOrClass;
64340
65106
  }
64341
- var import_utils108, stdioServing, FrontMcpInstance;
65107
+ var import_utils109, stdioServing, FrontMcpInstance;
64342
65108
  var init_front_mcp = __esm({
64343
65109
  "libs/sdk/src/front-mcp/front-mcp.ts"() {
64344
65110
  "use strict";
64345
- import_utils108 = require("@frontmcp/utils");
65111
+ import_utils109 = require("@frontmcp/utils");
64346
65112
  init_common();
64347
65113
  init_direct();
64348
65114
  init_errors();
@@ -64527,11 +65293,16 @@ var init_front_mcp = __esm({
64527
65293
  */
64528
65294
  static async createFetchHandler(options) {
64529
65295
  const build = async (deferred) => {
64530
- const frontMcp = new _FrontMcpInstance2(frontMcpMetadataSchema.parse(options));
65296
+ let frontMcp;
64531
65297
  try {
65298
+ frontMcp = new _FrontMcpInstance2(frontMcpMetadataSchema.parse(options));
64532
65299
  await frontMcp.ready;
64533
65300
  } catch (err) {
64534
- if (deferred) frontMcp.log?.error("FrontMCP failed to start; requests are refused until it does", err);
65301
+ if (deferred) {
65302
+ const message = "FrontMCP failed to start; requests are refused until a retry succeeds";
65303
+ if (frontMcp?.log) frontMcp.log.error(message, err);
65304
+ else console.error(`[frontmcp] ${message}`, err);
65305
+ }
64535
65306
  throw err;
64536
65307
  }
64537
65308
  const scope = frontMcp.getPrimaryScope();
@@ -64541,25 +65312,15 @@ var init_front_mcp = __esm({
64541
65312
  frontMcp.log?.info("FrontMCP fetch handler created (web-standard transport)");
64542
65313
  return createWebFetchHandler(scope);
64543
65314
  };
64544
- if (!(0, import_utils108.isEdgeRuntime)()) return build(false);
65315
+ if (!(0, import_utils109.isEdgeRuntime)()) return build(false);
64545
65316
  assertStaticStartupConfig(options);
64546
- let inner;
64547
- let building;
65317
+ const server = createDeferredServerBuild(() => build(true));
64548
65318
  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
- }
65319
+ let inner;
65320
+ try {
65321
+ inner = await server.get();
65322
+ } catch (err) {
65323
+ return startupFailureResponse(err, server.retryAfterSeconds());
64563
65324
  }
64564
65325
  return inner(request, ctx, env);
64565
65326
  };
@@ -64696,8 +65457,8 @@ var init_front_mcp = __esm({
64696
65457
  frontMcp.log?.info(`MCP server listening on unix://${socketPath}`);
64697
65458
  const cleanup = async () => {
64698
65459
  try {
64699
- if (await (0, import_utils108.fileExists)(socketPath)) {
64700
- await (0, import_utils108.unlink)(socketPath);
65460
+ if (await (0, import_utils109.fileExists)(socketPath)) {
65461
+ await (0, import_utils109.unlink)(socketPath);
64701
65462
  }
64702
65463
  } catch {
64703
65464
  }
@@ -64841,7 +65602,7 @@ var init_front_mcp = __esm({
64841
65602
  };
64842
65603
  const mcpServer = new McpServer2(scope.metadata.info, serverOptions);
64843
65604
  startupCleanups.push(() => mcpServer.close());
64844
- const sessionId = `stdio:${(0, import_utils108.randomUUID)()}`;
65605
+ const sessionId = `stdio:${(0, import_utils109.randomUUID)()}`;
64845
65606
  const handlers = createMcpHandlers2({ scope, serverOptions, composeInstructions });
64846
65607
  for (const handler of handlers) {
64847
65608
  const originalHandler = handler.handler;
@@ -65161,9 +65922,9 @@ function FrontMcp(providedMetadata) {
65161
65922
  Reflect.defineMetadata(FrontMcpTokens[property] ?? property, metadata[property], target);
65162
65923
  }
65163
65924
  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");
65925
+ const isServerless2 = (0, import_utils110.getEnvFlag)("FRONTMCP_SERVERLESS");
65926
+ const isSchemaExtract = (0, import_utils110.getEnvFlag)("FRONTMCP_SCHEMA_EXTRACT");
65927
+ const isStdio = (0, import_utils110.getEnvFlag)("FRONTMCP_STDIO");
65167
65928
  const taskWorkerId = typeof process !== "undefined" ? process.env?.["FRONTMCP_RUN_TASK_ID"] : void 0;
65168
65929
  if (taskWorkerId && metadata.serve !== false) {
65169
65930
  void (async () => {
@@ -65189,7 +65950,7 @@ function FrontMcp(providedMetadata) {
65189
65950
  } else if (isServerless2) {
65190
65951
  const ServerlessInstance = getFrontMcpInstance();
65191
65952
  const { setServerlessHandler: setServerlessHandler2, setServerlessHandlerPromise: setServerlessHandlerPromise2, setServerlessHandlerError: setServerlessHandlerError2 } = getServerlessHandlerFns();
65192
- const isWorker = (0, import_utils109.getEnvFlag)("FRONTMCP_WORKER");
65953
+ const isWorker = (0, import_utils110.getEnvFlag)("FRONTMCP_WORKER");
65193
65954
  const handlerPromise = isWorker ? ServerlessInstance.createFetchHandler(metadata) : ServerlessInstance.createHandler(metadata);
65194
65955
  setServerlessHandlerPromise2(handlerPromise);
65195
65956
  handlerPromise.then(setServerlessHandler2).catch((err) => {
@@ -65202,12 +65963,12 @@ function FrontMcp(providedMetadata) {
65202
65963
  }
65203
65964
  };
65204
65965
  }
65205
- var import_reflect_metadata28, import_utils109, FRONT_MCP_DECORATOR_BASENAMES, FRONTMCP_CONFIG_METADATA_KEY, _FrontMcpInstance, _serverlessHandlerFns;
65966
+ var import_reflect_metadata28, import_utils110, FRONT_MCP_DECORATOR_BASENAMES, FRONTMCP_CONFIG_METADATA_KEY, _FrontMcpInstance, _serverlessHandlerFns;
65206
65967
  var init_front_mcp_decorator = __esm({
65207
65968
  "libs/sdk/src/common/decorators/front-mcp.decorator.ts"() {
65208
65969
  "use strict";
65209
65970
  import_reflect_metadata28 = require("reflect-metadata");
65210
- import_utils109 = require("@frontmcp/utils");
65971
+ import_utils110 = require("@frontmcp/utils");
65211
65972
  init_decorator_errors();
65212
65973
  init_mcp_error();
65213
65974
  init_metadata();
@@ -65960,49 +66721,6 @@ var init_dynamic_adapter = __esm({
65960
66721
  }
65961
66722
  });
65962
66723
 
65963
- // libs/sdk/src/common/dynamic/dynamic.plugin.ts
65964
- function isDynamicPluginClass(value) {
65965
- return typeof value === "function" && value.prototype instanceof DynamicPlugin;
65966
- }
65967
- var DynamicPlugin;
65968
- var init_dynamic_plugin = __esm({
65969
- "libs/sdk/src/common/dynamic/dynamic.plugin.ts"() {
65970
- "use strict";
65971
- init_transport_errors();
65972
- init_dynamic_utils();
65973
- DynamicPlugin = class {
65974
- get(token) {
65975
- throw new MethodNotImplementedError("DynamicPlugin", "get");
65976
- }
65977
- /**
65978
- * Static init() method to create a plugin provider.
65979
- * @param options - Input options (with optional fields for defaults)
65980
- */
65981
- static init(options) {
65982
- const extraProviders = options.providers;
65983
- const typedOptions = options;
65984
- if ("useFactory" in options) {
65985
- return {
65986
- ...typedOptions,
65987
- provide: this,
65988
- inject: options.inject,
65989
- useFactory: options.useFactory,
65990
- providers: dedupePluginProviders(extraProviders ?? [])
65991
- };
65992
- }
65993
- const dyn = collectDynamicProviders(this, typedOptions);
65994
- const mergedProviders = dedupePluginProviders([...dyn ?? [], ...extraProviders ?? []]);
65995
- return {
65996
- ...typedOptions,
65997
- provide: this,
65998
- useValue: new this(options),
65999
- providers: mergedProviders
66000
- };
66001
- }
66002
- };
66003
- }
66004
- });
66005
-
66006
66724
  // libs/sdk/src/common/dynamic/index.ts
66007
66725
  var init_dynamic = __esm({
66008
66726
  "libs/sdk/src/common/dynamic/index.ts"() {
@@ -66166,12 +66884,12 @@ var init_adapter_entry = __esm({
66166
66884
  });
66167
66885
 
66168
66886
  // libs/sdk/src/common/entries/tool.entry.ts
66169
- var import_lazy_zod109, import_utils111, ToolEntry;
66887
+ var import_lazy_zod109, import_utils112, ToolEntry;
66170
66888
  var init_tool_entry = __esm({
66171
66889
  "libs/sdk/src/common/entries/tool.entry.ts"() {
66172
66890
  "use strict";
66173
66891
  import_lazy_zod109 = require("@frontmcp/lazy-zod");
66174
- import_utils111 = require("@frontmcp/utils");
66892
+ import_utils112 = require("@frontmcp/utils");
66175
66893
  init_base_entry();
66176
66894
  ToolEntry = class extends BaseEntry {
66177
66895
  owner;
@@ -66238,7 +66956,7 @@ var init_tool_entry = __esm({
66238
66956
  try {
66239
66957
  return (0, import_lazy_zod109.toJSONSchema)(import_lazy_zod109.z.object(this.inputSchema), { io: "input", unrepresentable: "any" });
66240
66958
  } catch (error) {
66241
- if ((0, import_utils111.isDebug)() || (0, import_utils111.isDevelopment)()) {
66959
+ if ((0, import_utils112.isDebug)() || (0, import_utils112.isDevelopment)()) {
66242
66960
  console.warn("[ToolEntry] Failed to convert Zod schema to JSON Schema:", error);
66243
66961
  }
66244
66962
  return { type: "object", properties: {} };
@@ -66284,7 +67002,7 @@ var init_tool_entry = __esm({
66284
67002
  const json = (0, import_lazy_zod109.toJSONSchema)(zodSchema);
66285
67003
  return json && json["type"] === "object" ? json : null;
66286
67004
  } catch (error) {
66287
- if ((0, import_utils111.isDebug)() || (0, import_utils111.isDevelopment)()) {
67005
+ if ((0, import_utils112.isDebug)() || (0, import_utils112.isDevelopment)()) {
66288
67006
  console.warn("[ToolEntry] Failed to convert output Zod schema to JSON Schema:", error);
66289
67007
  }
66290
67008
  return null;
@@ -67476,6 +68194,7 @@ __export(index_exports, {
67476
68194
  create: () => create,
67477
68195
  createChildSpanContext: () => createChildSpanContext,
67478
68196
  createContextResolver: () => createContextResolver,
68197
+ createDeferredServerBuild: () => createDeferredServerBuild,
67479
68198
  createDirectResolver: () => createDirectResolver,
67480
68199
  createElicitationStore: () => createElicitationStore,
67481
68200
  createElicitationStoreFromStorage: () => createElicitationStoreFromStorage,
@@ -67583,6 +68302,7 @@ __export(index_exports, {
67583
68302
  getElicitationSecret: () => getElicitationSecret,
67584
68303
  getGlobalStoreConfig: () => getGlobalStoreConfig,
67585
68304
  getNestedValue: () => getNestedValue,
68305
+ getPinnedPublicUrl: () => getPinnedPublicUrl,
67586
68306
  getRegistryUrl: () => getRegistryUrl,
67587
68307
  getRequestBaseUrl: () => getRequestBaseUrl,
67588
68308
  getRunningTool: () => getRunningTool,
@@ -67600,6 +68320,7 @@ __export(index_exports, {
67600
68320
  httpRequestInputSchema: () => httpRequestInputSchema,
67601
68321
  httpRespond: () => httpRespond,
67602
68322
  incrementalAuthConfigSchema: () => import_auth5.incrementalAuthConfigSchema,
68323
+ initOptionsOf: () => initOptionsOf,
67603
68324
  intentSchema: () => intentSchema,
67604
68325
  isAlive: () => isAlive,
67605
68326
  isAnonymousSubject: () => import_auth61.isAnonymousSubject,
@@ -67758,6 +68479,7 @@ __export(index_exports, {
67758
68479
  skillsConfigOptionsSchema: () => skillsConfigOptionsSchema,
67759
68480
  skippedAppBehaviorSchema: () => import_auth5.skippedAppBehaviorSchema,
67760
68481
  sqliteOptionsSchema: () => sqliteOptionsSchema,
68482
+ startupFailureResponse: () => startupFailureResponse,
67761
68483
  staticAuthOptionsSchema: () => import_auth7.staticAuthOptionsSchema,
67762
68484
  storageProviderSchema: () => storageProviderSchema,
67763
68485
  supportsStreaming: () => supportsStreaming,
@@ -67945,22 +68667,22 @@ function createMemorySkillProvider(options = {}) {
67945
68667
  init_skill_utils();
67946
68668
 
67947
68669
  // libs/sdk/src/skill/skill-directory-loader.ts
67948
- var import_utils112 = require("@frontmcp/utils");
68670
+ var import_utils113 = require("@frontmcp/utils");
67949
68671
  init_skill_metadata();
67950
68672
  init_sdk_errors();
67951
68673
  init_skill_md_parser();
67952
68674
  async function scanSkillResources(dirPath) {
67953
68675
  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");
68676
+ const scriptsPath = (0, import_utils113.joinPath)(dirPath, "scripts");
68677
+ const referencesPath = (0, import_utils113.joinPath)(dirPath, "references");
68678
+ const assetsPath = (0, import_utils113.joinPath)(dirPath, "assets");
68679
+ const examplesPath = (0, import_utils113.joinPath)(dirPath, "examples");
67958
68680
  const checks = await Promise.all([
67959
68681
  checkDirectory(scriptsPath),
67960
68682
  checkDirectory(referencesPath),
67961
68683
  checkDirectory(assetsPath),
67962
68684
  checkDirectory(examplesPath),
67963
- (0, import_utils112.fileExists)((0, import_utils112.joinPath)(dirPath, "SKILL.md"))
68685
+ (0, import_utils113.fileExists)((0, import_utils113.joinPath)(dirPath, "SKILL.md"))
67964
68686
  ]);
67965
68687
  if (checks[0]) resources.scripts = scriptsPath;
67966
68688
  if (checks[1]) resources.references = referencesPath;
@@ -67973,7 +68695,7 @@ async function scanSkillResources(dirPath) {
67973
68695
  }
67974
68696
  async function checkDirectory(path3) {
67975
68697
  try {
67976
- const s = await (0, import_utils112.stat)(path3);
68698
+ const s = await (0, import_utils113.stat)(path3);
67977
68699
  return s.isDirectory();
67978
68700
  } catch (err) {
67979
68701
  if (err.code === "ENOENT") return false;
@@ -67986,16 +68708,16 @@ async function findNestedSkillMd(dirPath, maxDepth = Number.POSITIVE_INFINITY) {
67986
68708
  if (depth > maxDepth) return;
67987
68709
  let entries;
67988
68710
  try {
67989
- entries = await (0, import_utils112.readdir)(current);
68711
+ entries = await (0, import_utils113.readdir)(current);
67990
68712
  } catch (err) {
67991
68713
  if (err.code === "ENOENT") return;
67992
68714
  throw err;
67993
68715
  }
67994
68716
  for (const entry of entries) {
67995
- const full = (0, import_utils112.joinPath)(current, entry);
68717
+ const full = (0, import_utils113.joinPath)(current, entry);
67996
68718
  let entryStat;
67997
68719
  try {
67998
- entryStat = await (0, import_utils112.stat)(full);
68720
+ entryStat = await (0, import_utils113.stat)(full);
67999
68721
  } catch (err) {
68000
68722
  if (err.code === "ENOENT") continue;
68001
68723
  throw err;
@@ -68011,19 +68733,19 @@ async function findNestedSkillMd(dirPath, maxDepth = Number.POSITIVE_INFINITY) {
68011
68733
  return found.sort();
68012
68734
  }
68013
68735
  async function loadSkillDirectory(dirPath, logger) {
68014
- const skillMdPath = (0, import_utils112.joinPath)(dirPath, "SKILL.md");
68015
- const exists = await (0, import_utils112.fileExists)(skillMdPath);
68736
+ const skillMdPath = (0, import_utils113.joinPath)(dirPath, "SKILL.md");
68737
+ const exists = await (0, import_utils113.fileExists)(skillMdPath);
68016
68738
  if (!exists) {
68017
- throw new InvalidSkillError((0, import_utils112.basename)(dirPath), "SKILL.md not found in directory");
68739
+ throw new InvalidSkillError((0, import_utils113.basename)(dirPath), "SKILL.md not found in directory");
68018
68740
  }
68019
68741
  let content;
68020
68742
  let partialMetadata;
68021
68743
  try {
68022
- content = await (0, import_utils112.readFile)(skillMdPath, "utf-8");
68744
+ content = await (0, import_utils113.readFile)(skillMdPath, "utf-8");
68023
68745
  const { frontmatter, body } = parseSkillMdFrontmatter(content);
68024
68746
  partialMetadata = skillMdFrontmatterToMetadata(frontmatter, body);
68025
68747
  } catch (err) {
68026
- const name44 = (0, import_utils112.basename)(dirPath);
68748
+ const name44 = (0, import_utils113.basename)(dirPath);
68027
68749
  const message = err instanceof Error ? err.message : String(err);
68028
68750
  throw new InvalidSkillError(name44, `Failed to read or parse SKILL.md: ${message}`);
68029
68751
  }
@@ -68034,13 +68756,13 @@ async function loadSkillDirectory(dirPath, logger) {
68034
68756
  }
68035
68757
  const nested = await findNestedSkillMd(dirPath);
68036
68758
  if (nested.length > 0) {
68037
- const skillName2 = partialMetadata.name ?? (0, import_utils112.basename)(dirPath);
68759
+ const skillName2 = partialMetadata.name ?? (0, import_utils113.basename)(dirPath);
68038
68760
  throw new InvalidSkillError(
68039
68761
  skillName2,
68040
68762
  `Skill directory contains nested SKILL.md files (forbidden by SEP-2640 \xA7Resource Mapping): ${nested.join(", ")}`
68041
68763
  );
68042
68764
  }
68043
- const dirName = (0, import_utils112.basename)(dirPath);
68765
+ const dirName = (0, import_utils113.basename)(dirPath);
68044
68766
  if (partialMetadata.name && dirName && partialMetadata.name !== dirName) {
68045
68767
  const msg = `Skill name "${partialMetadata.name}" does not match directory name "${dirName}"`;
68046
68768
  if (logger) {
@@ -68716,6 +69438,7 @@ var ChannelListHook = FlowHooksOf("channels:list");
68716
69438
  create,
68717
69439
  createChildSpanContext,
68718
69440
  createContextResolver,
69441
+ createDeferredServerBuild,
68719
69442
  createDirectResolver,
68720
69443
  createElicitationStore,
68721
69444
  createElicitationStoreFromStorage,
@@ -68822,6 +69545,7 @@ var ChannelListHook = FlowHooksOf("channels:list");
68822
69545
  getElicitationSecret,
68823
69546
  getGlobalStoreConfig,
68824
69547
  getNestedValue,
69548
+ getPinnedPublicUrl,
68825
69549
  getRegistryUrl,
68826
69550
  getRequestBaseUrl,
68827
69551
  getRunningTool,
@@ -68839,6 +69563,7 @@ var ChannelListHook = FlowHooksOf("channels:list");
68839
69563
  httpRequestInputSchema,
68840
69564
  httpRespond,
68841
69565
  incrementalAuthConfigSchema,
69566
+ initOptionsOf,
68842
69567
  intentSchema,
68843
69568
  isAlive,
68844
69569
  isAnonymousSubject,
@@ -68997,6 +69722,7 @@ var ChannelListHook = FlowHooksOf("channels:list");
68997
69722
  skillsConfigOptionsSchema,
68998
69723
  skippedAppBehaviorSchema,
68999
69724
  sqliteOptionsSchema,
69725
+ startupFailureResponse,
69000
69726
  staticAuthOptionsSchema,
69001
69727
  storageProviderSchema,
69002
69728
  supportsStreaming,