skybridge 0.0.0-dev.ad93096 → 0.0.0-dev.ad9702c

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 (236) hide show
  1. package/README.md +149 -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/use-execute-steps.d.ts +11 -0
  15. package/dist/cli/use-execute-steps.js +36 -0
  16. package/dist/cli/use-execute-steps.js.map +1 -0
  17. package/dist/cli/use-nodemon.d.ts +7 -0
  18. package/dist/cli/use-nodemon.js +76 -0
  19. package/dist/cli/use-nodemon.js.map +1 -0
  20. package/dist/cli/use-typescript-check.d.ts +8 -0
  21. package/dist/cli/use-typescript-check.js +59 -0
  22. package/dist/cli/use-typescript-check.js.map +1 -0
  23. package/dist/commands/build.d.ts +9 -0
  24. package/dist/commands/build.js +46 -0
  25. package/dist/commands/build.js.map +1 -0
  26. package/dist/commands/dev.d.ts +3 -1
  27. package/dist/commands/dev.js +25 -24
  28. package/dist/commands/dev.js.map +1 -1
  29. package/dist/commands/start.d.ts +9 -0
  30. package/dist/commands/start.js +52 -0
  31. package/dist/commands/start.js.map +1 -0
  32. package/dist/commands/telemetry/disable.d.ts +5 -0
  33. package/dist/commands/telemetry/disable.js +14 -0
  34. package/dist/commands/telemetry/disable.js.map +1 -0
  35. package/dist/commands/telemetry/enable.d.ts +5 -0
  36. package/dist/commands/telemetry/enable.js +14 -0
  37. package/dist/commands/telemetry/enable.js.map +1 -0
  38. package/dist/commands/telemetry/status.d.ts +5 -0
  39. package/dist/commands/telemetry/status.js +14 -0
  40. package/dist/commands/telemetry/status.js.map +1 -0
  41. package/dist/server/asset-base-url-transform-plugin.d.ts +11 -0
  42. package/dist/server/asset-base-url-transform-plugin.js +34 -0
  43. package/dist/server/asset-base-url-transform-plugin.js.map +1 -0
  44. package/dist/server/asset-base-url-transform-plugin.test.js +56 -0
  45. package/dist/server/asset-base-url-transform-plugin.test.js.map +1 -0
  46. package/dist/server/express.d.ts +11 -0
  47. package/dist/server/express.js +72 -0
  48. package/dist/server/express.js.map +1 -0
  49. package/dist/server/express.test.d.ts +1 -0
  50. package/dist/server/express.test.js +80 -0
  51. package/dist/server/express.test.js.map +1 -0
  52. package/dist/server/index.d.ts +1 -0
  53. package/dist/server/index.js.map +1 -1
  54. package/dist/server/middleware.d.ts +124 -0
  55. package/dist/server/middleware.js +93 -0
  56. package/dist/server/middleware.js.map +1 -0
  57. package/dist/server/middleware.test-d.d.ts +1 -0
  58. package/dist/server/middleware.test-d.js +75 -0
  59. package/dist/server/middleware.test-d.js.map +1 -0
  60. package/dist/server/middleware.test.d.ts +1 -0
  61. package/dist/server/middleware.test.js +383 -0
  62. package/dist/server/middleware.test.js.map +1 -0
  63. package/dist/server/server.d.ts +51 -5
  64. package/dist/server/server.js +215 -60
  65. package/dist/server/server.js.map +1 -1
  66. package/dist/server/templates/development.hbs +1 -55
  67. package/dist/server/templates/production.hbs +1 -2
  68. package/dist/server/widgetsDevServer.d.ts +2 -1
  69. package/dist/server/widgetsDevServer.js +12 -2
  70. package/dist/server/widgetsDevServer.js.map +1 -1
  71. package/dist/test/widget.test.js +51 -25
  72. package/dist/test/widget.test.js.map +1 -1
  73. package/dist/web/bridges/apps-sdk/adaptor.d.ts +22 -0
  74. package/dist/web/bridges/apps-sdk/adaptor.js +75 -0
  75. package/dist/web/bridges/apps-sdk/adaptor.js.map +1 -0
  76. package/dist/web/bridges/apps-sdk/bridge.d.ts +10 -0
  77. package/dist/web/bridges/{apps-sdk-bridge.js → apps-sdk/bridge.js} +2 -2
  78. package/dist/web/bridges/apps-sdk/bridge.js.map +1 -0
  79. package/dist/web/bridges/apps-sdk/index.d.ts +5 -0
  80. package/dist/web/bridges/apps-sdk/index.js +5 -0
  81. package/dist/web/bridges/apps-sdk/index.js.map +1 -0
  82. package/dist/web/bridges/apps-sdk/types.d.ts +121 -0
  83. package/dist/web/bridges/apps-sdk/types.js +10 -0
  84. package/dist/web/bridges/apps-sdk/types.js.map +1 -0
  85. package/dist/web/bridges/apps-sdk/use-apps-sdk-context.d.ts +2 -0
  86. package/dist/web/bridges/{hooks/use-apps-sdk-bridge.js → apps-sdk/use-apps-sdk-context.js} +3 -3
  87. package/dist/web/bridges/apps-sdk/use-apps-sdk-context.js.map +1 -0
  88. package/dist/web/bridges/get-adaptor.d.ts +2 -0
  89. package/dist/web/bridges/get-adaptor.js +8 -0
  90. package/dist/web/bridges/get-adaptor.js.map +1 -0
  91. package/dist/web/bridges/index.d.ts +5 -4
  92. package/dist/web/bridges/index.js +5 -4
  93. package/dist/web/bridges/index.js.map +1 -1
  94. package/dist/web/bridges/mcp-app/adaptor.d.ts +38 -0
  95. package/dist/web/bridges/mcp-app/adaptor.js +184 -0
  96. package/dist/web/bridges/mcp-app/adaptor.js.map +1 -0
  97. package/dist/web/bridges/mcp-app/bridge.d.ts +26 -0
  98. package/dist/web/bridges/mcp-app/bridge.js +102 -0
  99. package/dist/web/bridges/mcp-app/bridge.js.map +1 -0
  100. package/dist/web/bridges/mcp-app/index.d.ts +4 -0
  101. package/dist/web/bridges/mcp-app/index.js +4 -0
  102. package/dist/web/bridges/mcp-app/index.js.map +1 -0
  103. package/dist/web/bridges/mcp-app/types.d.ts +8 -0
  104. package/dist/web/bridges/mcp-app/types.js +2 -0
  105. package/dist/web/bridges/mcp-app/types.js.map +1 -0
  106. package/dist/web/bridges/mcp-app/use-mcp-app-context.d.ts +7 -0
  107. package/dist/web/bridges/mcp-app/use-mcp-app-context.js +7 -0
  108. package/dist/web/bridges/mcp-app/use-mcp-app-context.js.map +1 -0
  109. package/dist/web/bridges/mcp-app/use-mcp-app-context.test.d.ts +1 -0
  110. package/dist/web/bridges/mcp-app/use-mcp-app-context.test.js +26 -0
  111. package/dist/web/bridges/mcp-app/use-mcp-app-context.test.js.map +1 -0
  112. package/dist/web/bridges/types.d.ts +72 -27
  113. package/dist/web/bridges/use-host-context.d.ts +2 -0
  114. package/dist/web/bridges/use-host-context.js +8 -0
  115. package/dist/web/bridges/use-host-context.js.map +1 -0
  116. package/dist/web/components/modal-provider.d.ts +4 -0
  117. package/dist/web/components/modal-provider.js +47 -0
  118. package/dist/web/components/modal-provider.js.map +1 -0
  119. package/dist/web/create-store.js +6 -7
  120. package/dist/web/create-store.js.map +1 -1
  121. package/dist/web/create-store.test.js +113 -52
  122. package/dist/web/create-store.test.js.map +1 -1
  123. package/dist/web/data-llm.js +5 -3
  124. package/dist/web/data-llm.js.map +1 -1
  125. package/dist/web/data-llm.test.js +131 -65
  126. package/dist/web/data-llm.test.js.map +1 -1
  127. package/dist/web/generate-helpers.d.ts +3 -2
  128. package/dist/web/generate-helpers.js +1 -1
  129. package/dist/web/generate-helpers.js.map +1 -1
  130. package/dist/web/helpers/state.js +13 -8
  131. package/dist/web/helpers/state.js.map +1 -1
  132. package/dist/web/hooks/index.d.ts +2 -2
  133. package/dist/web/hooks/index.js +1 -1
  134. package/dist/web/hooks/index.js.map +1 -1
  135. package/dist/web/hooks/test/utils.d.ts +8 -2
  136. package/dist/web/hooks/test/utils.js +37 -13
  137. package/dist/web/hooks/test/utils.js.map +1 -1
  138. package/dist/web/hooks/use-call-tool.d.ts +3 -2
  139. package/dist/web/hooks/use-call-tool.js +2 -2
  140. package/dist/web/hooks/use-call-tool.js.map +1 -1
  141. package/dist/web/hooks/use-call-tool.test-d.js +1 -1
  142. package/dist/web/hooks/use-call-tool.test-d.js.map +1 -1
  143. package/dist/web/hooks/use-call-tool.test.js +3 -3
  144. package/dist/web/hooks/use-call-tool.test.js.map +1 -1
  145. package/dist/web/hooks/use-display-mode.d.ts +3 -3
  146. package/dist/web/hooks/use-display-mode.js +3 -4
  147. package/dist/web/hooks/use-display-mode.js.map +1 -1
  148. package/dist/web/hooks/use-display-mode.test-d.d.ts +1 -0
  149. package/dist/web/hooks/use-display-mode.test-d.js +8 -0
  150. package/dist/web/hooks/use-display-mode.test-d.js.map +1 -0
  151. package/dist/web/hooks/use-files.d.ts +2 -6
  152. package/dist/web/hooks/use-files.js +4 -2
  153. package/dist/web/hooks/use-files.js.map +1 -1
  154. package/dist/web/hooks/use-files.test.js +10 -5
  155. package/dist/web/hooks/use-files.test.js.map +1 -1
  156. package/dist/web/hooks/use-layout.d.ts +2 -2
  157. package/dist/web/hooks/use-layout.js +4 -4
  158. package/dist/web/hooks/use-layout.js.map +1 -1
  159. package/dist/web/hooks/use-layout.test.js +7 -6
  160. package/dist/web/hooks/use-layout.test.js.map +1 -1
  161. package/dist/web/hooks/use-open-external.d.ts +3 -1
  162. package/dist/web/hooks/use-open-external.js +3 -3
  163. package/dist/web/hooks/use-open-external.js.map +1 -1
  164. package/dist/web/hooks/use-open-external.test.js +27 -12
  165. package/dist/web/hooks/use-open-external.test.js.map +1 -1
  166. package/dist/web/hooks/use-request-modal.d.ts +2 -2
  167. package/dist/web/hooks/use-request-modal.js +9 -7
  168. package/dist/web/hooks/use-request-modal.js.map +1 -1
  169. package/dist/web/hooks/use-send-follow-up-message.js +2 -2
  170. package/dist/web/hooks/use-set-open-in-app-url.d.ts +1 -0
  171. package/dist/web/hooks/use-set-open-in-app-url.js +8 -0
  172. package/dist/web/hooks/use-set-open-in-app-url.js.map +1 -0
  173. package/dist/web/hooks/use-set-open-in-app-url.test.d.ts +1 -0
  174. package/dist/web/hooks/use-set-open-in-app-url.test.js +43 -0
  175. package/dist/web/hooks/use-set-open-in-app-url.test.js.map +1 -0
  176. package/dist/web/hooks/use-tool-info.js +4 -4
  177. package/dist/web/hooks/use-tool-info.js.map +1 -1
  178. package/dist/web/hooks/use-tool-info.test.js +5 -5
  179. package/dist/web/hooks/use-tool-info.test.js.map +1 -1
  180. package/dist/web/hooks/use-user.d.ts +1 -1
  181. package/dist/web/hooks/use-user.js +3 -3
  182. package/dist/web/hooks/use-user.js.map +1 -1
  183. package/dist/web/hooks/use-user.test.js +5 -4
  184. package/dist/web/hooks/use-user.test.js.map +1 -1
  185. package/dist/web/hooks/use-widget-state.js +10 -10
  186. package/dist/web/hooks/use-widget-state.js.map +1 -1
  187. package/dist/web/hooks/use-widget-state.test.js +9 -6
  188. package/dist/web/hooks/use-widget-state.test.js.map +1 -1
  189. package/dist/web/mount-widget.js +9 -1
  190. package/dist/web/mount-widget.js.map +1 -1
  191. package/dist/web/plugin/plugin.js +24 -9
  192. package/dist/web/plugin/plugin.js.map +1 -1
  193. package/dist/web/plugin/validate-widget.d.ts +5 -0
  194. package/dist/web/plugin/validate-widget.js +27 -0
  195. package/dist/web/plugin/validate-widget.js.map +1 -0
  196. package/dist/web/plugin/validate-widget.test.d.ts +1 -0
  197. package/dist/web/plugin/validate-widget.test.js +42 -0
  198. package/dist/web/plugin/validate-widget.test.js.map +1 -0
  199. package/dist/web/proxy.js +0 -1
  200. package/dist/web/proxy.js.map +1 -1
  201. package/dist/web/types.d.ts +0 -133
  202. package/dist/web/types.js +0 -9
  203. package/dist/web/types.js.map +1 -1
  204. package/package.json +35 -23
  205. package/tsconfig.base.json +28 -0
  206. package/dist/cli/use-version.d.ts +0 -1
  207. package/dist/cli/use-version.js +0 -34
  208. package/dist/cli/use-version.js.map +0 -1
  209. package/dist/web/bridges/adaptors/apps-sdk-adaptor.d.ts +0 -13
  210. package/dist/web/bridges/adaptors/apps-sdk-adaptor.js +0 -33
  211. package/dist/web/bridges/adaptors/apps-sdk-adaptor.js.map +0 -1
  212. package/dist/web/bridges/adaptors/mcp-app-adaptor.d.ts +0 -16
  213. package/dist/web/bridges/adaptors/mcp-app-adaptor.js +0 -115
  214. package/dist/web/bridges/adaptors/mcp-app-adaptor.js.map +0 -1
  215. package/dist/web/bridges/apps-sdk-bridge.d.ts +0 -10
  216. package/dist/web/bridges/apps-sdk-bridge.js.map +0 -1
  217. package/dist/web/bridges/hooks/use-adaptor.d.ts +0 -2
  218. package/dist/web/bridges/hooks/use-adaptor.js +0 -8
  219. package/dist/web/bridges/hooks/use-adaptor.js.map +0 -1
  220. package/dist/web/bridges/hooks/use-apps-sdk-bridge.d.ts +0 -2
  221. package/dist/web/bridges/hooks/use-apps-sdk-bridge.js.map +0 -1
  222. package/dist/web/bridges/hooks/use-bridge.d.ts +0 -2
  223. package/dist/web/bridges/hooks/use-bridge.js +0 -8
  224. package/dist/web/bridges/hooks/use-bridge.js.map +0 -1
  225. package/dist/web/bridges/hooks/use-mcp-app-bridge.d.ts +0 -5
  226. package/dist/web/bridges/hooks/use-mcp-app-bridge.js +0 -7
  227. package/dist/web/bridges/hooks/use-mcp-app-bridge.js.map +0 -1
  228. package/dist/web/bridges/hooks/use-mcp-app-bridge.test.js +0 -41
  229. package/dist/web/bridges/hooks/use-mcp-app-bridge.test.js.map +0 -1
  230. package/dist/web/bridges/mcp-app-bridge.d.ts +0 -38
  231. package/dist/web/bridges/mcp-app-bridge.js +0 -162
  232. package/dist/web/bridges/mcp-app-bridge.js.map +0 -1
  233. package/dist/web/hooks/use-openai-global.d.ts +0 -3
  234. package/dist/web/hooks/use-openai-global.js +0 -6
  235. package/dist/web/hooks/use-openai-global.js.map +0 -1
  236. /package/dist/{web/bridges/hooks/use-mcp-app-bridge.test.d.ts → server/asset-base-url-transform-plugin.test.d.ts} +0 -0
