mcp-use 1.34.6 → 1.34.8

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 (38) hide show
  1. package/dist/.tsbuildinfo +1 -1
  2. package/dist/{chunk-DDMBF2UU.js → chunk-4KPCPZWE.js} +1 -1
  3. package/dist/{chunk-NF3GBT4Z.js → chunk-5ZFGXHAU.js} +1 -1
  4. package/dist/{chunk-FRPS4EPQ.js → chunk-ENBB6Z2J.js} +2 -2
  5. package/dist/{chunk-XHHLR7B5.js → chunk-FXO3CJ7N.js} +2 -2
  6. package/dist/{chunk-3XAZE4TX.js → chunk-HIRI2UY6.js} +4 -4
  7. package/dist/{chunk-7R56RSZ6.js → chunk-I3P2KX5U.js} +1 -1
  8. package/dist/{chunk-V64O255P.js → chunk-MG47YFCC.js} +1 -1
  9. package/dist/{chunk-FOGBXW3W.js → chunk-NCK7SUJD.js} +1 -1
  10. package/dist/{chunk-J4VRW5ND.js → chunk-RDJKEIOW.js} +2 -2
  11. package/dist/{chunk-DI4WNZQC.js → chunk-SZYMINCU.js} +1 -1
  12. package/dist/{chunk-GLMTGH37.js → chunk-TYVQPHZW.js} +1 -1
  13. package/dist/{client-JFSDQX2A.js → client-YE5FSLWK.js} +4 -4
  14. package/dist/index.cjs +1 -1
  15. package/dist/index.js +6 -6
  16. package/dist/src/agents/index.cjs +1 -1
  17. package/dist/src/agents/index.js +4 -4
  18. package/dist/src/browser-agent.cjs +1 -1
  19. package/dist/src/browser-agent.js +2 -2
  20. package/dist/src/browser.cjs +1 -1
  21. package/dist/src/browser.js +4 -4
  22. package/dist/src/client.cjs +1 -1
  23. package/dist/src/client.js +4 -4
  24. package/dist/src/react/index.cjs +1 -1
  25. package/dist/src/react/index.js +4 -4
  26. package/dist/src/server/connect-adapter.d.ts.map +1 -1
  27. package/dist/src/server/endpoints/mount-mcp.d.ts.map +1 -1
  28. package/dist/src/server/index.cjs +540 -315
  29. package/dist/src/server/index.js +546 -321
  30. package/dist/src/server/logging.d.ts +2 -1
  31. package/dist/src/server/logging.d.ts.map +1 -1
  32. package/dist/src/server/sessions/streams/redis.d.ts +18 -0
  33. package/dist/src/server/sessions/streams/redis.d.ts.map +1 -1
  34. package/dist/src/version.d.ts +1 -1
  35. package/dist/{stdio-KQEZC3W4.js → stdio-5L34RAVM.js} +3 -3
  36. package/dist/{stdio-Q655HD2H.js → stdio-RCVXPUI4.js} +2 -2
  37. package/dist/{tool-execution-helpers-5OCWDSO5.js → tool-execution-helpers-IJTPZL55.js} +2 -2
  38. package/package.json +3 -3
@@ -17,7 +17,7 @@ import {
17
17
  findSessionContext,
18
18
  isValidLogLevel,
19
19
  supportsApps
20
- } from "../../chunk-DI4WNZQC.js";
20
+ } from "../../chunk-SZYMINCU.js";
21
21
  import {
22
22
  getRequestContext,
23
23
  hasRequestContext,
@@ -59,7 +59,7 @@ import {
59
59
  Telemetry,
60
60
  VERSION,
61
61
  getPackageVersion
62
- } from "../../chunk-FOGBXW3W.js";
62
+ } from "../../chunk-NCK7SUJD.js";
63
63
  import "../../chunk-BXZHMYMM.js";
