@cyanheads/mcp-ts-core 0.11.5 → 0.12.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 (357) hide show
  1. package/AGENTS.md +27 -23
  2. package/CLAUDE.md +27 -23
  3. package/README.md +10 -10
  4. package/biome.json +1 -1
  5. package/changelog/0.12.x/0.12.0.md +96 -0
  6. package/dist/cli/init.js +1 -1
  7. package/dist/cli/init.js.map +1 -1
  8. package/dist/config/index.d.ts +0 -18
  9. package/dist/config/index.d.ts.map +1 -1
  10. package/dist/config/index.js +0 -25
  11. package/dist/config/index.js.map +1 -1
  12. package/dist/core/app.d.ts +5 -7
  13. package/dist/core/app.d.ts.map +1 -1
  14. package/dist/core/app.js +18 -27
  15. package/dist/core/app.js.map +1 -1
  16. package/dist/core/context.d.ts +90 -57
  17. package/dist/core/context.d.ts.map +1 -1
  18. package/dist/core/context.js +32 -33
  19. package/dist/core/context.js.map +1 -1
  20. package/dist/core/gcPressure.d.ts.map +1 -1
  21. package/dist/core/gcPressure.js +3 -4
  22. package/dist/core/gcPressure.js.map +1 -1
  23. package/dist/core/index.d.ts +6 -7
  24. package/dist/core/index.d.ts.map +1 -1
  25. package/dist/core/index.js +10 -2
  26. package/dist/core/index.js.map +1 -1
  27. package/dist/core/serverManifest.d.ts +1 -3
  28. package/dist/core/serverManifest.d.ts.map +1 -1
  29. package/dist/core/serverManifest.js +9 -4
  30. package/dist/core/serverManifest.js.map +1 -1
  31. package/dist/core/worker.d.ts.map +1 -1
  32. package/dist/core/worker.js +32 -35
  33. package/dist/core/worker.js.map +1 -1
  34. package/dist/logs/combined.log +10 -22
  35. package/dist/logs/error.log +6 -18
  36. package/dist/mcp-server/inputRequired.d.ts +52 -0
  37. package/dist/mcp-server/inputRequired.d.ts.map +1 -0
  38. package/dist/mcp-server/inputRequired.js +76 -0
  39. package/dist/mcp-server/inputRequired.js.map +1 -0
  40. package/dist/mcp-server/notifications.d.ts +33 -31
  41. package/dist/mcp-server/notifications.d.ts.map +1 -1
  42. package/dist/mcp-server/notifications.js +34 -26
  43. package/dist/mcp-server/notifications.js.map +1 -1
  44. package/dist/mcp-server/prompts/prompt-registration.d.ts +2 -2
  45. package/dist/mcp-server/prompts/prompt-registration.d.ts.map +1 -1
  46. package/dist/mcp-server/prompts/prompt-registration.js +15 -5
  47. package/dist/mcp-server/prompts/prompt-registration.js.map +1 -1
  48. package/dist/mcp-server/prompts/utils/promptDefinition.d.ts +1 -1
  49. package/dist/mcp-server/prompts/utils/promptDefinition.d.ts.map +1 -1
  50. package/dist/mcp-server/resources/resource-registration.d.ts +3 -3
  51. package/dist/mcp-server/resources/resource-registration.d.ts.map +1 -1
  52. package/dist/mcp-server/resources/resource-registration.js +8 -15
  53. package/dist/mcp-server/resources/resource-registration.js.map +1 -1
  54. package/dist/mcp-server/resources/resourceSubscriptions.d.ts +45 -0
  55. package/dist/mcp-server/resources/resourceSubscriptions.d.ts.map +1 -0
  56. package/dist/mcp-server/resources/resourceSubscriptions.js +51 -0
  57. package/dist/mcp-server/resources/resourceSubscriptions.js.map +1 -0
  58. package/dist/mcp-server/resources/utils/resourceDefinition.d.ts +3 -5
  59. package/dist/mcp-server/resources/utils/resourceDefinition.d.ts.map +1 -1
  60. package/dist/mcp-server/resources/utils/resourceDefinition.js +1 -1
  61. package/dist/mcp-server/resources/utils/resourceDefinition.js.map +1 -1
  62. package/dist/mcp-server/resources/utils/resourceHandlerFactory.d.ts +8 -15
  63. package/dist/mcp-server/resources/utils/resourceHandlerFactory.d.ts.map +1 -1
  64. package/dist/mcp-server/resources/utils/resourceHandlerFactory.js +21 -22
  65. package/dist/mcp-server/resources/utils/resourceHandlerFactory.js.map +1 -1
  66. package/dist/mcp-server/server.d.ts +12 -21
  67. package/dist/mcp-server/server.d.ts.map +1 -1
  68. package/dist/mcp-server/server.js +38 -28
  69. package/dist/mcp-server/server.js.map +1 -1
  70. package/dist/mcp-server/tools/tool-registration.d.ts +11 -33
  71. package/dist/mcp-server/tools/tool-registration.d.ts.map +1 -1
  72. package/dist/mcp-server/tools/tool-registration.js +17 -243
  73. package/dist/mcp-server/tools/tool-registration.js.map +1 -1
  74. package/dist/mcp-server/tools/utils/toolDefinition.d.ts +1 -3
  75. package/dist/mcp-server/tools/utils/toolDefinition.d.ts.map +1 -1
  76. package/dist/mcp-server/tools/utils/toolDefinition.js +48 -1
  77. package/dist/mcp-server/tools/utils/toolDefinition.js.map +1 -1
  78. package/dist/mcp-server/tools/utils/toolHandlerFactory.d.ts +46 -20
  79. package/dist/mcp-server/tools/utils/toolHandlerFactory.d.ts.map +1 -1
  80. package/dist/mcp-server/tools/utils/toolHandlerFactory.js +131 -25
  81. package/dist/mcp-server/tools/utils/toolHandlerFactory.js.map +1 -1
  82. package/dist/mcp-server/transports/ITransport.d.ts +2 -2
  83. package/dist/mcp-server/transports/ITransport.d.ts.map +1 -1
  84. package/dist/mcp-server/transports/auth/authFactory.js +1 -1
  85. package/dist/mcp-server/transports/auth/authFactory.js.map +1 -1
  86. package/dist/mcp-server/transports/auth/lib/authTypes.d.ts +1 -1
  87. package/dist/mcp-server/transports/auth/lib/authTypes.d.ts.map +1 -1
  88. package/dist/mcp-server/transports/auth/lib/authUtils.d.ts.map +1 -1
  89. package/dist/mcp-server/transports/auth/lib/authUtils.js +5 -13
  90. package/dist/mcp-server/transports/auth/lib/authUtils.js.map +1 -1
  91. package/dist/mcp-server/transports/auth/strategies/jwtStrategy.js +5 -11
  92. package/dist/mcp-server/transports/auth/strategies/jwtStrategy.js.map +1 -1
  93. package/dist/mcp-server/transports/auth/strategies/oauthStrategy.d.ts.map +1 -1
  94. package/dist/mcp-server/transports/auth/strategies/oauthStrategy.js +9 -22
  95. package/dist/mcp-server/transports/auth/strategies/oauthStrategy.js.map +1 -1
  96. package/dist/mcp-server/transports/heartbeat.d.ts.map +1 -1
  97. package/dist/mcp-server/transports/heartbeat.js +4 -8
  98. package/dist/mcp-server/transports/heartbeat.js.map +1 -1
  99. package/dist/mcp-server/transports/http/httpErrorHandler.d.ts.map +1 -1
  100. package/dist/mcp-server/transports/http/httpErrorHandler.js +6 -24
  101. package/dist/mcp-server/transports/http/httpErrorHandler.js.map +1 -1
  102. package/dist/mcp-server/transports/http/httpServer.d.ts +3 -3
  103. package/dist/mcp-server/transports/http/httpServer.d.ts.map +1 -1
  104. package/dist/mcp-server/transports/http/httpServer.js +15 -32
  105. package/dist/mcp-server/transports/http/httpServer.js.map +1 -1
  106. package/dist/mcp-server/transports/http/httpTransport.d.ts +25 -8
  107. package/dist/mcp-server/transports/http/httpTransport.d.ts.map +1 -1
  108. package/dist/mcp-server/transports/http/httpTransport.js +156 -373
  109. package/dist/mcp-server/transports/http/httpTransport.js.map +1 -1
  110. package/dist/mcp-server/transports/http/landing-page/assets/styles.d.ts.map +1 -1
  111. package/dist/mcp-server/transports/http/landing-page/assets/styles.js +0 -1
  112. package/dist/mcp-server/transports/http/landing-page/assets/styles.js.map +1 -1
  113. package/dist/mcp-server/transports/http/landing-page/handler.d.ts.map +1 -1
  114. package/dist/mcp-server/transports/http/landing-page/handler.js +4 -8
  115. package/dist/mcp-server/transports/http/landing-page/handler.js.map +1 -1
  116. package/dist/mcp-server/transports/http/landing-page/sections/tools.js +0 -2
  117. package/dist/mcp-server/transports/http/landing-page/sections/tools.js.map +1 -1
  118. package/dist/mcp-server/transports/http/protectedResourceMetadata.d.ts.map +1 -1
  119. package/dist/mcp-server/transports/http/protectedResourceMetadata.js +2 -6
  120. package/dist/mcp-server/transports/http/protectedResourceMetadata.js.map +1 -1
  121. package/dist/mcp-server/transports/http/robotsTxt.js +2 -2
  122. package/dist/mcp-server/transports/http/robotsTxt.js.map +1 -1
  123. package/dist/mcp-server/transports/http/serverCard.d.ts.map +1 -1
  124. package/dist/mcp-server/transports/http/serverCard.js +2 -6
  125. package/dist/mcp-server/transports/http/serverCard.js.map +1 -1
  126. package/dist/mcp-server/transports/http/sessionStore.d.ts +46 -66
  127. package/dist/mcp-server/transports/http/sessionStore.d.ts.map +1 -1
  128. package/dist/mcp-server/transports/http/sessionStore.js +140 -238
  129. package/dist/mcp-server/transports/http/sessionStore.js.map +1 -1
  130. package/dist/mcp-server/transports/manager.d.ts +12 -4
  131. package/dist/mcp-server/transports/manager.d.ts.map +1 -1
  132. package/dist/mcp-server/transports/manager.js +35 -33
  133. package/dist/mcp-server/transports/manager.js.map +1 -1
  134. package/dist/mcp-server/transports/stdio/stdioTransport.d.ts +24 -31
  135. package/dist/mcp-server/transports/stdio/stdioTransport.d.ts.map +1 -1
  136. package/dist/mcp-server/transports/stdio/stdioTransport.js +23 -38
  137. package/dist/mcp-server/transports/stdio/stdioTransport.js.map +1 -1
  138. package/dist/mcp-server/types.d.ts +25 -0
  139. package/dist/mcp-server/types.d.ts.map +1 -0
  140. package/dist/mcp-server/types.js +10 -0
  141. package/dist/mcp-server/types.js.map +1 -0
  142. package/dist/services/canvas/core/CanvasInstance.d.ts +2 -2
  143. package/dist/services/canvas/core/CanvasInstance.d.ts.map +1 -1
  144. package/dist/services/canvas/core/CanvasInstance.js.map +1 -1
  145. package/dist/services/canvas/core/CanvasRegistry.d.ts +4 -4
  146. package/dist/services/canvas/core/CanvasRegistry.d.ts.map +1 -1
  147. package/dist/services/canvas/core/CanvasRegistry.js +8 -18
  148. package/dist/services/canvas/core/CanvasRegistry.js.map +1 -1
  149. package/dist/services/canvas/core/DataCanvas.d.ts +5 -5
  150. package/dist/services/canvas/core/DataCanvas.d.ts.map +1 -1
  151. package/dist/services/canvas/core/DataCanvas.js +2 -3
  152. package/dist/services/canvas/core/DataCanvas.js.map +1 -1
  153. package/dist/services/canvas/core/IDataCanvasProvider.d.ts +11 -11
  154. package/dist/services/canvas/core/IDataCanvasProvider.d.ts.map +1 -1
  155. package/dist/services/canvas/providers/duckdb/DuckdbProvider.d.ts +11 -11
  156. package/dist/services/canvas/providers/duckdb/DuckdbProvider.d.ts.map +1 -1
  157. package/dist/services/canvas/providers/duckdb/DuckdbProvider.js +4 -10
  158. package/dist/services/canvas/providers/duckdb/DuckdbProvider.js.map +1 -1
  159. package/dist/services/llm/providers/openrouter.provider.d.ts.map +1 -1
  160. package/dist/services/llm/providers/openrouter.provider.js +2 -5
  161. package/dist/services/llm/providers/openrouter.provider.js.map +1 -1
  162. package/dist/services/mirror/core/defineMirror.js +1 -1
  163. package/dist/services/mirror/core/defineMirror.js.map +1 -1
  164. package/dist/services/mirror/types.d.ts +5 -5
  165. package/dist/services/mirror/types.d.ts.map +1 -1
  166. package/dist/services/speech/providers/elevenlabs.provider.js +1 -1
  167. package/dist/services/speech/providers/elevenlabs.provider.js.map +1 -1
  168. package/dist/services/speech/providers/whisper.provider.js +1 -1
  169. package/dist/services/speech/providers/whisper.provider.js.map +1 -1
  170. package/dist/storage/core/StorageService.d.ts +1 -1
  171. package/dist/storage/core/StorageService.d.ts.map +1 -1
  172. package/dist/storage/core/StorageService.js +12 -20
  173. package/dist/storage/core/StorageService.js.map +1 -1
  174. package/dist/storage/core/storageFactory.d.ts.map +1 -1
  175. package/dist/storage/core/storageFactory.js.map +1 -1
  176. package/dist/storage/core/storageValidation.d.ts +1 -1
  177. package/dist/storage/core/storageValidation.d.ts.map +1 -1
  178. package/dist/storage/core/storageValidation.js +2 -2
  179. package/dist/storage/core/storageValidation.js.map +1 -1
  180. package/dist/storage/providers/cloudflare/d1Provider.d.ts +1 -1
  181. package/dist/storage/providers/cloudflare/d1Provider.d.ts.map +1 -1
  182. package/dist/storage/providers/cloudflare/d1Provider.js +4 -12
  183. package/dist/storage/providers/cloudflare/d1Provider.js.map +1 -1
  184. package/dist/storage/providers/cloudflare/kvProvider.d.ts +1 -1
  185. package/dist/storage/providers/cloudflare/kvProvider.d.ts.map +1 -1
  186. package/dist/storage/providers/cloudflare/kvProvider.js +3 -8
  187. package/dist/storage/providers/cloudflare/kvProvider.js.map +1 -1
  188. package/dist/storage/providers/cloudflare/r2Provider.d.ts +1 -1
  189. package/dist/storage/providers/cloudflare/r2Provider.d.ts.map +1 -1
  190. package/dist/storage/providers/cloudflare/r2Provider.js +3 -8
  191. package/dist/storage/providers/cloudflare/r2Provider.js.map +1 -1
  192. package/dist/storage/providers/inMemory/inMemoryProvider.d.ts +1 -1
  193. package/dist/storage/providers/inMemory/inMemoryProvider.d.ts.map +1 -1
  194. package/dist/storage/providers/inMemory/inMemoryProvider.js +2 -4
  195. package/dist/storage/providers/inMemory/inMemoryProvider.js.map +1 -1
  196. package/dist/testing/index.d.ts +35 -12
  197. package/dist/testing/index.d.ts.map +1 -1
  198. package/dist/testing/index.js +53 -38
  199. package/dist/testing/index.js.map +1 -1
  200. package/dist/testing/vitest.d.ts +1 -1
  201. package/dist/testing/vitest.d.ts.map +1 -1
  202. package/dist/types-global/errors.d.ts +28 -15
  203. package/dist/types-global/errors.d.ts.map +1 -1
  204. package/dist/types-global/errors.js +8 -1
  205. package/dist/types-global/errors.js.map +1 -1
  206. package/dist/utils/formatting/diffFormatter.d.ts +5 -5
  207. package/dist/utils/formatting/diffFormatter.d.ts.map +1 -1
  208. package/dist/utils/formatting/diffFormatter.js +7 -20
  209. package/dist/utils/formatting/diffFormatter.js.map +1 -1
  210. package/dist/utils/formatting/tableFormatter.d.ts +3 -3
  211. package/dist/utils/formatting/tableFormatter.d.ts.map +1 -1
  212. package/dist/utils/formatting/tableFormatter.js +6 -17
  213. package/dist/utils/formatting/tableFormatter.js.map +1 -1
  214. package/dist/utils/formatting/treeFormatter.d.ts +3 -3
  215. package/dist/utils/formatting/treeFormatter.d.ts.map +1 -1
  216. package/dist/utils/formatting/treeFormatter.js +5 -18
  217. package/dist/utils/formatting/treeFormatter.js.map +1 -1
  218. package/dist/utils/index.d.ts +4 -2
  219. package/dist/utils/index.d.ts.map +1 -1
  220. package/dist/utils/index.js +2 -2
  221. package/dist/utils/index.js.map +1 -1
  222. package/dist/utils/internal/error-handler/errorHandler.d.ts +1 -1
  223. package/dist/utils/internal/error-handler/errorHandler.d.ts.map +1 -1
  224. package/dist/utils/internal/error-handler/errorHandler.js +23 -11
  225. package/dist/utils/internal/error-handler/errorHandler.js.map +1 -1
  226. package/dist/utils/internal/error-handler/types.d.ts +10 -13
  227. package/dist/utils/internal/error-handler/types.d.ts.map +1 -1
  228. package/dist/utils/internal/logger.d.ts +3 -3
  229. package/dist/utils/internal/logger.d.ts.map +1 -1
  230. package/dist/utils/internal/logger.js +11 -5
  231. package/dist/utils/internal/logger.js.map +1 -1
  232. package/dist/utils/internal/performance.d.ts +1 -1
  233. package/dist/utils/internal/performance.d.ts.map +1 -1
  234. package/dist/utils/internal/performance.js +57 -13
  235. package/dist/utils/internal/performance.js.map +1 -1
  236. package/dist/utils/internal/requestContext.d.ts +79 -55
  237. package/dist/utils/internal/requestContext.d.ts.map +1 -1
  238. package/dist/utils/internal/requestContext.js +77 -32
  239. package/dist/utils/internal/requestContext.js.map +1 -1
  240. package/dist/utils/metrics/tokenCounter.d.ts +3 -3
  241. package/dist/utils/metrics/tokenCounter.d.ts.map +1 -1
  242. package/dist/utils/metrics/tokenCounter.js.map +1 -1
  243. package/dist/utils/network/fetchWithTimeout.d.ts +2 -2
  244. package/dist/utils/network/fetchWithTimeout.d.ts.map +1 -1
  245. package/dist/utils/network/fetchWithTimeout.js +8 -24
  246. package/dist/utils/network/fetchWithTimeout.js.map +1 -1
  247. package/dist/utils/network/retry.d.ts +2 -2
  248. package/dist/utils/network/retry.d.ts.map +1 -1
  249. package/dist/utils/overflow/outlineOnOverflow.d.ts +1 -1
  250. package/dist/utils/overflow/outlineOnOverflow.d.ts.map +1 -1
  251. package/dist/utils/pagination/pagination.d.ts +3 -3
  252. package/dist/utils/pagination/pagination.d.ts.map +1 -1
  253. package/dist/utils/pagination/pagination.js +3 -3
  254. package/dist/utils/pagination/pagination.js.map +1 -1
  255. package/dist/utils/parsing/csvParser.d.ts +2 -2
  256. package/dist/utils/parsing/csvParser.d.ts.map +1 -1
  257. package/dist/utils/parsing/csvParser.js +4 -8
  258. package/dist/utils/parsing/csvParser.js.map +1 -1
  259. package/dist/utils/parsing/dateParser.d.ts +3 -3
  260. package/dist/utils/parsing/dateParser.d.ts.map +1 -1
  261. package/dist/utils/parsing/dateParser.js +3 -2
  262. package/dist/utils/parsing/dateParser.js.map +1 -1
  263. package/dist/utils/parsing/frontmatterParser.d.ts +2 -2
  264. package/dist/utils/parsing/frontmatterParser.d.ts.map +1 -1
  265. package/dist/utils/parsing/frontmatterParser.js +7 -10
  266. package/dist/utils/parsing/frontmatterParser.js.map +1 -1
  267. package/dist/utils/parsing/htmlExtractor.d.ts +2 -2
  268. package/dist/utils/parsing/htmlExtractor.d.ts.map +1 -1
  269. package/dist/utils/parsing/htmlExtractor.js +6 -11
  270. package/dist/utils/parsing/htmlExtractor.js.map +1 -1
  271. package/dist/utils/parsing/jsonParser.d.ts +2 -2
  272. package/dist/utils/parsing/jsonParser.d.ts.map +1 -1
  273. package/dist/utils/parsing/jsonParser.js +4 -8
  274. package/dist/utils/parsing/jsonParser.js.map +1 -1
  275. package/dist/utils/parsing/pdfParser.d.ts +10 -10
  276. package/dist/utils/parsing/pdfParser.d.ts.map +1 -1
  277. package/dist/utils/parsing/pdfParser.js +26 -89
  278. package/dist/utils/parsing/pdfParser.js.map +1 -1
  279. package/dist/utils/parsing/xmlParser.d.ts +2 -2
  280. package/dist/utils/parsing/xmlParser.d.ts.map +1 -1
  281. package/dist/utils/parsing/xmlParser.js +4 -8
  282. package/dist/utils/parsing/xmlParser.js.map +1 -1
  283. package/dist/utils/parsing/yamlParser.d.ts +2 -2
  284. package/dist/utils/parsing/yamlParser.d.ts.map +1 -1
  285. package/dist/utils/parsing/yamlParser.js +4 -8
  286. package/dist/utils/parsing/yamlParser.js.map +1 -1
  287. package/dist/utils/scheduling/scheduler.d.ts.map +1 -1
  288. package/dist/utils/scheduling/scheduler.js +5 -6
  289. package/dist/utils/scheduling/scheduler.js.map +1 -1
  290. package/dist/utils/security/rateLimiter.d.ts +2 -2
  291. package/dist/utils/security/rateLimiter.d.ts.map +1 -1
  292. package/dist/utils/security/rateLimiter.js +1 -1
  293. package/dist/utils/security/rateLimiter.js.map +1 -1
  294. package/dist/utils/telemetry/attributes.d.ts +23 -7
  295. package/dist/utils/telemetry/attributes.d.ts.map +1 -1
  296. package/dist/utils/telemetry/attributes.js +23 -10
  297. package/dist/utils/telemetry/attributes.js.map +1 -1
  298. package/dist/utils/telemetry/trace.d.ts +3 -3
  299. package/dist/utils/telemetry/trace.d.ts.map +1 -1
  300. package/dist/utils/telemetry/trace.js +4 -2
  301. package/dist/utils/telemetry/trace.js.map +1 -1
  302. package/package.json +22 -26
  303. package/scripts/lint-mcp.ts +1 -3
  304. package/scripts/lint-packaging.ts +1 -1
  305. package/skills/add-service/SKILL.md +2 -2
  306. package/skills/add-test/SKILL.md +42 -20
  307. package/skills/add-tool/SKILL.md +68 -19
  308. package/skills/api-config/SKILL.md +1 -11
  309. package/skills/api-context/SKILL.md +175 -111
  310. package/skills/api-linter/SKILL.md +2 -2
  311. package/skills/api-telemetry/SKILL.md +8 -12
  312. package/skills/api-testing/SKILL.md +53 -52
  313. package/skills/code-simplifier/SKILL.md +2 -2
  314. package/skills/design-mcp-server/SKILL.md +31 -17
  315. package/skills/field-test/SKILL.md +4 -2
  316. package/skills/polish-docs-meta/SKILL.md +1 -1
  317. package/skills/polish-docs-meta/references/agent-protocol.md +2 -2
  318. package/skills/polish-docs-meta/references/readme.md +1 -1
  319. package/skills/release-and-publish/SKILL.md +3 -1
  320. package/skills/report-issue-framework/SKILL.md +2 -2
  321. package/skills/report-issue-local/SKILL.md +2 -2
  322. package/skills/security-pass/SKILL.md +16 -14
  323. package/templates/AGENTS.md +6 -6
  324. package/templates/CLAUDE.md +6 -6
  325. package/templates/tests/resources/echo.resource.test.ts +16 -9
  326. package/dist/mcp-server/elicitation.d.ts +0 -64
  327. package/dist/mcp-server/elicitation.d.ts.map +0 -1
  328. package/dist/mcp-server/elicitation.js +0 -81
  329. package/dist/mcp-server/elicitation.js.map +0 -1
  330. package/dist/mcp-server/protocolSession.d.ts +0 -34
  331. package/dist/mcp-server/protocolSession.d.ts.map +0 -1
  332. package/dist/mcp-server/protocolSession.js +0 -13
  333. package/dist/mcp-server/protocolSession.js.map +0 -1
  334. package/dist/mcp-server/tasks/core/evictingTaskMessageQueue.d.ts +0 -53
  335. package/dist/mcp-server/tasks/core/evictingTaskMessageQueue.d.ts.map +0 -1
  336. package/dist/mcp-server/tasks/core/evictingTaskMessageQueue.js +0 -65
  337. package/dist/mcp-server/tasks/core/evictingTaskMessageQueue.js.map +0 -1
  338. package/dist/mcp-server/tasks/core/sessionAwareTaskStore.d.ts +0 -70
  339. package/dist/mcp-server/tasks/core/sessionAwareTaskStore.d.ts.map +0 -1
  340. package/dist/mcp-server/tasks/core/sessionAwareTaskStore.js +0 -130
  341. package/dist/mcp-server/tasks/core/sessionAwareTaskStore.js.map +0 -1
  342. package/dist/mcp-server/tasks/core/storageBackedTaskStore.d.ts +0 -109
  343. package/dist/mcp-server/tasks/core/storageBackedTaskStore.d.ts.map +0 -1
  344. package/dist/mcp-server/tasks/core/storageBackedTaskStore.js +0 -209
  345. package/dist/mcp-server/tasks/core/storageBackedTaskStore.js.map +0 -1
  346. package/dist/mcp-server/tasks/core/taskManager.d.ts +0 -91
  347. package/dist/mcp-server/tasks/core/taskManager.d.ts.map +0 -1
  348. package/dist/mcp-server/tasks/core/taskManager.js +0 -210
  349. package/dist/mcp-server/tasks/core/taskManager.js.map +0 -1
  350. package/dist/mcp-server/tasks/core/taskTypes.d.ts +0 -18
  351. package/dist/mcp-server/tasks/core/taskTypes.d.ts.map +0 -1
  352. package/dist/mcp-server/tasks/core/taskTypes.js +0 -20
  353. package/dist/mcp-server/tasks/core/taskTypes.js.map +0 -1
  354. package/dist/mcp-server/tasks/utils/taskToolDefinition.d.ts +0 -101
  355. package/dist/mcp-server/tasks/utils/taskToolDefinition.d.ts.map +0 -1
  356. package/dist/mcp-server/tasks/utils/taskToolDefinition.js +0 -14
  357. package/dist/mcp-server/tasks/utils/taskToolDefinition.js.map +0 -1
