@loop_ouroboros/mcp-hub-lite 1.0.0

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 (499) hide show
  1. package/CHANGELOG.md +228 -0
  2. package/LICENSE +21 -0
  3. package/README.md +354 -0
  4. package/dist/client/assets/HomeView-f901HeKw.js +1 -0
  5. package/dist/client/assets/ResourceDetailView-BdOaL_-o.css +1 -0
  6. package/dist/client/assets/ResourceDetailView-Bz66ZjfY.js +1 -0
  7. package/dist/client/assets/ResourcesView-CjMklkyv.css +1 -0
  8. package/dist/client/assets/ResourcesView-lI_fItfA.js +1 -0
  9. package/dist/client/assets/ServerDashboard-7_8Og9JJ.css +1 -0
  10. package/dist/client/assets/ServerDashboard-Bgx8WVkx.js +2 -0
  11. package/dist/client/assets/ServerListView-Bws09jNR.css +1 -0
  12. package/dist/client/assets/ServerListView-CP6Ffkfx.js +30 -0
  13. package/dist/client/assets/ServerStatusTags.vue_vue_type_script_setup_true_lang-BHiTFM7-.js +1 -0
  14. package/dist/client/assets/SessionsView-D68qKR4D.js +1 -0
  15. package/dist/client/assets/SettingsView-BPYfO9BE.js +1 -0
  16. package/dist/client/assets/ToolCallDialog-Br8FNebN.js +1 -0
  17. package/dist/client/assets/ToolCallDialog-C7eh7jHE.css +1 -0
  18. package/dist/client/assets/ToolsView-B6hUiR43.js +1 -0
  19. package/dist/client/assets/ToolsView-BLnU8cV6.css +1 -0
  20. package/dist/client/assets/_baseClone-DCmK847M.js +1 -0
  21. package/dist/client/assets/_plugin-vue_export-helper-DlAUqK2U.js +1 -0
  22. package/dist/client/assets/el-form-item-BVMLpmVC.css +1 -0
  23. package/dist/client/assets/el-form-item-DM0q2hmO.js +12 -0
  24. package/dist/client/assets/el-input-BtI9NECe.js +1 -0
  25. package/dist/client/assets/el-input-CmuHb8HS.css +1 -0
  26. package/dist/client/assets/el-loading-CChNmKeI.js +1 -0
  27. package/dist/client/assets/el-loading-bpKhqqQq.css +1 -0
  28. package/dist/client/assets/el-overlay-B2ZKM6Up.css +1 -0
  29. package/dist/client/assets/el-overlay-lZZbPmsX.js +1 -0
  30. package/dist/client/assets/el-select-BXztu2b5.js +1 -0
  31. package/dist/client/assets/el-select-C0U_l4IZ.css +1 -0
  32. package/dist/client/assets/el-tab-pane-CnNoQHsR.css +1 -0
  33. package/dist/client/assets/el-tab-pane-Dy9ruaEX.js +1 -0
  34. package/dist/client/assets/el-table-column-98UyVgBH.js +1 -0
  35. package/dist/client/assets/el-table-column-T_mV9jNw.css +1 -0
  36. package/dist/client/assets/el-tag-B5lLT51E.js +1 -0
  37. package/dist/client/assets/el-tag-DjxZVOpb.css +1 -0
  38. package/dist/client/assets/event-BB_Ol6Sd.js +1 -0
  39. package/dist/client/assets/index-BgKcmWJr.js +1 -0
  40. package/dist/client/assets/index-BsDWtoIl.css +1 -0
  41. package/dist/client/assets/index-BzQXG7no.js +1 -0
  42. package/dist/client/assets/index-CSTs2jYk.js +2 -0
  43. package/dist/client/assets/omit-Bx5HJNV_.js +1 -0
  44. package/dist/client/assets/raf-B9VDMtMF.js +1 -0
  45. package/dist/client/assets/typescript-Bp3YSIOJ.js +1 -0
  46. package/dist/client/assets/vnode-B8aJx8u4.js +1 -0
  47. package/dist/client/assets/vue-vendor-6ny5zj9i.js +3 -0
  48. package/dist/client/index.html +15 -0
  49. package/dist/server/shared/index.d.ts +3 -0
  50. package/dist/server/shared/index.d.ts.map +1 -0
  51. package/dist/server/shared/index.js +2 -0
  52. package/dist/server/shared/models/index.d.ts +4 -0
  53. package/dist/server/shared/models/index.d.ts.map +1 -0
  54. package/dist/server/shared/models/index.js +3 -0
  55. package/dist/server/shared/models/resource.model.d.ts +8 -0
  56. package/dist/server/shared/models/resource.model.d.ts.map +1 -0
  57. package/dist/server/shared/models/resource.model.js +1 -0
  58. package/dist/server/shared/models/server.model.d.ts +57 -0
  59. package/dist/server/shared/models/server.model.d.ts.map +1 -0
  60. package/dist/server/shared/models/server.model.js +1 -0
  61. package/dist/server/shared/models/session.model.d.ts +73 -0
  62. package/dist/server/shared/models/session.model.d.ts.map +1 -0
  63. package/dist/server/shared/models/session.model.js +70 -0
  64. package/dist/server/shared/models/tool.model.d.ts +22 -0
  65. package/dist/server/shared/models/tool.model.d.ts.map +1 -0
  66. package/dist/server/shared/models/tool.model.js +1 -0
  67. package/dist/server/shared/types/client.types.d.ts +35 -0
  68. package/dist/server/shared/types/client.types.d.ts.map +1 -0
  69. package/dist/server/shared/types/client.types.js +5 -0
  70. package/dist/server/shared/types/common.types.d.ts +5 -0
  71. package/dist/server/shared/types/common.types.d.ts.map +1 -0
  72. package/dist/server/shared/types/common.types.js +2 -0
  73. package/dist/server/shared/types/index.d.ts +4 -0
  74. package/dist/server/shared/types/index.d.ts.map +1 -0
  75. package/dist/server/shared/types/index.js +3 -0
  76. package/dist/server/shared/types/session.types.d.ts +15 -0
  77. package/dist/server/shared/types/session.types.d.ts.map +1 -0
  78. package/dist/server/shared/types/session.types.js +5 -0
  79. package/dist/server/shared/types/websocket.types.d.ts +177 -0
  80. package/dist/server/shared/types/websocket.types.d.ts.map +1 -0
  81. package/dist/server/shared/types/websocket.types.js +25 -0
  82. package/dist/server/src/api/mcp/debug-response-wrapper.d.ts +13 -0
  83. package/dist/server/src/api/mcp/debug-response-wrapper.d.ts.map +1 -0
  84. package/dist/server/src/api/mcp/debug-response-wrapper.js +271 -0
  85. package/dist/server/src/api/mcp/gateway.d.ts +14 -0
  86. package/dist/server/src/api/mcp/gateway.d.ts.map +1 -0
  87. package/dist/server/src/api/mcp/gateway.js +116 -0
  88. package/dist/server/src/api/mcp/session-context-extractor.d.ts +47 -0
  89. package/dist/server/src/api/mcp/session-context-extractor.d.ts.map +1 -0
  90. package/dist/server/src/api/mcp/session-context-extractor.js +127 -0
  91. package/dist/server/src/api/mcp/sse-stream-manager.d.ts +13 -0
  92. package/dist/server/src/api/mcp/sse-stream-manager.d.ts.map +1 -0
  93. package/dist/server/src/api/mcp/sse-stream-manager.js +40 -0
  94. package/dist/server/src/api/web/clients.d.ts +30 -0
  95. package/dist/server/src/api/web/clients.d.ts.map +1 -0
  96. package/dist/server/src/api/web/clients.js +41 -0
  97. package/dist/server/src/api/web/config.d.ts +21 -0
  98. package/dist/server/src/api/web/config.d.ts.map +1 -0
  99. package/dist/server/src/api/web/config.js +119 -0
  100. package/dist/server/src/api/web/health.d.ts +21 -0
  101. package/dist/server/src/api/web/health.d.ts.map +1 -0
  102. package/dist/server/src/api/web/health.js +24 -0
  103. package/dist/server/src/api/web/hub-tools.d.ts +30 -0
  104. package/dist/server/src/api/web/hub-tools.d.ts.map +1 -0
  105. package/dist/server/src/api/web/hub-tools.js +202 -0
  106. package/dist/server/src/api/web/logs.d.ts +30 -0
  107. package/dist/server/src/api/web/logs.d.ts.map +1 -0
  108. package/dist/server/src/api/web/logs.js +141 -0
  109. package/dist/server/src/api/web/mcp-status.d.ts +29 -0
  110. package/dist/server/src/api/web/mcp-status.d.ts.map +1 -0
  111. package/dist/server/src/api/web/mcp-status.js +126 -0
  112. package/dist/server/src/api/web/resources.d.ts +28 -0
  113. package/dist/server/src/api/web/resources.d.ts.map +1 -0
  114. package/dist/server/src/api/web/resources.js +93 -0
  115. package/dist/server/src/api/web/search.d.ts +26 -0
  116. package/dist/server/src/api/web/search.d.ts.map +1 -0
  117. package/dist/server/src/api/web/search.js +62 -0
  118. package/dist/server/src/api/web/servers.d.ts +33 -0
  119. package/dist/server/src/api/web/servers.d.ts.map +1 -0
  120. package/dist/server/src/api/web/servers.js +235 -0
  121. package/dist/server/src/api/web/sessions.d.ts +29 -0
  122. package/dist/server/src/api/web/sessions.d.ts.map +1 -0
  123. package/dist/server/src/api/web/sessions.js +104 -0
  124. package/dist/server/src/api/ws/events.d.ts +29 -0
  125. package/dist/server/src/api/ws/events.d.ts.map +1 -0
  126. package/dist/server/src/api/ws/events.js +44 -0
  127. package/dist/server/src/api/ws/ws-handler.d.ts +83 -0
  128. package/dist/server/src/api/ws/ws-handler.d.ts.map +1 -0
  129. package/dist/server/src/api/ws/ws-handler.js +217 -0
  130. package/dist/server/src/app.d.ts +24 -0
  131. package/dist/server/src/app.d.ts.map +1 -0
  132. package/dist/server/src/app.js +108 -0
  133. package/dist/server/src/cli/commands/list.d.ts +71 -0
  134. package/dist/server/src/cli/commands/list.d.ts.map +1 -0
  135. package/dist/server/src/cli/commands/list.js +97 -0
  136. package/dist/server/src/cli/commands/restart.d.ts +42 -0
  137. package/dist/server/src/cli/commands/restart.d.ts.map +1 -0
  138. package/dist/server/src/cli/commands/restart.js +61 -0
  139. package/dist/server/src/cli/commands/start.d.ts +43 -0
  140. package/dist/server/src/cli/commands/start.d.ts.map +1 -0
  141. package/dist/server/src/cli/commands/start.js +160 -0
  142. package/dist/server/src/cli/commands/status.d.ts +39 -0
  143. package/dist/server/src/cli/commands/status.d.ts.map +1 -0
  144. package/dist/server/src/cli/commands/status.js +164 -0
  145. package/dist/server/src/cli/commands/stop.d.ts +34 -0
  146. package/dist/server/src/cli/commands/stop.d.ts.map +1 -0
  147. package/dist/server/src/cli/commands/stop.js +47 -0
  148. package/dist/server/src/cli/commands/ui.d.ts +44 -0
  149. package/dist/server/src/cli/commands/ui.d.ts.map +1 -0
  150. package/dist/server/src/cli/commands/ui.js +95 -0
  151. package/dist/server/src/cli/index.d.ts +62 -0
  152. package/dist/server/src/cli/index.d.ts.map +1 -0
  153. package/dist/server/src/cli/index.js +88 -0
  154. package/dist/server/src/cli/parse-args.d.ts +108 -0
  155. package/dist/server/src/cli/parse-args.d.ts.map +1 -0
  156. package/dist/server/src/cli/parse-args.js +131 -0
  157. package/dist/server/src/cli/server.d.ts +171 -0
  158. package/dist/server/src/cli/server.d.ts.map +1 -0
  159. package/dist/server/src/cli/server.js +246 -0
  160. package/dist/server/src/config/config-change-logger.d.ts +16 -0
  161. package/dist/server/src/config/config-change-logger.d.ts.map +1 -0
  162. package/dist/server/src/config/config-change-logger.js +43 -0
  163. package/dist/server/src/config/config-loader.d.ts +22 -0
  164. package/dist/server/src/config/config-loader.d.ts.map +1 -0
  165. package/dist/server/src/config/config-loader.js +66 -0
  166. package/dist/server/src/config/config-manager.d.ts +158 -0
  167. package/dist/server/src/config/config-manager.d.ts.map +1 -0
  168. package/dist/server/src/config/config-manager.js +246 -0
  169. package/dist/server/src/config/config-saver.d.ts +16 -0
  170. package/dist/server/src/config/config-saver.d.ts.map +1 -0
  171. package/dist/server/src/config/config-saver.js +28 -0
  172. package/dist/server/src/config/config.schema.d.ts +108 -0
  173. package/dist/server/src/config/config.schema.d.ts.map +1 -0
  174. package/dist/server/src/config/config.schema.js +103 -0
  175. package/dist/server/src/config/server-config-manager.d.ts +74 -0
  176. package/dist/server/src/config/server-config-manager.d.ts.map +1 -0
  177. package/dist/server/src/config/server-config-manager.js +157 -0
  178. package/dist/server/src/config/type-converter.d.ts +13 -0
  179. package/dist/server/src/config/type-converter.d.ts.map +1 -0
  180. package/dist/server/src/config/type-converter.js +37 -0
  181. package/dist/server/src/index.d.ts +3 -0
  182. package/dist/server/src/index.d.ts.map +1 -0
  183. package/dist/server/src/index.js +352 -0
  184. package/dist/server/src/models/event.model.d.ts +106 -0
  185. package/dist/server/src/models/event.model.d.ts.map +1 -0
  186. package/dist/server/src/models/event.model.js +5 -0
  187. package/dist/server/src/models/server.model.d.ts +19 -0
  188. package/dist/server/src/models/server.model.d.ts.map +1 -0
  189. package/dist/server/src/models/server.model.js +1 -0
  190. package/dist/server/src/models/system-tools.constants.d.ts +73 -0
  191. package/dist/server/src/models/system-tools.constants.d.ts.map +1 -0
  192. package/dist/server/src/models/system-tools.constants.js +31 -0
  193. package/dist/server/src/models/types.d.ts +84 -0
  194. package/dist/server/src/models/types.d.ts.map +1 -0
  195. package/dist/server/src/models/types.js +67 -0
  196. package/dist/server/src/pid/file.d.ts +26 -0
  197. package/dist/server/src/pid/file.d.ts.map +1 -0
  198. package/dist/server/src/pid/file.js +68 -0
  199. package/dist/server/src/pid/manager.d.ts +98 -0
  200. package/dist/server/src/pid/manager.d.ts.map +1 -0
  201. package/dist/server/src/pid/manager.js +158 -0
  202. package/dist/server/src/pid/types.d.ts +13 -0
  203. package/dist/server/src/pid/types.d.ts.map +1 -0
  204. package/dist/server/src/pid/types.js +4 -0
  205. package/dist/server/src/server/dev-server.d.ts +2 -0
  206. package/dist/server/src/server/dev-server.d.ts.map +1 -0
  207. package/dist/server/src/server/dev-server.js +171 -0
  208. package/dist/server/src/server/runner.d.ts +57 -0
  209. package/dist/server/src/server/runner.d.ts.map +1 -0
  210. package/dist/server/src/server/runner.js +163 -0
  211. package/dist/server/src/services/client-tracker.service.d.ts +132 -0
  212. package/dist/server/src/services/client-tracker.service.d.ts.map +1 -0
  213. package/dist/server/src/services/client-tracker.service.js +203 -0
  214. package/dist/server/src/services/connection/connection-manager.d.ts +478 -0
  215. package/dist/server/src/services/connection/connection-manager.d.ts.map +1 -0
  216. package/dist/server/src/services/connection/connection-manager.js +929 -0
  217. package/dist/server/src/services/connection/index.d.ts +6 -0
  218. package/dist/server/src/services/connection/index.d.ts.map +1 -0
  219. package/dist/server/src/services/connection/index.js +4 -0
  220. package/dist/server/src/services/connection/tool-cache.d.ts +122 -0
  221. package/dist/server/src/services/connection/tool-cache.d.ts.map +1 -0
  222. package/dist/server/src/services/connection/tool-cache.js +202 -0
  223. package/dist/server/src/services/connection/types.d.ts +30 -0
  224. package/dist/server/src/services/connection/types.d.ts.map +1 -0
  225. package/dist/server/src/services/connection/types.js +1 -0
  226. package/dist/server/src/services/event-bus.service.d.ts +169 -0
  227. package/dist/server/src/services/event-bus.service.d.ts.map +1 -0
  228. package/dist/server/src/services/event-bus.service.js +200 -0
  229. package/dist/server/src/services/gateway/gateway.service.d.ts +61 -0
  230. package/dist/server/src/services/gateway/gateway.service.d.ts.map +1 -0
  231. package/dist/server/src/services/gateway/gateway.service.js +107 -0
  232. package/dist/server/src/services/gateway/index.d.ts +8 -0
  233. package/dist/server/src/services/gateway/index.d.ts.map +1 -0
  234. package/dist/server/src/services/gateway/index.js +6 -0
  235. package/dist/server/src/services/gateway/log-formatter.d.ts +26 -0
  236. package/dist/server/src/services/gateway/log-formatter.d.ts.map +1 -0
  237. package/dist/server/src/services/gateway/log-formatter.js +66 -0
  238. package/dist/server/src/services/gateway/request-handlers/call-tool-handler.d.ts +13 -0
  239. package/dist/server/src/services/gateway/request-handlers/call-tool-handler.d.ts.map +1 -0
  240. package/dist/server/src/services/gateway/request-handlers/call-tool-handler.js +131 -0
  241. package/dist/server/src/services/gateway/request-handlers/index.d.ts +9 -0
  242. package/dist/server/src/services/gateway/request-handlers/index.d.ts.map +1 -0
  243. package/dist/server/src/services/gateway/request-handlers/index.js +8 -0
  244. package/dist/server/src/services/gateway/request-handlers/initialize-handler.d.ts +11 -0
  245. package/dist/server/src/services/gateway/request-handlers/initialize-handler.d.ts.map +1 -0
  246. package/dist/server/src/services/gateway/request-handlers/initialize-handler.js +85 -0
  247. package/dist/server/src/services/gateway/request-handlers/resources-handler.d.ts +11 -0
  248. package/dist/server/src/services/gateway/request-handlers/resources-handler.d.ts.map +1 -0
  249. package/dist/server/src/services/gateway/request-handlers/resources-handler.js +56 -0
  250. package/dist/server/src/services/gateway/request-handlers/system-tools-handler.d.ts +11 -0
  251. package/dist/server/src/services/gateway/request-handlers/system-tools-handler.d.ts.map +1 -0
  252. package/dist/server/src/services/gateway/request-handlers/system-tools-handler.js +228 -0
  253. package/dist/server/src/services/gateway/request-handlers/tools-handler.d.ts +11 -0
  254. package/dist/server/src/services/gateway/request-handlers/tools-handler.d.ts.map +1 -0
  255. package/dist/server/src/services/gateway/request-handlers/tools-handler.js +79 -0
  256. package/dist/server/src/services/gateway/tool-list-generator.d.ts +23 -0
  257. package/dist/server/src/services/gateway/tool-list-generator.d.ts.map +1 -0
  258. package/dist/server/src/services/gateway/tool-list-generator.js +115 -0
  259. package/dist/server/src/services/gateway/types.d.ts +27 -0
  260. package/dist/server/src/services/gateway/types.d.ts.map +1 -0
  261. package/dist/server/src/services/gateway/types.js +4 -0
  262. package/dist/server/src/services/gateway.service.d.ts +7 -0
  263. package/dist/server/src/services/gateway.service.d.ts.map +1 -0
  264. package/dist/server/src/services/gateway.service.js +6 -0
  265. package/dist/server/src/services/hub-manager.service.d.ts +299 -0
  266. package/dist/server/src/services/hub-manager.service.d.ts.map +1 -0
  267. package/dist/server/src/services/hub-manager.service.js +413 -0
  268. package/dist/server/src/services/hub-tools/index.d.ts +10 -0
  269. package/dist/server/src/services/hub-tools/index.d.ts.map +1 -0
  270. package/dist/server/src/services/hub-tools/index.js +6 -0
  271. package/dist/server/src/services/hub-tools/resource-generator.d.ts +63 -0
  272. package/dist/server/src/services/hub-tools/resource-generator.d.ts.map +1 -0
  273. package/dist/server/src/services/hub-tools/resource-generator.js +141 -0
  274. package/dist/server/src/services/hub-tools/server-selector.d.ts +63 -0
  275. package/dist/server/src/services/hub-tools/server-selector.d.ts.map +1 -0
  276. package/dist/server/src/services/hub-tools/server-selector.js +108 -0
  277. package/dist/server/src/services/hub-tools/system-tool-definitions.d.ts +47 -0
  278. package/dist/server/src/services/hub-tools/system-tool-definitions.d.ts.map +1 -0
  279. package/dist/server/src/services/hub-tools/system-tool-definitions.js +225 -0
  280. package/dist/server/src/services/hub-tools/tool-search.d.ts +85 -0
  281. package/dist/server/src/services/hub-tools/tool-search.d.ts.map +1 -0
  282. package/dist/server/src/services/hub-tools/tool-search.js +115 -0
  283. package/dist/server/src/services/hub-tools/types.d.ts +42 -0
  284. package/dist/server/src/services/hub-tools/types.d.ts.map +1 -0
  285. package/dist/server/src/services/hub-tools/types.js +1 -0
  286. package/dist/server/src/services/hub-tools.service.d.ts +235 -0
  287. package/dist/server/src/services/hub-tools.service.d.ts.map +1 -0
  288. package/dist/server/src/services/hub-tools.service.js +457 -0
  289. package/dist/server/src/services/log-storage.service.d.ts +177 -0
  290. package/dist/server/src/services/log-storage.service.d.ts.map +1 -0
  291. package/dist/server/src/services/log-storage.service.js +243 -0
  292. package/dist/server/src/services/mcp-connection-manager.d.ts +7 -0
  293. package/dist/server/src/services/mcp-connection-manager.d.ts.map +1 -0
  294. package/dist/server/src/services/mcp-connection-manager.js +6 -0
  295. package/dist/server/src/services/mcp-session-manager.d.ts +6 -0
  296. package/dist/server/src/services/mcp-session-manager.d.ts.map +1 -0
  297. package/dist/server/src/services/mcp-session-manager.js +4 -0
  298. package/dist/server/src/services/search/index.d.ts +5 -0
  299. package/dist/server/src/services/search/index.d.ts.map +1 -0
  300. package/dist/server/src/services/search/index.js +5 -0
  301. package/dist/server/src/services/search/search-cache.d.ts +132 -0
  302. package/dist/server/src/services/search/search-cache.d.ts.map +1 -0
  303. package/dist/server/src/services/search/search-cache.js +151 -0
  304. package/dist/server/src/services/search/search-core.service.d.ts +108 -0
  305. package/dist/server/src/services/search/search-core.service.d.ts.map +1 -0
  306. package/dist/server/src/services/search/search-core.service.js +187 -0
  307. package/dist/server/src/services/search/search-scorer.d.ts +54 -0
  308. package/dist/server/src/services/search/search-scorer.d.ts.map +1 -0
  309. package/dist/server/src/services/search/search-scorer.js +83 -0
  310. package/dist/server/src/services/search/types.d.ts +20 -0
  311. package/dist/server/src/services/search/types.d.ts.map +1 -0
  312. package/dist/server/src/services/search/types.js +1 -0
  313. package/dist/server/src/services/session/index.d.ts +6 -0
  314. package/dist/server/src/services/session/index.d.ts.map +1 -0
  315. package/dist/server/src/services/session/index.js +4 -0
  316. package/dist/server/src/services/session/session-manager.d.ts +352 -0
  317. package/dist/server/src/services/session/session-manager.d.ts.map +1 -0
  318. package/dist/server/src/services/session/session-manager.js +835 -0
  319. package/dist/server/src/services/session/types.d.ts +32 -0
  320. package/dist/server/src/services/session/types.d.ts.map +1 -0
  321. package/dist/server/src/services/session/types.js +1 -0
  322. package/dist/server/src/services/simple-search.service.d.ts +67 -0
  323. package/dist/server/src/services/simple-search.service.d.ts.map +1 -0
  324. package/dist/server/src/services/simple-search.service.js +82 -0
  325. package/dist/server/src/services/system-tool-handler.d.ts +10 -0
  326. package/dist/server/src/services/system-tool-handler.d.ts.map +1 -0
  327. package/dist/server/src/services/system-tool-handler.js +77 -0
  328. package/dist/server/src/utils/error-handler.d.ts +18 -0
  329. package/dist/server/src/utils/error-handler.d.ts.map +1 -0
  330. package/dist/server/src/utils/error-handler.js +59 -0
  331. package/dist/server/src/utils/format-utils.d.ts +23 -0
  332. package/dist/server/src/utils/format-utils.d.ts.map +1 -0
  333. package/dist/server/src/utils/format-utils.js +58 -0
  334. package/dist/server/src/utils/index.d.ts +2 -0
  335. package/dist/server/src/utils/index.d.ts.map +1 -0
  336. package/dist/server/src/utils/index.js +1 -0
  337. package/dist/server/src/utils/json-utils.d.ts +82 -0
  338. package/dist/server/src/utils/json-utils.d.ts.map +1 -0
  339. package/dist/server/src/utils/json-utils.js +204 -0
  340. package/dist/server/src/utils/log-rotator.d.ts +223 -0
  341. package/dist/server/src/utils/log-rotator.d.ts.map +1 -0
  342. package/dist/server/src/utils/log-rotator.js +306 -0
  343. package/dist/server/src/utils/logger/dev-logger.d.ts +39 -0
  344. package/dist/server/src/utils/logger/dev-logger.d.ts.map +1 -0
  345. package/dist/server/src/utils/logger/dev-logger.js +63 -0
  346. package/dist/server/src/utils/logger/index.d.ts +36 -0
  347. package/dist/server/src/utils/logger/index.d.ts.map +1 -0
  348. package/dist/server/src/utils/logger/index.js +46 -0
  349. package/dist/server/src/utils/logger/log-colors.d.ts +27 -0
  350. package/dist/server/src/utils/logger/log-colors.d.ts.map +1 -0
  351. package/dist/server/src/utils/logger/log-colors.js +41 -0
  352. package/dist/server/src/utils/logger/log-context.d.ts +13 -0
  353. package/dist/server/src/utils/logger/log-context.d.ts.map +1 -0
  354. package/dist/server/src/utils/logger/log-context.js +5 -0
  355. package/dist/server/src/utils/logger/log-formatter.d.ts +47 -0
  356. package/dist/server/src/utils/logger/log-formatter.d.ts.map +1 -0
  357. package/dist/server/src/utils/logger/log-formatter.js +150 -0
  358. package/dist/server/src/utils/logger/log-modules.d.ts +141 -0
  359. package/dist/server/src/utils/logger/log-modules.d.ts.map +1 -0
  360. package/dist/server/src/utils/logger/log-modules.js +81 -0
  361. package/dist/server/src/utils/logger/log-output.d.ts +68 -0
  362. package/dist/server/src/utils/logger/log-output.d.ts.map +1 -0
  363. package/dist/server/src/utils/logger/log-output.js +320 -0
  364. package/dist/server/src/utils/logger/logger.d.ts +190 -0
  365. package/dist/server/src/utils/logger/logger.d.ts.map +1 -0
  366. package/dist/server/src/utils/logger/logger.js +350 -0
  367. package/dist/server/src/utils/logger.d.ts +12 -0
  368. package/dist/server/src/utils/logger.d.ts.map +1 -0
  369. package/dist/server/src/utils/logger.js +12 -0
  370. package/dist/server/src/utils/mcp-error-handler.d.ts +162 -0
  371. package/dist/server/src/utils/mcp-error-handler.d.ts.map +1 -0
  372. package/dist/server/src/utils/mcp-error-handler.js +270 -0
  373. package/dist/server/src/utils/port-checker.d.ts +16 -0
  374. package/dist/server/src/utils/port-checker.d.ts.map +1 -0
  375. package/dist/server/src/utils/port-checker.js +242 -0
  376. package/dist/server/src/utils/request-context.d.ts +71 -0
  377. package/dist/server/src/utils/request-context.d.ts.map +1 -0
  378. package/dist/server/src/utils/request-context.js +73 -0
  379. package/dist/server/src/utils/tool-args-parser.d.ts +17 -0
  380. package/dist/server/src/utils/tool-args-parser.d.ts.map +1 -0
  381. package/dist/server/src/utils/tool-args-parser.js +34 -0
  382. package/dist/server/src/utils/transports/sse-transport.d.ts +104 -0
  383. package/dist/server/src/utils/transports/sse-transport.d.ts.map +1 -0
  384. package/dist/server/src/utils/transports/sse-transport.js +189 -0
  385. package/dist/server/src/utils/transports/stdio-transport.d.ts +122 -0
  386. package/dist/server/src/utils/transports/stdio-transport.d.ts.map +1 -0
  387. package/dist/server/src/utils/transports/stdio-transport.js +324 -0
  388. package/dist/server/src/utils/transports/streamable-http-transport.d.ts +146 -0
  389. package/dist/server/src/utils/transports/streamable-http-transport.d.ts.map +1 -0
  390. package/dist/server/src/utils/transports/streamable-http-transport.js +212 -0
  391. package/dist/server/src/utils/transports/transport-factory.d.ts +26 -0
  392. package/dist/server/src/utils/transports/transport-factory.d.ts.map +1 -0
  393. package/dist/server/src/utils/transports/transport-factory.js +104 -0
  394. package/dist/server/src/utils/transports/transport.interface.d.ts +35 -0
  395. package/dist/server/src/utils/transports/transport.interface.d.ts.map +1 -0
  396. package/dist/server/src/utils/transports/transport.interface.js +1 -0
  397. package/dist/server/tests/contract/mcp-protocol/initialize.test.d.ts +2 -0
  398. package/dist/server/tests/contract/mcp-protocol/initialize.test.d.ts.map +1 -0
  399. package/dist/server/tests/contract/mcp-protocol/initialize.test.js +103 -0
  400. package/dist/server/tests/contract/mcp-protocol/tools-call.test.d.ts +2 -0
  401. package/dist/server/tests/contract/mcp-protocol/tools-call.test.d.ts.map +1 -0
  402. package/dist/server/tests/contract/mcp-protocol/tools-call.test.js +181 -0
  403. package/dist/server/tests/contract/mcp-protocol/tools-list.test.d.ts +2 -0
  404. package/dist/server/tests/contract/mcp-protocol/tools-list.test.d.ts.map +1 -0
  405. package/dist/server/tests/contract/mcp-protocol/tools-list.test.js +155 -0
  406. package/dist/server/tests/evaluation/evaluation.test.d.ts +2 -0
  407. package/dist/server/tests/evaluation/evaluation.test.d.ts.map +1 -0
  408. package/dist/server/tests/evaluation/evaluation.test.js +102 -0
  409. package/dist/server/tests/helpers/sdk-test-helpers.d.ts +32 -0
  410. package/dist/server/tests/helpers/sdk-test-helpers.d.ts.map +1 -0
  411. package/dist/server/tests/helpers/sdk-test-helpers.js +82 -0
  412. package/dist/server/tests/integration/api/gateway.test.d.ts +2 -0
  413. package/dist/server/tests/integration/api/gateway.test.d.ts.map +1 -0
  414. package/dist/server/tests/integration/api/gateway.test.js +366 -0
  415. package/dist/server/tests/integration/gateway/fault-tolerance.test.d.ts +2 -0
  416. package/dist/server/tests/integration/gateway/fault-tolerance.test.d.ts.map +1 -0
  417. package/dist/server/tests/integration/gateway/fault-tolerance.test.js +109 -0
  418. package/dist/server/tests/integration/gateway/mcp-connection.test.d.ts +2 -0
  419. package/dist/server/tests/integration/gateway/mcp-connection.test.d.ts.map +1 -0
  420. package/dist/server/tests/integration/gateway/mcp-connection.test.js +149 -0
  421. package/dist/server/tests/server.test.d.ts +2 -0
  422. package/dist/server/tests/server.test.d.ts.map +1 -0
  423. package/dist/server/tests/server.test.js +95 -0
  424. package/dist/server/tests/setup.d.ts +7 -0
  425. package/dist/server/tests/setup.d.ts.map +1 -0
  426. package/dist/server/tests/setup.js +53 -0
  427. package/dist/server/tests/types/logger-test-helpers.d.ts +17 -0
  428. package/dist/server/tests/types/logger-test-helpers.d.ts.map +1 -0
  429. package/dist/server/tests/types/logger-test-helpers.js +4 -0
  430. package/dist/server/tests/types/test-helpers.d.ts +27 -0
  431. package/dist/server/tests/types/test-helpers.d.ts.map +1 -0
  432. package/dist/server/tests/types/test-helpers.js +4 -0
  433. package/dist/server/tests/unit/cli/basic-cli.test.d.ts +2 -0
  434. package/dist/server/tests/unit/cli/basic-cli.test.d.ts.map +1 -0
  435. package/dist/server/tests/unit/cli/basic-cli.test.js +33 -0
  436. package/dist/server/tests/unit/cli/cli.test.d.ts +2 -0
  437. package/dist/server/tests/unit/cli/cli.test.d.ts.map +1 -0
  438. package/dist/server/tests/unit/cli/cli.test.js +17 -0
  439. package/dist/server/tests/unit/cli/commands.test.d.ts +2 -0
  440. package/dist/server/tests/unit/cli/commands.test.d.ts.map +1 -0
  441. package/dist/server/tests/unit/cli/commands.test.js +75 -0
  442. package/dist/server/tests/unit/cli/server.test.d.ts +2 -0
  443. package/dist/server/tests/unit/cli/server.test.d.ts.map +1 -0
  444. package/dist/server/tests/unit/cli/server.test.js +68 -0
  445. package/dist/server/tests/unit/server/runner.test.d.ts +2 -0
  446. package/dist/server/tests/unit/server/runner.test.d.ts.map +1 -0
  447. package/dist/server/tests/unit/server/runner.test.js +488 -0
  448. package/dist/server/tests/unit/server/setup.d.ts +2 -0
  449. package/dist/server/tests/unit/server/setup.d.ts.map +1 -0
  450. package/dist/server/tests/unit/server/setup.js +21 -0
  451. package/dist/server/tests/unit/services/gateway-logging.test.d.ts +2 -0
  452. package/dist/server/tests/unit/services/gateway-logging.test.d.ts.map +1 -0
  453. package/dist/server/tests/unit/services/gateway-logging.test.js +47 -0
  454. package/dist/server/tests/unit/services/hub-manager-service.test.d.ts +2 -0
  455. package/dist/server/tests/unit/services/hub-manager-service.test.d.ts.map +1 -0
  456. package/dist/server/tests/unit/services/hub-manager-service.test.js +140 -0
  457. package/dist/server/tests/unit/services/hub-manager.test.d.ts +2 -0
  458. package/dist/server/tests/unit/services/hub-manager.test.d.ts.map +1 -0
  459. package/dist/server/tests/unit/services/hub-manager.test.js +98 -0
  460. package/dist/server/tests/unit/services/hub-tools.service.test.d.ts +2 -0
  461. package/dist/server/tests/unit/services/hub-tools.service.test.d.ts.map +1 -0
  462. package/dist/server/tests/unit/services/hub-tools.service.test.js +674 -0
  463. package/dist/server/tests/unit/services/search/search-cache.test.d.ts +2 -0
  464. package/dist/server/tests/unit/services/search/search-cache.test.d.ts.map +1 -0
  465. package/dist/server/tests/unit/services/search/search-cache.test.js +58 -0
  466. package/dist/server/tests/unit/services/search/search-core.service.test.d.ts +2 -0
  467. package/dist/server/tests/unit/services/search/search-core.service.test.d.ts.map +1 -0
  468. package/dist/server/tests/unit/services/search/search-core.service.test.js +84 -0
  469. package/dist/server/tests/unit/services/search/search-scorer.test.d.ts +2 -0
  470. package/dist/server/tests/unit/services/search/search-scorer.test.d.ts.map +1 -0
  471. package/dist/server/tests/unit/services/search/search-scorer.test.js +97 -0
  472. package/dist/server/tests/unit/services/session-manager.test.d.ts +2 -0
  473. package/dist/server/tests/unit/services/session-manager.test.d.ts.map +1 -0
  474. package/dist/server/tests/unit/services/session-manager.test.js +481 -0
  475. package/dist/server/tests/unit/utils/config.test.d.ts +2 -0
  476. package/dist/server/tests/unit/utils/config.test.d.ts.map +1 -0
  477. package/dist/server/tests/unit/utils/config.test.js +634 -0
  478. package/dist/server/tests/unit/utils/json-utils.test.d.ts +2 -0
  479. package/dist/server/tests/unit/utils/json-utils.test.d.ts.map +1 -0
  480. package/dist/server/tests/unit/utils/json-utils.test.js +296 -0
  481. package/dist/server/tests/unit/utils/log-rotator.test.d.ts +2 -0
  482. package/dist/server/tests/unit/utils/log-rotator.test.d.ts.map +1 -0
  483. package/dist/server/tests/unit/utils/log-rotator.test.js +296 -0
  484. package/dist/server/tests/unit/utils/logger.test.d.ts +2 -0
  485. package/dist/server/tests/unit/utils/logger.test.d.ts.map +1 -0
  486. package/dist/server/tests/unit/utils/logger.test.js +462 -0
  487. package/dist/server/tests/unit/utils/mcp-error-handler.test.d.ts +2 -0
  488. package/dist/server/tests/unit/utils/mcp-error-handler.test.d.ts.map +1 -0
  489. package/dist/server/tests/unit/utils/mcp-error-handler.test.js +163 -0
  490. package/dist/server/tests/unit/utils/request-context.test.d.ts +2 -0
  491. package/dist/server/tests/unit/utils/request-context.test.d.ts.map +1 -0
  492. package/dist/server/tests/unit/utils/request-context.test.js +82 -0
  493. package/dist/server/vite.config.d.ts +3 -0
  494. package/dist/server/vite.config.d.ts.map +1 -0
  495. package/dist/server/vite.config.js +160 -0
  496. package/dist/server/vitest.config.d.ts +3 -0
  497. package/dist/server/vitest.config.d.ts.map +1 -0
  498. package/dist/server/vitest.config.js +53 -0
  499. package/package.json +113 -0
