@frontmcp/sdk 1.0.0-beta.1 → 1.0.0-beta.11

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 (174) hide show
  1. package/agent/agent.instance.d.ts +2 -2
  2. package/agent/agent.instance.d.ts.map +1 -1
  3. package/agent/agent.scope.d.ts +5 -4
  4. package/agent/agent.scope.d.ts.map +1 -1
  5. package/agent/flows/call-agent.flow.d.ts.map +1 -1
  6. package/app/instances/app.esm.instance.d.ts +7 -4
  7. package/app/instances/app.esm.instance.d.ts.map +1 -1
  8. package/app/instances/app.local.instance.d.ts +3 -3
  9. package/app/instances/app.local.instance.d.ts.map +1 -1
  10. package/app/instances/app.remote.instance.d.ts +7 -4
  11. package/app/instances/app.remote.instance.d.ts.map +1 -1
  12. package/auth/auth.registry.d.ts +2 -2
  13. package/auth/auth.registry.d.ts.map +1 -1
  14. package/auth/instances/instance.remote-primary-auth.d.ts.map +1 -1
  15. package/auth/session/utils/session-id.utils.d.ts +9 -6
  16. package/auth/session/utils/session-id.utils.d.ts.map +1 -1
  17. package/common/decorators/agent.decorator.d.ts +32 -7
  18. package/common/decorators/agent.decorator.d.ts.map +1 -1
  19. package/common/decorators/job.decorator.d.ts +56 -7
  20. package/common/decorators/job.decorator.d.ts.map +1 -1
  21. package/common/decorators/prompt.decorator.d.ts +7 -1
  22. package/common/decorators/prompt.decorator.d.ts.map +1 -1
  23. package/common/decorators/resource.decorator.d.ts +12 -2
  24. package/common/decorators/resource.decorator.d.ts.map +1 -1
  25. package/common/decorators/skill.decorator.d.ts.map +1 -1
  26. package/common/decorators/tool.decorator.d.ts +33 -42
  27. package/common/decorators/tool.decorator.d.ts.map +1 -1
  28. package/common/entries/app.entry.d.ts +8 -5
  29. package/common/entries/app.entry.d.ts.map +1 -1
  30. package/common/entries/plugin.entry.d.ts +1 -2
  31. package/common/entries/plugin.entry.d.ts.map +1 -1
  32. package/common/entries/provider.entry.d.ts +1 -2
  33. package/common/entries/provider.entry.d.ts.map +1 -1
  34. package/common/entries/resource.entry.d.ts +13 -0
  35. package/common/entries/resource.entry.d.ts.map +1 -1
  36. package/common/entries/scope.entry.d.ts +36 -8
  37. package/common/entries/scope.entry.d.ts.map +1 -1
  38. package/common/interfaces/app.interface.d.ts +1 -4
  39. package/common/interfaces/app.interface.d.ts.map +1 -1
  40. package/common/interfaces/internal/registry.interface.d.ts +15 -83
  41. package/common/interfaces/internal/registry.interface.d.ts.map +1 -1
  42. package/common/interfaces/plugin.interface.d.ts +1 -3
  43. package/common/interfaces/plugin.interface.d.ts.map +1 -1
  44. package/common/interfaces/provider.interface.d.ts +1 -3
  45. package/common/interfaces/provider.interface.d.ts.map +1 -1
  46. package/common/interfaces/resource.interface.d.ts +38 -0
  47. package/common/interfaces/resource.interface.d.ts.map +1 -1
  48. package/common/interfaces/scope.interface.d.ts +2 -4
  49. package/common/interfaces/scope.interface.d.ts.map +1 -1
  50. package/common/interfaces/skill.interface.d.ts +16 -0
  51. package/common/interfaces/skill.interface.d.ts.map +1 -1
  52. package/common/metadata/agent.metadata.d.ts +37 -6
  53. package/common/metadata/agent.metadata.d.ts.map +1 -1
  54. package/common/records/skill.record.d.ts +2 -0
  55. package/common/records/skill.record.d.ts.map +1 -1
  56. package/common/types/options/elicitation/schema.d.ts +2 -4
  57. package/common/types/options/elicitation/schema.d.ts.map +1 -1
  58. package/common/types/options/ext-apps/index.d.ts +1 -1
  59. package/common/types/options/ext-apps/index.d.ts.map +1 -1
  60. package/common/types/options/ext-apps/interfaces.d.ts +55 -0
  61. package/common/types/options/ext-apps/interfaces.d.ts.map +1 -1
  62. package/common/types/options/ext-apps/schema.d.ts +3 -1
  63. package/common/types/options/ext-apps/schema.d.ts.map +1 -1
  64. package/common/types/options/ext-apps/typecheck.d.ts +2 -0
  65. package/common/types/options/ext-apps/typecheck.d.ts.map +1 -0
  66. package/common/types/options/http/schema.d.ts +3 -1
  67. package/common/types/options/http/schema.d.ts.map +1 -1
  68. package/common/types/options/http/typecheck.d.ts +2 -0
  69. package/common/types/options/http/typecheck.d.ts.map +1 -0
  70. package/common/types/options/logging/schema.d.ts +3 -2
  71. package/common/types/options/logging/schema.d.ts.map +1 -1
  72. package/common/types/options/redis/index.d.ts +1 -1
  73. package/common/types/options/redis/index.d.ts.map +1 -1
  74. package/common/types/options/redis/interfaces.d.ts +4 -0
  75. package/common/types/options/redis/interfaces.d.ts.map +1 -1
  76. package/common/types/options/redis/schema.d.ts +7 -4
  77. package/common/types/options/redis/schema.d.ts.map +1 -1
  78. package/common/types/options/redis/typecheck.d.ts +2 -0
  79. package/common/types/options/redis/typecheck.d.ts.map +1 -0
  80. package/common/types/options/skills-http/schema.d.ts +3 -2
  81. package/common/types/options/skills-http/schema.d.ts.map +1 -1
  82. package/common/types/options/skills-http/typecheck.d.ts +2 -0
  83. package/common/types/options/skills-http/typecheck.d.ts.map +1 -0
  84. package/common/types/options/sqlite/index.d.ts +1 -0
  85. package/common/types/options/sqlite/index.d.ts.map +1 -1
  86. package/common/types/options/sqlite/interfaces.d.ts +25 -0
  87. package/common/types/options/sqlite/interfaces.d.ts.map +1 -0
  88. package/common/types/options/sqlite/schema.d.ts +3 -1
  89. package/common/types/options/sqlite/schema.d.ts.map +1 -1
  90. package/common/types/options/sqlite/typecheck.d.ts +2 -0
  91. package/common/types/options/sqlite/typecheck.d.ts.map +1 -0
  92. package/common/utils/decide-request-intent.utils.d.ts.map +1 -1
  93. package/completion/flows/complete.flow.d.ts.map +1 -1
  94. package/elicitation/flows/elicitation-request.flow.d.ts.map +1 -1
  95. package/elicitation/flows/elicitation-result.flow.d.ts.map +1 -1
  96. package/elicitation/helpers/fallback.helper.d.ts.map +1 -1
  97. package/elicitation/send-elicitation-result.tool.d.ts.map +1 -1
  98. package/errors/index.d.ts +1 -1
  99. package/errors/index.d.ts.map +1 -1
  100. package/errors/transport.errors.d.ts +6 -0
  101. package/errors/transport.errors.d.ts.map +1 -1
  102. package/esm/index.mjs +836 -286
  103. package/flows/flow.instance.d.ts +2 -3
  104. package/flows/flow.instance.d.ts.map +1 -1
  105. package/front-mcp/front-mcp.d.ts +2 -2
  106. package/front-mcp/front-mcp.d.ts.map +1 -1
  107. package/front-mcp/front-mcp.providers.d.ts.map +1 -1
  108. package/hooks/hook.registry.d.ts +2 -2
  109. package/hooks/hook.registry.d.ts.map +1 -1
  110. package/index.js +1050 -499
  111. package/job/job.instance.d.ts +2 -2
  112. package/job/job.instance.d.ts.map +1 -1
  113. package/notification/notification.service.d.ts +5 -1
  114. package/notification/notification.service.d.ts.map +1 -1
  115. package/package.json +10 -10
  116. package/plugin/plugin.registry.d.ts +9 -4
  117. package/plugin/plugin.registry.d.ts.map +1 -1
  118. package/prompt/prompt.instance.d.ts +2 -2
  119. package/prompt/prompt.instance.d.ts.map +1 -1
  120. package/prompt/prompt.registry.d.ts +2 -2
  121. package/prompt/prompt.registry.d.ts.map +1 -1
  122. package/provider/provider.registry.d.ts +7 -9
  123. package/provider/provider.registry.d.ts.map +1 -1
  124. package/resource/flows/read-resource.flow.d.ts.map +1 -1
  125. package/resource/resource.instance.d.ts +22 -3
  126. package/resource/resource.instance.d.ts.map +1 -1
  127. package/resource/resource.registry.d.ts +2 -2
  128. package/resource/resource.registry.d.ts.map +1 -1
  129. package/scope/flows/http.request.flow.d.ts.map +1 -1
  130. package/scope/scope.instance.d.ts +4 -3
  131. package/scope/scope.instance.d.ts.map +1 -1
  132. package/skill/flows/http/skills-api.flow.d.ts.map +1 -1
  133. package/skill/flows/load-skill.flow.d.ts.map +1 -1
  134. package/skill/skill-http.utils.d.ts +4 -3
  135. package/skill/skill-http.utils.d.ts.map +1 -1
  136. package/skill/skill-md-parser.d.ts.map +1 -1
  137. package/skill/skill-storage.factory.d.ts +3 -3
  138. package/skill/skill-storage.factory.d.ts.map +1 -1
  139. package/skill/skill-validator.d.ts +2 -2
  140. package/skill/skill-validator.d.ts.map +1 -1
  141. package/skill/skill.instance.d.ts +6 -2
  142. package/skill/skill.instance.d.ts.map +1 -1
  143. package/skill/skill.registry.d.ts +2 -3
  144. package/skill/skill.registry.d.ts.map +1 -1
  145. package/skill/skill.utils.d.ts +11 -2
  146. package/skill/skill.utils.d.ts.map +1 -1
  147. package/tool/flows/call-tool.flow.d.ts.map +1 -1
  148. package/tool/flows/tools-list.flow.d.ts.map +1 -1
  149. package/tool/tool.instance.d.ts +2 -2
  150. package/tool/tool.instance.d.ts.map +1 -1
  151. package/tool/tool.registry.d.ts +2 -2
  152. package/tool/tool.registry.d.ts.map +1 -1
  153. package/transport/adapters/streamable-http-transport.d.ts +23 -0
  154. package/transport/adapters/streamable-http-transport.d.ts.map +1 -1
  155. package/transport/adapters/transport.local.adapter.d.ts +29 -1
  156. package/transport/adapters/transport.local.adapter.d.ts.map +1 -1
  157. package/transport/adapters/transport.sse.adapter.d.ts.map +1 -1
  158. package/transport/adapters/transport.streamable-http.adapter.d.ts +13 -4
  159. package/transport/adapters/transport.streamable-http.adapter.d.ts.map +1 -1
  160. package/transport/flows/handle.sse.flow.d.ts.map +1 -1
  161. package/transport/flows/handle.stateless-http.flow.d.ts.map +1 -1
  162. package/transport/flows/handle.streamable-http.flow.d.ts +50 -1
  163. package/transport/flows/handle.streamable-http.flow.d.ts.map +1 -1
  164. package/transport/mcp-handlers/initialize-request.handler.d.ts.map +1 -1
  165. package/transport/transport.local.d.ts +2 -0
  166. package/transport/transport.local.d.ts.map +1 -1
  167. package/transport/transport.registry.d.ts +8 -0
  168. package/transport/transport.registry.d.ts.map +1 -1
  169. package/transport/transport.remote.d.ts +2 -0
  170. package/transport/transport.remote.d.ts.map +1 -1
  171. package/transport/transport.types.d.ts +10 -0
  172. package/transport/transport.types.d.ts.map +1 -1
  173. package/workflow/workflow.instance.d.ts +2 -2
  174. package/workflow/workflow.instance.d.ts.map +1 -1
package/esm/index.mjs CHANGED
@@ -623,7 +623,7 @@ var init_schema3 = __esm({
623
623
  maxAge: z4.number().optional()
624
624
  });
