mcp-use 1.10.5 → 1.11.0-canary.10

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 (135) hide show
  1. package/README.md +1 -1
  2. package/dist/.tsbuildinfo +1 -1
  3. package/dist/{chunk-IY632ZQS.js → chunk-3GB7G5X7.js} +2 -2
  4. package/dist/{chunk-E42PSMPK.js → chunk-5QAXQTDL.js} +1 -1
  5. package/dist/{chunk-C7WHRUWQ.js → chunk-BEGEDH6P.js} +196 -496
  6. package/dist/{chunk-MO5FM5B2.js → chunk-BLGIG2QD.js} +443 -969
  7. package/dist/{chunk-XG7SR6G4.js → chunk-CBJTHTR4.js} +3 -8
  8. package/dist/{chunk-34R6SIER.js → chunk-FRUZDWXH.js} +1 -1
  9. package/dist/chunk-GUB5GQDD.js +101 -0
  10. package/dist/chunk-GXNAXUDI.js +0 -0
  11. package/dist/{chunk-CPG2WZUL.js → chunk-JRGQRPTN.js} +1 -1
  12. package/dist/chunk-MFSO5PUW.js +1049 -0
  13. package/dist/{chunk-UD5FSFEZ.js → chunk-NXFHUS7A.js} +171 -11
  14. package/dist/chunk-ULFNVP5Z.js +12 -0
  15. package/dist/chunk-UWWLWLS2.js +62 -0
  16. package/dist/chunk-VTEYN43V.js +1055 -0
  17. package/dist/{chunk-YOHGE3NK.js → chunk-XPTKLSBC.js} +16 -2
  18. package/dist/index.cjs +5065 -4608
  19. package/dist/index.d.ts +2 -3
  20. package/dist/index.d.ts.map +1 -1
  21. package/dist/index.js +53 -1068
  22. package/dist/{langfuse-N5Y5BSXK.js → langfuse-74RGPTAH.js} +2 -2
  23. package/dist/notifications-FLGIFS56.js +9 -0
  24. package/dist/src/adapters/base.d.ts +44 -0
  25. package/dist/src/adapters/base.d.ts.map +1 -1
  26. package/dist/src/adapters/index.cjs +1346 -0
  27. package/dist/src/adapters/index.js +11 -0
  28. package/dist/src/adapters/langchain_adapter.d.ts +12 -1
  29. package/dist/src/adapters/langchain_adapter.d.ts.map +1 -1
  30. package/dist/src/agents/index.cjs +3141 -159
  31. package/dist/src/agents/index.d.ts +2 -0
  32. package/dist/src/agents/index.d.ts.map +1 -1
  33. package/dist/src/agents/index.js +10 -6
  34. package/dist/src/agents/mcp_agent.d.ts +59 -37
  35. package/dist/src/agents/mcp_agent.d.ts.map +1 -1
  36. package/dist/src/agents/remote.d.ts +25 -0
  37. package/dist/src/agents/remote.d.ts.map +1 -1
  38. package/dist/src/agents/types.d.ts +76 -0
  39. package/dist/src/agents/types.d.ts.map +1 -1
  40. package/dist/src/agents/utils/index.d.ts +1 -0
  41. package/dist/src/agents/utils/index.d.ts.map +1 -1
  42. package/dist/src/agents/utils/llm_provider.d.ts +53 -0
  43. package/dist/src/agents/utils/llm_provider.d.ts.map +1 -0
  44. package/dist/src/browser.cjs +1856 -423
  45. package/dist/src/browser.d.ts +1 -2
  46. package/dist/src/browser.d.ts.map +1 -1
  47. package/dist/src/browser.js +26 -15
  48. package/dist/src/client/base.d.ts +1 -0
  49. package/dist/src/client/base.d.ts.map +1 -1
  50. package/dist/src/client/browser.d.ts +2 -2
  51. package/dist/src/client/browser.d.ts.map +1 -1
  52. package/dist/src/client/prompts.cjs +1 -1
  53. package/dist/src/client/prompts.js +5 -4
  54. package/dist/src/client.cjs +3787 -0
  55. package/dist/src/client.js +20 -0
  56. package/dist/src/config.d.ts.map +1 -1
  57. package/dist/src/connectors/base.d.ts +8 -0
  58. package/dist/src/connectors/base.d.ts.map +1 -1
  59. package/dist/src/connectors/index.d.ts +0 -1
  60. package/dist/src/connectors/index.d.ts.map +1 -1
  61. package/dist/src/managers/server_manager.d.ts.map +1 -1
  62. package/dist/src/managers/tools/connect_mcp_server.d.ts.map +1 -1
  63. package/dist/src/react/index.cjs +259 -298
  64. package/dist/src/react/index.js +6 -5
  65. package/dist/src/react/types.d.ts +42 -4
  66. package/dist/src/react/types.d.ts.map +1 -1
  67. package/dist/src/react/useMcp.d.ts.map +1 -1
  68. package/dist/src/react/useWidget.d.ts +11 -7
  69. package/dist/src/react/useWidget.d.ts.map +1 -1
  70. package/dist/src/react/widget-types.d.ts +6 -2
  71. package/dist/src/react/widget-types.d.ts.map +1 -1
  72. package/dist/src/server/endpoints/mount-mcp.d.ts.map +1 -1
  73. package/dist/src/server/index.cjs +1379 -208
  74. package/dist/src/server/index.d.ts +2 -0
  75. package/dist/src/server/index.d.ts.map +1 -1
  76. package/dist/src/server/index.js +1353 -249
  77. package/dist/src/server/mcp-server.d.ts +5 -1
  78. package/dist/src/server/mcp-server.d.ts.map +1 -1
  79. package/dist/src/server/notifications/index.d.ts +1 -1
  80. package/dist/src/server/notifications/index.d.ts.map +1 -1
  81. package/dist/src/server/notifications/notification-registration.d.ts +51 -0
  82. package/dist/src/server/notifications/notification-registration.d.ts.map +1 -1
  83. package/dist/src/server/sessions/index.d.ts +3 -1
  84. package/dist/src/server/sessions/index.d.ts.map +1 -1
  85. package/dist/src/server/sessions/session-manager.d.ts +36 -19
  86. package/dist/src/server/sessions/session-manager.d.ts.map +1 -1
  87. package/dist/src/server/sessions/stores/filesystem.d.ts +121 -0
  88. package/dist/src/server/sessions/stores/filesystem.d.ts.map +1 -0
  89. package/dist/src/server/sessions/stores/index.d.ts +94 -0
  90. package/dist/src/server/sessions/stores/index.d.ts.map +1 -0
  91. package/dist/src/server/sessions/stores/memory.d.ts +82 -0
  92. package/dist/src/server/sessions/stores/memory.d.ts.map +1 -0
  93. package/dist/src/server/sessions/stores/redis.d.ts +164 -0
  94. package/dist/src/server/sessions/stores/redis.d.ts.map +1 -0
  95. package/dist/src/server/sessions/streams/index.d.ts +77 -0
  96. package/dist/src/server/sessions/streams/index.d.ts.map +1 -0
  97. package/dist/src/server/sessions/streams/memory.d.ts +76 -0
  98. package/dist/src/server/sessions/streams/memory.d.ts.map +1 -0
  99. package/dist/src/server/sessions/streams/redis.d.ts +146 -0
  100. package/dist/src/server/sessions/streams/redis.d.ts.map +1 -0
  101. package/dist/src/server/types/common.d.ts +120 -17
  102. package/dist/src/server/types/common.d.ts.map +1 -1
  103. package/dist/src/server/types/resource.d.ts +16 -0
  104. package/dist/src/server/types/resource.d.ts.map +1 -1
  105. package/dist/src/server/types/widget.d.ts +21 -2
  106. package/dist/src/server/types/widget.d.ts.map +1 -1
  107. package/dist/src/server/utils/response-helpers.d.ts +12 -6
  108. package/dist/src/server/utils/response-helpers.d.ts.map +1 -1
  109. package/dist/src/server/widgets/index.d.ts +1 -1
  110. package/dist/src/server/widgets/index.d.ts.map +1 -1
  111. package/dist/src/server/widgets/mount-widgets-dev.d.ts.map +1 -1
  112. package/dist/src/server/widgets/setup-widget-routes.d.ts.map +1 -1
  113. package/dist/src/server/widgets/ui-resource-registration.d.ts.map +1 -1
  114. package/dist/src/server/widgets/widget-helpers.d.ts +22 -0
  115. package/dist/src/server/widgets/widget-helpers.d.ts.map +1 -1
  116. package/dist/src/server/widgets/widget-types.d.ts +2 -0
  117. package/dist/src/server/widgets/widget-types.d.ts.map +1 -1
  118. package/dist/src/session.d.ts +16 -2
  119. package/dist/src/session.d.ts.map +1 -1
  120. package/dist/src/task_managers/index.d.ts +10 -1
  121. package/dist/src/task_managers/index.d.ts.map +1 -1
  122. package/dist/src/task_managers/sse.d.ts +34 -1
  123. package/dist/src/task_managers/sse.d.ts.map +1 -1
  124. package/dist/src/task_managers/streamable_http.d.ts +8 -2
  125. package/dist/src/task_managers/streamable_http.d.ts.map +1 -1
  126. package/dist/src/version.d.ts +1 -1
  127. package/dist/src/version.d.ts.map +1 -1
  128. package/dist/{tool-execution-helpers-XTDKRH6N.js → tool-execution-helpers-N7R7YGAW.js} +3 -3
  129. package/dist/tsup.config.d.ts.map +1 -1
  130. package/package.json +47 -14
  131. package/dist/src/connectors/websocket.d.ts +0 -38
  132. package/dist/src/connectors/websocket.d.ts.map +0 -1
  133. package/dist/src/task_managers/websocket.d.ts +0 -18
  134. package/dist/src/task_managers/websocket.d.ts.map +0 -1
  135. /package/dist/{chunk-EW4MJSHA.js → chunk-LGDFGYRL.js} +0 -0
