@zenalexa/unicli 0.213.1 → 0.213.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (350) hide show
  1. package/AGENTS.md +1 -1
  2. package/README.md +4 -4
  3. package/README.zh-CN.md +3 -3
  4. package/dist/cli.d.ts.map +1 -1
  5. package/dist/cli.js +11 -2
  6. package/dist/cli.js.map +1 -1
  7. package/dist/commands/describe.d.ts +26 -0
  8. package/dist/commands/describe.d.ts.map +1 -0
  9. package/dist/commands/describe.js +230 -0
  10. package/dist/commands/describe.js.map +1 -0
  11. package/dist/commands/dev.js +1 -1
  12. package/dist/commands/dev.js.map +1 -1
  13. package/dist/commands/dispatch.d.ts +14 -3
  14. package/dist/commands/dispatch.d.ts.map +1 -1
  15. package/dist/commands/dispatch.js +141 -81
  16. package/dist/commands/dispatch.js.map +1 -1
  17. package/dist/commands/health.d.ts.map +1 -1
  18. package/dist/commands/health.js +1 -1
  19. package/dist/commands/health.js.map +1 -1
  20. package/dist/commands/skills.d.ts.map +1 -1
  21. package/dist/commands/skills.js +1 -1
  22. package/dist/commands/skills.js.map +1 -1
  23. package/dist/constants.d.ts +2 -0
  24. package/dist/constants.d.ts.map +1 -1
  25. package/dist/constants.js +2 -0
  26. package/dist/constants.js.map +1 -1
  27. package/dist/discovery/loader.d.ts +8 -0
  28. package/dist/discovery/loader.d.ts.map +1 -1
  29. package/dist/discovery/loader.js +22 -1
  30. package/dist/discovery/loader.js.map +1 -1
  31. package/dist/engine/args.d.ts +83 -0
  32. package/dist/engine/args.d.ts.map +1 -0
  33. package/dist/engine/args.js +260 -0
  34. package/dist/engine/args.js.map +1 -0
  35. package/dist/engine/executor.d.ts +20 -1
  36. package/dist/engine/executor.d.ts.map +1 -1
  37. package/dist/engine/executor.js +11 -2
  38. package/dist/engine/executor.js.map +1 -1
  39. package/dist/engine/harden.d.ts +48 -0
  40. package/dist/engine/harden.d.ts.map +1 -0
  41. package/dist/engine/harden.js +327 -0
  42. package/dist/engine/harden.js.map +1 -0
  43. package/dist/engine/invoke.d.ts +16 -0
  44. package/dist/engine/invoke.d.ts.map +1 -0
  45. package/dist/engine/invoke.js +15 -0
  46. package/dist/engine/invoke.js.map +1 -0
  47. package/dist/engine/kernel/compile.d.ts +30 -0
  48. package/dist/engine/kernel/compile.d.ts.map +1 -0
  49. package/dist/engine/kernel/compile.js +167 -0
  50. package/dist/engine/kernel/compile.js.map +1 -0
  51. package/dist/engine/kernel/execute.d.ts +35 -0
  52. package/dist/engine/kernel/execute.d.ts.map +1 -0
  53. package/dist/engine/kernel/execute.js +221 -0
  54. package/dist/engine/kernel/execute.js.map +1 -0
  55. package/dist/engine/kernel/types.d.ts +49 -0
  56. package/dist/engine/kernel/types.d.ts.map +1 -0
  57. package/dist/engine/kernel/types.js +9 -0
  58. package/dist/engine/kernel/types.js.map +1 -0
  59. package/dist/engine/kernel/ulid.d.ts +21 -0
  60. package/dist/engine/kernel/ulid.d.ts.map +1 -0
  61. package/dist/engine/kernel/ulid.js +76 -0
  62. package/dist/engine/kernel/ulid.js.map +1 -0
  63. package/dist/engine/template.d.ts.map +1 -1
  64. package/dist/engine/template.js +11 -0
  65. package/dist/engine/template.js.map +1 -1
  66. package/dist/manifest.json +1 -1
  67. package/dist/mcp/dispatch.d.ts +46 -0
  68. package/dist/mcp/dispatch.d.ts.map +1 -0
  69. package/dist/mcp/dispatch.js +109 -0
  70. package/dist/mcp/dispatch.js.map +1 -0
  71. package/dist/mcp/handler.d.ts +30 -0
  72. package/dist/mcp/handler.d.ts.map +1 -0
  73. package/dist/mcp/handler.js +293 -0
  74. package/dist/mcp/handler.js.map +1 -0
  75. package/dist/mcp/http-transport.d.ts +16 -0
  76. package/dist/mcp/http-transport.d.ts.map +1 -0
  77. package/dist/mcp/http-transport.js +124 -0
  78. package/dist/mcp/http-transport.js.map +1 -0
  79. package/dist/mcp/server.d.ts +17 -33
  80. package/dist/mcp/server.d.ts.map +1 -1
  81. package/dist/mcp/server.js +31 -742
  82. package/dist/mcp/server.js.map +1 -1
  83. package/dist/mcp/sse-transport.js.map +1 -1
  84. package/dist/mcp/streamable-http/handle-post.d.ts +23 -0
  85. package/dist/mcp/streamable-http/handle-post.d.ts.map +1 -0
  86. package/dist/mcp/streamable-http/handle-post.js +357 -0
  87. package/dist/mcp/streamable-http/handle-post.js.map +1 -0
  88. package/dist/mcp/streamable-http/index.d.ts +45 -0
  89. package/dist/mcp/streamable-http/index.d.ts.map +1 -0
  90. package/dist/mcp/streamable-http/index.js +150 -0
  91. package/dist/mcp/streamable-http/index.js.map +1 -0
  92. package/dist/mcp/streamable-http/session.d.ts +85 -0
  93. package/dist/mcp/streamable-http/session.d.ts.map +1 -0
  94. package/dist/mcp/streamable-http/session.js +104 -0
  95. package/dist/mcp/streamable-http/session.js.map +1 -0
  96. package/dist/mcp/streamable-http.d.ts +7 -85
  97. package/dist/mcp/streamable-http.d.ts.map +1 -1
  98. package/dist/mcp/streamable-http.js +6 -568
  99. package/dist/mcp/streamable-http.js.map +1 -1
  100. package/dist/mcp/tools.d.ts +57 -0
  101. package/dist/mcp/tools.d.ts.map +1 -0
  102. package/dist/mcp/tools.js +237 -0
  103. package/dist/mcp/tools.js.map +1 -0
  104. package/dist/output/envelope.d.ts +29 -0
  105. package/dist/output/envelope.d.ts.map +1 -1
  106. package/dist/output/envelope.js +6 -0
  107. package/dist/output/envelope.js.map +1 -1
  108. package/dist/output/next-actions.d.ts +18 -0
  109. package/dist/output/next-actions.d.ts.map +1 -0
  110. package/dist/output/next-actions.js +64 -0
  111. package/dist/output/next-actions.js.map +1 -0
  112. package/dist/output/projection.d.ts +93 -0
  113. package/dist/output/projection.d.ts.map +1 -0
  114. package/dist/output/projection.js +255 -0
  115. package/dist/output/projection.js.map +1 -0
  116. package/dist/protocol/acp-helpers.d.ts +32 -0
  117. package/dist/protocol/acp-helpers.d.ts.map +1 -0
  118. package/dist/protocol/acp-helpers.js +175 -0
  119. package/dist/protocol/acp-helpers.js.map +1 -0
  120. package/dist/protocol/acp.d.ts +2 -27
  121. package/dist/protocol/acp.d.ts.map +1 -1
  122. package/dist/protocol/acp.js +7 -170
  123. package/dist/protocol/acp.js.map +1 -1
  124. package/dist/types.d.ts +32 -0
  125. package/dist/types.d.ts.map +1 -1
  126. package/dist/types.js.map +1 -1
  127. package/package.json +13 -2
  128. package/src/adapters/1688/item.yaml +1 -0
  129. package/src/adapters/1688/store.yaml +1 -0
  130. package/src/adapters/36kr/article.yaml +1 -0
  131. package/src/adapters/amazon/bestsellers.yaml +2 -0
  132. package/src/adapters/amazon/movers-shakers.yaml +2 -0
  133. package/src/adapters/amazon/new-releases.yaml +2 -0
  134. package/src/adapters/amazon/rankings.yaml +1 -0
  135. package/src/adapters/apple-podcasts/episodes.yaml +1 -0
  136. package/src/adapters/arxiv/paper.yaml +1 -0
  137. package/src/adapters/audacity/convert.yaml +1 -0
  138. package/src/adapters/audacity/effects.yaml +1 -0
  139. package/src/adapters/audacity/info.yaml +1 -0
  140. package/src/adapters/audacity/mix.yaml +1 -0
  141. package/src/adapters/audacity/normalize.yaml +1 -0
  142. package/src/adapters/audacity/spectrogram.yaml +1 -0
  143. package/src/adapters/audacity/split-channels.yaml +1 -0
  144. package/src/adapters/audacity/trim.yaml +1 -0
  145. package/src/adapters/bilibili/favorites.yaml +1 -0
  146. package/src/adapters/blender/animation.yaml +2 -0
  147. package/src/adapters/blender/camera.yaml +1 -0
  148. package/src/adapters/blender/convert.yaml +1 -0
  149. package/src/adapters/blender/export.yaml +1 -0
  150. package/src/adapters/blender/import.yaml +1 -0
  151. package/src/adapters/blender/info.yaml +1 -0
  152. package/src/adapters/blender/lighting.yaml +1 -0
  153. package/src/adapters/blender/materials.yaml +1 -0
  154. package/src/adapters/blender/objects.yaml +1 -0
  155. package/src/adapters/blender/render.yaml +2 -0
  156. package/src/adapters/blender/scene.yaml +1 -0
  157. package/src/adapters/blender/screenshot.yaml +2 -0
  158. package/src/adapters/blender/script.yaml +1 -0
  159. package/src/adapters/boss/detail.yaml +2 -0
  160. package/src/adapters/cloudcompare/info.yaml +1 -0
  161. package/src/adapters/coupang/add-to-cart.yaml +1 -0
  162. package/src/adapters/ctrip/search.yaml +1 -0
  163. package/src/adapters/douban/download.yaml +1 -0
  164. package/src/adapters/douban/photos.yaml +1 -0
  165. package/src/adapters/douban/subject.yaml +2 -0
  166. package/src/adapters/doubao-web/detail.yaml +1 -0
  167. package/src/adapters/doubao-web/meeting-summary.yaml +1 -0
  168. package/src/adapters/doubao-web/meeting-transcript.yaml +1 -0
  169. package/src/adapters/drawio/export.yaml +2 -0
  170. package/src/adapters/facebook/join-group.yaml +2 -0
  171. package/src/adapters/ffmpeg/compress.yaml +2 -0
  172. package/src/adapters/ffmpeg/concat.yaml +1 -0
  173. package/src/adapters/ffmpeg/convert.yaml +1 -0
  174. package/src/adapters/ffmpeg/extract-audio.yaml +2 -0
  175. package/src/adapters/ffmpeg/gif.yaml +2 -0
  176. package/src/adapters/ffmpeg/normalize.yaml +2 -0
  177. package/src/adapters/ffmpeg/probe.yaml +1 -0
  178. package/src/adapters/ffmpeg/resize.yaml +2 -0
  179. package/src/adapters/ffmpeg/subtitles.yaml +2 -0
  180. package/src/adapters/ffmpeg/thumbnail.yaml +2 -0
  181. package/src/adapters/ffmpeg/trim.yaml +2 -0
  182. package/src/adapters/freecad/assembly.yaml +1 -0
  183. package/src/adapters/freecad/bom.yaml +1 -0
  184. package/src/adapters/freecad/boolean.yaml +1 -0
  185. package/src/adapters/freecad/check.yaml +1 -0
  186. package/src/adapters/freecad/convert.yaml +1 -0
  187. package/src/adapters/freecad/export-stl.yaml +1 -0
  188. package/src/adapters/freecad/import.yaml +1 -0
  189. package/src/adapters/freecad/info.yaml +1 -0
  190. package/src/adapters/freecad/macro.yaml +1 -0
  191. package/src/adapters/freecad/measure.yaml +1 -0
  192. package/src/adapters/freecad/mesh.yaml +1 -0
  193. package/src/adapters/freecad/properties.yaml +1 -0
  194. package/src/adapters/freecad/render.yaml +1 -0
  195. package/src/adapters/freecad/section.yaml +1 -0
  196. package/src/adapters/freecad/sketch.yaml +1 -0
  197. package/src/adapters/gimp/adjust.yaml +1 -0
  198. package/src/adapters/gimp/batch.yaml +1 -0
  199. package/src/adapters/gimp/convert.yaml +1 -0
  200. package/src/adapters/gimp/crop.yaml +1 -0
  201. package/src/adapters/gimp/filter.yaml +1 -0
  202. package/src/adapters/gimp/flip.yaml +1 -0
  203. package/src/adapters/gimp/info.yaml +1 -0
  204. package/src/adapters/gimp/layers.yaml +1 -0
  205. package/src/adapters/gimp/merge-layers.yaml +1 -0
  206. package/src/adapters/gimp/resize.yaml +1 -0
  207. package/src/adapters/gimp/rotate.yaml +1 -0
  208. package/src/adapters/gimp/text.yaml +1 -0
  209. package/src/adapters/godot/scene-export.yaml +1 -0
  210. package/src/adapters/hackernews/comments.yaml +1 -0
  211. package/src/adapters/hackernews/item.yaml +1 -0
  212. package/src/adapters/hf/top.yaml +1 -0
  213. package/src/adapters/imagemagick/composite.yaml +1 -0
  214. package/src/adapters/imagemagick/convert.yaml +1 -0
  215. package/src/adapters/imagemagick/identify.yaml +1 -0
  216. package/src/adapters/imagemagick/montage.yaml +1 -0
  217. package/src/adapters/imagemagick/resize.yaml +1 -0
  218. package/src/adapters/imdb/person.yaml +1 -0
  219. package/src/adapters/imdb/reviews.yaml +1 -0
  220. package/src/adapters/imdb/title.yaml +1 -0
  221. package/src/adapters/inkscape/convert.yaml +2 -0
  222. package/src/adapters/inkscape/export.yaml +2 -0
  223. package/src/adapters/inkscape/optimize.yaml +2 -0
  224. package/src/adapters/instagram/stories.yaml +1 -0
  225. package/src/adapters/jd/item.yaml +1 -0
  226. package/src/adapters/jike/post.yaml +2 -0
  227. package/src/adapters/jike/topic.yaml +2 -0
  228. package/src/adapters/kdenlive/info.yaml +1 -0
  229. package/src/adapters/kdenlive/render.yaml +1 -0
  230. package/src/adapters/krita/batch.yaml +1 -0
  231. package/src/adapters/krita/convert.yaml +1 -0
  232. package/src/adapters/krita/export.yaml +1 -0
  233. package/src/adapters/krita/info.yaml +1 -0
  234. package/src/adapters/libreoffice/convert.yaml +1 -0
  235. package/src/adapters/libreoffice/print.yaml +1 -0
  236. package/src/adapters/linear/issue-update.yaml +1 -0
  237. package/src/adapters/linux-do/category.yaml +1 -0
  238. package/src/adapters/linux-do/topic.yaml +1 -0
  239. package/src/adapters/macos/calendar-create.yaml +1 -0
  240. package/src/adapters/macos/finder-copy.yaml +1 -0
  241. package/src/adapters/macos/finder-move.yaml +1 -0
  242. package/src/adapters/macos/finder-new-folder.yaml +1 -0
  243. package/src/adapters/macos/finder-tags.yaml +1 -0
  244. package/src/adapters/macos/open.yaml +2 -0
  245. package/src/adapters/macos/screen-recording.yaml +1 -0
  246. package/src/adapters/macos/screenshot.yaml +1 -0
  247. package/src/adapters/macos/spotlight.yaml +1 -0
  248. package/src/adapters/macos/wallpaper.yaml +1 -0
  249. package/src/adapters/medium/article.yaml +1 -0
  250. package/src/adapters/mermaid/render.yaml +1 -0
  251. package/src/adapters/minimax/tts.yaml +1 -0
  252. package/src/adapters/motion-studio/component-get.yaml +1 -0
  253. package/src/adapters/musescore/convert.yaml +2 -0
  254. package/src/adapters/musescore/export.yaml +2 -0
  255. package/src/adapters/musescore/info.yaml +1 -0
  256. package/src/adapters/musescore/instruments.yaml +1 -0
  257. package/src/adapters/musescore/transpose.yaml +1 -0
  258. package/src/adapters/netease-music/playlist.yaml +1 -0
  259. package/src/adapters/netease-music/top.yaml +1 -0
  260. package/src/adapters/notebooklm/get.yaml +1 -0
  261. package/src/adapters/notebooklm/history.yaml +1 -0
  262. package/src/adapters/notebooklm/note-list.yaml +1 -0
  263. package/src/adapters/notebooklm/notes-get.yaml +1 -0
  264. package/src/adapters/notebooklm/open.yaml +1 -0
  265. package/src/adapters/notebooklm/source-fulltext.yaml +1 -0
  266. package/src/adapters/notebooklm/source-get.yaml +1 -0
  267. package/src/adapters/notebooklm/source-guide.yaml +1 -0
  268. package/src/adapters/notebooklm/source-list.yaml +1 -0
  269. package/src/adapters/notebooklm/summary.yaml +1 -0
  270. package/src/adapters/novita/status.yaml +1 -0
  271. package/src/adapters/obs/screenshot.yaml +1 -0
  272. package/src/adapters/obsidian/open.yaml +1 -0
  273. package/src/adapters/ones/login.yaml +1 -0
  274. package/src/adapters/ones/task.yaml +1 -0
  275. package/src/adapters/pandoc/convert.yaml +1 -0
  276. package/src/adapters/paperreview/feedback.yaml +2 -0
  277. package/src/adapters/paperreview/review.yaml +2 -0
  278. package/src/adapters/paperreview/submit.yaml +2 -0
  279. package/src/adapters/pixiv/detail.yaml +1 -0
  280. package/src/adapters/pixiv/download.yaml +2 -0
  281. package/src/adapters/quark/ls.yaml +1 -0
  282. package/src/adapters/reddit/comments.yaml +2 -0
  283. package/src/adapters/renderdoc/capture-list.yaml +1 -0
  284. package/src/adapters/renderdoc/frame-export.yaml +1 -0
  285. package/src/adapters/reuters/article.yaml +2 -0
  286. package/src/adapters/shotcut/info.yaml +1 -0
  287. package/src/adapters/shotcut/render.yaml +1 -0
  288. package/src/adapters/sinablog/article.yaml +1 -0
  289. package/src/adapters/sinablog/user.yaml +1 -0
  290. package/src/adapters/sketch/artboards.yaml +1 -0
  291. package/src/adapters/sketch/export.yaml +1 -0
  292. package/src/adapters/sketch/symbols.yaml +1 -0
  293. package/src/adapters/smzdm/article.yaml +1 -0
  294. package/src/adapters/stackoverflow/question.yaml +1 -0
  295. package/src/adapters/stagehand/wrap-observe.yaml +1 -0
  296. package/src/adapters/steam/wishlist.yaml +1 -0
  297. package/src/adapters/tieba/read.yaml +1 -0
  298. package/src/adapters/tiktok/comment.yaml +1 -0
  299. package/src/adapters/tiktok/like.yaml +1 -0
  300. package/src/adapters/tiktok/save.yaml +1 -0
  301. package/src/adapters/tiktok/unlike.yaml +1 -0
  302. package/src/adapters/tiktok/unsave.yaml +1 -0
  303. package/src/adapters/twitch/streams.yaml +1 -0
  304. package/src/adapters/twitter/quotes.yaml +2 -0
  305. package/src/adapters/twitter/retweets.yaml +1 -0
  306. package/src/adapters/v2ex/replies.yaml +1 -0
  307. package/src/adapters/v2ex/topic.yaml +1 -0
  308. package/src/adapters/vscode/install-ext.yaml +1 -0
  309. package/src/adapters/vscode/open.yaml +1 -0
  310. package/src/adapters/web/read.yaml +1 -0
  311. package/src/adapters/weibo/comments.yaml +1 -0
  312. package/src/adapters/weibo/post.yaml +1 -0
  313. package/src/adapters/weixin/download.yaml +1 -0
  314. package/src/adapters/wiremock/create-stub.yaml +2 -0
  315. package/src/adapters/wiremock/delete-stub.yaml +1 -0
  316. package/src/adapters/wiremock/verify.yaml +1 -0
  317. package/src/adapters/xianyu/item.yaml +1 -0
  318. package/src/adapters/xiaoe/catalog.yaml +1 -0
  319. package/src/adapters/xiaoe/content.yaml +1 -0
  320. package/src/adapters/xiaoe/detail.yaml +1 -0
  321. package/src/adapters/xiaoe/play-url.yaml +1 -0
  322. package/src/adapters/xiaoyuzhou/podcast.yaml +2 -0
  323. package/src/adapters/yollomi/edit.yaml +2 -0
  324. package/src/adapters/yollomi/remove-bg.yaml +2 -0
  325. package/src/adapters/yollomi/restore.yaml +2 -0
  326. package/src/adapters/yollomi/upload.yaml +2 -0
  327. package/src/adapters/yollomi/upscale.yaml +2 -0
  328. package/src/adapters/youtube/playlist.yaml +1 -0
  329. package/src/adapters/yt-dlp/download.yaml +2 -0
  330. package/src/adapters/yt-dlp/extract-audio.yaml +2 -0
  331. package/src/adapters/yt-dlp/info.yaml +1 -0
  332. package/src/adapters/zhihu/answer.yaml +1 -0
  333. package/src/adapters/zhihu/answers.yaml +2 -0
  334. package/src/adapters/zhihu/article.yaml +1 -0
  335. package/src/adapters/zhihu/articles.yaml +2 -0
  336. package/src/adapters/zhihu/collections.yaml +2 -0
  337. package/src/adapters/zhihu/columns.yaml +2 -0
  338. package/src/adapters/zhihu/comment.yaml +1 -0
  339. package/src/adapters/zhihu/download.yaml +1 -0
  340. package/src/adapters/zhihu/followers.yaml +2 -0
  341. package/src/adapters/zhihu/following.yaml +2 -0
  342. package/src/adapters/zhihu/pins.yaml +2 -0
  343. package/src/adapters/zhihu/question.yaml +1 -0
  344. package/src/adapters/zhihu/topic.yaml +1 -0
  345. package/src/adapters/zhihu/topics.yaml +1 -0
  346. package/src/adapters/zhihu/user.yaml +2 -0
  347. package/src/adapters/zoom/join.yaml +1 -0
  348. package/src/adapters/zsxq/search.yaml +1 -0
  349. package/src/adapters/zsxq/topic.yaml +1 -0
  350. package/src/adapters/zsxq/topics.yaml +1 -0
