@frontmcp/sdk 0.6.2 → 0.6.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/index.js CHANGED
@@ -516,7 +516,7 @@ var init_http_options = __esm({
516
516
  });
517
517
 
518
518
  // libs/sdk/src/auth/session/transport-session.types.ts
519
- var import_zod6, transportProtocolSchema, sseTransportStateSchema, streamableHttpTransportStateSchema, statefulHttpTransportStateSchema, statelessHttpTransportStateSchema, legacySseTransportStateSchema, transportStateSchema, transportSessionSchema, sessionJwtPayloadSchema, statelessSessionJwtPayloadSchema, encryptedBlobSchema, storedSessionSchema, redisConfigSchema, statefulStorageSchema, sessionStorageConfigSchema;
519
+ var import_zod6, transportProtocolSchema, sseTransportStateSchema, streamableHttpTransportStateSchema, statefulHttpTransportStateSchema, statelessHttpTransportStateSchema, legacySseTransportStateSchema, transportStateSchema, transportSessionSchema, sessionJwtPayloadSchema, encryptedBlobSchema, storedSessionSchema, redisConfigSchema;
520
520
  var init_transport_session_types = __esm({
521
521
  "libs/sdk/src/auth/session/transport-session.types.ts"() {
522
522
  "use strict";
@@ -582,10 +582,6 @@ var init_transport_session_types = __esm({
582
582
  iat: import_zod6.z.number(),
583
583
  exp: import_zod6.z.number().optional()
584
584
  });
585
- statelessSessionJwtPayloadSchema = sessionJwtPayloadSchema.extend({
586
- state: import_zod6.z.string().optional(),
587
- tokens: import_zod6.z.string().optional()
588
- });
589
585
  encryptedBlobSchema = import_zod6.z.object({
590
586
  alg: import_zod6.z.literal("A256GCM"),
591
587
  kid: import_zod6.z.string().optional(),
@@ -600,7 +596,9 @@ var init_transport_session_types = __esm({
600
596
  authorizationId: import_zod6.z.string(),
601
597
  tokens: import_zod6.z.record(import_zod6.z.string(), encryptedBlobSchema).optional(),
602
598
  createdAt: import_zod6.z.number(),
603
- lastAccessedAt: import_zod6.z.number()
599
+ lastAccessedAt: import_zod6.z.number(),
600
+ initialized: import_zod6.z.boolean().optional(),
601
+ maxLifetimeAt: import_zod6.z.number().optional()
604
602
  });
605
603
  redisConfigSchema = import_zod6.z.object({
606
604
  host: import_zod6.z.string().min(1),
@@ -612,15 +610,6 @@ var init_transport_session_types = __esm({
612
610
  defaultTtlMs: import_zod6.z.number().int().positive().optional().default(36e5)
613
611
  // 1 hour default
614
612
  });
615
- statefulStorageSchema = import_zod6.z.discriminatedUnion("store", [
616
- import_zod6.z.object({ store: import_zod6.z.literal("memory") }),
617
- import_zod6.z.object({ store: import_zod6.z.literal("redis"), config: redisConfigSchema })
618
- ]);
619
- sessionStorageConfigSchema = import_zod6.z.union([
620
- import_zod6.z.object({ mode: import_zod6.z.literal("stateless") }),
621
- import_zod6.z.object({ mode: import_zod6.z.literal("stateful") }).merge(statefulStorageSchema.options[0]),
622
- import_zod6.z.object({ mode: import_zod6.z.literal("stateful") }).merge(statefulStorageSchema.options[1])
623
- ]);
624
613
  }
625
614
  });
626
615
 
@@ -15726,8 +15715,19 @@ var init_http_request_flow = __esm({
15726
15715
  try {
15727
15716
  const { request } = this.rawInput;
15728
15717
  this.logger.verbose(`[${this.requestId}] router: check request decision`);
15729
- const transport = this.scope.auth.transport;
15718
+ const transport = this.scope.metadata.transport ?? this.scope.auth.transport;
15719
+ this.logger.debug(`[${this.requestId}] transport config`, {
15720
+ enableLegacySSE: transport.enableLegacySSE,
15721
+ enableStreamableHttp: transport.enableStreamableHttp,
15722
+ path: request.path,
15723
+ accept: request.headers?.["accept"]
15724
+ });
15730
15725
  const decision = decideIntent(request, { ...transport, tolerateMissingAccept: true });
15726
+ this.logger.debug(`[${this.requestId}] decision result`, {
15727
+ intent: decision.intent,
15728
+ reasons: decision.reasons,
15729
+ debug: decision.debug
15730
+ });
15731
15731
  if (request.method.toUpperCase() === "DELETE") {
15732
15732
  this.logger.verbose(`[${this.requestId}] DELETE request, using decision intent: ${decision.intent}`);
15733
15733
  if (decision.intent === "unknown") {
@@ -16041,6 +16041,8 @@ var init_transport_remote = __esm({
16041
16041
  async destroy(_reason) {
16042
16042
  throw new Error("RemoteTransporter: destroy() not implemented.");
16043
16043
  }
16044
+ markAsInitialized() {
16045
+ }
16044
16046
  };
16045
16047
  }
16046
16048
  });
@@ -16220,6 +16222,79 @@ data: ${JSON.stringify(message)}
16220
16222
  }
16221
16223
  });
16222
16224
 
16225
+ // libs/sdk/src/transport/adapters/sse-transport.ts
16226
+ var RecreateableSSEServerTransport;
16227
+ var init_sse_transport = __esm({
16228
+ "libs/sdk/src/transport/adapters/sse-transport.ts"() {
16229
+ "use strict";
16230
+ init_legacy_sse_tranporter();
16231
+ RecreateableSSEServerTransport = class extends SSEServerTransport {
16232
+ _isRecreatedSession = false;
16233
+ constructor(endpoint, res, options) {
16234
+ super(endpoint, res, options);
16235
+ if (options?.initialEventId !== void 0 && this.isValidEventId(options.initialEventId)) {
16236
+ this.setEventIdCounter(options.initialEventId);
16237
+ this._isRecreatedSession = true;
16238
+ }
16239
+ }
16240
+ /**
16241
+ * Validates that an event ID is a valid non-negative integer.
16242
+ * Protects against negative values, NaN, Infinity, and non-integer values.
16243
+ */
16244
+ isValidEventId(eventId) {
16245
+ return Number.isInteger(eventId) && eventId >= 0 && eventId <= Number.MAX_SAFE_INTEGER;
16246
+ }
16247
+ /**
16248
+ * Returns whether this is a recreated session.
16249
+ */
16250
+ get isRecreatedSession() {
16251
+ return this._isRecreatedSession;
16252
+ }
16253
+ /**
16254
+ * Returns the current event ID counter value.
16255
+ * Alias for lastEventId for consistency with the recreation API.
16256
+ */
16257
+ get eventIdCounter() {
16258
+ return this.lastEventId;
16259
+ }
16260
+ /**
16261
+ * Sets the event ID counter for session recreation.
16262
+ * Use this when recreating a session from stored state to maintain
16263
+ * event ID continuity for SSE reconnection support.
16264
+ *
16265
+ * @param eventId - The event ID to restore (must be a non-negative integer)
16266
+ */
16267
+ setEventIdCounter(eventId) {
16268
+ if (!this.isValidEventId(eventId)) {
16269
+ console.warn(
16270
+ `[RecreateableSSEServerTransport] Invalid eventId: ${eventId}. Must be a non-negative integer. Ignoring.`
16271
+ );
16272
+ return;
16273
+ }
16274
+ this._eventIdCounter = eventId;
16275
+ }
16276
+ /**
16277
+ * Sets the transport to a recreated session state.
16278
+ * Use this when recreating a transport from a stored session.
16279
+ *
16280
+ * @param sessionId - The session ID (for verification, should match constructor)
16281
+ * @param lastEventId - The last event ID that was sent to the client
16282
+ */
16283
+ setSessionState(sessionId, lastEventId) {
16284
+ if (this.sessionId !== sessionId) {
16285
+ console.warn(
16286
+ `RecreateableSSEServerTransport: session ID mismatch. Expected ${sessionId}, got ${this.sessionId}. Using constructor value.`
16287
+ );
16288
+ }
16289
+ if (lastEventId !== void 0) {
16290
+ this.setEventIdCounter(lastEventId);
16291
+ }
16292
+ this._isRecreatedSession = true;
16293
+ }
16294
+ };
16295
+ }
16296
+ });
16297
+
16223
16298
  // libs/sdk/src/transport/transport.event-store.ts
16224
16299
  var InMemoryEventStore;
16225
16300
  var init_transport_event_store = __esm({
@@ -16719,6 +16794,12 @@ var init_transport_local_adapter = __esm({
16719
16794
  #requestId = 1;
16720
16795
  ready;
16721
16796
  server;
16797
+ /**
16798
+ * Marks this transport as pre-initialized for session recreation.
16799
+ * Override in subclasses that need to set the MCP SDK's _initialized flag.
16800
+ */
16801
+ markAsInitialized() {
16802
+ }
16722
16803
  connectServer() {
16723
16804
  const { info } = this.scope.metadata;
16724
16805
  const hasPrompts = this.scope.prompts.hasAny();
@@ -16851,23 +16932,46 @@ var import_v42, TransportSSEAdapter;
16851
16932
  var init_transport_sse_adapter = __esm({
16852
16933
  "libs/sdk/src/transport/adapters/transport.sse.adapter.ts"() {
16853
16934
  "use strict";
16854
- init_legacy_sse_tranporter();
16935
+ init_sse_transport();
16855
16936
  init_transport_local_adapter();
16856
16937
  import_v42 = require("zod/v4");
16857
16938
  init_transport_error();
16858
16939
  TransportSSEAdapter = class extends LocalTransportAdapter {
16859
16940
  sessionId;
16941
+ /**
16942
+ * Configures common error and close handlers for SSE transports.
16943
+ */
16944
+ configureTransportHandlers(transport) {
16945
+ transport.onerror = (error) => {
16946
+ console.error("SSE error:", error instanceof Error ? error.message : "Unknown error");
16947
+ };
16948
+ transport.onclose = this.destroy.bind(this);
16949
+ }
16860
16950
  createTransport(sessionId, res) {
16861
16951
  this.sessionId = sessionId;
16862
16952
  this.logger.info(`new transport session: ${sessionId.slice(0, 40)}`);
16863
- const scopePath = this.scope.fullPath;
16864
- const transport = new SSEServerTransport(`${scopePath}/message`, res, {
16953
+ const transport = new RecreateableSSEServerTransport(`${this.scope.fullPath}/message`, res, {
16865
16954
  sessionId
16866
16955
  });
16867
- transport.onerror = (error) => {
16868
- console.error("SSE error:", error instanceof Error ? error.message : "Unknown error");
16869
- };
16870
- transport.onclose = this.destroy.bind(this);
16956
+ this.configureTransportHandlers(transport);
16957
+ return transport;
16958
+ }
16959
+ /**
16960
+ * Recreates a transport with preserved session state.
16961
+ * Use this when restoring a session from Redis or other storage.
16962
+ *
16963
+ * @param sessionId - The session ID to restore
16964
+ * @param res - The new response stream for SSE
16965
+ * @param lastEventId - The last event ID that was sent (for reconnection support)
16966
+ */
16967
+ createTransportFromSession(sessionId, res, lastEventId) {
16968
+ this.sessionId = sessionId;
16969
+ this.logger.info(`recreating transport session: ${sessionId.slice(0, 40)}, lastEventId: ${lastEventId ?? "none"}`);
16970
+ const transport = new RecreateableSSEServerTransport(`${this.scope.fullPath}/message`, res, {
16971
+ sessionId,
16972
+ initialEventId: lastEventId
16973
+ });
16974
+ this.configureTransportHandlers(transport);
16871
16975
  return transport;
16872
16976
  }
16873
16977
  initialize(req, res) {
@@ -16914,22 +17018,73 @@ var init_transport_sse_adapter = __esm({
16914
17018
  }
16915
17019
  });
16916
17020
 
17021
+ // libs/sdk/src/transport/adapters/streamable-http-transport.ts
17022
+ var import_streamableHttp, RecreateableStreamableHTTPServerTransport;
17023
+ var init_streamable_http_transport = __esm({
17024
+ "libs/sdk/src/transport/adapters/streamable-http-transport.ts"() {
17025
+ "use strict";
17026
+ import_streamableHttp = require("@modelcontextprotocol/sdk/server/streamableHttp.js");
17027
+ RecreateableStreamableHTTPServerTransport = class extends import_streamableHttp.StreamableHTTPServerTransport {
17028
+ constructor(options = {}) {
17029
+ super(options);
17030
+ }
17031
+ /**
17032
+ * Returns whether the transport has been initialized.
17033
+ */
17034
+ get isInitialized() {
17035
+ return this._webStandardTransport?._initialized ?? false;
17036
+ }
17037
+ /**
17038
+ * Sets the transport to an initialized state with the given session ID.
17039
+ * Use this when recreating a transport from a stored session.
17040
+ *
17041
+ * This method allows you to "restore" a session without replaying the
17042
+ * initialization handshake. After calling this method, the transport
17043
+ * will accept requests with the given session ID.
17044
+ *
17045
+ * @param sessionId - The session ID that was previously assigned to this session
17046
+ * @throws Error if sessionId is empty or invalid
17047
+ */
17048
+ setInitializationState(sessionId) {
17049
+ if (!sessionId || typeof sessionId !== "string" || sessionId.trim() === "") {
17050
+ throw new Error("[RecreateableStreamableHTTPServerTransport] sessionId cannot be empty");
17051
+ }
17052
+ const webTransport = this._webStandardTransport;
17053
+ if (!webTransport) {
17054
+ console.warn(
17055
+ "[RecreateableStreamableHTTPServerTransport] Internal transport not found. This may indicate an incompatible MCP SDK version."
17056
+ );
17057
+ return;
17058
+ }
17059
+ if (!("_initialized" in webTransport) || !("sessionId" in webTransport)) {
17060
+ console.warn(
17061
+ "[RecreateableStreamableHTTPServerTransport] Expected fields not found on internal transport. This may indicate an incompatible MCP SDK version."
17062
+ );
17063
+ return;
17064
+ }
17065
+ webTransport._initialized = true;
17066
+ webTransport.sessionId = sessionId;
17067
+ }
17068
+ };
17069
+ }
17070
+ });
17071
+
16917
17072
  // libs/sdk/src/transport/adapters/transport.streamable-http.adapter.ts
16918
- var import_streamableHttp, import_v43, resolveSessionIdGenerator, TransportStreamableHttpAdapter;
17073
+ var import_v43, resolveSessionIdGenerator, TransportStreamableHttpAdapter;
16919
17074
  var init_transport_streamable_http_adapter = __esm({
16920
17075
  "libs/sdk/src/transport/adapters/transport.streamable-http.adapter.ts"() {
16921
17076
  "use strict";
16922
- import_streamableHttp = require("@modelcontextprotocol/sdk/server/streamableHttp.js");
16923
17077
  init_transport_local_adapter();
16924
17078
  import_v43 = require("zod/v4");
16925
17079
  init_transport_error();
17080
+ init_streamable_http_transport();
16926
17081
  resolveSessionIdGenerator = (transportType, sessionId) => {
16927
17082
  return transportType === "stateless-http" ? void 0 : () => sessionId;
16928
17083
  };
16929
17084
  TransportStreamableHttpAdapter = class extends LocalTransportAdapter {
16930
17085
  createTransport(sessionId, response) {
16931
17086
  const sessionIdGenerator = resolveSessionIdGenerator(this.key.type, sessionId);
16932
- return new import_streamableHttp.StreamableHTTPServerTransport({
17087
+ return new RecreateableStreamableHTTPServerTransport({
16933
17088
  sessionIdGenerator,
16934
17089
  onsessionclosed: () => {
16935
17090
  },
@@ -17008,6 +17163,21 @@ var init_transport_streamable_http_adapter = __esm({
17008
17163
  };
17009
17164
  });
17010
17165
  }
17166
+ /**
17167
+ * Marks this transport as pre-initialized for session recreation.
17168
+ * This is needed when recreating a transport from Redis because the
17169
+ * original initialize request was processed by a different transport instance.
17170
+ *
17171
+ * Uses the RecreateableStreamableHTTPServerTransport's public API to set
17172
+ * initialization state, avoiding access to private properties.
17173
+ */
17174
+ markAsInitialized() {
17175
+ this.transport.setInitializationState(this.key.sessionId);
17176
+ this.logger.info("[StreamableHttpAdapter] Marked transport as pre-initialized for session recreation", {
17177
+ sessionId: this.key.sessionId?.slice(0, 20),
17178
+ isInitialized: this.transport.isInitialized
17179
+ });
17180
+ }
17011
17181
  };
17012
17182
  }
17013
17183
  });
@@ -17066,6 +17236,14 @@ var init_transport_local = __esm({
17066
17236
  res.status(500).json(rpcError("Internal error"));
17067
17237
  }
17068
17238
  }
17239
+ /**
17240
+ * Marks this transport as pre-initialized for session recreation.
17241
+ * This is needed when recreating a transport from Redis because the
17242
+ * original initialize request was processed by a different transport instance.
17243
+ */
17244
+ markAsInitialized() {
17245
+ this.adapter.markAsInitialized();
17246
+ }
17069
17247
  async destroy(reason) {
17070
17248
  try {
17071
17249
  await this.adapter.destroy(reason);
@@ -17203,14 +17381,27 @@ var init_handle_streamable_http_flow = __esm({
17203
17381
  const logger = this.scopeLogger.child("handle:streamable-http:onMessage");
17204
17382
  const { request, response } = this.rawInput;
17205
17383
  const { token, session } = this.state.required;
17384
+ logger.info("onMessage: starting", {
17385
+ sessionId: session.id?.slice(0, 20),
17386
+ hasToken: !!token
17387
+ });
17206
17388
  let transport = await transportService.getTransporter("streamable-http", token, session.id);
17389
+ logger.info("onMessage: getTransporter result", { found: !!transport });
17207
17390
  if (!transport) {
17208
17391
  try {
17392
+ logger.info("onMessage: transport not in memory, checking Redis", {
17393
+ sessionId: session.id?.slice(0, 20)
17394
+ });
17209
17395
  const storedSession = await transportService.getStoredSession("streamable-http", token, session.id);
17396
+ logger.info("onMessage: getStoredSession result", {
17397
+ found: !!storedSession,
17398
+ initialized: storedSession?.initialized
17399
+ });
17210
17400
  if (storedSession) {
17211
17401
  logger.info("Recreating transport from Redis session", {
17212
17402
  sessionId: session.id?.slice(0, 20),
17213
- createdAt: storedSession.createdAt
17403
+ createdAt: storedSession.createdAt,
17404
+ initialized: storedSession.initialized
17214
17405
  });
17215
17406
  transport = await transportService.recreateTransporter(
17216
17407
  "streamable-http",
@@ -17219,6 +17410,7 @@ var init_handle_streamable_http_flow = __esm({
17219
17410
  storedSession,
17220
17411
  response
17221
17412
  );
17413
+ logger.info("onMessage: transport recreated successfully");
17222
17414
  }
17223
17415
  } catch (error) {
17224
17416
  logger.warn("Failed to recreate transport from stored session", {
@@ -17615,27 +17807,244 @@ var init_store_helpers = __esm({
17615
17807
  }
17616
17808
  });
17617
17809
 
17810
+ // libs/sdk/src/auth/session/session-crypto.ts
17811
+ function getSigningSecret(config) {
17812
+ const secret = config?.secret || process.env["MCP_SESSION_SECRET"];
17813
+ if (!secret) {
17814
+ if (process.env["NODE_ENV"] === "production") {
17815
+ throw new Error(
17816
+ "[SessionCrypto] MCP_SESSION_SECRET is required in production for session signing. Set this environment variable to a secure random string."
17817
+ );
17818
+ }
17819
+ console.warn("[SessionCrypto] MCP_SESSION_SECRET not set. Using insecure default for development only.");
17820
+ return "insecure-dev-secret-do-not-use-in-production";
17821
+ }
17822
+ return secret;
17823
+ }
17824
+ function computeSignature(data, secret) {
17825
+ return (0, import_crypto13.createHmac)("sha256", secret).update(data, "utf8").digest("base64url");
17826
+ }
17827
+ function signSession(session, config) {
17828
+ const secret = getSigningSecret(config);
17829
+ const data = JSON.stringify(session);
17830
+ const sig = computeSignature(data, secret);
17831
+ const signed = {
17832
+ data: session,
17833
+ sig,
17834
+ v: 1
17835
+ };
17836
+ return JSON.stringify(signed);
17837
+ }
17838
+ function verifySession(signedData, config) {
17839
+ try {
17840
+ const secret = getSigningSecret(config);
17841
+ const parsed = JSON.parse(signedData);
17842
+ if (!parsed || typeof parsed !== "object" || !("sig" in parsed)) {
17843
+ return null;
17844
+ }
17845
+ const signed = parsed;
17846
+ if (signed.v !== 1) {
17847
+ console.warn("[SessionCrypto] Unknown signature version:", signed.v);
17848
+ return null;
17849
+ }
17850
+ const data = JSON.stringify(signed.data);
17851
+ const expectedSig = computeSignature(data, secret);
17852
+ const sigBuffer = Buffer.from(signed.sig, "base64url");
17853
+ const expectedBuffer = Buffer.from(expectedSig, "base64url");
17854
+ if (sigBuffer.length !== expectedBuffer.length) {
17855
+ console.warn("[SessionCrypto] Signature length mismatch - possible tampering");
17856
+ return null;
17857
+ }
17858
+ if (!(0, import_crypto13.timingSafeEqual)(sigBuffer, expectedBuffer)) {
17859
+ console.warn("[SessionCrypto] HMAC verification failed - session data may be tampered");
17860
+ return null;
17861
+ }
17862
+ return signed.data;
17863
+ } catch (error) {
17864
+ console.warn("[SessionCrypto] Failed to verify session:", error.message);
17865
+ return null;
17866
+ }
17867
+ }
17868
+ function isSignedSession(data) {
17869
+ try {
17870
+ const parsed = JSON.parse(data);
17871
+ return parsed && typeof parsed === "object" && "sig" in parsed && "v" in parsed;
17872
+ } catch {
17873
+ return false;
17874
+ }
17875
+ }
17876
+ function verifyOrParseSession(data, config) {
17877
+ if (isSignedSession(data)) {
17878
+ return verifySession(data, config);
17879
+ }
17880
+ try {
17881
+ return JSON.parse(data);
17882
+ } catch {
17883
+ return null;
17884
+ }
17885
+ }
17886
+ var import_crypto13;
17887
+ var init_session_crypto = __esm({
17888
+ "libs/sdk/src/auth/session/session-crypto.ts"() {
17889
+ "use strict";
17890
+ import_crypto13 = require("crypto");
17891
+ }
17892
+ });
17893
+
17894
+ // libs/sdk/src/auth/session/session-rate-limiter.ts
17895
+ var SessionRateLimiter, defaultSessionRateLimiter;
17896
+ var init_session_rate_limiter = __esm({
17897
+ "libs/sdk/src/auth/session/session-rate-limiter.ts"() {
17898
+ "use strict";
17899
+ SessionRateLimiter = class {
17900
+ windowMs;
17901
+ maxRequests;
17902
+ requests = /* @__PURE__ */ new Map();
17903
+ cleanupTimer = null;
17904
+ constructor(config = {}) {
17905
+ this.windowMs = config.windowMs ?? 6e4;
17906
+ this.maxRequests = config.maxRequests ?? 100;
17907
+ const cleanupIntervalMs = config.cleanupIntervalMs ?? 6e4;
17908
+ if (cleanupIntervalMs > 0) {
17909
+ this.cleanupTimer = setInterval(() => this.cleanup(), cleanupIntervalMs);
17910
+ this.cleanupTimer.unref();
17911
+ }
17912
+ }
17913
+ /**
17914
+ * Check if a request is allowed for the given key.
17915
+ *
17916
+ * @param key - Identifier for rate limiting (e.g., client IP, session ID prefix)
17917
+ * @returns Rate limit result with allowed status and metadata
17918
+ */
17919
+ check(key) {
17920
+ const now = Date.now();
17921
+ const windowStart = now - this.windowMs;
17922
+ let timestamps = this.requests.get(key);
17923
+ if (!timestamps) {
17924
+ timestamps = [];
17925
+ this.requests.set(key, timestamps);
17926
+ }
17927
+ const validTimestamps = timestamps.filter((t) => t > windowStart);
17928
+ const oldestInWindow = validTimestamps[0] ?? now;
17929
+ const resetAt = oldestInWindow + this.windowMs;
17930
+ if (validTimestamps.length >= this.maxRequests) {
17931
+ return {
17932
+ allowed: false,
17933
+ remaining: 0,
17934
+ resetAt,
17935
+ retryAfterMs: resetAt - now
17936
+ };
17937
+ }
17938
+ validTimestamps.push(now);
17939
+ this.requests.set(key, validTimestamps);
17940
+ return {
17941
+ allowed: true,
17942
+ remaining: this.maxRequests - validTimestamps.length,
17943
+ resetAt
17944
+ };
17945
+ }
17946
+ /**
17947
+ * Check if a request would be allowed without recording it.
17948
+ * Useful for pre-checking without consuming quota.
17949
+ *
17950
+ * @param key - Identifier for rate limiting
17951
+ * @returns true if request would be allowed
17952
+ */
17953
+ wouldAllow(key) {
17954
+ const now = Date.now();
17955
+ const windowStart = now - this.windowMs;
17956
+ const timestamps = this.requests.get(key);
17957
+ if (!timestamps) return true;
17958
+ const validCount = timestamps.filter((t) => t > windowStart).length;
17959
+ return validCount < this.maxRequests;
17960
+ }
17961
+ /**
17962
+ * Reset rate limit for a specific key.
17963
+ * Useful for testing or after successful authentication.
17964
+ *
17965
+ * @param key - Identifier to reset
17966
+ */
17967
+ reset(key) {
17968
+ this.requests.delete(key);
17969
+ }
17970
+ /**
17971
+ * Clean up expired entries from all keys.
17972
+ * Called automatically on configured interval, but can be called manually.
17973
+ */
17974
+ cleanup() {
17975
+ const now = Date.now();
17976
+ const windowStart = now - this.windowMs;
17977
+ for (const [key, timestamps] of this.requests.entries()) {
17978
+ const valid = timestamps.filter((t) => t > windowStart);
17979
+ if (valid.length === 0) {
17980
+ this.requests.delete(key);
17981
+ } else if (valid.length < timestamps.length) {
17982
+ this.requests.set(key, valid);
17983
+ }
17984
+ }
17985
+ }
17986
+ /**
17987
+ * Get current statistics for monitoring.
17988
+ */
17989
+ getStats() {
17990
+ let totalRequests = 0;
17991
+ for (const timestamps of this.requests.values()) {
17992
+ totalRequests += timestamps.length;
17993
+ }
17994
+ return {
17995
+ totalKeys: this.requests.size,
17996
+ totalRequests
17997
+ };
17998
+ }
17999
+ /**
18000
+ * Stop the automatic cleanup timer.
18001
+ * Call this when disposing of the rate limiter.
18002
+ */
18003
+ dispose() {
18004
+ if (this.cleanupTimer) {
18005
+ clearInterval(this.cleanupTimer);
18006
+ this.cleanupTimer = null;
18007
+ }
18008
+ this.requests.clear();
18009
+ }
18010
+ };
18011
+ defaultSessionRateLimiter = new SessionRateLimiter();
18012
+ }
18013
+ });
18014
+
17618
18015
  // libs/sdk/src/auth/session/redis-session.store.ts
17619
18016
  var redis_session_store_exports = {};
17620
18017
  __export(redis_session_store_exports, {
17621
18018
  RedisSessionStore: () => RedisSessionStore
17622
18019
  });
17623
- var import_ioredis, import_crypto13, RedisSessionStore;
18020
+ var import_ioredis, import_crypto14, RedisSessionStore;
17624
18021
  var init_redis_session_store = __esm({
17625
18022
  "libs/sdk/src/auth/session/redis-session.store.ts"() {
17626
18023
  "use strict";
17627
18024
  import_ioredis = __toESM(require("ioredis"));
17628
- import_crypto13 = require("crypto");
18025
+ import_crypto14 = require("crypto");
17629
18026
  init_transport_session_types();
18027
+ init_session_crypto();
18028
+ init_session_rate_limiter();
17630
18029
  RedisSessionStore = class {
17631
18030
  redis;
17632
18031
  keyPrefix;
17633
18032
  defaultTtlMs;
17634
18033
  logger;
17635
18034
  externalInstance = false;
18035
+ // Security features
18036
+ security;
18037
+ rateLimiter;
17636
18038
  constructor(config, logger) {
17637
18039
  this.defaultTtlMs = ("defaultTtlMs" in config ? config.defaultTtlMs : void 0) ?? 36e5;
17638
18040
  this.logger = logger;
18041
+ this.security = ("security" in config ? config.security : void 0) ?? {};
18042
+ if (this.security.enableRateLimiting) {
18043
+ this.rateLimiter = new SessionRateLimiter({
18044
+ windowMs: this.security.rateLimiting?.windowMs,
18045
+ maxRequests: this.security.rateLimiting?.maxRequests
18046
+ });
18047
+ }
17639
18048
  if ("redis" in config && config.redis) {
17640
18049
  this.redis = config.redis;
17641
18050
  this.keyPrefix = config.keyPrefix ?? "mcp:session:";
@@ -17670,8 +18079,26 @@ var init_redis_session_store = __esm({
17670
18079
  *
17671
18080
  * Note: Uses atomic GETEX to extend TTL while reading, preventing race conditions
17672
18081
  * where concurrent readers might resurrect expired sessions.
17673
- */
17674
- async get(sessionId) {
18082
+ *
18083
+ * @param sessionId - The session ID to look up
18084
+ * @param options - Optional parameters for rate limiting
18085
+ * @param options.clientIdentifier - Client identifier (e.g., IP address) for rate limiting.
18086
+ * When provided, rate limiting is applied per-client to prevent session enumeration.
18087
+ * If not provided, falls back to sessionId which provides DoS protection per-session.
18088
+ */
18089
+ async get(sessionId, options) {
18090
+ if (this.rateLimiter) {
18091
+ const rateLimitKey = options?.clientIdentifier || sessionId;
18092
+ const rateLimitResult = this.rateLimiter.check(rateLimitKey);
18093
+ if (!rateLimitResult.allowed) {
18094
+ this.logger?.warn("[RedisSessionStore] Rate limit exceeded for session lookup", {
18095
+ sessionId: sessionId.slice(0, 20),
18096
+ clientIdentifier: options?.clientIdentifier ? options.clientIdentifier.slice(0, 20) : void 0,
18097
+ retryAfterMs: rateLimitResult.retryAfterMs
18098
+ });
18099
+ return null;
18100
+ }
18101
+ }
17675
18102
  const key = this.key(sessionId);
17676
18103
  let raw;
17677
18104
  try {
@@ -17681,7 +18108,19 @@ var init_redis_session_store = __esm({
17681
18108
  }
17682
18109
  if (!raw) return null;
17683
18110
  try {
17684
- const parsed = JSON.parse(raw);
18111
+ let parsed;
18112
+ if (this.security.enableSigning) {
18113
+ parsed = verifyOrParseSession(raw, { secret: this.security.signingSecret });
18114
+ if (!parsed) {
18115
+ this.logger?.warn("[RedisSessionStore] Session signature verification failed", {
18116
+ sessionId: sessionId.slice(0, 20)
18117
+ });
18118
+ this.delete(sessionId).catch(() => void 0);
18119
+ return null;
18120
+ }
18121
+ } else {
18122
+ parsed = JSON.parse(raw);
18123
+ }
17685
18124
  const result = storedSessionSchema.safeParse(parsed);
17686
18125
  if (!result.success) {
17687
18126
  this.logger?.warn("[RedisSessionStore] Invalid session format", {
@@ -17692,6 +18131,14 @@ var init_redis_session_store = __esm({
17692
18131
  return null;
17693
18132
  }
17694
18133
  const session = result.data;
18134
+ if (session.maxLifetimeAt && session.maxLifetimeAt < Date.now()) {
18135
+ this.logger?.info("[RedisSessionStore] Session exceeded max lifetime", {
18136
+ sessionId: sessionId.slice(0, 20),
18137
+ maxLifetimeAt: session.maxLifetimeAt
18138
+ });
18139
+ await this.delete(sessionId);
18140
+ return null;
18141
+ }
17695
18142
  if (session.session.expiresAt && session.session.expiresAt < Date.now()) {
17696
18143
  await this.delete(sessionId);
17697
18144
  return null;
@@ -17699,7 +18146,12 @@ var init_redis_session_store = __esm({
17699
18146
  if (session.session.expiresAt) {
17700
18147
  const ttlMs = Math.min(this.defaultTtlMs, session.session.expiresAt - Date.now());
17701
18148
  if (ttlMs > 0 && ttlMs < this.defaultTtlMs) {
17702
- this.redis.pexpire(key, ttlMs).catch(() => void 0);
18149
+ this.redis.pexpire(key, ttlMs).catch((err) => {
18150
+ this.logger?.warn("[RedisSessionStore] TTL extension failed", {
18151
+ sessionId: sessionId.slice(0, 20),
18152
+ error: err.message
18153
+ });
18154
+ });
17703
18155
  }
17704
18156
  }
17705
18157
  const updatedSession = {
@@ -17721,7 +18173,12 @@ var init_redis_session_store = __esm({
17721
18173
  */
17722
18174
  async set(sessionId, session, ttlMs) {
17723
18175
  const key = this.key(sessionId);
17724
- const value = JSON.stringify(session);
18176
+ let value;
18177
+ if (this.security.enableSigning) {
18178
+ value = signSession(session, { secret: this.security.signingSecret });
18179
+ } else {
18180
+ value = JSON.stringify(session);
18181
+ }
17725
18182
  if (ttlMs && ttlMs > 0) {
17726
18183
  await this.redis.set(key, value, "PX", ttlMs);
17727
18184
  } else if (session.session.expiresAt) {
@@ -17751,7 +18208,7 @@ var init_redis_session_store = __esm({
17751
18208
  * Allocate a new session ID
17752
18209
  */
17753
18210
  allocId() {
17754
- return (0, import_crypto13.randomUUID)();
18211
+ return (0, import_crypto14.randomUUID)();
17755
18212
  }
17756
18213
  /**
17757
18214
  * Disconnect from Redis (only if we created the connection)
@@ -17790,12 +18247,14 @@ var vercel_kv_session_store_exports = {};
17790
18247
  __export(vercel_kv_session_store_exports, {
17791
18248
  VercelKvSessionStore: () => VercelKvSessionStore
17792
18249
  });
17793
- var import_crypto14, VercelKvSessionStore;
18250
+ var import_crypto15, VercelKvSessionStore;
17794
18251
  var init_vercel_kv_session_store = __esm({
17795
18252
  "libs/sdk/src/auth/session/vercel-kv-session.store.ts"() {
17796
18253
  "use strict";
17797
- import_crypto14 = require("crypto");
18254
+ import_crypto15 = require("crypto");
17798
18255
  init_transport_session_types();
18256
+ init_session_crypto();
18257
+ init_session_rate_limiter();
17799
18258
  VercelKvSessionStore = class {
17800
18259
  kv = null;
17801
18260
  connectPromise = null;
@@ -17803,11 +18262,21 @@ var init_vercel_kv_session_store = __esm({
17803
18262
  defaultTtlMs;
17804
18263
  logger;
17805
18264
  config;
18265
+ // Security features
18266
+ security;
18267
+ rateLimiter;
17806
18268
  constructor(config, logger) {
17807
18269
  this.config = config;
17808
18270
  this.keyPrefix = config.keyPrefix ?? "mcp:session:";
17809
18271
  this.defaultTtlMs = config.defaultTtlMs ?? 36e5;
17810
18272
  this.logger = logger;
18273
+ this.security = ("security" in config ? config.security : void 0) ?? {};
18274
+ if (this.security.enableRateLimiting) {
18275
+ this.rateLimiter = new SessionRateLimiter({
18276
+ windowMs: this.security.rateLimiting?.windowMs,
18277
+ maxRequests: this.security.rateLimiting?.maxRequests
18278
+ });
18279
+ }
17811
18280
  }
17812
18281
  /**
17813
18282
  * Connect to Vercel KV
@@ -17860,15 +18329,51 @@ var init_vercel_kv_session_store = __esm({
17860
18329
  *
17861
18330
  * Note: Vercel KV doesn't support GETEX, so we use GET + PEXPIRE separately.
17862
18331
  * This is slightly less atomic than Redis GETEX but sufficient for most use cases.
17863
- */
17864
- async get(sessionId) {
18332
+ *
18333
+ * @param sessionId - The session ID to look up
18334
+ * @param options - Optional parameters for rate limiting
18335
+ * @param options.clientIdentifier - Client identifier (e.g., IP address) for rate limiting.
18336
+ * When provided, rate limiting is applied per-client to prevent session enumeration.
18337
+ * If not provided, falls back to sessionId which provides DoS protection per-session.
18338
+ */
18339
+ async get(sessionId, options) {
18340
+ if (this.rateLimiter) {
18341
+ const rateLimitKey = options?.clientIdentifier || sessionId;
18342
+ const rateLimitResult = this.rateLimiter.check(rateLimitKey);
18343
+ if (!rateLimitResult.allowed) {
18344
+ this.logger?.warn("[VercelKvSessionStore] Rate limit exceeded for session lookup", {
18345
+ sessionId: sessionId.slice(0, 20),
18346
+ clientIdentifier: options?.clientIdentifier ? options.clientIdentifier.slice(0, 20) : void 0,
18347
+ retryAfterMs: rateLimitResult.retryAfterMs
18348
+ });
18349
+ return null;
18350
+ }
18351
+ }
17865
18352
  const kv = await this.ensureConnected();
17866
18353
  const key = this.key(sessionId);
17867
18354
  const raw = await kv.get(key);
17868
18355
  if (!raw) return null;
17869
- kv.pexpire(key, this.defaultTtlMs).catch(() => void 0);
18356
+ kv.pexpire(key, this.defaultTtlMs).catch((err) => {
18357
+ this.logger?.warn("[VercelKvSessionStore] TTL extension failed", {
18358
+ sessionId: sessionId.slice(0, 20),
18359
+ error: err.message
18360
+ });
18361
+ });
17870
18362
  try {
17871
- const parsed = typeof raw === "string" ? JSON.parse(raw) : raw;
18363
+ let parsed;
18364
+ const rawStr = typeof raw === "string" ? raw : JSON.stringify(raw);
18365
+ if (this.security.enableSigning) {
18366
+ parsed = verifyOrParseSession(rawStr, { secret: this.security.signingSecret });
18367
+ if (!parsed) {
18368
+ this.logger?.warn("[VercelKvSessionStore] Session signature verification failed", {
18369
+ sessionId: sessionId.slice(0, 20)
18370
+ });
18371
+ this.delete(sessionId).catch(() => void 0);
18372
+ return null;
18373
+ }
18374
+ } else {
18375
+ parsed = typeof raw === "string" ? JSON.parse(raw) : raw;
18376
+ }
17872
18377
  const result = storedSessionSchema.safeParse(parsed);
17873
18378
  if (!result.success) {
17874
18379
  this.logger?.warn("[VercelKvSessionStore] Invalid session format", {
@@ -17879,6 +18384,14 @@ var init_vercel_kv_session_store = __esm({
17879
18384
  return null;
17880
18385
  }
17881
18386
  const session = result.data;
18387
+ if (session.maxLifetimeAt && session.maxLifetimeAt < Date.now()) {
18388
+ this.logger?.info("[VercelKvSessionStore] Session exceeded max lifetime", {
18389
+ sessionId: sessionId.slice(0, 20),
18390
+ maxLifetimeAt: session.maxLifetimeAt
18391
+ });
18392
+ await this.delete(sessionId);
18393
+ return null;
18394
+ }
17882
18395
  if (session.session.expiresAt && session.session.expiresAt < Date.now()) {
17883
18396
  await this.delete(sessionId);
17884
18397
  return null;
@@ -17886,7 +18399,12 @@ var init_vercel_kv_session_store = __esm({
17886
18399
  if (session.session.expiresAt) {
17887
18400
  const ttlMs = Math.min(this.defaultTtlMs, session.session.expiresAt - Date.now());
17888
18401
  if (ttlMs > 0 && ttlMs < this.defaultTtlMs) {
17889
- kv.pexpire(key, ttlMs).catch(() => void 0);
18402
+ kv.pexpire(key, ttlMs).catch((err) => {
18403
+ this.logger?.warn("[VercelKvSessionStore] TTL bound extension failed", {
18404
+ sessionId: sessionId.slice(0, 20),
18405
+ error: err.message
18406
+ });
18407
+ });
17890
18408
  }
17891
18409
  }
17892
18410
  const updatedSession = {
@@ -17909,7 +18427,12 @@ var init_vercel_kv_session_store = __esm({
17909
18427
  async set(sessionId, session, ttlMs) {
17910
18428
  const kv = await this.ensureConnected();
17911
18429
  const key = this.key(sessionId);
17912
- const value = JSON.stringify(session);
18430
+ let value;
18431
+ if (this.security.enableSigning) {
18432
+ value = signSession(session, { secret: this.security.signingSecret });
18433
+ } else {
18434
+ value = JSON.stringify(session);
18435
+ }
17913
18436
  if (ttlMs && ttlMs > 0) {
17914
18437
  await kv.set(key, value, { px: ttlMs });
17915
18438
  } else if (session.session.expiresAt) {
@@ -17941,7 +18464,7 @@ var init_vercel_kv_session_store = __esm({
17941
18464
  * Allocate a new session ID
17942
18465
  */
17943
18466
  allocId() {
17944
- return (0, import_crypto14.randomUUID)();
18467
+ return (0, import_crypto15.randomUUID)();
17945
18468
  }
17946
18469
  /**
17947
18470
  * Disconnect from Vercel KV
@@ -18066,11 +18589,11 @@ var init_store = __esm({
18066
18589
  });
18067
18590
 
18068
18591
  // libs/sdk/src/transport/transport.registry.ts
18069
- var import_crypto15, TransportService;
18592
+ var import_crypto16, TransportService;
18070
18593
  var init_transport_registry = __esm({
18071
18594
  "libs/sdk/src/transport/transport.registry.ts"() {
18072
18595
  "use strict";
18073
- import_crypto15 = require("crypto");
18596
+ import_crypto16 = require("crypto");
18074
18597
  init_transport_remote();
18075
18598
  init_transport_local();
18076
18599
  init_handle_streamable_http_flow();
@@ -18195,9 +18718,12 @@ var init_transport_registry = __esm({
18195
18718
  * @param type - Transport type
18196
18719
  * @param token - Authorization token
18197
18720
  * @param sessionId - Session ID
18721
+ * @param options - Optional validation options
18722
+ * @param options.clientFingerprint - Client fingerprint for additional validation
18723
+ * @param options.warnOnFingerprintMismatch - If true, log warning on mismatch but still return session
18198
18724
  * @returns Stored session data if exists and token matches, undefined otherwise
18199
18725
  */
18200
- async getStoredSession(type, token, sessionId) {
18726
+ async getStoredSession(type, token, sessionId, options) {
18201
18727
  if (!this.sessionStore || type !== "streamable-http") return void 0;
18202
18728
  const tokenHash = this.sha256(token);
18203
18729
  const stored = await this.sessionStore.get(sessionId);
@@ -18210,6 +18736,18 @@ var init_transport_registry = __esm({
18210
18736
  });
18211
18737
  return void 0;
18212
18738
  }
18739
+ if (options?.clientFingerprint && stored.session.clientFingerprint) {
18740
+ if (stored.session.clientFingerprint !== options.clientFingerprint) {
18741
+ this.scope.logger.warn("[TransportService] Client fingerprint mismatch", {
18742
+ sessionId: sessionId.slice(0, 20),
18743
+ storedFingerprint: stored.session.clientFingerprint.slice(0, 8),
18744
+ requestFingerprint: options.clientFingerprint.slice(0, 8)
18745
+ });
18746
+ if (!options.warnOnFingerprintMismatch) {
18747
+ return void 0;
18748
+ }
18749
+ }
18750
+ }
18213
18751
  return stored;
18214
18752
  }
18215
18753
  /**
@@ -18272,6 +18810,9 @@ var init_transport_registry = __esm({
18272
18810
  }
18273
18811
  });
18274
18812
  await transporter.ready();
18813
+ if (storedSession.initialized !== false) {
18814
+ transporter.markAsInitialized();
18815
+ }
18275
18816
  this.insertLocal(key, transporter);
18276
18817
  if (sessionStore) {
18277
18818
  const updatedSession = {
@@ -18338,7 +18879,9 @@ var init_transport_registry = __esm({
18338
18879
  },
18339
18880
  authorizationId: key.tokenHash,
18340
18881
  createdAt: Date.now(),
18341
- lastAccessedAt: Date.now()
18882
+ lastAccessedAt: Date.now(),
18883
+ initialized: true
18884
+ // Mark as initialized for session recreation
18342
18885
  };
18343
18886
  sessionStore.set(sessionId, storedSession, persistenceConfig?.defaultTtlMs).catch((err) => {
18344
18887
  this.scope.logger.warn("[TransportService] Failed to persist session to Redis", {
@@ -18444,7 +18987,7 @@ var init_transport_registry = __esm({
18444
18987
  }
18445
18988
  /* --------------------------------- internals -------------------------------- */
18446
18989
  sha256(value) {
18447
- return (0, import_crypto15.createHash)("sha256").update(value, "utf8").digest("hex");
18990
+ return (0, import_crypto16.createHash)("sha256").update(value, "utf8").digest("hex");
18448
18991
  }
18449
18992
  /**
18450
18993
  * Create a history key from components.
@@ -19927,6 +20470,54 @@ var init_front_mcp2 = __esm({
19927
20470
  }
19928
20471
  });
19929
20472
 
20473
+ // libs/sdk/src/front-mcp/serverless-handler.ts
20474
+ var serverless_handler_exports = {};
20475
+ __export(serverless_handler_exports, {
20476
+ getServerlessHandler: () => getServerlessHandler,
20477
+ getServerlessHandlerAsync: () => getServerlessHandlerAsync,
20478
+ setServerlessHandler: () => setServerlessHandler,
20479
+ setServerlessHandlerError: () => setServerlessHandlerError,
20480
+ setServerlessHandlerPromise: () => setServerlessHandlerPromise
20481
+ });
20482
+ function setServerlessHandler(handler) {
20483
+ globalHandler = handler;
20484
+ }
20485
+ function setServerlessHandlerPromise(promise) {
20486
+ globalHandlerPromise = promise;
20487
+ }
20488
+ function setServerlessHandlerError(error) {
20489
+ globalHandlerError = error;
20490
+ }
20491
+ function getServerlessHandler() {
20492
+ if (globalHandlerError) {
20493
+ throw globalHandlerError;
20494
+ }
20495
+ return globalHandler;
20496
+ }
20497
+ async function getServerlessHandlerAsync() {
20498
+ if (globalHandlerError) {
20499
+ throw globalHandlerError;
20500
+ }
20501
+ if (globalHandlerPromise) {
20502
+ return globalHandlerPromise;
20503
+ }
20504
+ if (!globalHandler) {
20505
+ throw new Error(
20506
+ "Serverless handler not initialized. Ensure @FrontMcp decorator ran and FRONTMCP_SERVERLESS=1 is set."
20507
+ );
20508
+ }
20509
+ return globalHandler;
20510
+ }
20511
+ var globalHandler, globalHandlerPromise, globalHandlerError;
20512
+ var init_serverless_handler = __esm({
20513
+ "libs/sdk/src/front-mcp/serverless-handler.ts"() {
20514
+ "use strict";
20515
+ globalHandler = null;
20516
+ globalHandlerPromise = null;
20517
+ globalHandlerError = null;
20518
+ }
20519
+ });
20520
+
19930
20521
  // libs/sdk/src/common/decorators/front-mcp.decorator.ts
19931
20522
  function getFrontMcpInstance() {
19932
20523
  if (!_FrontMcpInstance) {
@@ -19937,6 +20528,15 @@ function getFrontMcpInstance() {
19937
20528
  }
19938
20529
  return _FrontMcpInstance;
19939
20530
  }
20531
+ function getServerlessHandlerFns() {
20532
+ if (!_serverlessHandlerFns) {
20533
+ _serverlessHandlerFns = (init_serverless_handler(), __toCommonJS(serverless_handler_exports));
20534
+ }
20535
+ if (!_serverlessHandlerFns) {
20536
+ throw new InternalMcpError("Serverless handler functions not found", "MODULE_LOAD_FAILED");
20537
+ }
20538
+ return _serverlessHandlerFns;
20539
+ }
19940
20540
  function FrontMcp(providedMetadata) {
19941
20541
  return (target) => {
19942
20542
  const migratedMetadata = applyMigration(providedMetadata);
@@ -19977,18 +20577,8 @@ ${JSON.stringify(
19977
20577
  }
19978
20578
  const isServerless = typeof process !== "undefined" && process.env?.["FRONTMCP_SERVERLESS"] === "1";
19979
20579
  if (isServerless) {
19980
- const {
19981
- FrontMcpInstance: ServerlessInstance,
19982
- setServerlessHandler: setServerlessHandler2,
19983
- setServerlessHandlerPromise: setServerlessHandlerPromise2,
19984
- setServerlessHandlerError: setServerlessHandlerError2
19985
- } = require("@frontmcp/sdk");
19986
- if (!ServerlessInstance) {
19987
- throw new InternalMcpError(
19988
- "@frontmcp/sdk version mismatch, make sure you have the same version for all @frontmcp/* packages",
19989
- "SDK_VERSION_MISMATCH"
19990
- );
19991
- }
20580
+ const ServerlessInstance = getFrontMcpInstance();
20581
+ const { setServerlessHandler: setServerlessHandler2, setServerlessHandlerPromise: setServerlessHandlerPromise2, setServerlessHandlerError: setServerlessHandlerError2 } = getServerlessHandlerFns();
19992
20582
  const handlerPromise = ServerlessInstance.createHandler(metadata);
19993
20583
  setServerlessHandlerPromise2(handlerPromise);
19994
20584
  handlerPromise.then(setServerlessHandler2).catch((err) => {
@@ -20001,7 +20591,7 @@ ${JSON.stringify(
20001
20591
  }
20002
20592
  };
20003
20593
  }
20004
- var import_reflect_metadata20, _FrontMcpInstance;
20594
+ var import_reflect_metadata20, _FrontMcpInstance, _serverlessHandlerFns;
20005
20595
  var init_front_mcp_decorator = __esm({
20006
20596
  "libs/sdk/src/common/decorators/front-mcp.decorator.ts"() {
20007
20597
  "use strict";
@@ -20011,6 +20601,7 @@ var init_front_mcp_decorator = __esm({
20011
20601
  init_migrate();
20012
20602
  init_mcp_error();
20013
20603
  _FrontMcpInstance = null;
20604
+ _serverlessHandlerFns = null;
20014
20605
  }
20015
20606
  });
20016
20607
 
@@ -20493,11 +21084,11 @@ var init_flow_interface = __esm({
20493
21084
  });
20494
21085
 
20495
21086
  // libs/sdk/src/common/interfaces/execution-context.interface.ts
20496
- var import_crypto16, ExecutionContextBase;
21087
+ var import_crypto17, ExecutionContextBase;
20497
21088
  var init_execution_context_interface = __esm({
20498
21089
  "libs/sdk/src/common/interfaces/execution-context.interface.ts"() {
20499
21090
  "use strict";
20500
- import_crypto16 = require("crypto");
21091
+ import_crypto17 = require("crypto");
20501
21092
  init_flow_interface();
20502
21093
  init_context();
20503
21094
  init_mcp_error();
@@ -20516,7 +21107,7 @@ var init_execution_context_interface = __esm({
20516
21107
  _error;
20517
21108
  constructor(args) {
20518
21109
  const { providers, logger, authInfo } = args;
20519
- this.runId = (0, import_crypto16.randomUUID)();
21110
+ this.runId = (0, import_crypto17.randomUUID)();
20520
21111
  this.providers = providers;
20521
21112
  this.logger = logger;
20522
21113
  this._authInfo = authInfo;
@@ -20889,11 +21480,11 @@ var init_resource_interface = __esm({
20889
21480
  });
20890
21481
 
20891
21482
  // libs/sdk/src/common/interfaces/prompt.interface.ts
20892
- var import_crypto17, PromptContext;
21483
+ var import_crypto18, PromptContext;
20893
21484
  var init_prompt_interface = __esm({
20894
21485
  "libs/sdk/src/common/interfaces/prompt.interface.ts"() {
20895
21486
  "use strict";
20896
- import_crypto17 = require("crypto");
21487
+ import_crypto18 = require("crypto");
20897
21488
  init_flow_interface();
20898
21489
  PromptContext = class {
20899
21490
  providers;
@@ -20913,7 +21504,7 @@ var init_prompt_interface = __esm({
20913
21504
  _outputHistory = [];
20914
21505
  constructor(ctorArgs) {
20915
21506
  const { metadata, args, providers, logger, authInfo } = ctorArgs;
20916
- this.runId = (0, import_crypto17.randomUUID)();
21507
+ this.runId = (0, import_crypto18.randomUUID)();
20917
21508
  this.promptName = metadata.name;
20918
21509
  this.promptId = metadata.name;
20919
21510
  this.metadata = metadata;
@@ -22619,42 +23210,7 @@ module.exports = __toCommonJS(index_exports);
22619
23210
  var import_reflect_metadata31 = require("reflect-metadata");
22620
23211
  init_common();
22621
23212
  init_front_mcp2();
22622
-
22623
- // libs/sdk/src/front-mcp/serverless-handler.ts
22624
- var globalHandler = null;
22625
- var globalHandlerPromise = null;
22626
- var globalHandlerError = null;
22627
- function setServerlessHandler(handler) {
22628
- globalHandler = handler;
22629
- }
22630
- function setServerlessHandlerPromise(promise) {
22631
- globalHandlerPromise = promise;
22632
- }
22633
- function setServerlessHandlerError(error) {
22634
- globalHandlerError = error;
22635
- }
22636
- function getServerlessHandler() {
22637
- if (globalHandlerError) {
22638
- throw globalHandlerError;
22639
- }
22640
- return globalHandler;
22641
- }
22642
- async function getServerlessHandlerAsync() {
22643
- if (globalHandlerError) {
22644
- throw globalHandlerError;
22645
- }
22646
- if (globalHandlerPromise) {
22647
- return globalHandlerPromise;
22648
- }
22649
- if (!globalHandler) {
22650
- throw new Error(
22651
- "Serverless handler not initialized. Ensure @FrontMcp decorator ran and FRONTMCP_SERVERLESS=1 is set."
22652
- );
22653
- }
22654
- return globalHandler;
22655
- }
22656
-
22657
- // libs/sdk/src/index.ts
23213
+ init_serverless_handler();
22658
23214
  init_common();
22659
23215
  init_errors();
22660
23216
  init_context();