@@ -413,8 +413,8 @@ var init_runtime = __esm({
413
413
  if (isDeno) {
414
414
  return await globalThis.Deno.readTextFile(path);
415
415
  }
416
- const { readFileSync } = await import("fs");
417
- const result = readFileSync(path, encoding);
416
+ const { readFileSync: readFileSync2 } = await import("fs");
417
+ const result = readFileSync2(path, encoding);
418
418
  return typeof result === "string" ? result : result.toString(encoding);
419
419
  },
420
420
  async readFile(path) {
@@ -422,8 +422,8 @@ var init_runtime = __esm({
422
422
  const data = await globalThis.Deno.readFile(path);
423
423
  return data.buffer;
424
424
  }
425
- const { readFileSync } = await import("fs");
426
- const buffer = readFileSync(path);
425
+ const { readFileSync: readFileSync2 } = await import("fs");
426
+ const buffer = readFileSync2(path);
427
427
  return buffer.buffer.slice(
428
428
  buffer.byteOffset,
429
429
  buffer.byteOffset + buffer.byteLength
@@ -438,8 +438,8 @@ var init_runtime = __esm({
438
438
  return false;
439
439
  }
440
440
  }
441
- const { existsSync } = await import("fs");
442
- return existsSync(path);
441
+ const { existsSync: existsSync2 } = await import("fs");
442
+ return existsSync2(path);
443
443
  },
444
444
  async readdirSync(path) {
445
445
  if (isDeno) {
@@ -643,7 +643,7 @@ var init_logging = __esm({
643
643
  timestamp({ format: "HH:mm:ss" }),
644
644
  this.getFormatter()
645
645
  ),
646
- transports: []
646
+ transports: [new winston.transports.Console()]
647
647
  });
648
648
  }
649
649
  return this.instances[name];
@@ -761,7 +761,7 @@ var VERSION;
761
761
  var init_version = __esm({
762
762
  "src/version.ts"() {
763
763
  "use strict";
764
- VERSION = "1.10.5";
764
+ VERSION = "1.11.0-canary.10";
765
765
  __name(getPackageVersion, "getPackageVersion");
766
766
  }
767
767
  });
@@ -2098,10 +2098,84 @@ var init_conversion2 = __esm({
2098
2098
  }
2099
2099
  });
2100
2100
 
2101
+ // src/server/utils/jsonrpc-helpers.ts
2102
+ function createNotification(method, params) {
2103
+ return {
2104
+ jsonrpc: "2.0",
2105
+ method,
2106
+ ...params && { params }
2107
+ };
2108
+ }
2109
+ function createRequest(id, method, params) {
2110
+ return {
2111
+ jsonrpc: "2.0",
2112
+ id,
2113
+ method,
2114
+ ...params && { params }
2115
+ };
2116
+ }
2117
+ var init_jsonrpc_helpers = __esm({
2118
+ "src/server/utils/jsonrpc-helpers.ts"() {
2119
+ "use strict";
2120
+ __name(createNotification, "createNotification");
2121
+ __name(createRequest, "createRequest");
2122
+ }
2123
+ });
2124
+
2125
+ // src/server/sessions/notifications.ts
2126
+ var notifications_exports = {};
2127
+ __export(notifications_exports, {
2128
+ sendNotificationToAll: () => sendNotificationToAll,
2129
+ sendNotificationToSession: () => sendNotificationToSession
2130
+ });
2131
+ async function sendNotificationToAll(sessions, method, params) {
2132
+ const notification = createNotification(method, params);
2133
+ for (const [sessionId, session] of sessions.entries()) {
2134
+ try {
2135
+ await session.transport.send(notification);
2136
+ } catch (error2) {
2137
+ console.warn(
2138
+ `[MCP] Failed to send notification to session ${sessionId}:`,
2139
+ error2
2140
+ );
2141
+ }
2142
+ }
2143
+ }
2144
+ async function sendNotificationToSession(sessions, sessionId, method, params) {
2145
+ const session = sessions.get(sessionId);
2146
+ if (!session) {
2147
+ return false;
2148
+ }
2149
+ const notification = createNotification(method, params);
2150
+ try {
2151
+ await session.transport.send(notification);
2152
+ return true;
2153
+ } catch (error2) {
2154
+ console.warn(
2155
+ `[MCP] Failed to send notification to session ${sessionId}:`,
2156
+ error2
2157
+ );
2158
+ return false;
2159
+ }
2160
+ }
2161
+ var init_notifications = __esm({
2162
+ "src/server/sessions/notifications.ts"() {
2163
+ "use strict";
2164
+ init_jsonrpc_helpers();
2165
+ __name(sendNotificationToAll, "sendNotificationToAll");
2166
+ __name(sendNotificationToSession, "sendNotificationToSession");
2167
+ }
2168
+ });
2169
+
2101
2170
  // src/server/index.ts
2102
2171
  var server_exports = {};
2103
2172
  __export(server_exports, {
2173
+ FileSystemSessionStore: () => FileSystemSessionStore,
2174
+ InMemorySessionStore: () => InMemorySessionStore,
2175
+ InMemoryStreamManager: () => InMemoryStreamManager,
2104
2176
  MCPServer: () => MCPServer,
2177
+ RedisSessionStore: () => RedisSessionStore,
2178
+ RedisStreamManager: () => RedisStreamManager,
2105
2179
  VERSION: () => VERSION,
2106
2180
  adaptConnectMiddleware: () => adaptConnectMiddleware,
2107
2181
  adaptMiddleware: () => adaptMiddleware,
@@ -2427,17 +2501,23 @@ function binary(base64Data, mimeType) {
2427
2501
  }
2428
2502
  __name(binary, "binary");
2429
2503
  function widget(config) {
2430
- const { data, message } = config;
2431
- return {
2432
- content: [
2433
- {
2434
- type: "text",
2435
- text: message || ""
2436
- }
2437
- ],
2438
- // structuredContent will be injected as window.openai.toolOutput by Apps SDK
2439
- structuredContent: data
2504
+ const props = config.props || config.data || {};
2505
+ const { output, message } = config;
2506
+ const finalContent = message ? [{ type: "text", text: message }] : Array.isArray(output?.content) && output.content.length > 0 ? output.content : [{ type: "text", text: "" }];
2507
+ const meta = {
2508
+ ...output?._meta || {},
2509
+ "mcp-use/props": props
2440
2510
  };
2511
+ const result = {
2512
+ content: finalContent,
2513
+ _meta: meta
2514
+ };
2515
+ if (output?.structuredContent) {
2516
+ result.structuredContent = output.structuredContent;
2517
+ } else if (Object.keys(props).length > 0) {
2518
+ result.structuredContent = props;
2519
+ }
2520
+ return result;
2441
2521
  }
2442
2522
  __name(widget, "widget");
2443
2523
  function mix(...results) {
@@ -3107,7 +3187,7 @@ function processWidgetHtml(html2, widgetName, baseUrl) {
3107
3187
  }
3108
3188
  __name(processWidgetHtml, "processWidgetHtml");
3109
3189
  function createWidgetRegistration(widgetName, metadata, html2, serverConfig, isDev = false) {
3110
- const props = metadata.inputs || {};
3190
+ const props = metadata.props || metadata.inputs || metadata.schema || {};
3111
3191
  const description = metadata.description || `Widget: ${widgetName}`;
3112
3192
  const title = metadata.title || widgetName;
3113
3193
  const exposeAsTool = metadata.exposeAsTool !== void 0 ? metadata.exposeAsTool : true;
@@ -3249,6 +3329,33 @@ function setupPublicRoutes(app, useDistDirectory = false) {
3249
3329
  });
3250
3330
  }
3251
3331
  __name(setupPublicRoutes, "setupPublicRoutes");
3332
+ function setupFaviconRoute(app, faviconPath, useDistDirectory = false) {
3333
+ if (!faviconPath) {
3334
+ return;
3335
+ }
3336
+ app.get("/favicon.ico", async (c) => {
3337
+ const basePath = useDistDirectory ? "dist/public" : "public";
3338
+ const fullPath = pathHelpers.join(getCwd(), basePath, faviconPath);
3339
+ try {
3340
+ if (await fsHelpers.existsSync(fullPath)) {
3341
+ const content = await fsHelpers.readFile(fullPath);
3342
+ const contentType = getContentType(faviconPath);
3343
+ return new Response(content, {
3344
+ status: 200,
3345
+ headers: {
3346
+ "Content-Type": contentType,
3347
+ "Cache-Control": "public, max-age=31536000"
3348
+ // Cache for 1 year
3349
+ }
3350
+ });
3351
+ }
3352
+ return c.notFound();
3353
+ } catch {
3354
+ return c.notFound();
3355
+ }
3356
+ });
3357
+ }
3358
+ __name(setupFaviconRoute, "setupFaviconRoute");
3252
3359
 
3253
3360
  // src/server/widgets/mount-widgets-dev.ts
3254
3361
  var TMP_MCP_USE_DIR = ".mcp-use";
@@ -3298,6 +3405,19 @@ async function mountWidgetsDev(app, serverConfig, registerWidget, options) {
3298
3405
  return;
3299
3406
  }
3300
3407
  const tempDir = pathHelpers.join(getCwd(), TMP_MCP_USE_DIR);
3408
+ try {
3409
+ await fs.access(tempDir);
3410
+ const currentWidgetNames = new Set(entries.map((e) => e.name));
3411
+ const existingDirs = await fs.readdir(tempDir, { withFileTypes: true });
3412
+ for (const dirent of existingDirs) {
3413
+ if (dirent.isDirectory() && !currentWidgetNames.has(dirent.name)) {
3414
+ const staleDir = pathHelpers.join(tempDir, dirent.name);
3415
+ await fs.rm(staleDir, { recursive: true, force: true });
3416
+ console.log(`[WIDGETS] Cleaned up stale widget: ${dirent.name}`);
3417
+ }
3418
+ }
3419
+ } catch {
3420
+ }
3301
3421
  await fs.mkdir(tempDir, { recursive: true }).catch(() => {
3302
3422
  });
3303
3423
  let createServer;
@@ -3335,10 +3455,13 @@ async function mountWidgetsDev(app, serverConfig, registerWidget, options) {
3335
3455
  await fs.mkdir(widgetTempDir, { recursive: true });
3336
3456
  const resourcesPath = pathHelpers.join(getCwd(), resourcesDir);
3337
3457
  const relativeResourcesPath = pathHelpers.relative(widgetTempDir, resourcesPath).replace(/\\/g, "/");
3458
+ const mcpUsePath = pathHelpers.join(getCwd(), "node_modules", "mcp-use");
3459
+ const relativeMcpUsePath = pathHelpers.relative(widgetTempDir, mcpUsePath).replace(/\\/g, "/");
3338
3460
  const cssContent = `@import "tailwindcss";
3339
3461
 
3340
- /* Configure Tailwind to scan the resources directory */
3462
+ /* Configure Tailwind to scan the resources directory and mcp-use package */
3341
3463
  @source "${relativeResourcesPath}";
3464
+ @source "${relativeMcpUsePath}/**/*.{ts,tsx,js,jsx}";
3342
3465
  `;
3343
3466
  await fs.writeFile(
3344
3467
  pathHelpers.join(widgetTempDir, "styles.css"),
@@ -3361,7 +3484,8 @@ if (container && Component) {
3361
3484
  <head>
3362
3485
  <meta charset="UTF-8" />
3363
3486
  <meta name="viewport" content="width=device-width,initial-scale=1" />
3364
- <title>${widget2.name} Widget</title>
3487
+ <title>${widget2.name} Widget</title>${serverConfig.favicon ? `
3488
+ <link rel="icon" href="/mcp-use/public/${serverConfig.favicon}" />` : ""}
3365
3489
  </head>
3366
3490
  <body>
3367
3491
  <div id="widget-root"></div>
@@ -3405,6 +3529,50 @@ if (container && Component) {
3405
3529
  const resourcesPath = pathHelpers.join(getCwd(), resourcesDir);
3406
3530
  server.watcher.add(resourcesPath);
3407
3531
  console.log(`[WIDGETS] Watching resources directory: ${resourcesPath}`);
3532
+ server.watcher.on("unlink", async (filePath) => {
3533
+ const relativePath = pathHelpers.relative(resourcesPath, filePath);
3534
+ if ((relativePath.endsWith(".tsx") || relativePath.endsWith(".ts")) && !relativePath.includes("/")) {
3535
+ const widgetName = relativePath.replace(/\.tsx?$/, "");
3536
+ const widgetDir = pathHelpers.join(tempDir, widgetName);
3537
+ try {
3538
+ await fs.access(widgetDir);
3539
+ await fs.rm(widgetDir, { recursive: true, force: true });
3540
+ console.log(
3541
+ `[WIDGETS] Cleaned up stale widget (file removed): ${widgetName}`
3542
+ );
3543
+ } catch {
3544
+ }
3545
+ } else if (relativePath.endsWith("widget.tsx")) {
3546
+ const parts = relativePath.split("/");
3547
+ if (parts.length === 2) {
3548
+ const widgetName = parts[0];
3549
+ const widgetDir = pathHelpers.join(tempDir, widgetName);
3550
+ try {
3551
+ await fs.access(widgetDir);
3552
+ await fs.rm(widgetDir, { recursive: true, force: true });
3553
+ console.log(
3554
+ `[WIDGETS] Cleaned up stale widget (file removed): ${widgetName}`
3555
+ );
3556
+ } catch {
3557
+ }
3558
+ }
3559
+ }
3560
+ });
3561
+ server.watcher.on("unlinkDir", async (dirPath) => {
3562
+ const relativePath = pathHelpers.relative(resourcesPath, dirPath);
3563
+ if (relativePath && !relativePath.includes("/")) {
3564
+ const widgetName = relativePath;
3565
+ const widgetDir = pathHelpers.join(tempDir, widgetName);
3566
+ try {
3567
+ await fs.access(widgetDir);
3568
+ await fs.rm(widgetDir, { recursive: true, force: true });
3569
+ console.log(
3570
+ `[WIDGETS] Cleaned up stale widget (directory removed): ${widgetName}`
3571
+ );
3572
+ } catch {
3573
+ }
3574
+ }
3575
+ });
3408
3576
  }
3409
3577
  };
3410
3578
  const nodeStubsPlugin = {
@@ -3517,6 +3685,7 @@ export default PostHog;
3517
3685
  );
3518
3686
  app.use(`${baseRoute}/*`, viteMiddleware);
3519
3687
  setupPublicRoutes(app, false);
3688
+ setupFaviconRoute(app, serverConfig.favicon, false);
3520
3689
  app.use(`${baseRoute}/*`, async (c) => {
3521
3690
  const url = new URL(c.req.url);
3522
3691
  const isAsset = url.pathname.match(
@@ -3726,6 +3895,7 @@ function setupWidgetRoutes(app, serverConfig) {
3726
3895
  }
3727
3896
  });
3728
3897
  setupPublicRoutes(app, true);
3898
+ setupFaviconRoute(app, serverConfig.favicon, true);
3729
3899
  }
3730
3900
  __name(setupWidgetRoutes, "setupWidgetRoutes");
3731
3901
 
@@ -3860,18 +4030,30 @@ function uiResourceRegistration(server, definition) {
3860
4030
  );
3861
4031
  const uniqueToolMetadata = {
3862
4032
  ...toolMetadata,
3863
- "openai/outputTemplate": uniqueUri
4033
+ "openai/outputTemplate": uniqueUri,
4034
+ "mcp-use/props": params
4035
+ // Pass params as widget props
3864
4036
  };
4037
+ let toolOutputResult;
4038
+ if (definition.toolOutput) {
4039
+ toolOutputResult = typeof definition.toolOutput === "function" ? definition.toolOutput(params) : definition.toolOutput;
4040
+ } else {
4041
+ toolOutputResult = {
4042
+ content: [
4043
+ {
4044
+ type: "text",
4045
+ text: `Displaying ${displayName}`
4046
+ }
4047
+ ]
4048
+ };
4049
+ }
4050
+ const content = toolOutputResult.content || [
4051
+ { type: "text", text: `Displaying ${displayName}` }
4052
+ ];
3865
4053
  return {
3866
4054
  _meta: uniqueToolMetadata,
3867
- content: [
3868
- {
3869
- type: "text",
3870
- text: `Displaying ${displayName}`
3871
- }
3872
- ],
3873
- // structuredContent will be injected as window.openai.toolOutput by Apps SDK
3874
- structuredContent: params
4055
+ content,
4056
+ structuredContent: toolOutputResult.structuredContent
3875
4057
  };
3876
4058
  }
3877
4059
  return {
@@ -3897,7 +4079,8 @@ async function mountWidgets(server, options) {
3897
4079
  serverBaseUrl: server.serverBaseUrl || `http://${server.serverHost}:${server.serverPort || 3e3}`,
3898
4080
  serverPort: server.serverPort || 3e3,
3899
4081
  cspUrls: getCSPUrls(),
3900
- buildId: server.buildId
4082
+ buildId: server.buildId,
4083
+ favicon: server.favicon
3901
4084
  };
3902
4085
  const registerWidget = /* @__PURE__ */ __name((widgetDef) => {
3903
4086
  server.uiResource(widgetDef);
@@ -4392,27 +4575,7 @@ __name(registerPrompt, "registerPrompt");
4392
4575
 
4393
4576
  // src/server/roots/roots-registration.ts
4394
4577
  init_runtime();
4395
-
4396
- // src/server/utils/jsonrpc-helpers.ts
4397
- function createNotification(method, params) {
4398
- return {
4399
- jsonrpc: "2.0",
4400
- method,
4401
- ...params && { params }
4402
- };
4403
- }
4404
- __name(createNotification, "createNotification");
4405
- function createRequest(id, method, params) {
4406
- return {
4407
- jsonrpc: "2.0",
4408
- id,
4409
- method,
4410
- ...params && { params }
4411
- };
4412
- }
4413
- __name(createRequest, "createRequest");
4414
-
4415
- // src/server/roots/roots-registration.ts
4578
+ init_jsonrpc_helpers();
4416
4579
  function onRootsChanged(callback) {
4417
4580
  this.onRootsChangedCallback = callback;
4418
4581
  return this;
@@ -4574,41 +4737,8 @@ async function requestLogger(c, next) {
4574
4737
  }
4575
4738
  __name(requestLogger, "requestLogger");
4576
4739
 
4577
- // src/server/sessions/notifications.ts
4578
- async function sendNotificationToAll(sessions, method, params) {
4579
- const notification = createNotification(method, params);
4580
- for (const [sessionId, session] of sessions.entries()) {
4581
- try {
4582
- await session.transport.send(notification);
4583
- } catch (error2) {
4584
- console.warn(
4585
- `[MCP] Failed to send notification to session ${sessionId}:`,
4586
- error2
4587
- );
4588
- }
4589
- }
4590
- }
4591
- __name(sendNotificationToAll, "sendNotificationToAll");
4592
- async function sendNotificationToSession(sessions, sessionId, method, params) {
4593
- const session = sessions.get(sessionId);
4594
- if (!session) {
4595
- return false;
4596
- }
4597
- const notification = createNotification(method, params);
4598
- try {
4599
- await session.transport.send(notification);
4600
- return true;
4601
- } catch (error2) {
4602
- console.warn(
4603
- `[MCP] Failed to send notification to session ${sessionId}:`,
4604
- error2
4605
- );
4606
- return false;
4607
- }
4608
- }
4609
- __name(sendNotificationToSession, "sendNotificationToSession");
4610
-
4611
4740
  // src/server/notifications/notification-registration.ts
4741
+ init_notifications();
4612
4742
  function getActiveSessions() {
4613
4743
  return Array.from(this.sessions.keys());
4614
4744
  }
@@ -4626,13 +4756,34 @@ async function sendNotificationToSession2(sessionId, method, params) {
4626
4756
  );
4627
4757
  }
4628
4758
  __name(sendNotificationToSession2, "sendNotificationToSession");
4759
+ async function sendToolsListChanged() {
4760
+ await sendNotificationToAll(
4761
+ this.sessions,
4762
+ "notifications/tools/list_changed"
4763
+ );
4764
+ }
4765
+ __name(sendToolsListChanged, "sendToolsListChanged");
4766
+ async function sendResourcesListChanged() {
4767
+ await sendNotificationToAll(
4768
+ this.sessions,
4769
+ "notifications/resources/list_changed"
4770
+ );
4771
+ }
4772
+ __name(sendResourcesListChanged, "sendResourcesListChanged");
4773
+ async function sendPromptsListChanged() {
4774
+ await sendNotificationToAll(
4775
+ this.sessions,
4776
+ "notifications/prompts/list_changed"
4777
+ );
4778
+ }
4779
+ __name(sendPromptsListChanged, "sendPromptsListChanged");
4629
4780
 
4630
4781
  // src/server/mcp-server.ts
4631
4782
  init_tool_execution_helpers();
4632
4783
  init_context_storage();
4633
4784
 
4634
4785
  // src/server/sessions/session-manager.ts
4635
- function startIdleCleanup(sessions, idleTimeoutMs, mcpServerInstance) {
4786
+ function startIdleCleanup(sessions, idleTimeoutMs, transports, mcpServerInstance) {
4636
4787
  if (idleTimeoutMs <= 0) {
4637
4788
  return void 0;
4638
4789
  }
@@ -4649,136 +4800,1141 @@ function startIdleCleanup(sessions, idleTimeoutMs, mcpServerInstance) {
4649
4800
  `[MCP] Cleaning up ${expiredSessions.length} idle session(s)`
4650
4801
  );
4651
4802
  for (const sessionId of expiredSessions) {
4803
+ const transport = transports?.get(sessionId);
4804
+ if (transport?.close) {
4805
+ Promise.resolve(transport.close()).catch((e) => {
4806
+ console.warn(
4807
+ `[MCP] Error closing transport for session ${sessionId}:`,
4808
+ e
4809
+ );
4810
+ });
4811
+ }
4812
+ transports?.delete(sessionId);
4652
4813
  sessions.delete(sessionId);
4653
4814
  mcpServerInstance?.cleanupSessionSubscriptions?.(sessionId);
4815
+ console.log(
4816
+ `[MCP] Cleaned up resource subscriptions for session ${sessionId}`
4817
+ );
4654
4818
  }
4655
4819
  }
4656
4820
  }, 6e4);
4657
4821
  }
4658
4822
  __name(startIdleCleanup, "startIdleCleanup");
4659
4823
 
4660
- // src/server/endpoints/mount-mcp.ts
4661
- init_runtime();
4662
- init_telemetry2();
4663
- async function mountMcp(app, mcpServerInstance, sessions, config, isProductionMode2) {
4664
- const { FetchStreamableHTTPServerTransport } = await import("@mcp-use/modelcontextprotocol-sdk/experimental/fetch-streamable-http/index.js");
4665
- const idleTimeoutMs = config.sessionIdleTimeoutMs ?? 3e5;
4666
- const transports = /* @__PURE__ */ new Map();
4667
- let idleCleanupInterval;
4668
- if (idleTimeoutMs > 0) {
4669
- idleCleanupInterval = startIdleCleanup(
4670
- sessions,
4671
- idleTimeoutMs,
4672
- mcpServerInstance
4673
- );
4824
+ // src/server/sessions/index.ts
4825
+ init_notifications();
4826
+
4827
+ // src/server/sessions/stores/memory.ts
4828
+ var InMemorySessionStore = class {
4829
+ static {
4830
+ __name(this, "InMemorySessionStore");
4674
4831
  }
4675
- const handleRequest = /* @__PURE__ */ __name(async (c) => {
4676
- const sessionId = c.req.header("mcp-session-id");
4677
- if (sessionId && transports.has(sessionId)) {
4678
- const transport2 = transports.get(sessionId);
4679
- if (sessions.has(sessionId)) {
4680
- const session = sessions.get(sessionId);
4681
- session.lastAccessedAt = Date.now();
4682
- session.context = c;
4683
- session.honoContext = c;
4684
- }
4685
- return transport2.handleRequest(c.req.raw);
4686
- }
4687
- const server = mcpServerInstance.getServerForSession();
4688
- const transport = new FetchStreamableHTTPServerTransport({
4689
- sessionIdGenerator: /* @__PURE__ */ __name(() => generateUUID(), "sessionIdGenerator"),
4690
- onsessioninitialized: /* @__PURE__ */ __name((sid) => {
4691
- console.log(`[MCP] Session initialized: ${sid}`);
4692
- transports.set(sid, transport);
4693
- sessions.set(sid, {
4694
- transport,
4695
- server,
4696
- lastAccessedAt: Date.now(),
4697
- context: c,
4698
- honoContext: c
4699
- });
4700
- server.server.oninitialized = () => {
4701
- const clientCapabilities = server.server.getClientCapabilities();
4702
- const clientInfo = server.server.getClientInfo?.() || {};
4703
- const protocolVersion = server.server.getProtocolVersion?.() || "unknown";
4704
- if (clientCapabilities && sessions.has(sid)) {
4705
- const session = sessions.get(sid);
4706
- session.clientCapabilities = clientCapabilities;
4707
- console.log(
4708
- `[MCP] Captured client capabilities for session ${sid}:`,
4709
- Object.keys(clientCapabilities)
4710
- );
4711
- }
4712
- Telemetry.getInstance().trackServerInitialize({
4713
- protocolVersion: String(protocolVersion),
4714
- clientInfo: clientInfo || {},
4715
- clientCapabilities: clientCapabilities || {},
4716
- sessionId: sid
4717
- }).catch(
4718
- (e) => console.debug(`Failed to track server initialize: ${e}`)
4719
- );
4720
- };
4721
- }, "onsessioninitialized"),
4722
- onsessionclosed: /* @__PURE__ */ __name((sid) => {
4723
- console.log(`[MCP] Session closed: ${sid}`);
4724
- transports.delete(sid);
4725
- sessions.delete(sid);
4726
- mcpServerInstance.cleanupSessionSubscriptions?.(sid);
4727
- }, "onsessionclosed")
4728
- });
4729
- await server.connect(transport);
4730
- return transport.handleRequest(c.req.raw);
4731
- }, "handleRequest");
4732
- for (const endpoint of ["/mcp", "/sse"]) {
4733
- app.on(["GET", "POST", "DELETE"], endpoint, handleRequest);
4832
+ /**
4833
+ * Internal map storing session metadata
4834
+ * Key: sessionId, Value: SessionMetadata
4835
+ */
4836
+ sessions = /* @__PURE__ */ new Map();
4837
+ /**
4838
+ * Retrieve session metadata by ID
4839
+ */
4840
+ async get(sessionId) {
4841
+ const data = this.sessions.get(sessionId);
4842
+ return data ?? null;
4734
4843
  }
4735
- console.log(
4736
- `[MCP] Server mounted at /mcp and /sse (using FetchStreamableHTTPServerTransport - Web Standard APIs)`
4737
- );
4738
- return { mcpMounted: true, idleCleanupInterval };
4739
- }
4740
- __name(mountMcp, "mountMcp");
4741
-
4742
- // src/server/oauth/routes.ts
4743
- var import_cors2 = require("hono/cors");
4744
- function setupOAuthRoutes(app, provider, baseUrl) {
4745
- const mode = provider.getMode?.() || "proxy";
4746
- app.use(
4747
- "/.well-known/*",
4748
- (0, import_cors2.cors)({
4749
- origin: "*",
4750
- // Allow all origins for metadata discovery
4751
- allowMethods: ["GET", "OPTIONS"],
4752
- allowHeaders: ["Content-Type", "Authorization"],
4753
- exposeHeaders: ["Content-Type"],
4754
- maxAge: 86400
4755
- // Cache preflight for 24 hours
4756
- })
4757
- );
4758
- if (mode === "proxy") {
4759
- app.use(
4760
- "/authorize",
4761
- (0, import_cors2.cors)({
4762
- origin: "*",
4763
- allowMethods: ["GET", "POST", "OPTIONS"],
4764
- allowHeaders: ["Content-Type", "Authorization"],
4765
- maxAge: 86400
4766
- })
4767
- );
4768
- app.use(
4769
- "/token",
4770
- (0, import_cors2.cors)({
4771
- origin: "*",
4772
- allowMethods: ["POST", "OPTIONS"],
4773
- allowHeaders: ["Content-Type", "Authorization"],
4774
- maxAge: 86400
4775
- })
4776
- );
4844
+ /**
4845
+ * Store or update session metadata
4846
+ */
4847
+ async set(sessionId, data) {
4848
+ this.sessions.set(sessionId, data);
4777
4849
  }
4778
- if (mode === "proxy") {
4779
- const handleAuthorize = /* @__PURE__ */ __name(async (c) => {
4780
- const params = c.req.method === "POST" ? await c.req.parseBody() : c.req.query();
4781
- const clientId = params.client_id;
4850
+ /**
4851
+ * Delete session metadata
4852
+ */
4853
+ async delete(sessionId) {
4854
+ this.sessions.delete(sessionId);
4855
+ }
4856
+ /**
4857
+ * Check if session exists
4858
+ */
4859
+ async has(sessionId) {
4860
+ return this.sessions.has(sessionId);
4861
+ }
4862
+ /**
4863
+ * List all session IDs
4864
+ */
4865
+ async keys() {
4866
+ return Array.from(this.sessions.keys());
4867
+ }
4868
+ /**
4869
+ * Store session metadata with TTL (time-to-live)
4870
+ *
4871
+ * Note: In-memory implementation uses setTimeout for TTL.
4872
+ * For production TTL support, use Redis or another store with native TTL.
4873
+ */
4874
+ async setWithTTL(sessionId, data, ttlMs) {
4875
+ this.sessions.set(sessionId, data);
4876
+ setTimeout(() => {
4877
+ this.sessions.delete(sessionId);
4878
+ console.log(`[MCP] Session ${sessionId} expired after ${ttlMs}ms`);
4879
+ }, ttlMs);
4880
+ }
4881
+ /**
4882
+ * Get the number of active sessions
4883
+ * Useful for monitoring and debugging
4884
+ */
4885
+ get size() {
4886
+ return this.sessions.size;
4887
+ }
4888
+ /**
4889
+ * Clear all sessions
4890
+ * Useful for testing and manual cleanup
4891
+ */
4892
+ async clear() {
4893
+ this.sessions.clear();
4894
+ }
4895
+ };
4896
+
4897
+ // src/server/sessions/stores/redis.ts
4898
+ var RedisSessionStore = class {
4899
+ static {
4900
+ __name(this, "RedisSessionStore");
4901
+ }
4902
+ client;
4903
+ prefix;
4904
+ defaultTTL;
4905
+ constructor(config) {
4906
+ this.client = config.client;
4907
+ this.prefix = config.prefix ?? "mcp:session:";
4908
+ this.defaultTTL = config.defaultTTL ?? 3600;
4909
+ }
4910
+ /**
4911
+ * Get full Redis key for a session ID
4912
+ */
4913
+ getKey(sessionId) {
4914
+ return `${this.prefix}${sessionId}`;
4915
+ }
4916
+ /**
4917
+ * Retrieve session metadata by ID
4918
+ */
4919
+ async get(sessionId) {
4920
+ try {
4921
+ const key = this.getKey(sessionId);
4922
+ const data = await this.client.get(key);
4923
+ if (!data) {
4924
+ return null;
4925
+ }
4926
+ return JSON.parse(data);
4927
+ } catch (error2) {
4928
+ console.error(
4929
+ `[RedisSessionStore] Error getting session ${sessionId}:`,
4930
+ error2
4931
+ );
4932
+ return null;
4933
+ }
4934
+ }
4935
+ /**
4936
+ * Store or update session metadata
4937
+ */
4938
+ async set(sessionId, data) {
4939
+ try {
4940
+ const key = this.getKey(sessionId);
4941
+ const value = JSON.stringify(data);
4942
+ if (this.client.setEx) {
4943
+ await this.client.setEx(key, this.defaultTTL, value);
4944
+ } else if (this.client.setex) {
4945
+ await this.client.setex(key, this.defaultTTL, value);
4946
+ } else {
4947
+ await this.client.set(key, value, { EX: this.defaultTTL });
4948
+ }
4949
+ } catch (error2) {
4950
+ console.error(
4951
+ `[RedisSessionStore] Error setting session ${sessionId}:`,
4952
+ error2
4953
+ );
4954
+ throw error2;
4955
+ }
4956
+ }
4957
+ /**
4958
+ * Delete a session
4959
+ */
4960
+ async delete(sessionId) {
4961
+ try {
4962
+ const key = this.getKey(sessionId);
4963
+ await this.client.del(key);
4964
+ } catch (error2) {
4965
+ console.error(
4966
+ `[RedisSessionStore] Error deleting session ${sessionId}:`,
4967
+ error2
4968
+ );
4969
+ throw error2;
4970
+ }
4971
+ }
4972
+ /**
4973
+ * Check if a session exists
4974
+ */
4975
+ async has(sessionId) {
4976
+ try {
4977
+ const key = this.getKey(sessionId);
4978
+ const exists = await this.client.exists(key);
4979
+ return exists === 1;
4980
+ } catch (error2) {
4981
+ console.error(
4982
+ `[RedisSessionStore] Error checking session ${sessionId}:`,
4983
+ error2
4984
+ );
4985
+ return false;
4986
+ }
4987
+ }
4988
+ /**
4989
+ * List all session IDs
4990
+ *
4991
+ * WARNING: Uses KEYS command which blocks Redis. For production systems with
4992
+ * many sessions, consider using SCAN instead or maintaining a separate SET of
4993
+ * active session IDs.
4994
+ */
4995
+ async keys() {
4996
+ try {
4997
+ const pattern = `${this.prefix}*`;
4998
+ const keys = await this.client.keys(pattern);
4999
+ return keys.map((key) => key.substring(this.prefix.length));
5000
+ } catch (error2) {
5001
+ console.error("[RedisSessionStore] Error listing session keys:", error2);
5002
+ return [];
5003
+ }
5004
+ }
5005
+ /**
5006
+ * Store session metadata with custom TTL (time-to-live)
5007
+ */
5008
+ async setWithTTL(sessionId, data, ttlMs) {
5009
+ try {
5010
+ const key = this.getKey(sessionId);
5011
+ const value = JSON.stringify(data);
5012
+ const ttlSeconds = Math.ceil(ttlMs / 1e3);
5013
+ if (this.client.setEx) {
5014
+ await this.client.setEx(key, ttlSeconds, value);
5015
+ } else if (this.client.setex) {
5016
+ await this.client.setex(key, ttlSeconds, value);
5017
+ } else {
5018
+ await this.client.set(key, value, { EX: ttlSeconds });
5019
+ }
5020
+ } catch (error2) {
5021
+ console.error(
5022
+ `[RedisSessionStore] Error setting session ${sessionId} with TTL:`,
5023
+ error2
5024
+ );
5025
+ throw error2;
5026
+ }
5027
+ }
5028
+ /**
5029
+ * Close Redis connection
5030
+ * Should be called when shutting down the server
5031
+ */
5032
+ async close() {
5033
+ try {
5034
+ await this.client.quit();
5035
+ } catch (error2) {
5036
+ console.error(
5037
+ "[RedisSessionStore] Error closing Redis connection:",
5038
+ error2
5039
+ );
5040
+ throw error2;
5041
+ }
5042
+ }
5043
+ /**
5044
+ * Clear all sessions (useful for testing)
5045
+ * WARNING: This will delete all sessions with the configured prefix
5046
+ *
5047
+ * NOTE: Uses KEYS command which blocks Redis. This is acceptable for testing
5048
+ * but should be avoided in production with large datasets.
5049
+ */
5050
+ async clear() {
5051
+ try {
5052
+ const pattern = `${this.prefix}*`;
5053
+ const keys = await this.client.keys(pattern);
5054
+ if (keys.length > 0) {
5055
+ await this.client.del(keys);
5056
+ }
5057
+ } catch (error2) {
5058
+ console.error("[RedisSessionStore] Error clearing sessions:", error2);
5059
+ throw error2;
5060
+ }
5061
+ }
5062
+ };
5063
+
5064
+ // src/server/sessions/stores/filesystem.ts
5065
+ var import_promises = require("fs/promises");
5066
+ var import_node_path = require("path");
5067
+ var import_node_fs = require("fs");
5068
+ var FileSystemSessionStore = class {
5069
+ static {
5070
+ __name(this, "FileSystemSessionStore");
5071
+ }
5072
+ sessions = /* @__PURE__ */ new Map();
5073
+ filePath;
5074
+ debounceMs;
5075
+ maxAgeMs;
5076
+ saveTimer = null;
5077
+ saving = false;
5078
+ pendingSave = false;
5079
+ constructor(config = {}) {
5080
+ this.filePath = config.path ?? (0, import_node_path.join)(process.cwd(), ".mcp-use", "sessions.json");
5081
+ this.debounceMs = config.debounceMs ?? 100;
5082
+ this.maxAgeMs = config.maxAgeMs ?? 24 * 60 * 60 * 1e3;
5083
+ this.loadSessionsSync();
5084
+ }
5085
+ /**
5086
+ * Load sessions from file synchronously during construction
5087
+ * This ensures sessions are available immediately when the server starts
5088
+ */
5089
+ loadSessionsSync() {
5090
+ try {
5091
+ if (!(0, import_node_fs.existsSync)(this.filePath)) {
5092
+ console.log(
5093
+ `[FileSystemSessionStore] No session file found at ${this.filePath}, starting fresh`
5094
+ );
5095
+ return;
5096
+ }
5097
+ const data = (0, import_node_fs.readFileSync)(this.filePath, "utf-8");
5098
+ const parsed = JSON.parse(data);
5099
+ const now = Date.now();
5100
+ let loadedCount = 0;
5101
+ let expiredCount = 0;
5102
+ for (const [sessionId, metadata] of Object.entries(parsed)) {
5103
+ const sessionMetadata = metadata;
5104
+ const age = now - sessionMetadata.lastAccessedAt;
5105
+ if (age > this.maxAgeMs) {
5106
+ expiredCount++;
5107
+ continue;
5108
+ }
5109
+ this.sessions.set(sessionId, sessionMetadata);
5110
+ loadedCount++;
5111
+ }
5112
+ console.log(
5113
+ `[FileSystemSessionStore] Loaded ${loadedCount} session(s) from ${this.filePath}` + (expiredCount > 0 ? ` (cleaned up ${expiredCount} expired)` : "")
5114
+ );
5115
+ } catch (error2) {
5116
+ if (error2.code === "ENOENT") {
5117
+ console.log(`[FileSystemSessionStore] No existing sessions file`);
5118
+ } else if (error2 instanceof SyntaxError) {
5119
+ console.warn(
5120
+ `[FileSystemSessionStore] Corrupted session file, starting fresh:`,
5121
+ error2.message
5122
+ );
5123
+ } else {
5124
+ console.warn(
5125
+ `[FileSystemSessionStore] Error loading sessions, starting fresh:`,
5126
+ error2.message
5127
+ );
5128
+ }
5129
+ }
5130
+ }
5131
+ /**
5132
+ * Retrieve session metadata by ID
5133
+ */
5134
+ async get(sessionId) {
5135
+ const data = this.sessions.get(sessionId);
5136
+ return data ?? null;
5137
+ }
5138
+ /**
5139
+ * Store or update session metadata
5140
+ * Uses debouncing to batch rapid consecutive writes
5141
+ */
5142
+ async set(sessionId, data) {
5143
+ this.sessions.set(sessionId, data);
5144
+ await this.scheduleSave();
5145
+ }
5146
+ /**
5147
+ * Delete session metadata
5148
+ */
5149
+ async delete(sessionId) {
5150
+ this.sessions.delete(sessionId);
5151
+ await this.scheduleSave();
5152
+ }
5153
+ /**
5154
+ * Check if session exists
5155
+ */
5156
+ async has(sessionId) {
5157
+ return this.sessions.has(sessionId);
5158
+ }
5159
+ /**
5160
+ * List all session IDs
5161
+ */
5162
+ async keys() {
5163
+ return Array.from(this.sessions.keys());
5164
+ }
5165
+ /**
5166
+ * Store session metadata with TTL
5167
+ * Note: TTL is enforced on load, not with timers (simple implementation)
5168
+ */
5169
+ async setWithTTL(sessionId, data, ttlMs) {
5170
+ const metadataWithExpiry = {
5171
+ ...data,
5172
+ lastAccessedAt: Date.now()
5173
+ };
5174
+ this.sessions.set(sessionId, metadataWithExpiry);
5175
+ await this.scheduleSave();
5176
+ setTimeout(() => {
5177
+ this.sessions.delete(sessionId);
5178
+ this.scheduleSave();
5179
+ }, ttlMs);
5180
+ }
5181
+ /**
5182
+ * Get the number of active sessions
5183
+ */
5184
+ get size() {
5185
+ return this.sessions.size;
5186
+ }
5187
+ /**
5188
+ * Clear all sessions
5189
+ */
5190
+ async clear() {
5191
+ this.sessions.clear();
5192
+ await this.scheduleSave();
5193
+ }
5194
+ /**
5195
+ * Schedule a save operation with debouncing
5196
+ * Prevents excessive disk I/O from rapid consecutive writes
5197
+ */
5198
+ async scheduleSave() {
5199
+ if (this.saving) {
5200
+ this.pendingSave = true;
5201
+ return;
5202
+ }
5203
+ if (this.saveTimer) {
5204
+ clearTimeout(this.saveTimer);
5205
+ }
5206
+ this.saveTimer = setTimeout(() => {
5207
+ this.performSave();
5208
+ }, this.debounceMs);
5209
+ }
5210
+ /**
5211
+ * Perform the actual save operation with atomic writes
5212
+ * Uses write-to-temp-then-rename pattern to prevent corruption
5213
+ */
5214
+ async performSave() {
5215
+ this.saveTimer = null;
5216
+ this.saving = true;
5217
+ this.pendingSave = false;
5218
+ try {
5219
+ const dir = (0, import_node_path.dirname)(this.filePath);
5220
+ await (0, import_promises.mkdir)(dir, { recursive: true });
5221
+ const data = {};
5222
+ for (const [sessionId, metadata] of Array.from(this.sessions.entries())) {
5223
+ data[sessionId] = metadata;
5224
+ }
5225
+ const tempPath = `${this.filePath}.tmp`;
5226
+ await (0, import_promises.writeFile)(tempPath, JSON.stringify(data, null, 2), "utf-8");
5227
+ await (0, import_promises.rename)(tempPath, this.filePath);
5228
+ console.debug(
5229
+ `[FileSystemSessionStore] Saved ${this.sessions.size} session(s) to ${this.filePath}`
5230
+ );
5231
+ } catch (error2) {
5232
+ console.error(
5233
+ `[FileSystemSessionStore] Error saving sessions:`,
5234
+ error2.message
5235
+ );
5236
+ try {
5237
+ const tempPath = `${this.filePath}.tmp`;
5238
+ if ((0, import_node_fs.existsSync)(tempPath)) {
5239
+ await (0, import_promises.unlink)(tempPath);
5240
+ }
5241
+ } catch {
5242
+ }
5243
+ } finally {
5244
+ this.saving = false;
5245
+ if (this.pendingSave) {
5246
+ await this.scheduleSave();
5247
+ }
5248
+ }
5249
+ }
5250
+ /**
5251
+ * Force an immediate save (bypasses debouncing)
5252
+ * Useful for ensuring persistence before process exit
5253
+ */
5254
+ async flush() {
5255
+ if (this.saveTimer) {
5256
+ clearTimeout(this.saveTimer);
5257
+ this.saveTimer = null;
5258
+ }
5259
+ await this.performSave();
5260
+ }
5261
+ };
5262
+
5263
+ // src/server/sessions/streams/memory.ts
5264
+ var InMemoryStreamManager = class {
5265
+ static {
5266
+ __name(this, "InMemoryStreamManager");
5267
+ }
5268
+ /**
5269
+ * Map of active SSE stream controllers
5270
+ * Key: sessionId, Value: ReadableStreamDefaultController
5271
+ */
5272
+ streams = /* @__PURE__ */ new Map();
5273
+ /**
5274
+ * Text encoder for converting strings to Uint8Array
5275
+ */
5276
+ textEncoder = new TextEncoder();
5277
+ /**
5278
+ * Register an active SSE stream controller
5279
+ */
5280
+ async create(sessionId, controller) {
5281
+ this.streams.set(sessionId, controller);
5282
+ }
5283
+ /**
5284
+ * Send data to active SSE streams
5285
+ *
5286
+ * Directly enqueues data to in-memory controllers.
5287
+ * For distributed deployments, use RedisStreamManager instead.
5288
+ */
5289
+ async send(sessionIds, data) {
5290
+ const encoded = this.textEncoder.encode(data);
5291
+ if (!sessionIds) {
5292
+ for (const [_id, controller] of this.streams.entries()) {
5293
+ try {
5294
+ controller.enqueue(encoded);
5295
+ } catch (error2) {
5296
+ console.warn(
5297
+ `[InMemoryStreamManager] Failed to send to session ${_id}:`,
5298
+ error2
5299
+ );
5300
+ }
5301
+ }
5302
+ } else {
5303
+ for (const sessionId of sessionIds) {
5304
+ const controller = this.streams.get(sessionId);
5305
+ if (controller) {
5306
+ try {
5307
+ controller.enqueue(encoded);
5308
+ } catch (error2) {
5309
+ console.warn(
5310
+ `[InMemoryStreamManager] Failed to send to session ${sessionId}:`,
5311
+ error2
5312
+ );
5313
+ }
5314
+ }
5315
+ }
5316
+ }
5317
+ }
5318
+ /**
5319
+ * Remove an active SSE stream
5320
+ */
5321
+ async delete(sessionId) {
5322
+ const controller = this.streams.get(sessionId);
5323
+ if (controller) {
5324
+ try {
5325
+ controller.close();
5326
+ } catch (error2) {
5327
+ console.debug(
5328
+ `[InMemoryStreamManager] Controller already closed for session ${sessionId}`
5329
+ );
5330
+ }
5331
+ this.streams.delete(sessionId);
5332
+ }
5333
+ }
5334
+ /**
5335
+ * Check if an active stream exists
5336
+ */
5337
+ async has(sessionId) {
5338
+ return this.streams.has(sessionId);
5339
+ }
5340
+ /**
5341
+ * Close all active streams
5342
+ */
5343
+ async close() {
5344
+ for (const [sessionId, controller] of this.streams.entries()) {
5345
+ try {
5346
+ controller.close();
5347
+ } catch (error2) {
5348
+ console.debug(
5349
+ `[InMemoryStreamManager] Error closing stream for ${sessionId}:`,
5350
+ error2
5351
+ );
5352
+ }
5353
+ }
5354
+ this.streams.clear();
5355
+ }
5356
+ /**
5357
+ * Get the number of active streams
5358
+ * Useful for monitoring
5359
+ */
5360
+ get size() {
5361
+ return this.streams.size;
5362
+ }
5363
+ };
5364
+
5365
+ // src/server/sessions/streams/redis.ts
5366
+ var RedisStreamManager = class {
5367
+ static {
5368
+ __name(this, "RedisStreamManager");
5369
+ }
5370
+ pubSubClient;
5371
+ client;
5372
+ prefix;
5373
+ heartbeatInterval;
5374
+ textEncoder = new TextEncoder();
5375
+ /**
5376
+ * Map of local controllers (only on this server instance)
5377
+ * Key: sessionId, Value: controller
5378
+ */
5379
+ localControllers = /* @__PURE__ */ new Map();
5380
+ /**
5381
+ * Map of heartbeat intervals for keeping sessions alive
5382
+ * Key: sessionId, Value: interval timer
5383
+ */
5384
+ heartbeats = /* @__PURE__ */ new Map();
5385
+ constructor(config) {
5386
+ this.pubSubClient = config.pubSubClient;
5387
+ this.client = config.client;
5388
+ this.prefix = config.prefix ?? "mcp:stream:";
5389
+ this.heartbeatInterval = config.heartbeatInterval ?? 10;
5390
+ }
5391
+ /**
5392
+ * Get the Redis channel name for a session
5393
+ */
5394
+ getChannel(sessionId) {
5395
+ return `${this.prefix}${sessionId}`;
5396
+ }
5397
+ /**
5398
+ * Get the Redis key for tracking active sessions
5399
+ */
5400
+ getAvailableKey(sessionId) {
5401
+ return `available:${this.prefix}${sessionId}`;
5402
+ }
5403
+ /**
5404
+ * Get the Redis key for the active sessions SET
5405
+ */
5406
+ getActiveSessionsKey() {
5407
+ return `${this.prefix}active`;
5408
+ }
5409
+ /**
5410
+ * Register an active SSE stream and subscribe to Redis channel
5411
+ */
5412
+ async create(sessionId, controller) {
5413
+ try {
5414
+ this.localControllers.set(sessionId, controller);
5415
+ const availableKey = this.getAvailableKey(sessionId);
5416
+ await this.client.set(availableKey, "active");
5417
+ if (this.client.expire) {
5418
+ await this.client.expire(availableKey, this.heartbeatInterval * 2);
5419
+ }
5420
+ const activeSessionsKey = this.getActiveSessionsKey();
5421
+ if (this.client.sAdd) {
5422
+ await this.client.sAdd(activeSessionsKey, sessionId);
5423
+ if (this.client.expire) {
5424
+ await this.client.expire(
5425
+ activeSessionsKey,
5426
+ this.heartbeatInterval * 2
5427
+ );
5428
+ }
5429
+ }
5430
+ const heartbeat = setInterval(async () => {
5431
+ try {
5432
+ if (this.client.expire) {
5433
+ await this.client.expire(availableKey, this.heartbeatInterval * 2);
5434
+ const activeSessionsKey2 = this.getActiveSessionsKey();
5435
+ await this.client.expire(
5436
+ activeSessionsKey2,
5437
+ this.heartbeatInterval * 2
5438
+ );
5439
+ }
5440
+ } catch (error2) {
5441
+ console.warn(
5442
+ `[RedisStreamManager] Heartbeat failed for session ${sessionId}:`,
5443
+ error2
5444
+ );
5445
+ }
5446
+ }, this.heartbeatInterval * 1e3);
5447
+ this.heartbeats.set(sessionId, heartbeat);
5448
+ const channel = this.getChannel(sessionId);
5449
+ if (!this.pubSubClient.subscribe) {
5450
+ throw new Error(
5451
+ "[RedisStreamManager] Redis client does not support subscribe method"
5452
+ );
5453
+ }
5454
+ await this.pubSubClient.subscribe(channel, (message) => {
5455
+ const localController = this.localControllers.get(sessionId);
5456
+ if (localController) {
5457
+ try {
5458
+ localController.enqueue(this.textEncoder.encode(message));
5459
+ } catch (error2) {
5460
+ console.warn(
5461
+ `[RedisStreamManager] Failed to enqueue message for ${sessionId}:`,
5462
+ error2
5463
+ );
5464
+ }
5465
+ }
5466
+ });
5467
+ const deleteChannel = `delete:${this.getChannel(sessionId)}`;
5468
+ await this.pubSubClient.subscribe(deleteChannel, async () => {
5469
+ await this.delete(sessionId);
5470
+ });
5471
+ console.log(
5472
+ `[RedisStreamManager] Created stream for session ${sessionId}`
5473
+ );
5474
+ } catch (error2) {
5475
+ console.error(
5476
+ `[RedisStreamManager] Error creating stream for ${sessionId}:`,
5477
+ error2
5478
+ );
5479
+ throw error2;
5480
+ }
5481
+ }
5482
+ /**
5483
+ * Send data to sessions via Redis Pub/Sub
5484
+ *
5485
+ * This works across distributed servers - any server with an active
5486
+ * SSE connection for the target session will receive and forward the message.
5487
+ *
5488
+ * Note: Uses the regular client (not pubSubClient) for publishing.
5489
+ * In node-redis v5+, clients in subscriber mode cannot publish.
5490
+ */
5491
+ async send(sessionIds, data) {
5492
+ try {
5493
+ if (!sessionIds) {
5494
+ const activeSessionsKey = this.getActiveSessionsKey();
5495
+ if (this.client.sMembers) {
5496
+ const sessionIds2 = await this.client.sMembers(activeSessionsKey);
5497
+ for (const sessionId of sessionIds2) {
5498
+ const channel = this.getChannel(sessionId);
5499
+ if (!this.client.publish) {
5500
+ throw new Error(
5501
+ "[RedisStreamManager] Redis client does not support publish method"
5502
+ );
5503
+ }
5504
+ await this.client.publish(channel, data);
5505
+ }
5506
+ } else {
5507
+ const pattern = `available:${this.prefix}*`;
5508
+ const keys = await this.client.keys(pattern);
5509
+ for (const key of keys) {
5510
+ const sessionId = key.replace(`available:${this.prefix}`, "");
5511
+ const channel = this.getChannel(sessionId);
5512
+ if (!this.client.publish) {
5513
+ throw new Error(
5514
+ "[RedisStreamManager] Redis client does not support publish method"
5515
+ );
5516
+ }
5517
+ await this.client.publish(channel, data);
5518
+ }
5519
+ }
5520
+ } else {
5521
+ for (const sessionId of sessionIds) {
5522
+ const channel = this.getChannel(sessionId);
5523
+ if (!this.client.publish) {
5524
+ throw new Error(
5525
+ "[RedisStreamManager] Redis client does not support publish method"
5526
+ );
5527
+ }
5528
+ await this.client.publish(channel, data);
5529
+ }
5530
+ }
5531
+ } catch (error2) {
5532
+ console.error(`[RedisStreamManager] Error sending to sessions:`, error2);
5533
+ throw error2;
5534
+ }
5535
+ }
5536
+ /**
5537
+ * Remove an active SSE stream
5538
+ */
5539
+ async delete(sessionId) {
5540
+ try {
5541
+ const heartbeat = this.heartbeats.get(sessionId);
5542
+ if (heartbeat) {
5543
+ clearInterval(heartbeat);
5544
+ this.heartbeats.delete(sessionId);
5545
+ }
5546
+ const channel = this.getChannel(sessionId);
5547
+ const deleteChannel = `delete:${channel}`;
5548
+ if (!this.pubSubClient.unsubscribe) {
5549
+ throw new Error(
5550
+ "[RedisStreamManager] Redis client does not support unsubscribe method"
5551
+ );
5552
+ }
5553
+ await this.pubSubClient.unsubscribe(channel);
5554
+ await this.pubSubClient.unsubscribe(deleteChannel);
5555
+ if (!this.client.publish) {
5556
+ throw new Error(
5557
+ "[RedisStreamManager] Redis client does not support publish method"
5558
+ );
5559
+ }
5560
+ await this.client.publish(deleteChannel, "");
5561
+ await this.client.del(this.getAvailableKey(sessionId));
5562
+ const activeSessionsKey = this.getActiveSessionsKey();
5563
+ if (this.client.sRem) {
5564
+ await this.client.sRem(activeSessionsKey, sessionId);
5565
+ }
5566
+ const controller = this.localControllers.get(sessionId);
5567
+ if (controller) {
5568
+ try {
5569
+ controller.close();
5570
+ } catch (error2) {
5571
+ console.debug(
5572
+ `[RedisStreamManager] Controller already closed for ${sessionId}`
5573
+ );
5574
+ }
5575
+ this.localControllers.delete(sessionId);
5576
+ }
5577
+ console.log(
5578
+ `[RedisStreamManager] Deleted stream for session ${sessionId}`
5579
+ );
5580
+ } catch (error2) {
5581
+ console.error(
5582
+ `[RedisStreamManager] Error deleting stream for ${sessionId}:`,
5583
+ error2
5584
+ );
5585
+ throw error2;
5586
+ }
5587
+ }
5588
+ /**
5589
+ * Check if a session has an active stream (on ANY server)
5590
+ */
5591
+ async has(sessionId) {
5592
+ try {
5593
+ const availableKey = this.getAvailableKey(sessionId);
5594
+ const exists = await this.client.exists(availableKey);
5595
+ return exists === 1;
5596
+ } catch (error2) {
5597
+ console.error(
5598
+ `[RedisStreamManager] Error checking session ${sessionId}:`,
5599
+ error2
5600
+ );
5601
+ return false;
5602
+ }
5603
+ }
5604
+ /**
5605
+ * Close all connections and cleanup
5606
+ */
5607
+ async close() {
5608
+ try {
5609
+ for (const heartbeat of this.heartbeats.values()) {
5610
+ clearInterval(heartbeat);
5611
+ }
5612
+ this.heartbeats.clear();
5613
+ const activeSessionsKey = this.getActiveSessionsKey();
5614
+ const sessionIdsToCleanup = Array.from(this.localControllers.keys());
5615
+ for (const sessionId of sessionIdsToCleanup) {
5616
+ await this.client.del(this.getAvailableKey(sessionId));
5617
+ if (this.client.sRem) {
5618
+ await this.client.sRem(activeSessionsKey, sessionId);
5619
+ }
5620
+ }
5621
+ for (const controller of this.localControllers.values()) {
5622
+ try {
5623
+ controller.close();
5624
+ } catch (error2) {
5625
+ }
5626
+ }
5627
+ this.localControllers.clear();
5628
+ console.log(`[RedisStreamManager] Closed all streams`);
5629
+ } catch (error2) {
5630
+ console.error(`[RedisStreamManager] Error during close:`, error2);
5631
+ throw error2;
5632
+ }
5633
+ }
5634
+ /**
5635
+ * Get count of active local streams on this server instance
5636
+ */
5637
+ get localSize() {
5638
+ return this.localControllers.size;
5639
+ }
5640
+ };
5641
+
5642
+ // src/server/endpoints/mount-mcp.ts
5643
+ init_runtime();
5644
+ init_telemetry2();
5645
+ var import_node_path2 = require("path");
5646
+ async function mountMcp(app, mcpServerInstance, sessions, config, isProductionMode2) {
5647
+ const { FetchStreamableHTTPServerTransport } = await import("@mcp-use/modelcontextprotocol-sdk/experimental/fetch-streamable-http/index.js");
5648
+ const idleTimeoutMs = config.sessionIdleTimeoutMs ?? 3e5;
5649
+ const sessionStore = config.sessionStore ?? (isProductionMode2 ? new InMemorySessionStore() : new FileSystemSessionStore({
5650
+ path: (0, import_node_path2.join)(process.cwd(), ".mcp-use", "sessions.json")
5651
+ }));
5652
+ const streamManager = config.streamManager ?? new InMemoryStreamManager();
5653
+ const transports = /* @__PURE__ */ new Map();
5654
+ if (config.autoCreateSessionOnInvalidId !== void 0) {
5655
+ console.warn(
5656
+ "[MCP] WARNING: 'autoCreateSessionOnInvalidId' is deprecated and will be removed in a future version.\nThe MCP specification requires clients to send a new InitializeRequest when receiving a 404 for stale sessions.\nModern MCP clients handle this correctly. For session persistence across restarts, use the 'sessionStore' option.\nSee: https://modelcontextprotocol.io/specification/2025-11-25/basic/transports#session-management"
5657
+ );
5658
+ }
5659
+ let idleCleanupInterval;
5660
+ if (!config.stateless && idleTimeoutMs > 0) {
5661
+ idleCleanupInterval = startIdleCleanup(
5662
+ sessions,
5663
+ idleTimeoutMs,
5664
+ transports,
5665
+ mcpServerInstance
5666
+ );
5667
+ }
5668
+ const handleRequest = /* @__PURE__ */ __name(async (c) => {
5669
+ const acceptHeader = c.req.header("Accept") || c.req.header("accept") || "";
5670
+ const clientSupportsSSE = acceptHeader.includes("text/event-stream");
5671
+ const useStatelessMode = config.stateless || !clientSupportsSSE;
5672
+ if (useStatelessMode) {
5673
+ const server = mcpServerInstance.getServerForSession();
5674
+ const transport = new FetchStreamableHTTPServerTransport({
5675
+ sessionIdGenerator: void 0,
5676
+ // No session tracking
5677
+ // Enable plain JSON responses ONLY if client doesn't support SSE
5678
+ // This allows k6/curl to work while maintaining SSE format for compatible clients
5679
+ enableJsonResponse: !clientSupportsSSE
5680
+ });
5681
+ try {
5682
+ await server.connect(transport);
5683
+ const request = c.req.raw;
5684
+ if (!clientSupportsSSE) {
5685
+ const modifiedRequest = new Request(request.url, {
5686
+ method: request.method,
5687
+ headers: {
5688
+ ...Object.fromEntries(request.headers.entries()),
5689
+ Accept: "application/json, text/event-stream"
5690
+ },
5691
+ body: request.body,
5692
+ ...request.body && { duplex: "half" }
5693
+ });
5694
+ return await transport.handleRequest(modifiedRequest);
5695
+ }
5696
+ return await transport.handleRequest(request);
5697
+ } catch (error2) {
5698
+ console.error("[MCP] Stateless request error:", error2);
5699
+ transport.close();
5700
+ server.close();
5701
+ throw error2;
5702
+ }
5703
+ } else {
5704
+ const sessionId = c.req.header("mcp-session-id");
5705
+ if (c.req.method === "HEAD") {
5706
+ if (sessionId && await sessionStore.has(sessionId)) {
5707
+ const session = await sessionStore.get(sessionId);
5708
+ if (session) {
5709
+ session.lastAccessedAt = Date.now();
5710
+ await sessionStore.set(sessionId, session);
5711
+ }
5712
+ }
5713
+ return new Response(null, { status: 200 });
5714
+ }
5715
+ if (sessionId && await sessionStore.has(sessionId) && !transports.has(sessionId)) {
5716
+ console.log(
5717
+ `[MCP] Session metadata found but transport lost (likely hot reload): ${sessionId} - recreating transport`
5718
+ );
5719
+ const server2 = mcpServerInstance.getServerForSession();
5720
+ const transport2 = new FetchStreamableHTTPServerTransport({
5721
+ sessionIdGenerator: /* @__PURE__ */ __name(() => sessionId, "sessionIdGenerator"),
5722
+ // Reuse existing session ID
5723
+ onsessioninitialized: /* @__PURE__ */ __name(async (sid) => {
5724
+ console.log(`[MCP] Session reconnected: ${sid}`);
5725
+ transports.set(sid, transport2);
5726
+ const metadata = await sessionStore.get(sid);
5727
+ const sessionData = {
5728
+ transport: transport2,
5729
+ server: server2,
5730
+ lastAccessedAt: Date.now(),
5731
+ context: c,
5732
+ honoContext: c,
5733
+ ...metadata || {}
5734
+ };
5735
+ sessions.set(sid, sessionData);
5736
+ server2.server.oninitialized = async () => {
5737
+ const clientCapabilities = server2.server.getClientCapabilities();
5738
+ const clientInfo = server2.server.getClientInfo?.() || {};
5739
+ const protocolVersion = server2.server.getProtocolVersion?.() || "unknown";
5740
+ const metadata2 = await sessionStore.get(sid);
5741
+ if (metadata2) {
5742
+ metadata2.clientCapabilities = clientCapabilities;
5743
+ metadata2.clientInfo = clientInfo;
5744
+ metadata2.protocolVersion = String(protocolVersion);
5745
+ await sessionStore.set(sid, metadata2);
5746
+ }
5747
+ const sessionData2 = sessions.get(sid);
5748
+ if (sessionData2) {
5749
+ sessionData2.clientCapabilities = clientCapabilities;
5750
+ }
5751
+ Telemetry.getInstance().trackServerInitialize({
5752
+ protocolVersion: String(protocolVersion),
5753
+ clientInfo: clientInfo || {},
5754
+ clientCapabilities: clientCapabilities || {},
5755
+ sessionId: sid
5756
+ }).catch(
5757
+ (e) => console.debug(`Failed to track server initialize: ${e}`)
5758
+ );
5759
+ if (!isProductionMode2) {
5760
+ console.log(
5761
+ `[MCP] Development mode: Sending list_changed notifications to reconnected session ${sid}`
5762
+ );
5763
+ try {
5764
+ const { sendNotificationToSession: sendNotificationToSession3 } = await Promise.resolve().then(() => (init_notifications(), notifications_exports));
5765
+ await sendNotificationToSession3(
5766
+ sessions,
5767
+ sid,
5768
+ "notifications/tools/list_changed"
5769
+ );
5770
+ await sendNotificationToSession3(
5771
+ sessions,
5772
+ sid,
5773
+ "notifications/resources/list_changed"
5774
+ );
5775
+ await sendNotificationToSession3(
5776
+ sessions,
5777
+ sid,
5778
+ "notifications/prompts/list_changed"
5779
+ );
5780
+ } catch (err) {
5781
+ console.debug(
5782
+ `[MCP] Failed to send list_changed notification:`,
5783
+ err
5784
+ );
5785
+ }
5786
+ }
5787
+ };
5788
+ }, "onsessioninitialized"),
5789
+ onsessionclosed: /* @__PURE__ */ __name(async (sid) => {
5790
+ console.log(`[MCP] Session closed: ${sid}`);
5791
+ transports.delete(sid);
5792
+ await streamManager.delete(sid);
5793
+ await sessionStore.delete(sid);
5794
+ sessions.delete(sid);
5795
+ mcpServerInstance.cleanupSessionSubscriptions?.(sid);
5796
+ }, "onsessionclosed")
5797
+ });
5798
+ await server2.connect(transport2);
5799
+ return transport2.handleRequest(c.req.raw);
5800
+ }
5801
+ if (sessionId && !await sessionStore.has(sessionId)) {
5802
+ console.log(
5803
+ `[MCP] Session not found: ${sessionId} - returning 404 (client should re-initialize)`
5804
+ );
5805
+ return c.json(
5806
+ {
5807
+ jsonrpc: "2.0",
5808
+ error: { code: -32001, message: "Session not found" },
5809
+ id: null
5810
+ },
5811
+ 404
5812
+ );
5813
+ }
5814
+ if (sessionId && transports.has(sessionId)) {
5815
+ const transport2 = transports.get(sessionId);
5816
+ const metadata = await sessionStore.get(sessionId);
5817
+ if (metadata) {
5818
+ metadata.lastAccessedAt = Date.now();
5819
+ await sessionStore.set(sessionId, metadata);
5820
+ }
5821
+ const sessionData = sessions.get(sessionId);
5822
+ if (sessionData) {
5823
+ sessionData.lastAccessedAt = Date.now();
5824
+ sessionData.context = c;
5825
+ sessionData.honoContext = c;
5826
+ }
5827
+ return transport2.handleRequest(c.req.raw);
5828
+ }
5829
+ const server = mcpServerInstance.getServerForSession();
5830
+ const transport = new FetchStreamableHTTPServerTransport({
5831
+ sessionIdGenerator: /* @__PURE__ */ __name(() => generateUUID(), "sessionIdGenerator"),
5832
+ onsessioninitialized: /* @__PURE__ */ __name(async (sid) => {
5833
+ console.log(`[MCP] Session initialized: ${sid}`);
5834
+ transports.set(sid, transport);
5835
+ const sessionData = {
5836
+ transport,
5837
+ server,
5838
+ lastAccessedAt: Date.now(),
5839
+ context: c,
5840
+ honoContext: c
5841
+ };
5842
+ sessions.set(sid, sessionData);
5843
+ await sessionStore.set(sid, {
5844
+ lastAccessedAt: Date.now()
5845
+ });
5846
+ server.server.oninitialized = async () => {
5847
+ const clientCapabilities = server.server.getClientCapabilities();
5848
+ const clientInfo = server.server.getClientInfo?.() || {};
5849
+ const protocolVersion = server.server.getProtocolVersion?.() || "unknown";
5850
+ const metadata = await sessionStore.get(sid);
5851
+ if (metadata) {
5852
+ metadata.clientCapabilities = clientCapabilities;
5853
+ metadata.clientInfo = clientInfo;
5854
+ metadata.protocolVersion = String(protocolVersion);
5855
+ await sessionStore.set(sid, metadata);
5856
+ console.log(
5857
+ `[MCP] Captured client capabilities for session ${sid}:`,
5858
+ clientCapabilities ? Object.keys(clientCapabilities) : "none"
5859
+ );
5860
+ }
5861
+ const sessionData2 = sessions.get(sid);
5862
+ if (sessionData2) {
5863
+ sessionData2.clientCapabilities = clientCapabilities;
5864
+ }
5865
+ Telemetry.getInstance().trackServerInitialize({
5866
+ protocolVersion: String(protocolVersion),
5867
+ clientInfo: clientInfo || {},
5868
+ clientCapabilities: clientCapabilities || {},
5869
+ sessionId: sid
5870
+ }).catch(
5871
+ (e) => console.debug(`Failed to track server initialize: ${e}`)
5872
+ );
5873
+ };
5874
+ }, "onsessioninitialized"),
5875
+ onsessionclosed: /* @__PURE__ */ __name(async (sid) => {
5876
+ console.log(`[MCP] Session closed: ${sid}`);
5877
+ transports.delete(sid);
5878
+ await streamManager.delete(sid);
5879
+ await sessionStore.delete(sid);
5880
+ sessions.delete(sid);
5881
+ mcpServerInstance.cleanupSessionSubscriptions?.(sid);
5882
+ }, "onsessionclosed")
5883
+ });
5884
+ await server.connect(transport);
5885
+ return transport.handleRequest(c.req.raw);
5886
+ }
5887
+ }, "handleRequest");
5888
+ for (const endpoint of ["/mcp", "/sse"]) {
5889
+ app.on(["GET", "POST", "DELETE", "HEAD"], endpoint, handleRequest);
5890
+ }
5891
+ console.log(
5892
+ `[MCP] Server mounted at /mcp and /sse (${config.stateless ? "stateless" : "stateful"} mode)`
5893
+ );
5894
+ return { mcpMounted: true, idleCleanupInterval };
5895
+ }
5896
+ __name(mountMcp, "mountMcp");
5897
+
5898
+ // src/server/oauth/routes.ts
5899
+ var import_cors2 = require("hono/cors");
5900
+ function setupOAuthRoutes(app, provider, baseUrl) {
5901
+ const mode = provider.getMode?.() || "proxy";
5902
+ app.use(
5903
+ "/.well-known/*",
5904
+ (0, import_cors2.cors)({
5905
+ origin: "*",
5906
+ // Allow all origins for metadata discovery
5907
+ allowMethods: ["GET", "OPTIONS"],
5908
+ allowHeaders: ["Content-Type", "Authorization"],
5909
+ exposeHeaders: ["Content-Type"],
5910
+ maxAge: 86400
5911
+ // Cache preflight for 24 hours
5912
+ })
5913
+ );
5914
+ if (mode === "proxy") {
5915
+ app.use(
5916
+ "/authorize",
5917
+ (0, import_cors2.cors)({
5918
+ origin: "*",
5919
+ allowMethods: ["GET", "POST", "OPTIONS"],
5920
+ allowHeaders: ["Content-Type", "Authorization"],
5921
+ maxAge: 86400
5922
+ })
5923
+ );
5924
+ app.use(
5925
+ "/token",
5926
+ (0, import_cors2.cors)({
5927
+ origin: "*",
5928
+ allowMethods: ["POST", "OPTIONS"],
5929
+ allowHeaders: ["Content-Type", "Authorization"],
5930
+ maxAge: 86400
5931
+ })
5932
+ );
5933
+ }
5934
+ if (mode === "proxy") {
5935
+ const handleAuthorize = /* @__PURE__ */ __name(async (c) => {
5936
+ const params = c.req.method === "POST" ? await c.req.parseBody() : c.req.query();
5937
+ const clientId = params.client_id;
4782
5938
  const redirectUri = params.redirect_uri;
4783
5939
  const responseType = params.response_type;
4784
5940
  const codeChallenge = params.code_challenge;
@@ -5042,6 +6198,7 @@ var MCPServerClass = class {
5042
6198
  serverPort;
5043
6199
  serverHost;
5044
6200
  serverBaseUrl;
6201
+ favicon;
5045
6202
  registeredTools = [];
5046
6203
  registeredPrompts = [];
5047
6204
  registeredResources = [];
@@ -5099,8 +6256,15 @@ var MCPServerClass = class {
5099
6256
  */
5100
6257
  constructor(config) {
5101
6258
  this.config = config;
6259
+ if (this.config.stateless === void 0) {
6260
+ this.config.stateless = isDeno;
6261
+ if (this.config.stateless) {
6262
+ console.log("[MCP] Deno detected - using stateless mode (no sessions)");
6263
+ }
6264
+ }
5102
6265
  this.serverHost = config.host || "localhost";
5103
6266
  this.serverBaseUrl = config.baseUrl;
6267
+ this.favicon = config.favicon;
5104
6268
  this.nativeServer = new import_mcp2.McpServer(
5105
6269
  {
5106
6270
  name: config.name,
@@ -5165,7 +6329,10 @@ var MCPServerClass = class {
5165
6329
  "openai/widgetAccessible": widgetConfig.widgetAccessible ?? true,
5166
6330
  "openai/resultCanProduceWidget": widgetConfig.resultCanProduceWidget ?? true
5167
6331
  };
5168
- result._meta = responseMeta;
6332
+ result._meta = {
6333
+ ...result._meta || {},
6334
+ ...responseMeta
6335
+ };
5169
6336
  if (result.content?.[0]?.type === "text" && !result.content[0].text) {
5170
6337
  result.content[0].text = `Displaying ${widgetName}`;
5171
6338
  }
@@ -5568,6 +6735,9 @@ var MCPServerClass = class {
5568
6735
  getActiveSessions = getActiveSessions;
5569
6736
  sendNotification = sendNotification;
5570
6737
  sendNotificationToSession = sendNotificationToSession2;
6738
+ sendToolsListChanged = sendToolsListChanged;
6739
+ sendResourcesListChanged = sendResourcesListChanged;
6740
+ sendPromptsListChanged = sendPromptsListChanged;
5571
6741
  /**
5572
6742
  * Notify subscribed clients that a resource has been updated
5573
6743
  *
@@ -5820,7 +6990,8 @@ function createMCPServer(name, config = {}) {
5820
6990
  allowedOrigins: config.allowedOrigins,
5821
6991
  sessionIdleTimeoutMs: config.sessionIdleTimeoutMs,
5822
6992
  autoCreateSessionOnInvalidId: config.autoCreateSessionOnInvalidId,
5823
- oauth: config.oauth
6993
+ oauth: config.oauth,
6994
+ favicon: config.favicon
5824
6995
  });
5825
6996
  return instance;
5826
6997
  }