@@ -0,0 +1,150 @@
1
+ /**
2
+ * Streamable HTTP transport for the MCP server (spec 2025-11-25).
3
+ *
4
+ * Endpoints:
5
+ * GET /mcp — server capabilities + supported protocol version
6
+ * POST /mcp — JSON-RPC request (response as JSON or SSE per Accept header)
7
+ * DELETE /mcp — terminate session (requires MCP-Session-Id)
8
+ * GET /health — server status + active session count
9
+ *
10
+ * Session management via the MCP-Session-Id header; protocol-version
11
+ * enforcement via MCP-Protocol-Version. CORS validated per request
12
+ * (no wildcards), DNS-rebinding-safe origin check.
13
+ *
14
+ * Every SSE event carries an explicit `id:` so a future replay buffer
15
+ * can anchor to it. `Last-Event-ID` is accepted for forward-compat
16
+ * testing but replay is not yet implemented.
17
+ *
18
+ * Zero external dependencies — Node.js http + crypto only.
19
+ *
20
+ * Module layout (v0.213.3 P3 split):
21
+ * ./session.ts — session state, helpers, types, constants
22
+ * ./handle-post.ts — POST /mcp dispatch (sync + async + SSE)
23
+ * ./index.ts — public `startStreamableHttp` + routing
24
+ */
25
+ import { createServer, } from "node:http";
26
+ import { VERSION, MCP_PROTOCOL_VERSION } from "../../constants.js";
27
+ import { handleOAuthRoute, createOAuthMiddleware } from "../oauth.js";
28
+ import { handlePost } from "./handle-post.js";
29
+ import { ALLOWED_ORIGINS, HEARTBEAT_MS, PRUNE_INTERVAL_MS, SESSION_TTL_MS, asyncTasks, isOriginAllowed, jsonResponse, pruneStaleSessions, sessions, } from "./session.js";
30
+ // ── DELETE /mcp Handler ────────────────────────────────────────────────────
31
+ function handleDelete(req, res) {
32
+ const sessionId = req.headers["mcp-session-id"];
33
+ if (!sessionId || !sessions.has(sessionId)) {
34
+ jsonResponse(res, 404, {
35
+ jsonrpc: "2.0",
36
+ id: null,
37
+ error: { code: -32600, message: "Invalid or missing MCP-Session-Id" },
38
+ });
39
+ return;
40
+ }
41
+ sessions.delete(sessionId);
42
+ res.writeHead(204);
43
+ res.end();
44
+ }
45
+ // ── OPTIONS preflight ──────────────────────────────────────────────────────
46
+ function handleOptions(req, res) {
47
+ const origin = req.headers.origin;
48
+ const allowedOrigin = origin && isOriginAllowed(req) ? origin : "http://localhost";
49
+ res.writeHead(204, {
50
+ "Access-Control-Allow-Origin": allowedOrigin,
51
+ "Access-Control-Allow-Methods": "GET, POST, DELETE, OPTIONS",
52
+ "Access-Control-Allow-Headers": "Content-Type, MCP-Session-Id, MCP-Protocol-Version, Authorization, Accept, X-MCP-Async",
53
+ "Access-Control-Expose-Headers": "MCP-Session-Id, MCP-Protocol-Version",
54
+ "Access-Control-Max-Age": "86400",
55
+ });
56
+ res.end();
57
+ }
58
+ // ── Routing ────────────────────────────────────────────────────────────────
59
+ function route(req, res, handler, oauthMiddleware, auth) {
60
+ const method = req.method ?? "";
61
+ const pathname = (req.url ?? "/").split("?")[0];
62
+ if (method === "OPTIONS")
63
+ return handleOptions(req, res);
64
+ // OAuth routes — always public when auth is enabled
65
+ if (auth && handleOAuthRoute(req, res))
66
+ return;
67
+ if (method === "GET" && (pathname === "/health" || pathname === "/")) {
68
+ jsonResponse(res, 200, {
69
+ status: "ok",
70
+ transport: "streamable-http",
71
+ version: VERSION,
72
+ sessions: sessions.size,
73
+ protocolVersion: MCP_PROTOCOL_VERSION,
74
+ });
75
+ return;
76
+ }
77
+ if (pathname === "/mcp") {
78
+ if (oauthMiddleware?.(req, res))
79
+ return;
80
+ if (method === "GET") {
81
+ jsonResponse(res, 200, {
82
+ protocolVersion: MCP_PROTOCOL_VERSION,
83
+ serverInfo: { name: "unicli", version: VERSION },
84
+ capabilities: {},
85
+ });
86
+ return;
87
+ }
88
+ if (method === "POST") {
89
+ handlePost(req, res, handler).catch(() => {
90
+ if (!res.writableEnded) {
91
+ jsonResponse(res, 500, {
92
+ jsonrpc: "2.0",
93
+ id: null,
94
+ error: { code: -32603, message: "Unexpected server error" },
95
+ });
96
+ }
97
+ });
98
+ return;
99
+ }
100
+ if (method === "DELETE")
101
+ return handleDelete(req, res);
102
+ }
103
+ res.writeHead(404, { "Content-Type": "application/json" });
104
+ res.end(JSON.stringify({ error: "Not found" }));
105
+ }
106
+ // ── Public API ─────────────────────────────────────────────────────────────
107
+ /**
108
+ * Start the MCP Streamable HTTP transport server (spec 2025-11-25).
109
+ *
110
+ * Returns the actually-bound port so callers that pass `port: 0` (let
111
+ * the OS pick a free ephemeral port — used by tests to avoid Windows
112
+ * TIME_WAIT collisions) get the real port back.
113
+ */
114
+ export async function startStreamableHttp(port, handler, options) {
115
+ const oauthMiddleware = options?.auth ? createOAuthMiddleware() : null;
116
+ const pruneTimer = setInterval(pruneStaleSessions, PRUNE_INTERVAL_MS);
117
+ pruneTimer.unref();
118
+ const server = createServer((req, res) => route(req, res, handler, oauthMiddleware, options?.auth));
119
+ server.on("close", () => {
120
+ clearInterval(pruneTimer);
121
+ sessions.clear();
122
+ asyncTasks.clear();
123
+ });
124
+ await new Promise((resolve, reject) => {
125
+ server.once("error", reject);
126
+ server.listen(port, "127.0.0.1", () => {
127
+ server.off("error", reject);
128
+ resolve();
129
+ });
130
+ });
131
+ const addr = server.address();
132
+ const boundPort = addr && typeof addr === "object" ? addr.port : port;
133
+ process.stderr.write(`unicli MCP server v${VERSION} — Streamable HTTP transport on http://127.0.0.1:${boundPort}\n` +
134
+ ` MCP endpoint: GET/POST/DELETE http://127.0.0.1:${boundPort}/mcp\n` +
135
+ ` Health check: GET http://127.0.0.1:${boundPort}/health\n` +
136
+ ` Protocol: ${MCP_PROTOCOL_VERSION}\n`);
137
+ return boundPort;
138
+ }
139
+ // Exported for testing. Stable shape: mirrors the pre-split module.
140
+ export const _test = {
141
+ sessions,
142
+ asyncTasks,
143
+ pruneStaleSessions,
144
+ isOriginAllowed,
145
+ ALLOWED_ORIGINS,
146
+ SESSION_TTL_MS,
147
+ HEARTBEAT_MS,
148
+ MCP_PROTOCOL_VERSION,
149
+ };
150
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/mcp/streamable-http/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,85 @@
1
+ /**
2
+ * Streamable HTTP session + request helpers.
3
+ *
4
+ * Owns the module-level `sessions` / `asyncTasks` maps, the helpers that
5
+ * every POST/DELETE handler shares (CORS, body reading, JSON response),
6
+ * and origin validation. Kept in a single file so `handle-post.ts` and
7
+ * `index.ts` can both import from the same SSOT without circular deps.
8
+ */
9
+ import type { IncomingMessage, ServerResponse } from "node:http";
10
+ export interface JsonRpcRequest {
11
+ jsonrpc: "2.0";
12
+ id?: number | string | null;
13
+ method: string;
14
+ params?: Record<string, unknown>;
15
+ }
16
+ export interface JsonRpcResponse {
17
+ jsonrpc: "2.0";
18
+ id: number | string | null;
19
+ result?: unknown;
20
+ error?: {
21
+ code: number;
22
+ message: string;
23
+ data?: unknown;
24
+ };
25
+ }
26
+ /**
27
+ * Handler returns `undefined` for JSON-RPC notifications (no `id`). The
28
+ * widened type (P3 MN1 closeout) matches how `buildHandler` in
29
+ * `../handler.ts` produces the handler — sync OR async returns are both
30
+ * legal, `undefined` represents "no response", and the transport awaits
31
+ * uniformly so the cast-adapt in server.ts is no longer required.
32
+ */
33
+ export type Handler = (req: JsonRpcRequest) => JsonRpcResponse | undefined | Promise<JsonRpcResponse | undefined>;
34
+ export interface Session {
35
+ created: number;
36
+ lastSeen: number;
37
+ protocolVersion: string;
38
+ }
39
+ export interface AsyncTask {
40
+ id: string;
41
+ sessionId: string;
42
+ status: "running" | "completed" | "failed" | "cancelled";
43
+ progress?: {
44
+ current: number;
45
+ total: number;
46
+ message?: string;
47
+ };
48
+ result?: JsonRpcResponse;
49
+ error?: string;
50
+ created: number;
51
+ }
52
+ export interface StreamableHttpOptions {
53
+ auth?: boolean;
54
+ }
55
+ export declare const MAX_BODY = 1048576;
56
+ export declare const SESSION_TTL_MS = 3600000;
57
+ export declare const PRUNE_INTERVAL_MS = 300000;
58
+ export declare const HEARTBEAT_MS = 30000;
59
+ export declare const ALLOWED_ORIGINS: Set<string>;
60
+ export declare const MAX_SESSIONS = 100;
61
+ export declare const MAX_ASYNC_TASKS = 200;
62
+ /**
63
+ * Methods that may produce long-running responses — served as SSE when
64
+ * the client accepts text/event-stream.
65
+ */
66
+ export declare const STREAMING_METHODS: Set<string>;
67
+ export declare const sessions: Map<string, Session>;
68
+ export declare const asyncTasks: Map<string, AsyncTask>;
69
+ /** CORS headers injected into every response. Uses validated origin, not wildcard. */
70
+ export declare function corsHeaders(req?: IncomingMessage): Record<string, string>;
71
+ /**
72
+ * Emit a JSON-serializable body with the standard response headers.
73
+ * Accepts `JsonRpcResponse` or any `Record<string, unknown>` so call sites
74
+ * can hand over typed responses without `as unknown as Record<…>` casts.
75
+ */
76
+ export declare function jsonResponse(res: ServerResponse, status: number, body: JsonRpcResponse | Record<string, unknown>, extraHeaders?: Record<string, string>, req?: IncomingMessage): void;
77
+ export declare function readBody(req: IncomingMessage): Promise<string>;
78
+ export declare function pruneStaleSessions(): void;
79
+ /**
80
+ * Validate Origin header for DNS rebinding protection.
81
+ * Allow requests with no Origin (non-browser clients) or from localhost.
82
+ */
83
+ export declare function isOriginAllowed(req: IncomingMessage): boolean;
84
+ export declare function clientAcceptsSSE(req: IncomingMessage): boolean;
85
+ //# sourceMappingURL=session.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../../../src/mcp/streamable-http/session.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAIjE,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,KAAK,CAAC;IACf,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC;IAC5B,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAClC;AAED,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,KAAK,CAAC;IACf,EAAE,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC;IAC3B,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC;CAC3D;AAED;;;;;;GAMG;AACH,MAAM,MAAM,OAAO,GAAG,CACpB,GAAG,EAAE,cAAc,KAChB,eAAe,GAAG,SAAS,GAAG,OAAO,CAAC,eAAe,GAAG,SAAS,CAAC,CAAC;AAExE,MAAM,WAAW,OAAO;IACtB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,eAAe,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,SAAS;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,SAAS,GAAG,WAAW,GAAG,QAAQ,GAAG,WAAW,CAAC;IACzD,QAAQ,CAAC,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAChE,MAAM,CAAC,EAAE,eAAe,CAAC;IACzB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,qBAAqB;IACpC,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB;AAID,eAAO,MAAM,QAAQ,UAAY,CAAC;AAClC,eAAO,MAAM,cAAc,UAAY,CAAC;AACxC,eAAO,MAAM,iBAAiB,SAAU,CAAC;AACzC,eAAO,MAAM,YAAY,QAAS,CAAC;AACnC,eAAO,MAAM,eAAe,aAG1B,CAAC;AACH,eAAO,MAAM,YAAY,MAAM,CAAC;AAChC,eAAO,MAAM,eAAe,MAAM,CAAC;AAEnC;;;GAGG;AACH,eAAO,MAAM,iBAAiB,aAA0B,CAAC;AAIzD,eAAO,MAAM,QAAQ,sBAA6B,CAAC;AACnD,eAAO,MAAM,UAAU,wBAA+B,CAAC;AAIvD,sFAAsF;AACtF,wBAAgB,WAAW,CAAC,GAAG,CAAC,EAAE,eAAe,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAOzE;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAC1B,GAAG,EAAE,cAAc,EACnB,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,eAAe,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC/C,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EACrC,GAAG,CAAC,EAAE,eAAe,GACpB,IAAI,CAQN;AAED,wBAAgB,QAAQ,CAAC,GAAG,EAAE,eAAe,GAAG,OAAO,CAAC,MAAM,CAAC,CAgB9D;AAED,wBAAgB,kBAAkB,IAAI,IAAI,CAQzC;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,eAAe,GAAG,OAAO,CAa7D;AAED,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,eAAe,GAAG,OAAO,CAG9D"}
@@ -0,0 +1,104 @@
1
+ /**
2
+ * Streamable HTTP session + request helpers.
3
+ *
4
+ * Owns the module-level `sessions` / `asyncTasks` maps, the helpers that
5
+ * every POST/DELETE handler shares (CORS, body reading, JSON response),
6
+ * and origin validation. Kept in a single file so `handle-post.ts` and
7
+ * `index.ts` can both import from the same SSOT without circular deps.
8
+ */
9
+ // ── Constants ──────────────────────────────────────────────────────────────
10
+ export const MAX_BODY = 1_048_576; // 1 MB
11
+ export const SESSION_TTL_MS = 3_600_000; // 1 hour
12
+ export const PRUNE_INTERVAL_MS = 300_000; // 5 minutes
13
+ export const HEARTBEAT_MS = 30_000;
14
+ export const ALLOWED_ORIGINS = new Set([
15
+ "http://localhost",
16
+ "http://127.0.0.1",
17
+ ]);
18
+ export const MAX_SESSIONS = 100;
19
+ export const MAX_ASYNC_TASKS = 200;
20
+ /**
21
+ * Methods that may produce long-running responses — served as SSE when
22
+ * the client accepts text/event-stream.
23
+ */
24
+ export const STREAMING_METHODS = new Set(["tools/call"]);
25
+ // ── Module state ───────────────────────────────────────────────────────────
26
+ export const sessions = new Map();
27
+ export const asyncTasks = new Map();
28
+ // ── Helpers ────────────────────────────────────────────────────────────────
29
+ /** CORS headers injected into every response. Uses validated origin, not wildcard. */
30
+ export function corsHeaders(req) {
31
+ const origin = req?.headers?.origin;
32
+ const allowed = origin && isOriginAllowed(req) ? origin : "http://localhost";
33
+ return {
34
+ "Access-Control-Allow-Origin": allowed,
35
+ "Access-Control-Expose-Headers": "MCP-Session-Id, MCP-Protocol-Version",
36
+ };
37
+ }
38
+ /**
39
+ * Emit a JSON-serializable body with the standard response headers.
40
+ * Accepts `JsonRpcResponse` or any `Record<string, unknown>` so call sites
41
+ * can hand over typed responses without `as unknown as Record<…>` casts.
42
+ */
43
+ export function jsonResponse(res, status, body, extraHeaders, req) {
44
+ res.writeHead(status, {
45
+ "Content-Type": "application/json",
46
+ "Cache-Control": "no-store",
47
+ ...corsHeaders(req),
48
+ ...extraHeaders,
49
+ });
50
+ res.end(JSON.stringify(body));
51
+ }
52
+ export function readBody(req) {
53
+ return new Promise((resolve, reject) => {
54
+ const chunks = [];
55
+ let size = 0;
56
+ req.on("data", (chunk) => {
57
+ size += chunk.length;
58
+ if (size > MAX_BODY) {
59
+ req.destroy();
60
+ reject(new Error("Request too large"));
61
+ return;
62
+ }
63
+ chunks.push(chunk);
64
+ });
65
+ req.on("end", () => resolve(Buffer.concat(chunks).toString("utf-8")));
66
+ req.on("error", reject);
67
+ });
68
+ }
69
+ export function pruneStaleSessions() {
70
+ const cutoff = Date.now() - SESSION_TTL_MS;
71
+ for (const [id, session] of sessions) {
72
+ if (session.lastSeen < cutoff)
73
+ sessions.delete(id);
74
+ }
75
+ for (const [id, task] of asyncTasks) {
76
+ if (task.created < cutoff)
77
+ asyncTasks.delete(id);
78
+ }
79
+ }
80
+ /**
81
+ * Validate Origin header for DNS rebinding protection.
82
+ * Allow requests with no Origin (non-browser clients) or from localhost.
83
+ */
84
+ export function isOriginAllowed(req) {
85
+ const origin = req.headers.origin;
86
+ if (!origin)
87
+ return true; // Non-browser clients omit Origin
88
+ // Accept any localhost origin regardless of port
89
+ try {
90
+ const url = new URL(origin);
91
+ if (url.hostname === "localhost" || url.hostname === "127.0.0.1") {
92
+ return true;
93
+ }
94
+ }
95
+ catch {
96
+ // Malformed origin — reject
97
+ }
98
+ return ALLOWED_ORIGINS.has(origin);
99
+ }
100
+ export function clientAcceptsSSE(req) {
101
+ const accept = req.headers.accept ?? "";
102
+ return accept.includes("text/event-stream");
103
+ }
104
+ //# sourceMappingURL=session.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session.js","sourceRoot":"","sources":["../../../src/mcp/streamable-http/session.ts"],"names":[],"mappings":"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"}
@@ -1,89 +1,11 @@
1
1
  /**
2
- * Streamable HTTP transport for the MCP server (spec 2025-11-25).
2
+ * Streamable HTTP transport compatibility re-export shim.
3
3
  *
4
- * Endpoints:
5
- * GET /mcp — server capabilities and supported protocol version
6
- * POST /mcp — JSON-RPC request (response as JSON or SSE per Accept header)
7
- * DELETE /mcp terminate session (requires MCP-Session-Id)
8
- * GET /health — server status + active session count
9
- *
10
- * Session management via MCP-Session-Id header.
11
- * MCP-Protocol-Version header enforced post-initialization.
12
- * CORS headers on all responses (not just OPTIONS preflight).
13
- *
14
- * Event ID / Last-Event-ID (spec 2025-11-25 §5.3 SSE resumability):
15
- * - Every SSE event carries an `id:` line.
16
- * - On POST requests with Accept: text/event-stream, the server reads
17
- * the `Last-Event-ID` header and logs it so future replay work can
18
- * plug into the same path. Full replay lands in v0.213 once the
19
- * server maintains a persistent per-session event buffer — today
20
- * the current single-response SSE path terminates after one event,
21
- * so there is nothing to replay, but a compliant client can still
22
- * include the header without breaking.
23
- *
24
- * Zero external dependencies — Node.js http + crypto only.
25
- */
26
- import { type IncomingMessage } from "node:http";
27
- interface JsonRpcRequest {
28
- jsonrpc: "2.0";
29
- id?: number | string | null;
30
- method: string;
31
- params?: Record<string, unknown>;
32
- }
33
- interface JsonRpcResponse {
34
- jsonrpc: "2.0";
35
- id: number | string | null;
36
- result?: unknown;
37
- error?: {
38
- code: number;
39
- message: string;
40
- data?: unknown;
41
- };
42
- }
43
- type Handler = (req: JsonRpcRequest) => JsonRpcResponse | Promise<JsonRpcResponse>;
44
- interface Session {
45
- created: number;
46
- lastSeen: number;
47
- protocolVersion: string;
48
- }
49
- interface AsyncTask {
50
- id: string;
51
- sessionId: string;
52
- status: "running" | "completed" | "failed" | "cancelled";
53
- progress?: {
54
- current: number;
55
- total: number;
56
- message?: string;
57
- };
58
- result?: JsonRpcResponse;
59
- error?: string;
60
- created: number;
61
- }
62
- interface StreamableHttpOptions {
63
- auth?: boolean;
64
- }
65
- declare function pruneStaleSessions(): void;
66
- /**
67
- * Validate Origin header for DNS rebinding protection.
68
- * Allow requests with no Origin (non-browser clients) or from localhost.
69
- */
70
- declare function isOriginAllowed(req: IncomingMessage): boolean;
71
- /**
72
- * Start the MCP Streamable HTTP transport server (spec 2025-11-25).
73
- *
74
- * GET /mcp returns server capabilities. POST /mcp handles JSON-RPC.
75
- * DELETE /mcp terminates sessions. GET /health returns server info.
4
+ * v0.213.3 P3 split the transport into `streamable-http/{session,
5
+ * handle-post, index}.ts`. This file preserves the old import path
6
+ * (`./streamable-http.js`) so existing tests and external consumers
7
+ * keep working without churn. All logic lives in `streamable-http/`.
76
8
  */
