skybridge 0.0.0-dev.b59de48 → 0.0.0-dev.b5c271e

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 (358) hide show
  1. package/README.md +17 -23
  2. package/dist/cli/build-helpers.d.ts +6 -2
  3. package/dist/cli/build-helpers.js +39 -14
  4. package/dist/cli/build-helpers.js.map +1 -1
  5. package/dist/cli/build-helpers.test.js +73 -5
  6. package/dist/cli/build-helpers.test.js.map +1 -1
  7. package/dist/cli/build-steps.d.ts +2 -0
  8. package/dist/cli/build-steps.js +73 -0
  9. package/dist/cli/build-steps.js.map +1 -0
  10. package/dist/cli/build-steps.test.js +52 -0
  11. package/dist/cli/build-steps.test.js.map +1 -0
  12. package/dist/cli/detect-port.js +0 -1
  13. package/dist/cli/detect-port.js.map +1 -1
  14. package/dist/cli/header.d.ts +1 -1
  15. package/dist/cli/resolve-skybridge-config.d.ts +5 -0
  16. package/dist/cli/{resolve-views-dir.js → resolve-skybridge-config.js} +4 -3
  17. package/dist/cli/resolve-skybridge-config.js.map +1 -0
  18. package/dist/cli/run-plain.d.ts +18 -0
  19. package/dist/cli/run-plain.js +90 -0
  20. package/dist/cli/run-plain.js.map +1 -0
  21. package/dist/cli/telemetry.js +2 -0
  22. package/dist/cli/telemetry.js.map +1 -1
  23. package/dist/cli/use-execute-steps.d.ts +1 -1
  24. package/dist/cli/use-nodemon.d.ts +25 -1
  25. package/dist/cli/use-nodemon.js +96 -63
  26. package/dist/cli/use-nodemon.js.map +1 -1
  27. package/dist/cli/use-typescript-check.d.ts +8 -2
  28. package/dist/cli/use-typescript-check.js +70 -67
  29. package/dist/cli/use-typescript-check.js.map +1 -1
  30. package/dist/commands/build.d.ts +0 -2
  31. package/dist/commands/build.js +2 -61
  32. package/dist/commands/build.js.map +1 -1
  33. package/dist/commands/dev.d.ts +1 -0
  34. package/dist/commands/dev.js +38 -6
  35. package/dist/commands/dev.js.map +1 -1
  36. package/dist/commands/start.js +2 -2
  37. package/dist/commands/start.js.map +1 -1
  38. package/dist/context-warnings.d.ts +5 -0
  39. package/dist/context-warnings.js +31 -0
  40. package/dist/context-warnings.js.map +1 -0
  41. package/dist/context-warnings.test.js +28 -0
  42. package/dist/context-warnings.test.js.map +1 -0
  43. package/dist/server/app.d.ts +194 -0
  44. package/dist/server/app.js +266 -0
  45. package/dist/server/app.js.map +1 -0
  46. package/dist/server/app.test.js +97 -0
  47. package/dist/server/app.test.js.map +1 -0
  48. package/dist/server/auth/discovery.d.ts +2 -1
  49. package/dist/server/auth/discovery.js +2 -2
  50. package/dist/server/auth/discovery.js.map +1 -1
  51. package/dist/server/auth/index.d.ts +35 -5
  52. package/dist/server/auth/index.js.map +1 -1
  53. package/dist/server/auth/providers/auth0.d.ts +25 -3
  54. package/dist/server/auth/providers/auth0.js +13 -8
  55. package/dist/server/auth/providers/auth0.js.map +1 -1
  56. package/dist/server/auth/providers/auth0.test.js +5 -3
  57. package/dist/server/auth/providers/auth0.test.js.map +1 -1
  58. package/dist/server/auth/providers/authplane.d.ts +49 -0
  59. package/dist/server/auth/providers/authplane.js +57 -0
  60. package/dist/server/auth/providers/authplane.js.map +1 -0
  61. package/dist/server/auth/providers/authplane.test.js +143 -0
  62. package/dist/server/auth/providers/authplane.test.js.map +1 -0
  63. package/dist/server/auth/providers/clerk.d.ts +40 -3
  64. package/dist/server/auth/providers/clerk.js +4 -1
  65. package/dist/server/auth/providers/clerk.js.map +1 -1
  66. package/dist/server/auth/providers/clerk.test.js +5 -3
  67. package/dist/server/auth/providers/clerk.test.js.map +1 -1
  68. package/dist/server/auth/providers/custom.d.ts +24 -6
  69. package/dist/server/auth/providers/custom.js +33 -11
  70. package/dist/server/auth/providers/custom.js.map +1 -1
  71. package/dist/server/auth/providers/custom.test.js +71 -17
  72. package/dist/server/auth/providers/custom.test.js.map +1 -1
  73. package/dist/server/auth/providers/descope.d.ts +32 -3
  74. package/dist/server/auth/providers/descope.js +13 -2
  75. package/dist/server/auth/providers/descope.js.map +1 -1
  76. package/dist/server/auth/providers/descope.test.js +42 -17
  77. package/dist/server/auth/providers/descope.test.js.map +1 -1
  78. package/dist/server/auth/providers/stytch.d.ts +16 -3
  79. package/dist/server/auth/providers/stytch.js +4 -1
  80. package/dist/server/auth/providers/stytch.js.map +1 -1
  81. package/dist/server/auth/providers/verify-spy.d.ts +12 -0
  82. package/dist/server/auth/providers/verify-spy.js +21 -0
  83. package/dist/server/auth/providers/verify-spy.js.map +1 -0
  84. package/dist/server/auth/providers/workos.d.ts +28 -5
  85. package/dist/server/auth/providers/workos.js +6 -2
  86. package/dist/server/auth/providers/workos.js.map +1 -1
  87. package/dist/server/auth/security-schemes.d.ts +23 -0
  88. package/dist/server/auth/security-schemes.js +64 -0
  89. package/dist/server/auth/security-schemes.js.map +1 -0
  90. package/dist/server/auth/security-schemes.test.d.ts +1 -0
  91. package/dist/server/auth/security-schemes.test.js +99 -0
  92. package/dist/server/auth/security-schemes.test.js.map +1 -0
  93. package/dist/server/auth/setup.d.ts +3 -1
  94. package/dist/server/auth/setup.js +66 -26
  95. package/dist/server/auth/setup.js.map +1 -1
  96. package/dist/server/auth/setup.test.js +349 -20
  97. package/dist/server/auth/setup.test.js.map +1 -1
  98. package/dist/server/auth/verify.d.ts +42 -6
  99. package/dist/server/auth/verify.js +27 -3
  100. package/dist/server/auth/verify.js.map +1 -1
  101. package/dist/server/auth/verify.test.js +65 -0
  102. package/dist/server/auth/verify.test.js.map +1 -1
  103. package/dist/server/auth-extra.test-d.js +130 -0
  104. package/dist/server/auth-extra.test-d.js.map +1 -0
  105. package/dist/server/auth.d.ts +43 -6
  106. package/dist/server/auth.js +4 -5
  107. package/dist/server/auth.js.map +1 -1
  108. package/dist/server/build-manifest.test.js +7 -6
  109. package/dist/server/build-manifest.test.js.map +1 -1
  110. package/dist/server/content-helpers.d.ts +1 -1
  111. package/dist/server/content-helpers.js.map +1 -1
  112. package/dist/server/events.d.ts +113 -0
  113. package/dist/server/events.js +318 -0
  114. package/dist/server/events.js.map +1 -0
  115. package/dist/server/events.test.d.ts +1 -0
  116. package/dist/server/events.test.js +172 -0
  117. package/dist/server/events.test.js.map +1 -0
  118. package/dist/server/express.d.ts +23 -3
  119. package/dist/server/express.js +43 -32
  120. package/dist/server/express.js.map +1 -1
  121. package/dist/server/express.test.js +221 -103
  122. package/dist/server/express.test.js.map +1 -1
  123. package/dist/server/file-ref.d.ts +1 -1
  124. package/dist/server/file-ref.js +1 -1
  125. package/dist/server/file-ref.js.map +1 -1
  126. package/dist/server/form-input.d.ts +120 -0
  127. package/dist/server/form-input.js +89 -0
  128. package/dist/server/form-input.js.map +1 -0
  129. package/dist/server/host.d.ts +2 -0
  130. package/dist/server/host.js +11 -0
  131. package/dist/server/host.js.map +1 -0
  132. package/dist/server/host.test.d.ts +1 -0
  133. package/dist/server/host.test.js +13 -0
  134. package/dist/server/host.test.js.map +1 -0
  135. package/dist/server/index.d.ts +18 -9
  136. package/dist/server/index.js +11 -3
  137. package/dist/server/index.js.map +1 -1
  138. package/dist/server/mcp-errors.test.d.ts +1 -0
  139. package/dist/server/mcp-errors.test.js +41 -0
  140. package/dist/server/mcp-errors.test.js.map +1 -0
  141. package/dist/server/middleware.d.ts +35 -10
  142. package/dist/server/middleware.js +33 -0
  143. package/dist/server/middleware.js.map +1 -1
  144. package/dist/server/middleware.test-d.js +1 -1
  145. package/dist/server/middleware.test-d.js.map +1 -1
  146. package/dist/server/middleware.test.js +242 -134
  147. package/dist/server/middleware.test.js.map +1 -1
  148. package/dist/server/register-tool.test.d.ts +1 -0
  149. package/dist/server/register-tool.test.js +230 -0
  150. package/dist/server/register-tool.test.js.map +1 -0
  151. package/dist/server/server.d.ts +382 -129
  152. package/dist/server/server.js +468 -277
  153. package/dist/server/server.js.map +1 -1
  154. package/dist/server/skills-integration.test.d.ts +1 -0
  155. package/dist/server/skills-integration.test.js +86 -0
  156. package/dist/server/skills-integration.test.js.map +1 -0
  157. package/dist/server/skills.d.ts +27 -0
  158. package/dist/server/skills.js +188 -0
  159. package/dist/server/skills.js.map +1 -0
  160. package/dist/server/skills.test.d.ts +1 -0
  161. package/dist/server/skills.test.js +243 -0
  162. package/dist/server/skills.test.js.map +1 -0
  163. package/dist/server/templateHelper.d.ts +0 -3
  164. package/dist/server/templateHelper.js.map +1 -1
  165. package/dist/server/templates.generated.js +4 -8
  166. package/dist/server/templates.generated.js.map +1 -1
  167. package/dist/server/tool-extra.test.d.ts +1 -0
  168. package/dist/server/tool-extra.test.js +167 -0
  169. package/dist/server/tool-extra.test.js.map +1 -0
  170. package/dist/server/view-name.test-d.d.ts +1 -0
  171. package/dist/server/view-name.test-d.js +8 -0
  172. package/dist/server/view-name.test-d.js.map +1 -0
  173. package/dist/server/view-resource-resolution.test.js +81 -8
  174. package/dist/server/view-resource-resolution.test.js.map +1 -1
  175. package/dist/server/viewsDevServer.js +4 -1
  176. package/dist/server/viewsDevServer.js.map +1 -1
  177. package/dist/test/utils.d.ts +9 -11
  178. package/dist/test/utils.js +220 -201
  179. package/dist/test/utils.js.map +1 -1
  180. package/dist/test/view.test.js +44 -40
  181. package/dist/test/view.test.js.map +1 -1
  182. package/dist/views/index.d.ts +10 -0
  183. package/dist/views/index.js +3 -0
  184. package/dist/views/index.js.map +1 -0
  185. package/dist/{web/plugin → views}/scan-views.js +1 -1
  186. package/dist/views/scan-views.js.map +1 -0
  187. package/dist/views/scan-views.test.d.ts +1 -0
  188. package/dist/views/scan-views.test.js.map +1 -0
  189. package/dist/views/validate-view.d.ts +1 -0
  190. package/dist/{web/plugin → views}/validate-view.js +2 -2
  191. package/dist/views/validate-view.js.map +1 -0
  192. package/dist/views/validate-view.test.d.ts +1 -0
  193. package/dist/views/validate-view.test.js.map +1 -0
  194. package/dist/web/bridges/adaptor.d.ts +14 -3
  195. package/dist/web/bridges/adaptor.js +178 -17
  196. package/dist/web/bridges/adaptor.js.map +1 -1
  197. package/dist/web/bridges/adaptor.test.js +78 -11
  198. package/dist/web/bridges/adaptor.test.js.map +1 -1
  199. package/dist/web/bridges/apps-sdk/bridge.js +1 -1
  200. package/dist/web/bridges/apps-sdk/bridge.js.map +1 -1
  201. package/dist/web/bridges/apps-sdk/use-apps-sdk-context.d.ts +4 -4
  202. package/dist/web/bridges/apps-sdk/use-apps-sdk-context.js +4 -4
  203. package/dist/web/bridges/apps-sdk/use-apps-sdk-context.js.map +1 -1
  204. package/dist/web/bridges/get-adaptor.test.js +2 -2
  205. package/dist/web/bridges/get-adaptor.test.js.map +1 -1
  206. package/dist/web/bridges/mcp-app/bridge.d.ts +1 -1
  207. package/dist/web/bridges/mcp-app/bridge.js +15 -6
  208. package/dist/web/bridges/mcp-app/bridge.js.map +1 -1
  209. package/dist/web/bridges/mcp-app/bridge.test.js +2 -2
  210. package/dist/web/bridges/mcp-app/bridge.test.js.map +1 -1
  211. package/dist/web/bridges/mcp-app/types.d.ts +19 -2
  212. package/dist/web/bridges/mcp-app/types.js.map +1 -1
  213. package/dist/web/bridges/mcp-app/use-mcp-app-context.d.ts +2 -2
  214. package/dist/web/bridges/mcp-app/use-mcp-app-context.js +1 -1
  215. package/dist/web/bridges/mcp-app/use-mcp-app-context.js.map +1 -1
  216. package/dist/web/bridges/mcp-app/use-mcp-app-context.test.js +1 -1
  217. package/dist/web/bridges/mcp-app/use-mcp-app-context.test.js.map +1 -1
  218. package/dist/web/bridges/mcp-app/view-tools.test.js +1 -1
  219. package/dist/web/bridges/mcp-app/view-tools.test.js.map +1 -1
  220. package/dist/web/bridges/types.d.ts +74 -16
  221. package/dist/web/bridges/types.js +1 -1
  222. package/dist/web/bridges/types.js.map +1 -1
  223. package/dist/web/components/modal-provider.d.ts +1 -1
  224. package/dist/web/create-store.test.js +2 -2
  225. package/dist/web/create-store.test.js.map +1 -1
  226. package/dist/web/data-llm.d.ts +1 -1
  227. package/dist/web/data-llm.js +3 -2
  228. package/dist/web/data-llm.js.map +1 -1
  229. package/dist/web/data-llm.test.js +17 -2
  230. package/dist/web/data-llm.test.js.map +1 -1
  231. package/dist/web/generate-helpers.d.ts +23 -19
  232. package/dist/web/generate-helpers.js +21 -17
  233. package/dist/web/generate-helpers.js.map +1 -1
  234. package/dist/web/generate-helpers.test-d.js +3 -3
  235. package/dist/web/generate-helpers.test-d.js.map +1 -1
  236. package/dist/web/generate-helpers.test.js +2 -2
  237. package/dist/web/generate-helpers.test.js.map +1 -1
  238. package/dist/web/hooks/index.d.ts +6 -1
  239. package/dist/web/hooks/index.js +6 -1
  240. package/dist/web/hooks/index.js.map +1 -1
  241. package/dist/web/hooks/openai-extensions.test.d.ts +1 -0
  242. package/dist/web/hooks/openai-extensions.test.js +107 -0
  243. package/dist/web/hooks/openai-extensions.test.js.map +1 -0
  244. package/dist/web/hooks/test/utils.d.ts +2 -1
  245. package/dist/web/hooks/test/utils.js +1 -1
  246. package/dist/web/hooks/test/utils.js.map +1 -1
  247. package/dist/web/hooks/use-call-tool.d.ts +2 -2
  248. package/dist/web/hooks/use-call-tool.js +15 -10
  249. package/dist/web/hooks/use-call-tool.js.map +1 -1
  250. package/dist/web/hooks/use-call-tool.test.js +15 -4
  251. package/dist/web/hooks/use-call-tool.test.js.map +1 -1
  252. package/dist/web/hooks/use-deep-link.d.ts +22 -0
  253. package/dist/web/hooks/use-deep-link.js +34 -0
  254. package/dist/web/hooks/use-deep-link.js.map +1 -0
  255. package/dist/web/hooks/use-display-mode.d.ts +1 -1
  256. package/dist/web/hooks/use-display-mode.js +1 -1
  257. package/dist/web/hooks/use-display-mode.js.map +1 -1
  258. package/dist/web/hooks/use-display-mode.test.js +1 -1
  259. package/dist/web/hooks/use-display-mode.test.js.map +1 -1
  260. package/dist/web/hooks/use-download.d.ts +1 -3
  261. package/dist/web/hooks/use-download.js +1 -2
  262. package/dist/web/hooks/use-download.js.map +1 -1
  263. package/dist/web/hooks/use-download.test.js +7 -7
  264. package/dist/web/hooks/use-download.test.js.map +1 -1
  265. package/dist/web/hooks/use-file-resource.d.ts +37 -0
  266. package/dist/web/hooks/use-file-resource.js +98 -0
  267. package/dist/web/hooks/use-file-resource.js.map +1 -0
  268. package/dist/web/hooks/use-file-resource.test.d.ts +1 -0
  269. package/dist/web/hooks/use-file-resource.test.js +65 -0
  270. package/dist/web/hooks/use-file-resource.test.js.map +1 -0
  271. package/dist/web/hooks/use-files.test.js +1 -1
  272. package/dist/web/hooks/use-files.test.js.map +1 -1
  273. package/dist/web/hooks/use-host.d.ts +44 -0
  274. package/dist/web/hooks/use-host.js +45 -0
  275. package/dist/web/hooks/use-host.js.map +1 -0
  276. package/dist/web/hooks/use-host.test.d.ts +1 -0
  277. package/dist/web/hooks/use-host.test.js +59 -0
  278. package/dist/web/hooks/use-host.test.js.map +1 -0
  279. package/dist/web/hooks/use-model-context.d.ts +44 -0
  280. package/dist/web/hooks/use-model-context.js +63 -0
  281. package/dist/web/hooks/use-model-context.js.map +1 -0
  282. package/dist/web/hooks/use-open-external.test.js +2 -2
  283. package/dist/web/hooks/use-open-external.test.js.map +1 -1
  284. package/dist/web/hooks/use-open-file.d.ts +20 -0
  285. package/dist/web/hooks/use-open-file.js +25 -0
  286. package/dist/web/hooks/use-open-file.js.map +1 -0
  287. package/dist/web/hooks/use-register-view-tool.d.ts +3 -4
  288. package/dist/web/hooks/use-register-view-tool.js +1 -1
  289. package/dist/web/hooks/use-register-view-tool.js.map +1 -1
  290. package/dist/web/hooks/use-request-close.test.js +1 -1
  291. package/dist/web/hooks/use-request-close.test.js.map +1 -1
  292. package/dist/web/hooks/use-request-modal.test.js +1 -1
  293. package/dist/web/hooks/use-request-modal.test.js.map +1 -1
  294. package/dist/web/hooks/use-request-size.d.ts +1 -1
  295. package/dist/web/hooks/use-request-size.js +1 -1
  296. package/dist/web/hooks/use-request-size.js.map +1 -1
  297. package/dist/web/hooks/use-request-size.test.js +1 -1
  298. package/dist/web/hooks/use-request-size.test.js.map +1 -1
  299. package/dist/web/hooks/use-send-follow-up-message.d.ts +8 -2
  300. package/dist/web/hooks/use-send-follow-up-message.js +6 -0
  301. package/dist/web/hooks/use-send-follow-up-message.js.map +1 -1
  302. package/dist/web/hooks/use-set-open-in-app-url.test.js +2 -8
  303. package/dist/web/hooks/use-set-open-in-app-url.test.js.map +1 -1
  304. package/dist/web/hooks/use-tool-info.d.ts +1 -20
  305. package/dist/web/hooks/use-tool-info.js +0 -1
  306. package/dist/web/hooks/use-tool-info.js.map +1 -1
  307. package/dist/web/hooks/use-tool-info.test-d.js +0 -5
  308. package/dist/web/hooks/use-tool-info.test-d.js.map +1 -1
  309. package/dist/web/hooks/use-tool-info.test.js +2 -7
  310. package/dist/web/hooks/use-tool-info.test.js.map +1 -1
  311. package/dist/web/hooks/use-user.d.ts +3 -2
  312. package/dist/web/hooks/use-user.js +4 -3
  313. package/dist/web/hooks/use-user.js.map +1 -1
  314. package/dist/web/hooks/use-user.test.js +5 -3
  315. package/dist/web/hooks/use-user.test.js.map +1 -1
  316. package/dist/web/hooks/use-view-state.js +19 -9
  317. package/dist/web/hooks/use-view-state.js.map +1 -1
  318. package/dist/web/hooks/use-view-state.test.js +33 -2
  319. package/dist/web/hooks/use-view-state.test.js.map +1 -1
  320. package/dist/web/hooks/use-viewport.d.ts +20 -0
  321. package/dist/web/hooks/use-viewport.js +21 -0
  322. package/dist/web/hooks/use-viewport.js.map +1 -0
  323. package/dist/web/hooks/use-viewport.test.d.ts +1 -0
  324. package/dist/web/hooks/{use-layout.test.js → use-viewport.test.js} +12 -32
  325. package/dist/web/hooks/use-viewport.test.js.map +1 -0
  326. package/dist/web/mount-view.d.ts +3 -2
  327. package/dist/web/mount-view.js +5 -8
  328. package/dist/web/mount-view.js.map +1 -1
  329. package/package.json +34 -29
  330. package/dist/cli/resolve-views-dir.d.ts +0 -1
  331. package/dist/cli/resolve-views-dir.js.map +0 -1
  332. package/dist/web/hooks/use-layout.d.ts +0 -24
  333. package/dist/web/hooks/use-layout.js +0 -25
  334. package/dist/web/hooks/use-layout.js.map +0 -1
  335. package/dist/web/hooks/use-layout.test.js.map +0 -1
  336. package/dist/web/plugin/data-llm.test.js +0 -81
  337. package/dist/web/plugin/data-llm.test.js.map +0 -1
  338. package/dist/web/plugin/plugin.d.ts +0 -33
  339. package/dist/web/plugin/plugin.js +0 -189
  340. package/dist/web/plugin/plugin.js.map +0 -1
  341. package/dist/web/plugin/scan-views.js.map +0 -1
  342. package/dist/web/plugin/scan-views.test.js.map +0 -1
  343. package/dist/web/plugin/transform-data-llm.d.ts +0 -12
  344. package/dist/web/plugin/transform-data-llm.js +0 -96
  345. package/dist/web/plugin/transform-data-llm.js.map +0 -1
  346. package/dist/web/plugin/transform-data-llm.test.js +0 -81
  347. package/dist/web/plugin/transform-data-llm.test.js.map +0 -1
  348. package/dist/web/plugin/validate-view.d.ts +0 -1
  349. package/dist/web/plugin/validate-view.js.map +0 -1
  350. package/dist/web/plugin/validate-view.test.js.map +0 -1
  351. /package/dist/{web/hooks/use-layout.test.d.ts → cli/build-steps.test.d.ts} +0 -0
  352. /package/dist/{web/plugin/data-llm.test.d.ts → context-warnings.test.d.ts} +0 -0
  353. /package/dist/{web/plugin/scan-views.test.d.ts → server/app.test.d.ts} +0 -0
  354. /package/dist/{web/plugin/validate-view.test.d.ts → server/auth/providers/authplane.test.d.ts} +0 -0
  355. /package/dist/{web/plugin/transform-data-llm.test.d.ts → server/auth-extra.test-d.d.ts} +0 -0
  356. /package/dist/{web/plugin → views}/scan-views.d.ts +0 -0
  357. /package/dist/{web/plugin → views}/scan-views.test.js +0 -0
  358. /package/dist/{web/plugin → views}/validate-view.test.js +0 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"events.js","sourceRoot":"","sources":["../../src/server/events.ts"],"names":[],"mappings":"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","sourcesContent":["import { createHash, randomBytes, timingSafeEqual } from \"node:crypto\";\nimport http from \"node:http\";\nimport https from \"node:https\";\nimport {\n INVALID_PARAMS,\n ProtocolError,\n type McpServer as SdkMcpServer,\n type ServerContext,\n type StandardSchemaV1,\n type StandardSchemaWithJSON,\n} from \"@modelcontextprotocol/server\";\nimport { globalHttpAgent, globalHttpsAgent } from \"request-filtering-agent\";\nimport { Webhook } from \"standardwebhooks\";\nimport { z } from \"zod/v4\";\n\nconst EVENTS_ERROR = {\n NotFound: -32011,\n Forbidden: -32012,\n Unsupported: -32014,\n CallbackEndpointError: -32015,\n} as const;\n\nconst MAX_PAYLOAD_BYTES = 256 * 1024;\nconst DELIVERY_TIMEOUT_MS = 10_000;\nconst VERIFICATION_CACHE_MS = 60 * 60_000;\nconst MIN_TTL_MS = 60_000;\nconst DEFAULT_TTL_MS = 30 * 60_000;\nconst MAX_TTL_MS = 24 * 60 * 60_000;\n\n/**\n * A webhook subscription, as passed to `onSubscribe`. Whatever delivers the\n * events POSTs them to `url`, signed with `secret`, until `expiresAt`.\n */\nexport interface EventSubscription {\n /** Stable across refreshes: the same user subscribing to the same event with the same arguments and callback gets the same `id`. */\n id: string;\n /** Callback URL deliveries are POSTed to. */\n url: string;\n /** Standard Webhooks signing secret (`whsec_…`). A refresh may carry a new one. */\n secret: string;\n /** When the subscription lapses unless the host refreshes it. */\n expiresAt: Date;\n}\n\n/** An event occurrence, as passed to {@link deliverEvent}. */\nexport interface EventOccurrence {\n /** Event type name, as registered with `registerEvent`. */\n name: string;\n /** Stable identifier the host uses to drop duplicates, ideally the upstream's. Reuse it when retrying. */\n id: string;\n /** When the event happened. Defaults to now. */\n timestamp?: Date;\n /** Payload matching the event's `payloadSchema`. */\n data: unknown;\n}\n\n/** Outcome of {@link deliverEvent}. Retry with the same event `id` when `retryable`. */\nexport type DeliveryResult =\n | { ok: true }\n | { ok: false; reason: string; retryable: boolean; status?: number };\n\ntype EventSchema =\n | Record<string, StandardSchemaWithJSON>\n | StandardSchemaWithJSON;\n\nexport type InferEventSchema<T extends EventSchema> =\n T extends StandardSchemaWithJSON\n ? StandardSchemaV1.InferOutput<T>\n : T extends Record<string, StandardSchemaWithJSON>\n ? {\n [K in keyof T as undefined extends StandardSchemaV1.InferOutput<T[K]>\n ? never\n : K]: StandardSchemaV1.InferOutput<T[K]>;\n } & {\n [K in keyof T as undefined extends StandardSchemaV1.InferOutput<T[K]>\n ? K\n : never]?: StandardSchemaV1.InferOutput<T[K]>;\n }\n : never;\n\n/** The `registerEvent` config: how the event type is described in `events/list`. */\nexport interface EventConfig<TInput extends EventSchema> {\n /** Stable, specific name such as `comment.created`. */\n name: string;\n /** What the event is and when it fires. */\n description?: string;\n /** Subscription arguments (filters): a Zod shape or a Standard Schema object. */\n inputSchema?: TInput;\n /** Shape of the delivered `data`: a Zod shape or a Standard Schema object. */\n payloadSchema: EventSchema;\n _meta?: Record<string, unknown>;\n}\n\n/** What `registerEvent` does when the host subscribes and unsubscribes. */\nexport interface EventHooks<TArguments, TExtra> {\n /**\n * Runs on every `events/subscribe`, including the host's refreshes, once\n * the callback URL is verified. Hand the subscription to whatever delivers\n * the events, keyed by `subscription.id`. Throw (e.g. a `ProtocolError`) to\n * reject it, for instance when the user may not access what the arguments\n * point to.\n */\n onSubscribe(\n args: TArguments,\n context: { subscription: EventSubscription; extra: TExtra },\n ): void | Promise<void>;\n /** Runs on `events/unsubscribe`. Stop delivering to `subscription.id`. */\n onUnsubscribe(\n args: TArguments,\n context: { subscription: Pick<EventSubscription, \"id\">; extra: TExtra },\n ): void | Promise<void>;\n}\n\ntype RegisteredEvent = {\n name: string;\n description?: string;\n inputSchema: StandardSchemaWithJSON;\n payloadSchema: StandardSchemaWithJSON;\n _meta?: Record<string, unknown>;\n hooks: EventHooks<Record<string, unknown>, ServerContext>;\n};\n\nfunction toStandardSchema(schema: EventSchema): StandardSchemaWithJSON {\n return \"~standard\" in schema\n ? (schema as StandardSchemaWithJSON)\n : z.object(schema as unknown as z.ZodRawShape);\n}\n\nasync function validate(\n schema: StandardSchemaWithJSON,\n value: unknown,\n): Promise<Record<string, unknown>> {\n const result = await schema[\"~standard\"].validate(value);\n if (result.issues) {\n throw new ProtocolError(\n INVALID_PARAMS,\n `Invalid arguments: ${result.issues.map((issue) => issue.message).join(\", \")}`,\n );\n }\n return result.value as Record<string, unknown>;\n}\n\nfunction canonicalJson(value: unknown): string {\n if (Array.isArray(value)) {\n return `[${value.map(canonicalJson).join(\",\")}]`;\n }\n if (value && typeof value === \"object\") {\n return `{${Object.keys(value)\n .sort()\n .map(\n (key) =>\n `${JSON.stringify(key)}:${canonicalJson((value as Record<string, unknown>)[key])}`,\n )\n .join(\",\")}}`;\n }\n return JSON.stringify(value);\n}\n\nfunction subscriberOf(ctx: ServerContext): string {\n const authInfo = ctx.http?.authInfo;\n const subject = authInfo?.extra?.subject;\n const subscriber = typeof subject === \"string\" ? subject : authInfo?.clientId;\n if (!subscriber) {\n throw new ProtocolError(\n EVENTS_ERROR.Forbidden,\n \"Webhook subscriptions require an authenticated caller\",\n );\n }\n return subscriber;\n}\n\nfunction isValidSecret(secret: string): boolean {\n const encoded = secret.startsWith(\"whsec_\") ? secret.slice(6) : \"\";\n if (!/^[A-Za-z0-9+/]+={0,2}$/.test(encoded)) {\n return false;\n }\n const length = Buffer.from(encoded, \"base64\").length;\n return length >= 24 && length <= 64;\n}\n\nconst insecureCallbacksAllowed = () => process.env.NODE_ENV !== \"production\";\n\nfunction checkCallbackUrl(raw: string): string {\n let url: URL;\n try {\n url = new URL(raw);\n } catch {\n throw new ProtocolError(INVALID_PARAMS, \"delivery.url is not a valid URL\");\n }\n const allowed =\n url.protocol === \"https:\" ||\n (url.protocol === \"http:\" && insecureCallbacksAllowed());\n if (!allowed) {\n throw new ProtocolError(INVALID_PARAMS, \"delivery.url must use https\");\n }\n return url.href;\n}\n\ntype PostOutcome =\n | { ok: true; body: string }\n | { ok: false; status?: number; reason: string };\n\nfunction post(\n url: string,\n headers: Record<string, string>,\n body: string,\n): Promise<PostOutcome> {\n const signal = AbortSignal.timeout(DELIVERY_TIMEOUT_MS);\n const failure = (error?: NodeJS.ErrnoException): PostOutcome => ({\n ok: false,\n reason: signal.aborted\n ? \"timeout\"\n : error?.code?.startsWith(\"ERR_TLS\") || error?.code?.includes(\"CERT\")\n ? \"tls_error\"\n : \"connection_refused\",\n });\n const [client, agent] = url.startsWith(\"https:\")\n ? [https, globalHttpsAgent]\n : [http, globalHttpAgent];\n return new Promise((resolve) => {\n const request = client.request(\n url,\n {\n method: \"POST\",\n headers: { ...headers, \"content-length\": Buffer.byteLength(body) },\n signal,\n ...(!insecureCallbacksAllowed() && { agent }),\n },\n (response) => {\n const status = response.statusCode ?? 0;\n const chunks: Buffer[] = [];\n let size = 0;\n response.on(\"data\", (chunk: Buffer) => {\n size += chunk.length;\n if (size <= 64 * 1024) {\n chunks.push(chunk);\n }\n });\n response.on(\"end\", () => {\n if (status >= 200 && status < 300) {\n resolve({ ok: true, body: Buffer.concat(chunks).toString() });\n } else {\n resolve({\n ok: false,\n status,\n reason: status >= 500 ? \"http_5xx\" : \"http_4xx\",\n });\n }\n });\n response.on(\"error\", (error) => resolve(failure(error)));\n response.on(\"close\", () => resolve(failure()));\n },\n );\n request.on(\"error\", (error) => resolve(failure(error)));\n request.end(body);\n });\n}\n\nfunction signedPost(\n subscription: { id: string; url: string; secret: string | string[] },\n messageId: string,\n body: string,\n): Promise<PostOutcome> {\n const signedAt = new Date();\n const secrets = [subscription.secret].flat();\n return post(\n subscription.url,\n {\n \"content-type\": \"application/json\",\n \"webhook-id\": messageId,\n \"webhook-timestamp\": String(Math.floor(signedAt.getTime() / 1000)),\n \"webhook-signature\": secrets\n .map((secret) => new Webhook(secret).sign(messageId, signedAt, body))\n .join(\" \"),\n \"x-mcp-subscription-id\": subscription.id,\n },\n body,\n );\n}\n\n/**\n * @experimental POST one event to a subscription handed out by `onSubscribe`,\n * as MCP Events expects: the event envelope, Standard Webhooks signature\n * headers, a 10 second deadline and, outside development, no requests to\n * private addresses. Pass the old and new secrets during a rotation so both\n * sign the delivery. A subscription past `expiresAt` is not posted to.\n * Retrying is up to the caller. API may change.\n *\n * @example\n * ```ts\n * const result = await deliverEvent(subscription, {\n * name: \"comment.created\",\n * id: comment.id,\n * data: { documentId: comment.documentId, excerpt: comment.text.slice(0, 280) },\n * });\n * if (!result.ok && result.retryable) {\n * await queue.retryLater(subscription, comment);\n * }\n * ```\n *\n * @see https://docs.skybridge.tech/api-reference/register-event\n */\nexport async function deliverEvent(\n subscription: Pick<EventSubscription, \"id\" | \"url\"> & {\n secret: string | string[];\n expiresAt: Date | string;\n },\n event: EventOccurrence,\n): Promise<DeliveryResult> {\n if (!(new Date(subscription.expiresAt).getTime() > Date.now())) {\n return { ok: false, reason: \"expired\", retryable: false };\n }\n const body = JSON.stringify({\n eventId: event.id,\n name: event.name,\n timestamp: (event.timestamp ?? new Date()).toISOString(),\n data: event.data,\n cursor: null,\n });\n if (Buffer.byteLength(body) > MAX_PAYLOAD_BYTES) {\n throw new Error(\n `Event \"${event.name}\" payload exceeds ${MAX_PAYLOAD_BYTES} bytes; send a summary and expose a tool to fetch the rest.`,\n );\n }\n const outcome = await signedPost(subscription, event.id, body);\n if (outcome.ok) {\n return { ok: true };\n }\n const status = outcome.status ?? 0;\n return {\n ...outcome,\n retryable:\n status !== 410 && status !== 413 && (status < 300 || status >= 400),\n };\n}\n\nconst verifiedCallbacks = new Map<string, number>();\n\nasync function verifyCallback(\n subscriber: string,\n subscription: EventSubscription,\n): Promise<void> {\n const key = `${subscriber}\\n${subscription.url}`;\n const now = Date.now();\n if ((verifiedCallbacks.get(key) ?? 0) > now) {\n return;\n }\n const challenge = randomBytes(24).toString(\"base64url\");\n const outcome = await signedPost(\n subscription,\n `msg_verification_${randomBytes(12).toString(\"hex\")}`,\n JSON.stringify({ type: \"verification\", challenge }),\n );\n let echoed: unknown;\n if (outcome.ok) {\n try {\n echoed = JSON.parse(outcome.body)?.challenge;\n } catch {}\n }\n const matched =\n typeof echoed === \"string\" &&\n echoed.length === challenge.length &&\n timingSafeEqual(Buffer.from(echoed), Buffer.from(challenge));\n if (!matched) {\n throw new ProtocolError(\n EVENTS_ERROR.CallbackEndpointError,\n \"Callback endpoint verification failed\",\n { reason: outcome.ok ? \"challenge_failed\" : outcome.reason },\n );\n }\n for (const [cached, until] of verifiedCallbacks) {\n if (until <= now) {\n verifiedCallbacks.delete(cached);\n }\n }\n verifiedCallbacks.set(key, now + VERIFICATION_CACHE_MS);\n}\n\nconst SubscriptionKeySchema = z.object({\n name: z.string(),\n arguments: z.record(z.string(), z.unknown()).optional(),\n delivery: z.object({ url: z.string() }),\n});\n\nconst SubscribeParamsSchema = SubscriptionKeySchema.extend({\n delivery: z.object({\n mode: z.string().optional(),\n url: z.string(),\n secret: z.string(),\n }),\n cursor: z.string().nullable().optional(),\n ttlMs: z.number().nullable().optional(),\n});\n\n/**\n * The event types registered on one server, and the `events/*` handlers that\n * serve them.\n *\n * @internal\n */\nexport class EventRegistry {\n private readonly events = new Map<string, RegisteredEvent>();\n\n constructor(\n private readonly server: Pick<SdkMcpServer[\"server\"], \"setRequestHandler\">,\n ) {}\n\n add(config: EventConfig<EventSchema>, hooks: EventHooks<never, never>): void {\n if (this.events.has(config.name)) {\n throw new Error(`Event \"${config.name}\" is already registered.`);\n }\n if (this.events.size === 0) {\n this.install();\n }\n this.events.set(config.name, {\n name: config.name,\n description: config.description,\n inputSchema: toStandardSchema(config.inputSchema ?? {}),\n payloadSchema: toStandardSchema(config.payloadSchema),\n _meta: config._meta,\n hooks,\n });\n }\n\n private install(): void {\n this.server.setRequestHandler(\n \"events/list\",\n { params: z.object({ cursor: z.string().optional() }).optional() },\n () => ({\n events: [...this.events.values()].map((event) => ({\n name: event.name,\n ...(event.description && { description: event.description }),\n delivery: [\"webhook\"],\n inputSchema: event.inputSchema[\"~standard\"].jsonSchema.input({\n target: \"draft-2020-12\",\n }),\n payloadSchema: event.payloadSchema[\"~standard\"].jsonSchema.output({\n target: \"draft-2020-12\",\n }),\n ...(event._meta && { _meta: event._meta }),\n })),\n }),\n );\n\n this.server.setRequestHandler(\n \"events/subscribe\",\n { params: SubscribeParamsSchema },\n async (params, ctx) => {\n if (params.delivery.mode && params.delivery.mode !== \"webhook\") {\n throw new ProtocolError(\n EVENTS_ERROR.Unsupported,\n `Unsupported delivery mode: ${params.delivery.mode}`,\n { feature: \"deliveryMode\", value: params.delivery.mode },\n );\n }\n if (!isValidSecret(params.delivery.secret)) {\n throw new ProtocolError(\n INVALID_PARAMS,\n \"delivery.secret must be whsec_ followed by base64 of 24 to 64 bytes\",\n );\n }\n const { event, subscriber, args, id, url } = await this.resolve(\n params,\n ctx,\n );\n const ttl =\n params.ttlMs === null ? MAX_TTL_MS : (params.ttlMs ?? DEFAULT_TTL_MS);\n const subscription: EventSubscription = {\n id,\n url,\n secret: params.delivery.secret,\n expiresAt: new Date(\n Date.now() + Math.min(Math.max(ttl, MIN_TTL_MS), MAX_TTL_MS),\n ),\n };\n await verifyCallback(subscriber, subscription);\n await event.hooks.onSubscribe(args, { subscription, extra: ctx });\n return {\n id,\n refreshBefore: subscription.expiresAt.toISOString(),\n cursor: null,\n truncated: false,\n };\n },\n );\n\n this.server.setRequestHandler(\n \"events/unsubscribe\",\n { params: SubscriptionKeySchema },\n async (params, ctx) => {\n const { event, args, id } = await this.resolve(params, ctx);\n await event.hooks.onUnsubscribe(args, {\n subscription: { id },\n extra: ctx,\n });\n return {};\n },\n );\n }\n\n private async resolve(\n params: z.infer<typeof SubscriptionKeySchema>,\n ctx: ServerContext,\n ) {\n const event = this.events.get(params.name);\n if (!event) {\n throw new ProtocolError(\n EVENTS_ERROR.NotFound,\n `Unknown event: ${params.name}`,\n { kind: \"event\" },\n );\n }\n const url = checkCallbackUrl(params.delivery.url);\n const subscriber = subscriberOf(ctx);\n const rawArguments = params.arguments ?? {};\n const args = await validate(event.inputSchema, rawArguments);\n const hash = createHash(\"sha256\")\n .update(canonicalJson([subscriber, url, event.name, rawArguments]))\n .digest(\"hex\");\n return { event, subscriber, args, url, id: `sub_${hash.slice(0, 32)}` };\n }\n}\n"]}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,172 @@
