skybridge 0.0.0-dev.e341b6a → 0.0.0-dev.e365d28

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 (296) hide show
  1. package/README.md +153 -0
  2. package/dist/cli/detect-port.d.ts +18 -0
  3. package/dist/cli/detect-port.js +61 -0
  4. package/dist/cli/detect-port.js.map +1 -0
  5. package/dist/cli/header.d.ts +4 -0
  6. package/dist/cli/header.js +6 -0
  7. package/dist/cli/header.js.map +1 -0
  8. package/dist/cli/run-command.d.ts +2 -0
  9. package/dist/cli/run-command.js +43 -0
  10. package/dist/cli/run-command.js.map +1 -0
  11. package/dist/cli/telemetry.d.ts +7 -0
  12. package/dist/cli/telemetry.js +123 -0
  13. package/dist/cli/telemetry.js.map +1 -0
  14. package/dist/cli/types.d.ts +5 -0
  15. package/dist/cli/types.js +2 -0
  16. package/dist/cli/types.js.map +1 -0
  17. package/dist/cli/use-execute-steps.d.ts +11 -0
  18. package/dist/cli/use-execute-steps.js +36 -0
  19. package/dist/cli/use-execute-steps.js.map +1 -0
  20. package/dist/cli/use-messages.d.ts +3 -0
  21. package/dist/cli/use-messages.js +11 -0
  22. package/dist/cli/use-messages.js.map +1 -0
  23. package/dist/cli/use-nodemon.d.ts +2 -0
  24. package/dist/cli/use-nodemon.js +64 -0
  25. package/dist/cli/use-nodemon.js.map +1 -0
  26. package/dist/cli/use-tunnel.d.ts +14 -0
  27. package/dist/cli/use-tunnel.js +97 -0
  28. package/dist/cli/use-tunnel.js.map +1 -0
  29. package/dist/cli/use-typescript-check.d.ts +9 -0
  30. package/dist/cli/use-typescript-check.js +94 -0
  31. package/dist/cli/use-typescript-check.js.map +1 -0
  32. package/dist/commands/build.d.ts +9 -0
  33. package/dist/commands/build.js +67 -0
  34. package/dist/commands/build.js.map +1 -0
  35. package/dist/commands/dev.d.ts +11 -0
  36. package/dist/commands/dev.js +49 -0
  37. package/dist/commands/dev.js.map +1 -0
  38. package/dist/commands/start.d.ts +9 -0
  39. package/dist/commands/start.js +49 -0
  40. package/dist/commands/start.js.map +1 -0
  41. package/dist/commands/telemetry/disable.d.ts +5 -0
  42. package/dist/commands/telemetry/disable.js +14 -0
  43. package/dist/commands/telemetry/disable.js.map +1 -0
  44. package/dist/commands/telemetry/enable.d.ts +5 -0
  45. package/dist/commands/telemetry/enable.js +14 -0
  46. package/dist/commands/telemetry/enable.js.map +1 -0
  47. package/dist/commands/telemetry/status.d.ts +5 -0
  48. package/dist/commands/telemetry/status.js +14 -0
  49. package/dist/commands/telemetry/status.js.map +1 -0
  50. package/dist/server/asset-base-url-transform-plugin.d.ts +10 -0
  51. package/dist/server/asset-base-url-transform-plugin.js +33 -0
  52. package/dist/server/asset-base-url-transform-plugin.js.map +1 -0
  53. package/dist/server/asset-base-url-transform-plugin.test.js +84 -0
  54. package/dist/server/asset-base-url-transform-plugin.test.js.map +1 -0
  55. package/dist/server/content-helpers.d.ts +27 -0
  56. package/dist/server/content-helpers.js +46 -0
  57. package/dist/server/content-helpers.js.map +1 -0
  58. package/dist/server/content-helpers.test.js +70 -0
  59. package/dist/server/content-helpers.test.js.map +1 -0
  60. package/dist/server/express.d.ts +15 -0
  61. package/dist/server/express.js +77 -0
  62. package/dist/server/express.js.map +1 -0
  63. package/dist/server/express.test.js +289 -0
  64. package/dist/server/express.test.js.map +1 -0
  65. package/dist/server/index.d.ts +5 -3
  66. package/dist/server/index.js +3 -2
  67. package/dist/server/index.js.map +1 -1
  68. package/dist/server/inferUtilityTypes.d.ts +6 -6
  69. package/dist/server/metric.d.ts +14 -0
  70. package/dist/server/metric.js +62 -0
  71. package/dist/server/metric.js.map +1 -0
  72. package/dist/server/middleware.d.ts +124 -0
  73. package/dist/server/middleware.js +93 -0
  74. package/dist/server/middleware.js.map +1 -0
  75. package/dist/server/middleware.test-d.d.ts +1 -0
  76. package/dist/server/middleware.test-d.js +75 -0
  77. package/dist/server/middleware.test-d.js.map +1 -0
  78. package/dist/server/middleware.test.d.ts +1 -0
  79. package/dist/server/middleware.test.js +493 -0
  80. package/dist/server/middleware.test.js.map +1 -0
  81. package/dist/server/server.d.ts +137 -65
  82. package/dist/server/server.js +395 -89
  83. package/dist/server/server.js.map +1 -1
  84. package/dist/server/templateHelper.d.ts +5 -5
  85. package/dist/server/templates/development.hbs +3 -57
  86. package/dist/server/templates/production.hbs +2 -3
  87. package/dist/server/viewsDevServer.d.ts +14 -0
  88. package/dist/server/viewsDevServer.js +45 -0
  89. package/dist/server/viewsDevServer.js.map +1 -0
  90. package/dist/test/utils.d.ts +13 -21
  91. package/dist/test/utils.js +42 -37
  92. package/dist/test/utils.js.map +1 -1
  93. package/dist/test/view.test.d.ts +1 -0
  94. package/dist/test/view.test.js +523 -0
  95. package/dist/test/view.test.js.map +1 -0
  96. package/dist/version.d.ts +1 -0
  97. package/dist/version.js +5 -0
  98. package/dist/version.js.map +1 -0
  99. package/dist/web/bridges/apps-sdk/adaptor.d.ts +24 -0
  100. package/dist/web/bridges/apps-sdk/adaptor.js +93 -0
  101. package/dist/web/bridges/apps-sdk/adaptor.js.map +1 -0
  102. package/dist/web/bridges/apps-sdk/bridge.d.ts +10 -0
  103. package/dist/web/bridges/{apps-sdk-bridge.js → apps-sdk/bridge.js} +2 -2
  104. package/dist/web/bridges/apps-sdk/bridge.js.map +1 -0
  105. package/dist/web/bridges/apps-sdk/index.d.ts +5 -0
  106. package/dist/web/bridges/apps-sdk/index.js +5 -0
  107. package/dist/web/bridges/apps-sdk/index.js.map +1 -0
  108. package/dist/web/bridges/apps-sdk/types.d.ts +126 -0
  109. package/dist/web/bridges/apps-sdk/types.js +10 -0
  110. package/dist/web/bridges/apps-sdk/types.js.map +1 -0
  111. package/dist/web/bridges/apps-sdk/use-apps-sdk-context.d.ts +2 -0
  112. package/dist/web/bridges/{hooks/use-apps-sdk-bridge.js → apps-sdk/use-apps-sdk-context.js} +3 -3
  113. package/dist/web/bridges/apps-sdk/use-apps-sdk-context.js.map +1 -0
  114. package/dist/web/bridges/get-adaptor.d.ts +2 -0
  115. package/dist/web/bridges/get-adaptor.js +8 -0
  116. package/dist/web/bridges/get-adaptor.js.map +1 -0
  117. package/dist/web/bridges/index.d.ts +5 -4
  118. package/dist/web/bridges/index.js +5 -4
  119. package/dist/web/bridges/index.js.map +1 -1
  120. package/dist/web/bridges/mcp-app/adaptor.d.ts +48 -0
  121. package/dist/web/bridges/mcp-app/adaptor.js +268 -0
  122. package/dist/web/bridges/mcp-app/adaptor.js.map +1 -0
  123. package/dist/web/bridges/mcp-app/bridge.d.ts +26 -0
  124. package/dist/web/bridges/mcp-app/bridge.js +102 -0
  125. package/dist/web/bridges/mcp-app/bridge.js.map +1 -0
  126. package/dist/web/bridges/mcp-app/index.d.ts +4 -0
  127. package/dist/web/bridges/mcp-app/index.js +4 -0
  128. package/dist/web/bridges/mcp-app/index.js.map +1 -0
  129. package/dist/web/bridges/mcp-app/types.d.ts +8 -0
  130. package/dist/web/bridges/mcp-app/types.js +2 -0
  131. package/dist/web/bridges/mcp-app/types.js.map +1 -0
  132. package/dist/web/bridges/mcp-app/use-mcp-app-context.d.ts +7 -0
  133. package/dist/web/bridges/mcp-app/use-mcp-app-context.js +7 -0
  134. package/dist/web/bridges/mcp-app/use-mcp-app-context.js.map +1 -0
  135. package/dist/web/bridges/mcp-app/use-mcp-app-context.test.d.ts +1 -0
  136. package/dist/web/bridges/mcp-app/use-mcp-app-context.test.js +26 -0
  137. package/dist/web/bridges/mcp-app/use-mcp-app-context.test.js.map +1 -0
  138. package/dist/web/bridges/types.d.ts +79 -27
  139. package/dist/web/bridges/use-host-context.d.ts +2 -0
  140. package/dist/web/bridges/use-host-context.js +8 -0
  141. package/dist/web/bridges/use-host-context.js.map +1 -0
  142. package/dist/web/components/modal-provider.d.ts +4 -0
  143. package/dist/web/components/modal-provider.js +45 -0
  144. package/dist/web/components/modal-provider.js.map +1 -0
  145. package/dist/web/create-store.js +21 -8
  146. package/dist/web/create-store.js.map +1 -1
  147. package/dist/web/create-store.test.js +112 -53
  148. package/dist/web/create-store.test.js.map +1 -1
  149. package/dist/web/data-llm.d.ts +1 -1
  150. package/dist/web/data-llm.js +7 -5
  151. package/dist/web/data-llm.js.map +1 -1
  152. package/dist/web/data-llm.test.js +131 -65
  153. package/dist/web/data-llm.test.js.map +1 -1
  154. package/dist/web/generate-helpers.d.ts +22 -19
  155. package/dist/web/generate-helpers.js +20 -18
  156. package/dist/web/generate-helpers.js.map +1 -1
  157. package/dist/web/generate-helpers.test-d.js +26 -26
  158. package/dist/web/generate-helpers.test-d.js.map +1 -1
  159. package/dist/web/helpers/state.d.ts +2 -2
  160. package/dist/web/helpers/state.js +17 -12
  161. package/dist/web/helpers/state.js.map +1 -1
  162. package/dist/web/helpers/state.test.js +9 -9
  163. package/dist/web/helpers/state.test.js.map +1 -1
  164. package/dist/web/hooks/index.d.ts +3 -3
  165. package/dist/web/hooks/index.js +2 -2
  166. package/dist/web/hooks/index.js.map +1 -1
  167. package/dist/web/hooks/test/utils.d.ts +8 -2
  168. package/dist/web/hooks/test/utils.js +37 -13
  169. package/dist/web/hooks/test/utils.js.map +1 -1
  170. package/dist/web/hooks/use-call-tool.d.ts +3 -2
  171. package/dist/web/hooks/use-call-tool.js +2 -2
  172. package/dist/web/hooks/use-call-tool.js.map +1 -1
  173. package/dist/web/hooks/use-call-tool.test-d.js +1 -1
  174. package/dist/web/hooks/use-call-tool.test-d.js.map +1 -1
  175. package/dist/web/hooks/use-call-tool.test.js +3 -3
  176. package/dist/web/hooks/use-call-tool.test.js.map +1 -1
  177. package/dist/web/hooks/use-display-mode.d.ts +3 -3
  178. package/dist/web/hooks/use-display-mode.js +3 -4
  179. package/dist/web/hooks/use-display-mode.js.map +1 -1
  180. package/dist/web/hooks/use-display-mode.test-d.d.ts +1 -0
  181. package/dist/web/hooks/use-display-mode.test-d.js +8 -0
  182. package/dist/web/hooks/use-display-mode.test-d.js.map +1 -0
  183. package/dist/web/hooks/use-files.d.ts +3 -6
  184. package/dist/web/hooks/use-files.js +5 -2
  185. package/dist/web/hooks/use-files.js.map +1 -1
  186. package/dist/web/hooks/use-files.test.js +28 -3
  187. package/dist/web/hooks/use-files.test.js.map +1 -1
  188. package/dist/web/hooks/use-layout.d.ts +2 -2
  189. package/dist/web/hooks/use-layout.js +4 -4
  190. package/dist/web/hooks/use-layout.js.map +1 -1
  191. package/dist/web/hooks/use-layout.test.js +7 -6
  192. package/dist/web/hooks/use-layout.test.js.map +1 -1
  193. package/dist/web/hooks/use-open-external.d.ts +3 -1
  194. package/dist/web/hooks/use-open-external.js +3 -3
  195. package/dist/web/hooks/use-open-external.js.map +1 -1
  196. package/dist/web/hooks/use-open-external.test.js +27 -12
  197. package/dist/web/hooks/use-open-external.test.js.map +1 -1
  198. package/dist/web/hooks/use-request-modal.d.ts +3 -3
  199. package/dist/web/hooks/use-request-modal.js +10 -8
  200. package/dist/web/hooks/use-request-modal.js.map +1 -1
  201. package/dist/web/hooks/use-request-modal.test.js +5 -1
  202. package/dist/web/hooks/use-request-modal.test.js.map +1 -1
  203. package/dist/web/hooks/use-send-follow-up-message.js +2 -2
  204. package/dist/web/hooks/use-set-open-in-app-url.d.ts +1 -0
  205. package/dist/web/hooks/use-set-open-in-app-url.js +8 -0
  206. package/dist/web/hooks/use-set-open-in-app-url.js.map +1 -0
  207. package/dist/web/hooks/use-set-open-in-app-url.test.d.ts +1 -0
  208. package/dist/web/hooks/use-set-open-in-app-url.test.js +43 -0
  209. package/dist/web/hooks/use-set-open-in-app-url.test.js.map +1 -0
  210. package/dist/web/hooks/use-tool-info.js +4 -4
  211. package/dist/web/hooks/use-tool-info.js.map +1 -1
  212. package/dist/web/hooks/use-tool-info.test.js +5 -5
  213. package/dist/web/hooks/use-tool-info.test.js.map +1 -1
  214. package/dist/web/hooks/use-user.d.ts +1 -1
  215. package/dist/web/hooks/use-user.js +20 -4
  216. package/dist/web/hooks/use-user.js.map +1 -1
  217. package/dist/web/hooks/use-user.test.js +33 -4
  218. package/dist/web/hooks/use-user.test.js.map +1 -1
  219. package/dist/web/hooks/use-view-state.d.ts +4 -0
  220. package/dist/web/hooks/use-view-state.js +32 -0
  221. package/dist/web/hooks/use-view-state.js.map +1 -0
  222. package/dist/web/hooks/use-view-state.test.d.ts +1 -0
  223. package/dist/web/hooks/use-view-state.test.js +177 -0
  224. package/dist/web/hooks/use-view-state.test.js.map +1 -0
  225. package/dist/web/index.d.ts +1 -2
  226. package/dist/web/index.js +1 -2
  227. package/dist/web/index.js.map +1 -1
  228. package/dist/web/mount-view.d.ts +1 -0
  229. package/dist/web/{mount-widget.js → mount-view.js} +11 -3
  230. package/dist/web/mount-view.js.map +1 -0
  231. package/dist/web/plugin/plugin.d.ts +4 -1
  232. package/dist/web/plugin/plugin.js +142 -18
  233. package/dist/web/plugin/plugin.js.map +1 -1
  234. package/dist/web/plugin/scan-views.d.ts +16 -0
  235. package/dist/web/plugin/scan-views.js +88 -0
  236. package/dist/web/plugin/scan-views.js.map +1 -0
  237. package/dist/web/plugin/scan-views.test.d.ts +1 -0
  238. package/dist/web/plugin/scan-views.test.js +99 -0
  239. package/dist/web/plugin/scan-views.test.js.map +1 -0
  240. package/dist/web/plugin/transform-data-llm.js +1 -1
  241. package/dist/web/plugin/transform-data-llm.js.map +1 -1
  242. package/dist/web/plugin/validate-view.d.ts +1 -0
  243. package/dist/web/plugin/validate-view.js +9 -0
  244. package/dist/web/plugin/validate-view.js.map +1 -0
  245. package/dist/web/plugin/validate-view.test.d.ts +1 -0
  246. package/dist/web/plugin/validate-view.test.js +24 -0
  247. package/dist/web/plugin/validate-view.test.js.map +1 -0
  248. package/dist/web/proxy.js +0 -1
  249. package/dist/web/proxy.js.map +1 -1
  250. package/dist/web/types.d.ts +0 -133
  251. package/dist/web/types.js +0 -9
  252. package/dist/web/types.js.map +1 -1
  253. package/package.json +46 -27
  254. package/tsconfig.base.json +33 -0
  255. package/dist/server/widgetsDevServer.d.ts +0 -12
  256. package/dist/server/widgetsDevServer.js +0 -47
  257. package/dist/server/widgetsDevServer.js.map +0 -1
  258. package/dist/test/widget.test.js +0 -250
  259. package/dist/test/widget.test.js.map +0 -1
  260. package/dist/web/bridges/adaptors/apps-sdk-adaptor.d.ts +0 -13
  261. package/dist/web/bridges/adaptors/apps-sdk-adaptor.js +0 -33
  262. package/dist/web/bridges/adaptors/apps-sdk-adaptor.js.map +0 -1
  263. package/dist/web/bridges/adaptors/mcp-app-adaptor.d.ts +0 -16
  264. package/dist/web/bridges/adaptors/mcp-app-adaptor.js +0 -115
  265. package/dist/web/bridges/adaptors/mcp-app-adaptor.js.map +0 -1
  266. package/dist/web/bridges/apps-sdk-bridge.d.ts +0 -10
  267. package/dist/web/bridges/apps-sdk-bridge.js.map +0 -1
  268. package/dist/web/bridges/hooks/use-adaptor.d.ts +0 -2
  269. package/dist/web/bridges/hooks/use-adaptor.js +0 -8
  270. package/dist/web/bridges/hooks/use-adaptor.js.map +0 -1
  271. package/dist/web/bridges/hooks/use-apps-sdk-bridge.d.ts +0 -2
  272. package/dist/web/bridges/hooks/use-apps-sdk-bridge.js.map +0 -1
  273. package/dist/web/bridges/hooks/use-bridge.d.ts +0 -2
  274. package/dist/web/bridges/hooks/use-bridge.js +0 -8
  275. package/dist/web/bridges/hooks/use-bridge.js.map +0 -1
  276. package/dist/web/bridges/hooks/use-mcp-app-bridge.d.ts +0 -5
  277. package/dist/web/bridges/hooks/use-mcp-app-bridge.js +0 -7
  278. package/dist/web/bridges/hooks/use-mcp-app-bridge.js.map +0 -1
  279. package/dist/web/bridges/hooks/use-mcp-app-bridge.test.js +0 -41
  280. package/dist/web/bridges/hooks/use-mcp-app-bridge.test.js.map +0 -1
  281. package/dist/web/bridges/mcp-app-bridge.d.ts +0 -38
  282. package/dist/web/bridges/mcp-app-bridge.js +0 -162
  283. package/dist/web/bridges/mcp-app-bridge.js.map +0 -1
  284. package/dist/web/hooks/use-openai-global.d.ts +0 -3
  285. package/dist/web/hooks/use-openai-global.js +0 -6
  286. package/dist/web/hooks/use-openai-global.js.map +0 -1
  287. package/dist/web/hooks/use-widget-state.d.ts +0 -4
  288. package/dist/web/hooks/use-widget-state.js +0 -32
  289. package/dist/web/hooks/use-widget-state.js.map +0 -1
  290. package/dist/web/hooks/use-widget-state.test.js +0 -61
  291. package/dist/web/hooks/use-widget-state.test.js.map +0 -1
  292. package/dist/web/mount-widget.d.ts +0 -1
  293. package/dist/web/mount-widget.js.map +0 -1
  294. /package/dist/{test/widget.test.d.ts → server/asset-base-url-transform-plugin.test.d.ts} +0 -0
  295. /package/dist/{web/bridges/hooks/use-mcp-app-bridge.test.d.ts → server/content-helpers.test.d.ts} +0 -0
  296. /package/dist/{web/hooks/use-widget-state.test.d.ts → server/express.test.d.ts} +0 -0