625
625
  httpOptionsSchema = z4.object({
626
- port: z4.number().optional().default(3001),
626
+ port: z4.number().optional().default(Number(process.env["PORT"]) || 3e3),
627
627
  entryPath: z4.string().default(""),
628
628
  // Using z.any() because hostFactory accepts FrontMcpServer | ((config) => FrontMcpServer)
629
629
  // which Zod cannot validate at runtime - type safety is enforced via TypeScript interface
@@ -1794,19 +1794,11 @@ var init_schema10 = __esm({
1794
1794
  }
1795
1795
  });
1796
1796
 
1797
- // libs/sdk/src/common/types/options/ext-apps/interfaces.ts
1798
- var init_interfaces2 = __esm({
1799
- "libs/sdk/src/common/types/options/ext-apps/interfaces.ts"() {
1800
- "use strict";
1801
- }
1802
- });
1803
-
1804
1797
  // libs/sdk/src/common/types/options/ext-apps/index.ts
1805
1798
  var init_ext_apps = __esm({
1806
1799
  "libs/sdk/src/common/types/options/ext-apps/index.ts"() {
1807
1800
  "use strict";
1808
1801
  init_schema10();
1809
- init_interfaces2();
1810
1802
  }
1811
1803
  });
1812
1804
 
@@ -2392,6 +2384,7 @@ var init_logger_metadata = __esm({
2392
2384
 
2393
2385
  // libs/sdk/src/common/metadata/agent.metadata.ts
2394
2386
  import { z as z29 } from "zod";
2387
+ import { rateLimitConfigSchema as rateLimitConfigSchema2, concurrencyConfigSchema as concurrencyConfigSchema2, timeoutConfigSchema as timeoutConfigSchema2 } from "@frontmcp/guard";
2395
2388
  function withConfig(configPath, options) {
2396
2389
  if (typeof options === "function") {
2397
2390
  return { configPath, transform: options };
@@ -2480,9 +2473,9 @@ var init_agent_metadata = __esm({
2480
2473
  execution: executionConfigSchema.optional(),
2481
2474
  tags: z29.array(z29.string().min(1)).optional(),
2482
2475
  hideFromDiscovery: z29.boolean().optional().default(false),
2483
- rateLimit: z29.looseObject({ maxRequests: z29.number() }).optional(),
2484
- concurrency: z29.looseObject({ maxConcurrent: z29.number() }).optional(),
2485
- timeout: z29.looseObject({ executeMs: z29.number() }).optional()
2476
+ rateLimit: rateLimitConfigSchema2.optional(),
2477
+ concurrency: concurrencyConfigSchema2.optional(),
2478
+ timeout: timeoutConfigSchema2.optional()
2486
2479
  }).passthrough();
2487
2480
  }
2488
2481
  });
@@ -4105,7 +4098,7 @@ var init_provider_errors = __esm({
4105
4098
  });
4106
4099
 
4107
4100
  // libs/sdk/src/errors/transport.errors.ts
4108
- var MethodNotImplementedError, UnsupportedTransportTypeError, TransportBusRequiredError, InvalidTransportSessionError, TransportNotConnectedError, TransportAlreadyStartedError, UnsupportedContentTypeError;
4101
+ var MethodNotImplementedError, UnsupportedTransportTypeError, TransportBusRequiredError, InvalidTransportSessionError, TransportNotConnectedError, TransportAlreadyStartedError, UnsupportedContentTypeError, TransportServiceNotAvailableError;
4109
4102
  var init_transport_errors = __esm({
4110
4103
  "libs/sdk/src/errors/transport.errors.ts"() {
4111
4104
  "use strict";
@@ -4148,6 +4141,11 @@ var init_transport_errors = __esm({
4148
4141
  this.contentType = contentType2;
4149
4142
  }
4150
4143
  };
4144
+ TransportServiceNotAvailableError = class extends InternalMcpError {
4145
+ constructor() {
4146
+ super("Transport service not available", "TRANSPORT_SERVICE_NOT_AVAILABLE");
4147
+ }
4148
+ };
4151
4149
  }
4152
4150
  });
4153
4151
 
@@ -4570,6 +4568,7 @@ __export(errors_exports, {
4570
4568
  TransportAlreadyStartedError: () => TransportAlreadyStartedError,
4571
4569
  TransportBusRequiredError: () => TransportBusRequiredError,
4572
4570
  TransportNotConnectedError: () => TransportNotConnectedError,
4571
+ TransportServiceNotAvailableError: () => TransportServiceNotAvailableError,
4573
4572
  UnauthorizedError: () => UnauthorizedError,
4574
4573
  UnsupportedClientVersionError: () => UnsupportedClientVersionError,
4575
4574
  UnsupportedContentTypeError: () => UnsupportedContentTypeError,
@@ -6628,12 +6627,12 @@ var init_provider_registry = __esm({
6628
6627
  */
6629
6628
  getProviderInfo(token) {
6630
6629
  const def = this.defs.get(token);
6631
- const instance = this.instances.get(token);
6632
- if (!def || !instance)
6630
+ if (!def)
6633
6631
  throw new ProviderNotRegisteredError(
6634
6632
  tokenName2(token),
6635
6633
  "not a registered DEFAULT provider and not a constructable class"
6636
6634
  );
6635
+ const instance = this.instances.get(token);
6637
6636
  return {
6638
6637
  token,
6639
6638
  def,
@@ -7642,7 +7641,7 @@ function supportsElicitation(capabilities, mode) {
7642
7641
  if (mode === "url") {
7643
7642
  return capabilities.elicitation.url !== void 0;
7644
7643
  }
7645
- return capabilities.elicitation.form !== void 0 || capabilities.elicitation.url !== void 0;
7644
+ return true;
7646
7645
  }
7647
7646
  function matchCustomMappings(clientName, mappings) {
7648
7647
  if (!mappings || mappings.length === 0) {
@@ -7729,8 +7728,9 @@ var init_notification_service = __esm({
7729
7728
  emergency: 7
7730
7729
  };
7731
7730
  NotificationService = class _NotificationService {
7732
- constructor(scope) {
7731
+ constructor(scope, options = {}) {
7733
7732
  this.scope = scope;
7733
+ this.options = options;
7734
7734
  this.logger = scope.logger.child("NotificationService");
7735
7735
  }
7736
7736
  logger;
@@ -7747,6 +7747,9 @@ var init_notification_service = __esm({
7747
7747
  terminatedSessions = /* @__PURE__ */ new Set();
7748
7748
  /** Maximum number of terminated sessions to track before eviction */
7749
7749
  static MAX_TERMINATED_SESSIONS = 1e4;
7750
+ get maxTerminatedSessions() {
7751
+ return this.options.maxTerminatedSessions ?? _NotificationService.MAX_TERMINATED_SESSIONS;
7752
+ }
7750
7753
  /**
7751
7754
  * Initialize the notification service and subscribe to registry changes.
7752
7755
  * Called after all registries are ready.
@@ -7828,7 +7831,7 @@ var init_notification_service = __esm({
7828
7831
  const wasRegistered = this.unregisterServer(sessionId);
7829
7832
  this.scope.providers.cleanupSession(sessionId);
7830
7833
  this.logger.verbose(`Cleaned up provider cache for terminated session: ${sessionId.slice(0, 20)}...`);
7831
- if (this.terminatedSessions.size >= _NotificationService.MAX_TERMINATED_SESSIONS) {
7834
+ if (this.terminatedSessions.size >= this.maxTerminatedSessions) {
7832
7835
  const oldest = this.terminatedSessions.values().next().value;
7833
7836
  if (oldest) {
7834
7837
  this.terminatedSessions.delete(oldest);
@@ -8510,7 +8513,14 @@ async function handleWaitingFallback(deps, error) {
8510
8513
  );
8511
8514
  }
8512
8515
  }, ttl);
8513
- scope.elicitationStore.subscribeFallbackResult(
8516
+ const store = scope.elicitationStore;
8517
+ if (!store) {
8518
+ resolved = true;
8519
+ clearTimeout(timeoutHandle);
8520
+ reject(new ElicitationStoreNotInitializedError());
8521
+ return;
8522
+ }
8523
+ store.subscribeFallbackResult(
8514
8524
  error.elicitId,
8515
8525
  (result) => {
8516
8526
  if (!resolved) {
@@ -8591,7 +8601,7 @@ var init_tool_instance = __esm({
8591
8601
  this.name = record.metadata.id || record.metadata.name;
8592
8602
  this.fullName = this.owner.id + ":" + this.name;
8593
8603
  this.scope = this._providers.getActiveScope();
8594
- this.hooks = this.scope.providers.getHooksRegistry();
8604
+ this.hooks = this.scope.hooks;
8595
8605
  this.inputSchema = record.metadata.inputSchema ?? {};
8596
8606
  const meta = record.metadata;
8597
8607
  this.rawInputSchema = meta["rawInputSchema"];
@@ -9016,7 +9026,7 @@ function resourceRemote(url, targetName, options) {
9016
9026
  }
9017
9027
  };
9018
9028
  }
9019
- var Resource;
9029
+ var Resource, _ResourceTemplate;
9020
9030
  var init_resource_decorator = __esm({
9021
9031
  "libs/sdk/src/common/decorators/resource.decorator.ts"() {
9022
9032
  "use strict";
@@ -9029,6 +9039,7 @@ var init_resource_decorator = __esm({
9029
9039
  remote: resourceRemote
9030
9040
  });
9031
9041
  Resource = FrontMcpResource;
9042
+ _ResourceTemplate = FrontMcpResourceTemplate;
9032
9043
  }
9033
9044
  });
9034
9045
 
@@ -9201,8 +9212,7 @@ var init_tools_list_flow = __esm({
9201
9212
  );
9202
9213
  }
9203
9214
  const sessionId = authInfo.sessionId;
9204
- const scope = this.scope;
9205
- const platformType = authInfo.sessionIdPayload?.platformType ?? (sessionId ? scope.notifications?.getPlatformType(sessionId) : void 0) ?? "unknown";
9215
+ const platformType = authInfo.sessionIdPayload?.platformType ?? (sessionId ? this.scope.notifications?.getPlatformType(sessionId) : void 0) ?? "unknown";
9206
9216
  this.logger.verbose(`parseInput: detected platform=${platformType}`);
9207
9217
  const cursor = params?.cursor;
9208
9218
  if (cursor) this.logger.verbose(`parseInput: cursor=${cursor}`);
@@ -9309,8 +9319,7 @@ var init_tools_list_flow = __esm({
9309
9319
  try {
9310
9320
  const allResolved = this.state.required.resolvedTools;
9311
9321
  const platformType = this.state.platformType ?? "unknown";
9312
- const scope = this.scope;
9313
- const paginationConfig = scope.pagination?.tools;
9322
+ const paginationConfig = this.scope.metadata.pagination?.tools;
9314
9323
  const usePagination = this.shouldPaginate(allResolved.length, paginationConfig);
9315
9324
  const sortedResolved = [...allResolved].sort((a, b) => a.finalName.localeCompare(b.finalName));
9316
9325
  let resolved = sortedResolved;
@@ -9370,12 +9379,12 @@ var init_tools_list_flow = __esm({
9370
9379
  this.logger.warn(`parseTools: toolUI not available in scope for ${finalName}`);
9371
9380
  return item;
9372
9381
  }
9373
- const scope2 = this.scope;
9382
+ const scope = this.scope;
9374
9383
  let manifest;
9375
9384
  let detectedType;
9376
9385
  try {
9377
- manifest = scope2.toolUI.getManifest(finalName);
9378
- detectedType = scope2.toolUI.detectUIType(uiConfig.template);
9386
+ manifest = scope.toolUI.getManifest(finalName);
9387
+ detectedType = scope.toolUI.detectUIType(uiConfig.template);
9379
9388
  } catch (error) {
9380
9389
  this.logger.warn(`parseTools: failed to access toolUI for ${finalName}`, error);
9381
9390
  return item;
@@ -9897,8 +9906,11 @@ var init_call_tool_flow = __esm({
9897
9906
  const authInfo = this.state.authInfo;
9898
9907
  const authInfoWithTransport = authInfo;
9899
9908
  const sessionId = authInfo?.sessionId ?? "anonymous";
9900
- const scope = this.scope;
9901
- await scope.elicitationStore.setPendingFallback({
9909
+ const store = this.scope.elicitationStore;
9910
+ if (!store) {
9911
+ throw new InternalMcpError("Elicitation store not initialized");
9912
+ }
9913
+ await store.setPendingFallback({
9902
9914
  elicitId: error.elicitId,
9903
9915
  sessionId,
9904
9916
  toolName: error.toolName,
@@ -9909,11 +9921,11 @@ var init_call_tool_flow = __esm({
9909
9921
  expiresAt: Date.now() + error.ttl
9910
9922
  });
9911
9923
  const transportType = authInfoWithTransport?.transport?.type;
9912
- if (transportType === "streamable-http" && canDeliverNotifications(scope, sessionId)) {
9924
+ if (transportType === "streamable-http" && canDeliverNotifications(this.scope, sessionId)) {
9913
9925
  this.logger.info("execute: using waiting fallback for streamable-http", {
9914
9926
  elicitId: error.elicitId
9915
9927
  });
9916
- const deps = { scope, sessionId, logger: this.logger };
9928
+ const deps = { scope: this.scope, sessionId, logger: this.logger };
9917
9929
  const result = await handleWaitingFallback(deps, error);
9918
9930
  toolContext.output = result;
9919
9931
  this.logger.verbose("execute:done (elicitation waiting fallback)");
@@ -10023,6 +10035,10 @@ ${JSON.stringify(error.schema, null, 2)}
10023
10035
  const useStructuredContent = resolvedMode.useStructuredContent;
10024
10036
  let htmlContent;
10025
10037
  let uiMeta = {};
10038
+ if (!scope.toolUI) {
10039
+ this.logger.verbose("applyUI: toolUI not available, skipping UI rendering");
10040
+ return;
10041
+ }
10026
10042
  if (servingMode === "static") {
10027
10043
  this.logger.verbose("applyUI: UI using static mode (structured data only)", {
10028
10044
  tool: tool.metadata.name,
@@ -10240,8 +10256,8 @@ var init_send_elicitation_result_tool = __esm({
10240
10256
  async execute(input) {
10241
10257
  const { elicitId, action, content } = input;
10242
10258
  this.logger.info("sendElicitationResult: processing", { elicitId, action });
10243
- const scope = this.scope;
10244
- if (!scope?.elicitationStore) {
10259
+ const store = this.scope.elicitationStore;
10260
+ if (!store) {
10245
10261
  this.logger.error("sendElicitationResult: scope or elicitationStore not available");
10246
10262
  return {
10247
10263
  content: [
@@ -10253,7 +10269,7 @@ var init_send_elicitation_result_tool = __esm({
10253
10269
  isError: true
10254
10270
  };
10255
10271
  }
10256
- const pending = await scope.elicitationStore.getPendingFallback(elicitId);
10272
+ const pending = await store.getPendingFallback(elicitId);
10257
10273
  if (!pending) {
10258
10274
  this.logger.warn("sendElicitationResult: no pending elicitation found", { elicitId });
10259
10275
  return {
@@ -10267,7 +10283,7 @@ var init_send_elicitation_result_tool = __esm({
10267
10283
  };
10268
10284
  }
10269
10285
  if (Date.now() > pending.expiresAt) {
10270
- await scope.elicitationStore.deletePendingFallback(elicitId);
10286
+ await store.deletePendingFallback(elicitId);
10271
10287
  this.logger.warn("sendElicitationResult: elicitation expired", { elicitId });
10272
10288
  return {
10273
10289
  content: [
@@ -10283,8 +10299,8 @@ var init_send_elicitation_result_tool = __esm({
10283
10299
  status: action,
10284
10300
  ...action === "accept" && content !== void 0 && { content }
10285
10301
  };
10286
- await scope.elicitationStore.setResolvedResult(elicitId, elicitResult, pending.sessionId);
10287
- await scope.elicitationStore.deletePendingFallback(elicitId);
10302
+ await store.setResolvedResult(elicitId, elicitResult, pending.sessionId);
10303
+ await store.deletePendingFallback(elicitId);
10288
10304
  this.logger.info("sendElicitationResult: re-invoking original tool", {
10289
10305
  elicitId,
10290
10306
  toolName: pending.toolName
@@ -10294,7 +10310,7 @@ var init_send_elicitation_result_tool = __esm({
10294
10310
  ctx.setPreResolvedElicitResult(elicitResult);
10295
10311
  }
10296
10312
  try {
10297
- const toolResult = await scope.runFlowForOutput("tools:call-tool", {
10313
+ const toolResult = await this.scope.runFlowForOutput("tools:call-tool", {
10298
10314
  request: {
10299
10315
  method: "tools/call",
10300
10316
  params: {
@@ -10309,19 +10325,19 @@ var init_send_elicitation_result_tool = __esm({
10309
10325
  elicitId,
10310
10326
  toolName: pending.toolName
10311
10327
  });
10312
- await scope.elicitationStore.publishFallbackResult(elicitId, pending.sessionId, {
10328
+ await store.publishFallbackResult(elicitId, pending.sessionId, {
10313
10329
  success: true,
10314
10330
  result: toolResult
10315
10331
  });
10316
- await scope.elicitationStore.deleteResolvedResult(elicitId);
10332
+ await store.deleteResolvedResult(elicitId);
10317
10333
  return toolResult;
10318
10334
  } catch (error) {
10319
10335
  const errorMessage = error instanceof Error ? error.message : String(error);
10320
- await scope.elicitationStore.publishFallbackResult(elicitId, pending.sessionId, {
10336
+ await store.publishFallbackResult(elicitId, pending.sessionId, {
10321
10337
  success: false,
10322
10338
  error: errorMessage
10323
10339
  });
10324
- await scope.elicitationStore.deleteResolvedResult(elicitId);
10340
+ await store.deleteResolvedResult(elicitId);
10325
10341
  this.logger.error("sendElicitationResult: original tool failed", {
10326
10342
  elicitId,
10327
10343
  toolName: pending.toolName,
@@ -11010,19 +11026,26 @@ var init_resource_instance = __esm({
11010
11026
  init_mcp_error();
11011
11027
  init_errors();
11012
11028
  ResourceInstance = class extends ResourceEntry2 {
11013
- providers;
11029
+ _providers;
11014
11030
  scope;
11015
11031
  hooks;
11032
+ /**
11033
+ * Get the provider registry for this resource.
11034
+ * Used by flows to build context-aware providers for CONTEXT-scoped dependencies.
11035
+ */
11036
+ get providers() {
11037
+ return this._providers;
11038
+ }
11016
11039
  /** Parsed URI template info for template resources */
11017
11040
  templateInfo;
11018
11041
  constructor(record, providers, owner) {
11019
11042
  super(record);
11020
11043
  this.owner = owner;
11021
- this.providers = providers;
11044
+ this._providers = providers;
11022
11045
  this.name = record.metadata.name;
11023
11046
  this.fullName = this.owner.id + ":" + this.name;
11024
11047
  this.scope = this.providers.getActiveScope();
11025
- this.hooks = this.scope.providers.getHooksRegistry();
11048
+ this.hooks = this.scope.hooks;
11026
11049
  this.isTemplate = "uriTemplate" in record.metadata;
11027
11050
  if (this.isTemplate) {
11028
11051
  const templateMetadata = record.metadata;
@@ -11053,6 +11076,18 @@ var init_resource_instance = __esm({
11053
11076
  getMetadata() {
11054
11077
  return this.record.metadata;
11055
11078
  }
11079
+ /**
11080
+ * Get an argument completer from the resource class prototype.
11081
+ * Returns a completer function if the resource class overrides getArgumentCompleter,
11082
+ * or null if no completer is available.
11083
+ */
11084
+ getArgumentCompleter(argName) {
11085
+ const cls = this.record.provide;
11086
+ if (typeof cls === "function" && cls.prototype && typeof cls.prototype.getArgumentCompleter === "function") {
11087
+ return cls.prototype.getArgumentCompleter.call(cls.prototype, argName);
11088
+ }
11089
+ return null;
11090
+ }
11056
11091
  /**
11057
11092
  * Match a URI against this resource.
11058
11093
  * For static resources: exact match against uri
@@ -11168,6 +11203,7 @@ var init_resource_types = __esm({
11168
11203
  // libs/sdk/src/resource/flows/read-resource.flow.ts
11169
11204
  import { z as z40 } from "zod";
11170
11205
  import { ReadResourceRequestSchema, ReadResourceResultSchema } from "@frontmcp/protocol";
11206
+ import { randomBytes as randomBytes2 } from "@frontmcp/utils";
11171
11207
  var inputSchema4, outputSchema3, stateSchema3, plan3, name3, Stage3, ReadResourceFlow;
11172
11208
  var init_read_resource_flow = __esm({
11173
11209
  "libs/sdk/src/resource/flows/read-resource.flow.ts"() {
@@ -11277,11 +11313,14 @@ var init_read_resource_flow = __esm({
11277
11313
  this.logger.info(`findResource: looking for resource with URI "${uri}"`);
11278
11314
  if (isUIResourceUri(uri)) {
11279
11315
  this.logger.info(`findResource: detected UI resource URI "${uri}"`);
11280
- const scope = this.scope;
11281
11316
  const { sessionId, authInfo } = this.state;
11282
- const platformType = authInfo?.sessionIdPayload?.platformType ?? (sessionId ? scope.notifications.getPlatformType(sessionId) : void 0);
11317
+ const platformType = authInfo?.sessionIdPayload?.platformType ?? (sessionId ? this.scope.notifications.getPlatformType(sessionId) : void 0);
11283
11318
  this.logger.verbose(`findResource: platform type for session: ${platformType ?? "unknown"}`);
11284
- const uiResult = handleUIResourceRead(uri, scope.toolUI, platformType);
11319
+ if (!this.scope.toolUI) {
11320
+ this.logger.verbose("findResource: toolUI not available, skipping UI resource handling");
11321
+ throw new ResourceNotFoundError(uri);
11322
+ }
11323
+ const uiResult = handleUIResourceRead(uri, this.scope.toolUI, platformType);
11285
11324
  if (uiResult.handled) {
11286
11325
  if (uiResult.error) {
11287
11326
  this.logger.warn(`findResource: UI resource error - ${uiResult.error}`);
@@ -11317,7 +11356,15 @@ var init_read_resource_flow = __esm({
11317
11356
  const { ctx } = this.input;
11318
11357
  const { resource, input, params } = this.state.required;
11319
11358
  try {
11320
- const contextProviders = new FlowContextProviders(this.scope.providers, this.deps);
11359
+ const sessionKey = this.state.sessionId ?? ctx.authInfo?.sessionId ?? `req-${Date.now()}-${Buffer.from(randomBytes2(16)).toString("hex")}`;
11360
+ const resourceViews = await resource.providers.buildViews(sessionKey, new Map(this.deps));
11361
+ const mergedContextDeps = new Map(this.deps);
11362
+ for (const [token, instance] of resourceViews.context) {
11363
+ if (!mergedContextDeps.has(token)) {
11364
+ mergedContextDeps.set(token, instance);
11365
+ }
11366
+ }
11367
+ const contextProviders = new FlowContextProviders(resource.providers, mergedContextDeps);
11321
11368
  const context = resource.create(input.uri, params, { ...ctx, contextProviders });
11322
11369
  const resourceHooks = this.scope.hooks.getClsHooks(resource.record.provide).map((hook) => {
11323
11370
  hook.run = async () => {
@@ -12759,7 +12806,7 @@ var init_prompt_instance = __esm({
12759
12806
  this.name = record.metadata.name;
12760
12807
  this.fullName = this.owner.id + ":" + this.name;
12761
12808
  this.scope = this._providers.getActiveScope();
12762
- this.hooks = this.scope.providers.getHooksRegistry();
12809
+ this.hooks = this.scope.hooks;
12763
12810
  this.ready = this.initialize();
12764
12811
  }
12765
12812
  async initialize() {
@@ -14225,7 +14272,7 @@ var init_skill_md_parser = __esm({
14225
14272
 
14226
14273
  // libs/sdk/src/skill/skill.utils.ts
14227
14274
  import { isClass as isClass11, getMetadata as getMetadata13, depsOfClass as depsOfClass8 } from "@frontmcp/di";
14228
- import { readFile as readFile2 } from "@frontmcp/utils";
14275
+ import { readFile as readFile2, readdir, fileExists } from "@frontmcp/utils";
14229
14276
  function collectSkillMetadata(cls) {
14230
14277
  const extended = getMetadata13(extendedSkillMetadata, cls);
14231
14278
  const seed = extended ? { ...extended } : {};
@@ -14305,12 +14352,16 @@ async function loadInstructions(source, basePath) {
14305
14352
  }
14306
14353
  throw new InvalidInstructionSourceError();
14307
14354
  }
14308
- function buildSkillContent(metadata, instructions) {
14355
+ function buildSkillContent(metadata, instructions, resolvedReferences) {
14356
+ let finalInstructions = instructions;
14357
+ if (resolvedReferences && resolvedReferences.length > 0) {
14358
+ finalInstructions += buildReferencesTable(resolvedReferences);
14359
+ }
14309
14360
  return {
14310
14361
  id: metadata.id ?? metadata.name,
14311
14362
  name: metadata.name,
14312
14363
  description: metadata.description,
14313
- instructions,
14364
+ instructions: finalInstructions,
14314
14365
  tools: normalizeToolRefs(metadata.tools),
14315
14366
  parameters: metadata.parameters,
14316
14367
  examples: metadata.examples,
@@ -14318,9 +14369,73 @@ function buildSkillContent(metadata, instructions) {
14318
14369
  compatibility: metadata.compatibility,
14319
14370
  specMetadata: metadata.specMetadata,
14320
14371
  allowedTools: metadata.allowedTools,
14321
- resources: metadata.resources
14372
+ resources: metadata.resources,
14373
+ resolvedReferences
14322
14374
  };
14323
14375
  }
14376
+ function buildReferencesTable(refs) {
14377
+ const lines = ["", "", "## References", "", "| Reference | Description |", "| --------- | ----------- |"];
14378
+ for (const ref of refs) {
14379
+ lines.push(`| \`${ref.name}\` | ${ref.description} |`);
14380
+ }
14381
+ return lines.join("\n");
14382
+ }
14383
+ async function resolveReferences(refsDir) {
14384
+ if (!await fileExists(refsDir)) return void 0;
14385
+ let files;
14386
+ try {
14387
+ files = (await readdir(refsDir)).filter((f) => f.endsWith(".md")).sort();
14388
+ } catch {
14389
+ return void 0;
14390
+ }
14391
+ if (files.length === 0) return void 0;
14392
+ const refs = [];
14393
+ for (const file of files) {
14394
+ const content = await readFile2(`${refsDir}/${file}`, "utf-8");
14395
+ const filenameWithoutExt = file.replace(/\.md$/, "");
14396
+ let name33 = filenameWithoutExt;
14397
+ let description = "";
14398
+ const fmMatch = content.match(/^---\r?\n([\s\S]*?)\r?\n---/);
14399
+ if (fmMatch) {
14400
+ const fmLines = fmMatch[1].split(/\r?\n/);
14401
+ for (const line of fmLines) {
14402
+ const colonIdx = line.indexOf(":");
14403
+ if (colonIdx === -1) continue;
14404
+ const key = line.slice(0, colonIdx).trim();
14405
+ const val = line.slice(colonIdx + 1).trim().replace(/^["']|["']$/g, "");
14406
+ if (key === "name" && val) name33 = val;
14407
+ if (key === "description" && val) description = val;
14408
+ }
14409
+ }
14410
+ if (!description) {
14411
+ const body = fmMatch ? content.substring(content.indexOf("---", 3) + 3).trim() : content.trim();
14412
+ description = extractFirstParagraph(body);
14413
+ }
14414
+ refs.push({ name: name33, description, filename: file });
14415
+ }
14416
+ return refs;
14417
+ }
14418
+ function extractFirstParagraph(body) {
14419
+ const lines = body.split(/\r?\n/);
14420
+ let foundHeading = false;
14421
+ const paragraphLines = [];
14422
+ for (const line of lines) {
14423
+ const trimmed = line.trim();
14424
+ if (!foundHeading && trimmed.startsWith("#")) {
14425
+ foundHeading = true;
14426
+ continue;
14427
+ }
14428
+ if (foundHeading) {
14429
+ if (trimmed === "") {
14430
+ if (paragraphLines.length > 0) break;
14431
+ continue;
14432
+ }
14433
+ if (trimmed.startsWith("#") || trimmed.startsWith("|") || trimmed.startsWith("-")) break;
14434
+ paragraphLines.push(trimmed);
14435
+ }
14436
+ }
14437
+ return paragraphLines.join(" ").slice(0, 200) || "";
14438
+ }
14324
14439
  function normalizeToolRefs(tools) {
14325
14440
  if (!tools) return [];
14326
14441
  return tools.map((tool) => {
@@ -14439,6 +14554,7 @@ var init_skill_utils = __esm({
14439
14554
  });
14440
14555
 
14441
14556
  // libs/sdk/src/skill/skill.instance.ts
14557
+ import { dirname, pathResolve } from "@frontmcp/utils";
14442
14558
  function createSkillInstance(record, providers, owner) {
14443
14559
  return new SkillInstance(record, providers, owner);
14444
14560
  }
@@ -14500,6 +14616,8 @@ var init_skill_instance = __esm({
14500
14616
  const filePath = this.record.filePath;
14501
14617
  const lastSlash = filePath.lastIndexOf("/");
14502
14618
  basePath = lastSlash > 0 ? filePath.substring(0, lastSlash) : void 0;
14619
+ } else if (this.record.kind === "VALUE" /* VALUE */ && this.record.callerDir) {
14620
+ basePath = this.record.callerDir;
14503
14621
  }
14504
14622
  this.cachedInstructions = await loadInstructions(this.metadata.instructions, basePath);
14505
14623
  return this.cachedInstructions;
@@ -14508,12 +14626,33 @@ var init_skill_instance = __esm({
14508
14626
  * Load the full skill content.
14509
14627
  * Results are cached after the first load.
14510
14628
  */
14629
+ /**
14630
+ * Resolve the base directory for this skill (for file/reference resolution).
14631
+ */
14632
+ getBaseDir() {
14633
+ if (this.record.kind === "FILE" /* FILE */) {
14634
+ return dirname(this.record.filePath) || void 0;
14635
+ }
14636
+ if (this.record.kind === "VALUE" /* VALUE */ && this.record.callerDir) {
14637
+ return this.record.callerDir;
14638
+ }
14639
+ return void 0;
14640
+ }
14511
14641
  async load() {
14512
14642
  if (this.cachedContent !== void 0) {
14513
14643
  return this.cachedContent;
14514
14644
  }
14515
14645
  const instructions = await this.loadInstructions();
14516
- const baseContent = buildSkillContent(this.metadata, instructions);
14646
+ const refsPath = this.metadata.resources?.references;
14647
+ let resolvedRefs;
14648
+ if (refsPath) {
14649
+ const baseDir = this.getBaseDir();
14650
+ const refsDir = refsPath.startsWith("/") ? refsPath : baseDir ? pathResolve(baseDir, refsPath) : void 0;
14651
+ if (refsDir) {
14652
+ resolvedRefs = await resolveReferences(refsDir);
14653
+ }
14654
+ }
14655
+ const baseContent = buildSkillContent(this.metadata, instructions, resolvedRefs);
14517
14656
  this.cachedContent = {
14518
14657
  ...baseContent,
14519
14658
  tags: this.tags,
@@ -15793,6 +15932,13 @@ var init_plugin_registry = __esm({
15793
15932
  getPluginNames() {
15794
15933
  return [...this.defs.values()].map((rec) => rec.metadata.name);
15795
15934
  }
15935
+ /**
15936
+ * Returns all ToolRegistries created by plugins in this registry.
15937
+ * Used for propagating server-level plugin tools to the scope.
15938
+ */
15939
+ getToolRegistries() {
15940
+ return [...this.pTools.values()];
15941
+ }
15796
15942
  buildMap(list) {
15797
15943
  const tokens = /* @__PURE__ */ new Set();
15798
15944
  const defs = /* @__PURE__ */ new Map();
@@ -15809,12 +15955,14 @@ var init_plugin_registry = __esm({
15809
15955
  buildGraph() {
15810
15956
  for (const token of this.tokens) {
15811
15957
  const rec = this.defs.get(token);
15958
+ if (!rec) throw new RegistryDependencyNotRegisteredError("Plugin", tokenName10(token), "self");
15812
15959
  const deps = pluginDiscoveryDeps(rec);
15813
15960
  for (const d of deps) {
15814
15961
  if (!this.providers.get(d)) {
15815
15962
  throw new RegistryDependencyNotRegisteredError("Plugin", tokenName10(token), tokenName10(d));
15816
15963
  }
15817
- this.graph.get(token).add(d);
15964
+ const edges = this.graph.get(token);
15965
+ if (edges) edges.add(d);
15818
15966
  }
15819
15967
  }
15820
15968
  }
@@ -15822,7 +15970,8 @@ var init_plugin_registry = __esm({
15822
15970
  this.logger?.verbose(`PluginRegistry: initializing ${this.tokens.size} plugin(s)`);
15823
15971
  for (const token of this.tokens) {
15824
15972
  const rec = this.defs.get(token);
15825
- const deps = this.graph.get(token);
15973
+ if (!rec) continue;
15974
+ const deps = this.graph.get(token) ?? /* @__PURE__ */ new Set();
15826
15975
  const providers = new ProviderRegistry(rec.metadata.providers ?? [], this.providers);
15827
15976
  await providers.ready;
15828
15977
  const pluginOwner = {
@@ -15865,9 +16014,9 @@ var init_plugin_registry = __esm({
15865
16014
  const klass = rec.provide;
15866
16015
  pluginInstance = new klass(...depsInstances);
15867
16016
  } else if (rec.kind === "FACTORY" /* FACTORY */) {
15868
- const deps2 = [...rec.inject()];
16017
+ const factoryDeps = [...rec.inject()];
15869
16018
  const args = [];
15870
- for (const d of deps2) args.push(await this.providers.resolveBootstrapDep(d));
16019
+ for (const d of factoryDeps) args.push(await this.providers.resolveBootstrapDep(d));
15871
16020
  pluginInstance = rec.useFactory(...args);
15872
16021
  } else if (rec.kind === "VALUE" /* VALUE */) {
15873
16022
  pluginInstance = rec.useValue;
@@ -17661,7 +17810,7 @@ var init_config_symbols = __esm({
17661
17810
  });
17662
17811
 
17663
17812
  // libs/sdk/src/builtin/config/providers/env-loader.ts
17664
- import { readFile as readFile3, fileExists, getCwd, getEnv as getEnv2, setEnv, pathResolve } from "@frontmcp/utils";
17813
+ import { readFile as readFile3, fileExists as fileExists2, getCwd, getEnv as getEnv2, setEnv, pathResolve as pathResolve2 } from "@frontmcp/utils";
17665
17814
  function parseEnvContent(content) {
17666
17815
  const result = {};
17667
17816
  const lines = content.split("\n");
@@ -17689,13 +17838,13 @@ function parseEnvContent(content) {
17689
17838
  }
17690
17839
  async function loadEnvFiles(basePath = getCwd(), envPath = ".env", localEnvPath = ".env.local") {
17691
17840
  const result = {};
17692
- const envFile = pathResolve(basePath, envPath);
17693
- if (await fileExists(envFile)) {
17841
+ const envFile = pathResolve2(basePath, envPath);
17842
+ if (await fileExists2(envFile)) {
17694
17843
  const content = await readFile3(envFile);
17695
17844
  Object.assign(result, parseEnvContent(content));
17696
17845
  }
17697
- const localFile = pathResolve(basePath, localEnvPath);
17698
- if (await fileExists(localFile)) {
17846
+ const localFile = pathResolve2(basePath, localEnvPath);
17847
+ if (await fileExists2(localFile)) {
17699
17848
  const content = await readFile3(localFile);
17700
17849
  Object.assign(result, parseEnvContent(content));
17701
17850
  }
@@ -17816,7 +17965,7 @@ var init_env_loader = __esm({
17816
17965
 
17817
17966
  // libs/sdk/src/builtin/config/providers/config-loader.ts
17818
17967
  import * as yaml2 from "js-yaml";
17819
- import { readFile as readFile4, fileExists as fileExists2, getCwd as getCwd2, pathResolve as pathResolve2 } from "@frontmcp/utils";
17968
+ import { readFile as readFile4, fileExists as fileExists3, getCwd as getCwd2, pathResolve as pathResolve3 } from "@frontmcp/utils";
17820
17969
  async function loadConfig(schema, options = {}) {
17821
17970
  const {
17822
17971
  basePath = getCwd2(),
@@ -17857,8 +18006,8 @@ async function loadYamlConfig(basePath, configPath) {
17857
18006
  const extensions = ["", ".yml", ".yaml"];
17858
18007
  const baseName = configPath.replace(/\.(ya?ml)$/, "");
17859
18008
  for (const ext of extensions) {
17860
- const fullPath = pathResolve2(basePath, baseName + ext);
17861
- if (await fileExists2(fullPath)) {
18009
+ const fullPath = pathResolve3(basePath, baseName + ext);
18010
+ if (await fileExists3(fullPath)) {
17862
18011
  const content = await readFile4(fullPath);
17863
18012
  const parsed = yaml2.load(content);
17864
18013
  if (parsed && typeof parsed === "object") {
@@ -18341,7 +18490,7 @@ var init_flow_instance = __esm({
18341
18490
  this.FlowClass = this.record.provide;
18342
18491
  this.ready = this.initialize();
18343
18492
  this.plan = this.record.metadata.plan;
18344
- this.hooks = scope.providers.getHooksRegistry();
18493
+ this.hooks = scope.hooks;
18345
18494
  this.logger = scope.logger.child("FlowInstance");
18346
18495
  }
18347
18496
  async initialize() {
@@ -18709,8 +18858,8 @@ var init_flow_registry = __esm({
18709
18858
  const rec = this.defs.get(token);
18710
18859
  const deps = rec.metadata.dependsOn ?? [];
18711
18860
  for (const d of deps) {
18712
- if (d == ScopeEntry2) {
18713
- this.graph.get(token).add(ScopeEntry2);
18861
+ if (d == ScopeEntry3) {
18862
+ this.graph.get(token).add(ScopeEntry3);
18714
18863
  } else {
18715
18864
  if (!this.providers.get(d)) {
18716
18865
  throw new RegistryDependencyNotRegisteredError("Flow", tokenName11(token), tokenName11(d));
@@ -19031,7 +19180,7 @@ var init_agent_scope = __esm({
19031
19180
  {
19032
19181
  scope: ProviderScope4.GLOBAL,
19033
19182
  name: "ScopeEntry",
19034
- provide: ScopeEntry2,
19183
+ provide: ScopeEntry3,
19035
19184
  useValue: agentScopeEntry
19036
19185
  },
19037
19186
  // Also register as Scope so getActiveScope() returns the agent's scope
@@ -19142,6 +19291,12 @@ var init_agent_scope = __esm({
19142
19291
  get toolUI() {
19143
19292
  return this.parentScope.toolUI;
19144
19293
  }
19294
+ get skills() {
19295
+ return this.parentScope.skills;
19296
+ }
19297
+ get scopeMetadata() {
19298
+ return this.parentScope.metadata;
19299
+ }
19145
19300
  // ============================================================================
19146
19301
  // Flow Execution
19147
19302
  // ============================================================================
@@ -19200,18 +19355,42 @@ var init_agent_scope = __esm({
19200
19355
  get agents() {
19201
19356
  return this.agentScope.agents;
19202
19357
  }
19358
+ get skills() {
19359
+ return this.agentScope.skills;
19360
+ }
19203
19361
  get notifications() {
19204
19362
  return this.agentScope.notifications;
19205
19363
  }
19206
19364
  get toolUI() {
19207
19365
  return this.agentScope.toolUI;
19208
19366
  }
19367
+ get transportService() {
19368
+ return void 0;
19369
+ }
19370
+ get rateLimitManager() {
19371
+ return void 0;
19372
+ }
19373
+ get elicitationStore() {
19374
+ return void 0;
19375
+ }
19376
+ get metadata() {
19377
+ return this.agentScope.scopeMetadata;
19378
+ }
19379
+ get record() {
19380
+ return void 0;
19381
+ }
19382
+ get ready() {
19383
+ return this.agentScope.ready;
19384
+ }
19209
19385
  registryFlows(...flows) {
19210
19386
  return this.agentScope.registryFlows(...flows);
19211
19387
  }
19212
19388
  runFlow(name33, input, deps) {
19213
19389
  return this.agentScope.runFlow(name33, input, deps);
19214
19390
  }
19391
+ runFlowForOutput(name33, input, deps) {
19392
+ return this.agentScope.runFlowForOutput(name33, input, deps);
19393
+ }
19215
19394
  };
19216
19395
  }
19217
19396
  });
@@ -19259,7 +19438,7 @@ var init_agent_instance = __esm({
19259
19438
  this.id = record.metadata.id ?? record.metadata.name;
19260
19439
  this.fullName = this.owner.id + ":" + this.name;
19261
19440
  this.scope = this.providers.getActiveScope();
19262
- this.hooks = this.scope.providers.getHooksRegistry();
19441
+ this.hooks = this.scope.hooks;
19263
19442
  this.inputSchema = record.metadata.inputSchema ?? {};
19264
19443
  this.outputSchema = record.metadata.outputSchema;
19265
19444
  this.systemInstructions = record.metadata.systemInstructions;
@@ -22392,10 +22571,10 @@ var init_esm_cache = __esm({
22392
22571
  if (!this.cacheDir) return void 0;
22393
22572
  try {
22394
22573
  const path = __require("node:path");
22395
- const { fileExists: fileExists5, readJSON } = __require("@frontmcp/utils");
22574
+ const { fileExists: fileExists6, readJSON } = __require("@frontmcp/utils");
22396
22575
  const entryDir = this.getEntryDir(packageName, version);
22397
22576
  const metaPath = path.join(entryDir, "meta.json");
22398
- if (!await fileExists5(metaPath)) {
22577
+ if (!await fileExists6(metaPath)) {
22399
22578
  return void 0;
22400
22579
  }
22401
22580
  const meta = await readJSON(metaPath);
@@ -22405,7 +22584,7 @@ var init_esm_cache = __esm({
22405
22584
  if (Date.now() - meta.cachedAt > this.maxAgeMs) {
22406
22585
  return void 0;
22407
22586
  }
22408
- if (!await fileExists5(meta.bundlePath)) {
22587
+ if (!await fileExists6(meta.bundlePath)) {
22409
22588
  return void 0;
22410
22589
  }
22411
22590
  this.memoryStore.set(memKey, meta);
@@ -22469,20 +22648,20 @@ var init_esm_cache = __esm({
22469
22648
  if (!this.cacheDir) return;
22470
22649
  try {
22471
22650
  const path = __require("node:path");
22472
- const { fileExists: fileExists5, readJSON, rm } = __require("@frontmcp/utils");
22473
- const { readdir } = __require("@frontmcp/utils");
22474
- if (!await fileExists5(this.cacheDir)) {
22651
+ const { fileExists: fileExists6, readJSON, rm } = __require("@frontmcp/utils");
22652
+ const { readdir: readdir2 } = __require("@frontmcp/utils");
22653
+ if (!await fileExists6(this.cacheDir)) {
22475
22654
  return;
22476
22655
  }
22477
22656
  let entries;
22478
22657
  try {
22479
- entries = await readdir(this.cacheDir);
22658
+ entries = await readdir2(this.cacheDir);
22480
22659
  } catch {
22481
22660
  return;
22482
22661
  }
22483
22662
  for (const dirEntry of entries) {
22484
22663
  const metaPath = path.join(this.cacheDir, dirEntry, "meta.json");
22485
- if (await fileExists5(metaPath)) {
22664
+ if (await fileExists6(metaPath)) {
22486
22665
  const meta = await readJSON(metaPath);
22487
22666
  if (meta?.packageName === packageName) {
22488
22667
  await rm(path.join(this.cacheDir, dirEntry), { recursive: true, force: true });
@@ -22508,20 +22687,20 @@ var init_esm_cache = __esm({
22508
22687
  if (!this.cacheDir) return removed;
22509
22688
  try {
22510
22689
  const path = __require("node:path");
22511
- const { fileExists: fileExists5, readJSON, rm } = __require("@frontmcp/utils");
22512
- const { readdir } = __require("@frontmcp/utils");
22513
- if (!await fileExists5(this.cacheDir)) {
22690
+ const { fileExists: fileExists6, readJSON, rm } = __require("@frontmcp/utils");
22691
+ const { readdir: readdir2 } = __require("@frontmcp/utils");
22692
+ if (!await fileExists6(this.cacheDir)) {
22514
22693
  return removed;
22515
22694
  }
22516
22695
  let entries;
22517
22696
  try {
22518
- entries = await readdir(this.cacheDir);
22697
+ entries = await readdir2(this.cacheDir);
22519
22698
  } catch {
22520
22699
  return removed;
22521
22700
  }
22522
22701
  for (const dirEntry of entries) {
22523
22702
  const metaPath = path.join(this.cacheDir, dirEntry, "meta.json");
22524
- if (await fileExists5(metaPath)) {
22703
+ if (await fileExists6(metaPath)) {
22525
22704
  const meta = await readJSON(metaPath);
22526
22705
  if (meta && now - meta.cachedAt > threshold) {
22527
22706
  await rm(path.join(this.cacheDir, dirEntry), { recursive: true, force: true });
@@ -24287,20 +24466,22 @@ function createSessionId(protocol, token, options) {
24287
24466
  return { id, payload };
24288
24467
  }
24289
24468
  function updateSessionPayload(sessionId, updates) {
24469
+ let payload;
24290
24470
  const existing = cache.get(sessionId);
24291
24471
  if (existing) {
24292
- Object.assign(existing, updates);
24293
- cache.set(sessionId, existing);
24294
- return true;
24295
- }
24296
- const decrypted = safeDecrypt(sessionId);
24297
- if (hasValidSessionStructure(decrypted) || isValidPublicSessionPayload(decrypted)) {
24298
- const payload = decrypted;
24299
- Object.assign(payload, updates);
24300
- cache.set(sessionId, payload);
24301
- return true;
24472
+ payload = existing;
24473
+ } else {
24474
+ const decrypted = safeDecrypt(sessionId);
24475
+ if (hasValidSessionStructure(decrypted) || isValidPublicSessionPayload(decrypted)) {
24476
+ payload = decrypted;
24477
+ }
24302
24478
  }
24303
- return false;
24479
+ if (!payload) return null;
24480
+ Object.assign(payload, updates);
24481
+ const newSessionId = encryptJson(payload);
24482
+ cache.set(sessionId, payload);
24483
+ cache.set(newSessionId, payload);
24484
+ return newSessionId;
24304
24485
  }
24305
24486
  var cache;
24306
24487
  var init_session_id_utils = __esm({
@@ -25503,7 +25684,7 @@ var init_oauth_authorize_flow = __esm({
25503
25684
 
25504
25685
  // libs/sdk/src/auth/flows/oauth.register.flow.ts
25505
25686
  import { z as z56 } from "zod";
25506
- import { randomUUID as randomUUID12, randomBytes as randomBytes2, base64urlEncode, isProduction as isProduction3 } from "@frontmcp/utils";
25687
+ import { randomUUID as randomUUID12, randomBytes as randomBytes3, base64urlEncode, isProduction as isProduction3 } from "@frontmcp/utils";
25507
25688
  var CLIENTS, inputSchema16, outputSchema14, registrationRequestSchema, stateSchema14, plan14, name15, Stage15, OauthRegisterFlow;
25508
25689
  var init_oauth_register_flow = __esm({
25509
25690
  "libs/sdk/src/auth/flows/oauth.register.flow.ts"() {
@@ -25605,7 +25786,7 @@ var init_oauth_register_flow = __esm({
25605
25786
  const client_id = randomUUID12();
25606
25787
  let client_secret;
25607
25788
  if (token_endpoint_auth_method === "client_secret_post" || token_endpoint_auth_method === "client_secret_basic") {
25608
- client_secret = base64urlEncode(randomBytes2(24));
25789
+ client_secret = base64urlEncode(randomBytes3(24));
25609
25790
  }
25610
25791
  this.registered = {
25611
25792
  client_id,
@@ -26831,7 +27012,7 @@ var init_oauth_provider_callback_flow = __esm({
26831
27012
 
26832
27013
  // libs/sdk/src/auth/instances/instance.local-primary-auth.ts
26833
27014
  import { SignJWT } from "jose";
26834
- import { randomBytes as randomBytes3, randomUUID as randomUUID16, sha256Hex as sha256Hex4, getEnv as getEnv3, base64urlDecode } from "@frontmcp/utils";
27015
+ import { randomBytes as randomBytes4, randomUUID as randomUUID16, sha256Hex as sha256Hex4, getEnv as getEnv3, base64urlDecode } from "@frontmcp/utils";
26835
27016
  import { JwksService as JwksService4, InMemoryAuthorizationStore as InMemoryAuthorizationStore2, verifyPkce } from "@frontmcp/auth";
26836
27017
  import {
26837
27018
  InMemoryOrchestratedTokenStore,
@@ -26854,7 +27035,7 @@ var init_instance_local_primary_auth = __esm({
26854
27035
  init_cimd();
26855
27036
  init_oauth_provider_callback_flow();
26856
27037
  init_auth_internal_errors();
26857
- DEFAULT_NO_AUTH_SECRET = randomBytes3(32);
27038
+ DEFAULT_NO_AUTH_SECRET = randomBytes4(32);
26858
27039
  LocalPrimaryAuth = class extends FrontMcpAuth {
26859
27040
  constructor(scope, providers, options) {
26860
27041
  super(options);
@@ -27772,11 +27953,21 @@ var init_http_request_flow = __esm({
27772
27953
  const sessionId = authorization.session.id;
27773
27954
  request[ServerRequestTokens.sessionId] = sessionId;
27774
27955
  if (this.scope.notifications.isSessionTerminated(sessionId)) {
27775
- this.logger.warn(`[${this.requestId}] Request to terminated session: ${sessionId.slice(0, 20)}...`);
27776
- this.respond(httpRespond.notFound("Session not found"));
27777
- return;
27956
+ const body = request.body;
27957
+ if (body?.method === "initialize") {
27958
+ this.logger.info(
27959
+ `[${this.requestId}] Initialize with terminated session ${sessionId.slice(0, 20)}... - allowing reconnect`
27960
+ );
27961
+ authorization.session = void 0;
27962
+ delete request[ServerRequestTokens.sessionId];
27963
+ delete request.headers["mcp-session-id"];
27964
+ } else {
27965
+ this.logger.warn(`[${this.requestId}] Request to terminated session: ${sessionId.slice(0, 20)}...`);
27966
+ this.respond(httpRespond.notFound("Session not found"));
27967
+ return;
27968
+ }
27778
27969
  }
27779
- const protocol = authorization.session.payload?.protocol;
27970
+ const protocol = authorization.session?.payload?.protocol;
27780
27971
  if (protocol) {
27781
27972
  this.logger.info(`[${this.requestId}] decision from session: ${protocol}`);
27782
27973
  this.state.set("intent", protocol);
@@ -27899,6 +28090,18 @@ var init_http_request_flow = __esm({
27899
28090
  this.respond(httpRespond.notFound("Session not found"));
27900
28091
  return;
27901
28092
  }
28093
+ const authorization = request[ServerRequestTokens.auth];
28094
+ if (authorization?.token) {
28095
+ const transportService = this.scope.transportService;
28096
+ if (transportService) {
28097
+ for (const protocol of ["streamable-http", "sse"]) {
28098
+ try {
28099
+ await transportService.destroyTransporter(protocol, authorization.token, sessionId);
28100
+ } catch {
28101
+ }
28102
+ }
28103
+ }
28104
+ }
27902
28105
  this.logger.info(`[${this.requestId}] Session terminated: ${sessionId}`);
27903
28106
  this.respond(httpRespond.noContent());
27904
28107
  } catch (error) {
@@ -28076,8 +28279,13 @@ var init_transport_remote = __esm({
28076
28279
  async destroy(_reason) {
28077
28280
  throw new MethodNotImplementedError("RemoteTransporter", "destroy");
28078
28281
  }
28282
+ get isInitialized() {
28283
+ return false;
28284
+ }
28079
28285
  markAsInitialized() {
28080
28286
  }
28287
+ resetForReinitialization() {
28288
+ }
28081
28289
  };
28082
28290
  }
28083
28291
  });
@@ -28337,6 +28545,11 @@ var init_sse_transport = __esm({
28337
28545
  // libs/sdk/src/transport/mcp-handlers/initialize-request.handler.ts
28338
28546
  import { InitializeRequestSchema } from "@frontmcp/protocol";
28339
28547
  import { LATEST_PROTOCOL_VERSION, SUPPORTED_PROTOCOL_VERSIONS } from "@frontmcp/protocol";
28548
+ function persistInitPayload(sessionId, initPayload, ctx) {
28549
+ updateSessionPayload(sessionId, initPayload);
28550
+ const transport = ctx.authInfo?.transport;
28551
+ transport?.setInitSessionPayload(initPayload);
28552
+ }
28340
28553
  function guardClientVersion(clientVersion) {
28341
28554
  const parsed = new Date(clientVersion);
28342
28555
  if (isNaN(parsed.getTime())) {
@@ -28375,6 +28588,10 @@ function initializeRequestHandler({
28375
28588
  elicitation: request.params.capabilities.elicitation
28376
28589
  };
28377
28590
  scope.notifications.setClientCapabilities(sessionId, clientCapabilities);
28591
+ await scope.transportService.updateStoredSessionCapabilities(
28592
+ sessionId,
28593
+ clientCapabilities
28594
+ );
28378
28595
  detectedPlatform = detectPlatformFromCapabilities(clientCapabilities);
28379
28596
  }
28380
28597
  if (request.params.clientInfo) {
@@ -28393,22 +28610,30 @@ function initializeRequestHandler({
28393
28610
  if (finalPlatform) {
28394
28611
  ctx.authInfo.sessionIdPayload.platformType = finalPlatform;
28395
28612
  }
28396
- updateSessionPayload(sessionId, {
28397
- clientName,
28398
- clientVersion,
28399
- supportsElicitation: clientSupportsElicitation,
28400
- ...finalPlatform && { platformType: finalPlatform }
28401
- });
28613
+ persistInitPayload(
28614
+ sessionId,
28615
+ {
28616
+ clientName,
28617
+ clientVersion,
28618
+ supportsElicitation: clientSupportsElicitation,
28619
+ ...finalPlatform && { platformType: finalPlatform }
28620
+ },
28621
+ ctx
28622
+ );
28402
28623
  }
28403
28624
  } else if (ctx.authInfo?.sessionIdPayload) {
28404
28625
  ctx.authInfo.sessionIdPayload.supportsElicitation = clientSupportsElicitation;
28405
28626
  if (detectedPlatform) {
28406
28627
  ctx.authInfo.sessionIdPayload.platformType = detectedPlatform;
28407
28628
  }
28408
- updateSessionPayload(sessionId, {
28409
- supportsElicitation: clientSupportsElicitation,
28410
- ...detectedPlatform && { platformType: detectedPlatform }
28411
- });
28629
+ persistInitPayload(
28630
+ sessionId,
28631
+ {
28632
+ supportsElicitation: clientSupportsElicitation,
28633
+ ...detectedPlatform && { platformType: detectedPlatform }
28634
+ },
28635
+ ctx
28636
+ );
28412
28637
  }
28413
28638
  }
28414
28639
  const requestedVersion = request.params.protocolVersion;
@@ -29388,15 +29613,43 @@ var init_transport_local_adapter = __esm({
29388
29613
  * New elicit requests will cancel any pending one.
29389
29614
  */
29390
29615
  pendingElicit;
29616
+ /**
29617
+ * Session payload fields set during MCP initialize.
29618
+ * Stored on the adapter instance so they survive across requests in SSE mode
29619
+ * (where each POST creates a fresh anonymous HTTP session) and are available
29620
+ * in distributed environments without relying on local in-memory caches.
29621
+ */
29622
+ initSessionPayload;
29623
+ /**
29624
+ * Called by the initialize request handler to persist initialization data
29625
+ * on the transport adapter instance. This data is merged into the session
29626
+ * payload on every subsequent request via ensureAuthInfo().
29627
+ */
29628
+ setInitSessionPayload(payload) {
29629
+ this.initSessionPayload = payload;
29630
+ }
29391
29631
  #requestId = 1;
29392
29632
  ready;
29393
29633
  server;
29634
+ /**
29635
+ * Whether this transport has already been initialized via the MCP initialize handshake.
29636
+ * Override in subclasses that track initialization state.
29637
+ */
29638
+ get isInitialized() {
29639
+ return false;
29640
+ }
29394
29641
  /**
29395
29642
  * Marks this transport as pre-initialized for session recreation.
29396
29643
  * Override in subclasses that need to set the MCP SDK's _initialized flag.
29397
29644
  */
29398
29645
  markAsInitialized() {
29399
29646
  }
29647
+ /**
29648
+ * Resets initialization state to allow re-initialization.
29649
+ * Override in subclasses that support session re-initialization.
29650
+ */
29651
+ resetForReinitialization() {
29652
+ }
29400
29653
  connectServer() {
29401
29654
  const { info, apps } = this.scope.metadata;
29402
29655
  const hasRemoteApps = apps?.some((app) => this.isRemoteApp(app)) ?? false;
@@ -29406,6 +29659,7 @@ var init_transport_local_adapter = __esm({
29406
29659
  const hasAgents = this.scope.agents.hasAny();
29407
29660
  const completionsCapability = hasPrompts || hasResources ? { completions: {} } : {};
29408
29661
  const remoteCapabilities = hasRemoteApps ? this.buildRemoteCapabilities() : {};
29662
+ const elicitationCapability = this.scope.metadata.elicitation?.enabled ? { elicitation: {} } : {};
29409
29663
  const serverOptions = {
29410
29664
  instructions: "",
29411
29665
  capabilities: {
@@ -29418,7 +29672,8 @@ var init_transport_local_adapter = __esm({
29418
29672
  // Include agent capabilities (agents as tools)
29419
29673
  ...completionsCapability,
29420
29674
  // MCP logging protocol support - allows clients to set log level via logging/setLevel
29421
- logging: {}
29675
+ logging: {},
29676
+ ...elicitationCapability
29422
29677
  },
29423
29678
  serverInfo: info
29424
29679
  };
@@ -29483,8 +29738,28 @@ var init_transport_local_adapter = __esm({
29483
29738
  }
29484
29739
  ensureAuthInfo(req, transport) {
29485
29740
  const { token, user, session } = req[ServerRequestTokens.auth];
29486
- const sessionId = session?.id ?? `fallback:${Date.now()}`;
29487
- const sessionPayload = session?.payload ?? { protocol: "streamable-http" };
29741
+ if (!session?.id) {
29742
+ throw new Error(
29743
+ "Session ID is required in ensureAuthInfo. This indicates a bug in session propagation \u2014 the session should have been set by the flow."
29744
+ );
29745
+ }
29746
+ const sessionId = session.id;
29747
+ const sessionPayload = session.payload ?? { protocol: "streamable-http" };
29748
+ if (this.initSessionPayload) {
29749
+ const init = this.initSessionPayload;
29750
+ if (init.supportsElicitation !== void 0 && sessionPayload.supportsElicitation === void 0) {
29751
+ sessionPayload.supportsElicitation = init.supportsElicitation;
29752
+ }
29753
+ if (init.platformType !== void 0 && sessionPayload.platformType === void 0) {
29754
+ sessionPayload["platformType"] = init.platformType;
29755
+ }
29756
+ if (init.clientName !== void 0 && sessionPayload.clientName === void 0) {
29757
+ sessionPayload.clientName = init.clientName;
29758
+ }
29759
+ if (init.clientVersion !== void 0 && sessionPayload.clientVersion === void 0) {
29760
+ sessionPayload.clientVersion = init.clientVersion;
29761
+ }
29762
+ }
29488
29763
  const authInfo = {
29489
29764
  token,
29490
29765
  user,
@@ -29504,6 +29779,17 @@ var init_transport_local_adapter = __esm({
29504
29779
  get elicitStore() {
29505
29780
  return this.scope.elicitationStore;
29506
29781
  }
29782
+ /**
29783
+ * Get the elicitation store, throwing if not initialized.
29784
+ * Use in contexts where elicitation is required (sendElicitRequest, cancelPendingElicit).
29785
+ */
29786
+ requireElicitStore() {
29787
+ const store = this.elicitStore;
29788
+ if (!store) {
29789
+ throw new Error("Elicitation store not initialized");
29790
+ }
29791
+ return store;
29792
+ }
29507
29793
  /**
29508
29794
  * Cancel any pending elicitation request.
29509
29795
  * Called before sending a new elicit to enforce single-elicit-per-session.
@@ -29521,9 +29807,12 @@ var init_transport_local_adapter = __esm({
29521
29807
  clearTimeout(this.pendingElicit.timeoutHandle);
29522
29808
  this.pendingElicit.resolve({ status: "cancel" });
29523
29809
  const sessionId = this.key.sessionId;
29524
- const pending = await this.elicitStore.getPending(sessionId);
29525
- if (pending) {
29526
- await this.elicitStore.publishResult(pending.elicitId, sessionId, { status: "cancel" });
29810
+ const store = this.elicitStore;
29811
+ if (store) {
29812
+ const pending = await store.getPending(sessionId);
29813
+ if (pending) {
29814
+ await store.publishResult(pending.elicitId, sessionId, { status: "cancel" });
29815
+ }
29527
29816
  }
29528
29817
  this.pendingElicit = void 0;
29529
29818
  this.logger.info("Cancelled previous pending elicit");
@@ -30120,7 +30409,7 @@ import {
30120
30409
  hkdfSha256,
30121
30410
  encryptAesGcm,
30122
30411
  decryptAesGcm,
30123
- randomBytes as randomBytes4,
30412
+ randomBytes as randomBytes5,
30124
30413
  base64urlEncode as base64urlEncode2,
30125
30414
  base64urlDecode as base64urlDecode2,
30126
30415
  getEnv as getEnv4
@@ -30144,7 +30433,7 @@ async function deriveElicitationKey(sessionId, secret) {
30144
30433
  async function encryptElicitationData(data, sessionId, secret) {
30145
30434
  const key = await deriveElicitationKey(sessionId, secret);
30146
30435
  const plaintext = textEncoder.encode(JSON.stringify(data));
30147
- const iv = randomBytes4(12);
30436
+ const iv = randomBytes5(12);
30148
30437
  const { ciphertext, tag } = encryptAesGcm(key, plaintext, iv);
30149
30438
  return {
30150
30439
  alg: "A256GCM",
@@ -30880,7 +31169,8 @@ var init_transport_sse_adapter = __esm({
30880
31169
  const elicitId = elicitationId ?? `elicit-${this.newRequestId}`;
30881
31170
  const sessionId = this.key.sessionId;
30882
31171
  const expiresAt = Date.now() + ttl;
30883
- await this.elicitStore.setPending({
31172
+ const store = this.requireElicitStore();
31173
+ await store.setPending({
30884
31174
  elicitId,
30885
31175
  sessionId,
30886
31176
  createdAt: Date.now(),
@@ -30923,10 +31213,10 @@ var init_transport_sse_adapter = __esm({
30923
31213
  settled = true;
30924
31214
  this.pendingElicit = void 0;
30925
31215
  await unsubscribe?.();
30926
- await this.elicitStore.deletePending(sessionId);
31216
+ await store.deletePending(sessionId);
30927
31217
  reject(new ElicitationTimeoutError(elicitId, ttl));
30928
31218
  }, ttl);
30929
- this.elicitStore.subscribeResult(
31219
+ store.subscribeResult(
30930
31220
  elicitId,
30931
31221
  (result) => {
30932
31222
  safeResolve(result);
@@ -30940,7 +31230,7 @@ var init_transport_sse_adapter = __esm({
30940
31230
  clearTimeout(timeoutHandle);
30941
31231
  this.pendingElicit = void 0;
30942
31232
  await unsubscribe?.();
30943
- await this.elicitStore.deletePending(sessionId);
31233
+ await store.deletePending(sessionId);
30944
31234
  reject(err);
30945
31235
  });
30946
31236
  this.pendingElicit = {
@@ -30974,9 +31264,16 @@ var init_streamable_http_transport = __esm({
30974
31264
  * so we need to create fresh instances for each request.
30975
31265
  */
30976
31266
  _constructorOptions;
31267
+ /**
31268
+ * When true, the transport recreates the internal transport for each request.
31269
+ * Decoupled from sessionIdGenerator so stateless transports can still
31270
+ * provide a session ID (e.g., '__stateless__') in the response header.
31271
+ */
31272
+ _isStateless;
30977
31273
  constructor(options = {}) {
30978
31274
  super(options);
30979
31275
  this._constructorOptions = options;
31276
+ this._isStateless = options.isStateless ?? false;
30980
31277
  }
30981
31278
  /**
30982
31279
  * Returns whether the transport has been initialized.
@@ -31016,6 +31313,31 @@ var init_streamable_http_transport = __esm({
31016
31313
  }
31017
31314
  this._applyInitState(webTransport, sessionId);
31018
31315
  }
31316
+ /**
31317
+ * Resets the transport's initialization state to allow re-initialization.
31318
+ *
31319
+ * This is needed when a client reconnects after terminating its session:
31320
+ * the cached transport is still marked as initialized, but the client
31321
+ * needs to re-initialize. Resetting _initialized and sessionId allows
31322
+ * the MCP SDK to process a fresh initialize request.
31323
+ *
31324
+ * This is the inverse of setInitializationState().
31325
+ */
31326
+ resetForReinitialization() {
31327
+ const webTransport = this._webStandardTransport;
31328
+ if (!webTransport) {
31329
+ this._pendingInitState = void 0;
31330
+ return;
31331
+ }
31332
+ if (!("_initialized" in webTransport)) {
31333
+ throw new InvalidTransportSessionError(
31334
+ "[RecreateableStreamableHTTPServerTransport] Expected _initialized field not found on internal transport. This may indicate an incompatible MCP SDK version."
31335
+ );
31336
+ }
31337
+ webTransport._initialized = false;
31338
+ webTransport.sessionId = void 0;
31339
+ this._pendingInitState = void 0;
31340
+ }
31019
31341
  /**
31020
31342
  * Applies initialization state to the internal transport.
31021
31343
  * @param webTransport - The internal _webStandardTransport object
@@ -31040,7 +31362,7 @@ var init_streamable_http_transport = __esm({
31040
31362
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
31041
31363
  async handleRequest(req, res, body) {
31042
31364
  const oldWebTransport = this._webStandardTransport;
31043
- if (oldWebTransport && !oldWebTransport.sessionIdGenerator && oldWebTransport._hasHandledRequest) {
31365
+ if (oldWebTransport && this._isStateless && oldWebTransport._hasHandledRequest) {
31044
31366
  const fresh = new WebStandardStreamableHTTPServerTransport(this._constructorOptions);
31045
31367
  fresh.onmessage = oldWebTransport.onmessage;
31046
31368
  fresh.onclose = oldWebTransport.onclose;
@@ -31072,14 +31394,16 @@ var init_transport_streamable_http_adapter = __esm({
31072
31394
  init_streamable_http_transport();
31073
31395
  init_errors();
31074
31396
  resolveSessionIdGenerator = (transportType, sessionId) => {
31075
- return transportType === "stateless-http" ? void 0 : () => sessionId;
31397
+ return transportType === "stateless-http" ? () => "__stateless__" : () => sessionId;
31076
31398
  };
31077
31399
  TransportStreamableHttpAdapter = class extends LocalTransportAdapter {
31078
31400
  createTransport(sessionId, response) {
31079
31401
  const sessionIdGenerator = resolveSessionIdGenerator(this.key.type, sessionId);
31080
31402
  const eventStore = this.scope.eventStore;
31403
+ const isStateless = this.key.type === "stateless-http";
31081
31404
  return new RecreateableStreamableHTTPServerTransport({
31082
31405
  sessionIdGenerator,
31406
+ isStateless,
31083
31407
  onsessionclosed: () => {
31084
31408
  },
31085
31409
  onsessioninitialized: (sessionId2) => {
@@ -31173,6 +31497,7 @@ var init_transport_streamable_http_adapter = __esm({
31173
31497
  */
31174
31498
  async sendElicitRequest(relatedRequestId, message, requestedSchema, options) {
31175
31499
  await this.cancelPendingElicit();
31500
+ const store = this.requireElicitStore();
31176
31501
  const sessionId = this.key.sessionId;
31177
31502
  const flowOutput = await this.scope.runFlowForOutput("elicitation:request", {
31178
31503
  relatedRequestId,
@@ -31197,7 +31522,7 @@ var init_transport_streamable_http_adapter = __esm({
31197
31522
  } catch (error) {
31198
31523
  this.logger.error("[StreamableHttpAdapter] sendElicitRequest: transport.send() failed", error);
31199
31524
  try {
31200
- await this.elicitStore.deletePending(sessionId);
31525
+ await store.deletePending(sessionId);
31201
31526
  this.logger.verbose("[StreamableHttpAdapter] sendElicitRequest: cleaned up pending record after send failure");
31202
31527
  } catch (cleanupError) {
31203
31528
  this.logger.warn("[StreamableHttpAdapter] sendElicitRequest: failed to clean up pending record", cleanupError);
@@ -31229,10 +31554,10 @@ var init_transport_streamable_http_adapter = __esm({
31229
31554
  settled = true;
31230
31555
  this.pendingElicit = void 0;
31231
31556
  await unsubscribe?.();
31232
- await this.elicitStore.deletePending(sessionId);
31557
+ await store.deletePending(sessionId);
31233
31558
  reject(new ElicitationTimeoutError(elicitId, ttl));
31234
31559
  }, ttl);
31235
- this.elicitStore.subscribeResult(
31560
+ store.subscribeResult(
31236
31561
  elicitId,
31237
31562
  (result) => {
31238
31563
  safeResolve(result);
@@ -31246,7 +31571,7 @@ var init_transport_streamable_http_adapter = __esm({
31246
31571
  clearTimeout(timeoutHandle);
31247
31572
  this.pendingElicit = void 0;
31248
31573
  await unsubscribe?.();
31249
- await this.elicitStore.deletePending(sessionId);
31574
+ await store.deletePending(sessionId);
31250
31575
  reject(err);
31251
31576
  });
31252
31577
  this.pendingElicit = {
@@ -31257,6 +31582,20 @@ var init_transport_streamable_http_adapter = __esm({
31257
31582
  };
31258
31583
  });
31259
31584
  }
31585
+ get isInitialized() {
31586
+ return this.transport.isInitialized || this.transport.hasPendingInitState;
31587
+ }
31588
+ /**
31589
+ * Resets the transport's initialization state to allow re-initialization.
31590
+ * Used when a client retries initialize on an already-initialized session
31591
+ * (e.g., after reconnect following session termination).
31592
+ */
31593
+ resetForReinitialization() {
31594
+ this.transport.resetForReinitialization();
31595
+ this.logger.info("[StreamableHttpAdapter] Reset transport for re-initialization", {
31596
+ sessionId: this.key.sessionId?.slice(0, 20)
31597
+ });
31598
+ }
31260
31599
  /**
31261
31600
  * Marks this transport as pre-initialized for session recreation.
31262
31601
  * This is needed when recreating a transport from Redis because the
@@ -31331,6 +31670,9 @@ var init_transport_local = __esm({
31331
31670
  res.status(500).json(rpcError("Internal error"));
31332
31671
  }
31333
31672
  }
31673
+ get isInitialized() {
31674
+ return this.adapter.isInitialized;
31675
+ }
31334
31676
  /**
31335
31677
  * Marks this transport as pre-initialized for session recreation.
31336
31678
  * This is needed when recreating a transport from Redis because the
@@ -31339,6 +31681,9 @@ var init_transport_local = __esm({
31339
31681
  markAsInitialized() {
31340
31682
  this.adapter.markAsInitialized();
31341
31683
  }
31684
+ resetForReinitialization() {
31685
+ this.adapter.resetForReinitialization();
31686
+ }
31342
31687
  async destroy(reason) {
31343
31688
  try {
31344
31689
  await this.adapter.destroy(reason);
@@ -31725,6 +32070,79 @@ var init_ext_apps2 = __esm({
31725
32070
  // libs/sdk/src/transport/flows/handle.streamable-http.flow.ts
31726
32071
  import { z as z62 } from "zod";
31727
32072
  import { ElicitResultSchema as ElicitResultSchema2, RequestSchema, CallToolResultSchema as CallToolResultSchema3 } from "@frontmcp/protocol";
32073
+ function resolveStreamableHttpSession(params) {
32074
+ const { rawHeader, authorizationSession, createSession } = params;
32075
+ const rawMcpSessionHeader = typeof rawHeader === "string" ? rawHeader : void 0;
32076
+ const mcpSessionHeader = validateMcpSessionHeader(rawMcpSessionHeader);
32077
+ if (rawHeader !== void 0 && !mcpSessionHeader) {
32078
+ return { responded404: true, createdNew: false };
32079
+ }
32080
+ if (mcpSessionHeader) {
32081
+ if (authorizationSession?.id === mcpSessionHeader) {
32082
+ return { session: authorizationSession, createdNew: false, responded404: false };
32083
+ }
32084
+ return { session: { id: mcpSessionHeader }, createdNew: false, responded404: false };
32085
+ }
32086
+ if (authorizationSession) {
32087
+ return { session: authorizationSession, createdNew: false, responded404: false };
32088
+ }
32089
+ return { session: createSession(), createdNew: true, responded404: false };
32090
+ }
32091
+ function classifyStreamableHttpRequest(params) {
32092
+ const { method, body } = params;
32093
+ if (method.toUpperCase() === "GET") {
32094
+ return { requestType: "sseListener" };
32095
+ }
32096
+ const jsonRpcMethod = body?.method;
32097
+ if (jsonRpcMethod === "initialize") {
32098
+ return { requestType: "initialize" };
32099
+ }
32100
+ if (typeof jsonRpcMethod === "string" && jsonRpcMethod.startsWith("ui/")) {
32101
+ return { requestType: "extApps" };
32102
+ }
32103
+ if (ElicitResultSchema2.safeParse(body?.result).success) {
32104
+ return { requestType: "elicitResult" };
32105
+ }
32106
+ if (jsonRpcMethod && RequestSchema.safeParse(body).success) {
32107
+ return { requestType: "message" };
32108
+ }
32109
+ return { error: "Invalid Request" };
32110
+ }
32111
+ function syncStreamableHttpAuthorizationSession(authorization, session) {
32112
+ if (!authorization.session) {
32113
+ authorization.session = session;
32114
+ }
32115
+ }
32116
+ async function lookupStreamableHttpTransport(params) {
32117
+ const { transportService, token, sessionId, response } = params;
32118
+ const inMemoryTransport = await transportService.getTransporter("streamable-http", token, sessionId);
32119
+ if (inMemoryTransport) {
32120
+ return { kind: "transport", source: "memory", transport: inMemoryTransport };
32121
+ }
32122
+ let recreationError;
32123
+ try {
32124
+ const storedSession = await transportService.getStoredSession("streamable-http", token, sessionId);
32125
+ if (storedSession) {
32126
+ const recreatedTransport = await transportService.recreateTransporter(
32127
+ "streamable-http",
32128
+ token,
32129
+ sessionId,
32130
+ storedSession,
32131
+ response
32132
+ );
32133
+ if (recreatedTransport) {
32134
+ return { kind: "transport", source: "redis", transport: recreatedTransport };
32135
+ }
32136
+ }
32137
+ } catch (error) {
32138
+ recreationError = error;
32139
+ }
32140
+ const wasCreated = await transportService.wasSessionCreatedAsync("streamable-http", token, sessionId);
32141
+ if (wasCreated) {
32142
+ return { kind: "session-expired", recreationError };
32143
+ }
32144
+ return { kind: "session-not-initialized", recreationError };
32145
+ }
31728
32146
  var plan19, stateSessionSchema, stateSchema18, name20, Stage20, HandleStreamableHttpFlow;
31729
32147
  var init_handle_streamable_http_flow = __esm({
31730
32148
  "libs/sdk/src/transport/flows/handle.streamable-http.flow.ts"() {
@@ -31766,64 +32184,60 @@ var init_handle_streamable_http_flow = __esm({
31766
32184
  const authorization = request[ServerRequestTokens.auth];
31767
32185
  const { token } = authorization;
31768
32186
  const logger = this.scopeLogger.child("handle:streamable-http:parseInput");
31769
- const raw = request.headers?.["mcp-session-id"];
31770
- const rawMcpSessionHeader = typeof raw === "string" ? raw : void 0;
31771
- const mcpSessionHeader = validateMcpSessionHeader(rawMcpSessionHeader);
31772
- if (raw !== void 0 && !mcpSessionHeader) {
32187
+ const sessionResolution = resolveStreamableHttpSession({
32188
+ rawHeader: request.headers?.["mcp-session-id"],
32189
+ authorizationSession: authorization.session,
32190
+ createSession: () => {
32191
+ const query = request.query;
32192
+ const skillsOnlyMode = detectSkillsOnlyMode(query);
32193
+ return createSessionId("streamable-http", token, {
32194
+ userAgent: request.headers?.["user-agent"],
32195
+ platformDetectionConfig: this.scope.metadata.transport?.platformDetection,
32196
+ skillsOnlyMode
32197
+ });
32198
+ }
32199
+ });
32200
+ if (sessionResolution.responded404 || !sessionResolution.session) {
31773
32201
  logger.warn("parseInput: invalid mcp-session-id header");
31774
32202
  this.respond(httpRespond.sessionNotFound("invalid session id"));
31775
32203
  return;
31776
32204
  }
31777
- let session;
31778
- if (mcpSessionHeader) {
31779
- if (authorization.session?.id === mcpSessionHeader) {
31780
- session = authorization.session;
31781
- } else {
31782
- session = { id: mcpSessionHeader };
31783
- }
31784
- } else if (authorization.session) {
31785
- session = authorization.session;
31786
- } else {
31787
- const query = request.query;
31788
- const skillsOnlyMode = detectSkillsOnlyMode(query);
31789
- session = createSessionId("streamable-http", token, {
31790
- userAgent: request.headers?.["user-agent"],
31791
- platformDetectionConfig: this.scope.metadata.transport?.platformDetection,
31792
- skillsOnlyMode
31793
- });
31794
- }
32205
+ const session = sessionResolution.session;
31795
32206
  this.state.set(stateSchema18.parse({ token, session }));
31796
32207
  logger.info("parseInput: session resolved", { sessionId: session.id?.slice(0, 20) });
31797
32208
  }
31798
32209
  async router() {
31799
32210
  const { request } = this.rawInput;
31800
32211
  const logger = this.scopeLogger.child("handle:streamable-http:router");
31801
- if (request.method.toUpperCase() === "GET") {
31802
- this.state.set("requestType", "sseListener");
31803
- logger.info("router: requestType=sseListener, method=GET");
32212
+ const classification = classifyStreamableHttpRequest({
32213
+ method: request.method,
32214
+ body: request.body
32215
+ });
32216
+ if ("error" in classification) {
32217
+ logger.warn("router: invalid request, no valid method");
32218
+ this.respond(httpRespond.rpcError("Invalid Request"));
31804
32219
  return;
31805
32220
  }
31806
- const body = request.body;
31807
- const method = body?.method;
31808
- if (method === "initialize") {
31809
- this.state.set("requestType", "initialize");
32221
+ this.state.set("requestType", classification.requestType);
32222
+ if (classification.requestType === "sseListener") {
32223
+ logger.info("router: requestType=sseListener, method=GET");
32224
+ } else if (classification.requestType === "initialize") {
31810
32225
  logger.info("router: requestType=initialize, method=POST");
31811
- } else if (typeof method === "string" && method.startsWith("ui/")) {
31812
- this.state.set("requestType", "extApps");
32226
+ } else if (classification.requestType === "extApps") {
32227
+ const method = request.body?.method;
31813
32228
  logger.info(`router: requestType=extApps, method=${method}`);
31814
- } else if (ElicitResultSchema2.safeParse(request.body?.result).success) {
31815
- this.state.set("requestType", "elicitResult");
32229
+ } else if (classification.requestType === "elicitResult") {
31816
32230
  logger.info("router: requestType=elicitResult, method=POST");
31817
- } else if (method && RequestSchema.safeParse(request.body).success) {
31818
- this.state.set("requestType", "message");
31819
- logger.info(`router: requestType=message, method=${method}`);
31820
32231
  } else {
31821
- logger.warn("router: invalid request, no valid method");
31822
- this.respond(httpRespond.rpcError("Invalid Request"));
32232
+ const method = request.body?.method;
32233
+ logger.info(`router: requestType=message, method=${method}`);
31823
32234
  }
31824
32235
  }
31825
32236
  async onInitialize() {
31826
32237
  const transportService = this.scope.transportService;
32238
+ if (!transportService) {
32239
+ throw new TransportServiceNotAvailableError();
32240
+ }
31827
32241
  const logger = this.scope.logger.child("handle:streamable-http:onInitialize");
31828
32242
  const { request, response } = this.rawInput;
31829
32243
  const { token, session } = this.state.required;
@@ -31833,7 +32247,15 @@ var init_handle_streamable_http_flow = __esm({
31833
32247
  tokenPrefix: token?.slice(0, 10)
31834
32248
  });
31835
32249
  try {
32250
+ const authorization = request[ServerRequestTokens.auth];
32251
+ syncStreamableHttpAuthorizationSession(authorization, session);
31836
32252
  const transport = await transportService.createTransporter("streamable-http", token, session.id, response);
32253
+ if (transport.isInitialized) {
32254
+ logger.info("onInitialize: transport already initialized, resetting for re-initialization", {
32255
+ sessionId: session.id?.slice(0, 20)
32256
+ });
32257
+ transport.resetForReinitialization();
32258
+ }
31837
32259
  logger.info("onInitialize: transport created, calling initialize");
31838
32260
  await transport.initialize(request, response);
31839
32261
  logger.info("onInitialize: completed successfully");
@@ -31851,6 +32273,9 @@ var init_handle_streamable_http_flow = __esm({
31851
32273
  }
31852
32274
  async onElicitResult() {
31853
32275
  const transportService = this.scope.transportService;
32276
+ if (!transportService) {
32277
+ throw new TransportServiceNotAvailableError();
32278
+ }
31854
32279
  const logger = this.scopeLogger.child("handle:streamable-http:onElicitResult");
31855
32280
  const { request, response } = this.rawInput;
31856
32281
  const { token, session } = this.state.required;
@@ -31901,6 +32326,9 @@ var init_handle_streamable_http_flow = __esm({
31901
32326
  }
31902
32327
  async onMessage() {
31903
32328
  const transportService = this.scope.transportService;
32329
+ if (!transportService) {
32330
+ throw new TransportServiceNotAvailableError();
32331
+ }
31904
32332
  const logger = this.scopeLogger.child("handle:streamable-http:onMessage");
31905
32333
  const { request, response } = this.rawInput;
31906
32334
  const { token, session } = this.state.required;
@@ -31908,44 +32336,21 @@ var init_handle_streamable_http_flow = __esm({
31908
32336
  sessionId: session.id?.slice(0, 20),
31909
32337
  hasToken: !!token
31910
32338
  });
31911
- let transport = await transportService.getTransporter("streamable-http", token, session.id);
31912
- logger.verbose("onMessage: getTransporter result", { found: !!transport });
31913
- if (!transport) {
31914
- try {
31915
- logger.verbose("onMessage: transport not in memory, checking Redis", {
31916
- sessionId: session.id?.slice(0, 20)
31917
- });
31918
- const storedSession = await transportService.getStoredSession("streamable-http", token, session.id);
31919
- logger.verbose("onMessage: getStoredSession result", {
31920
- found: !!storedSession,
31921
- initialized: storedSession?.initialized
31922
- });
31923
- if (storedSession) {
31924
- logger.verbose("onMessage: recreating transport from stored session", {
31925
- sessionId: session.id?.slice(0, 20),
31926
- createdAt: storedSession.createdAt,
31927
- initialized: storedSession.initialized
31928
- });
31929
- transport = await transportService.recreateTransporter(
31930
- "streamable-http",
31931
- token,
31932
- session.id,
31933
- storedSession,
31934
- response
31935
- );
31936
- logger.verbose("onMessage: transport recreated successfully");
31937
- }
31938
- } catch (error) {
32339
+ const transportLookup = await lookupStreamableHttpTransport({
32340
+ transportService,
32341
+ token,
32342
+ sessionId: session.id,
32343
+ response
32344
+ });
32345
+ if (transportLookup.kind !== "transport") {
32346
+ const body = request.body;
32347
+ if (transportLookup.recreationError) {
31939
32348
  logger.warn("Failed to recreate transport from stored session", {
31940
32349
  sessionId: session.id?.slice(0, 20),
31941
- error: error instanceof Error ? error.message : String(error)
32350
+ error: transportLookup.recreationError instanceof Error ? transportLookup.recreationError.message : String(transportLookup.recreationError)
31942
32351
  });
31943
32352
  }
31944
- }
31945
- if (!transport) {
31946
- const wasCreated = await transportService.wasSessionCreatedAsync("streamable-http", token, session.id);
31947
- const body = request.body;
31948
- if (wasCreated) {
32353
+ if (transportLookup.kind === "session-expired") {
31949
32354
  logger.info("Session expired - client should re-initialize", {
31950
32355
  sessionId: session.id?.slice(0, 20),
31951
32356
  tokenHash: token.slice(0, 8),
@@ -31967,6 +32372,8 @@ var init_handle_streamable_http_flow = __esm({
31967
32372
  }
31968
32373
  return;
31969
32374
  }
32375
+ const transport = transportLookup.transport;
32376
+ logger.verbose("onMessage: transport resolved", { source: transportLookup.source });
31970
32377
  try {
31971
32378
  await transport.handleRequest(request, response);
31972
32379
  this.handled();
@@ -31985,6 +32392,9 @@ var init_handle_streamable_http_flow = __esm({
31985
32392
  }
31986
32393
  async onSseListener() {
31987
32394
  const transportService = this.scope.transportService;
32395
+ if (!transportService) {
32396
+ throw new TransportServiceNotAvailableError();
32397
+ }
31988
32398
  const logger = this.scopeLogger.child("handle:streamable-http:onSseListener");
31989
32399
  const { request, response } = this.rawInput;
31990
32400
  const { token, session } = this.state.required;
@@ -32022,6 +32432,9 @@ var init_handle_streamable_http_flow = __esm({
32022
32432
  }
32023
32433
  async onExtApps() {
32024
32434
  const transportService = this.scope.transportService;
32435
+ if (!transportService) {
32436
+ throw new TransportServiceNotAvailableError();
32437
+ }
32025
32438
  const logger = this.scopeLogger.child("handle:streamable-http:onExtApps");
32026
32439
  const { request, response } = this.rawInput;
32027
32440
  const { token, session } = this.state.required;
@@ -32072,8 +32485,7 @@ var init_handle_streamable_http_flow = __esm({
32072
32485
  }
32073
32486
  return;
32074
32487
  }
32075
- const scope = this.scope;
32076
- const configuredCapabilities = scope.metadata.extApps?.hostCapabilities;
32488
+ const configuredCapabilities = this.scope.metadata.extApps?.hostCapabilities;
32077
32489
  const hostCapabilities = {
32078
32490
  serverToolProxy: configuredCapabilities?.serverToolProxy ?? true,
32079
32491
  logging: configuredCapabilities?.logging ?? true,
@@ -32082,9 +32494,9 @@ var init_handle_streamable_http_flow = __esm({
32082
32494
  const handler = createExtAppsMessageHandler({
32083
32495
  context: {
32084
32496
  sessionId: session.id,
32085
- logger: scope.logger,
32497
+ logger: this.scope.logger,
32086
32498
  callTool: async (name33, args) => {
32087
- const result = await scope.runFlow("tools:call-tool", {
32499
+ const result = await this.scope.runFlow("tools:call-tool", {
32088
32500
  request: { method: "tools/call", params: { name: name33, arguments: args } },
32089
32501
  ctx: {
32090
32502
  authInfo: {
@@ -32170,6 +32582,7 @@ var init_handle_sse_flow = __esm({
32170
32582
  "libs/sdk/src/transport/flows/handle.sse.flow.ts"() {
32171
32583
  "use strict";
32172
32584
  init_common();
32585
+ init_errors();
32173
32586
  init_session_id_utils();
32174
32587
  init_skill_mode_utils();
32175
32588
  plan20 = {
@@ -32239,10 +32652,9 @@ var init_handle_sse_flow = __esm({
32239
32652
  }
32240
32653
  async router() {
32241
32654
  const { request } = this.rawInput;
32242
- const scope = this.scope;
32243
32655
  const requestPath = normalizeEntryPrefix(request.path);
32244
- const prefix = normalizeEntryPrefix(scope.entryPath);
32245
- const scopePath = normalizeScopeBase(scope.routeBase);
32656
+ const prefix = normalizeEntryPrefix(this.scope.entryPath);
32657
+ const scopePath = normalizeScopeBase(this.scope.routeBase);
32246
32658
  const basePath = `${prefix}${scopePath}`;
32247
32659
  if (requestPath === `${basePath}/sse`) {
32248
32660
  this.state.set("requestType", "initialize");
@@ -32252,6 +32664,9 @@ var init_handle_sse_flow = __esm({
32252
32664
  }
32253
32665
  async onInitialize() {
32254
32666
  const transportService = this.scope.transportService;
32667
+ if (!transportService) {
32668
+ throw new TransportServiceNotAvailableError();
32669
+ }
32255
32670
  const { request, response } = this.rawInput;
32256
32671
  const { token, session } = this.state.required;
32257
32672
  const transport = await transportService.createTransporter("sse", token, session.id, response);
@@ -32263,6 +32678,9 @@ var init_handle_sse_flow = __esm({
32263
32678
  }
32264
32679
  async onMessage() {
32265
32680
  const transportService = this.scope.transportService;
32681
+ if (!transportService) {
32682
+ throw new TransportServiceNotAvailableError();
32683
+ }
32266
32684
  const logger = this.scopeLogger.child("handle:legacy-sse:onMessage");
32267
32685
  const { request, response } = this.rawInput;
32268
32686
  const { token, session } = this.state.required;
@@ -32335,6 +32753,7 @@ var init_handle_stateless_http_flow = __esm({
32335
32753
  "libs/sdk/src/transport/flows/handle.stateless-http.flow.ts"() {
32336
32754
  "use strict";
32337
32755
  init_common();
32756
+ init_errors();
32338
32757
  plan21 = {
32339
32758
  pre: ["parseInput", "router"],
32340
32759
  execute: ["handleRequest"],
@@ -32382,6 +32801,9 @@ var init_handle_stateless_http_flow = __esm({
32382
32801
  }
32383
32802
  async handleRequest() {
32384
32803
  const transportService = this.scope.transportService;
32804
+ if (!transportService) {
32805
+ throw new TransportServiceNotAvailableError();
32806
+ }
32385
32807
  const logger = this.scope.logger.child("HandleStatelessHttpFlow");
32386
32808
  const { request, response } = this.rawInput;
32387
32809
  const { token, isAuthenticated, requestType } = this.state;
@@ -32717,6 +33139,12 @@ var init_transport_registry = __esm({
32717
33139
  transporter.markAsInitialized();
32718
33140
  }
32719
33141
  this.insertLocal(key, transporter);
33142
+ if (storedSession.clientCapabilities) {
33143
+ this.scope.notifications.setClientCapabilities(sessionId, storedSession.clientCapabilities);
33144
+ this.scope.logger.verbose("[TransportService] Restored client capabilities from stored session", {
33145
+ sessionId: sessionId.slice(0, 20)
33146
+ });
33147
+ }
32720
33148
  if (sessionStore) {
32721
33149
  const updatedSession = {
32722
33150
  ...storedSession,
@@ -32814,6 +33242,40 @@ var init_transport_registry = __esm({
32814
33242
  }
32815
33243
  throw new InvalidTransportSessionError("Invalid session: cannot destroy non-existent transporter.");
32816
33244
  }
33245
+ /**
33246
+ * Update the stored session in Redis with client capabilities from the initialize handshake.
33247
+ * This ensures capabilities survive session recreation (e.g., after server restart or transport eviction).
33248
+ *
33249
+ * Best-effort: failures are logged but do not throw.
33250
+ * No-op when sessionStore is not configured.
33251
+ */
33252
+ async updateStoredSessionCapabilities(sessionId, clientCapabilities) {
33253
+ if (!this.sessionStore) return;
33254
+ try {
33255
+ const stored = await this.sessionStore.get(sessionId);
33256
+ if (!stored) {
33257
+ this.scope.logger.verbose("[TransportService] Cannot update capabilities: session not found in store", {
33258
+ sessionId: sessionId.slice(0, 20)
33259
+ });
33260
+ return;
33261
+ }
33262
+ const updatedSession = {
33263
+ ...stored,
33264
+ clientCapabilities,
33265
+ lastAccessedAt: Date.now()
33266
+ };
33267
+ const defaultTtlMs = this.getDefaultTtlMs();
33268
+ await this.sessionStore.set(sessionId, updatedSession, defaultTtlMs);
33269
+ this.scope.logger.verbose("[TransportService] Persisted client capabilities to session store", {
33270
+ sessionId: sessionId.slice(0, 20)
33271
+ });
33272
+ } catch (err) {
33273
+ this.scope.logger.warn("[TransportService] Failed to persist client capabilities", {
33274
+ sessionId: sessionId.slice(0, 20),
33275
+ error: err instanceof Error ? err.message : String(err)
33276
+ });
33277
+ }
33278
+ }
32817
33279
  /**
32818
33280
  * Get or create a shared singleton transport for anonymous stateless requests.
32819
33281
  * All anonymous requests share the same transport instance.
@@ -35901,18 +36363,15 @@ var init_complete_flow = __esm({
35901
36363
  } else {
35902
36364
  const resourceMatch = this.scope.resources.findResourceForUri(uri);
35903
36365
  if (resourceMatch) {
35904
- const instance = resourceMatch.instance;
35905
- if (typeof instance.getArgumentCompleter === "function") {
35906
- const completer = instance.getArgumentCompleter(argName);
35907
- if (completer) {
35908
- try {
35909
- const result = await completer(argValue);
35910
- values = result.values || [];
35911
- total = result.total;
35912
- hasMore = result.hasMore;
35913
- } catch (e) {
35914
- this.logger.warn(`complete: completer failed for resource "${uri}" argument "${argName}": ${e}`);
35915
- }
36366
+ const completer = resourceMatch.instance.getArgumentCompleter(argName);
36367
+ if (completer) {
36368
+ try {
36369
+ const result = await completer(argValue);
36370
+ values = result.values || [];
36371
+ total = result.total;
36372
+ hasMore = result.hasMore;
36373
+ } catch (e) {
36374
+ this.logger.warn(`complete: completer failed for resource "${uri}" argument "${argName}": ${e}`);
35916
36375
  }
35917
36376
  }
35918
36377
  } else {
@@ -36031,11 +36490,10 @@ var init_call_agent_flow = __esm({
36031
36490
  throw new InvalidMethodError(method, "tools/call");
36032
36491
  }
36033
36492
  const { name: toolName } = params;
36034
- const scope = this.scope;
36035
36493
  const agentId = toolName;
36036
36494
  let agent;
36037
- if (scope.agents) {
36038
- agent = scope.agents.findById(agentId) ?? scope.agents.findByName(agentId);
36495
+ if (this.scope.agents) {
36496
+ agent = this.scope.agents.findById(agentId) ?? this.scope.agents.findByName(agentId);
36039
36497
  }
36040
36498
  const agentOwnerId = agent?.owner?.id;
36041
36499
  const progressToken = params._meta?.progressToken;
@@ -36051,8 +36509,7 @@ var init_call_agent_flow = __esm({
36051
36509
  }
36052
36510
  async findAgent() {
36053
36511
  this.logger.verbose("findAgent:start");
36054
- const scope = this.scope;
36055
- const agents = scope.agents;
36512
+ const agents = this.scope.agents;
36056
36513
  if (!agents) {
36057
36514
  this.logger.warn("findAgent: no agent registry available");
36058
36515
  throw new AgentNotFoundError(this.state.required.input.name);
@@ -36473,7 +36930,10 @@ var init_elicitation_request_flow = __esm({
36473
36930
  async storePendingRecord() {
36474
36931
  this.logger.verbose("storePendingRecord:start");
36475
36932
  const { elicitId, sessionId, message, mode, expiresAt, requestedSchema } = this.state.required;
36476
- const scope = this.scope;
36933
+ const store = this.scope.elicitationStore;
36934
+ if (!store) {
36935
+ throw new ElicitationStoreNotInitializedError();
36936
+ }
36477
36937
  const pendingRecord = {
36478
36938
  elicitId,
36479
36939
  sessionId,
@@ -36483,7 +36943,7 @@ var init_elicitation_request_flow = __esm({
36483
36943
  mode,
36484
36944
  requestedSchema
36485
36945
  };
36486
- await scope.elicitationStore.setPending(pendingRecord);
36946
+ await store.setPending(pendingRecord);
36487
36947
  this.state.set("pendingRecord", pendingRecord);
36488
36948
  this.logger.verbose("storePendingRecord:done", { elicitId, sessionId });
36489
36949
  }
@@ -36610,8 +37070,11 @@ var init_elicitation_result_flow = __esm({
36610
37070
  async lookupPending() {
36611
37071
  this.logger.verbose("lookupPending:start");
36612
37072
  const { sessionId } = this.state.required;
36613
- const scope = this.scope;
36614
- const pendingRecord = await scope.elicitationStore.getPending(sessionId);
37073
+ const store = this.scope.elicitationStore;
37074
+ if (!store) {
37075
+ throw new ElicitationStoreNotInitializedError();
37076
+ }
37077
+ const pendingRecord = await store.getPending(sessionId);
36615
37078
  this.state.set("pendingRecord", pendingRecord ?? void 0);
36616
37079
  if (!pendingRecord) {
36617
37080
  this.logger.verbose("lookupPending:notFound", { sessionId });
@@ -36663,14 +37126,14 @@ var init_elicitation_result_flow = __esm({
36663
37126
  this.logger.verbose("publishResult:start");
36664
37127
  const { pendingRecord, elicitResult } = this.state;
36665
37128
  const { sessionId } = this.state.required;
36666
- const scope = this.scope;
36667
- if (!pendingRecord || !elicitResult) {
37129
+ const store = this.scope.elicitationStore;
37130
+ if (!pendingRecord || !elicitResult || !store) {
36668
37131
  this.state.set("handled", false);
36669
37132
  this.logger.verbose("publishResult:skip (no pending or no result)");
36670
37133
  return;
36671
37134
  }
36672
37135
  try {
36673
- await scope.elicitationStore.publishResult(pendingRecord.elicitId, sessionId, elicitResult);
37136
+ await store.publishResult(pendingRecord.elicitId, sessionId, elicitResult);
36674
37137
  this.state.set("handled", true);
36675
37138
  this.logger.verbose("publishResult:done", {
36676
37139
  elicitId: pendingRecord.elicitId,
@@ -36767,30 +37230,30 @@ var require_dist = __commonJS({
36767
37230
  sqliteStorageOptionsSchema: () => sqliteStorageOptionsSchema
36768
37231
  });
36769
37232
  module.exports = __toCommonJS2(index_exports);
36770
- var import_utils74 = __require("@frontmcp/utils");
37233
+ var import_utils77 = __require("@frontmcp/utils");
36771
37234
  var HKDF_SALT = new TextEncoder().encode("frontmcp-sqlite-storage-v1");
36772
37235
  var HKDF_INFO = new TextEncoder().encode("aes-256-gcm-value-encryption");
36773
37236
  var KEY_LENGTH = 32;
36774
37237
  function deriveEncryptionKey(secret) {
36775
37238
  const ikm = new TextEncoder().encode(secret);
36776
- return (0, import_utils74.hkdfSha256)(ikm, HKDF_SALT, HKDF_INFO, KEY_LENGTH);
37239
+ return (0, import_utils77.hkdfSha256)(ikm, HKDF_SALT, HKDF_INFO, KEY_LENGTH);
36777
37240
  }
36778
37241
  var SEPARATOR = ":";
36779
37242
  function encryptValue(key, plaintext) {
36780
- const iv = (0, import_utils74.randomBytes)(12);
37243
+ const iv = (0, import_utils77.randomBytes)(12);
36781
37244
  const plaintextBytes = new TextEncoder().encode(plaintext);
36782
- const { ciphertext, tag } = (0, import_utils74.encryptAesGcm)(key, plaintextBytes, iv);
36783
- return [(0, import_utils74.base64urlEncode)(iv), (0, import_utils74.base64urlEncode)(tag), (0, import_utils74.base64urlEncode)(ciphertext)].join(SEPARATOR);
37245
+ const { ciphertext, tag } = (0, import_utils77.encryptAesGcm)(key, plaintextBytes, iv);
37246
+ return [(0, import_utils77.base64urlEncode)(iv), (0, import_utils77.base64urlEncode)(tag), (0, import_utils77.base64urlEncode)(ciphertext)].join(SEPARATOR);
36784
37247
  }
36785
37248
  function decryptValue(key, encrypted) {
36786
37249
  const parts = encrypted.split(SEPARATOR);
36787
37250
  if (parts.length !== 3) {
36788
37251
  throw new Error("Invalid encrypted value format");
36789
37252
  }
36790
- const iv = (0, import_utils74.base64urlDecode)(parts[0]);
36791
- const tag = (0, import_utils74.base64urlDecode)(parts[1]);
36792
- const ciphertext = (0, import_utils74.base64urlDecode)(parts[2]);
36793
- const plaintext = (0, import_utils74.decryptAesGcm)(key, ciphertext, iv, tag);
37253
+ const iv = (0, import_utils77.base64urlDecode)(parts[0]);
37254
+ const tag = (0, import_utils77.base64urlDecode)(parts[1]);
37255
+ const ciphertext = (0, import_utils77.base64urlDecode)(parts[2]);
37256
+ const plaintext = (0, import_utils77.decryptAesGcm)(key, ciphertext, iv, tag);
36794
37257
  return new TextDecoder().decode(plaintext);
36795
37258
  }
36796
37259
  var SqliteKvStore = class {
@@ -38063,7 +38526,7 @@ var init_job_instance = __esm({
38063
38526
  this.name = record.metadata.id || record.metadata.name;
38064
38527
  this.fullName = this.owner.id + ":" + this.name;
38065
38528
  this.scope = this._providers.getActiveScope();
38066
- this.hooks = this.scope.providers.getHooksRegistry();
38529
+ this.hooks = this.scope.hooks;
38067
38530
  this.inputSchema = record.metadata.inputSchema ?? {};
38068
38531
  this.outputSchema = record.metadata.outputSchema ?? {};
38069
38532
  this.ready = this.initialize();
@@ -38449,7 +38912,7 @@ var init_workflow_instance = __esm({
38449
38912
  this.name = record.metadata.id || record.metadata.name;
38450
38913
  this.fullName = this.owner.id + ":" + this.name;
38451
38914
  this.scope = this._providers.getActiveScope();
38452
- this.hooks = this.scope.providers.getHooksRegistry();
38915
+ this.hooks = this.scope.hooks;
38453
38916
  this.ready = this.initialize();
38454
38917
  }
38455
38918
  async initialize() {
@@ -40107,13 +40570,12 @@ var init_scope_instance = __esm({
40107
40570
  init_plugin_registry();
40108
40571
  init_elicitation2();
40109
40572
  init_flows2();
40110
- init_elicitation_error();
40111
40573
  init_send_elicitation_result_tool();
40112
40574
  init_tool_utils();
40113
40575
  init_tool_instance();
40114
40576
  init_event_stores();
40115
40577
  init_job_scope_helper();
40116
- Scope = class _Scope extends ScopeEntry2 {
40578
+ Scope = class _Scope extends ScopeEntry3 {
40117
40579
  id;
40118
40580
  globalProviders;
40119
40581
  logger;
@@ -40237,20 +40699,21 @@ var init_scope_instance = __esm({
40237
40699
  };
40238
40700
  this.scopePlugins = new PluginRegistry(this.scopeProviders, serverPlugins, scopeRef, serverPluginScopeInfo);
40239
40701
  }
40702
+ if (this.scopePlugins) {
40703
+ await this.scopePlugins.ready;
40704
+ }
40240
40705
  this.scopeTools = new ToolRegistry(this.scopeProviders, [], scopeRef);
40241
40706
  this.scopeResources = new ResourceRegistry(this.scopeProviders, [], scopeRef);
40242
40707
  this.scopePrompts = new PromptRegistry(this.scopeProviders, [], scopeRef);
40243
40708
  this.scopeAgents = new AgentRegistry(this.scopeProviders, [], scopeRef);
40244
40709
  this.scopeSkills = new SkillRegistry(this.scopeProviders, this.metadata.skills ?? [], scopeRef);
40245
- const batch2 = [
40710
+ await Promise.all([
40246
40711
  this.scopeTools.ready,
40247
40712
  this.scopeResources.ready,
40248
40713
  this.scopePrompts.ready,
40249
40714
  this.scopeAgents.ready,
40250
40715
  this.scopeSkills.ready
40251
- ];
40252
- if (this.scopePlugins) batch2.push(this.scopePlugins.ready);
40253
- await Promise.all(batch2);
40716
+ ]);
40254
40717
  if (this.scopePlugins) {
40255
40718
  const pluginNames = this.scopePlugins.getPluginNames();
40256
40719
  this.logger.verbose(`PluginRegistry initialized (${pluginNames.length} plugin(s): [${pluginNames.join(", ")}])`);
@@ -40522,7 +40985,7 @@ var init_scope_instance = __esm({
40522
40985
  {
40523
40986
  scope: ProviderScope2.GLOBAL,
40524
40987
  name: "ScopeEntry",
40525
- provide: ScopeEntry2,
40988
+ provide: ScopeEntry3,
40526
40989
  useValue: this
40527
40990
  },
40528
40991
  {
@@ -40619,9 +41082,6 @@ var init_scope_instance = __esm({
40619
41082
  * @see createElicitationStore for factory implementation details
40620
41083
  */
40621
41084
  get elicitationStore() {
40622
- if (!this._elicitationStore) {
40623
- throw new ElicitationStoreNotInitializedError();
40624
- }
40625
41085
  return this._elicitationStore;
40626
41086
  }
40627
41087
  /**
@@ -40655,7 +41115,7 @@ var init_scope_instance = __esm({
40655
41115
  * Register the sendElicitationResult system tool.
40656
41116
  * This tool is hidden by default and only shown to clients that don't support elicitation.
40657
41117
  */
40658
- registerSendElicitationResultTool(scopeRef) {
41118
+ registerSendElicitationResultTool(_scopeRef) {
40659
41119
  try {
40660
41120
  const toolRecord = normalizeTool(SendElicitationResultTool);
40661
41121
  const systemToolEntry = new ToolInstance(toolRecord, this.scopeProviders, {
@@ -40866,7 +41326,7 @@ var init_base_host_adapter = __esm({
40866
41326
  import * as http from "node:http";
40867
41327
  import express from "express";
40868
41328
  import cors from "cors";
40869
- import { fileExists as fileExists3, unlink } from "@frontmcp/utils";
41329
+ import { fileExists as fileExists4, unlink } from "@frontmcp/utils";
40870
41330
  var ExpressHostAdapter;
40871
41331
  var init_express_host_adapter = __esm({
40872
41332
  "libs/sdk/src/server/adapters/express.host.adapter.ts"() {
@@ -40963,7 +41423,7 @@ var init_express_host_adapter = __esm({
40963
41423
  }
40964
41424
  async cleanupStaleSocket(socketPath) {
40965
41425
  try {
40966
- if (await fileExists3(socketPath)) {
41426
+ if (await fileExists4(socketPath)) {
40967
41427
  await unlink(socketPath);
40968
41428
  }
40969
41429
  } catch {
@@ -41088,7 +41548,7 @@ var init_front_mcp_providers = __esm({
41088
41548
  useValue: metadata
41089
41549
  })
41090
41550
  };
41091
- DEFAULT_HTTP_OPTIONS = { port: 3e3, entryPath: "/mcp" };
41551
+ DEFAULT_HTTP_OPTIONS = { port: Number(process.env["PORT"]) || 3e3, entryPath: "/mcp" };
41092
41552
  frontMcpServer = AsyncProvider({
41093
41553
  name: "frontmcp:server",
41094
41554
  scope: ProviderScope5.GLOBAL,
@@ -42421,7 +42881,7 @@ var front_mcp_exports = {};
42421
42881
  __export(front_mcp_exports, {
42422
42882
  FrontMcpInstance: () => FrontMcpInstance
42423
42883
  });
42424
- import { randomUUID as randomUUID25, fileExists as fileExists4, unlink as unlink2 } from "@frontmcp/utils";
42884
+ import { randomUUID as randomUUID25, fileExists as fileExists5, unlink as unlink2 } from "@frontmcp/utils";
42425
42885
  var FrontMcpInstance;
42426
42886
  var init_front_mcp = __esm({
42427
42887
  "libs/sdk/src/front-mcp/front-mcp.ts"() {
@@ -42464,6 +42924,9 @@ var init_front_mcp = __esm({
42464
42924
  throw new ServerNotFoundError();
42465
42925
  }
42466
42926
  await server.start();
42927
+ for (const scope of this.getScopes()) {
42928
+ await scope.emitServerStarted();
42929
+ }
42467
42930
  }
42468
42931
  /**
42469
42932
  * Get the configuration used to create this FrontMCP instance.
@@ -42479,7 +42942,13 @@ var init_front_mcp = __esm({
42479
42942
  return this.scopes.getScopes();
42480
42943
  }
42481
42944
  static async bootstrap(options) {
42482
- const frontMcp = new _FrontMcpInstance2(options);
42945
+ const parsedConfig = frontMcpMetadataSchema.parse(options);
42946
+ const daemonSocket = process.env["FRONTMCP_DAEMON_SOCKET"];
42947
+ if (daemonSocket) {
42948
+ await _FrontMcpInstance2.runUnixSocket({ ...parsedConfig, socketPath: daemonSocket });
42949
+ return;
42950
+ }
42951
+ const frontMcp = new _FrontMcpInstance2(parsedConfig);
42483
42952
  await frontMcp.ready;
42484
42953
  await frontMcp.start();
42485
42954
  frontMcp.log?.info("FrontMCP bootstrap complete");
@@ -42650,7 +43119,7 @@ var init_front_mcp = __esm({
42650
43119
  frontMcp.log?.info(`MCP server listening on unix://${socketPath}`);
42651
43120
  const cleanup = async () => {
42652
43121
  try {
42653
- if (await fileExists4(socketPath)) {
43122
+ if (await fileExists5(socketPath)) {
42654
43123
  await unlink2(socketPath);
42655
43124
  }
42656
43125
  } catch {
@@ -43294,6 +43763,7 @@ var init_agent_decorator = __esm({
43294
43763
 
43295
43764
  // libs/sdk/src/common/decorators/skill.decorator.ts
43296
43765
  import "reflect-metadata";
43766
+ import { dirname as dirname2 } from "@frontmcp/utils";
43297
43767
  function FrontMcpSkill(providedMetadata) {
43298
43768
  return (target) => {
43299
43769
  const metadata = skillMetadataSchema.parse(providedMetadata);
@@ -43313,13 +43783,33 @@ function FrontMcpSkill(providedMetadata) {
43313
43783
  function frontMcpSkill(providedMetadata) {
43314
43784
  const parsedMetadata = skillMetadataSchema.parse(providedMetadata);
43315
43785
  const skillToken = /* @__PURE__ */ Symbol(`skill:${parsedMetadata.name}`);
43786
+ const callerDir = resolveCallerDir();
43316
43787
  return {
43317
43788
  kind: "VALUE" /* VALUE */,
43318
43789
  provide: skillToken,
43319
43790
  // Cast to SkillMetadata - Zod's output type has internal type markers that don't match exactly
43320
- metadata: parsedMetadata
43791
+ metadata: parsedMetadata,
43792
+ callerDir
43321
43793
  };
43322
43794
  }
43795
+ function resolveCallerDir() {
43796
+ const err = new Error();
43797
+ const stack = err.stack;
43798
+ if (!stack) return void 0;
43799
+ const lines = stack.split("\n");
43800
+ for (let i = 1; i < lines.length; i++) {
43801
+ const line = lines[i];
43802
+ const match = line.match(/\(([^)]+):\d+:\d+\)/) || line.match(/at\s+([^\s:]+):\d+:\d+/);
43803
+ if (match) {
43804
+ const file = match[1];
43805
+ if (file.includes("node_modules") || file.includes("skill.decorator")) {
43806
+ continue;
43807
+ }
43808
+ return dirname2(file);
43809
+ }
43810
+ }
43811
+ return void 0;
43812
+ }
43323
43813
  function skillEsm(specifier, targetName, options) {
43324
43814
  const parsed = parsePackageSpecifier(specifier);
43325
43815
  return {
@@ -43392,7 +43882,7 @@ var init_skill_decorator = __esm({
43392
43882
 
43393
43883
  // libs/sdk/src/common/decorators/job.decorator.ts
43394
43884
  import "reflect-metadata";
43395
- function FrontMcpJob(providedMetadata) {
43885
+ function _FrontMcpJob(providedMetadata) {
43396
43886
  return (target) => {
43397
43887
  const metadata = frontMcpJobMetadataSchema.parse(providedMetadata);
43398
43888
  Reflect.defineMetadata(FrontMcpJobTokens.type, true, target);
@@ -43455,7 +43945,7 @@ function jobRemote(url, targetName, options) {
43455
43945
  }
43456
43946
  };
43457
43947
  }
43458
- var Job;
43948
+ var FrontMcpJob, Job;
43459
43949
  var init_job_decorator = __esm({
43460
43950
  "libs/sdk/src/common/decorators/job.decorator.ts"() {
43461
43951
  "use strict";
@@ -43463,11 +43953,12 @@ var init_job_decorator = __esm({
43463
43953
  init_job_metadata();
43464
43954
  init_package_specifier();
43465
43955
  init_validate_remote_url();
43466
- Object.assign(FrontMcpJob, {
43956
+ Object.assign(_FrontMcpJob, {
43467
43957
  esm: jobEsm,
43468
43958
  remote: jobRemote
43469
43959
  });
43470
- Job = FrontMcpJob;
43960
+ FrontMcpJob = _FrontMcpJob;
43961
+ Job = _FrontMcpJob;
43471
43962
  }
43472
43963
  });
43473
43964
 
@@ -43546,7 +44037,7 @@ var init_front_mcp_interface = __esm({
43546
44037
 
43547
44038
  // libs/sdk/src/common/interfaces/server.interface.ts
43548
44039
  import { IncomingMessage, HttpServerResponse } from "@frontmcp/protocol";
43549
- var ServerRequest11, ServerResponse3, FrontMcpServer;
44040
+ var ServerRequest11, ServerResponse4, FrontMcpServer;
43550
44041
  var init_server_interface = __esm({
43551
44042
  "libs/sdk/src/common/interfaces/server.interface.ts"() {
43552
44043
  "use strict";
@@ -43556,7 +44047,7 @@ var init_server_interface = __esm({
43556
44047
  body;
43557
44048
  authSession;
43558
44049
  };
43559
- ServerResponse3 = class extends HttpServerResponse {
44050
+ ServerResponse4 = class extends HttpServerResponse {
43560
44051
  };
43561
44052
  FrontMcpServer = class {
43562
44053
  };
@@ -43888,6 +44379,29 @@ var init_resource_interface = __esm({
43888
44379
  this.uri = uri;
43889
44380
  this.params = params;
43890
44381
  }
44382
+ /**
44383
+ * Override to provide autocompletion for resource template arguments.
44384
+ * Called by the MCP `completion/complete` handler when a client requests
44385
+ * suggestions for a template parameter.
44386
+ *
44387
+ * @param argName - The template parameter name (e.g., 'userId')
44388
+ * @returns A completer function, or null if no completion is available for this argument
44389
+ *
44390
+ * @example
44391
+ * ```typescript
44392
+ * getArgumentCompleter(argName: string): ResourceArgumentCompleter | null {
44393
+ * if (argName === 'userId') {
44394
+ * return async (partial) => ({
44395
+ * values: await this.searchUsers(partial),
44396
+ * });
44397
+ * }
44398
+ * return null;
44399
+ * }
44400
+ * ```
44401
+ */
44402
+ getArgumentCompleter(_argName) {
44403
+ return null;
44404
+ }
43891
44405
  get output() {
43892
44406
  return this._output;
43893
44407
  }
@@ -45037,9 +45551,12 @@ var init_decide_request_intent_utils = __esm({
45037
45551
  outcome: { intent: "sse", reason: "GET /sse with Mcp-Session-Id." }
45038
45552
  },
45039
45553
  // D) Initialize (POST → SSE)
45040
- // D1) Stateless initialize (no session, stateless enabled) - must come before streamable rules
45554
+ // D1) Stateless initialize (no session, stateless enabled, streamable NOT enabled)
45555
+ // When streamable is also enabled, prefer streamable-http (which creates a session).
45556
+ // Per MCP spec, first initialize naturally has no session — stateless should only
45557
+ // match when it's the sole enabled protocol.
45041
45558
  {
45042
- care: CH_MASK | B_HAS_SESSION | B_STATELESS_EN,
45559
+ care: CH_MASK | B_HAS_SESSION | B_STATELESS_EN | B_STREAMABLE_EN,
45043
45560
  match: CH_POST_INIT_SSE | B_STATELESS_EN,
45044
45561
  outcome: { intent: "stateless-http", reason: "Stateless initialize (no session)." }
45045
45562
  },
@@ -45058,9 +45575,9 @@ var init_decide_request_intent_utils = __esm({
45058
45575
  outcome: { intent: "streamable-http", reason: "Initialize with SSE." }
45059
45576
  },
45060
45577
  // E) Initialize (POST → JSON)
45061
- // E1) Stateless initialize JSON (no session, stateless enabled) - must come before stateful rules
45578
+ // E1) Stateless initialize JSON (no session, stateless enabled, streamable NOT enabled)
45062
45579
  {
45063
- care: CH_MASK | B_HAS_SESSION | B_STATELESS_EN,
45580
+ care: CH_MASK | B_HAS_SESSION | B_STATELESS_EN | B_STREAMABLE_EN,
45064
45581
  match: CH_POST_INIT_JSON | B_STATELESS_EN,
45065
45582
  outcome: { intent: "stateless-http", reason: "Stateless initialize JSON (no session)." }
45066
45583
  },
@@ -45089,7 +45606,7 @@ var init_decide_request_intent_utils = __esm({
45089
45606
  }
45090
45607
  },
45091
45608
  {
45092
- care: CH_MASK | B_STREAMABLE_EN,
45609
+ care: CH_MASK | B_STREAMABLE_EN | B_STATELESS_EN,
45093
45610
  match: CH_POST_SSE,
45094
45611
  outcome: {
45095
45612
  intent: "unknown",
@@ -45097,8 +45614,9 @@ var init_decide_request_intent_utils = __esm({
45097
45614
  recommendation: { httpStatus: 405, message: "Streamable HTTP disabled" }
45098
45615
  }
45099
45616
  },
45617
+ // Stateless short-lived SSE only when streamable is NOT enabled
45100
45618
  {
45101
- care: CH_MASK | B_HAS_SESSION | B_STATELESS_EN,
45619
+ care: CH_MASK | B_HAS_SESSION | B_STATELESS_EN | B_STREAMABLE_EN,
45102
45620
  match: CH_POST_SSE | B_STATELESS_EN,
45103
45621
  outcome: { intent: "stateless-http", reason: "Stateless short-lived SSE." }
45104
45622
  },
@@ -45118,7 +45636,7 @@ var init_decide_request_intent_utils = __esm({
45118
45636
  }
45119
45637
  },
45120
45638
  {
45121
- care: CH_MASK | B_STATEFUL_EN | B_STREAMABLE_EN,
45639
+ care: CH_MASK | B_STATEFUL_EN | B_STREAMABLE_EN | B_STATELESS_EN,
45122
45640
  match: CH_POST_JSON,
45123
45641
  outcome: {
45124
45642
  intent: "unknown",
@@ -45126,8 +45644,9 @@ var init_decide_request_intent_utils = __esm({
45126
45644
  recommendation: { httpStatus: 405, message: "JSON mode disabled" }
45127
45645
  }
45128
45646
  },
45647
+ // Stateless JSON only when streamable is NOT enabled
45129
45648
  {
45130
- care: CH_MASK | B_HAS_SESSION | B_STATELESS_EN,
45649
+ care: CH_MASK | B_HAS_SESSION | B_STATELESS_EN | B_STREAMABLE_EN,
45131
45650
  match: CH_POST_JSON | B_STATELESS_EN,
45132
45651
  outcome: { intent: "stateless-http", reason: "Stateless JSON request." }
45133
45652
  },
@@ -45331,7 +45850,7 @@ var init_internal = __esm({
45331
45850
  });
45332
45851
 
45333
45852
  // libs/sdk/src/common/interfaces/index.ts
45334
- var init_interfaces3 = __esm({
45853
+ var init_interfaces2 = __esm({
45335
45854
  "libs/sdk/src/common/interfaces/index.ts"() {
45336
45855
  "use strict";
45337
45856
  init_base_interface();
@@ -45496,18 +46015,40 @@ var init_dynamic = __esm({
45496
46015
  });
45497
46016
 
45498
46017
  // libs/sdk/src/common/entries/scope.entry.ts
45499
- var ScopeEntry2;
46018
+ var ScopeEntry3;
45500
46019
  var init_scope_entry = __esm({
45501
46020
  "libs/sdk/src/common/entries/scope.entry.ts"() {
45502
46021
  "use strict";
45503
46022
  init_base_entry();
45504
46023
  init_utils2();
45505
- ScopeEntry2 = class extends BaseEntry {
46024
+ ScopeEntry3 = class extends BaseEntry {
45506
46025
  get fullPath() {
45507
46026
  const prefix = normalizeEntryPrefix(this.entryPath ?? "");
45508
46027
  const scope = normalizeScopeBase(this.routeBase ?? "");
45509
46028
  return `${prefix}${scope}`;
45510
46029
  }
46030
+ /**
46031
+ * Lifecycle callbacks registered by plugins via onServerStarted().
46032
+ * Called after the HTTP server starts listening.
46033
+ */
46034
+ lifecycleCallbacks = [];
46035
+ /**
46036
+ * Register a callback to run after the server has started.
46037
+ * Plugins can use this for post-startup initialization (e.g., warming caches,
46038
+ * starting background jobs, logging readiness).
46039
+ */
46040
+ onServerStarted(callback) {
46041
+ this.lifecycleCallbacks.push(callback);
46042
+ }
46043
+ /**
46044
+ * Emit the server-started lifecycle event. Called by FrontMcpInstance after server.start().
46045
+ * @internal
46046
+ */
46047
+ async emitServerStarted() {
46048
+ for (const cb of this.lifecycleCallbacks) {
46049
+ await cb();
46050
+ }
46051
+ }
45511
46052
  };
45512
46053
  }
45513
46054
  });
@@ -45738,6 +46279,14 @@ var init_resource_entry = __esm({
45738
46279
  * Whether this resource is a template (has uriTemplate instead of uri)
45739
46280
  */
45740
46281
  isTemplate;
46282
+ /**
46283
+ * Get an argument completer for resource template autocompletion.
46284
+ * Override in subclasses to provide suggestions for template parameters.
46285
+ * Returns null by default (no completion available).
46286
+ */
46287
+ getArgumentCompleter(_argName) {
46288
+ return null;
46289
+ }
45741
46290
  };
45742
46291
  }
45743
46292
  });
@@ -46228,7 +46777,7 @@ var init_common = __esm({
46228
46777
  init_decorators();
46229
46778
  init_types();
46230
46779
  init_metadata();
46231
- init_interfaces3();
46780
+ init_interfaces2();
46232
46781
  init_tokens();
46233
46782
  init_dynamic();
46234
46783
  init_records();
@@ -46745,16 +47294,16 @@ export {
46745
47294
  ResourceNotFoundError,
46746
47295
  ResourceOutputSchema,
46747
47296
  ResourceReadError,
46748
- FrontMcpResourceTemplate as ResourceTemplate,
47297
+ _ResourceTemplate as ResourceTemplate,
46749
47298
  ResourceTemplateKind,
46750
47299
  ScopeConfigurationError,
46751
47300
  ScopeDeniedError,
46752
- ScopeEntry2 as ScopeEntry,
47301
+ ScopeEntry3 as ScopeEntry,
46753
47302
  ScopeKind,
46754
47303
  ServerNotFoundError,
46755
47304
  ServerRequest11 as ServerRequest,
46756
47305
  ServerRequestTokens,
46757
- ServerResponse3 as ServerResponse,
47306
+ ServerResponse4 as ServerResponse,
46758
47307
  ServerlessHandlerNotInitializedError,
46759
47308
  SessionHookStage,
46760
47309
  SessionIdEmptyError,
@@ -46787,6 +47336,7 @@ export {
46787
47336
  TransportAlreadyStartedError,
46788
47337
  TransportBusRequiredError,
46789
47338
  TransportNotConnectedError,
47339
+ TransportServiceNotAvailableError,
46790
47340
  UnauthorizedError,
46791
47341
  UnsupportedClientVersionError,
46792
47342
  UnsupportedContentTypeError,