@@ -1,16 +1,27 @@
1
1
  /**
2
2
  * @fileoverview Worker-safe Hono application factory for the HTTP MCP transport.
3
- * Builds the routing, auth, session, and per-request server lifecycle wiring
4
- * using only web-standard APIs. The Node-only `serve()` start path lives in
5
- * `httpServer.ts` so the Worker bundle drops `@hono/node-server` and
6
- * `node:http` via tree-shaking.
3
+ * Builds the routing, auth, session, and serving wiring using only web-standard
4
+ * APIs. The Node-only `serve()` start path lives in `httpServer.ts` so the
5
+ * Worker bundle drops `@hono/node-server` and `node:http` via tree-shaking.
7
6
  *
8
- * Implements MCP Specification 2025-06-18 Streamable HTTP Transport.
9
- * @see {@link https://modelcontextprotocol.io/specification/2025-06-18/basic/transports#streamable-http | MCP Streamable HTTP Transport}
7
+ * Serving is split by session mode:
8
+ *
9
+ * - **stateless** — one `createMcpHandler(factory)` answers both protocol eras
10
+ * per request (its default `legacy: 'stateless'` posture).
11
+ * - **stateful / auto** — `isLegacyRequest(request)` splits the traffic. 2025-era
12
+ * requests go to a sessionful `WebStandardStreamableHTTPServerTransport` with
13
+ * one persistent `McpServer` per `Mcp-Session-Id`; 2026-07-28 requests go to a
14
+ * strict (`legacy: 'reject'`) modern handler, which is per-request by
15
+ * construction — that revision has no session.
16
+ *
17
+ * The sessionful arm is load-bearing rather than legacy courtesy: the SDK's
18
+ * multi-round-trip legacy shim needs a live session, so dropping it would break
19
+ * interactive tools for every 2025 client that is not on stdio.
20
+ *
21
+ * @see {@link https://modelcontextprotocol.io/specification/2026-07-28/basic/transports | MCP Transports}
10
22
  * @module src/mcp-server/transports/http/httpTransport
11
23
  */