1
+ import { createHmac, randomBytes } from "node:crypto";
2
+ import http from "node:http";
3
+ import net, {} from "node:net";
4
+ import { Client, InMemoryTransport } from "@modelcontextprotocol/client";
5
+ import { afterEach, describe, expect, it } from "vitest";
6
+ import { z } from "zod";
7
+ import { deliverEvent, Skybridge } from "./index.js";
8
+ async function startReceiver() {
9
+ const received = [];
10
+ const state = { dropResponses: false };
11
+ const server = http.createServer((req, res) => {
12
+ let body = "";
13
+ req.on("data", (chunk) => {
14
+ body += chunk;
15
+ });
16
+ req.on("end", () => {
17
+ received.push({ headers: req.headers, body });
18
+ if (state.dropResponses) {
19
+ res.writeHead(200, { "content-length": "1000" });
20
+ res.write("partial");
21
+ setTimeout(() => res.socket?.destroy(), 20);
22
+ return;
23
+ }
24
+ const parsed = JSON.parse(body);
25
+ res.setHeader("content-type", "application/json");
26
+ res.end(parsed.type === "verification"
27
+ ? JSON.stringify({ challenge: parsed.challenge })
28
+ : "{}");
29
+ });
30
+ });
31
+ await new Promise((resolve) => server.listen(0, "127.0.0.1", resolve));
32
+ const { port } = server.address();
33
+ return { url: `http://127.0.0.1:${port}/hooks`, received, server, state };
34
+ }
35
+ let cleanup;
36
+ afterEach(async () => {
37
+ await cleanup?.();
38
+ cleanup = undefined;
39
+ });
40
+ describe("MCP events", () => {
41
+ it("verifies the callback, hands the subscription to the hooks, and delivers signed events", async () => {
42
+ const receiver = await startReceiver();
43
+ const subscriptions = new Map();
44
+ const app = new Skybridge({
45
+ name: "t",
46
+ version: "0.0.1",
47
+ handler: (server) => server.registerEvent({
48
+ name: "comment.created",
49
+ inputSchema: { documentId: z.string() },
50
+ payloadSchema: { documentId: z.string(), excerpt: z.string() },
51
+ }, {
52
+ onSubscribe: (_args, { subscription }) => {
53
+ subscriptions.set(subscription.id, subscription);
54
+ },
55
+ onUnsubscribe: (_args, { subscription }) => {
56
+ subscriptions.delete(subscription.id);
57
+ },
58
+ }),
59
+ });
60
+ const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair();
61
+ const send = clientTransport.send.bind(clientTransport);
62
+ clientTransport.send = (message) => send(message, {
63
+ authInfo: {
64
+ token: "t",
65
+ clientId: "chatgpt",
66
+ scopes: [],
67
+ extra: { subject: "user-1" },
68
+ },
69
+ });
70
+ const instance = await app.createServerInstance();
71
+ await instance.connect(serverTransport);
72
+ const client = new Client({ name: "c", version: "0.0.1" });
73
+ await client.connect(clientTransport);
74
+ cleanup = async () => {
75
+ await client.close();
76
+ await instance.close();
77
+ receiver.server.close();
78
+ };
79
+ expect(instance.getCapabilities().events).toEqual({});
80
+ const list = await client.request({ method: "events/list", params: {} }, z.object({
81
+ events: z.array(z.object({ name: z.string(), delivery: z.array(z.string()) })),
82
+ }));
83
+ expect(list.events).toEqual([
84
+ expect.objectContaining({
85
+ name: "comment.created",
86
+ delivery: ["webhook"],
87
+ }),
88
+ ]);
89
+ const secret = `whsec_${randomBytes(32).toString("base64")}`;
90
+ const subscription = await client.request({
91
+ method: "events/subscribe",
92
+ params: {
93
+ name: "comment.created",
94
+ arguments: { documentId: "doc-1" },
95
+ delivery: { mode: "webhook", url: receiver.url, secret },
96
+ },
97
+ }, z.object({ id: z.string(), refreshBefore: z.string() }));
98
+ expect(receiver.received.map(({ body }) => JSON.parse(body).type)).toEqual([
99
+ "verification",
100
+ ]);
101
+ const stored = subscriptions.get(subscription.id);
102
+ expect(stored).toMatchObject({ url: receiver.url, secret });
103
+ if (!stored) {
104
+ return;
105
+ }
106
+ await expect(deliverEvent(stored, {
107
+ name: "comment.created",
108
+ id: "evt_1",
109
+ data: { documentId: "doc-1", excerpt: "hello" },
110
+ })).resolves.toEqual({ ok: true });
111
+ expect(receiver.received).toHaveLength(2);
112
+ const [, delivery] = receiver.received;
113
+ expect(JSON.parse(delivery?.body ?? "")).toMatchObject({
114
+ eventId: "evt_1",
115
+ name: "comment.created",
116
+ data: { documentId: "doc-1", excerpt: "hello" },
117
+ });
118
+ const headers = delivery?.headers ?? {};
119
+ expect(headers["x-mcp-subscription-id"]).toBe(subscription.id);
120
+ const expected = createHmac("sha256", Buffer.from(secret.slice(6), "base64"))
121
+ .update(`${headers["webhook-id"]}.${headers["webhook-timestamp"]}.${delivery?.body}`)
122
+ .digest("base64");
123
+ expect(headers["webhook-signature"]).toBe(`v1,${expected}`);
124
+ receiver.state.dropResponses = true;
125
+ const delivered = deliverEvent(stored, {
126
+ name: "comment.created",
127
+ id: "evt_2",
128
+ data: { documentId: "doc-1", excerpt: "dropped" },
129
+ });
130
+ await expect(Promise.race([
131
+ delivered,
132
+ new Promise((resolve) => setTimeout(() => resolve("hung"), 2_000)),
133
+ ])).resolves.toMatchObject({ ok: false, retryable: true });
134
+ await client.request({
135
+ method: "events/unsubscribe",
136
+ params: {
137
+ name: "comment.created",
138
+ arguments: { documentId: "doc-1" },
139
+ delivery: { url: receiver.url },
140
+ },
141
+ }, z.object({}));
142
+ expect(subscriptions.size).toBe(0);
143
+ let connections = 0;
144
+ const privateTarget = net.createServer((socket) => {
145
+ connections += 1;
146
+ socket.destroy();
147
+ });
148
+ await new Promise((resolve) => privateTarget.listen(0, "127.0.0.1", resolve));
149
+ const { port: privatePort } = privateTarget.address();
150
+ const nodeEnv = process.env.NODE_ENV;
151
+ process.env.NODE_ENV = "production";
152
+ try {
153
+ await expect(client.request({
154
+ method: "events/subscribe",
155
+ params: {
156
+ name: "comment.created",
157
+ arguments: { documentId: "doc-1" },
158
+ delivery: {
159
+ url: `https://127.0.0.1:${privatePort}/hooks`,
160
+ secret,
161
+ },
162
+ },
163
+ }, z.object({}))).rejects.toMatchObject({ code: -32015 });
164
+ expect(connections).toBe(0);
165
+ }
166
+ finally {
167
+ process.env.NODE_ENV = nodeEnv;
168
+ privateTarget.close();
169
+ }
170
+ });
171
+ });
172
+ //# sourceMappingURL=events.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"events.test.js","sourceRoot":"","sources":["../../src/server/events.test.ts"],"names":[],"mappings":"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","sourcesContent":["import { createHmac, randomBytes } from \"node:crypto\";\nimport http from \"node:http\";\nimport net, { type AddressInfo } from \"node:net\";\nimport { Client, InMemoryTransport } from \"@modelcontextprotocol/client\";\nimport type { JSONRPCMessage } from \"@modelcontextprotocol/server\";\nimport { afterEach, describe, expect, it } from \"vitest\";\nimport { z } from \"zod\";\nimport { deliverEvent, type EventSubscription, Skybridge } from \"./index.js\";\n\ntype Received = { headers: http.IncomingHttpHeaders; body: string };\n\nasync function startReceiver() {\n const received: Received[] = [];\n const state = { dropResponses: false };\n const server = http.createServer((req, res) => {\n let body = \"\";\n req.on(\"data\", (chunk) => {\n body += chunk;\n });\n req.on(\"end\", () => {\n received.push({ headers: req.headers, body });\n if (state.dropResponses) {\n res.writeHead(200, { \"content-length\": \"1000\" });\n res.write(\"partial\");\n setTimeout(() => res.socket?.destroy(), 20);\n return;\n }\n const parsed = JSON.parse(body);\n res.setHeader(\"content-type\", \"application/json\");\n res.end(\n parsed.type === \"verification\"\n ? JSON.stringify({ challenge: parsed.challenge })\n : \"{}\",\n );\n });\n });\n await new Promise<void>((resolve) => server.listen(0, \"127.0.0.1\", resolve));\n const { port } = server.address() as AddressInfo;\n return { url: `http://127.0.0.1:${port}/hooks`, received, server, state };\n}\n\nlet cleanup: (() => Promise<void>) | undefined;\nafterEach(async () => {\n await cleanup?.();\n cleanup = undefined;\n});\n\ndescribe(\"MCP events\", () => {\n it(\"verifies the callback, hands the subscription to the hooks, and delivers signed events\", async () => {\n const receiver = await startReceiver();\n const subscriptions = new Map<string, EventSubscription>();\n const app = new Skybridge({\n name: \"t\",\n version: \"0.0.1\",\n handler: (server) =>\n server.registerEvent(\n {\n name: \"comment.created\",\n inputSchema: { documentId: z.string() },\n payloadSchema: { documentId: z.string(), excerpt: z.string() },\n },\n {\n onSubscribe: (_args, { subscription }) => {\n subscriptions.set(subscription.id, subscription);\n },\n onUnsubscribe: (_args, { subscription }) => {\n subscriptions.delete(subscription.id);\n },\n },\n ),\n });\n\n const [clientTransport, serverTransport] =\n InMemoryTransport.createLinkedPair();\n const send = clientTransport.send.bind(clientTransport);\n clientTransport.send = (message: JSONRPCMessage) =>\n send(message, {\n authInfo: {\n token: \"t\",\n clientId: \"chatgpt\",\n scopes: [],\n extra: { subject: \"user-1\" },\n },\n });\n const instance = await app.createServerInstance();\n await instance.connect(serverTransport);\n const client = new Client({ name: \"c\", version: \"0.0.1\" });\n await client.connect(clientTransport);\n cleanup = async () => {\n await client.close();\n await instance.close();\n receiver.server.close();\n };\n\n expect(\n (instance.getCapabilities() as Record<string, unknown>).events,\n ).toEqual({});\n const list = await client.request(\n { method: \"events/list\", params: {} },\n z.object({\n events: z.array(\n z.object({ name: z.string(), delivery: z.array(z.string()) }),\n ),\n }),\n );\n expect(list.events).toEqual([\n expect.objectContaining({\n name: \"comment.created\",\n delivery: [\"webhook\"],\n }),\n ]);\n\n const secret = `whsec_${randomBytes(32).toString(\"base64\")}`;\n const subscription = await client.request(\n {\n method: \"events/subscribe\",\n params: {\n name: \"comment.created\",\n arguments: { documentId: \"doc-1\" },\n delivery: { mode: \"webhook\", url: receiver.url, secret },\n },\n },\n z.object({ id: z.string(), refreshBefore: z.string() }),\n );\n expect(receiver.received.map(({ body }) => JSON.parse(body).type)).toEqual([\n \"verification\",\n ]);\n\n const stored = subscriptions.get(subscription.id);\n expect(stored).toMatchObject({ url: receiver.url, secret });\n if (!stored) {\n return;\n }\n await expect(\n deliverEvent(stored, {\n name: \"comment.created\",\n id: \"evt_1\",\n data: { documentId: \"doc-1\", excerpt: \"hello\" },\n }),\n ).resolves.toEqual({ ok: true });\n\n expect(receiver.received).toHaveLength(2);\n const [, delivery] = receiver.received;\n expect(JSON.parse(delivery?.body ?? \"\")).toMatchObject({\n eventId: \"evt_1\",\n name: \"comment.created\",\n data: { documentId: \"doc-1\", excerpt: \"hello\" },\n });\n const headers: http.IncomingHttpHeaders = delivery?.headers ?? {};\n expect(headers[\"x-mcp-subscription-id\"]).toBe(subscription.id);\n const expected = createHmac(\n \"sha256\",\n Buffer.from(secret.slice(6), \"base64\"),\n )\n .update(\n `${headers[\"webhook-id\"]}.${headers[\"webhook-timestamp\"]}.${delivery?.body}`,\n )\n .digest(\"base64\");\n expect(headers[\"webhook-signature\"]).toBe(`v1,${expected}`);\n\n receiver.state.dropResponses = true;\n const delivered = deliverEvent(stored, {\n name: \"comment.created\",\n id: \"evt_2\",\n data: { documentId: \"doc-1\", excerpt: \"dropped\" },\n });\n await expect(\n Promise.race([\n delivered,\n new Promise((resolve) => setTimeout(() => resolve(\"hung\"), 2_000)),\n ]),\n ).resolves.toMatchObject({ ok: false, retryable: true });\n\n await client.request(\n {\n method: \"events/unsubscribe\",\n params: {\n name: \"comment.created\",\n arguments: { documentId: \"doc-1\" },\n delivery: { url: receiver.url },\n },\n },\n z.object({}),\n );\n expect(subscriptions.size).toBe(0);\n\n let connections = 0;\n const privateTarget = net.createServer((socket) => {\n connections += 1;\n socket.destroy();\n });\n await new Promise<void>((resolve) =>\n privateTarget.listen(0, \"127.0.0.1\", resolve),\n );\n const { port: privatePort } = privateTarget.address() as AddressInfo;\n const nodeEnv = process.env.NODE_ENV;\n process.env.NODE_ENV = \"production\";\n try {\n await expect(\n client.request(\n {\n method: \"events/subscribe\",\n params: {\n name: \"comment.created\",\n arguments: { documentId: \"doc-1\" },\n delivery: {\n url: `https://127.0.0.1:${privatePort}/hooks`,\n secret,\n },\n },\n },\n z.object({}),\n ),\n ).rejects.toMatchObject({ code: -32015 });\n expect(connections).toBe(0);\n } finally {\n process.env.NODE_ENV = nodeEnv;\n privateTarget.close();\n }\n });\n});\n"]}
@@ -1,11 +1,31 @@
1
1
  import type http from "node:http";