@@ -1,71 +1,180 @@
1
+ import crypto from "node:crypto";
1
2
  import { readFileSync } from "node:fs";
3
+ import http from "node:http";
2
4
  import path from "node:path";
3
5
  import { McpServer as McpServerBase, } from "@modelcontextprotocol/sdk/server/mcp.js";
4
- import { toMerged } from "es-toolkit";
6
+ import { mergeWith, union } from "es-toolkit";
7
+ import { createApp } from "./express.js";
8
+ import { buildMiddlewareChain, getHandlerMaps } from "./middleware.js";
5
9
  import { templateHelper } from "./templateHelper.js";
10
+ const mergeWithUnion = (target, source) => {
11
+ return mergeWith(target, source, (targetVal, sourceVal) => {
12
+ if (Array.isArray(targetVal) && Array.isArray(sourceVal)) {
13
+ return union(targetVal, sourceVal);
14
+ }
15
+ });
16
+ };
6
17
  export class McpServer extends McpServerBase {
18
+ express;
19
+ customMiddleware = [];
20
+ mcpMiddlewareEntries = [];
21
+ mcpMiddlewareApplied = false;
22
+ use(pathOrHandler, ...handlers) {
23
+ if (typeof pathOrHandler === "string") {
24
+ this.customMiddleware.push({
25
+ path: pathOrHandler,
26
+ handlers,
27
+ });
28
+ }
29
+ else {
30
+ this.customMiddleware.push({
31
+ handlers: [pathOrHandler, ...handlers],
32
+ });
33
+ }
34
+ return this;
35
+ }
36
+ mcpMiddleware(filterOrHandler,
37
+ // biome-ignore lint/suspicious/noExplicitAny: overloads narrow the handler type at call sites; implementation must accept all variants
38
+ maybeHandler) {
39
+ if (this.mcpMiddlewareApplied) {
40
+ throw new Error("Cannot register MCP middleware after run() or connect() has been called");
41
+ }
42
+ const handler = maybeHandler;
43
+ if (typeof filterOrHandler === "function") {
44
+ this.mcpMiddlewareEntries.push({
45
+ filter: null,
46
+ handler: filterOrHandler,
47
+ });
48
+ }
49
+ else if (handler) {
50
+ this.mcpMiddlewareEntries.push({
51
+ filter: filterOrHandler,
52
+ handler,
53
+ });
54
+ }
55
+ else {
56
+ throw new Error("mcpMiddleware requires a handler function when a filter is provided");
57
+ }
58
+ return this;
59
+ }
60
+ applyMcpMiddleware() {
61
+ if (this.mcpMiddlewareApplied) {
62
+ return;
63
+ }
64
+ this.mcpMiddlewareApplied = true;
65
+ if (this.mcpMiddlewareEntries.length === 0) {
66
+ return;
67
+ }
68
+ const { requestHandlers, notificationHandlers } = getHandlerMaps(this.server);
69
+ const entries = this.mcpMiddlewareEntries;
70
+ // Wrap existing handlers and proxy future .set() for lazy SDK registration
71
+ const instrumentMap = (map, isNotification) => {
72
+ for (const [method, handler] of map) {
73
+ map.set(method, buildMiddlewareChain(method, isNotification, handler, entries));
74
+ }
75
+ const originalSet = map.set.bind(map);
76
+ map.set = (method, handler) => originalSet(method, buildMiddlewareChain(method, isNotification, handler, entries));
77
+ };
78
+ instrumentMap(requestHandlers, false);
79
+ instrumentMap(notificationHandlers, true);
80
+ }
81
+ async connect(transport) {
82
+ this.applyMcpMiddleware();
83
+ return super.connect(transport);
84
+ }
85
+ async run() {
86
+ this.applyMcpMiddleware();
87
+ const httpServer = http.createServer();
88
+ if (!this.express) {
89
+ this.express = await createApp({
90
+ mcpServer: this,
91
+ httpServer,
92
+ customMiddleware: this.customMiddleware,
93
+ });
94
+ }
95
+ httpServer.on("request", this.express);
96
+ return new Promise((resolve, reject) => {
97
+ httpServer.on("error", (error) => {
98
+ console.error("Failed to start server:", error);
99
+ reject(error);
100
+ });
101
+ const port = parseInt(process.env.__PORT ?? "3000", 10);
102
+ httpServer.listen(port, () => {
103
+ resolve();
104
+ });
105
+ });
106
+ }
7
107
  registerWidget(name, resourceConfig, toolConfig, toolCallback) {
8
108
  const userMeta = resourceConfig._meta;
9
- const appsSdkResourceConfig = {
10
- hostType: "apps-sdk",
11
- uri: `ui://widgets/apps-sdk/${name}.html`,
12
- mimeType: "text/html+skybridge",
13
- buildContentMeta: ({ serverUrl, wsServerUrl }) => {
14
- const userUi = userMeta?.ui;
15
- const defaults = {
16
- "openai/widgetCSP": {
17
- resource_domains: [serverUrl],
18
- connect_domains: [serverUrl, wsServerUrl],
19
- },
20
- "openai/widgetDomain": serverUrl,
21
- "openai/widgetDescription": toolConfig.description,
22
- };
23
- const userCsp = userUi?.csp;
24
- const fromUi = {
25
- "openai/widgetCSP": {
26
- resource_domains: userCsp?.resourceDomains,
27
- connect_domains: userCsp?.connectDomains,
28
- frame_domains: userCsp?.frameDomains,
29
- redirect_domains: userCsp?.redirectDomains,
30
- },
31
- "openai/widgetDomain": userUi?.domain,
32
- "openai/widgetPrefersBorder": userUi?.prefersBorder,
33
- };
34
- const directOpenaiKeys = Object.fromEntries(Object.entries(userMeta ?? {}).filter(([key]) => key.startsWith("openai/")));
35
- return toMerged(toMerged(defaults, fromUi), directOpenaiKeys);
36
- },
109
+ const toolMeta = {
110
+ ...toolConfig._meta,
37
111
  };
38
- const extAppsResourceConfig = {
39
- hostType: "mcp-app",
40
- uri: `ui://widgets/ext-apps/${name}.html`,
41
- mimeType: "text/html;profile=mcp-app",
42
- buildContentMeta: ({ serverUrl, wsServerUrl }) => {
43
- const defaults = {
44
- ui: {
45
- csp: {
46
- resourceDomains: [serverUrl],
47
- connectDomains: [serverUrl, wsServerUrl],
112
+ if (!resourceConfig.hosts || resourceConfig.hosts.includes("apps-sdk")) {
113
+ const widgetConfig = {
114
+ hostType: "apps-sdk",
115
+ uri: `ui://widgets/apps-sdk/${name}.html`,
116
+ mimeType: "text/html+skybridge",
117
+ buildContentMeta: ({ resourceDomains, connectDomains, domain }, overrides) => {
118
+ const userUi = userMeta?.ui;
119
+ const userCsp = userUi?.csp;
120
+ const defaults = {
121
+ "openai/widgetCSP": {
122
+ resource_domains: resourceDomains,
123
+ connect_domains: connectDomains,
48
124
  },
49
- domain: serverUrl,
50
- },
51
- };
52
- return toMerged(defaults, { ui: userMeta?.ui });
53
- },
54
- };
55
- [appsSdkResourceConfig, extAppsResourceConfig].forEach((widgetConfig) => {
125
+ "openai/widgetDomain": domain,
126
+ "openai/widgetDescription": resourceConfig.description,
127
+ };
128
+ const fromUi = {
129
+ "openai/widgetCSP": {
130
+ resource_domains: userCsp?.resourceDomains,
131
+ connect_domains: userCsp?.connectDomains,
132
+ frame_domains: userCsp?.frameDomains,
133
+ redirect_domains: userCsp?.redirectDomains,
134
+ },
135
+ "openai/widgetDomain": userUi?.domain,
136
+ "openai/widgetPrefersBorder": userUi?.prefersBorder,
137
+ };
138
+ const directOpenaiKeys = Object.fromEntries(Object.entries(userMeta ?? {}).filter(([key]) => key.startsWith("openai/")));
139
+ return mergeWithUnion(mergeWithUnion(mergeWithUnion(defaults, fromUi), directOpenaiKeys), { "openai/widgetDomain": overrides.domain });
140
+ },
141
+ };
56
142
  this.registerWidgetResource({
57
143
  name,
58
144
  widgetConfig,
59
145
  resourceConfig,
60
146
  });
61
- });
62
- const toolMeta = {
63
- ...toolConfig._meta,
64
- "openai/outputTemplate": appsSdkResourceConfig.uri,
65
- ui: {
66
- resourceUri: extAppsResourceConfig.uri,
67
- },
68
- };
147
+ toolMeta["openai/outputTemplate"] = widgetConfig.uri;
148
+ }
149
+ if (!resourceConfig.hosts || resourceConfig.hosts.includes("mcp-app")) {
150
+ const widgetConfig = {
151
+ hostType: "mcp-app",
152
+ uri: `ui://widgets/ext-apps/${name}.html`,
153
+ mimeType: "text/html;profile=mcp-app",
154
+ buildContentMeta: ({ resourceDomains, connectDomains, domain }, overrides) => {
155
+ const defaults = {
156
+ ui: {
157
+ csp: {
158
+ resourceDomains,
159
+ connectDomains,
160
+ },
161
+ domain,
162
+ },
163
+ };
164
+ return mergeWithUnion(defaults, {
165
+ ui: { ...userMeta?.ui, ...overrides },
166
+ });
167
+ },
168
+ };
169
+ this.registerWidgetResource({
170
+ name,
171
+ widgetConfig,
172
+ resourceConfig,
173
+ });
174
+ // @ts-expect-error - For backwards compatibility with Claude current implementation of the specs
175
+ toolMeta["ui/resourceUri"] = widgetConfig.uri;
176
+ toolMeta.ui = { resourceUri: widgetConfig.uri };
177
+ }
69
178
  this.registerTool(name, {
70
179
  ...toolConfig,
71
180
  _meta: toolMeta,
@@ -79,11 +188,35 @@ export class McpServer extends McpServerBase {
79
188
  registerWidgetResource({ name, widgetConfig, resourceConfig, }) {
80
189
  const { hostType, uri: widgetUri, mimeType, buildContentMeta, } = widgetConfig;
81
190
  this.registerResource(name, widgetUri, { ...resourceConfig, _meta: resourceConfig._meta }, async (uri, extra) => {
82
- const serverUrl = process.env.NODE_ENV === "production"
83
- ? `https://${extra?.requestInfo?.headers?.["x-forwarded-host"] ?? extra?.requestInfo?.headers?.host}`
84
- : `http://localhost:3000`;
85
- const wsServerUrl = serverUrl.replace(/^http/, "ws");
86
- const html = process.env.NODE_ENV === "production"
191
+ const isProduction = process.env.NODE_ENV === "production";
192
+ const isClaude = extra?.requestInfo?.headers?.["user-agent"] === "Claude-User";
193
+ const headers = extra?.requestInfo?.headers || {};
194
+ const header = (key) => {
195
+ const val = headers[key];
196
+ return Array.isArray(val) ? val[0] : val;
197
+ };
198
+ let serverUrl;
199
+ const forwardedHost = header("x-forwarded-host");
200
+ const origin = header("origin");
201
+ const host = header("host");
202
+ if (forwardedHost) {
203
+ const proto = header("x-forwarded-proto") || "https";
204
+ serverUrl = `${proto}://${forwardedHost}`;
205
+ }
206
+ else if (origin) {
207
+ serverUrl = origin;
208
+ }
209
+ else if (host) {
210
+ const proto = ["127.0.0.1:", "localhost:"].some((p) => host.startsWith(p))
211
+ ? "http"
212
+ : "https";
213
+ serverUrl = `${proto}://${host}`;
214
+ }
215
+ else {
216
+ const devPort = process.env.__PORT || "3000";
217
+ serverUrl = `http://localhost:${devPort}`;
218
+ }
219
+ const html = isProduction
87
220
  ? templateHelper.renderProduction({
88
221
  hostType,
89
222
  serverUrl,
@@ -95,7 +228,29 @@ export class McpServer extends McpServerBase {
95
228
  serverUrl,
96
229
  widgetName: name,
97
230
  });
98
- const contentMeta = buildContentMeta({ serverUrl, wsServerUrl });
231
+ const connectDomains = [serverUrl];
232
+ if (!isProduction) {
233
+ const wsUrl = new URL(serverUrl);
234
+ wsUrl.protocol = wsUrl.protocol === "https:" ? "wss:" : "ws:";
235
+ connectDomains.push(wsUrl.origin);
236
+ }
237
+ let contentMetaOverrides = {};
238
+ if (isClaude) {
239
+ const pathname = extra?.requestInfo?.url?.pathname ?? "";
240
+ const url = `${serverUrl}${pathname}`;
241
+ const hash = crypto
242
+ .createHash("sha256")
243
+ .update(url)
244
+ .digest("hex")
245
+ .slice(0, 32);
246
+ contentMetaOverrides = { domain: `${hash}.claudemcpcontent.com` };
247
+ }
248
+ const contentMeta = buildContentMeta({
249
+ resourceDomains: [serverUrl],
250
+ connectDomains,
251
+ domain: serverUrl,
252
+ baseUriDomains: [serverUrl],
253
+ }, contentMetaOverrides);
99
254
  return {
100
255
  contents: [
101
256
  { uri: uri.href, mimeType, text: html, _meta: contentMeta },
@@ -1 +1 @@
1
- {"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/server/server.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/server/server.ts"],"names":[],"mappings":"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"}
@@ -1,5 +1,4 @@
1
- <base href="{{serverUrl}}" />
2
- <script type="module">window.skybridge = { hostType: "{{hostType}}" };</script>
1
+ <script type="module">window.skybridge = { hostType: "{{hostType}}", serverUrl: "{{serverUrl}}" };</script>
3
2
  <script type="module">
4
3
  import { injectIntoGlobalHook } from "{{serverUrl}}/assets/@react-refresh";
5
4
  injectIntoGlobalHook(window); window.$RefreshReg$ = () => {};
@@ -7,59 +6,6 @@
7
6
  window.__vite_plugin_react_preamble_installed__ = true;
8
7
  </script>
9
8
  <script type="module" src="{{serverUrl}}/@vite/client"></script>
10
- <script type="module">
11
- // Checks for browser support and shows error if local network access is denied
12
- (async () => {
13
- if (!navigator.permissions?.query) {
14
- return;
15
- }
16
-
17
- // Skip for non-http(s) protocols (file://, custom protocols in Electron, etc.)
18
- const protocol = window.location.protocol;
19
- const isNonHttpProtocol = protocol !== 'http:' && protocol !== 'https:'
20
- if (isNonHttpProtocol) {
21
- return;
22
- }
23
-
24
- const host = window.location.hostname;
25
- const isLoopback = host === 'localhost'
26
- || /^127\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/.test(host)
27
- || host === '::1';
28
- if (isLoopback) {
29
- return;
30
- }
31
-
32
- try {
33
- const status = await navigator.permissions.query({ name: "local-network-access" });
34
- if (status.state === "denied") {
35
- const errorDiv = document.createElement("div");
36
- errorDiv.style.cssText = "background: #fef2f2; border: 2px solid #ef4444; border-radius: 8px; padding: 16px; text-align: center; z-index: 10000; font-family: system-ui, sans-serif;";
37
-
38
- const errorTitle = document.createElement("div");
39
- errorTitle.style.cssText = "color: #ef4444; font-size: 18px; font-weight: 600; margin-bottom: 8px;";
40
- errorTitle.textContent = "Error: Local network access permission is denied.";
41
-
42
- const errorMessage = document.createElement("div");
43
- errorMessage.style.cssText = "color: #ef4444; font-size: 14px;";
44
- errorMessage.textContent = "Local network access is required for your widget to connect to the local dev server. Please enable it in your browser settings. ";
45
-
46
- const link = document.createElement("a");
47
- link.href = "https://developer.chrome.com/blog/local-network-access";
48
- link.target = "_blank";
49
- link.rel = "noopener noreferrer";
50
- link.style.cssText = "color: #ef4444; text-decoration: underline;";
51
- link.textContent = "Learn more";
52
- errorMessage.appendChild(link);
53
-
54
- errorDiv.appendChild(errorTitle);
55
- errorDiv.appendChild(errorMessage);
56
- document.body.appendChild(errorDiv);
57
- }
58
- } catch (e) {
59
- // Permission API doesn't support local-network-access, ignore silently
60
- }
61
- })();
62
- </script>
63
9
  <div id="root"></div>
64
10
  <script type="module" id="dev-widget-entry">
65
11
  import('{{serverUrl}}/src/widgets/{{widgetName}}');
@@ -1,5 +1,4 @@
1
- <base href="{{serverUrl}}" />
2
- <script type="module">window.skybridge = { hostType: "{{hostType}}" };</script>
1
+ <script type="module">window.skybridge = { hostType: "{{hostType}}", serverUrl: "{{serverUrl}}" };</script>
3
2
  <div id="root"></div>
4
3
  <script type="module">
5
4
  import('{{serverUrl}}/assets/{{widgetFile}}');
@@ -1,3 +1,4 @@
1
+ import type http from "node:http";
1
2
  import { type Router } from "express";
2
3
  /**
3
4
  * Install Vite dev server
@@ -9,4 +10,4 @@ import { type Router } from "express";
9
10
  * app.use(await widgetsRouter());
10
11
  * }
11
12
  */
12
- export declare const widgetsDevServer: () => Promise<Router>;
13
+ export declare const widgetsDevServer: (httpServer: http.Server) => Promise<Router>;
@@ -2,6 +2,7 @@ import { existsSync } from "node:fs";
2
2
  import path from "node:path";
3
3
  import cors from "cors";
4
4
  import express, {} from "express";
5
+ import { assetBaseUrlTransformPlugin } from "./asset-base-url-transform-plugin.js";
5
6
  /**
6
7
  * Install Vite dev server
7
8
  * This router MUST be installed at the application root, like so:
@@ -12,7 +13,7 @@ import express, {} from "express";
12
13
  * app.use(await widgetsRouter());
13
14
  * }
14
15
  */
15
- export const widgetsDevServer = async () => {
16
+ export const widgetsDevServer = async (httpServer) => {
16
17
  const router = express.Router();
17
18
  const { createServer, searchForWorkspaceRoot, loadConfigFromFile } = await import("vite");
18
19
  // Since 0.16.0, the template is a single package that does not rely on workspace.
@@ -26,7 +27,7 @@ export const widgetsDevServer = async () => {
26
27
  webAppRoot = path.join(workspaceRoot, "web");
27
28
  }
28
29
  const configResult = await loadConfigFromFile({ command: "serve", mode: "development" }, path.join(webAppRoot, "vite.config.ts"), webAppRoot);
29
- const { build, preview, ...devConfig } = configResult?.config || {};
30
+ const { build, preview, plugins: userPlugins = [], ...devConfig } = configResult?.config || {};
30
31
  const vite = await createServer({
31
32
  ...devConfig,
32
33
  configFile: false, // Keep this to prevent vite from trying to resolve path in the target config file
@@ -34,11 +35,20 @@ export const widgetsDevServer = async () => {
34
35
  server: {
35
36
  allowedHosts: true,
36
37
  middlewareMode: true,
38
+ hmr: {
39
+ server: httpServer,
40
+ },
37
41
  },
38
42
  root: webAppRoot,
39
43
  optimizeDeps: {
40
44
  include: ["react", "react-dom/client"],
41
45
  },
46
+ plugins: [
47
+ ...userPlugins,
48
+ assetBaseUrlTransformPlugin({
49
+ devServerOrigin: `http://localhost:${process.env.__PORT ?? "3000"}`,
50
+ }),
51
+ ],
42
52
  });
43
53
  router.use(cors());
44
54
  router.use("/", vite.middlewares);
@@ -1 +1 @@
1
- {"version":3,"file":"widgetsDevServer.js","sourceRoot":"","sources":["../../src/server/widgetsDevServer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,OAAO,EAAE,EAAe,MAAM,SAAS,CAAC;AAC/C;;;;;;;;;GASG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG,KAAK,IAAqB,EAAE;IAC1D,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IAEhC,MAAM,EAAE,YAAY,EAAE,sBAAsB,EAAE,kBAAkB,EAAE,GAChE,MAAM,MAAM,CAAC,MAAM,CAAC,CAAC;IAEvB,kFAAkF;IAClF,gFAAgF;IAChF,+FAA+F;IAC/F,IAAI,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,KAAK,CAAC,CAAC;IAEjD,0DAA0D;IAC1D,MAAM,aAAa,GAAG,UAAU,CAAC,UAAU,CAAC,CAAC;IAC7C,IAAI,CAAC,aAAa,EAAE,CAAC;QACnB,MAAM,aAAa,GAAG,sBAAsB,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC;QAC5D,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,KAAK,CAAC,CAAC;IAC/C,CAAC;IAED,MAAM,YAAY,GAAG,MAAM,kBAAkB,CAC3C,EAAE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,aAAa,EAAE,EACzC,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,gBAAgB,CAAC,EACvC,UAAU,CACX,CAAC;IAEF,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,SAAS,EAAE,GAAG,YAAY,EAAE,MAAM,IAAI,EAAE,CAAC;IAEpE,MAAM,IAAI,GAAG,MAAM,YAAY,CAAC;QAC9B,GAAG,SAAS;QACZ,UAAU,EAAE,KAAK,EAAE,kFAAkF;QACrG,OAAO,EAAE,QAAQ;QACjB,MAAM,EAAE;YACN,YAAY,EAAE,IAAI;YAClB,cAAc,EAAE,IAAI;SACrB;QACD,IAAI,EAAE,UAAU;QAChB,YAAY,EAAE;YACZ,OAAO,EAAE,CAAC,OAAO,EAAE,kBAAkB,CAAC;SACvC;KACF,CAAC,CAAC;IAEH,MAAM,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC;IACnB,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,WAAW,CAAC,CAAC;IAElC,OAAO,MAAM,CAAC;AAChB,CAAC,CAAC"}
1
+ {"version":3,"file":"widgetsDevServer.js","sourceRoot":"","sources":["../../src/server/widgetsDevServer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAErC,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,OAAO,EAAE,EAAe,MAAM,SAAS,CAAC;AAC/C,OAAO,EAAE,2BAA2B,EAAE,MAAM,sCAAsC,CAAC;AAEnF;;;;;;;;;GASG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG,KAAK,EACnC,UAAuB,EACN,EAAE;IACnB,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IAEhC,MAAM,EAAE,YAAY,EAAE,sBAAsB,EAAE,kBAAkB,EAAE,GAChE,MAAM,MAAM,CAAC,MAAM,CAAC,CAAC;IAEvB,kFAAkF;IAClF,gFAAgF;IAChF,+FAA+F;IAC/F,IAAI,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,KAAK,CAAC,CAAC;IAEjD,0DAA0D;IAC1D,MAAM,aAAa,GAAG,UAAU,CAAC,UAAU,CAAC,CAAC;IAC7C,IAAI,CAAC,aAAa,EAAE,CAAC;QACnB,MAAM,aAAa,GAAG,sBAAsB,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC;QAC5D,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,KAAK,CAAC,CAAC;IAC/C,CAAC;IAED,MAAM,YAAY,GAAG,MAAM,kBAAkB,CAC3C,EAAE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,aAAa,EAAE,EACzC,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,gBAAgB,CAAC,EACvC,UAAU,CACX,CAAC;IAEF,MAAM,EACJ,KAAK,EACL,OAAO,EACP,OAAO,EAAE,WAAW,GAAG,EAAE,EACzB,GAAG,SAAS,EACb,GAAG,YAAY,EAAE,MAAM,IAAI,EAAE,CAAC;IAE/B,MAAM,IAAI,GAAG,MAAM,YAAY,CAAC;QAC9B,GAAG,SAAS;QACZ,UAAU,EAAE,KAAK,EAAE,kFAAkF;QACrG,OAAO,EAAE,QAAQ;QACjB,MAAM,EAAE;YACN,YAAY,EAAE,IAAI;YAClB,cAAc,EAAE,IAAI;YACpB,GAAG,EAAE;gBACH,MAAM,EAAE,UAAU;aACnB;SACF;QACD,IAAI,EAAE,UAAU;QAChB,YAAY,EAAE;YACZ,OAAO,EAAE,CAAC,OAAO,EAAE,kBAAkB,CAAC;SACvC;QACD,OAAO,EAAE;YACP,GAAG,WAAW;YACd,2BAA2B,CAAC;gBAC1B,eAAe,EAAE,oBAAoB,OAAO,CAAC,GAAG,CAAC,MAAM,IAAI,MAAM,EAAE;aACpE,CAAC;SACH;KACF,CAAC,CAAC;IAEH,MAAM,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC;IACnB,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,WAAW,CAAC,CAAC;IAElC,OAAO,MAAM,CAAC;AAChB,CAAC,CAAC"}
@@ -43,8 +43,10 @@ describe("McpServer.registerWidget", () => {
43
43
  const appsSdkResourceCallback = mockRegisterResource.mock
44
44
  .calls[0]?.[3];
45
45
  expect(appsSdkResourceCallback).toBeDefined();
46
- const serverUrl = "http://localhost:3000";
47
- const mockExtra = createMockExtra("__not_used__");
46
+ const host = "localhost:3000";
47
+ const serverUrl = `http://${host}`;
48
+ const hmrUrl = `ws://${host}`;
49
+ const mockExtra = createMockExtra(host);
48
50
  const result = await appsSdkResourceCallback(new URL("ui://widgets/apps-sdk/my-widget.html"), mockExtra);
49
51
  expect(mockRegisterTool).toHaveBeenCalled();
50
52
  expect(result).toEqual({
@@ -56,10 +58,10 @@ describe("McpServer.registerWidget", () => {
56
58
  _meta: {
57
59
  "openai/widgetCSP": {
58
60
  resource_domains: [serverUrl],
59
- connect_domains: [serverUrl, "ws://localhost:3000"],
61
+ connect_domains: [serverUrl, hmrUrl],
60
62
  },
61
63
  "openai/widgetDomain": serverUrl,
62
- "openai/widgetDescription": "Test tool",
64
+ "openai/widgetDescription": "Test widget",
63
65
  },
64
66
  },
65
67
  ],
@@ -93,10 +95,10 @@ describe("McpServer.registerWidget", () => {
93
95
  _meta: {
94
96
  "openai/widgetCSP": {
95
97
  resource_domains: [serverUrl],
96
- connect_domains: [serverUrl, `wss://${host}`],
98
+ connect_domains: [serverUrl],
97
99
  },
98
100
  "openai/widgetDomain": serverUrl,
99
- "openai/widgetDescription": "Test tool",
101
+ "openai/widgetDescription": "Test widget",
100
102
  },
101
103
  },
102
104
  ],
@@ -134,7 +136,10 @@ describe("McpServer.registerWidget", () => {
134
136
  expect(mockRegisterResource).toHaveBeenCalledTimes(2);
135
137
  const appsSdkCallback = mockRegisterResource.mock
136
138
  .calls[0]?.[3];
137
- const appsSdkResult = await appsSdkCallback(new URL("ui://widgets/apps-sdk/my-widget.html"), createMockExtra("__not_used__"));
139
+ const appsSdkResult = await appsSdkCallback(new URL("ui://widgets/apps-sdk/my-widget.html"), createMockExtra("localhost:3000"));
140
+ const host = "localhost:3000";
141
+ const serverUrl = `http://${host}`;
142
+ const hmrUrl = `ws://${host}`;
138
143
  expect(appsSdkResult).toEqual({
139
144
  contents: [
140
145
  {
@@ -143,21 +148,21 @@ describe("McpServer.registerWidget", () => {
143
148
  text: expect.stringContaining('<div id="root"></div>'),
144
149
  _meta: {
145
150
  "openai/widgetCSP": {
146
- resource_domains: ["http://localhost:3000"],
147
- connect_domains: ["http://localhost:3000", "ws://localhost:3000"],
151
+ resource_domains: [serverUrl],
152
+ connect_domains: [serverUrl, hmrUrl],
148
153
  },
149
- "openai/widgetDomain": "http://localhost:3000",
150
- "openai/widgetDescription": "Test tool",
154
+ "openai/widgetDomain": serverUrl,
155
+ "openai/widgetDescription": "Test widget",
151
156
  "openai/widgetPrefersBorder": true,
152
157
  },
153
158
  },
154
159
  ],
155
160
  });
156
- expect(appsSdkResult.contents[0]?.text).toContain('window.skybridge = { hostType: "apps-sdk" }');
161
+ expect(appsSdkResult.contents[0]?.text).toContain('window.skybridge = { hostType: "apps-sdk", serverUrl: "http://localhost:3000" };');
157
162
  const extAppsResourceCallback = mockRegisterResource.mock
158
163
  .calls[1]?.[3];
159
164
  expect(extAppsResourceCallback).toBeDefined();
160
- const extAppsResult = await extAppsResourceCallback(new URL("ui://widgets/ext-apps/my-widget.html"), createMockExtra("__not_used__"));
165
+ const extAppsResult = await extAppsResourceCallback(new URL("ui://widgets/ext-apps/my-widget.html"), createMockExtra(host));
161
166
  expect(extAppsResult).toEqual({
162
167
  contents: [
163
168
  {
@@ -167,19 +172,16 @@ describe("McpServer.registerWidget", () => {
167
172
  _meta: {
168
173
  ui: {
169
174
  csp: {
170
- resourceDomains: ["http://localhost:3000"],
171
- connectDomains: [
172
- "http://localhost:3000",
173
- "ws://localhost:3000",
174
- ],
175
+ resourceDomains: [serverUrl],
176
+ connectDomains: [serverUrl, hmrUrl],
175
177
  },
176
- domain: "http://localhost:3000",
178
+ domain: serverUrl,
177
179
  },
178
180
  },
179
181
  },
180
182
  ],
181
183
  });
182
- expect(extAppsResult.contents[0]?.text).toContain('window.skybridge = { hostType: "mcp-app" }');
184
+ expect(extAppsResult.contents[0]?.text).toContain('window.skybridge = { hostType: "mcp-app", serverUrl: "http://localhost:3000" };');
183
185
  });
184
186
  it("should register tool with ui.resourceUri metadata (not deprecated ui/resourceUri)", async () => {
185
187
  const mockToolCallback = vi.fn();
@@ -217,12 +219,15 @@ describe("McpServer.registerWidget", () => {
217
219
  server.registerWidget("override-test", mockRegisterResourceConfig, mockToolConfig, mockToolCallback);
218
220
  const appsSdkCallback = mockRegisterResource.mock
219
221
  .calls[0]?.[3];
220
- const result = await appsSdkCallback(new URL("ui://widgets/apps-sdk/override-test.html"), createMockExtra("__not_used__"));
222
+ const host = `localhost:3000`;
223
+ const serverUrl = `http://${host}`;
224
+ const hmrUrl = `ws://${host}`;
225
+ const result = await appsSdkCallback(new URL("ui://widgets/apps-sdk/override-test.html"), createMockExtra(host));
221
226
  const meta = result.contents[0]?._meta;
222
- // CSP arrays are merged by index - user value replaces at index 0, defaults remain at other indices
227
+ // CSP arrays are merged with union - all unique domains from defaults and user config are preserved
223
228
  expect(meta["openai/widgetCSP"]).toEqual({
224
- resource_domains: ["https://from-ui-csp.com"],
225
- connect_domains: ["https://from-ui-csp.com", "ws://localhost:3000"],
229
+ resource_domains: [serverUrl, "https://from-ui-csp.com"],
230
+ connect_domains: [serverUrl, hmrUrl, "https://from-ui-csp.com"],
226
231
  frame_domains: undefined,
227
232
  redirect_domains: undefined,
228
233
  });
@@ -231,7 +236,28 @@ describe("McpServer.registerWidget", () => {
231
236
  // PrefersBorder should be overridden by direct openai/* key (highest priority)
232
237
  expect(meta["openai/widgetPrefersBorder"]).toBe(true);
233
238
  // Description should be from defaults (toolConfig.description)
234
- expect(meta["openai/widgetDescription"]).toBe("Test tool");
239
+ expect(meta["openai/widgetDescription"]).toBe("Test widget");
240
+ });
241
+ it("should register tool with ui.resourceUri metadata only", async () => {
242
+ const mockToolCallback = vi.fn();
243
+ server.registerWidget("my-widget", { description: "Test widget", hosts: ["mcp-app"] }, { description: "Test tool" }, mockToolCallback);
244
+ expect(mockRegisterTool).toHaveBeenCalledTimes(1);
245
+ const toolCallArgs = mockRegisterTool.mock.calls[0];
246
+ const toolConfig = toolCallArgs?.[1];
247
+ expect(toolConfig._meta).toHaveProperty("ui");
248
+ expect(toolConfig._meta?.ui).toEqual({
249
+ resourceUri: "ui://widgets/ext-apps/my-widget.html",
250
+ });
251
+ expect(toolConfig._meta?.["openai/outputTemplate"]).to.be.undefined;
252
+ });
253
+ it("should register tool with uopenai/outputTemplate metadata only", async () => {
254
+ const mockToolCallback = vi.fn();
255
+ server.registerWidget("my-widget", { description: "Test widget", hosts: ["apps-sdk"] }, { description: "Test tool" }, mockToolCallback);
256
+ expect(mockRegisterTool).toHaveBeenCalledTimes(1);
257
+ const toolCallArgs = mockRegisterTool.mock.calls[0];
258
+ const toolConfig = toolCallArgs?.[1];
259
+ expect(toolConfig._meta).not.toHaveProperty("ui");
260
+ expect(toolConfig._meta?.["openai/outputTemplate"]).to.eq("ui://widgets/apps-sdk/my-widget.html");
235
261
  });
236
262
  });
237
263
  //# sourceMappingURL=widget.test.js.map