12
- import { StreamableHTTPTransport } from '@hono/mcp';
13
- import { isJSONRPCErrorResponse, isJSONRPCRequest, isJSONRPCResultResponse, } from '@modelcontextprotocol/sdk/types.js';
24
+ import { createMcpHandler, isJsonContentType, isLegacyRequest, WebStandardStreamableHTTPServerTransport, } from '@modelcontextprotocol/server';
14
25
  import { Hono } from 'hono';
15
26
  import { cors } from 'hono/cors';
16
27
  import { config } from '../../../config/index.js';
@@ -23,151 +34,18 @@ import { protectedResourceMetadataHandler } from '../../../mcp-server/transports
23
34
  import { createRobotsTxtHandler } from '../../../mcp-server/transports/http/robotsTxt.js';
24
35
  import { createServerCardHandler } from '../../../mcp-server/transports/http/serverCard.js';
25
36
  import { generateSecureSessionId } from '../../../mcp-server/transports/http/sessionIdUtils.js';
26
- import { SessionStore, } from '../../../mcp-server/transports/http/sessionStore.js';
37
+ import { closeConnection, SessionStore, } from '../../../mcp-server/transports/http/sessionStore.js';
27
38
  import { logger } from '../../../utils/internal/logger.js';
28
- import { requestContextService } from '../../../utils/internal/requestContext.js';
29
- import { createCounter, createObservableGauge } from '../../../utils/telemetry/metrics.js';
30
- /**
31
- * Extends the base StreamableHTTPTransport to include a session ID.
32
- */
33
- class McpSessionTransport extends StreamableHTTPTransport {
34
- sessionStore;
35
- sessionId;
36
- pendingServerRequests = new Set();
37
- constructor(sessionId, sessionStore) {
38
- super();
39
- this.sessionStore = sessionStore;
40
- this.sessionId = sessionId;
41
- }
42
- /**
43
- * Rewrites outbound server-request IDs into the durable session namespace.
44
- * Every per-request SDK Server starts its request counter at zero; without
45
- * this bridge concurrent elicitations collide and a response POST reaches a
46
- * different Server instance than the one holding the response handler.
47
- */
48
- async send(message, options) {
49
- if (!isJSONRPCRequest(message) || !this.sessionStore) {
50
- await super.send(message, options);
51
- return;
52
- }
53
- const registration = this.sessionStore.registerServerRequest(this.sessionId, (response) => {
54
- this.settleServerRequest(registration);
55
- this.onmessage?.({ ...response, id: message.id });
56
- });
57
- if (!registration)
58
- throw new Error('Cannot send a server request after its session ended.');
59
- this.pendingServerRequests.add(registration);
60
- try {
61
- await super.send({ ...message, id: registration.requestId }, options);
62
- }
63
- catch (error) {
64
- this.settleServerRequest(registration);
65
- throw error;
66
- }
67
- }
68
- async close() {
69
- for (const registration of this.pendingServerRequests)
70
- registration.unregister();
71
- this.pendingServerRequests.clear();
72
- await super.close();
73
- }
74
- /** Drops a route from both the session registry and this transport's pending set. */
75
- settleServerRequest(registration) {
76
- if (!registration)
77
- return;
78
- registration.unregister();
79
- this.pendingServerRequests.delete(registration);
80
- }
81
- }
82
- /** Max time (ms) we wait for per-request transport/server close to complete
83
- * before giving up and logging a failure. Silent hangs here accumulate
84
- * retained closures — bounded wait surfaces the issue as a metric. */
85
- const PER_REQUEST_CLOSE_TIMEOUT_MS = 5_000;
39
+ import { requestContextService, withExtra, } from '../../../utils/internal/requestContext.js';
40
+ import { createObservableGauge } from '../../../utils/telemetry/metrics.js';
86
41
  /** Matches loopback origins (http(s)://localhost|127.0.0.1|[::1] with optional port).
87
42
  * Used as the fail-closed default for the Origin guard when no explicit
88
43
  * MCP_ALLOWED_ORIGINS is configured — an unauthenticated MCP server must not
89
- * accept browser Origin headers from arbitrary hosts (MCP Spec 2025-06-18 DNS
90
- * rebinding protection). */
44
+ * accept browser Origin headers from arbitrary hosts (DNS rebinding
45
+ * protection). The SDK's `validateOriginHeader` matches bare hostnames from a
46
+ * fixed list and so cannot express "any loopback port", which is exactly the
47
+ * dev-server case this guard exists for. */
91
48
  const LOOPBACK_ORIGIN_RE = /^https?:\/\/(?:localhost|127\.0\.0\.1|\[::1\])(?::\d+)?$/i;