@@ -0,0 +1,84 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { assetBaseUrlTransform } from "./asset-base-url-transform-plugin.js";
3
+ describe("assetBaseUrlTransform", () => {
4
+ it("should transform asset paths to use window.skybridge.serverUrl", () => {
5
+ const cases = [
6
+ {
7
+ desc: "single-quoted",
8
+ code: `const image = '/assets/logo.png';`,
9
+ expected: `const image = (window.skybridge?.serverUrl ?? "") + "/assets/logo.png";`,
10
+ },
11
+ {
12
+ desc: "double-quoted",
13
+ code: `const image = "/assets/logo.png";`,
14
+ expected: `const image = (window.skybridge?.serverUrl ?? "") + "/assets/logo.png";`,
15
+ },
16
+ {
17
+ desc: "backtick-quoted",
18
+ code: "const image = `/assets/logo.png`;",
19
+ expected: `const image = (window.skybridge?.serverUrl ?? "") + "/assets/logo.png";`,
20
+ },
21
+ ];
22
+ for (const { code, expected } of cases) {
23
+ const result = assetBaseUrlTransform(code);
24
+ expect(result).toBe(expected);
25
+ }
26
+ });
27
+ it("should transform multiple asset paths", () => {
28
+ const code = `
29
+ const logo = '/assets/logo.png';
30
+ const icon = '/assets/icon.svg';
31
+ const font = '/assets/font.woff2';
32
+ `;
33
+ const result = assetBaseUrlTransform(code);
34
+ expect(result).toContain(`(window.skybridge?.serverUrl ?? "") + "/assets/logo.png"`);
35
+ expect(result).toContain(`(window.skybridge?.serverUrl ?? "") + "/assets/icon.svg"`);
36
+ expect(result).toContain(`(window.skybridge?.serverUrl ?? "") + "/assets/font.woff2"`);
37
+ });
38
+ it("should not transform already absolute URLs", () => {
39
+ const code = `
40
+ const local = '/assets/logo.png';
41
+ const http = 'http://example.com/image.png';
42
+ const https = 'https://example.com/image.png';
43
+ `;
44
+ const result = assetBaseUrlTransform(code);
45
+ expect(result).toContain(`(window.skybridge?.serverUrl ?? "") + "/assets/logo.png"`);
46
+ expect(result).toContain("http://example.com/image.png");
47
+ expect(result).toContain("https://example.com/image.png");
48
+ });
49
+ it("should not transform code without asset paths", () => {
50
+ const code = `const text = "Hello World";`;
51
+ const result = assetBaseUrlTransform(code);
52
+ expect(result).toBe(code);
53
+ });
54
+ it("should not transform asset paths inside static `import ... from` clauses", () => {
55
+ // Reproducer for #713: a dep does `import * as sprite from './icons.svg'`,
56
+ // Vite resolves the relative path to absolute, then this transform used
57
+ // to rewrite the resolved string — producing invalid JS like
58
+ // `import * as sprite from (expr) + "..."` that crashes vite:import-analysis.
59
+ const cases = [
60
+ `import * as sprite from "/Users/me/proj/node_modules/pkg/icons.svg";`,
61
+ `import sprite from '/assets/icons.svg';`,
62
+ `import sprite from "/assets/icons.svg";`,
63
+ `export { default } from "/assets/icons.svg";`,
64
+ `export * from '/assets/sprites.svg';`,
65
+ ];
66
+ for (const code of cases) {
67
+ expect(assetBaseUrlTransform(code)).toBe(code);
68
+ }
69
+ });
70
+ it("should still transform value-position asset paths in files that also have unrelated imports", () => {
71
+ const code = [
72
+ `import { foo } from "./foo.js";`,
73
+ `import * as sprite from "/assets/sprite.svg";`,
74
+ `const logo = "/assets/logo.png";`,
75
+ ].join("\n");
76
+ const result = assetBaseUrlTransform(code);
77
+ // Imports untouched
78
+ expect(result).toContain(`from "./foo.js"`);
79
+ expect(result).toContain(`from "/assets/sprite.svg"`);
80
+ // Value-position rewritten
81
+ expect(result).toContain(`const logo = (window.skybridge?.serverUrl ?? "") + "/assets/logo.png";`);
82
+ });
83
+ });
84
+ //# sourceMappingURL=asset-base-url-transform-plugin.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"asset-base-url-transform-plugin.test.js","sourceRoot":"","sources":["../../src/server/asset-base-url-transform-plugin.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,qBAAqB,EAAE,MAAM,sCAAsC,CAAC;AAE7E,QAAQ,CAAC,uBAAuB,EAAE,GAAG,EAAE;IACrC,EAAE,CAAC,gEAAgE,EAAE,GAAG,EAAE;QACxE,MAAM,KAAK,GAAG;YACZ;gBACE,IAAI,EAAE,eAAe;gBACrB,IAAI,EAAE,mCAAmC;gBACzC,QAAQ,EAAE,yEAAyE;aACpF;YACD;gBACE,IAAI,EAAE,eAAe;gBACrB,IAAI,EAAE,mCAAmC;gBACzC,QAAQ,EAAE,yEAAyE;aACpF;YACD;gBACE,IAAI,EAAE,iBAAiB;gBACvB,IAAI,EAAE,mCAAmC;gBACzC,QAAQ,EAAE,yEAAyE;aACpF;SACF,CAAC;QAEF,KAAK,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,KAAK,EAAE,CAAC;YACvC,MAAM,MAAM,GAAG,qBAAqB,CAAC,IAAI,CAAC,CAAC;YAC3C,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QAChC,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,uCAAuC,EAAE,GAAG,EAAE;QAC/C,MAAM,IAAI,GAAG;;;;KAIZ,CAAC;QACF,MAAM,MAAM,GAAG,qBAAqB,CAAC,IAAI,CAAC,CAAC;QAE3C,MAAM,CAAC,MAAM,CAAC,CAAC,SAAS,CACtB,0DAA0D,CAC3D,CAAC;QACF,MAAM,CAAC,MAAM,CAAC,CAAC,SAAS,CACtB,0DAA0D,CAC3D,CAAC;QACF,MAAM,CAAC,MAAM,CAAC,CAAC,SAAS,CACtB,4DAA4D,CAC7D,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,4CAA4C,EAAE,GAAG,EAAE;QACpD,MAAM,IAAI,GAAG;;;;KAIZ,CAAC;QACF,MAAM,MAAM,GAAG,qBAAqB,CAAC,IAAI,CAAC,CAAC;QAE3C,MAAM,CAAC,MAAM,CAAC,CAAC,SAAS,CACtB,0DAA0D,CAC3D,CAAC;QACF,MAAM,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,8BAA8B,CAAC,CAAC;QACzD,MAAM,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,+BAA+B,CAAC,CAAC;IAC5D,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,+CAA+C,EAAE,GAAG,EAAE;QACvD,MAAM,IAAI,GAAG,6BAA6B,CAAC;QAC3C,MAAM,MAAM,GAAG,qBAAqB,CAAC,IAAI,CAAC,CAAC;QAE3C,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC5B,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,0EAA0E,EAAE,GAAG,EAAE;QAClF,2EAA2E;QAC3E,wEAAwE;QACxE,6DAA6D;QAC7D,8EAA8E;QAC9E,MAAM,KAAK,GAAG;YACZ,sEAAsE;YACtE,yCAAyC;YACzC,yCAAyC;YACzC,8CAA8C;YAC9C,sCAAsC;SACvC,CAAC;QAEF,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACzB,MAAM,CAAC,qBAAqB,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACjD,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,6FAA6F,EAAE,GAAG,EAAE;QACrG,MAAM,IAAI,GAAG;YACX,iCAAiC;YACjC,+CAA+C;YAC/C,kCAAkC;SACnC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACb,MAAM,MAAM,GAAG,qBAAqB,CAAC,IAAI,CAAC,CAAC;QAE3C,oBAAoB;QACpB,MAAM,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,iBAAiB,CAAC,CAAC;QAC5C,MAAM,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,2BAA2B,CAAC,CAAC;QACtD,2BAA2B;QAC3B,MAAM,CAAC,MAAM,CAAC,CAAC,SAAS,CACtB,wEAAwE,CACzE,CAAC;IACJ,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -0,0 +1,27 @@
1
+ import type { AudioContent, EmbeddedResource, ImageContent, ResourceLink, TextContent } from "@modelcontextprotocol/sdk/types.js";
2
+ type ContentAnnotations = {
3
+ audience?: ("user" | "assistant")[];
4
+ priority?: number;
5
+ lastModified?: string;
6
+ };
7
+ export declare function text(value: string, annotations?: ContentAnnotations): TextContent;
8
+ export declare function image(data: string | Uint8Array, mimeType: string, annotations?: ContentAnnotations): ImageContent;
9
+ export declare function audio(data: string | Uint8Array, mimeType: string, annotations?: ContentAnnotations): AudioContent;
10
+ export declare function embeddedResource(resource: {
11
+ uri: string;
12
+ mimeType?: string;
13
+ text: string;
14
+ } | {
15
+ uri: string;
16
+ mimeType?: string;
17
+ blob: string;
18
+ }, annotations?: ContentAnnotations): EmbeddedResource;
19
+ export declare function resourceLink(link: {
20
+ uri: string;
21
+ name: string;
22
+ title?: string;
23
+ description?: string;
24
+ mimeType?: string;
25
+ size?: number;
26
+ }, annotations?: ContentAnnotations): ResourceLink;
27
+ export {};
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Returns a base64-encoded string.
3
+ * - `Uint8Array` input is encoded via `Buffer.toString("base64")`.
4
+ * - `string` input is assumed to be **already base64-encoded** and is returned
5
+ * as-is. Passing raw/unencoded string bytes will produce invalid MCP content.
6
+ */
7
+ function toBase64(data) {
8
+ if (typeof data === "string") {
9
+ return data;
10
+ }
11
+ return Buffer.from(data).toString("base64");
12
+ }
13
+ export function text(value, annotations) {
14
+ return { type: "text", text: value, ...(annotations && { annotations }) };
15
+ }
16
+ export function image(data, mimeType, annotations) {
17
+ return {
18
+ type: "image",
19
+ data: toBase64(data),
20
+ mimeType,
21
+ ...(annotations && { annotations }),
22
+ };
23
+ }
24
+ export function audio(data, mimeType, annotations) {
25
+ return {
26
+ type: "audio",
27
+ data: toBase64(data),
28
+ mimeType,
29
+ ...(annotations && { annotations }),
30
+ };
31
+ }
32
+ export function embeddedResource(resource, annotations) {
33
+ return {
34
+ type: "resource",
35
+ resource,
36
+ ...(annotations && { annotations }),
37
+ };
38
+ }
39
+ export function resourceLink(link, annotations) {
40
+ return {
41
+ type: "resource_link",
42
+ ...link,
43
+ ...(annotations && { annotations }),
44
+ };
45
+ }
46
+ //# sourceMappingURL=content-helpers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"content-helpers.js","sourceRoot":"","sources":["../../src/server/content-helpers.ts"],"names":[],"mappings":"AAcA;;;;;GAKG;AACH,SAAS,QAAQ,CAAC,IAAyB;IACzC,IAAI,OAAO,IAAI,KAAK,QAAQ,EAAE,CAAC;QAC7B,OAAO,IAAI,CAAC;IACd,CAAC;IACD,OAAO,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;AAC9C,CAAC;AAED,MAAM,UAAU,IAAI,CAClB,KAAa,EACb,WAAgC;IAEhC,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,CAAC,WAAW,IAAI,EAAE,WAAW,EAAE,CAAC,EAAE,CAAC;AAC5E,CAAC;AAED,MAAM,UAAU,KAAK,CACnB,IAAyB,EACzB,QAAgB,EAChB,WAAgC;IAEhC,OAAO;QACL,IAAI,EAAE,OAAO;QACb,IAAI,EAAE,QAAQ,CAAC,IAAI,CAAC;QACpB,QAAQ;QACR,GAAG,CAAC,WAAW,IAAI,EAAE,WAAW,EAAE,CAAC;KACpC,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,KAAK,CACnB,IAAyB,EACzB,QAAgB,EAChB,WAAgC;IAEhC,OAAO;QACL,IAAI,EAAE,OAAO;QACb,IAAI,EAAE,QAAQ,CAAC,IAAI,CAAC;QACpB,QAAQ;QACR,GAAG,CAAC,WAAW,IAAI,EAAE,WAAW,EAAE,CAAC;KACpC,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,gBAAgB,CAC9B,QAEoD,EACpD,WAAgC;IAEhC,OAAO;QACL,IAAI,EAAE,UAAU;QAChB,QAAQ;QACR,GAAG,CAAC,WAAW,IAAI,EAAE,WAAW,EAAE,CAAC;KACpC,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,YAAY,CAC1B,IAOC,EACD,WAAgC;IAEhC,OAAO;QACL,IAAI,EAAE,eAAe;QACrB,GAAG,IAAI;QACP,GAAG,CAAC,WAAW,IAAI,EAAE,WAAW,EAAE,CAAC;KACpC,CAAC;AACJ,CAAC"}
@@ -0,0 +1,70 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { audio, embeddedResource, image, resourceLink, text, } from "./content-helpers.js";
3
+ describe("text", () => {
4
+ it("returns a TextContent without annotations when none given", () => {
5
+ expect(text("hello")).toEqual({ type: "text", text: "hello" });
6
+ });
7
+ it("includes annotations when provided", () => {
8
+ expect(text("hi", { priority: 1 })).toEqual({
9
+ type: "text",
10
+ text: "hi",
11
+ annotations: { priority: 1 },
12
+ });
13
+ });
14
+ });
15
+ describe("image", () => {
16
+ it("base64-encodes Uint8Array data", () => {
17
+ const bytes = new Uint8Array([104, 105]);
18
+ expect(image(bytes, "image/png")).toEqual({
19
+ type: "image",
20
+ data: "aGk=",
21
+ mimeType: "image/png",
22
+ });
23
+ });
24
+ it("passes string data through unchanged (caller is responsible for base64)", () => {
25
+ expect(image("YWxyZWFkeS1iNjQ=", "image/png")).toEqual({
26
+ type: "image",
27
+ data: "YWxyZWFkeS1iNjQ=",
28
+ mimeType: "image/png",
29
+ });
30
+ });
31
+ });
32
+ describe("audio", () => {
33
+ it("base64-encodes Uint8Array data", () => {
34
+ const bytes = new Uint8Array([104, 105]);
35
+ expect(audio(bytes, "audio/mpeg")).toMatchObject({
36
+ type: "audio",
37
+ data: "aGk=",
38
+ mimeType: "audio/mpeg",
39
+ });
40
+ });
41
+ });
42
+ describe("embeddedResource", () => {
43
+ it("wraps a text resource with a type tag", () => {
44
+ expect(embeddedResource({
45
+ uri: "file:///a.txt",
46
+ mimeType: "text/plain",
47
+ text: "x",
48
+ })).toEqual({
49
+ type: "resource",
50
+ resource: { uri: "file:///a.txt", mimeType: "text/plain", text: "x" },
51
+ });
52
+ });
53
+ it("wraps a blob resource with a type tag", () => {
54
+ expect(embeddedResource({ uri: "file:///a.bin", blob: "YmFy" })).toEqual({
55
+ type: "resource",
56
+ resource: { uri: "file:///a.bin", blob: "YmFy" },
57
+ });
58
+ });
59
+ });
60
+ describe("resourceLink", () => {
61
+ it("spreads link fields alongside the type tag", () => {
62
+ expect(resourceLink({ uri: "file:///a", name: "a", title: "A" })).toEqual({
63
+ type: "resource_link",
64
+ uri: "file:///a",
65
+ name: "a",
66
+ title: "A",
67
+ });
68
+ });
69
+ });
70
+ //# sourceMappingURL=content-helpers.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"content-helpers.test.js","sourceRoot":"","sources":["../../src/server/content-helpers.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EACL,KAAK,EACL,gBAAgB,EAChB,KAAK,EACL,YAAY,EACZ,IAAI,GACL,MAAM,sBAAsB,CAAC;AAE9B,QAAQ,CAAC,MAAM,EAAE,GAAG,EAAE;IACpB,EAAE,CAAC,2DAA2D,EAAE,GAAG,EAAE;QACnE,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,CAAC;IACjE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,oCAAoC,EAAE,GAAG,EAAE;QAC5C,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,EAAE,QAAQ,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC;YAC1C,IAAI,EAAE,MAAM;YACZ,IAAI,EAAE,IAAI;YACV,WAAW,EAAE,EAAE,QAAQ,EAAE,CAAC,EAAE;SAC7B,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,OAAO,EAAE,GAAG,EAAE;IACrB,EAAE,CAAC,gCAAgC,EAAE,GAAG,EAAE;QACxC,MAAM,KAAK,GAAG,IAAI,UAAU,CAAC,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,CAAC;QACzC,MAAM,CAAC,KAAK,CAAC,KAAK,EAAE,WAAW,CAAC,CAAC,CAAC,OAAO,CAAC;YACxC,IAAI,EAAE,OAAO;YACb,IAAI,EAAE,MAAM;YACZ,QAAQ,EAAE,WAAW;SACtB,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,yEAAyE,EAAE,GAAG,EAAE;QACjF,MAAM,CAAC,KAAK,CAAC,kBAAkB,EAAE,WAAW,CAAC,CAAC,CAAC,OAAO,CAAC;YACrD,IAAI,EAAE,OAAO;YACb,IAAI,EAAE,kBAAkB;YACxB,QAAQ,EAAE,WAAW;SACtB,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,OAAO,EAAE,GAAG,EAAE;IACrB,EAAE,CAAC,gCAAgC,EAAE,GAAG,EAAE;QACxC,MAAM,KAAK,GAAG,IAAI,UAAU,CAAC,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,CAAC;QACzC,MAAM,CAAC,KAAK,CAAC,KAAK,EAAE,YAAY,CAAC,CAAC,CAAC,aAAa,CAAC;YAC/C,IAAI,EAAE,OAAO;YACb,IAAI,EAAE,MAAM;YACZ,QAAQ,EAAE,YAAY;SACvB,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,kBAAkB,EAAE,GAAG,EAAE;IAChC,EAAE,CAAC,uCAAuC,EAAE,GAAG,EAAE;QAC/C,MAAM,CACJ,gBAAgB,CAAC;YACf,GAAG,EAAE,eAAe;YACpB,QAAQ,EAAE,YAAY;YACtB,IAAI,EAAE,GAAG;SACV,CAAC,CACH,CAAC,OAAO,CAAC;YACR,IAAI,EAAE,UAAU;YAChB,QAAQ,EAAE,EAAE,GAAG,EAAE,eAAe,EAAE,QAAQ,EAAE,YAAY,EAAE,IAAI,EAAE,GAAG,EAAE;SACtE,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,uCAAuC,EAAE,GAAG,EAAE;QAC/C,MAAM,CAAC,gBAAgB,CAAC,EAAE,GAAG,EAAE,eAAe,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC;YACvE,IAAI,EAAE,UAAU;YAChB,QAAQ,EAAE,EAAE,GAAG,EAAE,eAAe,EAAE,IAAI,EAAE,MAAM,EAAE;SACjD,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,cAAc,EAAE,GAAG,EAAE;IAC5B,EAAE,CAAC,4CAA4C,EAAE,GAAG,EAAE;QACpD,MAAM,CAAC,YAAY,CAAC,EAAE,GAAG,EAAE,WAAW,EAAE,IAAI,EAAE,GAAG,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC;YACxE,IAAI,EAAE,eAAe;YACrB,GAAG,EAAE,WAAW;YAChB,IAAI,EAAE,GAAG;YACT,KAAK,EAAE,GAAG;SACX,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -0,0 +1,15 @@
1
+ import type http from "node:http";
2
+ import express from "express";
3
+ import type { McpServer } from "./server.js";
4
+ export declare function createApp({ mcpServer, httpServer, customMiddleware, errorMiddleware, }: {
5
+ mcpServer: McpServer;
6
+ httpServer: http.Server;
7
+ customMiddleware?: {
8
+ path?: string;
9
+ handlers: express.RequestHandler[];
10
+ }[];
11
+ errorMiddleware?: {
12
+ path?: string;
13
+ handlers: express.ErrorRequestHandler[];
14
+ }[];
15
+ }): Promise<express.Express>;
@@ -0,0 +1,77 @@
1
+ import path from "node:path";
2
+ import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
3
+ import cors from "cors";
4
+ import express from "express";
5
+ function applyMiddlewares(app, middlewares) {
6
+ for (const middleware of middlewares) {
7
+ if (middleware.path) {
8
+ app.use(middleware.path, ...middleware.handlers);
9
+ }
10
+ else {
11
+ app.use(...middleware.handlers);
12
+ }
13
+ }
14
+ }
15
+ function defaultErrorHandler(err, _req, res, _next) {
16
+ console.error("Error handling MCP request:", err);
17
+ if (!res.headersSent) {
18
+ res.status(500).json({
19
+ jsonrpc: "2.0",
20
+ error: { code: -32603, message: "Internal server error" },
21
+ id: null,
22
+ });
23
+ }
24
+ }
25
+ export async function createApp({ mcpServer, httpServer, customMiddleware = [], errorMiddleware = [], }) {
26
+ const app = express();
27
+ app.use(express.json());
28
+ const env = process.env.NODE_ENV || "development";
29
+ applyMiddlewares(app, customMiddleware);
30
+ if (env !== "production") {
31
+ const { devtoolsStaticServer } = await import("@skybridge/devtools");
32
+ app.use(await devtoolsStaticServer());
33
+ const { viewsDevServer } = await import("./viewsDevServer.js");
34
+ app.use(await viewsDevServer(httpServer));
35
+ }
36
+ if (env === "production") {
37
+ const assetsPath = path.join(process.cwd(), "dist", "assets");
38
+ app.use("/assets", cors());
39
+ app.use("/assets", express.static(assetsPath));
40
+ }
41
+ app.use("/mcp", mcpMiddleware(mcpServer));
42
+ applyMiddlewares(app, errorMiddleware);
43
+ app.use("/mcp", defaultErrorHandler);
44
+ return app;
45
+ }
46
+ const mcpMiddleware = (server) => {
47
+ return async (req, res, next) => {
48
+ if (req.method !== "POST") {
49
+ res.writeHead(405).end(JSON.stringify({
50
+ jsonrpc: "2.0",
51
+ error: {
52
+ code: -32000,
53
+ message: "Method not allowed.",
54
+ },
55
+ id: null,
56
+ }));
57
+ return;
58
+ }
59
+ try {
60
+ const transport = new StreamableHTTPServerTransport({
61
+ sessionIdGenerator: undefined,
62
+ });
63
+ res.on("close", () => {
64
+ transport.close();
65
+ });
66
+ await server.connectStatelessTransport(transport);
67
+ // Express strips the mount path from req.url (e.g. "/mcp" becomes "/").
68
+ // Restore it so the SDK builds the correct requestInfo.url.
69
+ req.url = req.originalUrl;
70
+ await transport.handleRequest(req, res, req.body);
71
+ }
72
+ catch (error) {
73
+ next(error);
74
+ }
75
+ };
76
+ };
77
+ //# sourceMappingURL=express.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"express.js","sourceRoot":"","sources":["../../src/server/express.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,289 @@
1
+ import http from "node:http";
2
+ import { afterEach, describe, expect, it, vi } from "vitest";
3
+ import { McpServer } from "./server.js";
4
+ vi.mock("@skybridge/devtools", () => ({
5
+ devtoolsStaticServer: () => ((_req, _res, next) => next()),
6
+ }));
7
+ vi.mock("./viewsDevServer.js", () => ({
8
+ viewsDevServer: (_httpServer) => ((_req, _res, next) => next()),
9
+ }));
10
+ const fakeServer = {};
11
+ async function listen(app) {
12
+ const server = http.createServer(app);
13
+ await new Promise((resolve) => server.listen(0, resolve));
14
+ const port = server.address().port;
15
+ return { port, server };
16
+ }
17
+ let openServer;
18
+ afterEach(() => openServer?.close());
19
+ async function postMcp(port) {
20
+ return fetch(`http://localhost:${port}/mcp`, {
21
+ method: "POST",
22
+ headers: { "Content-Type": "application/json" },
23
+ body: JSON.stringify({ jsonrpc: "2.0", method: "initialize", id: 1 }),
24
+ });
25
+ }
26
+ async function postApi(port) {
27
+ return fetch(`http://localhost:${port}/api/test`, { method: "POST" });
28
+ }
29
+ describe("createApp", () => {
30
+ it("runs global custom middleware before the /mcp handler", async () => {
31
+ const { createApp } = await import("./express.js");
32
+ const calls = [];
33
+ const mw = (_req, _res, next) => {
34
+ calls.push("custom");
35
+ next();
36
+ };
37
+ const httpServer = http.createServer();
38
+ const app = await createApp({
39
+ mcpServer: fakeServer,
40
+ httpServer,
41
+ customMiddleware: [{ handlers: [mw] }],
42
+ });
43
+ const { port, server } = await listen(app);
44
+ openServer = server;
45
+ await postMcp(port);
46
+ expect(calls).toEqual(["custom"]);
47
+ });
48
+ it("runs path-scoped middleware on /mcp", async () => {
49
+ const { createApp } = await import("./express.js");
50
+ const calls = [];
51
+ const mw = (_req, _res, next) => {
52
+ calls.push("auth");
53
+ next();
54
+ };
55
+ const httpServer = http.createServer();
56
+ const app = await createApp({
57
+ mcpServer: fakeServer,
58
+ httpServer,
59
+ customMiddleware: [{ path: "/mcp", handlers: [mw] }],
60
+ });
61
+ const { port, server } = await listen(app);
62
+ openServer = server;
63
+ await postMcp(port);
64
+ expect(calls).toEqual(["auth"]);
65
+ });
66
+ it("allows middleware to short-circuit with 401", async () => {
67
+ const { createApp } = await import("./express.js");
68
+ const calls = [];
69
+ const reject = (_req, res) => {
70
+ calls.push("reject");
71
+ res.status(401).json({ error: "Unauthorized" });
72
+ };
73
+ const httpServer = http.createServer();
74
+ const app = await createApp({
75
+ mcpServer: fakeServer,
76
+ httpServer,
77
+ customMiddleware: [{ path: "/mcp", handlers: [reject] }],
78
+ });
79
+ const { port, server } = await listen(app);
80
+ openServer = server;
81
+ const res = await postMcp(port);
82
+ expect(calls).toEqual(["reject"]);
83
+ expect(res.status).toBe(401);
84
+ expect(await res.json()).toEqual({ error: "Unauthorized" });
85
+ });
86
+ it("runs multiple global middleware in registration order", async () => {
87
+ const { createApp } = await import("./express.js");
88
+ const calls = [];
89
+ const mwA = (_req, _res, next) => {
90
+ calls.push("A");
91
+ next();
92
+ };
93
+ const mwB = (_req, _res, next) => {
94
+ calls.push("B");
95
+ next();
96
+ };
97
+ const httpServer = http.createServer();
98
+ const app = await createApp({
99
+ mcpServer: fakeServer,
100
+ httpServer,
101
+ customMiddleware: [{ handlers: [mwA] }, { handlers: [mwB] }],
102
+ });
103
+ const { port, server } = await listen(app);
104
+ openServer = server;
105
+ await postMcp(port);
106
+ expect(calls).toEqual(["A", "B"]);
107
+ });
108
+ it("path-scoped middleware does not run on non-matching paths", async () => {
109
+ const { createApp } = await import("./express.js");
110
+ const calls = [];
111
+ const apiMw = (_req, _res, next) => {
112
+ calls.push("api");
113
+ next();
114
+ };
115
+ const httpServer = http.createServer();
116
+ const app = await createApp({
117
+ mcpServer: fakeServer,
118
+ httpServer,
119
+ customMiddleware: [{ path: "/api", handlers: [apiMw] }],
120
+ });
121
+ const { port, server } = await listen(app);
122
+ openServer = server;
123
+ // Hit /mcp — the /api middleware should NOT fire
124
+ await postMcp(port);
125
+ expect(calls).toEqual([]);
126
+ });
127
+ it("supports Express Router via custom middleware", async () => {
128
+ const { createApp } = await import("./express.js");
129
+ const { Router } = await import("express");
130
+ const router = Router();
131
+ router.get("/health", (_req, res) => {
132
+ res.json({ status: "ok" });
133
+ });
134
+ const httpServer = http.createServer();
135
+ const app = await createApp({
136
+ mcpServer: fakeServer,
137
+ httpServer,
138
+ customMiddleware: [{ handlers: [router] }],
139
+ });
140
+ const { port, server } = await listen(app);
141
+ openServer = server;
142
+ const res = await fetch(`http://localhost:${port}/health`);
143
+ expect(res.status).toBe(200);
144
+ expect(await res.json()).toEqual({ status: "ok" });
145
+ });
146
+ it("supports path-prefixed Router", async () => {
147
+ const { createApp } = await import("./express.js");
148
+ const { Router } = await import("express");
149
+ const router = Router();
150
+ router.get("/data", (_req, res) => {
151
+ res.json({ value: 42 });
152
+ });
153
+ const httpServer = http.createServer();
154
+ const app = await createApp({
155
+ mcpServer: fakeServer,
156
+ httpServer,
157
+ customMiddleware: [
158
+ { path: "/api", handlers: [router] },
159
+ ],
160
+ });
161
+ const { port, server } = await listen(app);
162
+ openServer = server;
163
+ const res = await fetch(`http://localhost:${port}/api/data`);
164
+ expect(res.status).toBe(200);
165
+ expect(await res.json()).toEqual({ value: 42 });
166
+ });
167
+ it("server survives middleware errors without crashing", async () => {
168
+ const { createApp } = await import("./express.js");
169
+ const throwing = () => {
170
+ throw new Error("boom");
171
+ };
172
+ const httpServer = http.createServer();
173
+ const app = await createApp({
174
+ mcpServer: fakeServer,
175
+ httpServer,
176
+ customMiddleware: [{ path: "/explode", handlers: [throwing] }],
177
+ });
178
+ const { port, server } = await listen(app);
179
+ openServer = server;
180
+ const res = await fetch(`http://localhost:${port}/explode`);
181
+ expect(res.status).toBe(500);
182
+ // Server process did not crash — it still accepts connections
183
+ const followUp = await fetch(`http://localhost:${port}/explode`);
184
+ expect(followUp.status).toBe(500);
185
+ });
186
+ it("returns 500 JSON-RPC error when the MCP handler throws and no error middleware is registered", async () => {
187
+ const { createApp } = await import("./express.js");
188
+ const consoleSpy = vi.spyOn(console, "error").mockImplementation(() => { });
189
+ const httpServer = http.createServer();
190
+ const app = await createApp({ mcpServer: fakeServer, httpServer });
191
+ const { port, server } = await listen(app);
192
+ openServer = server;
193
+ const res = await postMcp(port);
194
+ expect(res.status).toBe(500);
195
+ expect(await res.json()).toEqual({
196
+ jsonrpc: "2.0",
197
+ error: { code: -32603, message: "Internal server error" },
198
+ id: null,
199
+ });
200
+ expect(consoleSpy).toHaveBeenCalledWith("Error handling MCP request:", expect.any(Error));
201
+ consoleSpy.mockRestore();
202
+ });
203
+ it("invokes a custom error handler when the MCP handler throws", async () => {
204
+ const { createApp } = await import("./express.js");
205
+ const calls = [];
206
+ const errorHandler = (_err, _req, res, _next) => {
207
+ calls.push("error-handler");
208
+ res.status(503).json({ custom: true });
209
+ };
210
+ const httpServer = http.createServer();
211
+ const app = await createApp({
212
+ mcpServer: fakeServer,
213
+ httpServer,
214
+ errorMiddleware: [{ handlers: [errorHandler] }],
215
+ });
216
+ const { port, server } = await listen(app);
217
+ openServer = server;
218
+ const res = await postMcp(port);
219
+ expect(calls).toEqual(["error-handler"]);
220
+ expect(res.status).toBe(503);
221
+ expect(await res.json()).toEqual({ custom: true });
222
+ });
223
+ it("invokes a path-scoped error handler only for matching routes", async () => {
224
+ const { createApp } = await import("./express.js");
225
+ const calls = [];
226
+ const mcpErrorHandler = (_err, _req, res, _next) => {
227
+ calls.push("mcp-error-handler");
228
+ res.status(503).json({ from: "mcp-error-handler" });
229
+ };
230
+ const throwingApiRoute = (_req, _res, next) => {
231
+ next(new Error("api error"));
232
+ };
233
+ const httpServer = http.createServer();
234
+ const app = await createApp({
235
+ mcpServer: fakeServer,
236
+ httpServer,
237
+ customMiddleware: [{ path: "/api/test", handlers: [throwingApiRoute] }],
238
+ errorMiddleware: [{ path: "/mcp", handlers: [mcpErrorHandler] }],
239
+ });
240
+ const { port, server } = await listen(app);
241
+ openServer = server;
242
+ const mcpRes = await postMcp(port);
243
+ expect(calls).toEqual(["mcp-error-handler"]);
244
+ expect(mcpRes.status).toBe(503);
245
+ expect(await mcpRes.json()).toEqual({ from: "mcp-error-handler" });
246
+ const consoleSpy = vi.spyOn(console, "error").mockImplementation(() => { });
247
+ const apiRes = await postApi(port);
248
+ expect(calls).toEqual(["mcp-error-handler"]);
249
+ expect(apiRes.status).toBe(500);
250
+ consoleSpy.mockRestore();
251
+ });
252
+ it("handles concurrent /mcp requests without 'Already connected to a transport'", async () => {
253
+ const { createApp } = await import("./express.js");
254
+ const mcpServer = new McpServer({
255
+ name: "concurrent-test",
256
+ version: "0.0.0",
257
+ });
258
+ // Slow tool: keeps the underlying transport bound long enough to overlap
259
+ // with concurrent requests, exposing the shared-McpServer race.
260
+ mcpServer.registerTool({ name: "slow", description: "slow" }, async () => {
261
+ await new Promise((r) => setTimeout(r, 50));
262
+ return { content: [{ type: "text", text: "done" }] };
263
+ });
264
+ const consoleSpy = vi.spyOn(console, "error").mockImplementation(() => { });
265
+ const httpServer = http.createServer();
266
+ const app = await createApp({ mcpServer, httpServer });
267
+ const { port, server } = await listen(app);
268
+ openServer = server;
269
+ const callBody = (id) => JSON.stringify({
270
+ jsonrpc: "2.0",
271
+ method: "tools/call",
272
+ id,
273
+ params: { name: "slow", arguments: {} },
274
+ });
275
+ const N = 10;
276
+ const responses = await Promise.all(Array.from({ length: N }, (_, i) => fetch(`http://localhost:${port}/mcp`, {
277
+ method: "POST",
278
+ headers: {
279
+ "Content-Type": "application/json",
280
+ Accept: "application/json, text/event-stream",
281
+ },
282
+ body: callBody(i + 1),
283
+ })));
284
+ expect(responses.map((r) => r.status)).toEqual(Array(N).fill(200));
285
+ expect(consoleSpy).not.toHaveBeenCalledWith("Error handling MCP request:", expect.any(Error));
286
+ consoleSpy.mockRestore();
287
+ });
288
+ });
289
+ //# sourceMappingURL=express.test.js.map