skybridge 0.0.0-dev.ecfdeac → 0.0.0-dev.ed005a8

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 (345) hide show
  1. package/README.md +10 -16
  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 +89 -0
  20. package/dist/cli/run-plain.js.map +1 -0
  21. package/dist/cli/use-execute-steps.d.ts +1 -1
  22. package/dist/cli/use-nodemon.d.ts +14 -0
  23. package/dist/cli/use-nodemon.js +81 -62
  24. package/dist/cli/use-nodemon.js.map +1 -1
  25. package/dist/cli/use-typescript-check.d.ts +8 -2
  26. package/dist/cli/use-typescript-check.js +70 -67
  27. package/dist/cli/use-typescript-check.js.map +1 -1
  28. package/dist/commands/build.d.ts +0 -2
  29. package/dist/commands/build.js +2 -61
  30. package/dist/commands/build.js.map +1 -1
  31. package/dist/commands/dev.d.ts +1 -0
  32. package/dist/commands/dev.js +35 -3
  33. package/dist/commands/dev.js.map +1 -1
  34. package/dist/commands/start.js +2 -2
  35. package/dist/commands/start.js.map +1 -1
  36. package/dist/context-warnings.d.ts +5 -0
  37. package/dist/context-warnings.js +31 -0
  38. package/dist/context-warnings.js.map +1 -0
  39. package/dist/context-warnings.test.js +28 -0
  40. package/dist/context-warnings.test.js.map +1 -0
  41. package/dist/server/app.d.ts +194 -0
  42. package/dist/server/app.js +266 -0
  43. package/dist/server/app.js.map +1 -0
  44. package/dist/server/app.test.js +97 -0
  45. package/dist/server/app.test.js.map +1 -0
  46. package/dist/server/auth/discovery.d.ts +2 -1
  47. package/dist/server/auth/discovery.js +2 -2
  48. package/dist/server/auth/discovery.js.map +1 -1
  49. package/dist/server/auth/index.d.ts +35 -5
  50. package/dist/server/auth/index.js.map +1 -1
  51. package/dist/server/auth/providers/auth0.d.ts +25 -3
  52. package/dist/server/auth/providers/auth0.js +13 -8
  53. package/dist/server/auth/providers/auth0.js.map +1 -1
  54. package/dist/server/auth/providers/auth0.test.js +5 -3
  55. package/dist/server/auth/providers/auth0.test.js.map +1 -1
  56. package/dist/server/auth/providers/authplane.d.ts +49 -0
  57. package/dist/server/auth/providers/authplane.js +57 -0
  58. package/dist/server/auth/providers/authplane.js.map +1 -0
  59. package/dist/server/auth/providers/authplane.test.js +143 -0
  60. package/dist/server/auth/providers/authplane.test.js.map +1 -0
  61. package/dist/server/auth/providers/clerk.d.ts +40 -3
  62. package/dist/server/auth/providers/clerk.js +4 -1
  63. package/dist/server/auth/providers/clerk.js.map +1 -1
  64. package/dist/server/auth/providers/clerk.test.js +5 -3
  65. package/dist/server/auth/providers/clerk.test.js.map +1 -1
  66. package/dist/server/auth/providers/custom.d.ts +20 -4
  67. package/dist/server/auth/providers/custom.js +33 -11
  68. package/dist/server/auth/providers/custom.js.map +1 -1
  69. package/dist/server/auth/providers/custom.test.js +71 -17
  70. package/dist/server/auth/providers/custom.test.js.map +1 -1
  71. package/dist/server/auth/providers/descope.d.ts +37 -6
  72. package/dist/server/auth/providers/descope.js +18 -5
  73. package/dist/server/auth/providers/descope.js.map +1 -1
  74. package/dist/server/auth/providers/descope.test.js +42 -17
  75. package/dist/server/auth/providers/descope.test.js.map +1 -1
  76. package/dist/server/auth/providers/stytch.d.ts +16 -3
  77. package/dist/server/auth/providers/stytch.js +4 -1
  78. package/dist/server/auth/providers/stytch.js.map +1 -1
  79. package/dist/server/auth/providers/verify-spy.d.ts +12 -0
  80. package/dist/server/auth/providers/verify-spy.js +21 -0
  81. package/dist/server/auth/providers/verify-spy.js.map +1 -0
  82. package/dist/server/auth/providers/workos.d.ts +25 -3
  83. package/dist/server/auth/providers/workos.js +4 -1
  84. package/dist/server/auth/providers/workos.js.map +1 -1
  85. package/dist/server/auth/security-schemes.d.ts +23 -0
  86. package/dist/server/auth/security-schemes.js +64 -0
  87. package/dist/server/auth/security-schemes.js.map +1 -0
  88. package/dist/server/auth/security-schemes.test.d.ts +1 -0
  89. package/dist/server/auth/security-schemes.test.js +99 -0
  90. package/dist/server/auth/security-schemes.test.js.map +1 -0
  91. package/dist/server/auth/setup.d.ts +3 -1
  92. package/dist/server/auth/setup.js +66 -26
  93. package/dist/server/auth/setup.js.map +1 -1
  94. package/dist/server/auth/setup.test.js +349 -20
  95. package/dist/server/auth/setup.test.js.map +1 -1
  96. package/dist/server/auth/verify.d.ts +37 -3
  97. package/dist/server/auth/verify.js +27 -3
  98. package/dist/server/auth/verify.js.map +1 -1
  99. package/dist/server/auth/verify.test.js +49 -0
  100. package/dist/server/auth/verify.test.js.map +1 -1
  101. package/dist/server/auth-extra.test-d.js +130 -0
  102. package/dist/server/auth-extra.test-d.js.map +1 -0
  103. package/dist/server/auth.d.ts +43 -6
  104. package/dist/server/auth.js +4 -5
  105. package/dist/server/auth.js.map +1 -1
  106. package/dist/server/build-manifest.test.js +7 -6
  107. package/dist/server/build-manifest.test.js.map +1 -1
  108. package/dist/server/content-helpers.d.ts +1 -1
  109. package/dist/server/content-helpers.js.map +1 -1
  110. package/dist/server/express.d.ts +23 -3
  111. package/dist/server/express.js +38 -32
  112. package/dist/server/express.js.map +1 -1
  113. package/dist/server/express.test.js +220 -103
  114. package/dist/server/express.test.js.map +1 -1
  115. package/dist/server/file-ref.d.ts +1 -1
  116. package/dist/server/file-ref.js +1 -1
  117. package/dist/server/file-ref.js.map +1 -1
  118. package/dist/server/host.d.ts +2 -0
  119. package/dist/server/host.js +11 -0
  120. package/dist/server/host.js.map +1 -0
  121. package/dist/server/host.test.d.ts +1 -0
  122. package/dist/server/host.test.js +13 -0
  123. package/dist/server/host.test.js.map +1 -0
  124. package/dist/server/index.d.ts +16 -9
  125. package/dist/server/index.js +9 -3
  126. package/dist/server/index.js.map +1 -1
  127. package/dist/server/mcp-errors.test.d.ts +1 -0
  128. package/dist/server/mcp-errors.test.js +41 -0
  129. package/dist/server/mcp-errors.test.js.map +1 -0
  130. package/dist/server/middleware.d.ts +35 -10
  131. package/dist/server/middleware.js +33 -0
  132. package/dist/server/middleware.js.map +1 -1
  133. package/dist/server/middleware.test-d.js +1 -1
  134. package/dist/server/middleware.test-d.js.map +1 -1
  135. package/dist/server/middleware.test.js +242 -134
  136. package/dist/server/middleware.test.js.map +1 -1
  137. package/dist/server/register-tool.test.d.ts +1 -0
  138. package/dist/server/register-tool.test.js +27 -0
  139. package/dist/server/register-tool.test.js.map +1 -0
  140. package/dist/server/server.d.ts +133 -116
  141. package/dist/server/server.js +251 -346
  142. package/dist/server/server.js.map +1 -1
  143. package/dist/server/skills-integration.test.d.ts +1 -0
  144. package/dist/server/skills-integration.test.js +86 -0
  145. package/dist/server/skills-integration.test.js.map +1 -0
  146. package/dist/server/skills.d.ts +27 -0
  147. package/dist/server/skills.js +188 -0
  148. package/dist/server/skills.js.map +1 -0
  149. package/dist/server/skills.test.d.ts +1 -0
  150. package/dist/server/skills.test.js +243 -0
  151. package/dist/server/skills.test.js.map +1 -0
  152. package/dist/server/templateHelper.d.ts +0 -3
  153. package/dist/server/templateHelper.js.map +1 -1
  154. package/dist/server/templates.generated.js +4 -8
  155. package/dist/server/templates.generated.js.map +1 -1
  156. package/dist/server/tool-extra.test.d.ts +1 -0
  157. package/dist/server/tool-extra.test.js +88 -0
  158. package/dist/server/tool-extra.test.js.map +1 -0
  159. package/dist/server/view-name.test-d.d.ts +1 -0
  160. package/dist/server/view-name.test-d.js +8 -0
  161. package/dist/server/view-name.test-d.js.map +1 -0
  162. package/dist/server/view-resource-resolution.test.js +105 -17
  163. package/dist/server/view-resource-resolution.test.js.map +1 -1
  164. package/dist/server/viewsDevServer.js +4 -1
  165. package/dist/server/viewsDevServer.js.map +1 -1
  166. package/dist/test/utils.d.ts +9 -11
  167. package/dist/test/utils.js +220 -201
  168. package/dist/test/utils.js.map +1 -1
  169. package/dist/test/view.test.js +105 -133
  170. package/dist/test/view.test.js.map +1 -1
  171. package/dist/views/index.d.ts +10 -0
  172. package/dist/views/index.js +3 -0
  173. package/dist/views/index.js.map +1 -0
  174. package/dist/{web/plugin → views}/scan-views.js +1 -1
  175. package/dist/views/scan-views.js.map +1 -0
  176. package/dist/views/scan-views.test.d.ts +1 -0
  177. package/dist/views/scan-views.test.js.map +1 -0
  178. package/dist/views/validate-view.d.ts +1 -0
  179. package/dist/{web/plugin → views}/validate-view.js +2 -2
  180. package/dist/views/validate-view.js.map +1 -0
  181. package/dist/views/validate-view.test.d.ts +1 -0
  182. package/dist/views/validate-view.test.js.map +1 -0
  183. package/dist/web/bridges/adaptor.d.ts +51 -0
  184. package/dist/web/bridges/adaptor.js +327 -0
  185. package/dist/web/bridges/adaptor.js.map +1 -0
  186. package/dist/web/bridges/adaptor.test.d.ts +1 -0
  187. package/dist/web/bridges/adaptor.test.js +225 -0
  188. package/dist/web/bridges/adaptor.test.js.map +1 -0
  189. package/dist/web/bridges/apps-sdk/bridge.d.ts +6 -2
  190. package/dist/web/bridges/apps-sdk/bridge.js +15 -4
  191. package/dist/web/bridges/apps-sdk/bridge.js.map +1 -1
  192. package/dist/web/bridges/apps-sdk/index.d.ts +0 -1
  193. package/dist/web/bridges/apps-sdk/index.js +0 -1
  194. package/dist/web/bridges/apps-sdk/index.js.map +1 -1
  195. package/dist/web/bridges/apps-sdk/use-apps-sdk-context.d.ts +1 -1
  196. package/dist/web/bridges/apps-sdk/use-apps-sdk-context.js +1 -1
  197. package/dist/web/bridges/apps-sdk/use-apps-sdk-context.js.map +1 -1
  198. package/dist/web/bridges/get-adaptor.d.ts +4 -4
  199. package/dist/web/bridges/get-adaptor.js +6 -11
  200. package/dist/web/bridges/get-adaptor.js.map +1 -1
  201. package/dist/web/bridges/get-adaptor.test.d.ts +1 -0
  202. package/dist/web/bridges/get-adaptor.test.js +32 -0
  203. package/dist/web/bridges/get-adaptor.test.js.map +1 -0
  204. package/dist/web/bridges/mcp-app/bridge.d.ts +13 -2
  205. package/dist/web/bridges/mcp-app/bridge.js +52 -8
  206. package/dist/web/bridges/mcp-app/bridge.js.map +1 -1
  207. package/dist/web/bridges/mcp-app/bridge.test.d.ts +1 -0
  208. package/dist/web/bridges/mcp-app/bridge.test.js +17 -0
  209. package/dist/web/bridges/mcp-app/bridge.test.js.map +1 -0
  210. package/dist/web/bridges/mcp-app/index.d.ts +0 -1
  211. package/dist/web/bridges/mcp-app/index.js +0 -1
  212. package/dist/web/bridges/mcp-app/index.js.map +1 -1
  213. package/dist/web/bridges/mcp-app/types.d.ts +2 -0
  214. package/dist/web/bridges/mcp-app/types.js.map +1 -1
  215. package/dist/web/bridges/mcp-app/use-mcp-app-context.d.ts +1 -1
  216. package/dist/web/bridges/mcp-app/use-mcp-app-context.js +1 -1
  217. package/dist/web/bridges/mcp-app/use-mcp-app-context.js.map +1 -1
  218. package/dist/web/bridges/mcp-app/use-mcp-app-context.test.js +1 -1
  219. package/dist/web/bridges/mcp-app/use-mcp-app-context.test.js.map +1 -1
  220. package/dist/web/bridges/mcp-app/view-tools.test.js +6 -7
  221. package/dist/web/bridges/mcp-app/view-tools.test.js.map +1 -1
  222. package/dist/web/bridges/types.d.ts +27 -12
  223. package/dist/web/bridges/types.js +14 -1
  224. package/dist/web/bridges/types.js.map +1 -1
  225. package/dist/web/bridges/types.test.d.ts +1 -0
  226. package/dist/web/bridges/types.test.js +19 -0
  227. package/dist/web/bridges/types.test.js.map +1 -0
  228. package/dist/web/components/modal-provider.d.ts +1 -1
  229. package/dist/web/components/modal-provider.js +4 -3
  230. package/dist/web/components/modal-provider.js.map +1 -1
  231. package/dist/web/create-store.test.js +10 -7
  232. package/dist/web/create-store.test.js.map +1 -1
  233. package/dist/web/data-llm.d.ts +1 -1
  234. package/dist/web/data-llm.test.js +20 -7
  235. package/dist/web/data-llm.test.js.map +1 -1
  236. package/dist/web/generate-helpers.d.ts +22 -18
  237. package/dist/web/generate-helpers.js +19 -15
  238. package/dist/web/generate-helpers.js.map +1 -1
  239. package/dist/web/generate-helpers.test-d.js +3 -3
  240. package/dist/web/generate-helpers.test-d.js.map +1 -1
  241. package/dist/web/generate-helpers.test.js +2 -2
  242. package/dist/web/generate-helpers.test.js.map +1 -1
  243. package/dist/web/hooks/index.d.ts +2 -1
  244. package/dist/web/hooks/index.js +2 -1
  245. package/dist/web/hooks/index.js.map +1 -1
  246. package/dist/web/hooks/test/utils.d.ts +2 -1
  247. package/dist/web/hooks/test/utils.js +1 -1
  248. package/dist/web/hooks/test/utils.js.map +1 -1
  249. package/dist/web/hooks/use-call-tool.test.js +39 -25
  250. package/dist/web/hooks/use-call-tool.test.js.map +1 -1
  251. package/dist/web/hooks/use-display-mode.d.ts +1 -1
  252. package/dist/web/hooks/use-display-mode.js +1 -1
  253. package/dist/web/hooks/use-display-mode.js.map +1 -1
  254. package/dist/web/hooks/use-display-mode.test.js +57 -21
  255. package/dist/web/hooks/use-display-mode.test.js.map +1 -1
  256. package/dist/web/hooks/use-download.d.ts +1 -3
  257. package/dist/web/hooks/use-download.js +1 -2
  258. package/dist/web/hooks/use-download.js.map +1 -1
  259. package/dist/web/hooks/use-download.test.js +18 -10
  260. package/dist/web/hooks/use-download.test.js.map +1 -1
  261. package/dist/web/hooks/use-files.test.js +11 -3
  262. package/dist/web/hooks/use-files.test.js.map +1 -1
  263. package/dist/web/hooks/use-host.d.ts +22 -0
  264. package/dist/web/hooks/use-host.js +30 -0
  265. package/dist/web/hooks/use-host.js.map +1 -0
  266. package/dist/web/hooks/use-host.test.d.ts +1 -0
  267. package/dist/web/hooks/use-host.test.js +59 -0
  268. package/dist/web/hooks/use-host.test.js.map +1 -0
  269. package/dist/web/hooks/use-open-external.test.js +15 -5
  270. package/dist/web/hooks/use-open-external.test.js.map +1 -1
  271. package/dist/web/hooks/use-register-view-tool.d.ts +2 -3
  272. package/dist/web/hooks/use-register-view-tool.js.map +1 -1
  273. package/dist/web/hooks/use-request-close.test.js +35 -40
  274. package/dist/web/hooks/use-request-close.test.js.map +1 -1
  275. package/dist/web/hooks/use-request-modal.test.js +11 -1
  276. package/dist/web/hooks/use-request-modal.test.js.map +1 -1
  277. package/dist/web/hooks/use-request-size.d.ts +1 -1
  278. package/dist/web/hooks/use-request-size.js +1 -1
  279. package/dist/web/hooks/use-request-size.js.map +1 -1
  280. package/dist/web/hooks/use-request-size.test.js +35 -53
  281. package/dist/web/hooks/use-request-size.test.js.map +1 -1
  282. package/dist/web/hooks/use-set-open-in-app-url.test.js +26 -12
  283. package/dist/web/hooks/use-set-open-in-app-url.test.js.map +1 -1
  284. package/dist/web/hooks/use-tool-info.d.ts +1 -20
  285. package/dist/web/hooks/use-tool-info.js +0 -1
  286. package/dist/web/hooks/use-tool-info.js.map +1 -1
  287. package/dist/web/hooks/use-tool-info.test-d.js +0 -5
  288. package/dist/web/hooks/use-tool-info.test-d.js.map +1 -1
  289. package/dist/web/hooks/use-tool-info.test.js +38 -33
  290. package/dist/web/hooks/use-tool-info.test.js.map +1 -1
  291. package/dist/web/hooks/use-user.d.ts +3 -2
  292. package/dist/web/hooks/use-user.js +4 -3
  293. package/dist/web/hooks/use-user.js.map +1 -1
  294. package/dist/web/hooks/use-user.test.js +85 -37
  295. package/dist/web/hooks/use-user.test.js.map +1 -1
  296. package/dist/web/hooks/use-view-state.js +19 -9
  297. package/dist/web/hooks/use-view-state.js.map +1 -1
  298. package/dist/web/hooks/use-view-state.test.js +39 -4
  299. package/dist/web/hooks/use-view-state.test.js.map +1 -1
  300. package/dist/web/hooks/use-viewport.d.ts +20 -0
  301. package/dist/web/hooks/use-viewport.js +21 -0
  302. package/dist/web/hooks/use-viewport.js.map +1 -0
  303. package/dist/web/hooks/use-viewport.test.d.ts +1 -0
  304. package/dist/web/hooks/use-viewport.test.js +109 -0
  305. package/dist/web/hooks/use-viewport.test.js.map +1 -0
  306. package/dist/web/mount-view.d.ts +3 -2
  307. package/dist/web/mount-view.js +5 -8
  308. package/dist/web/mount-view.js.map +1 -1
  309. package/package.json +31 -28
  310. package/dist/cli/resolve-views-dir.d.ts +0 -1
  311. package/dist/cli/resolve-views-dir.js.map +0 -1
  312. package/dist/web/bridges/apps-sdk/adaptor.d.ts +0 -29
  313. package/dist/web/bridges/apps-sdk/adaptor.js +0 -117
  314. package/dist/web/bridges/apps-sdk/adaptor.js.map +0 -1
  315. package/dist/web/bridges/mcp-app/adaptor.d.ts +0 -53
  316. package/dist/web/bridges/mcp-app/adaptor.js +0 -283
  317. package/dist/web/bridges/mcp-app/adaptor.js.map +0 -1
  318. package/dist/web/hooks/use-layout.d.ts +0 -24
  319. package/dist/web/hooks/use-layout.js +0 -25
  320. package/dist/web/hooks/use-layout.js.map +0 -1
  321. package/dist/web/hooks/use-layout.test.js +0 -96
  322. package/dist/web/hooks/use-layout.test.js.map +0 -1
  323. package/dist/web/plugin/data-llm.test.js +0 -81
  324. package/dist/web/plugin/data-llm.test.js.map +0 -1
  325. package/dist/web/plugin/plugin.d.ts +0 -33
  326. package/dist/web/plugin/plugin.js +0 -189
  327. package/dist/web/plugin/plugin.js.map +0 -1
  328. package/dist/web/plugin/scan-views.js.map +0 -1
  329. package/dist/web/plugin/scan-views.test.js.map +0 -1
  330. package/dist/web/plugin/transform-data-llm.d.ts +0 -12
  331. package/dist/web/plugin/transform-data-llm.js +0 -96
  332. package/dist/web/plugin/transform-data-llm.js.map +0 -1
  333. package/dist/web/plugin/transform-data-llm.test.js +0 -81
  334. package/dist/web/plugin/transform-data-llm.test.js.map +0 -1
  335. package/dist/web/plugin/validate-view.d.ts +0 -1
  336. package/dist/web/plugin/validate-view.js.map +0 -1
  337. package/dist/web/plugin/validate-view.test.js.map +0 -1
  338. /package/dist/{web/hooks/use-layout.test.d.ts → cli/build-steps.test.d.ts} +0 -0
  339. /package/dist/{web/plugin/data-llm.test.d.ts → context-warnings.test.d.ts} +0 -0
  340. /package/dist/{web/plugin/scan-views.test.d.ts → server/app.test.d.ts} +0 -0
  341. /package/dist/{web/plugin/validate-view.test.d.ts → server/auth/providers/authplane.test.d.ts} +0 -0
  342. /package/dist/{web/plugin/transform-data-llm.test.d.ts → server/auth-extra.test-d.d.ts} +0 -0
  343. /package/dist/{web/plugin → views}/scan-views.d.ts +0 -0
  344. /package/dist/{web/plugin → views}/scan-views.test.js +0 -0
  345. /package/dist/{web/plugin → views}/validate-view.test.js +0 -0