64
64
  import {
65
65
  fsHelpers,
@@ -942,27 +942,19 @@ function isExpressMiddleware(middleware) {
942
942
  }
943
943
  const paramCount = middleware.length;
944
944
  const fnString = middleware.toString();
945
- const expressPatterns = [
946
- /\bres\.(send|json|status|end|redirect|render|sendFile|download)\b/,
947
- /\breq\.(body|params|query|cookies|session)\b/,
948
- /\breq\.get\s*\(/,
949
- /\bres\.set\s*\(/,
950
- /\bres\.statusCode\s*=/,
951
- /\bres\.writeHead\s*\(/
945
+ const expressResponsePatterns = [
946
+ /(?<![\w$.])res\.(send|json|status|end|redirect|render|sendFile|download)\b/,
947
+ /(?<![\w$.])res\.set\s*\(/,
948
+ /(?<![\w$.])res\.statusCode\s*=/,
949
+ /(?<![\w$.])res\.writeHead\s*\(/
952
950
  ];
953
- const hasExpressPattern = expressPatterns.some(
951
+ const hasExpressResponsePattern = expressResponsePatterns.some(
954
952
  (pattern) => pattern.test(fnString)
955
953
  );
956
954
  if (paramCount === 3 || paramCount === 4) {
957
- if (hasExpressPattern) {
958
- return true;
959
- }
960
955
  return true;
961
956
  }
962
957
  if (paramCount === 2) {
963
- if (hasExpressPattern) {
964
- return true;
965
- }
966
958
  const honoPatterns = [
967
959
  /\bc\.(req|res|json|text|html|status|header)\b/,
968
960
  /\bc\.get\s*\(/,
@@ -975,7 +967,7 @@ function isExpressMiddleware(middleware) {
975
967
  if (hasHonoPattern) {
976
968
  return false;
977
969
  }
978
- return false;
970
+ return hasExpressResponsePattern;
979
971
  }
980
972
  return false;
981
973
  }
@@ -1020,31 +1012,51 @@ async function adaptConnectMiddleware(connectMiddleware, middlewarePath) {
1020
1012
  ...request.body && { body: request.body }
1021
1013
  });
1022
1014
  const mockResponse = createResponse();
1023
- let responseResolved = false;
1024
- const res = await new Promise((resolve) => {
1015
+ const res = await new Promise((resolve, reject) => {
1016
+ let responseSettled = false;
1017
+ const resolveResponse = /* @__PURE__ */ __name((response) => {
1018
+ if (responseSettled) return;
1019
+ responseSettled = true;
1020
+ resolve(response);
1021
+ }, "resolveResponse");
1022
+ const rejectResponse = /* @__PURE__ */ __name((error2) => {
1023
+ if (responseSettled) return;
1024
+ responseSettled = true;
1025
+ reject(error2);
1026
+ }, "rejectResponse");
1025
1027
  const originalEnd = mockResponse.end.bind(mockResponse);
1026
1028
  mockResponse.end = (...args) => {
1027
- const result = originalEnd(...args);
1028
- if (!responseResolved && mockResponse.writableEnded) {
1029
- responseResolved = true;
1030
- const statusCode = mockResponse.statusCode;
1031
- const noBodyStatuses = [204, 304];
1032
- const responseBody = noBodyStatuses.includes(statusCode) ? null : mockResponse._getData() || mockResponse._getBuffer() || null;
1033
- const connectResponse = new Response(responseBody, {
1034
- status: statusCode,
1035
- statusText: mockResponse.statusMessage,
1036
- headers: mockResponse.getHeaders()
1037
- });
1038
- resolve(connectResponse);
1029
+ try {
1030
+ const result = originalEnd(...args);
1031
+ if (!responseSettled && mockResponse.writableEnded) {
1032
+ const statusCode = mockResponse.statusCode;
1033
+ const noBodyStatuses = [204, 304];
1034
+ const responseBody = noBodyStatuses.includes(statusCode) ? null : mockResponse._getData() || mockResponse._getBuffer() || null;
1035
+ const connectResponse = new Response(responseBody, {
1036
+ status: statusCode,
1037
+ statusText: mockResponse.statusMessage,
1038
+ headers: mockResponse.getHeaders()
1039
+ });
1040
+ resolveResponse(connectResponse);
1041
+ }
1042
+ return result;
1043
+ } catch (error2) {
1044
+ rejectResponse(error2);
1045
+ return mockResponse;
1039
1046
  }
1040
- return result;
1041
1047
  };
1042
- connectMiddleware(mockRequest, mockResponse, () => {
1043
- if (!responseResolved && !mockResponse.writableEnded) {
1044
- responseResolved = true;
1048
+ const connectNext = /* @__PURE__ */ __name((error2) => {
1049
+ if (responseSettled) return;
1050
+ if (error2) {
1051
+ rejectResponse(error2);
1052
+ return;
1053
+ }
1054
+ if (mockResponse.writableEnded) return;
1055
+ try {
1045
1056
  const statusCode = mockResponse.statusCode;
1046
1057
  const noBodyStatuses = [204, 304];
1047
- const responseBody = noBodyStatuses.includes(statusCode) ? null : mockResponse._getData() || mockResponse._getBuffer() || null;
1058
+ const buffer = mockResponse._getBuffer();
1059
+ const responseBody = noBodyStatuses.includes(statusCode) ? null : mockResponse._getData() || (buffer.length > 0 ? buffer : null);
1048
1060
  const preparedHeaders = c.newResponse(null, 204, {}).headers;
1049
1061
  for (const key of [...preparedHeaders.keys()]) {
1050
1062
  if (preparedHeaders.has(key)) {
@@ -1065,14 +1077,23 @@ async function adaptConnectMiddleware(connectMiddleware, middlewarePath) {
1065
1077
  }
1066
1078
  c.status(statusCode);
1067
1079
  if (noBodyStatuses.includes(statusCode)) {
1068
- resolve(c.newResponse(null, statusCode));
1080
+ resolveResponse(c.newResponse(null, statusCode));
1069
1081
  } else if (responseBody) {
1070
- resolve(c.body(responseBody));
1082
+ resolveResponse(c.body(responseBody));
1071
1083
  } else {
1072
- resolve(void 0);
1084
+ resolveResponse(void 0);
1073
1085
  }
1086
+ } catch (error3) {
1087
+ rejectResponse(error3);
1074
1088
  }
1075
- });
1089
+ }, "connectNext");
1090
+ try {
1091
+ Promise.resolve(
1092
+ connectMiddleware(mockRequest, mockResponse, connectNext)
1093
+ ).catch(rejectResponse);
1094
+ } catch (error2) {
1095
+ rejectResponse(error2);
1096
+ }
1076
1097
  });
1077
1098
  if (res) {
1078
1099
  c.res = res;
@@ -3332,7 +3353,7 @@ function registerPrompt(promptDefinition, callback) {
3332
3353
  }
3333
3354
  const wrappedCallback = /* @__PURE__ */ __name(async (params, extra) => {
3334
3355
  const { getRequestContext: getRequestContext2, runWithContext: runWithContext2 } = await import("../../context-storage-KMOAHEL7.js");
3335
- const { findSessionContext: findSessionContext2, createClientCapabilityChecker } = await import("../../tool-execution-helpers-5OCWDSO5.js");
3356
+ const { findSessionContext: findSessionContext2, createClientCapabilityChecker } = await import("../../tool-execution-helpers-IJTPZL55.js");
3336
3357
  const initialRequestContext = getRequestContext2();
3337
3358
  const sessions = this.sessions || /* @__PURE__ */ new Map();
3338
3359
  const { requestContext, session } = findSessionContext2(
@@ -3537,7 +3558,7 @@ function registerResource(resourceDefinition, callback) {
3537
3558
  const explicitMimeType = resourceDefinition.mimeType;
3538
3559
  const wrappedCallback = /* @__PURE__ */ __name(async () => {
3539
3560
  const { getRequestContext: getRequestContext2, runWithContext: runWithContext2 } = await import("../../context-storage-KMOAHEL7.js");
3540
- const { findSessionContext: findSessionContext2, createClientCapabilityChecker } = await import("../../tool-execution-helpers-5OCWDSO5.js");
3561
+ const { findSessionContext: findSessionContext2, createClientCapabilityChecker } = await import("../../tool-execution-helpers-IJTPZL55.js");
3541
3562
  const initialRequestContext = getRequestContext2();
3542
3563
  const sessions = this.sessions || /* @__PURE__ */ new Map();
3543
3564
  const { requestContext, session } = findSessionContext2(
@@ -3625,7 +3646,7 @@ function registerResourceTemplate(resourceTemplateDefinition, callback) {
3625
3646
  async (uri) => {
3626
3647
  const params = this.parseTemplateUri(uriTemplate, uri.toString());
3627
3648
  const { getRequestContext: getRequestContext2, runWithContext: runWithContext2 } = await import("../../context-storage-KMOAHEL7.js");
3628
- const { findSessionContext: findSessionContext2 } = await import("../../tool-execution-helpers-5OCWDSO5.js");
3649
+ const { findSessionContext: findSessionContext2 } = await import("../../tool-execution-helpers-IJTPZL55.js");
3629
3650
  const initialRequestContext = getRequestContext2();
3630
3651
  const sessions = this.sessions || /* @__PURE__ */ new Map();
3631
3652
  const { requestContext } = findSessionContext2(
@@ -5099,11 +5120,17 @@ var RedisStreamManager = class {
5099
5120
  * Key: sessionId, Value: controller
5100
5121
  */
5101
5122
  localControllers = /* @__PURE__ */ new Map();
5123
+ streamGenerations = /* @__PURE__ */ new Map();
5102
5124
  /**
5103
5125
  * Map of heartbeat intervals for keeping sessions alive
5104
5126
  * Key: sessionId, Value: interval timer
5105
5127
  */
5106
5128
  heartbeats = /* @__PURE__ */ new Map();
5129
+ // Track attempted subscriptions separately from controllers: a disconnected
5130
+ // controller or a rejected subscribe() must not lose its cleanup ownership.
5131
+ sessionSubscriptions = /* @__PURE__ */ new Map();
5132
+ sessionOperations = /* @__PURE__ */ new Map();
5133
+ closing = false;
5107
5134
  /**
5108
5135
  * Unique identifier for this server instance, used for request/response routing.
5109
5136
  */
@@ -5116,6 +5143,7 @@ var RedisStreamManager = class {
5116
5143
  * Whether the server-level response channel subscription is active.
5117
5144
  */
5118
5145
  serverChannelSubscribed = false;
5146
+ serverChannelSubscription;
5119
5147
  constructor(config) {
5120
5148
  this.pubSubClient = config.pubSubClient;
5121
5149
  this.client = config.client;
@@ -5140,90 +5168,265 @@ var RedisStreamManager = class {
5140
5168
  getActiveSessionsKey() {
5141
5169
  return `${this.prefix}active`;
5142
5170
  }
5143
- /**
5144
- * Register an active SSE stream and subscribe to Redis channel.
5145
- * Idempotent: if the session already has a subscription, it is cleaned up first.
5146
- */
5147
- async create(sessionId, controller) {
5171
+ withSessionOperation(sessionId, operation) {
5172
+ const previous = this.sessionOperations.get(sessionId) ?? Promise.resolve();
5173
+ const result = previous.then(operation);
5174
+ const settled = result.then(
5175
+ () => {
5176
+ },
5177
+ () => {
5178
+ }
5179
+ );
5180
+ this.sessionOperations.set(sessionId, settled);
5181
+ void settled.then(() => {
5182
+ if (this.sessionOperations.get(sessionId) === settled) {
5183
+ this.sessionOperations.delete(sessionId);
5184
+ }
5185
+ });
5186
+ return result;
5187
+ }
5188
+ clearLocalStream(sessionId) {
5189
+ const heartbeat = this.heartbeats.get(sessionId);
5190
+ if (heartbeat) clearInterval(heartbeat);
5191
+ this.heartbeats.delete(sessionId);
5192
+ const controller = this.localControllers.get(sessionId);
5193
+ this.localControllers.delete(sessionId);
5194
+ this.streamGenerations.delete(sessionId);
5195
+ this.closeController(controller);
5196
+ }
5197
+ closeController(controller) {
5148
5198
  try {
5149
- if (this.localControllers.has(sessionId)) {
5150
- const oldHeartbeat = this.heartbeats.get(sessionId);
5151
- if (oldHeartbeat) {
5152
- clearInterval(oldHeartbeat);
5153
- this.heartbeats.delete(sessionId);
5154
- }
5155
- const channel2 = this.getChannel(sessionId);
5156
- const deleteChannel2 = `delete:${channel2}`;
5157
- try {
5158
- await this.pubSubClient.unsubscribe?.(channel2);
5159
- await this.pubSubClient.unsubscribe?.(deleteChannel2);
5160
- } catch {
5161
- }
5199
+ controller?.close();
5200
+ } catch {
5201
+ }
5202
+ }
5203
+ async unsubscribeSession(sessionId) {
5204
+ const channels = this.sessionSubscriptions.get(sessionId);
5205
+ if (!channels?.size) return;
5206
+ if (!this.pubSubClient.unsubscribe) {
5207
+ throw new Error(
5208
+ "[RedisStreamManager] Redis client does not support unsubscribe method"
5209
+ );
5210
+ }
5211
+ const results = await Promise.allSettled(
5212
+ Array.from(channels, async (channel) => {
5213
+ await this.pubSubClient.unsubscribe(channel);
5214
+ channels.delete(channel);
5215
+ })
5216
+ );
5217
+ if (!channels.size) this.sessionSubscriptions.delete(sessionId);
5218
+ this.throwCleanupError(results);
5219
+ }
5220
+ throwCleanupError(results) {
5221
+ const failed = results.find((result) => result.status === "rejected");
5222
+ if (failed?.status === "rejected") throw failed.reason;
5223
+ }
5224
+ async markAvailable(sessionId) {
5225
+ const key = this.getAvailableKey(sessionId);
5226
+ const ttl = this.heartbeatInterval * 2;
5227
+ if (this.client.setEx) {
5228
+ await this.client.setEx(key, ttl, "active");
5229
+ } else if (this.client.setex) {
5230
+ await this.client.setex(key, ttl, "active");
5231
+ } else {
5232
+ await this.client.set(key, "active", { EX: ttl });
5233
+ if (this.client.expire) await this.client.expire(key, ttl);
5234
+ }
5235
+ if (this.client.sAdd) {
5236
+ await this.client.sAdd(this.getActiveSessionsKey(), sessionId);
5237
+ await this.client.expire?.(this.getActiveSessionsKey(), ttl);
5238
+ }
5239
+ }
5240
+ async removeAvailability(sessionId) {
5241
+ const results = await Promise.allSettled([
5242
+ Promise.resolve().then(
5243
+ () => this.client.del(this.getAvailableKey(sessionId))
5244
+ ),
5245
+ Promise.resolve().then(
5246
+ () => this.client.sRem?.(this.getActiveSessionsKey(), sessionId)
5247
+ )
5248
+ ]);
5249
+ this.throwCleanupError(results);
5250
+ }
5251
+ async removeStream(sessionId, notifyOtherServers, cleanupAvailability = true) {
5252
+ this.clearLocalStream(sessionId);
5253
+ const results = await Promise.allSettled([
5254
+ this.unsubscribeSession(sessionId),
5255
+ cleanupAvailability ? this.removeAvailability(sessionId) : Promise.resolve()
5256
+ ]);
5257
+ if (notifyOtherServers) {
5258
+ results.push(
5259
+ ...await Promise.allSettled([
5260
+ Promise.resolve().then(() => {
5261
+ if (!this.client.publish) {
5262
+ throw new Error(
5263
+ "[RedisStreamManager] Redis client does not support publish method"
5264
+ );
5265
+ }
5266
+ return this.client.publish(
5267
+ `delete:${this.getChannel(sessionId)}`,
5268
+ ""
5269
+ );
5270
+ })
5271
+ ])
5272
+ );
5273
+ }
5274
+ this.throwCleanupError(results);
5275
+ }
5276
+ // These factories have no enclosing controller or handler argument. Redis
5277
+ // may retain their callbacks after a failed unsubscribe, so the callbacks
5278
+ // must resolve controllers/handlers through the manager rather than capture
5279
+ // a registration operation's context.
5280
+ createMessageCallback(sessionId, generation) {
5281
+ return (message) => {
5282
+ if (this.streamGenerations.get(sessionId) !== generation) return;
5283
+ const controller = this.localControllers.get(sessionId);
5284
+ if (!controller) return;
5285
+ try {
5286
+ controller.enqueue(this.textEncoder.encode(message));
5287
+ } catch {
5288
+ this.clearLocalStream(sessionId);
5289
+ this.cleanupDisconnectedStream(sessionId);
5162
5290
  }
5163
- this.localControllers.set(sessionId, controller);
5164
- const availableKey = this.getAvailableKey(sessionId);
5165
- await this.client.set(availableKey, "active");
5166
- if (this.client.expire) {
5167
- await this.client.expire(availableKey, this.heartbeatInterval * 2);
5291
+ };
5292
+ }
5293
+ cleanupDisconnectedStream(sessionId) {
5294
+ void this.withSessionOperation(sessionId, async () => {
5295
+ if (!this.localControllers.has(sessionId)) {
5296
+ await this.unsubscribeSession(sessionId);
5168
5297
  }
5169
- const activeSessionsKey = this.getActiveSessionsKey();
5170
- if (this.client.sAdd) {
5171
- await this.client.sAdd(activeSessionsKey, sessionId);
5298
+ }).catch((error2) => {
5299
+ console.warn(
5300
+ `[RedisStreamManager] Failed to unsubscribe disconnected stream ${sessionId}:`,
5301
+ error2
5302
+ );
5303
+ });
5304
+ }
5305
+ createDeleteCallback(sessionId, generation) {
5306
+ return () => {
5307
+ if (this.streamGenerations.get(sessionId) !== generation) return;
5308
+ void this.withSessionOperation(
5309
+ sessionId,
5310
+ () => this.removeStream(sessionId, false)
5311
+ ).catch((error2) => {
5312
+ console.warn(
5313
+ `[RedisStreamManager] Failed to clean up deleted stream ${sessionId}:`,
5314
+ error2
5315
+ );
5316
+ });
5317
+ };
5318
+ }
5319
+ startHeartbeat(sessionId) {
5320
+ const availableKey = this.getAvailableKey(sessionId);
5321
+ const activeSessionsKey = this.getActiveSessionsKey();
5322
+ const heartbeat = setInterval(async () => {
5323
+ try {
5172
5324
  if (this.client.expire) {
5325
+ await this.client.expire(availableKey, this.heartbeatInterval * 2);
5173
5326
  await this.client.expire(
5174
5327
  activeSessionsKey,
5175
5328
  this.heartbeatInterval * 2
5176
5329
  );
5177
5330
  }
5331
+ } catch (error2) {
5332
+ console.warn(
5333
+ `[RedisStreamManager] Heartbeat failed for session ${sessionId}:`,
5334
+ error2
5335
+ );
5178
5336
  }
5179
- const heartbeat = setInterval(async () => {
5180
- try {
5181
- if (this.client.expire) {
5182
- await this.client.expire(availableKey, this.heartbeatInterval * 2);
5183
- const activeSessionsKey2 = this.getActiveSessionsKey();
5184
- await this.client.expire(
5185
- activeSessionsKey2,
5186
- this.heartbeatInterval * 2
5187
- );
5188
- }
5189
- } catch (error2) {
5190
- console.warn(
5191
- `[RedisStreamManager] Heartbeat failed for session ${sessionId}:`,
5192
- error2
5337
+ }, this.heartbeatInterval * 1e3);
5338
+ this.heartbeats.set(sessionId, heartbeat);
5339
+ }
5340
+ createForwardedResponseCallback() {
5341
+ return (raw) => {
5342
+ try {
5343
+ const { response, sessionId } = JSON.parse(raw);
5344
+ this.forwardedResponseHandler?.(response, sessionId);
5345
+ } catch (error2) {
5346
+ console.warn(
5347
+ `[RedisStreamManager] Failed to parse forwarded response:`,
5348
+ error2
5349
+ );
5350
+ }
5351
+ };
5352
+ }
5353
+ /**
5354
+ * Register an active SSE stream and subscribe to Redis channel.
5355
+ * Idempotent: if the session already has a subscription, it is cleaned up first.
5356
+ */
5357
+ async create(sessionId, controller) {
5358
+ return this.withSessionOperation(sessionId, async () => {
5359
+ if (this.closing) {
5360
+ this.closeController(controller);
5361
+ throw new Error("[RedisStreamManager] Stream manager is closed");
5362
+ }
5363
+ if (this.localControllers.get(sessionId) === controller) return;
5364
+ this.clearLocalStream(sessionId);
5365
+ let controllerRegistered = false;
5366
+ try {
5367
+ await this.unsubscribeSession(sessionId);
5368
+ if (this.closing) {
5369
+ throw new Error("[RedisStreamManager] Stream manager is closed");
5370
+ }
5371
+ this.localControllers.set(sessionId, controller);
5372
+ controllerRegistered = true;
5373
+ const generation = Symbol(sessionId);
5374
+ this.streamGenerations.set(sessionId, generation);
5375
+ if (!this.pubSubClient.subscribe) {
5376
+ throw new Error(
5377
+ "[RedisStreamManager] Redis client does not support subscribe method"
5193
5378
  );
5194
5379
  }
5195
- }, this.heartbeatInterval * 1e3);
5196
- this.heartbeats.set(sessionId, heartbeat);
5197
- const channel = this.getChannel(sessionId);
5198
- if (!this.pubSubClient.subscribe) {
5199
- throw new Error(
5200
- "[RedisStreamManager] Redis client does not support subscribe method"
5380
+ const channels = /* @__PURE__ */ new Set();
5381
+ this.sessionSubscriptions.set(sessionId, channels);
5382
+ const channel = this.getChannel(sessionId);
5383
+ channels.add(channel);
5384
+ await this.pubSubClient.subscribe(
5385
+ channel,
5386
+ this.createMessageCallback(sessionId, generation)
5201
5387
  );
5202
- }
5203
- await this.pubSubClient.subscribe(channel, (message) => {
5204
- const localController = this.localControllers.get(sessionId);
5205
- if (localController) {
5206
- try {
5207
- localController.enqueue(this.textEncoder.encode(message));
5208
- } catch {
5209
- this.localControllers.delete(sessionId);
5388
+ const deleteChannel = `delete:${channel}`;
5389
+ channels.add(deleteChannel);
5390
+ await this.pubSubClient.subscribe(
5391
+ deleteChannel,
5392
+ this.createDeleteCallback(sessionId, generation)
5393
+ );
5394
+ if (this.closing || this.streamGenerations.get(sessionId) !== generation) {
5395
+ throw new Error(
5396
+ "[RedisStreamManager] Stream closed during registration"
5397
+ );
5398
+ }
5399
+ await this.markAvailable(sessionId);
5400
+ if (this.closing || this.streamGenerations.get(sessionId) !== generation) {
5401
+ throw new Error(
5402
+ "[RedisStreamManager] Stream closed during registration"
5403
+ );
5404
+ }
5405
+ this.startHeartbeat(sessionId);
5406
+ console.log(
5407
+ `[RedisStreamManager] Created stream for session ${sessionId}`
5408
+ );
5409
+ } catch (error2) {
5410
+ this.clearLocalStream(sessionId);
5411
+ if (!controllerRegistered) this.closeController(controller);
5412
+ const cleanups = await Promise.allSettled([
5413
+ this.unsubscribeSession(sessionId)
5414
+ ]);
5415
+ for (const cleanup of cleanups) {
5416
+ if (cleanup.status === "rejected") {
5417
+ console.warn(
5418
+ `[RedisStreamManager] Failed to roll back stream ${sessionId}:`,
5419
+ cleanup.reason
5420
+ );
5210
5421
  }
5211
5422
  }
5212
- });
5213
- const deleteChannel = `delete:${this.getChannel(sessionId)}`;
5214
- await this.pubSubClient.subscribe(deleteChannel, async () => {
5215
- await this.delete(sessionId);
5216
- });
5217
- console.log(
5218
- `[RedisStreamManager] Created stream for session ${sessionId}`
5219
- );
5220
- } catch (error2) {
5221
- console.error(
5222
- `[RedisStreamManager] Error creating stream for ${sessionId}:`,
5223
- error2
5224
- );
5225
- throw error2;
5226
- }
5423
+ console.error(
5424
+ `[RedisStreamManager] Error creating stream for ${sessionId}:`,
5425
+ error2
5426
+ );
5427
+ throw error2;
5428
+ }
5429
+ });
5227
5430
  }
5228
5431
  /**
5229
5432
  * Send data to sessions via Redis Pub/Sub
@@ -5241,6 +5444,22 @@ var RedisStreamManager = class {
5241
5444
  if (this.client.sMembers) {
5242
5445
  const sessionIds2 = await this.client.sMembers(activeSessionsKey);
5243
5446
  for (const sessionId of sessionIds2) {
5447
+ const availableKey = this.getAvailableKey(sessionId);
5448
+ if (await this.client.exists(availableKey) !== 1) {
5449
+ await this.client.sRem?.(activeSessionsKey, sessionId);
5450
+ let available;
5451
+ try {
5452
+ available = await this.client.exists(availableKey);
5453
+ } catch (error2) {
5454
+ try {
5455
+ await this.client.sAdd?.(activeSessionsKey, sessionId);
5456
+ } catch {
5457
+ }
5458
+ throw error2;
5459
+ }
5460
+ if (available !== 1) continue;
5461
+ await this.client.sAdd?.(activeSessionsKey, sessionId);
5462
+ }
5244
5463
  const channel = this.getChannel(sessionId);
5245
5464
  if (!this.client.publish) {
5246
5465
  throw new Error(
@@ -5283,53 +5502,20 @@ var RedisStreamManager = class {
5283
5502
  * Remove an active SSE stream
5284
5503
  */
5285
5504
  async delete(sessionId) {
5286
- try {
5287
- const heartbeat = this.heartbeats.get(sessionId);
5288
- if (heartbeat) {
5289
- clearInterval(heartbeat);
5290
- this.heartbeats.delete(sessionId);
5291
- }
5292
- const channel = this.getChannel(sessionId);
5293
- const deleteChannel = `delete:${channel}`;
5294
- if (!this.pubSubClient.unsubscribe) {
5295
- throw new Error(
5296
- "[RedisStreamManager] Redis client does not support unsubscribe method"
5505
+ return this.withSessionOperation(sessionId, async () => {
5506
+ try {
5507
+ await this.removeStream(sessionId, true);
5508
+ console.log(
5509
+ `[RedisStreamManager] Deleted stream for session ${sessionId}`
5297
5510
  );
5298
- }
5299
- await this.pubSubClient.unsubscribe(channel);
5300
- await this.pubSubClient.unsubscribe(deleteChannel);
5301
- if (!this.client.publish) {
5302
- throw new Error(
5303
- "[RedisStreamManager] Redis client does not support publish method"
5511
+ } catch (error2) {
5512
+ console.error(
5513
+ `[RedisStreamManager] Error deleting stream for ${sessionId}:`,
5514
+ error2
5304
5515
  );
5516
+ throw error2;
5305
5517
  }
5306
- await this.client.publish(deleteChannel, "");
5307
- await this.client.del(this.getAvailableKey(sessionId));
5308
- const activeSessionsKey = this.getActiveSessionsKey();
5309
- if (this.client.sRem) {
5310
- await this.client.sRem(activeSessionsKey, sessionId);
5311
- }
5312
- const controller = this.localControllers.get(sessionId);
5313
- if (controller) {
5314
- try {
5315
- controller.close();
5316
- } catch (error2) {
5317
- console.debug(
5318
- `[RedisStreamManager] Controller already closed for ${sessionId}`
5319
- );
5320
- }
5321
- this.localControllers.delete(sessionId);
5322
- }
5323
- console.log(
5324
- `[RedisStreamManager] Deleted stream for session ${sessionId}`
5325
- );
5326
- } catch (error2) {
5327
- console.error(
5328
- `[RedisStreamManager] Error deleting stream for ${sessionId}:`,
5329
- error2
5330
- );
5331
- throw error2;
5332
- }
5518
+ });
5333
5519
  }
5334
5520
  /**
5335
5521
  * Check if a session has an active stream (on ANY server)
@@ -5351,31 +5537,42 @@ var RedisStreamManager = class {
5351
5537
  * Close all connections and cleanup
5352
5538
  */
5353
5539
  async close() {
5354
- try {
5355
- for (const heartbeat of this.heartbeats.values()) {
5356
- clearInterval(heartbeat);
5357
- }
5358
- this.heartbeats.clear();
5359
- const activeSessionsKey = this.getActiveSessionsKey();
5360
- const sessionIdsToCleanup = Array.from(this.localControllers.keys());
5361
- for (const sessionId of sessionIdsToCleanup) {
5362
- await this.client.del(this.getAvailableKey(sessionId));
5363
- if (this.client.sRem) {
5364
- await this.client.sRem(activeSessionsKey, sessionId);
5365
- }
5366
- }
5367
- for (const controller of this.localControllers.values()) {
5368
- try {
5369
- controller.close();
5370
- } catch (error2) {
5540
+ this.closing = true;
5541
+ this.forwardedResponseHandler = void 0;
5542
+ const sessionIds = /* @__PURE__ */ new Set([
5543
+ ...this.localControllers.keys(),
5544
+ ...this.heartbeats.keys(),
5545
+ ...this.sessionSubscriptions.keys(),
5546
+ ...this.sessionOperations.keys()
5547
+ ]);
5548
+ for (const sessionId of sessionIds) this.clearLocalStream(sessionId);
5549
+ const results = await Promise.allSettled([
5550
+ ...Array.from(
5551
+ sessionIds,
5552
+ (sessionId) => this.withSessionOperation(
5553
+ sessionId,
5554
+ () => (
5555
+ // Shared availability expires after shutdown; deleting it here
5556
+ // could invalidate another manager serving the same session.
5557
+ this.removeStream(sessionId, false, false)
5558
+ )
5559
+ )
5560
+ ),
5561
+ Promise.resolve().then(async () => {
5562
+ await this.serverChannelSubscription;
5563
+ if (this.serverChannelSubscribed) {
5564
+ if (!this.pubSubClient.unsubscribe) {
5565
+ throw new Error(
5566
+ "[RedisStreamManager] Redis client does not support unsubscribe method"
5567
+ );
5568
+ }
5569
+ await this.pubSubClient.unsubscribe(this.getServerChannel());
5570
+ this.serverChannelSubscribed = false;
5371
5571
  }
5372
- }
5373
- this.localControllers.clear();
5374
- console.log(`[RedisStreamManager] Closed all streams`);
5375
- } catch (error2) {
5376
- console.error(`[RedisStreamManager] Error during close:`, error2);
5377
- throw error2;
5378
- }
5572
+ })
5573
+ ]);
5574
+ this.throwCleanupError(results);
5575
+ console.log(`[RedisStreamManager] Closed all streams`);
5379
5576
  }
5380
5577
  /**
5381
5578
  * Get count of active local streams on this server instance
@@ -5440,9 +5637,9 @@ var RedisStreamManager = class {
5440
5637
  * Subscribes to this server's dedicated Pub/Sub channel.
5441
5638
  */
5442
5639
  onForwardedResponse(handler) {
5640
+ if (this.closing) return;
5443
5641
  this.forwardedResponseHandler = handler;
5444
5642
  if (this.serverChannelSubscribed) return;
5445
- this.serverChannelSubscribed = true;
5446
5643
  const serverChannel = this.getServerChannel();
5447
5644
  if (!this.pubSubClient.subscribe) {
5448
5645
  console.warn(
@@ -5450,17 +5647,21 @@ var RedisStreamManager = class {
5450
5647
  );
5451
5648
  return;
5452
5649
  }
5453
- this.pubSubClient.subscribe(serverChannel, (raw) => {
5650
+ this.serverChannelSubscribed = true;
5651
+ this.serverChannelSubscription = Promise.resolve().then(
5652
+ () => this.pubSubClient.subscribe(
5653
+ serverChannel,
5654
+ this.createForwardedResponseCallback()
5655
+ )
5656
+ ).then(() => {
5657
+ }).catch(async (error2) => {
5454
5658
  try {
5455
- const { response, sessionId } = JSON.parse(raw);
5456
- this.forwardedResponseHandler?.(response, sessionId);
5457
- } catch (error2) {
5458
- console.warn(
5459
- `[RedisStreamManager] Failed to parse forwarded response:`,
5460
- error2
5461
- );
5659
+ if (this.pubSubClient.unsubscribe) {
5660
+ await this.pubSubClient.unsubscribe(serverChannel);
5661
+ this.serverChannelSubscribed = false;
5662
+ }
5663
+ } catch {
5462
5664
  }
5463
- }).catch((error2) => {
5464
5665
  console.error(
5465
5666
  `[RedisStreamManager] Failed to subscribe to server channel:`,
5466
5667
  error2
@@ -5528,37 +5729,21 @@ async function extractResponseError(res) {
5528
5729
  return null;
5529
5730
  }
5530
5731
  if (!text2) return null;
5531
- const isSse = (res.headers.get("content-type") || "").includes(
5532
- "text/event-stream"
5533
- );
5534
- const payloads = [];
5535
- const tryParse = /* @__PURE__ */ __name((raw) => {
5536
- try {
5537
- payloads.push(JSON.parse(raw));
5538
- } catch {
5539
- }
5540
- }, "tryParse");
5541
- if (isSse) {
5542
- for (const line of text2.split(/\r?\n/)) {
5543
- if (line.startsWith("data:")) {
5544
- const data = line.slice(5).trim();
5545
- if (data) tryParse(data);
5546
- }
5547
- }
5548
- } else {
5549
- tryParse(text2);
5732
+ let payload;
5733
+ try {
5734
+ payload = JSON.parse(text2);
5735
+ } catch {
5736
+ return null;
5550
5737
  }
5551
- for (const payload of payloads) {
5552
- for (const msg of Array.isArray(payload) ? payload : [payload]) {
5553
- if (!msg || typeof msg !== "object") continue;
5554
- const m = msg;
5555
- if (typeof m.error?.message === "string") return m.error.message;
5556
- if (m.result?.isError === true) {
5557
- const textBlock = Array.isArray(m.result.content) ? m.result.content.find(
5558
- (b) => b?.type === "text" && typeof b.text === "string"
5559
- ) : null;
5560
- return textBlock ? String(textBlock.text) : "tool error";
5561
- }
5738
+ for (const msg of Array.isArray(payload) ? payload : [payload]) {
5739
+ if (!msg || typeof msg !== "object") continue;
5740
+ const m = msg;
5741
+ if (typeof m.error?.message === "string") return m.error.message;
5742
+ if (m.result?.isError === true) {
5743
+ const textBlock = Array.isArray(m.result.content) ? m.result.content.find(
5744
+ (b) => b?.type === "text" && typeof b.text === "string"
5745
+ ) : null;
5746
+ return textBlock ? String(textBlock.text) : "tool error";
5562
5747
  }
5563
5748
  }
5564
5749
  return null;
@@ -5602,6 +5787,7 @@ async function requestLogger(c, next) {
5602
5787
  await next();
5603
5788
  const durationMs = Date.now() - startedAt;
5604
5789
  const statusCode = c.res.status;
5790
+ const isStreaming = c.res.headers.get("content-type")?.split(";", 1)[0]?.trim().toLowerCase() === "text/event-stream";
5605
5791
  const incomingSid = c.req.header("mcp-session-id");
5606
5792
  const responseSid = c.res.headers.get("mcp-session-id");
5607
5793
  const mcpMethod = requestBody?.method;
@@ -5640,7 +5826,7 @@ async function requestLogger(c, next) {
5640
5826
  parts.push(chalk2.gray(`\u2192 session=${shortSessionId(responseSid)}`));
5641
5827
  }
5642
5828
  let outcomePart;
5643
- const errMsg = await extractResponseError(c.res);
5829
+ const errMsg = isStreaming ? null : await extractResponseError(c.res);
5644
5830
  if (errMsg) {
5645
5831
  outcomePart = chalk2.red(`ERROR ${errMsg}`);
5646
5832
  } else if (mcpMethod) {
@@ -5676,7 +5862,9 @@ async function requestLogger(c, next) {
5676
5862
  console.log(formatForLogging(responseHeaders));
5677
5863
  }
5678
5864
  try {
5679
- if (c.res.body !== null && c.res.body !== void 0) {
5865
+ if (isStreaming) {
5866
+ console.log(chalk2.yellow("Response Body:") + " (streaming; skipped)");
5867
+ } else if (c.res.body !== null && c.res.body !== void 0) {
5680
5868
  try {
5681
5869
  const clonedResponse = c.res.clone();
5682
5870
  const responseBody = await clonedResponse.text().catch(() => null);
@@ -5789,6 +5977,29 @@ async function mountMcp(app, mcpServerInstance, sessions, config, isProductionMo
5789
5977
  }));
5790
5978
  const streamManager = config.streamManager ?? new InMemoryStreamManager();
5791
5979
  const transports = /* @__PURE__ */ new Map();
5980
+ const cleanupClosedSession = /* @__PURE__ */ __name(async (sid) => {
5981
+ if (getDebugLevel() !== "info") {
5982
+ console.log(`[MCP] Session closed: ${sid}`);
5983
+ }
5984
+ transports.delete(sid);
5985
+ sessions.delete(sid);
5986
+ const cleanups = await Promise.allSettled([
5987
+ Promise.resolve().then(
5988
+ () => mcpServerInstance.cleanupSessionSubscriptions?.(sid)
5989
+ ),
5990
+ Promise.resolve().then(() => mcpServerInstance.cleanupSessionRefs?.(sid)),
5991
+ Promise.resolve().then(() => streamManager.delete(sid)),
5992
+ Promise.resolve().then(() => sessionStore.delete(sid))
5993
+ ]);
5994
+ for (const cleanup of cleanups) {
5995
+ if (cleanup.status === "rejected") {
5996
+ console.warn(
5997
+ `[MCP] Error cleaning up closed session ${sid}:`,
5998
+ cleanup.reason
5999
+ );
6000
+ }
6001
+ }
6002
+ }, "cleanupClosedSession");
5792
6003
  if (streamManager.onForwardedResponse) {
5793
6004
  streamManager.onForwardedResponse((message, sid) => {
5794
6005
  const transport = transports.get(sid);
@@ -5862,7 +6073,9 @@ async function mountMcp(app, mcpServerInstance, sessions, config, isProductionMo
5862
6073
  }
5863
6074
  const acceptHeader = c.req.header("Accept") || c.req.header("accept") || "";
5864
6075
  const clientSupportsSSE = acceptHeader.includes("text/event-stream");
5865
- const useStatelessMode = config.stateless || !clientSupportsSSE;
6076
+ const sessionId = c.req.header("mcp-session-id");
6077
+ const isSessionDelete = c.req.method === "DELETE" && !!sessionId;
6078
+ const useStatelessMode = config.stateless || !clientSupportsSSE && !isSessionDelete;
5866
6079
  if (useStatelessMode) {
5867
6080
  if (c.req.method === "HEAD") {
5868
6081
  return new Response(null, { status: 200 });
@@ -5905,7 +6118,6 @@ async function mountMcp(app, mcpServerInstance, sessions, config, isProductionMo
5905
6118
  throw error2;
5906
6119
  }
5907
6120
  } else {
5908
- const sessionId = c.req.header("mcp-session-id");
5909
6121
  if (c.req.method === "HEAD") {
5910
6122
  if (sessionId && await sessionStore.has(sessionId)) {
5911
6123
  const session = await sessionStore.get(sessionId);
@@ -5924,35 +6136,25 @@ async function mountMcp(app, mcpServerInstance, sessions, config, isProductionMo
5924
6136
  console.log(
5925
6137
  `[MCP] Session metadata found but transport lost (restart/hot-reload): ${sessionId} - recovering`
5926
6138
  );
5927
- const server2 = mcpServerInstance.getServerForSession(sessionId);
5928
- const transport2 = new WebStandardStreamableHTTPServerTransport({
6139
+ const server = mcpServerInstance.getServerForSession(sessionId);
6140
+ const transport = new WebStandardStreamableHTTPServerTransport({
5929
6141
  sessionIdGenerator: /* @__PURE__ */ __name(() => sessionId, "sessionIdGenerator"),
5930
6142
  onsessioninitialized: /* @__PURE__ */ __name(async (sid) => {
5931
6143
  if (getDebugLevel() !== "info") {
5932
6144
  console.log(`[MCP] Session re-initialized: ${sid}`);
5933
6145
  }
5934
6146
  }, "onsessioninitialized"),
5935
- onsessionclosed: /* @__PURE__ */ __name(async (sid) => {
5936
- if (getDebugLevel() !== "info") {
5937
- console.log(`[MCP] Session closed: ${sid}`);
5938
- }
5939
- transports.delete(sid);
5940
- await streamManager.delete(sid);
5941
- await sessionStore.delete(sid);
5942
- sessions.delete(sid);
5943
- mcpServerInstance.cleanupSessionSubscriptions?.(sid);
5944
- mcpServerInstance.cleanupSessionRefs?.(sid);
5945
- }, "onsessionclosed")
6147
+ onsessionclosed: cleanupClosedSession
5946
6148
  });
5947
- wrapTransportForStreamManager(transport2, sessionId, streamManager);
5948
- await server2.connect(transport2);
5949
- transport2._initialized = true;
5950
- transport2.sessionId = sessionId;
5951
- transports.set(sessionId, transport2);
6149
+ wrapTransportForStreamManager(transport, sessionId, streamManager);
6150
+ await server.connect(transport);
6151
+ transport._initialized = true;
6152
+ transport.sessionId = sessionId;
6153
+ transports.set(sessionId, transport);
5952
6154
  const metadata = await sessionStore.get(sessionId);
5953
6155
  const sessionData = {
5954
- transport: transport2,
5955
- server: server2,
6156
+ transport,
6157
+ server,
5956
6158
  lastAccessedAt: Date.now(),
5957
6159
  context: c,
5958
6160
  honoContext: c,
@@ -5964,11 +6166,11 @@ async function mountMcp(app, mcpServerInstance, sessions, config, isProductionMo
5964
6166
  }
5965
6167
  const recoveryResponse = await runWithContext(
5966
6168
  c,
5967
- async () => transport2.handleRequest(c.req.raw),
6169
+ async () => transport.handleRequest(c.req.raw),
5968
6170
  sessionId
5969
6171
  );
5970
6172
  if (c.req.method === "GET") {
5971
- await registerSseStream(transport2, sessionId, streamManager);
6173
+ await registerSseStream(transport, sessionId, streamManager);
5972
6174
  }
5973
6175
  return recoveryResponse;
5974
6176
  }
@@ -5986,7 +6188,7 @@ async function mountMcp(app, mcpServerInstance, sessions, config, isProductionMo
5986
6188
  );
5987
6189
  }
5988
6190
  if (sessionId && transports.has(sessionId)) {
5989
- const transport2 = transports.get(sessionId);
6191
+ const transport = transports.get(sessionId);
5990
6192
  const metadata = await sessionStore.get(sessionId);
5991
6193
  if (metadata) {
5992
6194
  metadata.lastAccessedAt = Date.now();
@@ -6003,89 +6205,112 @@ async function mountMcp(app, mcpServerInstance, sessions, config, isProductionMo
6003
6205
  }
6004
6206
  const existingResponse = await runWithContext(
6005
6207
  c,
6006
- async () => transport2.handleRequest(c.req.raw),
6208
+ async () => transport.handleRequest(c.req.raw),
6007
6209
  sessionId
6008
6210
  );
6009
6211
  if (c.req.method === "GET") {
6010
- await registerSseStream(transport2, sessionId, streamManager);
6212
+ await registerSseStream(transport, sessionId, streamManager);
6011
6213
  }
6012
6214
  return existingResponse;
6013
6215
  }
6014
6216
  const newSessionId = generateUUID();
6015
- const server = mcpServerInstance.getServerForSession(newSessionId);
6016
- const transport = new WebStandardStreamableHTTPServerTransport({
6017
- sessionIdGenerator: /* @__PURE__ */ __name(() => newSessionId, "sessionIdGenerator"),
6018
- onsessioninitialized: /* @__PURE__ */ __name(async (sid) => {
6019
- if (getDebugLevel() !== "info") {
6020
- console.log(`[MCP] Session initialized: ${sid}`);
6021
- }
6022
- transports.set(sid, transport);
6023
- const sessionData = {
6024
- transport,
6025
- server,
6026
- lastAccessedAt: Date.now(),
6027
- context: c,
6028
- honoContext: c
6029
- };
6030
- sessions.set(sid, sessionData);
6031
- await sessionStore.set(sid, {
6032
- lastAccessedAt: Date.now()
6033
- });
6034
- server.server.oninitialized = async () => {
6035
- const clientCapabilities = server.server.getClientCapabilities();
6036
- const clientInfo = server.server.getClientVersion?.() || server.server.getClientInfo?.() || {};
6037
- const protocolVersion = server.server.getProtocolVersion?.() || "unknown";
6038
- const metadata = await sessionStore.get(sid);
6039
- if (metadata) {
6040
- metadata.clientCapabilities = clientCapabilities;
6041
- metadata.clientInfo = clientInfo;
6042
- metadata.protocolVersion = String(protocolVersion);
6043
- await sessionStore.set(sid, metadata);
6044
- if (getDebugLevel() !== "info") {
6045
- console.log(
6046
- `[MCP] Captured client capabilities for session ${sid}:`,
6047
- clientCapabilities ? Object.keys(clientCapabilities) : "none"
6048
- );
6049
- }
6217
+ let allocatedServer;
6218
+ let allocatedTransport;
6219
+ let sessionEstablished = false;
6220
+ try {
6221
+ const server = mcpServerInstance.getServerForSession(newSessionId);
6222
+ allocatedServer = server;
6223
+ const transport = new WebStandardStreamableHTTPServerTransport({
6224
+ sessionIdGenerator: /* @__PURE__ */ __name(() => newSessionId, "sessionIdGenerator"),
6225
+ onsessioninitialized: /* @__PURE__ */ __name(async (sid) => {
6226
+ if (getDebugLevel() !== "info") {
6227
+ console.log(`[MCP] Session initialized: ${sid}`);
6050
6228
  }
6051
- const sessionData2 = sessions.get(sid);
6052
- if (sessionData2) {
6053
- sessionData2.clientCapabilities = clientCapabilities;
6054
- sessionData2.clientInfo = clientInfo;
6229
+ transports.set(sid, transport);
6230
+ const sessionData = {
6231
+ transport,
6232
+ server,
6233
+ lastAccessedAt: Date.now(),
6234
+ context: c,
6235
+ honoContext: c
6236
+ };
6237
+ sessions.set(sid, sessionData);
6238
+ await sessionStore.set(sid, {
6239
+ lastAccessedAt: Date.now()
6240
+ });
6241
+ server.server.oninitialized = async () => {
6242
+ const clientCapabilities = server.server.getClientCapabilities();
6243
+ const clientInfo = server.server.getClientVersion?.() || server.server.getClientInfo?.() || {};
6244
+ const protocolVersion = server.server.getProtocolVersion?.() || "unknown";
6245
+ const metadata = await sessionStore.get(sid);
6246
+ if (metadata) {
6247
+ metadata.clientCapabilities = clientCapabilities;
6248
+ metadata.clientInfo = clientInfo;
6249
+ metadata.protocolVersion = String(protocolVersion);
6250
+ await sessionStore.set(sid, metadata);
6251
+ if (getDebugLevel() !== "info") {
6252
+ console.log(
6253
+ `[MCP] Captured client capabilities for session ${sid}:`,
6254
+ clientCapabilities ? Object.keys(clientCapabilities) : "none"
6255
+ );
6256
+ }
6257
+ }
6258
+ const sessionData2 = sessions.get(sid);
6259
+ if (sessionData2) {
6260
+ sessionData2.clientCapabilities = clientCapabilities;
6261
+ sessionData2.clientInfo = clientInfo;
6262
+ }
6263
+ Telemetry.getInstance().trackServerInitialize({
6264
+ protocolVersion: String(protocolVersion),
6265
+ clientInfo: clientInfo || {},
6266
+ clientCapabilities: clientCapabilities || {},
6267
+ sessionId: sid
6268
+ }).catch(
6269
+ (e) => console.debug(`Failed to track server initialize: ${e}`)
6270
+ );
6271
+ };
6272
+ }, "onsessioninitialized"),
6273
+ onsessionclosed: cleanupClosedSession
6274
+ });
6275
+ allocatedTransport = transport;
6276
+ wrapTransportForStreamManager(transport, newSessionId, streamManager);
6277
+ await server.connect(transport);
6278
+ const newSessionResponse = await runWithContext(
6279
+ c,
6280
+ async () => transport.handleRequest(c.req.raw),
6281
+ newSessionId
6282
+ );
6283
+ if (c.req.method === "GET") {
6284
+ await registerSseStream(transport, newSessionId, streamManager);
6285
+ }
6286
+ sessionEstablished = newSessionResponse.ok && sessions.has(newSessionId) && !c.req.raw.signal.aborted;
6287
+ return newSessionResponse;
6288
+ } finally {
6289
+ if (!sessionEstablished) {
6290
+ transports.delete(newSessionId);
6291
+ sessions.delete(newSessionId);
6292
+ const cleanups = await Promise.allSettled([
6293
+ Promise.resolve().then(
6294
+ () => mcpServerInstance.cleanupSessionSubscriptions?.(newSessionId)
6295
+ ),
6296
+ Promise.resolve().then(
6297
+ () => mcpServerInstance.cleanupSessionRefs?.(newSessionId)
6298
+ ),
6299
+ Promise.resolve().then(() => streamManager.delete(newSessionId)),
6300
+ Promise.resolve().then(() => sessionStore.delete(newSessionId)),
6301
+ Promise.resolve().then(() => allocatedServer?.close()),
6302
+ Promise.resolve().then(() => allocatedTransport?.close())
6303
+ ]);
6304
+ for (const cleanup of cleanups) {
6305
+ if (cleanup.status === "rejected") {
6306
+ console.warn(
6307
+ `[MCP] Error cleaning up unsuccessful session ${newSessionId}:`,
6308
+ cleanup.reason
6309
+ );
6055
6310
  }
6056
- Telemetry.getInstance().trackServerInitialize({
6057
- protocolVersion: String(protocolVersion),
6058
- clientInfo: clientInfo || {},
6059
- clientCapabilities: clientCapabilities || {},
6060
- sessionId: sid
6061
- }).catch(
6062
- (e) => console.debug(`Failed to track server initialize: ${e}`)
6063
- );
6064
- };
6065
- }, "onsessioninitialized"),
6066
- onsessionclosed: /* @__PURE__ */ __name(async (sid) => {
6067
- if (getDebugLevel() !== "info") {
6068
- console.log(`[MCP] Session closed: ${sid}`);
6069
6311
  }
6070
- transports.delete(sid);
6071
- await streamManager.delete(sid);
6072
- await sessionStore.delete(sid);
6073
- sessions.delete(sid);
6074
- mcpServerInstance.cleanupSessionSubscriptions?.(sid);
6075
- mcpServerInstance.cleanupSessionRefs?.(sid);
6076
- }, "onsessionclosed")
6077
- });
6078
- wrapTransportForStreamManager(transport, newSessionId, streamManager);
6079
- await server.connect(transport);
6080
- const newSessionResponse = await runWithContext(
6081
- c,
6082
- async () => transport.handleRequest(c.req.raw),
6083
- newSessionId
6084
- );
6085
- if (c.req.method === "GET") {
6086
- await registerSseStream(transport, newSessionId, streamManager);
6312
+ }
6087
6313
  }
6088
- return newSessionResponse;
6089
6314
  }
6090
6315
  }, "handleRequest");
6091
6316
  for (const endpoint of ["/mcp", "/sse"]) {
@@ -6987,7 +7212,7 @@ var MCPServerClass = class _MCPServerClass {
6987
7212
  onSampling: /* @__PURE__ */ __name(async (params) => {
6988
7213
  try {
6989
7214
  const { getRequestContext: getRequestContext2 } = await import("../../context-storage-KMOAHEL7.js");
6990
- const { findSessionContext: findSessionContext2 } = await import("../../tool-execution-helpers-5OCWDSO5.js");
7215
+ const { findSessionContext: findSessionContext2 } = await import("../../tool-execution-helpers-IJTPZL55.js");
6991
7216
  const ctx = getRequestContext2();
6992
7217
  if (!ctx) throw new Error("No request context");
6993
7218
  const { session } = findSessionContext2(
@@ -7015,7 +7240,7 @@ var MCPServerClass = class _MCPServerClass {
7015
7240
  onElicitation: /* @__PURE__ */ __name(async (params) => {
7016
7241
  try {
7017
7242
  const { getRequestContext: getRequestContext2 } = await import("../../context-storage-KMOAHEL7.js");
7018
- const { findSessionContext: findSessionContext2 } = await import("../../tool-execution-helpers-5OCWDSO5.js");
7243
+ const { findSessionContext: findSessionContext2 } = await import("../../tool-execution-helpers-IJTPZL55.js");
7019
7244
  const ctx = getRequestContext2();
7020
7245
  if (!ctx) throw new Error("No request context");
7021
7246
  const { session } = findSessionContext2(