@@ -0,0 +1,835 @@
1
+ import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
2
+ import { gateway } from '../gateway.service.js';
3
+ import { logger, LOG_MODULES, formatMcpMessageForLogging, logNotificationMessage } from '../../utils/logger.js';
4
+ import { configManager } from '../../config/config-manager.js';
5
+ import { formatDuration } from '../../utils/format-utils.js';
6
+ import { SessionStateSchema, createEmptySessionStore } from '../../../shared/models/session.model.js';
7
+ import { clientTrackerService } from '../client-tracker.service.js';
8
+ import { getSessionDebugSetting, getMcpCommDebugSetting } from '../../utils/json-utils.js';
9
+ import * as fs from 'fs';
10
+ import path from 'path';
11
+ import os from 'os';
12
+ /**
13
+ * Enhanced MCP Session Manager with comprehensive persistence and lifecycle management.
14
+ *
15
+ * This service manages MCP sessions with full state persistence, automatic cleanup,
16
+ * and graceful shutdown handling. It provides session isolation through unique
17
+ * server and transport instances per session while maintaining shared resources
18
+ * for efficiency.
19
+ *
20
+ * Key features include:
21
+ * - Session state persistence to disk with individual session files and index
22
+ * - Dirty tracking with 5-second batch flushing to minimize I/O operations
23
+ * - Automatic cleanup of stale sessions based on configurable timeout (default 30 minutes)
24
+ * - Graceful shutdown handling with SIGTERM/SIGINT signal capture
25
+ * - Client metadata integration from client tracker service
26
+ * - Environment variable controlled debugging (SESSION_DEBUG)
27
+ *
28
+ * Session storage structure:
29
+ * ```
30
+ * ~/.mcp-hub-lite/
31
+ * └── sessions/
32
+ * ├── index.json # Session index file
33
+ * └── {sessionId}.json # Individual session state files
34
+ * ```
35
+ *
36
+ * @example
37
+ * ```typescript
38
+ * const sessionManager = new McpSessionManager();
39
+ * const session = await sessionManager.getSession('my-session-id');
40
+ * // Use session.server and session.transport for MCP communication
41
+ * ```
42
+ *
43
+ * @since 1.0.0
44
+ */
45
+ export class McpSessionManager {
46
+ sessions = new Map();
47
+ sessionStates = new Map();
48
+ dirtySessions = new Set();
49
+ sessionsPath;
50
+ flushTimeout = null; // Replace with one-time delay
51
+ isInitialized = false;
52
+ get SESSION_TIMEOUT() {
53
+ return configManager.getConfig().security.sessionTimeout;
54
+ }
55
+ get CLEANUP_INTERVAL() {
56
+ return configManager.getConfig().security.sessionFlushInterval;
57
+ }
58
+ constructor() {
59
+ // Initialize sessions directory path
60
+ const configPath = process.env.MCP_HUB_CONFIG_PATH ||
61
+ path.join(os.homedir(), '.mcp-hub-lite', 'config', '.mcp-hub.json');
62
+ const mcpHubDir = path.dirname(path.dirname(configPath)); // Get ~/.mcp-hub-lite directory
63
+ this.sessionsPath = path.join(mcpHubDir, 'sessions');
64
+ logger.info(`Using sessions directory: ${this.sessionsPath}`, LOG_MODULES.SESSION_MANAGER);
65
+ // Start cleanup interval
66
+ setInterval(() => this.cleanup(), this.CLEANUP_INTERVAL);
67
+ // Register graceful shutdown handler
68
+ this.registerShutdownHandler();
69
+ // Initialize by restoring sessions
70
+ this.restoreSessions().catch((error) => {
71
+ logger.error('Failed to restore sessions during initialization:', error, LOG_MODULES.SESSION_MANAGER);
72
+ });
73
+ }
74
+ /**
75
+ * Registers process shutdown handlers for graceful session cleanup.
76
+ *
77
+ * This method sets up event listeners for SIGTERM and SIGINT signals to ensure
78
+ * that all dirty sessions are flushed to disk before the process terminates.
79
+ * This prevents data loss during unexpected shutdowns or service restarts.
80
+ *
81
+ * The shutdown handler performs a final flush of all dirty sessions and
82
+ * clears any pending flush timeouts to ensure clean termination.
83
+ *
84
+ * @returns {void}
85
+ *
86
+ * @example
87
+ * ```typescript
88
+ * // Called automatically in constructor
89
+ * sessionManager.registerShutdownHandler();
90
+ * ```
91
+ */
92
+ registerShutdownHandler() {
93
+ const gracefulShutdown = async () => {
94
+ logger.info('Shutting down, flushing dirty sessions...', LOG_MODULES.SESSION_MANAGER);
95
+ try {
96
+ await this.flushDirtySessions();
97
+ }
98
+ catch (error) {
99
+ logger.error('Error during shutdown flush:', error, LOG_MODULES.SESSION_MANAGER);
100
+ }
101
+ if (this.flushTimeout) {
102
+ clearTimeout(this.flushTimeout);
103
+ }
104
+ };
105
+ process.on('SIGTERM', gracefulShutdown);
106
+ process.on('SIGINT', gracefulShutdown);
107
+ }
108
+ /**
109
+ * Marks a session as dirty, indicating it needs to be persisted to disk.
110
+ *
111
+ * This method adds the session ID to the dirty sessions set and initiates
112
+ * a delayed batch flush operation with a 5-second timeout. This optimization
113
+ * prevents excessive I/O operations by batching multiple session updates
114
+ * that occur in quick succession.
115
+ *
116
+ * When the SESSION_DEBUG environment variable is set, detailed logging
117
+ * is enabled for debugging session persistence behavior.
118
+ *
119
+ * @param {string} sessionId - Unique identifier of the session to mark as dirty
120
+ * @returns {void}
121
+ *
122
+ * @example
123
+ * ```typescript
124
+ * sessionManager.markAsDirty('my-session-id');
125
+ * // Session will be flushed to disk within 5 seconds
126
+ * ```
127
+ */
128
+ markAsDirty(sessionId) {
129
+ this.dirtySessions.add(sessionId);
130
+ if (getSessionDebugSetting()) {
131
+ logger.debug(`Session marked as dirty: ${sessionId}`, LOG_MODULES.SESSION_MANAGER);
132
+ }
133
+ // Set one-time delayed flush to avoid frequent writes (configurable delay, batch processing)
134
+ if (!this.flushTimeout) {
135
+ const flushInterval = configManager.getConfig().security.sessionFlushInterval || 5000;
136
+ this.flushTimeout = setTimeout(() => {
137
+ this.flushDirtySessions();
138
+ this.flushTimeout = null;
139
+ }, flushInterval);
140
+ }
141
+ }
142
+ /**
143
+ * Loads the complete session store from disk, including all individual session files.
144
+ *
145
+ * This method reads the sessions index file and loads each individual session state
146
+ * file, validating each session state against the SessionStateSchema. Invalid
147
+ * session states are skipped with warning logs, ensuring robust recovery from
148
+ * corrupted data.
149
+ *
150
+ * The method creates the sessions directory and index file if they don't exist,
151
+ * providing a clean initialization path for first-time usage.
152
+ *
153
+ * @returns {Promise<SessionStore>} Complete session store with all valid sessions
154
+ * @throws {Error} If critical I/O operations fail (non-recoverable errors)
155
+ *
156
+ * @example
157
+ * ```typescript
158
+ * const store = await sessionManager.loadSessionStore();
159
+ * console.log(`Loaded ${Object.keys(store.sessions).length} sessions`);
160
+ * ```
161
+ */
162
+ async loadSessionStore() {
163
+ try {
164
+ if (!fs.existsSync(this.sessionsPath)) {
165
+ if (getSessionDebugSetting()) {
166
+ logger.debug('Sessions directory not found, creating new one', LOG_MODULES.SESSION_MANAGER);
167
+ }
168
+ fs.mkdirSync(this.sessionsPath, { recursive: true });
169
+ return createEmptySessionStore();
170
+ }
171
+ const indexPath = path.join(this.sessionsPath, 'index.json');
172
+ if (!fs.existsSync(indexPath)) {
173
+ if (getSessionDebugSetting()) {
174
+ logger.debug('Sessions index file not found, creating new one', LOG_MODULES.SESSION_MANAGER);
175
+ }
176
+ return createEmptySessionStore();
177
+ }
178
+ const indexContent = fs.readFileSync(indexPath, 'utf-8');
179
+ const index = JSON.parse(indexContent);
180
+ if (!index.sessions || !Array.isArray(index.sessions)) {
181
+ logger.error('Invalid sessions index file, creating new one', LOG_MODULES.SESSION_MANAGER);
182
+ return createEmptySessionStore();
183
+ }
184
+ const store = createEmptySessionStore();
185
+ for (const sessionId of index.sessions) {
186
+ const sessionPath = path.join(this.sessionsPath, `${sessionId}.json`);
187
+ if (fs.existsSync(sessionPath)) {
188
+ try {
189
+ const sessionContent = fs.readFileSync(sessionPath, 'utf-8');
190
+ const sessionState = JSON.parse(sessionContent);
191
+ const validated = SessionStateSchema.parse(sessionState);
192
+ store.sessions[sessionId] = validated;
193
+ }
194
+ catch (error) {
195
+ logger.warn(`Failed to load session ${sessionId}:`, error, LOG_MODULES.SESSION_MANAGER);
196
+ }
197
+ }
198
+ }
199
+ if (getSessionDebugSetting()) {
200
+ logger.debug(`Loaded ${Object.keys(store.sessions).length} sessions from store`, LOG_MODULES.SESSION_MANAGER);
201
+ }
202
+ return store;
203
+ }
204
+ catch (error) {
205
+ logger.error('Failed to load sessions store:', error, LOG_MODULES.SESSION_MANAGER);
206
+ return createEmptySessionStore();
207
+ }
208
+ }
209
+ /**
210
+ * Saves the complete session store to disk, writing individual session files and index.
211
+ *
212
+ * This method persists all session states by writing individual JSON files for each
213
+ * session and updating the sessions index file. The operation is atomic at the
214
+ * file level, ensuring data integrity even if the process is interrupted.
215
+ *
216
+ * @param {SessionStore} store - Complete session store to persist
217
+ * @returns {Promise<void>} Resolves when all files are successfully written
218
+ * @throws {Error} If any file write operations fail
219
+ *
220
+ * @example
221
+ * ```typescript
222
+ * await sessionManager.saveSessionStore(mySessionStore);
223
+ * console.log('Session store saved successfully');
224
+ * ```
225
+ */
226
+ async saveSessionStore(store) {
227
+ try {
228
+ if (!fs.existsSync(this.sessionsPath)) {
229
+ fs.mkdirSync(this.sessionsPath, { recursive: true });
230
+ }
231
+ // Save individual session files
232
+ for (const [sessionId, state] of Object.entries(store.sessions)) {
233
+ const sessionPath = path.join(this.sessionsPath, `${sessionId}.json`);
234
+ fs.writeFileSync(sessionPath, JSON.stringify(state, null, 2));
235
+ }
236
+ // Save index file
237
+ const indexPath = path.join(this.sessionsPath, 'index.json');
238
+ fs.writeFileSync(indexPath, JSON.stringify({
239
+ sessions: Object.keys(store.sessions)
240
+ }, null, 2));
241
+ }
242
+ catch (error) {
243
+ logger.error('Failed to save sessions store:', error, LOG_MODULES.SESSION_MANAGER);
244
+ throw error;
245
+ }
246
+ }
247
+ /**
248
+ * Restores all sessions from disk during service startup.
249
+ *
250
+ * This method is called automatically during initialization to recover session states
251
+ * from persistent storage. It loads the session store, validates each session state,
252
+ * and populates the in-memory session state cache. Invalid sessions are skipped
253
+ * with warning logs to ensure robust startup even with corrupted data.
254
+ *
255
+ * The method ensures idempotent execution by checking the initialization flag,
256
+ * preventing multiple restoration attempts.
257
+ *
258
+ * @returns {Promise<void>} Resolves when all valid sessions are restored
259
+ *
260
+ * @example
261
+ * ```typescript
262
+ * await sessionManager.restoreSessions();
263
+ * console.log('Sessions restored from disk');
264
+ * ```
265
+ */
266
+ async restoreSessions() {
267
+ if (this.isInitialized) {
268
+ return;
269
+ }
270
+ try {
271
+ logger.info('Restoring sessions from disk...', LOG_MODULES.SESSION_MANAGER);
272
+ const store = await this.loadSessionStore();
273
+ let restoredCount = 0;
274
+ let expiredCount = 0;
275
+ const now = Date.now();
276
+ for (const [sessionId, state] of Object.entries(store.sessions)) {
277
+ try {
278
+ // Validate individual session state
279
+ const validatedState = SessionStateSchema.parse(state);
280
+ // Check if session has expired
281
+ if (now - validatedState.lastAccessedAt > this.SESSION_TIMEOUT) {
282
+ expiredCount++;
283
+ if (getSessionDebugSetting()) {
284
+ logger.debug(`Expired session: ${sessionId} (last accessed ${formatDuration(now - validatedState.lastAccessedAt)} ago)`, LOG_MODULES.SESSION_MANAGER);
285
+ }
286
+ }
287
+ else {
288
+ restoredCount++;
289
+ }
290
+ // Always restore to memory - cleanup() will handle deletion
291
+ this.sessionStates.set(sessionId, validatedState);
292
+ if (getSessionDebugSetting()) {
293
+ logger.debug(`Restored session state: ${sessionId}`, LOG_MODULES.SESSION_MANAGER);
294
+ }
295
+ }
296
+ catch (error) {
297
+ logger.warn(`Skipping invalid session state for ${sessionId}:`, error, LOG_MODULES.SESSION_MANAGER);
298
+ }
299
+ }
300
+ this.isInitialized = true;
301
+ logger.info(`Successfully restored ${restoredCount} valid, ${expiredCount} expired session(s)`, LOG_MODULES.SESSION_MANAGER);
302
+ // Immediately run cleanup on startup to clean up any expired sessions
303
+ this.cleanup();
304
+ }
305
+ catch (error) {
306
+ logger.error('Failed to restore sessions:', error, LOG_MODULES.SESSION_MANAGER);
307
+ // Continue with empty state rather than failing
308
+ this.isInitialized = true;
309
+ }
310
+ }
311
+ /**
312
+ * Flushes all dirty sessions to disk in a single batch operation.
313
+ *
314
+ * This method collects all sessions marked as dirty, loads the current session store,
315
+ * updates it with the dirty sessions, and saves the complete store back to disk.
316
+ * This batch approach minimizes I/O operations and ensures data consistency.
317
+ *
318
+ * The method includes comprehensive error handling to prevent partial writes and
319
+ * maintains detailed logging for debugging and monitoring purposes.
320
+ *
321
+ * @returns {Promise<void>} Resolves when all dirty sessions are successfully flushed
322
+ *
323
+ * @example
324
+ * ```typescript
325
+ * await sessionManager.flushDirtySessions();
326
+ * console.log('All dirty sessions flushed to disk');
327
+ * ```
328
+ */
329
+ async flushDirtySessions() {
330
+ if (this.dirtySessions.size === 0) {
331
+ return;
332
+ }
333
+ try {
334
+ logger.info(`Flushing ${this.dirtySessions.size} dirty session(s) to disk`, LOG_MODULES.SESSION_MANAGER);
335
+ // Load current store
336
+ const currentStore = await this.loadSessionStore();
337
+ const newStore = {
338
+ ...currentStore,
339
+ sessions: { ...currentStore.sessions }
340
+ };
341
+ // Process all dirty sessions
342
+ for (const sessionId of this.dirtySessions) {
343
+ const sessionState = this.sessionStates.get(sessionId);
344
+ if (sessionState) {
345
+ // Session exists - update it
346
+ newStore.sessions[sessionId] = sessionState;
347
+ }
348
+ else {
349
+ // Session does not exist - remove it
350
+ delete newStore.sessions[sessionId];
351
+ // Also remove the individual session file
352
+ const sessionPath = path.join(this.sessionsPath, `${sessionId}.json`);
353
+ if (fs.existsSync(sessionPath)) {
354
+ fs.unlinkSync(sessionPath);
355
+ logger.debug(`Deleted session file: ${sessionId}.json`, LOG_MODULES.SESSION_MANAGER);
356
+ }
357
+ }
358
+ }
359
+ await this.saveSessionStore(newStore);
360
+ const flushedCount = this.dirtySessions.size;
361
+ this.dirtySessions.clear();
362
+ logger.info(`Successfully flushed ${flushedCount} session(s)`, LOG_MODULES.SESSION_MANAGER);
363
+ }
364
+ catch (error) {
365
+ logger.error('Failed to flush dirty sessions:', error, LOG_MODULES.SESSION_MANAGER);
366
+ }
367
+ }
368
+ /**
369
+ * Updates session metadata by retrieving client information from the client tracker service.
370
+ *
371
+ * This method fetches current client information including name, version, working directory,
372
+ * and project context from the client tracker service and returns a partial session state
373
+ * update object containing only the relevant metadata fields.
374
+ *
375
+ * @param {string} sessionId - Unique identifier of the session to update
376
+ * @returns {Partial<SessionState>} Partial session state with updated client metadata
377
+ *
378
+ * @example
379
+ * ```typescript
380
+ * const metadata = sessionManager.updateSessionMetadataFromClient('my-session-id');
381
+ * console.log('Updated client metadata:', metadata);
382
+ * ```
383
+ */
384
+ updateSessionMetadataFromClient(sessionId) {
385
+ const clientInfo = clientTrackerService.getClient(sessionId);
386
+ return {
387
+ clientName: clientInfo?.clientName,
388
+ clientVersion: clientInfo?.clientVersion,
389
+ protocolVersion: clientInfo?.protocolVersion,
390
+ cwd: clientInfo?.cwd,
391
+ project: clientInfo?.project,
392
+ userAgent: clientInfo?.userAgent,
393
+ ip: clientInfo?.ip
394
+ };
395
+ }
396
+ /**
397
+ * Creates or updates a session state with current metadata and timestamps.
398
+ *
399
+ * This method merges existing session state (if any) with current client metadata
400
+ * from the client tracker service, updates access timestamps, and ensures proper
401
+ * session state structure. The updated state is stored in memory and marked as dirty
402
+ * for persistence.
403
+ *
404
+ * @param {string} sessionId - Unique identifier of the session to create or update
405
+ * @returns {SessionState} Complete updated session state object
406
+ *
407
+ * @example
408
+ * ```typescript
409
+ * const state = sessionManager.upsertSessionState('my-session-id');
410
+ * console.log('Session state updated:', state);
411
+ * ```
412
+ */
413
+ upsertSessionState(sessionId) {
414
+ const existing = this.sessionStates.get(sessionId);
415
+ const now = Date.now();
416
+ const clientMetadata = this.updateSessionMetadataFromClient(sessionId);
417
+ const state = {
418
+ sessionId,
419
+ clientName: clientMetadata.clientName || existing?.clientName,
420
+ clientVersion: clientMetadata.clientVersion || existing?.clientVersion,
421
+ protocolVersion: clientMetadata.protocolVersion || existing?.protocolVersion,
422
+ cwd: clientMetadata.cwd || existing?.cwd,
423
+ project: clientMetadata.project || existing?.project,
424
+ userAgent: clientMetadata.userAgent || existing?.userAgent,
425
+ ip: clientMetadata.ip || existing?.ip,
426
+ createdAt: existing?.createdAt || now,
427
+ lastAccessedAt: now,
428
+ metadata: existing?.metadata || {}
429
+ };
430
+ this.sessionStates.set(sessionId, state);
431
+ this.markAsDirty(sessionId);
432
+ return state;
433
+ }
434
+ /**
435
+ * Updates session metadata explicitly with provided key-value pairs.
436
+ *
437
+ * This method merges the provided metadata object with existing session metadata,
438
+ * updates the last accessed timestamp, and marks the session as dirty for persistence.
439
+ * It's useful for storing custom session-specific information that needs to be persisted.
440
+ *
441
+ * @param {string} sessionId - Unique identifier of the session to update
442
+ * @param {Record<string, unknown>} metadata - Key-value pairs to merge into session metadata
443
+ * @returns {Promise<void>} Resolves when metadata is updated and marked for persistence
444
+ *
445
+ * @example
446
+ * ```typescript
447
+ * await sessionManager.updateSessionMetadata('my-session-id', {
448
+ * userPreferences: { theme: 'dark' },
449
+ * lastProject: 'my-project'
450
+ * });
451
+ * ```
452
+ */
453
+ async updateSessionMetadata(sessionId, metadata) {
454
+ const existing = this.sessionStates.get(sessionId);
455
+ if (existing) {
456
+ const now = Date.now();
457
+ const updated = {
458
+ ...existing,
459
+ lastAccessedAt: now,
460
+ metadata: {
461
+ ...existing.metadata,
462
+ ...metadata
463
+ }
464
+ };
465
+ this.sessionStates.set(sessionId, updated);
466
+ this.markAsDirty(sessionId);
467
+ if (getSessionDebugSetting()) {
468
+ logger.debug(`Updated metadata for session: ${sessionId}`, LOG_MODULES.SESSION_MANAGER);
469
+ }
470
+ }
471
+ }
472
+ /**
473
+ * Retrieves an existing session or creates a new one if it doesn't exist.
474
+ *
475
+ * This method provides access to active sessions, creating new ones on demand with
476
+ * proper initialization based on the requireInitialize parameter. It updates session
477
+ * access timestamps and persists session state changes to ensure data consistency.
478
+ *
479
+ * The method includes optimization to prevent excessive timestamp updates for rapid
480
+ * successive requests by only updating when at least 100ms have passed since the last access.
481
+ *
482
+ * @param {string} sessionId - Unique identifier of the session to retrieve or create
483
+ * @param {boolean} [requireInitialize=true] - Whether to require MCP protocol initialization
484
+ * @returns {Promise<Session>} Active session object with server and transport
485
+ *
486
+ * @example
487
+ * ```typescript
488
+ * // Get or create session with default initialization
489
+ * const session = await sessionManager.getSession('my-session-id');
490
+ *
491
+ * // Get or create session without requiring initialization (for restored sessions)
492
+ * const restoredSession = await sessionManager.getSession('restored-session-id', false);
493
+ * ```
494
+ */
495
+ async getSession(sessionId, requireInitialize = true) {
496
+ if (getSessionDebugSetting()) {
497
+ const hasSessionState = this.sessionStates.has(sessionId);
498
+ const hasSessionObject = this.sessions.has(sessionId);
499
+ logger.debug(`getSession called for ${sessionId} (requireInitialize: ${requireInitialize}). State exists: ${hasSessionState}, Object exists: ${hasSessionObject}`, LOG_MODULES.SESSION_MANAGER);
500
+ }
501
+ let session = this.sessions.get(sessionId);
502
+ // Check if session state exists but session object doesn't (inconsistency case)
503
+ const hasSessionState = this.sessionStates.has(sessionId);
504
+ const hasSessionObject = this.sessions.has(sessionId);
505
+ if (hasSessionState && !hasSessionObject) {
506
+ // Session state exists but session object is missing - recreate session
507
+ if (getSessionDebugSetting()) {
508
+ logger.debug(`Session state exists but session object missing for ${sessionId}, recreating...`, LOG_MODULES.SESSION_MANAGER);
509
+ }
510
+ session = await this.createSession(sessionId, requireInitialize);
511
+ this.sessions.set(sessionId, session);
512
+ }
513
+ else if (!session) {
514
+ // Normal case: create new session
515
+ session = await this.createSession(sessionId, requireInitialize);
516
+ this.sessions.set(sessionId, session);
517
+ }
518
+ else {
519
+ // Enhanced check: ensure transport session state is correctly initialized
520
+ // This fixes the "Session not found" error when transport has lost session context
521
+ const hasRestoredState = this.sessionStates.has(sessionId);
522
+ const shouldManuallyInitialize = !requireInitialize || hasRestoredState;
523
+ // Always ensure sessionId is set on transport, regardless of initialization status
524
+ // This fixes the "Session not found" error for non-initialization requests
525
+ const webTransport = session.transport
526
+ ._webStandardTransport;
527
+ if (webTransport) {
528
+ const currentSessionId = webTransport.sessionId;
529
+ const isInitialized = webTransport._initialized;
530
+ if (getSessionDebugSetting()) {
531
+ logger.debug(`Transport state check - Expected: ${sessionId} (initialized: ${shouldManuallyInitialize}), Actual: ${currentSessionId} (initialized: ${isInitialized})`, LOG_MODULES.SESSION_MANAGER);
532
+ }
533
+ if (webTransport.sessionId !== sessionId) {
534
+ logger.warn(`Session ID mismatch detected for ${sessionId}. Resetting transport sessionId.`, LOG_MODULES.SESSION_MANAGER);
535
+ webTransport.sessionId = sessionId;
536
+ if (webTransport._session !== undefined) {
537
+ webTransport._session = sessionId;
538
+ }
539
+ }
540
+ if (shouldManuallyInitialize && !webTransport._initialized) {
541
+ logger.warn(`Session initialization state mismatch detected for ${sessionId}. Setting initialized flag.`, LOG_MODULES.SESSION_MANAGER);
542
+ webTransport._initialized = true;
543
+ }
544
+ }
545
+ }
546
+ const now = Date.now();
547
+ const timeDiff = Math.abs(now - session.lastAccessed);
548
+ // Only update lastAccessed if meaningful time has passed
549
+ // This prevents excessive updates for rapid successive requests
550
+ if (timeDiff >= 100) {
551
+ session.lastAccessed = now;
552
+ // Also update and persist session state
553
+ this.upsertSessionState(sessionId);
554
+ }
555
+ if (getSessionDebugSetting()) {
556
+ logger.debug(`Returning session for ${sessionId}`, LOG_MODULES.SESSION_MANAGER);
557
+ }
558
+ return session;
559
+ }
560
+ /**
561
+ * Retrieves all persisted session states for API exposure.
562
+ *
563
+ * This method returns an array of all current session states, providing a complete
564
+ * snapshot of all active sessions and their metadata. It's primarily used by the
565
+ * web API to expose session information to clients.
566
+ *
567
+ * @returns {SessionState[]} Array of all current session states
568
+ *
569
+ * @example
570
+ * ```typescript
571
+ * const allStates = sessionManager.getAllSessionStates();
572
+ * console.log(`Total active sessions: ${allStates.length}`);
573
+ * ```
574
+ */
575
+ getAllSessionStates() {
576
+ return Array.from(this.sessionStates.values());
577
+ }
578
+ /**
579
+ * Retrieves a specific session state by session ID for API exposure.
580
+ *
581
+ * This method returns the current state of a specific session or undefined if
582
+ * the session doesn't exist. It's primarily used by the web API to provide
583
+ * detailed information about individual sessions.
584
+ *
585
+ * @param {string} sessionId - Unique identifier of the session to retrieve
586
+ * @returns {SessionState | undefined} Session state object or undefined if not found
587
+ *
588
+ * @example
589
+ * ```typescript
590
+ * const state = sessionManager.getSessionState('my-session-id');
591
+ * if (state) {
592
+ * console.log('Session client:', state.clientName);
593
+ * }
594
+ * ```
595
+ */
596
+ getSessionState(sessionId) {
597
+ return this.sessionStates.get(sessionId);
598
+ }
599
+ /**
600
+ * Checks if a session object exists in the active sessions map.
601
+ *
602
+ * @param sessionId - Unique identifier of the session to check
603
+ * @returns {boolean} True if the session object exists, false otherwise
604
+ */
605
+ hasSession(sessionId) {
606
+ return this.sessions.has(sessionId);
607
+ }
608
+ /**
609
+ * Deletes a session and removes it from persistent storage.
610
+ *
611
+ * This method gracefully closes the session's MCP server, removes it from memory,
612
+ * and ensures it's removed from persistent storage by marking it as dirty and
613
+ * performing an immediate flush. It returns true if the session existed and was deleted.
614
+ *
615
+ * @param {string} sessionId - Unique identifier of the session to delete
616
+ * @returns {Promise<boolean>} True if session existed and was deleted, false otherwise
617
+ *
618
+ * @example
619
+ * ```typescript
620
+ * const deleted = await sessionManager.deleteSession('my-session-id');
621
+ * if (deleted) {
622
+ * console.log('Session deleted successfully');
623
+ * }
624
+ * ```
625
+ */
626
+ async deleteSession(sessionId) {
627
+ const session = this.sessions.get(sessionId);
628
+ if (session) {
629
+ try {
630
+ await session.server.close();
631
+ }
632
+ catch (e) {
633
+ logger.error(`Error closing session ${sessionId}:`, e, LOG_MODULES.SESSION_MANAGER);
634
+ }
635
+ this.sessions.delete(sessionId);
636
+ }
637
+ const existed = this.sessionStates.has(sessionId);
638
+ if (existed) {
639
+ this.sessionStates.delete(sessionId);
640
+ // Mark as dirty to remove from persistence
641
+ this.markAsDirty(sessionId);
642
+ // Flush immediately to remove from disk
643
+ await this.flushDirtySessions();
644
+ }
645
+ return existed;
646
+ }
647
+ /**
648
+ * Creates a new MCP session with proper initialization and transport setup.
649
+ *
650
+ * This method sets up a complete MCP session including server, transport, and
651
+ * communication logging. It handles both fresh sessions and restored sessions,
652
+ * with special logic for skipping initialization when appropriate.
653
+ *
654
+ * Key features:
655
+ * - Creates StreamableHTTPServerTransport with custom session ID generation
656
+ * - Sets up comprehensive message logging for debugging
657
+ * - Handles restored sessions by manually initializing transport state
658
+ * - Integrates with client tracker service for metadata
659
+ * - Creates/updates session state and marks as dirty for persistence
660
+ *
661
+ * @param {string} sessionId - Unique identifier for the new session
662
+ * @param {boolean} [requireInitialize=true] - Whether to require MCP protocol initialization
663
+ * @returns {Promise<Session>} Newly created session object
664
+ * @throws {Error} If session creation or connection fails
665
+ *
666
+ * @example
667
+ * ```typescript
668
+ * const session = await sessionManager.createSession('new-session-id');
669
+ * // Use session.server and session.transport for MCP communication
670
+ * ```
671
+ */
672
+ async createSession(sessionId, requireInitialize = true) {
673
+ logger.info(`Creating new MCP session: ${sessionId}, requireInitialize: ${requireInitialize}`, LOG_MODULES.SESSION_MANAGER);
674
+ const transport = new StreamableHTTPServerTransport({
675
+ sessionIdGenerator: () => sessionId,
676
+ onsessioninitialized: (id) => {
677
+ if (getSessionDebugSetting()) {
678
+ logger.debug(`Session initialized: ${id}`, LOG_MODULES.SESSION_MANAGER);
679
+ }
680
+ },
681
+ onsessionclosed: (id) => {
682
+ if (getSessionDebugSetting()) {
683
+ logger.debug(`Session closed: ${id}`, LOG_MODULES.SESSION_MANAGER);
684
+ }
685
+ this.sessions.delete(id);
686
+ }
687
+ });
688
+ // Directly set sessionId property to avoid validateSession failure
689
+ // because WebStandardStreamableHTTPServerTransport only calls sessionIdGenerator during initialize
690
+ const webTransport = transport._webStandardTransport;
691
+ if (webTransport) {
692
+ webTransport.sessionId = sessionId;
693
+ // Do not set _initialized = true here because it will cause initialize request to fail
694
+ // _initialized flag will be set as needed in the shouldManuallyInitialize logic
695
+ if (getSessionDebugSetting()) {
696
+ logger.debug(`Directly set sessionId on transport: ${sessionId}`, LOG_MODULES.SESSION_MANAGER);
697
+ }
698
+ }
699
+ // Check if we have a restored state for this session
700
+ const restoredState = this.sessionStates.get(sessionId);
701
+ if (restoredState) {
702
+ logger.info(`Reusing restored session state for: ${sessionId}`, LOG_MODULES.SESSION_MANAGER);
703
+ }
704
+ // For restored sessions or when explicitly requested to skip initialization, manually set the initialized flag
705
+ // This allows sending requests like tools/list directly without needing to initialize first
706
+ const shouldManuallyInitialize = !requireInitialize || !!restoredState;
707
+ if (getSessionDebugSetting()) {
708
+ logger.debug(`shouldManuallyInitialize: ${shouldManuallyInitialize} (!requireInitialize: ${!requireInitialize} || restored: ${!!restoredState})`, LOG_MODULES.SESSION_MANAGER);
709
+ }
710
+ if (shouldManuallyInitialize) {
711
+ const webTransport = transport._webStandardTransport;
712
+ if (webTransport) {
713
+ webTransport.sessionId = sessionId;
714
+ webTransport._initialized = true;
715
+ // Ensure session property is also set if it exists
716
+ if (webTransport._session !== undefined) {
717
+ webTransport._session = sessionId;
718
+ }
719
+ if (getSessionDebugSetting()) {
720
+ logger.debug(`Manually initialized transport for session: ${sessionId} (skipInitialize: ${!requireInitialize}, restored: ${!!restoredState})`, LOG_MODULES.SESSION_MANAGER);
721
+ logger.debug(`Transport state after manual init - sessionId: ${webTransport.sessionId}, _initialized: ${webTransport._initialized}, _session: ${webTransport._session}`, LOG_MODULES.SESSION_MANAGER);
722
+ }
723
+ }
724
+ else {
725
+ logger.error(`Failed to get _webStandardTransport from transport!`, LOG_MODULES.SESSION_MANAGER);
726
+ // Even if we can't manually initialize, continue with normal initialization
727
+ // The transport will be initialized when the first request arrives
728
+ }
729
+ }
730
+ // Always set up message handler for notifications/message
731
+ transport.onmessage = (message) => {
732
+ // Communication debug logs: controlled by MCP_COMM_DEBUG environment variable
733
+ if (getMcpCommDebugSetting()) {
734
+ const logMessage = formatMcpMessageForLogging(message);
735
+ logger.debug(`MCP message received: ${logMessage}`, LOG_MODULES.COMMUNICATION);
736
+ }
737
+ // Log notifications/message to application logs (always enabled)
738
+ logNotificationMessage(message, sessionId);
739
+ };
740
+ // Wrap send method for debug logging (if enabled)
741
+ if (getMcpCommDebugSetting()) {
742
+ const originalSend = transport.send;
743
+ transport.send = async (message, options) => {
744
+ try {
745
+ const logMessage = formatMcpMessageForLogging(message);
746
+ logger.debug(`MCP message sent: ${logMessage}`, LOG_MODULES.COMMUNICATION);
747
+ }
748
+ catch {
749
+ logger.debug(`MCP message sent: [Error formatting response]`, LOG_MODULES.COMMUNICATION);
750
+ }
751
+ // Call original send method
752
+ return await originalSend.call(transport, message, options);
753
+ };
754
+ }
755
+ const server = gateway.createConnectionServer();
756
+ // Ensure server is fully connected before returning session
757
+ await server.connect(transport);
758
+ // Note: StreamableHTTPServerTransport's onsessioninitialized callback is triggered
759
+ // when the first actual request (GET /mcp) arrives, not during connect().
760
+ // Therefore, we don't need to wait for this event - the transport is ready to receive requests.
761
+ if (getSessionDebugSetting()) {
762
+ logger.debug(`StreamableHTTPServerTransport ready for session: ${sessionId}`, LOG_MODULES.SESSION_MANAGER);
763
+ }
764
+ // Create/update session state
765
+ this.upsertSessionState(sessionId);
766
+ logger.info(`MCP session created successfully: ${sessionId}`, LOG_MODULES.SESSION_MANAGER);
767
+ const session = {
768
+ server,
769
+ transport,
770
+ sessionId,
771
+ lastAccessed: Date.now()
772
+ };
773
+ return session;
774
+ }
775
+ /**
776
+ * Cleans up stale sessions based on the configured session timeout.
777
+ *
778
+ * This method is called periodically (every 60 seconds) to identify and close
779
+ * sessions that haven't been accessed within the configured timeout period
780
+ * (default 30 minutes). It gracefully closes MCP servers and removes sessions
781
+ * from memory to prevent resource leaks.
782
+ *
783
+ * The method logs detailed information about cleaned sessions and maintains
784
+ * active session count for monitoring purposes.
785
+ *
786
+ * @returns {void}
787
+ *
788
+ * @example
789
+ * ```typescript
790
+ * // Called automatically every 60 seconds
791
+ * sessionManager.cleanup();
792
+ * ```
793
+ */
794
+ cleanup() {
795
+ const now = Date.now();
796
+ let cleanedCount = 0;
797
+ // Cleanup in-memory session objects
798
+ for (const [sessionId, session] of this.sessions.entries()) {
799
+ if (now - session.lastAccessed > this.SESSION_TIMEOUT) {
800
+ logger.debug(`Removing stale session: ${sessionId}. Last accessed ${formatDuration(now - session.lastAccessed)} ago, timeout ${formatDuration(this.SESSION_TIMEOUT)}`, LOG_MODULES.SESSION_MANAGER);
801
+ try {
802
+ session.server.close();
803
+ cleanedCount++;
804
+ }
805
+ catch (e) {
806
+ logger.error(`Error closing session ${sessionId}:`, e, LOG_MODULES.SESSION_MANAGER);
807
+ }
808
+ this.sessions.delete(sessionId);
809
+ }
810
+ }
811
+ // Cleanup persisted session states
812
+ const expiredSessionIds = [];
813
+ for (const [sessionId, session] of this.sessionStates.entries()) {
814
+ if (now - session.lastAccessedAt > this.SESSION_TIMEOUT) {
815
+ expiredSessionIds.push(sessionId);
816
+ cleanedCount++;
817
+ }
818
+ }
819
+ for (const sessionId of expiredSessionIds) {
820
+ this.sessionStates.delete(sessionId);
821
+ this.markAsDirty(sessionId);
822
+ logger.debug(`Marking expired session for deletion: ${sessionId}`, LOG_MODULES.SESSION_MANAGER);
823
+ }
824
+ // Immediately flush to delete session files from disk
825
+ if (expiredSessionIds.length > 0) {
826
+ this.flushDirtySessions().catch((error) => {
827
+ logger.error('Failed to flush expired sessions:', error, LOG_MODULES.SESSION_MANAGER);
828
+ });
829
+ }
830
+ if (cleanedCount > 0) {
831
+ logger.info(`Cleaned up ${cleanedCount} stale sessions. Active sessions: ${this.sessions.size}, Persisted states: ${this.sessionStates.size}`, LOG_MODULES.SESSION_MANAGER);
832
+ }
833
+ }
834
+ }
835
+ export const mcpSessionManager = new McpSessionManager();