77
- export declare function startStreamableHttp(port: number, handler: Handler, options?: StreamableHttpOptions): Promise<number>;
78
- export declare const _test: {
79
- readonly sessions: Map<string, Session>;
80
- readonly asyncTasks: Map<string, AsyncTask>;
81
- readonly pruneStaleSessions: typeof pruneStaleSessions;
82
- readonly isOriginAllowed: typeof isOriginAllowed;
83
- readonly ALLOWED_ORIGINS: Set<string>;
84
- readonly SESSION_TTL_MS: 3600000;
85
- readonly HEARTBEAT_MS: 30000;
86
- readonly MCP_PROTOCOL_VERSION: "2025-11-25";
87
- };
88
- export {};
9
+ export { startStreamableHttp, _test } from "./streamable-http/index.js";
10
+ export type { Handler, StreamableHttpOptions, } from "./streamable-http/session.js";
89
11
  //# sourceMappingURL=streamable-http.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"streamable-http.d.ts","sourceRoot":"","sources":["../../src/mcp/streamable-http.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAGH,OAAO,EAEL,KAAK,eAAe,EAErB,MAAM,WAAW,CAAC;AAMnB,UAAU,cAAc;IACtB,OAAO,EAAE,KAAK,CAAC;IACf,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC;IAC5B,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAClC;AAED,UAAU,eAAe;IACvB,OAAO,EAAE,KAAK,CAAC;IACf,EAAE,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC;IAC3B,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC;CAC3D;AAED,KAAK,OAAO,GAAG,CACb,GAAG,EAAE,cAAc,KAChB,eAAe,GAAG,OAAO,CAAC,eAAe,CAAC,CAAC;AAEhD,UAAU,OAAO;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,eAAe,EAAE,MAAM,CAAC;CACzB;AAED,UAAU,SAAS;IACjB,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,SAAS,GAAG,WAAW,GAAG,QAAQ,GAAG,WAAW,CAAC;IACzD,QAAQ,CAAC,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAChE,MAAM,CAAC,EAAE,eAAe,CAAC;IACzB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,UAAU,qBAAqB;IAC7B,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB;AAkED,iBAAS,kBAAkB,IAAI,IAAI,CAQlC;AAED;;;GAGG;AACH,iBAAS,eAAe,CAAC,GAAG,EAAE,eAAe,GAAG,OAAO,CAatD;AA8bD;;;;;GAKG;AACH,wBAAsB,mBAAmB,CACvC,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,OAAO,EAChB,OAAO,CAAC,EAAE,qBAAqB,GAC9B,OAAO,CAAC,MAAM,CAAC,CA8GjB;AAGD,eAAO,MAAM,KAAK;;;;;;;;;CASR,CAAC"}
1
+ {"version":3,"file":"streamable-http.d.ts","sourceRoot":"","sources":["../../src/mcp/streamable-http.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,mBAAmB,EAAE,KAAK,EAAE,MAAM,4BAA4B,CAAC;AACxE,YAAY,EACV,OAAO,EACP,qBAAqB,GACtB,MAAM,8BAA8B,CAAC"}