2
+ import { type McpHttpHandler } from "@modelcontextprotocol/server";
2
3
  import express from "express";
3
- import type { McpServer } from "./server.js";
4
- export declare function createApp({ mcpServer, httpServer, errorMiddleware, }: {
5
- mcpServer: McpServer;
4
+ import type { Skybridge } from "./app.js";
5
+ import type { JsonOptions } from "./server.js";
6
+ type SkybridgeApp = Pick<Skybridge, "express" | "createServerInstance" | "ready">;
7
+ /**
8
+ * The `/mcp` fetch handler for a {@link Skybridge} app: a `Request` →
9
+ * `Response` function that builds a fresh MCP server per request.
10
+ *
11
+ * @internal
12
+ */
13
+ export declare function buildMcpHandler(skybridgeApp: SkybridgeApp): McpHttpHandler;
14
+ /**
15
+ * Build the bare Express app a {@link Skybridge} instance owns: the instance
16
+ * plus the built-in `express.json()` body parser, tuned by the `json` config
17
+ * field.
18
+ *
19
+ * @internal
20
+ */
21
+ export declare function createBaseApp(json?: JsonOptions): express.Express;
22
+ export declare function createApp({ app: skybridgeApp, httpServer, mcpHandler, errorMiddleware, }: {
23
+ app: SkybridgeApp;
6
24
  httpServer: http.Server;
25
+ mcpHandler?: McpHttpHandler;
7
26
  errorMiddleware?: {
8
27
  path?: string;
9
28
  handlers: express.ErrorRequestHandler[];
10
29
  }[];
11
30
  }): Promise<express.Express>;
31
+ export {};
@@ -1,7 +1,41 @@
1
1
  import path from "node:path";
2
- import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
2
+ import { toNodeHandler } from "@modelcontextprotocol/node";
3
+ import { createMcpHandler, UnsupportedProtocolVersionError, } from "@modelcontextprotocol/server";
3
4
  import cors from "cors";
4
5
  import express from "express";
6
+ /**
7
+ * The `/mcp` fetch handler for a {@link Skybridge} app: a `Request` →
8
+ * `Response` function that builds a fresh MCP server per request.
9
+ *
10
+ * @internal
11
+ */
12
+ export function buildMcpHandler(skybridgeApp) {
13
+ return createMcpHandler(() => skybridgeApp.createServerInstance(), {
14
+ onerror: (error) => {
15
+ if (error instanceof UnsupportedProtocolVersionError) {
16
+ return;
17
+ }
18
+ console.error("Error handling MCP request:", error);
19
+ },
20
+ });
21
+ }
22
+ /**
23
+ * Build the bare Express app a {@link Skybridge} instance owns: the instance
24
+ * plus the built-in `express.json()` body parser, tuned by the `json` config
25
+ * field.
26
+ *
27
+ * @internal
28
+ */
29
+ export function createBaseApp(json) {
30
+ const app = express();
31
+ app.disable("x-powered-by");
32
+ app.use((_req, res, next) => {
33
+ res.setHeader("X-Powered-By", "skybridge");
34
+ next();
35
+ });
36
+ app.use(express.json(json));
37
+ return app;
38
+ }
5
39
  function parseControlPort(raw) {
6
40
  if (raw === undefined) {
7
41
  return null;
@@ -32,8 +66,9 @@ function defaultErrorHandler(err, _req, res, _next) {
32
66
  });
33
67
  }
34
68
  }
35
- export async function createApp({ mcpServer, httpServer, errorMiddleware = [], }) {
36
- const app = mcpServer.express;
69
+ export async function createApp({ app: skybridgeApp, httpServer, mcpHandler, errorMiddleware = [], }) {
70
+ await skybridgeApp.ready();
71
+ const app = skybridgeApp.express;
37
72
  // Read `process.env.NODE_ENV` inline: wrangler/esbuild only substitute the literal expression,
38
73
  // so a local const would defeat dead-code elimination of the dev-only imports below.
39
74
  if (process.env.NODE_ENV !== "production") {
@@ -55,43 +90,19 @@ export async function createApp({ mcpServer, httpServer, errorMiddleware = [], }
55
90
  app.use("/assets", cors());
56
91
  app.use("/assets", express.static(assetsPath));
57
92
  }
58
- app.use("/mcp", mcpMiddleware(mcpServer));
93
+ app.use("/mcp", mcpMiddleware(mcpHandler ?? buildMcpHandler(skybridgeApp)));
59
94
  applyMiddlewares(app, errorMiddleware);
60
95
  app.use("/mcp", defaultErrorHandler);
61
96
  return app;
62
97
  }
63
- const mcpMiddleware = (server) => {
98
+ const mcpMiddleware = (mcpHandler) => {
99
+ const handler = toNodeHandler(mcpHandler);
64
100
  return async (req, res, next) => {
65
- if (req.method !== "POST") {
66
- res.writeHead(405).end(JSON.stringify({
67
- jsonrpc: "2.0",
68
- error: {
69
- code: -32000,
70
- message: "Method not allowed.",
71
- },
72
- id: null,
73
- }));
74
- return;
75
- }
76
101
  try {
77
- const transport = new StreamableHTTPServerTransport({
78
- sessionIdGenerator: undefined,
79
- // Respond with a single JSON body instead of SSE. Skybridge's stateless
80
- // transport never streams server-initiated messages, so SSE adds no
81
- // capability — and on workerd specifically, `cloudflare:node`'s http
82
- // bridge silently drops chunked writes that happen after the request
83
- // handler awaits, which manifests as a 200 with empty body for any
84
- // async tools/call.
85
- enableJsonResponse: true,
86
- });
87
- res.on("close", () => {
88
- transport.close();
89
- });
90
- await server.connectStatelessTransport(transport);
91
102
  // Express strips the mount path from req.url (e.g. "/mcp" becomes "/").
92
- // Restore it so the SDK builds the correct requestInfo.url.
103
+ // Restore it so the SDK builds the correct request URL.
93
104
  req.url = req.originalUrl;
94
- await transport.handleRequest(req, res, req.body);
105
+ await handler(req, res, req.body);
95
106
  }
96
107
  catch (error) {
97
108
  next(error);
@@ -1 +1 @@
1
- {"version":3,"file":"express.js","sourceRoot":"","sources":["../../src/server/express.ts"],"names":[],"mappings":"AACA,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,6BAA6B,EAAE,MAAM,oDAAoD,CAAC;AACnG,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,OAAO,MAAM,SAAS,CAAC;AAG9B,SAAS,gBAAgB,CAAC,GAAuB;IAC/C,IAAI,GAAG,KAAK,SAAS,EAAE,CAAC;QACtB,OAAO,IAAI,CAAC;IACd,CAAC;IACD,MAAM,CAAC,GAAG,MAAM,CAAC,QAAQ,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;IACnC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,KAAK,EAAE,CAAC;QAChD,OAAO,IAAI,CAAC;IACd,CAAC;IACD,OAAO,CAAC,CAAC;AACX,CAAC;AAED,SAAS,gBAAgB,CACvB,GAAoB,EACpB,WAGE;IAEF,KAAK,MAAM,UAAU,IAAI,WAAW,EAAE,CAAC;QACrC,IAAI,UAAU,CAAC,IAAI,EAAE,CAAC;YACpB,GAAG,CAAC,GAAG,CAAC,UAAU,CAAC,IAAI,EAAE,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC;QACnD,CAAC;aAAM,CAAC;YACN,GAAG,CAAC,GAAG,CAAC,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC;QAClC,CAAC;IACH,CAAC;AACH,CAAC;AAED,SAAS,mBAAmB,CAC1B,GAAY,EACZ,IAAqB,EACrB,GAAqB,EACrB,KAA2B;IAE3B,OAAO,CAAC,KAAK,CAAC,6BAA6B,EAAE,GAAG,CAAC,CAAC;IAClD,IAAI,CAAC,GAAG,CAAC,WAAW,EAAE,CAAC;QACrB,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC;YACnB,OAAO,EAAE,KAAK;YACd,KAAK,EAAE,EAAE,IAAI,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,uBAAuB,EAAE;YACzD,EAAE,EAAE,IAAI;SACT,CAAC,CAAC;IACL,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,EAC9B,SAAS,EACT,UAAU,EACV,eAAe,GAAG,EAAE,GAQrB;IACC,MAAM,GAAG,GAAG,SAAS,CAAC,OAAO,CAAC;IAE9B,+FAA+F;IAC/F,qFAAqF;IACrF,IAAI,OAAO,CAAC,GAAG,CAAC,QAAQ,KAAK,YAAY,EAAE,CAAC;QAC1C,MAAM,EAAE,oBAAoB,EAAE,GAAG,MAAM,MAAM,CAAC,qBAAqB,CAAC,CAAC;QACrE,GAAG,CAAC,GAAG,CAAC,MAAM,oBAAoB,EAAE,CAAC,CAAC;QACtC,MAAM,EAAE,cAAc,EAAE,GAAG,MAAM,MAAM,CAAC,qBAAqB,CAAC,CAAC;QAC/D,GAAG,CAAC,GAAG,CAAC,MAAM,cAAc,CAAC,UAAU,CAAC,CAAC,CAAC;QAE1C,MAAM,WAAW,GAAG,gBAAgB,CAAC,OAAO,CAAC,GAAG,CAAC,qBAAqB,CAAC,CAAC;QACxE,IAAI,WAAW,KAAK,IAAI,EAAE,CAAC;YACzB,MAAM,EAAE,uBAAuB,EAAE,GAAG,MAAM,MAAM,CAC9C,0BAA0B,CAC3B,CAAC;YACF,GAAG,CAAC,GAAG,CAAC,uBAAuB,CAAC,WAAW,CAAC,CAAC,CAAC;QAChD,CAAC;aAAM,IAAI,OAAO,CAAC,GAAG,CAAC,qBAAqB,KAAK,SAAS,EAAE,CAAC;YAC3D,OAAO,CAAC,IAAI,CACV,0CAA0C,OAAO,CAAC,GAAG,CAAC,qBAAqB,EAAE,CAC9E,CAAC;QACJ,CAAC;IACH,CAAC;SAAM,CAAC;QACN,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAC;QAE9D,GAAG,CAAC,GAAG,CAAC,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAC3B,GAAG,CAAC,GAAG,CAAC,SAAS,EAAE,OAAO,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC;IACjD,CAAC;IAED,GAAG,CAAC,GAAG,CAAC,MAAM,EAAE,aAAa,CAAC,SAAS,CAAC,CAAC,CAAC;IAE1C,gBAAgB,CAAC,GAAG,EAAE,eAAe,CAAC,CAAC;IAEvC,GAAG,CAAC,GAAG,CAAC,MAAM,EAAE,mBAAmB,CAAC,CAAC;IAErC,OAAO,GAAG,CAAC;AACb,CAAC;AAED,MAAM,aAAa,GAAG,CAAC,MAAiB,EAA0B,EAAE;IAClE,OAAO,KAAK,EACV,GAAoB,EACpB,GAAqB,EACrB,IAA0B,EAC1B,EAAE;QACF,IAAI,GAAG,CAAC,MAAM,KAAK,MAAM,EAAE,CAAC;YAC1B,GAAG,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,GAAG,CACpB,IAAI,CAAC,SAAS,CAAC;gBACb,OAAO,EAAE,KAAK;gBACd,KAAK,EAAE;oBACL,IAAI,EAAE,CAAC,KAAK;oBACZ,OAAO,EAAE,qBAAqB;iBAC/B;gBACD,EAAE,EAAE,IAAI;aACT,CAAC,CACH,CAAC;YACF,OAAO;QACT,CAAC;QAED,IAAI,CAAC;YACH,MAAM,SAAS,GAAG,IAAI,6BAA6B,CAAC;gBAClD,kBAAkB,EAAE,SAAS;gBAC7B,wEAAwE;gBACxE,oEAAoE;gBACpE,qEAAqE;gBACrE,qEAAqE;gBACrE,mEAAmE;gBACnE,oBAAoB;gBACpB,kBAAkB,EAAE,IAAI;aACzB,CAAC,CAAC;YAEH,GAAG,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE;gBACnB,SAAS,CAAC,KAAK,EAAE,CAAC;YACpB,CAAC,CAAC,CAAC;YAEH,MAAM,MAAM,CAAC,yBAAyB,CAAC,SAAS,CAAC,CAAC;YAClD,wEAAwE;YACxE,4DAA4D;YAC5D,GAAG,CAAC,GAAG,GAAG,GAAG,CAAC,WAAW,CAAC;YAC1B,MAAM,SAAS,CAAC,aAAa,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,IAAI,CAAC,CAAC;QACpD,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,CAAC,KAAK,CAAC,CAAC;QACd,CAAC;IACH,CAAC,CAAC;AACJ,CAAC,CAAC","sourcesContent":["import type http from \"node:http\";\nimport path from \"node:path\";\nimport { StreamableHTTPServerTransport } from \"@modelcontextprotocol/sdk/server/streamableHttp.js\";\nimport cors from \"cors\";\nimport express from \"express\";\nimport type { McpServer } from \"./server.js\";\n\nfunction parseControlPort(raw: string | undefined): number | null {\n if (raw === undefined) {\n return null;\n }\n const n = Number.parseInt(raw, 10);\n if (!Number.isFinite(n) || n <= 0 || n >= 65536) {\n return null;\n }\n return n;\n}\n\nfunction applyMiddlewares(\n app: express.Express,\n middlewares: Array<{\n path?: string;\n handlers: express.ErrorRequestHandler[];\n }>,\n): void {\n for (const middleware of middlewares) {\n if (middleware.path) {\n app.use(middleware.path, ...middleware.handlers);\n } else {\n app.use(...middleware.handlers);\n }\n }\n}\n\nfunction defaultErrorHandler(\n err: unknown,\n _req: express.Request,\n res: express.Response,\n _next: express.NextFunction,\n) {\n console.error(\"Error handling MCP request:\", err);\n if (!res.headersSent) {\n res.status(500).json({\n jsonrpc: \"2.0\",\n error: { code: -32603, message: \"Internal server error\" },\n id: null,\n });\n }\n}\n\nexport async function createApp({\n mcpServer,\n httpServer,\n errorMiddleware = [],\n}: {\n mcpServer: McpServer;\n httpServer: http.Server;\n errorMiddleware?: {\n path?: string;\n handlers: express.ErrorRequestHandler[];\n }[];\n}): Promise<express.Express> {\n const app = mcpServer.express;\n\n // Read `process.env.NODE_ENV` inline: wrangler/esbuild only substitute the literal expression,\n // so a local const would defeat dead-code elimination of the dev-only imports below.\n if (process.env.NODE_ENV !== \"production\") {\n const { devtoolsStaticServer } = await import(\"@skybridge/devtools\");\n app.use(await devtoolsStaticServer());\n const { viewsDevServer } = await import(\"./viewsDevServer.js\");\n app.use(await viewsDevServer(httpServer));\n\n const controlPort = parseControlPort(process.env.__TUNNEL_CONTROL_PORT);\n if (controlPort !== null) {\n const { createTunnelProxyRouter } = await import(\n \"./tunnel-proxy-router.js\"\n );\n app.use(createTunnelProxyRouter(controlPort));\n } else if (process.env.__TUNNEL_CONTROL_PORT !== undefined) {\n console.warn(\n `Ignoring invalid __TUNNEL_CONTROL_PORT=${process.env.__TUNNEL_CONTROL_PORT}`,\n );\n }\n } else {\n const assetsPath = path.join(process.cwd(), \"dist\", \"assets\");\n\n app.use(\"/assets\", cors());\n app.use(\"/assets\", express.static(assetsPath));\n }\n\n app.use(\"/mcp\", mcpMiddleware(mcpServer));\n\n applyMiddlewares(app, errorMiddleware);\n\n app.use(\"/mcp\", defaultErrorHandler);\n\n return app;\n}\n\nconst mcpMiddleware = (server: McpServer): express.RequestHandler => {\n return async (\n req: express.Request,\n res: express.Response,\n next: express.NextFunction,\n ) => {\n if (req.method !== \"POST\") {\n res.writeHead(405).end(\n JSON.stringify({\n jsonrpc: \"2.0\",\n error: {\n code: -32000,\n message: \"Method not allowed.\",\n },\n id: null,\n }),\n );\n return;\n }\n\n try {\n const transport = new StreamableHTTPServerTransport({\n sessionIdGenerator: undefined,\n // Respond with a single JSON body instead of SSE. Skybridge's stateless\n // transport never streams server-initiated messages, so SSE adds no\n // capability — and on workerd specifically, `cloudflare:node`'s http\n // bridge silently drops chunked writes that happen after the request\n // handler awaits, which manifests as a 200 with empty body for any\n // async tools/call.\n enableJsonResponse: true,\n });\n\n res.on(\"close\", () => {\n transport.close();\n });\n\n await server.connectStatelessTransport(transport);\n // Express strips the mount path from req.url (e.g. \"/mcp\" becomes \"/\").\n // Restore it so the SDK builds the correct requestInfo.url.\n req.url = req.originalUrl;\n await transport.handleRequest(req, res, req.body);\n } catch (error) {\n next(error);\n }\n };\n};\n"]}
1
+ {"version":3,"file":"express.js","sourceRoot":"","sources":["../../src/server/express.ts"],"names":[],"mappings":"AACA,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAC;AAC3D,OAAO,EACL,gBAAgB,EAEhB,+BAA+B,GAChC,MAAM,8BAA8B,CAAC;AACtC,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,OAAO,MAAM,SAAS,CAAC;AAS9B;;;;;GAKG;AACH,MAAM,UAAU,eAAe,CAAC,YAA0B;IACxD,OAAO,gBAAgB,CAAC,GAAG,EAAE,CAAC,YAAY,CAAC,oBAAoB,EAAE,EAAE;QACjE,OAAO,EAAE,CAAC,KAAK,EAAE,EAAE;YACjB,IAAI,KAAK,YAAY,+BAA+B,EAAE,CAAC;gBACrD,OAAO;YACT,CAAC;YACD,OAAO,CAAC,KAAK,CAAC,6BAA6B,EAAE,KAAK,CAAC,CAAC;QACtD,CAAC;KACF,CAAC,CAAC;AACL,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,aAAa,CAAC,IAAkB;IAC9C,MAAM,GAAG,GAAG,OAAO,EAAE,CAAC;IACtB,GAAG,CAAC,OAAO,CAAC,cAAc,CAAC,CAAC;IAC5B,GAAG,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE;QAC1B,GAAG,CAAC,SAAS,CAAC,cAAc,EAAE,WAAW,CAAC,CAAC;QAC3C,IAAI,EAAE,CAAC;IACT,CAAC,CAAC,CAAC;IACH,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;IAC5B,OAAO,GAAG,CAAC;AACb,CAAC;AAED,SAAS,gBAAgB,CAAC,GAAuB;IAC/C,IAAI,GAAG,KAAK,SAAS,EAAE,CAAC;QACtB,OAAO,IAAI,CAAC;IACd,CAAC;IACD,MAAM,CAAC,GAAG,MAAM,CAAC,QAAQ,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;IACnC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,KAAK,EAAE,CAAC;QAChD,OAAO,IAAI,CAAC;IACd,CAAC;IACD,OAAO,CAAC,CAAC;AACX,CAAC;AAED,SAAS,gBAAgB,CACvB,GAAoB,EACpB,WAGE;IAEF,KAAK,MAAM,UAAU,IAAI,WAAW,EAAE,CAAC;QACrC,IAAI,UAAU,CAAC,IAAI,EAAE,CAAC;YACpB,GAAG,CAAC,GAAG,CAAC,UAAU,CAAC,IAAI,EAAE,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC;QACnD,CAAC;aAAM,CAAC;YACN,GAAG,CAAC,GAAG,CAAC,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC;QAClC,CAAC;IACH,CAAC;AACH,CAAC;AAED,SAAS,mBAAmB,CAC1B,GAAY,EACZ,IAAqB,EACrB,GAAqB,EACrB,KAA2B;IAE3B,OAAO,CAAC,KAAK,CAAC,6BAA6B,EAAE,GAAG,CAAC,CAAC;IAClD,IAAI,CAAC,GAAG,CAAC,WAAW,EAAE,CAAC;QACrB,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC;YACnB,OAAO,EAAE,KAAK;YACd,KAAK,EAAE,EAAE,IAAI,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,uBAAuB,EAAE;YACzD,EAAE,EAAE,IAAI;SACT,CAAC,CAAC;IACL,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,EAC9B,GAAG,EAAE,YAAY,EACjB,UAAU,EACV,UAAU,EACV,eAAe,GAAG,EAAE,GASrB;IACC,MAAM,YAAY,CAAC,KAAK,EAAE,CAAC;IAC3B,MAAM,GAAG,GAAG,YAAY,CAAC,OAAO,CAAC;IAEjC,+FAA+F;IAC/F,qFAAqF;IACrF,IAAI,OAAO,CAAC,GAAG,CAAC,QAAQ,KAAK,YAAY,EAAE,CAAC;QAC1C,MAAM,EAAE,oBAAoB,EAAE,GAAG,MAAM,MAAM,CAAC,qBAAqB,CAAC,CAAC;QACrE,GAAG,CAAC,GAAG,CAAC,MAAM,oBAAoB,EAAE,CAAC,CAAC;QACtC,MAAM,EAAE,cAAc,EAAE,GAAG,MAAM,MAAM,CAAC,qBAAqB,CAAC,CAAC;QAC/D,GAAG,CAAC,GAAG,CAAC,MAAM,cAAc,CAAC,UAAU,CAAC,CAAC,CAAC;QAE1C,MAAM,WAAW,GAAG,gBAAgB,CAAC,OAAO,CAAC,GAAG,CAAC,qBAAqB,CAAC,CAAC;QACxE,IAAI,WAAW,KAAK,IAAI,EAAE,CAAC;YACzB,MAAM,EAAE,uBAAuB,EAAE,GAAG,MAAM,MAAM,CAC9C,0BAA0B,CAC3B,CAAC;YACF,GAAG,CAAC,GAAG,CAAC,uBAAuB,CAAC,WAAW,CAAC,CAAC,CAAC;QAChD,CAAC;aAAM,IAAI,OAAO,CAAC,GAAG,CAAC,qBAAqB,KAAK,SAAS,EAAE,CAAC;YAC3D,OAAO,CAAC,IAAI,CACV,0CAA0C,OAAO,CAAC,GAAG,CAAC,qBAAqB,EAAE,CAC9E,CAAC;QACJ,CAAC;IACH,CAAC;SAAM,CAAC;QACN,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAC;QAE9D,GAAG,CAAC,GAAG,CAAC,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAC3B,GAAG,CAAC,GAAG,CAAC,SAAS,EAAE,OAAO,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC;IACjD,CAAC;IAED,GAAG,CAAC,GAAG,CAAC,MAAM,EAAE,aAAa,CAAC,UAAU,IAAI,eAAe,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;IAE5E,gBAAgB,CAAC,GAAG,EAAE,eAAe,CAAC,CAAC;IAEvC,GAAG,CAAC,GAAG,CAAC,MAAM,EAAE,mBAAmB,CAAC,CAAC;IAErC,OAAO,GAAG,CAAC;AACb,CAAC;AAED,MAAM,aAAa,GAAG,CAAC,UAA0B,EAA0B,EAAE;IAC3E,MAAM,OAAO,GAAG,aAAa,CAAC,UAAU,CAAC,CAAC;IAE1C,OAAO,KAAK,EACV,GAAoB,EACpB,GAAqB,EACrB,IAA0B,EAC1B,EAAE;QACF,IAAI,CAAC;YACH,wEAAwE;YACxE,wDAAwD;YACxD,GAAG,CAAC,GAAG,GAAG,GAAG,CAAC,WAAW,CAAC;YAC1B,MAAM,OAAO,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,IAAI,CAAC,CAAC;QACpC,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,CAAC,KAAK,CAAC,CAAC;QACd,CAAC;IACH,CAAC,CAAC;AACJ,CAAC,CAAC","sourcesContent":["import type http from \"node:http\";\nimport path from \"node:path\";\nimport { toNodeHandler } from \"@modelcontextprotocol/node\";\nimport {\n createMcpHandler,\n type McpHttpHandler,\n UnsupportedProtocolVersionError,\n} from \"@modelcontextprotocol/server\";\nimport cors from \"cors\";\nimport express from \"express\";\nimport type { Skybridge } from \"./app.js\";\nimport type { JsonOptions } from \"./server.js\";\n\ntype SkybridgeApp = Pick<\n Skybridge,\n \"express\" | \"createServerInstance\" | \"ready\"\n>;\n\n/**\n * The `/mcp` fetch handler for a {@link Skybridge} app: a `Request` →\n * `Response` function that builds a fresh MCP server per request.\n *\n * @internal\n */\nexport function buildMcpHandler(skybridgeApp: SkybridgeApp): McpHttpHandler {\n return createMcpHandler(() => skybridgeApp.createServerInstance(), {\n onerror: (error) => {\n if (error instanceof UnsupportedProtocolVersionError) {\n return;\n }\n console.error(\"Error handling MCP request:\", error);\n },\n });\n}\n\n/**\n * Build the bare Express app a {@link Skybridge} instance owns: the instance\n * plus the built-in `express.json()` body parser, tuned by the `json` config\n * field.\n *\n * @internal\n */\nexport function createBaseApp(json?: JsonOptions): express.Express {\n const app = express();\n app.disable(\"x-powered-by\");\n app.use((_req, res, next) => {\n res.setHeader(\"X-Powered-By\", \"skybridge\");\n next();\n });\n app.use(express.json(json));\n return app;\n}\n\nfunction parseControlPort(raw: string | undefined): number | null {\n if (raw === undefined) {\n return null;\n }\n const n = Number.parseInt(raw, 10);\n if (!Number.isFinite(n) || n <= 0 || n >= 65536) {\n return null;\n }\n return n;\n}\n\nfunction applyMiddlewares(\n app: express.Express,\n middlewares: Array<{\n path?: string;\n handlers: express.ErrorRequestHandler[];\n }>,\n): void {\n for (const middleware of middlewares) {\n if (middleware.path) {\n app.use(middleware.path, ...middleware.handlers);\n } else {\n app.use(...middleware.handlers);\n }\n }\n}\n\nfunction defaultErrorHandler(\n err: unknown,\n _req: express.Request,\n res: express.Response,\n _next: express.NextFunction,\n) {\n console.error(\"Error handling MCP request:\", err);\n if (!res.headersSent) {\n res.status(500).json({\n jsonrpc: \"2.0\",\n error: { code: -32603, message: \"Internal server error\" },\n id: null,\n });\n }\n}\n\nexport async function createApp({\n app: skybridgeApp,\n httpServer,\n mcpHandler,\n errorMiddleware = [],\n}: {\n app: SkybridgeApp;\n httpServer: http.Server;\n mcpHandler?: McpHttpHandler;\n errorMiddleware?: {\n path?: string;\n handlers: express.ErrorRequestHandler[];\n }[];\n}): Promise<express.Express> {\n await skybridgeApp.ready();\n const app = skybridgeApp.express;\n\n // Read `process.env.NODE_ENV` inline: wrangler/esbuild only substitute the literal expression,\n // so a local const would defeat dead-code elimination of the dev-only imports below.\n if (process.env.NODE_ENV !== \"production\") {\n const { devtoolsStaticServer } = await import(\"@skybridge/devtools\");\n app.use(await devtoolsStaticServer());\n const { viewsDevServer } = await import(\"./viewsDevServer.js\");\n app.use(await viewsDevServer(httpServer));\n\n const controlPort = parseControlPort(process.env.__TUNNEL_CONTROL_PORT);\n if (controlPort !== null) {\n const { createTunnelProxyRouter } = await import(\n \"./tunnel-proxy-router.js\"\n );\n app.use(createTunnelProxyRouter(controlPort));\n } else if (process.env.__TUNNEL_CONTROL_PORT !== undefined) {\n console.warn(\n `Ignoring invalid __TUNNEL_CONTROL_PORT=${process.env.__TUNNEL_CONTROL_PORT}`,\n );\n }\n } else {\n const assetsPath = path.join(process.cwd(), \"dist\", \"assets\");\n\n app.use(\"/assets\", cors());\n app.use(\"/assets\", express.static(assetsPath));\n }\n\n app.use(\"/mcp\", mcpMiddleware(mcpHandler ?? buildMcpHandler(skybridgeApp)));\n\n applyMiddlewares(app, errorMiddleware);\n\n app.use(\"/mcp\", defaultErrorHandler);\n\n return app;\n}\n\nconst mcpMiddleware = (mcpHandler: McpHttpHandler): express.RequestHandler => {\n const handler = toNodeHandler(mcpHandler);\n\n return async (\n req: express.Request,\n res: express.Response,\n next: express.NextFunction,\n ) => {\n try {\n // Express strips the mount path from req.url (e.g. \"/mcp\" becomes \"/\").\n // Restore it so the SDK builds the correct request URL.\n req.url = req.originalUrl;\n await handler(req, res, req.body);\n } catch (error) {\n next(error);\n }\n };\n};\n"]}