@@ -1 +1 @@
1
- {"version":3,"file":"middleware.js","sourceRoot":"","sources":["../../src/server/middleware.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Server } from \"@modelcontextprotocol/sdk/server/index.js\";\nimport type { RequestHandlerExtra } from \"@modelcontextprotocol/sdk/shared/protocol.js\";\nimport type {\n CallToolResult,\n CancelTaskResult,\n ClientNotification,\n ClientRequest,\n CompleteResult,\n EmptyResult,\n GetPromptResult,\n GetTaskPayloadResult,\n GetTaskResult,\n InitializeResult,\n ListPromptsResult,\n ListResourcesResult,\n ListResourceTemplatesResult,\n ListTasksResult,\n ListToolsResult,\n ReadResourceResult,\n ServerNotification,\n ServerRequest,\n ServerResult,\n} from \"@modelcontextprotocol/sdk/types.js\";\n\n/**\n * The `extra` context object provided by the MCP SDK to request handlers.\n */\nexport type McpExtra = RequestHandlerExtra<ServerRequest, ServerNotification>;\n\n/**\n * A single MCP middleware function following the onion model.\n * Call `next()` to invoke the next middleware or the final handler.\n * For notifications, `extra` is `undefined` (SDK does not provide extra context)\n * and `next()` resolves to `undefined`.\n */\nexport type McpMiddlewareFn = (\n request: { method: string; params: Record<string, unknown> },\n extra: McpExtra | undefined,\n next: () => Promise<unknown>,\n) => Promise<unknown> | unknown;\n\n/**\n * MCP methods the server handles (incoming from client).\n */\nexport type McpMethodString =\n | ClientRequest[\"method\"]\n | ClientNotification[\"method\"];\n\n/**\n * Resolve the `params` type for a specific MCP method (request or notification)\n * from the SDK's typed unions. Falls back to `Record<string, unknown>` for\n * unknown methods. Used by {@link McpTypedMiddlewareFn} to narrow the request\n * shape in typed middleware.\n */\nexport type McpRequestParams<M extends string> =\n Extract<ClientRequest, { method: M }> extends { params: infer P }\n ? P\n : Extract<ClientNotification, { method: M }> extends { params: infer P }\n ? P\n : Record<string, unknown>;\n\n/**\n * Resolve the `extra` arg type for a specific MCP method: {@link McpExtra} for\n * request methods, `undefined` for notification methods (the SDK does not\n * pass extra context for notifications).\n */\nexport type McpExtraFor<M extends string> = M extends ClientRequest[\"method\"]\n ? McpExtra\n : M extends ClientNotification[\"method\"]\n ? undefined\n : McpExtra | undefined;\n\n/** Maps each MCP request method to its SDK result type. */\ninterface McpResultMap {\n ping: EmptyResult;\n initialize: InitializeResult;\n \"tools/list\": ListToolsResult;\n \"tools/call\": CallToolResult;\n \"resources/list\": ListResourcesResult;\n \"resources/templates/list\": ListResourceTemplatesResult;\n \"resources/read\": ReadResourceResult;\n \"resources/subscribe\": EmptyResult;\n \"resources/unsubscribe\": EmptyResult;\n \"prompts/list\": ListPromptsResult;\n \"prompts/get\": GetPromptResult;\n \"completion/complete\": CompleteResult;\n \"logging/setLevel\": EmptyResult;\n \"tasks/get\": GetTaskResult;\n \"tasks/result\": GetTaskPayloadResult;\n \"tasks/list\": ListTasksResult;\n \"tasks/cancel\": CancelTaskResult;\n}\n\n/**\n * Map an MCP method string to its corresponding result type.\n * For request methods, resolves to the specific SDK result type.\n * For wildcard patterns (e.g. `\"tools/*\"`), resolves to the union of matching result types.\n * For notification methods, resolves to `undefined`.\n * For unknown/unmatched methods, falls back to `ServerResult`.\n */\nexport type McpResultFor<M extends string> = M extends keyof McpResultMap\n ? McpResultMap[M]\n : M extends `${infer Prefix}/*`\n ? [McpResultMap[keyof McpResultMap & `${Prefix}/${string}`]] extends [never]\n ? M extends ToWildcard<ClientNotification[\"method\"]>\n ? undefined\n : ServerResult\n : McpResultMap[keyof McpResultMap & `${Prefix}/${string}`]\n : M extends ClientNotification[\"method\"]\n ? undefined\n : ServerResult;\n\n/**\n * Typed middleware function for a specific method. Narrows `request.params`\n * via {@link McpRequestParams}, `extra` via {@link McpExtraFor}, and the\n * resolved value of `next()` via {@link McpResultFor}.\n */\nexport type McpTypedMiddlewareFn<M extends string> = (\n request: { method: M; params: McpRequestParams<M> },\n extra: McpExtraFor<M>,\n next: () => Promise<McpResultFor<M>>,\n) => Promise<unknown> | unknown;\n\n/** Extracts `\"prefix/*\"` from `\"prefix/anything\"` — distributive over unions. */\ntype ToWildcard<T extends string> = T extends `${infer Prefix}/${string}`\n ? `${Prefix}/*`\n : never;\n\n/** Wildcard prefixes derived from method strings (e.g. `\"tools/*\"` from `\"tools/call\"`). */\nexport type McpWildcard = ToWildcard<McpMethodString>;\n\n/** Category keywords matching all requests or all notifications. */\ntype McpCategory = \"request\" | \"notification\";\n\n/**\n * A single filter pattern for MCP middleware:\n * - Exact method: `\"tools/call\"`\n * - Wildcard: `\"tools/*\"`\n * - Category: `\"request\"` | `\"notification\"`\n * - Escape hatch: arbitrary string via `string & {}`\n */\ntype McpMiddlewareFilterPattern =\n | McpMethodString\n | McpWildcard\n | McpCategory\n | (string & {});\n\n/**\n * Filter determining which MCP methods a middleware applies to.\n * A single pattern or an array of patterns (OR logic).\n */\nexport type McpMiddlewareFilter =\n | McpMiddlewareFilterPattern\n | McpMiddlewareFilterPattern[];\n\n/**\n * Internal entry stored for each registered middleware.\n * `filter: null` means catch-all (matches everything).\n */\nexport type McpMiddlewareEntry = {\n filter: McpMiddlewareFilter | null;\n handler: McpMiddlewareFn;\n};\n\ntype HandlerMap = Map<string, (...args: unknown[]) => Promise<unknown>>;\n\n/**\n * Extract the TS-private `_requestHandlers` and `_notificationHandlers` maps\n * from the SDK's `Server` (extends `Protocol`). These are runtime-accessible\n * but declared `private` in TypeScript.\n *\n * Validates with `instanceof Map` so an incompatible SDK version fails fast\n * instead of silently breaking.\n */\nexport function getHandlerMaps(server: Server) {\n const obj: object = server;\n\n if (\n !(\"_requestHandlers\" in obj && obj._requestHandlers instanceof Map) ||\n !(\n \"_notificationHandlers\" in obj && obj._notificationHandlers instanceof Map\n )\n ) {\n throw new Error(\n \"Incompatible MCP SDK version: expected _requestHandlers and _notificationHandlers on Server\",\n );\n }\n\n return {\n requestHandlers: obj._requestHandlers as HandlerMap,\n notificationHandlers: obj._notificationHandlers as HandlerMap,\n };\n}\n\n/**\n * Check if a single filter pattern matches a given method.\n *\n * - Exact: `\"tools/call\"` matches only `\"tools/call\"`\n * - Wildcard: `\"tools/*\"` matches any method starting with `\"tools/\"`\n * - Category `\"request\"`: matches when `isNotification` is false\n * - Category `\"notification\"`: matches when `isNotification` is true\n */\nexport function matchesFilter(\n method: string,\n filter: string,\n isNotification: boolean,\n): boolean {\n if (filter === \"request\") {\n return !isNotification;\n }\n if (filter === \"notification\") {\n return isNotification;\n }\n if (filter.endsWith(\"/*\")) {\n const prefix = filter.slice(0, -1); // \"tools/*\" → \"tools/\"\n return method.startsWith(prefix);\n }\n return method === filter;\n}\n\nfunction matchesAnyFilter(\n method: string,\n filter: McpMiddlewareFilter | null,\n isNotification: boolean,\n): boolean {\n if (filter === null) {\n return true;\n }\n if (typeof filter === \"string\") {\n return matchesFilter(method, filter, isNotification);\n }\n return filter.some((pattern) =>\n matchesFilter(method, pattern, isNotification),\n );\n}\n\n/**\n * Build an onion-model middleware chain for a specific method.\n *\n * Filters `entries` to those matching `method`, then composes them\n * so the first registered middleware is the outermost layer.\n * `next()` is guarded against multiple calls within a single middleware.\n */\nexport function buildMiddlewareChain(\n method: string,\n isNotification: boolean,\n originalHandler: (...args: unknown[]) => Promise<unknown>,\n entries: McpMiddlewareEntry[],\n) {\n const applicable = entries.filter((entry) =>\n matchesAnyFilter(method, entry.filter, isNotification),\n );\n\n if (applicable.length === 0) {\n return originalHandler;\n }\n\n return (...args: unknown[]) => {\n const rawRequest = args[0] as Record<string, unknown> | undefined;\n // SDK calls request handlers as handler(request, extra) but\n // notification handlers as handler(notification) — no extra arg.\n const extra = isNotification ? undefined : (args[1] as McpExtra);\n const mcpRequest = {\n method,\n params: (rawRequest?.params as Record<string, unknown>) ?? {},\n };\n\n let index = 0;\n\n const executeLayer = (): Promise<unknown> => {\n const entry = applicable[index++];\n if (!entry) {\n if (rawRequest) {\n rawRequest.params = mcpRequest.params;\n }\n return originalHandler(...args);\n }\n\n let nextCalled = false;\n\n const next = (): Promise<unknown> => {\n if (nextCalled) {\n throw new Error(\n `next() called multiple times in middleware for \"${method}\"`,\n );\n }\n nextCalled = true;\n return executeLayer();\n };\n\n return Promise.resolve(entry.handler(mcpRequest, extra, next));\n };\n\n return executeLayer();\n };\n}\n"]}
1
+ {"version":3,"file":"middleware.js","sourceRoot":"","sources":["../../src/server/middleware.ts"],"names":[],"mappings":"AA+CA,MAAM,UAAU,GAAG,MAAM,CAAC,GAAG,CAAC,qBAAqB,CAAC,CAAC;AAErD,SAAS,aAAa,CACpB,KAA2B;IAE3B,OAAO,KAAuD,CAAC;AACjE,CAAC;AAED,MAAM,UAAU,gBAAgB,CAC9B,KAA2B,EAC3B,KAAc;IAEd,MAAM,IAAI,GAAG,aAAa,CAAC,KAAK,CAAC,CAAC;IAClC,IAAI,IAAI,EAAE,CAAC;QACT,IAAI,CAAC,UAAU,CAAC,GAAG,KAAK,CAAC;IAC3B,CAAC;AACH,CAAC;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,UAAU,YAAY,CAAC,KAA2B;IACtD,OAAO,aAAa,CAAC,KAAK,CAAC,EAAE,CAAC,UAAU,CAAC,CAAC;AAC5C,CAAC;AAqID;;;;;;;GAOG;AACH,MAAM,UAAU,cAAc,CAAC,MAAc;IAC3C,MAAM,GAAG,GAAW,MAAM,CAAC;IAE3B,IACE,CAAC,CAAC,kBAAkB,IAAI,GAAG,IAAI,GAAG,CAAC,gBAAgB,YAAY,GAAG,CAAC;QACnE,CAAC,CACC,uBAAuB,IAAI,GAAG,IAAI,GAAG,CAAC,qBAAqB,YAAY,GAAG,CAC3E,EACD,CAAC;QACD,MAAM,IAAI,KAAK,CACb,6FAA6F,CAC9F,CAAC;IACJ,CAAC;IAED,OAAO;QACL,eAAe,EAAE,GAAG,CAAC,gBAA8B;QACnD,oBAAoB,EAAE,GAAG,CAAC,qBAAmC;KAC9D,CAAC;AACJ,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,aAAa,CAC3B,MAAc,EACd,MAAc,EACd,cAAuB;IAEvB,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;QACzB,OAAO,CAAC,cAAc,CAAC;IACzB,CAAC;IACD,IAAI,MAAM,KAAK,cAAc,EAAE,CAAC;QAC9B,OAAO,cAAc,CAAC;IACxB,CAAC;IACD,IAAI,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;QAC1B,MAAM,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,uBAAuB;QAC3D,OAAO,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC;IACnC,CAAC;IACD,OAAO,MAAM,KAAK,MAAM,CAAC;AAC3B,CAAC;AAED,SAAS,gBAAgB,CACvB,MAAc,EACd,MAAkC,EAClC,cAAuB;IAEvB,IAAI,MAAM,KAAK,IAAI,EAAE,CAAC;QACpB,OAAO,IAAI,CAAC;IACd,CAAC;IACD,IAAI,OAAO,MAAM,KAAK,QAAQ,EAAE,CAAC;QAC/B,OAAO,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,cAAc,CAAC,CAAC;IACvD,CAAC;IACD,OAAO,MAAM,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,EAAE,CAC7B,aAAa,CAAC,MAAM,EAAE,OAAO,EAAE,cAAc,CAAC,CAC/C,CAAC;AACJ,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,oBAAoB,CAClC,MAAc,EACd,cAAuB,EACvB,eAAyD,EACzD,OAA6B;IAE7B,MAAM,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAC1C,gBAAgB,CAAC,MAAM,EAAE,KAAK,CAAC,MAAM,EAAE,cAAc,CAAC,CACvD,CAAC;IAEF,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC5B,OAAO,eAAe,CAAC;IACzB,CAAC;IAED,OAAO,CAAC,GAAG,IAAe,EAAE,EAAE;QAC5B,MAAM,UAAU,GAAG,IAAI,CAAC,CAAC,CAAwC,CAAC;QAClE,4DAA4D;QAC5D,iEAAiE;QACjE,MAAM,KAAK,GAAG,cAAc,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAE,IAAI,CAAC,CAAC,CAAc,CAAC;QACjE,MAAM,UAAU,GAAG;YACjB,MAAM;YACN,MAAM,EAAG,UAAU,EAAE,MAAkC,IAAI,EAAE;SAC9D,CAAC;QAEF,IAAI,KAAK,GAAG,CAAC,CAAC;QAEd,MAAM,YAAY,GAAG,GAAqB,EAAE;YAC1C,MAAM,KAAK,GAAG,UAAU,CAAC,KAAK,EAAE,CAAC,CAAC;YAClC,IAAI,CAAC,KAAK,EAAE,CAAC;gBACX,IAAI,UAAU,EAAE,CAAC;oBACf,UAAU,CAAC,MAAM,GAAG,UAAU,CAAC,MAAM,CAAC;gBACxC,CAAC;gBACD,OAAO,eAAe,CAAC,GAAG,IAAI,CAAC,CAAC;YAClC,CAAC;YAED,IAAI,UAAU,GAAG,KAAK,CAAC;YAEvB,MAAM,IAAI,GAAG,GAAqB,EAAE;gBAClC,IAAI,UAAU,EAAE,CAAC;oBACf,MAAM,IAAI,KAAK,CACb,mDAAmD,MAAM,GAAG,CAC7D,CAAC;gBACJ,CAAC;gBACD,UAAU,GAAG,IAAI,CAAC;gBAClB,OAAO,YAAY,EAAE,CAAC;YACxB,CAAC,CAAC;YAEF,OAAO,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,UAAU,EAAE,KAAK,EAAE,IAAI,CAAC,CAAC,CAAC;QACjE,CAAC,CAAC;QAEF,OAAO,YAAY,EAAE,CAAC;IACxB,CAAC,CAAC;AACJ,CAAC","sourcesContent":["import type {\n CallToolResult,\n CancelTaskResult,\n ClientNotification,\n ClientRequest,\n CompleteResult,\n EmptyResult,\n GetPromptResult,\n GetTaskPayloadResult,\n GetTaskResult,\n InitializeResult,\n ListPromptsResult,\n ListResourcesResult,\n ListResourceTemplatesResult,\n ListTasksResult,\n ListToolsResult,\n ReadResourceResult,\n Server,\n ServerContext,\n ServerResult,\n} from \"@modelcontextprotocol/server\";\nimport type { AuthInfo, ExtraClaims } from \"./auth.js\";\n\n/**\n * The per-request handler context provided by the MCP SDK to request handlers.\n */\nexport type McpExtra<TAuthExtra extends ExtraClaims = ExtraClaims> = Omit<\n ServerContext,\n \"http\"\n> & {\n http?: Omit<NonNullable<ServerContext[\"http\"]>, \"authInfo\"> & {\n authInfo?: AuthInfo<TAuthExtra>;\n };\n};\n\n/**\n * A single MCP middleware function following the onion model.\n * Call `next()` to invoke the next middleware or the final handler.\n * For notifications, `extra` is `undefined` (SDK does not provide extra context)\n * and `next()` resolves to `undefined`.\n */\nexport type McpMiddlewareFn<TAuthExtra extends ExtraClaims = ExtraClaims> = (\n request: { method: string; params: Record<string, unknown> },\n extra: McpExtra<TAuthExtra> | undefined,\n next: () => Promise<unknown>,\n) => Promise<unknown> | unknown;\n\nconst TOOL_ERROR = Symbol.for(\"skybridge.toolError\");\n\nfunction toolErrorSlot(\n extra: McpExtra | undefined,\n): Record<symbol, unknown> | undefined {\n return extra as unknown as Record<symbol, unknown> | undefined;\n}\n\nexport function captureToolError(\n extra: McpExtra | undefined,\n error: unknown,\n): void {\n const slot = toolErrorSlot(extra);\n if (slot) {\n slot[TOOL_ERROR] = error;\n }\n}\n\n/**\n * Retrieve the error a tool handler threw during this request, if any.\n *\n * The MCP SDK catches anything a tool handler throws and turns it into an\n * `isError` tool result, so `await next()` in middleware resolves normally and\n * the original `Error` — stack, `cause` — is lost. Skybridge stores it on\n * `extra` (never serialized to the client) so observability middleware can\n * report it:\n *\n * ```ts\n * server.mcpMiddleware(\"tools/call\", async (request, extra, next) => {\n * const result = await next();\n * const error = getToolError(extra);\n * if (error) {\n * Sentry.captureException(error);\n * }\n * return result;\n * });\n * ```\n */\nexport function getToolError(extra: McpExtra | undefined): unknown {\n return toolErrorSlot(extra)?.[TOOL_ERROR];\n}\n\n/**\n * MCP methods the server handles (incoming from client).\n */\nexport type McpMethodString =\n | ClientRequest[\"method\"]\n | ClientNotification[\"method\"];\n\n/**\n * Resolve the `params` type for a specific MCP method (request or notification)\n * from the SDK's typed unions. Falls back to `Record<string, unknown>` for\n * unknown methods. Used by {@link McpTypedMiddlewareFn} to narrow the request\n * shape in typed middleware.\n */\nexport type McpRequestParams<M extends string> =\n Extract<ClientRequest, { method: M }> extends { params: infer P }\n ? P\n : Extract<ClientNotification, { method: M }> extends { params: infer P }\n ? P\n : Record<string, unknown>;\n\n/**\n * Resolve the `extra` arg type for a specific MCP method: {@link McpExtra} for\n * request methods, `undefined` for notification methods (the SDK does not\n * pass extra context for notifications).\n */\nexport type McpExtraFor<\n M extends string,\n TAuthExtra extends ExtraClaims = ExtraClaims,\n> = M extends ClientRequest[\"method\"]\n ? McpExtra<TAuthExtra>\n : M extends ClientNotification[\"method\"]\n ? undefined\n : McpExtra<TAuthExtra> | undefined;\n\n/** Maps each MCP request method to its SDK result type. */\ninterface McpResultMap {\n ping: EmptyResult;\n initialize: InitializeResult;\n \"tools/list\": ListToolsResult;\n \"tools/call\": CallToolResult;\n \"resources/list\": ListResourcesResult;\n \"resources/templates/list\": ListResourceTemplatesResult;\n \"resources/read\": ReadResourceResult;\n \"resources/subscribe\": EmptyResult;\n \"resources/unsubscribe\": EmptyResult;\n \"prompts/list\": ListPromptsResult;\n \"prompts/get\": GetPromptResult;\n \"completion/complete\": CompleteResult;\n \"logging/setLevel\": EmptyResult;\n \"tasks/get\": GetTaskResult;\n \"tasks/result\": GetTaskPayloadResult;\n \"tasks/list\": ListTasksResult;\n \"tasks/cancel\": CancelTaskResult;\n}\n\n/**\n * Map an MCP method string to its corresponding result type.\n * For request methods, resolves to the specific SDK result type.\n * For wildcard patterns (e.g. `\"tools/*\"`), resolves to the union of matching result types.\n * For notification methods, resolves to `undefined`.\n * For unknown/unmatched methods, falls back to `ServerResult`.\n */\nexport type McpResultFor<M extends string> = M extends keyof McpResultMap\n ? McpResultMap[M]\n : M extends `${infer Prefix}/*`\n ? [McpResultMap[keyof McpResultMap & `${Prefix}/${string}`]] extends [never]\n ? M extends ToWildcard<ClientNotification[\"method\"]>\n ? undefined\n : ServerResult\n : McpResultMap[keyof McpResultMap & `${Prefix}/${string}`]\n : M extends ClientNotification[\"method\"]\n ? undefined\n : ServerResult;\n\n/**\n * Typed middleware function for a specific method. Narrows `request.params`\n * via {@link McpRequestParams}, `extra` via {@link McpExtraFor}, and the\n * resolved value of `next()` via {@link McpResultFor}.\n */\nexport type McpTypedMiddlewareFn<\n M extends string,\n TAuthExtra extends ExtraClaims = ExtraClaims,\n> = (\n request: { method: M; params: McpRequestParams<M> },\n extra: McpExtraFor<M, TAuthExtra>,\n next: () => Promise<McpResultFor<M>>,\n) => Promise<unknown> | unknown;\n\n/** Extracts `\"prefix/*\"` from `\"prefix/anything\"` — distributive over unions. */\ntype ToWildcard<T extends string> = T extends `${infer Prefix}/${string}`\n ? `${Prefix}/*`\n : never;\n\n/** Wildcard prefixes derived from method strings (e.g. `\"tools/*\"` from `\"tools/call\"`). */\nexport type McpWildcard = ToWildcard<McpMethodString>;\n\n/** Category keywords matching all requests or all notifications. */\ntype McpCategory = \"request\" | \"notification\";\n\n/**\n * A single filter pattern for MCP middleware:\n * - Exact method: `\"tools/call\"`\n * - Wildcard: `\"tools/*\"`\n * - Category: `\"request\"` | `\"notification\"`\n * - Escape hatch: arbitrary string via `string & {}`\n */\ntype McpMiddlewareFilterPattern =\n | McpMethodString\n | McpWildcard\n | McpCategory\n | (string & {});\n\n/**\n * Filter determining which MCP methods a middleware applies to.\n * A single pattern or an array of patterns (OR logic).\n */\nexport type McpMiddlewareFilter =\n | McpMiddlewareFilterPattern\n | McpMiddlewareFilterPattern[];\n\n/**\n * Internal entry stored for each registered middleware.\n * `filter: null` means catch-all (matches everything).\n */\nexport type McpMiddlewareEntry = {\n filter: McpMiddlewareFilter | null;\n handler: McpMiddlewareFn;\n};\n\ntype HandlerMap = Map<string, (...args: unknown[]) => Promise<unknown>>;\n\n/**\n * Extract the TS-private `_requestHandlers` and `_notificationHandlers` maps\n * from the SDK's `Server` (extends `Protocol`). These are runtime-accessible\n * but declared `private` in TypeScript.\n *\n * Validates with `instanceof Map` so an incompatible SDK version fails fast\n * instead of silently breaking.\n */\nexport function getHandlerMaps(server: Server) {\n const obj: object = server;\n\n if (\n !(\"_requestHandlers\" in obj && obj._requestHandlers instanceof Map) ||\n !(\n \"_notificationHandlers\" in obj && obj._notificationHandlers instanceof Map\n )\n ) {\n throw new Error(\n \"Incompatible MCP SDK version: expected _requestHandlers and _notificationHandlers on Server\",\n );\n }\n\n return {\n requestHandlers: obj._requestHandlers as HandlerMap,\n notificationHandlers: obj._notificationHandlers as HandlerMap,\n };\n}\n\n/**\n * Check if a single filter pattern matches a given method.\n *\n * - Exact: `\"tools/call\"` matches only `\"tools/call\"`\n * - Wildcard: `\"tools/*\"` matches any method starting with `\"tools/\"`\n * - Category `\"request\"`: matches when `isNotification` is false\n * - Category `\"notification\"`: matches when `isNotification` is true\n */\nexport function matchesFilter(\n method: string,\n filter: string,\n isNotification: boolean,\n): boolean {\n if (filter === \"request\") {\n return !isNotification;\n }\n if (filter === \"notification\") {\n return isNotification;\n }\n if (filter.endsWith(\"/*\")) {\n const prefix = filter.slice(0, -1); // \"tools/*\" → \"tools/\"\n return method.startsWith(prefix);\n }\n return method === filter;\n}\n\nfunction matchesAnyFilter(\n method: string,\n filter: McpMiddlewareFilter | null,\n isNotification: boolean,\n): boolean {\n if (filter === null) {\n return true;\n }\n if (typeof filter === \"string\") {\n return matchesFilter(method, filter, isNotification);\n }\n return filter.some((pattern) =>\n matchesFilter(method, pattern, isNotification),\n );\n}\n\n/**\n * Build an onion-model middleware chain for a specific method.\n *\n * Filters `entries` to those matching `method`, then composes them\n * so the first registered middleware is the outermost layer.\n * `next()` is guarded against multiple calls within a single middleware.\n */\nexport function buildMiddlewareChain(\n method: string,\n isNotification: boolean,\n originalHandler: (...args: unknown[]) => Promise<unknown>,\n entries: McpMiddlewareEntry[],\n) {\n const applicable = entries.filter((entry) =>\n matchesAnyFilter(method, entry.filter, isNotification),\n );\n\n if (applicable.length === 0) {\n return originalHandler;\n }\n\n return (...args: unknown[]) => {\n const rawRequest = args[0] as Record<string, unknown> | undefined;\n // SDK calls request handlers as handler(request, extra) but\n // notification handlers as handler(notification) — no extra arg.\n const extra = isNotification ? undefined : (args[1] as McpExtra);\n const mcpRequest = {\n method,\n params: (rawRequest?.params as Record<string, unknown>) ?? {},\n };\n\n let index = 0;\n\n const executeLayer = (): Promise<unknown> => {\n const entry = applicable[index++];\n if (!entry) {\n if (rawRequest) {\n rawRequest.params = mcpRequest.params;\n }\n return originalHandler(...args);\n }\n\n let nextCalled = false;\n\n const next = (): Promise<unknown> => {\n if (nextCalled) {\n throw new Error(\n `next() called multiple times in middleware for \"${method}\"`,\n );\n }\n nextCalled = true;\n return executeLayer();\n };\n\n return Promise.resolve(entry.handler(mcpRequest, extra, next));\n };\n\n return executeLayer();\n };\n}\n"]}
@@ -3,7 +3,7 @@ const server = null;
3
3
  test("request category narrows extra and next() result", () => {
4
4
  server.mcpMiddleware("request", async (_request, extra, next) => {
5
5
  expectTypeOf(extra).toEqualTypeOf();
6
- extra.signal;
6
+ extra.mcpReq.signal;
7
7
  const result = await next();
8
8
  expectTypeOf(result).toEqualTypeOf();
9
9
  return result;
@@ -1 +1 @@
1
- {"version":3,"file":"middleware.test-d.js","sourceRoot":"","sources":["../../src/server/middleware.test-d.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n CallToolResult,\n ListToolsResult,\n ServerResult,\n} from \"@modelcontextprotocol/sdk/types.js\";\nimport { expectTypeOf, test } from \"vitest\";\nimport type {\n McpExtra,\n McpResultFor,\n McpTypedMiddlewareFn,\n} from \"./middleware.js\";\nimport type { McpServer } from \"./server.js\";\n\nconst server = null as unknown as McpServer;\n\ntest(\"request category narrows extra and next() result\", () => {\n server.mcpMiddleware(\"request\", async (_request, extra, next) => {\n expectTypeOf(extra).toEqualTypeOf<McpExtra>();\n extra.signal;\n const result = await next();\n expectTypeOf(result).toEqualTypeOf<ServerResult>();\n return result;\n });\n});\n\ntest(\"notification category narrows extra and next() result\", () => {\n server.mcpMiddleware(\"notification\", async (_request, extra, next) => {\n expectTypeOf(extra).toEqualTypeOf<undefined>();\n // @ts-expect-error extra is undefined, cannot access .signal\n extra.signal;\n const result = await next();\n expectTypeOf(result).toEqualTypeOf<undefined>();\n });\n});\n\ntest(\"exact method tools/call narrows params, extra, and next() result\", () => {\n server.mcpMiddleware(\"tools/call\", async (request, extra, next) => {\n expectTypeOf(request.params.name).toBeString();\n expectTypeOf(extra).toEqualTypeOf<McpExtra>();\n const result = await next();\n expectTypeOf(result).toEqualTypeOf<CallToolResult>();\n return result;\n });\n});\n\ntest(\"exact method tools/list narrows next() to ListToolsResult\", () => {\n server.mcpMiddleware(\"tools/list\", async (_request, _extra, next) => {\n const result = await next();\n expectTypeOf(result).toEqualTypeOf<ListToolsResult>();\n return result;\n });\n});\n\ntest(\"exact notification method narrows extra and next() result\", () => {\n server.mcpMiddleware(\n \"notifications/initialized\",\n async (_request, extra, next) => {\n expectTypeOf(extra).toEqualTypeOf<undefined>();\n // @ts-expect-error extra is undefined\n extra.signal;\n const result = await next();\n expectTypeOf(result).toEqualTypeOf<undefined>();\n },\n );\n});\n\ntest(\"McpTypedMiddlewareFn narrows params, extra, and next() per method\", () => {\n expectTypeOf<\n Parameters<McpTypedMiddlewareFn<\"tools/call\">>[0][\"params\"][\"name\"]\n >().toBeString();\n expectTypeOf<\n Parameters<McpTypedMiddlewareFn<\"tools/call\">>[1]\n >().toEqualTypeOf<McpExtra>();\n expectTypeOf<\n Parameters<McpTypedMiddlewareFn<\"notifications/initialized\">>[1]\n >().toEqualTypeOf<undefined>();\n expectTypeOf<\n ReturnType<Parameters<McpTypedMiddlewareFn<\"tools/list\">>[2]>\n >().toEqualTypeOf<Promise<ListToolsResult>>();\n expectTypeOf<\n ReturnType<Parameters<McpTypedMiddlewareFn<\"notifications/initialized\">>[2]>\n >().toEqualTypeOf<Promise<undefined>>();\n});\n\ntest(\"McpResultFor maps methods to correct result types\", () => {\n expectTypeOf<McpResultFor<\"tools/list\">>().toEqualTypeOf<ListToolsResult>();\n expectTypeOf<McpResultFor<\"tools/call\">>().toEqualTypeOf<CallToolResult>();\n expectTypeOf<\n McpResultFor<\"notifications/initialized\">\n >().toEqualTypeOf<undefined>();\n});\n\ntest(\"wildcard tools/* narrows next() to union of tools result types\", () => {\n server.mcpMiddleware(\"tools/*\", async (_request, _extra, next) => {\n const result = await next();\n expectTypeOf(result).toEqualTypeOf<ListToolsResult | CallToolResult>();\n return result;\n });\n});\n\ntest(\"McpResultFor resolves wildcards to union of matching result types\", () => {\n expectTypeOf<McpResultFor<\"tools/*\">>().toEqualTypeOf<\n ListToolsResult | CallToolResult\n >();\n});\n\ntest(\"catch-all middleware has no narrowing on extra or params\", () => {\n server.mcpMiddleware((_request, extra, next) => {\n expectTypeOf(extra).toEqualTypeOf<McpExtra | undefined>();\n expectTypeOf(_request.params).toEqualTypeOf<Record<string, unknown>>();\n return next();\n });\n});\n"]}
1
+ {"version":3,"file":"middleware.test-d.js","sourceRoot":"","sources":["../../src/server/middleware.test-d.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n CallToolResult,\n ListToolsResult,\n ServerResult,\n} from \"@modelcontextprotocol/server\";\nimport { expectTypeOf, test } from \"vitest\";\nimport type {\n McpExtra,\n McpResultFor,\n McpTypedMiddlewareFn,\n} from \"./middleware.js\";\nimport type { McpServer } from \"./server.js\";\n\nconst server = null as unknown as McpServer;\n\ntest(\"request category narrows extra and next() result\", () => {\n server.mcpMiddleware(\"request\", async (_request, extra, next) => {\n expectTypeOf(extra).toEqualTypeOf<McpExtra>();\n extra.mcpReq.signal;\n const result = await next();\n expectTypeOf(result).toEqualTypeOf<ServerResult>();\n return result;\n });\n});\n\ntest(\"notification category narrows extra and next() result\", () => {\n server.mcpMiddleware(\"notification\", async (_request, extra, next) => {\n expectTypeOf(extra).toEqualTypeOf<undefined>();\n // @ts-expect-error extra is undefined, cannot access .signal\n extra.signal;\n const result = await next();\n expectTypeOf(result).toEqualTypeOf<undefined>();\n });\n});\n\ntest(\"exact method tools/call narrows params, extra, and next() result\", () => {\n server.mcpMiddleware(\"tools/call\", async (request, extra, next) => {\n expectTypeOf(request.params.name).toBeString();\n expectTypeOf(extra).toEqualTypeOf<McpExtra>();\n const result = await next();\n expectTypeOf(result).toEqualTypeOf<CallToolResult>();\n return result;\n });\n});\n\ntest(\"exact method tools/list narrows next() to ListToolsResult\", () => {\n server.mcpMiddleware(\"tools/list\", async (_request, _extra, next) => {\n const result = await next();\n expectTypeOf(result).toEqualTypeOf<ListToolsResult>();\n return result;\n });\n});\n\ntest(\"exact notification method narrows extra and next() result\", () => {\n server.mcpMiddleware(\n \"notifications/initialized\",\n async (_request, extra, next) => {\n expectTypeOf(extra).toEqualTypeOf<undefined>();\n // @ts-expect-error extra is undefined\n extra.signal;\n const result = await next();\n expectTypeOf(result).toEqualTypeOf<undefined>();\n },\n );\n});\n\ntest(\"McpTypedMiddlewareFn narrows params, extra, and next() per method\", () => {\n expectTypeOf<\n Parameters<McpTypedMiddlewareFn<\"tools/call\">>[0][\"params\"][\"name\"]\n >().toBeString();\n expectTypeOf<\n Parameters<McpTypedMiddlewareFn<\"tools/call\">>[1]\n >().toEqualTypeOf<McpExtra>();\n expectTypeOf<\n Parameters<McpTypedMiddlewareFn<\"notifications/initialized\">>[1]\n >().toEqualTypeOf<undefined>();\n expectTypeOf<\n ReturnType<Parameters<McpTypedMiddlewareFn<\"tools/list\">>[2]>\n >().toEqualTypeOf<Promise<ListToolsResult>>();\n expectTypeOf<\n ReturnType<Parameters<McpTypedMiddlewareFn<\"notifications/initialized\">>[2]>\n >().toEqualTypeOf<Promise<undefined>>();\n});\n\ntest(\"McpResultFor maps methods to correct result types\", () => {\n expectTypeOf<McpResultFor<\"tools/list\">>().toEqualTypeOf<ListToolsResult>();\n expectTypeOf<McpResultFor<\"tools/call\">>().toEqualTypeOf<CallToolResult>();\n expectTypeOf<\n McpResultFor<\"notifications/initialized\">\n >().toEqualTypeOf<undefined>();\n});\n\ntest(\"wildcard tools/* narrows next() to union of tools result types\", () => {\n server.mcpMiddleware(\"tools/*\", async (_request, _extra, next) => {\n const result = await next();\n expectTypeOf(result).toEqualTypeOf<ListToolsResult | CallToolResult>();\n return result;\n });\n});\n\ntest(\"McpResultFor resolves wildcards to union of matching result types\", () => {\n expectTypeOf<McpResultFor<\"tools/*\">>().toEqualTypeOf<\n ListToolsResult | CallToolResult\n >();\n});\n\ntest(\"catch-all middleware has no narrowing on extra or params\", () => {\n server.mcpMiddleware((_request, extra, next) => {\n expectTypeOf(extra).toEqualTypeOf<McpExtra | undefined>();\n expectTypeOf(_request.params).toEqualTypeOf<Record<string, unknown>>();\n return next();\n });\n});\n"]}
@@ -1,9 +1,8 @@
1
- import { Client } from "@modelcontextprotocol/sdk/client/index.js";
2
- import { InMemoryTransport } from "@modelcontextprotocol/sdk/inMemory.js";
1
+ import { Client, InMemoryTransport } from "@modelcontextprotocol/client";
3
2
  import { describe, expect, it, vi } from "vitest";
4
3
  import { z } from "zod";
5
- import { buildMiddlewareChain, getHandlerMaps, matchesFilter, } from "./middleware.js";
6
- import { McpServer } from "./server.js";
4
+ import { Skybridge } from "./app.js";
5
+ import { buildMiddlewareChain, getHandlerMaps, getToolError, matchesFilter, } from "./middleware.js";
7
6
  // ---------------------------------------------------------------------------
8
7
  // matchesFilter
9
8
  // ---------------------------------------------------------------------------
@@ -208,29 +207,42 @@ describe("McpServer.mcpMiddleware()", () => {
208
207
  return new Client({ name: "test-client", version: "1.0.0" });
209
208
  }
210
209
  it("returns this for chaining", () => {
211
- const server = new McpServer({ name: "test", version: "1.0.0" });
212
- const result = server.mcpMiddleware(async (_req, _extra, next) => next());
213
- expect(result).toBe(server);
210
+ new Skybridge({
211
+ name: "test",
212
+ version: "1.0.0",
213
+ handler: (server) => {
214
+ const result = server.mcpMiddleware(async (_req, _extra, next) => next());
215
+ expect(result).toBe(server);
216
+ return server;
217
+ },
218
+ });
214
219
  });
215
220
  it("intercepts tools/call and exposes request params", async () => {
216
221
  let capturedMethod = "";
217
222
  let capturedParams = {};
218
- const server = new McpServer({ name: "test", version: "1.0.0" });
219
- server.registerTool({
220
- name: "greet",
221
- description: "greet",
222
- inputSchema: { name: z.string() },
223
- }, (args) => ({
224
- content: [{ type: "text", text: `hi ${args.name}` }],
225
- }));
226
- server.mcpMiddleware("tools/call", async (request, _extra, next) => {
227
- capturedMethod = request.method;
228
- capturedParams = request.params;
229
- return next();
223
+ const app = new Skybridge({
224
+ name: "test",
225
+ version: "1.0.0",
226
+ handler: (server) => {
227
+ server.registerTool({
228
+ name: "greet",
229
+ description: "greet",
230
+ inputSchema: { name: z.string() },
231
+ }, (args) => ({
232
+ content: [{ type: "text", text: `hi ${args.name}` }],
233
+ }));
234
+ server.mcpMiddleware("tools/call", async (request, _extra, next) => {
235
+ capturedMethod = request.method;
236
+ capturedParams = request.params;
237
+ return next();
238
+ });
239
+ return server;
240
+ },
230
241
  });
231
242
  const client = createClient();
232
243
  const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair();
233
- await server.connect(serverTransport);
244
+ const instance = await app.createServerInstance();
245
+ await instance.connect(serverTransport);
234
246
  await client.connect(clientTransport);
235
247
  const result = await client.callTool({
236
248
  name: "greet",
@@ -243,59 +255,70 @@ describe("McpServer.mcpMiddleware()", () => {
243
255
  });
244
256
  expect(result.content).toEqual([{ type: "text", text: "hi World" }]);
245
257
  await client.close();
246
- await server.close();
258
+ await instance.close();
247
259
  });
248
260
  it("array filter works", async () => {
249
261
  const matchedMethods = [];
250
- const server = new McpServer({ name: "test", version: "1.0.0" });
251
- server.registerTool({ name: "t1", description: "t1" }, () => ({
252
- content: [{ type: "text", text: "ok" }],
253
- }));
254
- server.mcpMiddleware(["tools/call", "tools/list"], async (request, _extra, next) => {
255
- matchedMethods.push(request.method);
256
- return next();
262
+ const app = new Skybridge({
263
+ name: "test",
264
+ version: "1.0.0",
265
+ handler: (server) => {
266
+ server.registerTool({ name: "t1", description: "t1" }, () => ({
267
+ content: [{ type: "text", text: "ok" }],
268
+ }));
269
+ server.mcpMiddleware(["tools/call", "tools/list"], async (request, _extra, next) => {
270
+ matchedMethods.push(request.method);
271
+ return next();
272
+ });
273
+ return server;
274
+ },
257
275
  });
258
276
  const client = createClient();
259
277
  const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair();
260
- await server.connect(serverTransport);
278
+ const instance = await app.createServerInstance();
279
+ await instance.connect(serverTransport);
261
280
  await client.connect(clientTransport);
262
281
  await client.listTools();
263
282
  await client.callTool({ name: "t1" });
264
283
  expect(matchedMethods).toEqual(["tools/list", "tools/call"]);
265
284
  await client.close();
266
- await server.close();
267
- });
268
- it("throws if registered after connect()", async () => {
269
- const server = new McpServer({ name: "test", version: "1.0.0" });
270
- const [_clientTransport, serverTransport] = InMemoryTransport.createLinkedPair();
271
- await server.connect(serverTransport);
272
- expect(() => server.mcpMiddleware(async (_req, _extra, next) => next())).toThrow("Cannot register MCP middleware after run() or connect()");
273
- await server.close();
274
- });
275
- it("throws when filter provided without handler", () => {
276
- const server = new McpServer({ name: "test", version: "1.0.0" });
277
- expect(() =>
278
- // @ts-expect-error intentionally passing filter without handler
279
- server.mcpMiddleware("tools/call")).toThrow("mcpMiddleware requires a handler function");
285
+ await instance.close();
286
+ });
287
+ it("throws when filter provided without handler", async () => {
288
+ const app = new Skybridge({
289
+ name: "test",
290
+ version: "1.0.0",
291
+ handler: (server) =>
292
+ // @ts-expect-error intentionally passing filter without handler
293
+ server.mcpMiddleware("tools/call"),
294
+ });
295
+ await expect(app.createServerInstance()).rejects.toThrow("mcpMiddleware requires a handler function");
280
296
  });
281
297
  it("catch-all middleware + filtered middleware stack correctly", async () => {
282
298
  const calls = [];
283
- const server = new McpServer({ name: "test", version: "1.0.0" });
284
- server.registerTool({ name: "t1", description: "t1" }, () => ({
285
- content: [{ type: "text", text: "ok" }],
286
- }));
287
- server
288
- .mcpMiddleware(async (request, _extra, next) => {
289
- calls.push(`global:${request.method}`);
290
- return next();
291
- })
292
- .mcpMiddleware("tools/call", async (_req, _extra, next) => {
293
- calls.push("tools-only");
294
- return next();
299
+ const app = new Skybridge({
300
+ name: "test",
301
+ version: "1.0.0",
302
+ handler: (server) => {
303
+ server.registerTool({ name: "t1", description: "t1" }, () => ({
304
+ content: [{ type: "text", text: "ok" }],
305
+ }));
306
+ server
307
+ .mcpMiddleware(async (request, _extra, next) => {
308
+ calls.push(`global:${request.method}`);
309
+ return next();
310
+ })
311
+ .mcpMiddleware("tools/call", async (_req, _extra, next) => {
312
+ calls.push("tools-only");
313
+ return next();
314
+ });
315
+ return server;
316
+ },
295
317
  });
296
318
  const client = createClient();
297
319
  const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair();
298
- await server.connect(serverTransport);
320
+ const instance = await app.createServerInstance();
321
+ await instance.connect(serverTransport);
299
322
  await client.connect(clientTransport);
300
323
  await client.listTools();
301
324
  await client.callTool({ name: "t1" });
@@ -309,41 +332,55 @@ describe("McpServer.mcpMiddleware()", () => {
309
332
  const toolsCallIdx = calls.indexOf("global:tools/call");
310
333
  expect(calls[toolsCallIdx + 1]).toBe("tools-only");
311
334
  await client.close();
312
- await server.close();
335
+ await instance.close();
313
336
  });
314
337
  it("notification middleware receives extra as undefined", async () => {
315
338
  let capturedExtra = "sentinel";
316
- const server = new McpServer({ name: "test", version: "1.0.0" });
317
- server.registerTool({ name: "t1", description: "t1" }, () => ({
318
- content: [{ type: "text", text: "ok" }],
319
- }));
320
- server.mcpMiddleware("notification", async (_request, extra, next) => {
321
- capturedExtra = extra;
322
- return next();
339
+ const app = new Skybridge({
340
+ name: "test",
341
+ version: "1.0.0",
342
+ handler: (server) => {
343
+ server.registerTool({ name: "t1", description: "t1" }, () => ({
344
+ content: [{ type: "text", text: "ok" }],
345
+ }));
346
+ server.mcpMiddleware("notification", async (_request, extra, next) => {
347
+ capturedExtra = extra;
348
+ return next();
349
+ });
350
+ return server;
351
+ },
323
352
  });
324
353
  const client = createClient();
325
354
  const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair();
326
- await server.connect(serverTransport);
355
+ const instance = await app.createServerInstance();
356
+ await instance.connect(serverTransport);
327
357
  await client.connect(clientTransport);
328
358
  // The client sends notifications/initialized automatically on connect.
329
359
  await client.listTools();
330
360
  expect(capturedExtra).toBeUndefined();
331
361
  await client.close();
332
- await server.close();
362
+ await instance.close();
333
363
  });
334
364
  it("wildcard filter intercepts matching methods only", async () => {
335
365
  const matchedMethods = [];
336
- const server = new McpServer({ name: "test", version: "1.0.0" });
337
- server.registerTool({ name: "t1", description: "t1" }, () => ({
338
- content: [{ type: "text", text: "ok" }],
339
- }));
340
- server.mcpMiddleware("tools/*", async (request, _extra, next) => {
341
- matchedMethods.push(request.method);
342
- return next();
366
+ const app = new Skybridge({
367
+ name: "test",
368
+ version: "1.0.0",
369
+ handler: (server) => {
370
+ server.registerTool({ name: "t1", description: "t1" }, () => ({
371
+ content: [{ type: "text", text: "ok" }],
372
+ }));
373
+ server.mcpMiddleware("tools/*", async (request, _extra, next) => {
374
+ matchedMethods.push(request.method);
375
+ return next();
376
+ });
377
+ return server;
378
+ },
343
379
  });
344
380
  const client = createClient();
345
381
  const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair();
346
- await server.connect(serverTransport);
382
+ const instance = await app.createServerInstance();
383
+ await instance.connect(serverTransport);
347
384
  await client.connect(clientTransport);
348
385
  await client.listTools();
349
386
  await client.callTool({ name: "t1" });
@@ -353,30 +390,37 @@ describe("McpServer.mcpMiddleware()", () => {
353
390
  expect(matchedMethods).not.toContain("initialize");
354
391
  expect(matchedMethods).not.toContain("notifications/initialized");
355
392
  await client.close();
356
- await server.close();
357
- });
358
- it("middleware can modify tool result via McpServer integration", async () => {
359
- const server = new McpServer({ name: "test", version: "1.0.0" });
360
- server.registerTool({
361
- name: "greet",
362
- description: "greet",
363
- inputSchema: { name: z.string() },
364
- }, (args) => ({
365
- content: [{ type: "text", text: `hi ${args.name}` }],
366
- }));
367
- server.mcpMiddleware("tools/call", async (_req, _extra, next) => {
368
- const result = (await next());
369
- return {
370
- ...result,
371
- content: [
372
- ...result.content,
373
- { type: "text", text: " (modified)" },
374
- ],
375
- };
393
+ await instance.close();
394
+ });
395
+ it("middleware can modify tool result via Skybridge integration", async () => {
396
+ const app = new Skybridge({
397
+ name: "test",
398
+ version: "1.0.0",
399
+ handler: (server) => {
400
+ server.registerTool({
401
+ name: "greet",
402
+ description: "greet",
403
+ inputSchema: { name: z.string() },
404
+ }, (args) => ({
405
+ content: [{ type: "text", text: `hi ${args.name}` }],
406
+ }));
407
+ server.mcpMiddleware("tools/call", async (_req, _extra, next) => {
408
+ const result = (await next());
409
+ return {
410
+ ...result,
411
+ content: [
412
+ ...result.content,
413
+ { type: "text", text: " (modified)" },
414
+ ],
415
+ };
416
+ });
417
+ return server;
418
+ },
376
419
  });
377
420
  const client = createClient();
378
421
  const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair();
379
- await server.connect(serverTransport);
422
+ const instance = await app.createServerInstance();
423
+ await instance.connect(serverTransport);
380
424
  await client.connect(clientTransport);
381
425
  const result = await client.callTool({
382
426
  name: "greet",
@@ -387,50 +431,107 @@ describe("McpServer.mcpMiddleware()", () => {
387
431
  { type: "text", text: " (modified)" },
388
432
  ]);
389
433
  await client.close();
390
- await server.close();
434
+ await instance.close();
391
435
  });
392
- it("middleware can short-circuit via McpServer integration", async () => {
436
+ it("middleware can short-circuit via Skybridge integration", async () => {
393
437
  const handlerCalled = vi.fn();
394
- const server = new McpServer({ name: "test", version: "1.0.0" });
395
- server.registerTool({ name: "t1", description: "t1" }, () => {
396
- handlerCalled();
397
- return {
398
- content: [{ type: "text", text: "original" }],
399
- };
438
+ const app = new Skybridge({
439
+ name: "test",
440
+ version: "1.0.0",
441
+ handler: (server) => {
442
+ server.registerTool({ name: "t1", description: "t1" }, () => {
443
+ handlerCalled();
444
+ return {
445
+ content: [{ type: "text", text: "original" }],
446
+ };
447
+ });
448
+ server.mcpMiddleware("tools/call", async () => ({
449
+ content: [{ type: "text", text: "short-circuited" }],
450
+ }));
451
+ return server;
452
+ },
400
453
  });
401
- server.mcpMiddleware("tools/call", async () => ({
402
- content: [{ type: "text", text: "short-circuited" }],
403
- }));
404
454
  const client = createClient();
405
455
  const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair();
406
- await server.connect(serverTransport);
456
+ const instance = await app.createServerInstance();
457
+ await instance.connect(serverTransport);
407
458
  await client.connect(clientTransport);
408
459
  const result = await client.callTool({ name: "t1" });
409
460
  expect(result.content).toEqual([{ type: "text", text: "short-circuited" }]);
410
461
  expect(handlerCalled).not.toHaveBeenCalled();
411
462
  await client.close();
412
- await server.close();
463
+ await instance.close();
464
+ });
465
+ it("exposes a thrown tool error to middleware via getToolError", async () => {
466
+ const schemaError = new Error("boom with schema");
467
+ const schemalessError = new Error("boom without schema");
468
+ let captured;
469
+ const app = new Skybridge({
470
+ name: "test",
471
+ version: "1.0.0",
472
+ handler: (server) => {
473
+ server.registerTool({
474
+ name: "withSchema",
475
+ description: "withSchema",
476
+ inputSchema: { name: z.string() },
477
+ }, () => {
478
+ throw schemaError;
479
+ });
480
+ server.registerTool({ name: "withoutSchema", description: "withoutSchema" }, () => {
481
+ throw schemalessError;
482
+ });
483
+ server.mcpMiddleware("tools/call", async (_request, extra, next) => {
484
+ const result = await next();
485
+ captured = getToolError(extra);
486
+ return result;
487
+ });
488
+ return server;
489
+ },
490
+ });
491
+ const client = createClient();
492
+ const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair();
493
+ const instance = await app.createServerInstance();
494
+ await instance.connect(serverTransport);
495
+ await client.connect(clientTransport);
496
+ const withSchema = await client.callTool({
497
+ name: "withSchema",
498
+ arguments: { name: "World" },
499
+ });
500
+ expect(withSchema.isError).toBe(true);
501
+ expect(captured).toBe(schemaError);
502
+ const withoutSchema = await client.callTool({ name: "withoutSchema" });
503
+ expect(withoutSchema.isError).toBe(true);
504
+ expect(captured).toBe(schemalessError);
505
+ await client.close();
506
+ await instance.close();
413
507
  });
414
- it("middleware can mutate tool call params via McpServer integration", async () => {
508
+ it("middleware can mutate tool call params via Skybridge integration", async () => {
415
509
  let receivedName = "";
416
- const server = new McpServer({ name: "test", version: "1.0.0" });
417
- server.registerTool({
418
- name: "greet",
419
- description: "greet",
420
- inputSchema: { name: z.string() },
421
- }, (args) => {
422
- receivedName = args.name;
423
- return {
424
- content: [{ type: "text", text: `hi ${args.name}` }],
425
- };
426
- });
427
- server.mcpMiddleware("tools/call", async (request, _extra, next) => {
428
- request.params.arguments = { name: "Overridden" };
429
- return next();
510
+ const app = new Skybridge({
511
+ name: "test",
512
+ version: "1.0.0",
513
+ handler: (server) => {
514
+ server.registerTool({
515
+ name: "greet",
516
+ description: "greet",
517
+ inputSchema: { name: z.string() },
518
+ }, (args) => {
519
+ receivedName = args.name;
520
+ return {
521
+ content: [{ type: "text", text: `hi ${args.name}` }],
522
+ };
523
+ });
524
+ server.mcpMiddleware("tools/call", async (request, _extra, next) => {
525
+ request.params.arguments = { name: "Overridden" };
526
+ return next();
527
+ });
528
+ return server;
529
+ },
430
530
  });
431
531
  const client = createClient();
432
532
  const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair();
433
- await server.connect(serverTransport);
533
+ const instance = await app.createServerInstance();
534
+ await instance.connect(serverTransport);
434
535
  await client.connect(clientTransport);
435
536
  await client.callTool({
436
537
  name: "greet",
@@ -438,21 +539,28 @@ describe("McpServer.mcpMiddleware()", () => {
438
539
  });
439
540
  expect(receivedName).toBe("Overridden");
440
541
  await client.close();
441
- await server.close();
542
+ await instance.close();
442
543
  });
443
544
  it("category 'request' filter matches requests but not notifications", async () => {
444
545
  const matchedMethods = [];
445
- const server = new McpServer({ name: "test", version: "1.0.0" });
446
- server.registerTool({ name: "t1", description: "t1" }, () => ({
447
- content: [{ type: "text", text: "ok" }],
448
- }));
449
- server.mcpMiddleware("request", async (request, _extra, next) => {
450
- matchedMethods.push(request.method);
451
- return next();
546
+ const app = new Skybridge({
547
+ name: "test",
548
+ version: "1.0.0",
549
+ handler: (server) => {
550
+ server.registerTool({ name: "t1", description: "t1" }, () => ({
551
+ content: [{ type: "text", text: "ok" }],
552
+ }));
553
+ server.mcpMiddleware("request", async (request, _extra, next) => {
554
+ matchedMethods.push(request.method);
555
+ return next();
556
+ });
557
+ return server;
558
+ },
452
559
  });
453
560
  const client = createClient();
454
561
  const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair();
455
- await server.connect(serverTransport);
562
+ const instance = await app.createServerInstance();
563
+ await instance.connect(serverTransport);
456
564
  await client.connect(clientTransport);
457
565
  await client.listTools();
458
566
  await client.callTool({ name: "t1" });
@@ -460,7 +568,7 @@ describe("McpServer.mcpMiddleware()", () => {
460
568
  expect(matchedMethods).toContain("tools/call");
461
569
  expect(matchedMethods).not.toContain("notifications/initialized");
462
570
  await client.close();
463
- await server.close();
571
+ await instance.close();
464
572
  });
465
573
  });
466
574
  // ---------------------------------------------------------------------------