92
- let closeFailureCounter;
93
- function getCloseFailureCounter() {
94
- closeFailureCounter ??= createCounter('mcp.http.close_failures', 'Per-request HTTP close failures (transport or server close threw or timed out)', '{failures}');
95
- return closeFailureCounter;
96
- }
97
- // ---------------------------------------------------------------------------
98
- // Finalization-lag diagnostic (issue #50)
99
- //
100
- // The HTTP transport constructs a fresh `McpServer` + `McpSessionTransport`
101
- // per request (SDK 1.26 GHSA-345p-7cg4-v4c7). Production heap graphs show
102
- // linear growth proportional to request volume, which suggests some of
103
- // these per-request instances are being retained past their scope.
104
- //
105
- // Compare `mcp.http.per_request.created_total` against
106
- // `mcp.http.per_request.finalized_total` (both tagged with `kind`) to
107
- // detect retention. A persistent gap that grows with traffic is the
108
- // signature of the leak; a gap that closes within minutes is just GC
109
- // timing.
110
- // ---------------------------------------------------------------------------
111
- let perRequestCreatedCounter;
112
- let perRequestFinalizedCounter;
113
- function getPerRequestCreatedCounter() {
114
- perRequestCreatedCounter ??= createCounter('mcp.http.per_request.created', 'Per-request McpServer/McpSessionTransport instances created', '{instances}');
115
- return perRequestCreatedCounter;
116
- }
117
- function getPerRequestFinalizedCounter() {
118
- perRequestFinalizedCounter ??= createCounter('mcp.http.per_request.finalized', 'Per-request McpServer/McpSessionTransport instances reclaimed by GC', '{instances}');
119
- return perRequestFinalizedCounter;
120
- }
121
- /** Lazy so the registry only exists when HTTP transport is active. The
122
- * FinalizationRegistry callback itself does not run during this process's
123
- * exit teardown — so we deliberately don't try to drain it. */
124
- let perRequestFinalizationRegistry;
125
- function getPerRequestFinalizationRegistry() {
126
- perRequestFinalizationRegistry ??= new FinalizationRegistry((kind) => {
127
- getPerRequestFinalizedCounter().add(1, { kind });
128
- });
129
- return perRequestFinalizationRegistry;
130
- }
131
- function trackPerRequestInstance(obj, kind) {
132
- getPerRequestCreatedCounter().add(1, { kind });
133
- getPerRequestFinalizationRegistry().register(obj, kind);
134
- }
135
- /** Await a promise with a bounded timeout. Rejects with the timeout error
136
- * if the promise doesn't settle first. */
137
- function withTimeout(p, ms, label) {
138
- let timer;
139
- return Promise.race([
140
- p,
141
- new Promise((_, reject) => {
142
- timer = setTimeout(() => reject(new Error(`${label} timed out after ${ms}ms`)), ms);
143
- }),
144
- ]).finally(() => {
145
- if (timer !== undefined)
146
- clearTimeout(timer);
147
- });
148
- }
149
- /** Closes the per-request transport and server under a bounded timeout.
150
- * Runs the two closes in parallel so one hanging close doesn't delay the
151
- * other, and records each failure to `mcp.http.close_failures` with
152
- * `surface` and `trigger` tags. */
153
- async function closePerRequestInstances(transport, server, sessionId, requestContext, trigger) {
154
- const tasks = [['transport', () => transport.close()]];
155
- if (server)
156
- tasks.push(['server', () => server.close()]);
157
- await Promise.all(tasks.map(async ([surface, close]) => {
158
- try {
159
- await withTimeout(close(), PER_REQUEST_CLOSE_TIMEOUT_MS, `${surface}.close`);
160
- }
161
- catch (err) {
162
- getCloseFailureCounter().add(1, { surface, trigger });
163
- logger.warning(`Failed to close ${surface} (trigger=${trigger})`, {
164
- ...requestContext,
165
- sessionId,
166
- error: err instanceof Error ? err.message : String(err),
167
- });
168
- }
169
- }));
170
- }
171
49
  /** Reads at most one stream chunk beyond `maxBytes`, cancelling as soon as the
172
50
  * limit is crossed. Fetch body chunks are not size-bounded by the consumer, so
173
51
  * the final chunk may itself be larger than the remaining allowance. */
@@ -218,10 +96,7 @@ async function readBodyWithinLimit(request, maxBytes) {
218
96
  */
219
97
  export async function createHttpApp(serverFactory, parentContext, manifest) {
220
98
  const app = new Hono();
221
- const transportContext = {
222
- ...parentContext,
223
- component: 'HttpTransportSetup',
224
- };
99
+ const transportContext = withExtra(parentContext, { component: 'HttpTransportSetup' });
225
100
  // Initialize session store for stateful mode.
226
101
  // 'auto' resolves to stateful for HTTP (per MCP spec conformance).
227
102
  const isStateful = config.mcpSessionMode === 'stateful' || config.mcpSessionMode === 'auto';
@@ -298,13 +173,12 @@ export async function createHttpApp(serverFactory, parentContext, manifest) {
298
173
  if (!isAllowed) {
299
174
  const requestContext = requestContextService.createRequestContext({
300
175
  operation: 'HttpOriginGuard',
301
- component: 'HttpTransport',
176
+ additionalContext: { component: 'HttpTransport' },
302
177
  });
303
- logger.warning('Rejected request with invalid Origin header', {
304
- ...requestContext,
178
+ logger.warning('Rejected request with invalid Origin header', withExtra(requestContext, {
305
179
  origin,
306
180
  allowedOrigins: explicitOrigins ?? 'loopback-only',
307
- });
181
+ }));
308
182
  return c.json({ error: 'Invalid origin. DNS rebinding protection.' }, 403);
309
183
  }
310
184
  }
@@ -329,13 +203,9 @@ export async function createHttpApp(serverFactory, parentContext, manifest) {
329
203
  const rejectOversized = (bytes) => {
330
204
  const requestContext = requestContextService.createRequestContext({
331
205
  operation: 'HttpBodyLimit',
332
- component: 'HttpTransport',
333
- });
334
- logger.warning('Rejected request exceeding body size limit', {
335
- ...requestContext,
336
- bytes,
337
- maxBodyBytes,
206
+ additionalContext: { component: 'HttpTransport' },
338
207
  });
208
+ logger.warning('Rejected request exceeding body size limit', withExtra(requestContext, { bytes, maxBodyBytes }));
339
209
  return c.json({
340
210
  error: `Request body exceeds the ${maxBodyBytes}-byte limit (configurable via MCP_HTTP_MAX_BODY_BYTES).`,
341
211
  }, 413);
@@ -488,246 +358,159 @@ export async function createHttpApp(serverFactory, parentContext, manifest) {
488
358
  subject: authInfo.subject,
489
359
  }).filter(([, v]) => v != null));
490
360
  }
491
- // MCP Spec 2025-06-18: DELETE endpoint for session termination
492
- // Clients SHOULD send DELETE to explicitly terminate sessions
493
- // https://modelcontextprotocol.io/specification/2025-06-18/basic/transports#session-management
494
- app.delete(config.mcpHttpEndpointPath, (c) => {
361
+ // DELETE terminates a session. Ownership is validated here, before the SDK
362
+ // transport is allowed to tear the session down; the transport itself owns
363
+ // the spec-shaped response and fires `onsessionclosed`.
364
+ // https://modelcontextprotocol.io/specification/2026-07-28/basic/transports
365
+ app.delete(config.mcpHttpEndpointPath, async (c, next) => {
495
366
  const requestContext = requestContextService.createRequestContext({
496
367
  operation: 'HttpSessionTermination',
497
- component: 'HttpTransport',
368
+ additionalContext: { component: 'HttpTransport' },
498
369
  });
499
370
  const sessionId = c.req.header('mcp-session-id');
371
+ // Stateless mode has no session to terminate — the stateless handler
372
+ // answers with the spec's 405.
373
+ if (!sessionStore)
374
+ return await next();
500
375
  if (!sessionId) {
501
376
  logger.warning('DELETE request without session ID', requestContext);
502
377
  return c.json({ error: 'Mcp-Session-Id header required' }, 400);
503
378
  }
504
- logger.info('Session termination requested', {
505
- ...requestContext,
506
- sessionId,
507
- });
508
- // For stateless mode or if session management is disabled, return 405
509
- if (config.mcpSessionMode === 'stateless' || !sessionStore) {
510
- return c.json({ error: 'Session termination not supported in stateless mode' }, 405);
511
- }
512
- // SECURITY: Validate session ownership before termination
513
- const sessionIdentity = extractSessionIdentity();
514
- if (!sessionStore.isValidForIdentity(sessionId, sessionIdentity)) {
379
+ logger.info('Session termination requested', { ...requestContext, sessionId });
380
+ // SECURITY: validate session ownership before termination.
381
+ if (!sessionStore.isValidForIdentity(sessionId, extractSessionIdentity())) {
515
382
  logger.warning('Session termination rejected - ownership validation failed', {
516
383
  ...requestContext,
517
384
  sessionId,
518
- requestTenant: sessionIdentity?.tenantId,
519
- requestClient: sessionIdentity?.clientId,
520
385
  });
521
386
  return c.json({ error: 'Session not found or access denied' }, 404);
522
387
  }
523
- // Terminate the session in the store
524
- sessionStore.terminate(sessionId);
525
- logger.info('Session terminated successfully', {
526
- ...requestContext,
527
- sessionId,
528
- });
529
- return c.json({ status: 'terminated', sessionId }, 200);
388
+ const connection = sessionStore.getConnection(sessionId);
389
+ if (!connection) {
390
+ // Identity-valid but no live connection (already torn down). Drop the
391
+ // record and answer exactly as the transport does below — one status and
392
+ // one body shape for one operation.
393
+ await sessionStore.terminate(sessionId);
394
+ return c.body(null, 200);
395
+ }
396
+ return await connection.transport.handleRequest(c.req.raw);
530
397
  });
398
+ // -------------------------------------------------------------------------
531
399
  // JSON-RPC over HTTP (Streamable)
400
+ // -------------------------------------------------------------------------
401
+ /** Validated auth info for pass-through to the SDK's `ctx.http.authInfo`. */
402
+ const currentAuthInfo = () => authContext.getStore()?.authInfo;
403
+ // The modern (2026-07-28) leg. In stateless mode the same handler also serves
404
+ // 2025-era traffic per request (`legacy: 'stateless'`, the default); in
405
+ // stateful mode it is strict and the sessionful arm below owns 2025.
406
+ const handler = createMcpHandler(serverFactory, {
407
+ ...(isStateful && { legacy: 'reject' }),
408
+ onerror: (error) => {
409
+ logger.debug(`MCP handler reported: ${error.message}`, transportContext);
410
+ },
411
+ });
412
+ /** Serves one 2025-era request on the sessionful arm. */
413
+ const handleLegacySessionful = async (store, request, parsedBody, requestContext) => {
414
+ const authInfo = currentAuthInfo();
415
+ const identity = extractSessionIdentity();
416
+ const providedSessionId = request.headers.get('mcp-session-id') ?? undefined;
417
+ const options = {
418
+ ...(parsedBody !== undefined && { parsedBody }),
419
+ ...(authInfo && { authInfo }),
420
+ };
421
+ if (providedSessionId) {
422
+ // SECURITY: identity binding is checked before the session is reachable,
423
+ // so a stolen session ID alone cannot resume someone else's session.
424
+ if (!store.isValidForIdentity(providedSessionId, identity)) {
425
+ logger.warning('Session validation failed - invalid or hijacked session', {
426
+ ...requestContext,
427
+ sessionId: providedSessionId,
428
+ });
429
+ return Response.json({ error: 'Session not found or expired' }, { status: 404 });
430
+ }
431
+ const connection = store.getConnection(providedSessionId);
432
+ if (!connection) {
433
+ return Response.json({ error: 'Session not found or expired' }, { status: 404 });
434
+ }
435
+ return await connection.transport.handleRequest(request, options);
436
+ }
437
+ // No session yet: this is an `initialize` (anything else is rejected by the
438
+ // SDK transport with the spec's 400). Capacity is checked before the
439
+ // instance is built so a saturated server never allocates one.
440
+ store.assertCapacity();
441
+ const server = await serverFactory({
442
+ era: 'legacy',
443
+ ...(authInfo && { authInfo }),
444
+ requestInfo: request,
445
+ });
446
+ const transport = new WebStandardStreamableHTTPServerTransport({
447
+ sessionIdGenerator: generateSecureSessionId,
448
+ onsessioninitialized: (sessionId) => {
449
+ store.register(sessionId, { server, transport }, identity);
450
+ logger.debug('Session initialized', { ...requestContext, sessionId });
451
+ },
452
+ onsessionclosed: (sessionId) => store.terminate(sessionId),
453
+ });
454
+ await server.connect(transport);
455
+ try {
456
+ const response = await transport.handleRequest(request, options);
457
+ // A handshake the transport refused never mints a session, so the
458
+ // instance pair it would have owned has no owner — close it here.
459
+ if (!transport.sessionId)
460
+ await closeConnection({ server, transport });
461
+ return response;
462
+ }
463
+ catch (error) {
464
+ await closeConnection({ server, transport });
465
+ throw error;
466
+ }
467
+ };
532
468
  app.all(config.mcpHttpEndpointPath, async (c) => {
533
469
  const requestContext = requestContextService.createRequestContext({
534
470
  operation: 'HttpRpcRequest',
535
- component: 'HttpTransport',
536
- });
537
- const protocolVersion = c.req.header('mcp-protocol-version') ?? '2025-03-26';
538
- logger.debug('Handling MCP request.', {
539
- ...requestContext,
540
- path: c.req.path,
541
- method: c.req.method,
542
- protocolVersion,
471
+ additionalContext: { component: 'HttpTransport' },
543
472
  });
544
- // Per MCP Spec 2025-06-18: MCP-Protocol-Version header MUST be validated
545
- // Server MUST respond with 400 Bad Request for unsupported versions
546
- // We default to 2025-03-26 for backward compatibility if not provided
547
- const supportedVersions = manifest.protocol.supportedVersions;
548
- if (!supportedVersions.includes(protocolVersion)) {
549
- logger.warning('Unsupported MCP protocol version requested.', {
550
- ...requestContext,
551
- protocolVersion,
552
- supportedVersions,
553
- });
554
- return c.json({
555
- error: 'Unsupported MCP protocol version',
556
- protocolVersion,
557
- supportedVersions,
558
- }, 400);
473
+ logger.debug('Handling MCP request.', withExtra(requestContext, { path: c.req.path, method: c.req.method }));
474
+ // Media type before body: a POST whose Content-Type is not JSON is a 415,
475
+ // not a parse error, and the SDK answers it that way. Parsing first turns
476
+ // even a well-formed body into a misleading `-32700`, so hand those
477
+ // straight to the handler and let it produce the canonical response.
478
+ if (c.req.method === 'POST' && !isJsonContentType(c.req.header('content-type'))) {
479
+ const authInfo = currentAuthInfo();
480
+ return await handler.fetch(c.req.raw, { ...(authInfo && { authInfo }) });
559
481
  }
560
- const providedSessionId = c.req.header('mcp-session-id');
561
- // Parse once for the small amount of protocol state the per-request SDK
562
- // server cannot retain itself. Hono caches body parsing, so the transport
563
- // still receives the same body below. Malformed JSON remains the SDK's job.
564
- let parsedRpcBody;
565
- let rpcMessages = [];
482
+ // Read the POST body once, from the cache the body-limit guard may have
483
+ // seeded. Every downstream consumer takes it as `parsedBody` because the raw
484
+ // stream is not guaranteed re-readable after that guard runs.
485
+ let parsedBody;
566
486
  if (c.req.method === 'POST') {
567
487
  try {
568
- const rawBody = await c.req.json();
569
- parsedRpcBody = rawBody;
570
- const messages = Array.isArray(rawBody) ? rawBody : [rawBody];
571
- rpcMessages = messages.filter((message) => typeof message === 'object' && message !== null);
488
+ parsedBody = await c.req.json();
572
489
  }
573
490
  catch {
574
- // Malformed body — let the SDK surface the parse error downstream.
575
- }
576
- }
577
- const initializeMessage = rpcMessages.find((message) => message.method === 'initialize');
578
- const declaredCapabilities = initializeMessage?.params?.capabilities;
579
- const initializeCapabilities = typeof declaredCapabilities === 'object' && declaredCapabilities !== null
580
- ? declaredCapabilities
581
- : undefined;
582
- // Extract identity from auth context (if auth is enabled)
583
- // This MUST happen before session validation for security
584
- const sessionIdentity = extractSessionIdentity();
585
- // MCP Spec 2025-06-18: Return 404 for invalid/terminated sessions
586
- // https://modelcontextprotocol.io/specification/2025-06-18/basic/transports#session-management
587
- // SECURITY: Validate session WITH identity binding to prevent hijacking
588
- if (sessionStore &&
589
- providedSessionId &&
590
- !sessionStore.isValidForIdentity(providedSessionId, sessionIdentity)) {
591
- logger.warning('Session validation failed - invalid or hijacked session', {
592
- ...requestContext,
593
- sessionId: providedSessionId,
594
- requestTenant: sessionIdentity?.tenantId,
595
- requestClient: sessionIdentity?.clientId,
596
- });
597
- return c.json({ error: 'Session not found or expired' }, 404);
598
- }
599
- // MCP Spec 2025-06-18: stateful sessions require Mcp-Session-Id on all
600
- // non-initialize requests. Without this gate, the SDK processes the RPC
601
- // on a fresh per-request McpServer (no protocol state to reject against)
602
- // and the session store mints an uninitialized session on response.ok.
603
- if (sessionStore && !providedSessionId && !initializeMessage) {
604
- logger.warning('Rejected non-initialize request without session in stateful mode', {
605
- ...requestContext,
606
- method: c.req.method,
607
- });
608
- return c.json({
609
- error: 'Bad Request: Mcp-Session-Id header is required. Send an initialize request first.',
610
- }, 400);
611
- }
612
- const sessionId = providedSessionId ?? generateSecureSessionId();
613
- // A response to a server-initiated request arrives on a new POST and would
614
- // otherwise be delivered to a fresh SDK Server with no matching response
615
- // handler. Route recognized responses through the durable session registry
616
- // and strip them before normal SDK dispatch. An emptied batch still goes to
617
- // the transport rather than short-circuiting here: it owns the Accept and
618
- // Content-Type checks, and returns the same 202 every other
619
- // response/notification POST does.
620
- let transportBody = parsedRpcBody;
621
- if (sessionStore && parsedRpcBody !== undefined) {
622
- const messages = Array.isArray(parsedRpcBody) ? parsedRpcBody : [parsedRpcBody];
623
- const unrouted = messages.filter((message) => {
624
- if (!isJSONRPCResultResponse(message) && !isJSONRPCErrorResponse(message))
625
- return true;
626
- return !sessionStore.routeServerResponse(sessionId, message);
627
- });
628
- if (unrouted.length !== messages.length)
629
- transportBody = unrouted;
630
- }
631
- // The cancellation notification is handled by a different SDK Server
632
- // instance than the original request. Mirror it into the durable session
633
- // registry before passing it to the SDK so the original handler's merged
634
- // `ctx.signal` is aborted without sharing Server instances across clients.
635
- if (sessionStore) {
636
- for (const message of rpcMessages) {
637
- if (message.method !== 'notifications/cancelled')
638
- continue;
639
- const params = message.params;
640
- const requestId = params?.requestId;
641
- if (typeof requestId !== 'string' && typeof requestId !== 'number')
642
- continue;
643
- const reason = params?.reason;
644
- sessionStore.cancelRequest(sessionId, requestId, typeof reason === 'string' ? reason : undefined);
645
- }
646
- }
647
- const transport = new McpSessionTransport(sessionId, sessionStore);
648
- trackPerRequestInstance(transport, 'transport');
649
- let server;
650
- let closePromise;
651
- const closeOnce = (trigger) => {
652
- closePromise ??= closePerRequestInstances(transport, server, sessionId, requestContext, trigger);
653
- return closePromise;
654
- };
655
- const handleRpc = async () => {
656
- // SDK 1.26.0: Protocol.connect() throws if already connected.
657
- // Create a fresh McpServer per request to prevent cross-client data leaks.
658
- // See GHSA-345p-7cg4-v4c7.
659
- server = await serverFactory(sessionStore?.createProtocolSessionHooks(sessionId));
660
- trackPerRequestInstance(server, 'server');
661
- await server.connect(transport);
662
- const response = await transport.handleRequest(c, transportBody);
663
- if (response) {
664
- // Only register the session in the store AFTER a successful response.
665
- // This avoids minting sessions for requests that fail protocol
666
- // validation (e.g. tools/list without prior initialize).
667
- if (sessionStore && response.ok) {
668
- sessionStore.getOrCreate(sessionId, sessionIdentity);
669
- if (initializeCapabilities) {
670
- sessionStore.setClientCapabilities(sessionId, initializeCapabilities);
671
- }
672
- }
673
- // MCP Spec 2025-06-18: For stateful sessions, return Mcp-Session-Id header
674
- // in InitializeResponse (and all subsequent responses).
675
- // 'auto' resolves to stateful for HTTP, matching the SessionStore creation above.
676
- if (isStateful && response.ok) {
677
- response.headers.set('Mcp-Session-Id', sessionId);
678
- logger.debug('Added Mcp-Session-Id header to response', {
679
- ...requestContext,
680
- sessionId,
681
- });
682
- }
683
- // Non-SSE responses (POST/202): close immediately via queueMicrotask.
684
- // SSE streams must stay open until the client disconnects — closing now
685
- // would abort the ReadableStream before Hono can write to it (see
686
- // GHSA-345p-7cg4-v4c7 above) — so cleanup is bound to the request's
687
- // AbortSignal instead. `@hono/mcp` `stream.onAbort` only clears its
688
- // internal stream map; it never fires `transport.close()` or `onclose`,
689
- // and real clients almost always disconnect ungracefully (no DELETE).
690
- // Without this hook every SSE GET leaks its per-request McpServer +
691
- // Transport pair (issue #50). `c.req.raw.signal` aborts on client
692
- // disconnect across runtimes (`@hono/node-server`, modern Bun, Workers).
693
- const isSSE = response.headers.get('content-type')?.includes('text/event-stream');
694
- if (!isSSE) {
695
- queueMicrotask(() => {
696
- void closeOnce('success');
697
- });
698
- }
699
- else {
700
- const sseCleanup = () => {
701
- void closeOnce('sse-abort');
702
- };
703
- const reqSignal = c.req.raw.signal;
704
- if (reqSignal.aborted) {
705
- queueMicrotask(sseCleanup);
706
- }
707
- else {
708
- reqSignal.addEventListener('abort', sseCleanup, { once: true });
709
- }
710
- }
711
- return response;
491
+ // Malformed JSON never reaches classification — answer the spec's parse
492
+ // error directly rather than letting an unreadable body reach a transport.
493
+ return c.json({ jsonrpc: '2.0', id: null, error: { code: -32700, message: 'Parse error' } }, 400);
712
494
  }
713
- queueMicrotask(() => {
714
- void closeOnce('success');
715
- });
716
- return c.body(null, 204);
717
- };
718
- // Auth context is already populated by the middleware's authContext.run().
719
- // ALS propagates through all async continuations in this handler.
720
- try {
721
- return await handleRpc();
722
495
  }
723
- catch (err) {
724
- // Close every surface that was successfully created. The idempotent
725
- // closure also protects against any cleanup already scheduled elsewhere.
726
- await closeOnce('error');
727
- throw err instanceof Error ? err : new Error(String(err));
496
+ if (sessionStore && (await isLegacyRequest(c.req.raw, parsedBody))) {
497
+ return await handleLegacySessionful(sessionStore, c.req.raw, parsedBody, requestContext);
728
498
  }
499
+ const authInfo = currentAuthInfo();
500
+ return await handler.fetch(c.req.raw, {
501
+ ...(parsedBody !== undefined && { parsedBody }),
502
+ ...(authInfo && { authInfo }),
503
+ });
729
504
  });
730
505
  logger.info('Hono application setup complete.', transportContext);
731
- return { app, sessionStore };
506
+ return {
507
+ app,
508
+ handler,
509
+ sessionStore,
510
+ close: async () => {
511
+ await handler.close();
512
+ await sessionStore?.destroy();
513
+ },
514
+ };
732
515
  }
733
516
  //# sourceMappingURL=httpTransport.js.map